difit 4.0.4 → 4.0.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.
Files changed (172) hide show
  1. package/README.ja.md +3 -2
  2. package/README.ko.md +3 -2
  3. package/README.md +3 -2
  4. package/README.zh.md +3 -2
  5. package/dist/cli/comment.d.ts +2 -0
  6. package/dist/cli/comment.js +91 -0
  7. package/dist/cli/comment.test.d.ts +1 -0
  8. package/dist/cli/comment.test.js +164 -0
  9. package/dist/cli/index.js +104 -13
  10. package/dist/cli/index.test.js +5 -5
  11. package/dist/cli/utils.d.ts +1 -0
  12. package/dist/cli/utils.js +7 -0
  13. package/dist/client/assets/{_baseFor-DKyA49xd.js → _baseFor-Dq1lbcoh.js} +1 -1
  14. package/dist/client/assets/{arc-COOp7iVe.js → arc-1g1LrDb3.js} +1 -1
  15. package/dist/client/assets/architecture-YZFGNWBL-MZfdAdY6.js +1 -0
  16. package/dist/client/assets/architectureDiagram-Q4EWVU46-D87-Rmwy.js +36 -0
  17. package/dist/client/assets/{blockDiagram-DXYQGD6D-CtNJnEWN.js → blockDiagram-DXYQGD6D-Cep-MIFv.js} +1 -1
  18. package/dist/client/assets/{c4Diagram-AHTNJAMY-BqG-1m6C.js → c4Diagram-AHTNJAMY-BQuH9Txx.js} +1 -1
  19. package/dist/client/assets/channel-CJqLEVLU.js +1 -0
  20. package/dist/client/assets/{chunk-2KRD3SAO-DqP2NJNd.js → chunk-2KRD3SAO-CpQQpmvx.js} +1 -1
  21. package/dist/client/assets/chunk-336JU56O-Ddk9EzgO.js +2 -0
  22. package/dist/client/assets/chunk-426QAEUC-2xhUznDE.js +1 -0
  23. package/dist/client/assets/{chunk-4BX2VUAB-BT78EnQ6.js → chunk-4BX2VUAB-Ca-N0Wd9.js} +1 -1
  24. package/dist/client/assets/{chunk-4TB4RGXK-C4w_Bwzw.js → chunk-4TB4RGXK-ZTWP_Onw.js} +2 -2
  25. package/dist/client/assets/{chunk-55IACEB6-z3MQSTaj.js → chunk-55IACEB6-Dub40zHG.js} +1 -1
  26. package/dist/client/assets/{chunk-5FUZZQ4R-Chei69aj.js → chunk-5FUZZQ4R-Cgda0gtZ.js} +1 -1
  27. package/dist/client/assets/{chunk-5PVQY5BW-HgRiIs0X.js → chunk-5PVQY5BW-D8JPH_tm.js} +1 -1
  28. package/dist/client/assets/{chunk-67CJDMHE-B2q10-fp.js → chunk-67CJDMHE-U1KyLHzG.js} +1 -1
  29. package/dist/client/assets/{chunk-7N4EOEYR-DPgxysWq.js → chunk-7N4EOEYR-WzOy51nD.js} +1 -1
  30. package/dist/client/assets/{chunk-AA7GKIK3-BqmVmKLq.js → chunk-AA7GKIK3-DlWOj4lr.js} +1 -1
  31. package/dist/client/assets/{chunk-BSJP7CBP-CaIgleFn.js → chunk-BSJP7CBP-CcZ0op08.js} +1 -1
  32. package/dist/client/assets/{chunk-CIAEETIT-ByD-tlNF.js → chunk-CIAEETIT-qVSphnw5.js} +1 -1
  33. package/dist/client/assets/{chunk-EDXVE4YY-d3RUKKAj.js → chunk-EDXVE4YY-76SPH4sf.js} +1 -1
  34. package/dist/client/assets/{chunk-ENJZ2VHE-CNq5Qmg9.js → chunk-ENJZ2VHE-CKULNIzL.js} +1 -1
  35. package/dist/client/assets/{chunk-FMBD7UC4-DYfHJ6MV.js → chunk-FMBD7UC4-CvDPP3mb.js} +1 -1
  36. package/dist/client/assets/{chunk-FOC6F5B3-BRpSWlZj.js → chunk-FOC6F5B3-DceW0hWA.js} +1 -1
  37. package/dist/client/assets/{chunk-ICPOFSXX-B_MThwG6.js → chunk-ICPOFSXX-ChGBNZMk.js} +2 -2
  38. package/dist/client/assets/{chunk-K5T4RW27-DmamW1Ds.js → chunk-K5T4RW27-DBHdC4ln.js} +10 -10
  39. package/dist/client/assets/{chunk-KGLVRYIC-CRbg4c4z.js → chunk-KGLVRYIC-DRS7yiGQ.js} +1 -1
  40. package/dist/client/assets/{chunk-LIHQZDEY-CHQPSdB3.js → chunk-LIHQZDEY-KsE8dyJP.js} +1 -1
  41. package/dist/client/assets/{chunk-ORNJ4GCN-CIsQ4Zi4.js → chunk-ORNJ4GCN-Dnp4oHRD.js} +1 -1
  42. package/dist/client/assets/{chunk-OYMX7WX6-Cxi0kdGg.js → chunk-OYMX7WX6-CciaotDu.js} +1 -1
  43. package/dist/client/assets/chunk-QZHKN3VN-BiVE5u_E.js +1 -0
  44. package/dist/client/assets/{chunk-U2HBQHQK-V_hneCfR.js → chunk-U2HBQHQK-nbp7CjBP.js} +1 -1
  45. package/dist/client/assets/{chunk-X2U36JSP-De4pvO-I.js → chunk-X2U36JSP-Chs85loT.js} +1 -1
  46. package/dist/client/assets/{chunk-XPW4576I-B_osXKp6.js → chunk-XPW4576I-VtI9b561.js} +1 -1
  47. package/dist/client/assets/{chunk-YZCP3GAM-C_kqXssD.js → chunk-YZCP3GAM-sBsewSoO.js} +1 -1
  48. package/dist/client/assets/{chunk-ZZ45TVLE-B_xtlma5.js → chunk-ZZ45TVLE-TMgeW_px.js} +1 -1
  49. package/dist/client/assets/classDiagram-6PBFFD2Q-CfyHazmg.js +1 -0
  50. package/dist/client/assets/classDiagram-v2-HSJHXN6E-D7Rb-bnu.js +1 -0
  51. package/dist/client/assets/clone-8xC1huEg.js +1 -0
  52. package/dist/client/assets/cose-bilkent-S5V4N54A-5TzM3w9g.js +1 -0
  53. package/dist/client/assets/{cytoscape.esm-DRReFUEO.js → cytoscape.esm-DdcHPZAZ.js} +2 -2
  54. package/dist/client/assets/{dagre-KV5264BT-BWYGReXF.js → dagre-KV5264BT-xvyFOxd3.js} +1 -1
  55. package/dist/client/assets/{dagre-DU-XBdcU.js → dagre-sb6WtN4K.js} +1 -1
  56. package/dist/client/assets/{diagram-5BDNPKRD-DpUUhvWz.js → diagram-5BDNPKRD-ChRpAe5p.js} +1 -1
  57. package/dist/client/assets/{diagram-G4DWMVQ6-BJoTrUAx.js → diagram-G4DWMVQ6-C_8BED4A.js} +1 -1
  58. package/dist/client/assets/{diagram-MMDJMWI5-CAk1GW5g.js → diagram-MMDJMWI5-BMwXEou2.js} +1 -1
  59. package/dist/client/assets/{diagram-TYMM5635-Cct6g7FA.js → diagram-TYMM5635-CeAkx82D.js} +1 -1
  60. package/dist/client/assets/{dist-61sCfOmN.js → dist-CwC9dd2Z.js} +1 -1
  61. package/dist/client/assets/{erDiagram-SMLLAGMA-DHs2bXUj.js → erDiagram-SMLLAGMA-yGCTeXGt.js} +1 -1
  62. package/dist/client/assets/{flatten-mnWyE-RB.js → flatten-SRIRKgqP.js} +1 -1
  63. package/dist/client/assets/{flowDiagram-DWJPFMVM-DLu-6dfC.js → flowDiagram-DWJPFMVM-CugkvbmM.js} +1 -1
  64. package/dist/client/assets/ganttDiagram-T4ZO3ILL-BXnlBFgK.js +292 -0
  65. package/dist/client/assets/gitGraph-7Q5UKJZL-BeTWkPrd.js +1 -0
  66. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-Bc_rL3_k.js → gitGraphDiagram-UUTBAWPF-B61aCwwu.js} +1 -1
  67. package/dist/client/assets/{graphlib-BVMK0xYE.js → graphlib-BMWKz3zT.js} +1 -1
  68. package/dist/client/assets/index-D2Y8-unG.css +2 -0
  69. package/dist/client/assets/index-D9v_eYzS.js +79 -0
  70. package/dist/client/assets/info-OMHHGYJF-MUNR2tTt.js +1 -0
  71. package/dist/client/assets/{infoDiagram-42DDH7IO-Cf8u4jgP.js → infoDiagram-42DDH7IO-Bkh6nTL2.js} +1 -1
  72. package/dist/client/assets/{isEmpty-CiiIHfXR.js → isEmpty-CStpjy4G.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-7n7DvfEb.js → ishikawaDiagram-UXIWVN3A-D_fdVT6_.js} +1 -1
  74. package/dist/client/assets/{journeyDiagram-VCZTEJTY-BMkeQqJb.js → journeyDiagram-VCZTEJTY-DkXVokNF.js} +1 -1
  75. package/dist/client/assets/{kanban-definition-6JOO6SKY-B8KkeZLS.js → kanban-definition-6JOO6SKY-y8qq7qvL.js} +1 -1
  76. package/dist/client/assets/{line-CVpcI6kj.js → line-B0LcTqNY.js} +1 -1
  77. package/dist/client/assets/{linear-DmhiOOKU.js → linear-CqIjr2qp.js} +1 -1
  78. package/dist/client/assets/mermaid-parser.core-Du6QzpZO.js +4 -0
  79. package/dist/client/assets/{mermaid.core-R7nXpPx-.js → mermaid.core-CZBu-oKJ.js} +3 -3
  80. package/dist/client/assets/{mindmap-definition-QFDTVHPH-CwcHocMZ.js → mindmap-definition-QFDTVHPH-BJrRxSkM.js} +1 -1
  81. package/dist/client/assets/{ordinal-k--hYEme.js → ordinal-DIg8h6NI.js} +1 -1
  82. package/dist/client/assets/packet-4T2RLAQJ-Ci-Uu57s.js +1 -0
  83. package/dist/client/assets/pie-ZZUOXDRM-pm57XGIg.js +1 -0
  84. package/dist/client/assets/{pieDiagram-DEJITSTG-BVAn8Lmr.js → pieDiagram-DEJITSTG-Debmhc0u.js} +1 -1
  85. package/dist/client/assets/prism-clojure-BpoF2XhX.js +1 -0
  86. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-C2XZ_zxa.js → quadrantDiagram-34T5L4WZ-SE3g2BC9.js} +1 -1
  87. package/dist/client/assets/radar-PYXPWWZC-CH-AuSDw.js +1 -0
  88. package/dist/client/assets/{reduce-BTlHjXna.js → reduce-CG4cgj93.js} +1 -1
  89. package/dist/client/assets/{requirementDiagram-MS252O5E-CfO16pkI.js → requirementDiagram-MS252O5E-1mv41puC.js} +1 -1
  90. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D_4_234M.js → sankeyDiagram-XADWPNL6-CLjPRtOP.js} +1 -1
  91. package/dist/client/assets/{sequenceDiagram-FGHM5R23-B-yHKMuK.js → sequenceDiagram-FGHM5R23-Cs-P3AtR.js} +1 -1
  92. package/dist/client/assets/src-5XpQHeIJ.js +1 -0
  93. package/dist/client/assets/{stateDiagram-FHFEXIEX-BeG2di4I.js → stateDiagram-FHFEXIEX-CmB1fohY.js} +1 -1
  94. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D6jsrR-f.js +1 -0
  95. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DhtnMGcE.js → timeline-definition-GMOUNBTQ-BMUafJOI.js} +1 -1
  96. package/dist/client/assets/treeView-SZITEDCU-BGsVMAdJ.js +1 -0
  97. package/dist/client/assets/treemap-W4RFUUIX-DXnhegXy.js +1 -0
  98. package/dist/client/assets/{vennDiagram-DHZGUBPP-CBn69TcQ.js → vennDiagram-DHZGUBPP-CpZ1Qhjz.js} +1 -1
  99. package/dist/client/assets/wardley-RL74JXVD-COd5nWj-.js +1 -0
  100. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CEoSJmN1.js → wardleyDiagram-NUSXRM2D-C-zH0lsd.js} +1 -1
  101. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BZ_X9tkn.js → xychartDiagram-5P7HB3ND-SkLFuEHZ.js} +1 -1
  102. package/dist/client/index.html +2 -4
  103. package/dist/client/site-data/blobs/080c0e6/cHVibGljL3NpdGUtZGF0YS9vZy1pbWFnZS5wbmc.png +0 -0
  104. package/dist/client/site-data/blobs/55f23a1/bGFuZGluZy9wdWJsaWMvZGlmaXQvbG9nby5wbmc.png +0 -0
  105. package/dist/client/site-data/blobs/66ff7c6/cHVibGljL2xvZ28ucG5n.png +0 -0
  106. package/dist/client/site-data/blobs/e6977fe/cHVibGljL2xvZ28ucG5n.png +0 -0
  107. package/dist/client/site-data/og-image.png +0 -0
  108. package/dist/server/file-watcher.d.ts +2 -1
  109. package/dist/server/file-watcher.js +9 -3
  110. package/dist/server/git-diff.d.ts +5 -0
  111. package/dist/server/git-diff.js +65 -1
  112. package/dist/server/git-diff.test.js +50 -0
  113. package/dist/server/server.js +265 -68
  114. package/dist/server/server.test.js +228 -0
  115. package/dist/tui/App.js +0 -1
  116. package/dist/types/diff.d.ts +4 -4
  117. package/dist/types/watch.d.ts +30 -1
  118. package/dist/utils/commentImports.d.ts +2 -0
  119. package/dist/utils/commentImports.js +119 -1
  120. package/dist/utils/editorOptions.d.ts +58 -35
  121. package/dist/utils/editorOptions.js +150 -24
  122. package/dist/utils/editorOptions.test.js +201 -9
  123. package/package.json +9 -6
  124. package/dist/client/assets/architecture-YZFGNWBL-Cs2Q6RQP.js +0 -1
  125. package/dist/client/assets/architectureDiagram-Q4EWVU46-BO4dVPUA.js +0 -36
  126. package/dist/client/assets/channel-_xDT1u3-.js +0 -1
  127. package/dist/client/assets/chunk-336JU56O-D1qa7Qzb.js +0 -2
  128. package/dist/client/assets/chunk-426QAEUC-6J_A_wvD.js +0 -1
  129. package/dist/client/assets/chunk-CFjPhJqf.js +0 -1
  130. package/dist/client/assets/chunk-QZHKN3VN-C0QzfgZ8.js +0 -1
  131. package/dist/client/assets/classDiagram-6PBFFD2Q-5XrS-DAQ.js +0 -1
  132. package/dist/client/assets/classDiagram-v2-HSJHXN6E-Covl2vKy.js +0 -1
  133. package/dist/client/assets/clone-rhRH8pyW.js +0 -1
  134. package/dist/client/assets/cose-bilkent-S5V4N54A-BvXFc7Rr.js +0 -1
  135. package/dist/client/assets/ganttDiagram-T4ZO3ILL-CMIzlKAR.js +0 -292
  136. package/dist/client/assets/gitGraph-7Q5UKJZL-A_wWsXju.js +0 -1
  137. package/dist/client/assets/index-Cq_APK7Y.css +0 -2
  138. package/dist/client/assets/index-RcU838Ah.js +0 -79
  139. package/dist/client/assets/info-OMHHGYJF-Bv3kK2Bb.js +0 -1
  140. package/dist/client/assets/mermaid-parser.core-CnJ9Tv8l.js +0 -4
  141. package/dist/client/assets/packet-4T2RLAQJ-D2q3-9ae.js +0 -1
  142. package/dist/client/assets/pie-ZZUOXDRM-GivlQcUF.js +0 -1
  143. package/dist/client/assets/preload-helper-DSXbuxSR.js +0 -1
  144. package/dist/client/assets/radar-PYXPWWZC-C9pD6VNR.js +0 -1
  145. package/dist/client/assets/src-CjDs0_Ij.js +0 -1
  146. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DvcSq7KE.js +0 -1
  147. package/dist/client/assets/treeView-SZITEDCU-BSNk8_yV.js +0 -1
  148. package/dist/client/assets/treemap-W4RFUUIX-ym4zQztE.js +0 -1
  149. package/dist/client/assets/wardley-RL74JXVD-B02H6ReJ.js +0 -1
  150. /package/dist/client/assets/{array-BNor45A1.js → array-DOVTz2Mq.js} +0 -0
  151. /package/dist/client/assets/{defaultLocale-DPzUsThw.js → defaultLocale-Ck2Xxk-C.js} +0 -0
  152. /package/dist/client/assets/{init-C0L3woqb.js → init-Bft5Ffpj.js} +0 -0
  153. /package/dist/client/assets/{katex-FOM3xZj7.js → katex-CeIlAR55.js} +0 -0
  154. /package/dist/client/assets/{path-sMK4d_s9.js → path-DfRbCp9y.js} +0 -0
  155. /package/dist/client/assets/{prism-bash-iQBez6et.js → prism-bash-CPkZUJMA.js} +0 -0
  156. /package/dist/client/assets/{prism-csharp-C1RDHXRk.js → prism-csharp-BEk8D1-3.js} +0 -0
  157. /package/dist/client/assets/{prism-dart-nIH9vDUM.js → prism-dart-ByLYrdQB.js} +0 -0
  158. /package/dist/client/assets/{prism-elixir-DUMUOd7H.js → prism-elixir-BZtyIEab.js} +0 -0
  159. /package/dist/client/assets/{prism-haskell-BP3SRvzt.js → prism-haskell-NAsbeo3V.js} +0 -0
  160. /package/dist/client/assets/{prism-hcl-C-ZHJGEE.js → prism-hcl-crnGqmVp.js} +0 -0
  161. /package/dist/client/assets/{prism-java-scuShSv5.js → prism-java-BovStacA.js} +0 -0
  162. /package/dist/client/assets/{prism-markup-templating-BFXREXfb.js → prism-markup-templating-Cl8NiLjy.js} +0 -0
  163. /package/dist/client/assets/{prism-nix-CO4UPu3E.js → prism-nix-BS_cm_1n.js} +0 -0
  164. /package/dist/client/assets/{prism-perl-BBDKnHRR.js → prism-perl-DGLVMq5H.js} +0 -0
  165. /package/dist/client/assets/{prism-php-DjIafOi_.js → prism-php-BskSwJN8.js} +0 -0
  166. /package/dist/client/assets/{prism-protobuf-BE1MoFmZ.js → prism-protobuf-DfbIYpO7.js} +0 -0
  167. /package/dist/client/assets/{prism-ruby-CZ-lrXfL.js → prism-ruby-FBVh1PRE.js} +0 -0
  168. /package/dist/client/assets/{prism-scala-DgnxHuDn.js → prism-scala--9AfMHPY.js} +0 -0
  169. /package/dist/client/assets/{prism-solidity-5fSUcW9Y.js → prism-solidity-BgJNkj1z.js} +0 -0
  170. /package/dist/client/assets/{prism-sql-CKkohPI_.js → prism-sql-C9Czmpov.js} +0 -0
  171. /package/dist/client/assets/{prism-vim-CkRmxTmK.js → prism-vim-CzUNf0WQ.js} +0 -0
  172. /package/dist/client/assets/{rough.esm-DeLgKbOI.js → rough.esm-Bbn_-PMU.js} +0 -0
