@agent-native/toolkit 0.213.1 → 0.216.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +9 -0
- package/dist/app/agentkit/react/chat.d.ts.map +1 -1
- package/dist/app/agentkit/react/chat.js +2 -2
- package/dist/app/agentkit/react/chat.js.map +1 -1
- package/dist/app/agentkit/react/components.d.ts +4 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +73 -13
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +19 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +7 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/index.d.ts +2 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +2 -0
- package/dist/app/agentkit/react/index.js.map +1 -1
- package/dist/app/agentkit/react/message-content-order.d.ts +21 -0
- package/dist/app/agentkit/react/message-content-order.d.ts.map +1 -0
- package/dist/app/agentkit/react/message-content-order.js +173 -0
- package/dist/app/agentkit/react/message-content-order.js.map +1 -0
- package/dist/app/agentkit/react/root.d.ts +6 -1
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +45 -8
- package/dist/app/agentkit/react/transcript-layout.d.ts +51 -0
- package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -0
- package/dist/app/agentkit/react/transcript-layout.js +34 -0
- package/dist/app/agentkit/react/transcript-layout.js.map +1 -0
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +9 -4
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.d.ts +16 -2
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +26 -5
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/chat/index.d.ts +2 -0
- package/dist/app/chat/chat/index.d.ts.map +1 -1
- package/dist/app/chat/chat/index.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +15 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts +7 -2
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +10 -9
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.js +300 -33
- package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -2
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.js +18 -9
- package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataTableWidget.d.ts +3 -2
- package/dist/app/chat/chat/widgets/DataTableWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataTableWidget.js +10 -66
- package/dist/app/chat/chat/widgets/DataTableWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +11 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +133 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -0
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.js +6 -5
- package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts +2 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts.map +1 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.js +11 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +9 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +9 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +9 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +9 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +9 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +9 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +9 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +9 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +9 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +9 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +9 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/providers/AppProviders.d.ts +2 -1
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +4 -3
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/providers/ThemeFamilySync.d.ts +2 -0
- package/dist/app/providers/ThemeFamilySync.d.ts.map +1 -0
- package/dist/app/providers/ThemeFamilySync.js +6 -0
- package/dist/app/providers/ThemeFamilySync.js.map +1 -0
- package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.js +2 -1
- package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
- package/dist/app/settings/account/ThemeFamilySettings.d.ts +2 -0
- package/dist/app/settings/account/ThemeFamilySettings.d.ts.map +1 -0
- package/dist/app/settings/account/ThemeFamilySettings.js +31 -0
- package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -0
- package/dist/app/settings/account/search-entries.d.ts.map +1 -1
- package/dist/app/settings/account/search-entries.js +6 -0
- package/dist/app/settings/account/search-entries.js.map +1 -1
- package/dist/app/shared/DefaultSpinner.d.ts.map +1 -1
- package/dist/app/shared/DefaultSpinner.js +4 -5
- package/dist/app/shared/DefaultSpinner.js.map +1 -1
- package/dist/app/shared/ErrorBoundary.d.ts +1 -0
- package/dist/app/shared/ErrorBoundary.d.ts.map +1 -1
- package/dist/app/shared/ErrorBoundary.js +27 -12
- package/dist/app/shared/ErrorBoundary.js.map +1 -1
- package/dist/app/shared/PoweredByBadge.d.ts.map +1 -1
- package/dist/app/shared/PoweredByBadge.js +8 -49
- package/dist/app/shared/PoweredByBadge.js.map +1 -1
- package/dist/app/styles/agent-conversation.css +0 -23
- package/dist/app/styles/agent-native.css +1 -1
- package/dist/app/styles/theme-families.css +66 -0
- package/dist/composer/ComposerContextBanner.d.ts +24 -0
- package/dist/composer/ComposerContextBanner.d.ts.map +1 -0
- package/dist/composer/ComposerContextBanner.js +9 -0
- package/dist/composer/ComposerContextBanner.js.map +1 -0
- package/dist/composer/ComposerModePicker.d.ts +26 -0
- package/dist/composer/ComposerModePicker.d.ts.map +1 -0
- package/dist/composer/ComposerModePicker.js +14 -0
- package/dist/composer/ComposerModePicker.js.map +1 -0
- package/dist/composer/index.d.ts +2 -0
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/index.js +2 -0
- package/dist/composer/index.js.map +1 -1
- package/dist/styles.css +1 -0
- package/package.json +3 -3
- package/src/app/agentkit/react/chat.tsx +2 -0
- package/src/app/agentkit/react/components.interaction.spec.tsx +107 -0
- package/src/app/agentkit/react/components.spec.tsx +40 -1
- package/src/app/agentkit/react/components.tsx +186 -31
- package/src/app/agentkit/react/context.tsx +29 -0
- package/src/app/agentkit/react/index.ts +2 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +289 -4
- package/src/app/agentkit/react/message-content-order.spec.ts +190 -0
- package/src/app/agentkit/react/message-content-order.ts +246 -0
- package/src/app/agentkit/react/root.tsx +11 -0
- package/src/app/agentkit/react/styles.css +45 -8
- package/src/app/agentkit/react/transcript-layout.spec.ts +72 -0
- package/src/app/agentkit/react/transcript-layout.ts +97 -0
- package/src/app/auth/AuthPage.attribution.spec.tsx +6 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +58 -0
- package/src/app/chat/AgentKitAssistantChat.tsx +15 -1
- package/src/app/chat/AgentPanel.header.spec.ts +72 -14
- package/src/app/chat/AgentPanel.page-header-share.spec.tsx +101 -0
- package/src/app/chat/AgentPanel.tsx +111 -55
- package/src/app/chat/chat/index.ts +2 -0
- package/src/app/chat/chat/surface-types.ts +18 -0
- package/src/app/chat/chat/tool-call-display.spec.tsx +83 -0
- package/src/app/chat/chat/tool-call-display.tsx +40 -8
- package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +849 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.tsx +567 -72
- package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +432 -0
- package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
- package/src/app/chat/chat/widgets/DataChartWidget.tsx +58 -33
- package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +2 -2
- package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +37 -3
- package/src/app/chat/chat/widgets/DataTableWidget.tsx +22 -86
- package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +321 -0
- package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -5
- package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -5
- package/src/app/chat/mcp-apps/mcp-app-theme.spec.ts +30 -0
- package/src/app/chat/mcp-apps/mcp-app-theme.ts +11 -0
- package/src/app/i18n/catalogs/ar-SA.ts +10 -0
- package/src/app/i18n/catalogs/de-DE.ts +11 -0
- package/src/app/i18n/catalogs/en-US.ts +11 -0
- package/src/app/i18n/catalogs/es-ES.ts +12 -0
- package/src/app/i18n/catalogs/fr-FR.ts +12 -0
- package/src/app/i18n/catalogs/hi-IN.ts +10 -0
- package/src/app/i18n/catalogs/ja-JP.ts +11 -0
- package/src/app/i18n/catalogs/ko-KR.ts +10 -0
- package/src/app/i18n/catalogs/pt-BR.ts +11 -0
- package/src/app/i18n/catalogs/zh-CN.ts +9 -0
- package/src/app/i18n/catalogs/zh-TW.ts +10 -0
- package/src/app/providers/AppProviders.spec.tsx +71 -0
- package/src/app/providers/AppProviders.tsx +10 -0
- package/src/app/providers/ThemeFamilySync.tsx +6 -0
- package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -2
- package/src/app/settings/account/PreferencesSettings.tsx +8 -0
- package/src/app/settings/account/ThemeFamilySettings.tsx +61 -0
- package/src/app/settings/account/search-entries.ts +6 -0
- package/src/app/shared/DefaultSpinner.spec.tsx +14 -0
- package/src/app/shared/DefaultSpinner.tsx +4 -5
- package/src/app/shared/ErrorBoundary.spec.tsx +45 -2
- package/src/app/shared/ErrorBoundary.tsx +37 -12
- package/src/app/shared/PoweredByBadge.spec.tsx +16 -0
- package/src/app/shared/PoweredByBadge.tsx +8 -50
- package/src/app/styles/agent-conversation.css +0 -23
- package/src/app/styles/agent-native.css +1 -1
- package/src/app/styles/agent-native.spec.ts +18 -0
- package/src/app/styles/theme-families.css +66 -0
- package/src/app/styles/theme-families.spec.ts +29 -0
- package/src/composer/ComposerContextBanner.spec.tsx +100 -0
- package/src/composer/ComposerContextBanner.tsx +77 -0
- package/src/composer/ComposerModePicker.spec.tsx +101 -0
- package/src/composer/ComposerModePicker.tsx +98 -0
- package/src/composer/index.ts +9 -0
- package/src/styles.css +1 -0
|
@@ -1068,6 +1068,11 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1068
1068
|
"tool.worked": "A travaillé",
|
|
1069
1069
|
"tool.workedFor": "A travaillé pendant {{duration}}",
|
|
1070
1070
|
"widget.chart": "Graphique",
|
|
1071
|
+
"widget.chartActions": "Actions du graphique",
|
|
1072
|
+
"widget.chartRowsOmitted":
|
|
1073
|
+
"Ce graphique n’affiche qu’une partie des lignes disponibles.",
|
|
1074
|
+
"widget.dataSources": "Sources de données",
|
|
1075
|
+
"widget.tableActions": "Actions du tableau",
|
|
1071
1076
|
"widget.dataChart": "Graphique de données",
|
|
1072
1077
|
"widget.dataInsights": "Informations sur les données",
|
|
1073
1078
|
"widget.dataTable": "Tableau de données",
|
|
@@ -2245,6 +2250,13 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2245
2250
|
"settingsShell.account.languageAndRegion": "Langue et région",
|
|
2246
2251
|
"settingsShell.account.languageDescription":
|
|
2247
2252
|
"S’applique sur tous vos appareils.",
|
|
2253
|
+
"settingsShell.account.appearance": "Apparence",
|
|
2254
|
+
"settingsShell.account.themeFamily": "Famille de thème",
|
|
2255
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2256
|
+
"settingsShell.account.themeFamilyOriginal":
|
|
2257
|
+
"Couleurs d’origine de l’application",
|
|
2258
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2259
|
+
"Impossible de mettre à jour le thème. Réessayez.",
|
|
2248
2260
|
"settingsShell.account.manage": "Gérer",
|
|
2249
2261
|
"settingsShell.account.name": "Nom",
|
|
2250
2262
|
"settingsShell.account.nameDescription":
|
|
@@ -1491,6 +1491,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1491
1491
|
"tool.worked": "काम किया",
|
|
1492
1492
|
"tool.workedFor": "{{duration}} तक काम किया",
|
|
1493
1493
|
"widget.chart": "चार्ट",
|
|
1494
|
+
"widget.chartActions": "चार्ट कार्रवाइयाँ",
|
|
1495
|
+
"widget.chartRowsOmitted": "यह चार्ट उपलब्ध पंक्तियों में से केवल कुछ दिखाता है।",
|
|
1496
|
+
"widget.dataSources": "डेटा स्रोत",
|
|
1497
|
+
"widget.tableActions": "टेबल कार्रवाइयाँ",
|
|
1494
1498
|
"widget.dataChart": "डेटा चार्ट",
|
|
1495
1499
|
"widget.dataInsights": "डेटा इनसाइट्स",
|
|
1496
1500
|
"widget.dataTable": "डेटा तालिका",
|
|
@@ -2063,6 +2067,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2063
2067
|
"इस बदलाव की पुष्टि करने के निर्देशों के लिए अपना ईमेल देखें।",
|
|
2064
2068
|
"settingsShell.account.languageAndRegion": "भाषा और क्षेत्र",
|
|
2065
2069
|
"settingsShell.account.languageDescription": "आपके सभी डिवाइस पर लागू होता है।",
|
|
2070
|
+
"settingsShell.account.appearance": "रूप-रंग",
|
|
2071
|
+
"settingsShell.account.themeFamily": "थीम शैली",
|
|
2072
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2073
|
+
"settingsShell.account.themeFamilyOriginal": "ऐप के मूल रंग",
|
|
2074
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2075
|
+
"थीम शैली अपडेट नहीं की जा सकी। फिर कोशिश करें।",
|
|
2066
2076
|
"settingsShell.account.manage": "प्रबंधित करें",
|
|
2067
2077
|
"settingsShell.account.name": "नाम",
|
|
2068
2078
|
"settingsShell.account.nameDescription":
|
|
@@ -1536,6 +1536,11 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1536
1536
|
"tool.worked": "作業しました",
|
|
1537
1537
|
"tool.workedFor": "{{duration}} 作業しました",
|
|
1538
1538
|
"widget.chart": "チャート",
|
|
1539
|
+
"widget.chartActions": "チャート操作",
|
|
1540
|
+
"widget.chartRowsOmitted":
|
|
1541
|
+
"このグラフには、利用可能な行の一部のみが表示されます。",
|
|
1542
|
+
"widget.dataSources": "データソース",
|
|
1543
|
+
"widget.tableActions": "テーブル操作",
|
|
1539
1544
|
"widget.dataChart": "データチャート",
|
|
1540
1545
|
"widget.dataInsights": "データインサイト",
|
|
1541
1546
|
"widget.dataTable": "データテーブル",
|
|
@@ -2093,6 +2098,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2093
2098
|
"settingsShell.account.languageAndRegion": "言語と地域",
|
|
2094
2099
|
"settingsShell.account.languageDescription":
|
|
2095
2100
|
"すべてのデバイスに適用されます。",
|
|
2101
|
+
"settingsShell.account.appearance": "外観",
|
|
2102
|
+
"settingsShell.account.themeFamily": "テーマ",
|
|
2103
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2104
|
+
"settingsShell.account.themeFamilyOriginal": "アプリのオリジナルカラー",
|
|
2105
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2106
|
+
"テーマを更新できませんでした。もう一度お試しください。",
|
|
2096
2107
|
"settingsShell.account.manage": "管理",
|
|
2097
2108
|
"settingsShell.account.name": "名前",
|
|
2098
2109
|
"settingsShell.account.nameDescription":
|
|
@@ -1510,6 +1510,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1510
1510
|
"tool.worked": "작업함",
|
|
1511
1511
|
"tool.workedFor": "{{duration}} 동안 작업함",
|
|
1512
1512
|
"widget.chart": "차트",
|
|
1513
|
+
"widget.chartActions": "차트 작업",
|
|
1514
|
+
"widget.chartRowsOmitted": "이 차트에는 사용 가능한 행 중 일부만 표시됩니다.",
|
|
1515
|
+
"widget.dataSources": "데이터 소스",
|
|
1516
|
+
"widget.tableActions": "표 작업",
|
|
1513
1517
|
"widget.dataChart": "데이터 차트",
|
|
1514
1518
|
"widget.dataInsights": "데이터 인사이트",
|
|
1515
1519
|
"widget.dataTable": "데이터 테이블",
|
|
@@ -2057,6 +2061,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2057
2061
|
"이메일을 확인하고 안내에 따라 이 변경을 확정하세요.",
|
|
2058
2062
|
"settingsShell.account.languageAndRegion": "언어 및 지역",
|
|
2059
2063
|
"settingsShell.account.languageDescription": "모든 기기에 적용됩니다.",
|
|
2064
|
+
"settingsShell.account.appearance": "모양",
|
|
2065
|
+
"settingsShell.account.themeFamily": "테마 스타일",
|
|
2066
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2067
|
+
"settingsShell.account.themeFamilyOriginal": "앱의 원래 색상",
|
|
2068
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2069
|
+
"테마 스타일을 업데이트하지 못했습니다. 다시 시도하세요.",
|
|
2060
2070
|
"settingsShell.account.manage": "관리",
|
|
2061
2071
|
"settingsShell.account.name": "이름",
|
|
2062
2072
|
"settingsShell.account.nameDescription":
|
|
@@ -1055,6 +1055,11 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1055
1055
|
"tool.worked": "Trabalhou",
|
|
1056
1056
|
"tool.workedFor": "Trabalhou por {{duration}}",
|
|
1057
1057
|
"widget.chart": "Gráfico",
|
|
1058
|
+
"widget.chartActions": "Ações do gráfico",
|
|
1059
|
+
"widget.chartRowsOmitted":
|
|
1060
|
+
"Este gráfico mostra apenas algumas das linhas disponíveis.",
|
|
1061
|
+
"widget.dataSources": "Fontes de dados",
|
|
1062
|
+
"widget.tableActions": "Ações da tabela",
|
|
1058
1063
|
"widget.dataChart": "Gráfico de dados",
|
|
1059
1064
|
"widget.dataInsights": "Insights de dados",
|
|
1060
1065
|
"widget.dataTable": "Tabela de dados",
|
|
@@ -2220,6 +2225,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2220
2225
|
"settingsShell.account.languageAndRegion": "Idioma e região",
|
|
2221
2226
|
"settingsShell.account.languageDescription":
|
|
2222
2227
|
"Vale para todos os seus dispositivos.",
|
|
2228
|
+
"settingsShell.account.appearance": "Aparência",
|
|
2229
|
+
"settingsShell.account.themeFamily": "Família do tema",
|
|
2230
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2231
|
+
"settingsShell.account.themeFamilyOriginal": "Cores originais do app",
|
|
2232
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2233
|
+
"Não foi possível atualizar o tema. Tente novamente.",
|
|
2223
2234
|
"settingsShell.account.manage": "Gerenciar",
|
|
2224
2235
|
"settingsShell.account.name": "Nome",
|
|
2225
2236
|
"settingsShell.account.nameDescription":
|
|
@@ -1419,6 +1419,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1419
1419
|
"tool.worked": "已处理",
|
|
1420
1420
|
"tool.workedFor": "处理了 {{duration}}",
|
|
1421
1421
|
"widget.chart": "图表",
|
|
1422
|
+
"widget.chartActions": "图表操作",
|
|
1423
|
+
"widget.chartRowsOmitted": "此图表仅显示部分可用行。",
|
|
1424
|
+
"widget.dataSources": "数据来源",
|
|
1425
|
+
"widget.tableActions": "表格操作",
|
|
1422
1426
|
"widget.dataChart": "数据图表",
|
|
1423
1427
|
"widget.dataInsights": "数据洞察",
|
|
1424
1428
|
"widget.dataTable": "数据表",
|
|
@@ -1940,6 +1944,11 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1940
1944
|
"settingsShell.account.emailChangeSent": "请查看您的邮箱,按说明确认此更改。",
|
|
1941
1945
|
"settingsShell.account.languageAndRegion": "语言和地区",
|
|
1942
1946
|
"settingsShell.account.languageDescription": "适用于您的所有设备。",
|
|
1947
|
+
"settingsShell.account.appearance": "外观",
|
|
1948
|
+
"settingsShell.account.themeFamily": "主题样式",
|
|
1949
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
1950
|
+
"settingsShell.account.themeFamilyOriginal": "原应用配色",
|
|
1951
|
+
"settingsShell.account.themeFamilySaveError": "无法更新主题样式。请重试。",
|
|
1943
1952
|
"settingsShell.account.manage": "管理",
|
|
1944
1953
|
"settingsShell.account.name": "姓名",
|
|
1945
1954
|
"settingsShell.account.nameDescription":
|
|
@@ -1424,6 +1424,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1424
1424
|
"tool.worked": "已處理",
|
|
1425
1425
|
"tool.workedFor": "處理了 {{duration}}",
|
|
1426
1426
|
"widget.chart": "圖表",
|
|
1427
|
+
"widget.chartActions": "圖表操作",
|
|
1428
|
+
"widget.chartRowsOmitted": "此圖表僅顯示部分可用資料列。",
|
|
1429
|
+
"widget.dataSources": "資料來源",
|
|
1430
|
+
"widget.tableActions": "表格操作",
|
|
1427
1431
|
"widget.dataChart": "資料圖表",
|
|
1428
1432
|
"widget.dataInsights": "資料洞察",
|
|
1429
1433
|
"widget.dataTable": "資料表",
|
|
@@ -1950,6 +1954,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1950
1954
|
"請查看您的電子郵件,依照說明確認此變更。",
|
|
1951
1955
|
"settingsShell.account.languageAndRegion": "語言和地區",
|
|
1952
1956
|
"settingsShell.account.languageDescription": "套用至您的所有裝置。",
|
|
1957
|
+
"settingsShell.account.appearance": "外觀",
|
|
1958
|
+
"settingsShell.account.themeFamily": "主題樣式",
|
|
1959
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
1960
|
+
"settingsShell.account.themeFamilyOriginal": "原始應用程式配色",
|
|
1961
|
+
"settingsShell.account.themeFamilySaveError":
|
|
1962
|
+
"無法更新主題樣式,請再試一次。",
|
|
1953
1963
|
"settingsShell.account.manage": "管理",
|
|
1954
1964
|
"settingsShell.account.name": "姓名",
|
|
1955
1965
|
"settingsShell.account.nameDescription":
|
|
@@ -9,12 +9,17 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
9
9
|
import { resetAgentEngineReadinessForTests } from "../../../../core/src/client/agent-engine-readiness.js";
|
|
10
10
|
|
|
11
11
|
const useSessionMock = vi.fn();
|
|
12
|
+
const useThemeFamilyMock = vi.fn();
|
|
12
13
|
vi.mock("@agent-native/core/client/use-session", async (importOriginal) => ({
|
|
13
14
|
...(await importOriginal<
|
|
14
15
|
typeof import("@agent-native/core/client/use-session")
|
|
15
16
|
>()),
|
|
16
17
|
useSession: () => useSessionMock(),
|
|
17
18
|
}));
|
|
19
|
+
vi.mock("@agent-native/core/client/ui", async (importOriginal) => ({
|
|
20
|
+
...(await importOriginal<typeof import("@agent-native/core/client/ui")>()),
|
|
21
|
+
useThemeFamily: () => useThemeFamilyMock(),
|
|
22
|
+
}));
|
|
18
23
|
vi.mock("@agent-native/core/client/action-query-cache", () => ({
|
|
19
24
|
ActionQueryCacheGate: ({ children }: { children: React.ReactNode }) => (
|
|
20
25
|
<div data-testid="action-query-cache-gate">{children}</div>
|
|
@@ -48,9 +53,34 @@ let originalLocation: Location;
|
|
|
48
53
|
let originalParent: Window;
|
|
49
54
|
let originalFetch: typeof window.fetch;
|
|
50
55
|
let originalModelContext: PropertyDescriptor | undefined;
|
|
56
|
+
let originalLocalStorage: PropertyDescriptor | undefined;
|
|
51
57
|
let originalDocumentTitle: string;
|
|
52
58
|
let replaceMock: ReturnType<typeof vi.fn>;
|
|
53
59
|
|
|
60
|
+
function createTestLocalStorage(): Storage {
|
|
61
|
+
const values = new Map<string, string>();
|
|
62
|
+
return {
|
|
63
|
+
get length() {
|
|
64
|
+
return values.size;
|
|
65
|
+
},
|
|
66
|
+
clear() {
|
|
67
|
+
values.clear();
|
|
68
|
+
},
|
|
69
|
+
getItem(key) {
|
|
70
|
+
return values.get(String(key)) ?? null;
|
|
71
|
+
},
|
|
72
|
+
key(index) {
|
|
73
|
+
return [...values.keys()][index] ?? null;
|
|
74
|
+
},
|
|
75
|
+
removeItem(key) {
|
|
76
|
+
values.delete(String(key));
|
|
77
|
+
},
|
|
78
|
+
setItem(key, value) {
|
|
79
|
+
values.set(String(key), String(value));
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
54
84
|
beforeEach(() => {
|
|
55
85
|
resetAgentEngineReadinessForTests();
|
|
56
86
|
container = document.createElement("div");
|
|
@@ -64,6 +94,14 @@ beforeEach(() => {
|
|
|
64
94
|
document,
|
|
65
95
|
"modelContext",
|
|
66
96
|
);
|
|
97
|
+
originalLocalStorage = Object.getOwnPropertyDescriptor(
|
|
98
|
+
window,
|
|
99
|
+
"localStorage",
|
|
100
|
+
);
|
|
101
|
+
Object.defineProperty(window, "localStorage", {
|
|
102
|
+
configurable: true,
|
|
103
|
+
value: createTestLocalStorage(),
|
|
104
|
+
});
|
|
67
105
|
Object.defineProperty(window, "fetch", {
|
|
68
106
|
configurable: true,
|
|
69
107
|
value: vi
|
|
@@ -102,6 +140,11 @@ afterEach(() => {
|
|
|
102
140
|
configurable: true,
|
|
103
141
|
value: originalFetch,
|
|
104
142
|
});
|
|
143
|
+
if (originalLocalStorage) {
|
|
144
|
+
Object.defineProperty(window, "localStorage", originalLocalStorage);
|
|
145
|
+
} else {
|
|
146
|
+
delete (window as Window & { localStorage?: Storage }).localStorage;
|
|
147
|
+
}
|
|
105
148
|
if (originalModelContext) {
|
|
106
149
|
Object.defineProperty(document, "modelContext", originalModelContext);
|
|
107
150
|
} else {
|
|
@@ -115,6 +158,7 @@ function renderProviders(props: {
|
|
|
115
158
|
isPublicPath?: boolean;
|
|
116
159
|
sessionBypass?: boolean;
|
|
117
160
|
skipFirstRunOnboarding?: boolean;
|
|
161
|
+
enableThemeFamily?: boolean;
|
|
118
162
|
disableWebMcp?: boolean;
|
|
119
163
|
children?: React.ReactNode;
|
|
120
164
|
}) {
|
|
@@ -183,6 +227,33 @@ const SIGNED_IN_SESSION = {
|
|
|
183
227
|
};
|
|
184
228
|
|
|
185
229
|
describe("AppProviders session gate", () => {
|
|
230
|
+
it("mounts theme-family sync by default in an authenticated app shell", async () => {
|
|
231
|
+
useSessionMock.mockReturnValue(SIGNED_IN_SESSION);
|
|
232
|
+
|
|
233
|
+
renderProviders({ skipFirstRunOnboarding: true });
|
|
234
|
+
await act(async () => {
|
|
235
|
+
await import("./ThemeFamilySync.js");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(useThemeFamilyMock).toHaveBeenCalledOnce();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("allows an authenticated app shell to disable theme-family sync", () => {
|
|
242
|
+
useSessionMock.mockReturnValue(SIGNED_IN_SESSION);
|
|
243
|
+
|
|
244
|
+
renderProviders({ skipFirstRunOnboarding: true, enableThemeFamily: false });
|
|
245
|
+
|
|
246
|
+
expect(useThemeFamilyMock).not.toHaveBeenCalled();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("does not load theme-family settings on public routes", () => {
|
|
250
|
+
useSessionMock.mockReturnValue(SIGNED_OUT_SESSION);
|
|
251
|
+
|
|
252
|
+
renderProviders({ isPublicPath: true });
|
|
253
|
+
|
|
254
|
+
expect(useThemeFamilyMock).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
|
|
186
257
|
it("uses Toolkit's theme-aware toaster by default", () => {
|
|
187
258
|
useSessionMock.mockReturnValue(SIGNED_OUT_SESSION);
|
|
188
259
|
|
|
@@ -95,6 +95,8 @@ export interface AppProvidersProps {
|
|
|
95
95
|
*/
|
|
96
96
|
ownsDocument?: boolean;
|
|
97
97
|
|
|
98
|
+
enableThemeFamily?: boolean;
|
|
99
|
+
|
|
98
100
|
documentTitleFallback?: string;
|
|
99
101
|
|
|
100
102
|
children: React.ReactNode;
|
|
@@ -397,6 +399,8 @@ function AgentEngineReadinessBootstrap() {
|
|
|
397
399
|
return null;
|
|
398
400
|
}
|
|
399
401
|
|
|
402
|
+
const ThemeFamilySync = React.lazy(() => import("./ThemeFamilySync.js"));
|
|
403
|
+
|
|
400
404
|
function ProvidersInner({
|
|
401
405
|
queryClient,
|
|
402
406
|
defaultTheme = "system",
|
|
@@ -507,6 +511,7 @@ export function AppProviders({
|
|
|
507
511
|
i18n,
|
|
508
512
|
documentTitleFallback,
|
|
509
513
|
ownsDocument = true,
|
|
514
|
+
enableThemeFamily = true,
|
|
510
515
|
children,
|
|
511
516
|
}: AppProvidersProps) {
|
|
512
517
|
const fallback = clientOnlyFallback ?? (
|
|
@@ -543,6 +548,11 @@ export function AppProviders({
|
|
|
543
548
|
children
|
|
544
549
|
) : (
|
|
545
550
|
<>
|
|
551
|
+
{ownsDocument && enableThemeFamily ? (
|
|
552
|
+
<React.Suspense fallback={null}>
|
|
553
|
+
<ThemeFamilySync />
|
|
554
|
+
</React.Suspense>
|
|
555
|
+
) : null}
|
|
546
556
|
<AgentEngineReadinessBootstrap />
|
|
547
557
|
<FirstRunOnboardingStartupGate
|
|
548
558
|
suppressSurface={skipFirstRunOnboarding}
|
|
@@ -25,6 +25,9 @@ vi.mock("../VoiceTranscriptionSection.js", () => ({
|
|
|
25
25
|
<div data-testid="voice" data-compact={String(!!compact)} />
|
|
26
26
|
),
|
|
27
27
|
}));
|
|
28
|
+
vi.mock("./ThemeFamilySettings.js", () => ({
|
|
29
|
+
ThemeFamilySettings: () => <div data-testid="theme-family-settings" />,
|
|
30
|
+
}));
|
|
28
31
|
|
|
29
32
|
import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
|
|
30
33
|
|
|
@@ -70,12 +73,14 @@ describe("PreferencesSettings", () => {
|
|
|
70
73
|
const groups = [...container.querySelectorAll("section")];
|
|
71
74
|
expect(groups.map((group) => group.id)).toEqual([
|
|
72
75
|
"language-region",
|
|
76
|
+
"appearance",
|
|
73
77
|
"voice-input",
|
|
74
78
|
]);
|
|
75
79
|
expect(groups[0]!.querySelector("h2")?.textContent).toBe(
|
|
76
80
|
"Language and region",
|
|
77
81
|
);
|
|
78
|
-
expect(groups[1]!.querySelector("h2")?.textContent).toBe("
|
|
82
|
+
expect(groups[1]!.querySelector("h2")?.textContent).toBe("Appearance");
|
|
83
|
+
expect(groups[2]!.querySelector("h2")?.textContent).toBe("Voice input");
|
|
79
84
|
|
|
80
85
|
const language = container.querySelector("#interface-language");
|
|
81
86
|
expect(language?.textContent).toContain("Interface language");
|
|
@@ -90,8 +95,14 @@ describe("PreferencesSettings", () => {
|
|
|
90
95
|
"Used for timestamps and scheduled automations.",
|
|
91
96
|
);
|
|
92
97
|
|
|
98
|
+
const themeFamily = container.querySelector("#theme-family");
|
|
99
|
+
expect(themeFamily?.textContent).toContain("Theme family");
|
|
100
|
+
expect(
|
|
101
|
+
themeFamily?.querySelector('[data-testid="theme-family-settings"]'),
|
|
102
|
+
).not.toBeNull();
|
|
103
|
+
|
|
93
104
|
expect(
|
|
94
|
-
groups[
|
|
105
|
+
groups[2]!
|
|
95
106
|
.querySelector('[data-testid="voice"]')
|
|
96
107
|
?.getAttribute("data-compact"),
|
|
97
108
|
).toBe("true");
|
|
@@ -4,6 +4,7 @@ import { LanguagePicker } from "../../shared/index.js";
|
|
|
4
4
|
import { SchedulingTimezoneField } from "../SchedulingTimezoneField.js";
|
|
5
5
|
import { SettingsGroup, SettingsRow } from "../SettingsRow.js";
|
|
6
6
|
import { VoiceTranscriptionSection } from "../VoiceTranscriptionSection.js";
|
|
7
|
+
import { ThemeFamilySettings } from "./ThemeFamilySettings.js";
|
|
7
8
|
|
|
8
9
|
const key = (name: string) => `agentChat.settingsShell.account.${name}`;
|
|
9
10
|
|
|
@@ -37,6 +38,13 @@ export function PreferencesSettings() {
|
|
|
37
38
|
control={<SchedulingTimezoneField compact />}
|
|
38
39
|
/>
|
|
39
40
|
</SettingsGroup>
|
|
41
|
+
<SettingsGroup id="appearance" title={t(key("appearance"))}>
|
|
42
|
+
<SettingsRow
|
|
43
|
+
id="theme-family"
|
|
44
|
+
label={t(key("themeFamily"))}
|
|
45
|
+
control={<ThemeFamilySettings />}
|
|
46
|
+
/>
|
|
47
|
+
</SettingsGroup>
|
|
40
48
|
<SettingsGroup id="voice-input" title={t(key("voiceInput"))}>
|
|
41
49
|
<VoiceTranscriptionSection compact />
|
|
42
50
|
</SettingsGroup>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { useThemeFamily, type ThemeFamily } from "@agent-native/core/client/ui";
|
|
3
|
+
import {
|
|
4
|
+
Select,
|
|
5
|
+
SelectContent,
|
|
6
|
+
SelectItem,
|
|
7
|
+
SelectTrigger,
|
|
8
|
+
SelectValue,
|
|
9
|
+
} from "@agent-native/toolkit/ui/select";
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
|
|
12
|
+
const key = (name: string) => `agentChat.settingsShell.account.${name}`;
|
|
13
|
+
|
|
14
|
+
export function ThemeFamilySettings() {
|
|
15
|
+
const t = useT();
|
|
16
|
+
const { family, status, setFamily } = useThemeFamily();
|
|
17
|
+
const [saving, setSaving] = useState(false);
|
|
18
|
+
const [saveFailed, setSaveFailed] = useState(false);
|
|
19
|
+
|
|
20
|
+
async function changeFamily(value: string) {
|
|
21
|
+
if (value !== "agent-native" && value !== "original") return;
|
|
22
|
+
if (value === family) return;
|
|
23
|
+
|
|
24
|
+
setSaving(true);
|
|
25
|
+
setSaveFailed(false);
|
|
26
|
+
try {
|
|
27
|
+
await setFamily(value as ThemeFamily);
|
|
28
|
+
} catch {
|
|
29
|
+
setSaveFailed(true);
|
|
30
|
+
} finally {
|
|
31
|
+
setSaving(false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="w-full sm:w-56">
|
|
37
|
+
<Select
|
|
38
|
+
value={family}
|
|
39
|
+
onValueChange={(value) => void changeFamily(value)}
|
|
40
|
+
disabled={saving || status === "loading"}
|
|
41
|
+
>
|
|
42
|
+
<SelectTrigger aria-label={t(key("themeFamily"))}>
|
|
43
|
+
<SelectValue />
|
|
44
|
+
</SelectTrigger>
|
|
45
|
+
<SelectContent>
|
|
46
|
+
<SelectItem value="agent-native">
|
|
47
|
+
{t(key("themeFamilyAgentNative"))}
|
|
48
|
+
</SelectItem>
|
|
49
|
+
<SelectItem value="original">
|
|
50
|
+
{t(key("themeFamilyOriginal"))}
|
|
51
|
+
</SelectItem>
|
|
52
|
+
</SelectContent>
|
|
53
|
+
</Select>
|
|
54
|
+
{saveFailed || status === "error" ? (
|
|
55
|
+
<p className="mt-2 text-xs text-destructive" role="alert">
|
|
56
|
+
{t(key("themeFamilySaveError"))}
|
|
57
|
+
</p>
|
|
58
|
+
) : null}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -33,6 +33,12 @@ export const PREFERENCES_SEARCH_ENTRIES: readonly SettingsPageSearchEntry[] = [
|
|
|
33
33
|
keywords: "language locale translation",
|
|
34
34
|
anchor: "interface-language",
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
id: "theme-family",
|
|
38
|
+
labelKey: key("themeFamily"),
|
|
39
|
+
keywords: "theme appearance style colors",
|
|
40
|
+
anchor: "theme-family",
|
|
41
|
+
},
|
|
36
42
|
{
|
|
37
43
|
id: "timezone",
|
|
38
44
|
labelKey: key("timezone"),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { DefaultSpinner } from "./DefaultSpinner.js";
|
|
5
|
+
|
|
6
|
+
describe("DefaultSpinner theme styles", () => {
|
|
7
|
+
it("uses the selected dark theme instead of the operating system preference", () => {
|
|
8
|
+
const html = renderToStaticMarkup(<DefaultSpinner />);
|
|
9
|
+
|
|
10
|
+
expect(html).toContain("html.dark");
|
|
11
|
+
expect(html).toContain('html[data-theme="dark"]');
|
|
12
|
+
expect(html).not.toContain("prefers-color-scheme");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -117,11 +117,10 @@ export function DefaultSpinner({
|
|
|
117
117
|
background: hsl(var(--background, 0 0% 100%));
|
|
118
118
|
color: hsl(var(--foreground, 240 10% 3.9%));
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
120
|
+
html.dark,
|
|
121
|
+
html[data-theme="dark"]:not(.light) {
|
|
122
|
+
background: hsl(var(--background, 240 10% 3.9%));
|
|
123
|
+
color: hsl(var(--foreground, 0 0% 98%));
|
|
125
124
|
}
|
|
126
125
|
`}</style>
|
|
127
126
|
</div>
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
isExpectedRouteNotFound,
|
|
5
|
+
resolveErrorBoundaryThemeClass,
|
|
6
|
+
} from "./ErrorBoundary.js";
|
|
7
|
+
|
|
8
|
+
function themeRoot(className: string, dataTheme: string | null) {
|
|
9
|
+
return {
|
|
10
|
+
classList: {
|
|
11
|
+
contains: (name: string) => className.split(" ").includes(name),
|
|
12
|
+
},
|
|
13
|
+
getAttribute: () => dataTheme,
|
|
14
|
+
} as unknown as Pick<HTMLElement, "classList" | "getAttribute">;
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
describe("isExpectedRouteNotFound", () => {
|
|
6
18
|
it("recognizes React Router 404 responses", () => {
|
|
@@ -26,3 +38,34 @@ describe("isExpectedRouteNotFound", () => {
|
|
|
26
38
|
).toBe(false);
|
|
27
39
|
});
|
|
28
40
|
});
|
|
41
|
+
|
|
42
|
+
describe("resolveErrorBoundaryThemeClass", () => {
|
|
43
|
+
it("honors an explicit data theme before storage or the system preference", () => {
|
|
44
|
+
const readStoredTheme = vi.fn(() => "dark");
|
|
45
|
+
const readSystemTheme = vi.fn(() => true);
|
|
46
|
+
|
|
47
|
+
expect(
|
|
48
|
+
resolveErrorBoundaryThemeClass(
|
|
49
|
+
themeRoot("", "light"),
|
|
50
|
+
readStoredTheme,
|
|
51
|
+
readSystemTheme,
|
|
52
|
+
),
|
|
53
|
+
).toBe("light");
|
|
54
|
+
expect(readStoredTheme).not.toHaveBeenCalled();
|
|
55
|
+
expect(readSystemTheme).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("uses the system theme only when no explicit theme is selected", () => {
|
|
59
|
+
const readStoredTheme = vi.fn(() => null);
|
|
60
|
+
const readSystemTheme = vi.fn(() => true);
|
|
61
|
+
|
|
62
|
+
expect(
|
|
63
|
+
resolveErrorBoundaryThemeClass(
|
|
64
|
+
themeRoot("", null),
|
|
65
|
+
readStoredTheme,
|
|
66
|
+
readSystemTheme,
|
|
67
|
+
),
|
|
68
|
+
).toBe("dark");
|
|
69
|
+
expect(readSystemTheme).toHaveBeenCalledOnce();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -196,21 +196,46 @@ function useErrorCopy() {
|
|
|
196
196
|
function useApplyThemeClass() {
|
|
197
197
|
useEffect(() => {
|
|
198
198
|
const root = document.documentElement;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
199
|
+
let storageUnreadable = false;
|
|
200
|
+
const theme = resolveErrorBoundaryThemeClass(
|
|
201
|
+
root,
|
|
202
|
+
() => {
|
|
203
|
+
try {
|
|
204
|
+
return localStorage.getItem("theme");
|
|
205
|
+
} catch {
|
|
206
|
+
storageUnreadable = true;
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
() =>
|
|
211
|
+
window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false,
|
|
212
|
+
);
|
|
213
|
+
if (storageUnreadable) {
|
|
214
|
+
root.setAttribute("data-theme-status", "unreadable");
|
|
215
|
+
} else {
|
|
216
|
+
root.removeAttribute("data-theme-status");
|
|
217
|
+
}
|
|
218
|
+
if (theme) root.classList.add(theme);
|
|
211
219
|
}, []);
|
|
212
220
|
}
|
|
213
221
|
|
|
222
|
+
export function resolveErrorBoundaryThemeClass(
|
|
223
|
+
root: Pick<HTMLElement, "classList" | "getAttribute">,
|
|
224
|
+
readStoredTheme: () => string | null,
|
|
225
|
+
readSystemTheme: () => boolean,
|
|
226
|
+
): "dark" | "light" | undefined {
|
|
227
|
+
if (root.classList.contains("dark") || root.classList.contains("light")) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const dataTheme = root.getAttribute("data-theme");
|
|
232
|
+
if (dataTheme === "dark" || dataTheme === "light") return dataTheme;
|
|
233
|
+
|
|
234
|
+
const storedTheme = readStoredTheme();
|
|
235
|
+
if (storedTheme === "dark" || storedTheme === "light") return storedTheme;
|
|
236
|
+
return readSystemTheme() ? "dark" : undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
214
239
|
function errorMessageOf(error: unknown): string {
|
|
215
240
|
if (error instanceof Error) return error.message;
|
|
216
241
|
return typeof error === "string" ? error : "";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { PoweredByBadge } from "./PoweredByBadge.js";
|
|
5
|
+
|
|
6
|
+
describe("PoweredByBadge theme styles", () => {
|
|
7
|
+
it("follows the selected theme classes and data attribute", () => {
|
|
8
|
+
const html = renderToStaticMarkup(<PoweredByBadge />);
|
|
9
|
+
|
|
10
|
+
expect(html).toContain("html.dark");
|
|
11
|
+
expect(html).toContain('html[data-theme="dark"]');
|
|
12
|
+
expect(html).toContain("html.light");
|
|
13
|
+
expect(html).toContain('html[data-theme="light"]');
|
|
14
|
+
expect(html).not.toContain("prefers-color-scheme");
|
|
15
|
+
});
|
|
16
|
+
});
|