@terra-symposium/docs 0.0.0-stage → 0.1.17

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 (241) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +72 -2
  3. package/lib-dist/Docs.d.ts +22 -0
  4. package/lib-dist/Docs.d.ts.map +1 -0
  5. package/lib-dist/Docs.js +60 -0
  6. package/lib-dist/Docs.js.map +1 -0
  7. package/lib-dist/LocalDocsWorkspace.d.ts +30 -0
  8. package/lib-dist/LocalDocsWorkspace.d.ts.map +1 -0
  9. package/lib-dist/LocalDocsWorkspace.js +148 -0
  10. package/lib-dist/LocalDocsWorkspace.js.map +1 -0
  11. package/lib-dist/SiteLayout.d.ts +30 -0
  12. package/lib-dist/SiteLayout.d.ts.map +1 -0
  13. package/lib-dist/SiteLayout.js +34 -0
  14. package/lib-dist/SiteLayout.js.map +1 -0
  15. package/lib-dist/StaticWorkspace.d.ts +21 -0
  16. package/lib-dist/StaticWorkspace.d.ts.map +1 -0
  17. package/lib-dist/StaticWorkspace.js +179 -0
  18. package/lib-dist/StaticWorkspace.js.map +1 -0
  19. package/lib-dist/_Toolbar.d.ts +7 -0
  20. package/lib-dist/_Toolbar.d.ts.map +1 -0
  21. package/lib-dist/_Toolbar.js +85 -0
  22. package/lib-dist/_Toolbar.js.map +1 -0
  23. package/lib-dist/blocks.d.ts +23 -0
  24. package/lib-dist/blocks.d.ts.map +1 -0
  25. package/lib-dist/blocks.js +321 -0
  26. package/lib-dist/blocks.js.map +1 -0
  27. package/lib-dist/docs-catalog.d.ts +5 -0
  28. package/lib-dist/docs-catalog.d.ts.map +1 -0
  29. package/lib-dist/docs-catalog.js +38 -0
  30. package/lib-dist/docs-catalog.js.map +1 -0
  31. package/lib-dist/docs-style.css +31 -0
  32. package/lib-dist/fonts/Avara-Bold.woff2 +0 -0
  33. package/lib-dist/fonts/Avara-BoldItalic.woff2 +0 -0
  34. package/lib-dist/fonts/OFL-1.1.txt +93 -0
  35. package/lib-dist/fonts/README.md +5 -0
  36. package/lib-dist/host-runtime.d.ts +5 -0
  37. package/lib-dist/host-runtime.d.ts.map +1 -0
  38. package/lib-dist/host-runtime.js +21 -0
  39. package/lib-dist/host-runtime.js.map +1 -0
  40. package/lib-dist/host.d.ts +170 -0
  41. package/lib-dist/host.d.ts.map +1 -0
  42. package/lib-dist/host.js +2 -0
  43. package/lib-dist/host.js.map +1 -0
  44. package/lib-dist/index.d.ts +35 -0
  45. package/lib-dist/index.d.ts.map +1 -0
  46. package/lib-dist/index.js +28 -0
  47. package/lib-dist/index.js.map +1 -0
  48. package/lib-dist/markdown-model.d.ts +7 -0
  49. package/lib-dist/markdown-model.d.ts.map +1 -0
  50. package/lib-dist/markdown-model.js +181 -0
  51. package/lib-dist/markdown-model.js.map +1 -0
  52. package/lib-dist/markdown.d.ts +3 -0
  53. package/lib-dist/markdown.d.ts.map +1 -0
  54. package/lib-dist/markdown.js +83 -0
  55. package/lib-dist/markdown.js.map +1 -0
  56. package/lib-dist/navigation.d.ts +17 -0
  57. package/lib-dist/navigation.d.ts.map +1 -0
  58. package/lib-dist/navigation.js +9 -0
  59. package/lib-dist/navigation.js.map +1 -0
  60. package/lib-dist/schema.d.ts +135 -0
  61. package/lib-dist/schema.d.ts.map +1 -0
  62. package/lib-dist/schema.js +55 -0
  63. package/lib-dist/schema.js.map +1 -0
  64. package/lib-dist/style.css +1 -0
  65. package/lib-dist/terra/Workspace.d.ts +5 -0
  66. package/lib-dist/terra/Workspace.d.ts.map +1 -0
  67. package/lib-dist/terra/Workspace.js +9 -0
  68. package/lib-dist/terra/Workspace.js.map +1 -0
  69. package/lib-dist/terra/editor/TerraContentEditor.d.ts +10 -0
  70. package/lib-dist/terra/editor/TerraContentEditor.d.ts.map +1 -0
  71. package/lib-dist/terra/editor/TerraContentEditor.js +47 -0
  72. package/lib-dist/terra/editor/TerraContentEditor.js.map +1 -0
  73. package/lib-dist/terra/editor/_blocks.d.ts +71 -0
  74. package/lib-dist/terra/editor/_blocks.d.ts.map +1 -0
  75. package/lib-dist/terra/editor/_blocks.js +110 -0
  76. package/lib-dist/terra/editor/_blocks.js.map +1 -0
  77. package/lib-dist/terra/editor/docs/_Callout.d.ts +14 -0
  78. package/lib-dist/terra/editor/docs/_Callout.d.ts.map +1 -0
  79. package/lib-dist/terra/editor/docs/_Callout.js +41 -0
  80. package/lib-dist/terra/editor/docs/_Callout.js.map +1 -0
  81. package/lib-dist/terra/editor/docs/_CodeBlock.d.ts +39 -0
  82. package/lib-dist/terra/editor/docs/_CodeBlock.d.ts.map +1 -0
  83. package/lib-dist/terra/editor/docs/_CodeBlock.js +494 -0
  84. package/lib-dist/terra/editor/docs/_CodeBlock.js.map +1 -0
  85. package/lib-dist/terra/editor/docs/_DocsCode.d.ts +14 -0
  86. package/lib-dist/terra/editor/docs/_DocsCode.d.ts.map +1 -0
  87. package/lib-dist/terra/editor/docs/_DocsCode.js +95 -0
  88. package/lib-dist/terra/editor/docs/_DocsCode.js.map +1 -0
  89. package/lib-dist/terra/editor/docs/_DocsImage.d.ts +18 -0
  90. package/lib-dist/terra/editor/docs/_DocsImage.d.ts.map +1 -0
  91. package/lib-dist/terra/editor/docs/_DocsImage.js +62 -0
  92. package/lib-dist/terra/editor/docs/_DocsImage.js.map +1 -0
  93. package/lib-dist/terra/editor/docs/_Embed.d.ts +15 -0
  94. package/lib-dist/terra/editor/docs/_Embed.d.ts.map +1 -0
  95. package/lib-dist/terra/editor/docs/_Embed.js +27 -0
  96. package/lib-dist/terra/editor/docs/_Embed.js.map +1 -0
  97. package/lib-dist/terra/editor/docs/_Math.d.ts +31 -0
  98. package/lib-dist/terra/editor/docs/_Math.d.ts.map +1 -0
  99. package/lib-dist/terra/editor/docs/_Math.js +126 -0
  100. package/lib-dist/terra/editor/docs/_Math.js.map +1 -0
  101. package/lib-dist/terra/editor/docs/_Nodes.d.ts +15 -0
  102. package/lib-dist/terra/editor/docs/_Nodes.d.ts.map +1 -0
  103. package/lib-dist/terra/editor/docs/_Nodes.js +52 -0
  104. package/lib-dist/terra/editor/docs/_Nodes.js.map +1 -0
  105. package/lib-dist/terra/editor/docs/_Raw.d.ts +15 -0
  106. package/lib-dist/terra/editor/docs/_Raw.d.ts.map +1 -0
  107. package/lib-dist/terra/editor/docs/_Raw.js +26 -0
  108. package/lib-dist/terra/editor/docs/_Raw.js.map +1 -0
  109. package/lib-dist/terra/editor/docs/_Table.d.ts +12 -0
  110. package/lib-dist/terra/editor/docs/_Table.d.ts.map +1 -0
  111. package/lib-dist/terra/editor/docs/_Table.js +71 -0
  112. package/lib-dist/terra/editor/docs/_Table.js.map +1 -0
  113. package/lib-dist/terra/editor/docs/_Tabs.d.ts +14 -0
  114. package/lib-dist/terra/editor/docs/_Tabs.d.ts.map +1 -0
  115. package/lib-dist/terra/editor/docs/_Tabs.js +104 -0
  116. package/lib-dist/terra/editor/docs/_Tabs.js.map +1 -0
  117. package/lib-dist/terra/editor/docs/_TsDocs.d.ts +15 -0
  118. package/lib-dist/terra/editor/docs/_TsDocs.d.ts.map +1 -0
  119. package/lib-dist/terra/editor/docs/_TsDocs.js +81 -0
  120. package/lib-dist/terra/editor/docs/_TsDocs.js.map +1 -0
  121. package/lib-dist/terra/editor/docs/_math_inline.d.ts +14 -0
  122. package/lib-dist/terra/editor/docs/_math_inline.d.ts.map +1 -0
  123. package/lib-dist/terra/editor/docs/_math_inline.js +30 -0
  124. package/lib-dist/terra/editor/docs/_math_inline.js.map +1 -0
  125. package/lib-dist/terra/editor/docs/heading.d.ts +23 -0
  126. package/lib-dist/terra/editor/docs/heading.d.ts.map +1 -0
  127. package/lib-dist/terra/editor/docs/heading.js +60 -0
  128. package/lib-dist/terra/editor/docs/heading.js.map +1 -0
  129. package/lib-dist/terra/editor/docs/index.d.ts +13 -0
  130. package/lib-dist/terra/editor/docs/index.d.ts.map +1 -0
  131. package/lib-dist/terra/editor/docs/index.js +14 -0
  132. package/lib-dist/terra/editor/docs/index.js.map +1 -0
  133. package/lib-dist/terra/editor/types.d.ts +40 -0
  134. package/lib-dist/terra/editor/types.d.ts.map +1 -0
  135. package/lib-dist/terra/editor/types.js +11 -0
  136. package/lib-dist/terra/editor/types.js.map +1 -0
  137. package/lib-dist/terra/flow/Nodes.d.ts +78 -0
  138. package/lib-dist/terra/flow/Nodes.d.ts.map +1 -0
  139. package/lib-dist/terra/flow/Nodes.js +912 -0
  140. package/lib-dist/terra/flow/Nodes.js.map +1 -0
  141. package/lib-dist/terra/flow/_chrome.d.ts +267 -0
  142. package/lib-dist/terra/flow/_chrome.d.ts.map +1 -0
  143. package/lib-dist/terra/flow/_chrome.js +798 -0
  144. package/lib-dist/terra/flow/_chrome.js.map +1 -0
  145. package/lib-dist/terra/flow/_layout.d.ts +30 -0
  146. package/lib-dist/terra/flow/_layout.d.ts.map +1 -0
  147. package/lib-dist/terra/flow/_layout.js +53 -0
  148. package/lib-dist/terra/flow/_layout.js.map +1 -0
  149. package/lib-dist/terra/flow/menuContext.d.ts +13 -0
  150. package/lib-dist/terra/flow/menuContext.d.ts.map +1 -0
  151. package/lib-dist/terra/flow/menuContext.js +10 -0
  152. package/lib-dist/terra/flow/menuContext.js.map +1 -0
  153. package/lib-dist/terra/flow/powerSource.d.ts +16 -0
  154. package/lib-dist/terra/flow/powerSource.d.ts.map +1 -0
  155. package/lib-dist/terra/flow/powerSource.js +59 -0
  156. package/lib-dist/terra/flow/powerSource.js.map +1 -0
  157. package/lib-dist/terra/pages/docs/_attachments.d.ts +20 -0
  158. package/lib-dist/terra/pages/docs/_attachments.d.ts.map +1 -0
  159. package/lib-dist/terra/pages/docs/_attachments.js +904 -0
  160. package/lib-dist/terra/pages/docs/_attachments.js.map +1 -0
  161. package/lib-dist/terra/pages/docs/_blocks.d.ts +48 -0
  162. package/lib-dist/terra/pages/docs/_blocks.d.ts.map +1 -0
  163. package/lib-dist/terra/pages/docs/_blocks.js +192 -0
  164. package/lib-dist/terra/pages/docs/_blocks.js.map +1 -0
  165. package/lib-dist/terra/pages/docs/_custom.d.ts +5 -0
  166. package/lib-dist/terra/pages/docs/_custom.d.ts.map +1 -0
  167. package/lib-dist/terra/pages/docs/_custom.js +6 -0
  168. package/lib-dist/terra/pages/docs/_custom.js.map +1 -0
  169. package/lib-dist/terra/pages/docs/_editable.d.ts +61 -0
  170. package/lib-dist/terra/pages/docs/_editable.d.ts.map +1 -0
  171. package/lib-dist/terra/pages/docs/_editable.js +404 -0
  172. package/lib-dist/terra/pages/docs/_editable.js.map +1 -0
  173. package/lib-dist/terra/pages/docs/_export-dialog.d.ts +19 -0
  174. package/lib-dist/terra/pages/docs/_export-dialog.d.ts.map +1 -0
  175. package/lib-dist/terra/pages/docs/_export-dialog.js +78 -0
  176. package/lib-dist/terra/pages/docs/_export-dialog.js.map +1 -0
  177. package/lib-dist/terra/pages/docs/_helpers.d.ts +395 -0
  178. package/lib-dist/terra/pages/docs/_helpers.d.ts.map +1 -0
  179. package/lib-dist/terra/pages/docs/_helpers.js +911 -0
  180. package/lib-dist/terra/pages/docs/_helpers.js.map +1 -0
  181. package/lib-dist/terra/pages/docs/_icons.d.ts +20 -0
  182. package/lib-dist/terra/pages/docs/_icons.d.ts.map +1 -0
  183. package/lib-dist/terra/pages/docs/_icons.js +29 -0
  184. package/lib-dist/terra/pages/docs/_icons.js.map +1 -0
  185. package/lib-dist/terra/pages/docs/_main.d.ts +51 -0
  186. package/lib-dist/terra/pages/docs/_main.d.ts.map +1 -0
  187. package/lib-dist/terra/pages/docs/_main.js +1959 -0
  188. package/lib-dist/terra/pages/docs/_main.js.map +1 -0
  189. package/lib-dist/terra/pages/docs/_sidebar.d.ts +42 -0
  190. package/lib-dist/terra/pages/docs/_sidebar.d.ts.map +1 -0
  191. package/lib-dist/terra/pages/docs/_sidebar.js +2450 -0
  192. package/lib-dist/terra/pages/docs/_sidebar.js.map +1 -0
  193. package/lib-dist/terra/pages/docs/_toolbar.d.ts +23 -0
  194. package/lib-dist/terra/pages/docs/_toolbar.d.ts.map +1 -0
  195. package/lib-dist/terra/pages/docs/_toolbar.js +1539 -0
  196. package/lib-dist/terra/pages/docs/_toolbar.js.map +1 -0
  197. package/lib-dist/terra/pages/docs/_tree.d.ts +178 -0
  198. package/lib-dist/terra/pages/docs/_tree.d.ts.map +1 -0
  199. package/lib-dist/terra/pages/docs/_tree.js +1168 -0
  200. package/lib-dist/terra/pages/docs/_tree.js.map +1 -0
  201. package/lib-dist/terra/regex.mjs +398 -0
  202. package/lib-dist/terra/runtime/action-proxy.d.ts +5 -0
  203. package/lib-dist/terra/runtime/action-proxy.d.ts.map +1 -0
  204. package/lib-dist/terra/runtime/action-proxy.js +19 -0
  205. package/lib-dist/terra/runtime/action-proxy.js.map +1 -0
  206. package/lib-dist/terra/runtime/content.d.ts +150 -0
  207. package/lib-dist/terra/runtime/content.d.ts.map +1 -0
  208. package/lib-dist/terra/runtime/content.js +1171 -0
  209. package/lib-dist/terra/runtime/content.js.map +1 -0
  210. package/lib-dist/terra/runtime/leaf.d.ts +5 -0
  211. package/lib-dist/terra/runtime/leaf.d.ts.map +1 -0
  212. package/lib-dist/terra/runtime/leaf.js +13 -0
  213. package/lib-dist/terra/runtime/leaf.js.map +1 -0
  214. package/lib-dist/terra/runtime/nodes.d.ts +112 -0
  215. package/lib-dist/terra/runtime/nodes.d.ts.map +1 -0
  216. package/lib-dist/terra/runtime/nodes.js +534 -0
  217. package/lib-dist/terra/runtime/nodes.js.map +1 -0
  218. package/lib-dist/terra/runtime/runtime.d.ts +25 -0
  219. package/lib-dist/terra/runtime/runtime.d.ts.map +1 -0
  220. package/lib-dist/terra/runtime/runtime.js +50 -0
  221. package/lib-dist/terra/runtime/runtime.js.map +1 -0
  222. package/lib-dist/terra/runtime/shared.d.ts +35 -0
  223. package/lib-dist/terra/runtime/shared.d.ts.map +1 -0
  224. package/lib-dist/terra/runtime/shared.js +79 -0
  225. package/lib-dist/terra/runtime/shared.js.map +1 -0
  226. package/lib-dist/terra/runtime/types.d.ts +266 -0
  227. package/lib-dist/terra/runtime/types.d.ts.map +1 -0
  228. package/lib-dist/terra/runtime/types.js +18 -0
  229. package/lib-dist/terra/runtime/types.js.map +1 -0
  230. package/lib-dist/terra/runtime/ui.d.ts +31 -0
  231. package/lib-dist/terra/runtime/ui.d.ts.map +1 -0
  232. package/lib-dist/terra/runtime/ui.js +16 -0
  233. package/lib-dist/terra/runtime/ui.js.map +1 -0
  234. package/lib-dist/workspace.d.ts +78 -0
  235. package/lib-dist/workspace.d.ts.map +1 -0
  236. package/lib-dist/workspace.js +151 -0
  237. package/lib-dist/workspace.js.map +1 -0
  238. package/package.json +90 -3
  239. package/src/SiteLayout.astro +38 -0
  240. package/src/navigation.tsx +47 -0
  241. package/src/style.css +733 -0
