@dxos/react-ui-chat 0.11.1 → 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264

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.
@@ -4,7 +4,7 @@ import { Dialog, Icon, IconButton, Tooltip, useControlledState, useThemeContext,
4
4
  import { ResizeHandle, resizeAttributes, sizeStyle } from "@dxos/react-ui-dnd";
5
5
  import { mx } from "@dxos/ui-theme";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- import { acceptCompletion, autocompletion, completionKeymap, completionStatus } from "@codemirror/autocomplete";
7
+ import { acceptCompletion, autocompletion, completionKeymap, completionStatus, currentCompletions, selectedCompletionIndex, setSelectedCompletion } from "@codemirror/autocomplete";
8
8
  import { Prec, RangeSet } from "@codemirror/state";
9
9
  import { Decoration, EditorView, ViewPlugin, WidgetType, keymap } from "@codemirror/view";
10
10
  import { Mutex } from "@dxos/async";
@@ -118,26 +118,103 @@ var ChatDialog = {
118
118
  //#endregion
119
119
  //#region src/components/ChatEditor/commands.ts
120
120
  /** Pure prefix matcher, exported for tests. */
121
- var matchCommands = (all, token) => token.startsWith("$") ? all.filter(({ sentinel }) => sentinel.startsWith(token)) : [];
121
+ var matchCommands = (all, token) => token.startsWith("$") || token.startsWith("/") ? all.filter(({ sentinel }) => sentinel.startsWith(token)) : [];
122
122
  /**
123
- * Sentinel-command completion: typing `$` offers the commands defined by the bound context's instructions.
123
+ * Command completion: typing `$` offers the bound context's instruction sentinels anywhere;
124
+ * typing `/` at the start of the prompt offers registered slash commands.
124
125
  */
125
- var commands = ({ getCommands }) => [autocompletion({ override: [(context) => {
126
- const word = context.matchBefore(/\$[\w-]*/);
127
- if (!word || word.from === word.to && !context.explicit) return null;
128
- const options = matchCommands(getCommands(), context.state.sliceDoc(word.from, word.to));
129
- if (options.length === 0) return null;
130
- return {
131
- from: word.from,
132
- options: options.map(({ sentinel, description }) => ({
133
- label: sentinel,
134
- detail: description
135
- }))
126
+ var commandMark = Decoration.mark({
127
+ class: "dx-tag",
128
+ attributes: {
129
+ "data-hue": "blue",
130
+ "style": "margin-block: -5px;",
131
+ "font-family": "var(--dx-font-mono, monospace)"
132
+ }
133
+ });
134
+ /** Decorates a prompt-leading registered command token as a tag. */
135
+ var commandDecorations = (getCommands) => {
136
+ const build = (view) => {
137
+ const match = /^[$/][\w:-]+/.exec(view.state.doc.line(1).text);
138
+ return match && getCommands().some(({ sentinel }) => sentinel === match[0]) ? Decoration.set([commandMark.range(0, match[0].length)]) : Decoration.none;
136
139
  };
137
- }] }), Prec.highest(keymap.of([{
138
- key: "Enter",
139
- run: (view) => completionStatus(view.state) === "active" ? acceptCompletion(view) : false
140
- }]))];
140
+ return ViewPlugin.fromClass(class {
141
+ decorations;
142
+ constructor(view) {
143
+ this.decorations = build(view);
144
+ }
145
+ update(update) {
146
+ if (update.docChanged) this.decorations = build(update.view);
147
+ }
148
+ }, {
149
+ decorations: (instance) => instance.decorations,
150
+ provide: (plugin) => EditorView.atomicRanges.of((view) => view.plugin(plugin)?.decorations ?? Decoration.none)
151
+ });
152
+ };
153
+ /** Two-column command/description layout; shared column widths via subgrid. */
154
+ var commandTooltipTheme = EditorView.theme({
155
+ ".cm-tooltip-autocomplete > ul": {
156
+ display: "grid",
157
+ gridTemplateColumns: "max-content 1fr",
158
+ columnGap: "0.75rem"
159
+ },
160
+ ".cm-tooltip-autocomplete > ul > li": {
161
+ display: "grid",
162
+ gridTemplateColumns: "subgrid",
163
+ gridColumn: "span 2",
164
+ alignItems: "center"
165
+ },
166
+ ".cm-tooltip-autocomplete .cm-completionLabel": { fontFamily: "var(--dx-font-mono, monospace)" },
167
+ ".cm-tooltip-autocomplete .cm-completionDetail": {
168
+ fontStyle: "normal",
169
+ fontSize: "0.875rem",
170
+ marginInlineStart: "0"
171
+ }
172
+ });
173
+ var commands = ({ getCommands }) => [
174
+ commandTooltipTheme,
175
+ commandDecorations(getCommands),
176
+ autocompletion({
177
+ aboveCursor: true,
178
+ override: [(context) => {
179
+ const word = context.matchBefore(/[$/][\w:-]*/);
180
+ if (!word || word.from === word.to && !context.explicit) return null;
181
+ if (context.state.sliceDoc(word.from, word.from + 1) === "/" && word.from !== 0) return null;
182
+ const options = matchCommands(getCommands(), context.state.sliceDoc(word.from, word.to));
183
+ if (options.length === 0) return null;
184
+ return {
185
+ from: word.from,
186
+ options: options.map(({ sentinel, description }) => ({
187
+ label: sentinel,
188
+ detail: description,
189
+ apply: `${sentinel} `
190
+ }))
191
+ };
192
+ }]
193
+ }),
194
+ Prec.highest(keymap.of([{
195
+ key: "ArrowDown",
196
+ run: (view) => {
197
+ if (completionStatus(view.state) !== "active") return false;
198
+ const index = selectedCompletionIndex(view.state);
199
+ if (index === null) return false;
200
+ if (index < currentCompletions(view.state).length - 1) view.dispatch({ effects: setSelectedCompletion(index + 1) });
201
+ return true;
202
+ }
203
+ }, {
204
+ key: "ArrowUp",
205
+ run: (view) => {
206
+ if (completionStatus(view.state) !== "active") return false;
207
+ const index = selectedCompletionIndex(view.state);
208
+ if (index === null) return false;
209
+ if (index > 0) view.dispatch({ effects: setSelectedCompletion(index - 1) });
210
+ return true;
211
+ }
212
+ }])),
213
+ Prec.highest(keymap.of([{
214
+ key: "Enter",
215
+ run: (view) => completionStatus(view.state) === "active" ? acceptCompletion(view) : false
216
+ }]))
217
+ ];
141
218
  //#endregion
142
219
  //#region src/components/ChatEditor/references.ts
143
220
  var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-chat/src/components/ChatEditor/references.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/ChatDialog/ChatDialog.tsx","../../src/components/ChatEditor/commands.ts","../../src/components/ChatEditor/references.ts","../../src/components/ChatEditor/useChatExtensions.ts","../../src/components/ChatEditor/ChatEditor.tsx","../../src/components/ChatStatus/ChatStatusContext.ts","../../src/components/ChatStatus/format.ts","../../src/components/ChatStatus/ChatStatus.tsx","../../src/components/ChatStatusIndicator/ChatStatusIndicator.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport React, { type Dispatch, type PropsWithChildren, type SetStateAction, useEffect, useState } from 'react';\n\nimport { Dialog, Icon, IconButton, type ThemedClassName, useControlledState } from '@dxos/react-ui';\nimport { ResizeHandle, type Size, resizeAttributes, sizeStyle } from '@dxos/react-ui-dnd';\nimport { mx } from '@dxos/ui-theme';\n\nconst preventDefault = (event: Event) => event.preventDefault();\n\n// TODO(burdon): Factor out.\nconst Endcap = ({ children }: PropsWithChildren) => {\n return (\n <div className='grid w-[var(--dx-rail-action)] h-[var(--dx-rail-action)] items-center justify-center'>\n {children}\n </div>\n );\n};\n\n//\n// Context\n//\n\ntype ChatDialogContextValue = {\n open: boolean;\n setOpen: Dispatch<SetStateAction<boolean>>;\n expanded: boolean;\n setExpanded: Dispatch<SetStateAction<boolean>>;\n size: Size;\n setSize: Dispatch<SetStateAction<Size>>;\n};\n\nconst [ChatDialogContextProvider, useChatDialogContext] = createContext<ChatDialogContextValue>('ChatDialog');\n\n//\n// Root\n//\n\ntype ChatDialogRootProps = PropsWithChildren<{\n open?: boolean;\n expanded?: boolean;\n onOpenChange?: (open: boolean) => void;\n onExpandedChange?: (expanded: boolean) => void;\n onEscape?: () => void;\n}>;\n\nconst ChatDialogRoot = ({\n children,\n open: openProp = false,\n expanded: expandedProp = false,\n onOpenChange,\n onExpandedChange,\n onEscape,\n}: ChatDialogRootProps) => {\n const [size, setSize] = useState<Size>('min-content');\n const [open, setOpen] = useControlledState<boolean>(openProp, onOpenChange);\n const [expanded, setExpanded] = useControlledState<boolean>(expandedProp, onExpandedChange);\n\n // NOTE: We set the min size to 5rem (80px), and the header and prompt bar to 40px (i.e., the rail-size) each.\n // The dialog has no vertical padding and has box-content so that when closed it collapses to the size of the header and prompt bar.\n return (\n <ChatDialogContextProvider\n open={open}\n setOpen={setOpen}\n expanded={expanded}\n setExpanded={setExpanded}\n size={size}\n setSize={setSize}\n >\n <Dialog.Root modal={false} open={open} onOpenChange={setOpen}>\n <div className='dx-dialog__overlay bg-transparent pointer-events-none' data-block-align='end'>\n <Dialog.Content\n size='md'\n inOverlayLayout\n classNames={[\n 'grid grid-rows-[var(--dx-rail-action)_1fr_min-content] p-0 overflow-hidden box-content pointer-events-auto',\n ]}\n onEscapeKeyDown={onEscape}\n onInteractOutside={preventDefault}\n >\n {children}\n </Dialog.Content>\n </div>\n </Dialog.Root>\n </ChatDialogContextProvider>\n );\n};\n\nChatDialogRoot.displayName = 'ChatDialog.Root';\n\n//\n// Header\n//\n\nconst CHAT_DIALOG_HEADER_NAME = 'ChatDialog.Header';\n\ntype ChatDialogHeaderProps = ThemedClassName<{\n title?: string;\n}>;\n\nconst ChatDialogHeader = ({ classNames, title }: ChatDialogHeaderProps) => {\n const { expanded, setExpanded } = useChatDialogContext(CHAT_DIALOG_HEADER_NAME);\n\n return (\n <div\n className={mx('grid grid-cols-[var(--dx-rail-action)_1fr_min-content] items-center overflow-hidden', classNames)}\n >\n <Endcap>\n <Dialog.Close>\n <Icon icon='ph--x--regular' />\n </Dialog.Close>\n </Endcap>\n <Dialog.Title\n classNames='flex w-full justify-center text-sm text-subdued select-none cursor-pointer'\n onClick={() => setExpanded((expanded) => !expanded)}\n >\n {title}\n </Dialog.Title>\n <Endcap>\n <IconButton\n variant='ghost'\n icon='ph--caret-up--regular'\n iconOnly\n classNames={mx('p-1! [&>svg]:transition [&>svg]:duration-200', expanded && '[&>svg]:rotate-180')}\n label={expanded ? 'Close' : 'Open'}\n onClick={() => setExpanded((expanded) => !expanded)}\n />\n </Endcap>\n </div>\n );\n};\n\nChatDialogHeader.displayName = CHAT_DIALOG_HEADER_NAME;\n\n//\n// Content\n//\n\nconst CHAT_DIALOG_CONTENT_NAME = 'ChatDialog.Content';\n\ntype ChatDialogContentProps = ThemedClassName<PropsWithChildren>;\n\nconst ChatDialogContent = ({ children, classNames }: ChatDialogContentProps) => {\n const { expanded, size, setSize } = useChatDialogContext(CHAT_DIALOG_CONTENT_NAME);\n useEffect(() => {\n setSize(expanded ? 'min-content' : 0);\n }, [expanded]);\n\n return (\n <div\n className={mx(\n 'border-t border-b border-subdued-separator',\n 'transition ease-in-out duration-0 [&:not([data-dx-resizing=\"true\"])]:duration-200',\n classNames,\n )}\n style={{\n ...sizeStyle(size, 'vertical', true),\n maxBlockSize: 'calc(100dvh - env(safe-area-inset-bottom) - env(safe-area-inset-top) - 14rem)',\n }}\n {...resizeAttributes}\n >\n <ResizeHandle\n side='block-start'\n iconPosition='center'\n defaultSize='min-content'\n fallbackSize={0}\n minSize={0}\n onSizeChange={setSize}\n />\n {children}\n </div>\n );\n};\n\nChatDialogContent.displayName = CHAT_DIALOG_CONTENT_NAME;\n\n//\n// Footer\n//\n\nconst CHAT_DIALOG_FOOTER_NAME = 'ChatDialog.Footer';\n\ntype ChatDialogFooterProps = ThemedClassName<PropsWithChildren>;\n\nconst ChatDialogFooter = ({ children, classNames }: ChatDialogFooterProps) => {\n return <div className={mx(classNames)}>{children}</div>;\n};\n\nChatDialogFooter.displayName = CHAT_DIALOG_FOOTER_NAME;\n\n//\n// ChatDialog\n//\n\nexport const ChatDialog = {\n Root: ChatDialogRoot,\n Header: ChatDialogHeader,\n Content: ChatDialogContent,\n Footer: ChatDialogFooter,\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport {\n type CompletionContext,\n type CompletionResult,\n acceptCompletion,\n autocompletion,\n completionStatus,\n} from '@codemirror/autocomplete';\nimport { type Extension, Prec } from '@codemirror/state';\nimport { keymap } from '@codemirror/view';\n\nexport type CommandData = { sentinel: string; description?: string };\n\n/** Pure prefix matcher, exported for tests. */\nexport const matchCommands = (all: CommandData[], token: string): CommandData[] =>\n token.startsWith('$') ? all.filter(({ sentinel }) => sentinel.startsWith(token)) : [];\n\nexport type CommandsOptions = { getCommands: () => CommandData[] };\n\n/**\n * Sentinel-command completion: typing `$` offers the commands defined by the bound context's instructions.\n */\nexport const commands = ({ getCommands }: CommandsOptions): Extension => [\n autocompletion({\n override: [\n (context: CompletionContext): CompletionResult | null => {\n const word = context.matchBefore(/\\$[\\w-]*/);\n if (!word || (word.from === word.to && !context.explicit)) {\n return null;\n }\n const options = matchCommands(getCommands(), context.state.sliceDoc(word.from, word.to));\n if (options.length === 0) {\n return null;\n }\n return {\n from: word.from,\n options: options.map(({ sentinel, description }) => ({ label: sentinel, detail: description })),\n };\n },\n ],\n }),\n // The host editor's `submit()` extension also binds Enter at `Prec.highest`; ties between equal\n // precedences break by extension order, so the caller must place this extension ahead of `submit()`.\n // `completionStatus`/`acceptCompletion` are imported from the same module as `autocompletion()` above\n // (not re-derived in a different package) so this always reads the completion state `autocompletion()`\n // actually wrote — a cross-package import of the same-named API can resolve to a distinct bundled\n // copy of `@codemirror/autocomplete` under some bundler configurations, silently reading `null`.\n Prec.highest(\n keymap.of([\n {\n key: 'Enter',\n run: (view) => (completionStatus(view.state) === 'active' ? acceptCompletion(view) : false),\n },\n ]),\n ),\n];\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type CompletionResult, autocompletion, completionKeymap } from '@codemirror/autocomplete';\nimport { type Extension, RangeSet } from '@codemirror/state';\nimport {\n Decoration,\n type DecorationSet,\n EditorView,\n ViewPlugin,\n type ViewUpdate,\n WidgetType,\n keymap,\n} from '@codemirror/view';\n\nimport { Mutex } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\n\n// TODO(dmaretskyi): Consider adding details renderer for when you hover over the reference.\nexport type ReferenceData = {\n uri: string; // TODO(burdon): Rename dxn.\n label: string;\n};\n\n/**\n * @deprecated\n */\n// TODO(burdon): Remove.\nexport interface ReferencesProvider {\n getReferences({ query }: { query: string }): Promise<ReferenceData[]>;\n resolveReference({ uri }: { uri: string }): Promise<ReferenceData | null>;\n}\n\nexport type ReferencesOptions = {\n provider: ReferencesProvider;\n\n /**\n * @default '@'\n */\n triggerCharacter?: string;\n\n /**\n * Prevent the autocomplete from closing when the user blurs the editor.\n * @default false\n */\n debug?: boolean;\n};\n\n/**\n * Lookup object references.\n * @deprecated\n */\n// TODO(burdon): Remove.\nexport const references = ({ provider, triggerCharacter = '@', debug = false }: ReferencesOptions): Extension => {\n invariant(triggerCharacter.length === 1);\n\n const decorationField = ViewPlugin.fromClass(\n class ReferenceView {\n // TODO(burdon): Why?\n private _mutex = new Mutex();\n\n decorations: DecorationSet = Decoration.set([]);\n\n constructor(view: EditorView) {\n queueMicrotask(async () => {\n const guard = await this._mutex.acquire();\n try {\n this.decorations = await this._computeDecorations(view);\n } finally {\n guard.release();\n }\n });\n }\n\n update(update: ViewUpdate) {\n if (update.docChanged) {\n queueMicrotask(async () => {\n const guard = await this._mutex.acquire();\n try {\n this.decorations = await this._computeDecorations(update.view);\n } finally {\n guard.release();\n }\n });\n }\n }\n\n private async _computeDecorations(view: EditorView): Promise<DecorationSet> {\n const text = view.state.doc.toString();\n const references = text.matchAll(new RegExp(`${triggerCharacter}[a-zA-Z0-9@:]+\\\\s`, 'g'));\n\n const decorations = [];\n for (const match of references) {\n const reference = match[0];\n const uri = reference.slice(1, -1);\n const data = await provider.resolveReference({ uri });\n if (data) {\n decorations.push(\n Decoration.replace({\n widget: new ReferenceWidget(data),\n }).range(match.index!, match.index! + reference.length),\n );\n }\n }\n\n return Decoration.set(decorations);\n }\n },\n {\n decorations: (v) => v.decorations,\n provide: () => [\n EditorView.atomicRanges.of(\n (view): DecorationSet => view.plugin(decorationField)?.decorations ?? RangeSet.empty,\n ),\n ],\n },\n );\n\n return [\n decorationField,\n\n EditorView.theme({\n '.cm-reference-pill': {\n borderRadius: '0.25rem',\n borderWidth: '1px',\n marginRight: '0.25rem',\n marginLeft: '0.25rem',\n },\n }),\n\n autocompletion({\n tooltipClass: () => 'shadow rounded-sm',\n activateOnTyping: true,\n aboveCursor: true,\n closeOnBlur: !debug,\n override: [\n async (context): Promise<CompletionResult | null> => {\n const match = context.matchBefore(new RegExp(`${triggerCharacter}[a-zA-Z0-9]+`));\n if (!match || match?.to === match?.from) {\n return null;\n }\n\n const query = match.text.slice(1);\n const references = await provider.getReferences({ query });\n return {\n from: match.from,\n filter: false,\n options: references.map((reference) => ({\n label: reference.label,\n apply: `${triggerCharacter}${reference.uri} `,\n })),\n };\n },\n ],\n }),\n\n keymap.of(completionKeymap),\n ];\n};\n\nclass ReferenceWidget extends WidgetType {\n constructor(private data: ReferenceData) {\n super();\n }\n\n override ignoreEvent() {\n return true;\n }\n\n override eq(other: this) {\n return this.data.uri === other.data.uri;\n }\n\n override toDOM() {\n const span = document.createElement('span');\n span.textContent = `@${this.data.label}`;\n span.className = 'cm-reference-pill';\n return span;\n }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { useMemo } from 'react';\n\nimport { useThemeContext } from '@dxos/react-ui';\nimport {\n createBasicExtensions,\n createMarkdownExtensions,\n createThemeExtensions,\n decorateMarkdown,\n formattingKeymap,\n submit,\n xmlFormatting,\n} from '@dxos/ui-editor';\nimport { isTruthy } from '@dxos/util';\n\nimport { type ChatEditorProps } from './ChatEditor';\n\n// Kept out of `ChatEditor.tsx`: react-refresh only fast-refreshes a module whose\n// exports are all components, so values exported beside them force a full page reload on\n// every edit.\n\nexport const useChatExtensions = ({\n extensions,\n markdown = false,\n lineWrapping = false,\n placeholder,\n onSubmit,\n}: ChatEditorProps) => {\n const { themeMode } = useThemeContext();\n return useMemo<Extension[]>(\n () =>\n [\n createThemeExtensions({ themeMode, syntaxHighlighting: markdown }),\n createBasicExtensions({ bracketMatching: false, lineWrapping, placeholder }),\n xmlFormatting(),\n markdown && [createMarkdownExtensions(), decorateMarkdown(), formattingKeymap()],\n // Caller extensions (e.g. `commands()`'s completion-aware Enter binding) must precede\n // `submit()`: both bind Enter at `Prec.highest`, and CodeMirror breaks precedence ties by\n // extension order, so listing `submit()` first would always win and swallow the keystroke.\n extensions,\n submit({ onSubmit }),\n ]\n .flat()\n .filter(isTruthy),\n [themeMode, markdown, lineWrapping, placeholder, extensions, onSubmit],\n );\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport React, { forwardRef } from 'react';\n\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { Editor, type EditorController, type UseTextEditorProps } from '@dxos/react-ui-editor';\nimport { type BasicExtensionsOptions, type SubmitOptions } from '@dxos/ui-editor';\n\nimport { type ReferencesOptions } from './references';\nimport { useChatExtensions } from './useChatExtensions';\n\nexport interface ChatEditorController extends EditorController {}\n\nexport type ChatEditorProps = ThemedClassName<\n {\n extensions?: Extension;\n references?: ReferencesOptions;\n /** Enable inline markdown formatting (decoration, syntax highlighting, and formatting shortcuts). */\n markdown?: boolean;\n } & (SubmitOptions &\n Pick<UseTextEditorProps, 'id' | 'autoFocus'> &\n Pick<BasicExtensionsOptions, 'lineWrapping' | 'placeholder'>)\n>;\n\nexport const ChatEditor = forwardRef<ChatEditorController, ChatEditorProps>(\n (\n {\n classNames,\n autoFocus,\n extensions: extensionsProp,\n markdown = false,\n lineWrapping = false,\n placeholder,\n onSubmit,\n },\n forwardRef,\n ) => {\n const extensions = useChatExtensions({ extensions: extensionsProp, markdown, lineWrapping, placeholder, onSubmit });\n\n // TODO(burdon): Popover.\n return (\n <Editor.Root ref={forwardRef}>\n <Editor.View classNames={classNames} autoFocus={autoFocus} extensions={extensions} />\n </Editor.Root>\n );\n },\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\n\n// Kept out of `ChatStatus.tsx`: react-refresh only fast-refreshes a module whose exports are all\n// components, so a context and its hook exported beside them force a full page reload on every edit.\n\n//\n// Context\n//\n\nexport type ChatStatusContextValue = {\n /** Whole seconds elapsed since ChatStatus.Root mounted. Only advances while `running` is true. */\n elapsed: number;\n /** Whether the ChatStatus.Root tick is currently active. Toggled via the ChatStatusController. */\n running: boolean;\n};\n\nexport const [ChatStatusProvider, useChatStatusContext] = createContext<ChatStatusContextValue>('ChatStatus');\n","//\n// Copyright 2025 DXOS.org\n//\n\nconst SECOND = 1_000;\nconst MINUTE = 60 * SECOND;\nconst HOUR = 60 * MINUTE;\n\n/**\n * Formats elapsed milliseconds as a human-readable duration.\n * Tiers: `Ns` (< 60s), `Nm Xs` (< 60m), `Nh Xm` (≥ 60m).\n * Negative inputs clamp to `0s`.\n */\nexport const formatElapsed = (ms: number): string => {\n const safe = Math.max(0, ms);\n if (safe < MINUTE) {\n return `${Math.floor(safe / SECOND)}s`;\n }\n if (safe < HOUR) {\n const m = Math.floor(safe / MINUTE);\n const s = Math.floor((safe % MINUTE) / SECOND);\n return `${m}m ${s}s`;\n }\n const h = Math.floor(safe / HOUR);\n const m = Math.floor((safe % HOUR) / MINUTE);\n return `${h}h ${m}m`;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { type PropsWithChildren, type Ref, forwardRef, useEffect, useImperativeHandle, useState } from 'react';\n\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { ChatStatusProvider, useChatStatusContext } from './ChatStatusContext';\nimport { formatElapsed } from './format';\n\nconst TICK_MS = 1_000;\n\n//\n// Controller\n//\n\nexport type ChatStatusController = {\n /** Resume the tick. No-op if already running. */\n start: () => void;\n /** Pause the tick. The reported `elapsed` freezes at its last value. */\n stop: () => void;\n};\n\n//\n// Root\n//\n\nexport type RootProps = ThemedClassName<\n PropsWithChildren<{\n /** Whether the tick starts running on mount. Defaults to `true`. */\n defaultRunning?: boolean;\n }>\n>;\n\nconst Root = forwardRef<ChatStatusController, RootProps>(\n ({ classNames, children, defaultRunning = true }: RootProps, forwardedRef: Ref<ChatStatusController>) => {\n const [elapsed, setElapsed] = useState(0);\n const [running, setRunning] = useState(defaultRunning);\n\n useEffect(() => {\n if (!running) {\n return;\n }\n\n // Anchor to a wall-clock timestamp so `elapsed` self-corrects after\n // tab-throttling: browsers clamp setTimeout to >=1s in inactive tabs\n // and >=1min when the page is hidden, so a count-the-ticks approach\n // would under-report after a background period. Computing elapsed from\n // (Date.now() - startedAt) restores the true wall-clock value on the\n // next fired tick. The initial elapsed (preserved across stop/start)\n // is folded into the anchor so paused intervals are excluded.\n const startedAt = Date.now() - elapsed * TICK_MS;\n let timeout: ReturnType<typeof setTimeout>;\n const tick = () => {\n setElapsed(Math.floor((Date.now() - startedAt) / TICK_MS));\n timeout = setTimeout(tick, TICK_MS - (Date.now() % TICK_MS));\n };\n timeout = setTimeout(tick, TICK_MS - (Date.now() % TICK_MS));\n return () => clearTimeout(timeout);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `elapsed` is\n // captured once at resume; depending on it would restart the timer on\n // every tick.\n }, [running]);\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n start: () => setRunning(true),\n stop: () => setRunning(false),\n }),\n [],\n );\n\n return (\n <ChatStatusProvider elapsed={elapsed} running={running}>\n <span className={mx('inline-flex items-center gap-2 text-description font-mono tabular-nums', classNames)}>\n {children}\n </span>\n </ChatStatusProvider>\n );\n },\n);\n\nRoot.displayName = 'ChatChatStatus.Root';\n\n//\n// Icon\n//\n\nexport type IconProps = ThemedClassName<PropsWithChildren>;\n\n/**\n * Animated leading indicator. Defaults to a halo-pulse dot. Pass children to override.\n */\nconst Icon = ({ classNames, children }: IconProps) => {\n if (children !== undefined) {\n return <>{children}</>;\n }\n return (\n <span\n aria-hidden='true'\n className={mx('inline-block size-2 rounded-full bg-current animate-halo-pulse', classNames)}\n />\n );\n};\n\n//\n// Stopwatch\n//\n\nexport type StopwatchProps = ThemedClassName<{\n /** Seconds to add to the context elapsed value before formatting. Defaults to 0. */\n offset?: number;\n}>;\n\n/**\n * Elapsed-time display, driven by the ChatStatus.Root context tick.\n * Use `offset` to shift the displayed value; remount `ChatStatus.Root` to reset the underlying counter.\n * Marked `aria-live='off'` so screen readers don't announce every tick.\n */\nconst Stopwatch = ({ classNames, offset = 0 }: StopwatchProps) => {\n const { elapsed } = useChatStatusContext('ChatChatStatus.Stopwatch');\n return (\n <span aria-live='off' className={mx(classNames)}>\n {formatElapsed((elapsed + offset) * 1_000)}\n </span>\n );\n};\n\n//\n// Separator\n//\n\nexport type SeparatorProps = ThemedClassName<unknown>;\n\n/**\n * Middle-dot separator. Decorative — `aria-hidden`.\n */\nconst Separator = ({ classNames }: SeparatorProps) => (\n <span aria-hidden='true' className={mx('opacity-50', classNames)}>\n ·\n </span>\n);\n\n//\n// Text\n//\n\nexport type TextProps = ThemedClassName<PropsWithChildren>;\n\n/**\n * Generic text node — useful for token counts and other inline metadata.\n */\nconst Text = ({ classNames, children }: TextProps) => <span className={mx(classNames)}>{children}</span>;\n\n//\n// ChatStatus\n//\n\nexport const ChatStatus = {\n Root,\n Icon,\n Stopwatch,\n Separator,\n Text,\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useEffect, useState } from 'react';\n\nimport { type ThemedClassName, Tooltip, useTimeout } from '@dxos/react-ui';\n// Leaf import (not the package barrel): the barrel re-exports QueryEditor/QueryForm, which pull the\n// heavy @dxos/ai + @dxos/echo-query stack (tiktoken wasm, etc.) — inappropriate for lean consumers\n// like the browser extension. Importing the Spinner leaf keeps that graph out.\nimport { Spinner, type SpinnerProps } from '@dxos/react-ui-components/Spinner';\nimport { mx } from '@dxos/ui-theme';\n\nconst period = 3_000;\n\nexport type ChatStatusIndicatorProps = ThemedClassName<\n {\n preset?: string;\n processing?: boolean;\n error?: Error;\n } & Pick<SpinnerProps, 'size' | 'onClick'>\n>;\n\nexport const ChatStatusIndicator = ({ classNames, preset, processing, error, ...props }: ChatStatusIndicatorProps) => {\n const [init, setInit] = useState(false);\n useEffect(() => setInit(false), [preset]);\n useTimeout(\n async () => {\n setInit(true);\n },\n period / 2,\n [preset],\n );\n\n return (\n <div className={mx('relative flex', classNames)}>\n <Spinner duration={period} state={!init ? 'flash' : error ? 'error' : processing ? 'spin' : 'pulse'} {...props} />\n {error && (\n <Tooltip.Trigger asChild content={error.message}>\n <div className='absolute inset-0' />\n </Tooltip.Trigger>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/ChatDialog/ChatDialog.tsx","../../src/components/ChatEditor/commands.ts","../../src/components/ChatEditor/references.ts","../../src/components/ChatEditor/useChatExtensions.ts","../../src/components/ChatEditor/ChatEditor.tsx","../../src/components/ChatStatus/ChatStatusContext.ts","../../src/components/ChatStatus/format.ts","../../src/components/ChatStatus/ChatStatus.tsx","../../src/components/ChatStatusIndicator/ChatStatusIndicator.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport React, { type Dispatch, type PropsWithChildren, type SetStateAction, useEffect, useState } from 'react';\n\nimport { Dialog, Icon, IconButton, type ThemedClassName, useControlledState } from '@dxos/react-ui';\nimport { ResizeHandle, type Size, resizeAttributes, sizeStyle } from '@dxos/react-ui-dnd';\nimport { mx } from '@dxos/ui-theme';\n\nconst preventDefault = (event: Event) => event.preventDefault();\n\n// TODO(burdon): Factor out.\nconst Endcap = ({ children }: PropsWithChildren) => {\n return (\n <div className='grid w-[var(--dx-rail-action)] h-[var(--dx-rail-action)] items-center justify-center'>\n {children}\n </div>\n );\n};\n\n//\n// Context\n//\n\ntype ChatDialogContextValue = {\n open: boolean;\n setOpen: Dispatch<SetStateAction<boolean>>;\n expanded: boolean;\n setExpanded: Dispatch<SetStateAction<boolean>>;\n size: Size;\n setSize: Dispatch<SetStateAction<Size>>;\n};\n\nconst [ChatDialogContextProvider, useChatDialogContext] = createContext<ChatDialogContextValue>('ChatDialog');\n\n//\n// Root\n//\n\ntype ChatDialogRootProps = PropsWithChildren<{\n open?: boolean;\n expanded?: boolean;\n onOpenChange?: (open: boolean) => void;\n onExpandedChange?: (expanded: boolean) => void;\n onEscape?: () => void;\n}>;\n\nconst ChatDialogRoot = ({\n children,\n open: openProp = false,\n expanded: expandedProp = false,\n onOpenChange,\n onExpandedChange,\n onEscape,\n}: ChatDialogRootProps) => {\n const [size, setSize] = useState<Size>('min-content');\n const [open, setOpen] = useControlledState<boolean>(openProp, onOpenChange);\n const [expanded, setExpanded] = useControlledState<boolean>(expandedProp, onExpandedChange);\n\n // NOTE: We set the min size to 5rem (80px), and the header and prompt bar to 40px (i.e., the rail-size) each.\n // The dialog has no vertical padding and has box-content so that when closed it collapses to the size of the header and prompt bar.\n return (\n <ChatDialogContextProvider\n open={open}\n setOpen={setOpen}\n expanded={expanded}\n setExpanded={setExpanded}\n size={size}\n setSize={setSize}\n >\n <Dialog.Root modal={false} open={open} onOpenChange={setOpen}>\n <div className='dx-dialog__overlay bg-transparent pointer-events-none' data-block-align='end'>\n <Dialog.Content\n size='md'\n inOverlayLayout\n classNames={[\n 'grid grid-rows-[var(--dx-rail-action)_1fr_min-content] p-0 overflow-hidden box-content pointer-events-auto',\n ]}\n onEscapeKeyDown={onEscape}\n onInteractOutside={preventDefault}\n >\n {children}\n </Dialog.Content>\n </div>\n </Dialog.Root>\n </ChatDialogContextProvider>\n );\n};\n\nChatDialogRoot.displayName = 'ChatDialog.Root';\n\n//\n// Header\n//\n\nconst CHAT_DIALOG_HEADER_NAME = 'ChatDialog.Header';\n\ntype ChatDialogHeaderProps = ThemedClassName<{\n title?: string;\n}>;\n\nconst ChatDialogHeader = ({ classNames, title }: ChatDialogHeaderProps) => {\n const { expanded, setExpanded } = useChatDialogContext(CHAT_DIALOG_HEADER_NAME);\n\n return (\n <div\n className={mx('grid grid-cols-[var(--dx-rail-action)_1fr_min-content] items-center overflow-hidden', classNames)}\n >\n <Endcap>\n <Dialog.Close>\n <Icon icon='ph--x--regular' />\n </Dialog.Close>\n </Endcap>\n <Dialog.Title\n classNames='flex w-full justify-center text-sm text-subdued select-none cursor-pointer'\n onClick={() => setExpanded((expanded) => !expanded)}\n >\n {title}\n </Dialog.Title>\n <Endcap>\n <IconButton\n variant='ghost'\n icon='ph--caret-up--regular'\n iconOnly\n classNames={mx('p-1! [&>svg]:transition [&>svg]:duration-200', expanded && '[&>svg]:rotate-180')}\n label={expanded ? 'Close' : 'Open'}\n onClick={() => setExpanded((expanded) => !expanded)}\n />\n </Endcap>\n </div>\n );\n};\n\nChatDialogHeader.displayName = CHAT_DIALOG_HEADER_NAME;\n\n//\n// Content\n//\n\nconst CHAT_DIALOG_CONTENT_NAME = 'ChatDialog.Content';\n\ntype ChatDialogContentProps = ThemedClassName<PropsWithChildren>;\n\nconst ChatDialogContent = ({ children, classNames }: ChatDialogContentProps) => {\n const { expanded, size, setSize } = useChatDialogContext(CHAT_DIALOG_CONTENT_NAME);\n useEffect(() => {\n setSize(expanded ? 'min-content' : 0);\n }, [expanded]);\n\n return (\n <div\n className={mx(\n 'border-t border-b border-subdued-separator',\n 'transition ease-in-out duration-0 [&:not([data-dx-resizing=\"true\"])]:duration-200',\n classNames,\n )}\n style={{\n ...sizeStyle(size, 'vertical', true),\n maxBlockSize: 'calc(100dvh - env(safe-area-inset-bottom) - env(safe-area-inset-top) - 14rem)',\n }}\n {...resizeAttributes}\n >\n <ResizeHandle\n side='block-start'\n iconPosition='center'\n defaultSize='min-content'\n fallbackSize={0}\n minSize={0}\n onSizeChange={setSize}\n />\n {children}\n </div>\n );\n};\n\nChatDialogContent.displayName = CHAT_DIALOG_CONTENT_NAME;\n\n//\n// Footer\n//\n\nconst CHAT_DIALOG_FOOTER_NAME = 'ChatDialog.Footer';\n\ntype ChatDialogFooterProps = ThemedClassName<PropsWithChildren>;\n\nconst ChatDialogFooter = ({ children, classNames }: ChatDialogFooterProps) => {\n return <div className={mx(classNames)}>{children}</div>;\n};\n\nChatDialogFooter.displayName = CHAT_DIALOG_FOOTER_NAME;\n\n//\n// ChatDialog\n//\n\nexport const ChatDialog = {\n Root: ChatDialogRoot,\n Header: ChatDialogHeader,\n Content: ChatDialogContent,\n Footer: ChatDialogFooter,\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport {\n type CompletionContext,\n type CompletionResult,\n acceptCompletion,\n autocompletion,\n completionStatus,\n currentCompletions,\n selectedCompletionIndex,\n setSelectedCompletion,\n} from '@codemirror/autocomplete';\nimport { type Extension, Prec } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, keymap } from '@codemirror/view';\n\nexport type CommandData = { sentinel: string; description?: string };\n\n/** Pure prefix matcher, exported for tests. */\nexport const matchCommands = (all: CommandData[], token: string): CommandData[] =>\n token.startsWith('$') || token.startsWith('/') ? all.filter(({ sentinel }) => sentinel.startsWith(token)) : [];\n\nexport type CommandsOptions = { getCommands: () => CommandData[] };\n\n/**\n * Command completion: typing `$` offers the bound context's instruction sentinels anywhere;\n * typing `/` at the start of the prompt offers registered slash commands.\n */\nconst commandMark = Decoration.mark({\n class: 'dx-tag',\n // Baseline alignment puts the pill's text on the line's baseline; the negative block margin\n // cancels the padded pill's line-box growth (same recipe as `.dx-tag--anchor` in CodeMirror).\n attributes: {\n 'data-hue': 'blue',\n 'style': 'margin-block: -5px;',\n 'font-family': 'var(--dx-font-mono, monospace)',\n },\n});\n\n/** Decorates a prompt-leading registered command token as a tag. */\nconst commandDecorations = (getCommands: () => CommandData[]) => {\n const build = (view: EditorView): DecorationSet => {\n const match = /^[$/][\\w:-]+/.exec(view.state.doc.line(1).text);\n return match && getCommands().some(({ sentinel }) => sentinel === match[0])\n ? Decoration.set([commandMark.range(0, match[0].length)])\n : Decoration.none;\n };\n return ViewPlugin.fromClass(\n class {\n decorations: DecorationSet;\n constructor(view: EditorView) {\n this.decorations = build(view);\n }\n update(update: ViewUpdate) {\n if (update.docChanged) {\n this.decorations = build(update.view);\n }\n }\n },\n {\n decorations: (instance) => instance.decorations,\n // Atomic: the cursor steps over the tag as one unit and backspace removes it whole.\n provide: (plugin) => EditorView.atomicRanges.of((view) => view.plugin(plugin)?.decorations ?? Decoration.none),\n },\n );\n};\n\n/** Two-column command/description layout; shared column widths via subgrid. */\nconst commandTooltipTheme = EditorView.theme({\n '.cm-tooltip-autocomplete > ul': {\n display: 'grid',\n gridTemplateColumns: 'max-content 1fr',\n columnGap: '0.75rem',\n },\n '.cm-tooltip-autocomplete > ul > li': {\n display: 'grid',\n gridTemplateColumns: 'subgrid',\n gridColumn: 'span 2',\n alignItems: 'center',\n },\n '.cm-tooltip-autocomplete .cm-completionLabel': {\n fontFamily: 'var(--dx-font-mono, monospace)',\n },\n '.cm-tooltip-autocomplete .cm-completionDetail': {\n fontStyle: 'normal',\n fontSize: '0.875rem',\n marginInlineStart: '0',\n },\n});\n\nexport const commands = ({ getCommands }: CommandsOptions): Extension => [\n commandTooltipTheme,\n commandDecorations(getCommands),\n autocompletion({\n // The prompt sits at the bottom of its panel, so the list opens upward.\n aboveCursor: true,\n override: [\n (context: CompletionContext): CompletionResult | null => {\n const word = context.matchBefore(/[$/][\\w:-]*/);\n if (!word || (word.from === word.to && !context.explicit)) {\n return null;\n }\n // Slash commands are prompt-leading only, so a mid-sentence path fragment never completes.\n if (context.state.sliceDoc(word.from, word.from + 1) === '/' && word.from !== 0) {\n return null;\n }\n const options = matchCommands(getCommands(), context.state.sliceDoc(word.from, word.to));\n if (options.length === 0) {\n return null;\n }\n return {\n from: word.from,\n // Accepting appends the separator space, so the arguments can be typed immediately.\n options: options.map(({ sentinel, description }) => ({\n label: sentinel,\n detail: description,\n apply: `${sentinel} `,\n })),\n };\n },\n ],\n }),\n // Arrow navigation stops at the ends of the list rather than cycling.\n Prec.highest(\n keymap.of([\n {\n key: 'ArrowDown',\n run: (view) => {\n if (completionStatus(view.state) !== 'active') {\n return false;\n }\n const index = selectedCompletionIndex(view.state);\n if (index === null) {\n return false;\n }\n if (index < currentCompletions(view.state).length - 1) {\n view.dispatch({ effects: setSelectedCompletion(index + 1) });\n }\n return true;\n },\n },\n {\n key: 'ArrowUp',\n run: (view) => {\n if (completionStatus(view.state) !== 'active') {\n return false;\n }\n const index = selectedCompletionIndex(view.state);\n if (index === null) {\n return false;\n }\n if (index > 0) {\n view.dispatch({ effects: setSelectedCompletion(index - 1) });\n }\n return true;\n },\n },\n ]),\n ),\n // The host editor's `submit()` extension also binds Enter at `Prec.highest`; ties between equal\n // precedences break by extension order, so the caller must place this extension ahead of `submit()`.\n // `completionStatus`/`acceptCompletion` are imported from the same module as `autocompletion()` above\n // (not re-derived in a different package) so this always reads the completion state `autocompletion()`\n // actually wrote — a cross-package import of the same-named API can resolve to a distinct bundled\n // copy of `@codemirror/autocomplete` under some bundler configurations, silently reading `null`.\n Prec.highest(\n keymap.of([\n {\n key: 'Enter',\n run: (view) => (completionStatus(view.state) === 'active' ? acceptCompletion(view) : false),\n },\n ]),\n ),\n];\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type CompletionResult, autocompletion, completionKeymap } from '@codemirror/autocomplete';\nimport { type Extension, RangeSet } from '@codemirror/state';\nimport {\n Decoration,\n type DecorationSet,\n EditorView,\n ViewPlugin,\n type ViewUpdate,\n WidgetType,\n keymap,\n} from '@codemirror/view';\n\nimport { Mutex } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\n\n// TODO(dmaretskyi): Consider adding details renderer for when you hover over the reference.\nexport type ReferenceData = {\n uri: string; // TODO(burdon): Rename dxn.\n label: string;\n};\n\n/**\n * @deprecated\n */\n// TODO(burdon): Remove.\nexport interface ReferencesProvider {\n getReferences({ query }: { query: string }): Promise<ReferenceData[]>;\n resolveReference({ uri }: { uri: string }): Promise<ReferenceData | null>;\n}\n\nexport type ReferencesOptions = {\n provider: ReferencesProvider;\n\n /**\n * @default '@'\n */\n triggerCharacter?: string;\n\n /**\n * Prevent the autocomplete from closing when the user blurs the editor.\n * @default false\n */\n debug?: boolean;\n};\n\n/**\n * Lookup object references.\n * @deprecated\n */\n// TODO(burdon): Remove.\nexport const references = ({ provider, triggerCharacter = '@', debug = false }: ReferencesOptions): Extension => {\n invariant(triggerCharacter.length === 1);\n\n const decorationField = ViewPlugin.fromClass(\n class ReferenceView {\n // TODO(burdon): Why?\n private _mutex = new Mutex();\n\n decorations: DecorationSet = Decoration.set([]);\n\n constructor(view: EditorView) {\n queueMicrotask(async () => {\n const guard = await this._mutex.acquire();\n try {\n this.decorations = await this._computeDecorations(view);\n } finally {\n guard.release();\n }\n });\n }\n\n update(update: ViewUpdate) {\n if (update.docChanged) {\n queueMicrotask(async () => {\n const guard = await this._mutex.acquire();\n try {\n this.decorations = await this._computeDecorations(update.view);\n } finally {\n guard.release();\n }\n });\n }\n }\n\n private async _computeDecorations(view: EditorView): Promise<DecorationSet> {\n const text = view.state.doc.toString();\n const references = text.matchAll(new RegExp(`${triggerCharacter}[a-zA-Z0-9@:]+\\\\s`, 'g'));\n\n const decorations = [];\n for (const match of references) {\n const reference = match[0];\n const uri = reference.slice(1, -1);\n const data = await provider.resolveReference({ uri });\n if (data) {\n decorations.push(\n Decoration.replace({\n widget: new ReferenceWidget(data),\n }).range(match.index!, match.index! + reference.length),\n );\n }\n }\n\n return Decoration.set(decorations);\n }\n },\n {\n decorations: (v) => v.decorations,\n provide: () => [\n EditorView.atomicRanges.of(\n (view): DecorationSet => view.plugin(decorationField)?.decorations ?? RangeSet.empty,\n ),\n ],\n },\n );\n\n return [\n decorationField,\n\n EditorView.theme({\n '.cm-reference-pill': {\n borderRadius: '0.25rem',\n borderWidth: '1px',\n marginRight: '0.25rem',\n marginLeft: '0.25rem',\n },\n }),\n\n autocompletion({\n tooltipClass: () => 'shadow rounded-sm',\n activateOnTyping: true,\n aboveCursor: true,\n closeOnBlur: !debug,\n override: [\n async (context): Promise<CompletionResult | null> => {\n const match = context.matchBefore(new RegExp(`${triggerCharacter}[a-zA-Z0-9]+`));\n if (!match || match?.to === match?.from) {\n return null;\n }\n\n const query = match.text.slice(1);\n const references = await provider.getReferences({ query });\n return {\n from: match.from,\n filter: false,\n options: references.map((reference) => ({\n label: reference.label,\n apply: `${triggerCharacter}${reference.uri} `,\n })),\n };\n },\n ],\n }),\n\n keymap.of(completionKeymap),\n ];\n};\n\nclass ReferenceWidget extends WidgetType {\n constructor(private data: ReferenceData) {\n super();\n }\n\n override ignoreEvent() {\n return true;\n }\n\n override eq(other: this) {\n return this.data.uri === other.data.uri;\n }\n\n override toDOM() {\n const span = document.createElement('span');\n span.textContent = `@${this.data.label}`;\n span.className = 'cm-reference-pill';\n return span;\n }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { useMemo } from 'react';\n\nimport { useThemeContext } from '@dxos/react-ui';\nimport {\n createBasicExtensions,\n createMarkdownExtensions,\n createThemeExtensions,\n decorateMarkdown,\n formattingKeymap,\n submit,\n xmlFormatting,\n} from '@dxos/ui-editor';\nimport { isTruthy } from '@dxos/util';\n\nimport { type ChatEditorProps } from './ChatEditor';\n\n// Kept out of `ChatEditor.tsx`: react-refresh only fast-refreshes a module whose\n// exports are all components, so values exported beside them force a full page reload on\n// every edit.\n\nexport const useChatExtensions = ({\n extensions,\n markdown = false,\n lineWrapping = false,\n placeholder,\n onSubmit,\n}: ChatEditorProps) => {\n const { themeMode } = useThemeContext();\n return useMemo<Extension[]>(\n () =>\n [\n createThemeExtensions({ themeMode, syntaxHighlighting: markdown }),\n createBasicExtensions({ bracketMatching: false, lineWrapping, placeholder }),\n xmlFormatting(),\n markdown && [createMarkdownExtensions(), decorateMarkdown(), formattingKeymap()],\n // Caller extensions (e.g. `commands()`'s completion-aware Enter binding) must precede\n // `submit()`: both bind Enter at `Prec.highest`, and CodeMirror breaks precedence ties by\n // extension order, so listing `submit()` first would always win and swallow the keystroke.\n extensions,\n submit({ onSubmit }),\n ]\n .flat()\n .filter(isTruthy),\n [themeMode, markdown, lineWrapping, placeholder, extensions, onSubmit],\n );\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport React, { forwardRef } from 'react';\n\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { Editor, type EditorController, type UseTextEditorProps } from '@dxos/react-ui-editor';\nimport { type BasicExtensionsOptions, type SubmitOptions } from '@dxos/ui-editor';\n\nimport { type ReferencesOptions } from './references';\nimport { useChatExtensions } from './useChatExtensions';\n\nexport interface ChatEditorController extends EditorController {}\n\nexport type ChatEditorProps = ThemedClassName<\n {\n extensions?: Extension;\n references?: ReferencesOptions;\n /** Enable inline markdown formatting (decoration, syntax highlighting, and formatting shortcuts). */\n markdown?: boolean;\n } & (SubmitOptions &\n Pick<UseTextEditorProps, 'id' | 'autoFocus'> &\n Pick<BasicExtensionsOptions, 'lineWrapping' | 'placeholder'>)\n>;\n\nexport const ChatEditor = forwardRef<ChatEditorController, ChatEditorProps>(\n (\n {\n classNames,\n autoFocus,\n extensions: extensionsProp,\n markdown = false,\n lineWrapping = false,\n placeholder,\n onSubmit,\n },\n forwardRef,\n ) => {\n const extensions = useChatExtensions({ extensions: extensionsProp, markdown, lineWrapping, placeholder, onSubmit });\n\n // TODO(burdon): Popover.\n return (\n <Editor.Root ref={forwardRef}>\n <Editor.View classNames={classNames} autoFocus={autoFocus} extensions={extensions} />\n </Editor.Root>\n );\n },\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\n\n// Kept out of `ChatStatus.tsx`: react-refresh only fast-refreshes a module whose exports are all\n// components, so a context and its hook exported beside them force a full page reload on every edit.\n\n//\n// Context\n//\n\nexport type ChatStatusContextValue = {\n /** Whole seconds elapsed since ChatStatus.Root mounted. Only advances while `running` is true. */\n elapsed: number;\n /** Whether the ChatStatus.Root tick is currently active. Toggled via the ChatStatusController. */\n running: boolean;\n};\n\nexport const [ChatStatusProvider, useChatStatusContext] = createContext<ChatStatusContextValue>('ChatStatus');\n","//\n// Copyright 2025 DXOS.org\n//\n\nconst SECOND = 1_000;\nconst MINUTE = 60 * SECOND;\nconst HOUR = 60 * MINUTE;\n\n/**\n * Formats elapsed milliseconds as a human-readable duration.\n * Tiers: `Ns` (< 60s), `Nm Xs` (< 60m), `Nh Xm` (≥ 60m).\n * Negative inputs clamp to `0s`.\n */\nexport const formatElapsed = (ms: number): string => {\n const safe = Math.max(0, ms);\n if (safe < MINUTE) {\n return `${Math.floor(safe / SECOND)}s`;\n }\n if (safe < HOUR) {\n const m = Math.floor(safe / MINUTE);\n const s = Math.floor((safe % MINUTE) / SECOND);\n return `${m}m ${s}s`;\n }\n const h = Math.floor(safe / HOUR);\n const m = Math.floor((safe % HOUR) / MINUTE);\n return `${h}h ${m}m`;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { type PropsWithChildren, type Ref, forwardRef, useEffect, useImperativeHandle, useState } from 'react';\n\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { ChatStatusProvider, useChatStatusContext } from './ChatStatusContext';\nimport { formatElapsed } from './format';\n\nconst TICK_MS = 1_000;\n\n//\n// Controller\n//\n\nexport type ChatStatusController = {\n /** Resume the tick. No-op if already running. */\n start: () => void;\n /** Pause the tick. The reported `elapsed` freezes at its last value. */\n stop: () => void;\n};\n\n//\n// Root\n//\n\nexport type RootProps = ThemedClassName<\n PropsWithChildren<{\n /** Whether the tick starts running on mount. Defaults to `true`. */\n defaultRunning?: boolean;\n }>\n>;\n\nconst Root = forwardRef<ChatStatusController, RootProps>(\n ({ classNames, children, defaultRunning = true }: RootProps, forwardedRef: Ref<ChatStatusController>) => {\n const [elapsed, setElapsed] = useState(0);\n const [running, setRunning] = useState(defaultRunning);\n\n useEffect(() => {\n if (!running) {\n return;\n }\n\n // Anchor to a wall-clock timestamp so `elapsed` self-corrects after\n // tab-throttling: browsers clamp setTimeout to >=1s in inactive tabs\n // and >=1min when the page is hidden, so a count-the-ticks approach\n // would under-report after a background period. Computing elapsed from\n // (Date.now() - startedAt) restores the true wall-clock value on the\n // next fired tick. The initial elapsed (preserved across stop/start)\n // is folded into the anchor so paused intervals are excluded.\n const startedAt = Date.now() - elapsed * TICK_MS;\n let timeout: ReturnType<typeof setTimeout>;\n const tick = () => {\n setElapsed(Math.floor((Date.now() - startedAt) / TICK_MS));\n timeout = setTimeout(tick, TICK_MS - (Date.now() % TICK_MS));\n };\n timeout = setTimeout(tick, TICK_MS - (Date.now() % TICK_MS));\n return () => clearTimeout(timeout);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `elapsed` is\n // captured once at resume; depending on it would restart the timer on\n // every tick.\n }, [running]);\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n start: () => setRunning(true),\n stop: () => setRunning(false),\n }),\n [],\n );\n\n return (\n <ChatStatusProvider elapsed={elapsed} running={running}>\n <span className={mx('inline-flex items-center gap-2 text-description font-mono tabular-nums', classNames)}>\n {children}\n </span>\n </ChatStatusProvider>\n );\n },\n);\n\nRoot.displayName = 'ChatChatStatus.Root';\n\n//\n// Icon\n//\n\nexport type IconProps = ThemedClassName<PropsWithChildren>;\n\n/**\n * Animated leading indicator. Defaults to a halo-pulse dot. Pass children to override.\n */\nconst Icon = ({ classNames, children }: IconProps) => {\n if (children !== undefined) {\n return <>{children}</>;\n }\n return (\n <span\n aria-hidden='true'\n className={mx('inline-block size-2 rounded-full bg-current animate-halo-pulse', classNames)}\n />\n );\n};\n\n//\n// Stopwatch\n//\n\nexport type StopwatchProps = ThemedClassName<{\n /** Seconds to add to the context elapsed value before formatting. Defaults to 0. */\n offset?: number;\n}>;\n\n/**\n * Elapsed-time display, driven by the ChatStatus.Root context tick.\n * Use `offset` to shift the displayed value; remount `ChatStatus.Root` to reset the underlying counter.\n * Marked `aria-live='off'` so screen readers don't announce every tick.\n */\nconst Stopwatch = ({ classNames, offset = 0 }: StopwatchProps) => {\n const { elapsed } = useChatStatusContext('ChatChatStatus.Stopwatch');\n return (\n <span aria-live='off' className={mx(classNames)}>\n {formatElapsed((elapsed + offset) * 1_000)}\n </span>\n );\n};\n\n//\n// Separator\n//\n\nexport type SeparatorProps = ThemedClassName<unknown>;\n\n/**\n * Middle-dot separator. Decorative — `aria-hidden`.\n */\nconst Separator = ({ classNames }: SeparatorProps) => (\n <span aria-hidden='true' className={mx('opacity-50', classNames)}>\n ·\n </span>\n);\n\n//\n// Text\n//\n\nexport type TextProps = ThemedClassName<PropsWithChildren>;\n\n/**\n * Generic text node — useful for token counts and other inline metadata.\n */\nconst Text = ({ classNames, children }: TextProps) => <span className={mx(classNames)}>{children}</span>;\n\n//\n// ChatStatus\n//\n\nexport const ChatStatus = {\n Root,\n Icon,\n Stopwatch,\n Separator,\n Text,\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useEffect, useState } from 'react';\n\nimport { type ThemedClassName, Tooltip, useTimeout } from '@dxos/react-ui';\n// Leaf import (not the package barrel): the barrel re-exports QueryEditor/QueryForm, which pull the\n// heavy @dxos/ai + @dxos/echo-query stack (tiktoken wasm, etc.) — inappropriate for lean consumers\n// like the browser extension. Importing the Spinner leaf keeps that graph out.\nimport { Spinner, type SpinnerProps } from '@dxos/react-ui-components/Spinner';\nimport { mx } from '@dxos/ui-theme';\n\nconst period = 3_000;\n\nexport type ChatStatusIndicatorProps = ThemedClassName<\n {\n preset?: string;\n processing?: boolean;\n error?: Error;\n } & Pick<SpinnerProps, 'size' | 'onClick'>\n>;\n\nexport const ChatStatusIndicator = ({ classNames, preset, processing, error, ...props }: ChatStatusIndicatorProps) => {\n const [init, setInit] = useState(false);\n useEffect(() => setInit(false), [preset]);\n useTimeout(\n async () => {\n setInit(true);\n },\n period / 2,\n [preset],\n );\n\n return (\n <div className={mx('relative flex', classNames)}>\n <Spinner duration={period} state={!init ? 'flash' : error ? 'error' : processing ? 'spin' : 'pulse'} {...props} />\n {error && (\n <Tooltip.Trigger asChild content={error.message}>\n <div className='absolute inset-0' />\n </Tooltip.Trigger>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -7,44 +7,32 @@ type ChatDialogRootProps = PropsWithChildren<{
7
7
  onExpandedChange?: (expanded: boolean) => void;
8
8
  onEscape?: () => void;
9
9
  }>;
10
- declare function ChatDialogRoot({ children, open: openProp, expanded: expandedProp, onOpenChange, onExpandedChange, onEscape }: ChatDialogRootProps): React.JSX.Element;
11
- declare namespace ChatDialogRoot {
12
- var displayName: string;
13
- }
10
+ declare const ChatDialogRoot: {
11
+ ({ children, open: openProp, expanded: expandedProp, onOpenChange, onExpandedChange, onEscape, }: ChatDialogRootProps): React.JSX.Element;
12
+ displayName: string;
13
+ };
14
14
  type ChatDialogHeaderProps = ThemedClassName<{
15
15
  title?: string;
16
16
  }>;
17
- declare function ChatDialogHeader({ classNames, title }: ChatDialogHeaderProps): React.JSX.Element;
18
- declare namespace ChatDialogHeader {
19
- var displayName: string;
20
- }
17
+ declare const ChatDialogHeader: {
18
+ ({ classNames, title }: ChatDialogHeaderProps): React.JSX.Element;
19
+ displayName: string;
20
+ };
21
21
  type ChatDialogContentProps = ThemedClassName<PropsWithChildren>;
22
- declare function ChatDialogContent({ children, classNames }: ChatDialogContentProps): React.JSX.Element;
23
- declare namespace ChatDialogContent {
24
- var displayName: string;
25
- }
22
+ declare const ChatDialogContent: {
23
+ ({ children, classNames }: ChatDialogContentProps): React.JSX.Element;
24
+ displayName: string;
25
+ };
26
26
  type ChatDialogFooterProps = ThemedClassName<PropsWithChildren>;
27
- declare function ChatDialogFooter({ children, classNames }: ChatDialogFooterProps): React.JSX.Element;
28
- declare namespace ChatDialogFooter {
29
- var displayName: string;
30
- }
27
+ declare const ChatDialogFooter: {
28
+ ({ children, classNames }: ChatDialogFooterProps): React.JSX.Element;
29
+ displayName: string;
30
+ };
31
31
  export declare const ChatDialog: {
32
- Root: {
33
- ({ children, open: openProp, expanded: expandedProp, onOpenChange, onExpandedChange, onEscape, }: ChatDialogRootProps): React.JSX.Element;
34
- displayName: string;
35
- };
36
- Header: {
37
- ({ classNames, title }: ChatDialogHeaderProps): React.JSX.Element;
38
- displayName: string;
39
- };
40
- Content: {
41
- ({ children, classNames }: ChatDialogContentProps): React.JSX.Element;
42
- displayName: string;
43
- };
44
- Footer: {
45
- ({ children, classNames }: ChatDialogFooterProps): React.JSX.Element;
46
- displayName: string;
47
- };
32
+ Root: typeof ChatDialogRoot;
33
+ Header: typeof ChatDialogHeader;
34
+ Content: typeof ChatDialogContent;
35
+ Footer: typeof ChatDialogFooter;
48
36
  };
49
37
  export {};
50
38
  //# sourceMappingURL=ChatDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatDialog.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatDialog/ChatDialog.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAiB,KAAK,iBAAiB,EAA4C,MAAM,OAAO,CAAC;AAE/G,OAAO,EAA4B,KAAK,eAAe,EAAsB,MAAM,gBAAgB,CAAC;AAkCpG,KAAK,mBAAmB,GAAG,iBAAiB,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC,CAAC;gCAEqB,EACtB,QAAQ,EACR,IAAI,EAAE,QAAgB,EACtB,QAAQ,EAAE,YAAoB,EAC9B,YAAY,EACZ,gBAAgB,EAChB,QAAQ,EACT,EAAE,mBAAmB;;;;AA2CtB,KAAK,qBAAqB,GAAG,eAAe,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;kCAEuB,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,qBAAqB;;;;AAwCtE,KAAK,sBAAsB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;mCAEtC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,sBAAsB;;;;AAwC3E,KAAK,qBAAqB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;kCAEtC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,qBAAqB;;;;AAUzE,eAAO,MAAM,UAAU;IACrB,IAAI;0GA9IH,mBAAmB;;;IA+IpB,MAAM;gCAhGyC,qBAAqB;;;IAiGpE,OAAO;mCAvD4C,sBAAsB;;;IAwDzE,MAAM;mCAd4C,qBAAqB;;;CAexE,CAAC"}
1
+ {"version":3,"file":"ChatDialog.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatDialog/ChatDialog.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAiB,KAAK,iBAAiB,EAA4C,MAAM,OAAO,CAAC;AAE/G,OAAO,EAA4B,KAAK,eAAe,EAAsB,MAAM,gBAAgB,CAAC;AAkCpG,KAAK,mBAAmB,GAAG,iBAAiB,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC,CAAC;AAEH,QAAA,MAAM,cAAc;sGAOjB,mBAAmB;;CAiCrB,CAAC;AAUF,KAAK,qBAAqB,GAAG,eAAe,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,QAAA,MAAM,gBAAgB;4BAA2B,qBAAqB;;CA8BrE,CAAC;AAUF,KAAK,sBAAsB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAEjE,QAAA,MAAM,iBAAiB;+BAA8B,sBAAsB;;CA8B1E,CAAC;AAUF,KAAK,qBAAqB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAEhE,QAAA,MAAM,gBAAgB;+BAA8B,qBAAqB;;CAExE,CAAC;AAQF,eAAO,MAAM,UAAU;IACrB,IAAI;IACJ,MAAM;IACN,OAAO;IACP,MAAM;CACP,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatEditor.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/ChatEditor.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAkB,MAAM,OAAO,CAAC;AAYvC,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;CAQyB,CAAC;eAErB,IAAI;AAInB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAKrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAMtB,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,KAsB7B,CAAC;AAkBF,eAAO,MAAM,cAAc,EAAE,KAkB5B,CAAC;AA0BF,eAAO,MAAM,YAAY,EAAE,KA6C1B,CAAC"}
1
+ {"version":3,"file":"ChatEditor.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/ChatEditor.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAkB,MAAM,OAAO,CAAC;AAYvC,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;CAQyB,CAAC;eAErB,IAAI;AAInB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAKrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAMtB,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,KAsB7B,CAAC;AAkBF,eAAO,MAAM,cAAc,EAAE,KAkB5B,CAAC;AA0BF,eAAO,MAAM,YAAY,EAAE,KA8C1B,CAAC"}
@@ -8,8 +8,5 @@ export declare const matchCommands: (all: CommandData[], token: string) => Comma
8
8
  export type CommandsOptions = {
9
9
  getCommands: () => CommandData[];
10
10
  };
11
- /**
12
- * Sentinel-command completion: typing `$` offers the commands defined by the bound context's instructions.
13
- */
14
11
  export declare const commands: ({ getCommands }: CommandsOptions) => Extension;
15
12
  //# sourceMappingURL=commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/commands.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,SAAS,EAAQ,MAAM,mBAAmB,CAAC;AAGzD,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,+CAA+C;AAC/C,eAAO,MAAM,aAAa,QAAS,WAAW,EAAE,SAAS,MAAM,KAAG,WAAW,EACU,CAAC;AAExF,MAAM,MAAM,eAAe,GAAG;IAAE,WAAW,EAAE,MAAM,WAAW,EAAE,CAAA;CAAE,CAAC;AAEnE;;GAEG;AACH,eAAO,MAAM,QAAQ,oBAAqB,eAAe,KAAG,SAiC3D,CAAC"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/commands.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,SAAS,EAAQ,MAAM,mBAAmB,CAAC;AAGzD,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,+CAA+C;AAC/C,eAAO,MAAM,aAAa,QAAS,WAAW,EAAE,SAAS,MAAM,KAAG,WAAW,EACmC,CAAC;AAEjH,MAAM,MAAM,eAAe,GAAG;IAAE,WAAW,EAAE,MAAM,WAAW,EAAE,CAAA;CAAE,CAAC;AAoEnE,eAAO,MAAM,QAAQ,oBAAqB,eAAe,KAAG,SAmF3D,CAAC"}
@@ -11,12 +11,30 @@ export type RootProps = ThemedClassName<PropsWithChildren<{
11
11
  defaultRunning?: boolean;
12
12
  }>>;
13
13
  export type IconProps = ThemedClassName<PropsWithChildren>;
14
+ /**
15
+ * Animated leading indicator. Defaults to a halo-pulse dot. Pass children to override.
16
+ */
17
+ declare const Icon: ({ classNames, children }: IconProps) => React.JSX.Element;
14
18
  export type StopwatchProps = ThemedClassName<{
15
19
  /** Seconds to add to the context elapsed value before formatting. Defaults to 0. */
16
20
  offset?: number;
17
21
  }>;
22
+ /**
23
+ * Elapsed-time display, driven by the ChatStatus.Root context tick.
24
+ * Use `offset` to shift the displayed value; remount `ChatStatus.Root` to reset the underlying counter.
25
+ * Marked `aria-live='off'` so screen readers don't announce every tick.
26
+ */
27
+ declare const Stopwatch: ({ classNames, offset }: StopwatchProps) => React.JSX.Element;
18
28
  export type SeparatorProps = ThemedClassName<unknown>;
29
+ /**
30
+ * Middle-dot separator. Decorative — `aria-hidden`.
31
+ */
32
+ declare const Separator: ({ classNames }: SeparatorProps) => React.JSX.Element;
19
33
  export type TextProps = ThemedClassName<PropsWithChildren>;
34
+ /**
35
+ * Generic text node — useful for token counts and other inline metadata.
36
+ */
37
+ declare const Text: ({ classNames, children }: TextProps) => React.JSX.Element;
20
38
  export declare const ChatStatus: {
21
39
  Root: React.ForwardRefExoticComponent<Omit<PropsWithChildren<{
22
40
  /** Whether the tick starts running on mount. Defaults to `true`. */
@@ -24,9 +42,10 @@ export declare const ChatStatus: {
24
42
  }>, "className"> & {
25
43
  classNames?: import("@dxos/ui-types").ClassNameValue;
26
44
  } & React.RefAttributes<ChatStatusController>>;
27
- Icon: ({ classNames, children }: IconProps) => React.JSX.Element;
28
- Stopwatch: ({ classNames, offset }: StopwatchProps) => React.JSX.Element;
29
- Separator: ({ classNames }: SeparatorProps) => React.JSX.Element;
30
- Text: ({ classNames, children }: TextProps) => React.JSX.Element;
45
+ Icon: typeof Icon;
46
+ Stopwatch: typeof Stopwatch;
47
+ Separator: typeof Separator;
48
+ Text: typeof Text;
31
49
  };
50
+ export {};
32
51
  //# sourceMappingURL=ChatStatus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatStatus.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatStatus/ChatStatus.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAkE,MAAM,OAAO,CAAC;AAEtH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYtD,MAAM,MAAM,oBAAoB,GAAG;IACjC,iDAAiD;IACjD,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,wEAAwE;IACxE,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG,eAAe,CACrC,iBAAiB,CAAC;IAChB,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC,CACH,CAAC;AAyDF,MAAM,MAAM,SAAS,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAqB3D,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;IAC3C,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAoBH,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;AAetD,MAAM,MAAM,SAAS,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAW3D,eAAO,MAAM,UAAU;;QAlInB,oEAAoE;yBACnD,OAAO;;;;qCAgEY,SAAS;wCA0BF,cAAc;gCAkB1B,cAAc;qCAeT,SAAS;CAYhD,CAAC"}
1
+ {"version":3,"file":"ChatStatus.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatStatus/ChatStatus.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAkE,MAAM,OAAO,CAAC;AAEtH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYtD,MAAM,MAAM,oBAAoB,GAAG;IACjC,iDAAiD;IACjD,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,wEAAwE;IACxE,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG,eAAe,CACrC,iBAAiB,CAAC;IAChB,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC,CACH,CAAC;AAyDF,MAAM,MAAM,SAAS,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAE3D;;GAEG;AACH,QAAA,MAAM,IAAI,6BAA8B,SAAS,sBAUhD,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;IAC3C,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH;;;;GAIG;AACH,QAAA,MAAM,SAAS,2BAAgC,cAAc,sBAO5D,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,QAAA,MAAM,SAAS,mBAAoB,cAAc,sBAIhD,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAE3D;;GAEG;AACH,QAAA,MAAM,IAAI,6BAA8B,SAAS,sBAAuD,CAAC;AAMzG,eAAO,MAAM,UAAU;;QAlInB,oEAAoE;yBACnD,OAAO;;;;;;;;CAuI3B,CAAC"}