@nextclaw/ui 0.14.1 → 0.14.3
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 +41 -0
- package/dist/assets/{api-DPcvCLfa.js → api-BP_8IaxB.js} +7 -7
- package/dist/assets/channels-list-page-DEh5Jjqx.js +8 -0
- package/dist/assets/chat-page-BRB0N5WW.js +105 -0
- package/dist/assets/config-split-page-CRhlZ_pH.js +1 -0
- package/dist/assets/{confirm-dialog-CWh5FfK2.js → confirm-dialog-DsivFJ4r.js} +2 -2
- package/dist/assets/{desktop-update-config-DsRhBizi.js → desktop-update-config-B0w9p2ep.js} +1 -1
- package/dist/assets/{dist-DPrgcEC0.js → dist-CikkhOlq.js} +1 -1
- package/dist/assets/{dist-DsE4OsSS.js → dist-PVA_xL1D.js} +1 -1
- package/dist/assets/doc-browser-BoNJ7-tE.js +1 -0
- package/dist/assets/doc-browser-CdJ_d4ik.js +1 -0
- package/dist/assets/doc-browser-context-MTw4EvKd.js +1 -0
- package/dist/assets/{ellipsis-B81FStLm.js → ellipsis-y6vDtq4M.js} +1 -1
- package/dist/assets/{external-link-C_dmG-WJ.js → external-link-DVJIQf_G.js} +1 -1
- package/dist/assets/index-BiE-K8-q.css +1 -0
- package/dist/assets/index-D9BAE_L_.js +105 -0
- package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +40 -0
- package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +1 -0
- package/dist/assets/middleware-CkFT3R4v.js +1 -0
- package/dist/assets/model-config-page-DotaTUHb.js +1 -0
- package/dist/assets/plus-BbZqquJe.js +1 -0
- package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +1 -0
- package/dist/assets/providers-config-page-DW4tQSLN.js +1 -0
- package/dist/assets/{react-sZz5HSCm.js → react-VNqT5Zp7.js} +1 -1
- package/dist/assets/remote-B8TiUfwW.js +1 -0
- package/dist/assets/rotate-cw-DjHDTkCv.js +1 -0
- package/dist/assets/runtime-config-page-S6ErRHR8.js +1 -0
- package/dist/assets/{save-MzjoiHri.js → save-BOJ477H-.js} +1 -1
- package/dist/assets/{search-B29dE4GN.js → search-BEti5beA.js} +1 -1
- package/dist/assets/{search-config-page-Dl5sZzob.js → search-config-page-DGj5am03.js} +1 -1
- package/dist/assets/{secrets-config-page-C_l-IFCK.js → secrets-config-page--IzrIOid.js} +2 -2
- package/dist/assets/{select-BKmh05hd.js → select-hoj8dK4K.js} +2 -2
- package/dist/assets/{tag-chip-BS_7Ueom.js → tag-chip-T8-h-die.js} +1 -1
- package/dist/assets/use-config-CjRV2hwL.js +1 -0
- package/dist/assets/x-D9PXs_xP.js +1 -0
- package/dist/index.html +70 -19
- package/index.html +55 -5
- package/package.json +9 -9
- package/src/app/components/app-presenter-provider.tsx +11 -2
- package/src/app/components/layout/__tests__/app-layout.test.tsx +11 -2
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +110 -79
- package/src/app/components/layout/app-layout.tsx +18 -4
- package/src/app/components/layout/header.tsx +3 -3
- package/src/app/components/layout/page-layout.tsx +2 -2
- package/src/app/components/layout/runtime-status-entry.tsx +7 -7
- package/src/app/components/layout/sidebar-items.tsx +194 -71
- package/src/app/components/layout/sidebar-rail.styles.ts +15 -0
- package/src/app/components/layout/sidebar.tsx +340 -83
- package/src/app/managers/viewport-layout.manager.ts +10 -0
- package/src/app/stores/viewport-layout.store.ts +39 -3
- package/src/app/styles/design-system.css +388 -26
- package/src/app/test/vitest-setup.ts +31 -0
- package/src/features/agents/components/__tests__/agents-page.test.tsx +62 -39
- package/src/features/agents/components/agents-page.tsx +10 -6
- package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +3 -3
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +145 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header.test.tsx +4 -2
- package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +69 -762
- package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +132 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +22 -15
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +15 -16
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +7 -6
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +19 -19
- package/src/features/chat/components/conversation/chat-conversation-skeleton.tsx +13 -13
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +10 -4
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +52 -11
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +18 -16
- package/src/features/chat/components/layout/chat-page-shell.tsx +1 -1
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +247 -0
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +93 -20
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +93 -36
- package/src/features/chat/components/layout/chat-sidebar.tsx +60 -93
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +0 -6
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +225 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +199 -0
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +308 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +439 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +194 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +66 -0
- package/src/features/chat/features/{input/hooks/use-chat-input-bar-query-state.ts → conversation/hooks/use-session-conversation-input-query.ts} +51 -20
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +281 -0
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +158 -0
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +56 -0
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +117 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +77 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +10 -0
- package/src/features/chat/features/input/input-surface-plugins/input-surface-search.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +161 -0
- package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +26 -13
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +7 -28
- package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +6 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +26 -51
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +2 -0
- package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +5 -6
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +47 -12
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +7 -7
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +99 -18
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +31 -12
- package/src/features/chat/features/message/utils/chat-message-markdown-part.utils.ts +38 -0
- package/src/features/chat/features/message/utils/chat-message-part.utils.ts +1 -1
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +7 -7
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +6 -6
- package/src/features/chat/features/session/components/session-context-icon.tsx +2 -2
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-badge.test.tsx +2 -2
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -3
- package/src/features/chat/features/session/components/session-run-badge.tsx +1 -1
- package/src/features/chat/features/session/hooks/__tests__/use-chat-session-project.test.tsx +3 -30
- package/src/features/chat/features/session/hooks/use-chat-session-project.ts +0 -5
- package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +8 -0
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-menu.test.tsx +2 -2
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +2 -0
- package/src/features/chat/features/session-type/components/chat-session-type-menu.tsx +2 -2
- package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +2 -2
- package/src/features/chat/features/session-type/hooks/use-chat-session-type-state.ts +10 -2
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +49 -31
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +2 -9
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +21 -10
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +3 -3
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +4 -4
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +14 -19
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +5 -5
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +6 -6
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +6 -8
- package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +1 -16
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +54 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +44 -76
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +7 -1
- package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +40 -31
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +59 -1
- package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +109 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +44 -4
- package/src/features/chat/index.ts +0 -1
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +51 -99
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +135 -21
- package/src/features/chat/managers/chat-session-list.manager.ts +12 -43
- package/src/features/chat/managers/chat-thread.manager.ts +108 -34
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +48 -1
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +72 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +0 -122
- package/src/features/chat/presenters/chat.presenter.ts +2 -19
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +31 -0
- package/src/features/chat/stores/chat-thread.store.ts +42 -26
- package/src/features/panel-apps/components/panel-app-list-item.tsx +8 -8
- package/src/features/panel-apps/components/panel-app-toolbar.tsx +4 -4
- package/src/features/panel-apps/components/panel-apps-list.tsx +20 -20
- package/src/features/panel-apps/hooks/use-panel-apps.ts +2 -1
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +70 -6
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +10 -5
- package/src/features/service-apps/components/service-action-authorization-dialog.tsx +8 -8
- package/src/features/service-apps/components/service-apps-panel.tsx +24 -24
- package/src/features/settings/components/config/provider-advanced-settings-section.tsx +5 -5
- package/src/features/settings/components/config/provider-auth-section.tsx +7 -7
- package/src/features/settings/components/config/provider-form.tsx +10 -10
- package/src/features/settings/components/config/provider-models-section.tsx +16 -16
- package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
- package/src/features/settings/pages/providers-config-page.tsx +17 -17
- package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +50 -10
- package/src/platforms/desktop/components/desktop-app-shell.tsx +34 -10
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +41 -16
- package/src/shared/components/config-split-page.tsx +11 -11
- package/src/shared/components/doc-browser/doc-browser-home-page.tsx +2 -2
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -4
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +4 -4
- package/src/shared/components/doc-browser/doc-browser.tsx +3 -3
- package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +1 -1
- package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
- package/src/shared/components/ui/button.tsx +4 -4
- package/src/shared/components/ui/card.tsx +4 -4
- package/src/shared/components/ui/dialog.tsx +5 -5
- package/src/shared/components/ui/input.tsx +1 -1
- package/src/shared/components/ui/label.tsx +1 -1
- package/src/shared/components/ui/popover.tsx +1 -1
- package/src/shared/components/ui/select.tsx +3 -3
- package/src/shared/components/ui/switch.tsx +3 -3
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +24 -1
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -4
- package/src/shared/components/ui/tabs.tsx +4 -4
- package/src/shared/components/ui/textarea.tsx +1 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +20 -2
- package/src/shared/lib/i18n/locales/en-US/core.json +9 -2
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -2
- package/src/shared/lib/i18n/locales/zh-CN/core.json +9 -2
- package/src/shared/lib/theme/index.ts +94 -52
- package/src/shared/lib/ui-document-title/__tests__/ui-document-title.test.ts +49 -0
- package/src/shared/lib/ui-document-title/index.ts +50 -21
- package/dist/assets/channels-list-page-D6jtziu4.js +0 -8
- package/dist/assets/chat-page-BBnNJoKG.js +0 -105
- package/dist/assets/config-split-page-BCJhqdK9.js +0 -1
- package/dist/assets/doc-browser-C8xOF_9F.js +0 -1
- package/dist/assets/doc-browser-DgQedeAn.js +0 -1
- package/dist/assets/doc-browser-context-B46tQGmO.js +0 -1
- package/dist/assets/index-7o1WdUbU.js +0 -103
- package/dist/assets/index-DQWmEWzk.css +0 -1
- package/dist/assets/mcp-marketplace-page-CWZN7ohK.js +0 -1
- package/dist/assets/mcp-marketplace-page-Dzull_4-.js +0 -40
- package/dist/assets/model-config-page-Cv0O3BIj.js +0 -1
- package/dist/assets/plus-Bnt56oqG.js +0 -1
- package/dist/assets/provider-scoped-model-input-Duuh7i3h.js +0 -1
- package/dist/assets/providers-config-page-CPDz0Lxc.js +0 -1
- package/dist/assets/remote-B0LMs8u7.js +0 -1
- package/dist/assets/rotate-cw-BfyG_KEF.js +0 -1
- package/dist/assets/runtime-config-page-q0fvv8ME.js +0 -1
- package/dist/assets/use-config-DZkBWIdL.js +0 -1
- package/dist/assets/x-CW2KO-Hz.js +0 -1
- package/src/features/chat/components/conversation/chat-conversation-alerts.tsx +0 -37
- package/src/features/chat/features/input/components/__tests__/chat-attachment-upload-limit.test.ts +0 -38
- package/src/features/chat/features/input/components/chat-input-bar.container.tsx +0 -496
- package/src/features/chat/features/message/utils/chat-message-inline-content.utils.ts +0 -95
- package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +0 -448
- package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +0 -129
- package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +0 -236
- package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +0 -219
- package/src/features/chat/managers/chat-input.manager.ts +0 -430
- package/src/features/chat/managers/chat-run.manager.ts +0 -110
- package/src/features/chat/managers/chat-session-preference-sync.manager.ts +0 -142
- package/src/features/chat/stores/chat-input.store.ts +0 -104
- /package/dist/assets/{config-hints-CTqBnCDp.js → config-hints-BkYBc7z0.js} +0 -0
|
@@ -11,41 +11,14 @@ import {
|
|
|
11
11
|
} from "@/features/chat/components/layout/chat-page-shell";
|
|
12
12
|
import { parseSessionKeyFromRoute } from "@/features/chat/features/session/utils/chat-session-route.utils";
|
|
13
13
|
import { ChatPresenter } from "@/features/chat/presenters/chat.presenter";
|
|
14
|
-
import {
|
|
15
|
-
isNcpAgentStartupUnavailableErrorMessage,
|
|
16
|
-
useNcpSessionConversation,
|
|
17
|
-
} from "@/features/chat/features/ncp/hooks/use-ncp-session-conversation";
|
|
18
|
-
import {
|
|
19
|
-
useNcpChatSelectedSession,
|
|
20
|
-
} from "@/features/chat/features/ncp/hooks/use-ncp-chat-derived-state";
|
|
21
14
|
import { useChatQueryStoreSync } from "@/features/chat/features/ncp/hooks/use-ncp-chat-query-store-sync";
|
|
22
15
|
import {
|
|
23
16
|
ChatPresenterProvider,
|
|
24
17
|
usePresenter,
|
|
25
18
|
} from "@/features/chat/components/providers/chat-presenter.provider";
|
|
26
19
|
import { useConfirmDialog } from "@/shared/hooks/use-confirm-dialog";
|
|
27
|
-
import { useSystemStatus } from "@/features/system-status";
|
|
28
20
|
import { useAppPresenter } from "@/app/components/app-presenter-provider";
|
|
29
|
-
import { isNcpChatRuntimeBlocked, resolveNcpChatSendErrorMessage } from "@/features/chat/features/runtime/utils/ncp-chat-runtime-availability.utils";
|
|
30
21
|
import { useUiShowContentEvent } from "@/features/chat/features/ncp/hooks/use-ui-show-content-event";
|
|
31
|
-
import { readNcpContextWindowValue } from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
|
|
32
|
-
|
|
33
|
-
function useChatDraftIntentConsumer() {
|
|
34
|
-
const appPresenter = useAppPresenter();
|
|
35
|
-
const presenter = usePresenter();
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const applyIntent = (intent: { id: number; prompt: string }) => {
|
|
38
|
-
presenter.startAgentCreationDraft(intent.prompt);
|
|
39
|
-
appPresenter.chatDraftIntentManager.markConsumed(intent.id);
|
|
40
|
-
};
|
|
41
|
-
const unsubscribe = appPresenter.chatDraftIntentManager.subscribe(applyIntent);
|
|
42
|
-
const pendingIntent = appPresenter.chatDraftIntentManager.consumePending();
|
|
43
|
-
if (pendingIntent) {
|
|
44
|
-
applyIntent(pendingIntent);
|
|
45
|
-
}
|
|
46
|
-
return unsubscribe;
|
|
47
|
-
}, [appPresenter, presenter]);
|
|
48
|
-
}
|
|
49
22
|
|
|
50
23
|
function useNcpChatRouteSelection() {
|
|
51
24
|
const { sessionId: routeSessionIdParam } = useParams<{ sessionId?: string }>();
|
|
@@ -59,31 +32,6 @@ function useNcpChatRouteSelection() {
|
|
|
59
32
|
};
|
|
60
33
|
}
|
|
61
34
|
|
|
62
|
-
type NcpChatConversation = ReturnType<typeof useNcpSessionConversation>;
|
|
63
|
-
|
|
64
|
-
function useChatRunRuntimeConnection(params: {
|
|
65
|
-
agent: NcpChatConversation;
|
|
66
|
-
sessionKey: string | undefined;
|
|
67
|
-
}) {
|
|
68
|
-
const presenter = usePresenter();
|
|
69
|
-
const { agent, sessionKey } = params;
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
presenter.chatRunManager.setActiveRuntime({
|
|
72
|
-
sessionKey: sessionKey ?? null,
|
|
73
|
-
sendEnvelope: (envelope) => agent.send(envelope),
|
|
74
|
-
abortCurrentRun: () => agent.abort(),
|
|
75
|
-
resumeCurrentSessionRun: () => agent.streamRun(),
|
|
76
|
-
});
|
|
77
|
-
return () => {
|
|
78
|
-
presenter.chatRunManager.setActiveRuntime(null);
|
|
79
|
-
};
|
|
80
|
-
}, [
|
|
81
|
-
agent,
|
|
82
|
-
presenter,
|
|
83
|
-
sessionKey,
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
35
|
function useNcpChatUiBindings() {
|
|
88
36
|
const presenter = usePresenter();
|
|
89
37
|
const { confirm, ConfirmDialog } = useConfirmDialog();
|
|
@@ -101,42 +49,6 @@ function useNcpChatUiBindings() {
|
|
|
101
49
|
return <ConfirmDialog />;
|
|
102
50
|
}
|
|
103
51
|
|
|
104
|
-
function useChatRunSnapshotSync(params: {
|
|
105
|
-
agent: NcpChatConversation;
|
|
106
|
-
isSending: boolean;
|
|
107
|
-
isRunning: boolean;
|
|
108
|
-
lastSendError: string | null;
|
|
109
|
-
routeSessionKey: string | null;
|
|
110
|
-
}) {
|
|
111
|
-
const presenter = usePresenter();
|
|
112
|
-
const { agent, isRunning, isSending, lastSendError, routeSessionKey } = params;
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
presenter.chatRunManager.applyRunSnapshot({
|
|
115
|
-
routeSessionKey,
|
|
116
|
-
isHydrating: agent.isHydrating,
|
|
117
|
-
isSending,
|
|
118
|
-
isRunning,
|
|
119
|
-
visibleMessages: agent.visibleMessages,
|
|
120
|
-
contextWindow: readNcpContextWindowValue(agent.snapshot.contextWindow),
|
|
121
|
-
sendErrorMessage: lastSendError,
|
|
122
|
-
materializedSessionKey:
|
|
123
|
-
agent.snapshot.activeRun?.sessionId ??
|
|
124
|
-
agent.visibleMessages.find((message) => message.sessionId.trim())?.sessionId ??
|
|
125
|
-
null,
|
|
126
|
-
});
|
|
127
|
-
}, [
|
|
128
|
-
agent.isHydrating,
|
|
129
|
-
agent.snapshot.activeRun?.sessionId,
|
|
130
|
-
agent.snapshot.contextWindow,
|
|
131
|
-
agent.visibleMessages,
|
|
132
|
-
isRunning,
|
|
133
|
-
isSending,
|
|
134
|
-
lastSendError,
|
|
135
|
-
presenter,
|
|
136
|
-
routeSessionKey,
|
|
137
|
-
]);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
52
|
export function NcpChatPage({ view }: ChatPageProps) {
|
|
141
53
|
const appPresenter = useAppPresenter();
|
|
142
54
|
const [presenter] = useState(() => new ChatPresenter(appPresenter));
|
|
@@ -149,52 +61,18 @@ export function NcpChatPage({ view }: ChatPageProps) {
|
|
|
149
61
|
|
|
150
62
|
function NcpChatPageContent({ view }: ChatPageProps) {
|
|
151
63
|
const presenter = usePresenter();
|
|
152
|
-
const systemStatus = useSystemStatus();
|
|
153
|
-
const isRuntimeBlocked = isNcpChatRuntimeBlocked(systemStatus);
|
|
154
64
|
const confirmDialog = useNcpChatUiBindings();
|
|
155
65
|
const routeSelection = useNcpChatRouteSelection();
|
|
156
66
|
const { routeSessionKey, sessionKey } = routeSelection;
|
|
157
67
|
useChatQueryStoreSync({
|
|
158
68
|
sessionKey: sessionKey ?? null,
|
|
159
69
|
});
|
|
160
|
-
const selectedSession = useNcpChatSelectedSession(sessionKey ?? null);
|
|
161
|
-
const agent = useNcpSessionConversation(sessionKey);
|
|
162
|
-
const rawLastSendError =
|
|
163
|
-
agent.hydrateError?.message ?? agent.snapshot.error?.message ?? null;
|
|
164
|
-
const filteredLastSendError =
|
|
165
|
-
systemStatus.phase === "ready" &&
|
|
166
|
-
isNcpAgentStartupUnavailableErrorMessage(rawLastSendError)
|
|
167
|
-
? null
|
|
168
|
-
: rawLastSendError;
|
|
169
|
-
const currentSessionRunning = agent.isRunning || selectedSession?.status === "running";
|
|
170
|
-
const isSending = agent.isSending || currentSessionRunning;
|
|
171
|
-
const lastSendError =
|
|
172
|
-
isRuntimeBlocked
|
|
173
|
-
? null
|
|
174
|
-
: systemStatus.phase === "ready"
|
|
175
|
-
? filteredLastSendError
|
|
176
|
-
: resolveNcpChatSendErrorMessage({
|
|
177
|
-
message: filteredLastSendError,
|
|
178
|
-
status: systemStatus,
|
|
179
|
-
});
|
|
180
|
-
useChatRunRuntimeConnection({
|
|
181
|
-
agent,
|
|
182
|
-
sessionKey,
|
|
183
|
-
});
|
|
184
70
|
useChatSessionSync({
|
|
185
71
|
view,
|
|
186
72
|
routeSessionKey,
|
|
187
73
|
syncRouteSessionSelection:
|
|
188
74
|
presenter.chatSessionListManager.syncRouteSessionSelection,
|
|
189
75
|
});
|
|
190
|
-
useChatDraftIntentConsumer();
|
|
191
76
|
useUiShowContentEvent();
|
|
192
|
-
useChatRunSnapshotSync({
|
|
193
|
-
agent,
|
|
194
|
-
isRunning: currentSessionRunning,
|
|
195
|
-
routeSessionKey,
|
|
196
|
-
lastSendError,
|
|
197
|
-
isSending,
|
|
198
|
-
});
|
|
199
77
|
return <ChatPageLayout view={view} confirmDialog={confirmDialog} />;
|
|
200
78
|
}
|
|
@@ -1,39 +1,22 @@
|
|
|
1
1
|
import { ChatSessionListManager } from '@/features/chat/managers/chat-session-list.manager';
|
|
2
|
-
import { ChatRunManager } from '@/features/chat/managers/chat-run.manager';
|
|
3
2
|
import { ChatUiManager } from '@/features/chat/managers/chat-ui.manager';
|
|
4
|
-
import { ChatInputManager } from '@/features/chat/managers/chat-input.manager';
|
|
5
3
|
import { ChatQueryManager } from '@/features/chat/managers/chat-query.manager';
|
|
6
4
|
import { ChatThreadManager } from '@/features/chat/managers/chat-thread.manager';
|
|
7
5
|
import type { AppPresenter } from '@/app/presenters/app.presenter';
|
|
8
6
|
|
|
9
7
|
export class ChatPresenter {
|
|
10
8
|
readonly chatUiManager: ChatUiManager;
|
|
11
|
-
readonly chatRunManager: ChatRunManager;
|
|
12
9
|
readonly chatSessionListManager: ChatSessionListManager;
|
|
13
|
-
readonly chatInputManager: ChatInputManager;
|
|
14
10
|
readonly chatQueryManager: ChatQueryManager;
|
|
15
11
|
readonly chatThreadManager: ChatThreadManager;
|
|
16
12
|
|
|
17
13
|
constructor(appPresenter: AppPresenter) {
|
|
18
14
|
this.chatUiManager = new ChatUiManager(appPresenter.docBrowserManager);
|
|
19
|
-
this.
|
|
20
|
-
this.chatSessionListManager = new ChatSessionListManager(this.chatUiManager, this.chatRunManager);
|
|
15
|
+
this.chatSessionListManager = new ChatSessionListManager(this.chatUiManager);
|
|
21
16
|
this.chatQueryManager = new ChatQueryManager();
|
|
22
|
-
this.chatInputManager = new ChatInputManager(
|
|
23
|
-
this.chatRunManager,
|
|
24
|
-
this.chatSessionListManager
|
|
25
|
-
);
|
|
26
17
|
this.chatThreadManager = new ChatThreadManager(
|
|
27
18
|
this.chatUiManager,
|
|
28
|
-
this.chatSessionListManager
|
|
29
|
-
this.chatRunManager
|
|
19
|
+
this.chatSessionListManager
|
|
30
20
|
);
|
|
31
21
|
}
|
|
32
|
-
|
|
33
|
-
startAgentCreationDraft = (prompt: string) => {
|
|
34
|
-
this.chatSessionListManager.createSession();
|
|
35
|
-
this.chatSessionListManager.setSelectedAgentId('main');
|
|
36
|
-
this.chatInputManager.setDraft(prompt);
|
|
37
|
-
this.chatInputManager.requestComposerFocusAtEnd();
|
|
38
|
-
};
|
|
39
22
|
}
|
|
@@ -22,6 +22,7 @@ function resetChatThreadStore() {
|
|
|
22
22
|
activeWorkspacePanelKind: null,
|
|
23
23
|
childSessionTabs: [],
|
|
24
24
|
activeChildSessionKey: null,
|
|
25
|
+
activeSideChatDraft: null,
|
|
25
26
|
workspaceFileTabs: [],
|
|
26
27
|
activeWorkspaceFileKey: null,
|
|
27
28
|
workspaceNavigationHistory: [],
|
|
@@ -85,6 +86,36 @@ describe('chat thread workspace panel persistence', () => {
|
|
|
85
86
|
expect(persisted.state.snapshot.workspaceFileTabs[0].fullLines).toBeUndefined();
|
|
86
87
|
});
|
|
87
88
|
|
|
89
|
+
it('does not persist an unmaterialized side chat draft', () => {
|
|
90
|
+
useChatThreadStore.getState().setSnapshot({
|
|
91
|
+
workspacePanelParentKey: 'session-1',
|
|
92
|
+
activeWorkspacePanelKind: 'side-chat-draft',
|
|
93
|
+
activeSideChatDraft: {
|
|
94
|
+
draftKey: 'draft-1',
|
|
95
|
+
parentSessionKey: 'session-1',
|
|
96
|
+
},
|
|
97
|
+
workspaceNavigationHistory: [
|
|
98
|
+
{ kind: 'child-session', key: 'child-session-1' },
|
|
99
|
+
{ kind: 'side-chat-draft', key: 'draft-1' },
|
|
100
|
+
],
|
|
101
|
+
workspaceNavigationHistoryIndex: 1,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const persisted = JSON.parse(
|
|
105
|
+
window.localStorage.getItem(chatThreadWorkspaceStorageKey) ?? '{}',
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(persisted.state.snapshot).toMatchObject({
|
|
109
|
+
workspacePanelParentKey: 'session-1',
|
|
110
|
+
activeWorkspacePanelKind: null,
|
|
111
|
+
workspaceNavigationHistory: [
|
|
112
|
+
{ kind: 'child-session', key: 'child-session-1' },
|
|
113
|
+
],
|
|
114
|
+
workspaceNavigationHistoryIndex: 0,
|
|
115
|
+
});
|
|
116
|
+
expect(persisted.state.snapshot.activeSideChatDraft).toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
|
|
88
119
|
it('hydrates the workspace panel state and repairs stale active file keys', async () => {
|
|
89
120
|
window.localStorage.setItem(
|
|
90
121
|
chatThreadWorkspaceStorageKey,
|
|
@@ -14,6 +14,11 @@ export type ChatChildSessionTab = {
|
|
|
14
14
|
agentId?: string | null;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
export type ChatWorkspaceSideChatDraft = {
|
|
18
|
+
draftKey: string;
|
|
19
|
+
parentSessionKey: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
17
22
|
export type ChatWorkspaceFileTab = {
|
|
18
23
|
key: string;
|
|
19
24
|
parentSessionKey: string | null;
|
|
@@ -32,17 +37,10 @@ export type ChatWorkspaceFileTab = {
|
|
|
32
37
|
};
|
|
33
38
|
|
|
34
39
|
export type ChatWorkspaceNavigationEntry =
|
|
35
|
-
| {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| {
|
|
40
|
-
kind: 'file';
|
|
41
|
-
key: string;
|
|
42
|
-
}
|
|
43
|
-
| {
|
|
44
|
-
kind: 'cron';
|
|
45
|
-
};
|
|
40
|
+
| { kind: 'child-session'; key: string }
|
|
41
|
+
| { kind: 'side-chat-draft'; key: string }
|
|
42
|
+
| { kind: 'file'; key: string }
|
|
43
|
+
| { kind: 'cron' };
|
|
46
44
|
|
|
47
45
|
export type ChatThreadSnapshot = {
|
|
48
46
|
sessionTypeLabel?: string | null;
|
|
@@ -63,9 +61,10 @@ export type ChatThreadSnapshot = {
|
|
|
63
61
|
parentSessionKey?: string | null;
|
|
64
62
|
parentSessionLabel?: string | null;
|
|
65
63
|
workspacePanelParentKey?: string | null;
|
|
66
|
-
activeWorkspacePanelKind?: "child-session" | "file" | "cron" | null;
|
|
64
|
+
activeWorkspacePanelKind?: "child-session" | "side-chat-draft" | "file" | "cron" | null;
|
|
67
65
|
childSessionTabs: ChatChildSessionTab[];
|
|
68
66
|
activeChildSessionKey?: string | null;
|
|
67
|
+
activeSideChatDraft?: ChatWorkspaceSideChatDraft | null;
|
|
69
68
|
workspaceFileTabs: ChatWorkspaceFileTab[];
|
|
70
69
|
activeWorkspaceFileKey?: string | null;
|
|
71
70
|
workspaceNavigationHistory: ChatWorkspaceNavigationEntry[];
|
|
@@ -105,6 +104,8 @@ type PersistedChatWorkspaceSnapshot = Pick<
|
|
|
105
104
|
| 'workspaceNavigationHistoryIndex'
|
|
106
105
|
>;
|
|
107
106
|
|
|
107
|
+
type PersistedWorkspacePanelKind = 'child-session' | 'file' | 'cron';
|
|
108
|
+
|
|
108
109
|
const initialSnapshot: ChatThreadSnapshot = {
|
|
109
110
|
sessionTypeLabel: null,
|
|
110
111
|
sessionTypeIcon: null,
|
|
@@ -127,6 +128,7 @@ const initialSnapshot: ChatThreadSnapshot = {
|
|
|
127
128
|
activeWorkspacePanelKind: null,
|
|
128
129
|
childSessionTabs: [],
|
|
129
130
|
activeChildSessionKey: null,
|
|
131
|
+
activeSideChatDraft: null,
|
|
130
132
|
workspaceFileTabs: [],
|
|
131
133
|
activeWorkspaceFileKey: null,
|
|
132
134
|
workspaceNavigationHistory: [],
|
|
@@ -158,7 +160,7 @@ function normalizeHistoryIndex(value: unknown, maxIndex: number): number {
|
|
|
158
160
|
|
|
159
161
|
function isWorkspacePanelKind(
|
|
160
162
|
value: unknown,
|
|
161
|
-
): value is
|
|
163
|
+
): value is PersistedWorkspacePanelKind {
|
|
162
164
|
return value === 'child-session' || value === 'file' || value === 'cron';
|
|
163
165
|
}
|
|
164
166
|
|
|
@@ -344,19 +346,33 @@ export const useChatThreadStore = create<ChatThreadStore>()(
|
|
|
344
346
|
name: CHAT_THREAD_WORKSPACE_STORAGE_KEY,
|
|
345
347
|
version: CHAT_THREAD_WORKSPACE_STORAGE_VERSION,
|
|
346
348
|
storage: createJSONStorage(() => window.localStorage),
|
|
347
|
-
partialize: (state): PersistedChatThreadStore =>
|
|
348
|
-
snapshot
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
partialize: (state): PersistedChatThreadStore => {
|
|
350
|
+
const workspaceNavigationHistory = state.snapshot.workspaceNavigationHistory
|
|
351
|
+
.filter((entry) => entry.kind !== 'side-chat-draft');
|
|
352
|
+
const workspaceNavigationHistoryIndex =
|
|
353
|
+
workspaceNavigationHistory.length > 0
|
|
354
|
+
? Math.min(
|
|
355
|
+
state.snapshot.workspaceNavigationHistoryIndex,
|
|
356
|
+
workspaceNavigationHistory.length - 1,
|
|
357
|
+
)
|
|
358
|
+
: 0;
|
|
359
|
+
return {
|
|
360
|
+
snapshot: {
|
|
361
|
+
workspacePanelParentKey: state.snapshot.workspacePanelParentKey,
|
|
362
|
+
activeWorkspacePanelKind:
|
|
363
|
+
state.snapshot.activeWorkspacePanelKind === 'side-chat-draft'
|
|
364
|
+
? null
|
|
365
|
+
: state.snapshot.activeWorkspacePanelKind,
|
|
366
|
+
activeChildSessionKey: state.snapshot.activeChildSessionKey,
|
|
367
|
+
workspaceFileTabs: state.snapshot.workspaceFileTabs
|
|
368
|
+
.slice(-CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS)
|
|
369
|
+
.map(toPersistedWorkspaceFileTab),
|
|
370
|
+
activeWorkspaceFileKey: state.snapshot.activeWorkspaceFileKey,
|
|
371
|
+
workspaceNavigationHistory,
|
|
372
|
+
workspaceNavigationHistoryIndex,
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
},
|
|
360
376
|
merge: (persistedState, currentState) => {
|
|
361
377
|
const persistedSnapshot = isRecord(persistedState)
|
|
362
378
|
? persistedState.snapshot
|
|
@@ -38,26 +38,26 @@ export function PanelAppListItem({
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<div className="group w-full min-w-0 rounded-lg border border-
|
|
41
|
+
<div className="group w-full min-w-0 rounded-lg border border-border bg-card px-2.5 py-2.5 transition-colors hover:border-primary/35 hover:bg-accent/60">
|
|
42
42
|
<div className="flex min-w-0 items-start gap-2">
|
|
43
43
|
<button type="button" onClick={onOpen} className="min-w-0 flex-1 text-left">
|
|
44
44
|
<span className="flex min-w-0 items-center gap-2">
|
|
45
|
-
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-
|
|
45
|
+
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-accent text-base text-accent-foreground">
|
|
46
46
|
<PanelAppIcon icon={entry.icon} title={entry.title} />
|
|
47
47
|
</span>
|
|
48
|
-
<span className="block min-w-0 flex-1 truncate text-sm font-medium text-
|
|
48
|
+
<span className="block min-w-0 flex-1 truncate text-sm font-medium text-foreground">{entry.title}</span>
|
|
49
49
|
</span>
|
|
50
50
|
{entry.description ? (
|
|
51
|
-
<span className="mt-1.5 block truncate text-xs leading-5 text-
|
|
51
|
+
<span className="mt-1.5 block truncate text-xs leading-5 text-muted-foreground">{entry.description}</span>
|
|
52
52
|
) : null}
|
|
53
|
-
<span className="mt-0.5 block truncate text-[11px] leading-4 text-
|
|
53
|
+
<span className="mt-0.5 block truncate text-[11px] leading-4 text-muted-foreground/70">{secondary}</span>
|
|
54
54
|
</button>
|
|
55
55
|
<div className="flex shrink-0 items-center gap-1">
|
|
56
56
|
<button
|
|
57
57
|
type="button"
|
|
58
58
|
onClick={handleFavorite}
|
|
59
59
|
disabled={favoritePending}
|
|
60
|
-
className="rounded-md p-1.5 text-
|
|
60
|
+
className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-background hover:text-amber-500 disabled:opacity-50"
|
|
61
61
|
title={favoriteLabel}
|
|
62
62
|
aria-label={favoriteLabel}
|
|
63
63
|
>
|
|
@@ -67,7 +67,7 @@ export function PanelAppListItem({
|
|
|
67
67
|
<PopoverTrigger asChild>
|
|
68
68
|
<button
|
|
69
69
|
type="button"
|
|
70
|
-
className="rounded-md p-1.5 text-
|
|
70
|
+
className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-background hover:text-foreground"
|
|
71
71
|
aria-label={t('panelAppsMoreActions')}
|
|
72
72
|
disabled={deletePending}
|
|
73
73
|
>
|
|
@@ -118,7 +118,7 @@ function PanelAppMenuItem({
|
|
|
118
118
|
type="button"
|
|
119
119
|
className={cn(
|
|
120
120
|
'flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50',
|
|
121
|
-
destructive ? 'text-destructive hover:bg-destructive/10' : 'text-
|
|
121
|
+
destructive ? 'text-destructive hover:bg-destructive/10' : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground',
|
|
122
122
|
)}
|
|
123
123
|
disabled={disabled}
|
|
124
124
|
onClick={onClick}
|
|
@@ -13,18 +13,18 @@ export function PanelAppToolbar({
|
|
|
13
13
|
onRefresh,
|
|
14
14
|
}: PanelAppToolbarProps) {
|
|
15
15
|
return (
|
|
16
|
-
<div className="flex items-center justify-between gap-2
|
|
16
|
+
<div className="flex items-center justify-between gap-2 border-b border-border/70 bg-card px-3.5 py-2 shrink-0">
|
|
17
17
|
<button
|
|
18
18
|
type="button"
|
|
19
19
|
onClick={onOpenApps}
|
|
20
|
-
className="inline-flex shrink-0 items-center rounded-md p-1.5 text-
|
|
20
|
+
className="inline-flex shrink-0 items-center rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
|
|
21
21
|
title={t('appsTitle')}
|
|
22
22
|
aria-label={t('appsTitle')}
|
|
23
23
|
>
|
|
24
24
|
<ArrowLeft className="h-3.5 w-3.5" />
|
|
25
25
|
</button>
|
|
26
26
|
<span
|
|
27
|
-
className="min-w-0 flex-1 truncate text-xs font-medium text-
|
|
27
|
+
className="min-w-0 flex-1 truncate text-xs font-medium text-muted-foreground"
|
|
28
28
|
title={appTitle}
|
|
29
29
|
>
|
|
30
30
|
{appTitle}
|
|
@@ -32,7 +32,7 @@ export function PanelAppToolbar({
|
|
|
32
32
|
<button
|
|
33
33
|
type="button"
|
|
34
34
|
onClick={onRefresh}
|
|
35
|
-
className="rounded-md p-1.5 text-
|
|
35
|
+
className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
|
|
36
36
|
title={t('panelAppsRefreshCurrent')}
|
|
37
37
|
aria-label={t('panelAppsRefreshCurrent')}
|
|
38
38
|
>
|
|
@@ -83,7 +83,7 @@ export function PanelAppsList({
|
|
|
83
83
|
|
|
84
84
|
if (panelApps.isLoading) {
|
|
85
85
|
return (
|
|
86
|
-
<div className="flex h-full items-center justify-center text-sm text-
|
|
86
|
+
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">{t('panelAppsLoading')}</div>
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -94,17 +94,17 @@ export function PanelAppsList({
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
return (
|
|
97
|
-
<div className="flex h-full min-h-0 flex-col bg-
|
|
98
|
-
<div className="flex items-center justify-between gap-2 border-b border-
|
|
97
|
+
<div className="flex h-full min-h-0 flex-col bg-card text-card-foreground">
|
|
98
|
+
<div className="flex items-center justify-between gap-2 border-b border-border/70 px-4 py-3">
|
|
99
99
|
<div className="flex min-w-0 items-center gap-1.5">
|
|
100
100
|
{headerContent ?? (
|
|
101
|
-
<div className="truncate text-sm font-semibold text-
|
|
101
|
+
<div className="truncate text-sm font-semibold text-foreground">{t('panelAppsTitle')}</div>
|
|
102
102
|
)}
|
|
103
103
|
{panelApps.data?.panelsPath ? (
|
|
104
104
|
<TooltipProvider delayDuration={250}>
|
|
105
105
|
<Tooltip>
|
|
106
106
|
<TooltipTrigger asChild>
|
|
107
|
-
<button type="button" className="rounded p-0.5 text-
|
|
107
|
+
<button type="button" className="rounded p-0.5 text-muted-foreground/70 transition-colors hover:bg-accent hover:text-accent-foreground" aria-label={t('panelAppsTitle')}><HelpCircle className="h-3.5 w-3.5" /></button>
|
|
108
108
|
</TooltipTrigger>
|
|
109
109
|
<TooltipContent side="bottom" className="max-w-[320px] break-all font-mono text-xs">{panelApps.data.panelsPath}</TooltipContent>
|
|
110
110
|
</Tooltip>
|
|
@@ -114,16 +114,16 @@ export function PanelAppsList({
|
|
|
114
114
|
<button
|
|
115
115
|
type="button"
|
|
116
116
|
onClick={() => void panelApps.refetch()}
|
|
117
|
-
className="rounded-md p-1.5 text-
|
|
117
|
+
className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
|
|
118
118
|
title={t('panelAppsRefresh')}
|
|
119
119
|
aria-label={t('panelAppsRefresh')}
|
|
120
120
|
>
|
|
121
121
|
<RefreshCw className="h-3.5 w-3.5" />
|
|
122
122
|
</button>
|
|
123
123
|
</div>
|
|
124
|
-
<div className="border-b border-
|
|
124
|
+
<div className="border-b border-border/70 px-3 py-2">
|
|
125
125
|
<Tabs value={viewMode} onValueChange={(value) => setViewMode(value as PanelAppViewMode)}>
|
|
126
|
-
<TabsList className="grid h-auto w-full grid-cols-5 rounded-lg bg-
|
|
126
|
+
<TabsList className="grid h-auto w-full grid-cols-5 rounded-lg bg-muted/70 p-0.5">
|
|
127
127
|
<TabsTrigger value="smart" className="px-2 py-1 text-xs">{t('panelAppsSortSmart')}</TabsTrigger>
|
|
128
128
|
<TabsTrigger value="favorites" className="px-2 py-1 text-xs">{t('panelAppsFavorites')}</TabsTrigger>
|
|
129
129
|
<TabsTrigger value="recent-open" className="px-2 py-1 text-xs">{t('panelAppsSortRecentOpen')}</TabsTrigger>
|
|
@@ -139,7 +139,7 @@ export function PanelAppsList({
|
|
|
139
139
|
onRefresh={() => void panelApps.refetch()}
|
|
140
140
|
/>
|
|
141
141
|
) : entries.length === 0 ? (
|
|
142
|
-
<div className="flex flex-1 items-center justify-center px-6 text-center text-sm text-
|
|
142
|
+
<div className="flex flex-1 items-center justify-center px-6 text-center text-sm text-muted-foreground">{t('panelAppsEmptyFiltered')}</div>
|
|
143
143
|
) : (
|
|
144
144
|
<div className="custom-scrollbar min-h-0 flex-1 overflow-y-auto p-3">
|
|
145
145
|
<div className="space-y-1.5">
|
|
@@ -176,8 +176,8 @@ function PanelAppsEmptyGuide({
|
|
|
176
176
|
<div className="mx-auto flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
177
177
|
<AppWindow className="h-5 w-5" />
|
|
178
178
|
</div>
|
|
179
|
-
<h2 className="mt-3 text-sm font-semibold text-
|
|
180
|
-
<p className="mt-1 text-xs leading-5 text-
|
|
179
|
+
<h2 className="mt-3 text-sm font-semibold text-foreground">{t('panelAppsEmptyTitle')}</h2>
|
|
180
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{t('panelAppsEmptyDescription')}</p>
|
|
181
181
|
|
|
182
182
|
<div className="mt-4 space-y-2 text-left">
|
|
183
183
|
<PanelAppsEmptyGuideStep
|
|
@@ -195,16 +195,16 @@ function PanelAppsEmptyGuide({
|
|
|
195
195
|
</div>
|
|
196
196
|
|
|
197
197
|
{panelsPath ? (
|
|
198
|
-
<div className="mt-4 rounded-md bg-
|
|
199
|
-
<div className="text-[11px] font-medium uppercase text-
|
|
200
|
-
<code className="mt-1 block break-all text-xs text-
|
|
198
|
+
<div className="mt-4 rounded-md bg-muted/60 px-3 py-2 text-left">
|
|
199
|
+
<div className="text-[11px] font-medium uppercase text-muted-foreground/70">{t('panelAppsPanelsPath')}</div>
|
|
200
|
+
<code className="mt-1 block break-all text-xs text-muted-foreground">{panelsPath}</code>
|
|
201
201
|
</div>
|
|
202
202
|
) : null}
|
|
203
203
|
|
|
204
204
|
<button
|
|
205
205
|
type="button"
|
|
206
206
|
onClick={onRefresh}
|
|
207
|
-
className="mt-4 inline-flex items-center justify-center gap-1.5 rounded-md border border-
|
|
207
|
+
className="mt-4 inline-flex items-center justify-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
|
|
208
208
|
>
|
|
209
209
|
<RefreshCw className="h-3.5 w-3.5" />
|
|
210
210
|
{t('panelAppsRefresh')}
|
|
@@ -229,10 +229,10 @@ function PanelAppsEmptyGuideStep({
|
|
|
229
229
|
}) {
|
|
230
230
|
const content = (
|
|
231
231
|
<>
|
|
232
|
-
<div className="mt-0.5 text-
|
|
232
|
+
<div className="mt-0.5 text-muted-foreground">{icon}</div>
|
|
233
233
|
<div className="min-w-0">
|
|
234
|
-
<div className="text-xs font-medium text-
|
|
235
|
-
<div className="mt-0.5 text-xs leading-5 text-
|
|
234
|
+
<div className="text-xs font-medium text-foreground">{title}</div>
|
|
235
|
+
<div className="mt-0.5 text-xs leading-5 text-muted-foreground">{description}</div>
|
|
236
236
|
{actionLabel ? (
|
|
237
237
|
<div className="mt-2 text-xs font-medium text-primary">{actionLabel}</div>
|
|
238
238
|
) : null}
|
|
@@ -245,7 +245,7 @@ function PanelAppsEmptyGuideStep({
|
|
|
245
245
|
<button
|
|
246
246
|
type="button"
|
|
247
247
|
onClick={onAction}
|
|
248
|
-
className="flex w-full gap-2 rounded-md bg-
|
|
248
|
+
className="flex w-full gap-2 rounded-md bg-muted/60 px-3 py-2.5 text-left transition-colors hover:bg-accent/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
|
|
249
249
|
>
|
|
250
250
|
{content}
|
|
251
251
|
</button>
|
|
@@ -253,7 +253,7 @@ function PanelAppsEmptyGuideStep({
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
return (
|
|
256
|
-
<div className="flex gap-2 rounded-md bg-
|
|
256
|
+
<div className="flex gap-2 rounded-md bg-muted/60 px-3 py-2.5">
|
|
257
257
|
{content}
|
|
258
258
|
</div>
|
|
259
259
|
);
|
|
@@ -4,10 +4,11 @@ import type { PanelAppPreferencesUpdateView } from '@/shared/lib/api';
|
|
|
4
4
|
|
|
5
5
|
const PANEL_APPS_QUERY_KEY = ['panel-apps'] as const;
|
|
6
6
|
|
|
7
|
-
export function usePanelApps() {
|
|
7
|
+
export function usePanelApps(options: { enabled?: boolean } = {}) {
|
|
8
8
|
return useQuery({
|
|
9
9
|
queryKey: PANEL_APPS_QUERY_KEY,
|
|
10
10
|
queryFn: () => nextclawClient.panelApps.listPanelApps(),
|
|
11
|
+
enabled: options.enabled ?? true,
|
|
11
12
|
staleTime: 0,
|
|
12
13
|
});
|
|
13
14
|
}
|