@bendyline/docblocks-react 1.1.1 → 2.0.0

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 (335) hide show
  1. package/README.md +50 -15
  2. package/dist/AppMenu/AppMenu.d.ts +29 -2
  3. package/dist/AppMenu/AppMenu.d.ts.map +1 -1
  4. package/dist/AppMenu/AppMenu.js +41 -2
  5. package/dist/AppMenu/AppMenu.js.map +1 -1
  6. package/dist/AppMenu/index.d.ts +1 -1
  7. package/dist/AppMenu/index.d.ts.map +1 -1
  8. package/dist/DocBlocksShell/DocBlocksShell.d.ts +17 -4
  9. package/dist/DocBlocksShell/DocBlocksShell.d.ts.map +1 -1
  10. package/dist/DocBlocksShell/DocBlocksShell.js +1710 -299
  11. package/dist/DocBlocksShell/DocBlocksShell.js.map +1 -1
  12. package/dist/DocBlocksShell/LazyEditorShell.d.ts +3 -0
  13. package/dist/DocBlocksShell/LazyEditorShell.d.ts.map +1 -0
  14. package/dist/DocBlocksShell/LazyEditorShell.js +3 -0
  15. package/dist/DocBlocksShell/LazyEditorShell.js.map +1 -0
  16. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts +13 -0
  17. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts.map +1 -0
  18. package/dist/DocBlocksShell/UpdateAvailableNotice.js +16 -0
  19. package/dist/DocBlocksShell/UpdateAvailableNotice.js.map +1 -0
  20. package/dist/DocBlocksShell/document-title.d.ts +3 -0
  21. package/dist/DocBlocksShell/document-title.d.ts.map +1 -0
  22. package/dist/DocBlocksShell/document-title.js +17 -0
  23. package/dist/DocBlocksShell/document-title.js.map +1 -0
  24. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts +14 -0
  25. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts.map +1 -0
  26. package/dist/DocBlocksShell/workspace-authority-barrier.js +23 -0
  27. package/dist/DocBlocksShell/workspace-authority-barrier.js.map +1 -0
  28. package/dist/Export/DeferredExportToolbarControls.d.ts +5 -0
  29. package/dist/Export/DeferredExportToolbarControls.d.ts.map +1 -0
  30. package/dist/Export/DeferredExportToolbarControls.js +10 -0
  31. package/dist/Export/DeferredExportToolbarControls.js.map +1 -0
  32. package/dist/Export/ExportDialog.d.ts +10 -1
  33. package/dist/Export/ExportDialog.d.ts.map +1 -1
  34. package/dist/Export/ExportDialog.js +39 -20
  35. package/dist/Export/ExportDialog.js.map +1 -1
  36. package/dist/Export/ExportToolbarControls.d.ts +21 -1
  37. package/dist/Export/ExportToolbarControls.d.ts.map +1 -1
  38. package/dist/Export/ExportToolbarControls.js +161 -26
  39. package/dist/Export/ExportToolbarControls.js.map +1 -1
  40. package/dist/Export/export-destination.d.ts +3 -0
  41. package/dist/Export/export-destination.d.ts.map +1 -0
  42. package/dist/Export/export-destination.js +32 -0
  43. package/dist/Export/export-destination.js.map +1 -0
  44. package/dist/Export/index.d.ts +8 -4
  45. package/dist/Export/index.d.ts.map +1 -1
  46. package/dist/Export/index.js +4 -1
  47. package/dist/Export/index.js.map +1 -1
  48. package/dist/Export/public-api.d.ts +12 -0
  49. package/dist/Export/public-api.d.ts.map +1 -0
  50. package/dist/Export/public-api.js +9 -0
  51. package/dist/Export/public-api.js.map +1 -0
  52. package/dist/Export/run-export.d.ts +3 -1
  53. package/dist/Export/run-export.d.ts.map +1 -1
  54. package/dist/Export/run-export.js +58 -25
  55. package/dist/Export/run-export.js.map +1 -1
  56. package/dist/Export/transform-summaries.d.ts +7 -0
  57. package/dist/Export/transform-summaries.d.ts.map +1 -0
  58. package/dist/Export/transform-summaries.js +6 -0
  59. package/dist/Export/transform-summaries.js.map +1 -0
  60. package/dist/ExportDialog-MEUISWZX.js +9 -0
  61. package/dist/ExportDialog-MEUISWZX.js.map +1 -0
  62. package/dist/ExportToolbarControls-D2YDWOXW.js +373 -0
  63. package/dist/ExportToolbarControls-D2YDWOXW.js.map +1 -0
  64. package/dist/FileExplorer/FileExplorer.d.ts +26 -4
  65. package/dist/FileExplorer/FileExplorer.d.ts.map +1 -1
  66. package/dist/FileExplorer/FileExplorer.js +204 -29
  67. package/dist/FileExplorer/FileExplorer.js.map +1 -1
  68. package/dist/FileExplorer/FileTreeNode.d.ts +24 -3
  69. package/dist/FileExplorer/FileTreeNode.d.ts.map +1 -1
  70. package/dist/FileExplorer/FileTreeNode.js +27 -8
  71. package/dist/FileExplorer/FileTreeNode.js.map +1 -1
  72. package/dist/FileExplorer/index.d.ts +1 -1
  73. package/dist/FileExplorer/index.d.ts.map +1 -1
  74. package/dist/FileExplorer/useFileTree.d.ts +5 -3
  75. package/dist/FileExplorer/useFileTree.d.ts.map +1 -1
  76. package/dist/FileExplorer/useFileTree.js +173 -13
  77. package/dist/FileExplorer/useFileTree.js.map +1 -1
  78. package/dist/Git/GitAuthGuidanceDialog.d.ts +18 -0
  79. package/dist/Git/GitAuthGuidanceDialog.d.ts.map +1 -0
  80. package/dist/Git/GitAuthGuidanceDialog.js +27 -0
  81. package/dist/Git/GitAuthGuidanceDialog.js.map +1 -0
  82. package/dist/Git/GitBranchesDialog.d.ts +13 -0
  83. package/dist/Git/GitBranchesDialog.d.ts.map +1 -0
  84. package/dist/Git/GitBranchesDialog.js +59 -0
  85. package/dist/Git/GitBranchesDialog.js.map +1 -0
  86. package/dist/Git/GitCloneDialog.d.ts +14 -0
  87. package/dist/Git/GitCloneDialog.d.ts.map +1 -0
  88. package/dist/Git/GitCloneDialog.js +79 -0
  89. package/dist/Git/GitCloneDialog.js.map +1 -0
  90. package/dist/Git/GitCommitDialog.d.ts +12 -0
  91. package/dist/Git/GitCommitDialog.d.ts.map +1 -0
  92. package/dist/Git/GitCommitDialog.js +38 -0
  93. package/dist/Git/GitCommitDialog.js.map +1 -0
  94. package/dist/Git/GitConflictsDialog.d.ts +12 -0
  95. package/dist/Git/GitConflictsDialog.d.ts.map +1 -0
  96. package/dist/Git/GitConflictsDialog.js +17 -0
  97. package/dist/Git/GitConflictsDialog.js.map +1 -0
  98. package/dist/Git/GitContext.d.ts +89 -0
  99. package/dist/Git/GitContext.d.ts.map +1 -0
  100. package/dist/Git/GitContext.js +15 -0
  101. package/dist/Git/GitContext.js.map +1 -0
  102. package/dist/Git/GitDiffDialog.d.ts +20 -0
  103. package/dist/Git/GitDiffDialog.d.ts.map +1 -0
  104. package/dist/Git/GitDiffDialog.js +100 -0
  105. package/dist/Git/GitDiffDialog.js.map +1 -0
  106. package/dist/Git/GitDiffView.d.ts +23 -0
  107. package/dist/Git/GitDiffView.d.ts.map +1 -0
  108. package/dist/Git/GitDiffView.js +77 -0
  109. package/dist/Git/GitDiffView.js.map +1 -0
  110. package/dist/Git/GitHistoryDialog.d.ts +15 -0
  111. package/dist/Git/GitHistoryDialog.d.ts.map +1 -0
  112. package/dist/Git/GitHistoryDialog.js +107 -0
  113. package/dist/Git/GitHistoryDialog.js.map +1 -0
  114. package/dist/Git/GitStatusBar.d.ts +8 -0
  115. package/dist/Git/GitStatusBar.d.ts.map +1 -0
  116. package/dist/Git/GitStatusBar.js +49 -0
  117. package/dist/Git/GitStatusBar.js.map +1 -0
  118. package/dist/Git/GitToolbarControl.d.ts +15 -0
  119. package/dist/Git/GitToolbarControl.d.ts.map +1 -0
  120. package/dist/Git/GitToolbarControl.js +51 -0
  121. package/dist/Git/GitToolbarControl.js.map +1 -0
  122. package/dist/Git/GitUI.d.ts +14 -0
  123. package/dist/Git/GitUI.d.ts.map +1 -0
  124. package/dist/Git/GitUI.js +21 -0
  125. package/dist/Git/GitUI.js.map +1 -0
  126. package/dist/Git/commit-selection.d.ts +22 -0
  127. package/dist/Git/commit-selection.d.ts.map +1 -0
  128. package/dist/Git/commit-selection.js +47 -0
  129. package/dist/Git/commit-selection.js.map +1 -0
  130. package/dist/Git/git-clone-utils.d.ts +8 -0
  131. package/dist/Git/git-clone-utils.d.ts.map +1 -0
  132. package/dist/Git/git-clone-utils.js +26 -0
  133. package/dist/Git/git-clone-utils.js.map +1 -0
  134. package/dist/Git/git-status.d.ts +24 -0
  135. package/dist/Git/git-status.d.ts.map +1 -0
  136. package/dist/Git/git-status.js +105 -0
  137. package/dist/Git/git-status.js.map +1 -0
  138. package/dist/Git/useGit.d.ts +15 -0
  139. package/dist/Git/useGit.d.ts.map +1 -0
  140. package/dist/Git/useGit.js +206 -0
  141. package/dist/Git/useGit.js.map +1 -0
  142. package/dist/Git/useGitStatus.d.ts +17 -0
  143. package/dist/Git/useGitStatus.d.ts.map +1 -0
  144. package/dist/Git/useGitStatus.js +63 -0
  145. package/dist/Git/useGitStatus.js.map +1 -0
  146. package/dist/GitToolbarControl-LMIR4R7I.js +117 -0
  147. package/dist/GitToolbarControl-LMIR4R7I.js.map +1 -0
  148. package/dist/GitUI-KGF5MSNV.js +1048 -0
  149. package/dist/GitUI-KGF5MSNV.js.map +1 -0
  150. package/dist/LazyEditorShell-EF2BWRXU.js +6 -0
  151. package/dist/LazyEditorShell-EF2BWRXU.js.map +1 -0
  152. package/dist/Settings/Settings.d.ts +23 -0
  153. package/dist/Settings/Settings.d.ts.map +1 -0
  154. package/dist/Settings/Settings.js +22 -0
  155. package/dist/Settings/Settings.js.map +1 -0
  156. package/dist/Settings/public-api.d.ts +3 -0
  157. package/dist/Settings/public-api.d.ts.map +1 -0
  158. package/dist/Settings/public-api.js +2 -0
  159. package/dist/Settings/public-api.js.map +1 -0
  160. package/dist/WorkspacePicker/WorkspacePicker.d.ts +6 -1
  161. package/dist/WorkspacePicker/WorkspacePicker.d.ts.map +1 -1
  162. package/dist/WorkspacePicker/WorkspacePicker.js +16 -5
  163. package/dist/WorkspacePicker/WorkspacePicker.js.map +1 -1
  164. package/dist/chunk-3SAXDU2U.js +157 -0
  165. package/dist/chunk-3SAXDU2U.js.map +1 -0
  166. package/dist/chunk-4XXSWN3T.js +13 -0
  167. package/dist/chunk-4XXSWN3T.js.map +1 -0
  168. package/dist/chunk-5A4KEWKT.js +97 -0
  169. package/dist/chunk-5A4KEWKT.js.map +1 -0
  170. package/dist/chunk-AOVTHZU7.js +321 -0
  171. package/dist/chunk-AOVTHZU7.js.map +1 -0
  172. package/dist/chunk-DD4VFJQI.js +51 -0
  173. package/dist/chunk-DD4VFJQI.js.map +1 -0
  174. package/dist/chunk-GSNZSCIK.js +55 -0
  175. package/dist/chunk-GSNZSCIK.js.map +1 -0
  176. package/dist/chunk-VHDP4IPL.js +70 -0
  177. package/dist/chunk-VHDP4IPL.js.map +1 -0
  178. package/dist/chunk-ZI56BNB7.js +270 -0
  179. package/dist/chunk-ZI56BNB7.js.map +1 -0
  180. package/dist/components/Dialog.d.ts +22 -0
  181. package/dist/components/Dialog.d.ts.map +1 -0
  182. package/dist/components/Dialog.js +33 -0
  183. package/dist/components/Dialog.js.map +1 -0
  184. package/dist/editor.d.ts +10 -0
  185. package/dist/editor.d.ts.map +1 -0
  186. package/dist/editor.js +70 -0
  187. package/dist/editor.js.map +1 -0
  188. package/dist/export/index.d.ts +87 -0
  189. package/dist/export/index.js +26 -0
  190. package/dist/export/index.js.map +1 -0
  191. package/dist/hooks/index.d.ts +2 -1
  192. package/dist/hooks/index.d.ts.map +1 -1
  193. package/dist/hooks/index.js +1 -1
  194. package/dist/hooks/index.js.map +1 -1
  195. package/dist/hooks/useDocumentSession.d.ts +16 -0
  196. package/dist/hooks/useDocumentSession.d.ts.map +1 -0
  197. package/dist/hooks/useDocumentSession.js +18 -0
  198. package/dist/hooks/useDocumentSession.js.map +1 -0
  199. package/dist/icons.d.ts +11 -6
  200. package/dist/icons.d.ts.map +1 -1
  201. package/dist/icons.js +21 -20
  202. package/dist/icons.js.map +1 -1
  203. package/dist/index.d.ts +8 -5
  204. package/dist/index.d.ts.map +1 -1
  205. package/dist/index.js +4 -2
  206. package/dist/index.js.map +1 -1
  207. package/dist/preferences/theme.d.ts +18 -0
  208. package/dist/preferences/theme.d.ts.map +1 -0
  209. package/dist/preferences/theme.js +63 -0
  210. package/dist/preferences/theme.js.map +1 -0
  211. package/dist/provider-lease.d.ts +10 -0
  212. package/dist/provider-lease.d.ts.map +1 -0
  213. package/dist/provider-lease.js +42 -0
  214. package/dist/provider-lease.js.map +1 -0
  215. package/dist/run-export-ICTZITXC.js +10 -0
  216. package/dist/run-export-ICTZITXC.js.map +1 -0
  217. package/dist/settings/index.d.ts +28 -0
  218. package/dist/settings/index.js +12 -0
  219. package/dist/settings/index.js.map +1 -0
  220. package/package.json +27 -10
  221. package/src/AppMenu/AppMenu.tsx +169 -99
  222. package/src/AppMenu/index.ts +1 -1
  223. package/src/DocBlocksShell/DocBlocksShell.tsx +2183 -516
  224. package/src/DocBlocksShell/LazyEditorShell.ts +2 -0
  225. package/src/DocBlocksShell/UpdateAvailableNotice.tsx +60 -0
  226. package/src/DocBlocksShell/document-title.ts +19 -0
  227. package/src/DocBlocksShell/workspace-authority-barrier.ts +26 -0
  228. package/src/Export/DeferredExportToolbarControls.tsx +23 -0
  229. package/src/Export/ExportDialog.tsx +102 -27
  230. package/src/Export/ExportToolbarControls.tsx +343 -57
  231. package/src/Export/export-destination.ts +30 -0
  232. package/src/Export/index.ts +18 -4
  233. package/src/Export/public-api.ts +17 -0
  234. package/src/Export/run-export.ts +66 -29
  235. package/src/Export/transform-summaries.ts +14 -0
  236. package/src/FileExplorer/FileExplorer.tsx +284 -38
  237. package/src/FileExplorer/FileTreeNode.tsx +100 -8
  238. package/src/FileExplorer/index.ts +1 -1
  239. package/src/FileExplorer/useFileTree.ts +190 -16
  240. package/src/Git/GitAuthGuidanceDialog.tsx +84 -0
  241. package/src/Git/GitBranchesDialog.tsx +144 -0
  242. package/src/Git/GitCloneDialog.tsx +200 -0
  243. package/src/Git/GitCommitDialog.tsx +119 -0
  244. package/src/Git/GitConflictsDialog.tsx +62 -0
  245. package/src/Git/GitContext.tsx +88 -0
  246. package/src/Git/GitDiffDialog.tsx +147 -0
  247. package/src/Git/GitDiffView.tsx +105 -0
  248. package/src/Git/GitHistoryDialog.tsx +195 -0
  249. package/src/Git/GitStatusBar.tsx +157 -0
  250. package/src/Git/GitToolbarControl.tsx +123 -0
  251. package/src/Git/GitUI.tsx +68 -0
  252. package/src/Git/commit-selection.ts +63 -0
  253. package/src/Git/git-clone-utils.ts +23 -0
  254. package/src/Git/git-status.ts +113 -0
  255. package/src/Git/useGit.ts +279 -0
  256. package/src/Git/useGitStatus.ts +78 -0
  257. package/src/Settings/Settings.tsx +118 -0
  258. package/src/Settings/public-api.ts +8 -0
  259. package/src/WorkspacePicker/WorkspacePicker.tsx +62 -8
  260. package/src/components/Dialog.tsx +81 -0
  261. package/src/editor.ts +72 -0
  262. package/src/hooks/index.ts +2 -1
  263. package/src/hooks/useDocumentSession.ts +37 -0
  264. package/src/icons.tsx +33 -55
  265. package/src/index.ts +35 -5
  266. package/src/preferences/theme.ts +67 -0
  267. package/src/provider-lease.ts +52 -0
  268. package/src/styles/docblocks.css +2247 -77
  269. package/src/types/pwa-dom.d.ts +31 -0
  270. package/dist/hooks/useAutoSave.d.ts +0 -15
  271. package/dist/hooks/useAutoSave.d.ts.map +0 -1
  272. package/dist/hooks/useAutoSave.js +0 -83
  273. package/dist/hooks/useAutoSave.js.map +0 -1
  274. package/src/fonts/fonts.css +0 -362
  275. package/src/fonts/licenses/LICENSE-cormorant-garamond.txt +0 -93
  276. package/src/fonts/licenses/LICENSE-crimson-text.txt +0 -93
  277. package/src/fonts/licenses/LICENSE-dm-sans.txt +0 -93
  278. package/src/fonts/licenses/LICENSE-dm-serif-display.txt +0 -93
  279. package/src/fonts/licenses/LICENSE-hanken-grotesk.txt +0 -94
  280. package/src/fonts/licenses/LICENSE-ibm-plex-sans.txt +0 -93
  281. package/src/fonts/licenses/LICENSE-inter.txt +0 -93
  282. package/src/fonts/licenses/LICENSE-jetbrains-mono.txt +0 -93
  283. package/src/fonts/licenses/LICENSE-lora.txt +0 -93
  284. package/src/fonts/licenses/LICENSE-merriweather.txt +0 -93
  285. package/src/fonts/licenses/LICENSE-oswald.txt +0 -93
  286. package/src/fonts/licenses/LICENSE-playfair-display.txt +0 -93
  287. package/src/fonts/licenses/LICENSE-pt-serif.txt +0 -93
  288. package/src/fonts/licenses/LICENSE-roboto.txt +0 -93
  289. package/src/fonts/licenses/LICENSE-source-serif-4.txt +0 -93
  290. package/src/fonts/woff2/cormorant-garamond-400.woff2 +0 -0
  291. package/src/fonts/woff2/cormorant-garamond-700.woff2 +0 -0
  292. package/src/fonts/woff2/crimson-text-400.woff2 +0 -0
  293. package/src/fonts/woff2/crimson-text-400i.woff2 +0 -0
  294. package/src/fonts/woff2/crimson-text-700.woff2 +0 -0
  295. package/src/fonts/woff2/dm-sans-400.woff2 +0 -0
  296. package/src/fonts/woff2/dm-sans-400i.woff2 +0 -0
  297. package/src/fonts/woff2/dm-sans-500.woff2 +0 -0
  298. package/src/fonts/woff2/dm-sans-700.woff2 +0 -0
  299. package/src/fonts/woff2/dm-serif-display-400.woff2 +0 -0
  300. package/src/fonts/woff2/hanken-grotesk-400.woff2 +0 -0
  301. package/src/fonts/woff2/hanken-grotesk-400i.woff2 +0 -0
  302. package/src/fonts/woff2/hanken-grotesk-500.woff2 +0 -0
  303. package/src/fonts/woff2/hanken-grotesk-600.woff2 +0 -0
  304. package/src/fonts/woff2/hanken-grotesk-700.woff2 +0 -0
  305. package/src/fonts/woff2/ibm-plex-sans-400.woff2 +0 -0
  306. package/src/fonts/woff2/ibm-plex-sans-400i.woff2 +0 -0
  307. package/src/fonts/woff2/ibm-plex-sans-500.woff2 +0 -0
  308. package/src/fonts/woff2/ibm-plex-sans-600.woff2 +0 -0
  309. package/src/fonts/woff2/ibm-plex-sans-700.woff2 +0 -0
  310. package/src/fonts/woff2/inter-400.woff2 +0 -0
  311. package/src/fonts/woff2/inter-400i.woff2 +0 -0
  312. package/src/fonts/woff2/inter-500.woff2 +0 -0
  313. package/src/fonts/woff2/inter-600.woff2 +0 -0
  314. package/src/fonts/woff2/inter-700.woff2 +0 -0
  315. package/src/fonts/woff2/jetbrains-mono-400.woff2 +0 -0
  316. package/src/fonts/woff2/jetbrains-mono-400i.woff2 +0 -0
  317. package/src/fonts/woff2/jetbrains-mono-700.woff2 +0 -0
  318. package/src/fonts/woff2/lora-400.woff2 +0 -0
  319. package/src/fonts/woff2/lora-400i.woff2 +0 -0
  320. package/src/fonts/woff2/lora-700.woff2 +0 -0
  321. package/src/fonts/woff2/merriweather-400.woff2 +0 -0
  322. package/src/fonts/woff2/merriweather-700.woff2 +0 -0
  323. package/src/fonts/woff2/oswald-400.woff2 +0 -0
  324. package/src/fonts/woff2/oswald-700.woff2 +0 -0
  325. package/src/fonts/woff2/playfair-display-400.woff2 +0 -0
  326. package/src/fonts/woff2/playfair-display-700.woff2 +0 -0
  327. package/src/fonts/woff2/pt-serif-400.woff2 +0 -0
  328. package/src/fonts/woff2/pt-serif-700.woff2 +0 -0
  329. package/src/fonts/woff2/roboto-400.woff2 +0 -0
  330. package/src/fonts/woff2/roboto-400i.woff2 +0 -0
  331. package/src/fonts/woff2/roboto-700.woff2 +0 -0
  332. package/src/fonts/woff2/source-serif-4-400.woff2 +0 -0
  333. package/src/fonts/woff2/source-serif-4-400i.woff2 +0 -0
  334. package/src/fonts/woff2/source-serif-4-700.woff2 +0 -0
  335. package/src/hooks/useAutoSave.ts +0 -116
