@ecsplico/editor 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/adapters/direct-ai.d.ts +36 -0
  3. package/dist/adapters/direct-ai.js +169 -0
  4. package/dist/adapters/direct-ai.js.map +1 -0
  5. package/dist/adapters/memory.js.map +1 -1
  6. package/dist/code/CodeSurface.vue.d.ts +2 -2
  7. package/dist/core/block-drag.js +3 -0
  8. package/dist/core/block-drag.js.map +1 -1
  9. package/dist/core/block-transfer.d.ts +44 -0
  10. package/dist/core/block-transfer.js +68 -0
  11. package/dist/core/block-transfer.js.map +1 -0
  12. package/dist/core/commands.js +5 -0
  13. package/dist/core/commands.js.map +1 -1
  14. package/dist/core/core-feature.d.ts +4 -1
  15. package/dist/core/core-feature.js +8 -3
  16. package/dist/core/core-feature.js.map +1 -1
  17. package/dist/core/editor-ui.d.ts +3 -1
  18. package/dist/core/editor-ui.js.map +1 -1
  19. package/dist/core/extension-points.d.ts +17 -1
  20. package/dist/core/feature.d.ts +6 -1
  21. package/dist/core/feature.js.map +1 -1
  22. package/dist/core/index.d.ts +4 -2
  23. package/dist/core/index.js +3 -2
  24. package/dist/core/schema/index.d.ts +24 -3
  25. package/dist/core/schema/index.js +21 -4
  26. package/dist/core/schema/index.js.map +1 -1
  27. package/dist/core/surface-session.js +3 -1
  28. package/dist/core/surface-session.js.map +1 -1
  29. package/dist/core/toolbar-items.js +1 -1
  30. package/dist/core/toolbar-items.js.map +1 -1
  31. package/dist/document/DocumentSurface.vue.d.ts +2 -2
  32. package/dist/features/ai/agent-op-review.js +1 -1
  33. package/dist/features/ai/agent-ops.js +2 -2
  34. package/dist/features/ai/ai-apply.js +1 -1
  35. package/dist/features/ai/assistant.js +1 -1
  36. package/dist/features/ai/components/AiSelectionMenu.js +2 -2
  37. package/dist/features/ai/components/AiSidebar.js +2 -2
  38. package/dist/features/ai/components/SettingsAiTab.js +1 -1
  39. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  40. package/dist/features/ai/document-chat-model.js +1 -1
  41. package/dist/features/ai/document-chat.js +2 -2
  42. package/dist/features/ai/ghost-autocomplete.js +1 -1
  43. package/dist/features/ai/index.d.ts +4 -3
  44. package/dist/features/ai/index.js +6 -5
  45. package/dist/features/ai/protocol/complete.d.ts +19 -0
  46. package/dist/features/ai/protocol/complete.js +27 -1
  47. package/dist/features/ai/protocol/complete.js.map +1 -1
  48. package/dist/features/ai/protocol/generate.d.ts +95 -0
  49. package/dist/features/ai/protocol/generate.js +58 -0
  50. package/dist/features/ai/protocol/generate.js.map +1 -0
  51. package/dist/features/ai/protocol/index.d.ts +4 -3
  52. package/dist/features/ai/protocol/index.js +6 -5
  53. package/dist/features/ai/protocol/tasks.d.ts +3 -1
  54. package/dist/features/ai/protocol/tasks.js +74 -2
  55. package/dist/features/ai/protocol/tasks.js.map +1 -1
  56. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  57. package/dist/features/ai/react/AiSidebar.js +2 -2
  58. package/dist/features/ai/react/SettingsAiTab.js +1 -1
  59. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  60. package/dist/features/ai/selection-flow.js +1 -1
  61. package/dist/features/ai/sidebar-flow.js +1 -1
  62. package/dist/features/block-ui/block-actions.d.ts +19 -3
  63. package/dist/features/block-ui/block-actions.js +49 -3
  64. package/dist/features/block-ui/block-actions.js.map +1 -1
  65. package/dist/features/block-ui/components/BlockActionsOverlay.js +24 -13
  66. package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -1
  67. package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +12 -5
  68. package/dist/features/block-ui/components/BlockUi.js +52 -20
  69. package/dist/features/block-ui/components/BlockUi.js.map +1 -1
  70. package/dist/features/block-ui/components/BlockUi.vue.d.ts +6 -4
  71. package/dist/features/block-ui/feature.d.ts +8 -2
  72. package/dist/features/block-ui/feature.js +7 -2
  73. package/dist/features/block-ui/feature.js.map +1 -1
  74. package/dist/features/block-ui/index.d.ts +3 -2
  75. package/dist/features/block-ui/index.js +2 -2
  76. package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +14 -7
  77. package/dist/features/block-ui/react/BlockActionsOverlay.js +25 -13
  78. package/dist/features/block-ui/react/BlockActionsOverlay.js.map +1 -1
  79. package/dist/features/block-ui/react/BlockUi.d.ts +6 -4
  80. package/dist/features/block-ui/react/BlockUi.js +43 -15
  81. package/dist/features/block-ui/react/BlockUi.js.map +1 -1
  82. package/dist/features/block-ui/react.d.ts +4 -2
  83. package/dist/features/block-ui/react.js +3 -3
  84. package/dist/features/block-ui/react.js.map +1 -1
  85. package/dist/features/block-ui/vue.d.ts +4 -2
  86. package/dist/features/block-ui/vue.js +3 -3
  87. package/dist/features/block-ui/vue.js.map +1 -1
  88. package/dist/features/composites/baskets.d.ts +27 -0
  89. package/dist/features/composites/baskets.js +46 -0
  90. package/dist/features/composites/baskets.js.map +1 -0
  91. package/dist/features/composites/components/CompositeBasketMenu.js +97 -0
  92. package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -0
  93. package/dist/features/composites/components/CompositeChrome.js +79 -23
  94. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  95. package/dist/features/composites/components/CompositeFieldEditor.js +1 -0
  96. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  97. package/dist/features/composites/components/CompositeView.js +17 -2
  98. package/dist/features/composites/components/CompositeView.js.map +1 -1
  99. package/dist/features/composites/components/DerivationBadge.js +12 -3
  100. package/dist/features/composites/components/DerivationBadge.js.map +1 -1
  101. package/dist/features/composites/components/FocusChip.js +53 -0
  102. package/dist/features/composites/components/FocusChip.js.map +1 -0
  103. package/dist/features/composites/components/FocusChip.vue.d.ts +14 -0
  104. package/dist/features/composites/components/SettingsEditingTab.js +40 -4
  105. package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
  106. package/dist/features/composites/derivation.d.ts +2 -1
  107. package/dist/features/composites/derivation.js +5 -1
  108. package/dist/features/composites/derivation.js.map +1 -1
  109. package/dist/features/composites/derived-chrome.js +13 -3
  110. package/dist/features/composites/derived-chrome.js.map +1 -1
  111. package/dist/features/composites/feature.d.ts +7 -1
  112. package/dist/features/composites/feature.js +96 -23
  113. package/dist/features/composites/feature.js.map +1 -1
  114. package/dist/features/composites/focus.d.ts +38 -0
  115. package/dist/features/composites/focus.js +86 -0
  116. package/dist/features/composites/focus.js.map +1 -0
  117. package/dist/features/composites/index.d.ts +6 -3
  118. package/dist/features/composites/index.js +6 -3
  119. package/dist/features/composites/meta-keys.d.ts +4 -0
  120. package/dist/features/composites/meta-keys.js +8 -4
  121. package/dist/features/composites/meta-keys.js.map +1 -1
  122. package/dist/features/composites/nodes.d.ts +14 -2
  123. package/dist/features/composites/nodes.js +61 -5
  124. package/dist/features/composites/nodes.js.map +1 -1
  125. package/dist/features/composites/outline.d.ts +4 -0
  126. package/dist/features/composites/outline.js +3 -0
  127. package/dist/features/composites/outline.js.map +1 -1
  128. package/dist/features/composites/react/CompositeBasketMenu.js +88 -0
  129. package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -0
  130. package/dist/features/composites/react/CompositeChrome.js +52 -4
  131. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  132. package/dist/features/composites/react/CompositeView.js +12 -2
  133. package/dist/features/composites/react/CompositeView.js.map +1 -1
  134. package/dist/features/composites/react/DerivationBadge.js +10 -1
  135. package/dist/features/composites/react/DerivationBadge.js.map +1 -1
  136. package/dist/features/composites/react/FocusChip.d.ts +11 -0
  137. package/dist/features/composites/react/FocusChip.js +50 -0
  138. package/dist/features/composites/react/FocusChip.js.map +1 -0
  139. package/dist/features/composites/react/SettingsEditingTab.js +36 -1
  140. package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
  141. package/dist/features/composites/react.d.ts +2 -1
  142. package/dist/features/composites/react.js +3 -1
  143. package/dist/features/composites/react.js.map +1 -1
  144. package/dist/features/composites/schema.d.ts +5 -3
  145. package/dist/features/composites/schema.js +8 -4
  146. package/dist/features/composites/schema.js.map +1 -1
  147. package/dist/features/composites/split.d.ts +62 -0
  148. package/dist/features/composites/split.js +172 -0
  149. package/dist/features/composites/split.js.map +1 -0
  150. package/dist/features/composites/vue.d.ts +4 -3
  151. package/dist/features/composites/vue.js +7 -5
  152. package/dist/features/composites/vue.js.map +1 -1
  153. package/dist/features/media/index.js +1 -1
  154. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  155. package/dist/features/media/react.js +1 -1
  156. package/dist/features/transfer/components/TransferMenu.js +93 -0
  157. package/dist/features/transfer/components/TransferMenu.js.map +1 -0
  158. package/dist/features/transfer/components/TransferMenu.vue.d.ts +15 -0
  159. package/dist/features/transfer/feature.d.ts +76 -0
  160. package/dist/features/transfer/feature.js +169 -0
  161. package/dist/features/transfer/feature.js.map +1 -0
  162. package/dist/features/transfer/index.d.ts +3 -0
  163. package/dist/features/transfer/index.js +3 -0
  164. package/dist/features/transfer/react/TransferMenu.d.ts +12 -0
  165. package/dist/features/transfer/react/TransferMenu.js +71 -0
  166. package/dist/features/transfer/react/TransferMenu.js.map +1 -0
  167. package/dist/features/transfer/react.d.ts +10 -0
  168. package/dist/features/transfer/react.js +11 -0
  169. package/dist/features/transfer/react.js.map +1 -0
  170. package/dist/features/transfer/transforms.d.ts +83 -0
  171. package/dist/features/transfer/transforms.js +137 -0
  172. package/dist/features/transfer/transforms.js.map +1 -0
  173. package/dist/features/transfer/vue.d.ts +9 -0
  174. package/dist/features/transfer/vue.js +11 -0
  175. package/dist/features/transfer/vue.js.map +1 -0
  176. package/dist/i18n/de/ai.js +4 -0
  177. package/dist/i18n/de/ai.js.map +1 -1
  178. package/dist/i18n/de/composites.js +20 -1
  179. package/dist/i18n/de/composites.js.map +1 -1
  180. package/dist/i18n/de/integrations.js +16 -0
  181. package/dist/i18n/de/integrations.js.map +1 -1
  182. package/dist/i18n/de/transfer.js +17 -0
  183. package/dist/i18n/de/transfer.js.map +1 -0
  184. package/dist/i18n/de/tree.js +6 -0
  185. package/dist/i18n/de/tree.js.map +1 -1
  186. package/dist/i18n/de.d.ts +55 -0
  187. package/dist/i18n/de.js +3 -1
  188. package/dist/i18n/de.js.map +1 -1
  189. package/dist/i18n/en/ai.js +4 -0
  190. package/dist/i18n/en/ai.js.map +1 -1
  191. package/dist/i18n/en/composites.js +20 -1
  192. package/dist/i18n/en/composites.js.map +1 -1
  193. package/dist/i18n/en/integrations.js +16 -0
  194. package/dist/i18n/en/integrations.js.map +1 -1
  195. package/dist/i18n/en/transfer.js +17 -0
  196. package/dist/i18n/en/transfer.js.map +1 -0
  197. package/dist/i18n/en/tree.js +6 -0
  198. package/dist/i18n/en/tree.js.map +1 -1
  199. package/dist/i18n/en.js +3 -1
  200. package/dist/i18n/en.js.map +1 -1
  201. package/dist/integrations/composite-ai/composite-fill.js +1 -1
  202. package/dist/integrations/composite-ai/composite-summary.js +1 -1
  203. package/dist/integrations/composite-ai/derive.d.ts +21 -0
  204. package/dist/integrations/composite-ai/derive.js +69 -47
  205. package/dist/integrations/composite-ai/derive.js.map +1 -1
  206. package/dist/integrations/composite-ai/index.d.ts +8 -3
  207. package/dist/integrations/composite-ai/index.js +50 -4
  208. package/dist/integrations/composite-ai/index.js.map +1 -1
  209. package/dist/integrations/composite-ai/outline-sanitize.d.ts +48 -0
  210. package/dist/integrations/composite-ai/outline-sanitize.js +175 -0
  211. package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -0
  212. package/dist/integrations/composite-ai/split.d.ts +44 -0
  213. package/dist/integrations/composite-ai/split.js +187 -0
  214. package/dist/integrations/composite-ai/split.js.map +1 -0
  215. package/dist/integrations/composite-ai/transfer-transforms.d.ts +27 -0
  216. package/dist/integrations/composite-ai/transfer-transforms.js +153 -0
  217. package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -0
  218. package/dist/integrations/transcript-ai/protocol.d.ts +13 -0
  219. package/dist/integrations/transcript-ai/protocol.js +49 -1
  220. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  221. package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
  222. package/dist/markdown/MarkdownSurface.vue.d.ts +6 -6
  223. package/dist/nuxt/module.d.ts +110 -0
  224. package/dist/presets/compose.d.ts +24 -8
  225. package/dist/presets/compose.js +117 -58
  226. package/dist/presets/compose.js.map +1 -1
  227. package/dist/react/compose.js +6 -2
  228. package/dist/react/compose.js.map +1 -1
  229. package/dist/react/composed-surface.d.ts +9 -0
  230. package/dist/react/composed-surface.js +4 -1
  231. package/dist/react/composed-surface.js.map +1 -1
  232. package/dist/server/ai.d.ts +2 -5
  233. package/dist/server/ai.js.map +1 -1
  234. package/dist/server/handlers/ai.d.ts +7 -78
  235. package/dist/server/handlers/ai.js +30 -94
  236. package/dist/server/handlers/ai.js.map +1 -1
  237. package/dist/server/handlers/editor.d.ts +1 -0
  238. package/dist/server/handlers/index.d.ts +3 -1
  239. package/dist/server/handlers/index.js +3 -1
  240. package/dist/server/index.d.ts +2 -1
  241. package/dist/settings/index.d.ts +8 -0
  242. package/dist/settings/index.js +16 -4
  243. package/dist/settings/index.js.map +1 -1
  244. package/dist/styles/composites.css +32 -0
  245. package/dist/tree/DocumentTree.js +229 -78
  246. package/dist/tree/DocumentTree.js.map +1 -1
  247. package/dist/tree/drop-rules.js +8 -0
  248. package/dist/tree/drop-rules.js.map +1 -1
  249. package/dist/tree/react/DocumentTree.js +152 -31
  250. package/dist/tree/react/DocumentTree.js.map +1 -1
  251. package/dist/tree/tree-host.js +30 -1
  252. package/dist/tree/tree-host.js.map +1 -1
  253. package/dist/tree/tree-items.js +52 -5
  254. package/dist/tree/tree-items.js.map +1 -1
  255. package/dist/tree/tree-model.js +113 -10
  256. package/dist/tree/tree-model.js.map +1 -1
  257. package/dist/tree/tree-rows.js +20 -7
  258. package/dist/tree/tree-rows.js.map +1 -1
  259. package/dist/tree/tree-view-model.d.ts +5 -0
  260. package/dist/tree/tree-view-model.js +13 -0
  261. package/dist/tree/tree-view-model.js.map +1 -1
  262. package/dist/vue/AdvancedEditor.js +6 -0
  263. package/dist/vue/AdvancedEditor.js.map +1 -1
  264. package/dist/vue/AdvancedEditor.vue.d.ts +13 -5
  265. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  266. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  267. package/dist/vue/compose.js +6 -2
  268. package/dist/vue/compose.js.map +1 -1
  269. package/dist/vue/kit/types.d.ts +2 -0
  270. package/package.json +21 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentTree.js","names":[],"sources":["../../../src/tree/react/DocumentTree.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type DragEvent, type KeyboardEvent, type ReactNode, useRef } from 'react'\nimport { Icon } from '../../react/Icon'\nimport { useEditorI18n } from '../../react/use-editor-i18n'\nimport { useEditorSettings } from '../../react/settings'\nimport { Button } from '../../react/ui/button'\nimport { Collapsible, CollapsibleContent } from '../../react/ui/collapsible'\nimport { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '../../react/ui/dropdown-menu'\nimport { Input } from '../../react/ui/input'\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../react/ui/tooltip'\nimport { cn } from '../../react/ui/utils'\nimport type { DocumentStore } from '../store'\nimport { DRAG_MIME, type TreeItem } from '../tree-items'\nimport { rowOf } from '../tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the Vue twin renders the same model.\n\n/** Props of `DocumentTree`. */\nexport interface DocumentTreeProps {\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n /** The folder new documents, folders and host imports go to (controlled). Omit it to let the tree keep it. Reset to `''` when it is deleted or moved away. */\n selectedFolder?: string\n onSelectedFolderChange?: (folder: string) => void\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n documentActions?: (document: { id: string, title: string }) => ReactNode\n className?: string\n}\n\nconst REVEAL_ON_HOVER = 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\nconst isOutlineItem = (item: TreeItem) => item.type === 'composite' || item.type === 'heading'\n\n/** The label with the part the title filter matched highlighted. */\nfunction Label({ label, match }: { label: string, match: readonly [number, number] | null }) {\n if (!match) return <>{label}</>\n const [at, length] = match\n return (\n <>\n {label.slice(0, at)}\n <mark className=\"rounded-sm bg-primary/20 text-inherit\">{label.slice(at, at + length)}</mark>\n {label.slice(at + length)}\n </>\n )\n}\n\n/** The file browser of a document store: folders and documents with their drag and drop, the open document's outline below it. */\nexport function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, documentActions, className }: DocumentTreeProps) {\n const { t } = useEditorI18n()\n const { update } = useEditorSettings()\n const binding = useTreeModel({ store, editor, selectedFolder, onSelectedFolderChange })\n const treeElement = useRef<HTMLUListElement>(null)\n if (!binding) return null\n const { model, state } = binding\n // Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n // it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\n const canUseFileRoot = state.hasEditor && state.structure === 'composite'\n const { view, creatingFolder } = state\n\n // Folders and the root toggle through the row click; documents and outline entries (whose row click opens or reveals)\n // toggle through their chevron.\n function onChevronClick(event: { stopPropagation: () => void }, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.toggle(item.key)\n }\n\n function onRowKeyDown(event: KeyboardEvent<HTMLDivElement>, item: TreeItem) {\n if (event.target !== event.currentTarget) return\n const { expandable, open } = rowOf(state, item, t)\n if (event.key === 'Enter' || event.key === ' ') model.activate(item)\n else if (event.key === 'ArrowRight' && expandable && !open) model.setOpen(item.key, true)\n else if (event.key === 'ArrowLeft' && expandable && open) model.setOpen(item.key, false)\n else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const rows = [...(treeElement.current?.querySelectorAll<HTMLElement>('[role=\"treeitem\"]') ?? [])]\n rows[rows.indexOf(event.currentTarget) + (event.key === 'ArrowDown' ? 1 : -1)]?.focus()\n } else return\n event.preventDefault()\n }\n\n // --- drag & drop -----------------------------------------------------------------------------------------------\n\n function onDragStart(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const payload = model.dragStart(item)\n if (!payload) return event.preventDefault()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n event.dataTransfer.effectAllowed = 'move'\n }\n\n function onDragOver(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const verdict = model.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n event.dataTransfer.dropEffect = verdict.ok ? 'move' : 'none'\n }\n\n function onDragLeave(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) model.dragLeave(item)\n }\n\n function onDrop(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (!state.drag) return\n event.preventDefault()\n void model.drop(item)\n }\n\n // --- rendering -------------------------------------------------------------------------------------------------\n\n const filterMenu = (item: TreeItem) => {\n const { filterKinds, hiddenKinds } = rowOf(state, item, t)\n return (\n <DropdownMenuContent align=\"start\" className=\"w-auto\">\n <DropdownMenuLabel>{t('tree.filter.show')}</DropdownMenuLabel>\n {filterKinds.map(kind => (\n <DropdownMenuCheckboxItem\n key={kind.id}\n checked={!hiddenKinds.includes(kind.id)}\n onCheckedChange={visible => model.setKindHidden(item.key, kind.id, !visible)}\n // Stay open: several kinds are usually toggled in one go.\n onSelect={event => event.preventDefault()}\n >\n <Icon name={kind.icon} />\n {kind.label}\n </DropdownMenuCheckboxItem>\n ))}\n {hiddenKinds.length > 0 && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => model.clearFilter(item.key)}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n )\n }\n\n const renderRow = (item: TreeItem, level: number) => {\n const row = rowOf(state, item, t)\n const add = model.addMenu(item)\n const filtered = row.hiddenKinds.length > 0\n const refused = row.drop && !row.drop.ok ? row.drop.reason : null\n return (\n <>\n <div\n role=\"treeitem\"\n tabIndex={0}\n aria-level={level + 1}\n aria-expanded={row.expandable ? row.open : undefined}\n aria-selected={row.active ? true : undefined}\n className={cn(\n 'group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring',\n row.active && 'bg-primary/10 font-medium text-primary',\n row.selected && 'bg-muted',\n row.drop?.ok && 'ring-1 ring-primary',\n refused && 'bg-destructive/10 ring-1 ring-destructive',\n )}\n draggable={model.isDraggable(item)}\n onClick={() => model.activate(item)}\n onKeyDown={event => onRowKeyDown(event, item)}\n onDragStart={event => onDragStart(event, item)}\n onDragOver={event => onDragOver(event, item)}\n onDragLeave={event => onDragLeave(event, item)}\n onDrop={event => onDrop(event, item)}\n onDragEnd={() => model.dragEnd()}\n >\n {row.expandable\n ? (\n <span className=\"flex shrink-0\" onClick={event => onChevronClick(event, item)}>\n <Icon name=\"i-lucide-chevron-right\" className={cn('size-3.5 text-muted-foreground transition-transform', row.open && 'rotate-90')} />\n </span>\n )\n : isOutlineItem(item) && <span className=\"size-3.5 shrink-0\" />}\n <Icon name={item.icon} />\n\n {row.renaming\n ? (\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <Input\n value={row.renaming.draft}\n className=\"h-6 min-w-0 px-2 text-xs md:text-xs\"\n autoFocus\n aria-label={t('tree.folder.name')}\n aria-invalid={!!row.renaming.error}\n onChange={event => model.setRenameDraft(event.target.value)}\n onFocus={event => event.currentTarget.select()}\n onClick={event => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === 'Enter') void model.commitRename()\n else if (event.key === 'Escape') model.cancelRename()\n }}\n onBlur={() => void model.blurRename()}\n />\n {row.renaming.error && <p className=\"text-xs text-destructive\" role=\"alert\" onClick={event => event.stopPropagation()}>{row.renaming.error}</p>}\n </div>\n )\n : (\n <>\n <span className=\"min-w-0 flex-1 truncate\" title={item.label}><Label label={item.label} match={row.match} /></span>\n {item.hasSummary && <Icon name=\"i-lucide-text-quote\" className=\"size-3.5 text-muted-foreground\" />}\n <div className=\"flex shrink-0\" onClick={event => event.stopPropagation()}>\n {row.showFilter && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={filtered ? 'secondary' : 'ghost'}\n className={cn(!filtered && REVEAL_ON_HOVER)}\n title={t('tree.filter.title')}\n aria-label={t('tree.filter.title')}\n >\n <Icon name=\"i-lucide-filter\" />\n </Button>\n </DropdownMenuTrigger>\n {filterMenu(item)}\n </DropdownMenu>\n )}\n <div className=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n {add.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.add')} aria-label={t('tree.add')}>\n <Icon name=\"i-lucide-plus\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-auto\" onCloseAutoFocus={event => event.preventDefault()}>\n {add.map((group, index) => (\n <div key={index}>\n {index > 0 && <DropdownMenuSeparator />}\n {group.map(entry => (\n <DropdownMenuItem key={entry.label} onSelect={entry.run}>\n <Icon name={entry.icon} />\n {entry.label}\n </DropdownMenuItem>\n ))}\n </div>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {item.type === 'document' && documentActions?.({ id: item.id!, title: item.label })}\n {(item.type === 'document' || item.type === 'folder') && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"text-destructive hover:text-destructive\"\n aria-label={t('tree.delete')}\n disabled={row.removing}\n onClick={() => void model.remove(item)}\n >\n <Icon name={row.removing ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={cn(row.removing && 'animate-spin')} />\n </Button>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n {refused && <p className=\"px-2 pt-0.5 text-xs text-destructive\" role=\"status\">{refused}</p>}\n </>\n )\n }\n\n const renderItems = (list: readonly TreeItem[], level: number): ReactNode => (\n <ul role={level === 0 ? 'tree' : 'group'} aria-label={level === 0 ? t('tree.aria') : undefined} ref={level === 0 ? treeElement : undefined} className={cn('m-0 list-none p-0', level > 0 && 'ml-3 border-l border-border pl-1')}>\n {list.map(item => (\n <li key={item.key} role=\"none\">\n <Collapsible open={rowOf(state, item, t).open}>\n {renderRow(item, level)}\n {item.children?.length ? <CollapsibleContent>{renderItems(item.children, level + 1)}</CollapsibleContent> : null}\n </Collapsible>\n </li>\n ))}\n </ul>\n )\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"relative\">\n <Icon name=\"i-lucide-search\" className=\"pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={state.filterText}\n className=\"h-7 pr-7 pl-7 text-sm md:text-sm\"\n placeholder={t('tree.filterText.placeholder')}\n aria-label={t('tree.filterText.placeholder')}\n onChange={event => model.setFilterText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Escape') model.setFilterText('')\n }}\n />\n {state.filterText && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"absolute top-1/2 right-1 -translate-y-1/2\"\n aria-label={t('tree.filterText.clear')}\n onClick={() => model.setFilterText('')}\n >\n <Icon name=\"i-lucide-x\" />\n </Button>\n )}\n </div>\n\n <div className=\"flex items-center justify-end\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n {/* A disabled button swallows pointer events, so the tooltip hangs on the wrapper. */}\n <span className=\"inline-flex\">\n <Button\n size=\"xs\"\n variant={state.virtual ? 'secondary' : 'ghost'}\n disabled={!canUseFileRoot}\n aria-pressed={state.virtual}\n onClick={() => update((draft) => { draft.blocks.treeRoot = draft.blocks.treeRoot === 'file' ? 'folders' : 'file' })}\n >\n <Icon name={state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'} />\n {t(state.virtual ? 'tree.root.file' : 'tree.root.folders')}\n </Button>\n </span>\n </TooltipTrigger>\n {!canUseFileRoot && <TooltipContent>{t('tree.root.unavailable')}</TooltipContent>}\n </Tooltip>\n </TooltipProvider>\n </div>\n\n {view.hasKindFilters && (\n <div className=\"flex items-center\">\n <Button size=\"xs\" variant=\"secondary\" onClick={() => model.clearAllFilters()}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </Button>\n </div>\n )}\n\n {creatingFolder && (\n <div className=\"flex flex-col gap-1\">\n <Input\n value={creatingFolder.draft}\n className=\"h-7 text-sm md:text-sm\"\n autoFocus\n placeholder={creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: creatingFolder.parent }) : t('tree.folder.placeholder')}\n aria-label={t('tree.folder.name')}\n aria-invalid={!!creatingFolder.error}\n onChange={event => model.setFolderDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void model.commitCreateFolder()\n else if (event.key === 'Escape') model.cancelCreateFolder()\n }}\n onBlur={() => model.cancelCreateFolder()}\n />\n {creatingFolder.error && <p className=\"px-0.5 text-xs text-destructive\" role=\"alert\">{creatingFolder.error}</p>}\n </div>\n )}\n\n {!state.tree && !state.virtual\n ? <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{state.tree === null ? t('tree.loadFailed') : t('tree.loading')}</p>\n : renderItems(view.items, 0)}\n\n {view.noMatches && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.noMatches')}</p>}\n {view.isEmpty && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.empty')}</p>}\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocumentTree.js","names":[],"sources":["../../../src/tree/react/DocumentTree.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type DragEvent, Fragment, type KeyboardEvent, type ReactNode, useRef } from 'react'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../../core/block-transfer'\nimport { findBlockById } from '../../core/schema/block-ids'\nimport { Icon } from '../../react/Icon'\nimport { useEditorI18n } from '../../react/use-editor-i18n'\nimport { useEditorSettings } from '../../react/settings'\nimport { Button } from '../../react/ui/button'\nimport { Collapsible, CollapsibleContent } from '../../react/ui/collapsible'\nimport { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '../../react/ui/dropdown-menu'\nimport { Input } from '../../react/ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../react/ui/popover'\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../react/ui/tooltip'\nimport { cn } from '../../react/ui/utils'\nimport type { DocumentStore } from '../store'\nimport { basketById } from '../../features/composites'\nimport { DRAG_MIME, focusCrumbs, itemBasket, type TreeItem } from '../tree-items'\nimport { rowOf } from '../tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the Vue twin renders the same model.\n\n/** Props of `DocumentTree`. */\nexport interface DocumentTreeProps {\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n /** The folder new documents, folders and host imports go to (controlled). Omit it to let the tree keep it. Reset to `''` when it is deleted or moved away. */\n selectedFolder?: string\n onSelectedFolderChange?: (folder: string) => void\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n documentActions?: (document: { id: string, title: string }) => ReactNode\n className?: string\n}\n\nconst BASKET_OPTION = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary'\n\nconst REVEAL_ON_HOVER = 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\nconst isOutlineItem = (item: TreeItem) => item.type === 'composite' || item.type === 'heading'\n\n/** The label with the part the title filter matched highlighted. */\nfunction Label({ label, match }: { label: string, match: readonly [number, number] | null }) {\n if (!match) return <>{label}</>\n const [at, length] = match\n return (\n <>\n {label.slice(0, at)}\n <mark className=\"rounded-sm bg-primary/20 text-inherit\">{label.slice(at, at + length)}</mark>\n {label.slice(at + length)}\n </>\n )\n}\n\n/** The file browser of a document store: folders and documents with their drag and drop, the open document's outline below it. */\nexport function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, documentActions, className }: DocumentTreeProps) {\n const { t } = useEditorI18n()\n const { update } = useEditorSettings()\n const binding = useTreeModel({ store, editor, selectedFolder, onSelectedFolderChange })\n const treeElement = useRef<HTMLUListElement>(null)\n if (!binding) return null\n const { model, state } = binding\n // Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n // it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\n const canUseFileRoot = state.hasEditor && state.structure === 'composite'\n const { view, creatingFolder } = state\n\n /** The basket filter button needs a live editor that offers baskets. */\n const showBasketFilter = state.hasEditor && state.baskets.length > 0\n /** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\n const shownBaskets = state.basketFilter.filter(id => basketById(state.baskets, id))\n /** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Bausteine down to the focused one. */\n const { document: focusDocument, crumbs } = focusCrumbs(state.focusPath)\n\n // Folders and the root toggle through the row click; documents and outline entries (whose row click opens or reveals)\n // toggle through their chevron.\n function onChevronClick(event: { stopPropagation: () => void }, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.toggle(item.key)\n }\n\n function onRowKeyDown(event: KeyboardEvent<HTMLDivElement>, item: TreeItem) {\n if (event.target !== event.currentTarget) return\n const { expandable, open } = rowOf(state, item, t)\n if (event.key === 'Enter' || event.key === ' ') model.activate(item)\n else if (event.key === 'ArrowRight' && expandable && !open) model.setOpen(item.key, true)\n else if (event.key === 'ArrowLeft' && expandable && open) model.setOpen(item.key, false)\n else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const rows = [...(treeElement.current?.querySelectorAll<HTMLElement>('[role=\"treeitem\"]') ?? [])]\n rows[rows.indexOf(event.currentTarget) + (event.key === 'ArrowDown' ? 1 : -1)]?.focus()\n } else return\n event.preventDefault()\n }\n\n // --- drag & drop -----------------------------------------------------------------------------------------------\n\n function onDragStart(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const payload = model.dragStart(item)\n if (!payload) return event.preventDefault()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n // A Baustein row also carries the block itself, so another surface or tree can take a copy.\n const found = payload.type === 'composite' && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null\n const transfer = found && editor ? blockTransferOf(editor, found.pos) : null\n if (transfer) writeBlockTransfer(event.dataTransfer, transfer)\n event.dataTransfer.effectAllowed = transfer ? 'copyMove' : 'move'\n }\n\n /** A drag of this tree's own rows is running (a foreign drag is not). */\n const ownDrag = !!state.drag && state.drag.source.type !== 'foreign-block'\n\n function onDragOver(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const foreign = !ownDrag && event.dataTransfer.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.foreignDragOver(item) : model.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n }\n\n function onDragLeave(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) model.dragLeave(item)\n }\n\n function onDrop(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (ownDrag) {\n event.preventDefault()\n void model.drop(item)\n return\n }\n const payload = readBlockTransfer(event.dataTransfer)\n if (!payload) return\n event.preventDefault()\n void model.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n }\n\n // --- rendering -------------------------------------------------------------------------------------------------\n\n const filterMenu = (item: TreeItem) => {\n const { filterKinds, hiddenKinds } = rowOf(state, item, t)\n return (\n <DropdownMenuContent align=\"start\" className=\"w-auto\">\n <DropdownMenuLabel>{t('tree.filter.show')}</DropdownMenuLabel>\n {filterKinds.map(kind => (\n <DropdownMenuCheckboxItem\n key={kind.id}\n checked={!hiddenKinds.includes(kind.id)}\n onCheckedChange={visible => model.setKindHidden(item.key, kind.id, !visible)}\n // Stay open: several kinds are usually toggled in one go.\n onSelect={event => event.preventDefault()}\n >\n <Icon name={kind.icon} />\n {kind.label}\n </DropdownMenuCheckboxItem>\n ))}\n {hiddenKinds.length > 0 && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => model.clearFilter(item.key)}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n )\n }\n\n const renderRow = (item: TreeItem, level: number) => {\n const row = rowOf(state, item, t)\n const add = model.addMenu(item)\n const filtered = row.hiddenKinds.length > 0\n const refused = row.drop && !row.drop.ok ? row.drop.reason : null\n const basket = itemBasket(item, state.baskets)\n return (\n <>\n <div\n role=\"treeitem\"\n tabIndex={0}\n aria-level={level + 1}\n aria-expanded={row.expandable ? row.open : undefined}\n aria-selected={row.active ? true : undefined}\n className={cn(\n 'group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring',\n row.active && 'bg-primary/10 font-medium text-primary',\n row.selected && 'bg-muted',\n row.drop?.ok && 'ring-1 ring-primary',\n refused && 'bg-destructive/10 ring-1 ring-destructive',\n row.dimmed && 'opacity-60',\n )}\n draggable={model.isDraggable(item)}\n onClick={() => model.activate(item)}\n onKeyDown={event => onRowKeyDown(event, item)}\n onDragStart={event => onDragStart(event, item)}\n onDragOver={event => onDragOver(event, item)}\n onDragLeave={event => onDragLeave(event, item)}\n onDrop={event => onDrop(event, item)}\n onDragEnd={() => model.dragEnd()}\n >\n {row.expandable\n ? (\n <span className=\"flex shrink-0\" onClick={event => onChevronClick(event, item)}>\n <Icon name=\"i-lucide-chevron-right\" className={cn('size-3.5 text-muted-foreground transition-transform', row.open && 'rotate-90')} />\n </span>\n )\n : isOutlineItem(item) && <span className=\"size-3.5 shrink-0\" />}\n <Icon name={item.icon} />\n\n {row.renaming\n ? (\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <Input\n value={row.renaming.draft}\n className=\"h-6 min-w-0 px-2 text-xs md:text-xs\"\n autoFocus\n aria-label={t('tree.folder.name')}\n aria-invalid={!!row.renaming.error}\n onChange={event => model.setRenameDraft(event.target.value)}\n onFocus={event => event.currentTarget.select()}\n onClick={event => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === 'Enter') void model.commitRename()\n else if (event.key === 'Escape') model.cancelRename()\n }}\n onBlur={() => void model.blurRename()}\n />\n {row.renaming.error && <p className=\"text-xs text-destructive\" role=\"alert\" onClick={event => event.stopPropagation()}>{row.renaming.error}</p>}\n </div>\n )\n : (\n <>\n {basket && (\n <span\n className=\"size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: basket.color }}\n title={basket.label}\n aria-label={basket.label}\n role=\"img\"\n data-basket-dot\n />\n )}\n <span className=\"min-w-0 flex-1 truncate\" title={item.label}><Label label={item.label} match={row.match} /></span>\n {item.hasSummary && <Icon name=\"i-lucide-text-quote\" className=\"size-3.5 text-muted-foreground\" />}\n <div className=\"flex shrink-0\" onClick={event => event.stopPropagation()}>\n {row.showFilter && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={filtered ? 'secondary' : 'ghost'}\n className={cn(!filtered && REVEAL_ON_HOVER)}\n title={t('tree.filter.title')}\n aria-label={t('tree.filter.title')}\n >\n <Icon name=\"i-lucide-filter\" />\n </Button>\n </DropdownMenuTrigger>\n {filterMenu(item)}\n </DropdownMenu>\n )}\n <div className=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n {item.type === 'composite' && item.blockId && (\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.focus.enter')} aria-label={t('tree.focus.enter')} onClick={() => model.focus(item.key)}>\n <Icon name=\"i-lucide-focus\" />\n </Button>\n )}\n {add.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.add')} aria-label={t('tree.add')}>\n <Icon name=\"i-lucide-plus\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-auto\" onCloseAutoFocus={event => event.preventDefault()}>\n {add.map((group, index) => (\n <div key={index}>\n {index > 0 && <DropdownMenuSeparator />}\n {group.map(entry => (\n <DropdownMenuItem key={entry.label} onSelect={entry.run}>\n <Icon name={entry.icon} />\n {entry.label}\n </DropdownMenuItem>\n ))}\n </div>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {item.type === 'document' && documentActions?.({ id: item.id!, title: item.label })}\n {(item.type === 'document' || item.type === 'folder') && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"text-destructive hover:text-destructive\"\n aria-label={t('tree.delete')}\n disabled={row.removing}\n onClick={() => void model.remove(item)}\n >\n <Icon name={row.removing ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={cn(row.removing && 'animate-spin')} />\n </Button>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n {refused && <p className=\"px-2 pt-0.5 text-xs text-destructive\" role=\"status\">{refused}</p>}\n </>\n )\n }\n\n const renderItems = (list: readonly TreeItem[], level: number): ReactNode => (\n <ul role={level === 0 ? 'tree' : 'group'} aria-label={level === 0 ? t('tree.aria') : undefined} ref={level === 0 ? treeElement : undefined} className={cn('m-0 list-none p-0', level > 0 && 'ml-3 border-l border-border pl-1')}>\n {list.map(item => (\n <li key={item.key} role=\"none\">\n <Collapsible open={rowOf(state, item, t).open}>\n {renderRow(item, level)}\n {item.children?.length ? <CollapsibleContent>{renderItems(item.children, level + 1)}</CollapsibleContent> : null}\n </Collapsible>\n </li>\n ))}\n </ul>\n )\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"flex items-center gap-1\">\n <div className=\"relative min-w-0 flex-1\">\n <Icon name=\"i-lucide-search\" className=\"pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={state.filterText}\n className=\"h-7 pr-7 pl-7 text-sm md:text-sm\"\n placeholder={t('tree.filterText.placeholder')}\n aria-label={t('tree.filterText.placeholder')}\n onChange={event => model.setFilterText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Escape') model.setFilterText('')\n }}\n />\n {state.filterText && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"absolute top-1/2 right-1 -translate-y-1/2\"\n aria-label={t('tree.filterText.clear')}\n onClick={() => model.setFilterText('')}\n >\n <Icon name=\"i-lucide-x\" />\n </Button>\n )}\n </div>\n {showBasketFilter && (\n // A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour.\n <Popover>\n <PopoverTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={shownBaskets.length ? 'secondary' : 'ghost'}\n title={t('tree.basketFilter.label')}\n aria-label={t('tree.basketFilter.label')}\n >\n <Icon name=\"i-lucide-paint-bucket\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent side=\"bottom\" align=\"end\" sideOffset={4} className=\"w-44 gap-0 p-1\" onCloseAutoFocus={event => event.preventDefault()}>\n <div role=\"menu\" aria-label={t('tree.basketFilter.label')}>\n {state.baskets.map((basket) => {\n const shown = shownBaskets.includes(basket.id)\n return (\n <button\n key={basket.id}\n type=\"button\"\n role=\"menuitemcheckbox\"\n aria-checked={shown}\n className={BASKET_OPTION}\n onClick={() => model.setBasketShown(basket.id, !shown)}\n >\n <span className=\"size-2.5 shrink-0 rounded-full\" style={{ backgroundColor: basket.color }} />\n <span className=\"truncate\">{basket.label}</span>\n {shown && <Icon name=\"i-lucide-check\" className=\"ml-auto size-3.5 shrink-0\" />}\n </button>\n )\n })}\n {shownBaskets.length > 0 && (\n <button type=\"button\" role=\"menuitem\" className={BASKET_OPTION} onClick={() => model.clearBasketFilter()}>\n <Icon name=\"i-lucide-filter-x\" className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{t('tree.basketFilter.reset')}</span>\n </button>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n\n <div className=\"flex items-center justify-end\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n {/* A disabled button swallows pointer events, so the tooltip hangs on the wrapper. */}\n <span className=\"inline-flex\">\n <Button\n size=\"xs\"\n variant={state.virtual ? 'secondary' : 'ghost'}\n disabled={!canUseFileRoot}\n aria-pressed={state.virtual}\n onClick={() => update((draft) => { draft.blocks.treeRoot = draft.blocks.treeRoot === 'file' ? 'folders' : 'file' })}\n >\n <Icon name={state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'} />\n {t(state.virtual ? 'tree.root.file' : 'tree.root.folders')}\n </Button>\n </span>\n </TooltipTrigger>\n {!canUseFileRoot && <TooltipContent>{t('tree.root.unavailable')}</TooltipContent>}\n </Tooltip>\n </TooltipProvider>\n </div>\n\n {view.hasKindFilters && (\n <div className=\"flex items-center\">\n <Button size=\"xs\" variant=\"secondary\" onClick={() => model.clearAllFilters()}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </Button>\n </div>\n )}\n\n {creatingFolder && (\n <div className=\"flex flex-col gap-1\">\n <Input\n value={creatingFolder.draft}\n className=\"h-7 text-sm md:text-sm\"\n autoFocus\n placeholder={creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: creatingFolder.parent }) : t('tree.folder.placeholder')}\n aria-label={t('tree.folder.name')}\n aria-invalid={!!creatingFolder.error}\n onChange={event => model.setFolderDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void model.commitCreateFolder()\n else if (event.key === 'Escape') model.cancelCreateFolder()\n }}\n onBlur={() => model.cancelCreateFolder()}\n />\n {creatingFolder.error && <p className=\"px-0.5 text-xs text-destructive\" role=\"alert\">{creatingFolder.error}</p>}\n </div>\n )}\n\n {crumbs.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <Button size=\"xs\" variant=\"ghost\" title={t('tree.focus.leave')} aria-label={t('tree.focus.leave')} onClick={() => model.defocus()}>\n <Icon name=\"i-lucide-corner-up-left\" />\n {focusDocument?.label ?? t('tree.focus.root')}\n </Button>\n {crumbs.map((crumb, index) => (\n <Fragment key={crumb.key}>\n <Icon name=\"i-lucide-chevron-right\" className=\"size-3 shrink-0 text-muted-foreground\" />\n {index === crumbs.length - 1\n ? <span className=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{crumb.label}</span>\n : <Button size=\"xs\" variant=\"ghost\" onClick={() => model.focus(crumb.key)}>{crumb.label}</Button>}\n </Fragment>\n ))}\n </div>\n )}\n\n {!state.tree && !state.virtual\n ? <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{state.tree === null ? t('tree.loadFailed') : t('tree.loading')}</p>\n : renderItems(view.items, 0)}\n\n {view.noMatches && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.noMatches')}</p>}\n {view.isEmpty && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.empty')}</p>}\n </div>\n )\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { findBlockById } from "../core/schema/block-ids.js";
2
- import { compositeJSON, compositeKindsOf } from "../features/composites/nodes.js";
2
+ import { compositeBasketsOf, compositeJSON, compositeKindsOf } from "../features/composites/nodes.js";
3
3
  import { revealBlockInEditor } from "../outline/heading-outline.js";
4
4
  import { pickFiles } from "../features/media/upload.js";
5
+ import { compositeFocusOf } from "../features/composites/focus.js";
5
6
  import { moveBlockInto, moveIntoVerdict } from "../features/composites/outline.js";
6
7
  import { readOutline } from "./tree-items.js";
7
8
  //#region src/tree/tree-host.ts
@@ -9,15 +10,28 @@ import { readOutline } from "./tree-items.js";
9
10
  function treeEditorOf(editor, t) {
10
11
  return {
11
12
  kinds: compositeKindsOf(editor),
13
+ baskets: compositeBasketsOf(editor),
12
14
  outline: (structure) => readOutline(editor, structure, t),
13
15
  moveVerdict: (id, targetId) => editor.isDestroyed ? {
14
16
  ok: false,
15
17
  reason: t("tree.drop.editorClosed")
16
18
  } : moveIntoVerdict(editor, id, targetId, t),
19
+ canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== void 0 && typeof editor.commands.transferBlock === "function",
20
+ focusedId: () => editor.isDestroyed ? null : compositeFocusOf(editor.state),
17
21
  onChange(listener) {
22
+ let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state);
23
+ const onTransaction = () => {
24
+ if (editor.isDestroyed) return;
25
+ const now = compositeFocusOf(editor.state);
26
+ if (now === focused) return;
27
+ focused = now;
28
+ listener();
29
+ };
18
30
  editor.on("update", listener);
31
+ editor.on("transaction", onTransaction);
19
32
  return () => {
20
33
  editor.off("update", listener);
34
+ editor.off("transaction", onTransaction);
21
35
  };
22
36
  }
23
37
  };
@@ -88,9 +102,24 @@ function createTreeExecutor(host) {
88
102
  return true;
89
103
  }
90
104
  case "importFiles": return importIntoComposite(command.targetId);
105
+ case "transferBlock": {
106
+ const editor = liveEditor();
107
+ if (!editor || typeof editor.commands.transferBlock !== "function") return false;
108
+ return editor.commands.transferBlock({
109
+ payload: command.payload,
110
+ pos: compositeInsertPos(editor, command.targetId),
111
+ menu: command.menu,
112
+ x: command.x,
113
+ y: command.y
114
+ });
115
+ }
91
116
  case "warn":
92
117
  host.warn(command.title, command.description);
93
118
  return true;
119
+ case "focusBlock": {
120
+ const editor = liveEditor();
121
+ return !!editor && editor.commands.focusComposite(command.id);
122
+ }
94
123
  }
95
124
  };
96
125
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeJSON, compositeKindsOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Baustein kinds, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: compositeKindsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n onChange(listener) {\n editor.on('update', listener)\n return () => { editor.off('update', listener) }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'composite' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createBausteinFolder': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'warn':\n host.warn(command.title, command.description)\n return true\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeBasketsOf, compositeFocusOf, compositeJSON, compositeKindsOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Baustein kinds, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: compositeKindsOf(editor),\n baskets: compositeBasketsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== undefined && typeof editor.commands.transferBlock === 'function',\n focusedId: () => (editor.isDestroyed ? null : compositeFocusOf(editor.state)),\n onChange(listener) {\n // A focus change is a transaction without a document change (the focus chip, a block's „Fokussieren“), so `update` does not see it.\n let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state)\n const onTransaction = () => {\n if (editor.isDestroyed) return\n const now = compositeFocusOf(editor.state)\n if (now === focused) return\n focused = now\n listener()\n }\n editor.on('update', listener)\n editor.on('transaction', onTransaction)\n return () => {\n editor.off('update', listener)\n editor.off('transaction', onTransaction)\n }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'composite' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createBausteinFolder': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'transferBlock': {\n const editor = liveEditor()\n // An editor without the transfer feature has no such command: the drop is refused (`false`), never thrown.\n if (!editor || typeof editor.commands.transferBlock !== 'function') return false\n return editor.commands.transferBlock({ payload: command.payload, pos: compositeInsertPos(editor, command.targetId), menu: command.menu, x: command.x, y: command.y })\n }\n case 'warn':\n host.warn(command.title, command.description)\n return true\n case 'focusBlock': {\n const editor = liveEditor()\n return !!editor && editor.commands.focusComposite(command.id)\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { compositeKind, defaultCompositeKinds, kindAccepts } from "../features/composites/kinds.js";
2
+ import { basketById } from "../features/composites/baskets.js";
2
3
  import { headingOutline } from "../outline/heading-outline.js";
3
4
  import { compositeOutline } from "../features/composites/outline.js";
4
5
  import { folderKey } from "./folder-paths.js";
@@ -62,6 +63,7 @@ function outlineItems(nodes, kinds, select) {
62
63
  pos: node.pos,
63
64
  kind: composite ? node.kind : void 0,
64
65
  hasSummary: composite ? node.hasSummary : void 0,
66
+ basket: composite ? node.basket : void 0,
65
67
  children: node.children.length ? outlineItems(node.children, kinds, select) : void 0,
66
68
  onSelect: () => select(item),
67
69
  onToggle: (e) => e?.preventDefault()
@@ -69,6 +71,8 @@ function outlineItems(nodes, kinds, select) {
69
71
  return item;
70
72
  });
71
73
  }
