@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.
Files changed (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
  4. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  5. package/corpus/templates/assets/app/global.css +4 -0
  6. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  7. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +18 -15
  8. package/corpus/templates/brain/app/routes/sources.tsx +1 -2
  9. package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
  10. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
  11. package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
  12. package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
  13. package/corpus/templates/clips/actions/create-folder.ts +7 -1
  14. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  15. package/corpus/templates/clips/actions/create-recording.ts +5 -3
  16. package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
  17. package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
  18. package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
  19. package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
  20. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
  21. package/corpus/templates/clips/app/components/player/share-dialog.tsx +177 -134
  22. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
  23. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
  24. package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
  25. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
  26. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +24 -17
  27. package/corpus/templates/clips/app/routes/share.$shareId.tsx +151 -20
  28. package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
  29. package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
  30. package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
  31. package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
  32. package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
  33. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
  34. package/corpus/templates/clips/desktop/src/app.tsx +24 -0
  35. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
  36. package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
  37. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
  38. package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
  39. package/corpus/templates/clips/desktop/src/styles.css +24 -9
  40. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
  41. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
  42. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
  43. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
  44. package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
  45. package/corpus/templates/clips/server/db/index.ts +1 -0
  46. package/corpus/templates/clips/server/db/schema.ts +1 -0
  47. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
  48. package/corpus/templates/clips/server/lib/emails.ts +23 -0
  49. package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
  50. package/corpus/templates/clips/server/lib/recordings.ts +2 -1
  51. package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
  52. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
  53. package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
  54. package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
  55. package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
  56. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
  58. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
  59. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
  60. package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
  61. package/corpus/templates/clips/shared/recording-link.ts +4 -0
  62. package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
  63. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
  64. package/corpus/templates/factory/AGENTS.md +5 -1
  65. package/corpus/templates/factory/README.md +6 -2
  66. package/corpus/templates/factory/actions/view-screen.ts +15 -0
  67. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
  68. package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
  69. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  70. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
  71. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
  72. package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
  73. package/corpus/templates/factory/app/routes/agents.tsx +9 -0
  74. package/corpus/templates/mail/server/plugins/auth.ts +0 -2
  75. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
  76. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
  77. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  78. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  79. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  80. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  81. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
  82. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  83. package/corpus/templates/slides/app/context/DeckContext.tsx +108 -19
  84. package/corpus/templates/slides/app/global.css +7 -40
  85. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  86. package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
  87. package/corpus/templates/slides/app/pages/DeckEditor.tsx +143 -45
  88. package/dist/agent/index.d.ts +1 -1
  89. package/dist/agent/production-agent.d.ts +30 -0
  90. package/dist/agent/production-agent.js +100 -8
  91. package/dist/agent/types.d.ts +9 -0
  92. package/dist/client/AgentPanel.d.ts +13 -1
  93. package/dist/client/AgentPanel.js +22 -17
  94. package/dist/client/AssistantChat.d.ts +15 -1
  95. package/dist/client/AssistantChat.js +135 -48
  96. package/dist/client/HighlightedCodeBlock.js +12 -2
  97. package/dist/client/MultiTabAssistantChat.js +44 -27
  98. package/dist/client/agent-chat/index.d.ts +1 -0
  99. package/dist/client/agent-chat/index.js +1 -0
  100. package/dist/client/chat/markdown-renderer.js +20 -5
  101. package/dist/client/chat/message-components.d.ts +2 -2
  102. package/dist/client/chat/message-components.js +69 -35
  103. package/dist/client/chat/run-recovery.d.ts +4 -1
  104. package/dist/client/chat/run-recovery.js +62 -30
  105. package/dist/client/chat/tool-call-display.d.ts +12 -3
  106. package/dist/client/chat/tool-call-display.js +79 -40
  107. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  108. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  109. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  110. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  111. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  112. package/dist/client/chat-first/apps-rail.js +1 -1
  113. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  114. package/dist/client/components/ui/message-scroller.js +7 -3
  115. package/dist/client/composer/runtime-adapters.js +3 -2
  116. package/dist/client/context-xray/ContextMeter.js +5 -1
  117. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  118. package/dist/client/error-format.d.ts +4 -0
  119. package/dist/client/error-format.js +93 -5
  120. package/dist/client/i18n.js +83 -17
  121. package/dist/client/observability/ThumbsFeedback.js +10 -8
  122. package/dist/client/settings/AccountSettingsCard.js +48 -19
  123. package/dist/client/sharing/ShareButton.d.ts +1 -2
  124. package/dist/client/sharing/ShareButton.js +151 -81
  125. package/dist/client/sharing/ShareDialog.js +5 -4
  126. package/dist/client/use-chat-threads.js +10 -14
  127. package/dist/coding-tools/run-code.js +2 -1
  128. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  129. package/dist/coding-tools/sandbox/background.js +8 -1
  130. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  131. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  132. package/dist/collab/routes.d.ts +1 -1
  133. package/dist/collab/struct-routes.d.ts +1 -1
  134. package/dist/index.d.ts +1 -1
  135. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  136. package/dist/localization/core-messages/ar-SA.js +541 -0
  137. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  138. package/dist/localization/core-messages/de-DE.js +513 -0
  139. package/dist/localization/core-messages/en-US.d.ts +513 -0
  140. package/dist/localization/core-messages/en-US.js +513 -0
  141. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  142. package/dist/localization/core-messages/es-ES.js +520 -0
  143. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  144. package/dist/localization/core-messages/fr-FR.js +520 -0
  145. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  146. package/dist/localization/core-messages/hi-IN.js +513 -0
  147. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  148. package/dist/localization/core-messages/ja-JP.js +506 -0
  149. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  150. package/dist/localization/core-messages/ko-KR.js +506 -0
  151. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  152. package/dist/localization/core-messages/pt-BR.js +520 -0
  153. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  154. package/dist/localization/core-messages/zh-CN.js +506 -0
  155. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  156. package/dist/localization/core-messages/zh-TW.js +506 -0
  157. package/dist/localization/core-messages.d.ts +524 -0
  158. package/dist/localization/core-messages.js +124 -0
  159. package/dist/localization/default-messages.d.ts +3 -0
  160. package/dist/localization/default-messages.js +3 -0
  161. package/dist/notifications/routes.d.ts +1 -1
  162. package/dist/observability/routes.d.ts +5 -5
  163. package/dist/progress/routes.d.ts +1 -1
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  165. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  166. package/dist/resources/handlers.d.ts +1 -1
  167. package/dist/secrets/routes.d.ts +9 -9
  168. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  169. package/dist/server/agent-chat-plugin.d.ts +10 -0
  170. package/dist/server/agent-chat-plugin.js +144 -41
  171. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  172. package/dist/server/agent-run-context.d.ts +7 -1
  173. package/dist/server/agent-run-context.js +9 -2
  174. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  175. package/dist/server/agent-teams.js +38 -9
  176. package/dist/server/auth-marketing.d.ts +1 -0
  177. package/dist/server/auth-marketing.js +0 -2
  178. package/dist/server/auth.d.ts +1 -0
  179. package/dist/server/auth.js +18 -0
  180. package/dist/server/index.d.ts +1 -1
  181. package/dist/server/onboarding-html.d.ts +1 -0
  182. package/dist/server/onboarding-html.js +2 -88
  183. package/dist/server/realtime-token.d.ts +1 -1
  184. package/dist/server/request-context.d.ts +7 -0
  185. package/dist/server/request-context.js +15 -0
  186. package/dist/shared/markdown-block-split.d.ts +16 -0
  187. package/dist/shared/markdown-block-split.js +81 -5
  188. package/dist/styles/agent-native.css +13 -0
  189. package/dist/voice/voice-cleanup-prompt.js +1 -0
  190. package/docs/content/agent-surfaces.mdx +30 -0
  191. package/package.json +2 -2
  192. 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: "Loading context view\u2026" }), 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 }));
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[Open Builder space settings](${BUILDER_SPACE_SETTINGS_URL})`;
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[Start new chat](${NEW_CHAT_ACTION_HREF})`;
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[Start new chat](${NEW_CHAT_ACTION_HREF})`;
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[Upgrade at builder.io](${upgradeUrl})`;
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: "Builder rejected the connected credentials. Reconnect Builder.io (free tier available) in Settings, then retry.",
215
+ message: BUILDER_AUTHENTICATION_ERROR,
128
216
  details: text,
