@pgege/kaboo-react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context/KabooProvider.tsx","../src/components/ActivityPanel.tsx","../src/components/GlassTabs.tsx","../src/components/DrillDetailView.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { CopilotKit, type CopilotKitProps } from \"@copilotkit/react-core/v2\";\nimport { KabooActivityProvider, type StructuredRenderers } from \"./ActivityProvider\";\nimport { DrillProvider } from \"./DrillContext\";\nimport { InterruptBridgeProvider } from \"./InterruptBridge\";\nimport { KabooInterruptHandler, type KabooInterruptHandlerProps } from \"../integrations/KabooInterruptHandler\";\nimport { KabooInlineCards } from \"../integrations/KabooInlineCards\";\n\n/** Props for {@link KabooProvider}. */\nexport interface KabooProviderProps {\n /** URL of the CopilotKit runtime endpoint (e.g. `/api/copilotkit`). */\n runtimeUrl: string;\n /** CopilotKit agent id to run (the workflow entry agent). */\n agent: string;\n /** Conversation id. Scopes both the run and its activity. */\n threadId?: string;\n /** Renderers for structured agent outputs, keyed by output schema name. */\n structuredRenderers?: StructuredRenderers;\n /** Per-interrupt-type renderer overrides for the built-in HITL handler. */\n interruptRenderers?: KabooInterruptHandlerProps[\"renderers\"];\n /** Skip auto-mounting the built-in {@link KabooInterruptHandler}. */\n disableInterruptHandler?: boolean;\n /** Skip auto-mounting the built-in {@link KabooInlineCards}. */\n disableInlineCards?: boolean;\n /** Extra props forwarded to the underlying `<CopilotKit>`. */\n copilotKitProps?: Partial<Omit<CopilotKitProps, \"children\" | \"runtimeUrl\" | \"agent\" | \"threadId\">>;\n /** Your app subtree, rendered inside all kaboo contexts. */\n children: ReactNode;\n}\n\n/**\n * Batteries-included CopilotKit plugin. Renders `<CopilotKit>` and nests every\n * kaboo context (activity -> drill -> interrupt bridge) plus the built-in HITL\n * and inline-card handlers, so integrators drop this once near the root and use\n * only kaboo-react. Activity rides the AG-UI run stream, so there is no separate\n * activity endpoint to configure.\n *\n * @example\n * ```tsx\n * import { CopilotChat } from \"@copilotkit/react-core/v2\";\n * import { KabooProvider, GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n * import { KabooMessageView } from \"@pgege/kaboo-react/copilotkit\";\n * import \"@pgege/kaboo-react/styles.css\";\n *\n * function App({ agent, threadId }: { agent: string; threadId: string }) {\n * return (\n * <KabooProvider runtimeUrl=\"/api/copilotkit\" agent={agent} threadId={threadId}>\n * <GlassTabs />\n * <CopilotChat messageView={KabooMessageView} />\n * <DrillDetailView />\n * </KabooProvider>\n * );\n * }\n * ```\n */\nexport function KabooProvider({\n runtimeUrl,\n agent,\n threadId,\n structuredRenderers,\n interruptRenderers,\n disableInterruptHandler = false,\n disableInlineCards = false,\n copilotKitProps,\n children,\n}: KabooProviderProps) {\n return (\n <CopilotKit\n runtimeUrl={runtimeUrl}\n agent={agent}\n threadId={threadId}\n useSingleEndpoint={false}\n {...copilotKitProps}\n >\n <KabooActivityProvider agentId={agent} structuredRenderers={structuredRenderers}>\n <DrillProvider>\n <InterruptBridgeProvider>\n {!disableInterruptHandler && (\n <KabooInterruptHandler agentId={agent} renderers={interruptRenderers} />\n )}\n {!disableInlineCards && <KabooInlineCards />}\n {children}\n </InterruptBridgeProvider>\n </DrillProvider>\n </KabooActivityProvider>\n </CopilotKit>\n );\n}\n","import { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\nimport { topLevelGroups, directChildren } from \"../utils/groups\";\nimport { AgentCard } from \"./AgentCard\";\n\n/**\n * Standalone panel that renders the current activity tree as a list of\n * {@link AgentCard}s — top-level groups at the root, or the drilled-in group's\n * children. Use it when you want the hierarchical activity view outside the chat\n * transcript (the in-chat view is handled by `KabooMessageView`). Renders\n * nothing when there is no activity.\n *\n * @example\n * ```tsx\n * import { ActivityPanel } from \"@pgege/kaboo-react\";\n *\n * function Sidebar() {\n * return (\n * <aside>\n * <ActivityPanel />\n * </aside>\n * );\n * }\n * ```\n */\nexport function ActivityPanel() {\n const { groups } = useActivity();\n const { activeDrill } = useDrill();\n\n if (Object.keys(groups).length === 0) return null;\n\n // A plain-agent entry group (`inlineChatOwner`) is rendered inline by the host\n // chat, so it must not also appear as a root card here — it only enriches\n // those inline tool rows. Nested/sub-agent drilling is unaffected.\n const filtered = activeDrill\n ? directChildren(groups, activeDrill)\n : topLevelGroups(groups).filter(([, g]) => !g.inlineChatOwner);\n\n if (filtered.length === 0 && activeDrill) {\n const exact = groups[activeDrill];\n if (exact) {\n return (\n <div className=\"kaboo-activity-panel\">\n <AgentCard groupId={activeDrill} group={exact} />\n </div>\n );\n }\n }\n\n return (\n <div className=\"kaboo-activity-panel\">\n {filtered.map(([id, group]) => (\n <AgentCard key={id} groupId={id} group={group} />\n ))}\n </div>\n );\n}\n","import { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\n\n/**\n * Breadcrumb navigation for the drill-down path: a \"Chat\" root followed by one\n * crumb per drilled-in group. Clicking a crumb jumps to that level; the current\n * (last) crumb is disabled. Renders nothing at the root. Pair with\n * {@link DrillDetailView}.\n *\n * @example\n * ```tsx\n * import { GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n *\n * function DrillArea() {\n * return (\n * <>\n * <GlassTabs />\n * <DrillDetailView />\n * </>\n * );\n * }\n * ```\n */\nexport function GlassTabs() {\n const { drillPath, drillToRoot, drillToLevel } = useDrill();\n const { groups } = useActivity();\n\n if (drillPath.length === 0) return null;\n\n const tabs: { id: string; label: string; level: number }[] = [\n { id: \"root\", label: \"Chat\", level: -1 },\n ...drillPath.map((id, i) => ({\n id,\n label: groups[id]?.title || id,\n level: i,\n })),\n ];\n\n return (\n <nav className=\"kaboo-breadcrumb-bar\">\n {tabs.map((tab, i) => {\n const isLast = i === tabs.length - 1;\n return (\n <span key={tab.id} className=\"kaboo-breadcrumb-item\">\n {i > 0 && (\n <svg width={12} height={12} viewBox=\"0 0 16 16\" className=\"kaboo-breadcrumb-sep\">\n <path d=\"M6 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )}\n <button\n className={`kaboo-breadcrumb-link ${isLast ? \"kaboo-breadcrumb-current\" : \"\"}`}\n onClick={() => {\n if (isLast) return;\n if (tab.level === -1) drillToRoot();\n else drillToLevel(tab.level);\n }}\n disabled={isLast}\n >\n {i === 0 && (\n <svg width={14} height={14} viewBox=\"0 0 16 16\" className=\"kaboo-breadcrumb-icon\">\n <path d=\"M3 8.5L8 4l5 4.5M4.5 7.5V12h2.5V9.5h2V12h2.5V7.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n {tab.label}\n </button>\n </span>\n );\n })}\n </nav>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\nimport { useInterruptBridge } from \"../context/InterruptBridge\";\nimport {\n directChildren,\n partitionChildrenByToolCall,\n pendingToolAnchorExists,\n} from \"../utils/groups\";\nimport { AgentCard } from \"./AgentCard\";\nimport { Timeline } from \"./Timeline\";\nimport { InterruptRenderer } from \"./InterruptRenderer\";\n\n/**\n * Detail pane for the currently drilled-in group: its task, full timeline\n * (with delegated sub-agents interleaved at their tool-call position), any\n * unanchored interrupt prompts, and a \"Sub-agents\" section for swarm/graph\n * members. Renders a hidden placeholder at the root. Pair with {@link GlassTabs}.\n *\n * @example\n * ```tsx\n * import { GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n *\n * function DrillArea() {\n * return (\n * <>\n * <GlassTabs />\n * <DrillDetailView />\n * </>\n * );\n * }\n * ```\n */\nexport function DrillDetailView() {\n const { activeDrill } = useDrill();\n const { groups } = useActivity();\n const { active: activeInterrupts } = useInterruptBridge();\n const bodyRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (activeDrill && bodyRef.current) {\n bodyRef.current.scrollTop = 0;\n }\n }, [activeDrill]);\n\n if (!activeDrill) {\n return <div className=\"kaboo-drill-detail kaboo-hidden\" />;\n }\n\n const group = groups[activeDrill];\n const timeline = group?.timeline ?? [];\n // Prompts anchored to a pending tool call are rendered inline by the Timeline\n // at that tool's position (chronological); this block only handles interrupts\n // with no on-screen tool anchor — never a duplicate.\n const unanchoredInterrupts =\n group?.status === \"interrupted\"\n ? activeInterrupts.filter(\n (i) => !pendingToolAnchorExists(groups, i.toolCallId),\n )\n : [];\n\n const childEntries = directChildren(groups, activeDrill);\n // Interleave delegated children at their delegating tool-call position so a\n // sub-agent's card sits where it was delegated (before the parent's summary\n // text) rather than in a trailing block. Children with no tool-call anchor\n // (swarm/graph members) still render in the Sub-agents section below.\n const { byToolCall: childByToolCall, leftover: leftoverChildren } =\n partitionChildrenByToolCall(childEntries, timeline);\n const renderToolCard = (toolUseId: string) => {\n const entry = childByToolCall.get(toolUseId);\n if (!entry) return null;\n return <AgentCard groupId={entry[0]} group={entry[1]} />;\n };\n\n return (\n <div ref={bodyRef} className=\"kaboo-drill-detail\">\n <div className=\"kaboo-drill-detail-inner\">\n {group?.task && (\n <div className=\"kaboo-agent-card-task kaboo-drill-task\">\n <strong>Task:</strong> {group.task}\n </div>\n )}\n\n {group && timeline.length > 0 && (\n <div className=\"kaboo-drill-timeline\">\n <Timeline\n timeline={timeline}\n active={group.status === \"active\"}\n variant=\"drill\"\n renderToolCard={renderToolCard}\n />\n </div>\n )}\n\n {unanchoredInterrupts.length > 0 && (\n <div className=\"kaboo-drill-interrupt\">\n {unanchoredInterrupts.map((it) => (\n <InterruptRenderer\n key={it.id}\n reason={it.reason}\n toolCallId={it.toolCallId}\n onResolve={it.onResolve}\n onCancel={it.onCancel}\n />\n ))}\n </div>\n )}\n\n {leftoverChildren.length > 0 && (\n <div className=\"kaboo-drill-children\">\n <div className=\"kaboo-drill-section-label\">Sub-agents</div>\n {leftoverChildren.map(([id, childGroup]) => (\n <AgentCard key={id} groupId={id} group={childGroup} />\n ))}\n </div>\n )}\n\n {group && timeline.length === 0 && childEntries.length === 0 && (\n <div className=\"kaboo-agent-card-empty\">\n Working...\n </div>\n )}\n\n {!group && (\n <div className=\"kaboo-agent-card-empty\">\n No activity data for this group.\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/context/KabooProvider.tsx","../src/references/ReferenceInput.tsx","../src/references/uploadProvider.tsx","../src/components/ActivityPanel.tsx","../src/components/GlassTabs.tsx","../src/components/DrillDetailView.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { CopilotKit, type CopilotKitProps } from \"@copilotkit/react-core/v2\";\nimport { KabooActivityProvider, type StructuredRenderers } from \"./ActivityProvider\";\nimport { DrillProvider } from \"./DrillContext\";\nimport { InterruptBridgeProvider } from \"./InterruptBridge\";\nimport { KabooInterruptHandler, type KabooInterruptHandlerProps } from \"../integrations/KabooInterruptHandler\";\nimport { KabooInlineCards } from \"../integrations/KabooInlineCards\";\nimport { ReferencesProvider } from \"../references/ReferencesProvider\";\nimport type { ReferenceProvider } from \"../references/types\";\n\n/** Props for {@link KabooProvider}. */\nexport interface KabooProviderProps {\n /** URL of the CopilotKit runtime endpoint (e.g. `/api/copilotkit`). */\n runtimeUrl: string;\n /** CopilotKit agent id to run (the workflow entry agent). */\n agent: string;\n /** Conversation id. Scopes both the run and its activity. */\n threadId?: string;\n /** Renderers for structured agent outputs, keyed by output schema name. */\n structuredRenderers?: StructuredRenderers;\n /** Per-interrupt-type renderer overrides for the built-in HITL handler. */\n interruptRenderers?: KabooInterruptHandlerProps[\"renderers\"];\n /** Skip auto-mounting the built-in {@link KabooInterruptHandler}. */\n disableInterruptHandler?: boolean;\n /** Skip auto-mounting the built-in {@link KabooInlineCards}. */\n disableInlineCards?: boolean;\n /**\n * `@` reference providers made available to the composer / `useReferences`.\n * File upload is not implicit — include `uploadProvider()` to offer uploads.\n */\n references?: ReferenceProvider[];\n /** Extra props forwarded to the underlying `<CopilotKit>`. */\n copilotKitProps?: Partial<Omit<CopilotKitProps, \"children\" | \"runtimeUrl\" | \"agent\" | \"threadId\">>;\n /** Your app subtree, rendered inside all kaboo contexts. */\n children: ReactNode;\n}\n\n/**\n * Batteries-included CopilotKit plugin. Renders `<CopilotKit>` and nests every\n * kaboo context (activity -> drill -> interrupt bridge) plus the built-in HITL\n * and inline-card handlers, so integrators drop this once near the root and use\n * only kaboo-react. Activity rides the AG-UI run stream, so there is no separate\n * activity endpoint to configure.\n *\n * @example\n * ```tsx\n * import { CopilotChat } from \"@copilotkit/react-core/v2\";\n * import { KabooProvider, GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n * import { KabooMessageView } from \"@pgege/kaboo-react/copilotkit\";\n * import \"@pgege/kaboo-react/styles.css\";\n *\n * function App({ agent, threadId }: { agent: string; threadId: string }) {\n * return (\n * <KabooProvider runtimeUrl=\"/api/copilotkit\" agent={agent} threadId={threadId}>\n * <GlassTabs />\n * <CopilotChat messageView={KabooMessageView} />\n * <DrillDetailView />\n * </KabooProvider>\n * );\n * }\n * ```\n */\nexport function KabooProvider({\n runtimeUrl,\n agent,\n threadId,\n structuredRenderers,\n interruptRenderers,\n disableInterruptHandler = false,\n disableInlineCards = false,\n references,\n copilotKitProps,\n children,\n}: KabooProviderProps) {\n return (\n <CopilotKit\n runtimeUrl={runtimeUrl}\n agent={agent}\n threadId={threadId}\n useSingleEndpoint={false}\n {...copilotKitProps}\n >\n <KabooActivityProvider agentId={agent} structuredRenderers={structuredRenderers}>\n <DrillProvider>\n <InterruptBridgeProvider>\n <ReferencesProvider providers={references} syncObjectStateTo={agent}>\n {!disableInterruptHandler && (\n <KabooInterruptHandler agentId={agent} renderers={interruptRenderers} />\n )}\n {!disableInlineCards && <KabooInlineCards />}\n {children}\n </ReferencesProvider>\n </InterruptBridgeProvider>\n </DrillProvider>\n </KabooActivityProvider>\n </CopilotKit>\n );\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type ComponentProps,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { CopilotChatInput, useAgent, useCopilotChatConfiguration } from \"@copilotkit/react-core/v2\";\nimport { useReferences } from \"./ReferencesProvider\";\nimport { isUploadProvider, uploadFileToReference, UPLOAD_MARKER } from \"./uploadProvider\";\nimport { buildUserContent, serializeReferences } from \"./serialize\";\nimport type { PendingReference, ReferenceItem, ReferenceProvider } from \"./types\";\n\ntype CopilotInputProps = ComponentProps<typeof CopilotChatInput>;\ntype TextAreaSlotProps = ComponentProps<typeof CopilotChatInput.TextArea>;\n\n/**\n * Props for {@link KabooReferenceInput}. This is a drop-in value for\n * `<CopilotChat input={…}>`, so it receives every prop CopilotKit hands the\n * input slot (value, onChange, onSubmitMessage, …) plus a couple of\n * kaboo-specific knobs.\n */\nexport interface KabooReferenceInputProps extends CopilotInputProps {\n /** Character that opens the reference popover from the editor. Default `\"@\"`. */\n trigger?: string;\n}\n\n// One row in the shared popover.\ntype Row =\n | { kind: \"action\"; group: string; provider: ReferenceProvider }\n | { kind: \"item\"; group: string; provider: ReferenceProvider; item: ReferenceItem };\n\nconst CHIP_CLASS = \"kaboo-chip\";\nconst NBSP = \"\\u00A0\";\n\nconst AT_SVG =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94\"/></svg>';\nconst FILE_SVG =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><path d=\"M14 2v6h6\"/></svg>';\nconst X_SVG =\n '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>';\n\nconst AttachIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n);\nconst AtIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94\" />\n </svg>\n);\nconst PlusIcon = (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M5 12h14M12 5v14\" />\n </svg>\n);\nconst SearchIcon = (\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n);\n\ninterface PopoverPosition {\n left: number;\n bottom: number;\n width: number;\n}\n\nconst POPOVER_WIDTH = 320;\nconst VIEWPORT_MARGIN = 8;\nconst ANCHOR_GAP = 6;\n\n/** Place the popover just above an anchor rect, left-aligned and clamped on-screen. */\nfunction computePosition(rect: DOMRect | null): PopoverPosition | null {\n if (!rect) return null;\n const width = Math.min(POPOVER_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2);\n const left = Math.max(\n VIEWPORT_MARGIN,\n Math.min(rect.left, window.innerWidth - width - VIEWPORT_MARGIN),\n );\n return { left, bottom: window.innerHeight - rect.top + ANCHOR_GAP, width };\n}\n\nfunction isImageMime(mime: string): boolean {\n return mime.startsWith(\"image/\");\n}\n\nfunction previewSrc(ref: Extract<PendingReference, { transport: \"attachment\" }>): string {\n return \"url\" in ref.source ? ref.source.url : `data:${ref.mimeType};base64,${ref.source.data}`;\n}\n\n/** Serialize the editor DOM to the plain string CopilotKit sends: text with\n * object chips as `@name` and file chips as their filename. */\nfunction serializeEditor(root: HTMLElement): string {\n let out = \"\";\n const walk = (node: ChildNode) => {\n if (node.nodeType === Node.TEXT_NODE) {\n out += node.textContent ?? \"\";\n return;\n }\n if (node instanceof HTMLElement) {\n if (node.classList.contains(CHIP_CLASS)) {\n const name = node.dataset.name ?? \"\";\n out += node.dataset.transport === \"object\" ? `@${name}` : name;\n return;\n }\n if (node.tagName === \"BR\") {\n out += \"\\n\";\n return;\n }\n if (node.tagName === \"DIV\" && out.length > 0 && !out.endsWith(\"\\n\")) out += \"\\n\";\n node.childNodes.forEach(walk);\n }\n };\n root.childNodes.forEach(walk);\n return out.replace(new RegExp(NBSP, \"g\"), \" \");\n}\n\n/**\n * A `<CopilotChat input={…}>` slot that keeps CopilotKit's native input chrome\n * (send button, disclaimer, theme, layout) but replaces the plain textarea with\n * a lightweight rich editor. References — your objects (via `@`) and files (via\n * the `+` button or the \"attach a file\" row) — render as interactive inline\n * chips: click a chip to swap it for another, or its `×` to remove it. The `+`\n * and `@` triggers open the *same* searchable popover. Object references ride\n * `state.kaboo_references` (agent sees them inline as `@name`); files ride the\n * message as attachment parts (agent is made aware via the manifest). On submit\n * the editor builds the multimodal message and runs the agent.\n *\n * @example\n * ```tsx\n * <KabooProvider references={[uploadProvider({ onUpload }), tableProvider]} …>\n * <CopilotChat input={KabooReferenceInput} />\n * </KabooProvider>\n * ```\n */\nexport function KabooReferenceInput({ trigger = \"@\", ...inputProps }: KabooReferenceInputProps) {\n const {\n providers,\n pending,\n addReference,\n removeReference,\n clearReferences,\n recordMessageReferences,\n } = useReferences();\n const config = useCopilotChatConfiguration();\n const configAgentId = (config as { agentId?: string } | null)?.agentId;\n const { agent } = useAgent(configAgentId ? { agentId: configAgentId } : undefined);\n const placeholder =\n (config as { labels?: { chatInputPlaceholder?: string } } | null)?.labels\n ?.chatInputPlaceholder ?? \"Type a message...\";\n\n const wrapRef = useRef<HTMLDivElement>(null);\n const trayRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const editorRef = useRef<HTMLDivElement | null>(null);\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n const addBtnRef = useRef<HTMLButtonElement | null>(null);\n const searchRef = useRef<HTMLInputElement | null>(null);\n const nativeChangeRef = useRef<((e: ChangeEvent<HTMLTextAreaElement>) => void) | undefined>(undefined);\n const anchorRef = useRef<() => DOMRect | null>(() => null);\n // The `@…` token being edited (contentEditable text node + start offset).\n const tokenRef = useRef<{ node: Text; start: number } | null>(null);\n // An inline chip currently being replaced (click-to-swap), if any.\n const replacingRef = useRef<HTMLElement | null>(null);\n // A tray reference id currently being replaced (click-to-swap), if any.\n const replacingTrayIdRef = useRef<string | null>(null);\n // Ids of references rendered inline (via `@`); everything else lives in the\n // tray. `+` never inserts inline — that's what distinguishes the two.\n const inlineIdsRef = useRef<Set<string>>(new Set());\n // Where the next selection lands: inline (from `@`) or the tray (from `+`).\n const commitTargetRef = useRef<\"inline\" | \"tray\">(\"inline\");\n\n const [open, setOpen] = useState(false);\n const [rows, setRows] = useState<Row[]>([]);\n const [active, setActive] = useState(0);\n const [query, setQuery] = useState(\"\");\n const [mode, setMode] = useState<\"token\" | \"menu\">(\"token\");\n const [pos, setPos] = useState<PopoverPosition | null>(null);\n\n const placeholderRef = useRef(placeholder);\n placeholderRef.current = placeholder;\n\n // Toggle the placeholder (contentEditable has no native one) via a DOM\n // attribute, so the editor node never has to re-render/remount.\n const markEmpty = useCallback(() => {\n const root = editorRef.current;\n if (!root) return;\n const isEmpty =\n root.querySelector(`.${CHIP_CLASS}`) === null && (root.textContent ?? \"\").trim().length === 0;\n root.dataset.empty = isEmpty ? \"true\" : \"false\";\n }, []);\n\n const uploadProviderDef = useMemo(() => providers.find(isUploadProvider), [providers]);\n const searchable = useMemo(() => providers.filter((p) => typeof p.search === \"function\"), [providers]);\n const actionProviders = useMemo(\n () => providers.filter((p) => typeof p.search !== \"function\"),\n [providers],\n );\n\n const buildRows = useCallback(\n async (q: string): Promise<Row[]> => {\n const next: Row[] = [];\n for (const provider of actionProviders) {\n const label = isUploadProvider(provider) ? \"Attach\" : provider.label;\n if (!q || label.toLowerCase().includes(q.toLowerCase())) {\n next.push({ kind: \"action\", group: label, provider });\n }\n }\n for (const provider of searchable) {\n try {\n const items = await provider.search!(q);\n for (const item of items) next.push({ kind: \"item\", group: provider.label, provider, item });\n } catch {\n /* a provider's search failure shouldn't break the menu */\n }\n }\n return next;\n },\n [actionProviders, searchable],\n );\n\n const refresh = useCallback(\n async (q: string) => {\n const next = await buildRows(q);\n setRows(next);\n setActive(0);\n },\n [buildRows],\n );\n\n const close = useCallback(() => {\n setOpen(false);\n setRows([]);\n setQuery(\"\");\n tokenRef.current = null;\n replacingRef.current = null;\n replacingTrayIdRef.current = null;\n }, []);\n\n // Push the serialized editor text into CopilotKit's controlled value.\n const emit = useCallback(() => {\n const root = editorRef.current;\n if (!root) return;\n const text = serializeEditor(root);\n markEmpty();\n nativeChangeRef.current?.({ target: { value: text } } as ChangeEvent<HTMLTextAreaElement>);\n }, [markEmpty]);\n\n // Drop any *inline* reference whose chip the user deleted by editing. Tray\n // references (added via `+`) are never in the editor DOM, so leave them be.\n const reconcile = useCallback(() => {\n const root = editorRef.current;\n if (!root) return;\n const live = new Set(\n Array.from(root.querySelectorAll<HTMLElement>(`.${CHIP_CLASS}`)).map((c) => c.dataset.refId),\n );\n for (const id of Array.from(inlineIdsRef.current)) {\n if (!live.has(id)) {\n inlineIdsRef.current.delete(id);\n removeReference(id);\n }\n }\n }, [removeReference]);\n\n const focusEditor = useCallback(() => editorRef.current?.focus(), []);\n\n // Build a chip DOM node with icon/preview, label, and a remove button.\n const createChip = useCallback((ref: PendingReference): HTMLElement => {\n const chip = document.createElement(\"span\");\n chip.className = `${CHIP_CLASS} ${CHIP_CLASS}-${ref.transport}`;\n chip.contentEditable = \"false\";\n chip.dataset.refId = ref.id;\n chip.dataset.kind = ref.kind;\n chip.dataset.name = ref.name;\n chip.dataset.transport = ref.transport;\n chip.title = ref.name;\n\n if (ref.transport === \"attachment\" && isImageMime(ref.mimeType)) {\n const img = document.createElement(\"img\");\n img.className = \"kaboo-chip-thumb\";\n img.src = previewSrc(ref);\n img.alt = ref.name;\n chip.appendChild(img);\n } else {\n const ic = document.createElement(\"span\");\n ic.className = \"kaboo-chip-ic\";\n ic.innerHTML = ref.transport === \"object\" ? AT_SVG : FILE_SVG;\n chip.appendChild(ic);\n }\n\n const label = document.createElement(\"span\");\n label.className = \"kaboo-chip-label\";\n label.textContent = ref.name;\n chip.appendChild(label);\n\n const x = document.createElement(\"button\");\n x.type = \"button\";\n x.className = \"kaboo-chip-x\";\n x.setAttribute(\"aria-label\", `Remove ${ref.name}`);\n x.innerHTML = X_SVG;\n x.addEventListener(\"mousedown\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n handlersRef.current.removeChip(chip);\n });\n chip.appendChild(x);\n\n chip.addEventListener(\"mousedown\", (e) => {\n if (e.target === x || x.contains(e.target as Node)) return;\n e.preventDefault();\n handlersRef.current.replaceChip(chip);\n });\n return chip;\n }, []);\n\n // Insert a node at the current selection (falls back to end of the editor).\n const insertAtCaret = useCallback((node: Node) => {\n const root = editorRef.current;\n if (!root) return;\n const sel = window.getSelection();\n let range: Range;\n if (sel && sel.rangeCount > 0 && root.contains(sel.focusNode)) {\n range = sel.getRangeAt(0);\n } else {\n range = document.createRange();\n range.selectNodeContents(root);\n range.collapse(false);\n }\n range.collapse(false);\n const space = document.createTextNode(NBSP);\n range.insertNode(space);\n range.insertNode(node);\n const after = document.createRange();\n after.setStartAfter(space);\n after.collapse(true);\n sel?.removeAllRanges();\n sel?.addRange(after);\n }, []);\n\n // Place a chip: replacing an existing one, dropping an `@` token, or at caret.\n const placeChip = useCallback(\n (ref: PendingReference) => {\n const chip = createChip(ref);\n const replacing = replacingRef.current;\n if (replacing) {\n const oldId = replacing.dataset.refId;\n if (oldId && oldId !== ref.id) removeReference(oldId);\n replacing.replaceWith(chip);\n replacingRef.current = null;\n } else if (tokenRef.current) {\n const { node, start } = tokenRef.current;\n const sel = window.getSelection();\n const range = document.createRange();\n range.setStart(node, Math.min(start, node.textContent?.length ?? 0));\n if (sel && sel.focusNode && editorRef.current?.contains(sel.focusNode)) {\n range.setEnd(sel.focusNode, sel.focusOffset);\n } else {\n range.setEnd(node, node.textContent?.length ?? 0);\n }\n range.deleteContents();\n const space = document.createTextNode(NBSP);\n range.insertNode(space);\n range.insertNode(chip);\n const after = document.createRange();\n after.setStartAfter(space);\n after.collapse(true);\n sel?.removeAllRanges();\n sel?.addRange(after);\n tokenRef.current = null;\n } else {\n insertAtCaret(chip);\n }\n emit();\n },\n [createChip, insertAtCaret, removeReference, emit],\n );\n\n // Stage a chosen reference either inline (in the editor) or in the tray.\n const commit = useCallback(\n (ref: PendingReference, target: \"inline\" | \"tray\") => {\n addReference(ref);\n if (target === \"inline\") {\n inlineIdsRef.current.add(ref.id);\n placeChip(ref);\n } else {\n const oldId = replacingTrayIdRef.current;\n if (oldId && oldId !== ref.id) removeReference(oldId);\n replacingTrayIdRef.current = null;\n inlineIdsRef.current.delete(ref.id);\n }\n },\n [addReference, placeChip, removeReference],\n );\n\n // Open the file picker; the chosen file is uploaded then staged.\n const openFilePicker = useCallback(\n (provider: ReferenceProvider, target: \"inline\" | \"tray\") => {\n const input = fileInputRef.current;\n if (!input) return;\n const cfg = isUploadProvider(provider) ? provider[UPLOAD_MARKER] : {};\n input.accept = (cfg as { accept?: string }).accept ?? \"\";\n input.onchange = async () => {\n const file = input.files?.[0];\n input.value = \"\";\n if (!file) return;\n try {\n const ref = await uploadFileToReference(file, cfg as Parameters<typeof uploadFileToReference>[1]);\n commit(ref, target);\n } catch (err) {\n console.error(\"[kaboo] upload failed\", err);\n }\n };\n input.click();\n },\n [commit],\n );\n\n // Remove the \"@…\" token that opened the menu (before an async upload).\n const stripToken = useCallback(() => {\n const t = tokenRef.current;\n if (!t) return;\n const sel = window.getSelection();\n const range = document.createRange();\n range.setStart(t.node, Math.min(t.start, t.node.textContent?.length ?? 0));\n if (sel?.focusNode && editorRef.current?.contains(sel.focusNode)) {\n range.setEnd(sel.focusNode, sel.focusOffset);\n } else {\n range.setEnd(t.node, t.node.textContent?.length ?? 0);\n }\n range.deleteContents();\n emit();\n }, [emit]);\n\n const select = useCallback(\n async (row: Row) => {\n // Capture target before close() resets it.\n const target = commitTargetRef.current;\n if (row.kind === \"action\") {\n const provider = row.provider;\n if (target === \"inline\") stripToken();\n close();\n if (isUploadProvider(provider)) openFilePicker(provider, target);\n else await provider.onSelect?.({ id: provider.id, label: provider.label });\n focusEditor();\n return;\n }\n // Commit (consumes token/replacing) *before* close() clears them.\n await row.provider.onSelect?.(row.item);\n const ref = row.provider.toReference?.(row.item);\n if (ref) commit(ref, target);\n close();\n focusEditor();\n },\n [stripToken, close, openFilePicker, focusEditor, commit],\n );\n\n // Open the popover anchored at the `@` caret.\n const openFromToken = useCallback(\n (q: string) => {\n commitTargetRef.current = \"inline\";\n replacingRef.current = null;\n replacingTrayIdRef.current = null;\n anchorRef.current = () => {\n const t = tokenRef.current;\n return t ? caretRectFor(t.node, t.start) : null;\n };\n setMode(\"token\");\n setQuery(q);\n setPos(computePosition(anchorRef.current()));\n setOpen(true);\n void refresh(q);\n },\n [refresh],\n );\n\n // Toggle the popover from the `+` button (menu mode: has its own search box).\n const toggleFromButton = useCallback(() => {\n setOpen((prev) => {\n if (prev) {\n close();\n return false;\n }\n commitTargetRef.current = \"tray\";\n tokenRef.current = null;\n replacingRef.current = null;\n replacingTrayIdRef.current = null;\n anchorRef.current = () => addBtnRef.current?.getBoundingClientRect() ?? null;\n setMode(\"menu\");\n setQuery(\"\");\n setPos(computePosition(anchorRef.current()));\n void refresh(\"\");\n return true;\n });\n }, [refresh, close]);\n\n const onSearch = useCallback(\n (value: string) => {\n setQuery(value);\n void refresh(value);\n },\n [refresh],\n );\n\n const handleNavKey = useCallback(\n (e: ReactKeyboardEvent): boolean => {\n if (!open || rows.length === 0) return false;\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActive((a) => (a + 1) % rows.length);\n return true;\n }\n if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((a) => (a - 1 + rows.length) % rows.length);\n return true;\n }\n if (e.key === \"Enter\" || e.key === \"Tab\") {\n e.preventDefault();\n void select(rows[active]);\n return true;\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n close();\n focusEditor();\n return true;\n }\n return false;\n },\n [open, rows, active, select, close, focusEditor],\n );\n\n // Click an inline chip to swap it: open the menu anchored to that chip.\n const replaceChip = useCallback(\n (chip: HTMLElement) => {\n commitTargetRef.current = \"inline\";\n replacingRef.current = chip;\n replacingTrayIdRef.current = null;\n tokenRef.current = null;\n anchorRef.current = () => chip.getBoundingClientRect();\n setMode(\"menu\");\n setQuery(\"\");\n setPos(computePosition(chip.getBoundingClientRect()));\n setOpen(true);\n void refresh(\"\");\n },\n [refresh],\n );\n\n // Click a tray chip to swap it: open the menu anchored to that chip.\n const replaceTrayChip = useCallback(\n (id: string, anchorEl: HTMLElement) => {\n commitTargetRef.current = \"tray\";\n replacingTrayIdRef.current = id;\n replacingRef.current = null;\n tokenRef.current = null;\n anchorRef.current = () => anchorEl.getBoundingClientRect();\n setMode(\"menu\");\n setQuery(\"\");\n setPos(computePosition(anchorEl.getBoundingClientRect()));\n setOpen(true);\n void refresh(\"\");\n },\n [refresh],\n );\n\n const removeChip = useCallback(\n (chip: HTMLElement) => {\n const id = chip.dataset.refId;\n const next = chip.nextSibling;\n chip.remove();\n if (next && next.nodeType === Node.TEXT_NODE && next.textContent === NBSP) next.remove();\n if (id) {\n inlineIdsRef.current.delete(id);\n removeReference(id);\n }\n emit();\n focusEditor();\n },\n [removeReference, emit, focusEditor],\n );\n\n const handlersRef = useRef({ removeChip, replaceChip });\n handlersRef.current = { removeChip, replaceChip };\n\n const ctl = { open, rows, active, trigger, openFromToken, toggleFromButton, close, handleNavKey };\n const ctlRef = useRef(ctl);\n ctlRef.current = ctl;\n\n // Read the `@…` token at the caret, if any.\n const readToken = useCallback((): { node: Text; start: number; query: string } | null => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null;\n const node = sel.focusNode;\n if (!node || node.nodeType !== Node.TEXT_NODE) return null;\n const text = node.textContent ?? \"\";\n const before = text.slice(0, sel.focusOffset);\n const at = before.lastIndexOf(ctlRef.current.trigger);\n if (at < 0) return null;\n const prev = at > 0 ? before[at - 1] : \"\";\n if (prev && !/\\s/.test(prev)) return null;\n const q = before.slice(at + 1);\n if (/\\s/.test(q)) return null;\n return { node: node as Text, start: at, query: q };\n }, []);\n\n const onEditorInput = useCallback(() => {\n reconcile();\n emit();\n const token = readToken();\n if (token) {\n tokenRef.current = { node: token.node, start: token.start };\n const c = ctlRef.current;\n if (!c.open || mode === \"token\") openFromToken(token.query);\n else {\n setQuery(token.query);\n void refresh(token.query);\n }\n } else if (ctlRef.current.open && mode === \"token\") {\n close();\n }\n }, [reconcile, emit, readToken, openFromToken, refresh, mode, close]);\n\n const TextAreaSlot = useMemo(\n () =>\n forwardRef<HTMLTextAreaElement, TextAreaSlotProps>(function KabooRefEditor(props, ref) {\n const { onChange, onKeyDown, className, autoFocus } = props as TextAreaSlotProps & {\n className?: string;\n autoFocus?: boolean;\n };\n nativeChangeRef.current = onChange as\n | ((e: ChangeEvent<HTMLTextAreaElement>) => void)\n | undefined;\n\n const setRefs = (node: HTMLDivElement | null) => {\n editorRef.current = node;\n if (typeof ref === \"function\") ref(node as unknown as HTMLTextAreaElement);\n else if (ref) (ref as RefObject<HTMLTextAreaElement | null>).current = node as unknown as HTMLTextAreaElement;\n };\n\n const handleKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (ctlRef.current.handleNavKey(e)) return;\n onKeyDown?.(e as unknown as ReactKeyboardEvent<HTMLTextAreaElement>);\n };\n\n return (\n <div\n ref={setRefs}\n role=\"textbox\"\n aria-multiline=\"true\"\n tabIndex={0}\n contentEditable\n suppressContentEditableWarning\n data-placeholder={placeholderRef.current}\n className={`kaboo-ref-editor cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:leading-relaxed cpk:text-[16px] ${className ?? \"\"}`}\n data-empty=\"true\"\n autoFocus={autoFocus}\n onInput={onEditorInput}\n onKeyDown={handleKeyDown}\n />\n );\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n const AddMenuButtonSlot = useMemo(\n () =>\n function KabooRefAddButton(props: { disabled?: boolean }) {\n return (\n <button\n ref={addBtnRef}\n type=\"button\"\n className=\"kaboo-ref-add\"\n aria-label=\"Add attachment or reference\"\n aria-haspopup=\"listbox\"\n aria-expanded={ctlRef.current.open}\n disabled={props.disabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => ctlRef.current.toggleFromButton()}\n >\n {PlusIcon}\n </button>\n );\n },\n [],\n );\n\n // Dismiss on outside-click / Escape (menu is portaled outside the wrapper).\n useEffect(() => {\n if (!open) return;\n const onDocMouseDown = (e: MouseEvent) => {\n const target = e.target as Node;\n if (wrapRef.current?.contains(target) || menuRef.current?.contains(target)) return;\n close();\n };\n const onDocKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n document.addEventListener(\"mousedown\", onDocMouseDown);\n document.addEventListener(\"keydown\", onDocKey);\n return () => {\n document.removeEventListener(\"mousedown\", onDocMouseDown);\n document.removeEventListener(\"keydown\", onDocKey);\n };\n }, [open, close]);\n\n // Keep the portaled menu pinned to its anchor as layout shifts.\n useEffect(() => {\n if (!open) return;\n const reposition = () => setPos(computePosition(anchorRef.current()));\n window.addEventListener(\"resize\", reposition);\n window.addEventListener(\"scroll\", reposition, true);\n return () => {\n window.removeEventListener(\"resize\", reposition);\n window.removeEventListener(\"scroll\", reposition, true);\n };\n }, [open]);\n\n useLayoutEffect(() => {\n if (!open) return;\n setPos(computePosition(anchorRef.current()));\n if (mode === \"menu\") searchRef.current?.focus();\n }, [open, mode, rows.length]);\n\n // Clear staged references once a run has actually started (not on send), so\n // object refs on `state.kaboo_references` survive the in-flight run.\n useEffect(() => {\n const target = agent as unknown as {\n subscribe?: (s: Record<string, unknown>) => { unsubscribe?: () => void };\n } | null;\n if (!target || typeof target.subscribe !== \"function\") return;\n const sub = target.subscribe({ onRunStartedEvent: () => clearReferences() });\n return () => sub?.unsubscribe?.();\n }, [agent, clearReferences]);\n\n // Clear the editor DOM when CopilotKit resets the value (after a send).\n useEffect(() => {\n const root = editorRef.current;\n if (!root) return;\n if ((inputProps.value ?? \"\") === \"\" && root.childNodes.length > 0) {\n root.textContent = \"\";\n markEmpty();\n }\n }, [inputProps.value, markEmpty]);\n\n // Build the multimodal message and run the agent ourselves (files ride as\n // attachment parts; object refs travel on state.kaboo_references).\n const handleSubmit = useCallback(\n (message: string) => {\n const text = message.trim();\n const { attachmentParts } = serializeReferences(pending);\n const target = agent as unknown as {\n addMessage?: (m: unknown) => void;\n runAgent?: () => Promise<unknown> | void;\n } | null;\n if (text.length === 0 && attachmentParts.length === 0) return;\n if (!target?.addMessage || !target.runAgent) {\n // Fallback: let CopilotKit's own send handle plain text.\n (inputProps.onSubmitMessage as ((m: string) => void) | undefined)?.(message);\n close();\n return;\n }\n const content =\n attachmentParts.length > 0 ? buildUserContent(text, attachmentParts) : text;\n const id =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n // Object references ride state, not the message content, so record them\n // against this message id for the bubble to render them as chips.\n recordMessageReferences(\n id,\n pending.filter((r) => r.transport === \"object\"),\n );\n target.addMessage({ id, role: \"user\", content });\n void Promise.resolve(target.runAgent()).catch((err) =>\n console.error(\"[kaboo] runAgent failed\", err),\n );\n const root = editorRef.current;\n if (root) root.textContent = \"\";\n markEmpty();\n close();\n },\n [pending, agent, inputProps, close, markEmpty, recordMessageReferences],\n );\n\n // Tray = staged references not rendered inline (added via `+`).\n const trayRefs = pending.filter((r) => !inlineIdsRef.current.has(r.id));\n\n // CopilotKit centers the input pill independently of our wrapper, so align\n // the tray's box to the pill (`.copilotKitInput`) rather than the wrapper.\n useLayoutEffect(() => {\n const tray = trayRef.current;\n const wrap = wrapRef.current;\n if (!tray || !wrap) return;\n const align = () => {\n const pill = wrap.querySelector<HTMLElement>(\".copilotKitInput\");\n if (!pill) return;\n const pr = pill.getBoundingClientRect();\n const wr = wrap.getBoundingClientRect();\n tray.style.marginLeft = `${Math.max(0, pr.left - wr.left)}px`;\n tray.style.width = `${pr.width}px`;\n };\n align();\n window.addEventListener(\"resize\", align);\n return () => window.removeEventListener(\"resize\", align);\n }, [trayRefs.length]);\n\n return (\n <div ref={wrapRef} className=\"kaboo-ref-input\">\n <input ref={fileInputRef} type=\"file\" className=\"kaboo-ref-file\" tabIndex={-1} aria-hidden=\"true\" />\n {trayRefs.length > 0 && (\n <div ref={trayRef} className=\"kaboo-ref-tray\">\n {trayRefs.map((r) => (\n <TrayChip\n key={r.id}\n reference={r}\n onRemove={() => removeReference(r.id)}\n onReplace={(el) => replaceTrayChip(r.id, el)}\n />\n ))}\n </div>\n )}\n <CopilotChatInput\n {...inputProps}\n onSubmitMessage={handleSubmit}\n textArea={TextAreaSlot}\n addMenuButton={AddMenuButtonSlot}\n />\n {open &&\n pos &&\n createPortal(\n <ReferencePopover\n ref={menuRef}\n pos={pos}\n rows={rows}\n active={active}\n query={query}\n mode={mode}\n searchRef={searchRef}\n hasUpload={!!uploadProviderDef}\n onSearch={onSearch}\n onSearchKeyDown={handleNavKey}\n onHover={setActive}\n onPick={select}\n />,\n document.body,\n )}\n </div>\n );\n}\n\n/** A staged reference shown in the tray (added via `+`, not inline). */\nfunction TrayChip({\n reference,\n onRemove,\n onReplace,\n}: {\n reference: PendingReference;\n onRemove: () => void;\n onReplace: (anchor: HTMLElement) => void;\n}) {\n const ref = useRef<HTMLSpanElement>(null);\n const isImage = reference.transport === \"attachment\" && isImageMime(reference.mimeType);\n return (\n <span\n ref={ref}\n className={`kaboo-chip ${CHIP_CLASS}-${reference.transport} kaboo-chip-tray`}\n title={reference.name}\n role=\"button\"\n tabIndex={0}\n onMouseDown={(e) => {\n e.preventDefault();\n if (ref.current) onReplace(ref.current);\n }}\n >\n {isImage ? (\n <img\n className=\"kaboo-chip-thumb\"\n src={previewSrc(reference as Extract<PendingReference, { transport: \"attachment\" }>)}\n alt={reference.name}\n />\n ) : (\n <span className=\"kaboo-chip-ic\">{reference.transport === \"object\" ? AtIcon : AttachIcon}</span>\n )}\n <span className=\"kaboo-chip-label\">{reference.name}</span>\n <button\n type=\"button\"\n className=\"kaboo-chip-x\"\n aria-label={`Remove ${reference.name}`}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onRemove();\n }}\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </span>\n );\n}\n\n/** Caret rect for a text node offset (mirror-measured like getCaretRect). */\nfunction caretRectFor(node: Text, offset: number): DOMRect | null {\n try {\n const range = document.createRange();\n range.setStart(node, Math.min(offset, node.textContent?.length ?? 0));\n range.collapse(true);\n const rect = range.getBoundingClientRect();\n if (rect.top === 0 && rect.left === 0) {\n const parent = node.parentElement;\n return parent ? parent.getBoundingClientRect() : null;\n }\n return rect;\n } catch {\n return null;\n }\n}\n\nconst ReferencePopover = forwardRef<\n HTMLDivElement,\n {\n pos: PopoverPosition;\n rows: Row[];\n active: number;\n query: string;\n mode: \"token\" | \"menu\";\n searchRef: RefObject<HTMLInputElement | null>;\n hasUpload: boolean;\n onSearch: (value: string) => void;\n onSearchKeyDown: (e: ReactKeyboardEvent) => boolean;\n onHover: (i: number) => void;\n onPick: (row: Row) => void;\n }\n>(function ReferencePopover(\n { pos, rows, active, query, mode, searchRef, onSearch, onSearchKeyDown, onHover, onPick },\n ref,\n) {\n return (\n <div\n ref={ref}\n className=\"kaboo-refmenu\"\n role=\"listbox\"\n style={{ left: pos.left, bottom: pos.bottom, width: pos.width }}\n >\n {mode === \"menu\" && (\n <div className=\"kaboo-refmenu-search\">\n <span className=\"kaboo-refmenu-search-icon\">{SearchIcon}</span>\n <input\n ref={searchRef}\n type=\"text\"\n className=\"kaboo-refmenu-search-input\"\n placeholder=\"Search…\"\n value={query}\n onChange={(e) => onSearch(e.target.value)}\n onKeyDown={(e) => onSearchKeyDown(e)}\n />\n </div>\n )}\n {rows.length === 0 && <div className=\"kaboo-refmenu-empty\">No matches</div>}\n {rows.map((row, i) => {\n const key = row.kind === \"action\" ? `action:${row.provider.id}` : `${row.provider.id}:${row.item.id}`;\n const showHeader = i === 0 || rows[i - 1].group !== row.group;\n const isUpload = row.kind === \"action\" && isUploadProvider(row.provider);\n const icon: ReactNode = isUpload\n ? AttachIcon\n : row.kind === \"item\"\n ? (row.provider.icon ?? AtIcon)\n : (row.provider.icon ?? AtIcon);\n const title =\n row.kind === \"action\" ? (isUpload ? \"Attach a file\" : row.provider.label) : row.item.label;\n const subtitle =\n row.kind === \"action\"\n ? isUpload\n ? \"Upload from your device\"\n : undefined\n : row.item.description;\n const custom = row.kind === \"item\" ? row.provider.renderItem?.(row.item) : undefined;\n return (\n <div key={key}>\n {showHeader && <div className=\"kaboo-refmenu-group\">{row.group}</div>}\n <div\n role=\"option\"\n aria-selected={i === active}\n className={`kaboo-refmenu-item${i === active ? \" is-active\" : \"\"}`}\n onMouseEnter={() => onHover(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n onPick(row);\n }}\n >\n <span className=\"kaboo-refmenu-icon\">{icon}</span>\n {custom ?? (\n <span className=\"kaboo-refmenu-text\">\n <span className=\"kaboo-refmenu-title\">{title}</span>\n {subtitle && <span className=\"kaboo-refmenu-sub\">{subtitle}</span>}\n </span>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n});\n","import type { AttachmentsConfig, AttachmentUploadResult } from \"@copilotkit/shared\";\nimport { REFERENCE_METADATA_KEYS } from \"./types\";\nimport { mintReferenceId, type PendingReference } from \"./serialize\";\nimport type { ReferenceProvider } from \"./types\";\n\n/** Configuration for the built-in file {@link uploadProvider}. */\nexport interface UploadProviderConfig {\n /** Provider id in the `@` menu. Default `\"upload\"`. */\n id?: string;\n /** Group label in the `@` menu. Default `\"Upload\"`. */\n label?: string;\n /** Reference kind stamped on emitted attachments. Default `\"file\"`. */\n kind?: string;\n /** `accept` filter for the file picker (e.g. `\"image/*,.pdf\"`). */\n accept?: string;\n /** Max file size in bytes. Files above this are rejected. Default 20MB. */\n maxSize?: number;\n /**\n * Store the file and return a fetchable source. Return a `url` (presigned or\n * public — the server fetches it with no auth) or inline `data` (base64).\n * When omitted, the file is inlined as base64 (fine for small files/tests,\n * bloats the event log for large ones).\n */\n onUpload?: (file: File) => AttachmentUploadResult | Promise<AttachmentUploadResult>;\n}\n\n/** Internal marker: identifies a {@link ReferenceProvider} produced by {@link uploadProvider}. */\nexport const UPLOAD_MARKER = \"__kabooUpload\" as const;\n\n/** A {@link ReferenceProvider} carrying its resolved upload config for the composer. */\nexport interface UploadReferenceProvider extends ReferenceProvider {\n /** Resolved upload config the composer reads to drive the file picker. */\n [UPLOAD_MARKER]: Required<Pick<UploadProviderConfig, \"kind\" | \"maxSize\">> &\n Pick<UploadProviderConfig, \"accept\" | \"onUpload\">;\n}\n\nconst DEFAULT_MAX_SIZE = 20 * 1024 * 1024;\n\nfunction readAsBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = String(reader.result);\n const comma = result.indexOf(\",\");\n resolve(comma >= 0 ? result.slice(comma + 1) : result);\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(file);\n });\n}\n\n/**\n * Upload one file and return the pending attachment reference (id minted here).\n * Uses `config.onUpload` when provided, else inlines the bytes as base64.\n */\nexport async function uploadFileToReference(\n file: File,\n config: UploadProviderConfig,\n): Promise<PendingReference> {\n const kind = config.kind ?? \"file\";\n const id = mintReferenceId(kind);\n const maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;\n if (file.size > maxSize) {\n throw new Error(`File \"${file.name}\" exceeds the ${maxSize}-byte limit.`);\n }\n\n if (config.onUpload) {\n const result = await config.onUpload(file);\n const mimeType = result.mimeType ?? file.type ?? \"application/octet-stream\";\n const source =\n result.type === \"url\" ? { url: result.value } : { data: result.value };\n return { transport: \"attachment\", kind, id, name: file.name, mimeType, source };\n }\n\n const data = await readAsBase64(file);\n return {\n transport: \"attachment\",\n kind,\n id,\n name: file.name,\n mimeType: file.type || \"application/octet-stream\",\n source: { data },\n };\n}\n\n/**\n * Built-in file-upload {@link ReferenceProvider}. Action-only: choosing it in\n * the `@` menu opens the file picker; the composer uploads the file (via\n * `onUpload` or base64) and adds an `attachment`-transport reference.\n *\n * @example\n * ```tsx\n * <KabooProvider references={[uploadProvider({ accept: \"image/*,.pdf\", onUpload })]} ...>\n * ```\n */\nexport function uploadProvider(config: UploadProviderConfig = {}): UploadReferenceProvider {\n return {\n id: config.id ?? \"upload\",\n label: config.label ?? \"Upload\",\n [UPLOAD_MARKER]: {\n kind: config.kind ?? \"file\",\n maxSize: config.maxSize ?? DEFAULT_MAX_SIZE,\n accept: config.accept,\n onUpload: config.onUpload,\n },\n };\n}\n\n/** Narrow a provider to an {@link UploadReferenceProvider}. */\nexport function isUploadProvider(\n provider: ReferenceProvider,\n): provider is UploadReferenceProvider {\n return UPLOAD_MARKER in provider;\n}\n\n/**\n * Build a CopilotKit `AttachmentsConfig` from an upload config, wrapping\n * `onUpload` so every uploaded file carries kaboo id/kind/name in its\n * `InputContent` metadata. Pass to `<CopilotChat attachments={...}>` to use\n * CopilotKit's native attachment UI instead of the `@` composer.\n */\nexport function buildAttachmentsConfig(config: UploadProviderConfig = {}): AttachmentsConfig {\n const kind = config.kind ?? \"file\";\n return {\n enabled: true,\n accept: config.accept,\n maxSize: config.maxSize,\n onUpload: async (file: File): Promise<AttachmentUploadResult> => {\n const id = mintReferenceId(kind);\n const kabooMeta = {\n [REFERENCE_METADATA_KEYS.id]: id,\n [REFERENCE_METADATA_KEYS.kind]: kind,\n [REFERENCE_METADATA_KEYS.name]: file.name,\n };\n if (config.onUpload) {\n const result = await config.onUpload(file);\n return { ...result, metadata: { ...(result.metadata ?? {}), ...kabooMeta } };\n }\n const value = await readAsBase64(file);\n return {\n type: \"data\",\n value,\n mimeType: file.type || \"application/octet-stream\",\n metadata: kabooMeta,\n };\n },\n };\n}\n","import { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\nimport { topLevelGroups, directChildren } from \"../utils/groups\";\nimport { AgentCard } from \"./AgentCard\";\n\n/**\n * Standalone panel that renders the current activity tree as a list of\n * {@link AgentCard}s — top-level groups at the root, or the drilled-in group's\n * children. Use it when you want the hierarchical activity view outside the chat\n * transcript (the in-chat view is handled by `KabooMessageView`). Renders\n * nothing when there is no activity.\n *\n * @example\n * ```tsx\n * import { ActivityPanel } from \"@pgege/kaboo-react\";\n *\n * function Sidebar() {\n * return (\n * <aside>\n * <ActivityPanel />\n * </aside>\n * );\n * }\n * ```\n */\nexport function ActivityPanel() {\n const { groups } = useActivity();\n const { activeDrill } = useDrill();\n\n if (Object.keys(groups).length === 0) return null;\n\n // A plain-agent entry group (`inlineChatOwner`) is rendered inline by the host\n // chat, so it must not also appear as a root card here — it only enriches\n // those inline tool rows. Nested/sub-agent drilling is unaffected.\n const filtered = activeDrill\n ? directChildren(groups, activeDrill)\n : topLevelGroups(groups).filter(([, g]) => !g.inlineChatOwner);\n\n if (filtered.length === 0 && activeDrill) {\n const exact = groups[activeDrill];\n if (exact) {\n return (\n <div className=\"kaboo-activity-panel\">\n <AgentCard groupId={activeDrill} group={exact} />\n </div>\n );\n }\n }\n\n return (\n <div className=\"kaboo-activity-panel\">\n {filtered.map(([id, group]) => (\n <AgentCard key={id} groupId={id} group={group} />\n ))}\n </div>\n );\n}\n","import { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\n\n/**\n * Breadcrumb navigation for the drill-down path: a \"Chat\" root followed by one\n * crumb per drilled-in group. Clicking a crumb jumps to that level; the current\n * (last) crumb is disabled. Renders nothing at the root. Pair with\n * {@link DrillDetailView}.\n *\n * @example\n * ```tsx\n * import { GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n *\n * function DrillArea() {\n * return (\n * <>\n * <GlassTabs />\n * <DrillDetailView />\n * </>\n * );\n * }\n * ```\n */\nexport function GlassTabs() {\n const { drillPath, drillToRoot, drillToLevel } = useDrill();\n const { groups } = useActivity();\n\n if (drillPath.length === 0) return null;\n\n const tabs: { id: string; label: string; level: number }[] = [\n { id: \"root\", label: \"Chat\", level: -1 },\n ...drillPath.map((id, i) => ({\n id,\n label: groups[id]?.title || id,\n level: i,\n })),\n ];\n\n return (\n <nav className=\"kaboo-breadcrumb-bar\">\n {tabs.map((tab, i) => {\n const isLast = i === tabs.length - 1;\n return (\n <span key={tab.id} className=\"kaboo-breadcrumb-item\">\n {i > 0 && (\n <svg width={12} height={12} viewBox=\"0 0 16 16\" className=\"kaboo-breadcrumb-sep\">\n <path d=\"M6 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )}\n <button\n className={`kaboo-breadcrumb-link ${isLast ? \"kaboo-breadcrumb-current\" : \"\"}`}\n onClick={() => {\n if (isLast) return;\n if (tab.level === -1) drillToRoot();\n else drillToLevel(tab.level);\n }}\n disabled={isLast}\n >\n {i === 0 && (\n <svg width={14} height={14} viewBox=\"0 0 16 16\" className=\"kaboo-breadcrumb-icon\">\n <path d=\"M3 8.5L8 4l5 4.5M4.5 7.5V12h2.5V9.5h2V12h2.5V7.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n {tab.label}\n </button>\n </span>\n );\n })}\n </nav>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { useActivity } from \"../hooks/useActivity\";\nimport { useDrill } from \"../hooks/useDrill\";\nimport { useInterruptBridge } from \"../context/InterruptBridge\";\nimport {\n directChildren,\n partitionChildrenByToolCall,\n pendingToolAnchorExists,\n} from \"../utils/groups\";\nimport { AgentCard } from \"./AgentCard\";\nimport { Timeline } from \"./Timeline\";\nimport { InterruptRenderer } from \"./InterruptRenderer\";\n\n/**\n * Detail pane for the currently drilled-in group: its task, full timeline\n * (with delegated sub-agents interleaved at their tool-call position), any\n * unanchored interrupt prompts, and a \"Sub-agents\" section for swarm/graph\n * members. Renders a hidden placeholder at the root. Pair with {@link GlassTabs}.\n *\n * @example\n * ```tsx\n * import { GlassTabs, DrillDetailView } from \"@pgege/kaboo-react\";\n *\n * function DrillArea() {\n * return (\n * <>\n * <GlassTabs />\n * <DrillDetailView />\n * </>\n * );\n * }\n * ```\n */\nexport function DrillDetailView() {\n const { activeDrill } = useDrill();\n const { groups } = useActivity();\n const { active: activeInterrupts } = useInterruptBridge();\n const bodyRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (activeDrill && bodyRef.current) {\n bodyRef.current.scrollTop = 0;\n }\n }, [activeDrill]);\n\n if (!activeDrill) {\n return <div className=\"kaboo-drill-detail kaboo-hidden\" />;\n }\n\n const group = groups[activeDrill];\n const timeline = group?.timeline ?? [];\n // Prompts anchored to a pending tool call are rendered inline by the Timeline\n // at that tool's position (chronological); this block only handles interrupts\n // with no on-screen tool anchor — never a duplicate.\n const unanchoredInterrupts =\n group?.status === \"interrupted\"\n ? activeInterrupts.filter(\n (i) => !pendingToolAnchorExists(groups, i.toolCallId),\n )\n : [];\n\n const childEntries = directChildren(groups, activeDrill);\n // Interleave delegated children at their delegating tool-call position so a\n // sub-agent's card sits where it was delegated (before the parent's summary\n // text) rather than in a trailing block. Children with no tool-call anchor\n // (swarm/graph members) still render in the Sub-agents section below.\n const { byToolCall: childByToolCall, leftover: leftoverChildren } =\n partitionChildrenByToolCall(childEntries, timeline);\n const renderToolCard = (toolUseId: string) => {\n const entry = childByToolCall.get(toolUseId);\n if (!entry) return null;\n return <AgentCard groupId={entry[0]} group={entry[1]} />;\n };\n\n return (\n <div ref={bodyRef} className=\"kaboo-drill-detail\">\n <div className=\"kaboo-drill-detail-inner\">\n {group?.task && (\n <div className=\"kaboo-agent-card-task kaboo-drill-task\">\n <strong>Task:</strong> {group.task}\n </div>\n )}\n\n {group && timeline.length > 0 && (\n <div className=\"kaboo-drill-timeline\">\n <Timeline\n timeline={timeline}\n active={group.status === \"active\"}\n variant=\"drill\"\n renderToolCard={renderToolCard}\n />\n </div>\n )}\n\n {unanchoredInterrupts.length > 0 && (\n <div className=\"kaboo-drill-interrupt\">\n {unanchoredInterrupts.map((it) => (\n <InterruptRenderer\n key={it.id}\n reason={it.reason}\n toolCallId={it.toolCallId}\n onResolve={it.onResolve}\n onCancel={it.onCancel}\n />\n ))}\n </div>\n )}\n\n {leftoverChildren.length > 0 && (\n <div className=\"kaboo-drill-children\">\n <div className=\"kaboo-drill-section-label\">Sub-agents</div>\n {leftoverChildren.map(([id, childGroup]) => (\n <AgentCard key={id} groupId={id} group={childGroup} />\n ))}\n </div>\n )}\n\n {group && timeline.length === 0 && childEntries.length === 0 && (\n <div className=\"kaboo-agent-card-empty\">\n Working...\n </div>\n )}\n\n {!group && (\n <div className=\"kaboo-agent-card-empty\">\n No activity data for this group.\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","ReferencePopover","jsx","jsx","jsxs","useEffect","useRef","jsx","jsxs","useRef","useEffect"]}
package/dist/styles.css CHANGED
@@ -708,5 +708,324 @@
708
708
  animation: kaboo-blink 1s step-end infinite;
