@bendyline/docblocks-react 2.0.0 → 2.1.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 (300) hide show
  1. package/README.md +6 -0
  2. package/dist/{ExportDialog-MEUISWZX.js → ExportDialog-7LDIXJZ3.js} +3 -2
  3. package/dist/{ExportToolbarControls-D2YDWOXW.js → ExportToolbarControls-GHAOJMH6.js} +147 -12
  4. package/dist/ExportToolbarControls-GHAOJMH6.js.map +1 -0
  5. package/dist/{GitToolbarControl-LMIR4R7I.js → GitToolbarControl-GNMRVKWQ.js} +62 -43
  6. package/dist/GitToolbarControl-GNMRVKWQ.js.map +1 -0
  7. package/dist/{GitUI-KGF5MSNV.js → GitUI-VHOBQY43.js} +225 -140
  8. package/dist/GitUI-VHOBQY43.js.map +1 -0
  9. package/dist/ShareDialog-63ENBEDZ.js +213 -0
  10. package/dist/ShareDialog-63ENBEDZ.js.map +1 -0
  11. package/dist/chunk-57ZJJOC5.js +89 -0
  12. package/dist/chunk-57ZJJOC5.js.map +1 -0
  13. package/dist/chunk-FEWQUQML.js +256 -0
  14. package/dist/chunk-FEWQUQML.js.map +1 -0
  15. package/dist/chunk-FMGTHSTX.js +110 -0
  16. package/dist/chunk-FMGTHSTX.js.map +1 -0
  17. package/dist/chunk-UNYZQXWR.js +324 -0
  18. package/dist/chunk-UNYZQXWR.js.map +1 -0
  19. package/dist/editor.d.ts +5 -4
  20. package/dist/editor.js +8 -69
  21. package/dist/editor.js.map +1 -1
  22. package/dist/export/index.d.ts +7 -1
  23. package/dist/export/index.js +2 -1
  24. package/dist/index.d.ts +346 -21
  25. package/dist/index.js +6169 -20
  26. package/dist/index.js.map +1 -1
  27. package/dist/settings/index.d.ts +9 -1
  28. package/dist/settings/index.js +6 -4
  29. package/package.json +18 -13
  30. package/src/styles/docblocks.css +537 -26
  31. package/dist/AppMenu/AppMenu.d.ts +0 -53
  32. package/dist/AppMenu/AppMenu.d.ts.map +0 -1
  33. package/dist/AppMenu/AppMenu.js +0 -67
  34. package/dist/AppMenu/AppMenu.js.map +0 -1
  35. package/dist/AppMenu/index.d.ts +0 -3
  36. package/dist/AppMenu/index.d.ts.map +0 -1
  37. package/dist/AppMenu/index.js +0 -2
  38. package/dist/AppMenu/index.js.map +0 -1
  39. package/dist/DocBlocksShell/DocBlocksShell.d.ts +0 -49
  40. package/dist/DocBlocksShell/DocBlocksShell.d.ts.map +0 -1
  41. package/dist/DocBlocksShell/DocBlocksShell.js +0 -2730
  42. package/dist/DocBlocksShell/DocBlocksShell.js.map +0 -1
  43. package/dist/DocBlocksShell/LazyEditorShell.d.ts +0 -3
  44. package/dist/DocBlocksShell/LazyEditorShell.d.ts.map +0 -1
  45. package/dist/DocBlocksShell/LazyEditorShell.js +0 -3
  46. package/dist/DocBlocksShell/LazyEditorShell.js.map +0 -1
  47. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts +0 -13
  48. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts.map +0 -1
  49. package/dist/DocBlocksShell/UpdateAvailableNotice.js +0 -16
  50. package/dist/DocBlocksShell/UpdateAvailableNotice.js.map +0 -1
  51. package/dist/DocBlocksShell/document-title.d.ts +0 -3
  52. package/dist/DocBlocksShell/document-title.d.ts.map +0 -1
  53. package/dist/DocBlocksShell/document-title.js +0 -17
  54. package/dist/DocBlocksShell/document-title.js.map +0 -1
  55. package/dist/DocBlocksShell/index.d.ts +0 -3
  56. package/dist/DocBlocksShell/index.d.ts.map +0 -1
  57. package/dist/DocBlocksShell/index.js +0 -2
  58. package/dist/DocBlocksShell/index.js.map +0 -1
  59. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts +0 -14
  60. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts.map +0 -1
  61. package/dist/DocBlocksShell/workspace-authority-barrier.js +0 -23
  62. package/dist/DocBlocksShell/workspace-authority-barrier.js.map +0 -1
  63. package/dist/Export/DeferredExportToolbarControls.d.ts +0 -5
  64. package/dist/Export/DeferredExportToolbarControls.d.ts.map +0 -1
  65. package/dist/Export/DeferredExportToolbarControls.js +0 -10
  66. package/dist/Export/DeferredExportToolbarControls.js.map +0 -1
  67. package/dist/Export/ExportDialog.d.ts +0 -25
  68. package/dist/Export/ExportDialog.d.ts.map +0 -1
  69. package/dist/Export/ExportDialog.js +0 -160
  70. package/dist/Export/ExportDialog.js.map +0 -1
  71. package/dist/Export/ExportToolbarControls.d.ts +0 -37
  72. package/dist/Export/ExportToolbarControls.d.ts.map +0 -1
  73. package/dist/Export/ExportToolbarControls.js +0 -285
  74. package/dist/Export/ExportToolbarControls.js.map +0 -1
  75. package/dist/Export/export-destination.d.ts +0 -3
  76. package/dist/Export/export-destination.d.ts.map +0 -1
  77. package/dist/Export/export-destination.js +0 -32
  78. package/dist/Export/export-destination.js.map +0 -1
  79. package/dist/Export/export-options.d.ts +0 -53
  80. package/dist/Export/export-options.d.ts.map +0 -1
  81. package/dist/Export/export-options.js +0 -49
  82. package/dist/Export/export-options.js.map +0 -1
  83. package/dist/Export/index.d.ts +0 -10
  84. package/dist/Export/index.d.ts.map +0 -1
  85. package/dist/Export/index.js +0 -6
  86. package/dist/Export/index.js.map +0 -1
  87. package/dist/Export/public-api.d.ts +0 -12
  88. package/dist/Export/public-api.d.ts.map +0 -1
  89. package/dist/Export/public-api.js +0 -9
  90. package/dist/Export/public-api.js.map +0 -1
  91. package/dist/Export/run-export.d.ts +0 -10
  92. package/dist/Export/run-export.d.ts.map +0 -1
  93. package/dist/Export/run-export.js +0 -313
  94. package/dist/Export/run-export.js.map +0 -1
  95. package/dist/Export/transform-summaries.d.ts +0 -7
  96. package/dist/Export/transform-summaries.d.ts.map +0 -1
  97. package/dist/Export/transform-summaries.js +0 -6
  98. package/dist/Export/transform-summaries.js.map +0 -1
  99. package/dist/ExportToolbarControls-D2YDWOXW.js.map +0 -1
  100. package/dist/FileExplorer/FileExplorer.d.ts +0 -43
  101. package/dist/FileExplorer/FileExplorer.d.ts.map +0 -1
  102. package/dist/FileExplorer/FileExplorer.js +0 -315
  103. package/dist/FileExplorer/FileExplorer.js.map +0 -1
  104. package/dist/FileExplorer/FileTreeNode.d.ts +0 -39
  105. package/dist/FileExplorer/FileTreeNode.d.ts.map +0 -1
  106. package/dist/FileExplorer/FileTreeNode.js +0 -131
  107. package/dist/FileExplorer/FileTreeNode.js.map +0 -1
  108. package/dist/FileExplorer/index.d.ts +0 -6
  109. package/dist/FileExplorer/index.d.ts.map +0 -1
  110. package/dist/FileExplorer/index.js +0 -4
  111. package/dist/FileExplorer/index.js.map +0 -1
  112. package/dist/FileExplorer/useFileTree.d.ts +0 -37
  113. package/dist/FileExplorer/useFileTree.d.ts.map +0 -1
  114. package/dist/FileExplorer/useFileTree.js +0 -284
  115. package/dist/FileExplorer/useFileTree.js.map +0 -1
  116. package/dist/Git/GitAuthGuidanceDialog.d.ts +0 -18
  117. package/dist/Git/GitAuthGuidanceDialog.d.ts.map +0 -1
  118. package/dist/Git/GitAuthGuidanceDialog.js +0 -27
  119. package/dist/Git/GitAuthGuidanceDialog.js.map +0 -1
  120. package/dist/Git/GitBranchesDialog.d.ts +0 -13
  121. package/dist/Git/GitBranchesDialog.d.ts.map +0 -1
  122. package/dist/Git/GitBranchesDialog.js +0 -59
  123. package/dist/Git/GitBranchesDialog.js.map +0 -1
  124. package/dist/Git/GitCloneDialog.d.ts +0 -14
  125. package/dist/Git/GitCloneDialog.d.ts.map +0 -1
  126. package/dist/Git/GitCloneDialog.js +0 -79
  127. package/dist/Git/GitCloneDialog.js.map +0 -1
  128. package/dist/Git/GitCommitDialog.d.ts +0 -12
  129. package/dist/Git/GitCommitDialog.d.ts.map +0 -1
  130. package/dist/Git/GitCommitDialog.js +0 -38
  131. package/dist/Git/GitCommitDialog.js.map +0 -1
  132. package/dist/Git/GitConflictsDialog.d.ts +0 -12
  133. package/dist/Git/GitConflictsDialog.d.ts.map +0 -1
  134. package/dist/Git/GitConflictsDialog.js +0 -17
  135. package/dist/Git/GitConflictsDialog.js.map +0 -1
  136. package/dist/Git/GitContext.d.ts +0 -89
  137. package/dist/Git/GitContext.d.ts.map +0 -1
  138. package/dist/Git/GitContext.js +0 -15
  139. package/dist/Git/GitContext.js.map +0 -1
  140. package/dist/Git/GitDiffDialog.d.ts +0 -20
  141. package/dist/Git/GitDiffDialog.d.ts.map +0 -1
  142. package/dist/Git/GitDiffDialog.js +0 -100
  143. package/dist/Git/GitDiffDialog.js.map +0 -1
  144. package/dist/Git/GitDiffView.d.ts +0 -23
  145. package/dist/Git/GitDiffView.d.ts.map +0 -1
  146. package/dist/Git/GitDiffView.js +0 -77
  147. package/dist/Git/GitDiffView.js.map +0 -1
  148. package/dist/Git/GitHistoryDialog.d.ts +0 -15
  149. package/dist/Git/GitHistoryDialog.d.ts.map +0 -1
  150. package/dist/Git/GitHistoryDialog.js +0 -107
  151. package/dist/Git/GitHistoryDialog.js.map +0 -1
  152. package/dist/Git/GitStatusBar.d.ts +0 -8
  153. package/dist/Git/GitStatusBar.d.ts.map +0 -1
  154. package/dist/Git/GitStatusBar.js +0 -49
  155. package/dist/Git/GitStatusBar.js.map +0 -1
  156. package/dist/Git/GitToolbarControl.d.ts +0 -15
  157. package/dist/Git/GitToolbarControl.d.ts.map +0 -1
  158. package/dist/Git/GitToolbarControl.js +0 -51
  159. package/dist/Git/GitToolbarControl.js.map +0 -1
  160. package/dist/Git/GitUI.d.ts +0 -14
  161. package/dist/Git/GitUI.d.ts.map +0 -1
  162. package/dist/Git/GitUI.js +0 -21
  163. package/dist/Git/GitUI.js.map +0 -1
  164. package/dist/Git/commit-selection.d.ts +0 -22
  165. package/dist/Git/commit-selection.d.ts.map +0 -1
  166. package/dist/Git/commit-selection.js +0 -47
  167. package/dist/Git/commit-selection.js.map +0 -1
  168. package/dist/Git/git-clone-utils.d.ts +0 -8
  169. package/dist/Git/git-clone-utils.d.ts.map +0 -1
  170. package/dist/Git/git-clone-utils.js +0 -26
  171. package/dist/Git/git-clone-utils.js.map +0 -1
  172. package/dist/Git/git-status.d.ts +0 -24
  173. package/dist/Git/git-status.d.ts.map +0 -1
  174. package/dist/Git/git-status.js +0 -105
  175. package/dist/Git/git-status.js.map +0 -1
  176. package/dist/Git/useGit.d.ts +0 -15
  177. package/dist/Git/useGit.d.ts.map +0 -1
  178. package/dist/Git/useGit.js +0 -206
  179. package/dist/Git/useGit.js.map +0 -1
  180. package/dist/Git/useGitStatus.d.ts +0 -17
  181. package/dist/Git/useGitStatus.d.ts.map +0 -1
  182. package/dist/Git/useGitStatus.js +0 -63
  183. package/dist/Git/useGitStatus.js.map +0 -1
  184. package/dist/GitToolbarControl-LMIR4R7I.js.map +0 -1
  185. package/dist/GitUI-KGF5MSNV.js.map +0 -1
  186. package/dist/Settings/Settings.d.ts +0 -23
  187. package/dist/Settings/Settings.d.ts.map +0 -1
  188. package/dist/Settings/Settings.js +0 -22
  189. package/dist/Settings/Settings.js.map +0 -1
  190. package/dist/Settings/public-api.d.ts +0 -3
  191. package/dist/Settings/public-api.d.ts.map +0 -1
  192. package/dist/Settings/public-api.js +0 -2
  193. package/dist/Settings/public-api.js.map +0 -1
  194. package/dist/WorkspacePicker/WorkspacePicker.d.ts +0 -22
  195. package/dist/WorkspacePicker/WorkspacePicker.d.ts.map +0 -1
  196. package/dist/WorkspacePicker/WorkspacePicker.js +0 -74
  197. package/dist/WorkspacePicker/WorkspacePicker.js.map +0 -1
  198. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts +0 -11
  199. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts.map +0 -1
  200. package/dist/WorkspacePicker/WorkspaceSettingsButton.js +0 -27
  201. package/dist/WorkspacePicker/WorkspaceSettingsButton.js.map +0 -1
  202. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts +0 -20
  203. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts.map +0 -1
  204. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js +0 -36
  205. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js.map +0 -1
  206. package/dist/WorkspacePicker/index.d.ts +0 -3
  207. package/dist/WorkspacePicker/index.d.ts.map +0 -1
  208. package/dist/WorkspacePicker/index.js +0 -2
  209. package/dist/WorkspacePicker/index.js.map +0 -1
  210. package/dist/chunk-3SAXDU2U.js +0 -157
  211. package/dist/chunk-3SAXDU2U.js.map +0 -1
  212. package/dist/chunk-AOVTHZU7.js +0 -321
  213. package/dist/chunk-AOVTHZU7.js.map +0 -1
  214. package/dist/chunk-GSNZSCIK.js +0 -55
  215. package/dist/chunk-GSNZSCIK.js.map +0 -1
  216. package/dist/components/Dialog.d.ts +0 -22
  217. package/dist/components/Dialog.d.ts.map +0 -1
  218. package/dist/components/Dialog.js +0 -33
  219. package/dist/components/Dialog.js.map +0 -1
  220. package/dist/editor.d.ts.map +0 -1
  221. package/dist/hooks/index.d.ts +0 -3
  222. package/dist/hooks/index.d.ts.map +0 -1
  223. package/dist/hooks/index.js +0 -2
  224. package/dist/hooks/index.js.map +0 -1
  225. package/dist/hooks/useDocumentSession.d.ts +0 -16
  226. package/dist/hooks/useDocumentSession.d.ts.map +0 -1
  227. package/dist/hooks/useDocumentSession.js +0 -18
  228. package/dist/hooks/useDocumentSession.js.map +0 -1
  229. package/dist/icons.d.ts +0 -12
  230. package/dist/icons.d.ts.map +0 -1
  231. package/dist/icons.js +0 -28
  232. package/dist/icons.js.map +0 -1
  233. package/dist/index.d.ts.map +0 -1
  234. package/dist/preferences/theme.d.ts +0 -18
  235. package/dist/preferences/theme.d.ts.map +0 -1
  236. package/dist/preferences/theme.js +0 -63
  237. package/dist/preferences/theme.js.map +0 -1
  238. package/dist/preferences/versioning.d.ts +0 -27
  239. package/dist/preferences/versioning.d.ts.map +0 -1
  240. package/dist/preferences/versioning.js +0 -62
  241. package/dist/preferences/versioning.js.map +0 -1
  242. package/dist/provider-lease.d.ts +0 -10
  243. package/dist/provider-lease.d.ts.map +0 -1
  244. package/dist/provider-lease.js +0 -42
  245. package/dist/provider-lease.js.map +0 -1
  246. package/src/AppMenu/AppMenu.tsx +0 -334
  247. package/src/AppMenu/index.ts +0 -2
  248. package/src/DocBlocksShell/DocBlocksShell.tsx +0 -3398
  249. package/src/DocBlocksShell/LazyEditorShell.ts +0 -2
  250. package/src/DocBlocksShell/UpdateAvailableNotice.tsx +0 -60
  251. package/src/DocBlocksShell/document-title.ts +0 -19
  252. package/src/DocBlocksShell/index.ts +0 -2
  253. package/src/DocBlocksShell/workspace-authority-barrier.ts +0 -26
  254. package/src/Export/DeferredExportToolbarControls.tsx +0 -23
  255. package/src/Export/ExportDialog.tsx +0 -437
  256. package/src/Export/ExportToolbarControls.tsx +0 -511
  257. package/src/Export/export-destination.ts +0 -30
  258. package/src/Export/export-options.ts +0 -99
  259. package/src/Export/index.ts +0 -19
  260. package/src/Export/public-api.ts +0 -17
  261. package/src/Export/run-export.ts +0 -374
  262. package/src/Export/transform-summaries.ts +0 -14
  263. package/src/FileExplorer/FileExplorer.tsx +0 -528
  264. package/src/FileExplorer/FileTreeNode.tsx +0 -306
  265. package/src/FileExplorer/index.ts +0 -5
  266. package/src/FileExplorer/useFileTree.ts +0 -352
  267. package/src/Git/GitAuthGuidanceDialog.tsx +0 -84
  268. package/src/Git/GitBranchesDialog.tsx +0 -144
  269. package/src/Git/GitCloneDialog.tsx +0 -200
  270. package/src/Git/GitCommitDialog.tsx +0 -119
  271. package/src/Git/GitConflictsDialog.tsx +0 -62
  272. package/src/Git/GitContext.tsx +0 -88
  273. package/src/Git/GitDiffDialog.tsx +0 -147
  274. package/src/Git/GitDiffView.tsx +0 -105
  275. package/src/Git/GitHistoryDialog.tsx +0 -195
  276. package/src/Git/GitStatusBar.tsx +0 -157
  277. package/src/Git/GitToolbarControl.tsx +0 -123
  278. package/src/Git/GitUI.tsx +0 -68
  279. package/src/Git/commit-selection.ts +0 -63
  280. package/src/Git/git-clone-utils.ts +0 -23
  281. package/src/Git/git-status.ts +0 -113
  282. package/src/Git/useGit.ts +0 -279
  283. package/src/Git/useGitStatus.ts +0 -78
  284. package/src/Settings/Settings.tsx +0 -118
  285. package/src/Settings/public-api.ts +0 -8
  286. package/src/WorkspacePicker/WorkspacePicker.tsx +0 -191
  287. package/src/WorkspacePicker/WorkspaceSettingsButton.tsx +0 -88
  288. package/src/WorkspacePicker/WorkspaceSettingsDialog.tsx +0 -121
  289. package/src/WorkspacePicker/index.ts +0 -2
  290. package/src/components/Dialog.tsx +0 -81
  291. package/src/editor.ts +0 -72
  292. package/src/hooks/index.ts +0 -2
  293. package/src/hooks/useDocumentSession.ts +0 -37
  294. package/src/icons.tsx +0 -48
  295. package/src/index.ts +0 -61
  296. package/src/preferences/theme.ts +0 -67
  297. package/src/preferences/versioning.ts +0 -69
  298. package/src/provider-lease.ts +0 -52
  299. package/src/types/pwa-dom.d.ts +0 -31
  300. /package/dist/{ExportDialog-MEUISWZX.js.map → ExportDialog-7LDIXJZ3.js.map} +0 -0