74
+ /** The basket of a composite item among `baskets`; `undefined` without one (or when the host no longer offers it). */
75
+ const itemBasket = (item, baskets) => basketById(baskets, item.basket);
72
76
  /** The store's folders and documents as items; the open document (`activeId`) lists `outline` below it. */
73
77
  function folderItem(folder, activeId, outline, select) {
74
78
  const root = folder.path === "";
@@ -108,14 +112,39 @@ function virtualRootItem(label, outline, select) {
108
112
  };
109
113
  return item;
110
114
  }
111
- /** Adds the keys of `list` and all items below it to `into`; returns `into`. */
112
- function collectKeys(list, into) {
115
+ /** Adds `list` and all items below it to `into` by key; returns `into`. */
116
+ function indexItems(list, into, parent = null) {
113
117
  for (const item of list) {
114
- into.add(item.key);
115
- if (item.children) collectKeys(item.children, into);
118
+ into.set(item.key, {
119
+ item,
120
+ parent
121
+ });
122
+ if (item.children) indexItems(item.children, into, item.key);
116
123
  }
117
124
  return into;
118
125
  }
126
+ /** The items from the top-level one down to the item at `key` (empty when `key` is not in `index`). */
127
+ function pathTo(index, key) {
128
+ const path = [];
129
+ let entry = index.get(key);
130
+ if (!entry) return path;
131
+ for (;;) {
132
+ path.unshift(entry.item);
133
+ if (entry.parent === null) return path;
134
+ entry = index.get(entry.parent);
135
+ }
136
+ }
137
+ /**
138
+ * The breadcrumb of a focus (`TreeState.focusPath`): `document` is the open document's own row (the virtual root, or the
139
+ * document row of the store; its label words the first crumb, which leaves focus), `crumbs` the Bausteine from there
140
+ * down to the focused one, which is last.
141
+ */
142
+ function focusCrumbs(path) {
143
+ return {
144
+ document: path.find((item) => item.type === "document" || item.key === "vroot"),
145
+ crumbs: path.filter((item) => item.type === "composite")
146
+ };
147
+ }
119
148
  const fileKindsOf = /* @__PURE__ */ new WeakMap();
120
149
  /** The kinds a folder lists: Ordner and Dokumente (in the language of `t`; one list per translator). */
121
150
  function fileKinds(t) {
@@ -156,6 +185,24 @@ function applyNodeFilters(list, filters, inherited = /* @__PURE__ */ new Set())
156
185
  });
