@agent-native/core 0.155.0 → 0.157.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/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
- package/corpus/templates/assets/app/i18n-data.ts +21 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +15 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +16 -2
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
- package/corpus/templates/slides/app/global.css +7 -40
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +30 -0
- package/dist/agent/production-agent.js +100 -8
- package/dist/agent/types.d.ts +9 -0
- package/dist/client/AgentPanel.js +12 -9
- package/dist/client/AssistantChat.d.ts +15 -1
- package/dist/client/AssistantChat.js +110 -43
- package/dist/client/MultiTabAssistantChat.js +44 -27
- package/dist/client/chat/markdown-renderer.js +4 -2
- package/dist/client/chat/message-components.d.ts +2 -2
- package/dist/client/chat/message-components.js +69 -35
- package/dist/client/chat/run-recovery.d.ts +4 -1
- package/dist/client/chat/run-recovery.js +62 -30
- package/dist/client/chat/tool-call-display.d.ts +12 -3
- package/dist/client/chat/tool-call-display.js +79 -40
- package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
- package/dist/client/chat/widgets/DataChartWidget.js +8 -2
- package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
- package/dist/client/chat/widgets/DataTableWidget.js +23 -9
- package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
- package/dist/client/components/ui/message-scroller.d.ts +1 -1
- package/dist/client/components/ui/message-scroller.js +7 -3
- package/dist/client/composer/runtime-adapters.js +3 -2
- package/dist/client/context-xray/ContextMeter.js +5 -1
- package/dist/client/context-xray/ContextXRayPanel.js +5 -5
- package/dist/client/error-format.d.ts +4 -0
- package/dist/client/error-format.js +93 -5
- package/dist/client/i18n.js +83 -17
- package/dist/client/observability/ThumbsFeedback.js +10 -8
- package/dist/client/sharing/ShareButton.js +160 -54
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/sandbox/background.d.ts +1 -0
- package/dist/coding-tools/sandbox/background.js +8 -1
- package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
- package/dist/coding-tools/sandbox/executions-store.js +41 -3
- package/dist/collab/routes.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.d.ts +3 -0
- package/dist/localization/core-messages/ar-SA.js +541 -0
- package/dist/localization/core-messages/de-DE.d.ts +3 -0
- package/dist/localization/core-messages/de-DE.js +513 -0
- package/dist/localization/core-messages/en-US.d.ts +513 -0
- package/dist/localization/core-messages/en-US.js +513 -0
- package/dist/localization/core-messages/es-ES.d.ts +3 -0
- package/dist/localization/core-messages/es-ES.js +520 -0
- package/dist/localization/core-messages/fr-FR.d.ts +3 -0
- package/dist/localization/core-messages/fr-FR.js +520 -0
- package/dist/localization/core-messages/hi-IN.d.ts +3 -0
- package/dist/localization/core-messages/hi-IN.js +513 -0
- package/dist/localization/core-messages/ja-JP.d.ts +3 -0
- package/dist/localization/core-messages/ja-JP.js +506 -0
- package/dist/localization/core-messages/ko-KR.d.ts +3 -0
- package/dist/localization/core-messages/ko-KR.js +506 -0
- package/dist/localization/core-messages/pt-BR.d.ts +3 -0
- package/dist/localization/core-messages/pt-BR.js +520 -0
- package/dist/localization/core-messages/zh-CN.d.ts +3 -0
- package/dist/localization/core-messages/zh-CN.js +506 -0
- package/dist/localization/core-messages/zh-TW.d.ts +3 -0
- package/dist/localization/core-messages/zh-TW.js +506 -0
- package/dist/localization/core-messages.d.ts +524 -0
- package/dist/localization/core-messages.js +124 -0
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.js +2 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/plugin-options.d.ts +7 -2
- package/dist/server/agent-chat-plugin.d.ts +10 -0
- package/dist/server/agent-chat-plugin.js +144 -41
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +7 -1
- package/dist/server/agent-run-context.js +9 -2
- package/dist/server/agent-teams-run-queue.d.ts +3 -0
- package/dist/server/agent-teams.js +38 -9
- package/dist/server/index.d.ts +1 -1
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.js +15 -0
- package/docs/content/agent-surfaces.mdx +30 -0
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ import { fetchBuilderStatus, fetchEnvironmentStatus, } from "./client-status-req
|
|
|
12
12
|
import { Popover, PopoverAnchor, PopoverContent, } from "./components/ui/popover.js";
|
|
13
13
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
|
|
14
14
|
import { isTrustedFrameMessage } from "./frame.js";
|
|
15
|
+
import { DEFAULT_LOCALE, useOptionalLocale, useT } from "./i18n.js";
|
|
15
16
|
import { RunStuckBanner } from "./RunStuckBanner.js";
|
|
16
17
|
import { callAction } from "./use-action.js";
|
|
17
18
|
import { useChangeVersion } from "./use-change-version.js";
|
|
@@ -118,20 +119,25 @@ function formatScopeType(type) {
|
|
|
118
119
|
return type.replace(/[-_]+/g, " ");
|
|
119
120
|
}
|
|
120
121
|
// ─── History Popover ─────────────────────────────────────────────────────────
|
|
121
|
-
function formatThreadTime(ts) {
|
|
122
|
+
function formatThreadTime(ts, locale, yesterdayLabel) {
|
|
122
123
|
const d = new Date(ts);
|
|
123
124
|
const now = new Date();
|
|
124
125
|
const diffMs = now.getTime() - d.getTime();
|
|
125
126
|
const diffDays = Math.floor(diffMs / 86400000);
|
|
126
127
|
if (diffDays === 0)
|
|
127
|
-
return d.toLocaleTimeString(
|
|
128
|
+
return d.toLocaleTimeString(locale, {
|
|
129
|
+
hour: "numeric",
|
|
130
|
+
minute: "2-digit",
|
|
131
|
+
});
|
|
128
132
|
if (diffDays === 1)
|
|
129
|
-
return
|
|
133
|
+
return yesterdayLabel;
|
|
130
134
|
if (diffDays < 7)
|
|
131
|
-
return d.toLocaleDateString(
|
|
132
|
-
return d.toLocaleDateString(
|
|
135
|
+
return d.toLocaleDateString(locale, { weekday: "short" });
|
|
136
|
+
return d.toLocaleDateString(locale, { month: "short", day: "numeric" });
|
|
133
137
|
}
|
|
134
138
|
function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads = false, isLoadingMoreThreads = false, loadError, onSelect, onClose, onLoadMore, onSearch, onTogglePin, onRename, }) {
|
|
139
|
+
const t = useT();
|
|
140
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
135
141
|
const [search, setSearch] = useState("");
|
|
136
142
|
const [searchResults, setSearchResults] = useState(null);
|
|
137
143
|
const [isSearching, setIsSearching] = useState(false);
|
|
@@ -189,7 +195,7 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
|
|
|
189
195
|
const unpinnedThreads = filtered.filter((t) => t.pinnedAt == null);
|
|
190
196
|
const toHistoryItem = (thread) => {
|
|
191
197
|
const isActive = thread.id === activeThreadId;
|
|
192
|
-
const title = thread.title || thread.preview || "
|
|
198
|
+
const title = thread.title || thread.preview || t("agentChat.history.untitledChat");
|
|
193
199
|
return {
|
|
194
200
|
id: thread.id,
|
|
195
201
|
title,
|
|
@@ -198,10 +204,10 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
|
|
|
198
204
|
? thread.preview
|
|
199
205
|
: undefined,
|
|
200
206
|
timestamp: isActive
|
|
201
|
-
? "
|
|
207
|
+
? t("agentChat.history.active")
|
|
202
208
|
: openTabIds.has(thread.id)
|
|
203
|
-
? "
|
|
204
|
-
: formatThreadTime(thread.updatedAt),
|
|
209
|
+
? t("agentChat.history.open")
|
|
210
|
+
: formatThreadTime(thread.updatedAt, locale, t("agentChat.history.yesterday")),
|
|
205
211
|
pinned: thread.pinnedAt != null,
|
|
206
212
|
};
|
|
207
213
|
};
|
|
@@ -210,7 +216,7 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
|
|
|
210
216
|
? [
|
|
211
217
|
{
|
|
212
218
|
id: "pinned",
|
|
213
|
-
label: "
|
|
219
|
+
label: t("agentChat.history.pinned"),
|
|
214
220
|
items: pinnedThreads.map(toHistoryItem),
|
|
215
221
|
},
|
|
216
222
|
]
|
|
@@ -228,10 +234,13 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
|
|
|
228
234
|
const isPinned = threads.find((t) => t.id === id)?.pinnedAt != null;
|
|
229
235
|
onTogglePin(id, !isPinned);
|
|
230
236
|
}
|
|
231
|
-
: undefined, onRename: onRename, searchValue: search, onSearchChange: setSearch, searchPlaceholder: "
|
|
237
|
+
: undefined, onRename: onRename, searchValue: search, onSearchChange: setSearch, searchPlaceholder: t("agentChat.history.search"), searchInputRef: inputRef, loading: isSearching, loadingLabel: t("agentChat.history.searching"), error: loadError && !search.trim() ? loadError : undefined, emptyLabel: t("agentChat.history.empty"), emptySearchLabel: t("agentChat.history.noMatches"), footer: !search.trim() && hasMoreThreads ? (_jsx("button", { type: "button", onClick: () => onLoadMore?.(), disabled: isLoadingMoreThreads, className: "mx-1 mt-1 flex w-[calc(100%-0.5rem)] items-center justify-center rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-60", children: isLoadingMoreThreads
|
|
238
|
+
? t("agentChat.common.loading")
|
|
239
|
+
: t("agentChat.history.loadOlder") })) : undefined }) })] }));
|
|
232
240
|
}
|
|
233
241
|
// ─── Help Popover ────────────────────────────────────────────────────────────
|
|
234
242
|
function HelpPopover({ onClose }) {
|
|
243
|
+
const t = useT();
|
|
235
244
|
useEffect(() => {
|
|
236
245
|
const handler = (e) => {
|
|
237
246
|
if (e.key === "Escape")
|
|
@@ -243,16 +252,16 @@ function HelpPopover({ onClose }) {
|
|
|
243
252
|
const commands = [
|
|
244
253
|
{
|
|
245
254
|
name: "/clear",
|
|
246
|
-
description: "
|
|
255
|
+
description: t("agentChat.commands.clear"),
|
|
247
256
|
},
|
|
248
|
-
{ name: "/new", description: "
|
|
249
|
-
{ name: "/history", description: "
|
|
250
|
-
{ name: "/plan", description: "
|
|
251
|
-
{ name: "/act", description: "
|
|
252
|
-
{ name: "/help", description: "
|
|
253
|
-
{ name: "@", description: "
|
|
257
|
+
{ name: "/new", description: t("agentChat.commands.new") },
|
|
258
|
+
{ name: "/history", description: t("agentChat.commands.history") },
|
|
259
|
+
{ name: "/plan", description: t("agentChat.commands.plan") },
|
|
260
|
+
{ name: "/act", description: t("agentChat.commands.act") },
|
|
261
|
+
{ name: "/help", description: t("agentChat.commands.help") },
|
|
262
|
+
{ name: "@", description: t("agentChat.commands.mention") },
|
|
254
263
|
];
|
|
255
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }), _jsxs("div", { className: "absolute end-2 top-0 z-50 w-72 rounded-lg border border-border bg-popover shadow-lg", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border", children: [_jsx("span", { className: "text-xs font-medium text-foreground", children: "
|
|
264
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }), _jsxs("div", { className: "absolute end-2 top-0 z-50 w-72 rounded-lg border border-border bg-popover shadow-lg", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border", children: [_jsx("span", { className: "text-xs font-medium text-foreground", children: t("agentChat.commands.available") }), _jsx("button", { onClick: onClose, "aria-label": t("agentChat.commands.closeHelp"), className: "flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: _jsx(IconX, { size: 12 }) })] }), _jsx("div", { className: "py-1", children: commands.map((cmd) => (_jsxs("div", { className: "px-3 py-1.5", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: cmd.name }), _jsx("div", { className: "text-[11px] text-muted-foreground", children: cmd.description })] }, cmd.name))) })] })] }));
|
|
256
265
|
}
|
|
257
266
|
function readString(value) {
|
|
258
267
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -376,6 +385,7 @@ function runToAgentTeamTabInfo(run) {
|
|
|
376
385
|
};
|
|
377
386
|
}
|
|
378
387
|
export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderOverlay, contentHidden = false, apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, restoreActiveThread = true, browserTabId, threadUrlSync = false, scope = null, agentTeamPollMs = DEFAULT_AGENT_TEAM_POLL_MS, ...props }) {
|
|
388
|
+
const translate = useT();
|
|
379
389
|
const { enabled: threadUrlSyncEnabled, paramName: threadUrlParamName, routeThreadId, getPath: getThreadPath, navigate: navigateThreadUrl, } = resolveThreadUrlSync(threadUrlSync);
|
|
380
390
|
const threadRouteControlsActiveThread = threadUrlSyncEnabled &&
|
|
381
391
|
threadUrlSync !== true &&
|
|
@@ -1661,7 +1671,9 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1661
1671
|
const agentTeamStatus = chatTabStatusFromAgentTeamStatus(subAgentStatuses[id]);
|
|
1662
1672
|
return {
|
|
1663
1673
|
id,
|
|
1664
|
-
label: t?.title ||
|
|
1674
|
+
label: t?.title ||
|
|
1675
|
+
t?.preview?.slice(0, 30) ||
|
|
1676
|
+
translate("agentChat.tabs.newChat"),
|
|
1665
1677
|
status: agentTeamStatus ??
|
|
1666
1678
|
(runningThreads.has(id)
|
|
1667
1679
|
? "running"
|
|
@@ -1677,7 +1689,10 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1677
1689
|
if (!tabs.some((t) => t.id === id)) {
|
|
1678
1690
|
tabs.push({
|
|
1679
1691
|
id,
|
|
1680
|
-
label: subAgentNames[id] ||
|
|
1692
|
+
label: subAgentNames[id] ||
|
|
1693
|
+
(parentMap[id]
|
|
1694
|
+
? translate("agentChat.tabs.subAgent")
|
|
1695
|
+
: translate("agentChat.tabs.newChat")),
|
|
1681
1696
|
status: chatTabStatusFromAgentTeamStatus(subAgentStatuses[id]) ??
|
|
1682
1697
|
"running",
|
|
1683
1698
|
parentThreadId: parentMap[id],
|
|
@@ -1726,7 +1741,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1726
1741
|
activeTab?.parentThreadId === tab.id);
|
|
1727
1742
|
return (_jsxs("div", { ref: isActive ? activeTabRefCb : undefined, className: cn("agent-tab relative flex items-center rounded-md text-[11px] font-medium shrink-0 max-w-[130px]", isActive
|
|
1728
1743
|
? "bg-accent text-foreground ring-1 ring-inset ring-border/60 shadow-sm"
|
|
1729
|
-
: "text-muted-foreground hover:text-foreground hover:bg-accent/50"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2.5 py-1.5 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": "
|
|
1744
|
+
: "text-muted-foreground hover:text-foreground hover:bg-accent/50"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2.5 py-1.5 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": translate("agentChat.tabs.closeTab"), onClick: (e) => {
|
|
1730
1745
|
e.stopPropagation();
|
|
1731
1746
|
e.preventDefault();
|
|
1732
1747
|
closeTab(tab.id);
|
|
@@ -1740,13 +1755,13 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1740
1755
|
borderRadius: "0 6px 6px 0",
|
|
1741
1756
|
background: "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
|
|
1742
1757
|
}, children: _jsx(IconX, { size: 12 }) })] }, tab.id));
|
|
1743
|
-
}) }), _jsx(TooltipProvider, { delayDuration: 200, children: _jsxs("div", { className: "flex items-center gap-px shrink-0 ms-auto", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: addTab, "aria-label": "
|
|
1758
|
+
}) }), _jsx(TooltipProvider, { delayDuration: 200, children: _jsxs("div", { className: "flex items-center gap-px shrink-0 ms-auto", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: addTab, "aria-label": translate("agentChat.tabs.newChat"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 12 }) }) }), _jsx(TooltipContent, { children: translate("agentChat.tabs.newChat") })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => setShowHistory(!showHistory), "aria-label": translate("agentChat.tabs.allChats"), className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", showHistory && "bg-accent text-foreground"), children: _jsx(IconHistory, { size: 12 }) }) }), _jsx(TooltipContent, { children: translate("agentChat.tabs.allChats") })] })] }) })] }), hasSubTabs && (_jsx("div", { className: "flex items-center px-1 py-0.5 border-b border-border shrink-0 gap-0.5 bg-muted/30", children: _jsxs("div", { className: "agent-tabs-scroll flex items-center gap-0.5 min-w-0 overflow-x-auto flex-1", children: [_jsx("button", { onClick: () => switchThread(focusParentId), className: cn("flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium cursor-pointer", activeThreadId === focusParentId
|
|
1744
1759
|
? "bg-accent text-foreground"
|
|
1745
|
-
: "text-muted-foreground hover:bg-accent hover:text-foreground"), children: "
|
|
1760
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground"), children: translate("agentChat.tabs.main") }), childTabs.map((tab) => (_jsxs("div", { ref: tab.id === activeThreadId
|
|
1746
1761
|
? activeTabRefCb
|
|
1747
1762
|
: undefined, className: cn("agent-tab relative flex shrink-0 items-center rounded-md text-[10px] font-medium max-w-[130px]", tab.id === activeThreadId
|
|
1748
1763
|
? "bg-accent text-foreground"
|
|
1749
|
-
: "text-muted-foreground hover:bg-accent hover:text-foreground"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2 py-1 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.subAgentName || tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1 h-1 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": "
|
|
1764
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2 py-1 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.subAgentName || tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1 h-1 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": translate("agentChat.tabs.closeTab"), onClick: (e) => {
|
|
1750
1765
|
e.stopPropagation();
|
|
1751
1766
|
e.preventDefault();
|
|
1752
1767
|
closeTab(tab.id);
|
|
@@ -1772,7 +1787,9 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1772
1787
|
display: contentHidden || tabId !== activeThreadId ? "none" : "flex",
|
|
1773
1788
|
}, children: [_jsx(RunStuckBanner, { threadId: tabId, enabled: tabId === activeThreadId, apiUrl: apiUrl, autoRetry: true, autoRetryOwnerId: browserTabId, hasInFlightWork: () => chatRefs.current.get(tabId)?.hasInFlightWork() ?? false, isAwaitingResponse: () => chatRefs.current.get(tabId)?.isRunning() ?? false, onRetry: () => {
|
|
1774
1789
|
const handle = chatRefs.current.get(tabId);
|
|
1775
|
-
handle?.sendRecoveryMessage(
|
|
1790
|
+
handle?.sendRecoveryMessage(
|
|
1791
|
+
// i18n-ignore -- stable hidden agent instruction.
|
|
1792
|
+
"Continue from where you left off and finish my last request. Do not repeat completed work.", "continue");
|
|
1776
1793
|
} }), _jsx(AssistantChat, { ...props, dynamicSuggestions: tabDynamicSuggestions, ref: (handle) => {
|
|
1777
1794
|
if (handle) {
|
|
1778
1795
|
chatRefs.current.set(tabId, handle);
|
|
@@ -1788,7 +1805,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1788
1805
|
// the in-flight team chunk. Disable the composer and show a
|
|
1789
1806
|
// hint so users know to send via the orchestrator chat instead.
|
|
1790
1807
|
composerDisabled: Boolean(parentMap[tabId]), composerDisabledPlaceholder: parentMap[tabId]
|
|
1791
|
-
? "
|
|
1808
|
+
? translate("agentChat.composer.subAgentReadOnly")
|
|
1792
1809
|
: undefined })] }, tabId));
|
|
1793
1810
|
})] })] }));
|
|
1794
1811
|
}
|
|
@@ -7,8 +7,9 @@ import { IconPlus, IconExternalLink } from "@tabler/icons-react";
|
|
|
7
7
|
import React, { useState, useEffect, useCallback, useMemo, useRef, } from "react";
|
|
8
8
|
import { splitMarkdownBlocks } from "../../shared/markdown-block-split.js";
|
|
9
9
|
import { initialSmoothStreamingGraphemeCount, SMOOTH_STREAMING_COMMIT_INTERVAL_MS, smoothStreamingPunctuationDelayMs, smoothStreamingRevealCount, splitStreamingTextGraphemes, } from "../../shared/streaming-text-smoothing.js";
|
|
10
|
-
import { NEW_CHAT_ACTION_HREF } from "../error-format.js";
|
|
10
|
+
import { localizeKnownChatErrorText, NEW_CHAT_ACTION_HREF, } from "../error-format.js";
|
|
11
11
|
import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
|
|
12
|
+
import { useT } from "../i18n.js";
|
|
12
13
|
import { IframeEmbed, parseEmbedBody } from "../IframeEmbed.js";
|
|
13
14
|
import { cn } from "../utils.js";
|
|
14
15
|
import { LEGACY_CHART_SHORTHAND_LANG, LegacyChartShorthandChart, LegacyChartShorthandFallback, parseLegacyChartShorthand, wrapLegacyChartShorthandLines, } from "./legacy-chart-shorthand.js";
|
|
@@ -485,11 +486,12 @@ export function SmoothMarkdownText({ text, streaming, resetKey, statusType = "co
|
|
|
485
486
|
}
|
|
486
487
|
// ─── MarkdownText ──────────────────────────────────────────────────────────────
|
|
487
488
|
export function MarkdownText() {
|
|
489
|
+
const t = useT();
|
|
488
490
|
const textPart = useMessagePartText();
|
|
489
491
|
const messageRuntime = useMessageRuntime();
|
|
490
492
|
const message = messageRuntime.getState();
|
|
491
493
|
const textStreaming = React.useContext(TextStreamingContext);
|
|
492
494
|
const isLastAssistantMessage = message.role === "assistant" && message.isLast;
|
|
493
495
|
const statusType = textPart.status?.type ?? message.status?.type ?? "complete";
|
|
494
|
-
return (_jsx(SmoothMarkdownText, { text: textPart.text, streaming: textStreaming && isLastAssistantMessage, resetKey: message.id, statusType: statusType }));
|
|
496
|
+
return (_jsx(SmoothMarkdownText, { text: localizeKnownChatErrorText(textPart.text, t), streaming: textStreaming && isLastAssistantMessage, resetKey: message.id, statusType: statusType }));
|
|
495
497
|
}
|
|
@@ -9,7 +9,7 @@ interface FormattedMessageTimestamp {
|
|
|
9
9
|
short: string;
|
|
10
10
|
full: string;
|
|
11
11
|
}
|
|
12
|
-
export declare function formatMessageTimestamp(value: unknown): FormattedMessageTimestamp | null;
|
|
12
|
+
export declare function formatMessageTimestamp(value: unknown, locale?: string, yesterdayLabel?: string): FormattedMessageTimestamp | null;
|
|
13
13
|
export declare function MessageTimestamp({ timestamp, className, }: {
|
|
14
14
|
timestamp: FormattedMessageTimestamp;
|
|
15
15
|
className?: string;
|
|
@@ -184,6 +184,6 @@ export declare function AssistantMessage(): React.JSX.Element | null;
|
|
|
184
184
|
export declare function RunningActivityStatus({ label }: {
|
|
185
185
|
label: string;
|
|
186
186
|
}): React.JSX.Element;
|
|
187
|
-
export declare function ThinkingIndicator({ label
|
|
187
|
+
export declare function ThinkingIndicator({ label }?: {
|
|
188
188
|
label?: string;
|
|
189
189
|
}): React.JSX.Element;
|
|
@@ -14,6 +14,8 @@ import { writeClipboardText } from "../clipboard.js";
|
|
|
14
14
|
import { Dialog, DialogClose, DialogContent, DialogTitle, } from "../components/ui/dialog.js";
|
|
15
15
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../components/ui/dropdown-menu.js";
|
|
16
16
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
17
|
+
import { localizeKnownChatErrorText } from "../error-format.js";
|
|
18
|
+
import { DEFAULT_LOCALE, useFormatters, useOptionalLocale, useT, } from "../i18n.js";
|
|
17
19
|
import { ThumbsFeedback } from "../observability/ThumbsFeedback.js";
|
|
18
20
|
import { McpConnectionSuggestion } from "../resources/McpConnectionSuggestion.js";
|
|
19
21
|
import { isCallAgentToolCallShadowed, isToolCallActive, shadowedCallAgentToolCallIds, } from "../tool-display.js";
|
|
@@ -21,7 +23,7 @@ import { cn } from "../utils.js";
|
|
|
21
23
|
import { MarkdownText, renderMarkdownToClipboardHtml, SmoothMarkdownText, } from "./markdown-renderer.js";
|
|
22
24
|
import { getAssistantRunDurationMs } from "./repo-helpers.js";
|
|
23
25
|
import { getRunErrorMetadata, runErrorHeadline, runErrorKey, } from "./run-recovery.js";
|
|
24
|
-
import { ToolCallFallback, ToolActivityPresentation, ToolCallStackMotion, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunningRunIdContext, ChatRunningTurnIdContext, ChatRunDurationContext,
|
|
26
|
+
import { ToolCallFallback, ToolActivityPresentation, ToolCallStackMotion, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunningRunIdContext, ChatRunningTurnIdContext, ChatRunDurationContext, ReasoningCell, useLocalizedWorkedDuration, WorkedForSummary, toolCallHasPendingApproval, } from "./tool-call-display.js";
|
|
25
27
|
export { toolCallHasPendingApproval };
|
|
26
28
|
// ─── Pending selection context key ───────────────────────────────────────────
|
|
27
29
|
// Mirrored from AssistantChat to avoid a cross-import on a private constant.
|
|
@@ -29,7 +31,7 @@ const PENDING_SELECTION_KEY = "pending-selection-context";
|
|
|
29
31
|
// ─── displayableUserMessageText ───────────────────────────────────────────────
|
|
30
32
|
export function displayableUserMessageText(text) {
|
|
31
33
|
return text
|
|
32
|
-
.replace(/<context\b[^>]*>[\s\S]*?<\/context>\n?/gi, "")
|
|
34
|
+
.replace(/<context\b[^>]*>[\s\S]*?<\/context>\n?/gi, "") // i18n-ignore -- parsing regex, not UI copy.
|
|
33
35
|
.replace(/<context\b[^>]*>[\s\S]*$/gi, "")
|
|
34
36
|
.replace(/<\/context>/gi, "")
|
|
35
37
|
.trim();
|
|
@@ -56,14 +58,14 @@ function isSameCalendarDay(a, b) {
|
|
|
56
58
|
a.getMonth() === b.getMonth() &&
|
|
57
59
|
a.getDate() === b.getDate());
|
|
58
60
|
}
|
|
59
|
-
export function formatMessageTimestamp(value) {
|
|
61
|
+
export function formatMessageTimestamp(value, locale, yesterdayLabel = "Yesterday") {
|
|
60
62
|
const date = coerceMessageDate(value);
|
|
61
63
|
if (!date)
|
|
62
64
|
return null;
|
|
63
65
|
const now = new Date();
|
|
64
66
|
const yesterday = new Date(now);
|
|
65
67
|
yesterday.setDate(now.getDate() - 1);
|
|
66
|
-
const time = new Intl.DateTimeFormat(
|
|
68
|
+
const time = new Intl.DateTimeFormat(locale, {
|
|
67
69
|
hour: "numeric",
|
|
68
70
|
minute: "2-digit",
|
|
69
71
|
}).format(date);
|
|
@@ -72,16 +74,16 @@ export function formatMessageTimestamp(value) {
|
|
|
72
74
|
short = time;
|
|
73
75
|
}
|
|
74
76
|
else if (isSameCalendarDay(date, yesterday)) {
|
|
75
|
-
short =
|
|
77
|
+
short = `${yesterdayLabel} ${time}`;
|
|
76
78
|
}
|
|
77
79
|
else if (date.getFullYear() === now.getFullYear()) {
|
|
78
|
-
short = `${new Intl.DateTimeFormat(
|
|
80
|
+
short = `${new Intl.DateTimeFormat(locale, {
|
|
79
81
|
month: "short",
|
|
80
82
|
day: "numeric",
|
|
81
83
|
}).format(date)}, ${time}`;
|
|
82
84
|
}
|
|
83
85
|
else {
|
|
84
|
-
short = `${new Intl.DateTimeFormat(
|
|
86
|
+
short = `${new Intl.DateTimeFormat(locale, {
|
|
85
87
|
month: "short",
|
|
86
88
|
day: "numeric",
|
|
87
89
|
year: "numeric",
|
|
@@ -89,7 +91,7 @@ export function formatMessageTimestamp(value) {
|
|
|
89
91
|
}
|
|
90
92
|
return {
|
|
91
93
|
short,
|
|
92
|
-
full: new Intl.DateTimeFormat(
|
|
94
|
+
full: new Intl.DateTimeFormat(locale, {
|
|
93
95
|
weekday: "short",
|
|
94
96
|
month: "short",
|
|
95
97
|
day: "numeric",
|
|
@@ -104,6 +106,8 @@ export function MessageTimestamp({ timestamp, className, }) {
|
|
|
104
106
|
}
|
|
105
107
|
// ─── SelectionAttachedPill ────────────────────────────────────────────────────
|
|
106
108
|
export function SelectionAttachedPill() {
|
|
109
|
+
const t = useT();
|
|
110
|
+
const { formatNumber } = useFormatters();
|
|
107
111
|
const [length, setLength] = useState(null);
|
|
108
112
|
useEffect(() => {
|
|
109
113
|
let cancelled = false;
|
|
@@ -140,7 +144,10 @@ export function SelectionAttachedPill() {
|
|
|
140
144
|
}, []);
|
|
141
145
|
if (length === null || length === 0)
|
|
142
146
|
return null;
|
|
143
|
-
return (_jsx("div", { className: "agent-selection-attached-pill shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }),
|
|
147
|
+
return (_jsx("div", { className: "agent-selection-attached-pill shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }), _jsx("span", { children: t("agentChat.selection.attached", {
|
|
148
|
+
count: length,
|
|
149
|
+
formattedCount: formatNumber(length),
|
|
150
|
+
}) }), _jsx("button", { type: "button", "aria-label": t("agentChat.selection.clear"), onClick: () => {
|
|
144
151
|
setLength(null);
|
|
145
152
|
// Dispatch clear event; AssistantChat owns the DELETE call.
|
|
146
153
|
window.dispatchEvent(new CustomEvent("agent-panel:selection-clear-requested"));
|
|
@@ -193,7 +200,8 @@ export function resolveAssistantRequestId(message, activeRun, threadId) {
|
|
|
193
200
|
}
|
|
194
201
|
// ─── MessageBranchPicker ──────────────────────────────────────────────────────
|
|
195
202
|
export function MessageBranchPicker() {
|
|
196
|
-
|
|
203
|
+
const t = useT();
|
|
204
|
+
return (_jsxs(BranchPickerPrimitive.Root, { hideWhenSingleBranch: true, className: "flex items-center gap-0.5 text-[11px] text-muted-foreground", children: [_jsx(BranchPickerPrimitive.Previous, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.previousBranch"), className: "flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(IconChevronLeft, { className: "h-3.5 w-3.5" }) }) }), _jsxs("span", { className: "tabular-nums select-none", children: [_jsx(BranchPickerPrimitive.Number, {}), "/", _jsx(BranchPickerPrimitive.Count, {})] }), _jsx(BranchPickerPrimitive.Next, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.nextBranch"), className: "flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(IconChevronRight, { className: "h-3.5 w-3.5" }) }) })] }));
|
|
197
205
|
}
|
|
198
206
|
// ─── Mention rendering ────────────────────────────────────────────────────────
|
|
199
207
|
const mentionIconProps = {
|
|
@@ -299,23 +307,27 @@ function UserMessageAttachments() {
|
|
|
299
307
|
}) }));
|
|
300
308
|
}
|
|
301
309
|
export function ChatImageAttachmentPreview({ src, alt, }) {
|
|
310
|
+
const t = useT();
|
|
302
311
|
const [open, setOpen] = useState(false);
|
|
303
|
-
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label":
|
|
312
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": t("agentChat.composer.previewAttachment", { name: alt }), title: alt, onClick: () => setOpen(true), className: "h-16 w-16 cursor-zoom-in overflow-hidden rounded-lg border border-border/70 bg-muted/50 p-0 transition-[border-color,box-shadow] hover:border-foreground/40 hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx("img", { src: src, alt: alt, className: "h-full w-full object-cover" }) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { hideClose: true, "aria-describedby": undefined, className: "fixed inset-0 left-0 top-0 flex h-screen max-h-none w-screen max-w-none translate-x-0 translate-y-0 items-center justify-center gap-0 overflow-hidden rounded-none border-0 bg-foreground/90 p-0 text-background shadow-none backdrop-blur-sm dark:bg-background/95 dark:text-foreground", onOpenAutoFocus: (event) => event.preventDefault(), children: [_jsx(DialogTitle, { className: "sr-only", children: alt || t("agentChat.composer.imagePreview") }), _jsx("div", { className: "flex h-full w-full items-center justify-center overflow-auto p-6", onClick: (event) => {
|
|
304
313
|
if (event.target === event.currentTarget)
|
|
305
314
|
setOpen(false);
|
|
306
|
-
}, children: _jsx("img", { src: src, alt: alt, draggable: false, className: "max-h-[90vh] max-w-[92vw] rounded-lg object-contain shadow-2xl" }) }), _jsx(DialogClose, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "
|
|
315
|
+
}, children: _jsx("img", { src: src, alt: alt, draggable: false, className: "max-h-[90vh] max-w-[92vw] rounded-lg object-contain shadow-2xl" }) }), _jsx(DialogClose, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.composer.closePreview"), className: "absolute end-4 top-4 inline-flex size-9 items-center justify-center rounded-full border border-background/25 bg-background/10 text-background transition-colors hover:bg-background/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring dark:border-foreground/25 dark:bg-foreground/10 dark:text-foreground dark:hover:bg-foreground/20", children: _jsx(IconX, { className: "size-4" }) }) })] }) })] }));
|
|
307
316
|
}
|
|
308
317
|
// ─── UserMessageEditComposer ──────────────────────────────────────────────────
|
|
309
318
|
function UserMessageEditComposer() {
|
|
310
|
-
|
|
319
|
+
const t = useT();
|
|
320
|
+
return (_jsxs(ComposerPrimitive.Root, { className: "flex flex-col gap-2 rounded-lg border border-border bg-background px-3 py-2 shadow-sm", children: [_jsx(ComposerPrimitive.Input, { className: "w-full resize-none bg-transparent text-sm leading-relaxed text-foreground placeholder:text-muted-foreground focus:outline-none", rows: 1, submitMode: "enter" }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx("button", { type: "button", className: "cursor-pointer rounded-md px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: t("agentChat.common.cancel") }) }), _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx("button", { type: "submit", className: "cursor-pointer rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background hover:opacity-90 disabled:opacity-50", children: t("agentChat.common.save") }) })] })] }));
|
|
311
321
|
}
|
|
312
322
|
// ─── MessageActionsMenu ────────────────────────────────────────────────────────
|
|
313
323
|
export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}) {
|
|
324
|
+
const t = useT();
|
|
325
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
314
326
|
const [open, setOpen] = useState(false);
|
|
315
327
|
const [copied, setCopied] = useState(null);
|
|
316
328
|
const messageRuntime = useMessageRuntime();
|
|
317
329
|
const actionsCtx = React.useContext(MessageActionsContext);
|
|
318
|
-
const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt);
|
|
330
|
+
const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt, locale, t("agentChat.history.yesterday"));
|
|
319
331
|
const handleCopyMessage = useCallback(() => {
|
|
320
332
|
const m = messageRuntime.getState();
|
|
321
333
|
const text = m.content
|
|
@@ -362,28 +374,32 @@ export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}
|
|
|
362
374
|
setOpen(false);
|
|
363
375
|
onRevert?.();
|
|
364
376
|
}, [onRevert]);
|
|
365
|
-
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": "
|
|
377
|
+
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": t("agentChat.message.actions"), className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), t("agentChat.message.forkChat")] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
|
|
366
378
|
e.preventDefault();
|
|
367
379
|
handleCopyMessage();
|
|
368
|
-
}, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message"
|
|
380
|
+
}, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message"
|
|
381
|
+
? t("agentChat.common.copied")
|
|
382
|
+
: t("agentChat.message.copyMessage")] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
|
|
369
383
|
e.preventDefault();
|
|
370
384
|
handleCopyRequestId();
|
|
371
385
|
}, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id"
|
|
372
|
-
? "
|
|
386
|
+
? t("agentChat.common.copied")
|
|
373
387
|
: copied === "id-unavailable"
|
|
374
|
-
? "
|
|
388
|
+
? t("agentChat.message.requestIdUnavailable")
|
|
375
389
|
: copied === "id-failed"
|
|
376
|
-
? "
|
|
377
|
-
: "
|
|
390
|
+
? t("agentChat.recovery.copyFailed")
|
|
391
|
+
: t("agentChat.message.copyRequestId")] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), t("agentChat.message.revertHere")] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: t("agentChat.message.sentAt", { time: timestamp.short }) })] }))] })] }));
|
|
378
392
|
}
|
|
379
393
|
// ─── UserMessage ──────────────────────────────────────────────────────────────
|
|
380
394
|
export function UserMessage() {
|
|
395
|
+
const t = useT();
|
|
396
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
381
397
|
const [expanded, setExpanded] = useState(false);
|
|
382
398
|
const [isExpandable, setIsExpandable] = useState(false);
|
|
383
399
|
const contentRef = useRef(null);
|
|
384
400
|
const messageRuntime = useMessageRuntime();
|
|
385
401
|
const message = messageRuntime.getState();
|
|
386
|
-
const timestamp = formatMessageTimestamp(message.createdAt);
|
|
402
|
+
const timestamp = formatMessageTimestamp(message.createdAt, locale, t("agentChat.history.yesterday"));
|
|
387
403
|
const isEditing = useComposer((state) => state.isEditing);
|
|
388
404
|
const chatRunning = React.useContext(ChatRunningContext);
|
|
389
405
|
const hidden = isHiddenUserMessage(message);
|
|
@@ -429,7 +445,9 @@ export function UserMessage() {
|
|
|
429
445
|
e.clipboardData.setData("text/plain", div.textContent || "");
|
|
430
446
|
}, children: [_jsx("div", { ref: contentRef, className: cn("whitespace-pre-wrap break-words", !expanded && isExpandable && "max-h-[200px] overflow-hidden"), children: _jsx(MessagePrimitive.Parts, { components: {
|
|
431
447
|
Text: UserMessageText,
|
|
432
|
-
} }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" })), !chatRunning && hasDisplayableText && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Edit, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "
|
|
448
|
+
} }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" })), !chatRunning && hasDisplayableText && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Edit, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.edit"), className: "absolute -left-8 top-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/0 transition-colors duration-150 group-hover:text-muted-foreground/70 group-hover:hover:bg-accent group-hover:hover:text-foreground", children: _jsx(IconPencil, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "left", className: "text-xs", children: t("agentChat.message.edit") })] }) }))] })), hasDisplayableText && isExpandable && (_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", expanded && "rotate-180") }), expanded
|
|
449
|
+
? t("agentChat.common.collapse")
|
|
450
|
+
: t("agentChat.common.expand")] })), _jsxs("div", { className: "mt-1 flex items-center justify-end gap-1", children: [_jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] })] }) }));
|
|
433
451
|
}
|
|
434
452
|
// ─── AssistantMessage ─────────────────────────────────────────────────────────
|
|
435
453
|
function assistantMessageHasRenderableContent(message) {
|
|
@@ -790,17 +808,27 @@ export function shouldShowInlineRunError({ runError, bannerRunErrorKey, }) {
|
|
|
790
808
|
return runErrorKey(runError) !== bannerRunErrorKey;
|
|
791
809
|
}
|
|
792
810
|
export function InlineRunErrorNotice({ info, durationMs, onRetry, }) {
|
|
811
|
+
const t = useT();
|
|
812
|
+
const formatDuration = useLocalizedWorkedDuration();
|
|
793
813
|
const [open, setOpen] = useState(false);
|
|
794
|
-
const headline = runErrorHeadline(info
|
|
814
|
+
const headline = runErrorHeadline(info, {
|
|
815
|
+
recoverable: t("agentChat.error.stopped"),
|
|
816
|
+
terminal: t("agentChat.error.failed"),
|
|
817
|
+
});
|
|
795
818
|
const label = durationMs != null && durationMs >= 1000
|
|
796
|
-
?
|
|
819
|
+
? t("agentChat.error.afterDuration", {
|
|
820
|
+
headline,
|
|
821
|
+
duration: formatDuration(durationMs),
|
|
822
|
+
})
|
|
797
823
|
: headline;
|
|
798
|
-
return (_jsxs("div", { className: "my-1 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex cursor-pointer items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconAlertTriangle, { className: "size-3.5 shrink-0 text-amber-500" }), _jsx("span", { className: "truncate", children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), open && (_jsxs("div", { className: "mt-1 rounded-md border border-border/60 bg-background/70 p-2 text-[11px] leading-relaxed text-muted-foreground", children: [_jsx("p", { className: "whitespace-pre-wrap break-words", children: info.message }), info.errorCode && (_jsxs("div", { className: "mt-1 font-mono", children: ["code: ", info.errorCode] })), info.runId && _jsxs("div", { className: "font-mono", children: ["run: ", info.runId] }), info.details && (_jsx("pre", { className: "mt-1 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details })), onRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "mt-2 inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { className: "size-3" }), "
|
|
824
|
+
return (_jsxs("div", { className: "my-1 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex cursor-pointer items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconAlertTriangle, { className: "size-3.5 shrink-0 text-amber-500" }), _jsx("span", { className: "truncate", children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), open && (_jsxs("div", { className: "mt-1 rounded-md border border-border/60 bg-background/70 p-2 text-[11px] leading-relaxed text-muted-foreground", children: [_jsx("p", { className: "whitespace-pre-wrap break-words", children: localizeKnownChatErrorText(info.message, t) }), info.errorCode && (_jsxs("div", { className: "mt-1 font-mono", children: ["code: ", info.errorCode] })), info.runId && _jsxs("div", { className: "font-mono", children: ["run: ", info.runId] }), info.details && (_jsx("pre", { className: "mt-1 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details })), onRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "mt-2 inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { className: "size-3" }), t("agentChat.common.retry")] }))] }))] }));
|
|
799
825
|
}
|
|
800
826
|
function MissingFinalResponseNotice({ messageId, text, animate, onRevealComplete, }) {
|
|
801
827
|
return (_jsx("div", { className: "my-1 w-full text-muted-foreground", role: "status", "aria-live": "polite", "data-testid": "missing-final-response", children: _jsx(SmoothMarkdownText, { text: text, streaming: animate, resetKey: `missing-final-response:${messageId}`, statusType: animate ? "running" : "complete", onRevealComplete: animate ? onRevealComplete : undefined }) }));
|
|
802
828
|
}
|
|
803
829
|
export function AssistantMessage() {
|
|
830
|
+
const t = useT();
|
|
831
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
804
832
|
const [restoreState, setRestoreState] = useState("idle");
|
|
805
833
|
const [restoreError, setRestoreError] = useState(null);
|
|
806
834
|
const messageRuntime = useMessageRuntime();
|
|
@@ -811,7 +839,7 @@ export function AssistantMessage() {
|
|
|
811
839
|
const lastRunDurationMs = React.useContext(ChatRunDurationContext);
|
|
812
840
|
const msg = messageRuntime.getState();
|
|
813
841
|
const persistedDurationMs = getAssistantRunDurationMs(msg);
|
|
814
|
-
const timestamp = formatMessageTimestamp(msg.createdAt);
|
|
842
|
+
const timestamp = formatMessageTimestamp(msg.createdAt, locale, t("agentChat.history.yesterday"));
|
|
815
843
|
const isLast = msg.isLast;
|
|
816
844
|
const wasLiveRef = useRef(false);
|
|
817
845
|
const messageRunId = assistantMessageRunId(msg);
|
|
@@ -848,12 +876,12 @@ export function AssistantMessage() {
|
|
|
848
876
|
const showMissingFinalResponse = useSettledFlag(missingFinalResponseCandidate, isLast ? MISSING_FINAL_RESPONSE_SETTLE_MS : 0);
|
|
849
877
|
const shouldShowUserStoppedNotice = isUserStoppedRun && isLast && responseConnectionText.trim().length === 0;
|
|
850
878
|
const missingFinalResponseNoticeText = shouldShowUserStoppedNotice
|
|
851
|
-
? "
|
|
879
|
+
? t("agentChat.error.stopped")
|
|
852
880
|
: isUserStoppedRun
|
|
853
881
|
? null
|
|
854
882
|
: (missingWarningText ??
|
|
855
883
|
(showMissingFinalResponse
|
|
856
|
-
? "
|
|
884
|
+
? t("agentChat.message.missingFinal")
|
|
857
885
|
: null));
|
|
858
886
|
const animateMissingFinalResponse = Boolean(isLast && missingFinalResponseNoticeText && wasLiveRef.current);
|
|
859
887
|
const missingFinalResponseAnimationKey = animateMissingFinalResponse
|
|
@@ -950,7 +978,7 @@ export function AssistantMessage() {
|
|
|
950
978
|
if (restoreState !== "confirming" || !cpCtx)
|
|
951
979
|
return;
|
|
952
980
|
if (!messageRunId) {
|
|
953
|
-
setRestoreError("
|
|
981
|
+
setRestoreError(t("agentChat.message.noRestoreRun"));
|
|
954
982
|
setRestoreState("error");
|
|
955
983
|
return;
|
|
956
984
|
}
|
|
@@ -968,14 +996,18 @@ export function AssistantMessage() {
|
|
|
968
996
|
const payload = (await restoreRes.json().catch(() => null));
|
|
969
997
|
setRestoreError(typeof payload?.error === "string"
|
|
970
998
|
? payload.error
|
|
971
|
-
:
|
|
999
|
+
: t("agentChat.message.restoreFailed", {
|
|
1000
|
+
status: restoreRes.status,
|
|
1001
|
+
}));
|
|
972
1002
|
setRestoreState("error");
|
|
973
1003
|
}
|
|
974
1004
|
catch (err) {
|
|
975
|
-
setRestoreError(err instanceof Error
|
|
1005
|
+
setRestoreError(err instanceof Error
|
|
1006
|
+
? err.message
|
|
1007
|
+
: t("agentChat.message.restoreRequestFailed"));
|
|
976
1008
|
setRestoreState("error");
|
|
977
1009
|
}
|
|
978
|
-
}, [restoreState, cpCtx, messageRunId]);
|
|
1010
|
+
}, [restoreState, cpCtx, messageRunId, t]);
|
|
979
1011
|
const cancelRestore = useCallback(() => {
|
|
980
1012
|
setRestoreError(null);
|
|
981
1013
|
setRestoreState("idle");
|
|
@@ -1050,12 +1082,14 @@ export function AssistantMessage() {
|
|
|
1050
1082
|
? messageActions?.onRetryRunError
|
|
1051
1083
|
: undefined })), missingWarningText == null &&
|
|
1052
1084
|
missingFinalResponseNoticeText != null &&
|
|
1053
|
-
!showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore, threadId: cpCtx?.threadId ?? "" }), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "
|
|
1085
|
+
!showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore, threadId: cpCtx?.threadId ?? "" }), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.regenerate"), className: `flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-accent hover:text-foreground ${messageFooterFadeClassName} disabled:cursor-not-allowed disabled:opacity-40`, children: _jsx(IconRefresh, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: t("agentChat.message.regenerate") })] }) })), _jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: t("agentChat.message.restoreQuestion") }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.cancel") })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), t("agentChat.message.restoring")] })) : restoreState === "error" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: restoreError }), _jsx("button", { onClick: cancelRestore, className: "cursor-pointer rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.dismiss") })] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: msg.index }))] }))] }));
|
|
1054
1086
|
}
|
|
1055
1087
|
// ─── RunningActivityStatus / ThinkingIndicator ────────────────────────────────
|
|
1056
1088
|
export function RunningActivityStatus({ label }) {
|
|
1057
1089
|
return (_jsx("div", { className: "agent-running-activity", children: _jsx(ThinkingIndicator, { label: label }) }));
|
|
1058
1090
|
}
|
|
1059
|
-
export function ThinkingIndicator({ label
|
|
1060
|
-
|
|
1091
|
+
export function ThinkingIndicator({ label } = {}) {
|
|
1092
|
+
const t = useT();
|
|
1093
|
+
const resolvedLabel = label ?? t("agentChat.status.thinking");
|
|
1094
|
+
return (_jsx("div", { className: "agent-thinking-indicator", role: "status", "aria-live": "polite", "aria-label": resolvedLabel, children: _jsx("span", { className: "agent-thinking-indicator__text", children: resolvedLabel }) }));
|
|
1061
1095
|
}
|
|
@@ -16,7 +16,10 @@ export declare function getRunErrorMetadata(message: unknown): RunErrorInfo | nu
|
|
|
16
16
|
* the same run is never announced twice.
|
|
17
17
|
*/
|
|
18
18
|
export declare function runErrorKey(info: RunErrorInfo): string;
|
|
19
|
-
export declare function runErrorHeadline(info: RunErrorInfo
|
|
19
|
+
export declare function runErrorHeadline(info: RunErrorInfo, labels?: {
|
|
20
|
+
recoverable: string;
|
|
21
|
+
terminal: string;
|
|
22
|
+
}): string;
|
|
20
23
|
export declare function getRequestModeMetadata(message: unknown): "act" | "plan" | null;
|
|
21
24
|
export declare function BuilderConnectCta({ variant, onConnected, }: {
|
|
22
25
|
variant?: "primary" | "compact";
|