@dxos/react-ui-assistant 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.
- package/dist/lib/index.mjs +201 -183
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/testing.mjs +7 -0
- package/dist/lib/testing.mjs.map +1 -1
- package/dist/lib/translations.mjs +21 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +21 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/components/ChatThread/ChatThread.d.ts +27 -26
- package/dist/types/src/components/ChatThread/ChatThread.d.ts.map +1 -1
- package/dist/types/src/components/ChatThread/ChatThread.stories.d.ts.map +1 -1
- package/dist/types/src/components/MessageChrome/MessageChrome.d.ts +8 -1
- package/dist/types/src/components/MessageChrome/MessageChrome.d.ts.map +1 -1
- package/dist/types/src/components/MessageChrome/MessageChrome.stories.d.ts +58 -0
- package/dist/types/src/components/MessageChrome/MessageChrome.stories.d.ts.map +1 -0
- package/dist/types/src/components/MessageChrome/format-time.d.ts +12 -0
- package/dist/types/src/components/MessageChrome/format-time.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/registry.d.ts.map +1 -1
- package/dist/types/src/registry.stories.d.ts +55 -0
- package/dist/types/src/registry.stories.d.ts.map +1 -0
- package/dist/types/src/renderer.d.ts.map +1 -1
- package/dist/types/src/testing/test-generator.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/widgets/FallbackWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/ReasoningWidget.d.ts.map +1 -0
- package/dist/types/src/{components/widgets → widgets}/ReferenceWidget.d.ts +1 -1
- package/dist/types/src/widgets/ReferenceWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/SelectWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/StatsWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/StatusWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/SuggestionWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/SummaryWidget.d.ts.map +1 -0
- package/dist/types/src/widgets/ToolWidget.d.ts.map +1 -0
- package/dist/types/src/{components/widgets → widgets}/index.d.ts +0 -1
- package/dist/types/src/widgets/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -17
- package/src/components/ChatThread/ChatThread.stories.tsx +27 -7
- package/src/components/ChatThread/ChatThread.tsx +35 -37
- package/src/components/MessageChrome/MessageChrome.stories.tsx +135 -0
- package/src/components/MessageChrome/MessageChrome.tsx +91 -18
- package/src/components/{widgets → MessageChrome}/format-time.ts +19 -3
- package/src/components/index.ts +0 -1
- package/src/registry.stories.tsx +151 -0
- package/src/registry.tsx +10 -1
- package/src/renderer.ts +8 -5
- package/src/testing/test-generator.ts +14 -0
- package/src/translations.ts +2 -0
- package/src/types.ts +3 -0
- package/src/{components/widgets → widgets}/ReasoningWidget.ts +22 -26
- package/src/{components/widgets → widgets}/ReferenceWidget.ts +4 -4
- package/src/{components/widgets → widgets}/SelectWidget.ts +1 -3
- package/src/{components/widgets → widgets}/StatsWidget.ts +1 -3
- package/src/widgets/StatusWidget.ts +49 -0
- package/src/{components/widgets → widgets}/SuggestionWidget.ts +2 -4
- package/src/{components/widgets → widgets}/SummaryWidget.tsx +2 -3
- package/src/{components/widgets → widgets}/ToolWidget.tsx +69 -58
- package/src/{components/widgets → widgets}/index.ts +0 -1
- package/dist/types/src/components/widgets/FallbackWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/ReasoningWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/ReasoningWidget.stories.d.ts +0 -18
- package/dist/types/src/components/widgets/ReasoningWidget.stories.d.ts.map +0 -1
- package/dist/types/src/components/widgets/ReferenceWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/SelectWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/StatsWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/StatusWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/SuggestionWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/SummaryWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/ToolWidget.d.ts.map +0 -1
- package/dist/types/src/components/widgets/ToolWidget.stories.d.ts +0 -31
- package/dist/types/src/components/widgets/ToolWidget.stories.d.ts.map +0 -1
- package/dist/types/src/components/widgets/defaults.d.ts +0 -11
- package/dist/types/src/components/widgets/defaults.d.ts.map +0 -1
- package/dist/types/src/components/widgets/format-time.d.ts +0 -2
- package/dist/types/src/components/widgets/format-time.d.ts.map +0 -1
- package/dist/types/src/components/widgets/index.d.ts.map +0 -1
- package/src/components/widgets/ReasoningWidget.stories.tsx +0 -68
- package/src/components/widgets/StatusWidget.ts +0 -53
- package/src/components/widgets/ToolWidget.stories.tsx +0 -55
- package/src/components/widgets/defaults.ts +0 -14
- /package/dist/types/src/{components/widgets → widgets}/FallbackWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/ReasoningWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/SelectWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/StatsWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/StatusWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/SuggestionWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/SummaryWidget.d.ts +0 -0
- /package/dist/types/src/{components/widgets → widgets}/ToolWidget.d.ts +0 -0
- /package/src/{components/widgets → widgets}/FallbackWidget.tsx +0 -0
package/dist/lib/index.mjs
CHANGED
|
@@ -1,43 +1,19 @@
|
|
|
1
|
+
import { translationKey, translations } from "./translations.mjs";
|
|
2
|
+
import { ChatView } from "./types.mjs";
|
|
1
3
|
import { createContext } from "@radix-ui/react-context";
|
|
2
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { MessageList, useMessageList } from "@dxos/react-ui-feed";
|
|
5
|
+
import { MessageList, isPrompt } from "@dxos/react-ui-feed";
|
|
5
6
|
import { AnchorWidget, getXmlTextChild } from "@dxos/ui-editor";
|
|
6
|
-
import { NumericTabs,
|
|
7
|
+
import { NumericTabs, TogglePanel } from "@dxos/react-ui-components";
|
|
7
8
|
import { JsonHighlighter } from "@dxos/react-ui-syntax-highlighter";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
import { WidgetType } from "@codemirror/view";
|
|
10
11
|
import { Domino, mx } from "@dxos/ui";
|
|
11
|
-
import {
|
|
12
|
+
import { Icon, IconBlock, IconButton, TextCrawl, useTranslation } from "@dxos/react-ui";
|
|
13
|
+
import { safeParseJson } from "@dxos/util";
|
|
12
14
|
import { Message } from "@dxos/types";
|
|
13
15
|
import { mx as mx$1 } from "@dxos/ui-theme";
|
|
14
|
-
|
|
15
|
-
//#region src/components/widgets/format-time.ts
|
|
16
|
-
/**
|
|
17
|
-
* Relative ("5 min ago", "yesterday") within two days, absolute date beyond that. The exact
|
|
18
|
-
* timestamp is always available via the `title` tooltip.
|
|
19
|
-
*
|
|
20
|
-
* Recent prompts are the ones a reader places by elapsed time; older ones by date.
|
|
21
|
-
*/
|
|
22
|
-
var DAY_MS = 864e5;
|
|
23
|
-
var formatTime = (created, now = Date.now()) => {
|
|
24
|
-
const date = new Date(created);
|
|
25
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
26
|
-
const elapsed = now - date.getTime();
|
|
27
|
-
if (elapsed < 0 || elapsed >= 2 * DAY_MS) return date.toLocaleDateString(void 0, {
|
|
28
|
-
month: "short",
|
|
29
|
-
day: "numeric"
|
|
30
|
-
});
|
|
31
|
-
const format = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
32
|
-
const minutes = Math.round(elapsed / 6e4);
|
|
33
|
-
if (minutes < 1) return format.format(-Math.max(1, Math.round(elapsed / 1e3)), "second");
|
|
34
|
-
if (minutes < 60) return format.format(-minutes, "minute");
|
|
35
|
-
const hours = Math.round(minutes / 60);
|
|
36
|
-
if (hours < 24) return format.format(-hours, "hour");
|
|
37
|
-
return format.format(-Math.round(hours / 24), "day");
|
|
38
|
-
};
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/components/widgets/FallbackWidget.tsx
|
|
16
|
+
//#region src/widgets/FallbackWidget.tsx
|
|
41
17
|
var FallbackWidget = ({ _tag, ...props }) => {
|
|
42
18
|
return /* @__PURE__ */ jsx(TogglePanel.Root, { children: /* @__PURE__ */ jsxs(TogglePanel.Content, { children: [/* @__PURE__ */ jsx(TogglePanel.Header, {
|
|
43
19
|
classNames: "dx-group-surface",
|
|
@@ -52,19 +28,7 @@ var FallbackWidget = ({ _tag, ...props }) => {
|
|
|
52
28
|
};
|
|
53
29
|
FallbackWidget.displayName = "Fallback";
|
|
54
30
|
//#endregion
|
|
55
|
-
//#region src/
|
|
56
|
-
var styles = {
|
|
57
|
-
/**
|
|
58
|
-
* Vertical rhythm for block widgets. Padding, never margin: CodeMirror measures a widget from its own
|
|
59
|
-
* box, so a margin sits outside what it can see and its height model drifts from what is rendered —
|
|
60
|
-
* the error accumulates down the document until the turn-fold gutter markers are visibly misaligned.
|
|
61
|
-
* Note padding does not collapse between adjacent siblings the way margins do
|
|
62
|
-
*/
|
|
63
|
-
padding: "pt-2 pb-4",
|
|
64
|
-
border: "border border-subdued-separator rounded-sm"
|
|
65
|
-
};
|
|
66
|
-
//#endregion
|
|
67
|
-
//#region src/components/widgets/ReasoningWidget.ts
|
|
31
|
+
//#region src/widgets/ReasoningWidget.ts
|
|
68
32
|
/**
|
|
69
33
|
* AI reasoning widget.
|
|
70
34
|
*/
|
|
@@ -82,7 +46,7 @@ var ReasoningWidget = class extends WidgetType {
|
|
|
82
46
|
return this.text === other.text && this.#pos === other.#pos;
|
|
83
47
|
}
|
|
84
48
|
toDOM() {
|
|
85
|
-
return Domino.of("div").
|
|
49
|
+
return Domino.of("div").append(Domino.of("div").classNames("relative overflow-hidden p-px border border-subdued-separator rounded-md").attributes({ "data-trail-container": "" }).append(Domino.of("div").classNames("relative z-10 dx-base-surface rounded-md text-sm text-subdued py-1", "grid grid-cols-[24px_1fr] gap-x-0.5 items-start").append(Domino.of("div").classNames("flex h-5 w-full shrink-0 items-center justify-center self-start").append(Domino.svg("ph--brain--regular").classNames("shrink-0 size-4 opacity-70")), Domino.of("div").classNames("px-2 max-h-[5lh] overflow-y-auto dx-scrollbar-thin min-w-0").text(this.text).attributes({ "data-reasoning-text": "" })), Domino.of("div").attributes({ "data-id": this.#pos }))).root;
|
|
86
50
|
}
|
|
87
51
|
updateDOM(dom) {
|
|
88
52
|
dom.querySelector("[data-reasoning-text]")?.replaceChildren(this.text);
|
|
@@ -134,7 +98,7 @@ var TRAIL_REMOVAL_DELAY_MS = 1e3;
|
|
|
134
98
|
var trailRemovalTimers = /* @__PURE__ */ new Map();
|
|
135
99
|
var createTrailLayers = () => [Domino.of("div").classNames(...trail, "animate-trail").root, Domino.of("div").classNames(...trail, "animate-trail-offset").root];
|
|
136
100
|
//#endregion
|
|
137
|
-
//#region src/
|
|
101
|
+
//#region src/widgets/ReferenceWidget.ts
|
|
138
102
|
var ReferenceWidget = class extends WidgetType {
|
|
139
103
|
text;
|
|
140
104
|
dxn;
|
|
@@ -147,11 +111,11 @@ var ReferenceWidget = class extends WidgetType {
|
|
|
147
111
|
return this.dxn === other.dxn;
|
|
148
112
|
}
|
|
149
113
|
toDOM() {
|
|
150
|
-
return Domino.of("
|
|
114
|
+
return Domino.of("span").classNames("inline-flex align-baseline").append(Domino.of("dx-anchor").classNames("dx-tag--anchor").attributes({ dxn: this.dxn }).text(this.text)).root;
|
|
151
115
|
}
|
|
152
116
|
};
|
|
153
117
|
//#endregion
|
|
154
|
-
//#region src/
|
|
118
|
+
//#region src/widgets/SelectWidget.ts
|
|
155
119
|
/**
|
|
156
120
|
* Simple prompt widget.
|
|
157
121
|
*/
|
|
@@ -168,7 +132,7 @@ var SelectWidget = class extends WidgetType {
|
|
|
168
132
|
* NOTE: Container must set var based on user's identity.
|
|
169
133
|
*/
|
|
170
134
|
toDOM() {
|
|
171
|
-
return Domino.of("div").attributes({ role: "group" }).classNames(
|
|
135
|
+
return Domino.of("div").attributes({ role: "group" }).classNames("flex flex-wrap gap-1").append(...this.options.map((option) => Domino.of("button").classNames("dx-button inline-block max-w-[100cqi]").attributes({
|
|
172
136
|
"data-action": "submit",
|
|
173
137
|
"data-value": option,
|
|
174
138
|
"data-density": "md"
|
|
@@ -176,7 +140,7 @@ var SelectWidget = class extends WidgetType {
|
|
|
176
140
|
}
|
|
177
141
|
};
|
|
178
142
|
//#endregion
|
|
179
|
-
//#region src/
|
|
143
|
+
//#region src/widgets/StatsWidget.ts
|
|
180
144
|
/**
|
|
181
145
|
* Simple stats widget.
|
|
182
146
|
*/
|
|
@@ -190,7 +154,7 @@ var StatsWidget = class extends WidgetType {
|
|
|
190
154
|
return this.text === other.text;
|
|
191
155
|
}
|
|
192
156
|
toDOM() {
|
|
193
|
-
return Domino.of("div").classNames(
|
|
157
|
+
return Domino.of("div").classNames("text-sm text-placeholder").text(this.text).root;
|
|
194
158
|
}
|
|
195
159
|
updateDOM(dom) {
|
|
196
160
|
dom.textContent = this.text;
|
|
@@ -198,7 +162,7 @@ var StatsWidget = class extends WidgetType {
|
|
|
198
162
|
}
|
|
199
163
|
};
|
|
200
164
|
//#endregion
|
|
201
|
-
//#region src/
|
|
165
|
+
//#region src/widgets/StatusWidget.ts
|
|
202
166
|
/**
|
|
203
167
|
* AI status widget.
|
|
204
168
|
*/
|
|
@@ -212,7 +176,7 @@ var StatusWidget = class extends WidgetType {
|
|
|
212
176
|
return this.text === other.text;
|
|
213
177
|
}
|
|
214
178
|
toDOM() {
|
|
215
|
-
return Domino.of("div").
|
|
179
|
+
return Domino.of("div").append(Domino.of("div").classNames("relative overflow-hidden rounded-md").append(Domino.of("div").classNames("grid grid-cols-[24px_1fr] gap-x-0.5 gap-y-0 items-start px-0.5 py-0.5 text-placeholder").append(Domino.of("div").classNames("flex h-5 w-full shrink-0 items-center justify-center self-start").append(Domino.svg("ph--info--regular").classNames("shrink-0 size-4 opacity-70")), Domino.of("div").classNames("relative min-w-0").append(Domino.of("div").classNames("relative z-10 rounded-md text-sm leading-5").attributes({ "data-status-text": "" }).text(this.text))))).root;
|
|
216
180
|
}
|
|
217
181
|
updateDOM(dom) {
|
|
218
182
|
dom.querySelector("[data-status-text]")?.replaceChildren(this.text);
|
|
@@ -220,7 +184,7 @@ var StatusWidget = class extends WidgetType {
|
|
|
220
184
|
}
|
|
221
185
|
};
|
|
222
186
|
//#endregion
|
|
223
|
-
//#region src/
|
|
187
|
+
//#region src/widgets/SuggestionWidget.ts
|
|
224
188
|
/**
|
|
225
189
|
* Simple prompt widget.
|
|
226
190
|
*/
|
|
@@ -234,7 +198,7 @@ var SuggestionWidget = class extends WidgetType {
|
|
|
234
198
|
return this.text === other.text;
|
|
235
199
|
}
|
|
236
200
|
toDOM() {
|
|
237
|
-
return Domino.of("span").classNames(mx("inline-flex max-w-[calc(100cqi-8px)]
|
|
201
|
+
return Domino.of("span").classNames(mx("inline-flex max-w-[calc(100cqi-8px)] overflow-hidden")).append(Domino.of("button").attributes({
|
|
238
202
|
"data-density": "md",
|
|
239
203
|
"data-action": "submit",
|
|
240
204
|
"data-value": this.text
|
|
@@ -242,27 +206,11 @@ var SuggestionWidget = class extends WidgetType {
|
|
|
242
206
|
}
|
|
243
207
|
};
|
|
244
208
|
//#endregion
|
|
245
|
-
//#region src/
|
|
246
|
-
var translationKey = "@dxos/react-ui-assistant";
|
|
247
|
-
var translations = [{ "en-US": { [translationKey]: {
|
|
248
|
-
"copy.label": "Copy",
|
|
249
|
-
"rewind.label": "Rewind to this prompt",
|
|
250
|
-
"summary.label": "Summary",
|
|
251
|
-
"stats.label": "Stats",
|
|
252
|
-
"tool-call.label": "Calling",
|
|
253
|
-
"tool-result.label": "Result",
|
|
254
|
-
"tool-error.label": "Tool error",
|
|
255
|
-
"nav-first.label": "First message",
|
|
256
|
-
"nav-previous.label": "Previous message",
|
|
257
|
-
"nav-next.label": "Next message",
|
|
258
|
-
"nav-last.label": "Last message"
|
|
259
|
-
} } }];
|
|
260
|
-
//#endregion
|
|
261
|
-
//#region src/components/widgets/SummaryWidget.tsx
|
|
209
|
+
//#region src/widgets/SummaryWidget.tsx
|
|
262
210
|
var SummaryWidget = ({ children }) => {
|
|
263
211
|
const { t } = useTranslation(translationKey);
|
|
264
212
|
return /* @__PURE__ */ jsx(TogglePanel.Root, { children: /* @__PURE__ */ jsxs(TogglePanel.Content, {
|
|
265
|
-
classNames:
|
|
213
|
+
classNames: "border border-subdued-separator rounded-md",
|
|
266
214
|
children: [/* @__PURE__ */ jsx(TogglePanel.Header, {
|
|
267
215
|
classNames: "text-sm dx-group-surface",
|
|
268
216
|
children: t("summary.label")
|
|
@@ -273,7 +221,7 @@ var SummaryWidget = ({ children }) => {
|
|
|
273
221
|
}) });
|
|
274
222
|
};
|
|
275
223
|
//#endregion
|
|
276
|
-
//#region src/
|
|
224
|
+
//#region src/widgets/ToolWidget.tsx
|
|
277
225
|
/**
|
|
278
226
|
* A run of tool blocks as one collapsible panel with a tab per call. The `<toolkit>` tag carries
|
|
279
227
|
* the run as JSON — the item is rebuilt from its message alone, so there is no widget-state side
|
|
@@ -288,51 +236,58 @@ var ToolWidget = ({ view, children }) => {
|
|
|
288
236
|
const items = useMemo(() => {
|
|
289
237
|
let lastToolCall;
|
|
290
238
|
const tools = [];
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
239
|
+
const items = [];
|
|
240
|
+
const indexByToolCallId = /* @__PURE__ */ new Map();
|
|
241
|
+
const push = (id, item) => {
|
|
242
|
+
const existing = id !== void 0 ? indexByToolCallId.get(id) : void 0;
|
|
243
|
+
if (existing !== void 0) items[existing] = item;
|
|
244
|
+
else {
|
|
245
|
+
if (id !== void 0) indexByToolCallId.set(id, items.length);
|
|
246
|
+
items.push(item);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
for (const block of blocks) switch (block._tag) {
|
|
250
|
+
case "toolCall": {
|
|
251
|
+
const tool = tools.find((tool) => tool.name === block.name);
|
|
252
|
+
lastToolCall = {
|
|
253
|
+
tool,
|
|
254
|
+
block
|
|
255
|
+
};
|
|
256
|
+
push(block.toolCallId, {
|
|
257
|
+
title: tool?.description ?? [t("tool-call.label"), block.name].filter(Boolean).join(" "),
|
|
258
|
+
icon: block.operationIcon,
|
|
259
|
+
content: safeParseJson(block.input) ?? (block.input || {})
|
|
260
|
+
});
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
case "toolResult": {
|
|
264
|
+
if (block.error) {
|
|
265
|
+
push(void 0, {
|
|
311
266
|
title: t("tool-error.label"),
|
|
312
267
|
icon: lastToolCall?.block.operationIcon,
|
|
313
|
-
content: block
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const icon = lastToolCall?.block.operationIcon;
|
|
317
|
-
lastToolCall = void 0;
|
|
318
|
-
return {
|
|
319
|
-
title,
|
|
320
|
-
icon,
|
|
321
|
-
content: {
|
|
322
|
-
...block,
|
|
323
|
-
result: typeof block.result === "string" ? safeParseJson(block.result) : block.result
|
|
324
|
-
}
|
|
325
|
-
};
|
|
268
|
+
content: block.error
|
|
269
|
+
});
|
|
270
|
+
break;
|
|
326
271
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
272
|
+
const title = lastToolCall?.tool?.description ?? [t("tool-result.label"), lastToolCall?.block.name].filter(Boolean).join(" ");
|
|
273
|
+
const icon = lastToolCall?.block.operationIcon;
|
|
274
|
+
lastToolCall = void 0;
|
|
275
|
+
push(void 0, {
|
|
276
|
+
title,
|
|
277
|
+
icon,
|
|
278
|
+
content: typeof block.result === "string" ? safeParseJson(block.result) ?? block.result : block.result
|
|
279
|
+
});
|
|
280
|
+
break;
|
|
334
281
|
}
|
|
335
|
-
|
|
282
|
+
case "stats":
|
|
283
|
+
if (!lastToolCall) break;
|
|
284
|
+
push(void 0, {
|
|
285
|
+
title: t("stats.label"),
|
|
286
|
+
icon: lastToolCall.block.operationIcon,
|
|
287
|
+
content: block
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return items;
|
|
336
291
|
}, [blocks, t]);
|
|
337
292
|
const handleChangeOpen = useCallback(() => {
|
|
338
293
|
setTimeout(() => {
|
|
@@ -553,7 +508,7 @@ var isBlockVisible = (viewType, block) => {
|
|
|
553
508
|
var blockToMarkdown = (message, block, getObjectLabel) => {
|
|
554
509
|
switch (block._tag) {
|
|
555
510
|
case "text":
|
|
556
|
-
if (message.sender.role === "user") return block.disposition === "synthetic" ?
|
|
511
|
+
if (message.sender.role === "user") return block.disposition === "synthetic" ? void 0 : tag("prompt", block.text, block);
|
|
557
512
|
return block.text.trim() || void 0;
|
|
558
513
|
case "reasoning": return tag("reasoning", block.reasoningText ?? block.redactedText ?? "", block);
|
|
559
514
|
case "status": return tag("status", block.statusText, block);
|
|
@@ -605,6 +560,10 @@ var estimateRow = (message) => {
|
|
|
605
560
|
let height = ROW_CHROME;
|
|
606
561
|
for (const block of message.blocks) switch (block._tag) {
|
|
607
562
|
case "text":
|
|
563
|
+
if (block.disposition === "synthetic") {
|
|
564
|
+
height += PANEL_HEIGHT;
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
608
567
|
for (const paragraph of block.text.split("\n\n")) height += Math.max(1, Math.ceil(paragraph.length / LINE_CHARS)) * LINE_HEIGHT;
|
|
609
568
|
break;
|
|
610
569
|
case "suggestion":
|
|
@@ -620,9 +579,42 @@ var estimateRow = (message) => {
|
|
|
620
579
|
return height;
|
|
621
580
|
};
|
|
622
581
|
//#endregion
|
|
582
|
+
//#region src/components/MessageChrome/format-time.ts
|
|
583
|
+
/**
|
|
584
|
+
* Relative ("5 min ago", "yesterday") within two days, absolute date beyond that. The exact
|
|
585
|
+
* timestamp is always available via the `title` tooltip.
|
|
586
|
+
*
|
|
587
|
+
* Recent prompts are the ones a reader places by elapsed time; older ones by date.
|
|
588
|
+
*/
|
|
589
|
+
var DAY_MS = 864e5;
|
|
590
|
+
/**
|
|
591
|
+
* Relative time for the message toolbars: `justNow` (caller-translated) under a minute, relative
|
|
592
|
+
* ("5 min ago", "yesterday") within two days, an absolute date beyond that. `now` defaults to the
|
|
593
|
+
* wall clock; an invalid timestamp renders as the empty string.
|
|
594
|
+
*/
|
|
595
|
+
var formatTime = (created, { now = Date.now(), justNow } = {}) => {
|
|
596
|
+
const date = new Date(created);
|
|
597
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
598
|
+
const elapsed = now - date.getTime();
|
|
599
|
+
if (elapsed < 0 || elapsed >= 2 * DAY_MS) return date.toLocaleDateString(void 0, {
|
|
600
|
+
month: "short",
|
|
601
|
+
day: "numeric"
|
|
602
|
+
});
|
|
603
|
+
const format = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
604
|
+
if (elapsed < 6e4) {
|
|
605
|
+
if (justNow) return justNow;
|
|
606
|
+
return format.format(-Math.max(1, Math.round(elapsed / 1e3)), "second");
|
|
607
|
+
}
|
|
608
|
+
const minutes = Math.round(elapsed / 6e4);
|
|
609
|
+
if (minutes < 60) return format.format(-minutes, "minute");
|
|
610
|
+
const hours = Math.round(minutes / 60);
|
|
611
|
+
if (hours < 24) return format.format(-hours, "hour");
|
|
612
|
+
return format.format(-Math.round(hours / 24), "day");
|
|
613
|
+
};
|
|
614
|
+
//#endregion
|
|
623
615
|
//#region src/components/MessageChrome/MessageChrome.tsx
|
|
624
616
|
var MESSAGE_CHROME_NAME = "MessageChrome";
|
|
625
|
-
var [MessageChromeProvider, useMessageChromeContext] = createContext(MESSAGE_CHROME_NAME);
|
|
617
|
+
var [MessageChromeProvider, useMessageChromeContext] = createContext(MESSAGE_CHROME_NAME, {});
|
|
626
618
|
/** Copies the message's extracted text — the model's truth, not the DOM's partial render. */
|
|
627
619
|
var CopyButton = ({ message }) => {
|
|
628
620
|
const { t } = useTranslation(translationKey);
|
|
@@ -635,11 +627,23 @@ var CopyButton = ({ message }) => {
|
|
|
635
627
|
onClick: () => void navigator.clipboard?.writeText(Message.extractText(message))
|
|
636
628
|
});
|
|
637
629
|
};
|
|
638
|
-
var
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
630
|
+
var MessageId = ({ message }) => {
|
|
631
|
+
const { debug } = useMessageChromeContext("MessageId");
|
|
632
|
+
if (!debug) return null;
|
|
633
|
+
return /* @__PURE__ */ jsx("span", {
|
|
634
|
+
className: "font-mono text-subdued",
|
|
635
|
+
title: message.id,
|
|
636
|
+
children: message.id.slice(-8)
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
var Time = ({ message }) => {
|
|
640
|
+
const { t } = useTranslation(translationKey);
|
|
641
|
+
return /* @__PURE__ */ jsx("time", {
|
|
642
|
+
dateTime: message.created,
|
|
643
|
+
title: new Date(message.created).toLocaleString(),
|
|
644
|
+
children: formatTime(message.created, { justNow: t("just-now.label") })
|
|
645
|
+
});
|
|
646
|
+
};
|
|
643
647
|
/**
|
|
644
648
|
* The controls under the reader's own prompt: copy, rewind (edit-and-resend), and when it was
|
|
645
649
|
* sent. Revealed on hover by the chrome, but never removed from flow — chrome that appears and
|
|
@@ -663,7 +667,8 @@ var PromptToolbar = ({ classNames, message }) => {
|
|
|
663
667
|
"data-testid": "chat.rewind",
|
|
664
668
|
onClick: () => onRewind(message.id)
|
|
665
669
|
}),
|
|
666
|
-
|
|
670
|
+
/* @__PURE__ */ jsx(Time, { message }),
|
|
671
|
+
/* @__PURE__ */ jsx(MessageId, { message })
|
|
667
672
|
]
|
|
668
673
|
});
|
|
669
674
|
};
|
|
@@ -673,14 +678,51 @@ var AssistantToolbar = ({ classNames, message }) => {
|
|
|
673
678
|
return /* @__PURE__ */ jsxs("div", {
|
|
674
679
|
role: "toolbar",
|
|
675
680
|
className: mx$1("flex items-center gap-1 text-xs text-description", classNames),
|
|
676
|
-
children: [
|
|
681
|
+
children: [
|
|
682
|
+
/* @__PURE__ */ jsx(CopyButton, { message }),
|
|
683
|
+
/* @__PURE__ */ jsx(Time, { message }),
|
|
684
|
+
/* @__PURE__ */ jsx(MessageId, { message })
|
|
685
|
+
]
|
|
677
686
|
});
|
|
678
687
|
};
|
|
679
688
|
AssistantToolbar.displayName = "AssistantToolbar";
|
|
689
|
+
/**
|
|
690
|
+
* Synthetic context riding on a prompt (a document selection, an encoded event): system-generated
|
|
691
|
+
* input, so it renders as its own dimmed panel above the bubble — the bubble frames only the
|
|
692
|
+
* reader's words.
|
|
693
|
+
*/
|
|
694
|
+
var SyntheticContext = ({ message }) => {
|
|
695
|
+
const { t } = useTranslation(translationKey);
|
|
696
|
+
const { showContext = true } = useMessageChromeContext("SyntheticContext");
|
|
697
|
+
const context = message.blocks.filter((block) => block._tag === "text" && block.disposition === "synthetic").map((block) => block.text).join("\n\n");
|
|
698
|
+
if (!showContext || !context.length) return null;
|
|
699
|
+
return /* @__PURE__ */ jsx("div", {
|
|
700
|
+
className: "pb-1 opacity-60",
|
|
701
|
+
"data-testid": "chat.context",
|
|
702
|
+
children: /* @__PURE__ */ jsx(TogglePanel.Root, { children: /* @__PURE__ */ jsxs(TogglePanel.Content, {
|
|
703
|
+
classNames: "border border-subdued-separator rounded-sm",
|
|
704
|
+
children: [/* @__PURE__ */ jsxs(TogglePanel.Header, {
|
|
705
|
+
classNames: "flex items-center gap-2 px-2 py-1 text-sm",
|
|
706
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
707
|
+
className: "grow text-description truncate",
|
|
708
|
+
children: t("context.label")
|
|
709
|
+
}), /* @__PURE__ */ jsx(Icon, {
|
|
710
|
+
icon: "ph--brain--regular",
|
|
711
|
+
size: 4,
|
|
712
|
+
classNames: "text-description"
|
|
713
|
+
})]
|
|
714
|
+
}), /* @__PURE__ */ jsx(TogglePanel.Body, { children: /* @__PURE__ */ jsx(TogglePanel.Viewport, {
|
|
715
|
+
classNames: "px-2 pb-1 max-h-40 overflow-y-auto text-sm text-description whitespace-pre-wrap",
|
|
716
|
+
children: context
|
|
717
|
+
}) })]
|
|
718
|
+
}) })
|
|
719
|
+
});
|
|
720
|
+
};
|
|
680
721
|
/** Shared hover reveal: present in flow at all times, visible when the row is under the pointer. */
|
|
681
|
-
var reveal = "pt-1 opacity-0 transition-opacity
|
|
722
|
+
var reveal = "pt-1 opacity-0 transition-opacity";
|
|
723
|
+
var revealOnHover = "group-hover/message:opacity-100";
|
|
682
724
|
var Row = ({ children, classNames }) => /* @__PURE__ */ jsx("div", {
|
|
683
|
-
className: mx$1("group relative px-2 py-2", classNames),
|
|
725
|
+
className: mx$1("group/message relative px-2 py-2", classNames),
|
|
684
726
|
"data-testid": "feed.message",
|
|
685
727
|
children
|
|
686
728
|
});
|
|
@@ -694,25 +736,30 @@ var Row = ({ children, classNames }) => /* @__PURE__ */ jsx("div", {
|
|
|
694
736
|
* bubble's edge.
|
|
695
737
|
*/
|
|
696
738
|
var MessageChrome = ({ message, selected, children }) => {
|
|
697
|
-
const
|
|
739
|
+
const { streaming } = useMessageChromeContext(MESSAGE_CHROME_NAME);
|
|
740
|
+
const prompt = isPrompt(message);
|
|
698
741
|
return /* @__PURE__ */ jsx(Row, {
|
|
699
742
|
classNames: mx$1(selected && "bg-hover-surface"),
|
|
700
743
|
children: prompt ? /* @__PURE__ */ jsx("div", {
|
|
701
744
|
className: "min-w-0 flex flex-col items-end",
|
|
702
745
|
children: /* @__PURE__ */ jsxs("div", {
|
|
703
|
-
className: "max-w-[
|
|
704
|
-
children: [
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
746
|
+
className: "max-w-[70%] min-w-0",
|
|
747
|
+
children: [
|
|
748
|
+
/* @__PURE__ */ jsx(SyntheticContext, { message }),
|
|
749
|
+
/* @__PURE__ */ jsx("div", {
|
|
750
|
+
className: "px-3 py-2 border-s-2 border-accent-bg rounded-sm bg-input-surface",
|
|
751
|
+
children
|
|
752
|
+
}),
|
|
753
|
+
/* @__PURE__ */ jsx(PromptToolbar, {
|
|
754
|
+
classNames: mx$1("justify-end", reveal, !streaming && revealOnHover),
|
|
755
|
+
message
|
|
756
|
+
})
|
|
757
|
+
]
|
|
711
758
|
})
|
|
712
759
|
}) : /* @__PURE__ */ jsxs("div", {
|
|
713
760
|
className: "min-w-0",
|
|
714
761
|
children: [children, /* @__PURE__ */ jsx(AssistantToolbar, {
|
|
715
|
-
classNames: reveal,
|
|
762
|
+
classNames: mx$1(reveal, !streaming && revealOnHover),
|
|
716
763
|
message
|
|
717
764
|
})]
|
|
718
765
|
})
|
|
@@ -722,50 +769,36 @@ MessageChrome.displayName = MESSAGE_CHROME_NAME;
|
|
|
722
769
|
//#endregion
|
|
723
770
|
//#region src/components/ChatThread/ChatThread.tsx
|
|
724
771
|
var [ChatThreadProvider, useChatThreadContext] = createContext("ChatThread");
|
|
725
|
-
/** Bridges the feed's context out to the host's ref; renders nothing. */
|
|
726
|
-
var ControllerBridge = ({ controllerRef }) => {
|
|
727
|
-
const { model, scrollToBottom, scrollToIndex, navigation } = useMessageList("ChatThread.Controller");
|
|
728
|
-
useEffect(() => {
|
|
729
|
-
setRef(controllerRef, {
|
|
730
|
-
model,
|
|
731
|
-
scrollToBottom,
|
|
732
|
-
scrollToIndex,
|
|
733
|
-
navigation
|
|
734
|
-
});
|
|
735
|
-
return () => {
|
|
736
|
-
setRef(controllerRef, null);
|
|
737
|
-
};
|
|
738
|
-
}, [
|
|
739
|
-
controllerRef,
|
|
740
|
-
model,
|
|
741
|
-
scrollToBottom,
|
|
742
|
-
scrollToIndex,
|
|
743
|
-
navigation
|
|
744
|
-
]);
|
|
745
|
-
return null;
|
|
746
|
-
};
|
|
747
772
|
/**
|
|
748
773
|
* Headless root: the feed's `MessageList.Root` configured as an assistant thread — the view-typed
|
|
749
774
|
* renderer, the widget registry, the prompt/answer chrome, and the chat behaviours (follow the
|
|
750
775
|
* streaming tail; reserve room to bring the last prompt to the top). Composes with the feed's own
|
|
751
776
|
* parts: `MessageList.Nav`, `useMessageList`, and the rails all work inside it.
|
|
752
777
|
*/
|
|
753
|
-
var ChatThreadRoot = ({ children, model, viewType, registry, getObjectLabel, userHue, debug, onEvent, onRangeChange, controllerRef }) => {
|
|
778
|
+
var ChatThreadRoot = ({ children, model, viewType, registry, getObjectLabel, userHue, tailLines, debug, onEvent, onRangeChange, controllerRef }) => {
|
|
754
779
|
const renderer = useMemo(() => createRenderer(viewType, { getObjectLabel }), [viewType, getObjectLabel]);
|
|
755
|
-
const merged = useMemo(() => registry ? {
|
|
780
|
+
const merged = useMemo(() => viewType === "debug" ? void 0 : registry ? {
|
|
756
781
|
...assistantRegistry,
|
|
757
782
|
...registry
|
|
758
|
-
} : assistantRegistry, [registry]);
|
|
783
|
+
} : assistantRegistry, [registry, viewType]);
|
|
759
784
|
const handleRewind = useCallback((id) => onEvent?.({
|
|
760
785
|
type: "rewind",
|
|
761
786
|
id
|
|
762
787
|
}), [onEvent]);
|
|
788
|
+
const [streaming, setStreaming] = useState(!!model.streamingId);
|
|
789
|
+
useEffect(() => {
|
|
790
|
+
setStreaming(!!model.streamingId);
|
|
791
|
+
return model.subscribe(() => setStreaming(!!model.streamingId));
|
|
792
|
+
}, [model]);
|
|
763
793
|
return /* @__PURE__ */ jsx(ChatThreadProvider, {
|
|
764
794
|
userHue,
|
|
765
795
|
onEvent,
|
|
766
796
|
children: /* @__PURE__ */ jsx(MessageChromeProvider, {
|
|
767
797
|
onRewind: onEvent ? handleRewind : void 0,
|
|
768
|
-
|
|
798
|
+
streaming,
|
|
799
|
+
showContext: viewType !== "summary",
|
|
800
|
+
debug,
|
|
801
|
+
children: /* @__PURE__ */ jsx(MessageList.Root, {
|
|
769
802
|
model,
|
|
770
803
|
renderer,
|
|
771
804
|
registry: merged,
|
|
@@ -773,9 +806,10 @@ var ChatThreadRoot = ({ children, model, viewType, registry, getObjectLabel, use
|
|
|
773
806
|
estimateSize: estimateRow,
|
|
774
807
|
debug,
|
|
775
808
|
stickyBottom: true,
|
|
776
|
-
|
|
809
|
+
tailLines,
|
|
777
810
|
onRangeChange,
|
|
778
|
-
|
|
811
|
+
controllerRef,
|
|
812
|
+
children
|
|
779
813
|
})
|
|
780
814
|
})
|
|
781
815
|
});
|
|
@@ -787,7 +821,7 @@ var CHAT_THREAD_VIEWPORT_NAME = "ChatThread.Viewport";
|
|
|
787
821
|
* `data-action="submit"` buttons; one delegated listener here turns those clicks into `submit`
|
|
788
822
|
* events, which is what keeps the widgets renderable from the tag alone.
|
|
789
823
|
*/
|
|
790
|
-
var ChatThreadViewport = ({ children, ...props }) => {
|
|
824
|
+
var ChatThreadViewport = ({ children, classNames, ...props }) => {
|
|
791
825
|
const { userHue, onEvent } = useChatThreadContext(CHAT_THREAD_VIEWPORT_NAME);
|
|
792
826
|
const handleClick = useCallback((event) => {
|
|
793
827
|
const text = event.target.closest("[data-action=\"submit\"]")?.getAttribute("data-value");
|
|
@@ -807,6 +841,7 @@ var ChatThreadViewport = ({ children, ...props }) => {
|
|
|
807
841
|
onClickCapture: handleClick,
|
|
808
842
|
children: /* @__PURE__ */ jsx(MessageList.Viewport, {
|
|
809
843
|
...props,
|
|
844
|
+
classNames: ["grow min-h-0", classNames],
|
|
810
845
|
children
|
|
811
846
|
})
|
|
812
847
|
});
|
|
@@ -817,23 +852,6 @@ var ChatThread = {
|
|
|
817
852
|
Viewport: ChatThreadViewport
|
|
818
853
|
};
|
|
819
854
|
//#endregion
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* Which projection of the thread the reader sees. A view is a filter over blocks, applied by the
|
|
823
|
-
* renderer — the model always carries everything.
|
|
824
|
-
*
|
|
825
|
-
* - `summary`: only conversational text (prompts and replies).
|
|
826
|
-
* - `normal`: everything except reasoning.
|
|
827
|
-
* - `thinking`: everything, reasoning included.
|
|
828
|
-
* - `debug`: every block, raw fallbacks visible.
|
|
829
|
-
*/
|
|
830
|
-
var ChatView = Schema.Union([
|
|
831
|
-
Schema.Literal("normal").annotate({ title: "Normal" }),
|
|
832
|
-
Schema.Literal("summary").annotate({ title: "Summary" }),
|
|
833
|
-
Schema.Literal("thinking").annotate({ title: "Thinking" }),
|
|
834
|
-
Schema.Literal("debug").annotate({ title: "Debug" })
|
|
835
|
-
]);
|
|
836
|
-
//#endregion
|
|
837
|
-
export { AssistantToolbar, ChatThread, ChatView, FallbackWidget, MessageChrome, MessageChromeProvider, PromptToolbar, ReasoningWidget, ReferenceWidget, SelectWidget, StatsWidget, StatusWidget, SuggestionWidget, SummaryWidget, ToolWidget, assistantRegistry, createRenderer, estimateRow, formatTime, translationKey, translations };
|
|
855
|
+
export { AssistantToolbar, ChatThread, ChatView, MessageChrome, MessageChromeProvider, PromptToolbar, assistantRegistry, createRenderer, estimateRow, translationKey, translations };
|
|
838
856
|
|
|
839
857
|
//# sourceMappingURL=index.mjs.map
|