Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
afcc59a
Fix MCP widget dark mode readability
ved015 Jun 30, 2026
677b9d4
Fix Biome CSS formatting in tokens.css
github-actions[bot] Jun 30, 2026
f7d2bb7
style(mcp): align widget UI with Nova theme
ishaanxgupta Jul 1, 2026
0d7e231
style(mcp): solidify widget form surfaces
ishaanxgupta Jul 1, 2026
a8f3644
style(mcp): align graph widget surface
ishaanxgupta Jul 1, 2026
c811b40
fix(mcp): resolve Biome lint and format errors
github-actions[bot] Jul 1, 2026
d5d0cb7
Align MCP widget cards with web theme
ishaanxgupta Jul 2, 2026
645a440
Preview Cursor MCP widget shell in studio
ishaanxgupta Jul 2, 2026
a09b5b6
Use neutral hover states for MCP cards
ishaanxgupta Jul 2, 2026
6a85341
Use Supermemory loader in MCP widget
ishaanxgupta Jul 2, 2026
ef3762d
Remove icons from MCP workspace cards
ishaanxgupta Jul 2, 2026
d7e2991
Match MCP workspace cards to web style
ishaanxgupta Jul 2, 2026
a81a7ed
Match MCP permission pill to web card
ishaanxgupta Jul 2, 2026
ca71ed6
Remove MCP status card borders
ishaanxgupta Jul 2, 2026
f0023bb
Match MCP confirmation card style
ishaanxgupta Jul 2, 2026
649d985
Polish MCP widget UI
ishaanxgupta Jul 3, 2026
1e81a3e
Fix Biome formatting in Button.tsx
github-actions[bot] Jul 3, 2026
2f391cf
Match MCP widget typography to web
ishaanxgupta Jul 3, 2026
af0bc63
Avoid picker scroll for short workspace lists
ishaanxgupta Jul 3, 2026
e72f775
Hide picker scrollbar chrome
ishaanxgupta Jul 3, 2026
166317b
Tighten MCP picker card spacing
ishaanxgupta Jul 3, 2026
440e9e9
Constrain MCP picker card width
ishaanxgupta Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 63 additions & 5 deletions apps/mcp/src/widget/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from "react"
import { type ReactNode, useEffect } from "react"
import type { ViewMessage } from "../shared/types"
import { useApplyHostTheme } from "./hooks/useApplyHostTheme"
import { useLog } from "./hooks/useLog"
Expand All @@ -25,15 +25,73 @@ export function App() {
}
}, [state, log])

if (state.kind === "loading") return <Loading />
if (state.kind === "error") return <ErrorView message={state.message} />
if (state.kind === "loading") {
return (
<WidgetShell>
<Loading />
</WidgetShell>
)
}
if (state.kind === "error") {
return (
<WidgetShell>
<ErrorView message={state.message} />
</WidgetShell>
)
}
if (state.kind === "raw") {
return (
<ErrorView message="Received unrecognized response from server. Try again." />
<WidgetShell>
<ErrorView message="Received unrecognized response from server. Try again." />
</WidgetShell>
)
}

return renderView(state.message, setView, setError)
const isGraphView = state.message.view === "graph"
return (
<WidgetShell immersive={isGraphView}>
{renderView(state.message, setView, setError)}
</WidgetShell>
)
}

export function WidgetShell({
children,
immersive = false,
}: {
children: ReactNode
immersive?: boolean
}) {
const shellClassName = immersive
? "mcp-widget-shell mcp-widget-shell-graph"
: "mcp-widget-shell"

return (
<div className={shellClassName}>
<div aria-hidden className="mcp-widget-glow" />
{immersive ? null : (
<header className="mcp-widget-brand">
<span aria-hidden className="mcp-widget-brand-mark">
<svg aria-hidden="true" viewBox="0 0 314 256">
<path
d="M313.728 100.982H197.297V0H159.68V109.567C159.68 121.205 164.284 132.381 172.466 140.615L267.535 236.283L294.134 209.517L223.917 138.858H313.75V101.004L313.728 100.982Z"
fill="currentColor"
/>
<path
d="M19.616 46.5043L89.8323 117.163H0V155.017H116.431V255.999H154.048V146.432C154.048 134.795 149.444 123.618 141.262 115.384L46.2144 19.7383L19.616 46.5043Z"
fill="currentColor"
/>
</svg>
</span>
<span className="mcp-widget-brand-copy">
<span className="mcp-widget-brand-name">supermemory</span>
<span className="mcp-widget-brand-mode">MCP</span>
</span>
</header>
)}
<main className="mcp-widget-content">{children}</main>
</div>
)
}