129
217
  };
130
218
  }
@@ -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 && args.initialMessages) {
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: hydration.locale === sourceLocale && hydration.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
- i18n.addResourceBundle(sourceLocale, namespace, sourceMessages, true, true);
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
- if (locale !== sourceLocale &&
237
- !i18n.hasResourceBundle(locale, namespace)) {
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 loaded = preloaded ?? (await loadMessages?.(locale));
244
- const messages = normalizeLoadedMessages(loaded);
245
- if (messages) {
246
- i18n.addResourceBundle(locale, namespace, messages, true, true);
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": "Thumbs up", onClick: handleThumbsUp, className: cn("flex h-6 w-6 items-center justify-center rounded", selection === "up"
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": "Thumbs down", onClick: handleThumbsDown, className: cn("flex h-6 w-6 items-center justify-center rounded", selection === "down"
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-left text-xs", submittedCategory === category
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 nameEditedRef = useRef(false);
111
- const displayName = profileQuery.data?.name ||
112
- session?.name ||
113
- email ||
114
- t("settings.profileSignedOut");
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
- nameEditedRef.current = false;
117
+ nameFieldEditedRef.current = false;
118
+ setIsEditingName(false);
119
+ setName("");
120
+ setSavedName("");
117
121
  }, [email]);
118
122
  useEffect(() => {
119
- if (nameEditedRef.current)
123
+ if (nameFieldEditedRef.current)
120
124
  return;
121
- const nextName = profileQuery.data?.name || session?.name;
122
- if (nextName)
123
- setName(nextName);
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
- nameEditedRef.current = true;
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" }), _jsx(ActionButton, { type: "button", intent: "primary", emphasis: "solid", size: "compact", pending: updateProfile.isPending, disabled: !email || updateProfile.isPending || !name.trim(), onPress: handleProfileSave, children: updateProfile.isPending
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
- /** Optional trigger style. Defaults to a text-only "Share" label.
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;