@@ -0,0 +1,1168 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck -- source-preserving extraction; public host contracts are typed in src/host.ts.
3
+ /**
4
+ * astro/src/pages/docs/_tree.tsx
5
+ * Recursively rendering branch nodes composing the sidebar UI tree structure, alongside leaves and menus.
6
+ * Folder boundary: recursive sidebar tree presentation only; sidebar state and dialogs remain in `_sidebar.tsx`.
7
+ */
8
+ /*//////////////////////////////////////////////////
9
+ IMPORTS
10
+ //////////////////////////////////////////////////*/
11
+ // API
12
+ import { $unsavedDrafts, clearTransition, download, extractAllNodeIds, getStableKey, isIndexFile, memoryDrafts, resetEditorMounts, runMutation, sanitizeName, setTransition, uploadAssetChunked, useAtom, } from './_helpers.js';
13
+ import { actions } from '../../runtime/action-proxy.js';
14
+ import { bName, cTitle, dirname, docsPath, docsRootPath, toNodeLabel, toPaths } from '../../runtime/shared.js';
15
+ import { cn, notify } from '../../runtime/runtime.js';
16
+ import { toPortableBody } from '../../runtime/leaf.js';
17
+ import { toHeadingId } from '../../editor/docs/heading.js';
18
+ import { toast } from 'sonner';
19
+ import { useEffect, useRef, useState } from 'react';
20
+ // COMPONENTS
21
+ import * as TerraQualityRegex from '../../regex.mjs';
22
+ import React, { Fragment } from 'react';
23
+ import { Add as AddIcon, ArrowUpRight as PopOutIcon, Box as BoxIcon, Copy as CopyIcon, Doc as DocumentIcon, Download as DownloadIcon, Draggable as DragIcon, Edit as EditIcon, Link as LinkIcon, MagicWand as MagicIcon, OverflowMenuVertical as OverflowMenuIcon, TrashCan as TrashIcon, } from '@carbon/icons-react';
24
+ import { Button, ButtonLink } from '@terra-symposium/styling/ui/terra-original';
25
+ import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from '@terra-symposium/styling/ui/terra-original';
26
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@terra-symposium/styling/ui/terra-original';
27
+ import { ErrorBoundary } from '../../runtime/ui.js';
28
+ import { SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubItem } from '@terra-symposium/styling/ui/terra-original';
29
+ // DATA
30
+ import { REORDER } from '../../runtime/shared.js';
31
+ /*//////////////////////////////////////////////////
32
+ CONFIG
33
+ //////////////////////////////////////////////////*/
34
+ // DATA
35
+ /** Validated interactive literal mappings explicitly capturing action intents. */
36
+ const UI = { add: { doc: 'Add page', group: 'Add group' }, open: 'bg-primary-3 text-primary-12' };
37
+ /** Returns the rendered Docs mode shell used by navigation and the table of contents. */
38
+ const docsShellOf = () => Array.from(document.querySelectorAll('[data-docs-shell]')).find(e => {
39
+ const style = window.getComputedStyle(e);
40
+ return (e.dataset.docsMode === (new URL(window.location.href).searchParams.get('mode') || 'view') &&
41
+ style.display !== 'none' &&
42
+ style.visibility !== 'hidden' &&
43
+ e.dataset.loading !== 'true' &&
44
+ e.querySelector('[data-docs-transition-content]')?.getAttribute('aria-hidden') !== 'true' &&
45
+ e.getClientRects().length > 0);
46
+ }) ?? null;
47
+ /**
48
+ * Handles navigate heading.
49
+ *
50
+ * @param id - Tree node identifier used to create the link target.
51
+ * @param text - Tree node label displayed to the reader.
52
+ */
53
+ const navigateHeading = (id, text) => {
54
+ const shell = docsShellOf();
55
+ const root = shell?.dataset.docsMode === 'view'
56
+ ? shell.querySelector('[data-docs-view-root], [data-docs-body-content]')
57
+ : shell?.dataset.docsMode === 'edit'
58
+ ? shell.querySelector('[data-docs-body-editor], [data-docs-body-content]')
59
+ : null;
60
+ Array.from(root?.querySelectorAll('h2') ?? [])
61
+ .find(e => e.id === id || e.textContent?.trim() === text)
62
+ ?.scrollIntoView({ behavior: 'auto', block: 'start' });
63
+ window.history.pushState(null, '', `#${id}`);
64
+ };
65
+ // TYPES
66
+ /*//////////////////////////////////////////////////
67
+ MENU COMPONENTS
68
+ //////////////////////////////////////////////////*/
69
+ /**
70
+ * Renders a standard ContextMenu with support for nested sub-actions and separators.
71
+ *
72
+ * @param props - Ordered menu actions, including labels, optional icons, disabled state, separators, and nested sub-actions.
73
+ *
74
+ * @return The context menu with the supplied actions and any nested menus.
75
+ */
76
+ export function Menu({ actions }) {
77
+ // prettier-ignore
78
+ return _jsx(ErrorBoundary, { name: 'Menu', children: actions.map((a, i) => (_jsxs(Fragment, { children: [a.separator && _jsx(ContextMenuSeparator, {}), a.subActions ? (_jsxs(ContextMenuSub, { children: [_jsxs(ContextMenuSubTrigger, { disabled: a.disabled, className: a.destructive ? 'text-red-600 focus:text-red-600' : undefined, children: [a.icon, " ", _jsx("span", { children: a.label })] }), _jsx(ContextMenuSubContent, { children: _jsx(Menu, { actions: a.subActions }) })] })) : _jsxs(ContextMenuItem, { className: 'px-3', disabled: a.disabled, intent: a.destructive ? 'destructive' : 'default', onSelect: a.onSelect, children: [a.icon, " ", a.label] })] }, a.label + i))) });
79
+ }
80
+ /**
81
+ * Renders the link component.
82
+ *
83
+ * @param props - Tree node state, display mode, navigation context, and optional trailing control for one sidebar link.
84
+ * @param active - Boolean flag controlling link's active behaviour.
85
+ * @param contexting - Contexting input supplied to link's operation.
86
+ * @param dragging - Dragging input supplied to link's operation.
87
+ * @param renaming - Renaming input supplied to link's operation.
88
+ * @param node - Node input supplied to link's operation.
89
+ * @param trailing - Trailing input supplied to link's operation.
90
+ * @param mode - Mode input supplied to link's operation.
91
+ * @param transitioning - Transitioning input supplied to link's operation.
92
+ *
93
+ * @return The interactive sidebar link with its current selection and drag/rename states.
94
+ */
95
+ function Link({ active: a, contexting = false, dragging = false, renaming = false, node, trailing, mode, transitioning = false, }) {
96
+ const unsaved = useAtom($unsavedDrafts);
97
+ const asset = node.type === 'leaf' && node.asset === true;
98
+ const isDirty = node.type === 'leaf' && (unsaved[(node.type === 'leaf' ? node.path : '') ?? ''] || unsaved[(node.type === 'leaf' ? node.url : '') ?? '']);
99
+ return (_jsx(SidebarMenuButton, { asChild: true, isActive: a, className: cn('group h-auto justify-between py-0 transition-colors', contexting && UI.open, renaming && 'ring-1 ring-ring', isDirty && 'pl-1.5'), children: _jsxs("div", { className: 'flex w-full items-center gap-1', children: [_jsxs("a", { href: asset ? (node.type === 'leaf' && node.url ? node.url : '') : 'url' in node && node.url ? node.url : '', target: asset || (node.type === 'leaf' && node.fileType === 'pdf') ? '_blank' : '_self', rel: asset || (node.type === 'leaf' && node.fileType === 'pdf') ? 'noreferrer' : undefined, className: cn('flex min-w-0 flex-1 items-center gap-1.5', dragging && 'pointer-events-none'), draggable: false, onDragStart: e => {
100
+ e.preventDefault();
101
+ }, onClick: async (e) => {
102
+ if (asset || (node.type === 'leaf' && node.fileType === 'pdf'))
103
+ return;
104
+ if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)
105
+ return;
106
+ e.preventDefault();
107
+ if (a || transitioning || !('url' in node) || !node.url)
108
+ return;
109
+ if (node.type !== 'leaf')
110
+ return;
111
+ setTransition('local');
112
+ let data;
113
+ try {
114
+ const result = (await actions.read.doc.get({ path: node.path }));
115
+ if (!result.error)
116
+ data = result.data;
117
+ }
118
+ catch {
119
+ // Keep the current document visible when its replacement cannot be loaded.
120
+ }
121
+ if (!data || !Array.isArray(data.content) || typeof data.source !== 'string') {
122
+ clearTransition();
123
+ toast.error('Failed to load document.');
124
+ return;
125
+ }
126
+ const content = toPortableBody(data.content);
127
+ resetEditorMounts(node.path, ['root']);
128
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => {
129
+ resolve();
130
+ })));
131
+ const url = new URL(node.url, window.location.origin);
132
+ new URL(window.location.href).searchParams.forEach((v, k) => {
133
+ if (k !== 'mode' && !url.searchParams.has(k))
134
+ url.searchParams.set(k, v);
135
+ });
136
+ if (mode !== 'view')
137
+ url.searchParams.set('mode', mode);
138
+ else
139
+ url.searchParams.delete('mode');
140
+ // PIPELINE: sidebar navigation carries stored JSON; View never compiles Markdown.
141
+ window.history.pushState(null, '', url.toString());
142
+ // prettier-ignore
143
+ document.dispatchEvent(new CustomEvent('docs:navigate', { detail: { url: url.pathname + url.search, pathname: url.pathname + url.search, path: node.path, title: data.title || node.title, description: data.description ?? node.description, contributors: node.contributors, updatedAt: node.updatedAt, source: data.source, content, compiledMDX: '' } }));
144
+ }, children: [isDirty && _jsx("span", { className: 'size-1.5 shrink-0 rounded-full bg-yellow-500', title: 'Unsaved changes' }), _jsx("span", { className: 'line-clamp-1', children: cTitle(toNodeLabel(node.title || node.origName)) }), asset && _jsx(PopOutIcon, { className: 'ml-auto size-3.5 shrink-0 text-primary-10', "aria-hidden": 'true' })] }), trailing] }) }));
145
+ }
146
+ /*//////////////////////////////////////////////////
147
+ COMPONENTS
148
+ //////////////////////////////////////////////////*/
149
+ /**
150
+ * Encapsulates the drag handler button for consistent visual implementation
151
+ * across branch and leaf levels to reduce boilerplate logic.
152
+ *
153
+ * @param props - Drag availability, visibility, and start/end callbacks for the branch or leaf reorder handle.
154
+ * @param disabled - Disabled state controlling whether drag handle accepts interaction.
155
+ * @param isVisible - Boolean flag controlling drag handle's is visible behaviour.
156
+ * @param onDragStart - Callback invoked for drag start.
157
+ * @param onDragEnd - Callback invoked for drag end.
158
+ *
159
+ * @return The draggable reorder button, hidden or disabled when reordering is unavailable.
160
+ */
161
+ function DragHandle({ disabled, isVisible, onDragStart, onDragEnd }) {
162
+ return (_jsx(Button, { variant: 'ghost', size: 'icon', "aria-label": 'Reorder item', draggable: true, className: cn(REORDER.handle, isVisible && !disabled ? 'opacity-100' : 'opacity-0 disabled:opacity-0'), disabled: disabled, onClick: e => {
163
+ e.preventDefault();
164
+ }, onDragStart: e => {
165
+ e.stopPropagation();
166
+ onDragStart(e);
167
+ }, onDragEnd: onDragEnd, title: 'Drag', children: _jsx(DragIcon, { className: 'size-3', "aria-hidden": 'true' }) }));
168
+ }
169
+ /*//////////////////////////////////////////////////
170
+ TOC COMPONENTS
171
+ //////////////////////////////////////////////////*/
172
+ /**
173
+ * Renders the toc component.
174
+ *
175
+ * @param props - Optional heading override used while collecting the document's navigable section headings.
176
+ * @param title - Heading text shown in toc.
177
+ *
178
+ * @return A table of contents for discovered headings, or `null` when there are no headings to navigate.
179
+ */
180
+ const TREE_COMPONENTS = {
181
+ Toc({ title }) {
182
+ const [heads, setHeads] = useState([]);
183
+ const [a, setActive] = useState(null);
184
+ useEffect(() => {
185
+ const syncState = { frame: 0, tries: 0, els: [], observedRoot: null, threshold: 0 };
186
+ const surfaceObserver = new MutationObserver(() => syncLifecycle.sync());
187
+ const bodyObserver = new MutationObserver(() => {
188
+ syncLifecycle.sync();
189
+ if (syncState.observedRoot)
190
+ bodyObserver.disconnect();
191
+ });
192
+ /**
193
+ * Sync active.
194
+ */
195
+ const syncLifecycle = {
196
+ syncActive: () => {
197
+ if (!syncState.els.length) {
198
+ setActive(null);
199
+ return;
200
+ }
201
+ syncState.threshold = window.innerWidth >= 768 ? 160 : 112;
202
+ for (let i = syncState.els.length - 1; i >= 0; i--) {
203
+ const el = syncState.els[i];
204
+ if (el && el.getBoundingClientRect().top <= syncState.threshold) {
205
+ setActive(el.id);
206
+ return;
207
+ }
208
+ }
209
+ setActive(syncState.els[0]?.id ?? null);
210
+ },
211
+ /*
212
+ * Queue active.
213
+ */
214
+ queueActive: () => {
215
+ cancelAnimationFrame(syncState.frame);
216
+ syncState.frame = requestAnimationFrame(syncLifecycle.syncActive);
217
+ },
218
+ /*
219
+ * Synchronizes the current view state with the source data.
220
+ */
221
+ sync: () => {
222
+ try {
223
+ const seen = new Set();
224
+ const shell = docsShellOf();
225
+ let root = null;
226
+ for (const e of document.querySelectorAll(shell?.dataset.docsMode === 'view'
227
+ ? '[data-docs-view-root], [data-docs-body-content]'
228
+ : '[data-docs-body-editor], [data-docs-body-content]')) {
229
+ const style = window.getComputedStyle(e);
230
+ if (style.display !== 'none' && style.visibility !== 'hidden' && e.getClientRects().length > 0) {
231
+ root = e;
232
+ break;
233
+ }
234
+ }
235
+ if (!root)
236
+ return;
237
+ if (root !== syncState.observedRoot) {
238
+ surfaceObserver.disconnect();
239
+ surfaceObserver.observe(root, { childList: true, characterData: true, subtree: true });
240
+ syncState.observedRoot = root;
241
+ }
242
+ const lowerTitle = title ? title.toLowerCase() : null;
243
+ const validEls = [];
244
+ const headsList = [];
245
+ for (const e of Array.from(root.querySelectorAll('h2') ?? [])) {
246
+ if (e.closest('[data-placeholder], [data-tabs-placeholder]') || e.closest('[hidden]'))
247
+ continue;
248
+ let text = '';
249
+ for (const child of Array.from(e.childNodes)) {
250
+ if (child instanceof Element && child.classList.contains('heading-anchor-icon'))
251
+ continue;
252
+ text += child.textContent || '';
253
+ }
254
+ text = text.trim();
255
+ if (lowerTitle && text.toLowerCase() === lowerTitle)
256
+ continue;
257
+ const id = e.id || (e.id = toHeadingId(text));
258
+ if (!id || seen.has(id))
259
+ continue;
260
+ seen.add(id);
261
+ validEls.push(e);
262
+ headsList.push({ id, text });
263
+ }
264
+ syncState.els = validEls;
265
+ if (!syncState.els.length) {
266
+ setHeads([]);
267
+ setActive(null);
268
+ return;
269
+ }
270
+ setHeads(headsList);
271
+ syncLifecycle.queueActive();
272
+ }
273
+ catch {
274
+ /* ignore */
275
+ }
276
+ },
277
+ /*
278
+ * Re-reads headings until the surface has mounted them.
279
+ *
280
+ */
281
+ pump: () => {
282
+ syncLifecycle.sync();
283
+ if (syncState.els.length || syncState.tries++ >= 60)
284
+ return;
285
+ syncState.frame = requestAnimationFrame(syncLifecycle.pump);
286
+ },
287
+ /*
288
+ * Handles navigate.
289
+ *
290
+ * @param e - Pointer event received from the tree interaction.
291
+ */
292
+ navigate: (e) => {
293
+ const link = e.target instanceof Element ? e.target.closest('[data-docs-toc-heading]') : null;
294
+ const id = link?.dataset.docsTocId;
295
+ const text = link?.dataset.docsTocHeading;
296
+ if (!id || !text)
297
+ return;
298
+ e.preventDefault();
299
+ navigateHeading(id, text);
300
+ },
301
+ };
302
+ syncLifecycle.pump();
303
+ if (!syncState.observedRoot)
304
+ bodyObserver.observe(document.body, { childList: true, subtree: true });
305
+ window.addEventListener('click', syncLifecycle.navigate, true);
306
+ document.addEventListener('docs:surface-ready', syncLifecycle.sync);
307
+ document.addEventListener('astro:after-swap', syncLifecycle.sync);
308
+ window.addEventListener('hashchange', syncLifecycle.sync);
309
+ window.addEventListener('resize', syncLifecycle.queueActive);
310
+ window.addEventListener('scroll', syncLifecycle.queueActive, { passive: true });
311
+ return () => {
312
+ window.removeEventListener('click', syncLifecycle.navigate, true);
313
+ document.removeEventListener('docs:surface-ready', syncLifecycle.sync);
314
+ document.removeEventListener('astro:after-swap', syncLifecycle.sync);
315
+ window.removeEventListener('hashchange', syncLifecycle.sync);
316
+ window.removeEventListener('resize', syncLifecycle.queueActive);
317
+ window.removeEventListener('scroll', syncLifecycle.queueActive);
318
+ bodyObserver.disconnect();
319
+ surfaceObserver.disconnect();
320
+ cancelAnimationFrame(syncState.frame);
321
+ };
322
+ }, [title]);
323
+ useEffect(() => {
324
+ const id = decodeURIComponent(window.location.hash.slice(1));
325
+ if (!id)
326
+ return;
327
+ let heading;
328
+ for (const i of heads) {
329
+ if (i.id === id) {
330
+ heading = i;
331
+ break;
332
+ }
333
+ }
334
+ if (heading)
335
+ navigateHeading(id, heading.text);
336
+ }, [heads]);
337
+ if (!heads?.length)
338
+ return null;
339
+ return (_jsx("nav", { "aria-label": 'Table of contents', children: _jsx(SidebarMenuSub, { className: 'mr-0 pr-0', children: heads.map(h => (_jsx(SidebarMenuSubItem, { children: _jsx(ButtonLink, { "data-docs-toc-id": h.id, "data-docs-toc-heading": h.text, href: `#${h.id}`, variant: 'ghost', size: 'xs', "aria-label": `Jump to ${h.text}`, className: cn('relative w-full justify-start truncate pl-4 before:absolute before:left-1.5 before:top-1/2 before:size-1 before:-translate-y-1/2 before:rounded-full before:content-[""] hover:!bg-transparent', a === h.id ? 'text-primary-12 before:bg-current' : 'text-primary-11 before:bg-primary-11 before:opacity-0 hover:before:opacity-100'), children: h.text }) }, h.id))) }) }));
340
+ },
341
+ /*//////////////////////////////////////////////////
342
+ LEAF COMPONENTS
343
+ //////////////////////////////////////////////////*/
344
+ /**
345
+ * Renders the leaf component.
346
+ *
347
+ * @param props - Leaf metadata, parent path, sibling nodes, and tree interaction handlers for rendering a document entry.
348
+ * @param active - Boolean flag controlling leaf's active behaviour.
349
+ * @param bPath - B path input supplied to leaf's operation.
350
+ * @param node - Node input supplied to leaf's operation.
351
+ * @param ctx - Ctx input supplied to leaf's operation.
352
+ * @param siblings - Siblings input supplied to leaf's operation.
353
+ *
354
+ * @return The document row with its navigation, selection, rename, attachment, export, and reorder controls.
355
+ */
356
+ Leaf({ active: a, bPath, node, ctx, siblings, }) {
357
+ // STATES
358
+ const { creating, drag, dState, mode, shellPermission, hovered } = ctx;
359
+ const [contexting, setContexting] = useState(false);
360
+ const [renaming, setRenaming] = useState(false);
361
+ const [progress, setProgress] = useState(null);
362
+ const [isDragOverFile, setIsDragOverFile] = useState(false);
363
+ const fileRef = useRef(null);
364
+ const leafRef = useRef(null);
365
+ const permission = shellPermission === 'write' ? 'write' : node.permission;
366
+ /**
367
+ * Checks whether editable file.
368
+ *
369
+ * @param path - Path to process.
370
+ * @param fileType - Value passed to the helper.
371
+ */
372
+ const editable = permission === 'write' && !!node.path && (node.fileType === 'md' || node.fileType === 'mdx');
373
+ const path = node.path ?? '';
374
+ const drop = `leaf:${path}`;
375
+ const disabled = creating || renaming || progress !== null;
376
+ const isIdx = isIndexFile(path);
377
+ const [sis, setSis] = useState([]);
378
+ const [sisLoading, setSisLoading] = useState(false);
379
+ const isSimulated = node.url === docsRootPath();
380
+ const isMultiSel = ctx.selectedLeaves.includes(path);
381
+ // SCROLL ACTIVE TO VIEWPORT TOP
382
+ useEffect(() => {
383
+ if (!a || !leafRef.current)
384
+ return;
385
+ /**
386
+ * Returns the raise.
387
+ */
388
+ const raise = () => {
389
+ const el = leafRef.current;
390
+ if (!el)
391
+ return;
392
+ const container = el.closest('.overflow-y-auto');
393
+ if (container) {
394
+ const rects = [container.getBoundingClientRect(), el.getBoundingClientRect()];
395
+ const offset = rects[1].top - rects[0].top;
396
+ if (Math.abs(offset - 16) > 5)
397
+ container.scrollBy({ top: offset - 16, behavior: 'smooth' });
398
+ }
399
+ else
400
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' });
401
+ };
402
+ const timers = new Set();
403
+ timers.add(setTimeout(raise, 50));
404
+ timers.add(setTimeout(raise, 400));
405
+ timers.add(setTimeout(raise, 1000));
406
+ return () => {
407
+ timers.forEach(clearTimeout);
408
+ };
409
+ }, [a]);
410
+ // RESOLVE SISTER FILES (ASSETS)
411
+ useEffect(() => {
412
+ let isActive = true;
413
+ if (contexting && editable && path) {
414
+ setSisLoading(true);
415
+ const dir = dirname(path);
416
+ const siblingByPath = new Map();
417
+ for (const sibling of siblings ?? [])
418
+ if (sibling.type === 'leaf' && sibling.path)
419
+ siblingByPath.set(sibling.path, sibling);
420
+ actions.read.doc
421
+ .list({ path: dir, type: 'folderEntries' })
422
+ .then(({ data: d }) => {
423
+ if (isActive && d && Array.isArray(d)) {
424
+ const sisList = [];
425
+ for (const p of d) {
426
+ if (p === path || dirname(p) !== dir)
427
+ continue;
428
+ const isMd = TerraQualityRegex.REGEX_MD_MDX_I_REGEX().test(p);
429
+ if (!isIdx && isMd)
430
+ continue;
431
+ const sNode = siblingByPath.get(p);
432
+ sisList.push({ name: bName(p), path: p, title: sNode && sNode.type === 'leaf' ? sNode.title : undefined, isMd });
433
+ }
434
+ setSis(sisList);
435
+ }
436
+ })
437
+ .catch((e) => {
438
+ console.error('[DocsTree:sisters] load-failed', { path, error: e });
439
+ toast.error(e instanceof Error ? e.message : 'Failed to load sibling files.');
440
+ })
441
+ .finally(() => {
442
+ if (isActive)
443
+ setSisLoading(false);
444
+ });
445
+ }
446
+ return () => {
447
+ isActive = false;
448
+ };
449
+ }, [contexting, editable, path, siblings, isIdx]);
450
+ // HANDLERS
451
+ /**
452
+ * Returns the rename page.
453
+ *
454
+ */
455
+ const pageActions = {
456
+ renamePage: () => {
457
+ if (renaming || !node.path)
458
+ return;
459
+ const currTitle = cTitle(toNodeLabel(node.title || node.origName)) || bName(node.path);
460
+ setRenaming(true);
461
+ ctx.setInputDlg({
462
+ open: true,
463
+ title: 'Rename Page',
464
+ label: 'Page title',
465
+ value: currTitle,
466
+ onCancel: () => {
467
+ setRenaming(false);
468
+ },
469
+ onSave: async (nTitle) => {
470
+ ctx.setInputDlg(null);
471
+ if (!nTitle || nTitle === currTitle) {
472
+ setRenaming(false);
473
+ return;
474
+ }
475
+ try {
476
+ const { data: res, error: resErr } = await actions.read.doc.resolvePath({ path: node.path, title: nTitle });
477
+ if (resErr || !res)
478
+ throw resErr || new Error('Failed to resolve path');
479
+ const nPath = res.path;
480
+ const { data: optRouteRes } = await actions.read.util.pathname({ path: nPath });
481
+ const optRoute = optRouteRes || ('url' in node ? node.url : docsRootPath());
482
+ // optimistic UI updates
483
+ // prettier-ignore
484
+ document.dispatchEvent(new CustomEvent('docs:rename', { detail: { nfilepath: nPath, path: node.path, pathname: optRoute, title: nTitle, isActive: a } }));
485
+ if (a) {
486
+ const url = new URL(window.location.href);
487
+ url.pathname = optRoute;
488
+ window.history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`);
489
+ }
490
+ if (nPath !== node.path) {
491
+ const { data: docData } = await actions.read.doc.get({ path: node.path });
492
+ const { error: err } = await actions.write.doc.commit({
493
+ cur: node.path,
494
+ next: nPath,
495
+ title: nTitle,
496
+ source: docData && typeof docData === 'object' && 'source' in docData && typeof docData.source === 'string' ? docData.source : '',
497
+ });
498
+ if (err)
499
+ throw err;
500
+ }
501
+ else {
502
+ const { error: writeErr } = await actions.write.doc.editor({ path: nPath, title: nTitle });
503
+ if (writeErr)
504
+ throw writeErr;
505
+ }
506
+ }
507
+ catch (err) {
508
+ toast.error('Failed to rename docs page.');
509
+ // revert on failure
510
+ const originalRoute = 'url' in node ? node.url : docsRootPath();
511
+ // prettier-ignore
512
+ document.dispatchEvent(new CustomEvent('docs:rename', { detail: { nfilepath: node.path, path: node.path, pathname: originalRoute, title: currTitle, isActive: a } }));
513
+ if (a) {
514
+ const url = new URL(window.location.href);
515
+ url.pathname = originalRoute;
516
+ window.history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`);
517
+ }
518
+ console.error('Rename failed', err);
519
+ }
520
+ finally {
521
+ setRenaming(false);
522
+ }
523
+ },
524
+ });
525
+ },
526
+ /*
527
+ * Returns the duplicate.
528
+ *
529
+ * @param mode - Value passed to the helper.
530
+ */
531
+ duplicate: (mode) => {
532
+ if (creating || !node.path)
533
+ return;
534
+ // prettier-ignore
535
+ ctx.setInputDlg({
536
+ open: true,
537
+ title: mode === 'reference' ? 'Duplicate by Reference' : 'Duplicate by Content',
538
+ label: 'New page name',
539
+ value: `${path.split('/').at(-1)?.replace(TerraQualityRegex.REGEX_MD_MDX_I_REGEX(), '') ?? ''}-copy`,
540
+ onCancel: () => { },
541
+ onSave: t => runMutation(async () => {
542
+ const { data: cur, error: readErr } = (await actions.read.doc.get({ path }));
543
+ if (readErr || !cur)
544
+ throw readErr || new Error('Failed to read doc.');
545
+ const nPath = `${bPath}/${sanitizeName(t)}.mdx`;
546
+ // MDX natively acts as a reference symlink when imported
547
+ let finalSrc = cur.source;
548
+ if (mode === 'reference') {
549
+ finalSrc = `import Reference from './${path.split('/').pop()}';\n\n<Reference />`;
550
+ }
551
+ // find the parent ID directly from bPath to prevent orphaned docs
552
+ const parentIds = bPath.split('/').filter(Boolean);
553
+ // prettier-ignore
554
+ const { data: doc, error: writeErr } = (await actions.write.doc.save({ path: nPath, source: finalSrc, parentId: parentIds.length > 0 ? parentIds[parentIds.length - 1] : undefined, title: t }));
555
+ if (writeErr || !doc)
556
+ throw writeErr || new Error('Failed to write doc.');
557
+ const nLeafPath = doc.path || nPath;
558
+ const nRoute = docsPath([...bPath.split('/').filter(Boolean), nLeafPath].join('/'));
559
+ // 1. Optimistically insert into the sidebar tree to prevent flashing
560
+ ctx.setTree(c => {
561
+ const nTree = JSON.parse(JSON.stringify(c));
562
+ const insertionFrames = [{ nodes: nTree, index: 0 }];
563
+ while (insertionFrames.length) {
564
+ const frame = insertionFrames[insertionFrames.length - 1];
565
+ if (!frame)
566
+ break;
567
+ if (frame.index >= frame.nodes.length) {
568
+ insertionFrames.pop();
569
+ continue;
570
+ }
571
+ const n = frame.nodes[frame.index];
572
+ frame.index += 1;
573
+ if (n.type === 'leaf' && n.path === path) {
574
+ // GUARANTEE ORIGINAL KEY: Fallback to a highly unique temp ID so React doesn't share state
575
+ // prettier-ignore
576
+ frame.nodes.splice(frame.index, 0, { type: 'leaf', fileType: 'mdx', origName: typeof doc.origName === 'string' ? doc.origName : `temp_l_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`, path: nLeafPath, permission: 'write', title: t, description: doc.description || '', url: nRoute, source: finalSrc });
577
+ break;
578
+ }
579
+ if (n.type === 'branch' && n.children)
580
+ insertionFrames.push({ nodes: n.children, index: 0 });
581
+ }
582
+ return nTree;
583
+ });
584
+ // 2. Navigate smoothly & retain parameterized context IDs
585
+ const tgtUrl = new URL(nRoute, window.location.origin);
586
+ new URL(window.location.href).searchParams.forEach((v, k) => {
587
+ if (k !== 'mode' && !tgtUrl.searchParams.has(k))
588
+ tgtUrl.searchParams.set(k, v);
589
+ });
590
+ tgtUrl.searchParams.set('mode', 'edit');
591
+ window.history.pushState(null, '', tgtUrl.toString());
592
+ const ePathname = `${tgtUrl.pathname}${tgtUrl.search}`;
593
+ // 3. Prime memoryDrafts to prevent <Main> from fetching undefined state and crashing React
594
+ memoryDrafts.set(ePathname, {
595
+ source: finalSrc,
596
+ draft: { content: Array.isArray(doc.content) ? doc.content : [], description: doc.description || '', path: nLeafPath, title: t },
597
+ metadata: doc,
598
+ });
599
+ $unsavedDrafts.setKey(ePathname, false);
600
+ // prettier-ignore
601
+ document.dispatchEvent(new CustomEvent('docs:navigate', { detail: { url: ePathname, pathname: ePathname, path: nLeafPath, title: t, description: doc.description || '', source: finalSrc, content: Array.isArray(doc.content) ? doc.content : [], compiledMDX: '' } }));
602
+ ctx.setInputDlg(null);
603
+ // 4. Silently fetch the real Sanity IDs in the background
604
+ setTimeout(() => {
605
+ void ctx.refreshTree();
606
+ }, 2500);
607
+ }, 'Failed to duplicate.'),
608
+ });
609
+ },
610
+ };
611
+ /**
612
+ * Returns the add.
613
+ *
614
+ * @param files - Value passed to the helper.
615
+ */
616
+ const add = async (files) => {
617
+ if (creating || !files || files.length === 0 || !node.path)
618
+ return;
619
+ runMutation(async () => {
620
+ let totalSize = 0;
621
+ let uploadedSize = 0;
622
+ for (let i = 0; i < files.length; i++)
623
+ if (files[i])
624
+ totalSize += files[i].size;
625
+ for (let i = 0; i < files.length; i++) {
626
+ const file = files[i];
627
+ if (!file)
628
+ continue;
629
+ const tmpId = `temp-${Date.now()}-${i}`;
630
+ const isMdFile = TerraQualityRegex.REGEX_MD_MDX_I_REGEX().test(file.name);
631
+ setSis(p => [...p, { id: tmpId, path: tmpId, name: file.name, isMd: isMdFile }]);
632
+ const finalAsset = await uploadAssetChunked(file, node.path, {
633
+ onProgress: ({ bytes }) => {
634
+ uploadedSize += bytes;
635
+ setProgress(Math.round((uploadedSize / totalSize) * 100));
636
+ },
637
+ });
638
+ const fPath = finalAsset.path || finalAsset.src || tmpId;
639
+ const nName = fPath.split('/').pop() || file.name;
640
+ setSis(p => {
641
+ if (p.some(s => s.name === nName && s.id !== tmpId))
642
+ return p.filter(s => s.id !== tmpId);
643
+ return p.map(s => (s.id === tmpId ? { ...s, path: fPath, name: nName, isMd: isMdFile } : s));
644
+ });
645
+ }
646
+ toast.success('Files attached successfully.');
647
+ }, 'Failed to upload docs file.', () => {
648
+ setProgress(0);
649
+ }, () => {
650
+ setProgress(null);
651
+ setContexting(false);
652
+ if (fileRef.current)
653
+ fileRef.current.value = '';
654
+ });
655
+ };
656
+ // SISTER ACTION HANDLERS
657
+ /**
658
+ * Rename sister.
659
+ *
660
+ * @param s - Value passed to the helper.
661
+ *
662
+ * @returns True when the helper predicate succeeds.
663
+ */
664
+ const sisterActions = {
665
+ renameSister: (s) => {
666
+ ctx.setInputDlg({
667
+ open: true,
668
+ title: 'Rename Attachment',
669
+ label: 'New name',
670
+ value: s.name.replace(TerraQualityRegex.REGEX_I_REGEX(), ''),
671
+ onCancel: () => { },
672
+ onSave: nName => runMutation(async () => {
673
+ const fileName = { extension: s.name.match(TerraQualityRegex.REGEX_I_REGEX())?.[0] ?? '', stem: sanitizeName(nName) };
674
+ const nFilename = `${fileName.stem}${fileName.extension}`;
675
+ const dir = dirname(s.path);
676
+ const nPath = `${dir && dir !== '.' ? `${dir}/` : ''}${nFilename}`;
677
+ // optimistic UI update
678
+ setSis(p => p.map(x => (x.path === s.path ? { ...x, path: nPath, name: nFilename } : x)));
679
+ ctx.setInputDlg(null);
680
+ try {
681
+ if (s.isMd) {
682
+ const { data: docData } = await actions.read.doc.get({ path: s.path });
683
+ const { error } = await actions.write.doc.commit({
684
+ cur: s.path,
685
+ next: nPath,
686
+ source: docData && typeof docData === 'object' && 'source' in docData && typeof docData.source === 'string' ? docData.source : '',
687
+ });
688
+ if (error)
689
+ throw error;
690
+ }
691
+ else {
692
+ const { data: buf, error: readErr } = await actions.read.asset.get({ path: s.path });
693
+ if (readErr || !buf)
694
+ throw readErr || new Error('Failed to read asset');
695
+ const { error: writeErr } = await actions.write.asset.raw({ bytes: buf, path: nPath });
696
+ if (writeErr)
697
+ throw writeErr;
698
+ await actions.write.doc.rm({ path: s.path, type: 'asset', assets: false });
699
+ }
700
+ toast.success('Renamed successfully');
701
+ }
702
+ catch (err) {
703
+ // revert on failure
704
+ setSis(p => p.map(x => (x.path === nPath ? { ...x, path: s.path, name: s.name } : x)));
705
+ throw err;
706
+ }
707
+ }, 'Failed to rename attachment.'),
708
+ });
709
+ },
710
+ /*
711
+ * Duplicates the duplicate Sister.
712
+ *
713
+ * @param s - Value passed to the helper.
714
+ *
715
+ * @returns True when the helper predicate succeeds.
716
+ */
717
+ duplicateSister: async (s) => {
718
+ ctx.setInputDlg({
719
+ open: true,
720
+ title: 'Duplicate Attachment',
721
+ label: 'New name',
722
+ value: `${s.name.replace(TerraQualityRegex.REGEX_I_REGEX(), '')}-copy`,
723
+ onCancel: () => { },
724
+ onSave: n => runMutation(async () => {
725
+ const tgt = { path: s.path, stem: sanitizeName(n) };
726
+ const { data: docData, error: err } = await actions.read.doc.get({ path: tgt.path });
727
+ if (err || !docData)
728
+ throw err || new Error('Failed to read doc.');
729
+ const nPath = `${dirname(tgt.path)}/${tgt.stem}.mdx`;
730
+ const { data, error: wErr } = await actions.write.doc.save({
731
+ path: nPath,
732
+ source: typeof docData === 'object' && 'source' in docData && typeof docData.source === 'string' ? docData.source : '',
733
+ });
734
+ if (wErr || !data)
735
+ throw wErr || new Error('Failed to write doc.');
736
+ const sisterDetails = {
737
+ name: nPath.split('/').pop() || '',
738
+ title: typeof data === 'object' && 'title' in data && typeof data.title === 'string' ? data.title : undefined,
739
+ };
740
+ setSis(p => [...p, { path: nPath, name: sisterDetails.name, isMd: true, title: sisterDetails.title }]);
741
+ ctx.setInputDlg(null);
742
+ }, 'Failed to duplicate.'),
743
+ });
744
+ },
745
+ /*
746
+ * Deletes the delete Sister.
747
+ *
748
+ * @param s - Value passed to the helper.
749
+ *
750
+ * @returns True when the helper predicate succeeds.
751
+ */
752
+ deleteSister: async (s) => {
753
+ if (!window.confirm(`Are you sure you want to delete ${s.name}?`))
754
+ return;
755
+ const deletion = {
756
+ previousSisters: [...sis],
757
+ remove: () => setSis(p => p.filter(x => x.path !== s.path)),
758
+ restore: () => setSis(deletion.previousSisters),
759
+ };
760
+ deletion.remove();
761
+ runMutation(async () => {
762
+ try {
763
+ const { error } = await actions.write.doc.rm({ assets: false, path: s.path, type: s.isMd ? 'doc' : 'asset' });
764
+ if (error)
765
+ throw error;
766
+ }
767
+ catch (err) {
768
+ deletion.restore();
769
+ throw err;
770
+ }
771
+ }, 'Failed to delete attachment.');
772
+ },
773
+ /*
774
+ * Synthesize sister.
775
+ *
776
+ * @param s - Value passed to the helper.
777
+ */
778
+ synthesizeSister: (s) => {
779
+ try {
780
+ document.dispatchEvent(new CustomEvent('docs:synthesize', { detail: { path: s.path, name: s.name } }));
781
+ }
782
+ catch (err) {
783
+ console.error('Synthesis failed', err);
784
+ toast.error(err instanceof Error ? err.message : 'Synthesis failed');
785
+ }
786
+ },
787
+ };
788
+ // RENDER
789
+ return (_jsxs(SidebarMenuItem, { ref: leafRef, children: [!!node.path && !isSimulated ? (_jsxs(ContextMenu, { onOpenChange: setContexting, children: [_jsx("input", { ref: fileRef, type: 'file', className: 'hidden', multiple: true, onChange: e => void add(e.currentTarget.files), disabled: disabled }), _jsx(ContextMenuTrigger, { asChild: true, children: _jsx("div", { "data-docs-drop": drop, "data-docs-parent": bPath, className: cn('relative rounded-md', dState?.key === drop && dState.position === 'before' && REORDER.before, dState?.key === drop && dState.position === 'after' && REORDER.after, (isMultiSel || isDragOverFile) && 'bg-primary-4/50 ring-2 ring-primary-8', progress !== null && 'pointer-events-none opacity-70'), onMouseEnter: () => {
790
+ ctx.setHovered(drop);
791
+ }, onMouseLeave: () => {
792
+ if (ctx.hovered === drop)
793
+ ctx.setHovered(null);
794
+ }, onClickCapture: e => {
795
+ if (e.metaKey || e.ctrlKey || e.shiftKey) {
796
+ e.preventDefault();
797
+ e.stopPropagation();
798
+ ctx.handleSelect(path, e.metaKey || e.ctrlKey, e.shiftKey);
799
+ }
800
+ else if (ctx.selectedLeaves.length > 0)
801
+ ctx.clearSelection();
802
+ }, onDragEnter: e => {
803
+ if (permission === 'write' && !drag && e.dataTransfer.types.includes('Files')) {
804
+ e.preventDefault();
805
+ e.stopPropagation();
806
+ setIsDragOverFile(true);
807
+ }
808
+ }, onDragOver: e => {
809
+ if (permission !== 'write')
810
+ return;
811
+ if (!drag && e.dataTransfer.types.includes('Files')) {
812
+ e.preventDefault();
813
+ e.stopPropagation();
814
+ const rect = e.currentTarget.getBoundingClientRect();
815
+ const y = e.clientY - rect.top;
816
+ if (y < rect.height * 0.25) {
817
+ if (isDragOverFile)
818
+ setIsDragOverFile(false);
819
+ if (dState?.key !== drop || dState.position !== 'before')
820
+ ctx.setDState({ key: drop, position: 'before' });
821
+ }
822
+ else if (y > rect.height * 0.75) {
823
+ if (isDragOverFile)
824
+ setIsDragOverFile(false);
825
+ if (dState?.key !== drop || dState.position !== 'after')
826
+ ctx.setDState({ key: drop, position: 'after' });
827
+ }
828
+ else {
829
+ if (!isDragOverFile)
830
+ setIsDragOverFile(true);
831
+ if (dState?.key === drop)
832
+ ctx.setDState(null);
833
+ }
834
+ return;
835
+ }
836
+ if (!drag || drag.kind !== 'leaf')
837
+ return;
838
+ e.preventDefault();
839
+ e.stopPropagation();
840
+ if (isDragOverFile)
841
+ setIsDragOverFile(false);
842
+ const rect = e.currentTarget.getBoundingClientRect();
843
+ const pos = e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
844
+ if (dState?.key !== drop || dState.position !== pos)
845
+ ctx.setDState({ key: drop, position: pos });
846
+ }, onDragLeave: e => {
847
+ if (isDragOverFile)
848
+ setIsDragOverFile(false);
849
+ if (e.currentTarget.contains(e.relatedTarget))
850
+ return;
851
+ if (dState?.key === drop)
852
+ ctx.setDState(null);
853
+ }, onDrop: e => {
854
+ e.preventDefault();
855
+ e.stopPropagation();
856
+ if (e.dataTransfer.files?.length && !drag) {
857
+ if (isDragOverFile) {
858
+ setIsDragOverFile(false);
859
+ void add(e.dataTransfer.files);
860
+ }
861
+ else if (dState?.key === drop) {
862
+ ctx.setDState(null);
863
+ void ctx.uploadAsLeaf(e.dataTransfer.files, node.path, dState.position === 'inside' ? 'after' : dState.position, 'file');
864
+ }
865
+ return;
866
+ }
867
+ const dPath = e.dataTransfer.getData('application/docs-path');
868
+ const move = drag ?? (e.dataTransfer.getData('application/docs-kind') === 'leaf' && dPath ? { kind: 'leaf', path: dPath } : null);
869
+ if (!move || move.kind !== 'leaf' || !node.path)
870
+ return;
871
+ const rect = e.currentTarget.getBoundingClientRect();
872
+ void ctx.mvLeaf({ path: move.path, tPosition: e.clientY < rect.top + rect.height / 2 ? 'before' : 'after', tTarget: node.path, tpath: bPath });
873
+ ctx.setDrag(null);
874
+ }, children: _jsx(Link, { active: a, contexting: contexting || dState?.key === drop, dragging: drag?.kind === 'leaf', renaming: renaming, node: node, mode: mode, trailing: _jsxs("div", { className: 'flex items-center gap-2', children: [progress !== null && _jsxs("span", { className: 'text-xs font-medium text-primary-10', children: [progress, "%"] }), _jsx(DragHandle, { disabled: disabled, isVisible: hovered === drop, onDragStart: e => {
875
+ ctx.setDrag({ active: a, kind: 'leaf', path });
876
+ e.dataTransfer.effectAllowed = 'move';
877
+ e.dataTransfer.setData('application/docs-kind', 'leaf');
878
+ e.dataTransfer.setData('application/docs-path', path);
879
+ e.dataTransfer.setData('application/docs-parent', bPath);
880
+ e.dataTransfer.setData('text/plain', path);
881
+ }, onDragEnd: () => {
882
+ ctx.setDrag(null);
883
+ } })] }) }) }) }), _jsx(ContextMenuContent, { children: permission !== 'write' ? (_jsx(Menu, { actions: [{ icon: _jsx(LinkIcon, {}), label: 'Copy link', onSelect: () => { void navigator.clipboard.writeText(new URL(node.url || docsPath(path), window.location.origin).href); toast.success('Link copied'); } }] })) : isMultiSel && ctx.selectedLeaves.length > 1 ? (_jsx(Menu, { actions: [
884
+ {
885
+ disabled: disabled,
886
+ icon: _jsx(DocumentIcon, {}),
887
+ label: `Merge ${ctx.selectedLeaves.length} pages`,
888
+ onSelect: () => {
889
+ ctx.mergeLeaves(ctx.selectedLeaves);
890
+ },
891
+ },
892
+ {
893
+ icon: _jsx(TrashIcon, {}),
894
+ label: 'Clear selection',
895
+ onSelect: () => {
896
+ ctx.clearSelection();
897
+ },
898
+ },
899
+ ] })) : (_jsx(Menu
900
+ // prettier-ignore
901
+ , {
902
+ // prettier-ignore
903
+ actions: [
904
+ { disabled: disabled, icon: _jsx(LinkIcon, {}), label: 'Copy link', onSelect: () => { void navigator.clipboard.writeText(`${window.location.origin}${node.url || ''}`); toast.success('Link copied'); } },
905
+ { disabled: disabled, icon: _jsx(EditIcon, {}), label: 'Rename page', onSelect: pageActions.renamePage },
906
+ {
907
+ disabled: disabled,
908
+ icon: _jsx(BoxIcon, {}),
909
+ label: 'Files',
910
+ subActions: sisLoading
911
+ ? [{ label: 'Loading...', disabled: true }]
912
+ : sis.length === 0
913
+ ? [{ label: 'No files', disabled: true }, { separator: true, label: 'sep' }, { icon: _jsx(AddIcon, {}), label: 'New', onSelect: () => fileRef.current?.click() },]
914
+ : [
915
+ ...sis.map(s => ({
916
+ icon: s.isMd ? _jsx(DocumentIcon, {}) : _jsx(LinkIcon, {}),
917
+ label: cTitle(toNodeLabel(s.title || s.name)) || s.name,
918
+ subActions: [
919
+ {
920
+ icon: _jsx(DownloadIcon, {}),
921
+ label: s.isMd ? 'Download' : 'Open',
922
+ onSelect: () => {
923
+ if (s.isMd) {
924
+ void actions.read.doc
925
+ .get({ path: s.path })
926
+ .then(({ data: d }) => {
927
+ download(s.name, d && typeof d === 'object' && 'source' in d && typeof d.source === 'string' ? d.source : '', 'text/markdown');
928
+ })
929
+ .catch(() => { notify({ error: new Error('Failed to download file.'), fallback: 'Failed to download file.' }); });
930
+ }
931
+ else {
932
+ const a = document.createElement('a');
933
+ a.href = docsPath(s.path);
934
+ let dlName = s.name;
935
+ const extMatch = s.path.split('?')[0]?.match(TerraQualityRegex.REGEX_A_Z0_9_I_REGEX());
936
+ if (extMatch && !dlName.toLowerCase().endsWith(extMatch[0].toLowerCase()))
937
+ dlName = `${dlName}${extMatch[0]}`;
938
+ a.download = dlName;
939
+ a.target = '_blank';
940
+ document.body.appendChild(a);
941
+ a.click();
942
+ document.body.removeChild(a);
943
+ }
944
+ },
945
+ },
946
+ { icon: _jsx(EditIcon, {}), label: 'Rename', onSelect: () => { sisterActions.renameSister(s); } },
947
+ ...(s.isMd ? [{ icon: _jsx(MagicIcon, {}), label: 'Synthesize Content', onSelect: () => { sisterActions.synthesizeSister(s); } }, { icon: _jsx(CopyIcon, {}), label: 'Duplicate', onSelect: () => sisterActions.duplicateSister(s) },] : []),
948
+ { separator: true, destructive: true, icon: _jsx(TrashIcon, {}), label: 'Delete', onSelect: () => sisterActions.deleteSister(s) },
949
+ ],
950
+ })),
951
+ { separator: sis.length > 0, icon: _jsx(AddIcon, {}), label: 'New', onSelect: () => fileRef.current?.click() },
952
+ ],
953
+ },
954
+ {
955
+ disabled: disabled,
956
+ icon: _jsx(CopyIcon, {}),
957
+ label: 'Duplicate page',
958
+ subActions: [
959
+ { icon: _jsx(LinkIcon, {}), label: 'By reference (Point to same leaf)', onSelect: () => { pageActions.duplicate('reference'); } },
960
+ { icon: _jsx(CopyIcon, {}), label: 'By content (Duplicate leaf)', onSelect: () => { pageActions.duplicate('content'); } }
961
+ ]
962
+ },
963
+ { icon: _jsx(PopOutIcon, {}), label: 'Make asset', disabled: disabled || !(permission === 'write' && !node.asset && !isSimulated && !!path), onSelect: () => { ctx.setAssetTarget({ path, title: cTitle(toNodeLabel(node.title || node.origName)) || bName(path) }); } },
964
+ { icon: _jsx(DownloadIcon, {}), label: 'Download page', disabled: disabled, onSelect: () => { ctx.setExportTarget({ paths: [path], rootDir: path, filename: sanitizeName(node.title || bName(path)), title: cTitle(toNodeLabel(node.title || node.origName)) || 'Document' }); } },
965
+ { destructive: true, disabled: disabled, icon: _jsx(TrashIcon, {}), label: 'Delete page', onSelect: () => { ctx.setDelTarget({ kind: 'leaf', path: node.path, bPath, active: a, title: node.title || node.path }); } },
966
+ ] })) })] })) : (_jsx(Link, { active: a, node: node, mode: mode })), a && _jsx(TREE_COMPONENTS.Toc, { title: node.title })] }));
967
+ },
968
+ /*//////////////////////////////////////////////////
969
+ TREE COMPONENTS
970
+ //////////////////////////////////////////////////*/
971
+ /**
972
+ * Main recursive tree mapping structural layers evaluating context dependencies natively.
973
+ *
974
+ * @param props - Tree path, current node, sibling list, and shared navigation/mutation state for recursively rendering sidebar levels.
975
+ * @param {TreeType[]} [props.siblings] - Sibling nodes within the same local directory layer.
976
+ * @param bPath - B path input supplied to tree's operation.
977
+ * @param node - Node input supplied to tree's operation.
978
+ * @param ctx - Ctx input supplied to tree's operation.
979
+ *
980
+ * @return {JSX.Element} The rendered SidebarGroup bounding nested leaves and child trees.
981
+ */
982
+ Tree({ bPath, node, ctx, siblings: sibs, }) {
983
+ // STATES
984
+ const { creating, drag, dState, pathname, shellPermission, setDState, setHovered, hovered } = ctx;
985
+ const nodeUrl = 'url' in node ? node.url : '';
986
+ const permission = shellPermission === 'write' ? 'write' : node.permission;
987
+ const fPath = [bPath, node.type === 'branch' ? node.origName : ''].filter(Boolean).join('/');
988
+ const slugPath = 'url' in node && node.url ? node.url.replace(TerraQualityRegex.REGEX_DOCS_REGEX(), '') : '';
989
+ const [renaming, setRenaming] = useState(false);
990
+ const [contexting, setContexting] = useState(false);
991
+ const drop = node.type === 'branch' ? `branch:${fPath}` : `leaf:${node.path ?? ''}`;
992
+ const disabled = creating || renaming;
993
+ // HANDLERS
994
+ /**
995
+ * Returns the rename node.
996
+ *
997
+ */
998
+ const treeActions = {
999
+ renameNode: () => {
1000
+ if (renaming || node.type !== 'branch')
1001
+ return;
1002
+ const currName = node.origName;
1003
+ const currTitle = cTitle(toNodeLabel(node.title || currName)) || currName;
1004
+ setRenaming(true);
1005
+ ctx.setInputDlg({
1006
+ open: true,
1007
+ title: 'Rename Group',
1008
+ label: 'Group name',
1009
+ value: currTitle,
1010
+ onCancel: () => {
1011
+ setRenaming(false);
1012
+ },
1013
+ onSave: async (nName) => {
1014
+ if (!nName || nName === currTitle) {
1015
+ ctx.setInputDlg(null);
1016
+ setRenaming(false);
1017
+ return;
1018
+ }
1019
+ // optimistic UI mutation bypasses global loading sweep
1020
+ ctx.setTree(c => {
1021
+ const updatedTree = [...c];
1022
+ const titleFrames = [{ nodes: updatedTree, index: 0 }];
1023
+ while (titleFrames.length) {
1024
+ const frame = titleFrames[titleFrames.length - 1];
1025
+ if (!frame)
1026
+ break;
1027
+ if (frame.index >= frame.nodes.length) {
1028
+ titleFrames.pop();
1029
+ continue;
1030
+ }
1031
+ const idx = frame.index;
1032
+ const curr = frame.nodes[idx];
1033
+ frame.index += 1;
1034
+ if (curr.type !== 'branch')
1035
+ continue;
1036
+ if (curr.origName === node.origName) {
1037
+ frame.nodes[idx] = { ...curr, title: nName };
1038
+ continue;
1039
+ }
1040
+ if (curr.children) {
1041
+ const children = [...curr.children];
1042
+ frame.nodes[idx] = { ...curr, children };
1043
+ titleFrames.push({ nodes: children, index: 0 });
1044
+ }
1045
+ }
1046
+ return updatedTree;
1047
+ });
1048
+ ctx.setInputDlg(null);
1049
+ try {
1050
+ const { error: err } = await actions.write.doc.editor({ path: node.origName, title: nName });
1051
+ if (err)
1052
+ throw err;
1053
+ toast.success('Group renamed successfully.');
1054
+ }
1055
+ catch (err) {
1056
+ console.error('[renameNode] Error renaming group:', err);
1057
+ toast.error('Failed to rename group. The document has not been changed.');
1058
+ void ctx.refreshTree();
1059
+ }
1060
+ finally {
1061
+ setRenaming(false);
1062
+ }
1063
+ },
1064
+ });
1065
+ },
1066
+ /*
1067
+ * Handles drag over.
1068
+ *
1069
+ * @param e - Value passed to the helper.
1070
+ */
1071
+ handleDragOver: (e) => {
1072
+ if (permission !== 'write')
1073
+ return;
1074
+ if (!drag && e.dataTransfer.types.includes('Files')) {
1075
+ e.preventDefault();
1076
+ e.stopPropagation();
1077
+ const rect = e.currentTarget.getBoundingClientRect();
1078
+ const pos = e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
1079
+ if (dState?.key !== drop || dState.position !== pos)
1080
+ setDState({ key: drop, position: pos });
1081
+ return;
1082
+ }
1083
+ if (!drag)
1084
+ return;
1085
+ if (node.type === 'leaf' && drag.kind !== 'leaf')
1086
+ return;
1087
+ if (node.type === 'branch' && drag.kind === 'branch' && drag.pPath !== bPath)
1088
+ return;
1089
+ e.preventDefault();
1090
+ e.stopPropagation();
1091
+ const rect = e.currentTarget.getBoundingClientRect();
1092
+ const pos = e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
1093
+ if (dState?.key !== drop || dState.position !== pos)
1094
+ setDState({ key: drop, position: pos });
1095
+ },
1096
+ /*
1097
+ * Handles drag leave.
1098
+ *
1099
+ * @param e - Value passed to the helper.
1100
+ */
1101
+ handleDragLeave: (e) => {
1102
+ if (e.currentTarget.contains(e.relatedTarget))
1103
+ return;
1104
+ if (dState?.key === drop)
1105
+ setDState(null);
1106
+ },
1107
+ };
1108
+ // RENDER
1109
+ // leaf
1110
+ if (node.type === 'leaf')
1111
+ return (_jsx(ErrorBoundary, { name: 'Tree', children: _jsx(TREE_COMPONENTS.Leaf, { active: pathname !== '' && pathname === nodeUrl, bPath: bPath, node: node, ctx: ctx, siblings: sibs }) }));
1112
+ // branch
1113
+ const branchActions = [
1114
+ { disabled, icon: _jsx(LinkIcon, {}), label: 'Copy link', onSelect: () => { void navigator.clipboard.writeText(`${window.location.origin}${nodeUrl}`); toast.success('Link copied'); } },
1115
+ { disabled, icon: _jsx(AddIcon, {}), label: 'Add page', onSelect: () => { ctx.openCreate(fPath, node.origName); } },
1116
+ { disabled, icon: _jsx(BoxIcon, {}), label: 'Manage attachments', onSelect: () => { ctx.setManageAttTgt({ path: slugPath, title: node.title || slugPath, scope: 'group', node }); } },
1117
+ { disabled, icon: _jsx(DownloadIcon, {}), label: 'Download group', onSelect: () => { ctx.setExportTarget({ paths: toPaths(node.children), rootDir: slugPath, filename: sanitizeName(cTitle(toNodeLabel(node.title || node.origName)) || node.origName), title: `${cTitle(toNodeLabel(node.title || node.origName))} docs`, nodeIds: extractAllNodeIds([node]) }); } },
1118
+ { disabled, icon: _jsx(EditIcon, {}), label: 'Rename group', onSelect: treeActions.renameNode },
1119
+ { separator: true, destructive: true, disabled, icon: _jsx(TrashIcon, {}), label: 'Delete group', onSelect: () => { ctx.setDelTarget({ kind: 'branch', path: fPath, bPath, slugPath, title: node.title || slugPath }); } },
1120
+ ];
1121
+ const label = (_jsx(SidebarGroupLabel, { asChild: true, className: 'p-0', children: _jsxs("div", { "data-docs-drop": drop, className: cn('group flex w-full items-center gap-1 rounded-md px-2 py-1 text-left text-primary-11', dState?.key === drop && dState.position === 'before' && REORDER.before, dState?.key === drop && dState.position === 'after' && REORDER.after, contexting && UI.open, renaming && 'ring-1 ring-ring'), onMouseEnter: () => {
1122
+ setHovered(drop);
1123
+ }, onMouseLeave: () => {
1124
+ if (hovered === drop)
1125
+ setHovered(null);
1126
+ }, onDragOver: treeActions.handleDragOver, onDragLeave: treeActions.handleDragLeave, onDrop: e => {
1127
+ e.preventDefault();
1128
+ e.stopPropagation();
1129
+ if (e.dataTransfer.files?.length && !drag) {
1130
+ if (dState?.key === drop) {
1131
+ ctx.setDState(null);
1132
+ void ctx.uploadAsLeaf(e.dataTransfer.files, slugPath, dState.position === 'inside' ? 'after' : dState.position, 'folder');
1133
+ }
1134
+ return;
1135
+ }
1136
+ const dPath = e.dataTransfer.getData('application/docs-path');
1137
+ const dParent = e.dataTransfer.getData('application/docs-parent');
1138
+ const move = drag ??
1139
+ (e.dataTransfer.getData('application/docs-kind') === 'leaf' && dPath
1140
+ ? { kind: 'leaf', path: dPath }
1141
+ : dPath && dParent
1142
+ ? { fPath: dPath, kind: 'branch', pPath: dParent }
1143
+ : null);
1144
+ if (!move)
1145
+ return;
1146
+ const rect = e.currentTarget.getBoundingClientRect();
1147
+ const tPos = e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
1148
+ if (move.kind === 'leaf')
1149
+ void ctx.mvLeaf({ path: move.path, tPosition: tPos, tTarget: fPath, tpath: bPath });
1150
+ else
1151
+ void ctx.mvBranch({ fPath: move.fPath, pPath: move.pPath, tpath: bPath, tPosition: tPos, tTarget: fPath });
1152
+ ctx.setDrag(null);
1153
+ }, children: [_jsx("span", { className: 'min-w-0 flex-1 truncate', children: cTitle(toNodeLabel(node.title || node.origName)) }), permission === 'write' && !node.simulated && (_jsx(DragHandle, { disabled: disabled, isVisible: hovered === drop, onDragStart: e => {
1154
+ ctx.setDrag({ fPath, kind: 'branch', pPath: bPath });
1155
+ e.dataTransfer.effectAllowed = 'move';
1156
+ e.dataTransfer.setData('application/docs-kind', 'branch');
1157
+ e.dataTransfer.setData('application/docs-path', fPath);
1158
+ e.dataTransfer.setData('application/docs-parent', bPath);
1159
+ e.dataTransfer.setData('text/plain', fPath);
1160
+ }, onDragEnd: () => {
1161
+ ctx.setDrag(null);
1162
+ } })), permission === 'write' && !node.simulated && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: 'ghost', size: 'xs', "aria-label": `Actions for group ${cTitle(toNodeLabel(node.title || node.origName))}`, className: 'h-6 w-6 shrink-0 px-1 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100', onClick: event => event.stopPropagation(), children: _jsx(OverflowMenuIcon, { size: 16 }) }) }), _jsx(DropdownMenuContent, { align: 'end', children: branchActions.map((action, index) => (_jsxs(Fragment, { children: [action.separator && _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { disabled: action.disabled, onSelect: () => action.onSelect?.(), className: action.destructive ? 'text-destructive focus:text-destructive' : undefined, children: [action.icon, action.label] })] }, `${action.label}-${index}`))) })] }))] }) }));
1163
+ return (_jsx(ErrorBoundary, { name: 'Tree', children: _jsxs(SidebarGroup, { className: cn('p-0', !bPath.includes('/') && 'mt-4 first:mt-0'), children: [permission === 'write' && !node.simulated ? (_jsxs(ContextMenu, { onOpenChange: setContexting, children: [_jsx(ContextMenuTrigger, { asChild: true, children: label }), _jsx(ContextMenuContent, { children: _jsx(Menu, { actions: branchActions }) })] })) : (label), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: node.type === 'branch' && node.children?.map(c => _jsx(TREE_COMPONENTS.Tree, { bPath: fPath, node: c, ctx: ctx, siblings: node.children }, getStableKey(c))) }) })] }) }));
1164
+ },
1165
+ };
1166
+ export const { Tree } = TREE_COMPONENTS;
1167
+ export default TREE_COMPONENTS.Tree;
1168
+ //# sourceMappingURL=_tree.js.map