@agent-native/toolkit 0.212.1 → 0.214.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +10 -4
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +13 -1
  6. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  7. package/dist/app/agentkit/react/context.js +3 -1
  8. package/dist/app/agentkit/react/context.js.map +1 -1
  9. package/dist/app/agentkit/react/index.d.ts +1 -0
  10. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  11. package/dist/app/agentkit/react/index.js +1 -0
  12. package/dist/app/agentkit/react/index.js.map +1 -1
  13. package/dist/app/agentkit/react/message-content-order.d.ts +21 -0
  14. package/dist/app/agentkit/react/message-content-order.d.ts.map +1 -0
  15. package/dist/app/agentkit/react/message-content-order.js +173 -0
  16. package/dist/app/agentkit/react/message-content-order.js.map +1 -0
  17. package/dist/app/agentkit/react/root.d.ts +3 -1
  18. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  19. package/dist/app/agentkit/react/root.js +2 -2
  20. package/dist/app/agentkit/react/root.js.map +1 -1
  21. package/dist/app/agentkit/react/styles.css +2 -2
  22. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  23. package/dist/app/chat/AgentKitAssistantChat.js +7 -2
  24. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  25. package/dist/app/chat/AgentPanel.d.ts +14 -2
  26. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  27. package/dist/app/chat/AgentPanel.js +22 -5
  28. package/dist/app/chat/AgentPanel.js.map +1 -1
  29. package/dist/app/chat/chat/surface-types.d.ts +8 -0
  30. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  31. package/dist/app/chat/chat/surface-types.js.map +1 -1
  32. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  33. package/dist/app/chat/chat/widgets/DataChartRenderer.js +127 -30
  34. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  35. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -2
  36. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  37. package/dist/app/chat/chat/widgets/DataChartWidget.js +14 -9
  38. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  39. package/dist/app/chat/chat/widgets/DataInsightsWidget.js +1 -1
  40. package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
  41. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts +3 -2
  42. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts.map +1 -1
  43. package/dist/app/chat/chat/widgets/DataTableWidget.js +10 -66
  44. package/dist/app/chat/chat/widgets/DataTableWidget.js.map +1 -1
  45. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +11 -0
  46. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -0
  47. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +131 -0
  48. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -0
  49. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
  50. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
  51. package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  52. package/dist/app/chat/mcp-apps/McpAppRenderer.js +6 -5
  53. package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
  54. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts +2 -0
  55. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts.map +1 -0
  56. package/dist/app/chat/mcp-apps/mcp-app-theme.js +11 -0
  57. package/dist/app/chat/mcp-apps/mcp-app-theme.js.map +1 -0
  58. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  59. package/dist/app/i18n/catalogs/ar-SA.js +10 -0
  60. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  61. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  62. package/dist/app/i18n/catalogs/de-DE.js +10 -0
  63. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  64. package/dist/app/i18n/catalogs/en-US.d.ts +10 -0
  65. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  66. package/dist/app/i18n/catalogs/en-US.js +10 -0
  67. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  68. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  69. package/dist/app/i18n/catalogs/es-ES.js +10 -0
  70. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  71. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  72. package/dist/app/i18n/catalogs/fr-FR.js +10 -0
  73. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  74. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  75. package/dist/app/i18n/catalogs/hi-IN.js +10 -0
  76. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  77. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  78. package/dist/app/i18n/catalogs/ja-JP.js +10 -0
  79. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  80. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  81. package/dist/app/i18n/catalogs/ko-KR.js +10 -0
  82. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  83. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  84. package/dist/app/i18n/catalogs/pt-BR.js +10 -0
  85. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  86. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  87. package/dist/app/i18n/catalogs/zh-CN.js +10 -0
  88. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  89. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  90. package/dist/app/i18n/catalogs/zh-TW.js +10 -0
  91. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  92. package/dist/app/org/AuthenticationSection.d.ts +3 -0
  93. package/dist/app/org/AuthenticationSection.d.ts.map +1 -1
  94. package/dist/app/org/AuthenticationSection.js +9 -3
  95. package/dist/app/org/AuthenticationSection.js.map +1 -1
  96. package/dist/app/org/pages/OrgAuthenticationPage.d.ts.map +1 -1
  97. package/dist/app/org/pages/OrgAuthenticationPage.js +2 -2
  98. package/dist/app/org/pages/OrgAuthenticationPage.js.map +1 -1
  99. package/dist/app/providers/AppProviders.d.ts +2 -1
  100. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  101. package/dist/app/providers/AppProviders.js +4 -3
  102. package/dist/app/providers/AppProviders.js.map +1 -1
  103. package/dist/app/providers/ThemeFamilySync.d.ts +2 -0
  104. package/dist/app/providers/ThemeFamilySync.d.ts.map +1 -0
  105. package/dist/app/providers/ThemeFamilySync.js +6 -0
  106. package/dist/app/providers/ThemeFamilySync.js.map +1 -0
  107. package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
  108. package/dist/app/settings/account/PreferencesSettings.js +2 -1
  109. package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
  110. package/dist/app/settings/account/ThemeFamilySettings.d.ts +2 -0
  111. package/dist/app/settings/account/ThemeFamilySettings.d.ts.map +1 -0
  112. package/dist/app/settings/account/ThemeFamilySettings.js +31 -0
  113. package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -0
  114. package/dist/app/settings/account/search-entries.d.ts.map +1 -1
  115. package/dist/app/settings/account/search-entries.js +6 -0
  116. package/dist/app/settings/account/search-entries.js.map +1 -1
  117. package/dist/app/shared/DefaultSpinner.d.ts.map +1 -1
  118. package/dist/app/shared/DefaultSpinner.js +4 -5
  119. package/dist/app/shared/DefaultSpinner.js.map +1 -1
  120. package/dist/app/shared/ErrorBoundary.d.ts +1 -0
  121. package/dist/app/shared/ErrorBoundary.d.ts.map +1 -1
  122. package/dist/app/shared/ErrorBoundary.js +27 -12
  123. package/dist/app/shared/ErrorBoundary.js.map +1 -1
  124. package/dist/app/shared/PoweredByBadge.d.ts.map +1 -1
  125. package/dist/app/shared/PoweredByBadge.js +8 -49
  126. package/dist/app/shared/PoweredByBadge.js.map +1 -1
  127. package/dist/app/styles/agent-conversation.css +0 -23
  128. package/dist/app/styles/agent-native.css +1 -1
  129. package/dist/app/styles/theme-families.css +66 -0
  130. package/dist/composer/ComposerContextBanner.d.ts +22 -0
  131. package/dist/composer/ComposerContextBanner.d.ts.map +1 -0
  132. package/dist/composer/ComposerContextBanner.js +9 -0
  133. package/dist/composer/ComposerContextBanner.js.map +1 -0
  134. package/dist/composer/ComposerModePicker.d.ts +26 -0
  135. package/dist/composer/ComposerModePicker.d.ts.map +1 -0
  136. package/dist/composer/ComposerModePicker.js +14 -0
  137. package/dist/composer/ComposerModePicker.js.map +1 -0
  138. package/dist/composer/index.d.ts +2 -0
  139. package/dist/composer/index.d.ts.map +1 -1
  140. package/dist/composer/index.js +2 -0
  141. package/dist/composer/index.js.map +1 -1
  142. package/dist/styles.css +1 -0
  143. package/package.json +3 -3
  144. package/src/app/agentkit/react/components.spec.tsx +12 -0
  145. package/src/app/agentkit/react/components.tsx +41 -22
  146. package/src/app/agentkit/react/context.tsx +17 -0
  147. package/src/app/agentkit/react/index.ts +1 -0
  148. package/src/app/agentkit/react/lifecycle.spec.tsx +116 -1
  149. package/src/app/agentkit/react/message-content-order.spec.ts +190 -0
  150. package/src/app/agentkit/react/message-content-order.ts +246 -0
  151. package/src/app/agentkit/react/root.tsx +4 -0
  152. package/src/app/agentkit/react/styles.css +2 -2
  153. package/src/app/chat/AgentKitAssistantChat.spec.tsx +19 -0
  154. package/src/app/chat/AgentKitAssistantChat.tsx +10 -1
  155. package/src/app/chat/AgentPanel.header.spec.ts +72 -14
  156. package/src/app/chat/AgentPanel.tsx +97 -53
  157. package/src/app/chat/chat/surface-types.ts +8 -0
  158. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +217 -0
  159. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +306 -68
  160. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +329 -0
  161. package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
  162. package/src/app/chat/chat/widgets/DataChartWidget.tsx +48 -33
  163. package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +2 -2
  164. package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +37 -3
  165. package/src/app/chat/chat/widgets/DataTableWidget.tsx +22 -86
  166. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +301 -0
  167. package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -5
  168. package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -5
  169. package/src/app/chat/mcp-apps/mcp-app-theme.spec.ts +30 -0
  170. package/src/app/chat/mcp-apps/mcp-app-theme.ts +11 -0
  171. package/src/app/i18n/catalogs/ar-SA.ts +12 -0
  172. package/src/app/i18n/catalogs/de-DE.ts +13 -0
  173. package/src/app/i18n/catalogs/en-US.ts +12 -0
  174. package/src/app/i18n/catalogs/es-ES.ts +14 -0
  175. package/src/app/i18n/catalogs/fr-FR.ts +14 -0
  176. package/src/app/i18n/catalogs/hi-IN.ts +12 -0
  177. package/src/app/i18n/catalogs/ja-JP.ts +12 -0
  178. package/src/app/i18n/catalogs/ko-KR.ts +12 -0
  179. package/src/app/i18n/catalogs/pt-BR.ts +13 -0
  180. package/src/app/i18n/catalogs/zh-CN.ts +11 -0
  181. package/src/app/i18n/catalogs/zh-TW.ts +12 -0
  182. package/src/app/org/AuthenticationSection.spec.tsx +62 -0
  183. package/src/app/org/AuthenticationSection.tsx +47 -1
  184. package/src/app/org/pages/OrgAuthenticationPage.tsx +4 -0
  185. package/src/app/org/pages/OrgSettingsPages.spec.tsx +3 -0
  186. package/src/app/providers/AppProviders.spec.tsx +71 -0
  187. package/src/app/providers/AppProviders.tsx +10 -0
  188. package/src/app/providers/ThemeFamilySync.tsx +6 -0
  189. package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -2
  190. package/src/app/settings/account/PreferencesSettings.tsx +8 -0
  191. package/src/app/settings/account/ThemeFamilySettings.tsx +61 -0
  192. package/src/app/settings/account/search-entries.ts +6 -0
  193. package/src/app/shared/DefaultSpinner.spec.tsx +14 -0
  194. package/src/app/shared/DefaultSpinner.tsx +4 -5
  195. package/src/app/shared/ErrorBoundary.spec.tsx +45 -2
  196. package/src/app/shared/ErrorBoundary.tsx +37 -12
  197. package/src/app/shared/PoweredByBadge.spec.tsx +16 -0
  198. package/src/app/shared/PoweredByBadge.tsx +8 -50
  199. package/src/app/styles/agent-conversation.css +0 -23
  200. package/src/app/styles/agent-native.css +1 -1
  201. package/src/app/styles/agent-native.spec.ts +18 -0
  202. package/src/app/styles/theme-families.css +66 -0
  203. package/src/app/styles/theme-families.spec.ts +29 -0
  204. package/src/composer/ComposerContextBanner.spec.tsx +74 -0
  205. package/src/composer/ComposerContextBanner.tsx +72 -0
  206. package/src/composer/ComposerModePicker.spec.tsx +101 -0
  207. package/src/composer/ComposerModePicker.tsx +98 -0
  208. package/src/composer/index.ts +9 -0
  209. package/src/styles.css +1 -0
