basalt-ui 1.11.0 → 1.13.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/AGENTS.md +21 -21
- package/CHANGELOG.md +32 -0
- package/README.md +63 -51
- package/agent/rules/basalt-agent.md +14 -0
- package/agent/rules/basalt-content.md +16 -3
- package/configs/oxlint-plugin.js +7 -3
- package/configs/oxlint-plugin.test.ts +34 -5
- package/configs/oxlint.json +4 -4
- package/dist/agent/adapter.d.ts +150 -0
- package/dist/agent/adapter.d.ts.map +1 -0
- package/dist/agent/adapter.js +482 -0
- package/dist/agent/adapter.test.d.ts +2 -0
- package/dist/agent/adapter.test.d.ts.map +1 -0
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +6 -2
- package/dist/agent/ai-sdk-transport.test.d.ts +2 -0
- package/dist/agent/ai-sdk-transport.test.d.ts.map +1 -0
- package/dist/agent/coalesce.d.ts +20 -4
- package/dist/agent/coalesce.d.ts.map +1 -1
- package/dist/agent/coalesce.js +76 -8
- package/dist/agent/coalesce.test.d.ts +2 -0
- package/dist/agent/coalesce.test.d.ts.map +1 -0
- package/dist/agent/foreign.test.d.ts +2 -0
- package/dist/agent/foreign.test.d.ts.map +1 -0
- package/dist/agent/id.d.ts +78 -0
- package/dist/agent/id.d.ts.map +1 -1
- package/dist/agent/id.js +30 -0
- package/dist/agent/id.test.d.ts +2 -0
- package/dist/agent/id.test.d.ts.map +1 -0
- package/dist/agent/index.d.ts +2 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +3 -0
- package/dist/agent/merge.d.ts.map +1 -1
- package/dist/agent/merge.js +19 -7
- package/dist/agent/merge.test.d.ts +2 -0
- package/dist/agent/merge.test.d.ts.map +1 -0
- package/dist/agent/part-list.d.ts +49 -34
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +42 -6
- package/dist/agent/part-list.test.d.ts +2 -0
- package/dist/agent/part-list.test.d.ts.map +1 -0
- package/dist/agent/parts.d.ts +46 -10
- package/dist/agent/parts.d.ts.map +1 -1
- package/dist/agent/parts.js +16 -6
- package/dist/agent/parts.test.d.ts +2 -0
- package/dist/agent/parts.test.d.ts.map +1 -0
- package/dist/agent/thread.d.ts +18 -0
- package/dist/agent/thread.d.ts.map +1 -1
- package/dist/agent/thread.js +8 -2
- package/dist/agent/thread.test.d.ts +2 -0
- package/dist/agent/thread.test.d.ts.map +1 -0
- package/dist/agent/transport.test.d.ts +2 -0
- package/dist/agent/transport.test.d.ts.map +1 -0
- package/dist/agent/use-agent-stream.d.ts.map +1 -1
- package/dist/agent/use-agent-stream.js +2 -2
- package/dist/agent/use-agent-stream.test.d.ts +2 -0
- package/dist/agent/use-agent-stream.test.d.ts.map +1 -0
- package/dist/agent/use-agent-thread-runs.d.ts +24 -3
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +2 -0
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +1 -0
- package/dist/agent/use-agent-thread-runs.js +55 -15
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts +2 -0
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +1 -0
- package/dist/agent/use-agent-thread-runs.test.d.ts +2 -0
- package/dist/agent/use-agent-thread-runs.test.d.ts.map +1 -0
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +2 -0
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +1 -0
- package/dist/agent-chat/composer.d.ts +130 -8
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +189 -23
- package/dist/agent-chat/composer.test.d.ts +2 -0
- package/dist/agent-chat/composer.test.d.ts.map +1 -0
- package/dist/agent-chat/index.d.ts +5 -1
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/index.js +2 -0
- package/dist/agent-chat/message-affordances.d.ts +59 -0
- package/dist/agent-chat/message-affordances.d.ts.map +1 -0
- package/dist/agent-chat/message-affordances.js +7 -0
- package/dist/agent-chat/relative-time.d.ts +27 -0
- package/dist/agent-chat/relative-time.d.ts.map +1 -0
- package/dist/agent-chat/relative-time.js +27 -0
- package/dist/agent-chat/relative-time.test.d.ts +2 -0
- package/dist/agent-chat/relative-time.test.d.ts.map +1 -0
- package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-feed-row.d.ts +65 -0
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -0
- package/dist/agent-chat/thread-feed-row.js +156 -0
- package/dist/agent-chat/thread-feed-row.test.d.ts +2 -0
- package/dist/agent-chat/thread-feed-row.test.d.ts.map +1 -0
- package/dist/agent-chat/thread-feed.d.ts +34 -5
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +52 -19
- package/dist/agent-chat/thread-feed.test.d.ts +2 -0
- package/dist/agent-chat/thread-feed.test.d.ts.map +1 -0
- package/dist/agent-chat/thread-message.d.ts +183 -2
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +329 -37
- package/dist/agent-chat/thread-message.test.d.ts +2 -0
- package/dist/agent-chat/thread-message.test.d.ts.map +1 -0
- package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
- package/dist/agent-chat/thread-outcome-card.js +1 -16
- package/dist/agent-chat/thread-workspace.d.ts +14 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +30 -4
- package/dist/agent-chat/thread-workspace.test.d.ts +2 -0
- package/dist/agent-chat/thread-workspace.test.d.ts.map +1 -0
- package/dist/agent-chat/tool-chip.test.d.ts +2 -0
- package/dist/agent-chat/tool-chip.test.d.ts.map +1 -0
- package/dist/agent-chat/virtualize.d.ts +123 -0
- package/dist/agent-chat/virtualize.d.ts.map +1 -0
- package/dist/agent-chat/virtualize.js +9 -0
- package/dist/agent-chat/virtualize.type-guard.test.d.ts +2 -0
- package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartFrame.test.d.ts +2 -0
- package/dist/charts/primitives/ChartFrame.test.d.ts.map +1 -0
- package/dist/charts/utils/domain.test.d.ts +2 -0
- package/dist/charts/utils/domain.test.d.ts.map +1 -0
- package/dist/cli/check-theme.test.d.ts +2 -0
- package/dist/cli/check-theme.test.d.ts.map +1 -0
- package/dist/cli/help.test.d.ts +2 -0
- package/dist/cli/help.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/init-sync.test.d.ts +2 -0
- package/dist/cli/init-sync.test.d.ts.map +1 -0
- package/dist/cli/placement-engine.test.d.ts +2 -0
- package/dist/cli/placement-engine.test.d.ts.map +1 -0
- package/dist/cli/tarball-parity.test.d.ts +2 -0
- package/dist/cli/tarball-parity.test.d.ts.map +1 -0
- package/dist/cli/template-format.test.d.ts +2 -0
- package/dist/cli/template-format.test.d.ts.map +1 -0
- package/dist/cli/template-ledger.test.d.ts +2 -0
- package/dist/cli/template-ledger.test.d.ts.map +1 -0
- package/dist/cli/tokens-css.test.d.ts +2 -0
- package/dist/cli/tokens-css.test.d.ts.map +1 -0
- package/dist/content/article-actions.test.d.ts +2 -0
- package/dist/content/article-actions.test.d.ts.map +1 -0
- package/dist/content/article-model.test.d.ts +2 -0
- package/dist/content/article-model.test.d.ts.map +1 -0
- package/dist/content/block-split.test.d.ts +2 -0
- package/dist/content/block-split.test.d.ts.map +1 -0
- package/dist/content/fence-block.d.ts +37 -10
- package/dist/content/fence-block.d.ts.map +1 -1
- package/dist/content/fence-block.js +80 -14
- package/dist/content/heading-components.d.ts.map +1 -1
- package/dist/content/heading-components.js +7 -4
- package/dist/content/heading-components.test.d.ts +2 -0
- package/dist/content/heading-components.test.d.ts.map +1 -0
- package/dist/content/index.d.ts +6 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +5 -1
- package/dist/content/markdown.d.ts +101 -14
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +141 -28
- package/dist/content/markdown.test.d.ts +2 -0
- package/dist/content/markdown.test.d.ts.map +1 -0
- package/dist/content/prose.module.css.test.d.ts +2 -0
- package/dist/content/prose.module.css.test.d.ts.map +1 -0
- package/dist/content/sanitize.d.ts +170 -0
- package/dist/content/sanitize.d.ts.map +1 -0
- package/dist/content/sanitize.js +178 -0
- package/dist/content/sanitize.test.d.ts +2 -0
- package/dist/content/sanitize.test.d.ts.map +1 -0
- package/dist/content/slug.test.d.ts +2 -0
- package/dist/content/slug.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.test.d.ts +2 -0
- package/dist/dashboard/stat-card.test.d.ts.map +1 -0
- package/dist/data/index.d.ts +5 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual.d.ts +2 -1
- package/dist/data/virtual.d.ts.map +1 -1
- package/dist/guard/check-source.test.d.ts +2 -0
- package/dist/guard/check-source.test.d.ts.map +1 -0
- package/dist/guard/guard-hook.test.d.ts +2 -0
- package/dist/guard/guard-hook.test.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/provider/index.test.d.ts +2 -0
- package/dist/provider/index.test.d.ts.map +1 -0
- package/dist/provider/lab-theme.test.d.ts +2 -0
- package/dist/provider/lab-theme.test.d.ts.map +1 -0
- package/dist/router-tanstack/multi-search-param-store.test.d.ts +2 -0
- package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +1 -0
- package/dist/styles.floor.test.d.ts +2 -0
- package/dist/styles.floor.test.d.ts.map +1 -0
- package/dist/surfaces.d.ts +5 -5
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +14 -4
- package/dist/theme/border-coverage.test.d.ts +18 -0
- package/dist/theme/border-coverage.test.d.ts.map +1 -0
- package/dist/theme/contrast.test.d.ts +2 -0
- package/dist/theme/contrast.test.d.ts.map +1 -0
- package/dist/theme/density-option.test.d.ts +2 -0
- package/dist/theme/density-option.test.d.ts.map +1 -0
- package/dist/theme/density-relations.test.d.ts +2 -0
- package/dist/theme/density-relations.test.d.ts.map +1 -0
- package/dist/theme/derive-theme.test.d.ts +2 -0
- package/dist/theme/derive-theme.test.d.ts.map +1 -0
- package/dist/theme/fonts-option.test.d.ts +2 -0
- package/dist/theme/fonts-option.test.d.ts.map +1 -0
- package/dist/theme/radius-option.test.d.ts +2 -0
- package/dist/theme/radius-option.test.d.ts.map +1 -0
- package/dist/theme/radius.test.d.ts +2 -0
- package/dist/theme/radius.test.d.ts.map +1 -0
- package/dist/theme/shadow-surfaces.test.d.ts +26 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -0
- package/dist/theme/spacing.test.d.ts +2 -0
- package/dist/theme/spacing.test.d.ts.map +1 -0
- package/dist/theme/use-basalt-spacing.test.d.ts +2 -0
- package/dist/theme/use-basalt-spacing.test.d.ts.map +1 -0
- package/dist/theme-lab/derive-controls.test.d.ts +2 -0
- package/dist/theme-lab/derive-controls.test.d.ts.map +1 -0
- package/dist/theme-lab/derive-state.test.d.ts +2 -0
- package/dist/theme-lab/derive-state.test.d.ts.map +1 -0
- package/dist/theme-lab/theme-lab.test.d.ts +2 -0
- package/dist/theme-lab/theme-lab.test.d.ts.map +1 -0
- package/dist/tokens/build-fonts-css.test.d.ts +2 -0
- package/dist/tokens/build-fonts-css.test.d.ts.map +1 -0
- package/dist/tokens/build-radius-css.test.d.ts +2 -0
- package/dist/tokens/build-radius-css.test.d.ts.map +1 -0
- package/dist/tokens/density.test.d.ts +2 -0
- package/dist/tokens/density.test.d.ts.map +1 -0
- package/dist/tokens/derive-radius.test.d.ts +2 -0
- package/dist/tokens/derive-radius.test.d.ts.map +1 -0
- package/dist/tokens/derive.test.d.ts +2 -0
- package/dist/tokens/derive.test.d.ts.map +1 -0
- package/dist/tokens/palette.test.d.ts +2 -0
- package/dist/tokens/palette.test.d.ts.map +1 -0
- package/dist/vite-color-mix.test.d.ts +2 -0
- package/dist/vite-color-mix.test.d.ts.map +1 -0
- package/dist/vite.test.d.ts +2 -0
- package/dist/vite.test.d.ts.map +1 -0
- package/llms.txt +9 -9
- package/package.json +7 -2
- package/src/agent/adapter.test.ts +1640 -0
- package/src/agent/adapter.ts +945 -0
- package/src/agent/ai-sdk-transport.test.ts +117 -26
- package/src/agent/ai-sdk-transport.ts +8 -2
- package/src/agent/coalesce.test.ts +202 -9
- package/src/agent/coalesce.ts +126 -15
- package/src/agent/foreign.test.ts +5 -1
- package/src/agent/id.test.ts +101 -3
- package/src/agent/id.ts +118 -0
- package/src/agent/index.ts +8 -0
- package/src/agent/merge.test.ts +87 -0
- package/src/agent/merge.ts +57 -10
- package/src/agent/part-list.test.tsx +31 -1
- package/src/agent/part-list.tsx +77 -29
- package/src/agent/parts.test.ts +121 -4
- package/src/agent/parts.ts +78 -17
- package/src/agent/thread.test.ts +75 -2
- package/src/agent/thread.ts +25 -1
- package/src/agent/use-agent-stream.test.tsx +31 -5
- package/src/agent/use-agent-stream.ts +6 -3
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +5 -0
- package/src/agent/use-agent-thread-runs.resume.test.tsx +16 -11
- package/src/agent/use-agent-thread-runs.test.tsx +356 -17
- package/src/agent/use-agent-thread-runs.ts +159 -23
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +93 -7
- package/src/agent-chat/composer.test.tsx +924 -0
- package/src/agent-chat/composer.tsx +521 -52
- package/src/agent-chat/index.ts +13 -1
- package/src/agent-chat/message-affordances.ts +61 -0
- package/src/agent-chat/relative-time.test.ts +95 -0
- package/src/agent-chat/relative-time.ts +55 -0
- package/src/agent-chat/thread-detail-panel.tsx +3 -1
- package/src/agent-chat/thread-feed-row.test.tsx +510 -0
- package/src/agent-chat/thread-feed-row.tsx +274 -0
- package/src/agent-chat/thread-feed.test.tsx +241 -0
- package/src/agent-chat/thread-feed.tsx +119 -18
- package/src/agent-chat/thread-message.test.tsx +1078 -4
- package/src/agent-chat/thread-message.tsx +795 -42
- package/src/agent-chat/thread-outcome-card.tsx +1 -25
- package/src/agent-chat/thread-workspace.test.tsx +169 -0
- package/src/agent-chat/thread-workspace.tsx +70 -6
- package/src/agent-chat/virtualize.ts +132 -0
- package/src/agent-chat/virtualize.type-guard.test.ts +49 -0
- package/src/cli/index.ts +0 -1
- package/src/cli/template-format.test.ts +2 -2
- package/src/content/fence-block.tsx +242 -15
- package/src/content/heading-components.test.tsx +65 -0
- package/src/content/heading-components.tsx +26 -5
- package/src/content/index.ts +19 -3
- package/src/content/markdown.test.tsx +603 -0
- package/src/content/markdown.tsx +355 -35
- package/src/content/sanitize.test.ts +659 -0
- package/src/content/sanitize.ts +510 -0
- package/src/dashboard/stat-card.test.tsx +6 -1
- package/src/data/index.ts +5 -1
- package/src/data/virtual-list.tsx +2 -1
- package/src/data/virtual.ts +2 -1
- package/src/index.ts +18 -1
- package/src/surfaces.ts +13 -3
- package/src/theme/contrast.test.ts +1 -4
- package/src/theme/density-relations.test.ts +4 -2
- package/src/theme/fonts-option.test.ts +7 -1
- package/src/theme/shadow-surfaces.test.ts +21 -6
- package/src/theme/use-basalt-spacing.test.tsx +2 -1
|
@@ -10,12 +10,16 @@ import {
|
|
|
10
10
|
Text,
|
|
11
11
|
UnstyledButton
|
|
12
12
|
} from "@mantine/core";
|
|
13
|
-
import { useDisclosure } from "@mantine/hooks";
|
|
14
|
-
import { Fragment, memo, useMemo } from "react";
|
|
13
|
+
import { useDisclosure, useFocusWithin, useHover, useMergedRef } from "@mantine/hooks";
|
|
14
|
+
import { Fragment, lazy, memo, Suspense, useLayoutEffect, useMemo, useRef } from "react";
|
|
15
15
|
import { coalesceParts, narrowAgentPart, PartList } from "../agent/index.js";
|
|
16
16
|
import { Markdown } from "../content/markdown.js";
|
|
17
|
+
import { CopyAction } from "../content/copy-action.js";
|
|
17
18
|
import { VX } from "../tokens/index.js";
|
|
19
|
+
import { DEFAULT_AFFORDANCES } from "./message-affordances.js";
|
|
20
|
+
import { formatRelativeTime } from "./relative-time.js";
|
|
18
21
|
import { ToolChip } from "./tool-chip.js";
|
|
22
|
+
import { resolveVirtualize } from "./virtualize.js";
|
|
19
23
|
const MICRO_LABEL_STYLE = {
|
|
20
24
|
fontFamily: "var(--basalt-font-mono)",
|
|
21
25
|
fontSize: VX.text.micro,
|
|
@@ -24,8 +28,11 @@ const MICRO_LABEL_STYLE = {
|
|
|
24
28
|
textTransform: "uppercase",
|
|
25
29
|
color: VX.faint
|
|
26
30
|
};
|
|
27
|
-
function TextRenderer({
|
|
28
|
-
|
|
31
|
+
function TextRenderer({
|
|
32
|
+
part,
|
|
33
|
+
settled
|
|
34
|
+
}) {
|
|
35
|
+
return /* @__PURE__ */ jsx(Markdown, { streaming: !settled, contentTrust: "untrusted", density: "chat", children: part.text });
|
|
29
36
|
}
|
|
30
37
|
const RAIL_STYLE = {
|
|
31
38
|
borderLeft: `2px solid ${VX.divider}`,
|
|
@@ -95,6 +102,51 @@ function resolveSegments(parts, renderers, fallbackRenderer) {
|
|
|
95
102
|
flushBuffer();
|
|
96
103
|
return segments;
|
|
97
104
|
}
|
|
105
|
+
function extractCoalescedText(segments) {
|
|
106
|
+
const chunks = [];
|
|
107
|
+
for (const segment of segments) {
|
|
108
|
+
if (segment.kind !== "agent") continue;
|
|
109
|
+
for (const part of segment.parts) {
|
|
110
|
+
if (part.type === "text") chunks.push(part.text);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return chunks.join("\n\n");
|
|
114
|
+
}
|
|
115
|
+
const GROUP_WINDOW_MS = 5 * 6e4;
|
|
116
|
+
function isConsecutiveWithPredecessor(message, predecessor) {
|
|
117
|
+
if (predecessor === void 0) return false;
|
|
118
|
+
if (predecessor.role !== message.role) return false;
|
|
119
|
+
return message.createdAt - predecessor.createdAt <= GROUP_WINDOW_MS;
|
|
120
|
+
}
|
|
121
|
+
function MessageAffordanceRow({
|
|
122
|
+
message,
|
|
123
|
+
coalescedText,
|
|
124
|
+
affordances,
|
|
125
|
+
isLastAssistant,
|
|
126
|
+
visible
|
|
127
|
+
}) {
|
|
128
|
+
const timestampMode = affordances.timestamp ?? DEFAULT_AFFORDANCES.timestamp;
|
|
129
|
+
const showTimestamp = timestampMode !== "none";
|
|
130
|
+
const showCopy = affordances.copy ?? DEFAULT_AFFORDANCES.copy;
|
|
131
|
+
const showRegenerate = isLastAssistant && affordances.onRegenerate !== void 0;
|
|
132
|
+
const customActions = affordances.actions?.({ message });
|
|
133
|
+
if (!showTimestamp && !showCopy && !showRegenerate && customActions === void 0) return null;
|
|
134
|
+
const timestampLabel = timestampMode === "absolute" ? new Date(message.createdAt).toLocaleString() : formatRelativeTime(message.createdAt);
|
|
135
|
+
return /* @__PURE__ */ jsxs(
|
|
136
|
+
Group,
|
|
137
|
+
{
|
|
138
|
+
gap: 8,
|
|
139
|
+
"data-testid": `message-affordances-${message.id}`,
|
|
140
|
+
style: { opacity: visible ? 1 : 0, transition: "opacity 120ms ease" },
|
|
141
|
+
children: [
|
|
142
|
+
showTimestamp && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: timestampLabel }),
|
|
143
|
+
showCopy && /* @__PURE__ */ jsx(CopyAction, { value: coalescedText, ariaLabel: "Copy message" }),
|
|
144
|
+
showRegenerate && /* @__PURE__ */ jsx(UnstyledButton, { onClick: () => affordances.onRegenerate?.(message.id), children: /* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: "Regenerate" }) }),
|
|
145
|
+
customActions
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
98
150
|
const ROLE_LABEL = {
|
|
99
151
|
user: "You",
|
|
100
152
|
assistant: "Assistant"
|
|
@@ -108,83 +160,323 @@ function MessageBlockImpl({
|
|
|
108
160
|
message,
|
|
109
161
|
streaming = false,
|
|
110
162
|
renderers,
|
|
111
|
-
fallbackRenderer
|
|
163
|
+
fallbackRenderer,
|
|
164
|
+
grouped = false,
|
|
165
|
+
affordances,
|
|
166
|
+
isLastAssistant = false
|
|
112
167
|
}) {
|
|
113
168
|
messageBlockRenderCounter.count += 1;
|
|
114
169
|
const settled = !streaming;
|
|
170
|
+
const { hovered, ref: hoverRef } = useHover();
|
|
171
|
+
const { focused, ref: focusWithinRef } = useFocusWithin();
|
|
172
|
+
const ref = useMergedRef(hoverRef, focusWithinRef);
|
|
115
173
|
const segments = useMemo(
|
|
116
174
|
() => resolveSegments(message.parts, renderers, fallbackRenderer),
|
|
117
175
|
[message.parts, renderers, fallbackRenderer]
|
|
118
176
|
);
|
|
119
177
|
const finishIndicator = message.finish === void 0 ? void 0 : FINISH_INDICATOR[message.finish];
|
|
178
|
+
const coalescedText = useMemo(
|
|
179
|
+
() => affordances === void 0 ? "" : extractCoalescedText(segments),
|
|
180
|
+
[affordances, segments]
|
|
181
|
+
);
|
|
182
|
+
const showHeader = !grouped || streaming || finishIndicator !== void 0;
|
|
120
183
|
return /* @__PURE__ */ jsx(
|
|
121
184
|
Box,
|
|
122
185
|
{
|
|
186
|
+
ref,
|
|
187
|
+
"data-testid": `agent-message-${message.id}`,
|
|
188
|
+
"data-grouped": grouped,
|
|
123
189
|
style: {
|
|
124
190
|
padding: "var(--vx-space-agent-message-inset-y) var(--vx-space-agent-message-inset-x)",
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
191
|
+
...grouped ? {} : {
|
|
192
|
+
backgroundColor: message.role === "user" ? VX.surface.subtle : VX.surface.panel,
|
|
193
|
+
borderRadius: VX.radiusCard,
|
|
194
|
+
boxShadow: VX.shadowCard
|
|
195
|
+
}
|
|
128
196
|
},
|
|
129
197
|
children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
130
|
-
/* @__PURE__ */ jsxs(Group, { gap: 6, align: "center", children: [
|
|
131
|
-
/* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: ROLE_LABEL[message.role] }),
|
|
198
|
+
showHeader && /* @__PURE__ */ jsxs(Group, { gap: 6, align: "center", children: [
|
|
199
|
+
!grouped && /* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: ROLE_LABEL[message.role] }),
|
|
132
200
|
streaming && /* @__PURE__ */ jsx(Loader, { size: "xs" }),
|
|
133
201
|
finishIndicator !== void 0 && /* @__PURE__ */ jsx(Text, { style: { ...MICRO_LABEL_STYLE, color: finishIndicator.statusToken }, children: finishIndicator.label })
|
|
134
202
|
] }),
|
|
135
203
|
segments.map(
|
|
136
|
-
(segment) => segment.kind === "agent" ? /* @__PURE__ */ jsx(
|
|
204
|
+
(segment) => segment.kind === "agent" ? /* @__PURE__ */ jsx(
|
|
205
|
+
PartList,
|
|
206
|
+
{
|
|
207
|
+
parts: segment.parts,
|
|
208
|
+
components: threadPartRenderers,
|
|
209
|
+
settled
|
|
210
|
+
},
|
|
211
|
+
segment.key
|
|
212
|
+
) : /* @__PURE__ */ jsx(Fragment, { children: segment.render({
|
|
137
213
|
part: segment.part,
|
|
138
214
|
messageId: message.id,
|
|
139
215
|
partId: segment.part.id,
|
|
140
216
|
settled,
|
|
141
217
|
role: message.role
|
|
142
218
|
}) }, segment.key)
|
|
219
|
+
),
|
|
220
|
+
affordances !== void 0 && /* @__PURE__ */ jsx(
|
|
221
|
+
MessageAffordanceRow,
|
|
222
|
+
{
|
|
223
|
+
message,
|
|
224
|
+
coalescedText,
|
|
225
|
+
affordances,
|
|
226
|
+
isLastAssistant,
|
|
227
|
+
visible: hovered || focused
|
|
228
|
+
}
|
|
143
229
|
)
|
|
144
230
|
] })
|
|
145
231
|
}
|
|
146
232
|
);
|
|
147
233
|
}
|
|
148
234
|
function areMessageBlockPropsEqual(prev, next) {
|
|
149
|
-
return prev.message === next.message && prev.streaming === next.streaming && prev.renderers === next.renderers && prev.fallbackRenderer === next.fallbackRenderer;
|
|
235
|
+
return prev.message === next.message && prev.streaming === next.streaming && prev.renderers === next.renderers && prev.fallbackRenderer === next.fallbackRenderer && prev.grouped === next.grouped && prev.affordances === next.affordances && prev.isLastAssistant === next.isLastAssistant;
|
|
150
236
|
}
|
|
151
237
|
const MessageBlock = memo(MessageBlockImpl, areMessageBlockPropsEqual);
|
|
238
|
+
const DEFAULT_VIRTUALIZE_OVERSCAN = 6;
|
|
239
|
+
const DEFAULT_VIRTUALIZE_ESTIMATE_SIZE = 160;
|
|
240
|
+
const DEFAULT_VIRTUALIZE_SCROLL_END_THRESHOLD = 64;
|
|
241
|
+
const DEFAULT_VIRTUALIZE_INITIAL_SCROLL = "end";
|
|
242
|
+
const MAX_INITIAL_SCROLL_ATTEMPTS = 5;
|
|
243
|
+
function NonVirtualizedRows({ rows, height }) {
|
|
244
|
+
return (
|
|
245
|
+
// theme-allow — degrade target owns its own scroll node, same as the real virtualizer below.
|
|
246
|
+
/* @__PURE__ */ jsx(Box, { style: { height, overflow: "auto" }, children: /* @__PURE__ */ jsx(Stack, { gap: "sm", children: rows.map((row) => /* @__PURE__ */ jsx(Fragment, { children: row.node }, row.key)) }) })
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const nonVirtualizedRowsFallback = NonVirtualizedRows;
|
|
250
|
+
function resolveGuardedMeasurement(host, element, size) {
|
|
251
|
+
if (size > 0) return size;
|
|
252
|
+
const index = host.indexFromElement(element);
|
|
253
|
+
const key = host.options.getItemKey(index);
|
|
254
|
+
return host.itemSizeCache.get(key) ?? host.options.estimateSize(index);
|
|
255
|
+
}
|
|
256
|
+
function resolveInitialScrollAction(initialScroll, rowCount, containerHeight) {
|
|
257
|
+
if (initialScroll === "start") return "skip-done";
|
|
258
|
+
if (rowCount === 0) return "skip-done";
|
|
259
|
+
if (containerHeight <= 0) return "skip-retry";
|
|
260
|
+
return "scroll";
|
|
261
|
+
}
|
|
262
|
+
function applyInitialScroll(state, initialScroll, rowCount, getContainerHeight, scrollToEnd, isAtEnd) {
|
|
263
|
+
if (state.hasApplied) return;
|
|
264
|
+
if (state.attempts > 0) {
|
|
265
|
+
if (isAtEnd() || state.attempts >= MAX_INITIAL_SCROLL_ATTEMPTS) {
|
|
266
|
+
state.hasApplied = true;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
scrollToEnd();
|
|
270
|
+
state.attempts += 1;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const action = resolveInitialScrollAction(initialScroll, rowCount, getContainerHeight());
|
|
274
|
+
if (action === "skip-retry") return;
|
|
275
|
+
if (action === "skip-done") {
|
|
276
|
+
state.hasApplied = true;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
scrollToEnd();
|
|
280
|
+
state.attempts += 1;
|
|
281
|
+
}
|
|
282
|
+
function isContainerAtEnd(container, threshold) {
|
|
283
|
+
if (container === null) return false;
|
|
284
|
+
const distanceFromEnd = container.scrollHeight - container.clientHeight - container.scrollTop;
|
|
285
|
+
return distanceFromEnd <= threshold;
|
|
286
|
+
}
|
|
287
|
+
const LazyVirtualizedRows = lazy(
|
|
288
|
+
() => import("@tanstack/react-virtual").then(({ useVirtualizer, measureElement: defaultMeasureElement }) => {
|
|
289
|
+
function VirtualizedRowsInner({
|
|
290
|
+
rows,
|
|
291
|
+
height,
|
|
292
|
+
overscan,
|
|
293
|
+
estimateSize,
|
|
294
|
+
initialScroll
|
|
295
|
+
}) {
|
|
296
|
+
const parentRef = useRef(null);
|
|
297
|
+
const initialScrollStateRef = useRef({
|
|
298
|
+
hasApplied: false,
|
|
299
|
+
attempts: 0
|
|
300
|
+
});
|
|
301
|
+
const virtualizer = useVirtualizer({
|
|
302
|
+
count: rows.length,
|
|
303
|
+
getScrollElement: () => parentRef.current,
|
|
304
|
+
estimateSize: () => estimateSize,
|
|
305
|
+
overscan,
|
|
306
|
+
getItemKey: (index) => rows[index]?.key ?? index,
|
|
307
|
+
anchorTo: "end",
|
|
308
|
+
followOnAppend: true,
|
|
309
|
+
scrollEndThreshold: DEFAULT_VIRTUALIZE_SCROLL_END_THRESHOLD,
|
|
310
|
+
useFlushSync: false,
|
|
311
|
+
// Guards against the hidden-ancestor cache-poisoning mechanism
|
|
312
|
+
// `resolveGuardedMeasurement`'s doc and `virtualize.ts`'s composition rules describe —
|
|
313
|
+
// NOT `enabled: false`, which virtual-core 3.17.1 wires to WIPE
|
|
314
|
+
// `itemSizeCache`/`measurementsCache` entirely (`getMeasurements`'s `!enabled` branch)
|
|
315
|
+
// rather than pause measurement, so toggling it off/on around a collapse would be
|
|
316
|
+
// strictly worse than the bug it was meant to fix.
|
|
317
|
+
measureElement: (element, entry, instance) => resolveGuardedMeasurement(
|
|
318
|
+
instance,
|
|
319
|
+
element,
|
|
320
|
+
defaultMeasureElement(element, entry, instance)
|
|
321
|
+
)
|
|
322
|
+
});
|
|
323
|
+
useLayoutEffect(() => {
|
|
324
|
+
applyInitialScroll(
|
|
325
|
+
initialScrollStateRef.current,
|
|
326
|
+
initialScroll,
|
|
327
|
+
rows.length,
|
|
328
|
+
() => parentRef.current?.offsetHeight ?? 0,
|
|
329
|
+
() => virtualizer.scrollToEnd({ behavior: "auto" }),
|
|
330
|
+
() => isContainerAtEnd(parentRef.current, DEFAULT_VIRTUALIZE_SCROLL_END_THRESHOLD)
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
return (
|
|
334
|
+
// theme-allow — TanStack Virtual measures/scrolls this element (never nest in BasaltStickToBottom, see ./virtualize).
|
|
335
|
+
/* @__PURE__ */ jsx(Box, { ref: parentRef, style: { height, overflow: "auto" }, children: /* @__PURE__ */ jsx(Box, { style: { height: virtualizer.getTotalSize(), position: "relative" }, children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
336
|
+
const row = rows[virtualItem.index];
|
|
337
|
+
if (row === void 0) return null;
|
|
338
|
+
return /* @__PURE__ */ jsx(
|
|
339
|
+
Box,
|
|
340
|
+
{
|
|
341
|
+
ref: virtualizer.measureElement,
|
|
342
|
+
"data-index": virtualItem.index,
|
|
343
|
+
style: {
|
|
344
|
+
position: "absolute",
|
|
345
|
+
top: 0,
|
|
346
|
+
left: 0,
|
|
347
|
+
width: "100%",
|
|
348
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
349
|
+
},
|
|
350
|
+
children: row.node
|
|
351
|
+
},
|
|
352
|
+
row.key
|
|
353
|
+
);
|
|
354
|
+
}) }) })
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return { default: VirtualizedRowsInner };
|
|
358
|
+
}).catch(() => ({ default: NonVirtualizedRows }))
|
|
359
|
+
);
|
|
360
|
+
function VirtualizeSuspenseFallback({ height }) {
|
|
361
|
+
return /* @__PURE__ */ jsx(Box, { style: { height, overflow: "auto" } });
|
|
362
|
+
}
|
|
363
|
+
function VirtualizedTranscript(props) {
|
|
364
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(VirtualizeSuspenseFallback, { height: props.height }), children: /* @__PURE__ */ jsx(LazyVirtualizedRows, { ...props }) });
|
|
365
|
+
}
|
|
152
366
|
const EMPTY_RENDERERS = {};
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
367
|
+
function useStableCallback(callback) {
|
|
368
|
+
const callbackRef = useRef(callback);
|
|
369
|
+
callbackRef.current = callback;
|
|
370
|
+
const stable = useMemo(
|
|
371
|
+
() => (...args) => callbackRef.current?.(...args),
|
|
372
|
+
[]
|
|
373
|
+
);
|
|
374
|
+
return callback === void 0 ? void 0 : stable;
|
|
375
|
+
}
|
|
376
|
+
function ThreadTranscript(props) {
|
|
377
|
+
const {
|
|
378
|
+
messages,
|
|
379
|
+
liveParts,
|
|
380
|
+
liveStatus,
|
|
381
|
+
renderers: renderersProp,
|
|
382
|
+
fallbackRenderer = DEFAULT_FALLBACK_RENDERER,
|
|
383
|
+
affordances: affordancesProp,
|
|
384
|
+
groupConsecutive = true
|
|
385
|
+
} = props;
|
|
386
|
+
const virtualized = resolveVirtualize(props);
|
|
160
387
|
const renderers = useMemo(() => renderersProp ?? EMPTY_RENDERERS, [renderersProp]);
|
|
161
388
|
const liveMessage = useMemo(() => {
|
|
162
389
|
if (liveParts === void 0 || liveParts.length === 0) return null;
|
|
163
390
|
return { id: "__live__", role: "assistant", parts: liveParts, createdAt: 0 };
|
|
164
391
|
}, [liveParts]);
|
|
165
|
-
|
|
166
|
-
messages.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
392
|
+
const lastAssistantMessageId = useMemo(() => {
|
|
393
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
394
|
+
const candidate = messages[i];
|
|
395
|
+
if (candidate?.role === "assistant") return candidate.id;
|
|
396
|
+
}
|
|
397
|
+
return null;
|
|
398
|
+
}, [messages]);
|
|
399
|
+
const stableOnRegenerate = useStableCallback(affordancesProp?.onRegenerate);
|
|
400
|
+
const actions = affordancesProp?.actions;
|
|
401
|
+
const resolvedAffordances = useMemo(
|
|
402
|
+
() => ({
|
|
403
|
+
timestamp: affordancesProp?.timestamp ?? DEFAULT_AFFORDANCES.timestamp,
|
|
404
|
+
copy: affordancesProp?.copy ?? DEFAULT_AFFORDANCES.copy,
|
|
405
|
+
// `exactOptionalPropertyTypes` forbids assigning a possibly-`undefined` value to an optional
|
|
406
|
+
// property directly — spread each field in only when it is actually present, rather than
|
|
407
|
+
// widening `MessageAffordances` itself to accept an explicit `undefined`.
|
|
408
|
+
...stableOnRegenerate !== void 0 && { onRegenerate: stableOnRegenerate },
|
|
409
|
+
...actions !== void 0 && { actions }
|
|
410
|
+
}),
|
|
411
|
+
[affordancesProp?.timestamp, affordancesProp?.copy, stableOnRegenerate, actions]
|
|
412
|
+
);
|
|
413
|
+
const settledRows = useMemo(() => {
|
|
414
|
+
const out = [];
|
|
415
|
+
messages.forEach((message, index) => {
|
|
416
|
+
out.push({
|
|
417
|
+
key: message.id,
|
|
418
|
+
node: /* @__PURE__ */ jsx(
|
|
419
|
+
MessageBlock,
|
|
420
|
+
{
|
|
421
|
+
message,
|
|
422
|
+
renderers,
|
|
423
|
+
fallbackRenderer,
|
|
424
|
+
grouped: groupConsecutive && isConsecutiveWithPredecessor(message, messages[index - 1]),
|
|
425
|
+
isLastAssistant: message.id === lastAssistantMessageId,
|
|
426
|
+
affordances: resolvedAffordances
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
return out;
|
|
432
|
+
}, [
|
|
433
|
+
messages,
|
|
434
|
+
renderers,
|
|
435
|
+
fallbackRenderer,
|
|
436
|
+
groupConsecutive,
|
|
437
|
+
lastAssistantMessageId,
|
|
438
|
+
resolvedAffordances
|
|
439
|
+
]);
|
|
440
|
+
const liveRow = useMemo(() => {
|
|
441
|
+
if (liveMessage === null) return null;
|
|
442
|
+
return {
|
|
443
|
+
key: liveMessage.id,
|
|
444
|
+
node: /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-atomic": "false", "aria-relevant": "additions", children: /* @__PURE__ */ jsx(
|
|
445
|
+
MessageBlock,
|
|
446
|
+
{
|
|
447
|
+
message: liveMessage,
|
|
448
|
+
streaming: liveStatus === "streaming",
|
|
449
|
+
renderers,
|
|
450
|
+
fallbackRenderer
|
|
451
|
+
}
|
|
452
|
+
) })
|
|
453
|
+
};
|
|
454
|
+
}, [liveMessage, liveStatus, renderers, fallbackRenderer]);
|
|
455
|
+
const rows = useMemo(
|
|
456
|
+
() => liveRow === null ? settledRows : [...settledRows, liveRow],
|
|
457
|
+
[settledRows, liveRow]
|
|
458
|
+
);
|
|
459
|
+
if (virtualized !== null) {
|
|
460
|
+
return /* @__PURE__ */ jsx(
|
|
461
|
+
VirtualizedTranscript,
|
|
177
462
|
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
463
|
+
rows,
|
|
464
|
+
height: virtualized.height,
|
|
465
|
+
overscan: virtualized.options.overscan ?? DEFAULT_VIRTUALIZE_OVERSCAN,
|
|
466
|
+
estimateSize: virtualized.options.estimateSize ?? DEFAULT_VIRTUALIZE_ESTIMATE_SIZE,
|
|
467
|
+
initialScroll: virtualized.options.initialScroll ?? DEFAULT_VIRTUALIZE_INITIAL_SCROLL
|
|
182
468
|
}
|
|
183
|
-
)
|
|
184
|
-
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: rows.map((row) => /* @__PURE__ */ jsx(Fragment, { children: row.node }, row.key)) });
|
|
185
472
|
}
|
|
186
473
|
export {
|
|
474
|
+
MAX_INITIAL_SCROLL_ATTEMPTS,
|
|
187
475
|
ThreadTranscript,
|
|
476
|
+
applyInitialScroll,
|
|
188
477
|
messageBlockRenderCounter,
|
|
478
|
+
nonVirtualizedRowsFallback,
|
|
479
|
+
resolveGuardedMeasurement,
|
|
480
|
+
resolveInitialScrollAction,
|
|
189
481
|
threadPartRenderers
|
|
190
482
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-message.test.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-message.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-outcome-card.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-outcome-card.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"thread-outcome-card.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-outcome-card.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAA;AAiGzD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,EAAE,sBAAsB,GAAG,GAAG,CAAC,OAAO,CA+BtC"}
|
|
@@ -2,26 +2,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Badge, Group, Skeleton, Stack, Text, UnstyledButton } from "@mantine/core";
|
|
3
3
|
import { useHover } from "@mantine/hooks";
|
|
4
4
|
import { alpha, VX } from "../tokens/index.js";
|
|
5
|
+
import { formatRelativeTime } from "./relative-time.js";
|
|
5
6
|
const STATUS_BADGE = {
|
|
6
7
|
attention: { label: "Needs review", statusToken: VX.status.warn },
|
|
7
8
|
error: { label: "Failed", statusToken: VX.status.bad }
|
|
8
9
|
};
|
|
9
|
-
const RELATIVE_TIME_FORMAT = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
10
|
-
const RELATIVE_TIME_UNITS = [
|
|
11
|
-
{ unit: "year", ms: 31536e6 },
|
|
12
|
-
{ unit: "month", ms: 2628e6 },
|
|
13
|
-
{ unit: "week", ms: 6048e5 },
|
|
14
|
-
{ unit: "day", ms: 864e5 },
|
|
15
|
-
{ unit: "hour", ms: 36e5 },
|
|
16
|
-
{ unit: "minute", ms: 6e4 }
|
|
17
|
-
];
|
|
18
|
-
function formatRelativeTime(timestamp) {
|
|
19
|
-
const diffMs = timestamp - Date.now();
|
|
20
|
-
const absMs = Math.abs(diffMs);
|
|
21
|
-
if (absMs < 6e4) return "just now";
|
|
22
|
-
const unit = RELATIVE_TIME_UNITS.find(({ ms }) => absMs >= ms) ?? RELATIVE_TIME_UNITS.at(-1);
|
|
23
|
-
return RELATIVE_TIME_FORMAT.format(Math.round(diffMs / unit.ms), unit.unit);
|
|
24
|
-
}
|
|
25
10
|
function OutcomeSkeleton() {
|
|
26
11
|
return /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
27
12
|
/* @__PURE__ */ jsx(Skeleton, { height: 14, width: "60%", radius: "sm" }),
|
|
@@ -12,8 +12,20 @@ export type ThreadWorkspaceProps = {
|
|
|
12
12
|
readonly resolveOutcome: OutcomeResolver<AgentPart>;
|
|
13
13
|
/** Placeholder for the new-thread composer pinned under the feed. */
|
|
14
14
|
readonly newThreadPlaceholder?: string;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Rendered in place of the feed once the store is hydrated and there really are no threads.
|
|
17
|
+
* Shown ONLY when `store.hydrated` is true — while an async store's initial load is still in
|
|
18
|
+
* flight, `ThreadWorkspace` holds a neutral loading state instead (see `FeedHydratingState`),
|
|
19
|
+
* so a server-backed workspace never asserts "no threads yet" before it actually knows that.
|
|
20
|
+
*/
|
|
16
21
|
readonly emptyState?: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Rendered in place of the feed when the store's initial load has failed — `!store.hydrated &&
|
|
24
|
+
* store.error !== undefined` (see `ThreadsStore.hydrated`'s doc for the discriminant). Shown
|
|
25
|
+
* instead of `FeedHydratingState`, so a rejected `listThreads` surfaces a legible failure rather
|
|
26
|
+
* than an endless skeleton.
|
|
27
|
+
*/
|
|
28
|
+
readonly errorState?: ReactNode;
|
|
17
29
|
};
|
|
18
30
|
/**
|
|
19
31
|
* A two-pane master-detail thread workspace: a scrollable feed of threads with an anchored
|
|
@@ -23,5 +35,5 @@ export type ThreadWorkspaceProps = {
|
|
|
23
35
|
* @example
|
|
24
36
|
* <ThreadWorkspace useThreads={useThreads} transport={transport} resolveOutcome={heuristicOutcome} />
|
|
25
37
|
*/
|
|
26
|
-
export declare function ThreadWorkspace({ useThreads, transport, resolveOutcome, newThreadPlaceholder, emptyState, }: ThreadWorkspaceProps): JSX.Element;
|
|
38
|
+
export declare function ThreadWorkspace({ useThreads, transport, resolveOutcome, newThreadPlaceholder, emptyState, errorState, }: ThreadWorkspaceProps): JSX.Element;
|
|
27
39
|
//# sourceMappingURL=thread-workspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-workspace.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-workspace.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"thread-workspace.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-workspace.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAUxF,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,YAAY,CAAA;IACvC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;IACrD,mEAAmE;IACnE,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;IACnD,qEAAqE;IACrE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;CAChC,CAAA;AA4DD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,UAAU,GACX,EAAE,oBAAoB,GAAG,GAAG,CAAC,OAAO,CA8FpC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Divider, Flex, Stack, Text } from "@mantine/core";
|
|
2
|
+
import { Box, Divider, Flex, Skeleton, Stack, Text } from "@mantine/core";
|
|
3
3
|
import { useMediaQuery } from "@mantine/hooks";
|
|
4
4
|
import { useAgentThreadRuns } from "../agent/index.js";
|
|
5
|
+
import { VX } from "../tokens/index.js";
|
|
5
6
|
import { Composer } from "./composer.js";
|
|
6
7
|
import { ThreadDetailPanel } from "./thread-detail-panel.js";
|
|
7
8
|
import { ThreadFeed } from "./thread-feed.js";
|
|
@@ -13,12 +14,37 @@ function FeedEmptyState() {
|
|
|
13
14
|
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ta: "center", children: "Send a message below to start your first thread." })
|
|
14
15
|
] });
|
|
15
16
|
}
|
|
17
|
+
function FeedHydratingState() {
|
|
18
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", p: "sm", "data-testid": "thread-workspace-hydrating", children: [
|
|
19
|
+
/* @__PURE__ */ jsx(Skeleton, { height: 54, radius: "sm" }),
|
|
20
|
+
/* @__PURE__ */ jsx(Skeleton, { height: 54, radius: "sm" }),
|
|
21
|
+
/* @__PURE__ */ jsx(Skeleton, { height: 54, radius: "sm" })
|
|
22
|
+
] });
|
|
23
|
+
}
|
|
24
|
+
function FeedErrorState() {
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
Stack,
|
|
27
|
+
{
|
|
28
|
+
align: "center",
|
|
29
|
+
justify: "center",
|
|
30
|
+
gap: 4,
|
|
31
|
+
h: "100%",
|
|
32
|
+
p: "xl",
|
|
33
|
+
"data-testid": "thread-workspace-error",
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(Text, { fw: 600, size: "sm", style: { color: VX.status.bad }, children: "Couldn't load threads" }),
|
|
36
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ta: "center", children: "Something went wrong loading your threads. Try refreshing the page." })
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
16
41
|
function ThreadWorkspace({
|
|
17
42
|
useThreads,
|
|
18
43
|
transport,
|
|
19
44
|
resolveOutcome,
|
|
20
45
|
newThreadPlaceholder,
|
|
21
|
-
emptyState
|
|
46
|
+
emptyState,
|
|
47
|
+
errorState
|
|
22
48
|
}) {
|
|
23
49
|
const store = useThreads();
|
|
24
50
|
const runs = useAgentThreadRuns({ transport, store, resolveOutcome });
|
|
@@ -50,12 +76,12 @@ function ThreadWorkspace({
|
|
|
50
76
|
runs.retry(store.activeId);
|
|
51
77
|
};
|
|
52
78
|
const feed = /* @__PURE__ */ jsxs(Flex, { direction: "column", h: "100%", style: { overflow: "hidden" }, children: [
|
|
53
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minHeight: 0 }, children:
|
|
79
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minHeight: 0 }, children: threads.length === 0 ? store.hydrated ? emptyState ?? /* @__PURE__ */ jsx(FeedEmptyState, {}) : store.error !== void 0 ? errorState ?? /* @__PURE__ */ jsx(FeedErrorState, {}) : /* @__PURE__ */ jsx(FeedHydratingState, {}) : /* @__PURE__ */ jsx(ThreadFeed, { threads, activeId: store.activeId, onSelect: handleSelect }) }),
|
|
54
80
|
/* @__PURE__ */ jsx(Divider, { color: "var(--vx-divider)" }),
|
|
55
81
|
/* @__PURE__ */ jsx(Box, { p: "sm", children: /* @__PURE__ */ jsx(
|
|
56
82
|
Composer,
|
|
57
83
|
{
|
|
58
|
-
onSubmit: handleNewThread,
|
|
84
|
+
onSubmit: ({ text }) => handleNewThread(text),
|
|
59
85
|
...newThreadPlaceholder !== void 0 ? { placeholder: newThreadPlaceholder } : {}
|
|
60
86
|
}
|
|
61
87
|
) })
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-workspace.test.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-workspace.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-chip.test.d.ts","sourceRoot":"","sources":["../../src/agent-chat/tool-chip.test.tsx"],"names":[],"mappings":""}
|