@nextclaw/ui 0.15.4 → 0.15.5
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 +17 -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-BOXOrpcJ.js → api-IlX_CbwU.js} +2 -2
- package/dist/assets/app-presenter-provider-Bbl_MVbg.js +106 -0
- package/dist/assets/{appearance-settings-page-fLIJbx1C.js → appearance-settings-page-80fLa4-9.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-CN7Shpij.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-R7M6pzp6.js} +1 -1
- package/dist/assets/{chat-page-B42UUIFR.js → chat-page-152YuwfU.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-BlfvfmTm.js} +1 -1
- package/dist/assets/{confirm-dialog-GSXIvoRU.js → confirm-dialog-Dt8Kdbgp.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-7bmNeWIC.js → desktop-update-config-DreMJu9z.js} +1 -1
- 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-D5ycbwCo.js} +1 -1
- package/dist/assets/dist-dw-gAfM_.js +1 -0
- package/dist/assets/{doc-browser-D0monHjG.js → doc-browser-C9u7N9vH.js} +1 -1
- package/dist/assets/doc-browser-DJQ5XPsi2.js +1 -0
- package/dist/assets/{doc-browser-context-CGwEOhBy.js → doc-browser-context-CCdW9TJf.js} +1 -1
- package/dist/assets/{docx-preview-eFH9DRsS.js → docx-preview-zis3U0Fa.js} +1 -1
- package/dist/assets/{download-DiP9zx-J.js → download-CyPgglKr.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-BahW1sl3.js} +1 -1
- package/dist/assets/{eye-suvKmtwn.js → eye-D8gGXMkD.js} +1 -1
- package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
- package/dist/assets/{folder-CMELbQGG.js → folder-DGWqFDvE.js} +1 -1
- package/dist/assets/{form-actions-CUAzpNe_.js → form-actions-D2imZLnU.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-0av6WO9-.css +1 -0
- package/dist/assets/index-CT0iYlxV.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-DtKek-qG.js +1 -0
- package/dist/assets/{logo-badge-Cu5UGH_4.js → logo-badge-CuPetu-v.js} +1 -1
- package/dist/assets/map-Bdri5UL5.js +1 -0
- package/dist/assets/mcp-marketplace-page-4qMwFDpz.js +1 -0
- package/dist/assets/{mcp-marketplace-page-BUzHDwW6.js → mcp-marketplace-page-BbxN5aZF.js} +1 -1
- 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-DiJcvBML.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-BwgE-Zwf.js +1 -0
- package/dist/assets/{popover-DImguRok.js → popover-Btp7KVc3.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-DZMtoAJG.js} +1 -1
- package/dist/assets/{providers-config-page-BKPo8SjH.js → providers-config-page-DfiWveEF.js} +1 -1
- 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-BY-U3h3t.js} +1 -1
- package/dist/assets/{refresh-cw-CHLBzWWj.js → refresh-cw-Cme5qFej.js} +1 -1
- package/dist/assets/remote-BaVECEZw.js +1 -0
- package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
- package/dist/assets/rotate-cw-CMRZxIkH.js +1 -0
- package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
- package/dist/assets/{runtime-config-page-1Axv4QGu.js → runtime-config-page-wygwvMB4.js} +1 -1
- package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
- package/dist/assets/{save-BABzyrjB.js → save-DNg2yJTS.js} +1 -1
- package/dist/assets/{search-BTqxG3ZG.js → search-3ykfrAFt.js} +1 -1
- package/dist/assets/{search-config-page-BkKKmxmS.js → search-config-page-DrVKa_Ls.js} +1 -1
- package/dist/assets/{secrets-config-page-CWJMyu-k.js → secrets-config-page-B39uQ5kF.js} +1 -1
- package/dist/assets/security-config-BPzet76z.js +1 -0
- package/dist/assets/{select-TYZDBNmq.js → select-B5EecXJt.js} +1 -1
- package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
- package/dist/assets/{setting-row-DR23Z_5I.js → setting-row-Qv1Ps79v.js} +1 -1
- package/dist/assets/{settings-2-D8WWcBnw.js → settings-2-D-3um9Az.js} +1 -1
- package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
- package/dist/assets/{skeleton-CloHeg0S.js → skeleton-BgkzQd4U.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-BVD8gp3y.js} +1 -1
- package/dist/assets/{tag-chip-mCc23Rhb.js → tag-chip-0qn-f3Bo.js} +1 -1
- package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
- package/dist/assets/{trash-2-SAW1Z4Po.js → trash-2-cnsMppRC.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-BucdaK23.js → use-config-D6yx0lb0.js} +1 -1
- package/dist/assets/{use-confirm-dialog-C95F6sT1.js → use-confirm-dialog-C_o0eQeH.js} +1 -1
- package/dist/assets/{use-viewport-layout-C6m3Nte7.js → use-viewport-layout-BWav4KCL.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-B3o1dWR5.js +1 -0
- package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
- package/dist/index.html +33 -32
- package/package.json +5 -5
- package/src/features/chat/components/chat-popover-content.tsx +1 -26
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +28 -11
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +43 -0
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +14 -10
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +61 -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/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/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/shared/components/common/__tests__/brand-header.test.tsx +17 -7
- package/src/shared/components/common/brand-header.tsx +34 -34
- package/src/shared/hooks/use-server-path-read.ts +10 -5
- package/src/shared/lib/api/types.ts +3 -3
- package/src/shared/lib/api/utils/server-path.utils.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +8 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +8 -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/app-presenter-provider-CkHLbvZQ.js +0 -106
- 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-ohUAdfqJ.js +0 -1
- package/dist/assets/plus-tywizkeY.js +0 -1
- package/dist/assets/remote-DXJryUxB.js +0 -1
- package/dist/assets/rotate-cw-BefO5brU.js +0 -1
- package/dist/assets/security-config-DeQUIXfi.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/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
package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx
CHANGED
|
@@ -51,10 +51,10 @@ function buildPreviewBlock(params: {
|
|
|
51
51
|
path: string;
|
|
52
52
|
text: string;
|
|
53
53
|
languageHint?: string | null;
|
|
54
|
-
|
|
54
|
+
startLine?: number | null;
|
|
55
55
|
}): ChatFileOperationBlockViewModel {
|
|
56
|
-
const { languageHint,
|
|
57
|
-
const
|
|
56
|
+
const { languageHint, path, startLine, text } = params;
|
|
57
|
+
const normalizedStartLine = startLine ?? 1;
|
|
58
58
|
return {
|
|
59
59
|
key: `preview:${path}`,
|
|
60
60
|
path,
|
|
@@ -62,13 +62,13 @@ function buildPreviewBlock(params: {
|
|
|
62
62
|
lines: buildPreviewLines({
|
|
63
63
|
text,
|
|
64
64
|
kind: "context",
|
|
65
|
-
oldStartLine:
|
|
66
|
-
newStartLine:
|
|
65
|
+
oldStartLine: normalizedStartLine,
|
|
66
|
+
newStartLine: normalizedStartLine,
|
|
67
67
|
}),
|
|
68
68
|
rawText: text,
|
|
69
69
|
languageHint: languageHint ?? null,
|
|
70
|
-
oldStartLine:
|
|
71
|
-
newStartLine:
|
|
70
|
+
oldStartLine: normalizedStartLine,
|
|
71
|
+
newStartLine: normalizedStartLine,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -154,12 +154,21 @@ function WorkspaceDiffBody({
|
|
|
154
154
|
|
|
155
155
|
function WorkspaceCodeSurface({
|
|
156
156
|
block,
|
|
157
|
+
targetColumn,
|
|
158
|
+
targetLine,
|
|
157
159
|
}: {
|
|
158
160
|
block: ChatFileOperationBlockViewModel;
|
|
161
|
+
targetColumn?: number | null;
|
|
162
|
+
targetLine?: number | null;
|
|
159
163
|
}) {
|
|
160
164
|
return (
|
|
161
165
|
<div className="h-full overflow-auto custom-scrollbar bg-white">
|
|
162
|
-
<FileOperationCodeSurface
|
|
166
|
+
<FileOperationCodeSurface
|
|
167
|
+
block={block}
|
|
168
|
+
layout="workspace"
|
|
169
|
+
targetColumn={targetColumn}
|
|
170
|
+
targetLine={targetLine}
|
|
171
|
+
/>
|
|
163
172
|
</div>
|
|
164
173
|
);
|
|
165
174
|
}
|
|
@@ -176,6 +185,8 @@ function WorkspacePreviewBody({
|
|
|
176
185
|
previewViewer,
|
|
177
186
|
previewQuery,
|
|
178
187
|
previewText,
|
|
188
|
+
targetColumn,
|
|
189
|
+
targetLine,
|
|
179
190
|
}: {
|
|
180
191
|
contentUrl: string | null;
|
|
181
192
|
contentUrlKind: WorkspaceFileContentKind | null;
|
|
@@ -188,6 +199,8 @@ function WorkspacePreviewBody({
|
|
|
188
199
|
previewViewer: "source" | "rendered" | null;
|
|
189
200
|
previewQuery: ReturnType<typeof useServerPathRead> | null | undefined;
|
|
190
201
|
previewText: string | null;
|
|
202
|
+
targetColumn?: number | null;
|
|
203
|
+
targetLine?: number | null;
|
|
191
204
|
}) {
|
|
192
205
|
if (contentUrl && contentUrlKind) {
|
|
193
206
|
return (
|
|
@@ -260,7 +273,13 @@ function WorkspacePreviewBody({
|
|
|
260
273
|
}
|
|
261
274
|
|
|
262
275
|
if (previewBlock) {
|
|
263
|
-
return
|
|
276
|
+
return (
|
|
277
|
+
<WorkspaceCodeSurface
|
|
278
|
+
block={previewBlock}
|
|
279
|
+
targetColumn={targetColumn}
|
|
280
|
+
targetLine={targetLine}
|
|
281
|
+
/>
|
|
282
|
+
);
|
|
264
283
|
}
|
|
265
284
|
|
|
266
285
|
return <WorkspaceFilePreviewStatus text={t("chatWorkspacePreviewEmpty")} />;
|
|
@@ -271,6 +290,7 @@ type ChatSessionWorkspaceFilePreviewProps = {
|
|
|
271
290
|
refreshVersion?: number;
|
|
272
291
|
sessionProjectRoot: string | null;
|
|
273
292
|
sessionWorkingDir: string | null;
|
|
293
|
+
showBreadcrumbs?: boolean;
|
|
274
294
|
onFileOpen: (action: ChatFileOpenActionViewModel) => void;
|
|
275
295
|
};
|
|
276
296
|
|
|
@@ -279,6 +299,7 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
279
299
|
refreshVersion = 0,
|
|
280
300
|
sessionProjectRoot,
|
|
281
301
|
sessionWorkingDir,
|
|
302
|
+
showBreadcrumbs = true,
|
|
282
303
|
onFileOpen,
|
|
283
304
|
}: ChatSessionWorkspaceFilePreviewProps) {
|
|
284
305
|
const isPreviewMode = file.viewMode === "preview";
|
|
@@ -287,6 +308,7 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
287
308
|
const previewQuery = useServerPathRead({
|
|
288
309
|
path: file.path,
|
|
289
310
|
basePath: sessionWorkingDir,
|
|
311
|
+
line: file.line,
|
|
290
312
|
enabled: usesServerPath,
|
|
291
313
|
});
|
|
292
314
|
const directoryQuery = useServerPathBrowse({
|
|
@@ -351,15 +373,15 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
351
373
|
path: previewQuery?.data?.resolvedPath ?? file.path,
|
|
352
374
|
text: previewText,
|
|
353
375
|
languageHint: previewQuery?.data?.languageHint ?? null,
|
|
354
|
-
|
|
376
|
+
startLine: previewQuery?.data?.startLine ?? 1,
|
|
355
377
|
});
|
|
356
378
|
}, [
|
|
357
379
|
contentUrl,
|
|
358
|
-
file.line,
|
|
359
380
|
file.path,
|
|
360
381
|
isPreviewMode,
|
|
361
382
|
previewQuery?.data?.languageHint,
|
|
362
383
|
previewQuery?.data?.resolvedPath,
|
|
384
|
+
previewQuery?.data?.startLine,
|
|
363
385
|
previewText,
|
|
364
386
|
]);
|
|
365
387
|
const isTextPreviewTruncated = !contentUrl && Boolean(previewQuery?.data?.truncated);
|
|
@@ -370,14 +392,10 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
370
392
|
path: resolvedPath,
|
|
371
393
|
kind: previewPathKind,
|
|
372
394
|
sessionProjectRoot: breadcrumbBasePath,
|
|
373
|
-
line: file.line,
|
|
374
|
-
column: file.column,
|
|
375
395
|
truncated: isTextPreviewTruncated,
|
|
376
396
|
}),
|
|
377
397
|
[
|
|
378
398
|
breadcrumbBasePath,
|
|
379
|
-
file.column,
|
|
380
|
-
file.line,
|
|
381
399
|
isTextPreviewTruncated,
|
|
382
400
|
previewPathKind,
|
|
383
401
|
resolvedPath,
|
|
@@ -386,10 +404,12 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
386
404
|
|
|
387
405
|
return (
|
|
388
406
|
<div className="flex h-full min-h-0 flex-col bg-white">
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
407
|
+
{showBreadcrumbs ? (
|
|
408
|
+
<ChatSessionWorkspaceFileBreadcrumbs
|
|
409
|
+
breadcrumb={breadcrumb}
|
|
410
|
+
onFileOpen={onFileOpen}
|
|
411
|
+
/>
|
|
412
|
+
) : null}
|
|
393
413
|
|
|
394
414
|
<div className="flex-1 min-h-0 overflow-hidden">
|
|
395
415
|
{file.viewMode === "diff" ? (
|
|
@@ -407,6 +427,8 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
407
427
|
previewViewer={previewViewer}
|
|
408
428
|
previewQuery={previewQuery}
|
|
409
429
|
previewText={previewText}
|
|
430
|
+
targetColumn={file.column}
|
|
431
|
+
targetLine={file.line}
|
|
410
432
|
/>
|
|
411
433
|
)}
|
|
412
434
|
</div>
|
package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx
CHANGED
|
@@ -84,7 +84,11 @@ function buildCompactWorkspaceTabs(
|
|
|
84
84
|
</span>
|
|
85
85
|
) : null,
|
|
86
86
|
unreadIndicator: tab.showUnreadDot ? <span aria-label={t("chatSessionUnread")} className="h-2 w-2 shrink-0 rounded-full bg-primary" /> : null,
|
|
87
|
-
closeLabel:
|
|
87
|
+
closeLabel: `${
|
|
88
|
+
tab.kind === "file"
|
|
89
|
+
? t("chatWorkspaceCloseFile")
|
|
90
|
+
: t("chatWorkspaceCloseTab")
|
|
91
|
+
}: ${tab.title}`,
|
|
88
92
|
closePlacement: "leading-hover",
|
|
89
93
|
onSelect: tab.onSelect,
|
|
90
94
|
onClose: tab.onClose,
|
|
@@ -39,6 +39,7 @@ type ChatSessionWorkspacePanelProps = {
|
|
|
39
39
|
activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
|
|
40
40
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
41
41
|
activeWorkspaceFileKey: string | null;
|
|
42
|
+
closedWorkspaceTabEntries: readonly ChatWorkspaceNavigationEntry[];
|
|
42
43
|
workspaceNavigationHistory?: readonly ChatWorkspaceNavigationEntry[];
|
|
43
44
|
workspaceNavigationHistoryIndex?: number;
|
|
44
45
|
activePanelKind?: ChatWorkspacePanelKind | null;
|
|
@@ -56,6 +57,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
56
57
|
activeSideChatDraft,
|
|
57
58
|
workspaceFileTabs,
|
|
58
59
|
activeWorkspaceFileKey,
|
|
60
|
+
closedWorkspaceTabEntries,
|
|
59
61
|
workspaceNavigationHistory = [],
|
|
60
62
|
workspaceNavigationHistoryIndex = 0,
|
|
61
63
|
activePanelKind,
|
|
@@ -95,13 +97,14 @@ export function ChatSessionWorkspacePanel({
|
|
|
95
97
|
buildWorkspaceTabsViewModel({
|
|
96
98
|
resolvedChildTabs,
|
|
97
99
|
activeSideChatDraft,
|
|
100
|
+
closedWorkspaceTabEntries,
|
|
98
101
|
workspaceFileTabs,
|
|
99
102
|
activeSelection,
|
|
100
103
|
optimisticReadAtBySessionKey,
|
|
101
104
|
onSelectSession: presenter.chatThreadManager.selectChildSessionDetail,
|
|
102
105
|
onSelectFile: presenter.chatThreadManager.selectWorkspaceFile,
|
|
103
106
|
onOpenFileViewer: presenter.chatThreadManager.openWorkspaceFileViewer,
|
|
104
|
-
|
|
107
|
+
onCloseTab: presenter.chatThreadManager.closeWorkspaceTab,
|
|
105
108
|
onSelectOverview: () => {
|
|
106
109
|
if (sessionKey)
|
|
107
110
|
presenter.chatThreadManager.openWorkspaceOverview(sessionKey);
|
|
@@ -122,6 +125,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
122
125
|
[
|
|
123
126
|
activeSelection,
|
|
124
127
|
activeSideChatDraft,
|
|
128
|
+
closedWorkspaceTabEntries,
|
|
125
129
|
optimisticReadAtBySessionKey,
|
|
126
130
|
presenter.chatThreadManager,
|
|
127
131
|
resolvedChildTabs,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
buildWorkspaceTabsViewModel,
|
|
5
5
|
resolveWorkspaceSelection,
|
|
6
6
|
} from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
|
|
7
|
+
import { createWorkspaceFileTab } from "@/features/chat/features/workspace/utils/chat-workspace-file-tab.utils";
|
|
7
8
|
import { t } from "@/shared/lib/i18n";
|
|
8
9
|
|
|
9
10
|
function createChildTab(
|
|
@@ -104,7 +105,104 @@ describe("resolveWorkspaceSelection", () => {
|
|
|
104
105
|
});
|
|
105
106
|
});
|
|
106
107
|
|
|
108
|
+
describe("createWorkspaceFileTab", () => {
|
|
109
|
+
it("uses source view for located Markdown unless rendered view is explicit", () => {
|
|
110
|
+
const locatedSource = createWorkspaceFileTab(
|
|
111
|
+
{
|
|
112
|
+
path: "README.md",
|
|
113
|
+
viewMode: "preview",
|
|
114
|
+
line: 12,
|
|
115
|
+
},
|
|
116
|
+
"parent-1",
|
|
117
|
+
);
|
|
118
|
+
const explicitRendered = createWorkspaceFileTab(
|
|
119
|
+
{
|
|
120
|
+
path: "README.md",
|
|
121
|
+
viewMode: "preview",
|
|
122
|
+
previewViewer: "rendered",
|
|
123
|
+
line: 12,
|
|
124
|
+
},
|
|
125
|
+
"parent-1",
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(locatedSource).toMatchObject({
|
|
129
|
+
previewViewer: "source",
|
|
130
|
+
line: 12,
|
|
131
|
+
});
|
|
132
|
+
expect(explicitRendered).toMatchObject({
|
|
133
|
+
previewViewer: "rendered",
|
|
134
|
+
line: 12,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
107
139
|
describe("buildWorkspaceTabsViewModel", () => {
|
|
140
|
+
it("keeps overview fixed while every other visible workspace tab is closable", () => {
|
|
141
|
+
const childTab = createChildTab();
|
|
142
|
+
const draft = {
|
|
143
|
+
draftKey: "draft-1",
|
|
144
|
+
parentSessionKey: "parent-1",
|
|
145
|
+
};
|
|
146
|
+
const fileTab = {
|
|
147
|
+
key: "file-1",
|
|
148
|
+
parentSessionKey: "parent-1",
|
|
149
|
+
path: "README.md",
|
|
150
|
+
viewMode: "preview" as const,
|
|
151
|
+
};
|
|
152
|
+
const onCloseTab = vi.fn();
|
|
153
|
+
const tabs = buildWorkspaceTabsViewModel({
|
|
154
|
+
resolvedChildTabs: [childTab],
|
|
155
|
+
activeSideChatDraft: draft,
|
|
156
|
+
closedWorkspaceTabEntries: [],
|
|
157
|
+
workspaceFileTabs: [fileTab],
|
|
158
|
+
activeSelection: { kind: "overview" },
|
|
159
|
+
optimisticReadAtBySessionKey: {},
|
|
160
|
+
onSelectSession: vi.fn(),
|
|
161
|
+
onSelectFile: vi.fn(),
|
|
162
|
+
onOpenFileViewer: vi.fn(),
|
|
163
|
+
onCloseTab,
|
|
164
|
+
onSelectOverview: vi.fn(),
|
|
165
|
+
onSelectChildSessions: vi.fn(),
|
|
166
|
+
onSelectProjectFiles: vi.fn(),
|
|
167
|
+
onSelectCronJobs: vi.fn(),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(tabs.find((tab) => tab.kind === "overview")?.onClose).toBeUndefined();
|
|
171
|
+
expect(
|
|
172
|
+
tabs.filter((tab) => tab.kind !== "overview").every((tab) => tab.onClose),
|
|
173
|
+
).toBe(true);
|
|
174
|
+
tabs.find((tab) => tab.kind === "cron")?.onClose?.();
|
|
175
|
+
expect(onCloseTab).toHaveBeenCalledWith({ kind: "cron" });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("does not project workspace tabs that the user closed", () => {
|
|
179
|
+
const tabs = buildWorkspaceTabsViewModel({
|
|
180
|
+
resolvedChildTabs: [createChildTab()],
|
|
181
|
+
activeSideChatDraft: null,
|
|
182
|
+
closedWorkspaceTabEntries: [
|
|
183
|
+
{ kind: "project-files" },
|
|
184
|
+
{ kind: "child-session", key: "child-1" },
|
|
185
|
+
],
|
|
186
|
+
workspaceFileTabs: [],
|
|
187
|
+
activeSelection: { kind: "overview" },
|
|
188
|
+
optimisticReadAtBySessionKey: {},
|
|
189
|
+
onSelectSession: vi.fn(),
|
|
190
|
+
onSelectFile: vi.fn(),
|
|
191
|
+
onOpenFileViewer: vi.fn(),
|
|
192
|
+
onCloseTab: vi.fn(),
|
|
193
|
+
onSelectOverview: vi.fn(),
|
|
194
|
+
onSelectChildSessions: vi.fn(),
|
|
195
|
+
onSelectProjectFiles: vi.fn(),
|
|
196
|
+
onSelectCronJobs: vi.fn(),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(tabs.map((tab) => tab.key)).toEqual([
|
|
200
|
+
"overview",
|
|
201
|
+
"child-sessions",
|
|
202
|
+
"cron:session",
|
|
203
|
+
]);
|
|
204
|
+
});
|
|
205
|
+
|
|
108
206
|
it("builds active and unread state from resolved tabs", () => {
|
|
109
207
|
const childTab = createChildTab({
|
|
110
208
|
lastMessageAt: "2026-06-09T10:00:00.000Z",
|
|
@@ -115,13 +213,14 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
115
213
|
const tabs = buildWorkspaceTabsViewModel({
|
|
116
214
|
resolvedChildTabs: [childTab],
|
|
117
215
|
activeSideChatDraft: null,
|
|
216
|
+
closedWorkspaceTabEntries: [],
|
|
118
217
|
workspaceFileTabs: [],
|
|
119
218
|
activeSelection: null,
|
|
120
219
|
optimisticReadAtBySessionKey: {},
|
|
121
220
|
onSelectSession,
|
|
122
221
|
onSelectFile: vi.fn(),
|
|
123
222
|
onOpenFileViewer: vi.fn(),
|
|
124
|
-
|
|
223
|
+
onCloseTab: vi.fn(),
|
|
125
224
|
onSelectOverview: vi.fn(),
|
|
126
225
|
onSelectChildSessions: vi.fn(),
|
|
127
226
|
onSelectProjectFiles: vi.fn(),
|
|
@@ -150,6 +249,7 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
150
249
|
const tabs = buildWorkspaceTabsViewModel({
|
|
151
250
|
resolvedChildTabs: [childTab],
|
|
152
251
|
activeSideChatDraft: draft,
|
|
252
|
+
closedWorkspaceTabEntries: [],
|
|
153
253
|
workspaceFileTabs: [],
|
|
154
254
|
activeSelection: {
|
|
155
255
|
kind: "side-chat-draft",
|
|
@@ -159,7 +259,7 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
159
259
|
onSelectSession: vi.fn(),
|
|
160
260
|
onSelectFile: vi.fn(),
|
|
161
261
|
onOpenFileViewer: vi.fn(),
|
|
162
|
-
|
|
262
|
+
onCloseTab: vi.fn(),
|
|
163
263
|
onSelectOverview: vi.fn(),
|
|
164
264
|
onSelectChildSessions: vi.fn(),
|
|
165
265
|
onSelectProjectFiles: vi.fn(),
|
|
@@ -202,6 +302,7 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
202
302
|
const tabs = buildWorkspaceTabsViewModel({
|
|
203
303
|
resolvedChildTabs: [],
|
|
204
304
|
activeSideChatDraft: null,
|
|
305
|
+
closedWorkspaceTabEntries: [],
|
|
205
306
|
workspaceFileTabs: [sourceTab, renderedTab],
|
|
206
307
|
activeSelection: {
|
|
207
308
|
kind: "file",
|
|
@@ -211,7 +312,7 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
211
312
|
onSelectSession: vi.fn(),
|
|
212
313
|
onSelectFile: vi.fn(),
|
|
213
314
|
onOpenFileViewer,
|
|
214
|
-
|
|
315
|
+
onCloseTab: vi.fn(),
|
|
215
316
|
onSelectOverview: vi.fn(),
|
|
216
317
|
onSelectChildSessions: vi.fn(),
|
|
217
318
|
onSelectProjectFiles: vi.fn(),
|
|
@@ -33,6 +33,37 @@ export function areWorkspaceNavigationEntriesEqual(
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export function createWorkspaceNavigationEntryFromSnapshot(
|
|
37
|
+
snapshot: Pick<
|
|
38
|
+
ChatThreadSnapshot,
|
|
39
|
+
'activeChildSessionKey' | 'activeSideChatDraft' | 'activeWorkspaceFileKey' | 'activeWorkspacePanelKind'
|
|
40
|
+
>,
|
|
41
|
+
): ChatWorkspaceNavigationEntry | null {
|
|
42
|
+
const {
|
|
43
|
+
activeChildSessionKey,
|
|
44
|
+
activeSideChatDraft,
|
|
45
|
+
activeWorkspaceFileKey,
|
|
46
|
+
activeWorkspacePanelKind,
|
|
47
|
+
} = snapshot;
|
|
48
|
+
if (
|
|
49
|
+
activeWorkspacePanelKind === 'overview' ||
|
|
50
|
+
activeWorkspacePanelKind === 'child-sessions' ||
|
|
51
|
+
activeWorkspacePanelKind === 'project-files' ||
|
|
52
|
+
activeWorkspacePanelKind === 'cron'
|
|
53
|
+
) {
|
|
54
|
+
return { kind: activeWorkspacePanelKind };
|
|
55
|
+
}
|
|
56
|
+
if (activeWorkspacePanelKind === 'child-session' && activeChildSessionKey) {
|
|
57
|
+
return { kind: 'child-session', key: activeChildSessionKey };
|
|
58
|
+
}
|
|
59
|
+
if (activeWorkspacePanelKind === 'side-chat-draft' && activeSideChatDraft) {
|
|
60
|
+
return { kind: 'side-chat-draft', key: activeSideChatDraft.draftKey };
|
|
61
|
+
}
|
|
62
|
+
return activeWorkspacePanelKind === 'file' && activeWorkspaceFileKey
|
|
63
|
+
? { kind: 'file', key: activeWorkspaceFileKey }
|
|
64
|
+
: null;
|
|
65
|
+
}
|
|
66
|
+
|
|
36
67
|
export function createWorkspaceSelectionPatch(
|
|
37
68
|
entry: ChatWorkspaceNavigationEntry,
|
|
38
69
|
snapshot: ChatThreadSnapshot,
|
|
@@ -88,6 +119,58 @@ export function createWorkspaceSelectionPatch(
|
|
|
88
119
|
};
|
|
89
120
|
}
|
|
90
121
|
|
|
122
|
+
export function closeWorkspaceTabSnapshot(
|
|
123
|
+
snapshot: ChatThreadSnapshot,
|
|
124
|
+
entry: ChatWorkspaceNavigationEntry,
|
|
125
|
+
): Partial<ChatThreadSnapshot> | null {
|
|
126
|
+
if (entry.kind === 'overview') {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const history = filterNavigationHistoryEntries(
|
|
130
|
+
{
|
|
131
|
+
entries: snapshot.workspaceNavigationHistory,
|
|
132
|
+
index: snapshot.workspaceNavigationHistoryIndex,
|
|
133
|
+
},
|
|
134
|
+
(candidate) => !areWorkspaceNavigationEntriesEqual(candidate, entry),
|
|
135
|
+
{ kind: 'overview' },
|
|
136
|
+
);
|
|
137
|
+
const shouldRememberClosure = entry.kind !== 'file' && entry.kind !== 'side-chat-draft';
|
|
138
|
+
const patch: Partial<ChatThreadSnapshot> = {
|
|
139
|
+
closedWorkspaceTabEntries: shouldRememberClosure
|
|
140
|
+
? [
|
|
141
|
+
...snapshot.closedWorkspaceTabEntries.filter(
|
|
142
|
+
(candidate) => !areWorkspaceNavigationEntriesEqual(candidate, entry),
|
|
143
|
+
),
|
|
144
|
+
entry,
|
|
145
|
+
]
|
|
146
|
+
: snapshot.closedWorkspaceTabEntries,
|
|
147
|
+
childSessionTabs: entry.kind === 'child-session'
|
|
148
|
+
? snapshot.childSessionTabs.filter((tab) => tab.sessionKey !== entry.key)
|
|
149
|
+
: snapshot.childSessionTabs,
|
|
150
|
+
activeSideChatDraft: entry.kind === 'side-chat-draft'
|
|
151
|
+
? null
|
|
152
|
+
: snapshot.activeSideChatDraft,
|
|
153
|
+
workspaceFileTabs: entry.kind === 'file'
|
|
154
|
+
? snapshot.workspaceFileTabs.filter((tab) => tab.key !== entry.key)
|
|
155
|
+
: snapshot.workspaceFileTabs,
|
|
156
|
+
workspaceNavigationHistory: [...history.entries],
|
|
157
|
+
workspaceNavigationHistoryIndex: history.index,
|
|
158
|
+
};
|
|
159
|
+
const activeEntry = createWorkspaceNavigationEntryFromSnapshot(snapshot);
|
|
160
|
+
if (!activeEntry || !areWorkspaceNavigationEntriesEqual(activeEntry, entry)) {
|
|
161
|
+
return patch;
|
|
162
|
+
}
|
|
163
|
+
const restoreEntry = history.entries[history.index] ?? { kind: 'overview' };
|
|
164
|
+
const restorePatch = createWorkspaceSelectionPatch(restoreEntry, {
|
|
165
|
+
...snapshot,
|
|
166
|
+
...patch,
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
...patch,
|
|
170
|
+
...(restorePatch ?? createWorkspaceSelectionPatch({ kind: 'overview' }, snapshot)),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
91
174
|
export function createSideChatDraft(parentSessionKey: string): ChatWorkspaceSideChatDraft {
|
|
92
175
|
const randomPart =
|
|
93
176
|
typeof globalThis.crypto?.randomUUID === 'function'
|
|
@@ -12,7 +12,10 @@ export function createWorkspaceFileTab(
|
|
|
12
12
|
}
|
|
13
13
|
const normalizedParentSessionKey = parentSessionKey?.trim() || null;
|
|
14
14
|
const previewViewer = action.viewMode === 'preview'
|
|
15
|
-
? normalizeWorkspaceFilePreviewViewer(
|
|
15
|
+
? normalizeWorkspaceFilePreviewViewer(
|
|
16
|
+
path,
|
|
17
|
+
action.previewViewer ?? (action.line ? 'source' : undefined),
|
|
18
|
+
)
|
|
16
19
|
: action.previewViewer ?? null;
|
|
17
20
|
const viewIdentity = action.viewMode === 'preview' && previewViewer === 'rendered'
|
|
18
21
|
? 'preview:rendered'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
|
|
2
2
|
import { shouldShowUnreadSessionIndicator } from "@/features/chat/stores/chat-session-list.store";
|
|
3
3
|
import type {
|
|
4
|
+
ChatWorkspaceNavigationEntry,
|
|
4
5
|
ChatWorkspacePanelKind,
|
|
5
6
|
ChatWorkspaceFileTab,
|
|
6
7
|
ChatWorkspaceSideChatDraft,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
resolveWorkspaceFileViewer,
|
|
12
13
|
type ChatWorkspaceFileViewer,
|
|
13
14
|
} from "@/features/chat/features/workspace/utils/chat-workspace-file-viewer.utils";
|
|
15
|
+
import { areWorkspaceNavigationEntriesEqual } from "@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils";
|
|
14
16
|
|
|
15
17
|
export type WorkspaceSelection =
|
|
16
18
|
| {
|
|
@@ -151,41 +153,54 @@ export function resolveWorkspaceSelection(params: {
|
|
|
151
153
|
return null;
|
|
152
154
|
}
|
|
153
155
|
|
|
154
|
-
|
|
156
|
+
type WorkspaceTabsViewModelParams = {
|
|
155
157
|
resolvedChildTabs: ResolvedChildSessionTab[];
|
|
156
158
|
activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
|
|
159
|
+
closedWorkspaceTabEntries: readonly ChatWorkspaceNavigationEntry[];
|
|
157
160
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
158
161
|
activeSelection: WorkspaceSelection | null;
|
|
159
162
|
optimisticReadAtBySessionKey: Record<string, string>;
|
|
160
163
|
onSelectSession: (sessionKey: string) => void;
|
|
161
164
|
onSelectFile: (fileKey: string) => void;
|
|
162
165
|
onOpenFileViewer: (fileKey: string, viewer: ChatWorkspaceFileViewer) => void;
|
|
163
|
-
|
|
166
|
+
onCloseTab: (entry: ChatWorkspaceNavigationEntry) => void;
|
|
164
167
|
onSelectOverview: () => void;
|
|
165
168
|
onSelectChildSessions: () => void;
|
|
166
169
|
onSelectProjectFiles: () => void;
|
|
167
170
|
onSelectCronJobs: () => void;
|
|
168
|
-
}
|
|
169
|
-
const {
|
|
170
|
-
activeSideChatDraft,
|
|
171
|
-
resolvedChildTabs,
|
|
172
|
-
workspaceFileTabs,
|
|
173
|
-
activeSelection,
|
|
174
|
-
optimisticReadAtBySessionKey,
|
|
175
|
-
onSelectSession,
|
|
176
|
-
onSelectFile,
|
|
177
|
-
onOpenFileViewer,
|
|
178
|
-
onCloseFile,
|
|
179
|
-
onSelectOverview,
|
|
180
|
-
onSelectChildSessions,
|
|
181
|
-
onSelectProjectFiles,
|
|
182
|
-
onSelectCronJobs,
|
|
183
|
-
} = params;
|
|
171
|
+
};
|
|
184
172
|
|
|
185
|
-
|
|
173
|
+
function createWorkspacePageTabEntry(
|
|
174
|
+
kind: ChatWorkspacePanelKind,
|
|
175
|
+
): ChatWorkspaceNavigationEntry | null {
|
|
176
|
+
if (kind === "child-sessions" || kind === "cron" || kind === "project-files") {
|
|
177
|
+
return { kind };
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function buildWorkspacePageTabs({
|
|
183
|
+
activeSelection,
|
|
184
|
+
closedWorkspaceTabEntries,
|
|
185
|
+
onCloseTab,
|
|
186
|
+
onSelectChildSessions,
|
|
187
|
+
onSelectCronJobs,
|
|
188
|
+
onSelectOverview,
|
|
189
|
+
onSelectProjectFiles,
|
|
190
|
+
}: Pick<
|
|
191
|
+
WorkspaceTabsViewModelParams,
|
|
192
|
+
| "activeSelection"
|
|
193
|
+
| "closedWorkspaceTabEntries"
|
|
194
|
+
| "onCloseTab"
|
|
195
|
+
| "onSelectChildSessions"
|
|
196
|
+
| "onSelectCronJobs"
|
|
197
|
+
| "onSelectOverview"
|
|
198
|
+
| "onSelectProjectFiles"
|
|
199
|
+
>): WorkspaceTabViewModel[] {
|
|
200
|
+
const tabs: WorkspaceTabViewModel[] = [
|
|
186
201
|
{
|
|
187
202
|
key: "overview",
|
|
188
|
-
kind: "overview"
|
|
203
|
+
kind: "overview",
|
|
189
204
|
title: t("chatWorkspaceOverview"),
|
|
190
205
|
tooltip: t("chatWorkspaceOverview"),
|
|
191
206
|
active: activeSelection?.kind === "overview",
|
|
@@ -193,29 +208,69 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
193
208
|
},
|
|
194
209
|
{
|
|
195
210
|
key: "child-sessions",
|
|
196
|
-
kind: "child-sessions"
|
|
211
|
+
kind: "child-sessions",
|
|
197
212
|
title: t("chatWorkspaceChildSessions"),
|
|
198
213
|
tooltip: t("chatWorkspaceChildSessions"),
|
|
199
214
|
active: activeSelection?.kind === "child-sessions",
|
|
200
215
|
onSelect: onSelectChildSessions,
|
|
216
|
+
onClose: () => onCloseTab({ kind: "child-sessions" }),
|
|
201
217
|
},
|
|
202
218
|
{
|
|
203
219
|
key: "cron:session",
|
|
204
|
-
kind: "cron"
|
|
220
|
+
kind: "cron",
|
|
205
221
|
title: t("chatWorkspaceSessionCronJobs"),
|
|
206
222
|
tooltip: t("chatWorkspaceSessionCronJobs"),
|
|
207
223
|
active: activeSelection?.kind === "cron",
|
|
208
224
|
onSelect: onSelectCronJobs,
|
|
225
|
+
onClose: () => onCloseTab({ kind: "cron" }),
|
|
209
226
|
},
|
|
210
227
|
{
|
|
211
228
|
key: "project-files",
|
|
212
|
-
kind: "project-files"
|
|
229
|
+
kind: "project-files",
|
|
213
230
|
title: t("chatWorkspaceProjectFiles"),
|
|
214
231
|
tooltip: t("chatWorkspaceProjectFiles"),
|
|
215
232
|
active: activeSelection?.kind === "project-files",
|
|
216
233
|
onSelect: onSelectProjectFiles,
|
|
234
|
+
onClose: () => onCloseTab({ kind: "project-files" }),
|
|
217
235
|
},
|
|
218
236
|
];
|
|
237
|
+
return tabs.filter((tab) => {
|
|
238
|
+
const entry = createWorkspacePageTabEntry(tab.kind);
|
|
239
|
+
return !entry || !closedWorkspaceTabEntries.some((candidate) =>
|
|
240
|
+
areWorkspaceNavigationEntriesEqual(candidate, entry),
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function buildWorkspaceTabsViewModel(
|
|
246
|
+
params: WorkspaceTabsViewModelParams,
|
|
247
|
+
): WorkspaceTabViewModel[] {
|
|
248
|
+
const {
|
|
249
|
+
activeSideChatDraft,
|
|
250
|
+
closedWorkspaceTabEntries,
|
|
251
|
+
resolvedChildTabs,
|
|
252
|
+
workspaceFileTabs,
|
|
253
|
+
activeSelection,
|
|
254
|
+
optimisticReadAtBySessionKey,
|
|
255
|
+
onSelectSession,
|
|
256
|
+
onSelectFile,
|
|
257
|
+
onOpenFileViewer,
|
|
258
|
+
onCloseTab,
|
|
259
|
+
onSelectOverview,
|
|
260
|
+
onSelectChildSessions,
|
|
261
|
+
onSelectProjectFiles,
|
|
262
|
+
onSelectCronJobs,
|
|
263
|
+
} = params;
|
|
264
|
+
|
|
265
|
+
const workspacePages = buildWorkspacePageTabs({
|
|
266
|
+
activeSelection,
|
|
267
|
+
closedWorkspaceTabEntries,
|
|
268
|
+
onCloseTab,
|
|
269
|
+
onSelectChildSessions,
|
|
270
|
+
onSelectCronJobs,
|
|
271
|
+
onSelectOverview,
|
|
272
|
+
onSelectProjectFiles,
|
|
273
|
+
});
|
|
219
274
|
|
|
220
275
|
const sideChatDraftTabs = activeSideChatDraft
|
|
221
276
|
? [
|
|
@@ -228,11 +283,23 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
228
283
|
activeSelection?.kind === "side-chat-draft" &&
|
|
229
284
|
activeSelection.draft.draftKey === activeSideChatDraft.draftKey,
|
|
230
285
|
onSelect: () => undefined,
|
|
286
|
+
onClose: () => onCloseTab({
|
|
287
|
+
kind: "side-chat-draft",
|
|
288
|
+
key: activeSideChatDraft.draftKey,
|
|
289
|
+
}),
|
|
231
290
|
},
|
|
232
291
|
]
|
|
233
292
|
: [];
|
|
234
293
|
|
|
235
|
-
const childTabs = resolvedChildTabs.
|
|
294
|
+
const childTabs = resolvedChildTabs.filter(
|
|
295
|
+
(tab) =>
|
|
296
|
+
!closedWorkspaceTabEntries.some((entry) =>
|
|
297
|
+
areWorkspaceNavigationEntriesEqual(entry, {
|
|
298
|
+
kind: "child-session",
|
|
299
|
+
key: tab.sessionKey,
|
|
300
|
+
}),
|
|
301
|
+
),
|
|
302
|
+
).map((tab) => {
|
|
236
303
|
const optimisticReadAt = optimisticReadAtBySessionKey[tab.sessionKey];
|
|
237
304
|
const effectiveReadAt =
|
|
238
305
|
optimisticReadAt && tab.readAt
|
|
@@ -259,6 +326,10 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
259
326
|
runStatus: tab.runStatus,
|
|
260
327
|
}),
|
|
261
328
|
onSelect: () => onSelectSession(tab.sessionKey),
|
|
329
|
+
onClose: () => onCloseTab({
|
|
330
|
+
kind: "child-session",
|
|
331
|
+
key: tab.sessionKey,
|
|
332
|
+
}),
|
|
262
333
|
};
|
|
263
334
|
});
|
|
264
335
|
|
|
@@ -292,7 +363,7 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
292
363
|
active:
|
|
293
364
|
activeSelection?.kind === "file" && activeSelection.file.key === file.key,
|
|
294
365
|
onSelect: () => onSelectFile(file.key),
|
|
295
|
-
onClose: () =>
|
|
366
|
+
onClose: () => onCloseTab({ kind: "file", key: file.key }),
|
|
296
367
|
};
|
|
297
368
|
});
|
|
298
369
|
|