157
186
  }
158
187
  /**
188
+ * Hides the composites that are not in a `shown` basket and have no visible descendant; ancestors that stay only
189
+ * because of a visible descendant are collected in `dimmed`. Documents, folders, headings and the roots are never hidden.
190
+ */
191
+ function applyBasketFilter(list, shown, dimmed) {
192
+ return list.flatMap((item) => {
193
+ const children = item.children ? applyBasketFilter(item.children, shown, dimmed) : void 0;
194
+ if (item.type === "composite") {
195
+ const matches = !!item.basket && shown.has(item.basket);
196
+ if (!matches && !children?.length) return [];
197
+ if (!matches) dimmed.add(item.key);
198
+ }
199
+ return [{
200
+ ...item,
201
+ children: children?.length ? children : void 0
202
+ }];
203
+ });
204
+ }
205
+ /**
159
206
  * What `item` takes as new Bausteine of the open file: the file itself (the virtual root, or its document row) takes
160
207
  * every kind, a Baustein what its kind accepts as children (`kindAccepts`). Nothing in a flat structure or without an editor.
161
208
  */
@@ -195,6 +242,6 @@ function isDraggable(item, virtual) {
195
242
  return item.type === "document" || item.type === "folder";
196
243
  }
197
244
  //#endregion
198
- export { DRAG_MIME, EMPTY_OUTLINE, VIRTUAL_ROOT, ancestorKeys, applyNodeFilters, collectFolderPaths, collectKeys, creatableBelow, dragPayloadOf, fileKinds, filterKinds, findFolder, folderItem, folderNamesIn, isDraggable, joinPath, outlineItems, parentPath, readOutline, virtualRootItem };
245
+ export { DRAG_MIME, EMPTY_OUTLINE, VIRTUAL_ROOT, ancestorKeys, applyBasketFilter, applyNodeFilters, collectFolderPaths, creatableBelow, dragPayloadOf, fileKinds, filterKinds, findFolder, focusCrumbs, folderItem, folderNamesIn, indexItems, isDraggable, itemBasket, joinPath, outlineItems, parentPath, pathTo, readOutline, virtualRootItem };
199
246
 
