@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useT } from "../../i18n.js";
|
|
2
3
|
const DEFAULT_COLORS = [
|
|
3
4
|
"hsl(var(--primary))",
|
|
4
5
|
"hsl(199 89% 48%)",
|
|
@@ -105,5 +106,6 @@ function ChartBody({ chart }) {
|
|
|
105
106
|
return (_jsxs(_Fragment, { children: [axes, paths.map(({ series, color, areaPath }) => chart.type === "area" && areaPath ? (_jsx("path", { d: areaPath, fill: color, opacity: "0.18", stroke: "none" }, series.key)) : null), paths.map(({ series, color, linePath, points }) => (_jsxs("g", { children: [_jsx("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }), points.map((point, index) => (_jsx("circle", { cx: point.x, cy: point.y, r: "3", fill: "hsl(var(--background))", stroke: color, strokeWidth: "2" }, index)))] }, series.key)))] }));
|
|
106
107
|
}
|
|
107
108
|
export function DataChartRenderer({ chart }) {
|
|
108
|
-
|
|
109
|
+
const t = useT();
|
|
110
|
+
return (_jsx("div", { className: "h-60 w-full min-w-0 overflow-hidden", children: _jsx("svg", { className: "agent-data-chart-renderer h-full w-full", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": chart.title ?? t("agentChat.widget.dataChart"), preserveAspectRatio: "none", children: _jsx(ChartBody, { chart: chart }) }) }));
|
|
109
111
|
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { IconChartBar } from "@tabler/icons-react";
|
|
3
3
|
import { lazy, Suspense, useEffect, useState } from "react";
|
|
4
|
+
import { DEFAULT_LOCALE, useOptionalLocale, useT } from "../../i18n.js";
|
|
4
5
|
const LazyDataChartRenderer = lazy(() => import("./DataChartRenderer.js").then((module) => ({
|
|
5
6
|
default: module.DataChartRenderer,
|
|
6
7
|
})));
|
|
7
8
|
export function DataChartWidget({ chart }) {
|
|
9
|
+
const t = useT();
|
|
10
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
8
11
|
const [mounted, setMounted] = useState(false);
|
|
9
12
|
useEffect(() => {
|
|
10
13
|
setMounted(true);
|
|
11
14
|
}, []);
|
|
12
|
-
const fallback = (_jsx("div", { className: "flex h-60 items-center justify-center rounded-md bg-muted/30 text-xs text-muted-foreground", children: "
|
|
13
|
-
return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconChartBar, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: chart.title ?? "
|
|
15
|
+
const fallback = (_jsx("div", { className: "flex h-60 items-center justify-center rounded-md bg-muted/30 text-xs text-muted-foreground", children: t("agentChat.widget.chart") }));
|
|
16
|
+
return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconChartBar, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: chart.title ?? t("agentChat.widget.dataChart") }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [t("agentChat.widget.points", {
|
|
17
|
+
count: chart.data.length,
|
|
18
|
+
formattedCount: chart.data.length.toLocaleString(locale),
|
|
19
|
+
}), chart.sampled ? ` ${t("agentChat.widget.sampled")}` : ""] })] })] }), _jsx("div", { className: "p-3", children: !mounted ? (fallback) : (_jsx(Suspense, { fallback: fallback, children: _jsx(LazyDataChartRenderer, { chart: chart }) })) })] }));
|
|
14
20
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { IconDatabase } from "@tabler/icons-react";
|
|
3
|
+
import { useT } from "../../i18n.js";
|
|
3
4
|
import { DataChartWidget } from "./DataChartWidget.js";
|
|
4
5
|
import { DataTableWidget } from "./DataTableWidget.js";
|
|
5
6
|
function SummaryPill({ label, value }) {
|
|
@@ -8,7 +9,8 @@ function SummaryPill({ label, value }) {
|
|
|
8
9
|
return (_jsxs("div", { className: "rounded-md border border-border bg-muted/30 px-2 py-1", children: [_jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: label }), _jsx("div", { className: "text-xs font-medium text-foreground", children: typeof value === "number" ? value.toLocaleString() : String(value) })] }));
|
|
9
10
|
}
|
|
10
11
|
export function DataInsightsWidget({ result }) {
|
|
11
|
-
const
|
|
12
|
+
const t = useT();
|
|
13
|
+
const title = result.display?.title ?? result.title ?? t("agentChat.widget.dataInsights");
|
|
12
14
|
const summary = result.summary ?? {};
|
|
13
15
|
return (_jsxs("div", { className: "my-1.5 space-y-2", children: [_jsxs("div", { className: "rounded-lg border border-border bg-background p-3 text-foreground shadow-sm", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(IconDatabase, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), result.display?.description ? (_jsx("div", { className: "text-xs text-muted-foreground", children: result.display.description })) : null] })] }), _jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: Object.entries(summary)
|
|
14
16
|
.filter(([, value]) => typeof value !== "object")
|
|
@@ -4,23 +4,26 @@ import { requestAgentSidebarOpen } from "../../agent-sidebar-state.js";
|
|
|
4
4
|
import { appPath } from "../../api-path.js";
|
|
5
5
|
import { startAgentChatViewTransition } from "../../chat-view-transition.js";
|
|
6
6
|
import { downloadFile, toCSVTable } from "../../db-admin/export-utils.js";
|
|
7
|
+
import { DEFAULT_LOCALE, useOptionalLocale, useT } from "../../i18n.js";
|
|
7
8
|
import { cn } from "../../utils.js";
|
|
8
9
|
const SAFE_ACTION_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
|
|
9
|
-
function formatCell(value) {
|
|
10
|
+
function formatCell(value, locale, booleanLabels = { yes: "Yes", no: "No" }) {
|
|
10
11
|
if (value === undefined || value === null || value === "")
|
|
11
12
|
return "—";
|
|
12
13
|
if (typeof value === "boolean")
|
|
13
|
-
return value ?
|
|
14
|
+
return value ? booleanLabels.yes : booleanLabels.no;
|
|
14
15
|
if (typeof value === "number")
|
|
15
|
-
return value.toLocaleString();
|
|
16
|
+
return value.toLocaleString(locale);
|
|
16
17
|
if (Array.isArray(value))
|
|
17
|
-
return value
|
|
18
|
+
return value
|
|
19
|
+
.map((item) => formatCell(item, locale, booleanLabels))
|
|
20
|
+
.join(", ");
|
|
18
21
|
if (typeof value === "object")
|
|
19
22
|
return JSON.stringify(value);
|
|
20
23
|
const text = String(value);
|
|
21
24
|
const timestamp = Date.parse(text);
|
|
22
25
|
if (Number.isFinite(timestamp) && /^\d{4}-\d{2}-\d{2}T/.test(text)) {
|
|
23
|
-
return new Intl.DateTimeFormat(
|
|
26
|
+
return new Intl.DateTimeFormat(locale, {
|
|
24
27
|
month: "short",
|
|
25
28
|
day: "numeric",
|
|
26
29
|
hour: "numeric",
|
|
@@ -83,6 +86,8 @@ function slugifyFilenamePart(value) {
|
|
|
83
86
|
return slug || "data-table";
|
|
84
87
|
}
|
|
85
88
|
export function DataTableWidget({ table, action, }) {
|
|
89
|
+
const t = useT();
|
|
90
|
+
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
86
91
|
const rows = table.rows ?? [];
|
|
87
92
|
const actionHref = action ? normalizeActionHref(action.href) : null;
|
|
88
93
|
const canExportCsv = table.columns.length > 0;
|
|
@@ -92,9 +97,15 @@ export function DataTableWidget({ table, action, }) {
|
|
|
92
97
|
const stamp = new Date().toISOString().slice(0, 10);
|
|
93
98
|
downloadFile(`${slugifyFilenamePart(table.title)}-${stamp}.csv`, "text/csv;charset=utf-8", toCSVTable(table.columns.map((column) => column.label), rows.map((row) => table.columns.map((column) => row[column.key]))));
|
|
94
99
|
};
|
|
95
|
-
return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconTable, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: table.title ?? "
|
|
96
|
-
?
|
|
97
|
-
|
|
100
|
+
return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconTable, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: table.title ?? t("agentChat.widget.dataTable") }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [typeof table.totalRows === "number"
|
|
101
|
+
? t("agentChat.widget.rows", {
|
|
102
|
+
count: table.totalRows,
|
|
103
|
+
formattedCount: table.totalRows.toLocaleString(locale),
|
|
104
|
+
})
|
|
105
|
+
: t("agentChat.widget.rows", {
|
|
106
|
+
count: rows.length,
|
|
107
|
+
formattedCount: rows.length.toLocaleString(locale),
|
|
108
|
+
}), table.truncated ? ` ${t("agentChat.widget.sampled")}` : ""] })] }), action && actionHref ? (_jsxs("a", { href: actionHref, onClick: (event) => {
|
|
98
109
|
if (event.defaultPrevented ||
|
|
99
110
|
event.button !== 0 ||
|
|
100
111
|
event.metaKey ||
|
|
@@ -106,5 +117,8 @@ export function DataTableWidget({ table, action, }) {
|
|
|
106
117
|
}
|
|
107
118
|
event.preventDefault();
|
|
108
119
|
navigateSameAppHref(actionHref);
|
|
109
|
-
}, className: "inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline", children: [action.label, _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, _jsxs("button", { type: "button", onClick: exportCsv, disabled: !canExportCsv, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50", children: [_jsx(IconDownload, { className: "h-3 w-3" }), "
|
|
120
|
+
}, className: "inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline", children: [action.label, _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, _jsxs("button", { type: "button", onClick: exportCsv, disabled: !canExportCsv, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50", children: [_jsx(IconDownload, { className: "h-3 w-3" }), t("agentChat.widget.downloadCsv")] })] }), _jsx("div", { className: "max-h-[360px] overflow-auto", children: _jsxs("table", { className: "w-full min-w-[420px] border-collapse text-xs", children: [_jsx("thead", { className: "sticky top-0 z-10 bg-muted/70 backdrop-blur", children: _jsx("tr", { children: table.columns.map((column) => (_jsx("th", { className: cn("border-b border-border px-3 py-2 font-medium text-muted-foreground", column.align === "right" ? "text-right" : "text-left"), children: column.label }, column.key))) }) }), _jsx("tbody", { children: rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: Math.max(1, table.columns.length), className: "px-3 py-8 text-center text-muted-foreground", children: t("agentChat.widget.noRows") }) })) : (rows.map((row, rowIndex) => (_jsx("tr", { className: "border-b border-border/70 last:border-0", children: table.columns.map((column) => (_jsx("td", { className: cn("max-w-[260px] px-3 py-2 align-top text-foreground", column.align === "right" ? "text-right" : "text-left"), children: _jsx("span", { className: "line-clamp-3 break-words", children: formatCell(row[column.key], locale, {
|
|
121
|
+
yes: t("agentChat.common.yes"),
|
|
122
|
+
no: t("agentChat.common.no"),
|
|
123
|
+
}) }) }, `${rowIndex}-${column.key}`))) }, String(row.id ?? rowIndex))))) })] }) })] }));
|
|
110
124
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { lazy, Suspense } from "react";
|
|
3
3
|
import { ACTION_CHAT_UI_DATA_CHART_RENDERER, ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER, ACTION_CHAT_UI_DATA_TABLE_RENDERER, ACTION_CHAT_UI_DATA_WIDGET_RENDERER, ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER, ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER, } from "../../../action-ui.js";
|
|
4
|
+
import { useT } from "../../i18n.js";
|
|
4
5
|
import { registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, } from "../tool-render-registry.js";
|
|
5
6
|
import { DATA_CHART_WIDGET, DATA_INSIGHTS_WIDGET, DATA_TABLE_WIDGET, normalizeDataWidgetKind, normalizeDataWidgetResult, } from "./data-widget-types.js";
|
|
6
7
|
import { normalizeInlineExtensionToolResult } from "./inline-extension-result.js";
|
|
@@ -80,7 +81,8 @@ function renderDataWidget(context) {
|
|
|
80
81
|
return null;
|
|
81
82
|
}
|
|
82
83
|
function BuiltinToolRendererSkeleton({ framed = true }) {
|
|
83
|
-
|
|
84
|
+
const t = useT();
|
|
85
|
+
return (_jsx("div", { "aria-label": t("agentChat.widget.loadingToolResult"), className: framed
|
|
84
86
|
? "my-1.5 h-24 animate-pulse rounded-lg border border-border bg-muted/30"
|
|
85
87
|
: "h-24 animate-pulse bg-muted/30" }));
|
|
86
88
|
}
|
|
@@ -8,7 +8,7 @@ type MessageScrollerItemProps = React.ComponentProps<typeof ShadcnMessageScrolle
|
|
|
8
8
|
type MessageScrollerButtonProps = React.ComponentProps<typeof ShadcnMessageScroller.Button>;
|
|
9
9
|
declare function MessageScrollerProvider(props: MessageScrollerProviderProps): React.JSX.Element;
|
|
10
10
|
declare function MessageScroller({ className, ...props }: MessageScrollerRootProps): React.JSX.Element;
|
|
11
|
-
declare function MessageScrollerViewport({ className, children, ...props }: MessageScrollerViewportProps): React.JSX.Element;
|
|
11
|
+
declare function MessageScrollerViewport({ className, children, "aria-label": ariaLabel, ...props }: MessageScrollerViewportProps): React.JSX.Element;
|
|
12
12
|
declare function MessageScrollerContent({ className, spacerClassName, ...props }: MessageScrollerContentProps): React.JSX.Element;
|
|
13
13
|
declare function MessageScrollerItem(props: MessageScrollerItemProps): React.JSX.Element;
|
|
14
14
|
declare function MessageScrollerButton({ className, children, render, ...props }: MessageScrollerButtonProps): React.JSX.Element;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { MessageScroller as ShadcnMessageScroller, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, } from "@shadcn/react/message-scroller";
|
|
3
3
|
import { IconChevronDown } from "@tabler/icons-react";
|
|
4
|
+
import { useT } from "../../i18n.js";
|
|
4
5
|
import { cn } from "../../utils.js";
|
|
5
6
|
function MessageScrollerProvider(props) {
|
|
6
7
|
return _jsx(ShadcnMessageScroller.Provider, { ...props });
|
|
@@ -8,12 +9,14 @@ function MessageScrollerProvider(props) {
|
|
|
8
9
|
function MessageScroller({ className, ...props }) {
|
|
9
10
|
return (_jsx(ShadcnMessageScroller.Root, { className: cn("relative flex min-h-0 flex-1 flex-col", className), ...props }));
|
|
10
11
|
}
|
|
11
|
-
function MessageScrollerViewport({ className, children, ...props }) {
|
|
12
|
+
function MessageScrollerViewport({ className, children, "aria-label": ariaLabel, ...props }) {
|
|
13
|
+
const t = useT();
|
|
12
14
|
const { start: hasContentAbove } = useMessageScrollerScrollable();
|
|
13
15
|
return (_jsx(ShadcnMessageScroller.Viewport, { className: cn("min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
|
|
14
16
|
// The base class carries the mask itself so it is never added or
|
|
15
17
|
// removed; the modifier only widens the fade. See agent-native.css.
|
|
16
|
-
"message-scroller-viewport", hasContentAbove && "message-scroller-viewport--top-fade", className),
|
|
18
|
+
"message-scroller-viewport", hasContentAbove && "message-scroller-viewport--top-fade", className), "aria-label": ariaLabel ??
|
|
19
|
+
t("agentChat.message.messages", { defaultValue: "Messages" }), ...props, children: children }));
|
|
17
20
|
}
|
|
18
21
|
function MessageScrollerContent({ className, spacerClassName, ...props }) {
|
|
19
22
|
return (_jsx(ShadcnMessageScroller.Content, { className: cn("flex flex-col", className), spacerClassName: cn("shrink-0", spacerClassName), ...props }));
|
|
@@ -22,7 +25,8 @@ function MessageScrollerItem(props) {
|
|
|
22
25
|
return _jsx(ShadcnMessageScroller.Item, { ...props });
|
|
23
26
|
}
|
|
24
27
|
function MessageScrollerButton({ className, children, render, ...props }) {
|
|
28
|
+
const t = useT();
|
|
25
29
|
return (_jsx(ShadcnMessageScroller.Button, { render: render ??
|
|
26
|
-
((buttonProps) => (_jsx("div", { className: "shrink-0 flex justify-center -mb-1", children: _jsx("button", { ...buttonProps, className: cn("flex h-7 w-7 items-center justify-center rounded-full border border-border bg-background shadow-sm hover:bg-accent", "data-[active=false]:hidden", className, buttonProps.className), "aria-label": "
|
|
30
|
+
((buttonProps) => (_jsx("div", { className: "shrink-0 flex justify-center -mb-1", children: _jsx("button", { ...buttonProps, className: cn("flex h-7 w-7 items-center justify-center rounded-full border border-border bg-background shadow-sm hover:bg-accent", "data-[active=false]:hidden", className, buttonProps.className), "aria-label": t("agentChat.composer.scrollToBottom"), children: children ?? (_jsx(IconChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })) }) }))), ...props }));
|
|
27
31
|
}
|
|
28
32
|
export { MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, };
|
|
@@ -12,7 +12,7 @@ import { getBrowserTabId } from "../browser-tab-id.js";
|
|
|
12
12
|
import { isTrustedBuilderMessage, tryDelegateBuildRequestToBuilder, } from "../builder-frame.js";
|
|
13
13
|
import { BuilderSetupCard, BuilderSetupContent } from "../chat/run-recovery.js";
|
|
14
14
|
import { isTrustedFrameMessage } from "../frame.js";
|
|
15
|
-
import { useT } from "../i18n.js";
|
|
15
|
+
import { useFormatters, useT } from "../i18n.js";
|
|
16
16
|
import { useOrg } from "../org/hooks.js";
|
|
17
17
|
import { isMcpIntegrationCatalogAvailable } from "../resources/mcp-integration-catalog.js";
|
|
18
18
|
import { McpIntegrationDialog } from "../resources/McpIntegrationDialog.js";
|
|
@@ -79,6 +79,7 @@ const coreComposerAdapters = {
|
|
|
79
79
|
};
|
|
80
80
|
export function CoreComposerRuntimeProvider({ children, }) {
|
|
81
81
|
const translate = useT();
|
|
82
|
-
const
|
|
82
|
+
const { formatNumber } = useFormatters();
|
|
83
|
+
const adapters = useMemo(() => ({ ...coreComposerAdapters, formatNumber, translate }), [formatNumber, translate]);
|
|
83
84
|
return (_jsx(ComposerRuntimeAdaptersProvider, { adapters: adapters, children: children }));
|
|
84
85
|
}
|
|
@@ -2,12 +2,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { ContextMeterView } from "@agent-native/toolkit/context-ui";
|
|
3
3
|
import { lazy, Suspense, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { manifestConversationTokens, manifestSystemTokens, } from "../../shared/context-xray.js";
|
|
5
|
+
import { useT } from "../i18n.js";
|
|
5
6
|
import { useActionMutation, useActionQuery } from "../use-action.js";
|
|
6
7
|
import { resolveContextWindow } from "./format.js";
|
|
7
8
|
const ContextXRayPanel = lazy(() => import("./ContextXRayPanel.js").then((m) => ({
|
|
8
9
|
default: m.ContextXRayPanel,
|
|
9
10
|
})));
|
|
10
11
|
export function ContextMeter({ threadId, manifest: providedManifest, enabled = true, }) {
|
|
12
|
+
const t = useT();
|
|
11
13
|
const [open, setOpen] = useState(false);
|
|
12
14
|
const [optimistic, setOptimistic] = useState(new Map());
|
|
13
15
|
const currentThreadId = useRef(threadId);
|
|
@@ -61,5 +63,7 @@ export function ContextMeter({ threadId, manifest: providedManifest, enabled = t
|
|
|
61
63
|
...manifest,
|
|
62
64
|
systemTokens: manifestSystemTokens(manifest),
|
|
63
65
|
conversationTokens: manifestConversationTokens(manifest),
|
|
64
|
-
}, contextWindow: resolveContextWindow(manifest.model), open: open, onOpenChange: setOpen, children: open ? (_jsx(Suspense, { fallback: _jsx("div", { className: "flex h-52 items-center justify-center text-xs text-muted-foreground", children: "
|
|
66
|
+
}, contextWindow: resolveContextWindow(manifest.model), open: open, onOpenChange: setOpen, translate: t, children: open ? (_jsx(Suspense, { fallback: _jsx("div", { className: "flex h-52 items-center justify-center text-xs text-muted-foreground", children: t("agentChat.contextXray.loading", {
|
|
67
|
+
defaultValue: "Loading context view…",
|
|
68
|
+
}) }), children: _jsx(ContextXRayPanel, { manifest: manifest, optimistic: optimistic, onPin: (segmentId) => mutateStatus(segmentId, "pinned", "pin"), onEvict: (segmentId) => mutateStatus(segmentId, "evicted", "evict"), onRestore: (segmentId) => mutateStatus(segmentId, "active", "restore") }) })) : null }));
|
|
65
69
|
}
|
|
@@ -9,18 +9,18 @@ export function ContextXRayPanel({ manifest, optimistic, onPin, onEvict, onResto
|
|
|
9
9
|
...manifest,
|
|
10
10
|
systemTokens: manifestSystemTokens(manifest),
|
|
11
11
|
conversationTokens: manifestConversationTokens(manifest),
|
|
12
|
-
}, contextWindow: resolveContextWindow(manifest.model), optimistic: optimistic, onPin: onPin, onEvict: onEvict, onRestore: onRestore, titleLabel: t("contextXray.panelTitle", {
|
|
12
|
+
}, contextWindow: resolveContextWindow(manifest.model), optimistic: optimistic, onPin: onPin, onEvict: onEvict, onRestore: onRestore, translate: t, titleLabel: t("agentChat.contextXray.panelTitle", {
|
|
13
13
|
defaultValue: "Context X-Ray",
|
|
14
|
-
}), systemOrderedLabel: t("contextXray.systemOrdered", {
|
|
14
|
+
}), systemOrderedLabel: t("agentChat.contextXray.systemOrdered", {
|
|
15
15
|
defaultValue: "System · ordered, not evictable",
|
|
16
16
|
}), governanceLabels: {
|
|
17
|
-
required: t("contextXray.governance.required", {
|
|
17
|
+
required: t("agentChat.contextXray.governance.required", {
|
|
18
18
|
defaultValue: "Required",
|
|
19
19
|
}),
|
|
20
|
-
inherited: t("contextXray.governance.inherited", {
|
|
20
|
+
inherited: t("agentChat.contextXray.governance.inherited", {
|
|
21
21
|
defaultValue: "Inherited",
|
|
22
22
|
}),
|
|
23
|
-
user: t("contextXray.governance.user", {
|
|
23
|
+
user: t("agentChat.contextXray.governance.user", {
|
|
24
24
|
defaultValue: "Your context",
|
|
25
25
|
}),
|
|
26
26
|
} }));
|
|
@@ -18,5 +18,9 @@ export interface NormalizedChatError {
|
|
|
18
18
|
message: string;
|
|
19
19
|
details?: string;
|
|
20
20
|
}
|
|
21
|
+
type ErrorTranslate = (key: string, options?: Record<string, unknown>) => string;
|
|
22
|
+
/** Localize only Core's own normalized error copy; preserve provider details. */
|
|
23
|
+
export declare function localizeKnownChatErrorText(text: string, t: ErrorTranslate): string;
|
|
21
24
|
export declare function isProviderAuthenticationError(text: string, errorCode?: string): boolean;
|
|
22
25
|
export declare function normalizeChatError(errorMessage: string, errorCode?: string): NormalizedChatError;
|
|
26
|
+
export {};
|
|
@@ -16,6 +16,10 @@ export const BUILDER_SPACE_SETTINGS_URL = "https://builder.io/account/space?utm_
|
|
|
16
16
|
// error message. The chat renderer intercepts this href and renders a button
|
|
17
17
|
// that dispatches the `agent-native:new-chat` CustomEvent instead of navigating.
|
|
18
18
|
export const NEW_CHAT_ACTION_HREF = "agent-native:new-chat";
|
|
19
|
+
const OPEN_BUILDER_SPACE_SETTINGS_LABEL = "Open Builder space settings";
|
|
20
|
+
const START_NEW_CHAT_LABEL = "Start new chat";
|
|
21
|
+
const UPGRADE_AT_BUILDER_LABEL = "Upgrade at builder.io";
|
|
22
|
+
const BUILDER_AUTHENTICATION_ERROR = "Builder rejected the connected credentials. Reconnect Builder.io (free tier available) in Settings, then retry.";
|
|
19
23
|
function isSafeUpgradeUrl(url) {
|
|
20
24
|
try {
|
|
21
25
|
const parsed = new URL(url);
|
|
@@ -31,19 +35,103 @@ export function formatChatErrorText(errorMessage, upgradeUrl, errorCode) {
|
|
|
31
35
|
const normalized = normalizeChatError(errorMessage, errorCode);
|
|
32
36
|
if (errorCode === "gateway_not_enabled" ||
|
|
33
37
|
/space has not enabled the LLM gateway/i.test(normalized.message)) {
|
|
34
|
-
return `Error: ${normalized.message}\n\n[
|
|
38
|
+
return `Error: ${normalized.message}\n\n[${OPEN_BUILDER_SPACE_SETTINGS_LABEL}](${BUILDER_SPACE_SETTINGS_URL})`;
|
|
35
39
|
}
|
|
36
40
|
if (errorCode === "builder_gateway_error") {
|
|
37
|
-
return `Error: ${normalized.message}\n\n[
|
|
41
|
+
return `Error: ${normalized.message}\n\n[${START_NEW_CHAT_LABEL}](${NEW_CHAT_ACTION_HREF})`;
|
|
38
42
|
}
|
|
39
43
|
if (errorCode === "context_length_exceeded" ||
|
|
40
44
|
errorCode === "input_too_long") {
|
|
41
|
-
return `Error: ${normalized.message}\n\n[
|
|
45
|
+
return `Error: ${normalized.message}\n\n[${START_NEW_CHAT_LABEL}](${NEW_CHAT_ACTION_HREF})`;
|
|
42
46
|
}
|
|
43
47
|
if (!upgradeUrl || !isSafeUpgradeUrl(upgradeUrl)) {
|
|
44
48
|
return `Error: ${normalized.message}`;
|
|
45
49
|
}
|
|
46
|
-
return `Error: ${normalized.message}\n\n[
|
|
50
|
+
return `Error: ${normalized.message}\n\n[${UPGRADE_AT_BUILDER_LABEL}](${upgradeUrl})`;
|
|
51
|
+
}
|
|
52
|
+
const KNOWN_CHAT_ERROR_KEYS = new Map([
|
|
53
|
+
[
|
|
54
|
+
"No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io or add a provider key.",
|
|
55
|
+
"agentChat.errorMessages.noProviderConnected",
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"The provider behind this model rejected the request. Pick a different model, then retry.",
|
|
59
|
+
"agentChat.errorMessages.builderModelUnauthorized",
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
BUILDER_AUTHENTICATION_ERROR,
|
|
63
|
+
"agentChat.errorMessages.builderAuthentication",
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"This model can't use tools with the current settings. Switch models in Settings, then retry.",
|
|
67
|
+
"agentChat.errorMessages.providerConfiguration",
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"The model provider is rate-limiting this chat right now. Wait a moment, then retry.",
|
|
71
|
+
"agentChat.errorMessages.providerRateLimit",
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"The model provider rejected the saved API key. Update the key in Settings → Integrations → API keys, then retry.",
|
|
75
|
+
"agentChat.errorMessages.providerAuthentication",
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"The model provider could not be reached. Check your connection and retry.",
|
|
79
|
+
"agentChat.errorMessages.providerNetwork",
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"The agent connection was interrupted. Check your connection and retry.",
|
|
83
|
+
"agentChat.errorMessages.agentConnection",
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"The model gateway returned no error details and the chat couldn't recover. Wait a moment and retry, or start a new chat if it keeps happening.",
|
|
87
|
+
"agentChat.errorMessages.gatewayNoDetails",
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"The agent connection timed out before it could finish. You can continue from the partial work or retry.",
|
|
91
|
+
"agentChat.errorMessages.inactivityTimeout",
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"A tool schema was invalid, so the model rejected the request before it started. The invalid tool can be skipped and the request retried.",
|
|
95
|
+
"agentChat.errorMessages.invalidToolSchema",
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"The provider returned an HTML error page.",
|
|
99
|
+
"agentChat.errorMessages.providerHtml",
|
|
100
|
+
],
|
|
101
|
+
]);
|
|
102
|
+
const KNOWN_CHAT_ERROR_ACTION_KEYS = new Map([
|
|
103
|
+
[
|
|
104
|
+
"Open Builder space settings",
|
|
105
|
+
"agentChat.errorMessages.openBuilderSpaceSettings",
|
|
106
|
+
],
|
|
107
|
+
["Start new chat", "agentChat.errorMessages.startNewChat"],
|
|
108
|
+
["Upgrade at builder.io", "agentChat.errorMessages.upgradeAtBuilder"],
|
|
109
|
+
]);
|
|
110
|
+
/** Localize only Core's own normalized error copy; preserve provider details. */
|
|
111
|
+
export function localizeKnownChatErrorText(text, t) {
|
|
112
|
+
const newlineIndex = text.indexOf("\n");
|
|
113
|
+
const firstLine = newlineIndex >= 0 ? text.slice(0, newlineIndex) : text;
|
|
114
|
+
const suffix = newlineIndex >= 0 ? text.slice(newlineIndex) : "";
|
|
115
|
+
const hasPrefix = firstLine.startsWith("Error: ");
|
|
116
|
+
const message = hasPrefix ? firstLine.slice("Error: ".length) : firstLine;
|
|
117
|
+
const key = KNOWN_CHAT_ERROR_KEYS.get(message);
|
|
118
|
+
let localizedFirstLine = firstLine;
|
|
119
|
+
if (key) {
|
|
120
|
+
const translated = t(key, { defaultValue: message });
|
|
121
|
+
localizedFirstLine = hasPrefix
|
|
122
|
+
? t("agentChat.errorMessages.errorPrefix", {
|
|
123
|
+
defaultValue: "Error: {{message}}",
|
|
124
|
+
message: translated,
|
|
125
|
+
})
|
|
126
|
+
: translated;
|
|
127
|
+
}
|
|
128
|
+
const localizedSuffix = suffix.replace(/\[([^\]]+)]\(([^)]+)\)/g, (link, label, href) => {
|
|
129
|
+
const actionKey = KNOWN_CHAT_ERROR_ACTION_KEYS.get(label);
|
|
130
|
+
if (!actionKey)
|
|
131
|
+
return link;
|
|
132
|
+
return `[${t(actionKey, { defaultValue: label })}](${href})`;
|
|
133
|
+
});
|
|
134
|
+
return `${localizedFirstLine}${localizedSuffix}`;
|
|
47
135
|
}
|
|
48
136
|
function normalizeErrorCode(errorCode) {
|
|
49
137
|
return String(errorCode ?? "")
|
|
@@ -124,7 +212,7 @@ export function normalizeChatError(errorMessage, errorCode) {
|
|
|
124
212
|
}
|
|
125
213
|
if (code === "builder_auth_error") {
|
|
126
214
|
return {
|
|
127
|
-
message:
|
|
215
|
+
message: BUILDER_AUTHENTICATION_ERROR,
|
|
128
216
|
details: text,
|
|
129
217
|
};
|
|
130
218
|
}
|
package/dist/client/i18n.js
CHANGED
|
@@ -5,6 +5,7 @@ import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
|
|
|
5
5
|
import i18next from "i18next";
|
|
6
6
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
7
7
|
import { I18nextProvider, initReactI18next, useTranslation, } from "react-i18next";
|
|
8
|
+
import { coreMessagesForLocale, englishAgentChatMessages, loadCoreMessagesForLocale, normalizeCoreMessageOverrides, } from "../localization/core-messages.js";
|
|
8
9
|
import defaultEnglishMessages from "../localization/default-messages.js";
|
|
9
10
|
import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, localeDirection, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
|
|
10
11
|
import { injectedAgentNativeConfig } from "./app-config.js";
|
|
@@ -138,8 +139,7 @@ function resolveInitialState(args) {
|
|
|
138
139
|
}
|
|
139
140
|
function resolveSupportedLocales(args) {
|
|
140
141
|
const configured = injectedAgentNativeConfig().translations?.locales;
|
|
141
|
-
const candidates = configured ??
|
|
142
|
-
args.catalog?.supportedLocales ?? [args.sourceLocale];
|
|
142
|
+
const candidates = configured ?? args.catalog?.supportedLocales ?? SUPPORTED_LOCALES;
|
|
143
143
|
const supported = candidates.filter((locale) => SUPPORTED_LOCALES.includes(locale));
|
|
144
144
|
return [
|
|
145
145
|
args.sourceLocale,
|
|
@@ -160,16 +160,39 @@ function normalizeLoadedMessages(value) {
|
|
|
160
160
|
}
|
|
161
161
|
return value;
|
|
162
162
|
}
|
|
163
|
+
function mergeLocaleMessages(base, overrides) {
|
|
164
|
+
if (!overrides)
|
|
165
|
+
return base;
|
|
166
|
+
const merged = { ...base };
|
|
167
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
168
|
+
const current = merged[key];
|
|
169
|
+
if (current &&
|
|
170
|
+
value &&
|
|
171
|
+
typeof current === "object" &&
|
|
172
|
+
typeof value === "object" &&
|
|
173
|
+
!Array.isArray(current) &&
|
|
174
|
+
!Array.isArray(value)) {
|
|
175
|
+
merged[key] = mergeLocaleMessages(current, value);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
merged[key] = value;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return merged;
|
|
182
|
+
}
|
|
183
|
+
function composeLocaleMessages(coreMessages, appMessages) {
|
|
184
|
+
return mergeLocaleMessages(mergeLocaleMessages(defaultEnglishMessages, coreMessages), appMessages ? normalizeCoreMessageOverrides(appMessages) : null);
|
|
185
|
+
}
|
|
163
186
|
function createI18nInstance(args) {
|
|
164
187
|
const instance = i18next.createInstance();
|
|
165
188
|
const resources = {
|
|
166
189
|
[args.sourceLocale]: {
|
|
167
|
-
[args.namespace]: args.messages,
|
|
190
|
+
[args.namespace]: composeLocaleMessages(coreMessagesForLocale(args.sourceLocale), args.messages),
|
|
168
191
|
},
|
|
169
192
|
};
|
|
170
|
-
if (args.initialLocale !== args.sourceLocale
|
|
193
|
+
if (args.initialLocale !== args.sourceLocale) {
|
|
171
194
|
resources[args.initialLocale] = {
|
|
172
|
-
[args.namespace]: args.initialMessages,
|
|
195
|
+
[args.namespace]: composeLocaleMessages(coreMessagesForLocale(args.initialLocale), args.initialMessages),
|
|
173
196
|
};
|
|
174
197
|
}
|
|
175
198
|
void instance.use(initReactI18next).init({
|
|
@@ -202,24 +225,37 @@ export function AgentNativeI18nProvider({ children, catalog, initialLocale, init
|
|
|
202
225
|
const [locale, setLocale] = useState(initialState.locale);
|
|
203
226
|
const [loading, setLoading] = useState(false);
|
|
204
227
|
const i18nRef = useRef(null);
|
|
228
|
+
const appMessagesLoadedRef = useRef(null);
|
|
229
|
+
const appMessagesRef = useRef(null);
|
|
230
|
+
const coreMessagesLoadedRef = useRef(null);
|
|
205
231
|
if (!i18nRef.current) {
|
|
206
232
|
const preloadedMessages = normalizeLoadedMessages(hydration.locale === initialState.locale && hydration.messages
|
|
207
233
|
? hydration.messages
|
|
208
234
|
: initialMessages);
|
|
235
|
+
const resolvedSourceMessages = normalizeCoreMessageOverrides(hydration.locale === sourceLocale && hydration.messages
|
|
236
|
+
? hydration.messages
|
|
237
|
+
: sourceMessages);
|
|
209
238
|
i18nRef.current = createI18nInstance({
|
|
210
239
|
namespace,
|
|
211
240
|
sourceLocale,
|
|
212
|
-
messages:
|
|
213
|
-
? hydration.messages
|
|
214
|
-
: sourceMessages,
|
|
241
|
+
messages: resolvedSourceMessages,
|
|
215
242
|
initialLocale: initialState.locale,
|
|
216
243
|
initialMessages: preloadedMessages,
|
|
217
244
|
});
|
|
245
|
+
appMessagesRef.current = new Map([[sourceLocale, resolvedSourceMessages]]);
|
|
246
|
+
appMessagesLoadedRef.current = new Set([sourceLocale]);
|
|
247
|
+
if (initialState.locale !== sourceLocale && preloadedMessages) {
|
|
248
|
+
appMessagesRef.current.set(initialState.locale, normalizeCoreMessageOverrides(preloadedMessages));
|
|
249
|
+
appMessagesLoadedRef.current.add(initialState.locale);
|
|
250
|
+
}
|
|
251
|
+
coreMessagesLoadedRef.current = new Set([sourceLocale, initialState.locale].filter((candidate) => candidate === DEFAULT_LOCALE));
|
|
218
252
|
}
|
|
219
253
|
const i18n = i18nRef.current;
|
|
220
254
|
useEffect(() => {
|
|
221
255
|
if (i18n.hasResourceBundle(sourceLocale, namespace)) {
|
|
222
|
-
|
|
256
|
+
const normalizedSourceMessages = normalizeCoreMessageOverrides(sourceMessages);
|
|
257
|
+
appMessagesRef.current?.set(sourceLocale, normalizedSourceMessages);
|
|
258
|
+
i18n.addResourceBundle(sourceLocale, namespace, normalizedSourceMessages, true, true);
|
|
223
259
|
}
|
|
224
260
|
}, [i18n, namespace, sourceLocale, sourceMessages]);
|
|
225
261
|
useEffect(() => {
|
|
@@ -233,17 +269,37 @@ export function AgentNativeI18nProvider({ children, catalog, initialLocale, init
|
|
|
233
269
|
async function applyLocale() {
|
|
234
270
|
setLoading(true);
|
|
235
271
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
272
|
+
const shouldLoadAppMessages = locale !== sourceLocale && !appMessagesLoadedRef.current?.has(locale);
|
|
273
|
+
const shouldLoadCoreMessages = !coreMessagesLoadedRef.current?.has(locale);
|
|
274
|
+
if (shouldLoadAppMessages || shouldLoadCoreMessages) {
|
|
238
275
|
const preloaded = hydration.locale === locale && hydration.messages
|
|
239
276
|
? hydration.messages
|
|
240
277
|
: initialLocale === locale && initialMessages
|
|
241
278
|
? initialMessages
|
|
242
279
|
: null;
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
280
|
+
const [coreMessages, loadedAppMessages] = await Promise.all([
|
|
281
|
+
shouldLoadCoreMessages
|
|
282
|
+
? loadCoreMessagesForLocale(locale)
|
|
283
|
+
: Promise.resolve({}),
|
|
284
|
+
shouldLoadAppMessages
|
|
285
|
+
? Promise.resolve(preloaded ?? loadMessages?.(locale))
|
|
286
|
+
: Promise.resolve(null),
|
|
287
|
+
]);
|
|
288
|
+
const existingMessages = normalizeLoadedMessages(i18n.getResourceBundle(locale, namespace));
|
|
289
|
+
const loadedMessages = normalizeLoadedMessages(loadedAppMessages);
|
|
290
|
+
if (loadedMessages) {
|
|
291
|
+
appMessagesRef.current?.set(locale, normalizeCoreMessageOverrides(loadedMessages));
|
|
292
|
+
}
|
|
293
|
+
const appMessages = appMessagesRef.current?.get(locale) ?? null;
|
|
294
|
+
const nextMessages = shouldLoadCoreMessages
|
|
295
|
+
? composeLocaleMessages(coreMessages, appMessages)
|
|
296
|
+
: mergeLocaleMessages(existingMessages ?? {}, appMessages);
|
|
297
|
+
i18n.addResourceBundle(locale, namespace, nextMessages, true, true);
|
|
298
|
+
if (shouldLoadCoreMessages) {
|
|
299
|
+
coreMessagesLoadedRef.current?.add(locale);
|
|
300
|
+
}
|
|
301
|
+
if (shouldLoadAppMessages) {
|
|
302
|
+
appMessagesLoadedRef.current?.add(locale);
|
|
247
303
|
}
|
|
248
304
|
}
|
|
249
305
|
if (!cancelled) {
|
|
@@ -369,6 +425,10 @@ export function useOptionalLocale() {
|
|
|
369
425
|
return useContext(LocaleContext);
|
|
370
426
|
}
|
|
371
427
|
const CORE_FALLBACK_MESSAGES = {
|
|
428
|
+
...Object.fromEntries(Object.entries(englishAgentChatMessages).map(([key, value]) => [
|
|
429
|
+
`agentChat.${key}`,
|
|
430
|
+
value,
|
|
431
|
+
])),
|
|
372
432
|
"runsTray.runs": "Runs",
|
|
373
433
|
"runsTray.agentRuns": "Agent runs",
|
|
374
434
|
"runsTray.activeRun_one": "{{count}} active run",
|
|
@@ -497,6 +557,12 @@ function fallbackMessage(key, options) {
|
|
|
497
557
|
? interpolateFallbackMessage(template, options)
|
|
498
558
|
: humanizeFallbackKey(key);
|
|
499
559
|
}
|
|
560
|
+
function preserveTranslatedUserValues(translated, options) {
|
|
561
|
+
const defaultValue = options?.defaultValue;
|
|
562
|
+
return typeof defaultValue === "string" && translated === defaultValue
|
|
563
|
+
? interpolateFallbackMessage(defaultValue, options)
|
|
564
|
+
: translated;
|
|
565
|
+
}
|
|
500
566
|
export function useT() {
|
|
501
567
|
const { i18n, t } = useTranslation();
|
|
502
568
|
const context = useContext(LocaleContext);
|
|
@@ -504,11 +570,11 @@ export function useT() {
|
|
|
504
570
|
return useCallback((key, options) => {
|
|
505
571
|
const translated = t(key, options);
|
|
506
572
|
if (translated !== key)
|
|
507
|
-
return translated;
|
|
573
|
+
return preserveTranslatedUserValues(translated, options);
|
|
508
574
|
const getFixedT = i18n.getFixedT;
|
|
509
575
|
const sourceFallback = getFixedT?.(sourceLocale)(key, options);
|
|
510
576
|
if (sourceFallback && sourceFallback !== key)
|
|
511
|
-
return sourceFallback;
|
|
577
|
+
return preserveTranslatedUserValues(sourceFallback, options);
|
|
512
578
|
return fallbackMessage(key, options);
|
|
513
579
|
}, [i18n, sourceLocale, t]);
|
|
514
580
|
}
|