@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
@@ -432,7 +432,7 @@ it("maps file parts into previewable attachment view models", () => {
432
432
  });
433
433
  });
434
434
 
435
- it("renders inline skill tokens as structured inline content parts", () => {
435
+ it("attaches inline skill tokens to markdown parts", () => {
436
436
  const adapted = adapt([
437
437
  {
438
438
  id: "user-inline-skill",
@@ -452,19 +452,38 @@ it("renders inline skill tokens as structured inline content parts", () => {
452
452
  ] as unknown as ChatMessageSource[]);
453
453
 
454
454
  expect(adapted[0]?.parts[0]).toEqual({
455
- type: "inline-content",
456
- segments: [
457
- { type: "markdown", text: "please use " },
455
+ type: "markdown",
456
+ text: "please use $weather now",
457
+ inlineTokens: [
458
458
  {
459
- type: "token",
460
- token: {
461
- kind: "skill",
462
- key: "weather",
463
- label: "Weather",
464
- rawText: "$weather",
465
- },
459
+ kind: "skill",
460
+ key: "weather",
461
+ label: "Weather",
462
+ rawText: "$weather",
463
+ },
464
+ ],
465
+ });
466
+ });
467
+
468
+ it("attaches inline panel app text protocol tokens without metadata", () => {
469
+ const adapted = adapt([
470
+ {
471
+ id: "user-inline-panel-app",
472
+ role: "user",
473
+ parts: [{ type: "text", text: "review @panel-app:task-board now" }],
474
+ },
475
+ ] as unknown as ChatMessageSource[]);
476
+
477
+ expect(adapted[0]?.parts[0]).toEqual({
478
+ type: "markdown",
479
+ text: "review @panel-app:task-board now",
480
+ inlineTokens: [
481
+ {
482
+ kind: "panel_app",
483
+ key: "task-board",
484
+ label: "task-board",
485
+ rawText: "@panel-app:task-board",
466
486
  },
467
- { type: "markdown", text: " now" },
468
487
  ],
469
488
  });
470
489
  });
@@ -0,0 +1,38 @@
1
+ import {
2
+ resolveInlineTokensForText,
3
+ type ChatInlineTokenSource,
4
+ } from "@/features/chat/features/input/utils/chat-inline-token.utils";
5
+ import type { ChatMessagePartViewModel } from "@nextclaw/agent-chat-ui";
6
+ import type { ChatMessagePartSource } from "./chat-message-part.utils";
7
+
8
+ const INVISIBLE_ONLY_TEXT_PATTERN = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
9
+
10
+ function toRenderableText(value: string): string | null {
11
+ const trimmed = value.trim();
12
+ if (!trimmed) {
13
+ return null;
14
+ }
15
+ const visible = trimmed.replace(INVISIBLE_ONLY_TEXT_PATTERN, "").trim();
16
+ return visible ? trimmed : null;
17
+ }
18
+
19
+ export function buildTextPart(
20
+ part: Extract<ChatMessagePartSource, { type: "text" }>,
21
+ inlineTokens: readonly ChatInlineTokenSource[],
22
+ ): Extract<ChatMessagePartViewModel, { type: "markdown" }> | null {
23
+ const text = toRenderableText(part.text);
24
+ if (!text) {
25
+ return null;
26
+ }
27
+ const resolvedInlineTokens = resolveInlineTokensForText(part.text, inlineTokens);
28
+ return {
29
+ type: "markdown",
30
+ text,
31
+ inlineTokens:
32
+ resolvedInlineTokens.length > 0 ? resolvedInlineTokens : undefined,
33
+ };
34
+ }
35
+
36
+ export function buildRenderableText(value: string): string | null {
37
+ return toRenderableText(value);
38
+ }
@@ -6,7 +6,7 @@ import { type ChatInlineTokenSource } from "@/features/chat/features/input/utils
6
6
  import {
7
7
  buildRenderableText,
8
8
  buildTextPart,
9
- } from "./chat-message-inline-content.utils";
9
+ } from "./chat-message-markdown-part.utils";
10
10
  import {
11
11
  buildToolCard,
12
12
  buildToolInvocationInput,
@@ -3,14 +3,14 @@ import { Plus } from 'lucide-react';
3
3
  import { ChatSessionTypeMenu } from "@/features/chat/features/session-type/components/chat-session-type-menu";
4
4
  import { Popover, PopoverContent, PopoverTrigger } from '@/shared/components/ui/popover';
5
5
  import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
6
- import type { ChatInputSnapshot } from '@/features/chat/stores/chat-input.store';
6
+ import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
7
7
  import type { NcpSessionListItemView } from '@/features/chat/features/ncp/hooks/use-ncp-session-list-view';
8
8
  import type { ChatSidebarProjectGroup } from '@/features/chat/features/session/utils/chat-sidebar-session-groups.utils';
9
9
  import { t } from '@/shared/lib/i18n';
10
10
 
11
11
  export type { ChatSidebarProjectGroup };
12
12
 
13
- type SessionTypeOption = ChatInputSnapshot['sessionTypeOptions'][number];
13
+ type SessionTypeOption = ChatSessionTypeOption;
14
14
 
15
15
  type ChatSidebarProjectGroupsProps = {
16
16
  groups: ChatSidebarProjectGroup[];
@@ -49,12 +49,12 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
49
49
  <div className="flex items-center justify-between gap-2 px-2 py-0.5">
50
50
  <div className="flex min-w-0 items-center gap-1.5">
51
51
  <div
52
- className="truncate text-[11px] font-medium uppercase tracking-wider text-gray-500"
52
+ className="truncate text-[11px] font-medium uppercase tracking-wider text-muted-foreground"
53
53
  title={group.projectRoot}
54
54
  >
55
55
  {group.projectName}
56
56
  </div>
57
- <span className="shrink-0 text-[10px] text-gray-400">
57
+ <span className="shrink-0 text-[10px] text-muted-foreground/70">
58
58
  {group.items.length}
59
59
  </span>
60
60
  </div>
@@ -70,12 +70,12 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
70
70
  icon={<Plus className="h-3.5 w-3.5" />}
71
71
  label={actionLabel}
72
72
  tooltip={false}
73
- className="h-7 w-7 shrink-0 rounded-lg text-gray-400 hover:bg-white hover:text-gray-900"
73
+ className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-accent hover:text-accent-foreground"
74
74
  />
75
75
  </PopoverTrigger>
76
76
  <PopoverContent
77
77
  align="end"
78
- className="w-56 rounded-2xl border border-gray-200/80 bg-white p-1.5 shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
78
+ className="w-56 rounded-2xl border border-border bg-popover p-1.5 text-popover-foreground shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
79
79
  >
80
80
  <ChatSessionTypeMenu
81
81
  options={sessionTypeOptions}
@@ -91,7 +91,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
91
91
  <IconActionButton
92
92
  icon={<Plus className="h-3.5 w-3.5" />}
93
93
  label={actionLabel}
94
- className="h-7 w-7 shrink-0 rounded-lg text-gray-400 hover:bg-white hover:text-gray-900"
94
+ className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-accent hover:text-accent-foreground"
95
95
  onClick={() => onCreateSession(preferredSessionType, group.projectRoot)}
96
96
  />
97
97
  )}
@@ -6,10 +6,10 @@ import type {
6
6
  ChatSidebarProjectGroup,
7
7
  } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
8
8
  import { ChatSidebarProjectGroups } from "@/features/chat/features/session/components/chat-sidebar-project-groups";
9
- import type { ChatInputSnapshot } from "@/features/chat/stores/chat-input.store";
9
+ import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
10
10
  import { t } from "@/shared/lib/i18n";
11
11
 
12
- type SessionTypeOption = ChatInputSnapshot["sessionTypeOptions"][number];
12
+ type SessionTypeOption = ChatSessionTypeOption;
13
13
 
14
14
  type ChatSidebarSessionListProps = {
15
15
  isLoading: boolean;
@@ -25,8 +25,8 @@ type ChatSidebarSessionListProps = {
25
25
  function ChatSidebarEmptyState({ label }: { label: string }) {
26
26
  return (
27
27
  <div className="p-4 text-center">
28
- <MessageSquareText className="mx-auto mb-2 h-6 w-6 text-gray-300" />
29
- <div className="text-xs text-gray-500">{label}</div>
28
+ <MessageSquareText className="mx-auto mb-2 h-6 w-6 text-muted-foreground/45" />
29
+ <div className="text-xs text-muted-foreground">{label}</div>
30
30
  </div>
31
31
  );
32
32
  }
@@ -42,7 +42,7 @@ export function ChatSidebarSessionList({
42
42
  sessionTypeOptions,
43
43
  }: ChatSidebarSessionListProps) {
44
44
  if (isLoading) {
45
- return <div className="p-3 text-xs text-gray-500">{t("sessionsLoading")}</div>;
45
+ return <div className="p-3 text-xs text-muted-foreground">{t("sessionsLoading")}</div>;
46
46
  }
47
47
 
48
48
  if (isProjectFirstView) {
@@ -67,7 +67,7 @@ export function ChatSidebarSessionList({
67
67
  <div className="space-y-3">
68
68
  {groups.map((group) => (
69
69
  <div key={group.label}>
70
- <div className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-gray-400">
70
+ <div className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70">
71
71
  {group.label}
72
72
  </div>
73
73
  <div className="space-y-0.5">{group.items.map(renderSessionItem)}</div>
@@ -17,7 +17,7 @@ export function SessionContextIconNode({ icon, className }: { icon: SessionConte
17
17
  src={runtimeIconSrc ?? undefined}
18
18
  className={cn('h-[1.125rem] w-[1.125rem]', className)}
19
19
  imgClassName="h-full w-full object-contain"
20
- fallback={<Bot className={cn('h-3 w-3 text-gray-500', className)} />}
20
+ fallback={<Bot className={cn('h-3 w-3 text-muted-foreground', className)} />}
21
21
  />
22
22
  );
23
23
  }
@@ -34,7 +34,7 @@ function ChannelLogoIcon(
34
34
  src={logoSrc}
35
35
  className={cn('h-[1.125rem] w-[1.125rem]', className)}
36
36
  imgClassName="h-full w-full object-contain"
37
- fallback={<Bot className="h-3 w-3 text-gray-500" />}
37
+ fallback={<Bot className="h-3 w-3 text-muted-foreground" />}
38
38
  />
39
39
  );
40
40
  }
@@ -58,8 +58,8 @@ describe('ChatSessionProjectBadge', () => {
58
58
  renderProjectBadge();
59
59
 
60
60
  const trigger = screen.getByRole('button', { name: 'Set Project Directory' });
61
- expect(trigger.className).toContain('border-gray-200');
62
- expect(trigger.className).toContain('text-gray-600');
61
+ expect(trigger.className).toContain('border-border');
62
+ expect(trigger.className).toContain('text-muted-foreground');
63
63
  expect(trigger.className).not.toContain('emerald');
64
64
  });
65
65
 
@@ -9,7 +9,7 @@ import { ChatSessionProjectDialog } from './chat-session-project-dialog';
9
9
  import { t } from '@/shared/lib/i18n';
10
10
 
11
11
  const SESSION_HEADER_ACTION_GROUP_CLASS = 'flex shrink-0 items-center gap-1.5';
12
- const SESSION_HEADER_ACTION_BUTTON_CLASS = 'h-7 w-7 rounded-lg shrink-0 text-gray-400 hover:text-gray-700';
12
+ const SESSION_HEADER_ACTION_BUTTON_CLASS = 'h-7 w-7 shrink-0 rounded-lg text-muted-foreground/70 hover:bg-accent hover:text-accent-foreground';
13
13
 
14
14
  type ChatSessionHeaderActionsProps = {
15
15
  sessionKey: string;
@@ -23,7 +23,7 @@ export function ChatSessionHeaderMenuItem({
23
23
  'flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50',
24
24
  destructive
25
25
  ? 'text-destructive hover:bg-destructive/10'
26
- : 'text-gray-700 hover:bg-gray-100'
26
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
27
27
  )}
28
28
  onClick={onClick}
29
29
  disabled={disabled}
@@ -29,7 +29,7 @@ export function ChatSessionMetadataDialog({
29
29
  return (
30
30
  <Dialog open={open} onOpenChange={onOpenChange}>
31
31
  <DialogContent className="max-w-2xl gap-0 overflow-hidden p-0">
32
- <DialogHeader className="border-b border-gray-200 px-5 py-4 pr-12">
32
+ <DialogHeader className="border-b border-border px-5 py-4 pr-12">
33
33
  <DialogTitle className="text-base">{t('chatSessionMetadataDialogTitle')}</DialogTitle>
34
34
  <DialogDescription className="sr-only">
35
35
  {t('chatSessionMetadataDialogDescription')}
@@ -55,7 +55,7 @@ export function ChatSessionProjectBadge({
55
55
  <button
56
56
  type="button"
57
57
  title={projectRoot ?? undefined}
58
- className="min-w-0 max-w-[320px] shrink rounded-full border border-gray-200 bg-gray-100/90 px-2 py-0.5 text-[11px] font-medium text-gray-600 transition-colors hover:border-gray-300 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-60"
58
+ className="min-w-0 max-w-[320px] shrink rounded-full border border-border bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:border-primary/35 hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-60"
59
59
  aria-label={t('chatSessionSetProject')}
60
60
  disabled={isProjectPending}
61
61
  >
@@ -72,11 +72,11 @@ export function ChatSessionProjectBadge({
72
72
  style={SESSION_PROJECT_MENU_STYLE}
73
73
  >
74
74
  <div className="px-3 pb-2 pt-1">
75
- <div className="text-[11px] font-medium uppercase tracking-wider text-gray-500">
75
+ <div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
76
76
  {projectName}
77
77
  </div>
78
78
  {projectRoot ? (
79
- <div className="mt-1 break-all text-xs text-gray-500">
79
+ <div className="mt-1 break-all text-xs text-muted-foreground">
80
80
  {projectRoot}
81
81
  </div>
82
82
  ) : null}
@@ -12,7 +12,7 @@ export function SessionRunBadge({ status, className }: SessionRunBadgeProps) {
12
12
  const label = status === 'running' ? t('sessionsRunStatusRunning') : t('sessionsRunStatusQueued');
13
13
  return (
14
14
  <span
15
- className={cn('inline-flex h-3.5 w-3.5 items-center justify-center text-gray-400', className)}
15
+ className={cn('inline-flex h-3.5 w-3.5 items-center justify-center text-muted-foreground/70', className)}
16
16
  title={label}
17
17
  aria-label={label}
18
18
  >
@@ -1,7 +1,6 @@
1
1
  import { act, renderHook } from '@testing-library/react';
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
3
  import { toast } from 'sonner';
4
- import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
5
4
  import { useChatSessionProject } from '@/features/chat/features/session/hooks/use-chat-session-project';
6
5
 
7
6
  const mocks = vi.hoisted(() => ({
@@ -19,21 +18,11 @@ vi.mock('@/features/chat/features/session/hooks/use-chat-session-update', () =>
19
18
  }));
20
19
 
21
20
  describe('useChatSessionProject', () => {
22
- beforeEach(() => {
23
- useChatInputStore.setState((state) => ({
24
- snapshot: {
25
- ...state.snapshot,
26
- pendingProjectRoot: null,
27
- pendingProjectRootSessionKey: null
28
- }
29
- }));
30
- });
31
-
32
21
  afterEach(() => {
33
22
  vi.clearAllMocks();
34
23
  });
35
24
 
36
- it('stores the draft project root locally when the session does not exist yet', async () => {
25
+ it('does not persist draft project root through the session update hook', async () => {
37
26
  const { result } = renderHook(() => useChatSessionProject());
38
27
 
39
28
  await act(async () => {
@@ -45,14 +34,10 @@ describe('useChatSessionProject', () => {
45
34
  });
46
35
 
47
36
  expect(mocks.updateSession).not.toHaveBeenCalled();
48
- expect(useChatInputStore.getState().snapshot).toMatchObject({
49
- pendingProjectRoot: '/tmp/project-alpha',
50
- pendingProjectRootSessionKey: 'draft-session-1'
51
- });
52
37
  expect(toast.success).toHaveBeenCalledTimes(1);
53
38
  });
54
39
 
55
- it('keeps an explicit draft override when clearing the project root locally', async () => {
40
+ it('does not persist draft project clearing through the session update hook', async () => {
56
41
  const { result } = renderHook(() => useChatSessionProject());
57
42
 
58
43
  await act(async () => {
@@ -64,10 +49,6 @@ describe('useChatSessionProject', () => {
64
49
  });
65
50
 
66
51
  expect(mocks.updateSession).not.toHaveBeenCalled();
67
- expect(useChatInputStore.getState().snapshot).toMatchObject({
68
- pendingProjectRoot: null,
69
- pendingProjectRootSessionKey: 'draft-session-1'
70
- });
71
52
  expect(toast.success).toHaveBeenCalledTimes(1);
72
53
  });
73
54
 
@@ -87,10 +68,6 @@ describe('useChatSessionProject', () => {
87
68
  patch: { projectRoot: '/tmp/project-beta' },
88
69
  successMessage: 'Project directory updated'
89
70
  });
90
- expect(useChatInputStore.getState().snapshot).toMatchObject({
91
- pendingProjectRoot: null,
92
- pendingProjectRootSessionKey: null
93
- });
94
71
  });
95
72
 
96
73
  it('persists clearing to the server without keeping a session-scoped local override', async () => {
@@ -109,9 +86,5 @@ describe('useChatSessionProject', () => {
109
86
  patch: { projectRoot: null },
110
87
  successMessage: 'Project directory cleared'
111
88
  });
112
- expect(useChatInputStore.getState().snapshot).toMatchObject({
113
- pendingProjectRoot: null,
114
- pendingProjectRootSessionKey: null
115
- });
116
89
  });
117
90
  });
@@ -1,6 +1,5 @@
1
1
  import { toast } from 'sonner';
2
2
  import { t } from '@/shared/lib/i18n';
3
- import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
4
3
  import { useChatSessionUpdate } from '@/features/chat/features/session/hooks/use-chat-session-update';
5
4
 
6
5
  type UpdateChatSessionProjectParams = {
@@ -17,10 +16,6 @@ export function useChatSessionProject() {
17
16
  const successMessage = projectRoot ? t('chatSessionProjectUpdated') : t('chatSessionProjectCleared');
18
17
 
19
18
  if (!persistToServer) {
20
- useChatInputStore.getState().setSnapshot({
21
- pendingProjectRoot: projectRoot,
22
- pendingProjectRootSessionKey: sessionKey
23
- });
24
19
  toast.success(successMessage);
25
20
  return;
26
21
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ CHAT_SESSION_MATERIALIZATION_METADATA_KEY,
3
+ type AgentRunSessionMaterializationMetadata,
4
+ } from "@nextclaw/shared";
1
5
  import { buildInlineSkillTokensFromComposer, CHAT_UI_INLINE_TOKENS_METADATA_KEY } from "@/features/chat/features/input/utils/chat-inline-token.utils";
2
6
  import { normalizeRequestedSkills } from "@/features/chat/features/runtime/utils/chat-runtime.utils";
3
7
  import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
@@ -17,6 +21,7 @@ export function buildChatRunMetadata(payload: {
17
21
  projectRoot?: string | null;
18
22
  requestedSkills?: string[];
19
23
  composerNodes?: Parameters<typeof buildInlineSkillTokensFromComposer>[0];
24
+ sessionMaterialization?: AgentRunSessionMaterializationMetadata | null;
20
25
  }): Record<string, unknown> {
21
26
  const projectRoot = normalizeSessionProjectRootValue(payload.projectRoot);
22
27
  const metadata: Record<string, unknown> = {
@@ -36,6 +41,9 @@ export function buildChatRunMetadata(payload: {
36
41
  if (inlineSkillTokens.length > 0) {
37
42
  metadata[CHAT_UI_INLINE_TOKENS_METADATA_KEY] = inlineSkillTokens;
38
43
  }
44
+ if (payload.sessionMaterialization) {
45
+ metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY] = payload.sessionMaterialization;
46
+ }
39
47
  return metadata;
40
48
  }
41
49
 
@@ -4,8 +4,8 @@ import { ChatSessionTypeMenu } from "@/features/chat/features/session-type/compo
4
4
  import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
5
5
 
6
6
  const options = [
7
- { value: "native", label: "Native", ready: true },
8
- { value: "codex", label: "Codex", ready: true },
7
+ { value: "native", label: "Native", icon: null, ready: true },
8
+ { value: "codex", label: "Codex", icon: null, ready: true },
9
9
  ];
10
10
 
11
11
  describe("ChatSessionTypeMenu", () => {
@@ -50,6 +50,7 @@ describe("ChatSessionTypeOptionItem", () => {
50
50
  option={{
51
51
  value: "native",
52
52
  label: "Native",
53
+ icon: null,
53
54
  ready: true,
54
55
  }}
55
56
  onSelect={vi.fn()}
@@ -70,6 +71,7 @@ describe("ChatSessionTypeOptionItem", () => {
70
71
  option={{
71
72
  value: "claude",
72
73
  label: "Claude",
74
+ icon: null,
73
75
  ready: false,
74
76
  reasonMessage: "Configure a provider API key first.",
75
77
  }}
@@ -1,10 +1,10 @@
1
1
  import { ChatSessionTypeOptionItem } from "@/features/chat/features/session-type/components/chat-session-type-option-item";
2
- import type { ChatInputSnapshot } from "@/features/chat/stores/chat-input.store";
2
+ import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
3
3
  import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
4
4
  import { t } from "@/shared/lib/i18n";
5
5
  import { cn } from "@/shared/lib/utils";
6
6
 
7
- type SessionTypeOption = ChatInputSnapshot["sessionTypeOptions"][number];
7
+ type SessionTypeOption = ChatSessionTypeOption;
8
8
  const CHAT_SESSION_TYPE_MENU_MAX_HEIGHT =
9
9
  createPopoverAvailableHeightLimit("18rem");
10
10
  const CHAT_SESSION_TYPE_MENU_STYLE = {
@@ -1,11 +1,11 @@
1
- import type { ChatInputSnapshot } from "@/features/chat/stores/chat-input.store";
2
1
  import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
2
+ import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
3
3
  import { StatusDot } from "@/shared/components/status/status-dot";
4
4
  import { t } from "@/shared/lib/i18n";
5
5
  import { cn } from "@/shared/lib/utils";
6
6
  import { Bot, Check } from "lucide-react";
7
7
 
8
- type SessionTypeOption = ChatInputSnapshot["sessionTypeOptions"][number];
8
+ type SessionTypeOption = ChatSessionTypeOption;
9
9
 
10
10
  export function ChatSessionTypeOptionItem(props: {
11
11
  option: SessionTypeOption;
@@ -128,7 +128,7 @@ export function useChatSessionTypeState(params: UseChatSessionTypeStateParams):
128
128
  return null;
129
129
  }, [availableSessionTypeSet, selectedSession, selectedSessionType, selectedSessionTypeOption]);
130
130
 
131
- return {
131
+ return useMemo(() => ({
132
132
  sessionTypeOptions,
133
133
  selectedSessionTypeOption,
134
134
  defaultSessionType,
@@ -136,5 +136,13 @@ export function useChatSessionTypeState(params: UseChatSessionTypeStateParams):
136
136
  canEditSessionType,
137
137
  sessionTypeUnavailable,
138
138
  sessionTypeUnavailableMessage
139
- };
139
+ }), [
140
+ canEditSessionType,
141
+ defaultSessionType,
142
+ selectedSessionType,
143
+ selectedSessionTypeOption,
144
+ sessionTypeOptions,
145
+ sessionTypeUnavailable,
146
+ sessionTypeUnavailableMessage
147
+ ]);
140
148
  }