@@ -1,24 +1,216 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { DEFAULT_EDITOR_ID, EDITOR_OPTIONS, resolveEditorOption } from './editorOptions';
2
+ import { buildEditorSpawnSpec, CUSTOM_EDITOR_ID, DEFAULT_EDITOR_ID, EDITOR_OPTIONS, NONE_EDITOR_ID, parseEditorArgsTemplate, resolveEditorOption, } from './editorOptions';
3
3
  describe('editorOptions', () => {
4
- it('includes Zed in editor options', () => {
4
+ it('includes Zed in editor options with the unified command+args shape', () => {
5
5
  const zed = EDITOR_OPTIONS.find((option) => option.id === 'zed');
6
6
  expect(zed).toBeDefined();
7
7
  expect(zed).toMatchObject({
8
8
  id: 'zed',
9
9
  label: 'Zed',
10
- protocol: 'zed',
11
- cliCommand: 'zed',
12
- lineFormat: 'path-suffix',
10
+ command: 'zed',
11
+ argsTemplate: '%file:%line',
13
12
  });
14
13
  });
15
- it('resolves zed editor from id', () => {
14
+ it('resolves zed editor from id and case-insensitively', () => {
16
15
  expect(resolveEditorOption('zed').id).toBe('zed');
17
- });
18
- it('resolves zed editor case-insensitively', () => {
19
16
  expect(resolveEditorOption('ZED').id).toBe('zed');
20
17
  });
21
- it('falls back to default editor when unknown value is passed', () => {
18
+ it('falls back to the default editor when an unknown value is passed', () => {
22
19
  expect(resolveEditorOption('unknown-editor').id).toBe(DEFAULT_EDITOR_ID);
23
20
  });
21
+ it('keeps the "none" option with empty command/args so the UI can hide inputs', () => {
22
+ const none = EDITOR_OPTIONS.find((option) => option.id === NONE_EDITOR_ID);
23
+ expect(none).toBeDefined();
24
+ expect(none?.command).toBe('');
25
+ expect(none?.argsTemplate).toBe('');
26
+ });
27
+ it('exposes a "custom" placeholder entry with empty command/args', () => {
28
+ const custom = EDITOR_OPTIONS.find((option) => option.id === CUSTOM_EDITOR_ID);
29
+ expect(custom).toBeDefined();
30
+ expect(custom?.label).toBe('Custom…');
31
+ expect(custom?.command).toBe('');
32
+ expect(custom?.argsTemplate).toBe('');
33
+ });
34
+ it('excludes editors that are no longer widely used for code review', () => {
35
+ const ids = EDITOR_OPTIONS.map((option) => option.id);
36
+ for (const deprecated of ['textwrangler', 'lyx', 'texmaker', 'alpha', 'atom', 'textadept']) {
37
+ expect(ids, `${deprecated} should not be registered`).not.toContain(deprecated);
38
+ }
39
+ });
40
+ describe('preset editor templates', () => {
41
+ const cases = [
42
+ {
43
+ id: 'vscode',
44
+ command: 'code',
45
+ argsTemplate: '-g %file:%line',
46
+ expectedArgs: ['-g', '/tmp/file.ts:42'],
47
+ },
48
+ {
49
+ id: 'cursor',
50
+ command: 'cursor',
51
+ argsTemplate: '-g %file:%line',
52
+ expectedArgs: ['-g', '/tmp/file.ts:42'],
53
+ },
54
+ {
55
+ id: 'zed',
56
+ command: 'zed',
57
+ argsTemplate: '%file:%line',
58
+ expectedArgs: ['/tmp/file.ts:42'],
59
+ },
60
+ {
61
+ id: 'textmate',
62
+ command: 'mate',
63
+ argsTemplate: '-l %line %file',
64
+ expectedArgs: ['-l', '42', '/tmp/file.ts'],
65
+ },
66
+ {
67
+ id: 'bbedit',
68
+ command: 'bbedit',
69
+ argsTemplate: '+%line %file',
70
+ expectedArgs: ['+42', '/tmp/file.ts'],
71
+ },
72
+ {
73
+ id: 'emacs',
74
+ command: 'emacsclient',
75
+ argsTemplate: '--no-wait +%line %file',
76
+ expectedArgs: ['--no-wait', '+42', '/tmp/file.ts'],
77
+ },
78
+ {
79
+ id: 'macvim',
80
+ command: 'mvim',
81
+ argsTemplate: '--remote-silent +%line %file',
82
+ expectedArgs: ['--remote-silent', '+42', '/tmp/file.ts'],
83
+ },
84
+ {
85
+ id: 'sublime',
86
+ command: 'subl',
87
+ argsTemplate: '%file:%line',
88
+ expectedArgs: ['/tmp/file.ts:42'],
89
+ },
90
+ {
91
+ id: 'nova',
92
+ command: 'nova',
93
+ argsTemplate: 'open %file -l %line',
94
+ expectedArgs: ['open', '/tmp/file.ts', '-l', '42'],
95
+ },
96
+ ];
97
+ for (const testCase of cases) {
98
+ it(`registers ${testCase.id} with the expected command and template`, () => {
99
+ const option = EDITOR_OPTIONS.find((entry) => entry.id === testCase.id);
100
+ expect(option, `${testCase.id} should be registered`).toBeDefined();
101
+ expect(option?.command).toBe(testCase.command);
102
+ expect(option?.argsTemplate).toBe(testCase.argsTemplate);
103
+ });
104
+ it(`builds a spawn spec for ${testCase.id} with %file/%line substituted`, () => {
105
+ const option = resolveEditorOption(testCase.id);
106
+ const spec = buildEditorSpawnSpec({
107
+ command: option.command,
108
+ argsTemplate: option.argsTemplate,
109
+ filePath: '/tmp/file.ts',
110
+ lineNumber: 42,
111
+ });
112
+ expect(spec).not.toBeNull();
113
+ expect(spec?.command).toBe(testCase.command);
114
+ expect(spec?.args).toEqual(testCase.expectedArgs);
115
+ });
116
+ }
117
+ });
118
+ describe('parseEditorArgsTemplate', () => {
119
+ it('splits whitespace-separated tokens', () => {
120
+ expect(parseEditorArgsTemplate('-l %line %file')).toEqual(['-l', '%line', '%file']);
121
+ });
122
+ it('collapses runs of whitespace', () => {
123
+ expect(parseEditorArgsTemplate(' -l %line\t%file ')).toEqual(['-l', '%line', '%file']);
124
+ });
125
+ it('preserves double-quoted segments', () => {
126
+ expect(parseEditorArgsTemplate('+%line "%file"')).toEqual(['+%line', '%file']);
127
+ });
128
+ it('preserves single-quoted segments', () => {
129
+ expect(parseEditorArgsTemplate("'%file' -line %line")).toEqual(['%file', '-line', '%line']);
130
+ });
131
+ it('keeps adjacent quoted and unquoted fragments in a single token', () => {
132
+ expect(parseEditorArgsTemplate('"%file":%line')).toEqual(['%file:%line']);
133
+ });
134
+ it('returns an empty array for empty or whitespace-only input', () => {
135
+ expect(parseEditorArgsTemplate('')).toEqual([]);
136
+ expect(parseEditorArgsTemplate(' \t ')).toEqual([]);
137
+ expect(parseEditorArgsTemplate(undefined)).toEqual([]);
138
+ expect(parseEditorArgsTemplate(null)).toEqual([]);
139
+ });
140
+ it('decodes \\" and \\\\ inside double-quoted segments', () => {
141
+ expect(parseEditorArgsTemplate('--msg "say \\"hi\\""')).toEqual(['--msg', 'say "hi"']);
142
+ expect(parseEditorArgsTemplate('"a\\\\b"')).toEqual(['a\\b']);
143
+ });
144
+ it('leaves other backslash sequences inside double quotes untouched', () => {
145
+ expect(parseEditorArgsTemplate('"\\s+ \\$PATH"')).toEqual(['\\s+ \\$PATH']);
146
+ });
147
+ it('treats single-quoted segments as fully literal', () => {
148
+ expect(parseEditorArgsTemplate('\'say \\"hi\\"\'')).toEqual(['say \\"hi\\"']);
149
+ });
150
+ it('supports an emacsclient --eval template with escaped double quotes', () => {
151
+ const template = '--eval "(tctony/persp-view-file-line-external \\"%file\\" %line)"';
152
+ expect(parseEditorArgsTemplate(template)).toEqual([
153
+ '--eval',
154
+ '(tctony/persp-view-file-line-external "%file" %line)',
155
+ ]);
156
+ });
157
+ });
158
+ describe('buildEditorSpawnSpec', () => {
159
+ it('returns null when the command is blank', () => {
160
+ expect(buildEditorSpawnSpec({
161
+ command: '',
162
+ argsTemplate: '-l %line %file',
163
+ filePath: '/tmp/file.ts',
164
+ lineNumber: 1,
165
+ })).toBeNull();
166
+ expect(buildEditorSpawnSpec({
167
+ command: ' ',
168
+ argsTemplate: '%file',
169
+ filePath: '/tmp/file.ts',
170
+ lineNumber: 1,
171
+ })).toBeNull();
172
+ });
173
+ it('trims the command and substitutes %file and %line', () => {
174
+ const spec = buildEditorSpawnSpec({
175
+ command: ' mate ',
176
+ argsTemplate: '-l %line "%file"',
177
+ filePath: '/tmp/file.ts',
178
+ lineNumber: 12,
179
+ });
180
+ expect(spec).toEqual({
181
+ command: 'mate',
182
+ args: ['-l', '12', '/tmp/file.ts'],
183
+ });
184
+ });
185
+ it('substitutes line 1 when no line number is provided', () => {
186
+ const spec = buildEditorSpawnSpec({
187
+ command: 'mate',
188
+ argsTemplate: '-l %line %file',
189
+ filePath: '/tmp/file.ts',
190
+ lineNumber: null,
191
+ });
192
+ expect(spec?.args).toEqual(['-l', '1', '/tmp/file.ts']);
193
+ });
194
+ it('returns an empty args array when the template is empty', () => {
195
+ const spec = buildEditorSpawnSpec({
196
+ command: 'my-editor',
197
+ argsTemplate: '',
198
+ filePath: '/tmp/file.ts',
199
+ lineNumber: 3,
200
+ });
201
+ expect(spec).toEqual({ command: 'my-editor', args: [] });
202
+ });
203
+ it('builds a spawn spec for an emacsclient --eval elisp invocation', () => {
204
+ const spec = buildEditorSpawnSpec({
205
+ command: 'emacsclient',
206
+ argsTemplate: '--eval "(tctony/persp-view-file-line-external \\"%file\\" %line)"',
207
+ filePath: '/tmp/foo.ts',
208
+ lineNumber: 42,
209
+ });
210
+ expect(spec).toEqual({
211
+ command: 'emacsclient',
212
+ args: ['--eval', '(tctony/persp-view-file-line-external "/tmp/foo.ts" 42)'],
213
+ });
214
+ });
215
+ });
24
216
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "difit",
3
- "version": "4.0.4",
3
+ "version": "4.0.6",
4
4
  "description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
5
5
  "keywords": [
6
6
  "cli",
@@ -37,9 +37,12 @@
37
37
  },
38
38
  "scripts": {
39
39
  "dev": "node scripts/dev.js",
40
+ "dev:site": "pnpm run build:cli && node scripts/export-site-data.js && vite --config vite.config.site.ts --open /",
40
41
  "dev:cli": "tsc --project tsconfig.cli.json && NODE_ENV=development node dist/cli/index.js",
41
- "build": "tsc -b && vite build",
42
+ "build": "pnpm run build:cli && vite build",
43
+ "build:site": "pnpm run build:cli && node scripts/export-site-data.js && GITHUB_PAGES=true vite build --config vite.config.site.ts",
42
44
  "build:cli": "tsc --project tsconfig.cli.json",
45
+ "export:site-data": "node scripts/export-site-data.js",
43
46
  "package:vscode": "pnpm -C packages/vscode run package",
44
47
  "start": "pnpm run build && node dist/cli/index.js",
45
48
  "check": "oxlint . --type-aware --type-check --deny-warnings --report-unused-disable-directives",
@@ -62,7 +65,7 @@
62
65
  "@floating-ui/react": "^0.27.16",
63
66
  "@parcel/watcher": "^2.5.1",
64
67
  "commander": "^14.0.0",
65
- "diff": "^8.0.2",
68
+ "diff": "^9.0.0",
66
69
  "express": "^5.1.0",
67
70
  "ink": "^7.0.0",
68
71
  "lucide-react": "^1.0.0",
@@ -93,9 +96,9 @@
93
96
  "happy-dom": "^20.0.0",
94
97
  "knip": "^6.0.0",
95
98
  "lefthook": "^2.0.0",
96
- "oxfmt": "^0.44.0",
99
+ "oxfmt": "^0.46.0",
97
100
  "oxlint": "^1.49.0",
98
- "oxlint-tsgolint": "^0.20.0",
101
+ "oxlint-tsgolint": "^0.21.0",
99
102
  "playwright": "^1.54.1",
100
103
  "postcss": "^8.5.6",
101
104
  "tailwindcss": "^4.1.11",
@@ -107,5 +110,5 @@
107
110
  "engines": {
108
111
  "node": ">=21.0.0"
109
112
  },
110
- "packageManager": "pnpm@10.33.0"
113
+ "packageManager": "pnpm@10.33.2"
111
114
  }
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DmamW1Ds.js";import{n as e}from"./chunk-7N4EOEYR-DPgxysWq.js";export{e as createArchitectureServices};