@@ -1,123 +0,0 @@
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
- }
package/src/Git/GitUI.tsx DELETED
@@ -1,68 +0,0 @@
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
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Commit-dialog selection state — pure helpers, unit-testable.
3
- *
4
- * All changed files are included by default. While a merge is being
5
- * concluded, conflicted files are force-included: committing a subset that
6
- * omits them would not resolve the merge.
7
- */
8
-
9
- import type { GitFileChange, GitStatus } from '@bendyline/docblocks/host';
10
-
11
- export interface CommitSelection {
12
- /** Slash-prefixed path → include in the commit. */
13
- included: Map<string, boolean>;
14
- /** Paths the user cannot exclude (conflicted files during a merge). */
15
- locked: Set<string>;
16
- }
17
-
18
- function normalise(path: string): string {
19
- return path.startsWith('/') ? path : `/${path}`;
20
- }
21
-
22
- export function initSelection(
23
- changes: readonly GitFileChange[],
24
- merging: boolean,
25
- ): CommitSelection {
26
- const included = new Map<string, boolean>();
27
- const locked = new Set<string>();
28
- for (const change of changes) {
29
- const path = normalise(change.path);
30
- included.set(path, true);
31
- if (merging && change.conflicted) locked.add(path);
32
- }
33
- return { included, locked };
34
- }
35
-
36
- export function toggleSelection(selection: CommitSelection, path: string): CommitSelection {
37
- const key = normalise(path);
38
- if (selection.locked.has(key)) return selection;
39
- const included = new Map(selection.included);
40
- included.set(key, !included.get(key));
41
- return { included, locked: selection.locked };
42
- }
43
-
44
- export function setAllSelected(selection: CommitSelection, value: boolean): CommitSelection {
45
- const included = new Map<string, boolean>();
46
- for (const key of selection.included.keys()) {
47
- included.set(key, selection.locked.has(key) ? true : value);
48
- }
49
- return { included, locked: selection.locked };
50
- }
51
-
52
- export function selectedPaths(selection: CommitSelection): string[] {
53
- return [...selection.included.entries()].filter(([, on]) => on).map(([path]) => path);
54
- }
55
-
56
- export function canCommit(message: string, selection: CommitSelection): boolean {
57
- return message.trim().length > 0 && selectedPaths(selection).length > 0;
58
- }
59
-
60
- /** True when committing concludes an in-progress merge. */
61
- export function isMergeCommit(status: GitStatus | null): boolean {
62
- return status?.operation === 'merge';
63
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Clone-dialog helpers — pure URL utilities, unit-testable.
3
- */
4
-
5
- /** Folder name a clone of this URL will produce (mirrors the host's rule). */
6
- export function deriveCloneFolderName(url: string): string {
7
- const trimmed = url.trim().replace(/\/+$/, '');
8
- const lastSegment = trimmed.split(/[/:\\]/).pop() ?? '';
9
- const withoutGit = lastSegment.replace(/\.git$/i, '');
10
- const sanitized = withoutGit.replace(/[^A-Za-z0-9._-]/g, '-');
11
- return sanitized.length > 0 ? sanitized : 'repository';
12
- }
13
-
14
- /** Loose shape check for enabling the Clone button — the host re-validates. */
15
- export function isLikelyGitUrl(url: string): boolean {
16
- const trimmed = url.trim();
17
- if (trimmed.length === 0 || /\s/.test(trimmed)) return false;
18
- if (trimmed.startsWith('-') || trimmed.toLowerCase().startsWith('file:')) return false;
19
- if (/^(https?|ssh|git):\/\/\S+\/\S+/i.test(trimmed)) return true;
20
- // scp-like: user@host:owner/repo
21
- if (/^[\w.-]+@[\w.-]+:\S+/.test(trimmed)) return true;
22
- return false;
23
- }
@@ -1,113 +0,0 @@
1
- /**
2
- * Pure helpers mapping GitStatus onto UI concerns — file-tree badges,
3
- * ahead/behind chips, dirty summaries. No React, no host access.
4
- */
5
-
6
- import type { GitFileChange, GitStatus } from '@bendyline/docblocks/host';
7
-
8
- export type GitBadgeKind =
9
- | 'modified'
10
- | 'added'
11
- | 'untracked'
12
- | 'renamed'
13
- | 'deleted'
14
- | 'conflicted'
15
- | 'dir';
16
-
17
- export const BADGE_GLYPHS: Record<GitBadgeKind, string> = {
18
- modified: 'M',
19
- added: 'A',
20
- untracked: 'U',
21
- renamed: 'R',
22
- deleted: 'D',
23
- conflicted: '!',
24
- dir: '•',
25
- };
26
-
27
- export const BADGE_LABELS: Record<GitBadgeKind, string> = {
28
- modified: 'modified',
29
- added: 'added',
30
- untracked: 'untracked',
31
- renamed: 'renamed',
32
- deleted: 'deleted',
33
- conflicted: 'has conflicts',
34
- dir: 'contains changes',
35
- };
36
-
37
- function normalise(path: string): string {
38
- return path.startsWith('/') ? path : `/${path}`;
39
- }
40
-
41
- /** True when any path segment is a dot-entry (.git, .github, .gitignore…). */
42
- function isDotPath(path: string): boolean {
43
- return normalise(path)
44
- .split('/')
45
- .some((segment) => segment.startsWith('.'));
46
- }
47
-
48
- export function badgeKindFor(change: GitFileChange): GitBadgeKind {
49
- if (change.conflicted) return 'conflicted';
50
- if (change.worktree === 'untracked') return 'untracked';
51
- if (change.index === 'deleted' || change.worktree === 'deleted') return 'deleted';
52
- if (change.index === 'renamed' || change.worktree === 'renamed') return 'renamed';
53
- if (change.index === 'added' || change.worktree === 'added') return 'added';
54
- return 'modified';
55
- }
56
-
57
- /**
58
- * Slash-prefixed path → badge kind, including directory rollups: any
59
- * ancestor directory of a changed file gets a 'dir' dot (or 'conflicted'
60
- * when a conflicted descendant wins). Deleted files aren't in the tree, so
61
- * they contribute only their ancestor rollup. Dot-paths are excluded so a
62
- * `.gitignore` edit doesn't inexplicably dot the root.
63
- */
64
- export function buildBadgeMap(changes: readonly GitFileChange[]): Map<string, GitBadgeKind> {
65
- const badges = new Map<string, GitBadgeKind>();
66
- const dirKinds = new Map<string, GitBadgeKind>();
67
-
68
- for (const change of changes) {
69
- if (isDotPath(change.path)) continue;
70
- const filePath = normalise(change.path);
71
- const kind = badgeKindFor(change);
72
- if (kind !== 'deleted') {
73
- badges.set(filePath, kind);
74
- }
75
- const segments = filePath.split('/').slice(1, -1);
76
- let dir = '';
77
- for (const segment of segments) {
78
- dir += `/${segment}`;
79
- const rollup = kind === 'conflicted' ? 'conflicted' : 'dir';
80
- if (dirKinds.get(dir) !== 'conflicted') dirKinds.set(dir, rollup);
81
- }
82
- }
83
- for (const [dir, kind] of dirKinds) {
84
- if (!badges.has(dir)) badges.set(dir, kind);
85
- }
86
- return badges;
87
- }
88
-
89
- /** "2↑ 1↓" — empty string when neither applies. */
90
- export function formatAheadBehind(status: Pick<GitStatus, 'ahead' | 'behind'>): string {
91
- const parts: string[] = [];
92
- if (status.ahead > 0) parts.push(`${status.ahead}↑`);
93
- if (status.behind > 0) parts.push(`${status.behind}↓`);
94
- return parts.join(' ');
95
- }
96
-
97
- export function isFileDirty(status: GitStatus | null, path: string | null): boolean {
98
- if (!status || !path) return false;
99
- const target = normalise(path);
100
- return status.changes.some((c) => normalise(c.path) === target);
101
- }
102
-
103
- export function conflictedPaths(status: GitStatus | null): string[] {
104
- if (!status) return [];
105
- return status.changes.filter((c) => c.conflicted).map((c) => normalise(c.path));
106
- }
107
-
108
- /** "3 changes" / "1 change" / "" when clean. */
109
- export function summarizeDirty(status: GitStatus | null): string {
110
- const count = status?.changes.length ?? 0;
111
- if (count === 0) return '';
112
- return count === 1 ? '1 change' : `${count} changes`;
113
- }
package/src/Git/useGit.ts DELETED
@@ -1,279 +0,0 @@
1
- /**
2
- * useGit — the single hook DocBlocksShell calls to power all git UI.
3
- *
4
- * Tier 1 (`available`): running under the Electron host with a working git —
5
- * gates only the clone entry. Tier 2 (`repo`): the active workspace is an
6
- * electron-native folder that is a git repository — gates everything else.
7
- * On the site / VS Code webview / store builds every surface null-renders.
8
- *
9
- * Actions never throw: typed GitErrors are routed to the auth-guidance or
10
- * conflicts dialogs when actionable, otherwise to the aria-live result line.
11
- */
12
-
13
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
14
- import type {
15
- DocBlocksHostGitAPI,
16
- GitCapabilities,
17
- GitError,
18
- GitRepoDetection,
19
- GitRemoteInfo,
20
- } from '@bendyline/docblocks/host';
21
- import { maybeGetDocBlocksHost } from '@bendyline/docblocks/host';
22
- import type { FileSystemProvider } from '@bendyline/docblocks/filesystem';
23
- import { buildBadgeMap, conflictedPaths } from './git-status.js';
24
- import { useGitStatus } from './useGitStatus.js';
25
- import type { GitBusyKind, GitDialogState, GitInlineResult, GitValue } from './GitContext.js';
26
-
27
- /** Capabilities can't change within a session — probe once per page load. */
28
- let capabilitiesPromise: Promise<GitCapabilities> | null = null;
29
- function capabilitiesOnce(gitApi: DocBlocksHostGitAPI): Promise<GitCapabilities> {
30
- capabilitiesPromise ??= gitApi.capabilities();
31
- return capabilitiesPromise;
32
- }
33
-
34
- function describeError(error: GitError): string {
35
- return error.message || 'Git operation failed';
36
- }
37
-
38
- export function useGit(
39
- provider: FileSystemProvider | null,
40
- requestedWorkspaceId: string | null,
41
- theme: 'light' | 'dark',
42
- ): GitValue {
43
- const host = maybeGetDocBlocksHost();
44
- const gitApi = host?.git ?? null;
45
- const workspaceId = gitApi ? requestedWorkspaceId : null;
46
-
47
- const [capabilities, setCapabilities] = useState<GitCapabilities | null>(null);
48
- useEffect(() => {
49
- if (!gitApi) return;
50
- let cancelled = false;
51
- void capabilitiesOnce(gitApi).then((caps) => {
52
- if (!cancelled) setCapabilities(caps);
53
- });
54
- return () => {
55
- cancelled = true;
56
- };
57
- }, [gitApi]);
58
-
59
- const available = gitApi !== null && capabilities?.gitAvailable === true;
60
-
61
- const [repo, setRepo] = useState<GitRepoDetection | null>(null);
62
- const [remoteWeb, setRemoteWeb] = useState<GitValue['remoteWeb']>(null);
63
- useEffect(() => {
64
- setRepo(null);
65
- setRemoteWeb(null);
66
- if (!gitApi || !workspaceId || !available) return;
67
- let cancelled = false;
68
- void gitApi.detectRepo(workspaceId).then((result) => {
69
- if (cancelled || !result.ok || !result.value.isRepo || !result.value.repositoryId) {
70
- return;
71
- }
72
- setRepo(result.value);
73
- void gitApi.listRemotes(result.value.repositoryId).then((remotes) => {
74
- if (cancelled || !remotes.ok) return;
75
- const first: GitRemoteInfo | undefined = remotes.value[0];
76
- setRemoteWeb(first?.web ?? null);
77
- });
78
- });
79
- return () => {
80
- cancelled = true;
81
- };
82
- }, [gitApi, workspaceId, available]);
83
-
84
- const repositoryId = repo?.repositoryId ?? null;
85
- const isRepo = repositoryId !== null;
86
- const { status, refresh, scheduleRefresh } = useGitStatus(gitApi, repositoryId, isRepo);
87
- const badges = useMemo(() => buildBadgeMap(status?.changes ?? []), [status]);
88
-
89
- const [busy, setBusy] = useState<GitBusyKind>(null);
90
- const [lastResult, setLastResult] = useState<GitInlineResult | null>(null);
91
- const [dialog, setDialog] = useState<GitDialogState>({ kind: 'none' });
92
-
93
- const openDialog = useCallback((next: GitDialogState) => setDialog(next), []);
94
- const closeDialog = useCallback(() => setDialog({ kind: 'none' }), []);
95
-
96
- // Route a failed operation somewhere useful.
97
- const routeError = useCallback(
98
- (operation: 'push' | 'pull' | 'fetch' | 'commit' | 'branch' | 'pr', error: GitError) => {
99
- if (error.code === 'auth-failed') {
100
- setDialog({
101
- kind: 'auth-guidance',
102
- operation: operation === 'commit' || operation === 'branch' ? 'push' : operation,
103
- detail: error.message,
104
- });
105
- return;
106
- }
107
- setLastResult({ tone: 'error', message: describeError(error) });
108
- },
109
- [],
110
- );
111
-
112
- const statusRef = useRef(status);
113
- statusRef.current = status;
114
-
115
- const runAction = useCallback(
116
- async <T>(
117
- kind: Exclude<GitBusyKind, null>,
118
- operation: 'push' | 'pull' | 'fetch' | 'commit' | 'branch' | 'pr',
119
- action: (
120
- api: DocBlocksHostGitAPI,
121
- repository: string,
122
- ) => Promise<{ ok: true; value: T } | { ok: false; error: GitError }>,
123
- successMessage?: string,
124
- ): Promise<boolean> => {
125
- if (!gitApi || !repositoryId) return false;
126
- setBusy(kind);
127
- setLastResult(null);
128
- try {
129
- const result = await action(gitApi, repositoryId);
130
- if (result.ok) {
131
- if (successMessage) setLastResult({ tone: 'info', message: successMessage });
132
- refresh();
133
- return true;
134
- }
135
- if (operation === 'pull' && result.error.code === 'merge-conflict') {
136
- // Surface the conflicted files; the fresh status carries them.
137
- const fresh = await gitApi.status(repositoryId);
138
- const paths = fresh.ok ? conflictedPaths(fresh.value) : [];
139
- refresh();
140
- setDialog({ kind: 'conflicts', paths });
141
- setLastResult({ tone: 'error', message: 'Pull hit merge conflicts' });
142
- return false;
143
- }
144
- routeError(operation, result.error);
145
- refresh();
146
- return false;
147
- } finally {
148
- setBusy(null);
149
- }
150
- },
151
- [gitApi, repositoryId, refresh, routeError],
152
- );
153
-
154
- const commit = useCallback(
155
- (message: string, paths: string[]) =>
156
- runAction(
157
- 'commit',
158
- 'commit',
159
- (api, repository) => api.commit(repository, message, paths),
160
- 'Committed',
161
- ),
162
- [runAction],
163
- );
164
-
165
- const push = useCallback(async () => {
166
- const setUpstream = statusRef.current?.upstream === null;
167
- await runAction(
168
- 'push',
169
- 'push',
170
- (api, repository) => api.push(repository, setUpstream ? { setUpstream: true } : undefined),
171
- setUpstream ? 'Branch published' : 'Pushed',
172
- );
173
- }, [runAction]);
174
-
175
- const pull = useCallback(async () => {
176
- await runAction('pull', 'pull', (api, repository) => api.pull(repository), 'Pulled');
177
- }, [runAction]);
178
-
179
- const fetchRemote = useCallback(async () => {
180
- await runAction('fetch', 'fetch', (api, repository) => api.fetch(repository), 'Fetched');
181
- }, [runAction]);
182
-
183
- const createBranch = useCallback(
184
- (name: string) =>
185
- runAction(
186
- 'branch',
187
- 'branch',
188
- (api, repository) => api.createBranch(repository, name, { checkout: true }),
189
- `Switched to new branch ${name}`,
190
- ),
191
- [runAction],
192
- );
193
-
194
- const switchBranch = useCallback(
195
- (name: string) =>
196
- runAction(
197
- 'branch',
198
- 'branch',
199
- (api, repository) => api.checkoutBranch(repository, name),
200
- `Switched to ${name}`,
201
- ),
202
- [runAction],
203
- );
204
-
205
- const openOnRemote = useCallback(
206
- (filePath?: string) => {
207
- if (!remoteWeb || !host) return;
208
- let url = remoteWeb.webUrl;
209
- // Only GitHub's blob-path shape is a safe bet; other hosts get the repo.
210
- if (filePath && remoteWeb.host === 'github.com' && statusRef.current?.branch) {
211
- const rel = filePath.replace(/^\/+/, '');
212
- url = `${remoteWeb.webUrl}/blob/${encodeURIComponent(statusRef.current.branch)}/${rel
213
- .split('/')
214
- .map(encodeURIComponent)
215
- .join('/')}`;
216
- }
217
- void host.shell.openExternal(url);
218
- },
219
- [remoteWeb, host],
220
- );
221
-
222
- const createPullRequest = useCallback(async () => {
223
- if (!gitApi || !repositoryId) return;
224
- setBusy('pr');
225
- setLastResult(null);
226
- try {
227
- const result = await gitApi.createPullRequest(repositoryId);
228
- if (result.ok) {
229
- setLastResult({ tone: 'info', message: 'Opening pull request in browser' });
230
- return;
231
- }
232
- if (result.error.code === 'no-upstream') {
233
- setLastResult({
234
- tone: 'error',
235
- message: 'Push the branch first, then create the pull request',
236
- });
237
- return;
238
- }
239
- if (result.error.code === 'gh-not-available') {
240
- setLastResult({
241
- tone: 'error',
242
- message: 'Install the GitHub CLI (gh) to create pull requests',
243
- });
244
- return;
245
- }
246
- routeError('pr', result.error);
247
- } finally {
248
- setBusy(null);
249
- }
250
- }, [gitApi, repositoryId, routeError]);
251
-
252
- return {
253
- available,
254
- capabilities,
255
- repo,
256
- repositoryId,
257
- remoteWeb,
258
- gitApi,
259
- provider,
260
- theme,
261
- status,
262
- badges,
263
- busy,
264
- lastResult,
265
- refresh,
266
- scheduleRefresh,
267
- commit,
268
- push,
269
- pull,
270
- fetchRemote,
271
- createBranch,
272
- switchBranch,
273
- openOnRemote,
274
- createPullRequest,
275
- dialog,
276
- openDialog,
277
- closeDialog,
278
- };
279
- }