200
247
  //# sourceMappingURL=tree-items.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree-items.js","names":[],"sources":["../../src/tree/tree-items.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { compositeKind, type CompositeKind, compositeOutline, defaultCompositeKinds, kindAccepts, type OutlineNode } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type HeadingOutlineNode, headingOutline } from '../outline'\nimport { folderKey } from './folder-paths'\nimport type { TreeFolder } from './types'\nimport type { NodeFilters } from './tree-view-model'\n\n// What the Vue and the React `DocumentTree` share: the items of the file browser (folders and documents of the store,\n// the open document's outline), their kind filters, what can be dragged and created, and the path arithmetic. No\n// framework, no state; the drop rules are `drop-rules.ts`, the text filter and the visible rows `tree-rows.ts`.\n\n/** One row of the file browser: a store folder or document, or an entry of the open document's outline. Which fields are set depends on `type`. */\nexport interface TreeItem {\n /** Unique within the tree: `root`, `folder:<path>`, `doc:<id>`, `outline:<block key>` */\n key: string\n label: string\n icon: string\n type: 'root' | 'folder' | 'document' | 'composite' | 'heading'\n /** Folder path of a root/folder item (`''` = root) */\n path?: string\n /** Document id */\n id?: string\n /** Folder path a document item lives in */\n folder?: string\n /** Outline items: block ID of the composite/heading (`null` while it has none) and its position when the outline was read */\n blockId?: string | null\n pos?: number\n /** Composite items: the composite's kind, and whether it has a summary (description) */\n kind?: string\n hasSummary?: boolean\n children?: TreeItem[]\n onSelect?: (e?: Event) => void\n onToggle?: (e?: Event) => void\n}\n\n/** What a drag carries (serialized under `DRAG_MIME`): a document or folder of the store, or a composite of the open document. */\nexport type DragPayload\n = | {\n type: 'document' | 'folder'\n /** Document id */\n id?: string\n /** Folder path of a folder item, or the folder a document currently lives in */\n path: string\n }\n /** Virtual mode: a composite of the open document, by block ID */\n | { type: 'composite', blockId: string }\n\n/** Key of the virtual-mode root item (the open document). */\nexport const VIRTUAL_ROOT = 'vroot'\n\nexport const DRAG_MIME = 'application/x-tip-tree'\n\nexport const parentPath = (folder: string) => folder.slice(0, Math.max(folder.lastIndexOf('/'), 0))\nexport const joinPath = (folder: string, name: string) => (folder ? `${folder}/${name}` : name)\n\n/** Item keys from the root down to `folder`. */\nexport function ancestorKeys(folder: string): string[] {\n const keys = ['root']\n let path = ''\n for (const segment of folder.split('/').filter(Boolean)) {\n path = joinPath(path, segment)\n keys.push(folderKey(path))\n }\n return keys\n}\n\n/** The folder at `path` below (or at) `from`; undefined when there is none. */\nexport function findFolder(path: string, from: TreeFolder | null | undefined): TreeFolder | undefined {\n if (!from) return undefined\n if (from.path === path) return from\n for (const sub of from.folders) {\n const found = path === sub.path || path.startsWith(`${sub.path}/`) ? findFolder(path, sub) : undefined\n if (found) return found\n }\n return undefined\n}\n\n/** Names of the folders directly in `parent`, without the folder at path `except`. */\nexport const folderNamesIn = (tree: TreeFolder | null | undefined, parent: string, except?: string) =>\n (findFolder(parent, tree)?.folders ?? []).filter(sub => sub.path !== except).map(sub => sub.name)\n\nexport function collectFolderPaths(folder: TreeFolder, into: Set<string>) {\n into.add(folder.path)\n folder.folders.forEach(sub => collectFolderPaths(sub, into))\n}\n\n// --- outline of the open document ------------------------------------------------------------------------------\n\nexport type Outline = { structure: 'composite', nodes: OutlineNode[] } | { structure: 'flat', nodes: HeadingOutlineNode[] }\n\nexport const EMPTY_OUTLINE: Outline = { structure: 'composite', nodes: [] }\n\n/** The outline of the editor's document: the composites in composite structure, else the headings. */\nexport function readOutline(editor: Editor | undefined, structure: string, t: Translate): Outline {\n if (!editor || editor.isDestroyed) return EMPTY_OUTLINE\n const { doc } = editor.state\n return structure === 'composite'\n ? { structure: 'composite', nodes: compositeOutline(doc, new Set(), t('tree.untitled')) }\n : { structure: 'flat', nodes: headingOutline(doc, t('tree.untitled')) }\n}\n\n/** The outline entries as items; `select` runs when one is chosen. */\nexport function outlineItems(nodes: readonly (OutlineNode | HeadingOutlineNode)[], kinds: readonly CompositeKind[], select: (item: TreeItem) => void): TreeItem[] {\n return nodes.map((node) => {\n const composite = 'kind' in node\n const item: TreeItem = {\n key: `outline:${node.key}`,\n label: composite ? node.title : node.text,\n icon: composite ? compositeKind(kinds, node.kind).icon : `i-lucide-heading-${node.level}`,\n type: composite ? 'composite' : 'heading',\n blockId: node.id,\n pos: node.pos,\n kind: composite ? node.kind : undefined,\n hasSummary: composite ? node.hasSummary : undefined,\n children: node.children.length ? outlineItems(node.children as (OutlineNode | HeadingOutlineNode)[], kinds, select) : undefined,\n onSelect: () => select(item),\n onToggle: e => e?.preventDefault(), // the chevron toggles, a click reveals\n }\n return item\n })\n}\n\n// --- items -----------------------------------------------------------------------------------------------------\n\n/** The store's folders and documents as items; the open document (`activeId`) lists `outline` below it. */\nexport function folderItem(folder: TreeFolder, activeId: string | undefined, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const root = folder.path === ''\n const item: TreeItem = {\n key: root ? 'root' : folderKey(folder.path),\n label: folder.name,\n icon: root ? 'i-lucide-hard-drive' : 'i-lucide-folder',\n type: root ? 'root' : 'folder',\n path: folder.path,\n children: [\n ...folder.folders.map(sub => folderItem(sub, activeId, outline, select)),\n ...folder.documents.map((doc) => {\n const document: TreeItem = {\n key: `doc:${doc.id}`,\n label: doc.title,\n icon: 'i-lucide-file',\n type: 'document',\n id: doc.id,\n folder: doc.folder,\n // The open document lists its outline; its chevron (not a row click) opens and closes it.\n children: doc.id === activeId && outline.length ? outline : undefined,\n onSelect: () => select(document),\n onToggle: e => e?.preventDefault(),\n }\n return document\n }),\n ],\n onSelect: () => select(item),\n }\n return item\n}\n\n/** Virtual mode: the open document is the root, its `folder`/`document` composites (and all others) the tree below it. */\nexport function virtualRootItem(label: string, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const item: TreeItem = {\n key: VIRTUAL_ROOT,\n label,\n icon: 'i-lucide-package',\n type: 'root',\n children: outline.length ? outline : undefined,\n onSelect: () => select(item),\n }\n return item\n}\n\n/** Adds the keys of `list` and all items below it to `into`; returns `into`. */\nexport function collectKeys(list: readonly TreeItem[], into: Set<string>) {\n for (const item of list) {\n into.add(item.key)\n if (item.children) collectKeys(item.children, into)\n }\n return into\n}\n\n// --- filters ---------------------------------------------------------------------------------------------------\n\nconst fileKindsOf = new WeakMap<Translate, CompositeKind[]>()\n\n/** The kinds a folder lists: Ordner and Dokumente (in the language of `t`; one list per translator). */\nexport function fileKinds(t: Translate): CompositeKind[] {\n let kinds = fileKindsOf.get(t)\n if (!kinds) {\n const builtIn = defaultCompositeKinds(t)\n kinds = ['folder', 'document'].map(id => compositeKind(builtIn, id))\n fileKindsOf.set(t, kinds)\n }\n return kinds\n}\n\n/** What an item is filtered by: the composite's kind, or Ordner/Dokument for the store's folders and documents. */\nfunction itemKind(item: TreeItem): string | undefined {\n return item.kind ?? (item.type === 'folder' || item.type === 'document' ? item.type : undefined)\n}\n\n/** The kinds `item`'s filter can hide below it (`kinds` = those of the open editor). */\nexport function filterKinds(item: TreeItem, env: { structure: string, activeId: string | undefined, kinds: readonly CompositeKind[], t: Translate }): readonly CompositeKind[] {\n const composite = env.structure === 'composite'\n if (item.key === VIRTUAL_ROOT || item.type === 'composite') return env.kinds\n if (item.type === 'document') return composite && item.id === env.activeId ? env.kinds : []\n if (item.type === 'root' || item.type === 'folder') return fileKinds(env.t)\n return []\n}\n\n/**\n * Drops the items of kinds an ancestor's filter hides. Like the outline's own kind filter, their visible descendants\n * move up into the parent's list. A node's own filter applies to everything below it.\n */\nexport function applyNodeFilters(list: readonly TreeItem[], filters: NodeFilters, inherited: ReadonlySet<string> = new Set()): TreeItem[] {\n return list.flatMap((item) => {\n const own = filters[item.key]\n const children = item.children ? applyNodeFilters(item.children, filters, own?.length ? new Set([...inherited, ...own]) : inherited) : undefined\n const kind = itemKind(item)\n if (kind && inherited.has(kind)) return children ?? []\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n// --- creating ----------------------------------------------------------------------------------------------------\n\n/** What can be created below an item of the open file: Ordner-Bausteine, imported Dokument-Bausteine. */\nexport interface Creatable {\n folder: boolean\n document: boolean\n}\n\n/**\n * What `item` takes as new Bausteine of the open file: the file itself (the virtual root, or its document row) takes\n * every kind, a Baustein what its kind accepts as children (`kindAccepts`). Nothing in a flat structure or without an editor.\n */\nexport function creatableBelow(item: TreeItem, env: { structure: string, hasEditor: boolean, activeId: string | undefined, kinds: readonly CompositeKind[] }): Creatable {\n const none: Creatable = { folder: false, document: false }\n if (env.structure !== 'composite' || !env.hasEditor) return none\n if (item.key === VIRTUAL_ROOT || (item.type === 'document' && item.id === env.activeId)) return { folder: true, document: true }\n if (item.type !== 'composite' || !item.blockId || !item.kind) return none\n return { folder: kindAccepts(env.kinds, item.kind, 'folder'), document: kindAccepts(env.kinds, item.kind, 'document') }\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\n/** What a dragged item carries: its document or folder, or in virtual mode its composite's block ID. */\nexport function dragPayloadOf(item: TreeItem): DragPayload {\n return item.type === 'composite'\n ? { type: 'composite', blockId: item.blockId! }\n : item.type === 'document'\n ? { type: 'document', id: item.id, path: item.folder ?? '' }\n : { type: 'folder', path: item.path! }\n}\n\n/** Whether `item` may be dragged. Virtual mode moves composites of the open document; blocks without an ID cannot be addressed. */\nexport function isDraggable(item: TreeItem, virtual: boolean): boolean {\n if (virtual) return item.type === 'composite' && !!item.blockId\n return item.type === 'document' || item.type === 'folder'\n}\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree-items.js","names":[],"sources":["../../src/tree/tree-items.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type Basket, basketById, compositeKind, type CompositeKind, compositeOutline, defaultCompositeKinds, kindAccepts, type OutlineNode } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type HeadingOutlineNode, headingOutline } from '../outline'\nimport { folderKey } from './folder-paths'\nimport type { TreeFolder } from './types'\nimport type { NodeFilters } from './tree-view-model'\n\n// What the Vue and the React `DocumentTree` share: the items of the file browser (folders and documents of the store,\n// the open document's outline), their kind filters, what can be dragged and created, and the path arithmetic. No\n// framework, no state; the drop rules are `drop-rules.ts`, the text filter and the visible rows `tree-rows.ts`.\n\n/** One row of the file browser: a store folder or document, or an entry of the open document's outline. Which fields are set depends on `type`. */\nexport interface TreeItem {\n /** Unique within the tree: `root`, `folder:<path>`, `doc:<id>`, `outline:<block key>` */\n key: string\n label: string\n icon: string\n type: 'root' | 'folder' | 'document' | 'composite' | 'heading'\n /** Folder path of a root/folder item (`''` = root) */\n path?: string\n /** Document id */\n id?: string\n /** Folder path a document item lives in */\n folder?: string\n /** Outline items: block ID of the composite/heading (`null` while it has none) and its position when the outline was read */\n blockId?: string | null\n pos?: number\n /** Composite items: the composite's kind, and whether it has a summary (description) */\n kind?: string\n hasSummary?: boolean\n /** Composite items: the basket id (`meta.basket`), `null`/absent without one */\n basket?: string | null\n children?: TreeItem[]\n onSelect?: (e?: Event) => void\n onToggle?: (e?: Event) => void\n}\n\n/**\n * What a drag carries (serialized under `DRAG_MIME`): a document or folder of the store, or a composite of the open document.\n * `foreign-block` is a Baustein from another editor surface or document tree (it travels under `BLOCK_MIME`, not `DRAG_MIME`).\n */\nexport type DragPayload\n = | {\n type: 'document' | 'folder'\n /** Document id */\n id?: string\n /** Folder path of a folder item, or the folder a document currently lives in */\n path: string\n }\n /** Virtual mode: a composite of the open document, by block ID */\n | { type: 'composite', blockId: string }\n /** A Baustein dragged in from outside this tree; only its verdict is known until the drop reads the payload. */\n | { type: 'foreign-block' }\n\n/** Key of the virtual-mode root item (the open document). */\nexport const VIRTUAL_ROOT = 'vroot'\n\nexport const DRAG_MIME = 'application/x-tip-tree'\n\nexport const parentPath = (folder: string) => folder.slice(0, Math.max(folder.lastIndexOf('/'), 0))\nexport const joinPath = (folder: string, name: string) => (folder ? `${folder}/${name}` : name)\n\n/** Item keys from the root down to `folder`. */\nexport function ancestorKeys(folder: string): string[] {\n const keys = ['root']\n let path = ''\n for (const segment of folder.split('/').filter(Boolean)) {\n path = joinPath(path, segment)\n keys.push(folderKey(path))\n }\n return keys\n}\n\n/** The folder at `path` below (or at) `from`; undefined when there is none. */\nexport function findFolder(path: string, from: TreeFolder | null | undefined): TreeFolder | undefined {\n if (!from) return undefined\n if (from.path === path) return from\n for (const sub of from.folders) {\n const found = path === sub.path || path.startsWith(`${sub.path}/`) ? findFolder(path, sub) : undefined\n if (found) return found\n }\n return undefined\n}\n\n/** Names of the folders directly in `parent`, without the folder at path `except`. */\nexport const folderNamesIn = (tree: TreeFolder | null | undefined, parent: string, except?: string) =>\n (findFolder(parent, tree)?.folders ?? []).filter(sub => sub.path !== except).map(sub => sub.name)\n\nexport function collectFolderPaths(folder: TreeFolder, into: Set<string>) {\n into.add(folder.path)\n folder.folders.forEach(sub => collectFolderPaths(sub, into))\n}\n\n// --- outline of the open document ------------------------------------------------------------------------------\n\nexport type Outline = { structure: 'composite', nodes: OutlineNode[] } | { structure: 'flat', nodes: HeadingOutlineNode[] }\n\nexport const EMPTY_OUTLINE: Outline = { structure: 'composite', nodes: [] }\n\n/** The outline of the editor's document: the composites in composite structure, else the headings. */\nexport function readOutline(editor: Editor | undefined, structure: string, t: Translate): Outline {\n if (!editor || editor.isDestroyed) return EMPTY_OUTLINE\n const { doc } = editor.state\n return structure === 'composite'\n ? { structure: 'composite', nodes: compositeOutline(doc, new Set(), t('tree.untitled')) }\n : { structure: 'flat', nodes: headingOutline(doc, t('tree.untitled')) }\n}\n\n/** The outline entries as items; `select` runs when one is chosen. */\nexport function outlineItems(nodes: readonly (OutlineNode | HeadingOutlineNode)[], kinds: readonly CompositeKind[], select: (item: TreeItem) => void): TreeItem[] {\n return nodes.map((node) => {\n const composite = 'kind' in node\n const item: TreeItem = {\n key: `outline:${node.key}`,\n label: composite ? node.title : node.text,\n icon: composite ? compositeKind(kinds, node.kind).icon : `i-lucide-heading-${node.level}`,\n type: composite ? 'composite' : 'heading',\n blockId: node.id,\n pos: node.pos,\n kind: composite ? node.kind : undefined,\n hasSummary: composite ? node.hasSummary : undefined,\n basket: composite ? node.basket : undefined,\n children: node.children.length ? outlineItems(node.children as (OutlineNode | HeadingOutlineNode)[], kinds, select) : undefined,\n onSelect: () => select(item),\n onToggle: e => e?.preventDefault(), // the chevron toggles, a click reveals\n }\n return item\n })\n}\n\n/** The basket of a composite item among `baskets`; `undefined` without one (or when the host no longer offers it). */\nexport const itemBasket = (item: TreeItem, baskets: readonly Basket[]): Basket | undefined => basketById(baskets, item.basket)\n\n// --- items -----------------------------------------------------------------------------------------------------\n\n/** The store's folders and documents as items; the open document (`activeId`) lists `outline` below it. */\nexport function folderItem(folder: TreeFolder, activeId: string | undefined, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const root = folder.path === ''\n const item: TreeItem = {\n key: root ? 'root' : folderKey(folder.path),\n label: folder.name,\n icon: root ? 'i-lucide-hard-drive' : 'i-lucide-folder',\n type: root ? 'root' : 'folder',\n path: folder.path,\n children: [\n ...folder.folders.map(sub => folderItem(sub, activeId, outline, select)),\n ...folder.documents.map((doc) => {\n const document: TreeItem = {\n key: `doc:${doc.id}`,\n label: doc.title,\n icon: 'i-lucide-file',\n type: 'document',\n id: doc.id,\n folder: doc.folder,\n // The open document lists its outline; its chevron (not a row click) opens and closes it.\n children: doc.id === activeId && outline.length ? outline : undefined,\n onSelect: () => select(document),\n onToggle: e => e?.preventDefault(),\n }\n return document\n }),\n ],\n onSelect: () => select(item),\n }\n return item\n}\n\n/** Virtual mode: the open document is the root, its `folder`/`document` composites (and all others) the tree below it. */\nexport function virtualRootItem(label: string, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const item: TreeItem = {\n key: VIRTUAL_ROOT,\n label,\n icon: 'i-lucide-package',\n type: 'root',\n children: outline.length ? outline : undefined,\n onSelect: () => select(item),\n }\n return item\n}\n\n/** An item of the unfiltered tree and the key of its parent (`null` for a top-level item). */\nexport interface IndexedItem {\n item: TreeItem\n parent: string | null\n}\n\n/** Adds `list` and all items below it to `into` by key; returns `into`. */\nexport function indexItems(list: readonly TreeItem[], into: Map<string, IndexedItem>, parent: string | null = null) {\n for (const item of list) {\n into.set(item.key, { item, parent })\n if (item.children) indexItems(item.children, into, item.key)\n }\n return into\n}\n\n/** The items from the top-level one down to the item at `key` (empty when `key` is not in `index`). */\nexport function pathTo(index: ReadonlyMap<string, IndexedItem>, key: string): TreeItem[] {\n const path: TreeItem[] = []\n let entry = index.get(key)\n if (!entry) return path\n for (;;) {\n path.unshift(entry.item)\n if (entry.parent === null) return path\n entry = index.get(entry.parent)!\n }\n}\n\n/**\n * The breadcrumb of a focus (`TreeState.focusPath`): `document` is the open document's own row (the virtual root, or the\n * document row of the store; its label words the first crumb, which leaves focus), `crumbs` the Bausteine from there\n * down to the focused one, which is last.\n */\nexport function focusCrumbs(path: readonly TreeItem[]): { document: TreeItem | undefined, crumbs: TreeItem[] } {\n return {\n document: path.find(item => item.type === 'document' || item.key === VIRTUAL_ROOT),\n crumbs: path.filter(item => item.type === 'composite'),\n }\n}\n\n// --- filters ---------------------------------------------------------------------------------------------------\n\nconst fileKindsOf = new WeakMap<Translate, CompositeKind[]>()\n\n/** The kinds a folder lists: Ordner and Dokumente (in the language of `t`; one list per translator). */\nexport function fileKinds(t: Translate): CompositeKind[] {\n let kinds = fileKindsOf.get(t)\n if (!kinds) {\n const builtIn = defaultCompositeKinds(t)\n kinds = ['folder', 'document'].map(id => compositeKind(builtIn, id))\n fileKindsOf.set(t, kinds)\n }\n return kinds\n}\n\n/** What an item is filtered by: the composite's kind, or Ordner/Dokument for the store's folders and documents. */\nfunction itemKind(item: TreeItem): string | undefined {\n return item.kind ?? (item.type === 'folder' || item.type === 'document' ? item.type : undefined)\n}\n\n/** The kinds `item`'s filter can hide below it (`kinds` = those of the open editor). */\nexport function filterKinds(item: TreeItem, env: { structure: string, activeId: string | undefined, kinds: readonly CompositeKind[], t: Translate }): readonly CompositeKind[] {\n const composite = env.structure === 'composite'\n if (item.key === VIRTUAL_ROOT || item.type === 'composite') return env.kinds\n if (item.type === 'document') return composite && item.id === env.activeId ? env.kinds : []\n if (item.type === 'root' || item.type === 'folder') return fileKinds(env.t)\n return []\n}\n\n/**\n * Drops the items of kinds an ancestor's filter hides. Like the outline's own kind filter, their visible descendants\n * move up into the parent's list. A node's own filter applies to everything below it.\n */\nexport function applyNodeFilters(list: readonly TreeItem[], filters: NodeFilters, inherited: ReadonlySet<string> = new Set()): TreeItem[] {\n return list.flatMap((item) => {\n const own = filters[item.key]\n const children = item.children ? applyNodeFilters(item.children, filters, own?.length ? new Set([...inherited, ...own]) : inherited) : undefined\n const kind = itemKind(item)\n if (kind && inherited.has(kind)) return children ?? []\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n/**\n * Hides the composites that are not in a `shown` basket and have no visible descendant; ancestors that stay only\n * because of a visible descendant are collected in `dimmed`. Documents, folders, headings and the roots are never hidden.\n */\nexport function applyBasketFilter(list: readonly TreeItem[], shown: ReadonlySet<string>, dimmed: Set<string>): TreeItem[] {\n return list.flatMap((item) => {\n const children = item.children ? applyBasketFilter(item.children, shown, dimmed) : undefined\n if (item.type === 'composite') {\n const matches = !!item.basket && shown.has(item.basket)\n if (!matches && !children?.length) return []\n if (!matches) dimmed.add(item.key)\n }\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n// --- creating ----------------------------------------------------------------------------------------------------\n\n/** What can be created below an item of the open file: Ordner-Bausteine, imported Dokument-Bausteine. */\nexport interface Creatable {\n folder: boolean\n document: boolean\n}\n\n/**\n * What `item` takes as new Bausteine of the open file: the file itself (the virtual root, or its document row) takes\n * every kind, a Baustein what its kind accepts as children (`kindAccepts`). Nothing in a flat structure or without an editor.\n */\nexport function creatableBelow(item: TreeItem, env: { structure: string, hasEditor: boolean, activeId: string | undefined, kinds: readonly CompositeKind[] }): Creatable {\n const none: Creatable = { folder: false, document: false }\n if (env.structure !== 'composite' || !env.hasEditor) return none\n if (item.key === VIRTUAL_ROOT || (item.type === 'document' && item.id === env.activeId)) return { folder: true, document: true }\n if (item.type !== 'composite' || !item.blockId || !item.kind) return none\n return { folder: kindAccepts(env.kinds, item.kind, 'folder'), document: kindAccepts(env.kinds, item.kind, 'document') }\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\n/** What a dragged item carries: its document or folder, or in virtual mode its composite's block ID. */\nexport function dragPayloadOf(item: TreeItem): DragPayload {\n return item.type === 'composite'\n ? { type: 'composite', blockId: item.blockId! }\n : item.type === 'document'\n ? { type: 'document', id: item.id, path: item.folder ?? '' }\n : { type: 'folder', path: item.path! }\n}\n\n/** Whether `item` may be dragged. Virtual mode moves composites of the open document; blocks without an ID cannot be addressed. */\nexport function isDraggable(item: TreeItem, virtual: boolean): boolean {\n if (virtual) return item.type === 'composite' && !!item.blockId\n return item.type === 'document' || item.type === 'folder'\n}\n\n"],"mappings":"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"}