@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,10 +1,20 @@
|
|
|
1
1
|
import type { UpdateSnapshot } from '@nextclaw/shared';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
runtimeUpdateManager,
|
|
4
|
+
resolveUpdateReleaseNotesLink,
|
|
5
|
+
useCurrentVersionReleaseNotesLink,
|
|
6
|
+
useRuntimeUpdateStore,
|
|
7
|
+
type ReleaseNotesLink,
|
|
8
|
+
type RuntimeUpdateBusyAction
|
|
9
|
+
} from '@/features/system-status';
|
|
3
10
|
import { useAppMeta } from '@/shared/hooks/use-app-meta';
|
|
4
|
-
import {
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
5
12
|
import { RuntimeStatusEntry } from '@/app/components/layout/runtime-status-entry';
|
|
13
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/shared/components/ui/tooltip';
|
|
14
|
+
import { hostCapabilityManager } from '@/shared/lib/host-capabilities';
|
|
6
15
|
import { t } from '@/shared/lib/i18n';
|
|
7
16
|
import { cn } from '@/shared/lib/utils';
|
|
17
|
+
import { ExternalLink } from 'lucide-react';
|
|
8
18
|
|
|
9
19
|
type BrandHeaderProps = {
|
|
10
20
|
className?: string;
|
|
@@ -14,9 +24,11 @@ type BrandHeaderProps = {
|
|
|
14
24
|
|
|
15
25
|
export function BrandHeader({ className, density = 'sidebar', suffix }: BrandHeaderProps) {
|
|
16
26
|
const { data } = useAppMeta();
|
|
27
|
+
const { supported, busyAction, snapshot } = useRuntimeUpdateStore();
|
|
17
28
|
const productName = data?.name ?? 'NextClaw';
|
|
18
29
|
const productVersion = data?.productVersion?.trim();
|
|
19
30
|
const versionLabel = productVersion ? `v${productVersion}` : null;
|
|
31
|
+
const releaseNotesLink = useCurrentVersionReleaseNotesLink(productVersion);
|
|
20
32
|
const resolvedSuffix = suffix ?? <RuntimeStatusEntry />;
|
|
21
33
|
const shouldReserveMacWindowControls = typeof window !== 'undefined' && window.nextclawDesktop?.platform === 'darwin';
|
|
22
34
|
const isChromeDensity = density === 'chrome';
|
|
@@ -41,9 +53,9 @@ export function BrandHeader({ className, density = 'sidebar', suffix }: BrandHea
|
|
|
41
53
|
>
|
|
42
54
|
{productName}
|
|
43
55
|
</span>
|
|
44
|
-
{versionLabel ? <BrandVersionLabel versionLabel={versionLabel} density={density} /> : null}
|
|
56
|
+
{versionLabel ? <BrandVersionLabel versionLabel={versionLabel} density={density} releaseNotesLink={releaseNotesLink} /> : null}
|
|
45
57
|
</div>
|
|
46
|
-
<RuntimeUpdateInlineStatus />
|
|
58
|
+
<RuntimeUpdateInlineStatus supported={supported} busyAction={busyAction} snapshot={snapshot} />
|
|
47
59
|
{resolvedSuffix ? <span className="inline-flex items-center shrink-0">{resolvedSuffix}</span> : null}
|
|
48
60
|
</div>
|
|
49
61
|
</div>
|
|
@@ -53,75 +65,143 @@ export function BrandHeader({ className, density = 'sidebar', suffix }: BrandHea
|
|
|
53
65
|
function BrandVersionLabel({
|
|
54
66
|
versionLabel,
|
|
55
67
|
density,
|
|
68
|
+
releaseNotesLink,
|
|
56
69
|
}: {
|
|
57
70
|
versionLabel: string;
|
|
58
71
|
density: BrandHeaderProps['density'];
|
|
72
|
+
releaseNotesLink: ReleaseNotesLink | null;
|
|
59
73
|
}) {
|
|
60
|
-
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
|
61
74
|
const isChromeDensity = density === 'chrome';
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
const triggerClassName = cn(
|
|
76
|
+
'block min-w-0 flex-1 truncate font-medium text-gray-500 outline-none',
|
|
77
|
+
isChromeDensity ? 'text-[12px]' : 'text-[12px]',
|
|
78
|
+
releaseNotesLink && 'cursor-pointer rounded-sm transition-colors hover:text-gray-700 hover:underline focus-visible:ring-2 focus-visible:ring-gray-300'
|
|
79
|
+
);
|
|
80
|
+
const tooltip = releaseNotesLink
|
|
81
|
+
? [
|
|
82
|
+
t('desktopUpdatesVersionTooltipCurrent').replace('{version}', versionLabel),
|
|
83
|
+
t('desktopUpdatesVersionTooltipReleaseNotes').replace('{version}', releaseNotesLink.versionLabel)
|
|
84
|
+
].join('\n')
|
|
85
|
+
: versionLabel;
|
|
86
|
+
const trigger = releaseNotesLink ? (
|
|
87
|
+
<button
|
|
88
|
+
type="button"
|
|
89
|
+
aria-label={t('desktopUpdatesVersionReleaseNotesLabel').replace('{currentVersion}', versionLabel).replace('{releaseVersion}', releaseNotesLink.versionLabel)}
|
|
90
|
+
className={cn(triggerClassName, 'border-0 bg-transparent p-0 text-left')}
|
|
91
|
+
onClick={() => void hostCapabilityManager.openExternalUrl(releaseNotesLink.url)}
|
|
70
92
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
isChromeDensity ? 'text-[12px]' : 'text-[12px]',
|
|
77
|
-
)}
|
|
78
|
-
>
|
|
79
|
-
{versionLabel}
|
|
80
|
-
</span>
|
|
81
|
-
{isTooltipOpen ? (
|
|
82
|
-
<span className="pointer-events-none absolute left-0 top-full z-[var(--z-tooltip)] mt-1 w-max max-w-none whitespace-nowrap rounded-md border bg-popover px-3 py-1.5 text-xs font-medium text-popover-foreground shadow-md">
|
|
83
|
-
{versionLabel}
|
|
84
|
-
</span>
|
|
85
|
-
) : null}
|
|
93
|
+
{versionLabel}
|
|
94
|
+
</button>
|
|
95
|
+
) : (
|
|
96
|
+
<span tabIndex={0} aria-label={versionLabel} className={triggerClassName}>
|
|
97
|
+
{versionLabel}
|
|
86
98
|
</span>
|
|
87
99
|
);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<TooltipProvider delayDuration={250}>
|
|
103
|
+
<Tooltip>
|
|
104
|
+
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
|
|
105
|
+
<TooltipContent side="bottom" className="max-w-72 whitespace-pre-line text-xs leading-relaxed">{tooltip}</TooltipContent>
|
|
106
|
+
</Tooltip>
|
|
107
|
+
</TooltipProvider>
|
|
108
|
+
);
|
|
88
109
|
}
|
|
89
110
|
|
|
90
|
-
function RuntimeUpdateInlineStatus(
|
|
91
|
-
|
|
111
|
+
function RuntimeUpdateInlineStatus({
|
|
112
|
+
supported,
|
|
113
|
+
busyAction,
|
|
114
|
+
snapshot,
|
|
115
|
+
}: {
|
|
116
|
+
supported: boolean;
|
|
117
|
+
busyAction: RuntimeUpdateBusyAction;
|
|
118
|
+
snapshot: UpdateSnapshot | null;
|
|
119
|
+
}) {
|
|
92
120
|
if (!supported || !snapshot) {
|
|
93
121
|
return null;
|
|
94
122
|
}
|
|
95
|
-
|
|
123
|
+
const releaseNotesLink = resolveUpdateReleaseNotesLink(snapshot);
|
|
124
|
+
if (snapshot.status === 'downloading') {
|
|
125
|
+
return (
|
|
126
|
+
<RuntimeUpdateInlineGroup>
|
|
127
|
+
<RuntimeUpdateInlineBadge snapshot={snapshot} />
|
|
128
|
+
{releaseNotesLink ? <RuntimeUpdateReleaseNotesButton link={releaseNotesLink} tone="amber" /> : null}
|
|
129
|
+
</RuntimeUpdateInlineGroup>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (snapshot.status === 'blocked' || snapshot.status === 'failed') {
|
|
96
133
|
return <RuntimeUpdateInlineBadge snapshot={snapshot} />;
|
|
97
134
|
}
|
|
98
135
|
if (snapshot.status === 'downloaded') {
|
|
99
136
|
return (
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
137
|
+
<RuntimeUpdateInlineGroup>
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
className="inline-flex h-5 shrink-0 items-center rounded-full bg-emerald-50 px-2 text-[11px] font-semibold leading-none text-emerald-700 ring-1 ring-emerald-100 transition-colors hover:bg-emerald-100 disabled:opacity-70"
|
|
141
|
+
disabled={busyAction === 'applying'}
|
|
142
|
+
onClick={() => void runtimeUpdateManager.applyDownloadedUpdate()}
|
|
143
|
+
>
|
|
144
|
+
{busyAction === 'applying' ? t('desktopUpdatesInlineApplying') : t('desktopUpdatesInlineReady')}
|
|
145
|
+
</button>
|
|
146
|
+
{releaseNotesLink ? <RuntimeUpdateReleaseNotesButton link={releaseNotesLink} tone="emerald" /> : null}
|
|
147
|
+
</RuntimeUpdateInlineGroup>
|
|
108
148
|
);
|
|
109
149
|
}
|
|
110
150
|
if (snapshot.status === 'update-available') {
|
|
111
151
|
return (
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
152
|
+
<RuntimeUpdateInlineGroup>
|
|
153
|
+
<button
|
|
154
|
+
type="button"
|
|
155
|
+
className="inline-flex h-5 shrink-0 items-center rounded-full bg-amber-50 px-2 text-[11px] font-semibold leading-none text-amber-700 ring-1 ring-amber-100 transition-colors hover:bg-amber-100 disabled:opacity-70"
|
|
156
|
+
disabled={busyAction === 'downloading'}
|
|
157
|
+
onClick={() => void runtimeUpdateManager.downloadUpdate()}
|
|
158
|
+
>
|
|
159
|
+
{busyAction === 'downloading' ? t('desktopUpdatesInlineDownloading') : t('desktopUpdatesInlineDownload')}
|
|
160
|
+
</button>
|
|
161
|
+
{releaseNotesLink ? <RuntimeUpdateReleaseNotesButton link={releaseNotesLink} tone="amber" /> : null}
|
|
162
|
+
</RuntimeUpdateInlineGroup>
|
|
120
163
|
);
|
|
121
164
|
}
|
|
122
165
|
return null;
|
|
123
166
|
}
|
|
124
167
|
|
|
168
|
+
function RuntimeUpdateInlineGroup({ children }: { children: ReactNode }) {
|
|
169
|
+
return <span className="inline-flex shrink-0 items-center gap-1">{children}</span>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function RuntimeUpdateReleaseNotesButton({
|
|
173
|
+
link,
|
|
174
|
+
tone,
|
|
175
|
+
}: {
|
|
176
|
+
link: ReleaseNotesLink;
|
|
177
|
+
tone: 'amber' | 'emerald';
|
|
178
|
+
}) {
|
|
179
|
+
const label = t('desktopUpdatesUpdateReleaseNotesLabel').replace('{version}', link.versionLabel);
|
|
180
|
+
const className = cn(
|
|
181
|
+
'inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[11px] ring-1 transition-colors focus-visible:outline-none focus-visible:ring-2',
|
|
182
|
+
tone === 'emerald'
|
|
183
|
+
? 'bg-emerald-50 text-emerald-700 ring-emerald-100 hover:bg-emerald-100 focus-visible:ring-emerald-300'
|
|
184
|
+
: 'bg-amber-50 text-amber-700 ring-amber-100 hover:bg-amber-100 focus-visible:ring-amber-300'
|
|
185
|
+
);
|
|
186
|
+
return (
|
|
187
|
+
<TooltipProvider delayDuration={250}>
|
|
188
|
+
<Tooltip>
|
|
189
|
+
<TooltipTrigger asChild>
|
|
190
|
+
<button
|
|
191
|
+
type="button"
|
|
192
|
+
aria-label={label}
|
|
193
|
+
className={className}
|
|
194
|
+
onClick={() => void hostCapabilityManager.openExternalUrl(link.url)}
|
|
195
|
+
>
|
|
196
|
+
<ExternalLink className="h-3 w-3" aria-hidden="true" />
|
|
197
|
+
</button>
|
|
198
|
+
</TooltipTrigger>
|
|
199
|
+
<TooltipContent side="bottom" className="text-xs">{label}</TooltipContent>
|
|
200
|
+
</Tooltip>
|
|
201
|
+
</TooltipProvider>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
125
205
|
function RuntimeUpdateInlineBadge({ snapshot }: { snapshot: UpdateSnapshot }) {
|
|
126
206
|
if (snapshot.status === 'blocked' || snapshot.status === 'failed') {
|
|
127
207
|
return <RuntimeUpdateIssueIcon snapshot={snapshot} />;
|
|
@@ -149,15 +229,21 @@ function RuntimeUpdateIssueIcon({ snapshot }: { snapshot: UpdateSnapshot }) {
|
|
|
149
229
|
: null;
|
|
150
230
|
const tooltip = [title, rootCause, diagnostic, recoveryCommand].filter(Boolean).join('\n');
|
|
151
231
|
return (
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
232
|
+
<TooltipProvider delayDuration={250}>
|
|
233
|
+
<Tooltip>
|
|
234
|
+
<TooltipTrigger asChild>
|
|
235
|
+
<span
|
|
236
|
+
role="img"
|
|
237
|
+
aria-label={title}
|
|
238
|
+
tabIndex={0}
|
|
239
|
+
className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-amber-50 text-[13px] font-bold leading-none text-amber-700 ring-1 ring-amber-100 outline-none focus-visible:ring-2 focus-visible:ring-amber-300"
|
|
240
|
+
>
|
|
241
|
+
!
|
|
242
|
+
</span>
|
|
243
|
+
</TooltipTrigger>
|
|
244
|
+
<TooltipContent side="bottom" className="max-w-80 whitespace-pre-line break-words text-xs leading-relaxed">{tooltip}</TooltipContent>
|
|
245
|
+
</Tooltip>
|
|
246
|
+
</TooltipProvider>
|
|
161
247
|
);
|
|
162
248
|
}
|
|
163
249
|
|
|
@@ -5,6 +5,9 @@ import { setLanguage } from '@/shared/lib/i18n';
|
|
|
5
5
|
|
|
6
6
|
const mocks = vi.hoisted(() => ({
|
|
7
7
|
useServerPathBrowse: vi.fn(),
|
|
8
|
+
useServerPathCreateDirectory: vi.fn(),
|
|
9
|
+
createDirectory: vi.fn(),
|
|
10
|
+
resetCreateDirectory: vi.fn(),
|
|
8
11
|
refetch: vi.fn(),
|
|
9
12
|
}));
|
|
10
13
|
|
|
@@ -12,21 +15,75 @@ vi.mock('@/shared/hooks/use-server-path-browse', () => ({
|
|
|
12
15
|
useServerPathBrowse: mocks.useServerPathBrowse,
|
|
13
16
|
}));
|
|
14
17
|
|
|
18
|
+
vi.mock('@/shared/hooks/use-server-path-create-directory', () => ({
|
|
19
|
+
useServerPathCreateDirectory: mocks.useServerPathCreateDirectory,
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
type PickerOverrides = {
|
|
23
|
+
currentPath?: string;
|
|
24
|
+
defaultWorkspacePath?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
open?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createPickerElement = (overrides?: PickerOverrides) => (
|
|
30
|
+
<ServerPathPickerDialog
|
|
31
|
+
open={overrides?.open ?? true}
|
|
32
|
+
currentPath={overrides?.currentPath ?? '/workspace'}
|
|
33
|
+
defaultWorkspacePath={overrides?.defaultWorkspacePath}
|
|
34
|
+
isSaving={false}
|
|
35
|
+
onOpenChange={() => {}}
|
|
36
|
+
onConfirm={() => {}}
|
|
37
|
+
title="选择目录"
|
|
38
|
+
description={overrides?.description ?? '选择一个目录'}
|
|
39
|
+
pathLabel="目录"
|
|
40
|
+
confirmLabel="确定"
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const renderPicker = (overrides?: PickerOverrides) => render(createPickerElement(overrides));
|
|
45
|
+
|
|
15
46
|
describe('ServerPathPickerDialog', () => {
|
|
16
47
|
beforeEach(() => {
|
|
17
48
|
setLanguage('zh');
|
|
18
49
|
mocks.refetch.mockReset();
|
|
50
|
+
mocks.refetch.mockResolvedValue(undefined);
|
|
51
|
+
mocks.createDirectory.mockReset();
|
|
52
|
+
mocks.createDirectory.mockResolvedValue({ path: '/workspace/new-folder' });
|
|
53
|
+
mocks.resetCreateDirectory.mockReset();
|
|
54
|
+
mocks.useServerPathCreateDirectory.mockReturnValue({
|
|
55
|
+
mutateAsync: mocks.createDirectory,
|
|
56
|
+
reset: mocks.resetCreateDirectory,
|
|
57
|
+
isPending: false,
|
|
58
|
+
error: null,
|
|
59
|
+
});
|
|
19
60
|
mocks.useServerPathBrowse.mockReturnValue({
|
|
20
61
|
data: {
|
|
21
62
|
currentPath: '/workspace',
|
|
22
63
|
homePath: '/Users/peiwang',
|
|
23
64
|
parentPath: '/',
|
|
24
|
-
breadcrumbs: [
|
|
25
|
-
{ label: 'workspace', path: '/workspace' },
|
|
26
|
-
],
|
|
65
|
+
breadcrumbs: [{ label: 'workspace', path: '/workspace' }],
|
|
27
66
|
entries: [
|
|
28
|
-
{
|
|
29
|
-
|
|
67
|
+
{
|
|
68
|
+
name: 'playground',
|
|
69
|
+
path: '/workspace/playground',
|
|
70
|
+
kind: 'directory',
|
|
71
|
+
hidden: false,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'nextbot',
|
|
75
|
+
path: '/workspace/nextbot',
|
|
76
|
+
kind: 'directory',
|
|
77
|
+
hidden: false,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
locations: [
|
|
81
|
+
{ kind: 'desktop', path: '/Users/peiwang/Desktop' },
|
|
82
|
+
{ kind: 'documents', path: '/Users/peiwang/Documents' },
|
|
83
|
+
{ kind: 'downloads', path: '/Users/peiwang/Downloads' },
|
|
84
|
+
{ kind: 'icloud-drive', path: '/Users/peiwang/Library/Mobile Documents/com~apple~CloudDocs' },
|
|
85
|
+
{ kind: 'applications', path: '/Applications' },
|
|
86
|
+
{ kind: 'volumes', path: '/Volumes' },
|
|
30
87
|
],
|
|
31
88
|
},
|
|
32
89
|
isLoading: false,
|
|
@@ -37,55 +94,248 @@ describe('ServerPathPickerDialog', () => {
|
|
|
37
94
|
|
|
38
95
|
it('filters entries inside the current directory', async () => {
|
|
39
96
|
const user = userEvent.setup();
|
|
40
|
-
|
|
41
|
-
render(
|
|
42
|
-
<ServerPathPickerDialog
|
|
43
|
-
open
|
|
44
|
-
currentPath="/workspace"
|
|
45
|
-
isSaving={false}
|
|
46
|
-
onOpenChange={() => {}}
|
|
47
|
-
onConfirm={() => {}}
|
|
48
|
-
title="选择目录"
|
|
49
|
-
description="选择一个目录"
|
|
50
|
-
pathLabel="目录"
|
|
51
|
-
confirmLabel="确定"
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
97
|
+
renderPicker();
|
|
54
98
|
|
|
55
99
|
expect(screen.getByText('playground')).toBeTruthy();
|
|
56
100
|
expect(screen.getByText('nextbot')).toBeTruthy();
|
|
57
101
|
|
|
58
|
-
await user.type(screen.
|
|
102
|
+
await user.type(screen.getAllByPlaceholderText('搜索当前目录')[0], 'play');
|
|
59
103
|
|
|
60
104
|
expect(screen.getByText('playground')).toBeTruthy();
|
|
61
105
|
expect(screen.queryByText('nextbot')).toBeNull();
|
|
62
106
|
});
|
|
63
107
|
|
|
64
|
-
it('shows
|
|
108
|
+
it('shows server-backed Mac locations and opens sidebar locations with one click', async () => {
|
|
65
109
|
const user = userEvent.setup();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
renderPicker();
|
|
111
|
+
|
|
112
|
+
expect(screen.getByRole('button', { name: '桌面' })).toBeTruthy();
|
|
113
|
+
expect(screen.getByRole('button', { name: '文稿' })).toBeTruthy();
|
|
114
|
+
expect(screen.getByRole('button', { name: '下载' })).toBeTruthy();
|
|
115
|
+
expect(screen.getByRole('button', { name: 'iCloud 云盘' })).toBeTruthy();
|
|
116
|
+
expect(screen.getByRole('button', { name: '应用程序' })).toBeTruthy();
|
|
117
|
+
expect(screen.getByRole('button', { name: '磁盘与卷' })).toBeTruthy();
|
|
118
|
+
|
|
119
|
+
await user.click(screen.getByRole('button', { name: '下载' }));
|
|
120
|
+
|
|
121
|
+
expect(mocks.useServerPathBrowse).toHaveBeenLastCalledWith({
|
|
122
|
+
path: '/Users/peiwang/Downloads',
|
|
123
|
+
enabled: true,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('keeps the explicit current path when an older home browse result is still visible', () => {
|
|
128
|
+
mocks.useServerPathBrowse.mockReturnValue({
|
|
129
|
+
data: {
|
|
130
|
+
currentPath: '/Users/peiwang',
|
|
131
|
+
homePath: '/Users/peiwang',
|
|
132
|
+
parentPath: '/Users',
|
|
133
|
+
breadcrumbs: [
|
|
134
|
+
{ label: 'Users', path: '/Users' },
|
|
135
|
+
{ label: 'peiwang', path: '/Users/peiwang' },
|
|
136
|
+
],
|
|
137
|
+
entries: [],
|
|
138
|
+
},
|
|
139
|
+
isLoading: false,
|
|
140
|
+
error: null,
|
|
141
|
+
refetch: mocks.refetch,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
renderPicker({ currentPath: '/Users/peiwang/.nextclaw/workspace' });
|
|
145
|
+
|
|
146
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
147
|
+
'/Users/peiwang/.nextclaw/workspace',
|
|
78
148
|
);
|
|
149
|
+
expect(screen.getByText('/Users/peiwang/.nextclaw/workspace')).toBeTruthy();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('starts at the workdir and keeps the canonical address and selected folder aligned', () => {
|
|
153
|
+
mocks.useServerPathBrowse.mockReturnValue({
|
|
154
|
+
data: {
|
|
155
|
+
currentPath: '/Users/peiwang/.nextclaw/workspace',
|
|
156
|
+
homePath: '/Users/peiwang',
|
|
157
|
+
parentPath: '/Users/peiwang/.nextclaw',
|
|
158
|
+
breadcrumbs: [
|
|
159
|
+
{ label: '/', path: '/' },
|
|
160
|
+
{ label: 'Users', path: '/Users' },
|
|
161
|
+
{ label: 'peiwang', path: '/Users/peiwang' },
|
|
162
|
+
{ label: '.nextclaw', path: '/Users/peiwang/.nextclaw' },
|
|
163
|
+
{ label: 'workspace', path: '/Users/peiwang/.nextclaw/workspace' },
|
|
164
|
+
],
|
|
165
|
+
entries: [],
|
|
166
|
+
},
|
|
167
|
+
isLoading: false,
|
|
168
|
+
error: null,
|
|
169
|
+
refetch: mocks.refetch,
|
|
170
|
+
});
|
|
79
171
|
|
|
80
|
-
|
|
172
|
+
renderPicker({
|
|
173
|
+
currentPath: '',
|
|
174
|
+
defaultWorkspacePath: '~/.nextclaw/workspace',
|
|
175
|
+
description: undefined,
|
|
176
|
+
});
|
|
81
177
|
|
|
82
|
-
expect(
|
|
178
|
+
expect(mocks.useServerPathBrowse).toHaveBeenLastCalledWith({
|
|
179
|
+
path: '~/.nextclaw/workspace',
|
|
180
|
+
enabled: true,
|
|
181
|
+
});
|
|
182
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
183
|
+
'/Users/peiwang/.nextclaw/workspace',
|
|
184
|
+
);
|
|
185
|
+
expect(screen.getByRole('button', { name: 'NextClaw 工作区' })).toBeTruthy();
|
|
186
|
+
expect(screen.getByText('workspace')).toBeTruthy();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('normalizes a home-relative workdir with Windows separators', () => {
|
|
190
|
+
mocks.useServerPathBrowse.mockReturnValue({
|
|
191
|
+
data: {
|
|
192
|
+
currentPath: 'C:\\Users\\alice\\.nextclaw\\workspace',
|
|
193
|
+
homePath: 'C:\\Users\\alice',
|
|
194
|
+
parentPath: 'C:\\Users\\alice\\.nextclaw',
|
|
195
|
+
breadcrumbs: [
|
|
196
|
+
{ label: 'C:', path: 'C:\\' },
|
|
197
|
+
{ label: 'Users', path: 'C:\\Users' },
|
|
198
|
+
{ label: 'alice', path: 'C:\\Users\\alice' },
|
|
199
|
+
{ label: '.nextclaw', path: 'C:\\Users\\alice\\.nextclaw' },
|
|
200
|
+
{ label: 'workspace', path: 'C:\\Users\\alice\\.nextclaw\\workspace' },
|
|
201
|
+
],
|
|
202
|
+
entries: [],
|
|
203
|
+
locations: [],
|
|
204
|
+
},
|
|
205
|
+
isLoading: false,
|
|
206
|
+
error: null,
|
|
207
|
+
refetch: mocks.refetch,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
renderPicker({ currentPath: '', defaultWorkspacePath: '~/.nextclaw/workspace' });
|
|
211
|
+
|
|
212
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
213
|
+
'C:\\Users\\alice\\.nextclaw\\workspace',
|
|
214
|
+
);
|
|
215
|
+
expect(screen.getByText('workspace')).toBeTruthy();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('creates a folder in the current directory and selects it without entering it', async () => {
|
|
219
|
+
const user = userEvent.setup();
|
|
220
|
+
renderPicker();
|
|
221
|
+
|
|
222
|
+
await user.click(screen.getByRole('button', { name: '新建文件夹' }));
|
|
223
|
+
await user.type(screen.getByLabelText('文件夹名称'), 'new-folder');
|
|
224
|
+
await user.click(screen.getByRole('button', { name: '创建' }));
|
|
225
|
+
|
|
226
|
+
expect(mocks.createDirectory).toHaveBeenCalledWith({
|
|
227
|
+
parentPath: '/workspace',
|
|
228
|
+
name: 'new-folder',
|
|
229
|
+
});
|
|
230
|
+
expect(mocks.refetch).toHaveBeenCalledOnce();
|
|
231
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
232
|
+
'/workspace/new-folder',
|
|
233
|
+
);
|
|
234
|
+
expect((screen.getByRole('button', { name: '后退' }) as HTMLButtonElement).disabled).toBe(
|
|
235
|
+
true,
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('uses single click to select and double click to enter a folder', async () => {
|
|
240
|
+
const user = userEvent.setup();
|
|
241
|
+
renderPicker();
|
|
242
|
+
const playground = screen.getByRole('option', { name: /playground/ });
|
|
243
|
+
|
|
244
|
+
expect(screen.getByText('单击选择,双击进入')).toBeTruthy();
|
|
245
|
+
|
|
246
|
+
await user.click(playground);
|
|
247
|
+
|
|
248
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
249
|
+
'/workspace/playground',
|
|
250
|
+
);
|
|
251
|
+
expect(mocks.useServerPathBrowse).toHaveBeenLastCalledWith({
|
|
252
|
+
path: '/workspace',
|
|
253
|
+
enabled: true,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await user.dblClick(playground);
|
|
257
|
+
|
|
258
|
+
expect(mocks.useServerPathBrowse).toHaveBeenLastCalledWith({
|
|
259
|
+
path: '/workspace/playground',
|
|
260
|
+
enabled: true,
|
|
261
|
+
});
|
|
262
|
+
expect((screen.getByRole('button', { name: '后退' }) as HTMLButtonElement).disabled).toBe(
|
|
263
|
+
false,
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('starts a fresh selection session when the dialog is reopened', async () => {
|
|
268
|
+
const user = userEvent.setup();
|
|
269
|
+
const view = renderPicker();
|
|
83
270
|
|
|
271
|
+
await user.click(screen.getByRole('option', { name: /playground/ }));
|
|
272
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
273
|
+
'/workspace/playground',
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
view.rerender(createPickerElement({ open: false }));
|
|
277
|
+
view.rerender(createPickerElement());
|
|
278
|
+
|
|
279
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe('/workspace');
|
|
280
|
+
expect((screen.getByRole('button', { name: '后退' }) as HTMLButtonElement).disabled).toBe(
|
|
281
|
+
true,
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('opens an editable address and records navigation history', async () => {
|
|
286
|
+
const user = userEvent.setup();
|
|
287
|
+
renderPicker();
|
|
288
|
+
|
|
289
|
+
await user.click(screen.getByRole('button', { name: '编辑地址' }));
|
|
290
|
+
const addressInput = screen.getByLabelText('地址');
|
|
291
|
+
await user.clear(addressInput);
|
|
292
|
+
await user.type(addressInput, '/workspace/playground{Enter}');
|
|
293
|
+
|
|
294
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
295
|
+
'/workspace/playground',
|
|
296
|
+
);
|
|
297
|
+
expect((screen.getByRole('button', { name: '后退' }) as HTMLButtonElement).disabled).toBe(
|
|
298
|
+
false,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
await user.click(screen.getByRole('button', { name: '后退' }));
|
|
302
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe('/workspace');
|
|
303
|
+
expect((screen.getByRole('button', { name: '前进' }) as HTMLButtonElement).disabled).toBe(
|
|
304
|
+
false,
|
|
305
|
+
);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('supports arrow selection and Enter navigation from the directory list', async () => {
|
|
309
|
+
const user = userEvent.setup();
|
|
310
|
+
renderPicker();
|
|
311
|
+
const playground = screen.getByRole('option', { name: /playground/ });
|
|
312
|
+
|
|
313
|
+
await user.click(playground);
|
|
314
|
+
await user.keyboard('{ArrowDown}');
|
|
315
|
+
|
|
316
|
+
expect((screen.getByLabelText('目录') as HTMLInputElement).value).toBe(
|
|
317
|
+
'/workspace/nextbot',
|
|
318
|
+
);
|
|
319
|
+
await user.keyboard('{Enter}');
|
|
320
|
+
expect(mocks.useServerPathBrowse).toHaveBeenLastCalledWith({
|
|
321
|
+
path: '/workspace/nextbot',
|
|
322
|
+
enabled: true,
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('shows the empty-search hint and keeps the list region scroll-contained', async () => {
|
|
327
|
+
const user = userEvent.setup();
|
|
328
|
+
renderPicker();
|
|
329
|
+
|
|
330
|
+
await user.type(screen.getAllByPlaceholderText('搜索当前目录')[0], 'missing');
|
|
331
|
+
|
|
332
|
+
expect(screen.getByText('当前目录下没有匹配结果。')).toBeTruthy();
|
|
84
333
|
const dialog = screen.getByRole('dialog');
|
|
85
334
|
expect(dialog.className).toContain('overflow-hidden');
|
|
86
|
-
expect(dialog.className).toContain('sm:h-[
|
|
87
|
-
|
|
88
|
-
|
|
335
|
+
expect(dialog.className).toContain('sm:h-[44rem]');
|
|
336
|
+
const scrollArea = screen
|
|
337
|
+
.getByText('当前目录下没有匹配结果。')
|
|
338
|
+
.closest('.overflow-auto');
|
|
89
339
|
expect(scrollArea?.className).toContain('flex-1');
|
|
90
340
|
expect(scrollArea?.className).toContain('min-h-0');
|
|
91
341
|
});
|