@@ -0,0 +1,105 @@
1
+ /**
2
+ * GitDiffView — read-only Monaco side-by-side diff.
3
+ *
4
+ * Monaco is never statically imported: squisq's useMonacoLoader() lazily
5
+ * imports the editor bundle once per page and hands back the namespace.
6
+ * The diff editor and both text models are created imperatively into a
7
+ * host div and disposed on unmount (the classic Monaco leak). Content and
8
+ * theme changes update the existing editor without recreating it.
9
+ */
10
+
11
+ import { useEffect, useRef } from 'react';
12
+ import {
13
+ detectLanguageFromFileName,
14
+ useMonacoLoader,
15
+ type UseMonacoLoaderResult,
16
+ } from '@bendyline/squisq-editor-react';
17
+
18
+ type MonacoModule = NonNullable<UseMonacoLoaderResult['monaco']>;
19
+ type DiffEditorInstance = ReturnType<MonacoModule['editor']['createDiffEditor']>;
20
+ type TextModel = ReturnType<MonacoModule['editor']['createModel']>;
21
+
22
+ export interface GitDiffViewProps {
23
+ original: string;
24
+ modified: string;
25
+ /** Used for language detection (markdown for .md, plain text otherwise). */
26
+ fileName: string;
27
+ theme: 'light' | 'dark';
28
+ /** Optional column headers rendered above the two panes. */
29
+ labels?: { original: string; modified: string };
30
+ }
31
+
32
+ function languageFor(fileName: string): string {
33
+ const detected = detectLanguageFromFileName(fileName);
34
+ if (detected) return detected;
35
+ return fileName.toLowerCase().endsWith('.md') ? 'markdown' : 'plaintext';
36
+ }
37
+
38
+ export function GitDiffView({ original, modified, fileName, theme, labels }: GitDiffViewProps) {
39
+ const { monaco, ready } = useMonacoLoader();
40
+ const hostRef = useRef<HTMLDivElement | null>(null);
41
+ const editorRef = useRef<DiffEditorInstance | null>(null);
42
+ const modelsRef = useRef<{ original: TextModel; modified: TextModel } | null>(null);
43
+
44
+ const language = languageFor(fileName);
45
+
46
+ // Create the editor once monaco settles; dispose editor AND models on unmount.
47
+ useEffect(() => {
48
+ const host = hostRef.current;
49
+ if (!monaco || !host) return;
50
+ const originalModel = monaco.editor.createModel('', 'plaintext');
51
+ const modifiedModel = monaco.editor.createModel('', 'plaintext');
52
+ const editor = monaco.editor.createDiffEditor(host, {
53
+ readOnly: true,
54
+ renderSideBySide: true,
55
+ automaticLayout: true,
56
+ minimap: { enabled: false },
57
+ wordWrap: 'on',
58
+ scrollBeyondLastLine: false,
59
+ renderOverviewRuler: false,
60
+ });
61
+ editor.setModel({ original: originalModel, modified: modifiedModel });
62
+ editorRef.current = editor;
63
+ modelsRef.current = { original: originalModel, modified: modifiedModel };
64
+ return () => {
65
+ editorRef.current = null;
66
+ modelsRef.current = null;
67
+ editor.dispose();
68
+ originalModel.dispose();
69
+ modifiedModel.dispose();
70
+ };
71
+ }, [monaco]);
72
+
73
+ // Sync content + language into the existing models — never recreate the editor.
74
+ useEffect(() => {
75
+ if (!monaco) return;
76
+ const models = modelsRef.current;
77
+ if (!models) return;
78
+ if (models.original.getValue() !== original) models.original.setValue(original);
79
+ if (models.modified.getValue() !== modified) models.modified.setValue(modified);
80
+ monaco.editor.setModelLanguage(models.original, language);
81
+ monaco.editor.setModelLanguage(models.modified, language);
82
+ }, [monaco, original, modified, language]);
83
+
84
+ // React to theme prop changes.
85
+ useEffect(() => {
86
+ if (!monaco) return;
87
+ monaco.editor.setTheme(theme === 'dark' ? 'vs-dark' : 'vs');
88
+ }, [monaco, theme]);
89
+
90
+ if (!ready) {
91
+ return <div className="db-git-diff-loading">Loading diff…</div>;
92
+ }
93
+
94
+ return (
95
+ <>
96
+ {labels && (
97
+ <div className="db-git-diff-labels">
98
+ <span>{labels.original}</span>
99
+ <span>{labels.modified}</span>
100
+ </div>
101
+ )}
102
+ <div ref={hostRef} className="db-git-diff-host" />
103
+ </>
104
+ );
105
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * GitHistoryDialog — paged commit log for the repository or a single file.
3
+ *
4
+ * Pages `git log` 50 commits at a time; a row expands (one at a time) to
5
+ * show the full message body and the files changed in that commit — each
6
+ * file opens the commit-vs-parent diff dialog (which replaces this one;
7
+ * acceptable for v1).
8
+ */
9
+
10
+ import { useCallback, useEffect, useState } from 'react';
11
+ import type { GitFileChange, GitLogEntry } from '@bendyline/docblocks/host';
12
+ import { Dialog } from '../components/Dialog.js';
13
+ import { useGitContext } from './GitContext.js';
14
+ import { BADGE_GLYPHS, BADGE_LABELS, badgeKindFor } from './git-status.js';
15
+
16
+ export interface GitHistoryDialogProps {
17
+ /** Slash-prefixed workspace-relative path; absent → whole-repo history. */
18
+ path?: string;
19
+ onClose: () => void;
20
+ }
21
+
22
+ const PAGE_SIZE = 50;
23
+
24
+ type CommitDetail = { body: string; files: GitFileChange[] } | { error: string };
25
+
26
+ /** Message body beyond the subject line, or '' when it adds nothing. */
27
+ function extraBody(body: string, subject: string): string {
28
+ const trimmedBody = body.trim();
29
+ const trimmedSubject = subject.trim();
30
+ if (!trimmedBody || trimmedBody === trimmedSubject) return '';
31
+ if (trimmedBody.startsWith(trimmedSubject)) {
32
+ return trimmedBody.slice(trimmedSubject.length).trim();
33
+ }
34
+ return trimmedBody;
35
+ }
36
+
37
+ function formatDate(iso: string): string {
38
+ const date = new Date(iso);
39
+ return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
40
+ }
41
+
42
+ export function GitHistoryDialog({ path, onClose }: GitHistoryDialogProps) {
43
+ const git = useGitContext();
44
+ const gitApi = git?.gitApi ?? null;
45
+ const repositoryId = git?.repositoryId ?? null;
46
+
47
+ const [entries, setEntries] = useState<GitLogEntry[]>([]);
48
+ const [loading, setLoading] = useState(false);
49
+ const [error, setError] = useState<string | null>(null);
50
+ const [hasMore, setHasMore] = useState(false);
51
+ const [expandedSha, setExpandedSha] = useState<string | null>(null);
52
+ const [details, setDetails] = useState<ReadonlyMap<string, CommitDetail>>(new Map());
53
+
54
+ const loadPage = useCallback(
55
+ async (skip: number) => {
56
+ if (!gitApi || !repositoryId) return;
57
+ setLoading(true);
58
+ setError(null);
59
+ try {
60
+ const result = await gitApi.log(repositoryId, { path, maxCount: PAGE_SIZE, skip });
61
+ if (!result.ok) {
62
+ setError(result.error.message);
63
+ return;
64
+ }
65
+ setEntries((prev) => (skip === 0 ? result.value : [...prev, ...result.value]));
66
+ setHasMore(result.value.length === PAGE_SIZE);
67
+ } finally {
68
+ setLoading(false);
69
+ }
70
+ },
71
+ [gitApi, repositoryId, path],
72
+ );
73
+
74
+ useEffect(() => {
75
+ setEntries([]);
76
+ setExpandedSha(null);
77
+ setDetails(new Map());
78
+ setHasMore(false);
79
+ void loadPage(0);
80
+ }, [loadPage]);
81
+
82
+ const handleRowClick = (sha: string) => {
83
+ if (expandedSha === sha) {
84
+ setExpandedSha(null);
85
+ return;
86
+ }
87
+ setExpandedSha(sha);
88
+ if (!gitApi || !repositoryId || details.has(sha)) return;
89
+ void gitApi.commitFiles(repositoryId, sha).then((result) => {
90
+ setDetails((prev) => {
91
+ const next = new Map(prev);
92
+ next.set(sha, result.ok ? result.value : { error: result.error.message });
93
+ return next;
94
+ });
95
+ });
96
+ };
97
+
98
+ if (!git) return null;
99
+
100
+ const baseName = path ? path.replace(/^\/+/, '').split('/').pop() : null;
101
+ const title = baseName ? `History — ${baseName}` : 'Commit history';
102
+
103
+ return (
104
+ <Dialog title={title} size="wide" onClose={onClose}>
105
+ {error && (
106
+ <p className="db-git-form-error" role="alert">
107
+ {error}
108
+ </p>
109
+ )}
110
+ {!error && entries.length === 0 && loading && <p className="db-git-hint">Loading history…</p>}
111
+ {!error && entries.length === 0 && !loading && <p className="db-git-hint">No commits yet.</p>}
112
+ {entries.length > 0 && (
113
+ <ul className="db-git-history-list">
114
+ {entries.map((entry) => {
115
+ const expanded = expandedSha === entry.sha;
116
+ const detail = details.get(entry.sha);
117
+ const meta = [
118
+ entry.authorName,
119
+ entry.sha.slice(0, 7),
120
+ formatDate(entry.authorDate),
121
+ entry.refs.join(', '),
122
+ ]
123
+ .filter(Boolean)
124
+ .join(' · ');
125
+ const body =
126
+ detail && !('error' in detail) ? extraBody(detail.body, entry.subject) : '';
127
+ return (
128
+ <li key={entry.sha} className="db-git-history-item">
129
+ <button
130
+ type="button"
131
+ className="db-git-history-toggle"
132
+ aria-expanded={expanded}
133
+ onClick={() => handleRowClick(entry.sha)}
134
+ >
135
+ <span className="db-git-history-subject">{entry.subject}</span>
136
+ <span className="db-git-history-meta">{meta}</span>
137
+ </button>
138
+ {expanded && !detail && <p className="db-git-hint">Loading…</p>}
139
+ {expanded && detail && 'error' in detail && (
140
+ <p className="db-git-form-error" role="alert">
141
+ {detail.error}
142
+ </p>
143
+ )}
144
+ {expanded && detail && !('error' in detail) && (
145
+ <>
146
+ {body && <pre className="db-git-history-body">{body}</pre>}
147
+ <ul className="db-git-history-files">
148
+ {detail.files.map((file) => {
149
+ const kind = badgeKindFor(file);
150
+ return (
151
+ <li key={file.path}>
152
+ <button
153
+ type="button"
154
+ className="db-git-history-file"
155
+ onClick={() =>
156
+ git.openDialog({
157
+ kind: 'diff',
158
+ path: file.path,
159
+ revision: { sha: entry.sha },
160
+ })
161
+ }
162
+ >
163
+ <span
164
+ className="db-git-file-kind"
165
+ aria-label={BADGE_LABELS[kind]}
166
+ title={BADGE_LABELS[kind]}
167
+ >
168
+ {BADGE_GLYPHS[kind]}
169
+ </span>
170
+ {file.path.replace(/^\/+/, '')}
171
+ </button>
172
+ </li>
173
+ );
174
+ })}
175
+ </ul>
176
+ </>
177
+ )}
178
+ </li>
179
+ );
180
+ })}
181
+ </ul>
182
+ )}
183
+ {hasMore && (
184
+ <button
185
+ type="button"
186
+ className="db-git-secondary-btn"
187
+ disabled={loading}
188
+ onClick={() => void loadPage(entries.length)}
189
+ >
190
+ Show older commits
191
+ </button>
192
+ )}
193
+ </Dialog>
194
+ );
195
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * GitStatusBar — slim sidebar row showing branch, ahead/behind, and dirty
3
+ * count, with the repo actions menu (commit, sync, branches, history,
4
+ * remote conveniences). Renders nothing unless the active workspace is a
5
+ * git repository.
6
+ */
7
+
8
+ import { useCallback, useEffect, useRef, useState } from 'react';
9
+ import { useGitContext } from './GitContext.js';
10
+ import { formatAheadBehind, summarizeDirty } from './git-status.js';
11
+
12
+ export function GitStatusBar() {
13
+ const git = useGitContext();
14
+ const [menuOpen, setMenuOpen] = useState(false);
15
+ const rootRef = useRef<HTMLDivElement>(null);
16
+
17
+ useEffect(() => {
18
+ if (!menuOpen) return;
19
+ const onPointerDown = (e: PointerEvent) => {
20
+ if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
21
+ setMenuOpen(false);
22
+ }
23
+ };
24
+ document.addEventListener('pointerdown', onPointerDown);
25
+ return () => document.removeEventListener('pointerdown', onPointerDown);
26
+ }, [menuOpen]);
27
+
28
+ const runItem = useCallback((action: () => void) => {
29
+ setMenuOpen(false);
30
+ action();
31
+ }, []);
32
+
33
+ if (!git || !git.repo || !git.status) return null;
34
+
35
+ const { status } = git;
36
+ const branchLabel = status.detached ? 'detached HEAD' : (status.branch ?? 'no branch');
37
+ const aheadBehind = formatAheadBehind(status);
38
+ const dirty = summarizeDirty(status);
39
+ const merging = status.operation === 'merge';
40
+ const chipLabelParts = [`Git: branch ${branchLabel}`];
41
+ if (status.ahead > 0) chipLabelParts.push(`${status.ahead} ahead`);
42
+ if (status.behind > 0) chipLabelParts.push(`${status.behind} behind`);
43
+ if (dirty) chipLabelParts.push(dirty);
44
+ if (merging) chipLabelParts.push('merge in progress');
45
+
46
+ const canCreatePr =
47
+ git.capabilities?.ghAvailable === true && git.remoteWeb?.host === 'github.com';
48
+
49
+ return (
50
+ <div ref={rootRef} className="db-git-statusbar">
51
+ <button
52
+ type="button"
53
+ className="db-git-statusbar-chip"
54
+ onClick={() => setMenuOpen((open) => !open)}
55
+ aria-haspopup="menu"
56
+ aria-expanded={menuOpen}
57
+ aria-label={chipLabelParts.join(', ')}
58
+ title={chipLabelParts.join(', ')}
59
+ >
60
+ <span className="db-git-branch-glyph" aria-hidden="true">
61
+ {'⎇'}
62
+ </span>
63
+ <span className="db-git-statusbar-branch">{branchLabel}</span>
64
+ {aheadBehind && <span className="db-git-statusbar-counts">{aheadBehind}</span>}
65
+ {dirty && <span className="db-git-statusbar-dirty">{dirty}</span>}
66
+ {merging && <span className="db-git-statusbar-merge">merging</span>}
67
+ {git.busy && (
68
+ <span className="db-git-statusbar-busy" aria-hidden="true">
69
+ {'…'}
70
+ </span>
71
+ )}
72
+ </button>
73
+
74
+ {menuOpen && (
75
+ <div className="db-git-menu" role="menu" aria-label="Git actions">
76
+ <button
77
+ type="button"
78
+ role="menuitem"
79
+ className="db-git-menu-item"
80
+ disabled={status.changes.length === 0 && !merging}
81
+ onClick={() => runItem(() => git.openDialog({ kind: 'commit' }))}
82
+ >
83
+ {merging ? 'Commit merge…' : 'Commit…'}
84
+ </button>
85
+ <button
86
+ type="button"
87
+ role="menuitem"
88
+ className="db-git-menu-item"
89
+ disabled={git.busy !== null}
90
+ onClick={() => runItem(() => void git.push())}
91
+ >
92
+ {status.upstream === null && !status.unborn ? 'Publish branch' : 'Push'}
93
+ </button>
94
+ <button
95
+ type="button"
96
+ role="menuitem"
97
+ className="db-git-menu-item"
98
+ disabled={git.busy !== null}
99
+ onClick={() => runItem(() => void git.pull())}
100
+ >
101
+ Pull
102
+ </button>
103
+ <button
104
+ type="button"
105
+ role="menuitem"
106
+ className="db-git-menu-item"
107
+ disabled={git.busy !== null}
108
+ onClick={() => runItem(() => void git.fetchRemote())}
109
+ >
110
+ Fetch
111
+ </button>
112
+ <div className="db-git-menu-divider" role="separator" />
113
+ <button
114
+ type="button"
115
+ role="menuitem"
116
+ className="db-git-menu-item"
117
+ onClick={() => runItem(() => git.openDialog({ kind: 'branches' }))}
118
+ >
119
+ Branches…
120
+ </button>
121
+ <button
122
+ type="button"
123
+ role="menuitem"
124
+ className="db-git-menu-item"
125
+ onClick={() => runItem(() => git.openDialog({ kind: 'history' }))}
126
+ >
127
+ Commit history…
128
+ </button>
129
+ {(git.remoteWeb || canCreatePr) && (
130
+ <div className="db-git-menu-divider" role="separator" />
131
+ )}
132
+ {git.remoteWeb && (
133
+ <button
134
+ type="button"
135
+ role="menuitem"
136
+ className="db-git-menu-item"
137
+ onClick={() => runItem(() => git.openOnRemote())}
138
+ >
139
+ Open on {git.remoteWeb.host}
140
+ </button>
141
+ )}
142
+ {canCreatePr && (
143
+ <button
144
+ type="button"
145
+ role="menuitem"
146
+ className="db-git-menu-item"
147
+ disabled={git.busy !== null}
148
+ onClick={() => runItem(() => void git.createPullRequest())}
149
+ >
150
+ Create pull request
151
+ </button>
152
+ )}
153
+ </div>
154
+ )}
155
+ </div>
156
+ );
157
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * GitToolbarControl — per-file git actions in the editor toolbar.
3
+ *
4
+ * A single branch-glyph button (styled like the export trigger) opening a
5
+ * small dropdown: view working-tree changes, per-file history, and "Open
6
+ * on <host>" when a web remote is known. A dot overlay marks the selected
7
+ * file as modified. Null-renders without a git context, a repository, or a
8
+ * selected file. Deliberately has no editor dependency.
9
+ */
10
+
11
+ import { useEffect, useRef, useState } from 'react';
12
+ import { useGitContext } from './GitContext.js';
13
+ import { isFileDirty } from './git-status.js';
14
+
15
+ export interface GitToolbarControlProps {
16
+ /** Currently selected file path (slash-prefixed), or null. */
17
+ selectedFile: string | null;
18
+ }
19
+
20
+ export function GitToolbarControl({ selectedFile }: GitToolbarControlProps) {
21
+ const git = useGitContext();
22
+ const [menuOpen, setMenuOpen] = useState(false);
23
+ const menuRef = useRef<HTMLDivElement>(null);
24
+
25
+ // Close on outside click (same pointerdown pattern as ExportToolbarControls).
26
+ useEffect(() => {
27
+ if (!menuOpen) return;
28
+ const onPointerDown = (e: PointerEvent) => {
29
+ if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
30
+ setMenuOpen(false);
31
+ }
32
+ };
33
+ document.addEventListener('pointerdown', onPointerDown);
34
+ return () => document.removeEventListener('pointerdown', onPointerDown);
35
+ }, [menuOpen]);
36
+
37
+ if (!git || !git.repo || !selectedFile) return null;
38
+
39
+ const dirty = isFileDirty(git.status, selectedFile);
40
+ const label = dirty ? 'Git actions (file modified)' : 'Git actions';
41
+
42
+ const openDiff = () => {
43
+ setMenuOpen(false);
44
+ git.openDialog({ kind: 'diff', path: selectedFile });
45
+ };
46
+ const openHistory = () => {
47
+ setMenuOpen(false);
48
+ git.openDialog({ kind: 'history', path: selectedFile });
49
+ };
50
+ const openOnRemote = () => {
51
+ setMenuOpen(false);
52
+ git.openOnRemote(selectedFile);
53
+ };
54
+
55
+ return (
56
+ <div className="db-toolbar-menu" ref={menuRef}>
57
+ <button
58
+ type="button"
59
+ className="squisq-toolbar-button db-git-toolbar-trigger"
60
+ onClick={() => setMenuOpen((prev) => !prev)}
61
+ aria-label={label}
62
+ aria-haspopup="menu"
63
+ aria-expanded={menuOpen}
64
+ data-tooltip={label}
65
+ >
66
+ <BranchGlyph />
67
+ {dirty && <span className="db-git-toolbar-dot" aria-hidden="true" />}
68
+ </button>
69
+
70
+ {menuOpen && (
71
+ <div className="db-toolbar-menu-dropdown" role="menu">
72
+ <button
73
+ type="button"
74
+ role="menuitem"
75
+ className="db-toolbar-menu-item"
76
+ disabled={!dirty}
77
+ onClick={openDiff}
78
+ >
79
+ View changes
80
+ </button>
81
+ <button
82
+ type="button"
83
+ role="menuitem"
84
+ className="db-toolbar-menu-item"
85
+ onClick={openHistory}
86
+ >
87
+ File history…
88
+ </button>
89
+ {git.remoteWeb && (
90
+ <button
91
+ type="button"
92
+ role="menuitem"
93
+ className="db-toolbar-menu-item"
94
+ onClick={openOnRemote}
95
+ >
96
+ Open on {git.remoteWeb.host}
97
+ </button>
98
+ )}
99
+ </div>
100
+ )}
101
+ </div>
102
+ );
103
+ }
104
+
105
+ function BranchGlyph() {
106
+ return (
107
+ <svg
108
+ viewBox="0 0 16 16"
109
+ width="16"
110
+ height="16"
111
+ fill="none"
112
+ stroke="currentColor"
113
+ strokeWidth="1.5"
114
+ strokeLinecap="round"
115
+ strokeLinejoin="round"
116
+ aria-hidden="true"
117
+ >
118
+ <circle cx="4.5" cy="12" r="2" />
119
+ <circle cx="11.5" cy="4" r="2" />
120
+ <path d="M4.5 10V7.5A2.5 2.5 0 0 1 7 5h2.5" />
121
+ </svg>
122
+ );
123
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * GitUI — single mount point for the git status bar, the aria-live
3
+ * operation result line, and every git dialog. Dialog visibility lives in
4
+ * GitContext so the file tree, toolbar, and native menu can all open them.
5
+ */
6
+
7
+ import type { ElectronWorkspaceInfo } from '@bendyline/docblocks/host';
8
+ import { useGitContext } from './GitContext.js';
9
+ import { GitStatusBar } from './GitStatusBar.js';
10
+ import { GitCommitDialog } from './GitCommitDialog.js';
11
+ import { GitBranchesDialog } from './GitBranchesDialog.js';
12
+ import { GitHistoryDialog } from './GitHistoryDialog.js';
13
+ import { GitDiffDialog } from './GitDiffDialog.js';
14
+ import { GitCloneDialog } from './GitCloneDialog.js';
15
+ import { GitAuthGuidanceDialog } from './GitAuthGuidanceDialog.js';
16
+ import { GitConflictsDialog } from './GitConflictsDialog.js';
17
+
18
+ export interface GitUIProps {
19
+ /** Open a workspace file in the editor (from conflicts/history dialogs). */
20
+ onOpenFile: (path: string) => void;
21
+ /** A clone finished and its folder is registered — open the workspace. */
22
+ onWorkspaceCloned?: (info: ElectronWorkspaceInfo) => void;
23
+ }
24
+
25
+ export function GitUI({ onOpenFile, onWorkspaceCloned }: GitUIProps) {
26
+ const git = useGitContext();
27
+ if (!git || !git.available) return null;
28
+
29
+ const { dialog, closeDialog } = git;
30
+
31
+ return (
32
+ <>
33
+ <GitStatusBar />
34
+ {git.repo && (
35
+ <span className="db-git-inline-status" role="status" aria-live="polite">
36
+ {git.lastResult?.message ?? ''}
37
+ </span>
38
+ )}
39
+ {dialog.kind === 'commit' && <GitCommitDialog onClose={closeDialog} />}
40
+ {dialog.kind === 'branches' && (
41
+ <GitBranchesDialog createFocus={dialog.createFocus} onClose={closeDialog} />
42
+ )}
43
+ {dialog.kind === 'history' && <GitHistoryDialog path={dialog.path} onClose={closeDialog} />}
44
+ {dialog.kind === 'diff' && (
45
+ <GitDiffDialog path={dialog.path} revision={dialog.revision} onClose={closeDialog} />
46
+ )}
47
+ {dialog.kind === 'clone' && (
48
+ <GitCloneDialog
49
+ onClose={closeDialog}
50
+ onCloned={(info) => {
51
+ closeDialog();
52
+ onWorkspaceCloned?.(info);
53
+ }}
54
+ />
55
+ )}
56
+ {dialog.kind === 'auth-guidance' && (
57
+ <GitAuthGuidanceDialog
58
+ operation={dialog.operation}
59
+ detail={dialog.detail}
60
+ onClose={closeDialog}
61
+ />
62
+ )}
63
+ {dialog.kind === 'conflicts' && (
64
+ <GitConflictsDialog paths={dialog.paths} onOpenFile={onOpenFile} onClose={closeDialog} />
65
+ )}
66
+ </>
67
+ );
68
+ }