@@ -1,18 +1,20 @@
1
- import { requestAgentSidebarOpen } from "@agent-native/core/client/agent-chat";
2
- import { startAgentChatViewTransition } from "@agent-native/core/client/agent-chat";
3
- import { appPath } from "@agent-native/core/client/api-path";
4
1
  import {
5
2
  DEFAULT_LOCALE,
6
3
  useOptionalLocale,
7
4
  useT,
8
5
  } from "@agent-native/core/client/i18n";
9
6
  import { cn } from "@agent-native/toolkit/utils";
10
- import { IconDownload, IconExternalLink, IconTable } from "@tabler/icons-react";
7
+ import { IconDownload, IconTable } from "@tabler/icons-react";
11
8
 
12
9
  import { downloadFile, toCSVTable } from "../../../db-admin/export-utils.js";
13
- import type { DataTableWidget as DataTableWidgetData } from "./data-widget-types.js";
14
-
15
- const SAFE_ACTION_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
10
+ import type {
11
+ DataTableWidget as DataTableWidgetData,
12
+ DataWidgetDisplay,
13
+ } from "./data-widget-types.js";
14
+ import {
15
+ DataWidgetDisplayControls,
16
+ DataWidgetSourceChips,
17
+ } from "./DataWidgetDisplayControls.js";
16
18
 
