@nextclaw/ui 0.14.4 → 0.15.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/CHANGELOG.md +54 -0
- package/dist/assets/api-DLtsiBKm.js +13 -0
- package/dist/assets/appearance-settings-page-CtkeYXux.js +1 -0
- package/dist/assets/book-open-BQwWBDKb.js +1 -0
- package/dist/assets/channels-list-page-Cv8o9Hxg.js +8 -0
- package/dist/assets/chat-page-BtA7H1Ea.js +1 -0
- package/dist/assets/{config-split-page-CRhlZ_pH.js → config-split-page-EidRBpP2.js} +1 -1
- package/dist/assets/confirm-dialog-iIJFFp2T.js +5 -0
- package/dist/assets/desktop-update-config-C6n_TB1r.js +1 -0
- package/dist/assets/{dist-CikkhOlq.js → dist-CBxWOazz.js} +1 -1
- package/dist/assets/{dist-PVA_xL1D.js → dist-D2eZC3OR.js} +1 -1
- package/dist/assets/doc-browser-B76XOXmW.js +1 -0
- package/dist/assets/doc-browser-DBnj9UMB.js +1 -0
- package/dist/assets/{doc-browser-context-MTw4EvKd.js → doc-browser-context-Sv-044Cl.js} +1 -1
- package/dist/assets/{ellipsis-y6vDtq4M.js → ellipsis-2uzhypw5.js} +1 -1
- package/dist/assets/es2015-DhQo_oS6.js +41 -0
- package/dist/assets/{external-link-DVJIQf_G.js → external-link-CM5dnUI6.js} +1 -1
- package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
- package/dist/assets/i18n-provider-di1waTGX.js +1 -0
- package/dist/assets/index-CBRP-7-a.css +1 -0
- package/dist/assets/index-Z1fYpbNd.js +104 -0
- package/dist/assets/loader-circle-BjmN5DeJ.js +1 -0
- package/dist/assets/mcp-marketplace-page-C_MVQUbT.js +9 -0
- package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +1 -0
- package/dist/assets/model-config-page-Cdr9P6TQ.js +1 -0
- package/dist/assets/plus-3ztRZD_l.js +1 -0
- package/dist/assets/provider-scoped-model-input-g1WkGiJ8.js +1 -0
- package/dist/assets/providers-config-page-CWlO7J4A.js +1 -0
- package/dist/assets/{react-VNqT5Zp7.js → react-DjpzoZXB.js} +1 -1
- package/dist/assets/refresh-cw-DaTv3zxE.js +1 -0
- package/dist/assets/remote-CKWZfVVB.js +1 -0
- package/dist/assets/rotate-cw-CHcaRUDz.js +1 -0
- package/dist/assets/runtime-config-page-CfTqh1nv.js +1 -0
- package/dist/assets/{save-BOJ477H-.js → save-BZof1uie.js} +1 -1
- package/dist/assets/{search-BEti5beA.js → search-MxAarS_g.js} +1 -1
- package/dist/assets/search-config-page-Dm8daYny.js +1 -0
- package/dist/assets/{secrets-config-page--IzrIOid.js → secrets-config-page-D8AbcR05.js} +2 -2
- package/dist/assets/security-config-Ds0LsJmo.js +1 -0
- package/dist/assets/select-D7GcNP0z.js +1 -0
- package/dist/assets/setting-row-CuozlnJd.js +1 -0
- package/dist/assets/side-dock-BcLau5S_.js +3 -0
- package/dist/assets/skeleton-shfmXuYp.js +1 -0
- package/dist/assets/switch-C1BWMU5c.js +1 -0
- package/dist/assets/tag-chip-DCUrQUCP.js +1 -0
- package/dist/assets/tooltip-DfAHHA76.js +1 -0
- package/dist/assets/use-config-DAdbT5QJ.js +1 -0
- package/dist/assets/use-confirm-dialog-eWcJ6V-s.js +1 -0
- package/dist/assets/x-DqL1mYLC.js +1 -0
- package/dist/index.html +53 -17
- package/index.html +25 -2
- package/package.json +9 -9
- package/src/app/components/layout/__tests__/app-layout.test.tsx +27 -0
- package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +9 -1
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +6 -3
- package/src/app/components/layout/app-layout.tsx +10 -3
- package/src/app/components/layout/sidebar-items.tsx +8 -8
- package/src/app/components/layout/sidebar-rail.styles.ts +4 -2
- package/src/app/components/layout/sidebar.tsx +21 -28
- package/src/app/configs/app-navigation.config.ts +11 -5
- package/src/app/index.tsx +8 -0
- package/src/app/managers/__tests__/viewport-layout.manager.test.ts +59 -0
- package/src/app/managers/viewport-layout.manager.ts +39 -0
- package/src/app/presenters/app.presenter.ts +25 -2
- package/src/app/styles/design-system.css +91 -4
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +86 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +47 -17
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +7 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +9 -5
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +5 -5
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +51 -2
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +170 -1
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +23 -1
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +24 -7
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +96 -0
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +181 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +351 -89
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +29 -4
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +28 -3
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -8
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +50 -14
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +77 -6
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +21 -12
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +166 -0
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +19 -11
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +38 -64
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +59 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +56 -1
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -2
- package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +80 -0
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +12 -3
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +60 -0
- package/src/features/chat/features/message/utils/chat-message.utils.ts +3 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +39 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +3 -1
- package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +270 -115
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +121 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +75 -20
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +70 -5
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +83 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +79 -1
- package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +2 -0
- package/src/features/chat/managers/chat-thread.manager.ts +9 -3
- package/src/features/chat/managers/chat-ui.manager.ts +5 -1
- package/src/features/chat/presenters/chat.presenter.ts +2 -1
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
- package/src/features/chat/stores/chat-thread.store.ts +6 -64
- package/src/features/chat/types/chat-message.types.ts +4 -0
- package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +33 -26
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
- package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +1 -0
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +115 -0
- package/src/features/marketplace/components/marketplace-detail-doc.tsx +441 -0
- package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +3 -3
- package/src/features/marketplace/components/mcp/mcp-marketplace-doc.ts +0 -60
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +64 -29
- package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +42 -31
- package/src/features/marketplace/index.ts +3 -0
- package/src/features/marketplace/stores/marketplace-detail-doc.store.ts +38 -0
- package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +32 -2
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -0
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
- package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -0
- package/src/features/settings/pages/appearance-settings-page.tsx +40 -0
- package/src/features/side-dock/components/__tests__/side-dock.test.tsx +44 -2
- package/src/features/side-dock/components/side-dock.tsx +100 -13
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +10 -0
- package/src/features/side-dock/managers/__tests__/side-dock.manager.test.ts +32 -2
- package/src/features/side-dock/managers/side-dock.manager.ts +17 -7
- package/src/features/side-dock/stores/side-dock.store.ts +9 -0
- package/src/features/side-dock/types/side-dock.types.ts +10 -3
- package/src/features/side-dock/utils/side-dock-item.utils.ts +15 -2
- package/src/index.css +501 -27
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +30 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +206 -38
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +34 -11
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +23 -5
- package/src/shared/components/doc-browser/doc-browser.tsx +128 -75
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +9 -1
- package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +83 -0
- package/src/shared/components/ui/__tests__/dialog.test.tsx +27 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +14 -2
- package/src/shared/components/ui/dialog.tsx +2 -2
- package/src/shared/components/ui/popover.tsx +1 -1
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +4 -3
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +54 -0
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +4 -5
- package/src/shared/components/ui/tooltip.tsx +1 -1
- package/src/shared/lib/api/utils/server-path.utils.ts +27 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +15 -6
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/en-US/marketplace.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -6
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +5 -0
- package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +14 -0
- package/src/shared/lib/session-project/index.ts +1 -1
- package/src/shared/lib/session-project/{workspace-file-breadcrumb.ts → workspace-file-breadcrumb.utils.ts} +62 -8
- package/src/shared/lib/theme/index.ts +51 -13
- package/dist/assets/api-BP_8IaxB.js +0 -13
- package/dist/assets/channels-list-page-DEh5Jjqx.js +0 -8
- package/dist/assets/chat-page-BRB0N5WW.js +0 -105
- package/dist/assets/confirm-dialog-DsivFJ4r.js +0 -5
- package/dist/assets/desktop-update-config-B0w9p2ep.js +0 -1
- package/dist/assets/doc-browser-BoNJ7-tE.js +0 -1
- package/dist/assets/doc-browser-CdJ_d4ik.js +0 -1
- package/dist/assets/index-BiE-K8-q.css +0 -1
- package/dist/assets/index-D9BAE_L_.js +0 -105
- package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +0 -40
- package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +0 -1
- package/dist/assets/model-config-page-DotaTUHb.js +0 -1
- package/dist/assets/plus-BbZqquJe.js +0 -1
- package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +0 -1
- package/dist/assets/providers-config-page-DW4tQSLN.js +0 -1
- package/dist/assets/remote-B8TiUfwW.js +0 -1
- package/dist/assets/rotate-cw-DjHDTkCv.js +0 -1
- package/dist/assets/runtime-config-page-S6ErRHR8.js +0 -1
- package/dist/assets/search-config-page-DGj5am03.js +0 -1
- package/dist/assets/select-hoj8dK4K.js +0 -41
- package/dist/assets/tag-chip-T8-h-die.js +0 -1
- package/dist/assets/use-config-CjRV2hwL.js +0 -1
- package/dist/assets/x-D9PXs_xP.js +0 -1
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.ts +0 -40
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +0 -201
- package/src/features/marketplace/components/marketplace-detail-doc.ts +0 -142
- /package/dist/assets/{config-hints-BkYBc7z0.js → config-hints-fGnUjDe9.js} +0 -0
- /package/dist/assets/{middleware-CkFT3R4v.js → middleware-BILWbJE4.js} +0 -0
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { API_BASE } from '@/shared/lib/api/api-base';
|
|
2
|
+
import { nextclawClient } from '@/shared/lib/api/managers/client.manager';
|
|
2
3
|
import type { ServerPathBrowseView, ServerPathReadView } from '@/shared/lib/api/types';
|
|
3
4
|
|
|
5
|
+
const SERVER_PATH_CONTENT_BASE_PATH = '/api/server-paths/content';
|
|
6
|
+
const SERVER_PATH_CONTENT_ABSOLUTE_SCOPE = '__abs__';
|
|
7
|
+
const SERVER_PATH_CONTENT_WINDOWS_SCOPE = '__win__';
|
|
8
|
+
|
|
9
|
+
function encodeServerPathSegments(path: string): string {
|
|
10
|
+
return path
|
|
11
|
+
.replace(/\\/g, '/')
|
|
12
|
+
.split('/')
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.map((segment) => encodeURIComponent(segment))
|
|
15
|
+
.join('/');
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
export async function fetchServerPathBrowse(params?: {
|
|
5
19
|
path?: string | null;
|
|
6
20
|
includeFiles?: boolean;
|
|
@@ -14,3 +28,15 @@ export async function fetchServerPathRead(params: {
|
|
|
14
28
|
}): Promise<ServerPathReadView> {
|
|
15
29
|
return await nextclawClient.serverPaths.read(params);
|
|
16
30
|
}
|
|
31
|
+
|
|
32
|
+
export function buildServerPathContentUrl(resolvedPath: string): string {
|
|
33
|
+
const normalizedPath = resolvedPath.trim();
|
|
34
|
+
if (/^[a-z]:[\\/]/i.test(normalizedPath)) {
|
|
35
|
+
const [drive, ...segments] = normalizedPath.replace(/\\/g, '/').split('/');
|
|
36
|
+
return `${API_BASE}${SERVER_PATH_CONTENT_BASE_PATH}/${SERVER_PATH_CONTENT_WINDOWS_SCOPE}/${encodeURIComponent(drive)}/${segments
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.map((segment) => encodeURIComponent(segment))
|
|
39
|
+
.join('/')}`;
|
|
40
|
+
}
|
|
41
|
+
return `${API_BASE}${SERVER_PATH_CONTENT_BASE_PATH}/${SERVER_PATH_CONTENT_ABSOLUTE_SCOPE}/${encodeServerPathSegments(normalizedPath)}`;
|
|
42
|
+
}
|
|
@@ -50,30 +50,38 @@
|
|
|
50
50
|
"chatWorkspacePreviewUnsupported": "This file is not supported in the sidebar preview yet.",
|
|
51
51
|
"chatWorkspacePreviewEmpty": "No file content is available for preview.",
|
|
52
52
|
"chatWorkspaceDiffEmpty": "No diff content is available.",
|
|
53
|
+
"chatWorkspaceHtmlPreviewTitle": "Rendered HTML preview",
|
|
53
54
|
"chatWorkspacePreviewTruncated": "Preview truncated",
|
|
54
55
|
"chatWorkspaceSessionCronJobs": "Session cron jobs",
|
|
55
56
|
"chatWorkspaceCronJobEmpty": "This session has no cron jobs.",
|
|
56
57
|
"chatSessionUnread": "Session has unread updates",
|
|
57
58
|
"chatTyping": "Agent is thinking...",
|
|
59
|
+
"chatScrollToBottom": "Scroll to bottom",
|
|
60
|
+
"chatProcessSummaryProcessed": "Processed",
|
|
58
61
|
"chatRuntimeInitializing": "Chat is still initializing. You can keep typing and send once startup finishes.",
|
|
59
62
|
"chatRuntimeInitializationFailed": "Chat startup failed. Please retry in a moment or inspect the service logs.",
|
|
60
|
-
"chatInputPlaceholder": "Type a message, / for skills, @ for panel
|
|
63
|
+
"chatInputPlaceholder": "Type a message, / for commands, apps or skills, @ for panel app references, Enter to send, Shift + Enter for newline",
|
|
61
64
|
"chatInputPlaceholderCompact": "Message NextClaw...",
|
|
62
65
|
"chatInputHint": "Multi-turn context is preserved in the current session.",
|
|
63
66
|
"chatSlashSection": "Slash menu",
|
|
64
67
|
"chatSlashSectionCommands": "Commands",
|
|
65
68
|
"chatSlashSectionSkills": "Skills",
|
|
69
|
+
"chatSlashFilterAll": "All",
|
|
70
|
+
"chatSlashFilterCommands": "Commands",
|
|
71
|
+
"chatSlashFilterSkills": "Skills",
|
|
72
|
+
"chatSlashFilterPanelApps": "Panel Apps",
|
|
66
73
|
"chatSlashTypeCommand": "Command",
|
|
67
74
|
"chatSlashTypeSkill": "Skill",
|
|
68
75
|
"chatSlashSkillSpec": "Spec",
|
|
69
76
|
"chatSlashSkillScope": "Scope",
|
|
70
|
-
"chatSlashLoading": "Loading commands and skills…",
|
|
77
|
+
"chatSlashLoading": "Loading commands, panel apps and skills…",
|
|
71
78
|
"chatSlashNoResult": "No matches",
|
|
72
|
-
"chatSlashHint": "Type / to access commands
|
|
79
|
+
"chatSlashHint": "Type / to access commands, panel apps or skills",
|
|
73
80
|
"chatSlashCommandHint": "Press Enter to run this command.",
|
|
74
81
|
"chatSlashCommandSideChatTitle": "Side chat",
|
|
75
82
|
"chatSlashCommandSideChatDescription": "Open a draft side conversation that inherits this session context.",
|
|
76
83
|
"chatSlashCommandSideChatDetail": "Creates the child session only after your first message.",
|
|
84
|
+
"chatSlashPanelAppHint": "Press Enter to open this panel app on the right.",
|
|
77
85
|
"chatSlashSkillHint": "Press Enter to add this skill for the next turn.",
|
|
78
86
|
"chatPanelAppReferenceLoading": "Loading panel apps...",
|
|
79
87
|
"chatPanelAppReferenceSection": "Panel Apps",
|
|
@@ -118,9 +126,9 @@
|
|
|
118
126
|
"chatContextInheritanceMessages": "Inherited messages",
|
|
119
127
|
"chatSending": "Sending...",
|
|
120
128
|
"chatQueueSend": "Queue",
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
129
|
+
"chatQueuedBannerAttachmentFallback": "Attachment message",
|
|
130
|
+
"chatQueuedEdit": "Edit queued input",
|
|
131
|
+
"chatQueuedDelete": "Delete queued input",
|
|
124
132
|
"chatDeleteSession": "Delete Session",
|
|
125
133
|
"chatDeleteSessionConfirm": "Delete the current session?",
|
|
126
134
|
"chatSessionMoreActions": "More actions",
|
|
@@ -136,6 +144,7 @@
|
|
|
136
144
|
"chatSessionProjectUpdateHint": "The browser lists directories from the server machine. Changing the project directory only affects future messages and tool runs. History stays unchanged.",
|
|
137
145
|
"chatSessionProjectUpdated": "Project directory updated",
|
|
138
146
|
"chatSessionProjectCleared": "Project directory cleared",
|
|
147
|
+
"chatSessionErrorTitle": "Something went wrong",
|
|
139
148
|
"chatSendFailed": "Failed to send message",
|
|
140
149
|
"chatRoleUser": "You",
|
|
141
150
|
"chatRoleAssistant": "Assistant",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"marketplace": "Marketplace",
|
|
12
12
|
"advanced": "Advanced",
|
|
13
13
|
"settings": "Settings",
|
|
14
|
+
"appearance": "Appearance",
|
|
14
15
|
"settingsMenu": "Settings menu",
|
|
15
16
|
"sidebarCollapse": "Collapse sidebar",
|
|
16
17
|
"sidebarExpand": "Expand sidebar",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"themeCool": "Mist",
|
|
46
47
|
"themeDawn": "Dawn",
|
|
47
48
|
"themeGraphite": "Graphite",
|
|
49
|
+
"themeNight": "Night",
|
|
48
50
|
"themeProbe": "Probe",
|
|
49
51
|
"isRequired": "is required",
|
|
50
52
|
"duplicate": "duplicate",
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"docBrowserTitle": "Embedded Browser",
|
|
3
3
|
"docBrowserSearchPlaceholder": "Search, or enter a doc URL to open",
|
|
4
|
+
"docBrowserAddressLabel": "Address",
|
|
5
|
+
"docBrowserAddressPlaceholder": "Enter a URL, e.g. http://localhost:5173",
|
|
4
6
|
"docBrowserUrlPlaceholder": "Enter a doc path, e.g. /guide/channels",
|
|
5
7
|
"docBrowserOpenExternal": "Open in Docs",
|
|
8
|
+
"docBrowserOpenInBrowser": "Open in Browser",
|
|
9
|
+
"docBrowserRefresh": "Refresh",
|
|
6
10
|
"docBrowserFloatMode": "Float Window",
|
|
7
11
|
"docBrowserDockMode": "Dock to Sidebar",
|
|
8
12
|
"docBrowserClose": "Close",
|
|
@@ -17,6 +21,13 @@
|
|
|
17
21
|
"sideDockUnpinCurrent": "Remove from SideDock",
|
|
18
22
|
"sideDockBuiltInDocked": "Built-in shortcut",
|
|
19
23
|
"sideDockUnpinItem": "Remove shortcut",
|
|
24
|
+
"sideDockGitHubProject": "GitHub Project",
|
|
25
|
+
"sideDockHide": "Hide SideDock",
|
|
26
|
+
"sideDockHideConfirmTitle": "Hide SideDock?",
|
|
27
|
+
"sideDockHideConfirmDescription": "You can turn it back on from Settings > Appearance.",
|
|
28
|
+
"sideDockHideConfirmAction": "Hide",
|
|
29
|
+
"sideDockVisibilityTitle": "Show SideDock",
|
|
30
|
+
"sideDockVisibilityDescription": "Keep the right shortcut dock visible beside the workspace.",
|
|
20
31
|
"appsTitle": "Apps",
|
|
21
32
|
"panelAppsTitle": "Panel Apps",
|
|
22
33
|
"panelAppsLoading": "Loading panel apps...",
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
"marketplaceDetailPanelEmpty": "Click an item on the left to preview details here.",
|
|
59
59
|
"marketplaceDetailSummary": "Summary",
|
|
60
60
|
"marketplaceDetailDescription": "Description",
|
|
61
|
+
"marketplaceDetailMetadata": "Metadata",
|
|
62
|
+
"marketplaceDetailContent": "Content",
|
|
63
|
+
"marketplaceDetailSource": "Source",
|
|
64
|
+
"marketplaceDetailUnavailableTitle": "Detail unavailable",
|
|
65
|
+
"marketplaceDetailUnavailableDescription": "This right-side detail tab is missing content data for the current session. Open it again from the marketplace list.",
|
|
61
66
|
"marketplaceOpenInDocBrowserTab": "Open in New Tab",
|
|
62
67
|
"marketplaceMcpInstallDialogTitle": "Install MCP Server",
|
|
63
68
|
"marketplaceMcpServerName": "Server Name",
|
|
@@ -50,30 +50,38 @@
|
|
|
50
50
|
"chatWorkspacePreviewUnsupported": "该文件暂不支持在侧栏预览。",
|
|
51
51
|
"chatWorkspacePreviewEmpty": "当前没有可显示的文件内容。",
|
|
52
52
|
"chatWorkspaceDiffEmpty": "当前没有可显示的 diff 内容。",
|
|
53
|
+
"chatWorkspaceHtmlPreviewTitle": "HTML 渲染预览",
|
|
53
54
|
"chatWorkspacePreviewTruncated": "内容已截断",
|
|
54
55
|
"chatWorkspaceSessionCronJobs": "本会话定时任务",
|
|
55
56
|
"chatWorkspaceCronJobEmpty": "这个会话暂无定时任务。",
|
|
56
57
|
"chatSessionUnread": "会话有未读更新",
|
|
57
58
|
"chatTyping": "Agent 正在思考...",
|
|
59
|
+
"chatScrollToBottom": "滚动到底部",
|
|
60
|
+
"chatProcessSummaryProcessed": "已处理",
|
|
58
61
|
"chatRuntimeInitializing": "聊天能力正在初始化。你可以先输入内容,完成后即可发送。",
|
|
59
62
|
"chatRuntimeInitializationFailed": "聊天能力启动失败,请稍后重试或检查服务日志。",
|
|
60
|
-
"chatInputPlaceholder": "输入消息,/
|
|
63
|
+
"chatInputPlaceholder": "输入消息,/ 选命令、应用或技能,@ 引用面板应用,Enter 发送,Shift + Enter 换行",
|
|
61
64
|
"chatInputPlaceholderCompact": "发消息...",
|
|
62
65
|
"chatInputHint": "支持多轮上下文,默认走当前会话。",
|
|
63
66
|
"chatSlashSection": "斜杠菜单",
|
|
64
67
|
"chatSlashSectionCommands": "命令",
|
|
65
68
|
"chatSlashSectionSkills": "技能",
|
|
69
|
+
"chatSlashFilterAll": "全部",
|
|
70
|
+
"chatSlashFilterCommands": "命令",
|
|
71
|
+
"chatSlashFilterSkills": "技能",
|
|
72
|
+
"chatSlashFilterPanelApps": "面板应用",
|
|
66
73
|
"chatSlashTypeCommand": "命令",
|
|
67
74
|
"chatSlashTypeSkill": "技能",
|
|
68
75
|
"chatSlashSkillSpec": "标识",
|
|
69
76
|
"chatSlashSkillScope": "作用域",
|
|
70
|
-
"chatSlashLoading": "
|
|
77
|
+
"chatSlashLoading": "加载命令、面板应用与技能中…",
|
|
71
78
|
"chatSlashNoResult": "无匹配项",
|
|
72
|
-
"chatSlashHint": "输入 /
|
|
79
|
+
"chatSlashHint": "输入 / 触发命令、面板应用或技能选择",
|
|
73
80
|
"chatSlashCommandHint": "回车执行这个命令。",
|
|
74
81
|
"chatSlashCommandSideChatTitle": "Side chat",
|
|
75
82
|
"chatSlashCommandSideChatDescription": "打开一个继承当前会话上下文的右侧草稿会话。",
|
|
76
83
|
"chatSlashCommandSideChatDetail": "只有发送第一条消息后才会创建真实 child 会话。",
|
|
84
|
+
"chatSlashPanelAppHint": "回车在右侧打开这个面板应用。",
|
|
77
85
|
"chatSlashSkillHint": "回车把该技能加入本轮请求。",
|
|
78
86
|
"chatPanelAppReferenceLoading": "加载面板应用中…",
|
|
79
87
|
"chatPanelAppReferenceSection": "面板应用",
|
|
@@ -118,9 +126,9 @@
|
|
|
118
126
|
"chatContextInheritanceMessages": "继承消息数",
|
|
119
127
|
"chatSending": "发送中...",
|
|
120
128
|
"chatQueueSend": "排队发送",
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
129
|
+
"chatQueuedBannerAttachmentFallback": "附件消息",
|
|
130
|
+
"chatQueuedEdit": "编辑排队输入",
|
|
131
|
+
"chatQueuedDelete": "删除排队输入",
|
|
124
132
|
"chatDeleteSession": "删除会话",
|
|
125
133
|
"chatDeleteSessionConfirm": "确认删除当前会话?",
|
|
126
134
|
"chatSessionMoreActions": "更多操作",
|
|
@@ -136,6 +144,7 @@
|
|
|
136
144
|
"chatSessionProjectUpdateHint": "这里浏览和选择的是服务端机器上的目录。修改项目目录只影响后续消息和后续工具执行,不会改写历史会话。",
|
|
137
145
|
"chatSessionProjectUpdated": "项目目录已更新",
|
|
138
146
|
"chatSessionProjectCleared": "项目目录已清除",
|
|
147
|
+
"chatSessionErrorTitle": "出错了",
|
|
139
148
|
"chatSendFailed": "发送消息失败",
|
|
140
149
|
"chatRoleUser": "你",
|
|
141
150
|
"chatRoleAssistant": "助手",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"marketplace": "市场",
|
|
12
12
|
"advanced": "高级",
|
|
13
13
|
"settings": "设置",
|
|
14
|
+
"appearance": "外观",
|
|
14
15
|
"settingsMenu": "设置菜单",
|
|
15
16
|
"sidebarCollapse": "收起侧边栏",
|
|
16
17
|
"sidebarExpand": "展开侧边栏",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"themeCool": "雾蓝",
|
|
46
47
|
"themeDawn": "晨砂",
|
|
47
48
|
"themeGraphite": "石墨",
|
|
49
|
+
"themeNight": "暗夜",
|
|
48
50
|
"themeProbe": "探针",
|
|
49
51
|
"isRequired": "必填",
|
|
50
52
|
"duplicate": "重复",
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"docBrowserTitle": "内嵌浏览器",
|
|
3
3
|
"docBrowserSearchPlaceholder": "搜索,也可以输入文档地址直接打开",
|
|
4
|
+
"docBrowserAddressLabel": "地址",
|
|
5
|
+
"docBrowserAddressPlaceholder": "输入地址,例如 http://localhost:5173",
|
|
4
6
|
"docBrowserUrlPlaceholder": "输入文档路径,如 /guide/channels",
|
|
5
7
|
"docBrowserOpenExternal": "文档中心打开",
|
|
8
|
+
"docBrowserOpenInBrowser": "系统浏览器打开",
|
|
9
|
+
"docBrowserRefresh": "刷新",
|
|
6
10
|
"docBrowserFloatMode": "悬浮窗口",
|
|
7
11
|
"docBrowserDockMode": "固定到侧栏",
|
|
8
12
|
"docBrowserClose": "关闭",
|
|
@@ -17,6 +21,13 @@
|
|
|
17
21
|
"sideDockUnpinCurrent": "从快捷栏移除",
|
|
18
22
|
"sideDockBuiltInDocked": "内置快捷入口",
|
|
19
23
|
"sideDockUnpinItem": "移除快捷入口",
|
|
24
|
+
"sideDockGitHubProject": "GitHub 项目",
|
|
25
|
+
"sideDockHide": "隐藏快捷栏",
|
|
26
|
+
"sideDockHideConfirmTitle": "隐藏快捷栏?",
|
|
27
|
+
"sideDockHideConfirmDescription": "隐藏后可在「设置 > 外观」中重新打开。",
|
|
28
|
+
"sideDockHideConfirmAction": "隐藏",
|
|
29
|
+
"sideDockVisibilityTitle": "显示快捷栏",
|
|
30
|
+
"sideDockVisibilityDescription": "在工作区右侧显示快捷入口栏。",
|
|
20
31
|
"appsTitle": "应用",
|
|
21
32
|
"panelAppsTitle": "面板应用",
|
|
22
33
|
"panelAppsLoading": "加载面板应用中...",
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
"marketplaceDetailPanelEmpty": "点击左侧任意插件/技能,在这里查看详情。",
|
|
59
59
|
"marketplaceDetailSummary": "摘要",
|
|
60
60
|
"marketplaceDetailDescription": "描述",
|
|
61
|
+
"marketplaceDetailMetadata": "元数据",
|
|
62
|
+
"marketplaceDetailContent": "内容",
|
|
63
|
+
"marketplaceDetailSource": "来源",
|
|
64
|
+
"marketplaceDetailUnavailableTitle": "详情暂不可用",
|
|
65
|
+
"marketplaceDetailUnavailableDescription": "这个右侧详情标签页缺少当前会话中的内容数据,请从市场列表重新打开一次。",
|
|
61
66
|
"marketplaceOpenInDocBrowserTab": "新标签打开",
|
|
62
67
|
"marketplaceMcpInstallDialogTitle": "安装 MCP Server",
|
|
63
68
|
"marketplaceMcpServerName": "Server 名称",
|
|
@@ -14,24 +14,32 @@ describe("buildWorkspaceFileBreadcrumb", () => {
|
|
|
14
14
|
key: "workspace:project-alpha",
|
|
15
15
|
label: "project-alpha",
|
|
16
16
|
kind: "workspace",
|
|
17
|
+
path: "/Users/demo/project-alpha",
|
|
18
|
+
browsePath: "/Users/demo/project-alpha",
|
|
17
19
|
isCurrent: false,
|
|
18
20
|
},
|
|
19
21
|
{
|
|
20
22
|
key: "0:src",
|
|
21
23
|
label: "src",
|
|
22
24
|
kind: "directory",
|
|
25
|
+
path: "/Users/demo/project-alpha/src",
|
|
26
|
+
browsePath: "/Users/demo/project-alpha/src",
|
|
23
27
|
isCurrent: false,
|
|
24
28
|
},
|
|
25
29
|
{
|
|
26
30
|
key: "1:chat",
|
|
27
31
|
label: "chat",
|
|
28
32
|
kind: "directory",
|
|
33
|
+
path: "/Users/demo/project-alpha/src/chat",
|
|
34
|
+
browsePath: "/Users/demo/project-alpha/src/chat",
|
|
29
35
|
isCurrent: false,
|
|
30
36
|
},
|
|
31
37
|
{
|
|
32
38
|
key: "2:example.tsx",
|
|
33
39
|
label: "example.tsx",
|
|
34
40
|
kind: "file",
|
|
41
|
+
path: "/Users/demo/project-alpha/src/chat/example.tsx",
|
|
42
|
+
browsePath: "/Users/demo/project-alpha/src/chat",
|
|
35
43
|
isCurrent: true,
|
|
36
44
|
},
|
|
37
45
|
]);
|
|
@@ -49,18 +57,24 @@ describe("buildWorkspaceFileBreadcrumb", () => {
|
|
|
49
57
|
key: "root:/",
|
|
50
58
|
label: "/",
|
|
51
59
|
kind: "root",
|
|
60
|
+
path: "/",
|
|
61
|
+
browsePath: "/",
|
|
52
62
|
isCurrent: false,
|
|
53
63
|
},
|
|
54
64
|
{
|
|
55
65
|
key: "0:tmp",
|
|
56
66
|
label: "tmp",
|
|
57
67
|
kind: "directory",
|
|
68
|
+
path: "/tmp",
|
|
69
|
+
browsePath: "/tmp",
|
|
58
70
|
isCurrent: false,
|
|
59
71
|
},
|
|
60
72
|
{
|
|
61
73
|
key: "1:example.ts",
|
|
62
74
|
label: "example.ts",
|
|
63
75
|
kind: "file",
|
|
76
|
+
path: "/tmp/example.ts",
|
|
77
|
+
browsePath: "/tmp",
|
|
64
78
|
isCurrent: true,
|
|
65
79
|
},
|
|
66
80
|
]);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './session-project.utils';
|
|
2
|
-
export * from './workspace-file-breadcrumb';
|
|
2
|
+
export * from './workspace-file-breadcrumb.utils';
|
|
@@ -4,6 +4,8 @@ export type WorkspaceFileBreadcrumbSegmentViewModel = {
|
|
|
4
4
|
key: string;
|
|
5
5
|
label: string;
|
|
6
6
|
kind: "workspace" | "root" | "directory" | "file";
|
|
7
|
+
path: string;
|
|
8
|
+
browsePath: string | null;
|
|
7
9
|
isCurrent: boolean;
|
|
8
10
|
};
|
|
9
11
|
|
|
@@ -28,6 +30,41 @@ function normalizeComparablePath(value: string): string {
|
|
|
28
30
|
: trimmed;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
function joinPathSegments(basePath: string | null, segments: string[]): string {
|
|
34
|
+
if (!basePath) {
|
|
35
|
+
return segments.join("/");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const normalizedBase = trimPathSeparators(basePath.trim().replace(/\\/g, "/"));
|
|
39
|
+
if (segments.length === 0) {
|
|
40
|
+
return normalizedBase;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (normalizedBase === "/") {
|
|
44
|
+
return `/${segments.join("/")}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return `${normalizedBase}/${segments.join("/")}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readParentPath(path: string): string | null {
|
|
51
|
+
const normalizedPath = trimPathSeparators(path.trim().replace(/\\/g, "/"));
|
|
52
|
+
if (!normalizedPath || normalizedPath === "/" || /^[A-Za-z]:[\\/]?$/.test(normalizedPath)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const separatorIndex = normalizedPath.lastIndexOf("/");
|
|
57
|
+
if (separatorIndex < 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (separatorIndex === 0) {
|
|
61
|
+
return "/";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const parentPath = normalizedPath.slice(0, separatorIndex);
|
|
65
|
+
return /^[A-Za-z]:$/.test(parentPath) ? `${parentPath}/` : parentPath;
|
|
66
|
+
}
|
|
67
|
+
|
|
31
68
|
function readDisplaySegments(value: string): {
|
|
32
69
|
prefix: string | null;
|
|
33
70
|
segments: string[];
|
|
@@ -93,16 +130,23 @@ function readRelativeSegments(params: {
|
|
|
93
130
|
|
|
94
131
|
function buildSegmentsFromLabels(params: {
|
|
95
132
|
labels: string[];
|
|
133
|
+
basePath?: string | null;
|
|
96
134
|
leading?: WorkspaceFileBreadcrumbSegmentViewModel | null;
|
|
97
135
|
}): WorkspaceFileBreadcrumbSegmentViewModel[] {
|
|
98
|
-
const { labels, leading = null } = params;
|
|
136
|
+
const { basePath = null, labels, leading = null } = params;
|
|
99
137
|
const items = labels.map<WorkspaceFileBreadcrumbSegmentViewModel>(
|
|
100
|
-
(label, index) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
138
|
+
(label, index) => {
|
|
139
|
+
const kind = index === labels.length - 1 ? "file" : "directory";
|
|
140
|
+
const path = joinPathSegments(basePath, labels.slice(0, index + 1));
|
|
141
|
+
return {
|
|
142
|
+
key: `${index}:${label}`,
|
|
143
|
+
label,
|
|
144
|
+
kind,
|
|
145
|
+
path,
|
|
146
|
+
browsePath: kind === "file" ? readParentPath(path) : path,
|
|
147
|
+
isCurrent: index === labels.length - 1,
|
|
148
|
+
};
|
|
149
|
+
},
|
|
106
150
|
);
|
|
107
151
|
|
|
108
152
|
return leading ? [leading, ...items] : items;
|
|
@@ -141,27 +185,35 @@ export function buildWorkspaceFileBreadcrumb(params: {
|
|
|
141
185
|
let segments: WorkspaceFileBreadcrumbSegmentViewModel[];
|
|
142
186
|
|
|
143
187
|
if (sessionProjectRoot?.trim() && relativeSegments) {
|
|
188
|
+
const workspacePath = sessionProjectRoot.trim();
|
|
144
189
|
const workspaceLabel =
|
|
145
|
-
getSessionProjectName(
|
|
190
|
+
getSessionProjectName(workspacePath) ?? workspacePath;
|
|
146
191
|
|
|
147
192
|
segments = buildSegmentsFromLabels({
|
|
148
193
|
labels: relativeSegments,
|
|
194
|
+
basePath: workspacePath,
|
|
149
195
|
leading: {
|
|
150
196
|
key: `workspace:${workspaceLabel}`,
|
|
151
197
|
label: workspaceLabel,
|
|
152
198
|
kind: "workspace",
|
|
199
|
+
path: workspacePath,
|
|
200
|
+
browsePath: workspacePath,
|
|
153
201
|
isCurrent: relativeSegments.length === 0,
|
|
154
202
|
},
|
|
155
203
|
});
|
|
156
204
|
} else {
|
|
157
205
|
const { prefix, segments: labels } = readDisplaySegments(fullPath);
|
|
206
|
+
const rootPath = prefix === "/" ? "/" : prefix ? `${prefix}/` : null;
|
|
158
207
|
segments = buildSegmentsFromLabels({
|
|
159
208
|
labels,
|
|
209
|
+
basePath: rootPath,
|
|
160
210
|
leading: prefix
|
|
161
211
|
? {
|
|
162
212
|
key: `root:${prefix}`,
|
|
163
213
|
label: prefix,
|
|
164
214
|
kind: "root",
|
|
215
|
+
path: rootPath ?? prefix,
|
|
216
|
+
browsePath: rootPath ?? prefix,
|
|
165
217
|
isCurrent: labels.length === 0,
|
|
166
218
|
}
|
|
167
219
|
: null,
|
|
@@ -174,6 +226,8 @@ export function buildWorkspaceFileBreadcrumb(params: {
|
|
|
174
226
|
key: "file:unknown",
|
|
175
227
|
label: fullPath || "file",
|
|
176
228
|
kind: "file",
|
|
229
|
+
path: fullPath,
|
|
230
|
+
browsePath: readParentPath(fullPath),
|
|
177
231
|
isCurrent: true,
|
|
178
232
|
},
|
|
179
233
|
];
|
|
@@ -1,19 +1,44 @@
|
|
|
1
|
-
export type UiTheme =
|
|
1
|
+
export type UiTheme =
|
|
2
|
+
| 'natural'
|
|
3
|
+
| 'minimal'
|
|
4
|
+
| 'warm'
|
|
5
|
+
| 'cool'
|
|
6
|
+
| 'dawn'
|
|
7
|
+
| 'graphite'
|
|
8
|
+
| 'night'
|
|
9
|
+
| 'probe';
|
|
10
|
+
export type UiThemeAppearance = 'light' | 'dark';
|
|
11
|
+
|
|
12
|
+
type UiThemeDefinition = {
|
|
13
|
+
value: UiTheme;
|
|
14
|
+
labelKey: string;
|
|
15
|
+
appearance: UiThemeAppearance;
|
|
16
|
+
};
|
|
2
17
|
|
|
3
18
|
const THEME_STORAGE_KEY = 'nextclaw.ui.theme';
|
|
4
19
|
const DEFAULT_THEME: UiTheme = 'natural';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{ value: 'natural', labelKey: 'themeNatural' },
|
|
9
|
-
{ value: 'minimal', labelKey: 'themeMinimal' },
|
|
10
|
-
{ value: 'warm', labelKey: 'themeWarm' },
|
|
11
|
-
{ value: 'cool', labelKey: 'themeCool' },
|
|
12
|
-
{ value: 'dawn', labelKey: 'themeDawn' },
|
|
13
|
-
{ value: 'graphite', labelKey: 'themeGraphite' },
|
|
14
|
-
{ value: 'probe', labelKey: 'themeProbe' }
|
|
20
|
+
|
|
21
|
+
const THEME_DEFINITIONS: readonly UiThemeDefinition[] = [
|
|
22
|
+
{ value: 'night', labelKey: 'themeNight', appearance: 'dark' },
|
|
23
|
+
{ value: 'natural', labelKey: 'themeNatural', appearance: 'light' },
|
|
24
|
+
{ value: 'minimal', labelKey: 'themeMinimal', appearance: 'light' },
|
|
25
|
+
{ value: 'warm', labelKey: 'themeWarm', appearance: 'light' },
|
|
26
|
+
{ value: 'cool', labelKey: 'themeCool', appearance: 'light' },
|
|
27
|
+
{ value: 'dawn', labelKey: 'themeDawn', appearance: 'light' },
|
|
28
|
+
{ value: 'graphite', labelKey: 'themeGraphite', appearance: 'light' },
|
|
29
|
+
{ value: 'probe', labelKey: 'themeProbe', appearance: 'light' },
|
|
15
30
|
];
|
|
16
31
|
|
|
32
|
+
const THEME_VALUES: readonly UiTheme[] = THEME_DEFINITIONS.map(
|
|
33
|
+
({ value }) => value,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const THEME_OPTIONS: Array<{ value: UiTheme; labelKey: string }> =
|
|
37
|
+
THEME_DEFINITIONS.map(({ value, labelKey }) => ({
|
|
38
|
+
value,
|
|
39
|
+
labelKey,
|
|
40
|
+
}));
|
|
41
|
+
|
|
17
42
|
export function normalizeTheme(value: unknown): UiTheme | null {
|
|
18
43
|
if (typeof value !== 'string') {
|
|
19
44
|
return null;
|
|
@@ -26,6 +51,13 @@ export function normalizeTheme(value: unknown): UiTheme | null {
|
|
|
26
51
|
return value === 'leaf' ? 'warm' : null;
|
|
27
52
|
}
|
|
28
53
|
|
|
54
|
+
export function getThemeAppearance(theme: UiTheme): UiThemeAppearance {
|
|
55
|
+
return (
|
|
56
|
+
THEME_DEFINITIONS.find((definition) => definition.value === theme)
|
|
57
|
+
?.appearance ?? 'light'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
29
61
|
class UiThemeOwner {
|
|
30
62
|
private activeTheme: UiTheme = DEFAULT_THEME;
|
|
31
63
|
private initialized = false;
|
|
@@ -58,7 +90,8 @@ class UiThemeOwner {
|
|
|
58
90
|
return this.activeTheme;
|
|
59
91
|
};
|
|
60
92
|
|
|
61
|
-
getTheme = (): UiTheme =>
|
|
93
|
+
getTheme = (): UiTheme =>
|
|
94
|
+
this.initialized ? this.activeTheme : this.initializeTheme();
|
|
62
95
|
|
|
63
96
|
setTheme = (theme: UiTheme): void => {
|
|
64
97
|
this.initializeTheme();
|
|
@@ -83,7 +116,10 @@ class UiThemeOwner {
|
|
|
83
116
|
if (typeof document === 'undefined') {
|
|
84
117
|
return;
|
|
85
118
|
}
|
|
119
|
+
const appearance = getThemeAppearance(theme);
|
|
86
120
|
document.documentElement.setAttribute('data-theme', theme);
|
|
121
|
+
document.documentElement.setAttribute('data-theme-appearance', appearance);
|
|
122
|
+
document.documentElement.style.colorScheme = appearance;
|
|
87
123
|
};
|
|
88
124
|
|
|
89
125
|
private saveTheme = (theme: UiTheme): void => {
|
|
@@ -117,6 +153,8 @@ export function setTheme(theme: UiTheme): void {
|
|
|
117
153
|
uiThemeOwner.setTheme(theme);
|
|
118
154
|
}
|
|
119
155
|
|
|
120
|
-
export function subscribeThemeChange(
|
|
156
|
+
export function subscribeThemeChange(
|
|
157
|
+
listener: (theme: UiTheme) => void,
|
|
158
|
+
): () => void {
|
|
121
159
|
return uiThemeOwner.subscribeThemeChange(listener);
|
|
122
160
|
}
|