@agent-native/core 0.155.0 → 0.157.1
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/README.md +1 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
- package/corpus/templates/assets/app/global.css +4 -0
- package/corpus/templates/assets/app/i18n-data.ts +21 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +18 -15
- package/corpus/templates/brain/app/routes/sources.tsx +1 -2
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
- package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
- package/corpus/templates/clips/actions/create-folder.ts +7 -1
- package/corpus/templates/clips/actions/create-meeting.ts +6 -2
- package/corpus/templates/clips/actions/create-recording.ts +5 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
- package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +177 -134
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
- package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +24 -17
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +151 -20
- package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
- package/corpus/templates/clips/desktop/src/app.tsx +24 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +24 -9
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
- package/corpus/templates/clips/server/db/index.ts +1 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
- package/corpus/templates/clips/server/lib/emails.ts +23 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
- package/corpus/templates/clips/server/lib/recordings.ts +2 -1
- package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
- package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
- package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
- package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
- package/corpus/templates/clips/shared/recording-link.ts +4 -0
- package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +6 -2
- package/corpus/templates/factory/actions/view-screen.ts +15 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
- package/corpus/templates/factory/app/routes/agents.tsx +9 -0
- package/corpus/templates/mail/server/plugins/auth.ts +0 -2
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
- 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/HistoryPanel.tsx +7 -1
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +108 -19
- 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/lib/editor-shortcuts.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +143 -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.d.ts +13 -1
- package/dist/client/AgentPanel.js +22 -17
- package/dist/client/AssistantChat.d.ts +15 -1
- package/dist/client/AssistantChat.js +135 -48
- package/dist/client/HighlightedCodeBlock.js +12 -2
- package/dist/client/MultiTabAssistantChat.js +44 -27
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/chat/markdown-renderer.js +20 -5
- 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/chat-first/apps-rail.js +1 -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/settings/AccountSettingsCard.js +48 -19
- package/dist/client/sharing/ShareButton.d.ts +1 -2
- package/dist/client/sharing/ShareButton.js +151 -81
- package/dist/client/sharing/ShareDialog.js +5 -4
- package/dist/client/use-chat-threads.js +10 -14
- 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/collab/struct-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 +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- 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/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +0 -2
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.js +18 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +2 -88
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.js +15 -0
- package/dist/shared/markdown-block-split.d.ts +16 -0
- package/dist/shared/markdown-block-split.js +81 -5
- package/dist/styles/agent-native.css +13 -0
- package/dist/voice/voice-cleanup-prompt.js +1 -0
- package/docs/content/agent-surfaces.mdx +30 -0
- package/package.json +2 -2
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
|
@@ -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
|
}
|
|
@@ -3,14 +3,16 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
|
3
3
|
import { IconThumbUp, IconThumbDown } from "@tabler/icons-react";
|
|
4
4
|
import { useState, useCallback } from "react";
|
|
5
5
|
import { agentNativePath } from "../api-path.js";
|
|
6
|
+
import { useT } from "../i18n.js";
|
|
6
7
|
import { cn } from "../utils.js";
|
|
7
8
|
const THUMBS_DOWN_CATEGORIES = [
|
|
8
|
-
"Inaccurate",
|
|
9
|
-
"Not helpful",
|
|
10
|
-
"Wrong tool",
|
|
11
|
-
"Too slow",
|
|
9
|
+
{ value: "Inaccurate", key: "agentChat.feedback.inaccurate" },
|
|
10
|
+
{ value: "Not helpful", key: "agentChat.feedback.notHelpful" },
|
|
11
|
+
{ value: "Wrong tool", key: "agentChat.feedback.wrongTool" },
|
|
12
|
+
{ value: "Too slow", key: "agentChat.feedback.tooSlow" },
|
|
12
13
|
];
|
|
13
14
|
export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
|
|
15
|
+
const t = useT();
|
|
14
16
|
const [selection, setSelection] = useState(null);
|
|
15
17
|
const [categoryOpen, setCategoryOpen] = useState(false);
|
|
16
18
|
const [submittedCategory, setSubmittedCategory] = useState(null);
|
|
@@ -54,11 +56,11 @@ export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
|
|
|
54
56
|
setCategoryOpen(false);
|
|
55
57
|
sendFeedback("category", category);
|
|
56
58
|
}, [sendFeedback]);
|
|
57
|
-
return (_jsxs("div", { className: cn("inline-flex items-center gap-0.5", className), children: [_jsx("button", { type: "button", "aria-label": "
|
|
59
|
+
return (_jsxs("div", { className: cn("inline-flex items-center gap-0.5", className), children: [_jsx("button", { type: "button", "aria-label": t("agentChat.feedback.thumbsUp"), onClick: handleThumbsUp, className: cn("flex h-6 w-6 items-center justify-center rounded", selection === "up"
|
|
58
60
|
? "text-foreground"
|
|
59
|
-
: "text-muted-foreground/70 hover:text-muted-foreground hover:bg-accent/50"), children: _jsx(IconThumbUp, { size: 16, stroke: selection === "up" ? 2.5 : 1.5, fill: selection === "up" ? "currentColor" : "none" }) }), _jsxs(PopoverPrimitive.Root, { open: categoryOpen, onOpenChange: setCategoryOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "
|
|
61
|
+
: "text-muted-foreground/70 hover:text-muted-foreground hover:bg-accent/50"), children: _jsx(IconThumbUp, { size: 16, stroke: selection === "up" ? 2.5 : 1.5, fill: selection === "up" ? "currentColor" : "none" }) }), _jsxs(PopoverPrimitive.Root, { open: categoryOpen, onOpenChange: setCategoryOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.feedback.thumbsDown"), onClick: handleThumbsDown, className: cn("flex h-6 w-6 items-center justify-center rounded", selection === "down"
|
|
60
62
|
? "text-foreground"
|
|
61
|
-
: "text-muted-foreground/70 hover:text-muted-foreground hover:bg-accent/50"), children: _jsx(IconThumbDown, { size: 16, stroke: selection === "down" ? 2.5 : 1.5, fill: selection === "down" ? "currentColor" : "none" }) }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { side: "bottom", align: "start", sideOffset: 4, collisionPadding: 8, className: "z-[300] overflow-hidden rounded-lg border border-border bg-popover p-1 shadow-lg outline-none", children: _jsx("div", { className: "flex flex-col gap-0.5", children: THUMBS_DOWN_CATEGORIES.map((category) => (_jsx("button", { type: "button", onClick: () => handleCategory(category), className: cn("rounded-md px-3 py-1.5 text-
|
|
63
|
+
: "text-muted-foreground/70 hover:text-muted-foreground hover:bg-accent/50"), children: _jsx(IconThumbDown, { size: 16, stroke: selection === "down" ? 2.5 : 1.5, fill: selection === "down" ? "currentColor" : "none" }) }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { side: "bottom", align: "start", sideOffset: 4, collisionPadding: 8, className: "z-[300] overflow-hidden rounded-lg border border-border bg-popover p-1 shadow-lg outline-none", children: _jsx("div", { className: "flex flex-col gap-0.5", children: THUMBS_DOWN_CATEGORIES.map((category) => (_jsx("button", { type: "button", onClick: () => handleCategory(category.value), className: cn("rounded-md px-3 py-1.5 text-start text-xs", submittedCategory === category.value
|
|
62
64
|
? "bg-accent text-foreground font-medium"
|
|
63
|
-
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), children: category }, category))) }) }) })] })] }));
|
|
65
|
+
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), children: t(category.key) }, category.value))) }) }) })] })] }));
|
|
64
66
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ActionButton, Avatar, TextField, } from "@agent-native/toolkit/design-system";
|
|
3
|
-
import { IconCamera, IconCheck, IconLock } from "@tabler/icons-react";
|
|
2
|
+
import { ActionButton, Avatar, IconButton, TextField, } from "@agent-native/toolkit/design-system";
|
|
3
|
+
import { IconCamera, IconCheck, IconLock, IconPencil, } from "@tabler/icons-react";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
5
|
import { PASSWORD_MIN_LENGTH } from "../../shared/password-policy.js";
|
|
6
6
|
import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
7
|
+
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
7
8
|
import { useT } from "../i18n.js";
|
|
8
9
|
import { useActionMutation, useActionQuery } from "../use-action.js";
|
|
9
10
|
import { uploadAvatar, useAvatarUrl } from "../use-avatar.js";
|
|
@@ -107,21 +108,23 @@ export function AccountSettingsForm({ compact = false, }) {
|
|
|
107
108
|
const [uploading, setUploading] = useState(false);
|
|
108
109
|
const [photoStatus, setPhotoStatus] = useState("idle");
|
|
109
110
|
const [name, setName] = useState("");
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const [savedName, setSavedName] = useState("");
|
|
112
|
+
const [isEditingName, setIsEditingName] = useState(false);
|
|
113
|
+
const nameFieldEditedRef = useRef(false);
|
|
114
|
+
const profileName = profileQuery.data?.name || session?.name || "";
|
|
115
|
+
const displayName = name || profileName || email || t("settings.profileSignedOut");
|
|
115
116
|
useEffect(() => {
|
|
116
|
-
|
|
117
|
+
nameFieldEditedRef.current = false;
|
|
118
|
+
setIsEditingName(false);
|
|
119
|
+
setName("");
|
|
120
|
+
setSavedName("");
|
|
117
121
|
}, [email]);
|
|
118
122
|
useEffect(() => {
|
|
119
|
-
if (
|
|
123
|
+
if (nameFieldEditedRef.current)
|
|
120
124
|
return;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}, [profileQuery.data?.name, session?.name]);
|
|
125
|
+
setName(profileName);
|
|
126
|
+
setSavedName(profileName);
|
|
127
|
+
}, [email, profileName]);
|
|
125
128
|
const handleAvatarChange = async (event) => {
|
|
126
129
|
const file = event.target.files?.[0];
|
|
127
130
|
event.target.value = "";
|
|
@@ -142,20 +145,46 @@ export function AccountSettingsForm({ compact = false, }) {
|
|
|
142
145
|
};
|
|
143
146
|
const handleProfileSave = () => {
|
|
144
147
|
const nextName = name.trim();
|
|
145
|
-
if (!nextName || !email)
|
|
148
|
+
if (!nextName || !email || nextName === savedName.trim())
|
|
146
149
|
return;
|
|
147
|
-
updateProfile.mutate({ name: nextName }
|
|
150
|
+
updateProfile.mutate({ name: nextName }, {
|
|
151
|
+
onSuccess: (profile) => {
|
|
152
|
+
setName(profile.name);
|
|
153
|
+
setSavedName(profile.name);
|
|
154
|
+
nameFieldEditedRef.current = false;
|
|
155
|
+
setIsEditingName(false);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
148
158
|
};
|
|
159
|
+
const handleProfileEdit = () => {
|
|
160
|
+
updateProfile.reset();
|
|
161
|
+
nameFieldEditedRef.current = false;
|
|
162
|
+
setName(savedName);
|
|
163
|
+
setIsEditingName(true);
|
|
164
|
+
};
|
|
165
|
+
const handleProfileCancel = () => {
|
|
166
|
+
updateProfile.reset();
|
|
167
|
+
nameFieldEditedRef.current = false;
|
|
168
|
+
setName(savedName);
|
|
169
|
+
setIsEditingName(false);
|
|
170
|
+
};
|
|
171
|
+
const canSaveName = !!email &&
|
|
172
|
+
!updateProfile.isPending &&
|
|
173
|
+
!!name.trim() &&
|
|
174
|
+
name.trim() !== savedName.trim();
|
|
149
175
|
const profileStatus = photoStatus === "saved" ? (_jsx("span", { className: "text-primary", children: t("settings.profilePhotoUpdated") })) : photoStatus === "error" ? (_jsx("span", { className: "text-destructive", children: t("settings.profilePhotoError") })) : email ? (email) : undefined;
|
|
150
176
|
return (_jsxs(SettingsGroup, { id: "account", title: t("settings.profileTitle"), description: t("settings.profileDescription"), className: cn(compact && "[&>div:last-child>div>div]:py-3"), children: [_jsx(SettingsRow, { id: "profile", label: t("settings.profileTitle"), description: profileStatus, control: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Avatar, { name: displayName, src: avatarUrl, fallback: profileInitials(displayName), size: "default", className: "size-10 shrink-0 rounded-full border border-border bg-accent font-semibold text-muted-foreground" }), _jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", className: "hidden", onChange: handleAvatarChange }), _jsx(ActionButton, { type: "button", intent: "neutral", emphasis: "outline", size: "compact", disabled: !email || uploading, leadingIcon: _jsx(IconCamera, { className: "size-3.5" }), onPress: () => fileInputRef.current?.click(), children: uploading
|
|
151
177
|
? t("settings.profileUploading")
|
|
152
|
-
: t("settings.profileChangePhoto") })] }) }), _jsx(SettingsRow, { id: "profile-name", label: t("settings.profileNameLabel"), description: updateProfile.isSuccess ? (_jsx("span", { className: "text-primary", children: t("settings.profileSaved") })) : updateProfile.error ? (_jsx("span", { className: "text-destructive", children: t("settings.profileSaveError") })) : (t("settings.profileNameDescription")), control: _jsxs("div", { className: "flex w-full items-center gap-2 sm:w-80", children: [_jsx(TextField, { id: "agent-native-profile-name", value: name, onChange: (value) => {
|
|
153
|
-
|
|
178
|
+
: t("settings.profileChangePhoto") })] }) }), _jsx(SettingsRow, { id: "profile-name", label: t("settings.profileNameLabel"), description: updateProfile.isSuccess ? (_jsx("span", { className: "text-primary", children: t("settings.profileSaved") })) : updateProfile.error ? (_jsx("span", { className: "text-destructive", children: t("settings.profileSaveError") })) : (t("settings.profileNameDescription")), control: isEditingName ? (_jsxs("div", { className: "flex w-full items-center gap-2 sm:w-80", children: [_jsx(TextField, { id: "agent-native-profile-name", value: name, onChange: (value) => {
|
|
179
|
+
nameFieldEditedRef.current = true;
|
|
154
180
|
updateProfile.reset();
|
|
155
181
|
setName(value);
|
|
156
|
-
}, placeholder: t("settings.profileNamePlaceholder"), disabled: !email || updateProfile.isPending, "aria-label": t("settings.profileNameLabel"), className: "min-w-0 flex-1"
|
|
182
|
+
}, placeholder: t("settings.profileNamePlaceholder"), disabled: !email || updateProfile.isPending, autoFocus: true, "aria-label": t("settings.profileNameLabel"), className: "min-w-0 flex-1", onKeyDown: (event) => {
|
|
183
|
+
if (event.key === "Escape")
|
|
184
|
+
handleProfileCancel();
|
|
185
|
+
} }), _jsx(ActionButton, { type: "button", intent: "primary", emphasis: "solid", size: "compact", pending: updateProfile.isPending, disabled: !canSaveName, onPress: handleProfileSave, children: updateProfile.isPending
|
|
157
186
|
? t("settings.profileSaving")
|
|
158
|
-
: t("settings.profileSave") })] }) }), _jsx(SettingsRow, { id: "timezone", label: t("settings.timezoneLabel", { defaultValue: "Timezone" }), description: t("settings.timezoneHint", {
|
|
187
|
+
: t("settings.profileSave") })] })) : (_jsxs("div", { className: "flex w-full items-center justify-end gap-2 sm:w-80", children: [_jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: displayName }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(IconButton, { type: "button", intent: "neutral", emphasis: "ghost", size: "compact", icon: _jsx(IconPencil, { size: 14 }), label: t("settings.profileNameEdit"), title: t("settings.profileNameEdit"), disabled: !email, onPress: handleProfileEdit }) }), _jsx(TooltipContent, { children: t("settings.profileNameEdit") })] })] })) }), _jsx(SettingsRow, { id: "timezone", label: t("settings.timezoneLabel", { defaultValue: "Timezone" }), description: t("settings.timezoneHint", {
|
|
159
188
|
defaultValue: "Used for timestamps and scheduled automations.",
|
|
160
189
|
}), control: _jsx(SchedulingTimezoneField, { compact: true }) }), _jsx(PasswordSettings, {})] }));
|
|
161
190
|
}
|
|
@@ -7,8 +7,7 @@ export interface ShareButtonProps {
|
|
|
7
7
|
/** @deprecated No longer affects rendering — trigger always says
|
|
8
8
|
* "Share". Kept for callsite compatibility. */
|
|
9
9
|
variant?: "compact" | "label";
|
|
10
|
-
/**
|
|
11
|
-
* "label-icon" opts into an icon plus label; "icon" is icon-only. */
|
|
10
|
+
/** @deprecated Share triggers are standardized as text-only buttons. */
|
|
12
11
|
trigger?: "label" | "icon" | "label-icon";
|
|
13
12
|
/** @deprecated No longer affects rendering — kept for callsite compatibility. */
|
|
14
13
|
hideTriggerIcon?: boolean;
|