difit 4.0.5 → 4.0.7
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/README.ja.md +1 -0
- package/README.ko.md +1 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/dist/cli/comment.d.ts +2 -0
- package/dist/cli/comment.js +91 -0
- package/dist/cli/comment.test.d.ts +1 -0
- package/dist/cli/comment.test.js +164 -0
- package/dist/cli/index.js +103 -12
- package/dist/cli/utils.d.ts +1 -0
- package/dist/cli/utils.js +7 -0
- package/dist/client/assets/{_baseFor-DKyA49xd.js → _baseFor-Dq1lbcoh.js} +1 -1
- package/dist/client/assets/{arc-COOp7iVe.js → arc-DX2p9X2Y.js} +1 -1
- package/dist/client/assets/architecture-YZFGNWBL-2zVtKbnG.js +1 -0
- package/dist/client/assets/architectureDiagram-Q4EWVU46-FixTWViB.js +36 -0
- package/dist/client/assets/{blockDiagram-DXYQGD6D-CtNJnEWN.js → blockDiagram-DXYQGD6D-CUAMgGr9.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BqG-1m6C.js → c4Diagram-AHTNJAMY-BM_HNNZe.js} +1 -1
- package/dist/client/assets/channel-B_ddQhpW.js +1 -0
- package/dist/client/assets/{chunk-2KRD3SAO-DqP2NJNd.js → chunk-2KRD3SAO-DeT59g2K.js} +1 -1
- package/dist/client/assets/chunk-336JU56O-CaGvJA86.js +2 -0
- package/dist/client/assets/chunk-426QAEUC-CMTCMPn4.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-BT78EnQ6.js → chunk-4BX2VUAB-D9mNDl5f.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-C4w_Bwzw.js → chunk-4TB4RGXK-Df3b4HEG.js} +2 -2
- package/dist/client/assets/{chunk-55IACEB6-z3MQSTaj.js → chunk-55IACEB6-dCWLe_n4.js} +1 -1
- package/dist/client/assets/{chunk-5FUZZQ4R-Chei69aj.js → chunk-5FUZZQ4R-EScvXcSN.js} +1 -1
- package/dist/client/assets/{chunk-5PVQY5BW-HgRiIs0X.js → chunk-5PVQY5BW-ail-oj89.js} +1 -1
- package/dist/client/assets/{chunk-67CJDMHE-B2q10-fp.js → chunk-67CJDMHE-CHeCIL1u.js} +1 -1
- package/dist/client/assets/{chunk-7N4EOEYR-DPgxysWq.js → chunk-7N4EOEYR-P0tNRVMZ.js} +1 -1
- package/dist/client/assets/{chunk-AA7GKIK3-BqmVmKLq.js → chunk-AA7GKIK3-DloBHWSo.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CaIgleFn.js → chunk-BSJP7CBP-CGLThsR8.js} +1 -1
- package/dist/client/assets/{chunk-CIAEETIT-ByD-tlNF.js → chunk-CIAEETIT-rCt2IEMp.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-d3RUKKAj.js → chunk-EDXVE4YY-DIJEIKIq.js} +1 -1
- package/dist/client/assets/{chunk-ENJZ2VHE-CNq5Qmg9.js → chunk-ENJZ2VHE-CdrdxFfV.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DYfHJ6MV.js → chunk-FMBD7UC4-BH_GgR9u.js} +1 -1
- package/dist/client/assets/{chunk-FOC6F5B3-BRpSWlZj.js → chunk-FOC6F5B3-D71VljSN.js} +1 -1
- package/dist/client/assets/{chunk-ICPOFSXX-B_MThwG6.js → chunk-ICPOFSXX-2vcQKuhB.js} +2 -2
- package/dist/client/assets/{chunk-K5T4RW27-DmamW1Ds.js → chunk-K5T4RW27-BWIFd7pZ.js} +10 -10
- package/dist/client/assets/{chunk-KGLVRYIC-CRbg4c4z.js → chunk-KGLVRYIC-Ck8I8tdt.js} +1 -1
- package/dist/client/assets/{chunk-LIHQZDEY-CHQPSdB3.js → chunk-LIHQZDEY-Cc7TtI-w.js} +1 -1
- package/dist/client/assets/{chunk-ORNJ4GCN-CIsQ4Zi4.js → chunk-ORNJ4GCN-BMSqiphc.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-Cxi0kdGg.js → chunk-OYMX7WX6-B5faFb53.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-B-G9G-FB.js +1 -0
- package/dist/client/assets/{chunk-U2HBQHQK-V_hneCfR.js → chunk-U2HBQHQK-BILTfRyq.js} +1 -1
- package/dist/client/assets/{chunk-X2U36JSP-De4pvO-I.js → chunk-X2U36JSP-D4-56gWx.js} +1 -1
- package/dist/client/assets/{chunk-XPW4576I-B_osXKp6.js → chunk-XPW4576I-SxB401Zg.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-C_kqXssD.js → chunk-YZCP3GAM-CWXUVxFj.js} +1 -1
- package/dist/client/assets/{chunk-ZZ45TVLE-B_xtlma5.js → chunk-ZZ45TVLE-CXjZua4f.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DnUQ2iGN.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-Dwp5vuOB.js +1 -0
- package/dist/client/assets/clone-aWrl-obY.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-YToNpueF.js +1 -0
- package/dist/client/assets/{cytoscape.esm-DRReFUEO.js → cytoscape.esm-DdcHPZAZ.js} +2 -2
- package/dist/client/assets/{dagre-KV5264BT-BWYGReXF.js → dagre-KV5264BT-QFYoTa0z.js} +1 -1
- package/dist/client/assets/{dagre-DU-XBdcU.js → dagre-tvaMpP4D.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-DpUUhvWz.js → diagram-5BDNPKRD-DM0NNmEN.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-BJoTrUAx.js → diagram-G4DWMVQ6-TiLkMmwt.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-CAk1GW5g.js → diagram-MMDJMWI5-DM1ykqrB.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-Cct6g7FA.js → diagram-TYMM5635-BEOLX1wr.js} +1 -1
- package/dist/client/assets/{dist-61sCfOmN.js → dist-CCBhd9az.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-DHs2bXUj.js → erDiagram-SMLLAGMA-DZcjZq6z.js} +1 -1
- package/dist/client/assets/{flatten-mnWyE-RB.js → flatten-SRIRKgqP.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-DLu-6dfC.js → flowDiagram-DWJPFMVM-B1AVT9es.js} +1 -1
- package/dist/client/assets/ganttDiagram-T4ZO3ILL-BCEXws9V.js +292 -0
- package/dist/client/assets/gitGraph-7Q5UKJZL-BE3Mcr-v.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-Bc_rL3_k.js → gitGraphDiagram-UUTBAWPF-CVznBDOl.js} +1 -1
- package/dist/client/assets/{graphlib-BVMK0xYE.js → graphlib-C4fWcyt1.js} +1 -1
- package/dist/client/assets/index-6LShOAAb.js +79 -0
- package/dist/client/assets/index-C16wNcPQ.css +2 -0
- package/dist/client/assets/info-OMHHGYJF-CBpXVhw-.js +1 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-Cf8u4jgP.js → infoDiagram-42DDH7IO-D8Oxr-KJ.js} +1 -1
- package/dist/client/assets/{isEmpty-CiiIHfXR.js → isEmpty-CStpjy4G.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-7n7DvfEb.js → ishikawaDiagram-UXIWVN3A-BE9KniVE.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BMkeQqJb.js → journeyDiagram-VCZTEJTY-B3lGcz06.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-B8KkeZLS.js → kanban-definition-6JOO6SKY-Bs1QdB0j.js} +1 -1
- package/dist/client/assets/{line-CVpcI6kj.js → line-CO4-KhEq.js} +1 -1
- package/dist/client/assets/{linear-DmhiOOKU.js → linear-CnaJKs0I.js} +1 -1
- package/dist/client/assets/mermaid-parser.core-CravK6bS.js +4 -0
- package/dist/client/assets/{mermaid.core-R7nXpPx-.js → mermaid.core-DTh9KJvF.js} +3 -3
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-CwcHocMZ.js → mindmap-definition-QFDTVHPH-D2xU2hfX.js} +1 -1
- package/dist/client/assets/{ordinal-k--hYEme.js → ordinal-DIg8h6NI.js} +1 -1
- package/dist/client/assets/packet-4T2RLAQJ-abaJ3V5T.js +1 -0
- package/dist/client/assets/pie-ZZUOXDRM-B12dpA7V.js +1 -0
- package/dist/client/assets/{pieDiagram-DEJITSTG-BVAn8Lmr.js → pieDiagram-DEJITSTG-CRX6y4IQ.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-C2XZ_zxa.js → quadrantDiagram-34T5L4WZ-K2HFp8O8.js} +1 -1
- package/dist/client/assets/radar-PYXPWWZC-BbBaJJN8.js +1 -0
- package/dist/client/assets/{reduce-BTlHjXna.js → reduce-CG4cgj93.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-CfO16pkI.js → requirementDiagram-MS252O5E-C-8AW0uI.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-D_4_234M.js → sankeyDiagram-XADWPNL6-Bv-_ZFS5.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-B-yHKMuK.js → sequenceDiagram-FGHM5R23-Bk4QYIPk.js} +1 -1
- package/dist/client/assets/src-XMuEuFcU.js +1 -0
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BeG2di4I.js → stateDiagram-FHFEXIEX-CI1G7zGC.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DQ0U-oto.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-DhtnMGcE.js → timeline-definition-GMOUNBTQ-CnXv8xHg.js} +1 -1
- package/dist/client/assets/treeView-SZITEDCU-CM0rCBUc.js +1 -0
- package/dist/client/assets/treemap-W4RFUUIX-CXoNE_rL.js +1 -0
- package/dist/client/assets/{vennDiagram-DHZGUBPP-CBn69TcQ.js → vennDiagram-DHZGUBPP-M5x471Ar.js} +1 -1
- package/dist/client/assets/wardley-RL74JXVD-B_EtnvOk.js +1 -0
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CEoSJmN1.js → wardleyDiagram-NUSXRM2D-BG99uPNN.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-BZ_X9tkn.js → xychartDiagram-5P7HB3ND-DO7Upr9G.js} +1 -1
- package/dist/client/index.html +2 -4
- package/dist/client/site-data/blobs/080c0e6/cHVibGljL3NpdGUtZGF0YS9vZy1pbWFnZS5wbmc.png +0 -0
- package/dist/client/site-data/blobs/55f23a1/bGFuZGluZy9wdWJsaWMvZGlmaXQvbG9nby5wbmc.png +0 -0
- package/dist/client/site-data/blobs/66ff7c6/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/blobs/e6977fe/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/manifest.json +1 -0
- package/dist/client/site-data/og-image.png +0 -0
- package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
- package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
- package/dist/server/file-watcher.d.ts +2 -1
- package/dist/server/file-watcher.js +9 -3
- package/dist/server/git-diff.d.ts +5 -0
- package/dist/server/git-diff.js +65 -1
- package/dist/server/git-diff.test.js +50 -0
- package/dist/server/server.js +265 -68
- package/dist/server/server.test.js +228 -0
- package/dist/tui/App.js +0 -1
- package/dist/types/diff.d.ts +15 -4
- package/dist/types/watch.d.ts +30 -1
- package/dist/utils/commentImports.d.ts +2 -0
- package/dist/utils/commentImports.js +119 -1
- package/dist/utils/editorOptions.d.ts +58 -35
- package/dist/utils/editorOptions.js +150 -24
- package/dist/utils/editorOptions.test.js +201 -9
- package/package.json +7 -4
- package/dist/client/assets/architecture-YZFGNWBL-Cs2Q6RQP.js +0 -1
- package/dist/client/assets/architectureDiagram-Q4EWVU46-BO4dVPUA.js +0 -36
- package/dist/client/assets/channel-_xDT1u3-.js +0 -1
- package/dist/client/assets/chunk-336JU56O-D1qa7Qzb.js +0 -2
- package/dist/client/assets/chunk-426QAEUC-6J_A_wvD.js +0 -1
- package/dist/client/assets/chunk-CFjPhJqf.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-C0QzfgZ8.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-5XrS-DAQ.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-Covl2vKy.js +0 -1
- package/dist/client/assets/clone-rhRH8pyW.js +0 -1
- package/dist/client/assets/cose-bilkent-S5V4N54A-BvXFc7Rr.js +0 -1
- package/dist/client/assets/ganttDiagram-T4ZO3ILL-CMIzlKAR.js +0 -292
- package/dist/client/assets/gitGraph-7Q5UKJZL-A_wWsXju.js +0 -1
- package/dist/client/assets/index-BPoqJmrs.js +0 -79
- package/dist/client/assets/index-Cq_APK7Y.css +0 -2
- package/dist/client/assets/info-OMHHGYJF-Bv3kK2Bb.js +0 -1
- package/dist/client/assets/mermaid-parser.core-CnJ9Tv8l.js +0 -4
- package/dist/client/assets/packet-4T2RLAQJ-D2q3-9ae.js +0 -1
- package/dist/client/assets/pie-ZZUOXDRM-GivlQcUF.js +0 -1
- package/dist/client/assets/preload-helper-DSXbuxSR.js +0 -1
- package/dist/client/assets/radar-PYXPWWZC-C9pD6VNR.js +0 -1
- package/dist/client/assets/src-CjDs0_Ij.js +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DvcSq7KE.js +0 -1
- package/dist/client/assets/treeView-SZITEDCU-BSNk8_yV.js +0 -1
- package/dist/client/assets/treemap-W4RFUUIX-ym4zQztE.js +0 -1
- package/dist/client/assets/wardley-RL74JXVD-B02H6ReJ.js +0 -1
- /package/dist/client/assets/{array-BNor45A1.js → array-DOVTz2Mq.js} +0 -0
- /package/dist/client/assets/{defaultLocale-DPzUsThw.js → defaultLocale-Ck2Xxk-C.js} +0 -0
- /package/dist/client/assets/{init-C0L3woqb.js → init-Bft5Ffpj.js} +0 -0
- /package/dist/client/assets/{katex-FOM3xZj7.js → katex-CeIlAR55.js} +0 -0
- /package/dist/client/assets/{path-sMK4d_s9.js → path-DfRbCp9y.js} +0 -0
- /package/dist/client/assets/{prism-bash-iQBez6et.js → prism-bash-CPkZUJMA.js} +0 -0
- /package/dist/client/assets/{prism-clojure-CTkJ-FW_.js → prism-clojure-BpoF2XhX.js} +0 -0
- /package/dist/client/assets/{prism-csharp-DAAROvjt.js → prism-csharp-BEk8D1-3.js} +0 -0
- /package/dist/client/assets/{prism-dart-CMjMHaBW.js → prism-dart-ByLYrdQB.js} +0 -0
- /package/dist/client/assets/{prism-elixir-B9cwzXs0.js → prism-elixir-BZtyIEab.js} +0 -0
- /package/dist/client/assets/{prism-haskell-Vgx7BCAm.js → prism-haskell-NAsbeo3V.js} +0 -0
- /package/dist/client/assets/{prism-hcl-Du4YC80h.js → prism-hcl-crnGqmVp.js} +0 -0
- /package/dist/client/assets/{prism-java-CWuFbfVD.js → prism-java-BovStacA.js} +0 -0
- /package/dist/client/assets/{prism-markup-templating-h9TC-ifW.js → prism-markup-templating-Cl8NiLjy.js} +0 -0
- /package/dist/client/assets/{prism-nix-CqauNIYa.js → prism-nix-BS_cm_1n.js} +0 -0
- /package/dist/client/assets/{prism-perl-DhcRwJzx.js → prism-perl-DGLVMq5H.js} +0 -0
- /package/dist/client/assets/{prism-php-DcBIrISj.js → prism-php-BskSwJN8.js} +0 -0
- /package/dist/client/assets/{prism-protobuf-DuPg7Jbg.js → prism-protobuf-DfbIYpO7.js} +0 -0
- /package/dist/client/assets/{prism-ruby-lhDmuasn.js → prism-ruby-FBVh1PRE.js} +0 -0
- /package/dist/client/assets/{prism-scala-YlPat9I4.js → prism-scala--9AfMHPY.js} +0 -0
- /package/dist/client/assets/{prism-solidity-C3nR0EVH.js → prism-solidity-BgJNkj1z.js} +0 -0
- /package/dist/client/assets/{prism-sql-Cz-8DmQS.js → prism-sql-C9Czmpov.js} +0 -0
- /package/dist/client/assets/{prism-vim-C3oukvmk.js → prism-vim-CzUNf0WQ.js} +0 -0
- /package/dist/client/assets/{rough.esm-DeLgKbOI.js → rough.esm-Bbn_-PMU.js} +0 -0
|
@@ -526,6 +526,234 @@ describe('Server Integration Tests', () => {
|
|
|
526
526
|
expect(output).toContain('Multi-line comment');
|
|
527
527
|
expect(output).toContain('Total comments: 2');
|
|
528
528
|
});
|
|
529
|
+
it('POST /api/comment-imports accepts valid comment imports', async () => {
|
|
530
|
+
const imports = [
|
|
531
|
+
{
|
|
532
|
+
type: 'thread',
|
|
533
|
+
filePath: 'src/example.ts',
|
|
534
|
+
position: { side: 'new', line: 10 },
|
|
535
|
+
body: 'Review comment',
|
|
536
|
+
},
|
|
537
|
+
];
|
|
538
|
+
const response = await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
539
|
+
method: 'POST',
|
|
540
|
+
headers: { 'Content-Type': 'application/json' },
|
|
541
|
+
body: JSON.stringify(imports),
|
|
542
|
+
});
|
|
543
|
+
expect(response.ok).toBe(true);
|
|
544
|
+
const data = (await response.json());
|
|
545
|
+
expect(data.success).toBe(true);
|
|
546
|
+
expect(data.importId).toEqual(expect.any(String));
|
|
547
|
+
expect(data.count).toBe(1);
|
|
548
|
+
});
|
|
549
|
+
it('POST /api/comment-imports accepts a single object', async () => {
|
|
550
|
+
const singleImport = {
|
|
551
|
+
type: 'thread',
|
|
552
|
+
filePath: 'src/example.ts',
|
|
553
|
+
position: { side: 'new', line: 5 },
|
|
554
|
+
body: 'Single object import',
|
|
555
|
+
};
|
|
556
|
+
const response = await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
557
|
+
method: 'POST',
|
|
558
|
+
headers: { 'Content-Type': 'application/json' },
|
|
559
|
+
body: JSON.stringify(singleImport),
|
|
560
|
+
});
|
|
561
|
+
expect(response.ok).toBe(true);
|
|
562
|
+
const data = (await response.json());
|
|
563
|
+
expect(data.success).toBe(true);
|
|
564
|
+
expect(data.count).toBe(1);
|
|
565
|
+
});
|
|
566
|
+
it('POST /api/comment-imports rejects invalid data', async () => {
|
|
567
|
+
const response = await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
568
|
+
method: 'POST',
|
|
569
|
+
headers: { 'Content-Type': 'application/json' },
|
|
570
|
+
body: JSON.stringify({ invalid: true }),
|
|
571
|
+
});
|
|
572
|
+
expect(response.status).toBe(400);
|
|
573
|
+
const data = (await response.json());
|
|
574
|
+
expect(data).toHaveProperty('error');
|
|
575
|
+
});
|
|
576
|
+
it('GET /api/comments-json returns empty threads by default', async () => {
|
|
577
|
+
const response = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
578
|
+
expect(response.ok).toBe(true);
|
|
579
|
+
const data = (await response.json());
|
|
580
|
+
expect(data).toHaveProperty('threads');
|
|
581
|
+
expect(data.threads).toEqual([]);
|
|
582
|
+
});
|
|
583
|
+
it('GET /api/comments-json returns threads after posting comments', async () => {
|
|
584
|
+
const comments = [{ file: 'test.js', line: 10, body: 'JSON test comment' }];
|
|
585
|
+
await fetch(`http://localhost:${port}/api/comments`, {
|
|
586
|
+
method: 'POST',
|
|
587
|
+
headers: { 'Content-Type': 'application/json' },
|
|
588
|
+
body: JSON.stringify({ comments }),
|
|
589
|
+
});
|
|
590
|
+
const response = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
591
|
+
expect(response.ok).toBe(true);
|
|
592
|
+
const data = (await response.json());
|
|
593
|
+
expect(data.threads).toHaveLength(1);
|
|
594
|
+
expect(data.threads[0].messages[0].body).toBe('JSON test comment');
|
|
595
|
+
});
|
|
596
|
+
it('POST /api/comment-imports merges into server-side threads for comments-output', async () => {
|
|
597
|
+
const imports = [
|
|
598
|
+
{
|
|
599
|
+
type: 'thread',
|
|
600
|
+
filePath: 'src/example.ts',
|
|
601
|
+
position: { side: 'new', line: 42 },
|
|
602
|
+
body: 'Merged server-side comment',
|
|
603
|
+
},
|
|
604
|
+
];
|
|
605
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
606
|
+
method: 'POST',
|
|
607
|
+
headers: { 'Content-Type': 'application/json' },
|
|
608
|
+
body: JSON.stringify(imports),
|
|
609
|
+
});
|
|
610
|
+
const outputResponse = await fetch(`http://localhost:${port}/api/comments-output`);
|
|
611
|
+
const output = await outputResponse.text();
|
|
612
|
+
expect(output).toContain('src/example.ts:L42');
|
|
613
|
+
expect(output).toContain('Merged server-side comment');
|
|
614
|
+
});
|
|
615
|
+
it('POST /api/comment-imports merges reply into existing thread', async () => {
|
|
616
|
+
// First add a thread
|
|
617
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
618
|
+
method: 'POST',
|
|
619
|
+
headers: { 'Content-Type': 'application/json' },
|
|
620
|
+
body: JSON.stringify([
|
|
621
|
+
{
|
|
622
|
+
type: 'thread',
|
|
623
|
+
filePath: 'src/reply-test.ts',
|
|
624
|
+
position: { side: 'new', line: 5 },
|
|
625
|
+
body: 'Original comment',
|
|
626
|
+
author: 'User',
|
|
627
|
+
},
|
|
628
|
+
]),
|
|
629
|
+
});
|
|
630
|
+
// Then add a reply
|
|
631
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
632
|
+
method: 'POST',
|
|
633
|
+
headers: { 'Content-Type': 'application/json' },
|
|
634
|
+
body: JSON.stringify([
|
|
635
|
+
{
|
|
636
|
+
type: 'reply',
|
|
637
|
+
filePath: 'src/reply-test.ts',
|
|
638
|
+
position: { side: 'new', line: 5 },
|
|
639
|
+
body: 'Reply to comment',
|
|
640
|
+
author: 'AI',
|
|
641
|
+
},
|
|
642
|
+
]),
|
|
643
|
+
});
|
|
644
|
+
const jsonResponse = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
645
|
+
const data = (await jsonResponse.json());
|
|
646
|
+
const thread = data.threads.find((t) => t.filePath === 'src/reply-test.ts');
|
|
647
|
+
expect(thread).toBeDefined();
|
|
648
|
+
expect(thread.messages).toHaveLength(2);
|
|
649
|
+
expect(thread.messages[0].body).toBe('Original comment');
|
|
650
|
+
expect(thread.messages[1].body).toBe('Reply to comment');
|
|
651
|
+
});
|
|
652
|
+
it('POST /api/comment-imports deduplicates identical imports', async () => {
|
|
653
|
+
const imports = [
|
|
654
|
+
{
|
|
655
|
+
type: 'thread',
|
|
656
|
+
filePath: 'src/dedup.ts',
|
|
657
|
+
position: { side: 'new', line: 1 },
|
|
658
|
+
body: 'Unique comment',
|
|
659
|
+
},
|
|
660
|
+
];
|
|
661
|
+
// Send the same import twice
|
|
662
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
663
|
+
method: 'POST',
|
|
664
|
+
headers: { 'Content-Type': 'application/json' },
|
|
665
|
+
body: JSON.stringify(imports),
|
|
666
|
+
});
|
|
667
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
668
|
+
method: 'POST',
|
|
669
|
+
headers: { 'Content-Type': 'application/json' },
|
|
670
|
+
body: JSON.stringify(imports),
|
|
671
|
+
});
|
|
672
|
+
const jsonResponse = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
673
|
+
const data = (await jsonResponse.json());
|
|
674
|
+
const threads = data.threads.filter((t) => t.filePath === 'src/dedup.ts');
|
|
675
|
+
expect(threads).toHaveLength(1);
|
|
676
|
+
});
|
|
677
|
+
it('isolates comment sessions between different diff selections', async () => {
|
|
678
|
+
const importServer = await startServer({
|
|
679
|
+
selection: { targetCommitish: 'HEAD', baseCommitish: 'HEAD^' },
|
|
680
|
+
preferredPort: 9039,
|
|
681
|
+
commentImports: [
|
|
682
|
+
{
|
|
683
|
+
type: 'thread',
|
|
684
|
+
filePath: 'src/cli/comment.test.ts',
|
|
685
|
+
position: { side: 'new', line: 10 },
|
|
686
|
+
body: 'Startup comment',
|
|
687
|
+
},
|
|
688
|
+
],
|
|
689
|
+
});
|
|
690
|
+
servers.push(importServer.server);
|
|
691
|
+
const parser = parserInstances.at(-1);
|
|
692
|
+
parser?.parseDiff.mockImplementation(async (selection) => ({
|
|
693
|
+
targetCommit: 'abc123',
|
|
694
|
+
baseCommit: 'def456',
|
|
695
|
+
baseCommitish: selection.baseCommitish === 'HEAD^' ? 'def4567' : selection.baseCommitish,
|
|
696
|
+
targetCommitish: selection.targetCommitish === 'HEAD' ? 'abc1234' : selection.targetCommitish,
|
|
697
|
+
requestedBaseCommitish: selection.baseCommitish,
|
|
698
|
+
requestedTargetCommitish: selection.targetCommitish,
|
|
699
|
+
requestedBaseMode: selection.baseMode,
|
|
700
|
+
targetMessage: 'Test commit',
|
|
701
|
+
baseMessage: 'Previous commit',
|
|
702
|
+
files: [
|
|
703
|
+
{
|
|
704
|
+
path: 'src/cli/comment.test.ts',
|
|
705
|
+
additions: 10,
|
|
706
|
+
deletions: 5,
|
|
707
|
+
chunks: [],
|
|
708
|
+
},
|
|
709
|
+
],
|
|
710
|
+
stats: { additions: 10, deletions: 5 },
|
|
711
|
+
isEmpty: false,
|
|
712
|
+
}));
|
|
713
|
+
await fetch(`http://localhost:${importServer.port}/api/comment-imports`, {
|
|
714
|
+
method: 'POST',
|
|
715
|
+
headers: { 'Content-Type': 'application/json' },
|
|
716
|
+
body: JSON.stringify([
|
|
717
|
+
{
|
|
718
|
+
type: 'thread',
|
|
719
|
+
filePath: 'src/cli/comment.test.ts',
|
|
720
|
+
position: { side: 'new', line: 20 },
|
|
721
|
+
body: 'API comment',
|
|
722
|
+
},
|
|
723
|
+
]),
|
|
724
|
+
});
|
|
725
|
+
let response = await fetch(`http://localhost:${importServer.port}/api/comments-output`);
|
|
726
|
+
let output = await response.text();
|
|
727
|
+
expect(output).toContain('Startup comment');
|
|
728
|
+
expect(output).toContain('API comment');
|
|
729
|
+
await fetch(`http://localhost:${importServer.port}/api/diff?base=feat%2F292-comment-read-write&target=codex%2Fcomment-session-state`);
|
|
730
|
+
response = await fetch(`http://localhost:${importServer.port}/api/comments-output`);
|
|
731
|
+
output = await response.text();
|
|
732
|
+
expect(output).toBe('');
|
|
733
|
+
await fetch(`http://localhost:${importServer.port}/api/comment-imports`, {
|
|
734
|
+
method: 'POST',
|
|
735
|
+
headers: { 'Content-Type': 'application/json' },
|
|
736
|
+
body: JSON.stringify([
|
|
737
|
+
{
|
|
738
|
+
type: 'thread',
|
|
739
|
+
filePath: 'src/cli/comment.test.ts',
|
|
740
|
+
position: { side: 'new', line: 30 },
|
|
741
|
+
body: 'Other diff comment',
|
|
742
|
+
},
|
|
743
|
+
]),
|
|
744
|
+
});
|
|
745
|
+
response = await fetch(`http://localhost:${importServer.port}/api/comments-output`);
|
|
746
|
+
output = await response.text();
|
|
747
|
+
expect(output).toContain('Other diff comment');
|
|
748
|
+
expect(output).not.toContain('Startup comment');
|
|
749
|
+
expect(output).not.toContain('API comment');
|
|
750
|
+
await fetch(`http://localhost:${importServer.port}/api/diff?base=HEAD%5E&target=HEAD`);
|
|
751
|
+
response = await fetch(`http://localhost:${importServer.port}/api/comments-output`);
|
|
752
|
+
output = await response.text();
|
|
753
|
+
expect(output).toContain('Startup comment');
|
|
754
|
+
expect(output).toContain('API comment');
|
|
755
|
+
expect(output).not.toContain('Other diff comment');
|
|
756
|
+
});
|
|
529
757
|
it.skip('GET /api/heartbeat returns SSE headers', async () => {
|
|
530
758
|
// Skipped due to connection reset issues in test environment
|
|
531
759
|
// SSE endpoint functionality is verified through manual testing
|
package/dist/tui/App.js
CHANGED
|
@@ -28,7 +28,6 @@ const App = ({ selection, mode, repoPath, contextLines }) => {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
useEffect(() => {
|
|
31
|
-
// oxlint-disable-next-line react-hooks-js/set-state-in-effect -- intentional: trigger initial diff load when revisions change
|
|
32
31
|
void loadDiff();
|
|
33
32
|
// oxlint-disable-next-line react/exhaustive-deps
|
|
34
33
|
}, [baseCommitish, targetCommitish]);
|
package/dist/types/diff.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface ParsedDiff {
|
|
|
32
32
|
chunks: DiffChunk[];
|
|
33
33
|
}
|
|
34
34
|
export type DiffViewMode = 'split' | 'unified';
|
|
35
|
-
|
|
35
|
+
type LegacyDiffViewMode = 'side-by-side' | 'inline';
|
|
36
36
|
export type DiffSide = 'old' | 'new';
|
|
37
37
|
export type DiffLineRange = number | {
|
|
38
38
|
start: number;
|
|
@@ -138,6 +138,17 @@ export interface ViewedFileRecord {
|
|
|
138
138
|
viewedAt: string;
|
|
139
139
|
diffContentHash: string;
|
|
140
140
|
}
|
|
141
|
+
export interface ViewedHashIndexEntry {
|
|
142
|
+
filePath: string;
|
|
143
|
+
diffContentHash: string;
|
|
144
|
+
hashVersion: 1;
|
|
145
|
+
viewedAt: string;
|
|
146
|
+
}
|
|
147
|
+
export interface ViewedHashIndex {
|
|
148
|
+
version: 1;
|
|
149
|
+
lastModifiedAt: string;
|
|
150
|
+
entries: ViewedHashIndexEntry[];
|
|
151
|
+
}
|
|
141
152
|
export interface LegacyDiffContextStorage {
|
|
142
153
|
version: 1;
|
|
143
154
|
baseCommitish: string;
|
|
@@ -168,11 +179,11 @@ export interface CommentThread {
|
|
|
168
179
|
codeContent?: string;
|
|
169
180
|
messages: DiffCommentMessage[];
|
|
170
181
|
}
|
|
171
|
-
|
|
182
|
+
interface RevisionOption {
|
|
172
183
|
value: string;
|
|
173
184
|
label: string;
|
|
174
185
|
}
|
|
175
|
-
|
|
186
|
+
interface BranchInfo {
|
|
176
187
|
name: string;
|
|
177
188
|
current: boolean;
|
|
178
189
|
}
|
|
@@ -199,7 +210,7 @@ export interface FileExpandedState {
|
|
|
199
210
|
oldTotalLines?: number;
|
|
200
211
|
newTotalLines?: number;
|
|
201
212
|
}
|
|
202
|
-
|
|
213
|
+
interface ExpandedRange {
|
|
203
214
|
chunkIndex: number;
|
|
204
215
|
direction: 'up' | 'down';
|
|
205
216
|
count: number;
|
package/dist/types/watch.d.ts
CHANGED
|
@@ -5,12 +5,41 @@ export declare enum DiffMode {
|
|
|
5
5
|
DOT = "dot",// HEAD vs working (all changes)
|
|
6
6
|
SPECIFIC = "specific"
|
|
7
7
|
}
|
|
8
|
+
type WatchChangeType = 'file' | 'commit' | 'staging';
|
|
9
|
+
interface ConnectedWatchEvent {
|
|
10
|
+
type: 'connected';
|
|
11
|
+
diffMode: DiffMode;
|
|
12
|
+
changeType: WatchChangeType;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
message?: string;
|
|
15
|
+
}
|
|
16
|
+
interface ReloadWatchEvent {
|
|
17
|
+
type: 'reload';
|
|
18
|
+
diffMode: DiffMode;
|
|
19
|
+
changeType: WatchChangeType;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
message?: string;
|
|
22
|
+
}
|
|
23
|
+
interface ErrorWatchEvent {
|
|
24
|
+
type: 'error';
|
|
25
|
+
diffMode: DiffMode;
|
|
26
|
+
changeType: WatchChangeType;
|
|
27
|
+
timestamp: string;
|
|
28
|
+
message?: string;
|
|
29
|
+
}
|
|
30
|
+
interface CommentsChangedWatchEvent {
|
|
31
|
+
type: 'commentsChanged';
|
|
32
|
+
version: number;
|
|
33
|
+
timestamp: string;
|
|
34
|
+
}
|
|
35
|
+
export type WatchEvent = ConnectedWatchEvent | ReloadWatchEvent | ErrorWatchEvent | CommentsChangedWatchEvent;
|
|
8
36
|
export interface ClientWatchState {
|
|
9
37
|
isWatchEnabled: boolean;
|
|
10
38
|
diffMode: DiffMode;
|
|
11
39
|
shouldReload: boolean;
|
|
12
40
|
isReloading: boolean;
|
|
13
41
|
lastChangeTime: Date | null;
|
|
14
|
-
lastChangeType:
|
|
42
|
+
lastChangeType: WatchChangeType | null;
|
|
15
43
|
connectionStatus: 'connected' | 'disconnected' | 'reconnecting';
|
|
16
44
|
}
|
|
45
|
+
export {};
|
|
@@ -3,7 +3,9 @@ interface MergeCommentImportsResult {
|
|
|
3
3
|
threads: DiffCommentThread[];
|
|
4
4
|
warnings: string[];
|
|
5
5
|
}
|
|
6
|
+
export declare function normalizeCommentImports(input: unknown): CommentImport[];
|
|
6
7
|
export declare function parseCommentImportValue(value: string): CommentImport[];
|
|
8
|
+
export declare function mergeCommentThreads(existingThreads: DiffCommentThread[], incomingThreads: DiffCommentThread[]): MergeCommentImportsResult;
|
|
7
9
|
export declare function serializeCommentImports(commentImports: CommentImport[]): string;
|
|
8
10
|
export declare function mergeCommentImports(existingThreads: DiffCommentThread[], commentImports: CommentImport[]): MergeCommentImportsResult;
|
|
9
11
|
export {};
|
|
@@ -105,7 +105,7 @@ function normalizeCommentImportEntry(value) {
|
|
|
105
105
|
};
|
|
106
106
|
return normalized;
|
|
107
107
|
}
|
|
108
|
-
function normalizeCommentImports(input) {
|
|
108
|
+
export function normalizeCommentImports(input) {
|
|
109
109
|
if (Array.isArray(input)) {
|
|
110
110
|
return input.map((entry) => normalizeCommentImportEntry(entry));
|
|
111
111
|
}
|
|
@@ -204,6 +204,124 @@ function createImportedReply(commentImport, now) {
|
|
|
204
204
|
updatedAt,
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
|
+
function cloneLineRange(line) {
|
|
208
|
+
if (typeof line === 'number') {
|
|
209
|
+
return line;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
start: line.start,
|
|
213
|
+
end: line.end,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function clonePosition(position) {
|
|
217
|
+
return {
|
|
218
|
+
side: position.side,
|
|
219
|
+
line: cloneLineRange(position.line),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function cloneCodeSnapshot(snapshot) {
|
|
223
|
+
if (!snapshot) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
content: snapshot.content,
|
|
228
|
+
language: snapshot.language,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function cloneMessage(message) {
|
|
232
|
+
return {
|
|
233
|
+
id: message.id,
|
|
234
|
+
body: message.body,
|
|
235
|
+
author: message.author,
|
|
236
|
+
createdAt: message.createdAt,
|
|
237
|
+
updatedAt: message.updatedAt,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function cloneThread(thread) {
|
|
241
|
+
return {
|
|
242
|
+
id: thread.id,
|
|
243
|
+
filePath: thread.filePath,
|
|
244
|
+
createdAt: thread.createdAt,
|
|
245
|
+
updatedAt: thread.updatedAt,
|
|
246
|
+
position: clonePosition(thread.position),
|
|
247
|
+
codeSnapshot: cloneCodeSnapshot(thread.codeSnapshot),
|
|
248
|
+
messages: thread.messages.map(cloneMessage),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function messagesMatch(left, right) {
|
|
252
|
+
if (left.id === right.id) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (normalizeAuthor(left.author) !== normalizeAuthor(right.author)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return left.body === right.body && left.createdAt === right.createdAt;
|
|
259
|
+
}
|
|
260
|
+
function threadsMatch(left, right) {
|
|
261
|
+
if (left.id === right.id) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
if (left.filePath !== right.filePath || !positionsMatch(left.position, right.position)) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
const leftRoot = left.messages[0];
|
|
268
|
+
const rightRoot = right.messages[0];
|
|
269
|
+
if (!leftRoot || !rightRoot) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
return messagesMatch(leftRoot, rightRoot);
|
|
273
|
+
}
|
|
274
|
+
function sortReplies(left, right) {
|
|
275
|
+
const createdAtOrder = left.createdAt.localeCompare(right.createdAt);
|
|
276
|
+
if (createdAtOrder !== 0) {
|
|
277
|
+
return createdAtOrder;
|
|
278
|
+
}
|
|
279
|
+
return left.id.localeCompare(right.id);
|
|
280
|
+
}
|
|
281
|
+
function pickNewerMessage(existingMessage, incomingMessage) {
|
|
282
|
+
if (incomingMessage.updatedAt.localeCompare(existingMessage.updatedAt) >= 0) {
|
|
283
|
+
return cloneMessage(incomingMessage);
|
|
284
|
+
}
|
|
285
|
+
return cloneMessage(existingMessage);
|
|
286
|
+
}
|
|
287
|
+
function mergeThread(existingThread, incomingThread) {
|
|
288
|
+
const mergedMessages = existingThread.messages.map(cloneMessage);
|
|
289
|
+
for (const incomingMessage of incomingThread.messages) {
|
|
290
|
+
const matchIndex = mergedMessages.findIndex((message) => messagesMatch(message, incomingMessage));
|
|
291
|
+
if (matchIndex >= 0) {
|
|
292
|
+
mergedMessages[matchIndex] = pickNewerMessage(mergedMessages[matchIndex], incomingMessage);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
mergedMessages.push(cloneMessage(incomingMessage));
|
|
296
|
+
}
|
|
297
|
+
const [rootMessage, ...replyMessages] = mergedMessages;
|
|
298
|
+
const orderedMessages = rootMessage
|
|
299
|
+
? [rootMessage, ...replyMessages.sort(sortReplies)]
|
|
300
|
+
: replyMessages.sort(sortReplies);
|
|
301
|
+
const updatedAt = orderedMessages.reduce((latest, message) => maxIsoTimestamp(latest, message.updatedAt), maxIsoTimestamp(existingThread.updatedAt, incomingThread.updatedAt));
|
|
302
|
+
return {
|
|
303
|
+
...cloneThread(existingThread),
|
|
304
|
+
createdAt: existingThread.createdAt.localeCompare(incomingThread.createdAt) <= 0
|
|
305
|
+
? existingThread.createdAt
|
|
306
|
+
: incomingThread.createdAt,
|
|
307
|
+
updatedAt,
|
|
308
|
+
position: clonePosition(existingThread.position),
|
|
309
|
+
codeSnapshot: cloneCodeSnapshot(incomingThread.codeSnapshot ?? existingThread.codeSnapshot),
|
|
310
|
+
messages: orderedMessages,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
export function mergeCommentThreads(existingThreads, incomingThreads) {
|
|
314
|
+
const threads = existingThreads.map(cloneThread);
|
|
315
|
+
for (const incomingThread of incomingThreads) {
|
|
316
|
+
const existingIndex = threads.findIndex((thread) => threadsMatch(thread, incomingThread));
|
|
317
|
+
if (existingIndex < 0) {
|
|
318
|
+
threads.push(cloneThread(incomingThread));
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
threads[existingIndex] = mergeThread(threads[existingIndex], incomingThread);
|
|
322
|
+
}
|
|
323
|
+
return { threads, warnings: [] };
|
|
324
|
+
}
|
|
207
325
|
function serializeLineRange(line) {
|
|
208
326
|
if (typeof line === 'number') {
|
|
209
327
|
return line;
|
|
@@ -1,37 +1,60 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
readonly
|
|
4
|
-
readonly
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
readonly label: "Zed";
|
|
20
|
-
readonly protocol: "zed";
|
|
21
|
-
readonly cliCommand: "zed";
|
|
22
|
-
readonly cliArgs: readonly [];
|
|
23
|
-
readonly lineFormat: "path-suffix";
|
|
24
|
-
readonly aliases: readonly ["zed"];
|
|
25
|
-
}, {
|
|
26
|
-
readonly id: "none";
|
|
27
|
-
readonly label: "Hide “Open in editor” button";
|
|
28
|
-
readonly protocol: null;
|
|
29
|
-
readonly cliCommand: null;
|
|
30
|
-
readonly cliArgs: readonly [];
|
|
31
|
-
readonly lineFormat: "goto-flag";
|
|
32
|
-
readonly aliases: readonly ["none", "disabled", "off"];
|
|
33
|
-
}];
|
|
34
|
-
export type EditorOption = (typeof EDITOR_OPTIONS)[number];
|
|
35
|
-
export type EditorOptionId = EditorOption['id'];
|
|
1
|
+
export type EditorOptionId = 'vscode' | 'cursor' | 'zed' | 'textmate' | 'bbedit' | 'emacs' | 'macvim' | 'sublime' | 'nova' | 'custom' | 'none';
|
|
2
|
+
interface EditorOption {
|
|
3
|
+
readonly id: EditorOptionId;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
/**
|
|
6
|
+
* Executable to run. Empty string for the `custom` placeholder (filled in by
|
|
7
|
+
* the user) and for `none` (open-in-editor disabled).
|
|
8
|
+
*/
|
|
9
|
+
readonly command: string;
|
|
10
|
+
/**
|
|
11
|
+
* Whitespace-separated argument template. Supports the `%file` and `%line`
|
|
12
|
+
* placeholders. Parsed just before spawning so quoted segments survive:
|
|
13
|
+
* e.g. `'-l %line "%file"'`.
|
|
14
|
+
*/
|
|
15
|
+
readonly argsTemplate: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const CUSTOM_EDITOR_ID: EditorOptionId;
|
|
18
|
+
export declare const NONE_EDITOR_ID: EditorOptionId;
|
|
36
19
|
export declare const DEFAULT_EDITOR_ID: EditorOptionId;
|
|
20
|
+
export declare const EDITOR_OPTIONS: readonly [EditorOption, ...EditorOption[]];
|
|
21
|
+
export declare const DEFAULT_EDITOR_OPTION: EditorOption;
|
|
22
|
+
/**
|
|
23
|
+
* Case-insensitive lookup by id. Used mostly for the `DIFIT_EDITOR` / `EDITOR`
|
|
24
|
+
* environment-variable fallback on the server; the browser UI passes the full
|
|
25
|
+
* `{id, command, argsTemplate}` shape so it does not rely on this helper.
|
|
26
|
+
*/
|
|
37
27
|
export declare const resolveEditorOption: (input?: string) => EditorOption;
|
|
28
|
+
/**
|
|
29
|
+
* Tokenise a user-supplied arguments template string into individual CLI
|
|
30
|
+
* arguments. Supports single and double quoted segments so paths and flags
|
|
31
|
+
* containing spaces can be preserved.
|
|
32
|
+
*
|
|
33
|
+
* Inside double-quoted segments `\"` decodes to a literal `"` and `\\` to a
|
|
34
|
+
* literal `\`, mirroring common shell intuition. Other `\x` sequences pass
|
|
35
|
+
* through untouched so Lisp/regex fragments like `\s` or `\$` stay intact.
|
|
36
|
+
* Single-quoted segments are fully literal. The template is handed to
|
|
37
|
+
* `spawn` as argv directly – no shell is involved.
|
|
38
|
+
*/
|
|
39
|
+
export declare const parseEditorArgsTemplate: (template: string | undefined | null) => string[];
|
|
40
|
+
interface EditorSpawnSpec {
|
|
41
|
+
readonly command: string;
|
|
42
|
+
readonly args: readonly string[];
|
|
43
|
+
}
|
|
44
|
+
interface BuildEditorSpawnSpecInput {
|
|
45
|
+
readonly command: string;
|
|
46
|
+
readonly argsTemplate: string;
|
|
47
|
+
readonly filePath: string;
|
|
48
|
+
readonly lineNumber: number | null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build the final `{ command, args }` tuple to hand to `child_process.spawn`.
|
|
52
|
+
* Returns `null` when the command is empty so callers can surface a clear
|
|
53
|
+
* "not configured" error instead of spawning an empty process.
|
|
54
|
+
*
|
|
55
|
+
* `%file` and `%line` are substituted as-is inside each token, so composite
|
|
56
|
+
* tokens such as `"%file:%line"` become `"/abs/file.ts:42"`. When no line
|
|
57
|
+
* number is available the substitution falls back to `1`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const buildEditorSpawnSpec: (input: BuildEditorSpawnSpecInput) => EditorSpawnSpec | null;
|
|
60
|
+
export {};
|