@nextclaw/ui 0.15.4 → 0.15.6
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/abnfDiagram-VRR7QNED-BYpK0F5F.js +1 -0
- package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-D87ywFdC.js} +1 -1
- package/dist/assets/api-Dyb5rgh0.js +13 -0
- package/dist/assets/app-presenter-provider-7jV0nPiL.js +106 -0
- package/dist/assets/{appearance-settings-page-fLIJbx1C.js → appearance-settings-page-DFm3BbEb.js} +1 -1
- package/dist/assets/arc-DFELHWM2.js +1 -0
- package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +1 -0
- package/dist/assets/architectureDiagram-ZJ3FMSHR-CvRhdovL.js +36 -0
- package/dist/assets/array-BqYdCIE6.js +1 -0
- package/dist/assets/blockDiagram-677ZJIJ3-DVYNkbR_.js +132 -0
- package/dist/assets/{book-open-DWUjS6aW.js → book-open-L6LCuqOr.js} +1 -1
- package/dist/assets/c4Diagram-LMCZKHZV-B1w-iwRe.js +10 -0
- package/dist/assets/channel-BEHOSFc5.js +1 -0
- package/dist/assets/{channels-list-page-CvCTJraa.js → channels-list-page-DOaPKNJA.js} +2 -2
- package/dist/assets/{chat-page-B42UUIFR.js → chat-page-Cw9uDhD_.js} +1 -1
- package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +1 -0
- package/dist/assets/chunk-32BRIVSS-kG3lg0bC.js +1 -0
- package/dist/assets/chunk-52WLFC77-BLeB7iGk.js +10 -0
- package/dist/assets/chunk-5FCAYU7R-BQga1Rr7.js +1 -0
- package/dist/assets/chunk-5HE753X5-DU81hkcF.js +1 -0
- package/dist/assets/chunk-5JV3BV7I-C2bAx0m7.js +1 -0
- package/dist/assets/chunk-5TONJI2A-BExIIgWY.js +2 -0
- package/dist/assets/chunk-5VM5RSS4-C3XB05yh.js +15 -0
- package/dist/assets/chunk-7BUUIJ7U-JiyRtrGu.js +1 -0
- package/dist/assets/chunk-BIQX33UG-BZRV20pI.js +1 -0
- package/dist/assets/chunk-C7G6YPKG-AsyDSQ9j.js +1 -0
- package/dist/assets/chunk-CQNSW5MT-iN-fiXVA.js +1 -0
- package/dist/assets/chunk-CYSBUYHQ-DhC_TRzI.js +1 -0
- package/dist/assets/chunk-EMLP6XTP-QFLK8R6d.js +1 -0
- package/dist/assets/chunk-EX3LRPZG-DzUwhkCz.js +231 -0
- package/dist/assets/chunk-FWX5IMBZ-DFelpZBy.js +2 -0
- package/dist/assets/chunk-HOUHSVGY-BMPwFEwN.js +1 -0
- package/dist/assets/chunk-ICXQ74PX-DH8NJMok.js +2 -0
- package/dist/assets/chunk-JG7HCLWE-D-95AsO4.js +2 -0
- package/dist/assets/chunk-JWPE2WC7-DIaTv8kx.js +1 -0
- package/dist/assets/chunk-KEIR6QF5-A3jEqwzn.js +161 -0
- package/dist/assets/chunk-MOJQB5TN-BPFLzWgj.js +88 -0
- package/dist/assets/chunk-MOZMSUNE-DR_fzxG0.js +1 -0
- package/dist/assets/chunk-OGEWGWER-TIcEoPPH.js +1 -0
- package/dist/assets/chunk-OSBZ3O6U-BALNvSfG.js +1 -0
- package/dist/assets/chunk-PUDLZKDR-I-EXhf_k.js +156 -0
- package/dist/assets/chunk-Q4XR5HBZ-uvIGstBS.js +70 -0
- package/dist/assets/chunk-QBLGF6JB-r-TjLdxa.js +1 -0
- package/dist/assets/chunk-R7FJI6CG-N-1LDgvy.js +1 -0
- package/dist/assets/chunk-RYQCIY6F-jxNASx65.js +1 -0
- package/dist/assets/chunk-U6XO7XAA-Cphbuc7q.js +2 -0
- package/dist/assets/chunk-V7JOEXUC-BLEuBf1s.js +206 -0
- package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +1 -0
- package/dist/assets/chunk-VR4S4FIN-BAUbR0ke.js +1 -0
- package/dist/assets/chunk-WYO6CB5R-Do6SJrnY.js +127 -0
- package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +1 -0
- package/dist/assets/chunk-Y2CYZVJY-BGrd8tLy.js +1 -0
- package/dist/assets/chunk-YOTPTUD7-CQqvMTCr.js +1 -0
- package/dist/assets/chunk-ZGVPDNZ5-B7kWWaXv.js +62 -0
- package/dist/assets/chunk-ZIRB5QZD-DTU0cDNh.js +32 -0
- package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +1 -0
- package/dist/assets/{config-split-page-DBgt9S8b.js → config-split-page-CqlqjnCK.js} +1 -1
- package/dist/assets/{confirm-dialog-GSXIvoRU.js → confirm-dialog-bAp8NU93.js} +1 -1
- package/dist/assets/cose-bilkent-JH36ORCC-B9LrtV-q.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +1 -0
- package/dist/assets/cynefinDiagram-TSTJHNR4-BfjvW4su.js +62 -0
- package/dist/assets/cytoscape.esm-CRW8q92n.js +321 -0
- package/dist/assets/dagre-DkiXZKkL.js +1 -0
- package/dist/assets/dagre-VKFMJZFB-B1pQKQSY.js +4 -0
- package/dist/assets/defaultLocale-Cdv9x6q7.js +1 -0
- package/dist/assets/desktop-update-config-2SjMfCMl.js +1 -0
- package/dist/assets/diagram-FQU43EPY-cB4EZdaP.js +3 -0
- package/dist/assets/diagram-G47NLZAW-CtW8im1B.js +24 -0
- package/dist/assets/diagram-NH7WQ7WH-BDCtrCCm.js +24 -0
- package/dist/assets/diagram-OA4YK3LP-DEMJ42Gt.js +30 -0
- package/dist/assets/diagram-WEI45ONY-CdCcxSYU.js +41 -0
- package/dist/assets/{dist-DC7f4TJH.js → dist-CrtbGLlh.js} +1 -1
- package/dist/assets/dist-dw-gAfM_.js +1 -0
- package/dist/assets/doc-browser-A5IRyDXQ2.js +1 -0
- package/dist/assets/{doc-browser-D0monHjG.js → doc-browser-BmnQa30-.js} +1 -1
- package/dist/assets/{doc-browser-context-CGwEOhBy.js → doc-browser-context-BlpUUFrR.js} +1 -1
- package/dist/assets/{docx-preview-eFH9DRsS.js → docx-preview-zis3U0Fa.js} +1 -1
- package/dist/assets/{download-DiP9zx-J.js → download-BhZZNXD8.js} +1 -1
- package/dist/assets/ebnfDiagram-CCIWWBDH-DBZETBwE.js +1 -0
- package/dist/assets/erDiagram-Q63AITRT-y2zDXprP.js +85 -0
- package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +1 -0
- package/dist/assets/{external-link-C6ZeSPef.js → external-link-B9Uoq5J7.js} +1 -1
- package/dist/assets/{eye-suvKmtwn.js → eye-K5knauEM.js} +1 -1
- package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
- package/dist/assets/{folder-CMELbQGG.js → folder-BIYZZWJv.js} +1 -1
- package/dist/assets/{form-actions-CUAzpNe_.js → form-actions-CKprWFeM.js} +1 -1
- package/dist/assets/ganttDiagram-NO4QXBWP-D2DP344e.js +292 -0
- package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +1 -0
- package/dist/assets/gitGraphDiagram-IHSO6WYX-D37uiwA2.js +106 -0
- package/dist/assets/graphlib-BcNFQr5y.js +1 -0
- package/dist/assets/index-BVn702m4.css +1 -0
- package/dist/assets/index-CFxJ1Fkg.js +2 -0
- package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +1 -0
- package/dist/assets/infoDiagram-FWYZ7A6U-CG-WAIRi.js +2 -0
- package/dist/assets/init-D8TjIqfm.js +1 -0
- package/dist/assets/ishikawaDiagram-FXEZZL3T-WeCTnW0f.js +70 -0
- package/dist/assets/journeyDiagram-5HDEW3XC-DUFBW9Td.js +139 -0
- package/dist/assets/kanban-definition-HUTT4EX6-B0lxgMpg.js +89 -0
- package/dist/assets/katex-wEmrEFTg.js +257 -0
- package/dist/assets/line-B78frP_c.js +1 -0
- package/dist/assets/linear-C6lVEcb2.js +1 -0
- package/dist/assets/loader-circle-Bj2RMrCR.js +1 -0
- package/dist/assets/{logo-badge-Cu5UGH_4.js → logo-badge-BQ-XFZod.js} +1 -1
- package/dist/assets/map-Bdri5UL5.js +1 -0
- package/dist/assets/mcp-marketplace-page-BfA7A3n4.js +9 -0
- package/dist/assets/mcp-marketplace-page-CXHYO95j.js +1 -0
- package/dist/assets/mermaid-parser.core-Cue18vLo.js +4 -0
- package/dist/assets/mermaid.core-CXwWRh5i.js +11 -0
- package/dist/assets/mindmap-definition-LN4V7U3C-CFxz0P0-.js +96 -0
- package/dist/assets/{model-config-page-CWP6pyBm.js → model-config-page-BbWbW_qM.js} +1 -1
- package/dist/assets/ordinal-Cbd8t4LB.js +1 -0
- package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +1 -0
- package/dist/assets/path-DzBnMI1r.js +1 -0
- package/dist/assets/pegDiagram-2B236MQR-B_Qt9zyT.js +1 -0
- package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +1 -0
- package/dist/assets/pieDiagram-ENE6RG2P-CRQdSuwS.js +39 -0
- package/dist/assets/plus-TmFdwO3P.js +1 -0
- package/dist/assets/{popover-DImguRok.js → popover-CX9zFsuc.js} +1 -1
- package/dist/assets/preload-helper-D4M6sveU.js +1 -0
- package/dist/assets/{provider-scoped-model-input-CE7p4SbJ.js → provider-scoped-model-input-B-9_DtIP.js} +1 -1
- package/dist/assets/providers-config-page-xwIjGur2.js +1 -0
- package/dist/assets/quadrantDiagram-ABIIQ3AL-D5kPpttq.js +7 -0
- package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +1 -0
- package/dist/assets/railroadDiagram-RFXS5EU6-DGh_TGlQ.js +1 -0
- package/dist/assets/{react-5sjAT_6d.js → react-DsgudwpT.js} +1 -1
- package/dist/assets/{refresh-cw-CHLBzWWj.js → refresh-cw-kXdvbhFf.js} +1 -1
- package/dist/assets/remote-B9F2uCk9.js +1 -0
- package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
- package/dist/assets/rotate-cw-DEc6aihr.js +1 -0
- package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
- package/dist/assets/runtime-config-page-DTsi7czJ.js +1 -0
- package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
- package/dist/assets/{save-BABzyrjB.js → save-C9qpz2_2.js} +1 -1
- package/dist/assets/{search-BTqxG3ZG.js → search-_kEi29qo.js} +1 -1
- package/dist/assets/{search-config-page-BkKKmxmS.js → search-config-page-H7ug8f4D.js} +1 -1
- package/dist/assets/{secrets-config-page-CWJMyu-k.js → secrets-config-page-C1_6rINu.js} +2 -2
- package/dist/assets/security-config-Cr2ERKLu.js +1 -0
- package/dist/assets/{select-TYZDBNmq.js → select-MJKhKAWR.js} +1 -1
- package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
- package/dist/assets/{setting-row-DR23Z_5I.js → setting-row-DA3tJuew.js} +1 -1
- package/dist/assets/{settings-2-D8WWcBnw.js → settings-2-D_y7zXJz.js} +1 -1
- package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
- package/dist/assets/{skeleton-CloHeg0S.js → skeleton-0uTLUhm9.js} +1 -1
- package/dist/assets/src-Dad2ZbCt.js +1 -0
- package/dist/assets/stateDiagram-2N3HPSRC-BvAalBfz.js +1 -0
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +1 -0
- package/dist/assets/swimlanes-5IMT3BWC-DdVqVa1a.js +2 -0
- package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +8 -0
- package/dist/assets/{tabs-CX-ckWoo.js → tabs-h44XPocd.js} +1 -1
- package/dist/assets/{tag-chip-mCc23Rhb.js → tag-chip-bRZ04q3C.js} +1 -1
- package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
- package/dist/assets/{trash-2-SAW1Z4Po.js → trash-2-BRpSE-Wt.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +1 -0
- package/dist/assets/use-config-CwDEAl6f.js +1 -0
- package/dist/assets/{use-confirm-dialog-C95F6sT1.js → use-confirm-dialog-BbnWw-8c.js} +1 -1
- package/dist/assets/{use-viewport-layout-C6m3Nte7.js → use-viewport-layout-CqgIMDQL.js} +1 -1
- package/dist/assets/vennDiagram-L72KCM5P-CDXJ_i3p.js +34 -0
- package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +1 -0
- package/dist/assets/wardleyDiagram-EHGQE667-D-dSuT_7.js +78 -0
- package/dist/assets/x-DdEDL6ju.js +1 -0
- package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
- package/dist/index.html +33 -32
- package/package.json +8 -8
- package/src/features/chat/components/chat-popover-content.tsx +1 -26
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +84 -1
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +13 -2
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar-test.utils.ts +22 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +13 -24
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +16 -4
- package/src/features/chat/components/layout/chat-sidebar.tsx +38 -2
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +52 -2
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +22 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +36 -20
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +55 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +12 -11
- package/src/features/chat/features/conversation/hooks/use-session-conversation-preference-actions.ts +94 -0
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +37 -0
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +140 -63
- package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +175 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +66 -0
- package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +8 -1
- package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +274 -0
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +18 -11
- package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +26 -0
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +50 -4
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
- package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +4 -10
- package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +29 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +66 -8
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +62 -21
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +42 -10
- package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +80 -0
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +55 -60
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +41 -3
- package/src/features/chat/features/project/components/__tests__/chat-project-create-dialog.test.tsx +67 -0
- package/src/features/chat/features/project/components/chat-project-create-dialog.tsx +203 -0
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +7 -7
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-dialog.test.tsx +47 -0
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +6 -3
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -0
- package/src/features/chat/features/session/components/session-header/chat-session-project-dialog.tsx +3 -0
- package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +245 -0
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +81 -127
- package/src/features/chat/features/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +22 -0
- package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +6 -6
- package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +12 -1
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +3 -0
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +1 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +20 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +15 -2
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +22 -24
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +2 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +6 -17
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +41 -19
- 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 +5 -1
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +104 -3
- package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +83 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +4 -1
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +96 -25
- package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +25 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +5 -1
- package/src/features/chat/managers/chat-session-list.manager.ts +1 -0
- package/src/features/chat/managers/chat-thread.manager.ts +15 -37
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +23 -0
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +9 -0
- package/src/features/chat/stores/chat-thread.store.ts +16 -36
- package/src/features/chat/types/chat-input-bar.types.ts +2 -0
- package/src/features/settings/components/config/provider-form.tsx +0 -4
- package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +7 -9
- package/src/features/settings/utils/provider-form-model.utils.ts +0 -4
- package/src/features/system-status/hooks/use-current-version-release-notes-link.ts +33 -0
- package/src/features/system-status/index.ts +9 -0
- package/src/features/system-status/utils/update-release-notes.utils.ts +44 -3
- package/src/index.css +10 -16
- package/src/shared/components/common/__tests__/brand-header.test.tsx +127 -10
- package/src/shared/components/common/brand-header.tsx +142 -56
- package/src/shared/components/path-picker/__tests__/server-path-picker-dialog.test.tsx +288 -38
- package/src/shared/components/path-picker/server-path-picker-dialog.tsx +290 -199
- package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +103 -0
- package/src/shared/components/path-picker/server-path-picker-footer.tsx +58 -0
- package/src/shared/components/path-picker/server-path-picker-locations.tsx +148 -0
- package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +198 -0
- package/src/shared/components/path-picker/server-path-picker-new-folder.tsx +59 -0
- package/src/shared/components/path-picker/server-path-picker-toolbar.tsx +25 -0
- package/src/shared/hooks/use-projects.ts +28 -0
- package/src/shared/hooks/use-server-path-create-directory.ts +6 -0
- package/src/shared/hooks/use-server-path-read.ts +10 -5
- package/src/shared/hooks/use-server-path-search.ts +40 -0
- package/src/shared/lib/api/index.ts +1 -0
- package/src/shared/lib/api/projects/project.utils.ts +14 -0
- package/src/shared/lib/api/types.ts +30 -3
- package/src/shared/lib/api/utils/server-path.utils.ts +22 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +43 -2
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -3
- package/src/shared/lib/i18n/locales/en-US/desktop-update.json +4 -0
- package/src/shared/lib/i18n/locales/en-US/path-picker.json +25 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +43 -2
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -3
- package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +4 -0
- package/src/shared/lib/i18n/locales/zh-CN/path-picker.json +25 -0
- package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +1 -4
- package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +1 -18
- package/dist/assets/api-BOXOrpcJ.js +0 -13
- package/dist/assets/app-presenter-provider-CkHLbvZQ.js +0 -106
- package/dist/assets/desktop-update-config-7bmNeWIC.js +0 -1
- package/dist/assets/doc-browser-CgdoLUT92.js +0 -1
- package/dist/assets/index-BFeNDQ4R.css +0 -1
- package/dist/assets/index-D_6U-M94.js +0 -2
- package/dist/assets/loader-circle-tYHStefV.js +0 -1
- package/dist/assets/mcp-marketplace-page-BUzHDwW6.js +0 -9
- package/dist/assets/mcp-marketplace-page-ohUAdfqJ.js +0 -1
- package/dist/assets/plus-tywizkeY.js +0 -1
- package/dist/assets/providers-config-page-BKPo8SjH.js +0 -1
- package/dist/assets/remote-DXJryUxB.js +0 -1
- package/dist/assets/rotate-cw-BefO5brU.js +0 -1
- package/dist/assets/runtime-config-page-1Axv4QGu.js +0 -1
- package/dist/assets/security-config-DeQUIXfi.js +0 -1
- package/dist/assets/use-config-BucdaK23.js +0 -1
- package/dist/assets/x-DtCPHwCu.js +0 -1
- package/src/features/chat/components/__tests__/chat-popover-content.test.tsx +0 -41
- package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +0 -55
- package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +0 -77
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +0 -144
- /package/dist/assets/{config-hints-fDrYfl0l.js → config-hints-LxG80Uj5.js} +0 -0
- /package/dist/assets/{host-capabilities-zZvY9U63.js → host-capabilities-CFbAfrrA.js} +0 -0
- /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CP5uPNQa.js} +0 -0
- /package/dist/assets/{middleware-BLMHv9YF.js → middleware-CAXxl-af.js} +0 -0
- /package/dist/assets/{provider-models-C_yOh6DE.js → provider-models-BoUhFo9R.js} +0 -0
- /package/dist/assets/{xlsx-B6zVt1RK.js → xlsx-DUqJhg5u.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { render, screen, within } from "@testing-library/react";
|
|
3
3
|
import userEvent from "@testing-library/user-event";
|
|
4
4
|
import type { PersistStorage, StorageValue } from "zustand/middleware";
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
@@ -15,6 +15,8 @@ const mocks = vi.hoisted(() => ({
|
|
|
15
15
|
deleteSession: vi.fn(),
|
|
16
16
|
toggleWorkspacePanel: vi.fn(),
|
|
17
17
|
selectSession: vi.fn(),
|
|
18
|
+
setListMode: vi.fn(),
|
|
19
|
+
toggleProjectCollapsed: vi.fn(),
|
|
18
20
|
sessionItems: [] as NcpSessionListItemView[],
|
|
19
21
|
isSessionListLoading: false,
|
|
20
22
|
}));
|
|
@@ -27,6 +29,8 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
|
27
29
|
},
|
|
28
30
|
chatSessionListManager: {
|
|
29
31
|
selectSession: mocks.selectSession,
|
|
32
|
+
setListMode: mocks.setListMode,
|
|
33
|
+
toggleProjectCollapsed: mocks.toggleProjectCollapsed,
|
|
30
34
|
},
|
|
31
35
|
}),
|
|
32
36
|
}));
|
|
@@ -120,6 +124,8 @@ describe("ChatConversationHeaderSection", () => {
|
|
|
120
124
|
mocks.deleteSession.mockReset();
|
|
121
125
|
mocks.toggleWorkspacePanel.mockReset();
|
|
122
126
|
mocks.selectSession.mockReset();
|
|
127
|
+
mocks.setListMode.mockReset();
|
|
128
|
+
mocks.toggleProjectCollapsed.mockReset();
|
|
123
129
|
mocks.isSessionListLoading = false;
|
|
124
130
|
viewportLayoutManager.resetForTests();
|
|
125
131
|
useChatSessionListStore.persist.setOptions({
|
|
@@ -131,10 +137,25 @@ describe("ChatConversationHeaderSection", () => {
|
|
|
131
137
|
useChatSessionListStore.setState({
|
|
132
138
|
snapshot: {
|
|
133
139
|
...useChatSessionListStore.getState().snapshot,
|
|
140
|
+
collapsedProjectRoots: [],
|
|
141
|
+
listMode: "time-first",
|
|
142
|
+
pinnedProjectRoots: [],
|
|
143
|
+
pinnedSessionKeys: [],
|
|
134
144
|
selectedAgentId: "main",
|
|
135
145
|
selectedSessionKey: "parent-session-1",
|
|
136
146
|
},
|
|
137
147
|
});
|
|
148
|
+
mocks.setListMode.mockImplementation((listMode) => {
|
|
149
|
+
useChatSessionListStore.getState().setSnapshot({ listMode });
|
|
150
|
+
});
|
|
151
|
+
mocks.toggleProjectCollapsed.mockImplementation((projectRoot) => {
|
|
152
|
+
const { collapsedProjectRoots } = useChatSessionListStore.getState().snapshot;
|
|
153
|
+
useChatSessionListStore.getState().setSnapshot({
|
|
154
|
+
collapsedProjectRoots: collapsedProjectRoots.includes(projectRoot)
|
|
155
|
+
? collapsedProjectRoots.filter((root) => root !== projectRoot)
|
|
156
|
+
: [...collapsedProjectRoots, projectRoot],
|
|
157
|
+
});
|
|
158
|
+
});
|
|
138
159
|
useChatThreadStore.setState({
|
|
139
160
|
snapshot: {
|
|
140
161
|
...useChatThreadStore.getState().snapshot,
|
|
@@ -229,6 +250,68 @@ describe("ChatConversationHeaderSection", () => {
|
|
|
229
250
|
expect(mocks.selectSession).toHaveBeenCalledWith("session:ncp-2");
|
|
230
251
|
});
|
|
231
252
|
|
|
253
|
+
it("uses lightweight static groups for pinned and activity dates", async () => {
|
|
254
|
+
const user = userEvent.setup();
|
|
255
|
+
viewportLayoutManager.setSidebarCollapsed(true);
|
|
256
|
+
useChatSessionListStore.setState({
|
|
257
|
+
snapshot: {
|
|
258
|
+
...useChatSessionListStore.getState().snapshot,
|
|
259
|
+
pinnedSessionKeys: ["session:ncp-2"],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
renderHeaderSection();
|
|
264
|
+
await user.click(screen.getByRole("button", { name: /Switch session/ }));
|
|
265
|
+
|
|
266
|
+
const pinnedGroup = screen.getByRole("region", { name: "Pinned" });
|
|
267
|
+
const pinnedTitle = within(pinnedGroup).getByText("Pinned");
|
|
268
|
+
expect(pinnedTitle.className).toContain("uppercase");
|
|
269
|
+
expect(pinnedTitle.className).not.toContain("bg-muted");
|
|
270
|
+
expect(within(pinnedGroup).queryByRole("button", { name: "Pinned" })).toBeNull();
|
|
271
|
+
expect(within(pinnedGroup).getByText("Background Task")).toBeTruthy();
|
|
272
|
+
expect(
|
|
273
|
+
within(screen.getByRole("region", { name: "Older" })).getByText("Parent Task"),
|
|
274
|
+
).toBeTruthy();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("switches to collapsible project groups inside the header popover", async () => {
|
|
278
|
+
const user = userEvent.setup();
|
|
279
|
+
viewportLayoutManager.setSidebarCollapsed(true);
|
|
280
|
+
mocks.sessionItems = [
|
|
281
|
+
createSessionListItem({
|
|
282
|
+
key: "parent-session-1",
|
|
283
|
+
label: "Parent Task",
|
|
284
|
+
projectName: "Alpha",
|
|
285
|
+
projectRoot: "/workspace/alpha",
|
|
286
|
+
}),
|
|
287
|
+
createSessionListItem({
|
|
288
|
+
key: "session:ncp-2",
|
|
289
|
+
label: "Background Task",
|
|
290
|
+
projectName: "Beta",
|
|
291
|
+
projectRoot: "/workspace/beta",
|
|
292
|
+
}),
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
renderHeaderSection();
|
|
296
|
+
await user.click(screen.getByRole("button", { name: /Switch session/ }));
|
|
297
|
+
await user.click(screen.getByRole("button", { name: "Project" }));
|
|
298
|
+
|
|
299
|
+
expect(mocks.setListMode).toHaveBeenCalledWith("project-first");
|
|
300
|
+
const alphaGroup = screen.getByRole("region", { name: "Alpha" });
|
|
301
|
+
const alphaToggle = within(alphaGroup).getByRole("button", {
|
|
302
|
+
name: "Collapse project · Alpha",
|
|
303
|
+
});
|
|
304
|
+
expect(alphaToggle.getAttribute("aria-expanded")).toBe("true");
|
|
305
|
+
expect(alphaToggle.className).not.toContain("bg-muted");
|
|
306
|
+
expect(within(alphaGroup).getByText("Parent Task")).toBeTruthy();
|
|
307
|
+
|
|
308
|
+
await user.click(alphaToggle);
|
|
309
|
+
expect(mocks.toggleProjectCollapsed).toHaveBeenCalledWith("/workspace/alpha");
|
|
310
|
+
expect(
|
|
311
|
+
within(screen.getByRole("region", { name: "Alpha" })).queryByText("Parent Task"),
|
|
312
|
+
).toBeNull();
|
|
313
|
+
});
|
|
314
|
+
|
|
232
315
|
it("keeps the desktop title plain while the sidebar is expanded", () => {
|
|
233
316
|
renderHeaderSection();
|
|
234
317
|
|
|
@@ -17,7 +17,10 @@ import { useChatSessionListStore } from "@/features/chat/stores/chat-session-lis
|
|
|
17
17
|
import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
18
18
|
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
19
19
|
import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getSessionProjectName,
|
|
22
|
+
normalizeSessionProjectRootValue,
|
|
23
|
+
} from "@/shared/lib/session-project";
|
|
21
24
|
import { t } from "@/shared/lib/i18n";
|
|
22
25
|
|
|
23
26
|
type ChatConversationHeaderSectionProps = {
|
|
@@ -40,6 +43,9 @@ export function ChatConversationHeaderSection({
|
|
|
40
43
|
const sessionTypesData = useChatQueryStore(
|
|
41
44
|
(state) => state.snapshot.sessionTypesQuery?.data ?? null,
|
|
42
45
|
);
|
|
46
|
+
const config = useChatQueryStore(
|
|
47
|
+
(state) => state.snapshot.configQuery?.data ?? null,
|
|
48
|
+
);
|
|
43
49
|
const selectedSession = useNcpChatSelectedSession(sessionKey);
|
|
44
50
|
const sessionTypeOptions = useMemo(
|
|
45
51
|
() => buildSessionTypeOptions(sessionTypesData?.options ?? []),
|
|
@@ -55,6 +61,9 @@ export function ChatConversationHeaderSection({
|
|
|
55
61
|
const shouldShowSessionTypeBadge =
|
|
56
62
|
sessionType !== DEFAULT_SESSION_TYPE && Boolean(sessionTypeLabel);
|
|
57
63
|
const sessionProjectRoot = selectedSession?.projectRoot ?? null;
|
|
64
|
+
const defaultWorkspacePath = normalizeSessionProjectRootValue(
|
|
65
|
+
config?.agents.defaults.workspace,
|
|
66
|
+
);
|
|
58
67
|
const sessionProjectName =
|
|
59
68
|
selectedSession?.projectName ?? getSessionProjectName(sessionProjectRoot);
|
|
60
69
|
const canDeleteSession = Boolean(selectedSession);
|
|
@@ -129,6 +138,7 @@ export function ChatConversationHeaderSection({
|
|
|
129
138
|
sessionKey={sessionKey ?? "draft"}
|
|
130
139
|
projectName={sessionProjectName}
|
|
131
140
|
projectRoot={sessionProjectRoot}
|
|
141
|
+
defaultWorkspacePath={defaultWorkspacePath}
|
|
132
142
|
persistToServer={canDeleteSession}
|
|
133
143
|
/>
|
|
134
144
|
) : null
|
|
@@ -139,7 +149,8 @@ export function ChatConversationHeaderSection({
|
|
|
139
149
|
sessionKey={sessionKey}
|
|
140
150
|
canDeleteSession={canDeleteSession}
|
|
141
151
|
isDeletePending={snapshot.isDeletePending}
|
|
142
|
-
|
|
152
|
+
currentPath={sessionProjectRoot ?? selectedSession?.workingDir ?? null}
|
|
153
|
+
defaultWorkspacePath={defaultWorkspacePath}
|
|
143
154
|
metadata={selectedSession?.metadata ?? null}
|
|
144
155
|
isWorkspaceOpen={isWorkspaceOpen}
|
|
145
156
|
onToggleWorkspace={toggleWorkspace}
|
|
@@ -34,6 +34,7 @@ export function ChatConversationWorkspaceSection({
|
|
|
34
34
|
activeSideChatDraft={activeSideChatDraft}
|
|
35
35
|
workspaceFileTabs={workspaceFileTabs}
|
|
36
36
|
activeWorkspaceFileKey={snapshot.activeWorkspaceFileKey ?? null}
|
|
37
|
+
closedWorkspaceTabEntries={snapshot.closedWorkspaceTabEntries}
|
|
37
38
|
workspaceNavigationHistory={snapshot.workspaceNavigationHistory}
|
|
38
39
|
workspaceNavigationHistoryIndex={snapshot.workspaceNavigationHistoryIndex}
|
|
39
40
|
activePanelKind={snapshot.activeWorkspacePanelKind ?? null}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NcpSessionListItemView } from '@/features/chat/features/ncp/hooks/use-ncp-session-list-view';
|
|
2
|
+
import { useChatQueryStore } from '@/features/chat/stores/ncp-chat-query.store';
|
|
3
|
+
import type { ChatSessionTypeOptionView } from '@/shared/lib/api';
|
|
4
|
+
|
|
5
|
+
export function createSidebarSessionItem(
|
|
6
|
+
session: NcpSessionListItemView['session'],
|
|
7
|
+
runStatus?: NcpSessionListItemView['runStatus'],
|
|
8
|
+
): NcpSessionListItemView {
|
|
9
|
+
return { session, runStatus };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function setSidebarSessionTypes(
|
|
13
|
+
options: ChatSessionTypeOptionView[],
|
|
14
|
+
defaultType = 'native',
|
|
15
|
+
) {
|
|
16
|
+
useChatQueryStore.setState({
|
|
17
|
+
snapshot: {
|
|
18
|
+
...useChatQueryStore.getState().snapshot,
|
|
19
|
+
sessionTypesQuery: { data: { defaultType, options } } as never,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -3,11 +3,13 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
|
3
3
|
import { MemoryRouter } from "react-router-dom";
|
|
4
4
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import { ChatSidebar } from "@/features/chat/components/layout/chat-sidebar";
|
|
6
|
+
import {
|
|
7
|
+
createSidebarSessionItem as createSessionItem,
|
|
8
|
+
setSidebarSessionTypes as setSessionTypes,
|
|
9
|
+
} from "@/features/chat/components/layout/__tests__/chat-sidebar-test.utils";
|
|
6
10
|
import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
|
|
7
|
-
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
8
11
|
import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
|
|
9
12
|
import { PREFERENCE_KEYS } from "@/shared/lib/api";
|
|
10
|
-
import type { ChatSessionTypeOptionView } from "@/shared/lib/api";
|
|
11
13
|
import { viewportLayoutManager } from "@/app/managers/viewport-layout.manager";
|
|
12
14
|
const mocks = vi.hoisted(() => ({
|
|
13
15
|
createSession: vi.fn(() => "draft-session-key"),
|
|
@@ -39,28 +41,15 @@ vi.mock("@/shared/lib/api", async (importOriginal) => {
|
|
|
39
41
|
updatePreference: mocks.updatePreference,
|
|
40
42
|
};
|
|
41
43
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
) {
|
|
52
|
-
useChatQueryStore.setState({
|
|
53
|
-
snapshot: {
|
|
54
|
-
...useChatQueryStore.getState().snapshot,
|
|
55
|
-
sessionTypesQuery: {
|
|
56
|
-
data: {
|
|
57
|
-
defaultType,
|
|
58
|
-
options,
|
|
59
|
-
},
|
|
60
|
-
} as never,
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
}
|
|
44
|
+
vi.mock("@/shared/hooks/use-projects", () => ({
|
|
45
|
+
useProjects: () => ({ data: { projects: [], templates: [] } }),
|
|
46
|
+
useCreateProject: () => ({
|
|
47
|
+
mutateAsync: vi.fn(),
|
|
48
|
+
reset: vi.fn(),
|
|
49
|
+
isPending: false,
|
|
50
|
+
error: null,
|
|
51
|
+
}),
|
|
52
|
+
}));
|
|
64
53
|
function sidebarElement(variant?: "desktop" | "mobile") {
|
|
65
54
|
const queryClient = new QueryClient({
|
|
66
55
|
defaultOptions: {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
BrainCircuit,
|
|
19
19
|
PanelLeftClose,
|
|
20
20
|
PanelLeftOpen,
|
|
21
|
+
Plus,
|
|
21
22
|
} from "lucide-react";
|
|
22
23
|
import { ChatSidebarSessionList } from "@/features/chat/features/session/components/chat-sidebar-session-list";
|
|
23
24
|
import { ChatSidebarUtilityMenu } from "@/features/chat/components/layout/chat-sidebar-utility-menu";
|
|
@@ -134,6 +135,7 @@ export function ChatSidebarSessionArea({
|
|
|
134
135
|
isCollapsed,
|
|
135
136
|
isLoading,
|
|
136
137
|
isProjectFirstView,
|
|
138
|
+
onCreateProject,
|
|
137
139
|
onSelectMode,
|
|
138
140
|
projectGroups,
|
|
139
141
|
renderSessionItem,
|
|
@@ -144,6 +146,7 @@ export function ChatSidebarSessionArea({
|
|
|
144
146
|
isCollapsed: boolean;
|
|
145
147
|
isLoading: boolean;
|
|
146
148
|
isProjectFirstView: boolean;
|
|
149
|
+
onCreateProject: () => void;
|
|
147
150
|
onSelectMode: (mode: "time-first" | "project-first") => void;
|
|
148
151
|
projectGroups: ReturnType<typeof groupSessionsByProject>;
|
|
149
152
|
renderSessionItem: (item: NcpSessionListItemView) => JSX.Element;
|
|
@@ -161,10 +164,19 @@ export function ChatSidebarSessionArea({
|
|
|
161
164
|
<div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground/75">
|
|
162
165
|
{t("chatSidebarTaskRecords")}
|
|
163
166
|
</div>
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
<div className="flex items-center gap-1">
|
|
168
|
+
{isProjectFirstView ? (
|
|
169
|
+
<IconActionButton
|
|
170
|
+
icon={<Plus className="h-3.5 w-3.5" />}
|
|
171
|
+
label={t("chatProjectCreate")}
|
|
172
|
+
onClick={onCreateProject}
|
|
173
|
+
/>
|
|
174
|
+
) : null}
|
|
175
|
+
<ChatSidebarListModeSwitch
|
|
176
|
+
isProjectFirstView={isProjectFirstView}
|
|
177
|
+
onSelectMode={onSelectMode}
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
168
180
|
</div>
|
|
169
181
|
|
|
170
182
|
<div
|
|
@@ -16,6 +16,9 @@ import { useChatSessionListStore } from "@/features/chat/stores/chat-session-lis
|
|
|
16
16
|
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
17
17
|
import { useSystemStatus } from "@/features/system-status";
|
|
18
18
|
import { useAgents } from "@/shared/hooks/use-agents";
|
|
19
|
+
import { useCreateProject, useProjects } from "@/shared/hooks/use-projects";
|
|
20
|
+
import type { ProjectCreateRequest } from "@/shared/lib/api";
|
|
21
|
+
import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
|
|
19
22
|
import { cn } from "@/shared/lib/utils";
|
|
20
23
|
import { LANGUAGE_OPTIONS, t, type I18nLanguage } from "@/shared/lib/i18n";
|
|
21
24
|
import { THEME_OPTIONS } from "@/shared/lib/theme";
|
|
@@ -42,6 +45,7 @@ import {
|
|
|
42
45
|
import { useChatNewSessionTypePreference } from "@/features/chat/features/session-type/hooks/use-chat-new-session-type-preference";
|
|
43
46
|
import { useViewportLayoutStore } from "@/app/stores/viewport-layout.store";
|
|
44
47
|
import { SIDEBAR_RAIL_WIDTH_CLASS } from "@/app/components/layout/sidebar-rail.styles";
|
|
48
|
+
import { ChatProjectCreateDialog } from "@/features/chat/features/project/components/chat-project-create-dialog";
|
|
45
49
|
|
|
46
50
|
type ChatSidebarVariant = "desktop" | "mobile";
|
|
47
51
|
|
|
@@ -94,13 +98,19 @@ export function ChatSidebar({
|
|
|
94
98
|
(state) => state.isSidebarCollapsed,
|
|
95
99
|
);
|
|
96
100
|
const [isCreateMenuOpen, setIsCreateMenuOpen] = useState(false);
|
|
101
|
+
const [isProjectCreateOpen, setIsProjectCreateOpen] = useState(false);
|
|
97
102
|
const [isUtilityMenuOpen, setIsUtilityMenuOpen] = useState(false);
|
|
98
103
|
const listSnapshot = useChatSessionListStore((state) => state.snapshot);
|
|
99
104
|
const sessionTypesData = useChatQueryStore(
|
|
100
105
|
(state) => state.snapshot.sessionTypesQuery?.data ?? null,
|
|
101
106
|
);
|
|
107
|
+
const config = useChatQueryStore(
|
|
108
|
+
(state) => state.snapshot.configQuery?.data ?? null,
|
|
109
|
+
);
|
|
102
110
|
const systemStatus = useSystemStatus();
|
|
103
111
|
const agentsQuery = useAgents();
|
|
112
|
+
const projectsQuery = useProjects();
|
|
113
|
+
const projectCreateMutation = useCreateProject();
|
|
104
114
|
const { isLoading, items } = useNcpSessionListView();
|
|
105
115
|
const { language, setLanguage } = useI18n();
|
|
106
116
|
const { theme, setTheme } = useTheme();
|
|
@@ -153,8 +163,13 @@ export function ChatSidebar({
|
|
|
153
163
|
[pinnedSessionKeys, sortedItems],
|
|
154
164
|
);
|
|
155
165
|
const projectGroups = useMemo(
|
|
156
|
-
() => groupSessionsByProject(
|
|
157
|
-
|
|
166
|
+
() => groupSessionsByProject(
|
|
167
|
+
sortedItems,
|
|
168
|
+
pinnedSessionKeys,
|
|
169
|
+
pinnedProjectRoots,
|
|
170
|
+
projectsQuery.data?.projects ?? [],
|
|
171
|
+
),
|
|
172
|
+
[pinnedProjectRoots, pinnedSessionKeys, projectsQuery.data?.projects, sortedItems],
|
|
158
173
|
);
|
|
159
174
|
const sessionTypeOptions = useMemo(
|
|
160
175
|
() => buildSessionTypeOptions(sessionTypesData?.options ?? []),
|
|
@@ -238,6 +253,14 @@ export function ChatSidebar({
|
|
|
238
253
|
typeof projectRoot === "string" ? projectRoot : undefined,
|
|
239
254
|
);
|
|
240
255
|
};
|
|
256
|
+
const openProjectCreate = () => {
|
|
257
|
+
projectCreateMutation.reset();
|
|
258
|
+
setIsProjectCreateOpen(true);
|
|
259
|
+
};
|
|
260
|
+
const createProjectFromSidebar = async (input: ProjectCreateRequest): Promise<void> => {
|
|
261
|
+
await projectCreateMutation.mutateAsync(input);
|
|
262
|
+
setIsProjectCreateOpen(false);
|
|
263
|
+
};
|
|
241
264
|
|
|
242
265
|
return (
|
|
243
266
|
<aside
|
|
@@ -310,6 +333,7 @@ export function ChatSidebar({
|
|
|
310
333
|
isCollapsed={shouldCollapse}
|
|
311
334
|
isLoading={isLoading}
|
|
312
335
|
isProjectFirstView={isProjectFirstView}
|
|
336
|
+
onCreateProject={openProjectCreate}
|
|
313
337
|
onSelectMode={presenter.chatSessionListManager.setListMode}
|
|
314
338
|
projectGroups={projectGroups}
|
|
315
339
|
renderSessionItem={renderSessionItem}
|
|
@@ -338,6 +362,18 @@ export function ChatSidebar({
|
|
|
338
362
|
themeOptions={utilityThemeOptions}
|
|
339
363
|
/>
|
|
340
364
|
) : null}
|
|
365
|
+
|
|
366
|
+
<ChatProjectCreateDialog
|
|
367
|
+
open={isProjectCreateOpen}
|
|
368
|
+
defaultWorkspacePath={normalizeSessionProjectRootValue(
|
|
369
|
+
config?.agents.defaults.workspace,
|
|
370
|
+
)}
|
|
371
|
+
templates={projectsQuery.data?.templates ?? []}
|
|
372
|
+
isCreating={projectCreateMutation.isPending}
|
|
373
|
+
errorMessage={projectCreateMutation.error?.message}
|
|
374
|
+
onOpenChange={setIsProjectCreateOpen}
|
|
375
|
+
onCreate={createProjectFromSidebar}
|
|
376
|
+
/>
|
|
341
377
|
</aside>
|
|
342
378
|
);
|
|
343
379
|
}
|
|
@@ -21,6 +21,7 @@ const mocks = vi.hoisted(() => {
|
|
|
21
21
|
removeAttachment: vi.fn(),
|
|
22
22
|
setSelectedModel: vi.fn(),
|
|
23
23
|
setSelectedThinkingLevel: vi.fn(),
|
|
24
|
+
syncSessionPreferences: vi.fn(),
|
|
24
25
|
setPendingSessionType: vi.fn(),
|
|
25
26
|
setPendingProjectRoot: vi.fn(),
|
|
26
27
|
setSelectedSkills: vi.fn(),
|
|
@@ -110,6 +110,42 @@ function createStreamingInputSnapshot(
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
function setImeDomText(textbox: HTMLElement, text: string): HTMLParagraphElement {
|
|
114
|
+
const paragraph = textbox.querySelector('p');
|
|
115
|
+
if (!paragraph) {
|
|
116
|
+
throw new Error('Expected the Lexical composer paragraph to exist.');
|
|
117
|
+
}
|
|
118
|
+
const range = document.createRange();
|
|
119
|
+
const lexicalText = paragraph.querySelector('[data-lexical-text="true"]')?.firstChild;
|
|
120
|
+
if (lexicalText?.nodeType === Node.TEXT_NODE) {
|
|
121
|
+
lexicalText.nodeValue = text;
|
|
122
|
+
range.setStart(lexicalText, text.length);
|
|
123
|
+
range.collapse(true);
|
|
124
|
+
} else {
|
|
125
|
+
paragraph.textContent = text;
|
|
126
|
+
range.selectNodeContents(paragraph);
|
|
127
|
+
range.collapse(false);
|
|
128
|
+
}
|
|
129
|
+
const selection = window.getSelection();
|
|
130
|
+
selection?.removeAllRanges();
|
|
131
|
+
selection?.addRange(range);
|
|
132
|
+
return paragraph;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function insertText(textbox: HTMLElement, text: string): Promise<void> {
|
|
136
|
+
await act(async () => {
|
|
137
|
+
for (const character of text) {
|
|
138
|
+
textbox.dispatchEvent(new InputEvent('beforeinput', {
|
|
139
|
+
bubbles: true,
|
|
140
|
+
cancelable: true,
|
|
141
|
+
data: character,
|
|
142
|
+
inputType: 'insertText',
|
|
143
|
+
}));
|
|
144
|
+
await Promise.resolve();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
113
149
|
function StreamingSessionConversationInputHarness({
|
|
114
150
|
controllerOverride = controller,
|
|
115
151
|
controlRef,
|
|
@@ -145,6 +181,7 @@ function StreamingSessionConversationInputHarness({
|
|
|
145
181
|
setSelectedModel: vi.fn(),
|
|
146
182
|
setSelectedSkills: vi.fn(),
|
|
147
183
|
setSelectedThinkingLevel: vi.fn(),
|
|
184
|
+
syncSessionPreferences: vi.fn(),
|
|
148
185
|
setSendError: vi.fn(),
|
|
149
186
|
syncComposer: vi.fn(),
|
|
150
187
|
update: (patch: SessionConversationInputPatch) => {
|
|
@@ -209,23 +246,36 @@ describe('SessionConversationInput streaming stability', () => {
|
|
|
209
246
|
expect(track?.firstElementChild?.className).toContain('px-0');
|
|
210
247
|
});
|
|
211
248
|
|
|
212
|
-
it('keeps IME
|
|
249
|
+
it('keeps a numbered IME candidate commit stable while streamed output rerenders the owner', async () => {
|
|
213
250
|
const controlRef: MutableRefObject<StreamingInputControl | null> = { current: null };
|
|
214
251
|
render(<StreamingSessionConversationInputHarness controlRef={controlRef} />);
|
|
215
252
|
|
|
216
253
|
const textbox = screen.getByRole('textbox');
|
|
217
254
|
fireEvent.focus(textbox);
|
|
255
|
+
await insertText(textbox, 'n');
|
|
218
256
|
fireEvent.compositionStart(textbox);
|
|
257
|
+
const paragraph = setImeDomText(textbox, 'ni');
|
|
219
258
|
|
|
220
259
|
act(() => {
|
|
221
260
|
controlRef.current?.bumpStream();
|
|
222
261
|
controlRef.current?.bumpStream();
|
|
223
262
|
});
|
|
224
263
|
expect(screen.getByTestId('stream-chunk').textContent).toBe('2');
|
|
225
|
-
|
|
264
|
+
expect(textbox.querySelector('p')).toBe(paragraph);
|
|
265
|
+
expect(textbox.textContent).toBe('ni');
|
|
266
|
+
|
|
267
|
+
fireEvent.keyDown(textbox, { key: '1', isComposing: true });
|
|
268
|
+
setImeDomText(textbox, '你');
|
|
269
|
+
fireEvent.input(textbox, {
|
|
270
|
+
data: '你',
|
|
271
|
+
inputType: 'insertCompositionText',
|
|
272
|
+
isComposing: true,
|
|
273
|
+
});
|
|
226
274
|
fireEvent.compositionEnd(textbox, { data: '你' });
|
|
227
275
|
|
|
228
276
|
await waitFor(() => expect(textbox.textContent).toBe('你'));
|
|
277
|
+
expect(textbox.querySelector('p')).toBe(paragraph);
|
|
278
|
+
await waitFor(() => expect(window.getSelection()?.anchorOffset).toBe(1));
|
|
229
279
|
});
|
|
230
280
|
|
|
231
281
|
it('renders queued inputs above the composer with edit and delete actions', () => {
|
|
@@ -188,6 +188,28 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
|
|
|
188
188
|
inputSnapshot,
|
|
189
189
|
setPendingSessionType: inputActions.setPendingSessionType,
|
|
190
190
|
});
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
inputActions.syncSessionPreferences({
|
|
193
|
+
defaultModel: inputQuery.defaultModel,
|
|
194
|
+
fallbackPreferredModel: inputQuery.fallbackPreferredModel,
|
|
195
|
+
fallbackPreferredThinking: inputQuery.fallbackPreferredThinking,
|
|
196
|
+
modelOptions: inputQuery.modelOptions,
|
|
197
|
+
selectedSessionExists: Boolean(inputQuery.selectedSession),
|
|
198
|
+
selectedSessionKey: inputQuery.selectedSessionKey,
|
|
199
|
+
selectedSessionPreferredModel:
|
|
200
|
+
inputQuery.selectedSession?.preferredModel ?? undefined,
|
|
201
|
+
selectedSessionPreferredThinking:
|
|
202
|
+
inputQuery.selectedSession?.preferredThinking ?? null,
|
|
203
|
+
});
|
|
204
|
+
}, [
|
|
205
|
+
inputActions,
|
|
206
|
+
inputQuery.defaultModel,
|
|
207
|
+
inputQuery.fallbackPreferredModel,
|
|
208
|
+
inputQuery.fallbackPreferredThinking,
|
|
209
|
+
inputQuery.modelOptions,
|
|
210
|
+
inputQuery.selectedSession,
|
|
211
|
+
inputQuery.selectedSessionKey,
|
|
212
|
+
]);
|
|
191
213
|
useSessionConversationDraftRouteState({
|
|
192
214
|
sessionKey,
|
|
193
215
|
setPendingProjectRoot: inputActions.setPendingProjectRoot,
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
import { SessionQueuedInputRows } from './session-queued-input-rows';
|
|
68
68
|
|
|
69
69
|
type SessionConversationInputQuery = ReturnType<typeof useSessionConversationInputQuery>;
|
|
70
|
+
type SkillSource = SessionSkillEntryView['source'];
|
|
70
71
|
|
|
71
72
|
export type SessionConversationInputController = {
|
|
72
73
|
readonly canStopGeneration: boolean;
|
|
@@ -82,15 +83,18 @@ export type SessionConversationInputController = {
|
|
|
82
83
|
|
|
83
84
|
function toSkillRecords(
|
|
84
85
|
snapshotRecords: SessionSkillEntryView[],
|
|
85
|
-
scopeLabels: Record<
|
|
86
|
+
scopeLabels: Record<SkillSource, string>,
|
|
87
|
+
groupLabels: Record<SkillSource, string>,
|
|
86
88
|
): ChatSkillRecord[] {
|
|
87
89
|
return snapshotRecords.map((record) => ({
|
|
88
90
|
key: record.ref,
|
|
89
91
|
label: record.name,
|
|
90
|
-
scopeLabel: scopeLabels[record.
|
|
92
|
+
scopeLabel: scopeLabels[record.source],
|
|
93
|
+
groupKey: record.source,
|
|
94
|
+
groupLabel: groupLabels[record.source],
|
|
91
95
|
description: record.description,
|
|
92
96
|
descriptionZh: record.descriptionZh,
|
|
93
|
-
badgeLabel: scopeLabels[record.
|
|
97
|
+
badgeLabel: scopeLabels[record.source],
|
|
94
98
|
}));
|
|
95
99
|
}
|
|
96
100
|
|
|
@@ -109,12 +113,21 @@ function toModelRecords(snapshotModels: SessionConversationInputQuery['modelOpti
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
function useSessionConversationInputLabels(language: string) {
|
|
112
|
-
const skillScopeLabels = useMemo
|
|
116
|
+
const { skillGroupLabels, skillScopeLabels } = useMemo(() => {
|
|
113
117
|
void language;
|
|
114
118
|
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
skillScopeLabels: {
|
|
120
|
+
builtin: t('chatSkillScopeBuiltin'),
|
|
121
|
+
global: t('chatSkillScopeGlobal'),
|
|
122
|
+
project: t('chatSkillScopeProject'),
|
|
123
|
+
workspace: t('chatSkillScopeWorkspace'),
|
|
124
|
+
} satisfies Record<SkillSource, string>,
|
|
125
|
+
skillGroupLabels: {
|
|
126
|
+
builtin: t('chatSkillGroupBuiltin'),
|
|
127
|
+
global: t('chatSkillGroupGlobal'),
|
|
128
|
+
project: t('chatSkillGroupProject'),
|
|
129
|
+
workspace: t('chatSkillGroupWorkspace'),
|
|
130
|
+
} satisfies Record<SkillSource, string>,
|
|
118
131
|
};
|
|
119
132
|
}, [language]);
|
|
120
133
|
const slashTexts = useMemo(() => {
|
|
@@ -128,6 +141,7 @@ function useSessionConversationInputLabels(language: string) {
|
|
|
128
141
|
}, [language]);
|
|
129
142
|
return {
|
|
130
143
|
skillScopeLabels,
|
|
144
|
+
skillGroupLabels,
|
|
131
145
|
slashTexts,
|
|
132
146
|
recentModelsLabel: t('chatPickerRecentModels'),
|
|
133
147
|
allModelsLabel: t('chatPickerAllModels'),
|
|
@@ -144,13 +158,15 @@ function useSessionConversationInputLabels(language: string) {
|
|
|
144
158
|
function useSessionConversationInputCollections(params: {
|
|
145
159
|
modelOptions: SessionConversationInputQuery['modelOptions'];
|
|
146
160
|
skillRecords: SessionSkillEntryView[];
|
|
147
|
-
skillScopeLabels: Record<
|
|
161
|
+
skillScopeLabels: Record<SkillSource, string>;
|
|
162
|
+
skillGroupLabels: Record<SkillSource, string>;
|
|
148
163
|
}) {
|
|
164
|
+
const { modelOptions, skillGroupLabels, skillRecords: sourceSkillRecords, skillScopeLabels } = params;
|
|
149
165
|
const skillRecords = useMemo(
|
|
150
|
-
() => toSkillRecords(
|
|
151
|
-
[
|
|
166
|
+
() => toSkillRecords(sourceSkillRecords, skillScopeLabels, skillGroupLabels),
|
|
167
|
+
[skillGroupLabels, skillScopeLabels, sourceSkillRecords],
|
|
152
168
|
);
|
|
153
|
-
const modelRecords = useMemo(() => toModelRecords(
|
|
169
|
+
const modelRecords = useMemo(() => toModelRecords(modelOptions), [modelOptions]);
|
|
154
170
|
return {
|
|
155
171
|
skillRecords,
|
|
156
172
|
modelRecords,
|
|
@@ -203,6 +219,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
203
219
|
} = useSessionConversationInputCollections({
|
|
204
220
|
modelOptions: inputQuery.modelOptions,
|
|
205
221
|
skillRecords: inputQuery.skillRecords,
|
|
222
|
+
skillGroupLabels: labels.skillGroupLabels,
|
|
206
223
|
skillScopeLabels: labels.skillScopeLabels,
|
|
207
224
|
});
|
|
208
225
|
const slashCommands = useMemo(() => {
|
|
@@ -232,6 +249,9 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
232
249
|
}),
|
|
233
250
|
[presenter.chatUiManager],
|
|
234
251
|
);
|
|
252
|
+
const contextReferenceProjectRoot = inputQuery.selectedSessionKey
|
|
253
|
+
? inputQuery.selectedSession?.projectRoot ?? inputQuery.defaultProjectRoot ?? ''
|
|
254
|
+
: inputSnapshot.pendingProjectRoot ?? inputQuery.defaultProjectRoot ?? '';
|
|
235
255
|
const { inputSurfaceState, setInputSurfaceTrigger } = useChatInputSurfaceState({
|
|
236
256
|
commands: slashCommands,
|
|
237
257
|
isSkillsLoading: inputQuery.isSkillsLoading,
|
|
@@ -241,6 +261,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
241
261
|
language,
|
|
242
262
|
onSelectPanelApp: handleSlashPanelAppSelect,
|
|
243
263
|
onSelectSkill: chatRecentSkillsManager.remember,
|
|
264
|
+
projectRoot: contextReferenceProjectRoot,
|
|
244
265
|
recentSkillValues,
|
|
245
266
|
skillRecords,
|
|
246
267
|
});
|
|
@@ -252,15 +273,9 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
252
273
|
favoriteModelValues,
|
|
253
274
|
setModelFavorite,
|
|
254
275
|
} = useChatModelFavorites(modelRecordValues);
|
|
255
|
-
const selectedModel =
|
|
256
|
-
inputSnapshot.selectedModel ??
|
|
257
|
-
inputQuery.fallbackPreferredModel ??
|
|
258
|
-
inputQuery.defaultModel ??
|
|
259
|
-
'';
|
|
276
|
+
const selectedModel = inputSnapshot.selectedModel ?? '';
|
|
260
277
|
const selectedThinkingLevel = (
|
|
261
|
-
inputSnapshot.selectedThinkingLevel
|
|
262
|
-
inputQuery.fallbackPreferredThinking ??
|
|
263
|
-
null
|
|
278
|
+
inputSnapshot.selectedThinkingLevel
|
|
264
279
|
) as ChatThinkingLevel | null;
|
|
265
280
|
const availabilitySnapshot = {
|
|
266
281
|
isProviderStateResolved: inputQuery.isProviderStateResolved,
|
|
@@ -391,6 +406,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
391
406
|
skillRecords,
|
|
392
407
|
selectedSkills: inputSnapshot.selectedSkills,
|
|
393
408
|
});
|
|
409
|
+
const composerNodes = useMemo(() => [...inputSnapshot.nodes], [inputSnapshot.nodes]);
|
|
394
410
|
|
|
395
411
|
const useReadingTrack = surface === 'default' && messageLayout === 'flat';
|
|
396
412
|
const inputBar = (
|
|
@@ -403,7 +419,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
403
419
|
? <SessionQueuedInputRows controller={controller} />
|
|
404
420
|
: null}
|
|
405
421
|
composer={{
|
|
406
|
-
nodes:
|
|
422
|
+
nodes: composerNodes,
|
|
407
423
|
placeholder: textareaPlaceholder,
|
|
408
424
|
disabled: inputDisabled,
|
|
409
425
|
onNodesChange: handleNodesChange,
|