709
709
  }
710
710
 
711
+ /* ── Reference input (native + / @ → one popover) ── */
712
+ .kaboo-ref-input {
713
+ position: relative;
714
+ width: 100%;
715
+ }
716
+
717
+ /* Hidden picker used by the "attach a file" action. */
718
+ .kaboo-ref-file {
719
+ position: absolute;
720
+ width: 1px;
721
+ height: 1px;
722
+ padding: 0;
723
+ margin: -1px;
724
+ overflow: hidden;
725
+ clip: rect(0 0 0 0);
726
+ border: 0;
727
+ }
728
+
729
+ /* Tray of references added via the + button (never inline). Sits just above
730
+ the input, like a native attachment strip. */
731
+ .kaboo-ref-tray {
732
+ display: flex;
733
+ flex-wrap: wrap;
734
+ gap: 6px;
735
+ margin-bottom: 8px;
736
+ padding: 0 2px;
737
+ }
738
+
739
+ .kaboo-chip-tray {
740
+ font-size: 0.85rem;
741
+ padding: 3px 5px 3px 7px;
742
+ }
743
+
744
+ /* Object-reference chips shown in a sent user bubble. Object references ride
745
+ agent state (not the message content), so they're rendered here alongside
746
+ CopilotKit's native file attachment cards. */
747
+ .kaboo-user-message {
748
+ display: flex;
749
+ flex-direction: column;
750
+ align-items: flex-end;
751
+ }
752
+
753
+ .kaboo-msg-refs {
754
+ display: flex;
755
+ flex-wrap: wrap;
756
+ justify-content: flex-end;
757
+ gap: 6px;
758
+ margin-bottom: 6px;
759
+ max-width: 80%;
760
+ }
761
+
762
+ /* Non-interactive reference chips shown in a sent user bubble. Same visual
763
+ language as the composer chips, but static (no hover/click affordance). */
764
+ .kaboo-msg-chip {
765
+ cursor: default;
766
+ user-select: text;
767
+ max-width: 240px;
768
+ padding: 3px 9px 3px 7px;
769
+ }
770
+
771
+ .kaboo-msg-chip:hover {
772
+ border-color: var(--kaboo-border);
773
+ background: var(--kaboo-muted);
774
+ }
775
+
776
+ .kaboo-msg-chip .kaboo-chip-thumb {
777
+ width: 20px;
778
+ height: 20px;
779
+ }
780
+
781
+ /* Inline chip sits within the message text, so align it to the text baseline
782
+ and keep it compact. */
783
+ .kaboo-msg-chip-inline {
784
+ vertical-align: baseline;
785
+ margin: 0 1px;
786
+ padding: 1px 7px 1px 5px;
787
+ font-size: 0.92em;
788
+ translate: 0 2px;
789
+ }
790
+
791
+ /* contentEditable editor that replaces the plain textarea. Inherits the
792
+ CopilotKit textarea classes (transparent bg, font size) via className. */
793
+ .kaboo-ref-editor {
794
+ min-height: 1.5em;
795
+ max-height: 220px;
796
+ overflow-y: auto;
797
+ white-space: pre-wrap;
798
+ overflow-wrap: anywhere;
799
+ outline: none;
800
+ cursor: text;
801
+ }
802
+
803
+ .kaboo-ref-editor[data-empty="true"]::before {
804
+ content: attr(data-placeholder);
805
+ color: color-mix(in srgb, var(--kaboo-muted-fg) 85%, transparent);
806
+ pointer-events: none;
807
+ }
808
+
809
+ /* Inline reference chip (object mentions + file attachments). */
810
+ .kaboo-chip {
811
+ display: inline-flex;
812
+ align-items: center;
813
+ gap: 5px;
814
+ max-width: 220px;
815
+ margin: 0 1px;
816
+ padding: 1px 4px 1px 6px;
817
+ border-radius: 7px;
818
+ border: 1px solid var(--kaboo-border);
819
+ background: var(--kaboo-muted);
820
+ color: var(--kaboo-fg);
821
+ font-size: 0.9em;
822
+ line-height: 1.4;
823
+ vertical-align: baseline;
824
+ cursor: pointer;
825
+ user-select: none;
826
+ transition: background 0.12s ease, border-color 0.12s ease;
827
+ }
828
+
829
+ .kaboo-chip:hover {
830
+ border-color: var(--kaboo-accent);
831
+ background: color-mix(in srgb, var(--kaboo-accent) 12%, var(--kaboo-muted));
832
+ }
833
+
834
+ .kaboo-chip-ic {
835
+ display: inline-flex;
836
+ align-items: center;
837
+ color: var(--kaboo-fg);
838
+ opacity: 0.9;
839
+ }
840
+
841
+ .kaboo-chip-thumb {
842
+ width: 18px;
843
+ height: 18px;
844
+ border-radius: 4px;
845
+ object-fit: cover;
846
+ display: block;
847
+ }
848
+
849
+ .kaboo-chip-label {
850
+ overflow: hidden;
851
+ text-overflow: ellipsis;
852
+ white-space: nowrap;
853
+ }
854
+
855
+ .kaboo-chip-x {
856
+ display: inline-flex;
857
+ align-items: center;
858
+ justify-content: center;
859
+ width: 16px;
860
+ height: 16px;
861
+ padding: 0;
862
+ border: none;
863
+ border-radius: 5px;
864
+ background: transparent;
865
+ color: var(--kaboo-muted-fg);
866
+ cursor: pointer;
867
+ flex: 0 0 auto;
868
+ }
869
+
870
+ .kaboo-chip-x:hover {
871
+ background: color-mix(in srgb, var(--kaboo-fg) 12%, transparent);
872
+ color: var(--kaboo-fg);
873
+ }
874
+
875
+ /* Native "+" replacement — mirrors the built-in add button. */
876
+ .kaboo-ref-add {
877
+ display: inline-flex;
878
+ align-items: center;
879
+ justify-content: center;
880
+ width: 34px;
881
+ height: 34px;
882
+ border-radius: 999px;
883
+ border: none;
884
+ cursor: pointer;
885
+ color: var(--kaboo-muted-fg);
886
+ background: transparent;
887
+ transition: background 0.12s ease, color 0.12s ease;
888
+ pointer-events: auto;
889
+ }
890
+
891
+ .kaboo-ref-add:hover:not(:disabled) {
892
+ background: var(--kaboo-muted);
893
+ color: var(--kaboo-fg);
894
+ }
895
+
896
+ .kaboo-ref-add[aria-expanded="true"] {
897
+ background: var(--kaboo-accent);
898
+ color: var(--kaboo-accent-fg);
899
+ }
900
+
901
+ .kaboo-ref-add:disabled {
902
+ opacity: 0.4;
903
+ cursor: default;
904
+ }
905
+
906
+ /* Shared popover for both + and @. Portaled to <body>; left/bottom/width are
907
+ set inline from the input's measured position (see computePosition). */
908
+ .kaboo-refmenu {
909
+ position: fixed;
910
+ max-height: 320px;
911
+ overflow-y: auto;
912
+ padding: 6px;
913
+ border: 1px solid var(--kaboo-border);
914
+ border-radius: 12px;
915
+ background: var(--kaboo-card);
916
+ color: var(--kaboo-card-fg);
917
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
918
+ z-index: 9999;
919
+ pointer-events: auto;
920
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
921
+ }
922
+
923
+ /* Search box — keeps the list usable as providers/items grow. */
924
+ .kaboo-refmenu-search {
925
+ display: flex;
926
+ align-items: center;
927
+ gap: 8px;
928
+ padding: 6px 10px;
929
+ margin-bottom: 4px;
930
+ border-radius: 8px;
931
+ background: var(--kaboo-muted);
932
+ }
933
+
934
+ .kaboo-refmenu-search-icon {
935
+ display: inline-flex;
936
+ color: var(--kaboo-muted-fg);
937
+ flex: 0 0 auto;
938
+ }
939
+
940
+ .kaboo-refmenu-search-input {
941
+ flex: 1;
942
+ min-width: 0;
943
+ border: none;
944
+ outline: none;
945
+ background: transparent;
946
+ color: var(--kaboo-fg);
947
+ font-size: 0.85rem;
948
+ font-family: inherit;
949
+ }
950
+
951
+ .kaboo-refmenu-search-input::placeholder {
952
+ color: var(--kaboo-muted-fg);
953
+ }
954
+
955
+ .kaboo-refmenu-empty {
956
+ padding: 12px 10px;
957
+ font-size: 0.8rem;
958
+ color: var(--kaboo-muted-fg);
959
+ text-align: center;
960
+ }
961
+
962
+ .kaboo-refmenu-group {
963
+ padding: 8px 8px 4px;
964
+ font-size: 0.68rem;
965
+ font-weight: 600;
966
+ text-transform: uppercase;
967
+ letter-spacing: 0.04em;
968
+ color: var(--kaboo-muted-fg);
969
+ }
970
+
971
+ .kaboo-refmenu-item {
972
+ display: flex;
973
+ align-items: center;
974
+ gap: 10px;
975
+ height: 44px;
976
+ padding: 0 8px;
977
+ border-radius: 8px;
978
+ cursor: pointer;
979
+ }
980
+
981
+ .kaboo-refmenu-item.is-active {
982
+ background: var(--kaboo-accent);
983
+ color: var(--kaboo-accent-fg);
984
+ }
985
+
986
+ .kaboo-refmenu-icon {
987
+ display: inline-flex;
988
+ align-items: center;
989
+ justify-content: center;
990
+ width: 28px;
991
+ height: 28px;
992
+ flex: 0 0 auto;
993
+ border-radius: 7px;
994
+ background: var(--kaboo-muted);
995
+ color: var(--kaboo-fg);
996
+ }
997
+
998
+ .kaboo-refmenu-item.is-active .kaboo-refmenu-icon {
999
+ background: var(--kaboo-card);
1000
+ }
1001
+
1002
+ .kaboo-refmenu-text {
1003
+ display: flex;
1004
+ flex-direction: column;
1005
+ min-width: 0;
1006
+ flex: 1;
1007
+ }
1008
+
1009
+ .kaboo-refmenu-title {
1010
+ font-size: 0.85rem;
1011
+ font-weight: 500;
1012
+ overflow: hidden;
1013
+ text-overflow: ellipsis;
1014
+ white-space: nowrap;
1015
+ }
1016
+
1017
+ .kaboo-refmenu-sub {
1018
+ font-size: 0.72rem;
1019
+ color: var(--kaboo-muted-fg);
1020
+ overflow: hidden;
1021
+ text-overflow: ellipsis;
1022
+ white-space: nowrap;
1023
+ }
1024
+
1025
+ .kaboo-refmenu-item.is-active .kaboo-refmenu-sub {
1026
+ color: var(--kaboo-accent-fg);
1027
+ opacity: 0.75;
1028
+ }
1029
+
711
1030
  /* ── Animations ── */
712
1031
  @keyframes kaboo-blink { 50% { opacity: 0; } }