+
+
+ Credits
+
+
+ {isLoadingUsage ? "…" : `${tokensToCredits(tokensUsed)} / ${tokensToCredits(tokensLimit)}`}
+
+
+
+
80
+ ? "#ef4444"
+ : hasPaidPlan
+ ? "linear-gradient(to right, #4BA0FA 80%, #002757 100%)"
+ : "#0054AD",
+ }}
+ />
+
- {!hasProProduct && (
-
+
+
+
+
+ Search Queries
+
+
+ {isLoadingUsage ? "…" : `${formatUsageNumber(searchesUsed)} / ${formatUsageNumber(searchesLimit)}`}
+
+
+
80
+ ? "#ef4444"
+ : hasPaidPlan
+ ? "linear-gradient(to right, #4BA0FA 80%, #002757 100%)"
+ : "#0054AD",
+ }}
/>
+
+
+ {!hasPaidPlan && (
+
)}
)}
diff --git a/apps/web/components/new/documents-command-palette.tsx b/apps/web/components/new/documents-command-palette.tsx
index ee4a34099..bff1ebcf4 100644
--- a/apps/web/components/new/documents-command-palette.tsx
+++ b/apps/web/components/new/documents-command-palette.tsx
@@ -38,7 +38,7 @@ interface DocumentsCommandPaletteProps {
projectId: string
onOpenDocument: (document: DocumentWithMemories) => void
onAddMemory?: () => void
- onOpenMCP?: () => void
+ onOpenIntegrations?: () => void
initialSearch?: string
}
@@ -48,7 +48,7 @@ export function DocumentsCommandPalette({
projectId,
onOpenDocument,
onAddMemory,
- onOpenMCP,
+ onOpenIntegrations,
initialSearch = "",
}: DocumentsCommandPaletteProps) {
const isMobile = useIsMobile()
@@ -95,13 +95,13 @@ export function DocumentsCommandPalette({
action: () => { close(); onAddMemory() },
}]
: []),
- ...(onOpenMCP
+ ...(onOpenIntegrations
? [{
kind: "action" as const,
- id: "mcp",
- label: "Open MCP",
+ id: "integrations",
+ label: "Open Integrations",
icon:
,
- action: () => { close(); onOpenMCP() },
+ action: () => { close(); onOpenIntegrations() },
}]
: []),
]
diff --git a/apps/web/components/new/header.tsx b/apps/web/components/new/header.tsx
index c66220e57..be6716b87 100644
--- a/apps/web/components/new/header.tsx
+++ b/apps/web/components/new/header.tsx
@@ -11,6 +11,7 @@ import {
Settings,
Home,
Code2,
+ Cable,
ExternalLink,
HelpCircle,
MenuIcon,
@@ -43,14 +44,12 @@ import { feedbackParam } from "@/lib/search-params"
interface HeaderProps {
onAddMemory?: () => void
- onOpenMCP?: () => void
onOpenChat?: () => void
onOpenSearch?: () => void
}
export function Header({
onAddMemory,
- onOpenMCP,
onOpenChat,
onOpenSearch,
}: HeaderProps) {
@@ -155,8 +154,8 @@ export function Header({
{!isMobile && (
setViewMode(v === "grid" ? "list" : "graph")}
+ value={viewMode === "list" ? "grid" : viewMode}
+ onValueChange={(v) => setViewMode(v === "grid" ? "list" : v as "graph" | "integrations")}
>
Graph
+
+
+ Integrations
+
)}
@@ -220,11 +229,11 @@ export function Header({
Add memory
setViewMode("integrations")}
className="px-3 py-2.5 rounded-md hover:bg-[#293952]/40 cursor-pointer text-white text-sm font-medium gap-2"
>
-
- MCP
+
+ Integrations
-
@@ -160,48 +142,19 @@ export function ProfileStep({ onSubmit }: ProfileStepProps) {
>
LinkedIn
-
-
-
- linkedin.com/in/
-
-
handleLinkedInChange(e.target.value)}
- onBlur={() => {
- if (linkedinProfile.trim()) {
- const error = validateLinkedInHandle(linkedinProfile)
- setErrors((prev) => ({ ...prev, linkedin: error }))
- }
- }}
- className={`flex-1 px-4 py-2 bg-[#070E1B] text-white placeholder-onboarding focus:outline-none transition-colors ${
- errors.linkedin ? "bg-[#290F0A]" : ""
- }`}
- />
-
- {errors.linkedin && (
-
- )}
-
+
handleLinkedInChange(e.target.value)}
+ onBlur={handleLinkedInBlur}
+ className={`w-full px-4 py-2 bg-[#070E1B] border rounded-xl text-white placeholder-onboarding focus:outline-none focus:border-[#4A4A4A] transition-colors h-[40px] ${
+ errors.linkedin
+ ? "border-[#52596633] bg-[#290F0A]"
+ : "border-onboarding/20"
+ }`}
+ />
{
const formData = {
- twitter: toXProfileUrl(twitterHandle),
- linkedin: toLinkedInProfileUrl(linkedinProfile),
+ twitter: toXProfileUrl(parseXHandle(twitterHandle)),
+ linkedin: toLinkedInProfileUrl(parseLinkedInHandle(linkedinProfile)),
description: description,
- otherLinks: otherLinks.filter((l) => l.trim()),
+ otherLinks: otherLinks
+ .filter((l) => l.trim())
+ .map((l) => normalizeUrl(l.trim())),
}
analytics.onboardingProfileSubmitted({
has_twitter: !!twitterHandle.trim(),
diff --git a/apps/web/components/new/settings/integrations.tsx b/apps/web/components/new/settings/integrations.tsx
index f28695e7f..479a5852f 100644
--- a/apps/web/components/new/settings/integrations.tsx
+++ b/apps/web/components/new/settings/integrations.tsx
@@ -24,6 +24,11 @@ import Image from "next/image"
import { useSearchParams } from "next/navigation"
import { useEffect, useId, useState } from "react"
import { toast } from "sonner"
+import {
+ ChromeIcon,
+ AppleShortcutsIcon,
+ RaycastIcon,
+} from "@/components/new/integration-icons"
function SectionTitle({ children }: { children: React.ReactNode }) {
return (
@@ -106,73 +111,6 @@ function FeatureItem({ text }: { text: string }) {
)
}
-function ChromeIcon({ className }: { className?: string }) {
- return (
-
- )
-}
-
-function AppleShortcutsIcon() {
- return (
-
-
-
- )
-}
-
-function RaycastIcon({ className }: { className?: string }) {
- return (
-
- )
-}
export default function Integrations() {
const { org } = useAuth()
diff --git a/apps/web/lib/analytics.ts b/apps/web/lib/analytics.ts
index 6d8c00ebe..adc72e132 100644
--- a/apps/web/lib/analytics.ts
+++ b/apps/web/lib/analytics.ts
@@ -29,7 +29,7 @@ export const analytics = {
chatHistoryViewed: () => safeCapture("chat_history_viewed"),
chatDeleted: () => safeCapture("chat_deleted"),
- viewModeChanged: (mode: "graph" | "list") =>
+ viewModeChanged: (mode: "graph" | "list" | "integrations") =>
safeCapture("view_mode_changed", { mode }),
documentCardClicked: () => safeCapture("document_card_clicked"),
@@ -84,7 +84,7 @@ export const analytics = {
safeCapture("onboarding_integration_clicked", props),
onboardingChromeExtensionClicked: (props: {
- source: "onboarding" | "settings"
+ source: "onboarding" | "settings" | "integrations"
}) => safeCapture("onboarding_chrome_extension_clicked", props),
onboardingMcpDetailOpened: () => safeCapture("onboarding_mcp_detail_opened"),
diff --git a/apps/web/lib/search-params.ts b/apps/web/lib/search-params.ts
index 129331ca1..3a38939fd 100644
--- a/apps/web/lib/search-params.ts
+++ b/apps/web/lib/search-params.ts
@@ -23,6 +23,8 @@ export const shareParam = parseAsBoolean.withDefault(false)
export const feedbackParam = parseAsBoolean.withDefault(false)
// View & filter states
-export const viewParam = parseAsStringLiteral(["graph", "list"] as const).withDefault("graph")
+const viewLiterals = ["graph", "list", "integrations"] as const
+export type ViewParamValue = (typeof viewLiterals)[number]
+export const viewParam = parseAsStringLiteral(viewLiterals).withDefault("graph")
export const categoriesParam = parseAsArrayOf(parseAsString, ",").withDefault([])
export const projectParam = parseAsString.withDefault("sm_project_default")
diff --git a/apps/web/lib/view-mode-context.tsx b/apps/web/lib/view-mode-context.tsx
index c2a6837eb..e797b47ac 100644
--- a/apps/web/lib/view-mode-context.tsx
+++ b/apps/web/lib/view-mode-context.tsx
@@ -1,11 +1,13 @@
"use client"
import { useQueryState } from "nuqs"
-import { viewParam } from "@/lib/search-params"
+import { viewParam, type ViewParamValue } from "@/lib/search-params"
import { analytics } from "@/lib/analytics"
import { useCallback } from "react"
-type ViewMode = "graph" | "list"
+export type ViewMode = ViewParamValue
+
+type SetViewMode = (value: ViewMode | null) => Promise
export function useViewMode() {
const [viewMode, _setViewMode] = useQueryState("view", viewParam)
@@ -13,7 +15,7 @@ export function useViewMode() {
const setViewMode = useCallback(
(mode: ViewMode) => {
analytics.viewModeChanged(mode)
- _setViewMode(mode)
+ ;(_setViewMode as SetViewMode)(mode)
},
[_setViewMode],
)
diff --git a/apps/web/public/images/plugins/claude-code.svg b/apps/web/public/images/plugins/claude-code.svg
new file mode 100644
index 000000000..210688d84
--- /dev/null
+++ b/apps/web/public/images/plugins/claude-code.svg
@@ -0,0 +1,11 @@
+
diff --git a/apps/web/public/images/plugins/clawdbot.svg b/apps/web/public/images/plugins/clawdbot.svg
new file mode 100644
index 000000000..f50fb48a7
--- /dev/null
+++ b/apps/web/public/images/plugins/clawdbot.svg
@@ -0,0 +1,86 @@
+
+
+
+
\ No newline at end of file
diff --git a/apps/web/public/images/plugins/opencode.svg b/apps/web/public/images/plugins/opencode.svg
new file mode 100644
index 000000000..b79140a50
--- /dev/null
+++ b/apps/web/public/images/plugins/opencode.svg
@@ -0,0 +1 @@
+
\ No newline at end of file