@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.
Files changed (224) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/{api-DPcvCLfa.js → api-BP_8IaxB.js} +7 -7
  3. package/dist/assets/channels-list-page-DEh5Jjqx.js +8 -0
  4. package/dist/assets/chat-page-BRB0N5WW.js +105 -0
  5. package/dist/assets/config-split-page-CRhlZ_pH.js +1 -0
  6. package/dist/assets/{confirm-dialog-CWh5FfK2.js → confirm-dialog-DsivFJ4r.js} +2 -2
  7. package/dist/assets/{desktop-update-config-DsRhBizi.js → desktop-update-config-B0w9p2ep.js} +1 -1
  8. package/dist/assets/{dist-DPrgcEC0.js → dist-CikkhOlq.js} +1 -1
  9. package/dist/assets/{dist-DsE4OsSS.js → dist-PVA_xL1D.js} +1 -1
  10. package/dist/assets/doc-browser-BoNJ7-tE.js +1 -0
  11. package/dist/assets/doc-browser-CdJ_d4ik.js +1 -0
  12. package/dist/assets/doc-browser-context-MTw4EvKd.js +1 -0
  13. package/dist/assets/{ellipsis-B81FStLm.js → ellipsis-y6vDtq4M.js} +1 -1
  14. package/dist/assets/{external-link-C_dmG-WJ.js → external-link-DVJIQf_G.js} +1 -1
  15. package/dist/assets/index-BiE-K8-q.css +1 -0
  16. package/dist/assets/index-D9BAE_L_.js +105 -0
  17. package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +40 -0
  18. package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +1 -0
  19. package/dist/assets/middleware-CkFT3R4v.js +1 -0
  20. package/dist/assets/model-config-page-DotaTUHb.js +1 -0
  21. package/dist/assets/plus-BbZqquJe.js +1 -0
  22. package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +1 -0
  23. package/dist/assets/providers-config-page-DW4tQSLN.js +1 -0
  24. package/dist/assets/{react-sZz5HSCm.js → react-VNqT5Zp7.js} +1 -1
  25. package/dist/assets/remote-B8TiUfwW.js +1 -0
  26. package/dist/assets/rotate-cw-DjHDTkCv.js +1 -0
  27. package/dist/assets/runtime-config-page-S6ErRHR8.js +1 -0
  28. package/dist/assets/{save-MzjoiHri.js → save-BOJ477H-.js} +1 -1
  29. package/dist/assets/{search-B29dE4GN.js → search-BEti5beA.js} +1 -1
  30. package/dist/assets/{search-config-page-Dl5sZzob.js → search-config-page-DGj5am03.js} +1 -1
  31. package/dist/assets/{secrets-config-page-C_l-IFCK.js → secrets-config-page--IzrIOid.js} +2 -2
  32. package/dist/assets/{select-BKmh05hd.js → select-hoj8dK4K.js} +2 -2
  33. package/dist/assets/{tag-chip-BS_7Ueom.js → tag-chip-T8-h-die.js} +1 -1
  34. package/dist/assets/use-config-CjRV2hwL.js +1 -0
  35. package/dist/assets/x-D9PXs_xP.js +1 -0
  36. package/dist/index.html +70 -19
  37. package/index.html +55 -5
  38. package/package.json +9 -9
  39. package/src/app/components/app-presenter-provider.tsx +11 -2
  40. package/src/app/components/layout/__tests__/app-layout.test.tsx +11 -2
  41. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +110 -79
  42. package/src/app/components/layout/app-layout.tsx +18 -4
  43. package/src/app/components/layout/header.tsx +3 -3
  44. package/src/app/components/layout/page-layout.tsx +2 -2
  45. package/src/app/components/layout/runtime-status-entry.tsx +7 -7
  46. package/src/app/components/layout/sidebar-items.tsx +194 -71
  47. package/src/app/components/layout/sidebar-rail.styles.ts +15 -0
  48. package/src/app/components/layout/sidebar.tsx +340 -83
  49. package/src/app/managers/viewport-layout.manager.ts +10 -0
  50. package/src/app/stores/viewport-layout.store.ts +39 -3
  51. package/src/app/styles/design-system.css +388 -26
  52. package/src/app/test/vitest-setup.ts +31 -0
  53. package/src/features/agents/components/__tests__/agents-page.test.tsx +62 -39
  54. package/src/features/agents/components/agents-page.tsx +10 -6
  55. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +3 -3
  56. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +145 -0
  57. package/src/features/chat/components/conversation/__tests__/chat-conversation-header.test.tsx +4 -2
  58. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +69 -762
  59. package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +132 -0
  60. package/src/features/chat/components/conversation/chat-conversation-content.tsx +22 -15
  61. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +15 -16
  62. package/src/features/chat/components/conversation/chat-conversation-header.tsx +7 -6
  63. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +19 -19
  64. package/src/features/chat/components/conversation/chat-conversation-skeleton.tsx +13 -13
  65. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +10 -4
  66. package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +52 -11
  67. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +18 -16
  68. package/src/features/chat/components/layout/chat-page-shell.tsx +1 -1
  69. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +247 -0
  70. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +93 -20
  71. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +93 -36
  72. package/src/features/chat/components/layout/chat-sidebar.tsx +60 -93
  73. package/src/features/chat/components/providers/chat-presenter.provider.tsx +0 -6
  74. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +225 -0
  75. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +199 -0
  76. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +308 -0
  77. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +439 -0
  78. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +194 -0
  79. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +66 -0
  80. package/src/features/chat/features/{input/hooks/use-chat-input-bar-query-state.ts → conversation/hooks/use-session-conversation-input-query.ts} +51 -20
  81. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +281 -0
  82. package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +158 -0
  83. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +56 -0
  84. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +117 -0
  85. package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +77 -0
  86. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -0
  87. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +10 -0
  88. package/src/features/chat/features/input/input-surface-plugins/input-surface-search.utils.ts +108 -0
  89. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +108 -0
  90. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +161 -0
  91. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
  92. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +26 -13
  93. package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +7 -28
  94. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +6 -0
  95. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +26 -51
  96. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +2 -0
  97. package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +5 -6
  98. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +47 -12
  99. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +7 -7
  100. package/src/features/chat/features/message/components/chat-message-list.container.tsx +99 -18
  101. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +31 -12
  102. package/src/features/chat/features/message/utils/chat-message-markdown-part.utils.ts +38 -0
  103. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +1 -1
  104. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +7 -7
  105. package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +6 -6
  106. package/src/features/chat/features/session/components/session-context-icon.tsx +2 -2
  107. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-badge.test.tsx +2 -2
  108. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +1 -1
  109. package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +1 -1
  110. package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
  111. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -3
  112. package/src/features/chat/features/session/components/session-run-badge.tsx +1 -1
  113. package/src/features/chat/features/session/hooks/__tests__/use-chat-session-project.test.tsx +3 -30
  114. package/src/features/chat/features/session/hooks/use-chat-session-project.ts +0 -5
  115. package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +8 -0
  116. package/src/features/chat/features/session-type/components/__tests__/chat-session-type-menu.test.tsx +2 -2
  117. package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +2 -0
  118. package/src/features/chat/features/session-type/components/chat-session-type-menu.tsx +2 -2
  119. package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +2 -2
  120. package/src/features/chat/features/session-type/hooks/use-chat-session-type-state.ts +10 -2
  121. package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +49 -31
  122. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +2 -9
  123. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +21 -10
  124. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +3 -3
  125. package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +4 -4
  126. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +14 -19
  127. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +5 -5
  128. package/src/features/chat/features/welcome/components/chat-welcome.tsx +6 -6
  129. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +6 -8
  130. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +1 -16
  131. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +54 -0
  132. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +44 -76
  133. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
  134. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +7 -1
  135. package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +40 -31
  136. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +59 -1
  137. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +109 -0
  138. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +44 -4
  139. package/src/features/chat/index.ts +0 -1
  140. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +51 -99
  141. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +135 -21
  142. package/src/features/chat/managers/chat-session-list.manager.ts +12 -43
  143. package/src/features/chat/managers/chat-thread.manager.ts +108 -34
  144. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +48 -1
  145. package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +72 -0
  146. package/src/features/chat/pages/ncp-chat-page.tsx +0 -122
  147. package/src/features/chat/presenters/chat.presenter.ts +2 -19
  148. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +31 -0
  149. package/src/features/chat/stores/chat-thread.store.ts +42 -26
  150. package/src/features/panel-apps/components/panel-app-list-item.tsx +8 -8
  151. package/src/features/panel-apps/components/panel-app-toolbar.tsx +4 -4
  152. package/src/features/panel-apps/components/panel-apps-list.tsx +20 -20
  153. package/src/features/panel-apps/hooks/use-panel-apps.ts +2 -1
  154. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +70 -6
  155. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +10 -5
  156. package/src/features/service-apps/components/service-action-authorization-dialog.tsx +8 -8
  157. package/src/features/service-apps/components/service-apps-panel.tsx +24 -24
  158. package/src/features/settings/components/config/provider-advanced-settings-section.tsx +5 -5
  159. package/src/features/settings/components/config/provider-auth-section.tsx +7 -7
  160. package/src/features/settings/components/config/provider-form.tsx +10 -10
  161. package/src/features/settings/components/config/provider-models-section.tsx +16 -16
  162. package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
  163. package/src/features/settings/pages/providers-config-page.tsx +17 -17
  164. package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +50 -10
  165. package/src/platforms/desktop/components/desktop-app-shell.tsx +34 -10
  166. package/src/platforms/desktop/components/desktop-window-chrome.tsx +41 -16
  167. package/src/shared/components/config-split-page.tsx +11 -11
  168. package/src/shared/components/doc-browser/doc-browser-home-page.tsx +2 -2
  169. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -4
  170. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +4 -4
  171. package/src/shared/components/doc-browser/doc-browser.tsx +3 -3
  172. package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +1 -1
  173. package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
  174. package/src/shared/components/ui/button.tsx +4 -4
  175. package/src/shared/components/ui/card.tsx +4 -4
  176. package/src/shared/components/ui/dialog.tsx +5 -5
  177. package/src/shared/components/ui/input.tsx +1 -1
  178. package/src/shared/components/ui/label.tsx +1 -1
  179. package/src/shared/components/ui/popover.tsx +1 -1
  180. package/src/shared/components/ui/select.tsx +3 -3
  181. package/src/shared/components/ui/switch.tsx +3 -3
  182. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +24 -1
  183. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -4
  184. package/src/shared/components/ui/tabs.tsx +4 -4
  185. package/src/shared/components/ui/textarea.tsx +1 -1
  186. package/src/shared/lib/i18n/locales/en-US/chat.json +20 -2
  187. package/src/shared/lib/i18n/locales/en-US/core.json +9 -2
  188. package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -2
  189. package/src/shared/lib/i18n/locales/zh-CN/core.json +9 -2
  190. package/src/shared/lib/theme/index.ts +94 -52
  191. package/src/shared/lib/ui-document-title/__tests__/ui-document-title.test.ts +49 -0
  192. package/src/shared/lib/ui-document-title/index.ts +50 -21
  193. package/dist/assets/channels-list-page-D6jtziu4.js +0 -8
  194. package/dist/assets/chat-page-BBnNJoKG.js +0 -105
  195. package/dist/assets/config-split-page-BCJhqdK9.js +0 -1
  196. package/dist/assets/doc-browser-C8xOF_9F.js +0 -1
  197. package/dist/assets/doc-browser-DgQedeAn.js +0 -1
  198. package/dist/assets/doc-browser-context-B46tQGmO.js +0 -1
  199. package/dist/assets/index-7o1WdUbU.js +0 -103
  200. package/dist/assets/index-DQWmEWzk.css +0 -1
  201. package/dist/assets/mcp-marketplace-page-CWZN7ohK.js +0 -1
  202. package/dist/assets/mcp-marketplace-page-Dzull_4-.js +0 -40
  203. package/dist/assets/model-config-page-Cv0O3BIj.js +0 -1
  204. package/dist/assets/plus-Bnt56oqG.js +0 -1
  205. package/dist/assets/provider-scoped-model-input-Duuh7i3h.js +0 -1
  206. package/dist/assets/providers-config-page-CPDz0Lxc.js +0 -1
  207. package/dist/assets/remote-B0LMs8u7.js +0 -1
  208. package/dist/assets/rotate-cw-BfyG_KEF.js +0 -1
  209. package/dist/assets/runtime-config-page-q0fvv8ME.js +0 -1
  210. package/dist/assets/use-config-DZkBWIdL.js +0 -1
  211. package/dist/assets/x-CW2KO-Hz.js +0 -1
  212. package/src/features/chat/components/conversation/chat-conversation-alerts.tsx +0 -37
  213. package/src/features/chat/features/input/components/__tests__/chat-attachment-upload-limit.test.ts +0 -38
  214. package/src/features/chat/features/input/components/chat-input-bar.container.tsx +0 -496
  215. package/src/features/chat/features/message/utils/chat-message-inline-content.utils.ts +0 -95
  216. package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +0 -448
  217. package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +0 -129
  218. package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +0 -236
  219. package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +0 -219
  220. package/src/features/chat/managers/chat-input.manager.ts +0 -430
  221. package/src/features/chat/managers/chat-run.manager.ts +0 -110
  222. package/src/features/chat/managers/chat-session-preference-sync.manager.ts +0 -142
  223. package/src/features/chat/stores/chat-input.store.ts +0 -104
  224. /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.chatRunManager = new ChatRunManager(this.chatUiManager);
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
- kind: 'child-session';
37
- key: string;
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 NonNullable<ChatThreadSnapshot['activeWorkspacePanelKind']> {
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
- workspacePanelParentKey: state.snapshot.workspacePanelParentKey,
350
- activeWorkspacePanelKind: state.snapshot.activeWorkspacePanelKind,
351
- activeChildSessionKey: state.snapshot.activeChildSessionKey,
352
- workspaceFileTabs: state.snapshot.workspaceFileTabs
353
- .slice(-CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS)
354
- .map(toPersistedWorkspaceFileTab),
355
- activeWorkspaceFileKey: state.snapshot.activeWorkspaceFileKey,
356
- workspaceNavigationHistory: state.snapshot.workspaceNavigationHistory,
357
- workspaceNavigationHistoryIndex: state.snapshot.workspaceNavigationHistoryIndex,
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-gray-200 bg-white px-2.5 py-2.5 transition-colors hover:border-amber-200 hover:bg-amber-50/50">
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-amber-50 text-base text-amber-700">
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-gray-900">{entry.title}</span>
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-gray-600">{entry.description}</span>
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-gray-400">{secondary}</span>
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-gray-400 transition-colors hover:bg-white hover:text-amber-500 disabled:opacity-50"
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-gray-400 transition-colors hover:bg-white hover:text-gray-700"
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-gray-700 hover:bg-gray-100',
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 px-3.5 py-2 bg-white border-b border-gray-100 shrink-0">
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-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900"
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-gray-600"
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-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-800"
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-gray-500">{t('panelAppsLoading')}</div>
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-white">
98
- <div className="flex items-center justify-between gap-2 border-b border-gray-100 px-4 py-3">
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-gray-900">{t('panelAppsTitle')}</div>
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-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-700" aria-label={t('panelAppsTitle')}><HelpCircle className="h-3.5 w-3.5" /></button>
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-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-800"
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-gray-100 px-3 py-2">
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-gray-100/70 p-0.5">
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-gray-500">{t('panelAppsEmptyFiltered')}</div>
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-gray-900">{t('panelAppsEmptyTitle')}</h2>
180
- <p className="mt-1 text-xs leading-5 text-gray-500">{t('panelAppsEmptyDescription')}</p>
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-gray-50 px-3 py-2 text-left">
199
- <div className="text-[11px] font-medium uppercase text-gray-400">{t('panelAppsPanelsPath')}</div>
200
- <code className="mt-1 block break-all text-xs text-gray-600">{panelsPath}</code>
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-gray-200 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
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-gray-500">{icon}</div>
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-gray-800">{title}</div>
235
- <div className="mt-0.5 text-xs leading-5 text-gray-500">{description}</div>
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-gray-50 px-3 py-2.5 text-left transition-colors hover:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
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-gray-50 px-3 py-2.5">
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
  }