mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
rename mcp internals to space
This commit is contained in:
parent
8a9fcd52dc
commit
30e1242bc5
25 changed files with 132 additions and 143 deletions
|
|
@ -136,7 +136,7 @@ discovery and rejection tests still run.
|
|||
|
||||
## Storage And Rollout
|
||||
|
||||
`WorkspaceState` stores only the active space's container tag. It never stores bearer
|
||||
`SpaceState` stores only the active space's container tag. It never stores bearer
|
||||
tokens, MCP client identity, or protocol messages.
|
||||
|
||||
The old `SupermemoryMCP` class and binding remain inert for one rollout. This
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { validateOAuthToken, type AuthUser } from "./auth"
|
|||
import { SupermemoryMCP } from "./legacy-protocol-state"
|
||||
import { createSupermemoryServer } from "./server"
|
||||
import type { ActorContext, ServerEnv } from "./types"
|
||||
import { WorkspaceState } from "./workspace-state"
|
||||
import { SpaceState } from "./space-state"
|
||||
|
||||
type Bindings = ServerEnv
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ app.all("/", (c) => handleMcpRequest(c, "/mcp"))
|
|||
app.all("/mcp", (c) => handleMcpRequest(c))
|
||||
app.all("/mcp/", (c) => handleMcpRequest(c, "/mcp"))
|
||||
|
||||
export { SupermemoryMCP, WorkspaceState }
|
||||
export { SpaceState, SupermemoryMCP }
|
||||
export type { ActorContext, ServerEnv }
|
||||
|
||||
export default app
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
|
|||
import {
|
||||
compactDescription,
|
||||
formatFactSection,
|
||||
formatWorkspaceRow,
|
||||
sortWorkspaces,
|
||||
workspaceDisplayName,
|
||||
workspaceMetadata,
|
||||
} from "../workspace-presentation"
|
||||
formatSpaceRow,
|
||||
sortSpaces,
|
||||
spaceDisplayName,
|
||||
spaceMetadata,
|
||||
} from "../space-presentation"
|
||||
|
||||
const CONTEXT_FACT_LIMIT = 8
|
||||
const RECENT_WORKSPACE_LIMIT = 3
|
||||
const RECENT_SPACE_LIMIT = 3
|
||||
|
||||
export function registerContextPrompt(
|
||||
server: McpServer,
|
||||
|
|
@ -26,24 +26,24 @@ export function registerContextPrompt(
|
|||
try {
|
||||
const selectedTag = await resolveContainerTag()
|
||||
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
|
||||
const [profileResult, workspaces] = await Promise.all([
|
||||
const [profileResult, spaces] = await Promise.all([
|
||||
getClient(activeKey).getProfile(),
|
||||
getClient().listContainerTags(),
|
||||
])
|
||||
const activeWorkspace = workspaces.find(
|
||||
(workspace) => workspace.containerTag === activeKey,
|
||||
const activeSpace = spaces.find(
|
||||
(space) => space.containerTag === activeKey,
|
||||
)
|
||||
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
|
||||
const activeLabel = spaceDisplayName(activeSpace, activeKey)
|
||||
const fallback = selectedTag ? "" : " (default)"
|
||||
const parts: string[] = [
|
||||
"# Supermemory Context",
|
||||
`Active space: ${activeLabel} [${activeKey}]${fallback}`,
|
||||
]
|
||||
|
||||
if (activeWorkspace) {
|
||||
const metadata = workspaceMetadata(activeWorkspace)
|
||||
if (activeSpace) {
|
||||
const metadata = spaceMetadata(activeSpace)
|
||||
if (metadata) parts.push(metadata)
|
||||
const description = compactDescription(activeWorkspace.description)
|
||||
const description = compactDescription(activeSpace.description)
|
||||
if (description) parts.push(description)
|
||||
}
|
||||
|
||||
|
|
@ -68,15 +68,15 @@ export function registerContextPrompt(
|
|||
parts.push("No profile facts are available for this space yet.")
|
||||
}
|
||||
|
||||
const recentWorkspaces = sortWorkspaces(workspaces, activeKey)
|
||||
.filter((workspace) => workspace.containerTag !== activeKey)
|
||||
.slice(0, RECENT_WORKSPACE_LIMIT)
|
||||
if (recentWorkspaces.length > 0) {
|
||||
const recentSpaces = sortSpaces(spaces, activeKey)
|
||||
.filter((space) => space.containerTag !== activeKey)
|
||||
.slice(0, RECENT_SPACE_LIMIT)
|
||||
if (recentSpaces.length > 0) {
|
||||
parts.push(
|
||||
"",
|
||||
"## Recently Active Spaces",
|
||||
...recentWorkspaces.map((workspace) =>
|
||||
formatWorkspaceRow(workspace, activeKey, 100),
|
||||
...recentSpaces.map((space) =>
|
||||
formatSpaceRow(space, activeKey, 100),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { McpServer } from "@modelcontextprotocol/server"
|
||||
import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
|
||||
import {
|
||||
formatWorkspaceRow,
|
||||
sortWorkspaces,
|
||||
workspaceDisplayName,
|
||||
} from "../workspace-presentation"
|
||||
formatSpaceRow,
|
||||
sortSpaces,
|
||||
spaceDisplayName,
|
||||
} from "../space-presentation"
|
||||
|
||||
export function registerContainerTagsResource(
|
||||
server: McpServer,
|
||||
|
|
@ -18,13 +18,13 @@ export function registerContainerTagsResource(
|
|||
resolveContainerTag(),
|
||||
])
|
||||
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
|
||||
const activeWorkspace = containerTags.find(
|
||||
(workspace) => workspace.containerTag === activeKey,
|
||||
const activeSpace = containerTags.find(
|
||||
(space) => space.containerTag === activeKey,
|
||||
)
|
||||
const rows = sortWorkspaces(containerTags, activeKey).map((workspace) =>
|
||||
formatWorkspaceRow(workspace, activeKey),
|
||||
const rows = sortSpaces(containerTags, activeKey).map((space) =>
|
||||
formatSpaceRow(space, activeKey),
|
||||
)
|
||||
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
|
||||
const activeLabel = spaceDisplayName(activeSpace, activeKey)
|
||||
const fallback = selectedTag ? "" : " (default)"
|
||||
const text = [
|
||||
"# My Spaces",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { DEFAULT_PROJECT_ID, type SupermemoryClient } from "../client"
|
|||
import {
|
||||
compactDescription,
|
||||
formatFactSection,
|
||||
workspaceDisplayName,
|
||||
workspaceMetadata,
|
||||
} from "../workspace-presentation"
|
||||
spaceDisplayName,
|
||||
spaceMetadata,
|
||||
} from "../space-presentation"
|
||||
|
||||
const PROFILE_FACT_LIMIT = 12
|
||||
|
||||
|
|
@ -21,24 +21,24 @@ export function registerProfileResource(
|
|||
async () => {
|
||||
const selectedTag = await resolveContainerTag()
|
||||
const activeKey = selectedTag ?? DEFAULT_PROJECT_ID
|
||||
const [profileResult, workspaces] = await Promise.all([
|
||||
const [profileResult, spaces] = await Promise.all([
|
||||
getClient(activeKey).getProfile(),
|
||||
getClient().listContainerTags(),
|
||||
])
|
||||
const activeWorkspace = workspaces.find(
|
||||
(workspace) => workspace.containerTag === activeKey,
|
||||
const activeSpace = spaces.find(
|
||||
(space) => space.containerTag === activeKey,
|
||||
)
|
||||
const activeLabel = workspaceDisplayName(activeWorkspace, activeKey)
|
||||
const activeLabel = spaceDisplayName(activeSpace, activeKey)
|
||||
const fallback = selectedTag ? "" : " (default)"
|
||||
const parts: string[] = [
|
||||
"# Active Space Profile",
|
||||
`Space: ${activeLabel} [${activeKey}]${fallback}`,
|
||||
]
|
||||
|
||||
if (activeWorkspace) {
|
||||
const metadata = workspaceMetadata(activeWorkspace)
|
||||
if (activeSpace) {
|
||||
const metadata = spaceMetadata(activeSpace)
|
||||
if (metadata) parts.push(metadata)
|
||||
const description = compactDescription(activeWorkspace.description)
|
||||
const description = compactDescription(activeSpace.description)
|
||||
if (description) parts.push(description)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ import { registerAllTools } from "./tools"
|
|||
import { errorResult } from "./tools/types"
|
||||
import type { ActorContext, ServerEnv } from "./types"
|
||||
import {
|
||||
resolveContainerTag as resolveWorkspaceContainerTag,
|
||||
workspaceStateName,
|
||||
} from "./workspace"
|
||||
resolveContainerTag as resolveSpaceContainerTag,
|
||||
spaceStateName,
|
||||
} from "./space"
|
||||
|
||||
const DEFAULT_API_URL = "https://api.supermemory.ai"
|
||||
|
||||
|
|
@ -53,17 +53,15 @@ export function createSupermemoryServer(
|
|||
version: "1.0.0",
|
||||
})
|
||||
const apiUrl = env.API_URL || DEFAULT_API_URL
|
||||
const workspaceState = env.WORKSPACE_STATE.getByName(
|
||||
workspaceStateName(actor),
|
||||
)
|
||||
const spaceState = env.SPACE_STATE.getByName(spaceStateName(actor))
|
||||
|
||||
const getClient = (containerTag?: string) =>
|
||||
new SupermemoryClient(actor.bearerToken, containerTag, apiUrl)
|
||||
const getActiveContainerTag = () => workspaceState.getActiveContainerTag()
|
||||
const getActiveContainerTag = () => spaceState.getActiveContainerTag()
|
||||
const setActiveContainerTag = (containerTag: string) =>
|
||||
workspaceState.setActiveContainerTag(containerTag)
|
||||
spaceState.setActiveContainerTag(containerTag)
|
||||
const resolveContainerTag = (explicit?: string) =>
|
||||
resolveWorkspaceContainerTag(explicit, getActiveContainerTag)
|
||||
resolveSpaceContainerTag(explicit, getActiveContainerTag)
|
||||
const analytics = createPosthogAnalytics(env, actor, waitUntil)
|
||||
const toolServer = createTrackedToolServer(
|
||||
server,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import type { ContainerTag } from "../shared/types"
|
|||
import {
|
||||
compactDescription,
|
||||
formatFactSection,
|
||||
formatWorkspaceRow,
|
||||
sortWorkspaces,
|
||||
} from "./workspace-presentation"
|
||||
formatSpaceRow,
|
||||
sortSpaces,
|
||||
} from "./space-presentation"
|
||||
|
||||
const space = (
|
||||
containerTag: string,
|
||||
|
|
@ -27,7 +27,7 @@ const space = (
|
|||
|
||||
describe("space presentation", () => {
|
||||
it("keeps the active space first, then sorts by activity", () => {
|
||||
const sorted = sortWorkspaces(
|
||||
const sorted = sortSpaces(
|
||||
[
|
||||
space("older", "2026-01-01T00:00:00.000Z"),
|
||||
space("active", "2025-01-01T00:00:00.000Z"),
|
||||
|
|
@ -44,7 +44,7 @@ describe("space presentation", () => {
|
|||
})
|
||||
|
||||
it("formats compact rows without internal database IDs", () => {
|
||||
const row = formatWorkspaceRow(
|
||||
const row = formatSpaceRow(
|
||||
space("project-key", "2026-07-29T19:44:28.177Z"),
|
||||
"project-key",
|
||||
)
|
||||
|
|
@ -5,11 +5,11 @@ const DEFAULT_DESCRIPTION_LIMIT = 160
|
|||
const plural = (count: number, singular: string, pluralForm: string) =>
|
||||
`${count} ${count === 1 ? singular : pluralForm}`
|
||||
|
||||
export function workspaceDisplayName(
|
||||
workspace: ContainerTag | undefined,
|
||||
export function spaceDisplayName(
|
||||
space: ContainerTag | undefined,
|
||||
key: string,
|
||||
): string {
|
||||
return workspace?.name || key
|
||||
return space?.name || key
|
||||
}
|
||||
|
||||
export function compactDescription(
|
||||
|
|
@ -39,43 +39,40 @@ export function formatActivityDate(value: string | null): string | undefined {
|
|||
}).format(date)
|
||||
}
|
||||
|
||||
export function workspaceMetadata(workspace: ContainerTag): string {
|
||||
const lastActivity = formatActivityDate(workspace.lastActivityAt)
|
||||
export function spaceMetadata(space: ContainerTag): string {
|
||||
const lastActivity = formatActivityDate(space.lastActivityAt)
|
||||
const fields = [
|
||||
workspace.visibility
|
||||
? `${workspace.visibility.charAt(0).toUpperCase()}${workspace.visibility.slice(1)}`
|
||||
space.visibility
|
||||
? `${space.visibility.charAt(0).toUpperCase()}${space.visibility.slice(1)}`
|
||||
: undefined,
|
||||
plural(workspace.documentCount, "document", "documents"),
|
||||
plural(workspace.memoryCount, "memory", "memories"),
|
||||
plural(space.documentCount, "document", "documents"),
|
||||
plural(space.memoryCount, "memory", "memories"),
|
||||
lastActivity ? `Last active ${lastActivity}` : undefined,
|
||||
]
|
||||
|
||||
return fields.filter(Boolean).join(" · ")
|
||||
}
|
||||
|
||||
export function formatWorkspaceRow(
|
||||
workspace: ContainerTag,
|
||||
export function formatSpaceRow(
|
||||
space: ContainerTag,
|
||||
activeKey: string,
|
||||
descriptionLimit = DEFAULT_DESCRIPTION_LIMIT,
|
||||
): string {
|
||||
const active = workspace.containerTag === activeKey ? " · Active" : ""
|
||||
const metadata = workspaceMetadata(workspace)
|
||||
const description = compactDescription(
|
||||
workspace.description,
|
||||
descriptionLimit,
|
||||
)
|
||||
const active = space.containerTag === activeKey ? " · Active" : ""
|
||||
const metadata = spaceMetadata(space)
|
||||
const description = compactDescription(space.description, descriptionLimit)
|
||||
const firstLine =
|
||||
`- ${workspaceDisplayName(workspace, workspace.containerTag)} ` +
|
||||
`[${workspace.containerTag}]${active}${metadata ? ` · ${metadata}` : ""}`
|
||||
`- ${spaceDisplayName(space, space.containerTag)} ` +
|
||||
`[${space.containerTag}]${active}${metadata ? ` · ${metadata}` : ""}`
|
||||
|
||||
return description ? `${firstLine}\n ${description}` : firstLine
|
||||
}
|
||||
|
||||
export function sortWorkspaces(
|
||||
workspaces: ContainerTag[],
|
||||
export function sortSpaces(
|
||||
spaces: ContainerTag[],
|
||||
activeKey: string,
|
||||
): ContainerTag[] {
|
||||
return [...workspaces].sort((left, right) => {
|
||||
return [...spaces].sort((left, right) => {
|
||||
if (left.containerTag === activeKey) return -1
|
||||
if (right.containerTag === activeKey) return 1
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ import { containerTagSchema } from "./container-tag"
|
|||
|
||||
const ACTIVE_CONTAINER_TAG_KEY = "activeContainerTag"
|
||||
|
||||
export class WorkspaceState extends DurableObject {
|
||||
export class SpaceState extends DurableObject {
|
||||
async getActiveContainerTag(): Promise<string | undefined> {
|
||||
return this.ctx.storage.get<string>(ACTIVE_CONTAINER_TAG_KEY)
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { describe, expect, it, vi } from "vitest"
|
||||
import { optionalContainerTagSchema } from "./container-tag"
|
||||
import { resolveContainerTag, workspaceStateName } from "./workspace"
|
||||
import { resolveContainerTag, spaceStateName } from "./space"
|
||||
|
||||
describe("workspace application state", () => {
|
||||
describe("space application state", () => {
|
||||
it("keys active state by organization and user without collisions", () => {
|
||||
expect(
|
||||
workspaceStateName({
|
||||
spaceStateName({
|
||||
organizationId: "org:one",
|
||||
userId: "user:two",
|
||||
}),
|
||||
).not.toBe(
|
||||
workspaceStateName({
|
||||
spaceStateName({
|
||||
organizationId: "org",
|
||||
userId: "one:user:two",
|
||||
}),
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import type { ActorContext } from "./types"
|
||||
|
||||
export function workspaceStateName(
|
||||
export function spaceStateName(
|
||||
actor: Pick<ActorContext, "organizationId" | "userId">,
|
||||
): string {
|
||||
return `workspace:${JSON.stringify([actor.organizationId, actor.userId])}`
|
||||
return `space:${JSON.stringify([actor.organizationId, actor.userId])}`
|
||||
}
|
||||
|
||||
export async function resolveContainerTag(
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { WorkspaceState } from "./workspace-state"
|
||||
import type { SpaceState } from "./space-state"
|
||||
|
||||
export interface ActorContext {
|
||||
userId: string
|
||||
|
|
@ -8,7 +8,7 @@ export interface ActorContext {
|
|||
}
|
||||
|
||||
export interface ServerEnv {
|
||||
WORKSPACE_STATE: DurableObjectNamespace<WorkspaceState>
|
||||
SPACE_STATE: DurableObjectNamespace<SpaceState>
|
||||
API_URL?: string
|
||||
MCP_RESOURCE?: string
|
||||
ALLOWED_MCP_ORIGIN_HOSTNAMES?: string
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@ interface Props {
|
|||
onClick: (containerTag: string) => void
|
||||
}
|
||||
|
||||
export function WorkspaceCard({
|
||||
containerTag,
|
||||
active,
|
||||
access,
|
||||
onClick,
|
||||
}: Props) {
|
||||
export function SpaceCard({ containerTag, active, access, onClick }: Props) {
|
||||
const name = containerTag.name || formatTagLabel(containerTag.containerTag)
|
||||
const docs = containerTag.documentCount
|
||||
const mems = containerTag.memoryCount
|
||||
|
|
@ -26,26 +21,26 @@ export function WorkspaceCard({
|
|||
|
||||
return (
|
||||
<button
|
||||
className="workspace-card"
|
||||
className="space-card"
|
||||
data-active={active}
|
||||
onClick={() => onClick(containerTag.containerTag)}
|
||||
type="button"
|
||||
>
|
||||
<span className="workspace-card-inner">
|
||||
<span className="workspace-card-main">
|
||||
<span className="space-card-inner">
|
||||
<span className="space-card-main">
|
||||
<span
|
||||
className={cn(
|
||||
"workspace-card-title truncate text-sm font-medium",
|
||||
"space-card-title truncate text-sm font-medium",
|
||||
active ? "text-accent" : "text-text-primary",
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
<span className="workspace-card-meta text-[11px] leading-normal text-text-muted">
|
||||
<span className="space-card-meta text-[11px] leading-normal text-text-muted">
|
||||
{meta}
|
||||
</span>
|
||||
</span>
|
||||
<span className="workspace-card-trailing">
|
||||
<span className="space-card-trailing">
|
||||
{access ? <PermissionBadge permission={access.permission} /> : null}
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import { formatTagLabel } from "../lib/formatTag"
|
||||
|
||||
// A calm workspace pill: a small accent dot + the workspace name on a neutral
|
||||
// surface. Reads like a "space" tag rather than a blue-on-blue system badge.
|
||||
export function WorkspaceChip({ containerTag }: { containerTag: string }) {
|
||||
// A calm space pill: a small accent dot + the space name on a neutral surface.
|
||||
export function SpaceChip({ containerTag }: { containerTag: string }) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-bg-muted px-2.5 py-1 text-(length:--text-xs) font-medium text-text-primary">
|
||||
<span aria-hidden className="size-1.5 shrink-0 rounded-full bg-accent" />
|
||||
|
|
@ -183,8 +183,8 @@
|
|||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ── Workspace picker — a framed, scrollable list of rows ─────────────────── */
|
||||
.workspace-picker-grid {
|
||||
/* ── Space picker — a framed, scrollable list of rows ─────────────────────── */
|
||||
.space-picker-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 320px;
|
||||
|
|
@ -198,11 +198,11 @@
|
|||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.workspace-picker-grid::-webkit-scrollbar {
|
||||
.space-picker-grid::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.workspace-card {
|
||||
.space-card {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
|
@ -214,34 +214,34 @@
|
|||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.workspace-card:not(:last-child) {
|
||||
.space-card:not(:last-child) {
|
||||
border-bottom: 1px solid var(--border-muted);
|
||||
}
|
||||
|
||||
.workspace-card:hover {
|
||||
.space-card:hover {
|
||||
background: var(--card-bg-hover);
|
||||
}
|
||||
|
||||
.workspace-card:focus-visible {
|
||||
.space-card:focus-visible {
|
||||
outline: none;
|
||||
background: var(--card-bg-hover);
|
||||
box-shadow: inset 0 0 0 2px var(--accent-ring);
|
||||
}
|
||||
|
||||
/* Active workspace is marked by an accent-tinted row + accent-colored name
|
||||
* (see WorkspaceCard) — no rail, no check, so the trailing badge stays aligned. */
|
||||
.workspace-card[data-active="true"] {
|
||||
/* Active space is marked by an accent-tinted row + accent-colored name
|
||||
* (see SpaceCard) — no rail, no check, so the trailing badge stays aligned. */
|
||||
.space-card[data-active="true"] {
|
||||
background: var(--card-active-bg);
|
||||
}
|
||||
|
||||
.workspace-card-inner {
|
||||
.space-card-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-card-main {
|
||||
.space-card-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
|
@ -249,11 +249,11 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-card-title {
|
||||
.space-card-title {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workspace-card-trailing {
|
||||
.space-card-trailing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ import { Button } from "./Button"
|
|||
import { Popover, PopoverContent, PopoverTrigger } from "./Popover"
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip"
|
||||
|
||||
export interface WorkspaceOption {
|
||||
export interface SpaceOption {
|
||||
value: string
|
||||
label: string
|
||||
description?: string
|
||||
icon?: ReactNode
|
||||
}
|
||||
|
||||
interface WorkspaceSelectProps {
|
||||
interface SpaceSelectProps {
|
||||
value: string | null
|
||||
onValueChange: (value: string) => void
|
||||
options: WorkspaceOption[]
|
||||
options: SpaceOption[]
|
||||
placeholder?: string
|
||||
emptyText?: string
|
||||
searchPlaceholder?: string
|
||||
|
|
@ -25,7 +25,7 @@ interface WorkspaceSelectProps {
|
|||
|
||||
// Ports console-v2's ChipSelect/ContainerTagSelect: a Popover-anchored,
|
||||
// searchable list. Scales to hundreds of spaces where chips can't.
|
||||
export function WorkspaceSelect({
|
||||
export function SpaceSelect({
|
||||
value,
|
||||
onValueChange,
|
||||
options,
|
||||
|
|
@ -34,7 +34,7 @@ export function WorkspaceSelect({
|
|||
searchPlaceholder = "Search spaces…",
|
||||
disabled = false,
|
||||
className,
|
||||
}: WorkspaceSelectProps) {
|
||||
}: SpaceSelectProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [query, setQuery] = useState("")
|
||||
const selected = value ? options.find((o) => o.value === value) : null
|
||||
|
|
@ -63,7 +63,7 @@ export function WorkspaceSelect({
|
|||
<Button
|
||||
brandFont={false}
|
||||
className={cn(
|
||||
"workspace-select-trigger w-full justify-between normal-case tracking-normal font-medium",
|
||||
"space-select-trigger w-full justify-between normal-case tracking-normal font-medium",
|
||||
"shadow-[var(--shadow-inset)]",
|
||||
"data-[state=open]:border-[var(--border-accent)]",
|
||||
className,
|
||||
|
|
@ -21,4 +21,4 @@ export {
|
|||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "./Tooltip"
|
||||
export { type WorkspaceOption, WorkspaceSelect } from "./WorkspaceSelect"
|
||||
export { type SpaceOption, SpaceSelect } from "./SpaceSelect"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type ReactNode, useEffect, useState } from "react"
|
||||
import { WidgetShell } from "../App"
|
||||
import { WorkspaceCard } from "../components/WorkspaceCard"
|
||||
import { SpaceCard } from "../components/SpaceCard"
|
||||
import {
|
||||
ActionGroup,
|
||||
Badge,
|
||||
|
|
@ -13,7 +13,7 @@ import {
|
|||
PageHeader,
|
||||
Stack,
|
||||
TextArea,
|
||||
WorkspaceSelect,
|
||||
SpaceSelect,
|
||||
} from "../design/ui"
|
||||
import { Confirmation } from "../views/Confirmation"
|
||||
import { ErrorView } from "../views/Error"
|
||||
|
|
@ -288,7 +288,7 @@ export function Studio() {
|
|||
<Section title="Composite components">
|
||||
<div className="flex flex-wrap gap-(--space-4)">
|
||||
<div className="w-64">
|
||||
<WorkspaceCard
|
||||
<SpaceCard
|
||||
access={mockAssignedTags[0]}
|
||||
active={false}
|
||||
containerTag={mockContainerTags[0]}
|
||||
|
|
@ -296,7 +296,7 @@ export function Studio() {
|
|||
/>
|
||||
</div>
|
||||
<div className="w-64">
|
||||
<WorkspaceCard
|
||||
<SpaceCard
|
||||
access={mockAssignedTags[1]}
|
||||
active
|
||||
containerTag={mockContainerTags[1]}
|
||||
|
|
@ -304,7 +304,7 @@ export function Studio() {
|
|||
/>
|
||||
</div>
|
||||
<div className="w-64">
|
||||
<WorkspaceCard
|
||||
<SpaceCard
|
||||
active={false}
|
||||
containerTag={mockContainerTags[3]}
|
||||
onClick={() => {}}
|
||||
|
|
@ -334,7 +334,7 @@ export function Studio() {
|
|||
hint="Searchable Popover — scales to hundreds of spaces."
|
||||
label="Space select"
|
||||
>
|
||||
<WorkspaceSelect
|
||||
<SpaceSelect
|
||||
onValueChange={setSelectValue}
|
||||
options={mockContainerTags.map((t) => ({
|
||||
value: t.containerTag,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { WorkspaceChip } from "../components/WorkspaceChip"
|
||||
import { SpaceChip } from "../components/SpaceChip"
|
||||
import { Stack } from "../design/ui"
|
||||
import { Check } from "../lib/icons"
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export function Confirmation({ containerTag }: Props) {
|
|||
<div className="text-(length:--text-sm) font-semibold text-text-primary">
|
||||
Active space set
|
||||
</div>
|
||||
<WorkspaceChip containerTag={containerTag} />
|
||||
<SpaceChip containerTag={containerTag} />
|
||||
</Stack>
|
||||
<p className="max-w-xs text-(length:--text-xs) leading-relaxed text-text-muted">
|
||||
Saves and recalls will use this space until you change it.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type {
|
|||
ContainerTagAccess,
|
||||
ViewMessage,
|
||||
} from "../../shared/types"
|
||||
import { WorkspaceCard } from "../components/WorkspaceCard"
|
||||
import { SpaceCard } from "../components/SpaceCard"
|
||||
import { Input, PageHeader } from "../design/ui"
|
||||
import { useApp } from "../hooks/useApp"
|
||||
import { useLog } from "../hooks/useLog"
|
||||
|
|
@ -120,19 +120,19 @@ export function Picker({
|
|||
) : null}
|
||||
|
||||
{filtered.length === 0 ? (
|
||||
<div className="workspace-picker-grid items-center justify-center py-(--space-8)">
|
||||
<div className="space-picker-grid items-center justify-center py-(--space-8)">
|
||||
<p className="px-(--space-4) text-center text-(length:--text-sm) text-text-muted">
|
||||
No spaces match “{query}”.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="workspace-picker-grid">
|
||||
<div className="space-picker-grid">
|
||||
{filtered.map((tag) => {
|
||||
const access = assignedTags?.find(
|
||||
(t) => t.containerTag === tag.containerTag,
|
||||
)
|
||||
return (
|
||||
<WorkspaceCard
|
||||
<SpaceCard
|
||||
access={access}
|
||||
active={activeTag === tag.containerTag}
|
||||
containerTag={tag}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
PageHeader,
|
||||
Stack,
|
||||
TextArea,
|
||||
WorkspaceSelect,
|
||||
SpaceSelect,
|
||||
} from "../design/ui"
|
||||
import { useApp } from "../hooks/useApp"
|
||||
import { useLog } from "../hooks/useLog"
|
||||
|
|
@ -121,7 +121,7 @@ export function Save({
|
|||
|
||||
{writableTags.length > 0 ? (
|
||||
<Field label="Space">
|
||||
<WorkspaceSelect
|
||||
<SpaceSelect
|
||||
onValueChange={setSelectedTag}
|
||||
options={options}
|
||||
value={selectedTag}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { WorkspaceChip } from "../components/WorkspaceChip"
|
||||
import { SpaceChip } from "../components/SpaceChip"
|
||||
import { Stack } from "../design/ui"
|
||||
import { Check } from "../lib/icons"
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ export function Success(props: Props) {
|
|||
<div className="text-(length:--text-sm) font-semibold text-text-primary">
|
||||
{isUpload ? `Uploaded ${props.fileName}` : "Memory saved"}
|
||||
</div>
|
||||
<WorkspaceChip containerTag={props.containerTag} />
|
||||
<SpaceChip containerTag={props.containerTag} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
FileUpload,
|
||||
PageHeader,
|
||||
Stack,
|
||||
WorkspaceSelect,
|
||||
SpaceSelect,
|
||||
} from "../design/ui"
|
||||
import { useApp } from "../hooks/useApp"
|
||||
import { useLog } from "../hooks/useLog"
|
||||
|
|
@ -151,7 +151,7 @@ export function Upload({
|
|||
|
||||
{writableTags.length > 0 ? (
|
||||
<Field label="Space">
|
||||
<WorkspaceSelect
|
||||
<SpaceSelect
|
||||
onValueChange={setSelectedTag}
|
||||
options={options}
|
||||
value={selectedTag}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@
|
|||
"class_name": "SupermemoryMCP"
|
||||
},
|
||||
{
|
||||
"name": "WORKSPACE_STATE",
|
||||
"class_name": "WorkspaceState"
|
||||
"name": "SPACE_STATE",
|
||||
"class_name": "SpaceState"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
},
|
||||
{
|
||||
"tag": "v2",
|
||||
"new_sqlite_classes": ["WorkspaceState"]
|
||||
"new_sqlite_classes": ["SpaceState"]
|
||||
}
|
||||
],
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue