@bendyline/docblocks-react 2.0.0 → 2.1.1

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 (310) hide show
  1. package/README.md +18 -3
  2. package/dist/ExportDialog-VYATD5UC.js +9 -0
  3. package/dist/{ExportToolbarControls-D2YDWOXW.js → ExportToolbarControls-5HUE3LSX.js} +206 -42
  4. package/dist/{GitToolbarControl-LMIR4R7I.js → GitToolbarControl-FW3BZFXV.js} +62 -44
  5. package/dist/{GitUI-KGF5MSNV.js → GitUI-GMH3PAYL.js} +225 -141
  6. package/dist/{LazyEditorShell-EF2BWRXU.js → LazyEditorShell-RF54B5UD.js} +0 -1
  7. package/dist/ShareDialog-PAMOQQFB.js +212 -0
  8. package/dist/chunk-3CZ27FOA.js +359 -0
  9. package/dist/chunk-3QSZY74C.js +255 -0
  10. package/{src/Export/export-destination.ts → dist/chunk-BI7NVU6T.js} +16 -15
  11. package/dist/{chunk-VHDP4IPL.js → chunk-BPGEBGAN.js} +0 -1
  12. package/dist/{chunk-DD4VFJQI.js → chunk-EBWYGTN7.js} +2 -1
  13. package/dist/{chunk-ZI56BNB7.js → chunk-GHQ4X7KM.js} +27 -7
  14. package/dist/{chunk-5A4KEWKT.js → chunk-HFYPTTCH.js} +0 -1
  15. package/dist/chunk-LG6HAWCK.js +109 -0
  16. package/dist/chunk-TFMO7EVO.js +88 -0
  17. package/dist/{chunk-4XXSWN3T.js → chunk-YBEYTVU2.js} +0 -1
  18. package/dist/editor.d.ts +5 -4
  19. package/dist/editor.js +8 -70
  20. package/dist/export/index.d.ts +24 -8
  21. package/dist/export/index.js +10 -6
  22. package/dist/index.d.ts +355 -21
  23. package/dist/index.js +6419 -21
  24. package/dist/run-export-Z5BVFLIR.js +9 -0
  25. package/dist/settings/index.d.ts +9 -1
  26. package/dist/settings/index.js +6 -5
  27. package/package.json +18 -13
  28. package/src/styles/docblocks.css +985 -35
  29. package/dist/AppMenu/AppMenu.d.ts +0 -53
  30. package/dist/AppMenu/AppMenu.d.ts.map +0 -1
  31. package/dist/AppMenu/AppMenu.js +0 -67
  32. package/dist/AppMenu/AppMenu.js.map +0 -1
  33. package/dist/AppMenu/index.d.ts +0 -3
  34. package/dist/AppMenu/index.d.ts.map +0 -1
  35. package/dist/AppMenu/index.js +0 -2
  36. package/dist/AppMenu/index.js.map +0 -1
  37. package/dist/DocBlocksShell/DocBlocksShell.d.ts +0 -49
  38. package/dist/DocBlocksShell/DocBlocksShell.d.ts.map +0 -1
  39. package/dist/DocBlocksShell/DocBlocksShell.js +0 -2730
  40. package/dist/DocBlocksShell/DocBlocksShell.js.map +0 -1
  41. package/dist/DocBlocksShell/LazyEditorShell.d.ts +0 -3
  42. package/dist/DocBlocksShell/LazyEditorShell.d.ts.map +0 -1
  43. package/dist/DocBlocksShell/LazyEditorShell.js +0 -3
  44. package/dist/DocBlocksShell/LazyEditorShell.js.map +0 -1
  45. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts +0 -13
  46. package/dist/DocBlocksShell/UpdateAvailableNotice.d.ts.map +0 -1
  47. package/dist/DocBlocksShell/UpdateAvailableNotice.js +0 -16
  48. package/dist/DocBlocksShell/UpdateAvailableNotice.js.map +0 -1
  49. package/dist/DocBlocksShell/document-title.d.ts +0 -3
  50. package/dist/DocBlocksShell/document-title.d.ts.map +0 -1
  51. package/dist/DocBlocksShell/document-title.js +0 -17
  52. package/dist/DocBlocksShell/document-title.js.map +0 -1
  53. package/dist/DocBlocksShell/index.d.ts +0 -3
  54. package/dist/DocBlocksShell/index.d.ts.map +0 -1
  55. package/dist/DocBlocksShell/index.js +0 -2
  56. package/dist/DocBlocksShell/index.js.map +0 -1
  57. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts +0 -14
  58. package/dist/DocBlocksShell/workspace-authority-barrier.d.ts.map +0 -1
  59. package/dist/DocBlocksShell/workspace-authority-barrier.js +0 -23
  60. package/dist/DocBlocksShell/workspace-authority-barrier.js.map +0 -1
  61. package/dist/Export/DeferredExportToolbarControls.d.ts +0 -5
  62. package/dist/Export/DeferredExportToolbarControls.d.ts.map +0 -1
  63. package/dist/Export/DeferredExportToolbarControls.js +0 -10
  64. package/dist/Export/DeferredExportToolbarControls.js.map +0 -1
  65. package/dist/Export/ExportDialog.d.ts +0 -25
  66. package/dist/Export/ExportDialog.d.ts.map +0 -1
  67. package/dist/Export/ExportDialog.js +0 -160
  68. package/dist/Export/ExportDialog.js.map +0 -1
  69. package/dist/Export/ExportToolbarControls.d.ts +0 -37
  70. package/dist/Export/ExportToolbarControls.d.ts.map +0 -1
  71. package/dist/Export/ExportToolbarControls.js +0 -285
  72. package/dist/Export/ExportToolbarControls.js.map +0 -1
  73. package/dist/Export/export-destination.d.ts +0 -3
  74. package/dist/Export/export-destination.d.ts.map +0 -1
  75. package/dist/Export/export-destination.js +0 -32
  76. package/dist/Export/export-destination.js.map +0 -1
  77. package/dist/Export/export-options.d.ts +0 -53
  78. package/dist/Export/export-options.d.ts.map +0 -1
  79. package/dist/Export/export-options.js +0 -49
  80. package/dist/Export/export-options.js.map +0 -1
  81. package/dist/Export/index.d.ts +0 -10
  82. package/dist/Export/index.d.ts.map +0 -1
  83. package/dist/Export/index.js +0 -6
  84. package/dist/Export/index.js.map +0 -1
  85. package/dist/Export/public-api.d.ts +0 -12
  86. package/dist/Export/public-api.d.ts.map +0 -1
  87. package/dist/Export/public-api.js +0 -9
  88. package/dist/Export/public-api.js.map +0 -1
  89. package/dist/Export/run-export.d.ts +0 -10
  90. package/dist/Export/run-export.d.ts.map +0 -1
  91. package/dist/Export/run-export.js +0 -313
  92. package/dist/Export/run-export.js.map +0 -1
  93. package/dist/Export/transform-summaries.d.ts +0 -7
  94. package/dist/Export/transform-summaries.d.ts.map +0 -1
  95. package/dist/Export/transform-summaries.js +0 -6
  96. package/dist/Export/transform-summaries.js.map +0 -1
  97. package/dist/ExportDialog-MEUISWZX.js +0 -9
  98. package/dist/ExportDialog-MEUISWZX.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/LazyEditorShell-EF2BWRXU.js.map +0 -1
  187. package/dist/Settings/Settings.d.ts +0 -23
  188. package/dist/Settings/Settings.d.ts.map +0 -1
  189. package/dist/Settings/Settings.js +0 -22
  190. package/dist/Settings/Settings.js.map +0 -1
  191. package/dist/Settings/public-api.d.ts +0 -3
  192. package/dist/Settings/public-api.d.ts.map +0 -1
  193. package/dist/Settings/public-api.js +0 -2
  194. package/dist/Settings/public-api.js.map +0 -1
  195. package/dist/WorkspacePicker/WorkspacePicker.d.ts +0 -22
  196. package/dist/WorkspacePicker/WorkspacePicker.d.ts.map +0 -1
  197. package/dist/WorkspacePicker/WorkspacePicker.js +0 -74
  198. package/dist/WorkspacePicker/WorkspacePicker.js.map +0 -1
  199. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts +0 -11
  200. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts.map +0 -1
  201. package/dist/WorkspacePicker/WorkspaceSettingsButton.js +0 -27
  202. package/dist/WorkspacePicker/WorkspaceSettingsButton.js.map +0 -1
  203. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts +0 -20
  204. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts.map +0 -1
  205. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js +0 -36
  206. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js.map +0 -1
  207. package/dist/WorkspacePicker/index.d.ts +0 -3
  208. package/dist/WorkspacePicker/index.d.ts.map +0 -1
  209. package/dist/WorkspacePicker/index.js +0 -2
  210. package/dist/WorkspacePicker/index.js.map +0 -1
  211. package/dist/chunk-3SAXDU2U.js +0 -157
  212. package/dist/chunk-3SAXDU2U.js.map +0 -1
  213. package/dist/chunk-4XXSWN3T.js.map +0 -1
  214. package/dist/chunk-5A4KEWKT.js.map +0 -1
  215. package/dist/chunk-AOVTHZU7.js +0 -321
  216. package/dist/chunk-AOVTHZU7.js.map +0 -1
  217. package/dist/chunk-DD4VFJQI.js.map +0 -1
  218. package/dist/chunk-GSNZSCIK.js +0 -55
  219. package/dist/chunk-GSNZSCIK.js.map +0 -1
  220. package/dist/chunk-VHDP4IPL.js.map +0 -1
  221. package/dist/chunk-ZI56BNB7.js.map +0 -1
  222. package/dist/components/Dialog.d.ts +0 -22
  223. package/dist/components/Dialog.d.ts.map +0 -1
  224. package/dist/components/Dialog.js +0 -33
  225. package/dist/components/Dialog.js.map +0 -1
  226. package/dist/editor.d.ts.map +0 -1
  227. package/dist/editor.js.map +0 -1
  228. package/dist/export/index.js.map +0 -1
  229. package/dist/hooks/index.d.ts +0 -3
  230. package/dist/hooks/index.d.ts.map +0 -1
  231. package/dist/hooks/index.js +0 -2
  232. package/dist/hooks/index.js.map +0 -1
  233. package/dist/hooks/useDocumentSession.d.ts +0 -16
  234. package/dist/hooks/useDocumentSession.d.ts.map +0 -1
  235. package/dist/hooks/useDocumentSession.js +0 -18
  236. package/dist/hooks/useDocumentSession.js.map +0 -1
  237. package/dist/icons.d.ts +0 -12
  238. package/dist/icons.d.ts.map +0 -1
  239. package/dist/icons.js +0 -28
  240. package/dist/icons.js.map +0 -1
  241. package/dist/index.d.ts.map +0 -1
  242. package/dist/index.js.map +0 -1
  243. package/dist/preferences/theme.d.ts +0 -18
  244. package/dist/preferences/theme.d.ts.map +0 -1
  245. package/dist/preferences/theme.js +0 -63
  246. package/dist/preferences/theme.js.map +0 -1
  247. package/dist/preferences/versioning.d.ts +0 -27
  248. package/dist/preferences/versioning.d.ts.map +0 -1
  249. package/dist/preferences/versioning.js +0 -62
  250. package/dist/preferences/versioning.js.map +0 -1
  251. package/dist/provider-lease.d.ts +0 -10
  252. package/dist/provider-lease.d.ts.map +0 -1
  253. package/dist/provider-lease.js +0 -42
  254. package/dist/provider-lease.js.map +0 -1
  255. package/dist/run-export-ICTZITXC.js +0 -10
  256. package/dist/run-export-ICTZITXC.js.map +0 -1
  257. package/dist/settings/index.js.map +0 -1
  258. package/src/AppMenu/AppMenu.tsx +0 -334
  259. package/src/AppMenu/index.ts +0 -2
  260. package/src/DocBlocksShell/DocBlocksShell.tsx +0 -3398
  261. package/src/DocBlocksShell/LazyEditorShell.ts +0 -2
  262. package/src/DocBlocksShell/UpdateAvailableNotice.tsx +0 -60
  263. package/src/DocBlocksShell/document-title.ts +0 -19
  264. package/src/DocBlocksShell/index.ts +0 -2
  265. package/src/DocBlocksShell/workspace-authority-barrier.ts +0 -26
  266. package/src/Export/DeferredExportToolbarControls.tsx +0 -23
  267. package/src/Export/ExportDialog.tsx +0 -437
  268. package/src/Export/ExportToolbarControls.tsx +0 -511
  269. package/src/Export/export-options.ts +0 -99
  270. package/src/Export/index.ts +0 -19
  271. package/src/Export/public-api.ts +0 -17
  272. package/src/Export/run-export.ts +0 -374
  273. package/src/Export/transform-summaries.ts +0 -14
  274. package/src/FileExplorer/FileExplorer.tsx +0 -528
  275. package/src/FileExplorer/FileTreeNode.tsx +0 -306
  276. package/src/FileExplorer/index.ts +0 -5
  277. package/src/FileExplorer/useFileTree.ts +0 -352
  278. package/src/Git/GitAuthGuidanceDialog.tsx +0 -84
  279. package/src/Git/GitBranchesDialog.tsx +0 -144
  280. package/src/Git/GitCloneDialog.tsx +0 -200
  281. package/src/Git/GitCommitDialog.tsx +0 -119
  282. package/src/Git/GitConflictsDialog.tsx +0 -62
  283. package/src/Git/GitContext.tsx +0 -88
  284. package/src/Git/GitDiffDialog.tsx +0 -147
  285. package/src/Git/GitDiffView.tsx +0 -105
  286. package/src/Git/GitHistoryDialog.tsx +0 -195
  287. package/src/Git/GitStatusBar.tsx +0 -157
  288. package/src/Git/GitToolbarControl.tsx +0 -123
  289. package/src/Git/GitUI.tsx +0 -68
  290. package/src/Git/commit-selection.ts +0 -63
  291. package/src/Git/git-clone-utils.ts +0 -23
  292. package/src/Git/git-status.ts +0 -113
  293. package/src/Git/useGit.ts +0 -279
  294. package/src/Git/useGitStatus.ts +0 -78
  295. package/src/Settings/Settings.tsx +0 -118
  296. package/src/Settings/public-api.ts +0 -8
  297. package/src/WorkspacePicker/WorkspacePicker.tsx +0 -191
  298. package/src/WorkspacePicker/WorkspaceSettingsButton.tsx +0 -88
  299. package/src/WorkspacePicker/WorkspaceSettingsDialog.tsx +0 -121
  300. package/src/WorkspacePicker/index.ts +0 -2
  301. package/src/components/Dialog.tsx +0 -81
  302. package/src/editor.ts +0 -72
  303. package/src/hooks/index.ts +0 -2
  304. package/src/hooks/useDocumentSession.ts +0 -37
  305. package/src/icons.tsx +0 -48
  306. package/src/index.ts +0 -61
  307. package/src/preferences/theme.ts +0 -67
  308. package/src/preferences/versioning.ts +0 -69
  309. package/src/provider-lease.ts +0 -52
  310. package/src/types/pwa-dom.d.ts +0 -31
@@ -1,306 +0,0 @@
1
- /**
2
- * FileTreeNode — recursive tree node for the file explorer.
3
- */
4
-
5
- import { useCallback, useState, useRef, useEffect, useLayoutEffect } from 'react';
6
- import { createPortal } from 'react-dom';
7
- import type { FileSystemEntry } from '@bendyline/docblocks/filesystem';
8
- import { MoreIcon } from '../icons.js';
9
-
10
- /** Git decoration for a row — precomputed by FileExplorer so this node stays context-free. */
11
- export interface FileTreeNodeBadge {
12
- kind: string;
13
- glyph: string;
14
- label: string;
15
- }
16
-
17
- /** Git context-menu actions, prebound to this entry's path. */
18
- export interface FileTreeNodeGitActions {
19
- viewChanges?: () => void;
20
- fileHistory?: () => void;
21
- openOnRemote?: () => void;
22
- }
23
-
24
- export interface FileTreeNodeProps {
25
- entry: FileSystemEntry;
26
- depth: number;
27
- expanded: boolean;
28
- selected: boolean;
29
- badge?: FileTreeNodeBadge;
30
- gitActions?: FileTreeNodeGitActions;
31
- children?: FileSystemEntry[];
32
- onToggle: (path: string) => void;
33
- onSelect: (path: string) => void;
34
- onDelete: (path: string, kind: 'file' | 'directory') => Promise<void>;
35
- onRename: (oldPath: string, newPath: string, kind: 'file' | 'directory') => Promise<void>;
36
- draggable?: boolean;
37
- dragging?: boolean;
38
- dropTarget?: boolean;
39
- onDragStart?: (event: React.DragEvent, entry: FileSystemEntry) => void;
40
- onDragEnd?: () => void;
41
- onDragOverEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
42
- onDropEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
43
- renderChildren?: (dirPath: string) => React.ReactNode;
44
- }
45
-
46
- export function FileTreeNode({
47
- entry,
48
- depth,
49
- expanded,
50
- selected,
51
- badge,
52
- gitActions,
53
- onToggle,
54
- onSelect,
55
- onDelete,
56
- onRename,
57
- draggable = false,
58
- dragging = false,
59
- dropTarget = false,
60
- onDragStart,
61
- onDragEnd,
62
- onDragOverEntry,
63
- onDropEntry,
64
- renderChildren,
65
- }: FileTreeNodeProps) {
66
- const [renaming, setRenaming] = useState(false);
67
- const [renameValue, setRenameValue] = useState(entry.name);
68
- const [showContext, setShowContext] = useState(false);
69
- const [contextPos, setContextPos] = useState({ x: 0, y: 0 });
70
- const inputRef = useRef<HTMLInputElement>(null);
71
- const contextRef = useRef<HTMLDivElement>(null);
72
- const nodeRef = useRef<HTMLDivElement>(null);
73
-
74
- const isDir = entry.kind === 'directory';
75
- const icon = isDir ? (expanded ? '\u25BE' : '\u25B8') : '\u00A0\u00A0';
76
-
77
- const handleClick = useCallback(() => {
78
- if (isDir) {
79
- onToggle(entry.path);
80
- }
81
- onSelect(entry.path);
82
- }, [isDir, entry.path, onToggle, onSelect]);
83
-
84
- const handleContextMenu = useCallback((e: React.MouseEvent) => {
85
- e.preventDefault();
86
- e.stopPropagation();
87
- setContextPos({ x: e.clientX, y: e.clientY });
88
- setShowContext(true);
89
- }, []);
90
-
91
- const handleMoreClick = useCallback((e: React.MouseEvent) => {
92
- e.preventDefault();
93
- e.stopPropagation();
94
- const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
95
- setContextPos({ x: rect.right, y: rect.bottom });
96
- setShowContext(true);
97
- }, []);
98
-
99
- const handleRenameStart = useCallback(() => {
100
- setRenameValue(entry.name);
101
- setRenaming(true);
102
- setShowContext(false);
103
- }, [entry.name]);
104
-
105
- const handleRenameSubmit = useCallback(async () => {
106
- if (renameValue && renameValue !== entry.name) {
107
- const parentPath = entry.path.includes('/')
108
- ? entry.path.slice(0, entry.path.lastIndexOf('/'))
109
- : '';
110
- const newPath = parentPath ? `${parentPath}/${renameValue}` : renameValue;
111
- await onRename(entry.path, newPath, entry.kind);
112
- }
113
- setRenaming(false);
114
- }, [renameValue, entry.name, entry.path, entry.kind, onRename]);
115
-
116
- const handleDeleteClick = useCallback(async () => {
117
- setShowContext(false);
118
- const description = entry.kind === 'directory' ? 'folder and everything inside it' : 'document';
119
- if (!window.confirm(`Delete the ${description} "${entry.name}"? This cannot be undone.`)) {
120
- return;
121
- }
122
- await onDelete(entry.path, entry.kind);
123
- }, [entry.name, entry.path, entry.kind, onDelete]);
124
-
125
- // Close context menu on outside click or scroll
126
- useEffect(() => {
127
- if (!showContext) return;
128
- function handleClose(e: Event) {
129
- if (contextRef.current && !contextRef.current.contains(e.target as Node)) {
130
- setShowContext(false);
131
- }
132
- }
133
- // Use click (not mousedown) so menu button clicks register first
134
- document.addEventListener('click', handleClose, true);
135
- document.addEventListener('contextmenu', handleClose, true);
136
- document.addEventListener('scroll', () => setShowContext(false), { capture: true, once: true });
137
- return () => {
138
- document.removeEventListener('click', handleClose, true);
139
- document.removeEventListener('contextmenu', handleClose, true);
140
- };
141
- }, [showContext]);
142
-
143
- // Focus input when renaming
144
- useEffect(() => {
145
- if (renaming && inputRef.current) {
146
- inputRef.current.focus();
147
- inputRef.current.select();
148
- }
149
- }, [renaming]);
150
-
151
- // Active documents can be revealed after asynchronous ancestor loads. Keep
152
- // the selected row inside the explorer's scroll viewport once it mounts.
153
- useEffect(() => {
154
- if (selected) nodeRef.current?.scrollIntoView?.({ block: 'nearest' });
155
- }, [selected]);
156
-
157
- // Clamp menu inside viewport after it renders
158
- useLayoutEffect(() => {
159
- if (!showContext || !contextRef.current) return;
160
- const menu = contextRef.current;
161
- const rect = menu.getBoundingClientRect();
162
- const margin = 4;
163
- let left = contextPos.x;
164
- let top = contextPos.y;
165
- if (rect.right > window.innerWidth - margin) {
166
- left = Math.max(margin, window.innerWidth - rect.width - margin);
167
- }
168
- if (rect.bottom > window.innerHeight - margin) {
169
- top = Math.max(margin, window.innerHeight - rect.height - margin);
170
- }
171
- if (left !== contextPos.x || top !== contextPos.y) {
172
- menu.style.left = `${left}px`;
173
- menu.style.top = `${top}px`;
174
- }
175
- }, [showContext, contextPos]);
176
-
177
- return (
178
- <div className="db-tree-node" ref={nodeRef}>
179
- <div
180
- className={`db-tree-row ${selected ? 'db-tree-row--selected' : ''} ${dragging ? 'db-tree-row--dragging' : ''} ${dropTarget ? 'db-tree-row--drop-target' : ''}`}
181
- style={{ paddingLeft: depth * 16 + 4 }}
182
- onClick={handleClick}
183
- onContextMenu={handleContextMenu}
184
- draggable={draggable && !renaming}
185
- onDragStart={(event) => onDragStart?.(event, entry)}
186
- onDragEnd={onDragEnd}
187
- onDragOver={(event) => onDragOverEntry?.(event, entry)}
188
- onDrop={(event) => onDropEntry?.(event, entry)}
189
- role="treeitem"
190
- aria-expanded={isDir ? expanded : undefined}
191
- aria-selected={selected}
192
- aria-label={badge ? `${entry.name}, ${badge.label}` : undefined}
193
- tabIndex={0}
194
- onKeyDown={(e) => {
195
- if (e.key === 'Enter') handleClick();
196
- }}
197
- >
198
- <span className="db-tree-icon">{icon}</span>
199
- {renaming ? (
200
- <input
201
- ref={inputRef}
202
- className="db-tree-rename-input"
203
- value={renameValue}
204
- onChange={(e) => setRenameValue(e.target.value)}
205
- onBlur={handleRenameSubmit}
206
- onKeyDown={(e) => {
207
- if (e.key === 'Enter') handleRenameSubmit();
208
- if (e.key === 'Escape') setRenaming(false);
209
- }}
210
- onClick={(e) => e.stopPropagation()}
211
- />
212
- ) : (
213
- <>
214
- <span className="db-tree-label">
215
- {entry.name.endsWith('.md') ? entry.name.slice(0, -3) : entry.name}
216
- </span>
217
- {badge && (
218
- <span className={`db-git-badge db-git-badge--${badge.kind}`} aria-hidden="true">
219
- {badge.glyph}
220
- </span>
221
- )}
222
- <button
223
- type="button"
224
- className={`db-tree-more${showContext ? ' db-tree-more--active' : ''}`}
225
- onClick={handleMoreClick}
226
- onContextMenu={handleMoreClick}
227
- aria-label="More actions"
228
- aria-haspopup="menu"
229
- aria-expanded={showContext}
230
- tabIndex={-1}
231
- >
232
- <MoreIcon />
233
- </button>
234
- </>
235
- )}
236
- </div>
237
-
238
- {/* Context menu — portaled so ancestor overflow:hidden doesn't clip it */}
239
- {showContext &&
240
- createPortal(
241
- <div
242
- ref={contextRef}
243
- className="db-tree-context"
244
- style={{ left: contextPos.x, top: contextPos.y }}
245
- >
246
- <button className="db-tree-context-item" onClick={handleRenameStart}>
247
- Rename
248
- </button>
249
- {gitActions && (gitActions.viewChanges || gitActions.fileHistory) && (
250
- <>
251
- <div className="db-tree-context-divider" role="separator" />
252
- {gitActions.viewChanges && (
253
- <button
254
- className="db-tree-context-item"
255
- onClick={() => {
256
- setShowContext(false);
257
- gitActions.viewChanges?.();
258
- }}
259
- >
260
- View changes
261
- </button>
262
- )}
263
- {gitActions.fileHistory && (
264
- <button
265
- className="db-tree-context-item"
266
- onClick={() => {
267
- setShowContext(false);
268
- gitActions.fileHistory?.();
269
- }}
270
- >
271
- File history…
272
- </button>
273
- )}
274
- {gitActions.openOnRemote && (
275
- <button
276
- className="db-tree-context-item"
277
- onClick={() => {
278
- setShowContext(false);
279
- gitActions.openOnRemote?.();
280
- }}
281
- >
282
- Open on remote
283
- </button>
284
- )}
285
- <div className="db-tree-context-divider" role="separator" />
286
- </>
287
- )}
288
- <button
289
- className="db-tree-context-item db-tree-context-item--danger"
290
- onClick={handleDeleteClick}
291
- >
292
- Delete
293
- </button>
294
- </div>,
295
- document.body,
296
- )}
297
-
298
- {/* Render children if directory is expanded */}
299
- {isDir && expanded && renderChildren && (
300
- <div className="db-tree-children" role="group">
301
- {renderChildren(entry.path)}
302
- </div>
303
- )}
304
- </div>
305
- );
306
- }
@@ -1,5 +0,0 @@
1
- export { FileExplorer } from './FileExplorer.js';
2
- export type { FileExplorerProps, FileTreeChange, FileTreeMutationHandler } from './FileExplorer.js';
3
- export { FileTreeNode } from './FileTreeNode.js';
4
- export type { FileTreeNodeProps } from './FileTreeNode.js';
5
- export { useFileTree } from './useFileTree.js';
@@ -1,352 +0,0 @@
1
- /**
2
- * useFileTree — hook that reads from a FileSystemProvider,
3
- * maintains expanded/collapsed state, and provides CRUD operations.
4
- */
5
-
6
- import { useState, useCallback, useEffect, useRef } from 'react';
7
- import {
8
- getFileSystemProviderV2,
9
- moveFileSystemEntry,
10
- parseWorkspacePath,
11
- type FileSystemProvider,
12
- type FileSystemEntry,
13
- } from '@bendyline/docblocks/filesystem';
14
-
15
- function normalisePath(path: string): string {
16
- return parseWorkspacePath(path);
17
- }
18
-
19
- function relocatePath(path: string, oldPath: string, newPath: string): string {
20
- const current = normalisePath(path);
21
- const oldNormalised = normalisePath(oldPath);
22
- if (current !== oldNormalised && !current.startsWith(`${oldNormalised}/`)) return path;
23
- const suffix = current.slice(oldNormalised.length);
24
- return `${newPath.replace(/\/+$/, '')}${suffix}`;
25
- }
26
-
27
- function ancestorPaths(path: string): string[] {
28
- const canonical = normalisePath(path);
29
- if (!canonical) return [];
30
- const segments = canonical.split('/');
31
- const prefix = /^[\\/]/u.test(path) ? '/' : '';
32
- return segments
33
- .slice(0, -1)
34
- .map((_, index) => `${prefix}${segments.slice(0, index + 1).join('/')}`);
35
- }
36
-
37
- function equivalentPathKeys(path: string): string[] {
38
- const canonical = normalisePath(path);
39
- return canonical ? [path, canonical, `/${canonical}`] : [path, canonical];
40
- }
41
-
42
- function hasEquivalentPath(paths: ReadonlySet<string>, path: string): boolean {
43
- return equivalentPathKeys(path).some((candidate) => paths.has(candidate));
44
- }
45
-
46
- async function readProviderDirectory(
47
- provider: FileSystemProvider,
48
- path: string,
49
- ): Promise<FileSystemEntry[]> {
50
- const providerV2 = getFileSystemProviderV2(provider);
51
- if (!providerV2) return provider.readDirectory(path);
52
- const entries = await providerV2.readDirectory(parseWorkspacePath(path));
53
- return entries.map((entry) => ({ kind: entry.kind, name: entry.name, path: entry.path }));
54
- }
55
-
56
- export interface FileTreeState {
57
- /** Flat list of entries for the current directory view. */
58
- entries: FileSystemEntry[];
59
- /** Set of expanded directory paths. */
60
- expanded: Set<string>;
61
- /** Currently selected path — file or directory (null if none). */
62
- selectedPath: string | null;
63
- /** Kind of the selected entry. */
64
- selectedKind: 'file' | 'directory' | null;
65
- /** Whether the tree is loading. */
66
- loading: boolean;
67
- }
68
-
69
- export interface FileTreeActions {
70
- /** Toggle a directory's expanded state. */
71
- toggleExpand: (path: string) => void;
72
- /** Select a file or directory. */
73
- select: (path: string, kind: 'file' | 'directory') => void;
74
- /** Select an entry and expand each directory needed to reveal it. */
75
- reveal: (path: string, kind: 'file' | 'directory') => void;
76
- /** Create a new file with optional initial content. */
77
- createFile: (path: string, content?: string) => Promise<void>;
78
- /** Create a new directory. */
79
- createDirectory: (path: string) => Promise<void>;
80
- /** Delete a file or directory. */
81
- deleteEntry: (path: string) => Promise<void>;
82
- /** Rename or move a file or directory, including a markdown companion folder. */
83
- renameEntry: (oldPath: string, newPath: string, kind?: 'file' | 'directory') => Promise<void>;
84
- /** Force refresh the tree. */
85
- refresh: () => Promise<void>;
86
- }
87
-
88
- export function useFileTree(provider: FileSystemProvider | null): FileTreeState & FileTreeActions {
89
- const [entries, setEntries] = useState<FileSystemEntry[]>([]);
90
- const [expanded, setExpanded] = useState<Set<string>>(new Set());
91
- const [selectedPath, setSelectedPath] = useState<string | null>(null);
92
- const [selectedKind, setSelectedKind] = useState<'file' | 'directory' | null>(null);
93
- const [loading, setLoading] = useState(false);
94
-
95
- // Track child entries for expanded directories
96
- const [childEntries, setChildEntries] = useState<Map<string, FileSystemEntry[]>>(new Map());
97
-
98
- const providerRef = useRef(provider);
99
- providerRef.current = provider;
100
-
101
- const loadRoot = useCallback(async () => {
102
- if (!providerRef.current) {
103
- setEntries([]);
104
- return;
105
- }
106
- setLoading(true);
107
- try {
108
- const root = await readProviderDirectory(providerRef.current, '');
109
- setEntries(root);
110
- } finally {
111
- setLoading(false);
112
- }
113
- }, []);
114
-
115
- const loadChildren = useCallback(async (dirPath: string) => {
116
- if (!providerRef.current) return;
117
- const children = await readProviderDirectory(providerRef.current, dirPath);
118
- setChildEntries((prev) => {
119
- const next = new Map(prev);
120
- next.set(dirPath, children);
121
- return next;
122
- });
123
- }, []);
124
-
125
- // Load root on provider change
126
- useEffect(() => {
127
- setExpanded(new Set());
128
- setChildEntries(new Map());
129
- setSelectedPath(null);
130
- setSelectedKind(null);
131
- loadRoot();
132
- }, [provider, loadRoot]);
133
-
134
- const toggleExpand = useCallback(
135
- (path: string) => {
136
- setExpanded((prev) => {
137
- const next = new Set(prev);
138
- if (hasEquivalentPath(next, path)) {
139
- for (const candidate of equivalentPathKeys(path)) next.delete(candidate);
140
- } else {
141
- next.add(path);
142
- // Load children when expanding
143
- loadChildren(path);
144
- }
145
- return next;
146
- });
147
- },
148
- [loadChildren],
149
- );
150
-
151
- const select = useCallback((path: string, kind: 'file' | 'directory') => {
152
- setSelectedPath(path);
153
- setSelectedKind(kind);
154
- }, []);
155
-
156
- const reveal = useCallback(
157
- (path: string, kind: 'file' | 'directory') => {
158
- const ancestors = ancestorPaths(path);
159
- setSelectedPath(path);
160
- setSelectedKind(kind);
161
- setExpanded((prev) => {
162
- const next = new Set(prev);
163
- for (const ancestor of ancestors) {
164
- if (!hasEquivalentPath(next, ancestor)) next.add(ancestor);
165
- }
166
- return next;
167
- });
168
- for (const ancestor of ancestors) {
169
- void loadChildren(ancestor);
170
- }
171
- },
172
- [loadChildren],
173
- );
174
-
175
- const refresh = useCallback(async () => {
176
- await loadRoot();
177
- // Reload all expanded directories
178
- const expandedPaths = [...expanded];
179
- for (const dirPath of expandedPaths) {
180
- await loadChildren(dirPath);
181
- }
182
- }, [loadRoot, loadChildren, expanded]);
183
-
184
- const refreshRef = useRef(refresh);
185
- refreshRef.current = refresh;
186
-
187
- // Keep the explorer synchronized with provider changes. Watch-capable
188
- // providers (including Electron's chokidar-backed provider) update in real
189
- // time. Calls are serialized and coalesced so event bursts cannot publish
190
- // directory reads out of order.
191
- useEffect(() => {
192
- if (!provider) return;
193
- const providerV2 = getFileSystemProviderV2(provider);
194
- if (!providerV2?.capabilities.watch) return;
195
-
196
- let disposed = false;
197
- let refreshing = false;
198
- let refreshAgain = false;
199
-
200
- const requestRefresh = () => {
201
- if (disposed) return;
202
- if (refreshing) {
203
- refreshAgain = true;
204
- return;
205
- }
206
- refreshing = true;
207
- void (async () => {
208
- try {
209
- do {
210
- refreshAgain = false;
211
- await refreshRef.current();
212
- } while (refreshAgain && !disposed);
213
- } catch {
214
- // A later watcher event or window resume will retry the read.
215
- } finally {
216
- refreshing = false;
217
- if (refreshAgain && !disposed) requestRefresh();
218
- }
219
- })();
220
- };
221
-
222
- const subscription = providerV2.watch(requestRefresh, { onError: requestRefresh });
223
- void subscription.ready.catch(() => undefined);
224
- return () => {
225
- disposed = true;
226
- void subscription.dispose();
227
- };
228
- }, [provider]);
229
-
230
- // File System Access and IndexedDB do not expose a dependable external
231
- // watcher. Refreshing on resume catches changes made while this surface was
232
- // in the background without continuously polling the filesystem.
233
- useEffect(() => {
234
- if (!provider) return;
235
- const refreshOnFocus = () => {
236
- void refreshRef.current().catch(() => undefined);
237
- };
238
- const refreshOnVisibility = () => {
239
- if (document.visibilityState === 'visible') refreshOnFocus();
240
- };
241
- window.addEventListener('focus', refreshOnFocus);
242
- document.addEventListener('visibilitychange', refreshOnVisibility);
243
- return () => {
244
- window.removeEventListener('focus', refreshOnFocus);
245
- document.removeEventListener('visibilitychange', refreshOnVisibility);
246
- };
247
- }, [provider]);
248
-
249
- const createFile = useCallback(
250
- async (path: string, content = '') => {
251
- if (!providerRef.current) return;
252
- const providerV2 = getFileSystemProviderV2(providerRef.current);
253
- if (providerV2) {
254
- await providerV2.writeFile(parseWorkspacePath(path), new TextEncoder().encode(content), {
255
- mode: 'create',
256
- });
257
- } else {
258
- await providerRef.current.writeFile(path, content);
259
- }
260
- await refresh();
261
- },
262
- [refresh],
263
- );
264
-
265
- const createDirectory = useCallback(
266
- async (path: string) => {
267
- if (!providerRef.current) return;
268
- const providerV2 = getFileSystemProviderV2(providerRef.current);
269
- if (providerV2) {
270
- await providerV2.createDirectory(parseWorkspacePath(path), { mode: 'create' });
271
- } else {
272
- await providerRef.current.createDirectory(path);
273
- }
274
- await refresh();
275
- },
276
- [refresh],
277
- );
278
-
279
- const deleteEntry = useCallback(
280
- async (path: string) => {
281
- if (!providerRef.current) return;
282
- const providerV2 = getFileSystemProviderV2(providerRef.current);
283
- if (providerV2) {
284
- const canonical = parseWorkspacePath(path);
285
- const entry = await providerV2.stat(canonical);
286
- if (entry) {
287
- await providerV2.remove(canonical, {
288
- recursive: entry.kind === 'directory',
289
- missing: 'error',
290
- expectedVersion: entry.version,
291
- });
292
- }
293
- } else {
294
- await providerRef.current.delete(path);
295
- }
296
- if (selectedPath === path) {
297
- setSelectedPath(null);
298
- setSelectedKind(null);
299
- }
300
- await refresh();
301
- },
302
- [refresh, selectedPath],
303
- );
304
-
305
- const renameEntry = useCallback(
306
- async (oldPath: string, newPath: string, kind?: 'file' | 'directory') => {
307
- if (!providerRef.current) return;
308
- const oldNormalised = normalisePath(oldPath);
309
- const knownEntry = [...entries, ...childEntries.values()]
310
- .flat()
311
- .find((entry) => normalisePath(entry.path) === oldNormalised);
312
- const entryKind = kind ?? knownEntry?.kind ?? 'file';
313
- await moveFileSystemEntry(providerRef.current, oldPath, newPath, entryKind);
314
-
315
- if (selectedPath) {
316
- setSelectedPath(relocatePath(selectedPath, oldPath, newPath));
317
- }
318
-
319
- const nextExpanded = new Set(
320
- [...expanded].map((path) => relocatePath(path, oldPath, newPath)),
321
- );
322
- setExpanded(nextExpanded);
323
- setChildEntries(new Map());
324
- await loadRoot();
325
- for (const dirPath of nextExpanded) {
326
- await loadChildren(dirPath);
327
- }
328
- },
329
- [selectedPath, entries, childEntries, expanded, loadRoot, loadChildren],
330
- );
331
-
332
- // Merge root entries with child entries for a flat tree representation
333
- // (consumers use `expanded` and `childEntries` to render recursively)
334
-
335
- return {
336
- entries,
337
- expanded,
338
- selectedPath,
339
- selectedKind,
340
- loading,
341
- toggleExpand,
342
- select,
343
- reveal,
344
- createFile,
345
- createDirectory,
346
- deleteEntry,
347
- renameEntry,
348
- refresh,
349
- // Expose child entries for rendering
350
- ...{ childEntries },
351
- } as FileTreeState & FileTreeActions & { childEntries: Map<string, FileSystemEntry[]> };
352
- }