@ecsplico/editor 3.3.0 → 3.4.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.
- package/CHANGELOG.md +10 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/composites/components/CompositeChrome.js +10 -9
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/kinds.d.ts +4 -0
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/merge.d.ts +1 -1
- package/dist/features/composites/merge.js +5 -2
- package/dist/features/composites/merge.js.map +1 -1
- package/dist/features/composites/outline.d.ts +9 -2
- package/dist/features/composites/outline.js +41 -3
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/react/CompositeChrome.js +11 -9
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/rows.d.ts +2 -0
- package/dist/features/composites/rows.js +1 -0
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +9 -9
- package/dist/server/handlers/ai.d.ts +2 -2
- package/dist/tree/DocumentTree.js +5 -4
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +2 -1
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +1 -1
- package/dist/tree/tree-items.js +2 -1
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +6 -6
- package/dist/vue/SimpleEditor.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.vue.d.ts +6 -6
- package/package.json +1 -1
|
@@ -101,6 +101,7 @@ function CompositeChrome({ editor }) {
|
|
|
101
101
|
return [{
|
|
102
102
|
row,
|
|
103
103
|
anchor,
|
|
104
|
+
editable: editable && row.controls,
|
|
104
105
|
left,
|
|
105
106
|
top: (inStrip ? anchor.toggleTop + anchor.toggleSize / 2 : anchor.lastCenter) - ROW_HEIGHT / 2,
|
|
106
107
|
width: Math.max((frame?.right ?? anchor.right) - left, 120),
|
|
@@ -121,6 +122,7 @@ function CompositeChrome({ editor }) {
|
|
|
121
122
|
frame,
|
|
122
123
|
expanded,
|
|
123
124
|
revealedTitles,
|
|
125
|
+
editable,
|
|
124
126
|
t
|
|
125
127
|
]);
|
|
126
128
|
function toggleExpanded(row) {
|
|
@@ -141,8 +143,8 @@ function CompositeChrome({ editor }) {
|
|
|
141
143
|
children: /* @__PURE__ */ jsx("span", {
|
|
142
144
|
role: "button",
|
|
143
145
|
tabIndex: 0,
|
|
144
|
-
draggable: editable,
|
|
145
|
-
className: cn("absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary", editable ? "cursor-grab active:cursor-grabbing" : "cursor-pointer", iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && "opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100"),
|
|
146
|
+
draggable: item.editable,
|
|
147
|
+
className: cn("absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary", item.editable ? "cursor-grab active:cursor-grabbing" : "cursor-pointer", iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && "opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100"),
|
|
146
148
|
style: {
|
|
147
149
|
top: `${item.anchor.toggleTop}px`,
|
|
148
150
|
left: `${item.anchor.toggleLeft}px`,
|
|
@@ -150,7 +152,7 @@ function CompositeChrome({ editor }) {
|
|
|
150
152
|
height: `${item.anchor.toggleSize}px`
|
|
151
153
|
},
|
|
152
154
|
"aria-label": t("composites.chrome.details", { kind: item.row.kindLabel }),
|
|
153
|
-
title: editable ? t("composites.chrome.detailsDrag", { kind: item.row.kindLabel }) : t("composites.chrome.details", { kind: item.row.kindLabel }),
|
|
155
|
+
title: item.editable ? t("composites.chrome.detailsDrag", { kind: item.row.kindLabel }) : t("composites.chrome.details", { kind: item.row.kindLabel }),
|
|
154
156
|
onKeyDown: (event) => {
|
|
155
157
|
if (event.key !== "Enter" && event.key !== " ") return;
|
|
156
158
|
event.preventDefault();
|
|
@@ -170,9 +172,9 @@ function CompositeChrome({ editor }) {
|
|
|
170
172
|
children: /* @__PURE__ */ jsx(CompositeDetails, {
|
|
171
173
|
editor,
|
|
172
174
|
row: item.row,
|
|
173
|
-
editable
|
|
175
|
+
editable: item.editable
|
|
174
176
|
})
|
|
175
|
-
})] }), (editable || item.basket || item.status || item.derived || item.row.tagsVisible && item.row.tags.length > 0) && /* @__PURE__ */ jsxs("div", {
|
|
177
|
+
})] }), (item.editable || item.basket || item.status || item.derived || item.row.tagsVisible && item.row.tags.length > 0) && /* @__PURE__ */ jsxs("div", {
|
|
176
178
|
className: "absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background",
|
|
177
179
|
style: {
|
|
178
180
|
top: `${item.top}px`,
|
|
@@ -183,7 +185,7 @@ function CompositeChrome({ editor }) {
|
|
|
183
185
|
onMouseEnter: () => setChipsHoveredKey(item.row.key),
|
|
184
186
|
onMouseLeave: () => setChipsHoveredKey(null),
|
|
185
187
|
children: [
|
|
186
|
-
editable ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CompositeBasketMenu, {
|
|
188
|
+
item.editable ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CompositeBasketMenu, {
|
|
187
189
|
editor,
|
|
188
190
|
row: item.row,
|
|
189
191
|
children: /* @__PURE__ */ jsx("button", {
|
|
@@ -245,7 +247,7 @@ function CompositeChrome({ editor }) {
|
|
|
245
247
|
}), t("composites.derivation.stale")]
|
|
246
248
|
})
|
|
247
249
|
] }),
|
|
248
|
-
item.status && (editable ? /* @__PURE__ */ jsx(CompositeStatusMenu, {
|
|
250
|
+
item.status && (item.editable ? /* @__PURE__ */ jsx(CompositeStatusMenu, {
|
|
249
251
|
editor,
|
|
250
252
|
row: item.row,
|
|
251
253
|
children: /* @__PURE__ */ jsxs("button", {
|
|
@@ -269,7 +271,7 @@ function CompositeChrome({ editor }) {
|
|
|
269
271
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
270
272
|
className: "truncate",
|
|
271
273
|
children: ["#", tag]
|
|
272
|
-
}), editable && /* @__PURE__ */ jsx("button", {
|
|
274
|
+
}), item.editable && /* @__PURE__ */ jsx("button", {
|
|
273
275
|
type: "button",
|
|
274
276
|
className: "flex h-4 w-0 shrink-0 items-center justify-center overflow-hidden rounded-sm opacity-0 transition-all duration-150 group-hover:ml-1 group-hover:w-4 group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ml-1 focus-visible:w-4 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-primary",
|
|
275
277
|
"aria-label": t("composites.tag.remove", { tag }),
|
|
@@ -280,7 +282,7 @@ function CompositeChrome({ editor }) {
|
|
|
280
282
|
})
|
|
281
283
|
})]
|
|
282
284
|
}, tag)),
|
|
283
|
-
editable && item.row.tagsVisible && /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
285
|
+
item.editable && item.row.tagsVisible && /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
284
286
|
asChild: true,
|
|
285
287
|
children: /* @__PURE__ */ jsx("button", {
|
|
286
288
|
type: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompositeChrome.js","names":[],"sources":["../../../../src/features/composites/react/CompositeChrome.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { startBlockDrag } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useStore } from '../../../react/use-store'\nimport { useEditorSettings } from '../../../react/settings'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../react/ui/popover'\nimport { cn } from '../../../react/ui/utils'\nimport { compositeSectionOf } from '../anchors'\nimport { derivedChromeOf } from '../derived-chrome'\nimport { rowStatus } from '../meta-display'\nimport { basketById, basketOf, titleHiddenOf } from '../baskets'\nimport { compositeBasketsOf } from '../nodes'\nimport { compositeOfRow, type CompositeRow, setCompositeTags } from '../rows'\nimport { CompositeBasketMenu } from './CompositeBasketMenu'\nimport { CompositeDetails } from './CompositeDetails'\nimport { CompositeStatusMenu } from './CompositeStatusMenu'\nimport { CompositeTagsEditor } from './CompositeTagsEditor'\nimport { useCompositeAnchors } from './use-composite-anchors'\nimport { useCompositeRows } from './use-composite-rows'\n\n/**\n * Chrome of every composite, laid over the editor (mounted by the composites feature): on the title line the status\n * chip and one chip per tag (with a `+` chip to add tags), and on the front the kind icon. The icon is a button that\n * opens the details popover (kind with a confirmation before it changes, the kind's fields and the summary;\n * `CompositeDetails`) and, in an editable editor, the composite's drag handle: dragging it moves the composite\n * (`startBlockDrag`). With `blocks.kindIcon: 'hover'` it shows only while the composite is hovered, its popover is\n * open or the icon has keyboard focus. Chips and popover also render in read-only editors, without edit affordances.\n * The node view's own description and tags rows are hidden by CSS; this overlay shows and edits them.\n */\nexport function CompositeChrome({ editor }: { editor: Editor }) {\n const { rows } = useCompositeRows(editor)\n const { host, anchors, frame } = useCompositeAnchors(editor, rows)\n\n // `isEditable` is not reactive: follow it on every transaction (`EditorSurface` dispatches one after `setEditable`).\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable)\n useEffect(() => {\n const sync = () => setEditable(!editor.isDestroyed && editor.isEditable)\n sync()\n editor.on('transaction', sync)\n return () => {\n editor.off('transaction', sync)\n }\n }, [editor])\n\n const { settings } = useEditorSettings()\n const { t } = useEditorI18n()\n const iconOnHover = settings.blocks.kindIcon === 'hover'\n\n // Hover state of `kindIcon: 'hover'`: the keys of the rows whose composite is under the pointer (that one and every\n // composite around it), plus the row whose chips the pointer is on (they sit outside the editor's DOM).\n const [hoveredKeys, setHoveredKeys] = useState<ReadonlySet<string>>(NO_KEYS)\n const [chipsHoveredKey, setChipsHoveredKey] = useState<string | null>(null)\n const latestRows = useRef(rows)\n latestRows.current = rows\n\n useEffect(() => {\n let hoveredSection: Element | null = null\n const onMouseleave = () => {\n hoveredSection = null\n setHoveredKeys(NO_KEYS)\n }\n const onMouseover = (event: MouseEvent) => {\n const section = event.target instanceof Element ? event.target.closest('.composite') : null\n if (section === hoveredSection) return\n hoveredSection = section\n const chain = new Set<Element>()\n for (let el = section; el; el = el.parentElement?.closest('.composite') ?? null) chain.add(el)\n const { view, state } = editor\n setHoveredKeys(new Set(latestRows.current.filter((row) => {\n const found = compositeOfRow(state.doc, row)\n const dom = found ? view.nodeDOM(found.pos) : null\n const section = dom instanceof Element ? compositeSectionOf(dom) : null\n return section !== null && chain.has(section)\n }).map(row => row.key)))\n }\n\n onMouseleave()\n if (!iconOnHover || editor.isDestroyed) return\n const { dom } = editor.view\n dom.addEventListener('mouseover', onMouseover)\n dom.addEventListener('mouseleave', onMouseleave)\n return () => {\n dom.removeEventListener('mouseover', onMouseover)\n dom.removeEventListener('mouseleave', onMouseleave)\n }\n }, [editor, iconOnHover])\n\n function onIconDragStart(event: DragEvent, row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (!found || !startBlockDrag(editor, event, found.pos)) event.preventDefault()\n }\n\n const expanded = useStore(editor.storage.composite.expanded)\n const revealedTitles = useStore(editor.storage.composite.revealedTitles)\n\n const placed = useMemo(() => {\n const baskets = compositeBasketsOf(editor)\n // End of the collapsed derived Baustein the rows being walked sit in: its clipped content shows no chrome.\n let hiddenUntil = -1\n return rows.flatMap((row) => {\n if (row.pos < hiddenUntil) return []\n const derived = derivedChromeOf(editor, row)\n const open = !derived || !row.id || expanded.has(row.id)\n if (!open) hiddenUntil = row.end\n const anchor = anchors.get(row.key)\n if (!anchor) return []\n const titleHidden = titleHiddenOf(row)\n // A hidden title leaves nothing to measure: the chips sit in the strip beside the kind icon (`composites.css`).\n const inStrip = titleHidden && !(row.id && revealedTitles.has(row.id)) && anchor.toggleSize > 0\n // An empty title shows a placeholder the anchor cannot measure: keep clear of it.\n const left = inStrip ? anchor.toggleLeft + anchor.toggleSize + 6 : anchor.lineEnd + (anchor.titleEmpty ? 116 : 12)\n return [{\n row,\n anchor,\n left,\n top: (inStrip ? anchor.toggleTop + anchor.toggleSize / 2 : anchor.lastCenter) - ROW_HEIGHT / 2,\n width: Math.max((frame?.right ?? anchor.right) - left, 120),\n status: rowStatus(row, t),\n derived: derived && { stale: derived.stale, open, toggleable: !!row.id },\n basket: basketById(baskets, basketOf(row)) ?? null,\n titleHidden,\n }]\n })\n }, [editor, rows, anchors, frame, expanded, revealedTitles, t])\n\n function toggleExpanded(row: CompositeRow) {\n if (row.id) editor.commands.toggleCompositeExpanded(row.id)\n }\n\n function removeTag(row: CompositeRow, tag: string) {\n setCompositeTags(editor, row, row.tags.filter(candidate => candidate !== tag))\n }\n\n function toggleTitleHidden(row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (found) editor.commands.setCompositeTitleHidden(!titleHiddenOf(row), found.pos)\n }\n\n if (!host) return null\n\n return createPortal(\n <div className=\"absolute top-0 left-0 h-0 w-0\">\n {placed.map(item => (\n <Fragment key={item.row.key}>\n {item.anchor.toggleSize > 0 && (\n <Popover>\n <PopoverTrigger asChild>\n {/* A span, not a `button`: Firefox does not start drags from buttons. */}\n <span\n role=\"button\"\n tabIndex={0}\n draggable={editable}\n className={cn(\n 'absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary',\n editable ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer',\n iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && 'opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100',\n )}\n style={{ top: `${item.anchor.toggleTop}px`, left: `${item.anchor.toggleLeft}px`, width: `${item.anchor.toggleSize}px`, height: `${item.anchor.toggleSize}px` }}\n aria-label={t('composites.chrome.details', { kind: item.row.kindLabel })}\n title={editable ? t('composites.chrome.detailsDrag', { kind: item.row.kindLabel }) : t('composites.chrome.details', { kind: item.row.kindLabel })}\n onKeyDown={(event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n event.preventDefault()\n event.currentTarget.click()\n }}\n onDragStart={event => onIconDragStart(event.nativeEvent, item.row)}\n >\n <Icon key={item.row.icon} name={item.row.icon} className=\"size-3.5\" />\n </span>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={DETAILS_POPOVER}\n >\n <CompositeDetails editor={editor} row={item.row} editable={editable} />\n </PopoverContent>\n </Popover>\n )}\n\n {(editable || item.basket || item.status || item.derived || (item.row.tagsVisible && item.row.tags.length > 0)) && (\n <div\n className=\"absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background\"\n style={{ top: `${item.top}px`, left: `${item.left}px`, maxWidth: `${item.width}px`, height: `${ROW_HEIGHT}px` }}\n onMouseEnter={() => setChipsHoveredKey(item.row.key)}\n onMouseLeave={() => setChipsHoveredKey(null)}\n >\n {editable\n ? (\n <>\n <CompositeBasketMenu editor={editor} row={item.row}>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t('composites.basket.label')}\n title={item.basket ? `${t('composites.basket.label')}: ${item.basket.label}` : t('composites.basket.label')}\n >\n {item.basket\n ? <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n : <Icon name=\"i-lucide-circle-dashed\" className=\"size-3.5\" />}\n </button>\n </CompositeBasketMenu>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n title={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n onClick={() => toggleTitleHidden(item.row)}\n >\n <Icon name={item.titleHidden ? 'i-lucide-eye' : 'i-lucide-eye-off'} className=\"size-3.5\" />\n </button>\n </>\n )\n : item.basket && (\n <span className={CHIP} title={item.basket.label}>\n <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n </span>\n )}\n\n {item.derived && (\n <>\n {item.derived.toggleable && (\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-expanded={item.derived.open}\n aria-label={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n title={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n onClick={() => toggleExpanded(item.row)}\n >\n <Icon name={item.derived.open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'} className=\"size-3.5\" />\n </button>\n )}\n <span className={CHIP}>\n <Icon name=\"i-lucide-git-branch\" className=\"size-3.5\" />{t('composites.derivation.badge')}\n </span>\n {item.derived.stale && (\n <span className={cn(CHIP, 'text-(--sk-warning)')}>\n <Icon name=\"i-lucide-triangle-alert\" className=\"size-3.5\" />{t('composites.derivation.stale')}\n </span>\n )}\n </>\n )}\n\n {item.status && (editable\n ? (\n <CompositeStatusMenu editor={editor} row={item.row}>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.status.label', { status: item.status.label })}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </button>\n </CompositeStatusMenu>\n )\n : (\n <span className={CHIP}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </span>\n ))}\n\n {(item.row.tagsVisible ? item.row.tags : []).map(tag => (\n <span key={tag} className={cn(CHIP, 'group max-w-40 gap-0')}>\n <span className=\"truncate\">#{tag}</span>\n {editable && (\n <button\n type=\"button\"\n className=\"flex h-4 w-0 shrink-0 items-center justify-center overflow-hidden rounded-sm opacity-0 transition-all duration-150 group-hover:ml-1 group-hover:w-4 group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ml-1 focus-visible:w-4 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-primary\"\n aria-label={t('composites.tag.remove', { tag })}\n onClick={() => removeTag(item.row, tag)}\n >\n <Icon name=\"i-lucide-x\" className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n\n {editable && item.row.tagsVisible && (\n <Popover>\n <PopoverTrigger asChild>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.tag.add')} title={t('composites.tag.add')}>\n <Icon name=\"i-lucide-plus\" className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={6}\n className=\"w-auto gap-0 p-0\"\n onCloseAutoFocus={event => event.preventDefault()}\n >\n <div className=\"w-64 p-3\">\n <CompositeTagsEditor editor={editor} row={item.row} />\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n )}\n </Fragment>\n ))}\n </div>,\n host,\n )\n}\n\nconst NO_KEYS: ReadonlySet<string> = new Set()\n\n/** Chips start right after the title's last character and run on to the editor's right edge. */\nconst ROW_HEIGHT = 24\n\nconst CHIP = 'inline-flex h-5 shrink-0 items-center gap-1 rounded-sm px-1.5 text-xs leading-none whitespace-nowrap text-muted-foreground'\nconst CHIP_BUTTON = `${CHIP} transition-colors duration-150 hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary`\n\n/** `statusStyle` names Nuxt UI text colors: the shadcn counterparts (`--sk-info` and `--sk-success` come from the token stylesheets). */\nconst STATUS_COLOR: Record<string, string> = {\n 'text-muted': 'text-muted-foreground',\n 'text-info': 'text-(--sk-info)',\n 'text-success': 'text-(--sk-success)',\n 'text-dimmed': 'text-muted-foreground/70',\n}\n\n/** Scale and fade in from the icon (`transform-origin` is Radix's), a bit longer than the default; `composites.css` defines the keyframes and the reduced-motion override. */\nconst DETAILS_POPOVER = 'w-auto gap-0 p-0 composite-popover data-[state=open]:animate-[composite-pop-in_160ms_var(--ease-out)] data-[state=closed]:animate-[composite-pop-out_120ms_var(--ease-out)]'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompositeChrome.js","names":[],"sources":["../../../../src/features/composites/react/CompositeChrome.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { startBlockDrag } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useStore } from '../../../react/use-store'\nimport { useEditorSettings } from '../../../react/settings'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../react/ui/popover'\nimport { cn } from '../../../react/ui/utils'\nimport { compositeSectionOf } from '../anchors'\nimport { derivedChromeOf } from '../derived-chrome'\nimport { rowStatus } from '../meta-display'\nimport { basketById, basketOf, titleHiddenOf } from '../baskets'\nimport { compositeBasketsOf } from '../nodes'\nimport { compositeOfRow, type CompositeRow, setCompositeTags } from '../rows'\nimport { CompositeBasketMenu } from './CompositeBasketMenu'\nimport { CompositeDetails } from './CompositeDetails'\nimport { CompositeStatusMenu } from './CompositeStatusMenu'\nimport { CompositeTagsEditor } from './CompositeTagsEditor'\nimport { useCompositeAnchors } from './use-composite-anchors'\nimport { useCompositeRows } from './use-composite-rows'\n\n/**\n * Chrome of every composite, laid over the editor (mounted by the composites feature): on the title line the status\n * chip and one chip per tag (with a `+` chip to add tags), and on the front the kind icon. The icon is a button that\n * opens the details popover (kind with a confirmation before it changes, the kind's fields and the summary;\n * `CompositeDetails`) and, in an editable editor, the composite's drag handle: dragging it moves the composite\n * (`startBlockDrag`). With `blocks.kindIcon: 'hover'` it shows only while the composite is hovered, its popover is\n * open or the icon has keyboard focus. Chips and popover also render in read-only editors, without edit affordances.\n * The node view's own description and tags rows are hidden by CSS; this overlay shows and edits them.\n */\nexport function CompositeChrome({ editor }: { editor: Editor }) {\n const { rows } = useCompositeRows(editor)\n const { host, anchors, frame } = useCompositeAnchors(editor, rows)\n\n // `isEditable` is not reactive: follow it on every transaction (`EditorSurface` dispatches one after `setEditable`).\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable)\n useEffect(() => {\n const sync = () => setEditable(!editor.isDestroyed && editor.isEditable)\n sync()\n editor.on('transaction', sync)\n return () => {\n editor.off('transaction', sync)\n }\n }, [editor])\n\n const { settings } = useEditorSettings()\n const { t } = useEditorI18n()\n const iconOnHover = settings.blocks.kindIcon === 'hover'\n\n // Hover state of `kindIcon: 'hover'`: the keys of the rows whose composite is under the pointer (that one and every\n // composite around it), plus the row whose chips the pointer is on (they sit outside the editor's DOM).\n const [hoveredKeys, setHoveredKeys] = useState<ReadonlySet<string>>(NO_KEYS)\n const [chipsHoveredKey, setChipsHoveredKey] = useState<string | null>(null)\n const latestRows = useRef(rows)\n latestRows.current = rows\n\n useEffect(() => {\n let hoveredSection: Element | null = null\n const onMouseleave = () => {\n hoveredSection = null\n setHoveredKeys(NO_KEYS)\n }\n const onMouseover = (event: MouseEvent) => {\n const section = event.target instanceof Element ? event.target.closest('.composite') : null\n if (section === hoveredSection) return\n hoveredSection = section\n const chain = new Set<Element>()\n for (let el = section; el; el = el.parentElement?.closest('.composite') ?? null) chain.add(el)\n const { view, state } = editor\n setHoveredKeys(new Set(latestRows.current.filter((row) => {\n const found = compositeOfRow(state.doc, row)\n const dom = found ? view.nodeDOM(found.pos) : null\n const section = dom instanceof Element ? compositeSectionOf(dom) : null\n return section !== null && chain.has(section)\n }).map(row => row.key)))\n }\n\n onMouseleave()\n if (!iconOnHover || editor.isDestroyed) return\n const { dom } = editor.view\n dom.addEventListener('mouseover', onMouseover)\n dom.addEventListener('mouseleave', onMouseleave)\n return () => {\n dom.removeEventListener('mouseover', onMouseover)\n dom.removeEventListener('mouseleave', onMouseleave)\n }\n }, [editor, iconOnHover])\n\n function onIconDragStart(event: DragEvent, row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (!found || !startBlockDrag(editor, event, found.pos)) event.preventDefault()\n }\n\n const expanded = useStore(editor.storage.composite.expanded)\n const revealedTitles = useStore(editor.storage.composite.revealedTitles)\n\n const placed = useMemo(() => {\n const baskets = compositeBasketsOf(editor)\n // End of the collapsed derived Baustein the rows being walked sit in: its clipped content shows no chrome.\n let hiddenUntil = -1\n return rows.flatMap((row) => {\n if (row.pos < hiddenUntil) return []\n const derived = derivedChromeOf(editor, row)\n const open = !derived || !row.id || expanded.has(row.id)\n if (!open) hiddenUntil = row.end\n const anchor = anchors.get(row.key)\n if (!anchor) return []\n const titleHidden = titleHiddenOf(row)\n // A hidden title leaves nothing to measure: the chips sit in the strip beside the kind icon (`composites.css`).\n const inStrip = titleHidden && !(row.id && revealedTitles.has(row.id)) && anchor.toggleSize > 0\n // An empty title shows a placeholder the anchor cannot measure: keep clear of it.\n const left = inStrip ? anchor.toggleLeft + anchor.toggleSize + 6 : anchor.lineEnd + (anchor.titleEmpty ? 116 : 12)\n return [{\n row,\n anchor,\n // A kind without `chrome.controls` renders as in a read-only editor.\n editable: editable && row.controls,\n left,\n top: (inStrip ? anchor.toggleTop + anchor.toggleSize / 2 : anchor.lastCenter) - ROW_HEIGHT / 2,\n width: Math.max((frame?.right ?? anchor.right) - left, 120),\n status: rowStatus(row, t),\n derived: derived && { stale: derived.stale, open, toggleable: !!row.id },\n basket: basketById(baskets, basketOf(row)) ?? null,\n titleHidden,\n }]\n })\n }, [editor, rows, anchors, frame, expanded, revealedTitles, editable, t])\n\n function toggleExpanded(row: CompositeRow) {\n if (row.id) editor.commands.toggleCompositeExpanded(row.id)\n }\n\n function removeTag(row: CompositeRow, tag: string) {\n setCompositeTags(editor, row, row.tags.filter(candidate => candidate !== tag))\n }\n\n function toggleTitleHidden(row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (found) editor.commands.setCompositeTitleHidden(!titleHiddenOf(row), found.pos)\n }\n\n if (!host) return null\n\n return createPortal(\n <div className=\"absolute top-0 left-0 h-0 w-0\">\n {placed.map(item => (\n <Fragment key={item.row.key}>\n {item.anchor.toggleSize > 0 && (\n <Popover>\n <PopoverTrigger asChild>\n {/* A span, not a `button`: Firefox does not start drags from buttons. */}\n <span\n role=\"button\"\n tabIndex={0}\n draggable={item.editable}\n className={cn(\n 'absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary',\n item.editable ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer',\n iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && 'opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100',\n )}\n style={{ top: `${item.anchor.toggleTop}px`, left: `${item.anchor.toggleLeft}px`, width: `${item.anchor.toggleSize}px`, height: `${item.anchor.toggleSize}px` }}\n aria-label={t('composites.chrome.details', { kind: item.row.kindLabel })}\n title={item.editable ? t('composites.chrome.detailsDrag', { kind: item.row.kindLabel }) : t('composites.chrome.details', { kind: item.row.kindLabel })}\n onKeyDown={(event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n event.preventDefault()\n event.currentTarget.click()\n }}\n onDragStart={event => onIconDragStart(event.nativeEvent, item.row)}\n >\n <Icon key={item.row.icon} name={item.row.icon} className=\"size-3.5\" />\n </span>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={DETAILS_POPOVER}\n >\n <CompositeDetails editor={editor} row={item.row} editable={item.editable} />\n </PopoverContent>\n </Popover>\n )}\n\n {(item.editable || item.basket || item.status || item.derived || (item.row.tagsVisible && item.row.tags.length > 0)) && (\n <div\n className=\"absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background\"\n style={{ top: `${item.top}px`, left: `${item.left}px`, maxWidth: `${item.width}px`, height: `${ROW_HEIGHT}px` }}\n onMouseEnter={() => setChipsHoveredKey(item.row.key)}\n onMouseLeave={() => setChipsHoveredKey(null)}\n >\n {item.editable\n ? (\n <>\n <CompositeBasketMenu editor={editor} row={item.row}>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t('composites.basket.label')}\n title={item.basket ? `${t('composites.basket.label')}: ${item.basket.label}` : t('composites.basket.label')}\n >\n {item.basket\n ? <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n : <Icon name=\"i-lucide-circle-dashed\" className=\"size-3.5\" />}\n </button>\n </CompositeBasketMenu>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n title={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n onClick={() => toggleTitleHidden(item.row)}\n >\n <Icon name={item.titleHidden ? 'i-lucide-eye' : 'i-lucide-eye-off'} className=\"size-3.5\" />\n </button>\n </>\n )\n : item.basket && (\n <span className={CHIP} title={item.basket.label}>\n <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n </span>\n )}\n\n {item.derived && (\n <>\n {item.derived.toggleable && (\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-expanded={item.derived.open}\n aria-label={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n title={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n onClick={() => toggleExpanded(item.row)}\n >\n <Icon name={item.derived.open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'} className=\"size-3.5\" />\n </button>\n )}\n <span className={CHIP}>\n <Icon name=\"i-lucide-git-branch\" className=\"size-3.5\" />{t('composites.derivation.badge')}\n </span>\n {item.derived.stale && (\n <span className={cn(CHIP, 'text-(--sk-warning)')}>\n <Icon name=\"i-lucide-triangle-alert\" className=\"size-3.5\" />{t('composites.derivation.stale')}\n </span>\n )}\n </>\n )}\n\n {item.status && (item.editable\n ? (\n <CompositeStatusMenu editor={editor} row={item.row}>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.status.label', { status: item.status.label })}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </button>\n </CompositeStatusMenu>\n )\n : (\n <span className={CHIP}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </span>\n ))}\n\n {(item.row.tagsVisible ? item.row.tags : []).map(tag => (\n <span key={tag} className={cn(CHIP, 'group max-w-40 gap-0')}>\n <span className=\"truncate\">#{tag}</span>\n {item.editable && (\n <button\n type=\"button\"\n className=\"flex h-4 w-0 shrink-0 items-center justify-center overflow-hidden rounded-sm opacity-0 transition-all duration-150 group-hover:ml-1 group-hover:w-4 group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ml-1 focus-visible:w-4 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-primary\"\n aria-label={t('composites.tag.remove', { tag })}\n onClick={() => removeTag(item.row, tag)}\n >\n <Icon name=\"i-lucide-x\" className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n\n {item.editable && item.row.tagsVisible && (\n <Popover>\n <PopoverTrigger asChild>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.tag.add')} title={t('composites.tag.add')}>\n <Icon name=\"i-lucide-plus\" className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={6}\n className=\"w-auto gap-0 p-0\"\n onCloseAutoFocus={event => event.preventDefault()}\n >\n <div className=\"w-64 p-3\">\n <CompositeTagsEditor editor={editor} row={item.row} />\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n )}\n </Fragment>\n ))}\n </div>,\n host,\n )\n}\n\nconst NO_KEYS: ReadonlySet<string> = new Set()\n\n/** Chips start right after the title's last character and run on to the editor's right edge. */\nconst ROW_HEIGHT = 24\n\nconst CHIP = 'inline-flex h-5 shrink-0 items-center gap-1 rounded-sm px-1.5 text-xs leading-none whitespace-nowrap text-muted-foreground'\nconst CHIP_BUTTON = `${CHIP} transition-colors duration-150 hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary`\n\n/** `statusStyle` names Nuxt UI text colors: the shadcn counterparts (`--sk-info` and `--sk-success` come from the token stylesheets). */\nconst STATUS_COLOR: Record<string, string> = {\n 'text-muted': 'text-muted-foreground',\n 'text-info': 'text-(--sk-info)',\n 'text-success': 'text-(--sk-success)',\n 'text-dimmed': 'text-muted-foreground/70',\n}\n\n/** Scale and fade in from the icon (`transform-origin` is Radix's), a bit longer than the default; `composites.css` defines the keyframes and the reduced-motion override. */\nconst DETAILS_POPOVER = 'w-auto gap-0 p-0 composite-popover data-[state=open]:animate-[composite-pop-in_160ms_var(--ease-out)] data-[state=closed]:animate-[composite-pop-out_120ms_var(--ease-out)]'\n"],"mappings":"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"}
|
|
@@ -29,6 +29,8 @@ export interface CompositeRow {
|
|
|
29
29
|
fields: readonly CompositeMetaField[];
|
|
30
30
|
/** `false` when the composite's kind hides its tag row (`CompositeKind.chrome.tags === false`): chips, the add-tag control and the tags field stay away. */
|
|
31
31
|
tagsVisible: boolean;
|
|
32
|
+
/** `false` when the composite's kind has `chrome.controls === false`: its chrome (basket, status, title visibility, tags, drag handle) is read-only even in an editable editor. */
|
|
33
|
+
controls: boolean;
|
|
32
34
|
/** Nesting level among composites, 0 = outermost. */
|
|
33
35
|
depth: number;
|
|
34
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rows.js","names":[],"sources":["../../../src/features/composites/rows.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport { type CompositeKind, type CompositeMetaField, compositeKind } from './kinds'\nimport { referenceLabel, referenceTarget } from './reference-label'\n\n/** One composite („Baustein“) of a document with its metadata, flattened in document order. */\nexport interface CompositeRow {\n /** Block ID, or `pos:<pos>` for a composite without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite when the row was read; the write helpers re-resolve it, so a stale value is safe. */\n pos: number\n /** Position right after the composite, as of when the row was read. */\n end: number\n kind: string\n kindLabel: string\n icon: string\n /** Title text, empty for an untitled composite. */\n title: string\n /** Text of the description (summary), empty when there is none. */\n description: string\n /**\n * The description holds text only (marks such as bold, author or comment allowed, no mentions or tags); only then\n * can `setCompositeDescription` write it without losing inline content.\n */\n descriptionTextOnly: boolean\n tags: string[]\n meta: Record<string, unknown>\n /** The meta fields of the composite's kind. */\n fields: readonly CompositeMetaField[]\n /** `false` when the composite's kind hides its tag row (`CompositeKind.chrome.tags === false`): chips, the add-tag control and the tags field stay away. */\n tagsVisible: boolean\n /** Nesting level among composites, 0 = outermost. */\n depth: number\n}\n\n/** A distinct `@mention` chip of a document. */\nexport interface CompositeMention {\n id: string\n label: string\n color: string | null\n}\n\n/** A reference to a Baustein: a `reference` chip in the text of block `from`, or a `reference` field of the Baustein `from`. */\nexport interface CompositeReference {\n /** Block ID of the referenced Baustein. */\n target: string\n /** Block ID of the nearest block around the chip (the Baustein itself for a field); `null` when that block has no ID yet. */\n from: string | null\n /** The chip's locator (`S. 12`, `03:14`); `null` for a field or a chip without one. */\n locator: string | null\n}\n\nconst COMPOSITE = 'composite'\n\n/** True when `node`'s content is empty or text only (with any marks, but no inline nodes such as mentions or tags). */\nfunction isTextOnly(node: PMNode): boolean {\n let textOnly = true\n node.forEach((child) => {\n if (!child.isText) textOnly = false\n })\n return textOnly\n}\n\n/**\n * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, the distinct\n * `@mention` chips anywhere in the document and every reference (`reference` chips and `reference` fields) in document\n * order. Unknown kinds get the `section` kind's presentation under their own ID.\n */\nexport function compositeRows(doc: PMNode, kinds: readonly CompositeKind[]): { rows: CompositeRow[], mentions: CompositeMention[], references: CompositeReference[] } {\n const rows: CompositeRow[] = []\n const mentions = new Map<string, CompositeMention>()\n const references: CompositeReference[] = []\n\n function walk(parent: PMNode, start: number, depth: number, block: string | null) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === COMPOSITE) {\n const kind = compositeKind(kinds, String(node.attrs.kind))\n const id = blockIdOf(node)\n const tags: unknown = node.maybeChild(2)?.attrs.tags\n const description = node.maybeChild(1)\n const row: CompositeRow = {\n key: id ?? `pos:${pos}`,\n id,\n pos,\n end: pos + node.nodeSize,\n kind: kind.id,\n kindLabel: kind.label,\n icon: kind.icon,\n title: node.firstChild?.textContent.trim() ?? '',\n description: description?.textContent ?? '',\n descriptionTextOnly: description ? isTextOnly(description) : true,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n meta: (node.attrs.meta ?? {}) as Record<string, unknown>,\n fields: kind.fields,\n tagsVisible: kind.chrome?.tags !== false,\n depth,\n }\n rows.push(row)\n for (const field of kind.fields) {\n const value = field.type === 'reference' ? row.meta[field.key] : undefined\n if (typeof value === 'string' && value) references.push({ target: value, from: id, locator: null })\n }\n walk(node, pos + 1, depth + 1, id ?? block)\n }\n else if (node.type.name === 'reference') {\n const target = String(node.attrs.target ?? '')\n if (target) references.push({ target, from: block, locator: typeof node.attrs.locator === 'string' && node.attrs.locator ? node.attrs.locator : null })\n }\n else if (node.type.name === 'mention') {\n const mentionId = String(node.attrs.id ?? '')\n if (mentionId && !mentions.has(mentionId)) {\n mentions.set(mentionId, {\n id: mentionId,\n label: String(node.attrs.label ?? mentionId),\n color: typeof node.attrs.color === 'string' ? node.attrs.color : null,\n })\n }\n }\n else if (!node.isLeaf) {\n walk(node, pos + 1, depth, blockIdOf(node) ?? block)\n }\n })\n }\n walk(doc, 0, 0, null)\n return { rows, mentions: [...mentions.values()], references }\n}\n\n/** A place that refers to a Baustein: the block holding the chip (or the Baustein with the field) and a short text of it. */\nexport interface CompositeBacklink {\n from: string\n locator: string | null\n excerpt: string\n}\n\nconst EXCERPT_LENGTH = 40\n\n/** The text of `node` as read on screen: inline atoms count as their label (`@name`, `#tag`, a reference chip's short citation + locator), not as nothing. */\nfunction visibleText(doc: PMNode, kinds: readonly CompositeKind[], node: PMNode): string {\n return node.textBetween(0, node.content.size, ' ', (leaf) => {\n const attrs = leaf.attrs\n switch (leaf.type.name) {\n case 'mention': return `@${String(attrs.label ?? attrs.id ?? '')}`\n case 'tag': return `#${String(attrs.name ?? '')}`\n case 'reference': {\n const target = findBlockById(doc, String(attrs.target ?? ''))\n const label = target?.node.type.name === COMPOSITE ? referenceLabel(referenceTarget(target.node, kinds)) : String(attrs.label ?? '')\n return typeof attrs.locator === 'string' && attrs.locator ? `${label}, ${attrs.locator}` : label\n }\n default: return ''\n }\n })\n}\n\n/** Every reference to the Baustein `target` in document order; references whose enclosing block has no ID (or is gone) are left out, as nothing could reveal them. */\nexport function compositeBacklinks(doc: PMNode, kinds: readonly CompositeKind[], target: string): CompositeBacklink[] {\n const links: CompositeBacklink[] = []\n for (const reference of compositeRows(doc, kinds).references) {\n if (reference.target !== target || !reference.from) continue\n const found = findBlockById(doc, reference.from)\n if (!found) continue\n const source = found.node.type.name === COMPOSITE ? found.node.firstChild : found.node\n const text = source ? visibleText(doc, kinds, source).trim() : ''\n links.push({ from: reference.from, locator: reference.locator, excerpt: text.length > EXCERPT_LENGTH ? `${text.slice(0, EXCERPT_LENGTH)}…` : text })\n }\n return links\n}\n\n/** The innermost row around document position `pos`, `null` outside of any composite. */\nexport function compositeRowAt(rows: readonly CompositeRow[], pos: number): CompositeRow | null {\n let found: CompositeRow | null = null\n // Pre-order: the last match is the innermost composite.\n for (const row of rows) {\n if (row.pos <= pos && pos < row.end) found = row\n }\n return found\n}\n\n/**\n * The composite `row` stands for in `doc`: by block ID, else (the row has no ID yet) by its position.\n * Positions are re-resolved on every call, so a stale `row.pos` is safe for rows with an ID.\n */\nexport function compositeOfRow(doc: PMNode, row: Pick<CompositeRow, 'id' | 'pos'>): { pos: number, node: PMNode } | null {\n if (row.id) {\n const found = findBlockById(doc, row.id)\n return found?.node.type.name === COMPOSITE ? found : null\n }\n const node = doc.nodeAt(row.pos)\n return node?.type.name === COMPOSITE ? { pos: row.pos, node } : null\n}\n\n/**\n * `meta` with `value` stored under `field.key`: a number field converts a numeric string, an empty value (`''`,\n * `null`, `undefined`, `NaN`) removes the key. A `multiselect` field keeps the string entries of an array value and\n * removes the key when none is left. Returns a new object; `meta` is not changed.\n */\nexport function compositeMetaWith(meta: Record<string, unknown>, field: Pick<CompositeMetaField, 'key' | 'type'>, value: unknown): Record<string, unknown> {\n const next = { ...meta }\n if (field.type === 'multiselect') {\n const chosen = Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []\n if (chosen.length) next[field.key] = chosen\n else delete next[field.key]\n return next\n }\n const stored = field.type === 'number' && typeof value === 'string' && value !== '' ? Number(value) : value\n if (stored === '' || stored === null || stored === undefined || Number.isNaN(stored)) delete next[field.key]\n else next[field.key] = stored\n return next\n}\n\n/** Stores `value` under the meta field `key` of the row's composite (see `compositeMetaWith`), in one transaction. */\nexport function setCompositeMetaField(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos' | 'fields'>, key: string, value: unknown): boolean {\n const field = row.fields.find(candidate => candidate.key === key) ?? { key, type: 'text' as const }\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n if (!found) return false\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: compositeMetaWith(found.node.attrs.meta ?? {}, field, value) })\n return true\n })\n}\n\n/** Replaces the tags row of the row's composite (empty names are dropped), in one transaction. */\nexport function setCompositeTags(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, tags: readonly string[]): boolean {\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n const tagsNode = found?.node.maybeChild(2)\n if (!found || !title || !description || tagsNode?.type.name !== 'compositeTags') return false\n const tagsPos = found.pos + 1 + title.nodeSize + description.nodeSize\n tr.setNodeMarkup(tagsPos, undefined, { ...tagsNode.attrs, tags: tags.filter(tag => tag) })\n return true\n })\n}\n\n/** The text `setCompositeDescription` stores for `text`: the description is one line, so line breaks collapse to spaces. */\nexport function compositeDescriptionText(text: string): string {\n return text.replace(/\\s*\\n\\s*/g, ' ').trim()\n}\n\n/**\n * Sets the text of the row's composite description to `compositeDescriptionText(text)`; an empty text empties it.\n * Only the changed span is replaced, so marks on the unchanged text (bold, author, comment) stay. Refuses (`false`)\n * when the description holds a mention or tag (it would be lost), and, when `expected` is given, when the\n * description no longer holds that text (a peer changed it since the caller read it; writing would drop that change).\n */\nexport function setCompositeDescription(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, text: string, expected?: string): boolean {\n const value = compositeDescriptionText(text)\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n if (!found || !title || !description || description.type.name !== 'compositeDescription') return false\n if (!isTextOnly(description)) return false\n const current = description.textContent\n if (expected !== undefined && current !== expected) return false\n if (current === value) return true\n // Text only: every character is one position. Keep the common prefix and suffix, replace what lies between.\n let prefix = 0\n const max = Math.min(current.length, value.length)\n while (prefix < max && current[prefix] === value[prefix]) prefix++\n let suffix = 0\n while (suffix < max - prefix && current[current.length - 1 - suffix] === value[value.length - 1 - suffix]) suffix++\n const start = found.pos + 1 + title.nodeSize + 1\n // `insertText` with an empty text deletes the range; inserted text takes the marks at its start, as typing does.\n tr.insertText(value.slice(prefix, value.length - suffix), start + prefix, start + current.length - suffix)\n return true\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rows.js","names":[],"sources":["../../../src/features/composites/rows.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport { type CompositeKind, type CompositeMetaField, compositeKind } from './kinds'\nimport { referenceLabel, referenceTarget } from './reference-label'\n\n/** One composite („Baustein“) of a document with its metadata, flattened in document order. */\nexport interface CompositeRow {\n /** Block ID, or `pos:<pos>` for a composite without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite when the row was read; the write helpers re-resolve it, so a stale value is safe. */\n pos: number\n /** Position right after the composite, as of when the row was read. */\n end: number\n kind: string\n kindLabel: string\n icon: string\n /** Title text, empty for an untitled composite. */\n title: string\n /** Text of the description (summary), empty when there is none. */\n description: string\n /**\n * The description holds text only (marks such as bold, author or comment allowed, no mentions or tags); only then\n * can `setCompositeDescription` write it without losing inline content.\n */\n descriptionTextOnly: boolean\n tags: string[]\n meta: Record<string, unknown>\n /** The meta fields of the composite's kind. */\n fields: readonly CompositeMetaField[]\n /** `false` when the composite's kind hides its tag row (`CompositeKind.chrome.tags === false`): chips, the add-tag control and the tags field stay away. */\n tagsVisible: boolean\n /** `false` when the composite's kind has `chrome.controls === false`: its chrome (basket, status, title visibility, tags, drag handle) is read-only even in an editable editor. */\n controls: boolean\n /** Nesting level among composites, 0 = outermost. */\n depth: number\n}\n\n/** A distinct `@mention` chip of a document. */\nexport interface CompositeMention {\n id: string\n label: string\n color: string | null\n}\n\n/** A reference to a Baustein: a `reference` chip in the text of block `from`, or a `reference` field of the Baustein `from`. */\nexport interface CompositeReference {\n /** Block ID of the referenced Baustein. */\n target: string\n /** Block ID of the nearest block around the chip (the Baustein itself for a field); `null` when that block has no ID yet. */\n from: string | null\n /** The chip's locator (`S. 12`, `03:14`); `null` for a field or a chip without one. */\n locator: string | null\n}\n\nconst COMPOSITE = 'composite'\n\n/** True when `node`'s content is empty or text only (with any marks, but no inline nodes such as mentions or tags). */\nfunction isTextOnly(node: PMNode): boolean {\n let textOnly = true\n node.forEach((child) => {\n if (!child.isText) textOnly = false\n })\n return textOnly\n}\n\n/**\n * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, the distinct\n * `@mention` chips anywhere in the document and every reference (`reference` chips and `reference` fields) in document\n * order. Unknown kinds get the `section` kind's presentation under their own ID.\n */\nexport function compositeRows(doc: PMNode, kinds: readonly CompositeKind[]): { rows: CompositeRow[], mentions: CompositeMention[], references: CompositeReference[] } {\n const rows: CompositeRow[] = []\n const mentions = new Map<string, CompositeMention>()\n const references: CompositeReference[] = []\n\n function walk(parent: PMNode, start: number, depth: number, block: string | null) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === COMPOSITE) {\n const kind = compositeKind(kinds, String(node.attrs.kind))\n const id = blockIdOf(node)\n const tags: unknown = node.maybeChild(2)?.attrs.tags\n const description = node.maybeChild(1)\n const row: CompositeRow = {\n key: id ?? `pos:${pos}`,\n id,\n pos,\n end: pos + node.nodeSize,\n kind: kind.id,\n kindLabel: kind.label,\n icon: kind.icon,\n title: node.firstChild?.textContent.trim() ?? '',\n description: description?.textContent ?? '',\n descriptionTextOnly: description ? isTextOnly(description) : true,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n meta: (node.attrs.meta ?? {}) as Record<string, unknown>,\n fields: kind.fields,\n tagsVisible: kind.chrome?.tags !== false,\n controls: kind.chrome?.controls !== false,\n depth,\n }\n rows.push(row)\n for (const field of kind.fields) {\n const value = field.type === 'reference' ? row.meta[field.key] : undefined\n if (typeof value === 'string' && value) references.push({ target: value, from: id, locator: null })\n }\n walk(node, pos + 1, depth + 1, id ?? block)\n }\n else if (node.type.name === 'reference') {\n const target = String(node.attrs.target ?? '')\n if (target) references.push({ target, from: block, locator: typeof node.attrs.locator === 'string' && node.attrs.locator ? node.attrs.locator : null })\n }\n else if (node.type.name === 'mention') {\n const mentionId = String(node.attrs.id ?? '')\n if (mentionId && !mentions.has(mentionId)) {\n mentions.set(mentionId, {\n id: mentionId,\n label: String(node.attrs.label ?? mentionId),\n color: typeof node.attrs.color === 'string' ? node.attrs.color : null,\n })\n }\n }\n else if (!node.isLeaf) {\n walk(node, pos + 1, depth, blockIdOf(node) ?? block)\n }\n })\n }\n walk(doc, 0, 0, null)\n return { rows, mentions: [...mentions.values()], references }\n}\n\n/** A place that refers to a Baustein: the block holding the chip (or the Baustein with the field) and a short text of it. */\nexport interface CompositeBacklink {\n from: string\n locator: string | null\n excerpt: string\n}\n\nconst EXCERPT_LENGTH = 40\n\n/** The text of `node` as read on screen: inline atoms count as their label (`@name`, `#tag`, a reference chip's short citation + locator), not as nothing. */\nfunction visibleText(doc: PMNode, kinds: readonly CompositeKind[], node: PMNode): string {\n return node.textBetween(0, node.content.size, ' ', (leaf) => {\n const attrs = leaf.attrs\n switch (leaf.type.name) {\n case 'mention': return `@${String(attrs.label ?? attrs.id ?? '')}`\n case 'tag': return `#${String(attrs.name ?? '')}`\n case 'reference': {\n const target = findBlockById(doc, String(attrs.target ?? ''))\n const label = target?.node.type.name === COMPOSITE ? referenceLabel(referenceTarget(target.node, kinds)) : String(attrs.label ?? '')\n return typeof attrs.locator === 'string' && attrs.locator ? `${label}, ${attrs.locator}` : label\n }\n default: return ''\n }\n })\n}\n\n/** Every reference to the Baustein `target` in document order; references whose enclosing block has no ID (or is gone) are left out, as nothing could reveal them. */\nexport function compositeBacklinks(doc: PMNode, kinds: readonly CompositeKind[], target: string): CompositeBacklink[] {\n const links: CompositeBacklink[] = []\n for (const reference of compositeRows(doc, kinds).references) {\n if (reference.target !== target || !reference.from) continue\n const found = findBlockById(doc, reference.from)\n if (!found) continue\n const source = found.node.type.name === COMPOSITE ? found.node.firstChild : found.node\n const text = source ? visibleText(doc, kinds, source).trim() : ''\n links.push({ from: reference.from, locator: reference.locator, excerpt: text.length > EXCERPT_LENGTH ? `${text.slice(0, EXCERPT_LENGTH)}…` : text })\n }\n return links\n}\n\n/** The innermost row around document position `pos`, `null` outside of any composite. */\nexport function compositeRowAt(rows: readonly CompositeRow[], pos: number): CompositeRow | null {\n let found: CompositeRow | null = null\n // Pre-order: the last match is the innermost composite.\n for (const row of rows) {\n if (row.pos <= pos && pos < row.end) found = row\n }\n return found\n}\n\n/**\n * The composite `row` stands for in `doc`: by block ID, else (the row has no ID yet) by its position.\n * Positions are re-resolved on every call, so a stale `row.pos` is safe for rows with an ID.\n */\nexport function compositeOfRow(doc: PMNode, row: Pick<CompositeRow, 'id' | 'pos'>): { pos: number, node: PMNode } | null {\n if (row.id) {\n const found = findBlockById(doc, row.id)\n return found?.node.type.name === COMPOSITE ? found : null\n }\n const node = doc.nodeAt(row.pos)\n return node?.type.name === COMPOSITE ? { pos: row.pos, node } : null\n}\n\n/**\n * `meta` with `value` stored under `field.key`: a number field converts a numeric string, an empty value (`''`,\n * `null`, `undefined`, `NaN`) removes the key. A `multiselect` field keeps the string entries of an array value and\n * removes the key when none is left. Returns a new object; `meta` is not changed.\n */\nexport function compositeMetaWith(meta: Record<string, unknown>, field: Pick<CompositeMetaField, 'key' | 'type'>, value: unknown): Record<string, unknown> {\n const next = { ...meta }\n if (field.type === 'multiselect') {\n const chosen = Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []\n if (chosen.length) next[field.key] = chosen\n else delete next[field.key]\n return next\n }\n const stored = field.type === 'number' && typeof value === 'string' && value !== '' ? Number(value) : value\n if (stored === '' || stored === null || stored === undefined || Number.isNaN(stored)) delete next[field.key]\n else next[field.key] = stored\n return next\n}\n\n/** Stores `value` under the meta field `key` of the row's composite (see `compositeMetaWith`), in one transaction. */\nexport function setCompositeMetaField(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos' | 'fields'>, key: string, value: unknown): boolean {\n const field = row.fields.find(candidate => candidate.key === key) ?? { key, type: 'text' as const }\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n if (!found) return false\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: compositeMetaWith(found.node.attrs.meta ?? {}, field, value) })\n return true\n })\n}\n\n/** Replaces the tags row of the row's composite (empty names are dropped), in one transaction. */\nexport function setCompositeTags(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, tags: readonly string[]): boolean {\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n const tagsNode = found?.node.maybeChild(2)\n if (!found || !title || !description || tagsNode?.type.name !== 'compositeTags') return false\n const tagsPos = found.pos + 1 + title.nodeSize + description.nodeSize\n tr.setNodeMarkup(tagsPos, undefined, { ...tagsNode.attrs, tags: tags.filter(tag => tag) })\n return true\n })\n}\n\n/** The text `setCompositeDescription` stores for `text`: the description is one line, so line breaks collapse to spaces. */\nexport function compositeDescriptionText(text: string): string {\n return text.replace(/\\s*\\n\\s*/g, ' ').trim()\n}\n\n/**\n * Sets the text of the row's composite description to `compositeDescriptionText(text)`; an empty text empties it.\n * Only the changed span is replaced, so marks on the unchanged text (bold, author, comment) stay. Refuses (`false`)\n * when the description holds a mention or tag (it would be lost), and, when `expected` is given, when the\n * description no longer holds that text (a peer changed it since the caller read it; writing would drop that change).\n */\nexport function setCompositeDescription(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, text: string, expected?: string): boolean {\n const value = compositeDescriptionText(text)\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n if (!found || !title || !description || description.type.name !== 'compositeDescription') return false\n if (!isTextOnly(description)) return false\n const current = description.textContent\n if (expected !== undefined && current !== expected) return false\n if (current === value) return true\n // Text only: every character is one position. Keep the common prefix and suffix, replace what lies between.\n let prefix = 0\n const max = Math.min(current.length, value.length)\n while (prefix < max && current[prefix] === value[prefix]) prefix++\n let suffix = 0\n while (suffix < max - prefix && current[current.length - 1 - suffix] === value[value.length - 1 - suffix]) suffix++\n const start = found.pos + 1 + title.nodeSize + 1\n // `insertText` with an empty text deletes the range; inserted text takes the marks at its start, as typing does.\n tr.insertText(value.slice(prefix, value.length - suffix), start + prefix, start + current.length - suffix)\n return true\n })\n}\n"],"mappings":"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"}
|
|
@@ -41,28 +41,28 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
41
41
|
settingsOpen: import("vue").WritableComputedRef<boolean, boolean>;
|
|
42
42
|
settingsTab: import("vue").WritableComputedRef<string | undefined, string | undefined>;
|
|
43
43
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
+
"update:modelValue": (markdown: string) => any;
|
|
45
|
+
ready: (editor: Editor) => any;
|
|
46
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
44
47
|
"update:sidebarOpen": (value: boolean) => any;
|
|
45
48
|
"update:commentsOpen": (value: boolean) => any;
|
|
46
|
-
|
|
47
|
-
"update:modelValue": (markdown: string) => any;
|
|
49
|
+
frontmatter: (frontmatter: string | null) => any;
|
|
48
50
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
49
51
|
contentError: (error: Error) => any;
|
|
50
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
51
|
-
frontmatter: (frontmatter: string | null) => any;
|
|
52
52
|
diagnostics: (diagnostics: MarkdownDiagnostic[]) => any;
|
|
53
53
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
54
|
+
"onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
|
|
55
|
+
onReady?: ((editor: Editor) => any) | undefined;
|
|
56
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
54
57
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
55
58
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
56
|
-
|
|
57
|
-
"onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
|
|
59
|
+
onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
|
|
58
60
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
59
61
|
onContentError?: ((error: Error) => any) | undefined;
|
|
60
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
61
|
-
onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
|
|
62
62
|
onDiagnostics?: ((diagnostics: MarkdownDiagnostic[]) => any) | undefined;
|
|
63
63
|
}>, {
|
|
64
|
-
modelValue: string;
|
|
65
64
|
editable: boolean;
|
|
65
|
+
modelValue: string;
|
|
66
66
|
settingsUi: "internal" | "external";
|
|
67
67
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
68
68
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -11,9 +11,9 @@ export declare const chatBodySchema: z.ZodObject<{
|
|
|
11
11
|
feedback: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
12
12
|
op: z.ZodString;
|
|
13
13
|
type: z.ZodEnum<{
|
|
14
|
-
add: "add";
|
|
15
|
-
delete: "delete";
|
|
16
14
|
update: "update";
|
|
15
|
+
delete: "delete";
|
|
16
|
+
add: "add";
|
|
17
17
|
convert: "convert";
|
|
18
18
|
}>;
|
|
19
19
|
target: z.ZodNullable<z.ZodString>;
|
|
@@ -60,7 +60,7 @@ const _hoisted_18 = {
|
|
|
60
60
|
class: "flex min-w-0 flex-1 flex-col gap-0.5"
|
|
61
61
|
};
|
|
62
62
|
const _hoisted_19 = ["title", "aria-label"];
|
|
63
|
-
const _hoisted_20 = ["title"];
|
|
63
|
+
const _hoisted_20 = ["title", "data-label-from-body"];
|
|
64
64
|
const _hoisted_21 = { class: "rounded-sm bg-[color-mix(in_oklab,var(--sk-primary)_20%,transparent)] text-inherit" };
|
|
65
65
|
const _hoisted_22 = { class: "flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100" };
|
|
66
66
|
const _hoisted_23 = {
|
|
@@ -440,13 +440,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
440
440
|
"data-basket-dot": ""
|
|
441
441
|
}, null, 12, _hoisted_19)) : createCommentVNode("v-if", true),
|
|
442
442
|
createElementVNode("span", {
|
|
443
|
-
class: "min-w-0 flex-1 truncate",
|
|
444
|
-
title: item.label
|
|
443
|
+
class: normalizeClass(["min-w-0 flex-1 truncate", item.labelFromBody && "italic text-[var(--sk-muted)]"]),
|
|
444
|
+
title: item.label,
|
|
445
|
+
"data-label-from-body": item.labelFromBody || void 0
|
|
445
446
|
}, [labelParts(item) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
446
447
|
createTextVNode(toDisplayString(labelParts(item)[0]), 1),
|
|
447
448
|
createElementVNode("mark", _hoisted_21, toDisplayString(labelParts(item)[1]), 1),
|
|
448
449
|
createTextVNode(toDisplayString(labelParts(item)[2]), 1)
|
|
449
|
-
], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(item.label), 1)], 64))],
|
|
450
|
+
], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(item.label), 1)], 64))], 10, _hoisted_20),
|
|
450
451
|
item.hasSummary ? (openBlock(), createBlock(unref(UIcon), {
|
|
451
452
|
key: 1,
|
|
452
453
|
name: "i-lucide-text-quote",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentTree.js","names":["_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withKeys","_createSlots","_withCtx","_createCommentVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_withModifiers","_createTextVNode","_renderSlot"],"sources":["../../src/tree/DocumentTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed } from 'vue'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../core/block-transfer'\nimport { findBlockById } from '../core/schema/block-ids'\nimport type { DropdownItem } from '../vue/kit/types'\nimport { useUiKit } from '../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../vue/settings'\nimport { useEditorI18n } from '../vue/use-editor-i18n'\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 React twin renders the same model.\n\nconst props = defineProps<{\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}>()\n\ndefineSlots<{\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n 'document-actions'?: (props: { id: string, title: string }) => unknown\n}>()\n\n/** The folder new documents, folders and host imports go to. Reset to `''` when it is deleted or moved away. */\nconst selectedFolder = defineModel<string>('selectedFolder', { default: '' })\n\nconst { t } = useEditorI18n()\nconst { Button: UButton, DropdownMenu: UDropdownMenu, Icon: UIcon, Input: UInput, Popover: UPopover, Tooltip: UTooltip, Tree: UTree } = useUiKit()\nconst { model, state } = useTreeModel({ store: () => props.store, editor: () => props.editor, selectedFolder })\nconst { settings } = useEditorSettings()\n\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.\nconst canUseFileRoot = computed(() => state.value.hasEditor && state.value.structure === 'composite')\n\nfunction toggleTreeRoot() {\n settings.value.blocks.treeRoot = settings.value.blocks.treeRoot === 'file' ? 'folders' : 'file'\n}\n\nconst row = (item: TreeItem) => rowOf(state.value, item, t)\n\n/** The basket `item` is in, when the open editor offers it (the row shows its colour as a dot). */\nconst basketOf = (item: TreeItem) => itemBasket(item, state.value.baskets)\n\n/** The basket filter button needs a live editor that offers baskets. */\nconst showBasketFilter = computed(() => state.value.hasEditor && state.value.baskets.length > 0)\n/** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\nconst shownBaskets = computed(() => state.value.basketFilter.filter(id => basketById(state.value.baskets, id)))\nconst basketOption = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-[var(--sk-fg)] transition-colors duration-150 hover:bg-[var(--sk-bg-elevated)] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--sk-primary)]'\n\n/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Bausteine down to the focused one. */\nconst breadcrumb = computed(() => focusCrumbs(state.value.focusPath))\n\n/** Why dropping the drag on `item` is refused, while the pointer is over it. */\nfunction refusal(item: TreeItem): string | null {\n const drop = row(item).drop\n return drop && !drop.ok ? drop.reason : null\n}\n\n/** UTree reports the keys of the rows it wants open; the model decides what that changes. */\nfunction onExpanded(next: string[]) {\n const wanted = new Set(next)\n const open = new Set(state.value.view.open)\n for (const key of wanted) if (!open.has(key)) model.value.setOpen(key, true)\n for (const key of open) if (!wanted.has(key)) model.value.setOpen(key, false)\n}\n\n/** The label split around the part the title filter matched, `null` when it did not match here. */\nfunction labelParts(item: TreeItem): [string, string, string] | null {\n const match = row(item).match\n if (!match) return null\n const [at, length] = match\n return [item.label.slice(0, at), item.label.slice(at, at + length), item.label.slice(at + length)]\n}\n\n// Folders and the root toggle through the tree's own row click; documents and outline entries (whose row click opens or\n// reveals) toggle through their chevron.\nfunction onChevronClick(event: Event, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.value.toggle(item.key)\n}\n\nfunction filterMenu(item: TreeItem): DropdownItem[][] {\n const { filterKinds, hiddenKinds } = row(item)\n const groups: DropdownItem[][] = [[\n { type: 'label', label: t('tree.filter.show') },\n ...filterKinds.map<DropdownItem>(kind => ({\n label: kind.label,\n icon: kind.icon,\n type: 'checkbox',\n checked: !hiddenKinds.includes(kind.id),\n onUpdateChecked: visible => model.value.setKindHidden(item.key, kind.id, !visible),\n // Stay open: several kinds are usually toggled in one go.\n onSelect: event => event.preventDefault(),\n })),\n ]]\n if (hiddenKinds.length) groups.push([{ label: t('tree.filter.reset'), icon: 'i-lucide-filter-x', onSelect: () => model.value.clearFilter(item.key) }])\n return groups\n}\n\nfunction addMenu(item: TreeItem): DropdownItem[][] {\n return model.value.addMenu(item).map(group => group.map(entry => ({ label: entry.label, icon: entry.icon, onSelect: entry.run })))\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\nfunction onDragStart(event: DragEvent, item: TreeItem) {\n const payload = model.value.dragStart(item)\n if (!payload) return event.preventDefault()\n if (!event.dataTransfer) return model.value.dragEnd()\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 editor = props.editor\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). */\nconst ownDrag = () => !!state.value.drag && state.value.drag.source.type !== 'foreign-block'\n\nfunction onDragOver(event: DragEvent, item: TreeItem) {\n const foreign = !ownDrag() && !!event.dataTransfer?.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.value.foreignDragOver(item) : model.value.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 if (event.dataTransfer) event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n}\n\nfunction onDragLeave(event: DragEvent, item: TreeItem) {\n if (!(event.currentTarget as Node).contains(event.relatedTarget as Node | null)) model.value.dragLeave(item)\n}\n\nfunction onDrop(event: DragEvent, item: TreeItem) {\n if (ownDrag()) {\n event.preventDefault()\n return model.value.drop(item)\n }\n const payload = event.dataTransfer ? readBlockTransfer(event.dataTransfer) : null\n if (!payload) return\n event.preventDefault()\n return model.value.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-1\">\n <UInput\n :model-value=\"state.filterText\"\n class=\"min-w-0 flex-1\"\n icon=\"i-lucide-search\"\n size=\"sm\"\n :placeholder=\"t('tree.filterText.placeholder')\"\n :aria-label=\"t('tree.filterText.placeholder')\"\n @update:model-value=\"model.setFilterText($event)\"\n @keydown.esc=\"model.setFilterText('')\"\n >\n <template v-if=\"state.filterText\" #trailing>\n <UButton\n icon=\"i-lucide-x\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"link\"\n :aria-label=\"t('tree.filterText.clear')\"\n @click=\"model.setFilterText('')\"\n />\n </template>\n </UInput>\n <!-- A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour. -->\n <UPopover\n v-if=\"showBasketFilter\"\n :content=\"{ side: 'bottom', align: 'end', sideOffset: 4, onCloseAutoFocus: (event: Event) => event.preventDefault() }\"\n :ui=\"{ content: 'w-44 p-1' }\"\n >\n <UButton\n icon=\"i-lucide-paint-bucket\"\n size=\"xs\"\n :color=\"shownBaskets.length ? 'primary' : 'neutral'\"\n :variant=\"shownBaskets.length ? 'soft' : 'ghost'\"\n :title=\"t('tree.basketFilter.label')\"\n :aria-label=\"t('tree.basketFilter.label')\"\n />\n <template #content>\n <div role=\"menu\" :aria-label=\"t('tree.basketFilter.label')\">\n <button\n v-for=\"basket in state.baskets\"\n :key=\"basket.id\"\n type=\"button\"\n role=\"menuitemcheckbox\"\n :aria-checked=\"shownBaskets.includes(basket.id)\"\n :class=\"basketOption\"\n @click=\"model.setBasketShown(basket.id, !shownBaskets.includes(basket.id))\"\n >\n <span class=\"size-2.5 shrink-0 rounded-full\" :style=\"{ backgroundColor: basket.color }\" />\n <span class=\"truncate\">{{ basket.label }}</span>\n <UIcon v-if=\"shownBaskets.includes(basket.id)\" name=\"i-lucide-check\" class=\"ml-auto size-3.5 shrink-0\" />\n </button>\n <button v-if=\"shownBaskets.length\" type=\"button\" role=\"menuitem\" :class=\"basketOption\" @click=\"model.clearBasketFilter()\">\n <UIcon name=\"i-lucide-filter-x\" class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\" />\n <span class=\"truncate\">{{ t('tree.basketFilter.reset') }}</span>\n </button>\n </div>\n </template>\n </UPopover>\n </div>\n\n <div class=\"flex items-center justify-end\">\n <UTooltip :text=\"canUseFileRoot ? '' : t('tree.root.unavailable')\">\n <!-- A disabled button swallows pointer events, so the tooltip hangs on the wrapper. -->\n <span class=\"inline-flex\">\n <UButton\n :icon=\"state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'\"\n :label=\"t(state.virtual ? 'tree.root.file' : 'tree.root.folders')\"\n size=\"xs\"\n color=\"neutral\"\n :variant=\"state.virtual ? 'soft' : 'ghost'\"\n :disabled=\"!canUseFileRoot\"\n :aria-pressed=\"state.virtual\"\n @click=\"toggleTreeRoot\"\n />\n </span>\n </UTooltip>\n </div>\n\n <div v-if=\"state.view.hasKindFilters\" class=\"flex items-center\">\n <UButton\n icon=\"i-lucide-filter-x\"\n :label=\"t('tree.filter.reset')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"subtle\"\n @click=\"model.clearAllFilters\"\n />\n </div>\n\n <div v-if=\"state.creatingFolder\" class=\"flex flex-col gap-1\">\n <UInput\n :model-value=\"state.creatingFolder.draft\"\n size=\"sm\"\n autofocus\n :color=\"state.creatingFolder.error ? 'error' : undefined\"\n :highlight=\"!!state.creatingFolder.error\"\n :placeholder=\"state.creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: state.creatingFolder.parent }) : t('tree.folder.placeholder')\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!state.creatingFolder.error\"\n @update:model-value=\"model.setFolderDraft($event)\"\n @keydown.enter=\"model.commitCreateFolder\"\n @keydown.esc=\"model.cancelCreateFolder\"\n @blur=\"model.cancelCreateFolder\"\n />\n <p v-if=\"state.creatingFolder.error\" class=\"px-0.5 text-xs text-[var(--sk-danger)]\" role=\"alert\">\n {{ state.creatingFolder.error }}\n </p>\n </div>\n\n <div v-if=\"breadcrumb.crumbs.length\" class=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <UButton\n icon=\"i-lucide-corner-up-left\"\n :label=\"breadcrumb.document?.label ?? t('tree.focus.root')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.leave')\"\n :aria-label=\"t('tree.focus.leave')\"\n @click=\"model.defocus()\"\n />\n <template v-for=\"(crumb, index) in breadcrumb.crumbs\" :key=\"crumb.key\">\n <UIcon name=\"i-lucide-chevron-right\" class=\"size-3 shrink-0 text-[var(--sk-muted)]\" />\n <span v-if=\"index === breadcrumb.crumbs.length - 1\" class=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{{ crumb.label }}</span>\n <UButton v-else :label=\"crumb.label\" size=\"xs\" color=\"neutral\" variant=\"ghost\" @click=\"model.focus(crumb.key)\" />\n </template>\n </div>\n\n <p v-if=\"!state.tree && !state.virtual\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ state.tree === null ? t('tree.loadFailed') : t('tree.loading') }}\n </p>\n\n <UTree\n v-else\n :expanded=\"state.view.open\"\n :items=\"state.view.items\"\n :get-key=\"(item: TreeItem) => item.key\"\n size=\"sm\"\n :aria-label=\"t('tree.aria')\"\n @update:expanded=\"onExpanded\"\n >\n <template #item-wrapper=\"{ item }: { item: TreeItem }\">\n <div class=\"w-full\">\n <div\n class=\"group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none hover:bg-[color-mix(in_oklab,var(--sk-bg-elevated)_60%,transparent)]\"\n :class=\"[\n row(item).active && 'bg-[color-mix(in_oklab,var(--sk-primary)_10%,transparent)] font-medium text-[var(--sk-primary)]',\n row(item).selected && 'bg-[var(--sk-bg-elevated)]',\n row(item).drop?.ok && 'ring-1 ring-[var(--sk-primary)]',\n refusal(item) && 'bg-[color-mix(in_oklab,var(--sk-danger)_10%,transparent)] ring-1 ring-[var(--sk-danger)]',\n row(item).dimmed && 'opacity-60',\n ]\"\n :draggable=\"model.isDraggable(item)\"\n @dragstart=\"onDragStart($event, item)\"\n @dragover=\"onDragOver($event, item)\"\n @dragleave=\"onDragLeave($event, item)\"\n @drop=\"onDrop($event, item)\"\n @dragend=\"model.dragEnd\"\n >\n <UIcon\n v-if=\"row(item).expandable\"\n name=\"i-lucide-chevron-right\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)] transition-transform\"\n :class=\"row(item).open && 'rotate-90'\"\n @click=\"onChevronClick($event, item)\"\n />\n <span v-else-if=\"item.type === 'composite' || item.type === 'heading'\" class=\"size-3.5 shrink-0\" />\n <UIcon :name=\"item.icon\" class=\"size-4 shrink-0\" :style=\"item.tone ? { color: item.tone } : undefined\" />\n\n <div v-if=\"row(item).renaming\" class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <UInput\n :model-value=\"row(item).renaming!.draft\"\n size=\"xs\"\n class=\"min-w-0\"\n autofocus\n :color=\"row(item).renaming!.error ? 'error' : undefined\"\n :highlight=\"!!row(item).renaming!.error\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!row(item).renaming!.error\"\n @update:model-value=\"model.setRenameDraft($event)\"\n @focus=\"($event.target as HTMLInputElement).select()\"\n @click.stop\n @keydown.stop\n @keydown.enter=\"model.commitRename\"\n @keydown.esc=\"model.cancelRename\"\n @blur=\"model.blurRename\"\n />\n <p v-if=\"row(item).renaming!.error\" class=\"text-xs text-[var(--sk-danger)]\" role=\"alert\" @click.stop>\n {{ row(item).renaming!.error }}\n </p>\n </div>\n <template v-else>\n <span\n v-if=\"basketOf(item)\"\n class=\"size-2 shrink-0 rounded-full\"\n :style=\"{ backgroundColor: basketOf(item)!.color }\"\n :title=\"basketOf(item)!.label\"\n :aria-label=\"basketOf(item)!.label\"\n role=\"img\"\n data-basket-dot\n />\n <span class=\"min-w-0 flex-1 truncate\" :title=\"item.label\">\n <template v-if=\"labelParts(item)\">{{ labelParts(item)![0] }}<mark class=\"rounded-sm bg-[color-mix(in_oklab,var(--sk-primary)_20%,transparent)] text-inherit\">{{ labelParts(item)![1] }}</mark>{{ labelParts(item)![2] }}</template>\n <template v-else>{{ item.label }}</template>\n </span>\n <UIcon\n v-if=\"item.hasSummary\"\n name=\"i-lucide-text-quote\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\"\n :title=\"t('tree.summary')\"\n />\n <div class=\"flex shrink-0\" @click.stop>\n <UDropdownMenu v-if=\"row(item).showFilter\" :items=\"filterMenu(item)\" :content=\"{ align: 'start' }\">\n <UButton\n icon=\"i-lucide-filter\"\n size=\"xs\"\n :color=\"row(item).hiddenKinds.length ? 'primary' : 'neutral'\"\n :variant=\"row(item).hiddenKinds.length ? 'soft' : 'ghost'\"\n :class=\"!row(item).hiddenKinds.length && 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\"\n :title=\"t('tree.filter.title')\"\n :aria-label=\"t('tree.filter.title')\"\n />\n </UDropdownMenu>\n <div class=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n <UButton\n v-if=\"item.type === 'composite' && item.blockId\"\n icon=\"i-lucide-focus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.enter')\"\n :aria-label=\"t('tree.focus.enter')\"\n @click=\"model.focus(item.key)\"\n />\n <UButton\n v-if=\"model.canMerge(item)\"\n icon=\"i-lucide-merge\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.merge')\"\n :aria-label=\"t('tree.merge')\"\n @click=\"model.merge(item)\"\n />\n <UDropdownMenu v-if=\"addMenu(item).length\" :items=\"addMenu(item)\" :content=\"{ align: 'start', onCloseAutoFocus: (event: Event) => event.preventDefault() }\">\n <UButton\n icon=\"i-lucide-plus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.add')\"\n :aria-label=\"t('tree.add')\"\n />\n </UDropdownMenu>\n <slot v-if=\"item.type === 'document'\" name=\"document-actions\" :id=\"item.id!\" :title=\"item.label\" />\n <UButton\n v-if=\"item.type === 'document' || item.type === 'folder'\"\n icon=\"i-lucide-trash-2\"\n size=\"xs\"\n color=\"error\"\n variant=\"ghost\"\n :aria-label=\"t('tree.delete')\"\n loading-auto\n @click=\"model.remove(item)\"\n />\n </div>\n </div>\n </template>\n </div>\n <p v-if=\"refusal(item)\" class=\"px-2 pt-0.5 text-xs text-[var(--sk-danger)]\" role=\"status\">\n {{ refusal(item) }}\n </p>\n </div>\n </template>\n </UTree>\n\n <p v-if=\"state.view.noMatches\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.noMatches') }}\n </p>\n <p v-if=\"state.view.isEmpty\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.empty') }}\n </p>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocumentTree.js","names":["_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withKeys","_createSlots","_withCtx","_createCommentVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_withModifiers","_createTextVNode","_renderSlot"],"sources":["../../src/tree/DocumentTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed } from 'vue'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../core/block-transfer'\nimport { findBlockById } from '../core/schema/block-ids'\nimport type { DropdownItem } from '../vue/kit/types'\nimport { useUiKit } from '../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../vue/settings'\nimport { useEditorI18n } from '../vue/use-editor-i18n'\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 React twin renders the same model.\n\nconst props = defineProps<{\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}>()\n\ndefineSlots<{\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n 'document-actions'?: (props: { id: string, title: string }) => unknown\n}>()\n\n/** The folder new documents, folders and host imports go to. Reset to `''` when it is deleted or moved away. */\nconst selectedFolder = defineModel<string>('selectedFolder', { default: '' })\n\nconst { t } = useEditorI18n()\nconst { Button: UButton, DropdownMenu: UDropdownMenu, Icon: UIcon, Input: UInput, Popover: UPopover, Tooltip: UTooltip, Tree: UTree } = useUiKit()\nconst { model, state } = useTreeModel({ store: () => props.store, editor: () => props.editor, selectedFolder })\nconst { settings } = useEditorSettings()\n\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.\nconst canUseFileRoot = computed(() => state.value.hasEditor && state.value.structure === 'composite')\n\nfunction toggleTreeRoot() {\n settings.value.blocks.treeRoot = settings.value.blocks.treeRoot === 'file' ? 'folders' : 'file'\n}\n\nconst row = (item: TreeItem) => rowOf(state.value, item, t)\n\n/** The basket `item` is in, when the open editor offers it (the row shows its colour as a dot). */\nconst basketOf = (item: TreeItem) => itemBasket(item, state.value.baskets)\n\n/** The basket filter button needs a live editor that offers baskets. */\nconst showBasketFilter = computed(() => state.value.hasEditor && state.value.baskets.length > 0)\n/** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\nconst shownBaskets = computed(() => state.value.basketFilter.filter(id => basketById(state.value.baskets, id)))\nconst basketOption = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-[var(--sk-fg)] transition-colors duration-150 hover:bg-[var(--sk-bg-elevated)] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--sk-primary)]'\n\n/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Bausteine down to the focused one. */\nconst breadcrumb = computed(() => focusCrumbs(state.value.focusPath))\n\n/** Why dropping the drag on `item` is refused, while the pointer is over it. */\nfunction refusal(item: TreeItem): string | null {\n const drop = row(item).drop\n return drop && !drop.ok ? drop.reason : null\n}\n\n/** UTree reports the keys of the rows it wants open; the model decides what that changes. */\nfunction onExpanded(next: string[]) {\n const wanted = new Set(next)\n const open = new Set(state.value.view.open)\n for (const key of wanted) if (!open.has(key)) model.value.setOpen(key, true)\n for (const key of open) if (!wanted.has(key)) model.value.setOpen(key, false)\n}\n\n/** The label split around the part the title filter matched, `null` when it did not match here. */\nfunction labelParts(item: TreeItem): [string, string, string] | null {\n const match = row(item).match\n if (!match) return null\n const [at, length] = match\n return [item.label.slice(0, at), item.label.slice(at, at + length), item.label.slice(at + length)]\n}\n\n// Folders and the root toggle through the tree's own row click; documents and outline entries (whose row click opens or\n// reveals) toggle through their chevron.\nfunction onChevronClick(event: Event, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.value.toggle(item.key)\n}\n\nfunction filterMenu(item: TreeItem): DropdownItem[][] {\n const { filterKinds, hiddenKinds } = row(item)\n const groups: DropdownItem[][] = [[\n { type: 'label', label: t('tree.filter.show') },\n ...filterKinds.map<DropdownItem>(kind => ({\n label: kind.label,\n icon: kind.icon,\n type: 'checkbox',\n checked: !hiddenKinds.includes(kind.id),\n onUpdateChecked: visible => model.value.setKindHidden(item.key, kind.id, !visible),\n // Stay open: several kinds are usually toggled in one go.\n onSelect: event => event.preventDefault(),\n })),\n ]]\n if (hiddenKinds.length) groups.push([{ label: t('tree.filter.reset'), icon: 'i-lucide-filter-x', onSelect: () => model.value.clearFilter(item.key) }])\n return groups\n}\n\nfunction addMenu(item: TreeItem): DropdownItem[][] {\n return model.value.addMenu(item).map(group => group.map(entry => ({ label: entry.label, icon: entry.icon, onSelect: entry.run })))\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\nfunction onDragStart(event: DragEvent, item: TreeItem) {\n const payload = model.value.dragStart(item)\n if (!payload) return event.preventDefault()\n if (!event.dataTransfer) return model.value.dragEnd()\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 editor = props.editor\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). */\nconst ownDrag = () => !!state.value.drag && state.value.drag.source.type !== 'foreign-block'\n\nfunction onDragOver(event: DragEvent, item: TreeItem) {\n const foreign = !ownDrag() && !!event.dataTransfer?.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.value.foreignDragOver(item) : model.value.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 if (event.dataTransfer) event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n}\n\nfunction onDragLeave(event: DragEvent, item: TreeItem) {\n if (!(event.currentTarget as Node).contains(event.relatedTarget as Node | null)) model.value.dragLeave(item)\n}\n\nfunction onDrop(event: DragEvent, item: TreeItem) {\n if (ownDrag()) {\n event.preventDefault()\n return model.value.drop(item)\n }\n const payload = event.dataTransfer ? readBlockTransfer(event.dataTransfer) : null\n if (!payload) return\n event.preventDefault()\n return model.value.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-1\">\n <UInput\n :model-value=\"state.filterText\"\n class=\"min-w-0 flex-1\"\n icon=\"i-lucide-search\"\n size=\"sm\"\n :placeholder=\"t('tree.filterText.placeholder')\"\n :aria-label=\"t('tree.filterText.placeholder')\"\n @update:model-value=\"model.setFilterText($event)\"\n @keydown.esc=\"model.setFilterText('')\"\n >\n <template v-if=\"state.filterText\" #trailing>\n <UButton\n icon=\"i-lucide-x\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"link\"\n :aria-label=\"t('tree.filterText.clear')\"\n @click=\"model.setFilterText('')\"\n />\n </template>\n </UInput>\n <!-- A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour. -->\n <UPopover\n v-if=\"showBasketFilter\"\n :content=\"{ side: 'bottom', align: 'end', sideOffset: 4, onCloseAutoFocus: (event: Event) => event.preventDefault() }\"\n :ui=\"{ content: 'w-44 p-1' }\"\n >\n <UButton\n icon=\"i-lucide-paint-bucket\"\n size=\"xs\"\n :color=\"shownBaskets.length ? 'primary' : 'neutral'\"\n :variant=\"shownBaskets.length ? 'soft' : 'ghost'\"\n :title=\"t('tree.basketFilter.label')\"\n :aria-label=\"t('tree.basketFilter.label')\"\n />\n <template #content>\n <div role=\"menu\" :aria-label=\"t('tree.basketFilter.label')\">\n <button\n v-for=\"basket in state.baskets\"\n :key=\"basket.id\"\n type=\"button\"\n role=\"menuitemcheckbox\"\n :aria-checked=\"shownBaskets.includes(basket.id)\"\n :class=\"basketOption\"\n @click=\"model.setBasketShown(basket.id, !shownBaskets.includes(basket.id))\"\n >\n <span class=\"size-2.5 shrink-0 rounded-full\" :style=\"{ backgroundColor: basket.color }\" />\n <span class=\"truncate\">{{ basket.label }}</span>\n <UIcon v-if=\"shownBaskets.includes(basket.id)\" name=\"i-lucide-check\" class=\"ml-auto size-3.5 shrink-0\" />\n </button>\n <button v-if=\"shownBaskets.length\" type=\"button\" role=\"menuitem\" :class=\"basketOption\" @click=\"model.clearBasketFilter()\">\n <UIcon name=\"i-lucide-filter-x\" class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\" />\n <span class=\"truncate\">{{ t('tree.basketFilter.reset') }}</span>\n </button>\n </div>\n </template>\n </UPopover>\n </div>\n\n <div class=\"flex items-center justify-end\">\n <UTooltip :text=\"canUseFileRoot ? '' : t('tree.root.unavailable')\">\n <!-- A disabled button swallows pointer events, so the tooltip hangs on the wrapper. -->\n <span class=\"inline-flex\">\n <UButton\n :icon=\"state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'\"\n :label=\"t(state.virtual ? 'tree.root.file' : 'tree.root.folders')\"\n size=\"xs\"\n color=\"neutral\"\n :variant=\"state.virtual ? 'soft' : 'ghost'\"\n :disabled=\"!canUseFileRoot\"\n :aria-pressed=\"state.virtual\"\n @click=\"toggleTreeRoot\"\n />\n </span>\n </UTooltip>\n </div>\n\n <div v-if=\"state.view.hasKindFilters\" class=\"flex items-center\">\n <UButton\n icon=\"i-lucide-filter-x\"\n :label=\"t('tree.filter.reset')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"subtle\"\n @click=\"model.clearAllFilters\"\n />\n </div>\n\n <div v-if=\"state.creatingFolder\" class=\"flex flex-col gap-1\">\n <UInput\n :model-value=\"state.creatingFolder.draft\"\n size=\"sm\"\n autofocus\n :color=\"state.creatingFolder.error ? 'error' : undefined\"\n :highlight=\"!!state.creatingFolder.error\"\n :placeholder=\"state.creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: state.creatingFolder.parent }) : t('tree.folder.placeholder')\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!state.creatingFolder.error\"\n @update:model-value=\"model.setFolderDraft($event)\"\n @keydown.enter=\"model.commitCreateFolder\"\n @keydown.esc=\"model.cancelCreateFolder\"\n @blur=\"model.cancelCreateFolder\"\n />\n <p v-if=\"state.creatingFolder.error\" class=\"px-0.5 text-xs text-[var(--sk-danger)]\" role=\"alert\">\n {{ state.creatingFolder.error }}\n </p>\n </div>\n\n <div v-if=\"breadcrumb.crumbs.length\" class=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <UButton\n icon=\"i-lucide-corner-up-left\"\n :label=\"breadcrumb.document?.label ?? t('tree.focus.root')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.leave')\"\n :aria-label=\"t('tree.focus.leave')\"\n @click=\"model.defocus()\"\n />\n <template v-for=\"(crumb, index) in breadcrumb.crumbs\" :key=\"crumb.key\">\n <UIcon name=\"i-lucide-chevron-right\" class=\"size-3 shrink-0 text-[var(--sk-muted)]\" />\n <span v-if=\"index === breadcrumb.crumbs.length - 1\" class=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{{ crumb.label }}</span>\n <UButton v-else :label=\"crumb.label\" size=\"xs\" color=\"neutral\" variant=\"ghost\" @click=\"model.focus(crumb.key)\" />\n </template>\n </div>\n\n <p v-if=\"!state.tree && !state.virtual\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ state.tree === null ? t('tree.loadFailed') : t('tree.loading') }}\n </p>\n\n <UTree\n v-else\n :expanded=\"state.view.open\"\n :items=\"state.view.items\"\n :get-key=\"(item: TreeItem) => item.key\"\n size=\"sm\"\n :aria-label=\"t('tree.aria')\"\n @update:expanded=\"onExpanded\"\n >\n <template #item-wrapper=\"{ item }: { item: TreeItem }\">\n <div class=\"w-full\">\n <div\n class=\"group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none hover:bg-[color-mix(in_oklab,var(--sk-bg-elevated)_60%,transparent)]\"\n :class=\"[\n row(item).active && 'bg-[color-mix(in_oklab,var(--sk-primary)_10%,transparent)] font-medium text-[var(--sk-primary)]',\n row(item).selected && 'bg-[var(--sk-bg-elevated)]',\n row(item).drop?.ok && 'ring-1 ring-[var(--sk-primary)]',\n refusal(item) && 'bg-[color-mix(in_oklab,var(--sk-danger)_10%,transparent)] ring-1 ring-[var(--sk-danger)]',\n row(item).dimmed && 'opacity-60',\n ]\"\n :draggable=\"model.isDraggable(item)\"\n @dragstart=\"onDragStart($event, item)\"\n @dragover=\"onDragOver($event, item)\"\n @dragleave=\"onDragLeave($event, item)\"\n @drop=\"onDrop($event, item)\"\n @dragend=\"model.dragEnd\"\n >\n <UIcon\n v-if=\"row(item).expandable\"\n name=\"i-lucide-chevron-right\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)] transition-transform\"\n :class=\"row(item).open && 'rotate-90'\"\n @click=\"onChevronClick($event, item)\"\n />\n <span v-else-if=\"item.type === 'composite' || item.type === 'heading'\" class=\"size-3.5 shrink-0\" />\n <UIcon :name=\"item.icon\" class=\"size-4 shrink-0\" :style=\"item.tone ? { color: item.tone } : undefined\" />\n\n <div v-if=\"row(item).renaming\" class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <UInput\n :model-value=\"row(item).renaming!.draft\"\n size=\"xs\"\n class=\"min-w-0\"\n autofocus\n :color=\"row(item).renaming!.error ? 'error' : undefined\"\n :highlight=\"!!row(item).renaming!.error\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!row(item).renaming!.error\"\n @update:model-value=\"model.setRenameDraft($event)\"\n @focus=\"($event.target as HTMLInputElement).select()\"\n @click.stop\n @keydown.stop\n @keydown.enter=\"model.commitRename\"\n @keydown.esc=\"model.cancelRename\"\n @blur=\"model.blurRename\"\n />\n <p v-if=\"row(item).renaming!.error\" class=\"text-xs text-[var(--sk-danger)]\" role=\"alert\" @click.stop>\n {{ row(item).renaming!.error }}\n </p>\n </div>\n <template v-else>\n <span\n v-if=\"basketOf(item)\"\n class=\"size-2 shrink-0 rounded-full\"\n :style=\"{ backgroundColor: basketOf(item)!.color }\"\n :title=\"basketOf(item)!.label\"\n :aria-label=\"basketOf(item)!.label\"\n role=\"img\"\n data-basket-dot\n />\n <span class=\"min-w-0 flex-1 truncate\" :class=\"item.labelFromBody && 'italic text-[var(--sk-muted)]'\" :title=\"item.label\" :data-label-from-body=\"item.labelFromBody || undefined\">\n <template v-if=\"labelParts(item)\">{{ labelParts(item)![0] }}<mark class=\"rounded-sm bg-[color-mix(in_oklab,var(--sk-primary)_20%,transparent)] text-inherit\">{{ labelParts(item)![1] }}</mark>{{ labelParts(item)![2] }}</template>\n <template v-else>{{ item.label }}</template>\n </span>\n <UIcon\n v-if=\"item.hasSummary\"\n name=\"i-lucide-text-quote\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\"\n :title=\"t('tree.summary')\"\n />\n <div class=\"flex shrink-0\" @click.stop>\n <UDropdownMenu v-if=\"row(item).showFilter\" :items=\"filterMenu(item)\" :content=\"{ align: 'start' }\">\n <UButton\n icon=\"i-lucide-filter\"\n size=\"xs\"\n :color=\"row(item).hiddenKinds.length ? 'primary' : 'neutral'\"\n :variant=\"row(item).hiddenKinds.length ? 'soft' : 'ghost'\"\n :class=\"!row(item).hiddenKinds.length && 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\"\n :title=\"t('tree.filter.title')\"\n :aria-label=\"t('tree.filter.title')\"\n />\n </UDropdownMenu>\n <div class=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n <UButton\n v-if=\"item.type === 'composite' && item.blockId\"\n icon=\"i-lucide-focus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.enter')\"\n :aria-label=\"t('tree.focus.enter')\"\n @click=\"model.focus(item.key)\"\n />\n <UButton\n v-if=\"model.canMerge(item)\"\n icon=\"i-lucide-merge\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.merge')\"\n :aria-label=\"t('tree.merge')\"\n @click=\"model.merge(item)\"\n />\n <UDropdownMenu v-if=\"addMenu(item).length\" :items=\"addMenu(item)\" :content=\"{ align: 'start', onCloseAutoFocus: (event: Event) => event.preventDefault() }\">\n <UButton\n icon=\"i-lucide-plus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.add')\"\n :aria-label=\"t('tree.add')\"\n />\n </UDropdownMenu>\n <slot v-if=\"item.type === 'document'\" name=\"document-actions\" :id=\"item.id!\" :title=\"item.label\" />\n <UButton\n v-if=\"item.type === 'document' || item.type === 'folder'\"\n icon=\"i-lucide-trash-2\"\n size=\"xs\"\n color=\"error\"\n variant=\"ghost\"\n :aria-label=\"t('tree.delete')\"\n loading-auto\n @click=\"model.remove(item)\"\n />\n </div>\n </div>\n </template>\n </div>\n <p v-if=\"refusal(item)\" class=\"px-2 pt-0.5 text-xs text-[var(--sk-danger)]\" role=\"status\">\n {{ refusal(item) }}\n </p>\n </div>\n </template>\n </UTree>\n\n <p v-if=\"state.view.noMatches\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.noMatches') }}\n </p>\n <p v-if=\"state.view.isEmpty\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.empty') }}\n </p>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -190,8 +190,9 @@ function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, d
|
|
|
190
190
|
"data-basket-dot": true
|
|
191
191
|
}),
|
|
192
192
|
/* @__PURE__ */ jsx("span", {
|
|
193
|
-
className: "min-w-0 flex-1 truncate",
|
|
193
|
+
className: cn("min-w-0 flex-1 truncate", item.labelFromBody && "italic text-muted-foreground"),
|
|
194
194
|
title: item.label,
|
|
195
|
+
"data-label-from-body": item.labelFromBody || void 0,
|
|
195
196
|
children: /* @__PURE__ */ jsx(Label, {
|
|
196
197
|
label: item.label,
|
|
197
198
|
match: row.match
|