@dxos/react-ui-chat 0.9.1-staging.ee54ba693a → 0.11.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/lib/index.mjs +414 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/translations.mjs +7 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/types/src/components/ChatEditor/ChatEditor.d.ts +4 -1
- package/dist/types/src/components/ChatEditor/ChatEditor.d.ts.map +1 -1
- package/dist/types/src/components/ChatEditor/ChatEditor.stories.d.ts +6 -2
- package/dist/types/src/components/ChatEditor/ChatEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/ChatEditor/commands.d.ts +15 -0
- package/dist/types/src/components/ChatEditor/commands.d.ts.map +1 -0
- package/dist/types/src/components/ChatEditor/commands.test.d.ts +2 -0
- package/dist/types/src/components/ChatEditor/commands.test.d.ts.map +1 -0
- package/dist/types/src/components/ChatEditor/index.d.ts +2 -0
- package/dist/types/src/components/ChatEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/ChatEditor/useChatExtensions.d.ts +4 -0
- package/dist/types/src/components/ChatEditor/useChatExtensions.d.ts.map +1 -0
- package/dist/types/src/components/ChatStatus/ChatStatus.d.ts +0 -8
- package/dist/types/src/components/ChatStatus/ChatStatus.d.ts.map +1 -1
- package/dist/types/src/components/ChatStatus/ChatStatus.stories.d.ts.map +1 -1
- package/dist/types/src/components/ChatStatus/ChatStatusContext.d.ts +10 -0
- package/dist/types/src/components/ChatStatus/ChatStatusContext.d.ts.map +1 -0
- package/dist/types/src/components/ChatStatus/index.d.ts +1 -0
- package/dist/types/src/components/ChatStatus/index.d.ts.map +1 -1
- package/dist/types/src/components/ChatStatusIndicator/ChatStatusIndicator.d.ts +10 -0
- package/dist/types/src/components/ChatStatusIndicator/ChatStatusIndicator.d.ts.map +1 -0
- package/dist/types/src/components/ChatStatusIndicator/index.d.ts +2 -0
- package/dist/types/src/components/ChatStatusIndicator/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -31
- package/src/components/ChatEditor/ChatEditor.stories.tsx +83 -1
- package/src/components/ChatEditor/ChatEditor.tsx +19 -28
- package/src/components/ChatEditor/commands.test.ts +16 -0
- package/src/components/ChatEditor/commands.ts +59 -0
- package/src/components/ChatEditor/index.ts +3 -0
- package/src/components/ChatEditor/useChatExtensions.ts +51 -0
- package/src/components/ChatStatus/ChatStatus.stories.tsx +2 -1
- package/src/components/ChatStatus/ChatStatus.tsx +1 -16
- package/src/components/ChatStatus/ChatStatusContext.ts +21 -0
- package/src/components/ChatStatus/index.ts +2 -0
- package/src/components/ChatStatusIndicator/ChatStatusIndicator.tsx +45 -0
- package/src/components/ChatStatusIndicator/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/dist/lib/browser/index.mjs +0 -359
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/translations.mjs +0 -14
- package/dist/lib/browser/translations.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -361
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/translations.mjs +0 -16
- package/dist/lib/node-esm/translations.mjs.map +0 -7
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { createContext } from "@radix-ui/react-context";
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
|
3
|
+
import { Dialog, Icon, IconButton, Tooltip, useControlledState, useThemeContext, useTimeout } from "@dxos/react-ui";
|
|
4
|
+
import { ResizeHandle, resizeAttributes, sizeStyle } from "@dxos/react-ui-dnd";
|
|
5
|
+
import { mx } from "@dxos/ui-theme";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { acceptCompletion, autocompletion, completionKeymap, completionStatus } from "@codemirror/autocomplete";
|
|
8
|
+
import { Prec, RangeSet } from "@codemirror/state";
|
|
9
|
+
import { Decoration, EditorView, ViewPlugin, WidgetType, keymap } from "@codemirror/view";
|
|
10
|
+
import { Mutex } from "@dxos/async";
|
|
11
|
+
import { invariant } from "@dxos/invariant";
|
|
12
|
+
import { createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown, formattingKeymap, submit, xmlFormatting } from "@dxos/ui-editor";
|
|
13
|
+
import { isTruthy } from "@dxos/util";
|
|
14
|
+
import { Editor } from "@dxos/react-ui-editor";
|
|
15
|
+
import { Spinner } from "@dxos/react-ui-components/Spinner";
|
|
16
|
+
//#region src/components/ChatDialog/ChatDialog.tsx
|
|
17
|
+
var preventDefault = (event) => event.preventDefault();
|
|
18
|
+
var Endcap = ({ children }) => {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: "grid w-[var(--dx-rail-action)] h-[var(--dx-rail-action)] items-center justify-center",
|
|
21
|
+
children
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
var [ChatDialogContextProvider, useChatDialogContext] = createContext("ChatDialog");
|
|
25
|
+
var ChatDialogRoot = ({ children, open: openProp = false, expanded: expandedProp = false, onOpenChange, onExpandedChange, onEscape }) => {
|
|
26
|
+
const [size, setSize] = useState("min-content");
|
|
27
|
+
const [open, setOpen] = useControlledState(openProp, onOpenChange);
|
|
28
|
+
const [expanded, setExpanded] = useControlledState(expandedProp, onExpandedChange);
|
|
29
|
+
return /* @__PURE__ */ jsx(ChatDialogContextProvider, {
|
|
30
|
+
open,
|
|
31
|
+
setOpen,
|
|
32
|
+
expanded,
|
|
33
|
+
setExpanded,
|
|
34
|
+
size,
|
|
35
|
+
setSize,
|
|
36
|
+
children: /* @__PURE__ */ jsx(Dialog.Root, {
|
|
37
|
+
modal: false,
|
|
38
|
+
open,
|
|
39
|
+
onOpenChange: setOpen,
|
|
40
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
41
|
+
className: "dx-dialog__overlay bg-transparent pointer-events-none",
|
|
42
|
+
"data-block-align": "end",
|
|
43
|
+
children: /* @__PURE__ */ jsx(Dialog.Content, {
|
|
44
|
+
size: "md",
|
|
45
|
+
inOverlayLayout: true,
|
|
46
|
+
classNames: ["grid grid-rows-[var(--dx-rail-action)_1fr_min-content] p-0 overflow-hidden box-content pointer-events-auto"],
|
|
47
|
+
onEscapeKeyDown: onEscape,
|
|
48
|
+
onInteractOutside: preventDefault,
|
|
49
|
+
children
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
ChatDialogRoot.displayName = "ChatDialog.Root";
|
|
56
|
+
var CHAT_DIALOG_HEADER_NAME = "ChatDialog.Header";
|
|
57
|
+
var ChatDialogHeader = ({ classNames, title }) => {
|
|
58
|
+
const { expanded, setExpanded } = useChatDialogContext(CHAT_DIALOG_HEADER_NAME);
|
|
59
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
60
|
+
className: mx("grid grid-cols-[var(--dx-rail-action)_1fr_min-content] items-center overflow-hidden", classNames),
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ jsx(Endcap, { children: /* @__PURE__ */ jsx(Dialog.Close, { children: /* @__PURE__ */ jsx(Icon, { icon: "ph--x--regular" }) }) }),
|
|
63
|
+
/* @__PURE__ */ jsx(Dialog.Title, {
|
|
64
|
+
classNames: "flex w-full justify-center text-sm text-subdued select-none cursor-pointer",
|
|
65
|
+
onClick: () => setExpanded((expanded) => !expanded),
|
|
66
|
+
children: title
|
|
67
|
+
}),
|
|
68
|
+
/* @__PURE__ */ jsx(Endcap, { children: /* @__PURE__ */ jsx(IconButton, {
|
|
69
|
+
variant: "ghost",
|
|
70
|
+
icon: "ph--caret-up--regular",
|
|
71
|
+
iconOnly: true,
|
|
72
|
+
classNames: mx("p-1! [&>svg]:transition [&>svg]:duration-200", expanded && "[&>svg]:rotate-180"),
|
|
73
|
+
label: expanded ? "Close" : "Open",
|
|
74
|
+
onClick: () => setExpanded((expanded) => !expanded)
|
|
75
|
+
}) })
|
|
76
|
+
]
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
ChatDialogHeader.displayName = CHAT_DIALOG_HEADER_NAME;
|
|
80
|
+
var CHAT_DIALOG_CONTENT_NAME = "ChatDialog.Content";
|
|
81
|
+
var ChatDialogContent = ({ children, classNames }) => {
|
|
82
|
+
const { expanded, size, setSize } = useChatDialogContext(CHAT_DIALOG_CONTENT_NAME);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
setSize(expanded ? "min-content" : 0);
|
|
85
|
+
}, [expanded]);
|
|
86
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
87
|
+
className: mx("border-t border-b border-subdued-separator", "transition ease-in-out duration-0 [&:not([data-dx-resizing=\"true\"])]:duration-200", classNames),
|
|
88
|
+
style: {
|
|
89
|
+
...sizeStyle(size, "vertical", true),
|
|
90
|
+
maxBlockSize: "calc(100dvh - env(safe-area-inset-bottom) - env(safe-area-inset-top) - 14rem)"
|
|
91
|
+
},
|
|
92
|
+
...resizeAttributes,
|
|
93
|
+
children: [/* @__PURE__ */ jsx(ResizeHandle, {
|
|
94
|
+
side: "block-start",
|
|
95
|
+
iconPosition: "center",
|
|
96
|
+
defaultSize: "min-content",
|
|
97
|
+
fallbackSize: 0,
|
|
98
|
+
minSize: 0,
|
|
99
|
+
onSizeChange: setSize
|
|
100
|
+
}), children]
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
ChatDialogContent.displayName = CHAT_DIALOG_CONTENT_NAME;
|
|
104
|
+
var CHAT_DIALOG_FOOTER_NAME = "ChatDialog.Footer";
|
|
105
|
+
var ChatDialogFooter = ({ children, classNames }) => {
|
|
106
|
+
return /* @__PURE__ */ jsx("div", {
|
|
107
|
+
className: mx(classNames),
|
|
108
|
+
children
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
ChatDialogFooter.displayName = CHAT_DIALOG_FOOTER_NAME;
|
|
112
|
+
var ChatDialog = {
|
|
113
|
+
Root: ChatDialogRoot,
|
|
114
|
+
Header: ChatDialogHeader,
|
|
115
|
+
Content: ChatDialogContent,
|
|
116
|
+
Footer: ChatDialogFooter
|
|
117
|
+
};
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/components/ChatEditor/commands.ts
|
|
120
|
+
/** Pure prefix matcher, exported for tests. */
|
|
121
|
+
var matchCommands = (all, token) => token.startsWith("$") ? all.filter(({ sentinel }) => sentinel.startsWith(token)) : [];
|
|
122
|
+
/**
|
|
123
|
+
* Sentinel-command completion: typing `$` offers the commands defined by the bound context's instructions.
|
|
124
|
+
*/
|
|
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
|
+
}))
|
|
136
|
+
};
|
|
137
|
+
}] }), Prec.highest(keymap.of([{
|
|
138
|
+
key: "Enter",
|
|
139
|
+
run: (view) => completionStatus(view.state) === "active" ? acceptCompletion(view) : false
|
|
140
|
+
}]))];
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/components/ChatEditor/references.ts
|
|
143
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-chat/src/components/ChatEditor/references.ts";
|
|
144
|
+
/**
|
|
145
|
+
* Lookup object references.
|
|
146
|
+
* @deprecated
|
|
147
|
+
*/
|
|
148
|
+
var references = ({ provider, triggerCharacter = "@", debug = false }) => {
|
|
149
|
+
invariant(triggerCharacter.length === 1, void 0, {
|
|
150
|
+
"~LogMeta": "~LogMeta",
|
|
151
|
+
F: __dxlog_file,
|
|
152
|
+
L: 56,
|
|
153
|
+
S: void 0,
|
|
154
|
+
A: ["triggerCharacter.length === 1", ""]
|
|
155
|
+
});
|
|
156
|
+
const decorationField = ViewPlugin.fromClass(class ReferenceView {
|
|
157
|
+
_mutex = new Mutex();
|
|
158
|
+
decorations = Decoration.set([]);
|
|
159
|
+
constructor(view) {
|
|
160
|
+
queueMicrotask(async () => {
|
|
161
|
+
const guard = await this._mutex.acquire();
|
|
162
|
+
try {
|
|
163
|
+
this.decorations = await this._computeDecorations(view);
|
|
164
|
+
} finally {
|
|
165
|
+
guard.release();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
update(update) {
|
|
170
|
+
if (update.docChanged) queueMicrotask(async () => {
|
|
171
|
+
const guard = await this._mutex.acquire();
|
|
172
|
+
try {
|
|
173
|
+
this.decorations = await this._computeDecorations(update.view);
|
|
174
|
+
} finally {
|
|
175
|
+
guard.release();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async _computeDecorations(view) {
|
|
180
|
+
const references = view.state.doc.toString().matchAll(new RegExp(`${triggerCharacter}[a-zA-Z0-9@:]+\\s`, "g"));
|
|
181
|
+
const decorations = [];
|
|
182
|
+
for (const match of references) {
|
|
183
|
+
const reference = match[0];
|
|
184
|
+
const uri = reference.slice(1, -1);
|
|
185
|
+
const data = await provider.resolveReference({ uri });
|
|
186
|
+
if (data) decorations.push(Decoration.replace({ widget: new ReferenceWidget(data) }).range(match.index, match.index + reference.length));
|
|
187
|
+
}
|
|
188
|
+
return Decoration.set(decorations);
|
|
189
|
+
}
|
|
190
|
+
}, {
|
|
191
|
+
decorations: (v) => v.decorations,
|
|
192
|
+
provide: () => [EditorView.atomicRanges.of((view) => view.plugin(decorationField)?.decorations ?? RangeSet.empty)]
|
|
193
|
+
});
|
|
194
|
+
return [
|
|
195
|
+
decorationField,
|
|
196
|
+
EditorView.theme({ ".cm-reference-pill": {
|
|
197
|
+
borderRadius: "0.25rem",
|
|
198
|
+
borderWidth: "1px",
|
|
199
|
+
marginRight: "0.25rem",
|
|
200
|
+
marginLeft: "0.25rem"
|
|
201
|
+
} }),
|
|
202
|
+
autocompletion({
|
|
203
|
+
tooltipClass: () => "shadow rounded-sm",
|
|
204
|
+
activateOnTyping: true,
|
|
205
|
+
aboveCursor: true,
|
|
206
|
+
closeOnBlur: !debug,
|
|
207
|
+
override: [async (context) => {
|
|
208
|
+
const match = context.matchBefore(new RegExp(`${triggerCharacter}[a-zA-Z0-9]+`));
|
|
209
|
+
if (!match || match?.to === match?.from) return null;
|
|
210
|
+
const query = match.text.slice(1);
|
|
211
|
+
const references = await provider.getReferences({ query });
|
|
212
|
+
return {
|
|
213
|
+
from: match.from,
|
|
214
|
+
filter: false,
|
|
215
|
+
options: references.map((reference) => ({
|
|
216
|
+
label: reference.label,
|
|
217
|
+
apply: `${triggerCharacter}${reference.uri} `
|
|
218
|
+
}))
|
|
219
|
+
};
|
|
220
|
+
}]
|
|
221
|
+
}),
|
|
222
|
+
keymap.of(completionKeymap)
|
|
223
|
+
];
|
|
224
|
+
};
|
|
225
|
+
var ReferenceWidget = class extends WidgetType {
|
|
226
|
+
data;
|
|
227
|
+
constructor(data) {
|
|
228
|
+
super();
|
|
229
|
+
this.data = data;
|
|
230
|
+
}
|
|
231
|
+
ignoreEvent() {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
eq(other) {
|
|
235
|
+
return this.data.uri === other.data.uri;
|
|
236
|
+
}
|
|
237
|
+
toDOM() {
|
|
238
|
+
const span = document.createElement("span");
|
|
239
|
+
span.textContent = `@${this.data.label}`;
|
|
240
|
+
span.className = "cm-reference-pill";
|
|
241
|
+
return span;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/components/ChatEditor/useChatExtensions.ts
|
|
246
|
+
var useChatExtensions = ({ extensions, markdown = false, lineWrapping = false, placeholder, onSubmit }) => {
|
|
247
|
+
const { themeMode } = useThemeContext();
|
|
248
|
+
return useMemo(() => [
|
|
249
|
+
createThemeExtensions({
|
|
250
|
+
themeMode,
|
|
251
|
+
syntaxHighlighting: markdown
|
|
252
|
+
}),
|
|
253
|
+
createBasicExtensions({
|
|
254
|
+
bracketMatching: false,
|
|
255
|
+
lineWrapping,
|
|
256
|
+
placeholder
|
|
257
|
+
}),
|
|
258
|
+
xmlFormatting(),
|
|
259
|
+
markdown && [
|
|
260
|
+
createMarkdownExtensions(),
|
|
261
|
+
decorateMarkdown(),
|
|
262
|
+
formattingKeymap()
|
|
263
|
+
],
|
|
264
|
+
extensions,
|
|
265
|
+
submit({ onSubmit })
|
|
266
|
+
].flat().filter(isTruthy), [
|
|
267
|
+
themeMode,
|
|
268
|
+
markdown,
|
|
269
|
+
lineWrapping,
|
|
270
|
+
placeholder,
|
|
271
|
+
extensions,
|
|
272
|
+
onSubmit
|
|
273
|
+
]);
|
|
274
|
+
};
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/components/ChatEditor/ChatEditor.tsx
|
|
277
|
+
var ChatEditor = forwardRef(({ classNames, autoFocus, extensions: extensionsProp, markdown = false, lineWrapping = false, placeholder, onSubmit }, forwardRef) => {
|
|
278
|
+
const extensions = useChatExtensions({
|
|
279
|
+
extensions: extensionsProp,
|
|
280
|
+
markdown,
|
|
281
|
+
lineWrapping,
|
|
282
|
+
placeholder,
|
|
283
|
+
onSubmit
|
|
284
|
+
});
|
|
285
|
+
return /* @__PURE__ */ jsx(Editor.Root, {
|
|
286
|
+
ref: forwardRef,
|
|
287
|
+
children: /* @__PURE__ */ jsx(Editor.View, {
|
|
288
|
+
classNames,
|
|
289
|
+
autoFocus,
|
|
290
|
+
extensions
|
|
291
|
+
})
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/components/ChatStatus/ChatStatusContext.ts
|
|
296
|
+
var [ChatStatusProvider, useChatStatusContext] = createContext("ChatStatus");
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/components/ChatStatus/format.ts
|
|
299
|
+
var SECOND = 1e3;
|
|
300
|
+
var MINUTE = 60 * SECOND;
|
|
301
|
+
var HOUR = 60 * MINUTE;
|
|
302
|
+
/**
|
|
303
|
+
* Formats elapsed milliseconds as a human-readable duration.
|
|
304
|
+
* Tiers: `Ns` (< 60s), `Nm Xs` (< 60m), `Nh Xm` (≥ 60m).
|
|
305
|
+
* Negative inputs clamp to `0s`.
|
|
306
|
+
*/
|
|
307
|
+
var formatElapsed = (ms) => {
|
|
308
|
+
const safe = Math.max(0, ms);
|
|
309
|
+
if (safe < MINUTE) return `${Math.floor(safe / SECOND)}s`;
|
|
310
|
+
if (safe < HOUR) return `${Math.floor(safe / MINUTE)}m ${Math.floor(safe % MINUTE / SECOND)}s`;
|
|
311
|
+
return `${Math.floor(safe / HOUR)}h ${Math.floor(safe % HOUR / MINUTE)}m`;
|
|
312
|
+
};
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region src/components/ChatStatus/ChatStatus.tsx
|
|
315
|
+
var TICK_MS = 1e3;
|
|
316
|
+
var Root = forwardRef(({ classNames, children, defaultRunning = true }, forwardedRef) => {
|
|
317
|
+
const [elapsed, setElapsed] = useState(0);
|
|
318
|
+
const [running, setRunning] = useState(defaultRunning);
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
if (!running) return;
|
|
321
|
+
const startedAt = Date.now() - elapsed * TICK_MS;
|
|
322
|
+
let timeout;
|
|
323
|
+
const tick = () => {
|
|
324
|
+
setElapsed(Math.floor((Date.now() - startedAt) / TICK_MS));
|
|
325
|
+
timeout = setTimeout(tick, TICK_MS - Date.now() % TICK_MS);
|
|
326
|
+
};
|
|
327
|
+
timeout = setTimeout(tick, TICK_MS - Date.now() % TICK_MS);
|
|
328
|
+
return () => clearTimeout(timeout);
|
|
329
|
+
}, [running]);
|
|
330
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
331
|
+
start: () => setRunning(true),
|
|
332
|
+
stop: () => setRunning(false)
|
|
333
|
+
}), []);
|
|
334
|
+
return /* @__PURE__ */ jsx(ChatStatusProvider, {
|
|
335
|
+
elapsed,
|
|
336
|
+
running,
|
|
337
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
338
|
+
className: mx("inline-flex items-center gap-2 text-description font-mono tabular-nums", classNames),
|
|
339
|
+
children
|
|
340
|
+
})
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
Root.displayName = "ChatChatStatus.Root";
|
|
344
|
+
/**
|
|
345
|
+
* Animated leading indicator. Defaults to a halo-pulse dot. Pass children to override.
|
|
346
|
+
*/
|
|
347
|
+
var Icon$1 = ({ classNames, children }) => {
|
|
348
|
+
if (children !== void 0) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
349
|
+
return /* @__PURE__ */ jsx("span", {
|
|
350
|
+
"aria-hidden": "true",
|
|
351
|
+
className: mx("inline-block size-2 rounded-full bg-current animate-halo-pulse", classNames)
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Elapsed-time display, driven by the ChatStatus.Root context tick.
|
|
356
|
+
* Use `offset` to shift the displayed value; remount `ChatStatus.Root` to reset the underlying counter.
|
|
357
|
+
* Marked `aria-live='off'` so screen readers don't announce every tick.
|
|
358
|
+
*/
|
|
359
|
+
var Stopwatch = ({ classNames, offset = 0 }) => {
|
|
360
|
+
const { elapsed } = useChatStatusContext("ChatChatStatus.Stopwatch");
|
|
361
|
+
return /* @__PURE__ */ jsx("span", {
|
|
362
|
+
"aria-live": "off",
|
|
363
|
+
className: mx(classNames),
|
|
364
|
+
children: formatElapsed((elapsed + offset) * 1e3)
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Middle-dot separator. Decorative — `aria-hidden`.
|
|
369
|
+
*/
|
|
370
|
+
var Separator = ({ classNames }) => /* @__PURE__ */ jsx("span", {
|
|
371
|
+
"aria-hidden": "true",
|
|
372
|
+
className: mx("opacity-50", classNames),
|
|
373
|
+
children: "·"
|
|
374
|
+
});
|
|
375
|
+
/**
|
|
376
|
+
* Generic text node — useful for token counts and other inline metadata.
|
|
377
|
+
*/
|
|
378
|
+
var Text = ({ classNames, children }) => /* @__PURE__ */ jsx("span", {
|
|
379
|
+
className: mx(classNames),
|
|
380
|
+
children
|
|
381
|
+
});
|
|
382
|
+
var ChatStatus = {
|
|
383
|
+
Root,
|
|
384
|
+
Icon: Icon$1,
|
|
385
|
+
Stopwatch,
|
|
386
|
+
Separator,
|
|
387
|
+
Text
|
|
388
|
+
};
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/components/ChatStatusIndicator/ChatStatusIndicator.tsx
|
|
391
|
+
var period = 3e3;
|
|
392
|
+
var ChatStatusIndicator = ({ classNames, preset, processing, error, ...props }) => {
|
|
393
|
+
const [init, setInit] = useState(false);
|
|
394
|
+
useEffect(() => setInit(false), [preset]);
|
|
395
|
+
useTimeout(async () => {
|
|
396
|
+
setInit(true);
|
|
397
|
+
}, period / 2, [preset]);
|
|
398
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
399
|
+
className: mx("relative flex", classNames),
|
|
400
|
+
children: [/* @__PURE__ */ jsx(Spinner, {
|
|
401
|
+
duration: period,
|
|
402
|
+
state: !init ? "flash" : error ? "error" : processing ? "spin" : "pulse",
|
|
403
|
+
...props
|
|
404
|
+
}), error && /* @__PURE__ */ jsx(Tooltip.Trigger, {
|
|
405
|
+
asChild: true,
|
|
406
|
+
content: error.message,
|
|
407
|
+
children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0" })
|
|
408
|
+
})]
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
//#endregion
|
|
412
|
+
export { ChatDialog, ChatEditor, ChatStatus, ChatStatusIndicator, commands, formatElapsed, matchCommands, references, useChatExtensions, useChatStatusContext };
|
|
413
|
+
|
|
414
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translations.mjs","names":[],"sources":["../../src/translations.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-chat';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {},\n },\n },\n] as const satisfies Resource[];\n"],"mappings":";AAMA,IAAa,iBAAiB;AAE9B,IAAa,eAAe,CAC1B,EACE,SAAS,GACN,iBAAiB,CAAC,EACrB,EACF,CACF"}
|
|
@@ -9,11 +9,14 @@ export interface ChatEditorController extends EditorController {
|
|
|
9
9
|
export type ChatEditorProps = ThemedClassName<{
|
|
10
10
|
extensions?: Extension;
|
|
11
11
|
references?: ReferencesOptions;
|
|
12
|
+
/** Enable inline markdown formatting (decoration, syntax highlighting, and formatting shortcuts). */
|
|
13
|
+
markdown?: boolean;
|
|
12
14
|
} & (SubmitOptions & Pick<UseTextEditorProps, 'id' | 'autoFocus'> & Pick<BasicExtensionsOptions, 'lineWrapping' | 'placeholder'>)>;
|
|
13
|
-
export declare const useChatExtensions: ({ extensions, lineWrapping, placeholder, onSubmit }: ChatEditorProps) => Extension[];
|
|
14
15
|
export declare const ChatEditor: React.ForwardRefExoticComponent<Omit<{
|
|
15
16
|
extensions?: Extension;
|
|
16
17
|
references?: ReferencesOptions;
|
|
18
|
+
/** Enable inline markdown formatting (decoration, syntax highlighting, and formatting shortcuts). */
|
|
19
|
+
markdown?: boolean;
|
|
17
20
|
} & SubmitOptions & Pick<UseTextEditorProps, "autoFocus" | "id"> & Pick<BasicExtensionsOptions, "lineWrapping" | "placeholder">, "className"> & {
|
|
18
21
|
classNames?: import("@dxos/ui-types").ClassNameValue;
|
|
19
22
|
} & React.RefAttributes<ChatEditorController>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatEditor.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/ChatEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,
|
|
1
|
+
{"version":3,"file":"ChatEditor.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/ChatEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAU,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAElF,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;CAAG;AAEjE,MAAM,MAAM,eAAe,GAAG,eAAe,CAC3C;IACE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,qGAAqG;IACrG,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,CAAC,aAAa,GAChB,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,WAAW,CAAC,GAC5C,IAAI,CAAC,sBAAsB,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAChE,CAAC;AAEF,eAAO,MAAM,UAAU;iBATN,SAAS;iBACT,iBAAiB;IAC9B,qGAAqG;eAC1F,OAAO;;;8CA4BrB,CAAC"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
declare const meta: {
|
|
3
4
|
title: string;
|
|
4
|
-
component:
|
|
5
|
+
component: React.ForwardRefExoticComponent<Omit<{
|
|
5
6
|
extensions?: import("@codemirror/state").Extension;
|
|
6
7
|
references?: import("./references").ReferencesOptions;
|
|
8
|
+
markdown?: boolean;
|
|
7
9
|
} & import("@dxos/ui-editor").SubmitOptions & Pick<import("@dxos/react-ui-editor").UseTextEditorProps, "autoFocus" | "id"> & Pick<import("@dxos/ui-editor").BasicExtensionsOptions, "lineWrapping" | "placeholder">, "className"> & {
|
|
8
10
|
classNames?: import("@dxos/ui-types").ClassNameValue;
|
|
9
|
-
} &
|
|
11
|
+
} & React.RefAttributes<import("./ChatEditor").ChatEditorController>>;
|
|
10
12
|
decorators: import("@storybook/react").Decorator[];
|
|
11
13
|
parameters: {
|
|
12
14
|
layout: string;
|
|
@@ -20,6 +22,8 @@ declare const meta: {
|
|
|
20
22
|
export default meta;
|
|
21
23
|
type Story = StoryObj<typeof meta>;
|
|
22
24
|
export declare const Default: Story;
|
|
25
|
+
export declare const Markdown: Story;
|
|
23
26
|
export declare const WithSuggestions: Story;
|
|
24
27
|
export declare const WithReferences: Story;
|
|
28
|
+
export declare const WithCommands: Story;
|
|
25
29
|
//# sourceMappingURL=ChatEditor.stories.d.ts.map
|
|
@@ -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;
|
|
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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Extension } from '@codemirror/state';
|
|
2
|
+
export type CommandData = {
|
|
3
|
+
sentinel: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
};
|
|
6
|
+
/** Pure prefix matcher, exported for tests. */
|
|
7
|
+
export declare const matchCommands: (all: CommandData[], token: string) => CommandData[];
|
|
8
|
+
export type CommandsOptions = {
|
|
9
|
+
getCommands: () => CommandData[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Sentinel-command completion: typing `$` offers the commands defined by the bound context's instructions.
|
|
13
|
+
*/
|
|
14
|
+
export declare const commands: ({ getCommands }: CommandsOptions) => Extension;
|
|
15
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.test.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/commands.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAE7B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAE7B,cAAc,qBAAqB,CAAC;AAEpC,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Extension } from '@codemirror/state';
|
|
2
|
+
import { type ChatEditorProps } from './ChatEditor';
|
|
3
|
+
export declare const useChatExtensions: ({ extensions, markdown, lineWrapping, placeholder, onSubmit, }: ChatEditorProps) => Extension[];
|
|
4
|
+
//# sourceMappingURL=useChatExtensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChatExtensions.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatEditor/useChatExtensions.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAenD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAMpD,eAAO,MAAM,iBAAiB,mEAM3B,eAAe,gBAmBjB,CAAC"}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react';
|
|
2
2
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
type ChatStatusContextValue = {
|
|
4
|
-
/** Whole seconds elapsed since ChatStatus.Root mounted. Only advances while `running` is true. */
|
|
5
|
-
elapsed: number;
|
|
6
|
-
/** Whether the ChatStatus.Root tick is currently active. Toggled via the ChatStatusController. */
|
|
7
|
-
running: boolean;
|
|
8
|
-
};
|
|
9
|
-
declare const useChatStatusContext: (consumerName: string) => ChatStatusContextValue;
|
|
10
|
-
export { useChatStatusContext };
|
|
11
3
|
export type ChatStatusController = {
|
|
12
4
|
/** Resume the tick. No-op if already running. */
|
|
13
5
|
start: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatStatus.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatStatus/ChatStatus.tsx"],"names":[],"mappings":"
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatStatus.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatStatus/ChatStatus.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAiB,MAAM,OAAO,CAAC;AAMtC,OAAO,EAAc,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ChatStatus.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ChatStatus/ChatStatus.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAiB,MAAM,OAAO,CAAC;AAMtC,OAAO,EAAc,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGrE,QAAA,MAAM,IAAI;;;;;;;;;QAQN,MAAM;;CAE8B,CAAC;eAE1B,IAAI;AAEnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAOrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAWtB,CAAC;AAsBF,eAAO,MAAM,cAAc,EAAE,KAa5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAWzB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,KAkBxB,CAAC"}
|