17
19
  function formatCell(
18
20
  value: unknown,
@@ -42,60 +44,6 @@ function formatCell(
42
44
  return text;
43
45
  }
44
46
 
45
- function normalizeActionHref(href: string): string | null {
46
- const trimmed = href.trim();
47
- if (!trimmed) return null;
48
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
49
- return appPath(trimmed);
50
- }
51
-
52
- const base =
53
- typeof window === "undefined"
54
- ? "http://agent-native.local/"
55
- : window.location.href;
56
- try {
57
- const url = new URL(trimmed, base);
58
- if (!SAFE_ACTION_PROTOCOLS.has(url.protocol)) return null;
59
-
60
- if (
61
- typeof window !== "undefined" &&
62
- url.origin === window.location.origin
63
- ) {
64
- return appPath(`${url.pathname}${url.search}${url.hash}`);
65
- }
66
-
67
- return url.toString();
68
- } catch {
69
- return null;
70
- }
71
- }
72
-
73
- function isSameAppHref(href: string): boolean {
74
- if (typeof window === "undefined") return false;
75
- try {
76
- const url = new URL(href, window.location.href);
77
- return (
78
- url.origin === window.location.origin && url.pathname.startsWith("/")
79
- );
80
- } catch {
81
- return false;
82
- }
83
- }
84
-
85
- function navigateSameAppHref(href: string): void {
86
- if (typeof window === "undefined") return;
87
- const url = new URL(href, window.location.href);
88
- startAgentChatViewTransition(() => {
89
- requestAgentSidebarOpen();
90
- window.history.pushState(
91
- window.history.state,
92
- "",
93
- `${url.pathname}${url.search}${url.hash}`,
94
- );
95
- window.dispatchEvent(new PopStateEvent("popstate"));
96
- });
97
- }
98
-
99
47
  function slugifyFilenamePart(value: string | undefined): string {
100
48
  const slug = (value ?? "data-table")
101
49
  .trim()
@@ -108,14 +56,20 @@ function slugifyFilenamePart(value: string | undefined): string {
108
56
  export function DataTableWidget({
109
57
  table,
110
58
  action,
59
+ display,
111
60
  }: {
112
61
  table: DataTableWidgetData;
113
62
  action?: { label: string; href: string };
63
+ display?: DataWidgetDisplay;
114
64
  }) {
115
65
  const t = useT();
116
66
  const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
117
67
  const rows = table.rows ?? [];
118
- const actionHref = action ? normalizeActionHref(action.href) : null;
68
+ const resolvedDisplay = display
69
+ ? { ...display, primaryAction: display.primaryAction ?? action }
70
+ : action
71
+ ? { primaryAction: action }
72
+ : undefined;
119
73
  const canExportCsv = table.columns.length > 0;
120
74
  const exportCsv = () => {
121
75
  if (!canExportCsv) return;
@@ -151,30 +105,11 @@ export function DataTableWidget({
151
105
  {table.truncated ? ` ${t("agentChat.widget.sampled")}` : ""}
152
106
  </div>
153
107
  </div>
154
- {action && actionHref ? (
155
- <a
156
- href={actionHref}
157
- onClick={(event) => {
158
- if (
159
- event.defaultPrevented ||
160
- event.button !== 0 ||
161
- event.metaKey ||
162
- event.ctrlKey ||
163
- event.shiftKey ||
164
- event.altKey ||
165
- !isSameAppHref(actionHref)
166
- ) {
167
- return;
168
- }
169
- event.preventDefault();
170
- navigateSameAppHref(actionHref);
171
- }}
172
- className="inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline"
173
- >
174
- {action.label}
175
- <IconExternalLink className="h-3 w-3" />
176
- </a>
177
- ) : null}
108
+ <DataWidgetDisplayControls
109
+ display={resolvedDisplay}
110
+ variant="secondary"
111
+ kind="table"
112
+ />
178
113
  <button
179
114
  type="button"
180
115
  onClick={exportCsv}
@@ -240,6 +175,7 @@ export function DataTableWidget({
240
175
  </tbody>
241
176
  </table>
242
177
  </div>
178
+ <DataWidgetSourceChips sources={display?.sources ?? []} />
243
179
  </div>
244
180
  );
245
181
  }
@@ -0,0 +1,301 @@
1
+ import {
2
+ requestAgentSidebarOpen,
3
+ startAgentChatViewTransition,
4
+ } from "@agent-native/core/client/agent-chat";
5
+ import { appPath } from "@agent-native/core/client/api-path";
6
+ import { useT } from "@agent-native/core/client/i18n";
7
+ import { writeClipboardText } from "@agent-native/toolkit/clipboard";
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger,
13
+ } from "@agent-native/toolkit/ui/dropdown-menu";
14
+ import {
15
+ IconCamera,
16
+ IconCompass,
17
+ IconCopy,
18
+ IconDotsVertical,
19
+ IconExternalLink,
20
+ IconNotebook,
21
+ } from "@tabler/icons-react";
22
+ import { forwardRef } from "react";
23
+
24
+ import { toast } from "../../../../hooks/use-toast.js";
25
+ import type {
26
+ DataWidgetAction,
27
+ DataWidgetActionIcon,
28
+ DataWidgetDataSource,
29
+ DataWidgetDisplay,
30
+ DataWidgetDisplayAction,
31
+ } from "./data-widget-types.js";
32
+
33
+ const SAFE_ACTION_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
34
+
35
+ function normalizeActionHref(href: string): string | null {
36
+ const trimmed = href.trim();
37
+ if (!trimmed) return null;
38
+
39
+ const hasWindow = typeof window !== "undefined";
40
+ const isRelative =
41
+ !/^[a-z][a-z\d+.-]*:/i.test(trimmed) && !trimmed.startsWith("//");
42
+ const base = hasWindow ? window.location.href : "http://agent-native.local/";
43
+ let url: URL;
44
+ try {
45
+ url = new URL(trimmed, base);
46
+ } catch (error) {
47
+ if (error instanceof TypeError) return null;
48
+ throw error;
49
+ }
50
+
51
+ if (!SAFE_ACTION_PROTOCOLS.has(url.protocol)) return null;
52
+
53
+ if (!hasWindow && isRelative) {
54
+ return appPath(`${url.pathname}${url.search}${url.hash}`);
55
+ }
56
+
57
+ if (hasWindow && url.origin === window.location.origin) {
58
+ return appPath(`${url.pathname}${url.search}${url.hash}`);
59
+ }
60
+
61
+ return url.toString();
62
+ }
63
+
64
+ function navigateSameAppHref(href: string): void {
65
+ if (typeof window === "undefined") return;
66
+ const url = new URL(href, window.location.href);
67
+ startAgentChatViewTransition(() => {
68
+ requestAgentSidebarOpen();
69
+ window.history.pushState(
70
+ window.history.state,
71
+ "",
72
+ `${url.pathname}${url.search}${url.hash}`,
73
+ );
74
+ window.dispatchEvent(new PopStateEvent("popstate"));
75
+ });
76
+ }
77
+
78
+ const WidgetActionLink = forwardRef<
79
+ HTMLAnchorElement,
80
+ {
81
+ href: string;
82
+ label: string;
83
+ className: string;
84
+ icon?: DataWidgetActionIcon;
85
+ }
86
+ >(function WidgetActionLink({ href, label, className, icon }, ref) {
87
+ return (
88
+ <a
89
+ ref={ref}
90
+ href={href}
91
+ onClick={(event) => {
92
+ if (
93
+ event.defaultPrevented ||
94
+ event.button !== 0 ||
95
+ event.metaKey ||
96
+ event.ctrlKey ||
97
+ event.shiftKey ||
98
+ event.altKey ||
99
+ typeof window === "undefined"
100
+ ) {
101
+ return;
102
+ }
103
+
104
+ const url = new URL(href, window.location.href);
105
+ if (url.origin !== window.location.origin) return;
106
+
107
+ event.preventDefault();
108
+ navigateSameAppHref(href);
109
+ }}
110
+ className={className}
111
+ >
112
+ <span className="truncate">{label}</span>
113
+ {icon === "explore" ? (
114
+ <IconCompass aria-hidden="true" className="size-3.5 shrink-0" />
115
+ ) : icon === "camera" ? (
116
+ <IconCamera aria-hidden="true" className="size-3.5 shrink-0" />
117
+ ) : icon === "notebook" ? (
118
+ <IconNotebook aria-hidden="true" className="size-3.5 shrink-0" />
119
+ ) : (
120
+ <IconExternalLink aria-hidden="true" className="size-3.5 shrink-0" />
121
+ )}
122
+ </a>
123
+ );
124
+ });
125
+
126
+ type PreparedDisplayAction =
127
+ | {
128
+ type: "link";
129
+ action: Extract<DataWidgetDisplayAction, { type: "link" }>;
130
+ href: string;
131
+ key: string;
132
+ }
133
+ | {
134
+ type: "copy";
135
+ action: Extract<DataWidgetDisplayAction, { type: "copy" }>;
136
+ href: null;
137
+ key: string;
138
+ };
139
+
140
+ export function DataWidgetDisplayControls({
141
+ display,
142
+ chartActions = [],
143
+ variant = "primary",
144
+ kind = "chart",
145
+ }: {
146
+ display?: DataWidgetDisplay;
147
+ chartActions?: DataWidgetAction[];
148
+ variant?: "primary" | "secondary";
149
+ kind?: "chart" | "table";
150
+ }) {
151
+ const t = useT();
152
+ const copyText = async (text: string) => {
153
+ let copied = false;
154
+ try {
155
+ copied = await writeClipboardText(text);
156
+ } catch {
157
+ copied = false;
158
+ }
159
+
160
+ toast({
161
+ title: copied
162
+ ? t("agentChat.common.copied")
163
+ : t("agentChat.recovery.copyFailed"),
164
+ });
165
+ };
166
+ const displayActions: DataWidgetDisplayAction[] = [
167
+ ...(display?.actions ?? []),
168
+ ...chartActions.map((action) => ({ ...action, type: "link" as const })),
169
+ ];
170
+ const primary = display?.primaryAction
171
+ ? {
172
+ ...display.primaryAction,
173
+ href: normalizeActionHref(display.primaryAction.href),
174
+ }
175
+ : null;
176
+ const actions: PreparedDisplayAction[] = [];
177
+ for (const [index, action] of displayActions.entries()) {
178
+ if (action.type === "link") {
179
+ const href = normalizeActionHref(action.href);
180
+ if (href) {
181
+ actions.push({
182
+ type: "link",
183
+ action,
184
+ href,
185
+ key: action.id ?? `link-${index}`,
186
+ });
187
+ }
188
+ continue;
189
+ }
190
+ actions.push({
191
+ type: "copy",
192
+ action,
193
+ href: null,
194
+ key: action.id ?? `copy-${index}`,
195
+ });
196
+ }
197
+ const actionsLabel =
198
+ kind === "table"
199
+ ? t("agentChat.widget.tableActions", { defaultValue: "Table actions" })
200
+ : t("agentChat.widget.chartActions", { defaultValue: "Chart actions" });
201
+
202
+ return (
203
+ <div className="flex shrink-0 items-center gap-1.5">
204
+ {primary?.href ? (
205
+ <WidgetActionLink
206
+ href={primary.href}
207
+ label={primary.label}
208
+ icon={primary.icon}
209
+ className={
210
+ variant === "secondary"
211
+ ? "inline-flex max-w-44 items-center gap-1.5 truncate rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline"
212
+ : "inline-flex max-w-44 items-center gap-1.5 truncate rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground no-underline hover:bg-primary/90 hover:no-underline"
213
+ }
214
+ />
215
+ ) : null}
216
+ {actions.length > 0 ? (
217
+ <DropdownMenu>
218
+ <DropdownMenuTrigger asChild>
219
+ <button
220
+ type="button"
221
+ aria-label={actionsLabel}
222
+ title={actionsLabel}
223
+ className="inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
224
+ >
225
+ <IconDotsVertical aria-hidden="true" className="size-4" />
226
+ </button>
227
+ </DropdownMenuTrigger>
228
+ <DropdownMenuContent align="end">
229
+ {actions.map((item) =>
230
+ item.type === "copy" ? (
231
+ <DropdownMenuItem
232
+ key={item.key}
233
+ onSelect={() => void copyText(item.action.text)}
234
+ >
235
+ <IconCopy aria-hidden="true" className="size-4" />
236
+ {item.action.label}
237
+ </DropdownMenuItem>
238
+ ) : (
239
+ <DropdownMenuItem key={item.key} asChild>
240
+ <WidgetActionLink
241
+ href={item.href}
242
+ label={item.action.label}
243
+ icon={
244
+ item.action.type === "link" ? item.action.icon : undefined
245
+ }
246
+ className="flex w-full items-center justify-between gap-3"
247
+ />
248
+ </DropdownMenuItem>
249
+ ),
250
+ )}
251
+ </DropdownMenuContent>
252
+ </DropdownMenu>
253
+ ) : null}
254
+ </div>
255
+ );
256
+ }
257
+
258
+ export function DataWidgetSourceChips({
259
+ sources,
260
+ }: {
261
+ sources: DataWidgetDataSource[];
262
+ }) {
263
+ const t = useT();
264
+
265
+ if (sources.length === 0) return null;
266
+
267
+ return (
268
+ <div
269
+ aria-label={t("agentChat.widget.dataSources", {
270
+ defaultValue: "Data sources",
271
+ })}
272
+ className="flex flex-wrap items-center gap-1.5 border-t border-border px-3 py-2"
273
+ role="group"
274
+ >
275
+ <span className="text-[10px] font-medium text-muted-foreground">
276
+ {t("agentChat.widget.dataSources", { defaultValue: "Data sources" })}
277
+ </span>
278
+ {sources.map((source, index) => {
279
+ const href = source.href ? normalizeActionHref(source.href) : null;
280
+ const className =
281
+ "inline-flex max-w-52 items-center truncate rounded-full border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground no-underline hover:bg-muted hover:text-foreground hover:no-underline";
282
+
283
+ return href ? (
284
+ <WidgetActionLink
285
+ key={source.id ?? `${source.label}-${index}`}
286
+ href={href}
287
+ label={source.label}
288
+ className={className}
289
+ />
290
+ ) : (
291
+ <span
292
+ key={source.id ?? `${source.label}-${index}`}
293
+ className={className}
294
+ >
295
+ {source.label}
296
+ </span>
297
+ );
298
+ })}
299
+ </div>
300
+ );
301
+ }
@@ -282,17 +282,17 @@ function renderDataWidget(context: ToolRendererContext) {
282
282
  if (widget === DATA_TABLE_WIDGET && result.table) {
283
283
  return (
284
284
  <Suspense fallback={<BuiltinToolRendererSkeleton />}>
285
- <LazyDataTableWidget
286
- table={result.table}
287
- action={result.display?.primaryAction}
288
- />
285
+ <LazyDataTableWidget table={result.table} display={result.display} />
289
286
  </Suspense>
290
287
  );
291
288
  }
292
289
  if (widget === DATA_CHART_WIDGET && result.chartSeries) {
293
290
  return (
294
291
  <Suspense fallback={<BuiltinToolRendererSkeleton />}>
295
- <LazyDataChartWidget chart={result.chartSeries} />
292
+ <LazyDataChartWidget
293
+ chart={result.chartSeries}
294
+ display={result.display}
295
+ />
296
296
  </Suspense>
297
297
  );
298
298
  }
@@ -36,6 +36,7 @@ import {
36
36
  SESSION_REPLAY_BLOCK_PROPS,
37
37
  SESSION_REPLAY_MASK_PROPS,
38
38
  } from "../session-replay-privacy.js";
39
+ import { resolveMcpAppTheme } from "./mcp-app-theme.js";
39
40
 
40
41
  type CallToolResult = Parameters<
41
42
  InstanceType<typeof AppBridge>["sendToolResult"]
@@ -810,11 +811,15 @@ function buildHostContext(app: AgentMcpAppPayload, maxHeight: number) {
810
811
  : null;
811
812
  const cssVar = (name: string) => root?.getPropertyValue(name).trim() || "";
812
813
  const theme =
813
- typeof document !== "undefined" &&
814
- (document.documentElement.classList.contains("dark") ||
815
- window.matchMedia?.("(prefers-color-scheme: dark)").matches)
816
- ? "dark"
817
- : "light";
814
+ typeof document === "undefined"
815
+ ? "light"
816
+ : resolveMcpAppTheme(
817
+ document.documentElement,
818
+ () =>
819
+ typeof window !== "undefined" &&
820
+ (window.matchMedia?.("(prefers-color-scheme: dark)").matches ??
821
+ false),
822
+ );
818
823
  return {
819
824
  toolInfo: app.tool
820
825
  ? {
@@ -0,0 +1,30 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ import { resolveMcpAppTheme } from "./mcp-app-theme.js";
4
+
5
+ function themeRoot(className: string, dataTheme: string | null) {
6
+ return {
7
+ classList: {
8
+ contains: (name: string) => className.split(" ").includes(name),
9
+ },
10
+ getAttribute: () => dataTheme,
11
+ } as unknown as Pick<HTMLElement, "classList" | "getAttribute">;
12
+ }
13
+
14
+ describe("resolveMcpAppTheme", () => {
15
+ it("keeps the selected host theme when the operating system prefers another theme", () => {
16
+ const readSystemTheme = vi.fn(() => true);
17
+ expect(
18
+ resolveMcpAppTheme(themeRoot("light", "light"), readSystemTheme),
19
+ ).toBe("light");
20
+ expect(resolveMcpAppTheme(themeRoot("dark", "dark"), readSystemTheme)).toBe(
21
+ "dark",
22
+ );
23
+ expect(readSystemTheme).not.toHaveBeenCalled();
24
+ });
25
+
26
+ it("uses the system preference only when no selected theme is present", () => {
27
+ expect(resolveMcpAppTheme(themeRoot("", null), () => true)).toBe("dark");
28
+ expect(resolveMcpAppTheme(themeRoot("", null), () => false)).toBe("light");
29
+ });
30
+ });
@@ -0,0 +1,11 @@
1
+ export function resolveMcpAppTheme(
2
+ root: Pick<HTMLElement, "classList" | "getAttribute">,
3
+ readSystemTheme: () => boolean,
4
+ ): "dark" | "light" {
5
+ if (root.classList.contains("dark")) return "dark";
6
+ if (root.classList.contains("light")) return "light";
7
+
8
+ const dataTheme = root.getAttribute("data-theme");
9
+ if (dataTheme === "dark" || dataTheme === "light") return dataTheme;
10
+ return readSystemTheme() ? "dark" : "light";
11
+ }
@@ -1522,6 +1522,9 @@ const messages: ToolkitAgentChatTranslation = {
1522
1522
  "tool.worked": "عمل",
1523
1523
  "tool.workedFor": "عمل لمدة {{duration}}",
1524
1524
  "widget.chart": "مخطط",
1525
+ "widget.chartActions": "إجراءات المخطط",
1526
+ "widget.dataSources": "مصادر البيانات",
1527
+ "widget.tableActions": "إجراءات الجدول",
1525
1528
  "widget.dataChart": "مخطط بيانات",
1526
1529
  "widget.dataInsights": "رؤى البيانات",
1527
1530
  "widget.dataTable": "جدول بيانات",
@@ -2157,6 +2160,9 @@ const messages: ToolkitAgentChatTranslation = {
2157
2160
  "معيّن. يتيح لتطبيقات مساحة العمل هذه التحقق من بعضها.",
2158
2161
  "settingsOrg.auth.sharedSecretNotSet":
2159
2162
  "غير معيّن. يتيح لتطبيقات مساحة العمل هذه التحقق من بعضها.",
2163
+ "settingsOrg.auth.memberDelegation": "تفويض هوية أعضاء المؤسسة",
2164
+ "settingsOrg.auth.memberDelegationHelp":
2165
+ "يمكن للتطبيقات المتصلة التي تستخدم السر المشترك لهذه المؤسسة ادّعاء هوية أي عضو حالي عند الاتصال بهذا التطبيق. يتحقق هذا التطبيق من قائمة الأعضاء الحالية ويقيّد الوصول المفوّض إلى المؤسسة بصلاحيات مستوى العضو.",
2160
2166
  "settingsOrg.auth.secretNotSetValue": "غير معيّن",
2161
2167
  "settingsOrg.auth.manage": "إدارة",
2162
2168
  "settingsOrg.auth.reveal": "إظهار",
@@ -2243,6 +2249,12 @@ const messages: ToolkitAgentChatTranslation = {
2243
2249
  "تحقق من بريدك الإلكتروني للاطلاع على تعليمات تأكيد هذا التغيير.",
2244
2250
  "settingsShell.account.languageAndRegion": "اللغة والمنطقة",
2245
2251
  "settingsShell.account.languageDescription": "يُطبَّق على جميع أجهزتك.",
2252
+ "settingsShell.account.appearance": "المظهر",
2253
+ "settingsShell.account.themeFamily": "نمط السمة",
2254
+ "settingsShell.account.themeFamilyAgentNative": "Agent-Native",
2255
+ "settingsShell.account.themeFamilyOriginal": "الألوان الأصلية للتطبيق",
2256
+ "settingsShell.account.themeFamilySaveError":
2257
+ "تعذّر تحديث نمط السمة. حاول مرة أخرى.",
2246
2258
  "settingsShell.account.manage": "إدارة",
2247
2259
  "settingsShell.account.name": "الاسم",
2248
2260
  "settingsShell.account.nameDescription":
@@ -1068,6 +1068,9 @@ const messages: ToolkitAgentChatTranslation = {
1068
1068
  "tool.worked": "Verarbeitet",
1069
1069
  "tool.workedFor": "{{duration}} verarbeitet",
1070
1070
  "widget.chart": "Diagramm",
1071
+ "widget.chartActions": "Diagrammaktionen",
1072
+ "widget.dataSources": "Datenquellen",
1073
+ "widget.tableActions": "Tabellenaktionen",
1071
1074
  "widget.dataChart": "Datendiagramm",
1072
1075
  "widget.dataInsights": "Datenerkenntnisse",
1073
1076
  "widget.dataTable": "Datentabelle",
@@ -2135,6 +2138,10 @@ const messages: ToolkitAgentChatTranslation = {
2135
2138
  "Festgelegt. Damit können sich die Apps in diesem Workspace gegenseitig verifizieren.",
2136
2139
  "settingsOrg.auth.sharedSecretNotSet":
2137
2140
  "Nicht festgelegt. Damit können sich die Apps in diesem Workspace gegenseitig verifizieren.",
2141
+ "settingsOrg.auth.memberDelegation":
2142
+ "Identitätsdelegierung für Organisationsmitglieder",
2143
+ "settingsOrg.auth.memberDelegationHelp":
2144
+ "Verbundene Apps mit diesem gemeinsamen Organisations-Secret können beim Aufruf dieser App die Identität eines beliebigen aktuellen Mitglieds annehmen. Diese App prüft die aktuelle Mitgliederliste und beschränkt den delegierten Organisationszugriff auf Berechtigungen der Mitgliedsrolle.",
2138
2145
  "settingsOrg.auth.secretNotSetValue": "Nicht festgelegt",
2139
2146
  "settingsOrg.auth.manage": "Verwalten",
2140
2147
  "settingsOrg.auth.reveal": "Anzeigen",
@@ -2200,6 +2207,12 @@ const messages: ToolkitAgentChatTranslation = {
2200
2207
  "Prüfe deine E-Mails, um die Änderung zu bestätigen.",
2201
2208
  "settingsShell.account.languageAndRegion": "Sprache und Region",
2202
2209
  "settingsShell.account.languageDescription": "Gilt auf allen deinen Geräten.",
2210
+ "settingsShell.account.appearance": "Darstellung",
2211
+ "settingsShell.account.themeFamily": "Farbschema",
2212
+ "settingsShell.account.themeFamilyAgentNative": "Agent-Native",
2213
+ "settingsShell.account.themeFamilyOriginal": "Originalfarben der App",
2214
+ "settingsShell.account.themeFamilySaveError":
2215
+ "Das Farbschema konnte nicht aktualisiert werden. Versuch es noch einmal.",
2203
2216
  "settingsShell.account.manage": "Verwalten",
2204
2217
  "settingsShell.account.name": "Name",
2205
2218
  "settingsShell.account.nameDescription":
@@ -1513,6 +1513,9 @@ const messages = {
1513
1513
  "tool.worked": "Worked",
1514
1514
  "tool.workedFor": "Worked for {{duration}}",
1515
1515
  "widget.chart": "Chart",
1516
+ "widget.chartActions": "Chart actions",
1517
+ "widget.dataSources": "Data sources",
1518
+ "widget.tableActions": "Table actions",
1516
1519
  "widget.dataChart": "Data chart",
1517
1520
  "widget.dataInsights": "Data insights",
1518
1521
  "widget.dataTable": "Data table",
@@ -2030,6 +2033,9 @@ const messages = {
2030
2033
  "Set. Lets the apps in this workspace verify each other.",
2031
2034
  "settingsOrg.auth.sharedSecretNotSet":
2032
2035
  "Not set. Lets the apps in this workspace verify each other.",
2036
+ "settingsOrg.auth.memberDelegation": "Org-member identity delegation",
2037
+ "settingsOrg.auth.memberDelegationHelp":
2038
+ "Connected apps with this shared organization secret can claim any current member's identity when calling this app. This app checks the live member roster and limits delegated organization access to member-level permissions.",
2033
2039
  "settingsOrg.auth.secretNotSetValue": "Not set",
2034
2040
  "settingsOrg.auth.manage": "Manage",
2035
2041
  "settingsOrg.auth.reveal": "Reveal",
@@ -2092,6 +2098,12 @@ const messages = {
2092
2098
  "Check your email for instructions to confirm this change.",
2093
2099
  "settingsShell.account.languageAndRegion": "Language and region",
2094
2100
  "settingsShell.account.languageDescription": "Applies on all your devices.",
2101
+ "settingsShell.account.appearance": "Appearance",
2102
+ "settingsShell.account.themeFamily": "Theme family",
2103
+ "settingsShell.account.themeFamilyAgentNative": "Agent-Native",
2104
+ "settingsShell.account.themeFamilyOriginal": "Original app colors",
2105
+ "settingsShell.account.themeFamilySaveError":
2106
+ "Could not update the theme family. Try again.",
2095
2107
  "settingsShell.account.manage": "Manage",
2096
2108
  "settingsShell.account.name": "Name",
2097
2109
  "settingsShell.account.nameDescription":