@agent-native/toolkit 0.213.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.
- package/agent-native.eject.json +9 -0
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +10 -4
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +13 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +3 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/index.d.ts +1 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +1 -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 +3 -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 +2 -2
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +7 -2
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.d.ts +14 -2
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +22 -5
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +8 -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/widgets/DataChartRenderer.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.js +127 -30
- 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 +14 -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 +131 -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 +8 -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 +8 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +8 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +8 -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 +8 -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 +8 -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 +8 -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 +8 -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 +8 -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 +8 -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 +8 -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 +8 -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 +22 -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/components.spec.tsx +12 -0
- package/src/app/agentkit/react/components.tsx +41 -22
- package/src/app/agentkit/react/context.tsx +17 -0
- package/src/app/agentkit/react/index.ts +1 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +116 -1
- 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 +4 -0
- package/src/app/agentkit/react/styles.css +2 -2
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +19 -0
- package/src/app/chat/AgentKitAssistantChat.tsx +10 -1
- package/src/app/chat/AgentPanel.header.spec.ts +72 -14
- package/src/app/chat/AgentPanel.tsx +97 -53
- package/src/app/chat/chat/surface-types.ts +8 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +217 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.tsx +306 -68
- package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +329 -0
- package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
- package/src/app/chat/chat/widgets/DataChartWidget.tsx +48 -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 +301 -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 +9 -0
- package/src/app/i18n/catalogs/de-DE.ts +9 -0
- package/src/app/i18n/catalogs/en-US.ts +9 -0
- package/src/app/i18n/catalogs/es-ES.ts +10 -0
- package/src/app/i18n/catalogs/fr-FR.ts +10 -0
- package/src/app/i18n/catalogs/hi-IN.ts +9 -0
- package/src/app/i18n/catalogs/ja-JP.ts +9 -0
- package/src/app/i18n/catalogs/ko-KR.ts +9 -0
- package/src/app/i18n/catalogs/pt-BR.ts +9 -0
- package/src/app/i18n/catalogs/zh-CN.ts +8 -0
- package/src/app/i18n/catalogs/zh-TW.ts +9 -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 +74 -0
- package/src/composer/ComposerContextBanner.tsx +72 -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
|
@@ -1491,6 +1491,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1491
1491
|
"tool.worked": "काम किया",
|
|
1492
1492
|
"tool.workedFor": "{{duration}} तक काम किया",
|
|
1493
1493
|
"widget.chart": "चार्ट",
|
|
1494
|
+
"widget.chartActions": "चार्ट कार्रवाइयाँ",
|
|
1495
|
+
"widget.dataSources": "डेटा स्रोत",
|
|
1496
|
+
"widget.tableActions": "टेबल कार्रवाइयाँ",
|
|
1494
1497
|
"widget.dataChart": "डेटा चार्ट",
|
|
1495
1498
|
"widget.dataInsights": "डेटा इनसाइट्स",
|
|
1496
1499
|
"widget.dataTable": "डेटा तालिका",
|
|
@@ -2063,6 +2066,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2063
2066
|
"इस बदलाव की पुष्टि करने के निर्देशों के लिए अपना ईमेल देखें।",
|
|
2064
2067
|
"settingsShell.account.languageAndRegion": "भाषा और क्षेत्र",
|
|
2065
2068
|
"settingsShell.account.languageDescription": "आपके सभी डिवाइस पर लागू होता है।",
|
|
2069
|
+
"settingsShell.account.appearance": "रूप-रंग",
|
|
2070
|
+
"settingsShell.account.themeFamily": "थीम शैली",
|
|
2071
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2072
|
+
"settingsShell.account.themeFamilyOriginal": "ऐप के मूल रंग",
|
|
2073
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2074
|
+
"थीम शैली अपडेट नहीं की जा सकी। फिर कोशिश करें।",
|
|
2066
2075
|
"settingsShell.account.manage": "प्रबंधित करें",
|
|
2067
2076
|
"settingsShell.account.name": "नाम",
|
|
2068
2077
|
"settingsShell.account.nameDescription":
|
|
@@ -1536,6 +1536,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1536
1536
|
"tool.worked": "作業しました",
|
|
1537
1537
|
"tool.workedFor": "{{duration}} 作業しました",
|
|
1538
1538
|
"widget.chart": "チャート",
|
|
1539
|
+
"widget.chartActions": "チャート操作",
|
|
1540
|
+
"widget.dataSources": "データソース",
|
|
1541
|
+
"widget.tableActions": "テーブル操作",
|
|
1539
1542
|
"widget.dataChart": "データチャート",
|
|
1540
1543
|
"widget.dataInsights": "データインサイト",
|
|
1541
1544
|
"widget.dataTable": "データテーブル",
|
|
@@ -2093,6 +2096,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2093
2096
|
"settingsShell.account.languageAndRegion": "言語と地域",
|
|
2094
2097
|
"settingsShell.account.languageDescription":
|
|
2095
2098
|
"すべてのデバイスに適用されます。",
|
|
2099
|
+
"settingsShell.account.appearance": "外観",
|
|
2100
|
+
"settingsShell.account.themeFamily": "テーマ",
|
|
2101
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2102
|
+
"settingsShell.account.themeFamilyOriginal": "アプリのオリジナルカラー",
|
|
2103
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2104
|
+
"テーマを更新できませんでした。もう一度お試しください。",
|
|
2096
2105
|
"settingsShell.account.manage": "管理",
|
|
2097
2106
|
"settingsShell.account.name": "名前",
|
|
2098
2107
|
"settingsShell.account.nameDescription":
|
|
@@ -1510,6 +1510,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1510
1510
|
"tool.worked": "작업함",
|
|
1511
1511
|
"tool.workedFor": "{{duration}} 동안 작업함",
|
|
1512
1512
|
"widget.chart": "차트",
|
|
1513
|
+
"widget.chartActions": "차트 작업",
|
|
1514
|
+
"widget.dataSources": "데이터 소스",
|
|
1515
|
+
"widget.tableActions": "표 작업",
|
|
1513
1516
|
"widget.dataChart": "데이터 차트",
|
|
1514
1517
|
"widget.dataInsights": "데이터 인사이트",
|
|
1515
1518
|
"widget.dataTable": "데이터 테이블",
|
|
@@ -2057,6 +2060,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2057
2060
|
"이메일을 확인하고 안내에 따라 이 변경을 확정하세요.",
|
|
2058
2061
|
"settingsShell.account.languageAndRegion": "언어 및 지역",
|
|
2059
2062
|
"settingsShell.account.languageDescription": "모든 기기에 적용됩니다.",
|
|
2063
|
+
"settingsShell.account.appearance": "모양",
|
|
2064
|
+
"settingsShell.account.themeFamily": "테마 스타일",
|
|
2065
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2066
|
+
"settingsShell.account.themeFamilyOriginal": "앱의 원래 색상",
|
|
2067
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2068
|
+
"테마 스타일을 업데이트하지 못했습니다. 다시 시도하세요.",
|
|
2060
2069
|
"settingsShell.account.manage": "관리",
|
|
2061
2070
|
"settingsShell.account.name": "이름",
|
|
2062
2071
|
"settingsShell.account.nameDescription":
|
|
@@ -1055,6 +1055,9 @@ 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.dataSources": "Fontes de dados",
|
|
1060
|
+
"widget.tableActions": "Ações da tabela",
|
|
1058
1061
|
"widget.dataChart": "Gráfico de dados",
|
|
1059
1062
|
"widget.dataInsights": "Insights de dados",
|
|
1060
1063
|
"widget.dataTable": "Tabela de dados",
|
|
@@ -2220,6 +2223,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
2220
2223
|
"settingsShell.account.languageAndRegion": "Idioma e região",
|
|
2221
2224
|
"settingsShell.account.languageDescription":
|
|
2222
2225
|
"Vale para todos os seus dispositivos.",
|
|
2226
|
+
"settingsShell.account.appearance": "Aparência",
|
|
2227
|
+
"settingsShell.account.themeFamily": "Família do tema",
|
|
2228
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
2229
|
+
"settingsShell.account.themeFamilyOriginal": "Cores originais do app",
|
|
2230
|
+
"settingsShell.account.themeFamilySaveError":
|
|
2231
|
+
"Não foi possível atualizar o tema. Tente novamente.",
|
|
2223
2232
|
"settingsShell.account.manage": "Gerenciar",
|
|
2224
2233
|
"settingsShell.account.name": "Nome",
|
|
2225
2234
|
"settingsShell.account.nameDescription":
|
|
@@ -1419,6 +1419,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1419
1419
|
"tool.worked": "已处理",
|
|
1420
1420
|
"tool.workedFor": "处理了 {{duration}}",
|
|
1421
1421
|
"widget.chart": "图表",
|
|
1422
|
+
"widget.chartActions": "图表操作",
|
|
1423
|
+
"widget.dataSources": "数据来源",
|
|
1424
|
+
"widget.tableActions": "表格操作",
|
|
1422
1425
|
"widget.dataChart": "数据图表",
|
|
1423
1426
|
"widget.dataInsights": "数据洞察",
|
|
1424
1427
|
"widget.dataTable": "数据表",
|
|
@@ -1940,6 +1943,11 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1940
1943
|
"settingsShell.account.emailChangeSent": "请查看您的邮箱,按说明确认此更改。",
|
|
1941
1944
|
"settingsShell.account.languageAndRegion": "语言和地区",
|
|
1942
1945
|
"settingsShell.account.languageDescription": "适用于您的所有设备。",
|
|
1946
|
+
"settingsShell.account.appearance": "外观",
|
|
1947
|
+
"settingsShell.account.themeFamily": "主题样式",
|
|
1948
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
1949
|
+
"settingsShell.account.themeFamilyOriginal": "原应用配色",
|
|
1950
|
+
"settingsShell.account.themeFamilySaveError": "无法更新主题样式。请重试。",
|
|
1943
1951
|
"settingsShell.account.manage": "管理",
|
|
1944
1952
|
"settingsShell.account.name": "姓名",
|
|
1945
1953
|
"settingsShell.account.nameDescription":
|
|
@@ -1424,6 +1424,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1424
1424
|
"tool.worked": "已處理",
|
|
1425
1425
|
"tool.workedFor": "處理了 {{duration}}",
|
|
1426
1426
|
"widget.chart": "圖表",
|
|
1427
|
+
"widget.chartActions": "圖表操作",
|
|
1428
|
+
"widget.dataSources": "資料來源",
|
|
1429
|
+
"widget.tableActions": "表格操作",
|
|
1427
1430
|
"widget.dataChart": "資料圖表",
|
|
1428
1431
|
"widget.dataInsights": "資料洞察",
|
|
1429
1432
|
"widget.dataTable": "資料表",
|
|
@@ -1950,6 +1953,12 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1950
1953
|
"請查看您的電子郵件,依照說明確認此變更。",
|
|
1951
1954
|
"settingsShell.account.languageAndRegion": "語言和地區",
|
|
1952
1955
|
"settingsShell.account.languageDescription": "套用至您的所有裝置。",
|
|
1956
|
+
"settingsShell.account.appearance": "外觀",
|
|
1957
|
+
"settingsShell.account.themeFamily": "主題樣式",
|
|
1958
|
+
"settingsShell.account.themeFamilyAgentNative": "Agent-Native",
|
|
1959
|
+
"settingsShell.account.themeFamilyOriginal": "原始應用程式配色",
|
|
1960
|
+
"settingsShell.account.themeFamilySaveError":
|
|
1961
|
+
"無法更新主題樣式,請再試一次。",
|
|
1953
1962
|
"settingsShell.account.manage": "管理",
|
|
1954
1963
|
"settingsShell.account.name": "姓名",
|
|
1955
1964
|
"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
|
+
});
|