function renderView(
Expand Down
12 changes: 5 additions & 7 deletions apps/mcp/src/widget/components/PermissionBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { Badge } from "../design/ui"

interface Props {
permission: string
}

export function PermissionBadge({ permission }: Props) {
const isWrite = permission === "write"
return (
<Badge
className="mt-2 self-start uppercase"
variant={isWrite ? "accent" : "neutral"}
<span
className="text-[#8B8B8B]"
style={{ fontFamily: "var(--font-brand)" }}
>
{isWrite ? "read/write" : "read only"}
</Badge>
{isWrite ? "Read/write" : "Read only"}
</span>
)
}
41 changes: 18 additions & 23 deletions apps/mcp/src/widget/components/WorkspaceCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ContainerTag, ContainerTagAccess } from "../../shared/types"
import { Card, Stack } from "../design/ui"
import { Check, Package } from "../lib/icons"
import { Check } from "../lib/icons"
import { PermissionBadge } from "./PermissionBadge"

interface Props {
Expand All @@ -20,46 +20,41 @@ export function WorkspaceCard({
return (
<Card
as="button"
className="w-full"
className="min-h-[92px] w-full p-2.5"
onClick={() => onClick(containerTag.containerTag)}
variant={active ? "active" : "interactive"}
>
<Stack gap="sm">
<div className="absolute right-2.5 top-2.5 text-[11px] font-medium leading-none">
{access ? <PermissionBadge permission={access.permission} /> : null}
</div>
{active ? (
<Check className="absolute bottom-2.5 right-2.5 size-3 shrink-0 text-accent" />
) : null}
<Stack className="h-full" gap="xs">
<Stack align="center" direction="row" gap="sm" justify="between">
<span className="flex min-w-0 items-center gap-(--space-2)">
{containerTag.emoji ? (
<span aria-hidden className="text-base leading-none">
{containerTag.emoji}
</span>
) : (
<Package className="size-4 shrink-0 text-text-secondary" />
)}
<span
className="truncate text-(length:--text-sm) font-semibold text-text-primary"
style={{ fontFamily: "var(--font-brand)" }}
>
{name}
</span>
<span
className="min-w-0 max-w-[calc(100%-82px)] truncate text-sm font-medium text-white"
style={{ fontFamily: "var(--font-brand)" }}
>
{name}
</span>
{active ? <Check className="size-4 shrink-0 text-accent" /> : null}
</Stack>
<div className="truncate font-mono text-(length:--text-xs) text-text-muted">
<div className="truncate text-xs leading-normal text-[#8B8B8B]">
{containerTag.containerTag}
</div>
{(containerTag.documentCount > 0 || containerTag.memoryCount > 0) && (
<div className="flex items-center gap-(--space-3) text-(length:--text-xs) text-text-muted">
<div className="mt-0.5 flex items-center gap-(--space-2) text-xs leading-normal text-[#8B8B8B]">
<span>
{containerTag.documentCount} doc
{containerTag.documentCount === 1 ? "" : "s"}
</span>
<span aria-hidden>·</span>
<span>
{containerTag.memoryCount} mem
{containerTag.memoryCount === 1 ? "" : "s"}
{containerTag.memoryCount}{" "}
{containerTag.memoryCount === 1 ? "memory" : "memories"}
</span>
</div>
)}
{access ? <PermissionBadge permission={access.permission} /> : null}
</Stack>
</Card>
)
Expand Down
Loading
Loading