@ecsplico/editor 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +4 -3
- package/dist/adapters/anchors.d.ts +80 -0
- package/dist/adapters/anchors.js +270 -0
- package/dist/adapters/anchors.js.map +1 -0
- package/dist/adapters/comments/client.d.ts +64 -0
- package/dist/adapters/comments/client.js +536 -0
- package/dist/adapters/comments/client.js.map +1 -0
- package/dist/adapters/comments/drizzle.d.ts +430 -0
- package/dist/adapters/comments/drizzle.js +236 -0
- package/dist/adapters/comments/drizzle.js.map +1 -0
- package/dist/adapters/comments/errors.d.ts +9 -0
- package/dist/adapters/comments/errors.js +19 -0
- package/dist/adapters/comments/errors.js.map +1 -0
- package/dist/adapters/comments/json.d.ts +19 -0
- package/dist/adapters/comments/json.js +94 -0
- package/dist/adapters/comments/json.js.map +1 -0
- package/dist/adapters/comments/record.js +78 -0
- package/dist/adapters/comments/record.js.map +1 -0
- package/dist/adapters/comments/server.d.ts +23 -0
- package/dist/adapters/comments/server.js +102 -0
- package/dist/adapters/comments/server.js.map +1 -0
- package/dist/adapters/comments/types.d.ts +109 -0
- package/dist/adapters/documents.js +108 -0
- package/dist/adapters/documents.js.map +1 -0
- package/dist/adapters/drizzle.d.ts +293 -0
- package/dist/adapters/drizzle.js +157 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/files.js +30 -0
- package/dist/adapters/files.js.map +1 -0
- package/dist/adapters/forge.d.ts +58 -0
- package/dist/adapters/forge.js +91 -0
- package/dist/adapters/forge.js.map +1 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.js +0 -0
- package/dist/adapters/json.d.ts +24 -0
- package/dist/adapters/json.js +128 -0
- package/dist/adapters/json.js.map +1 -0
- package/dist/adapters/nitro.d.ts +61 -0
- package/dist/adapters/nitro.js +212 -0
- package/dist/adapters/nitro.js.map +1 -0
- package/dist/adapters/routes.js +16 -0
- package/dist/adapters/routes.js.map +1 -0
- package/dist/adapters/shared.d.ts +37 -0
- package/dist/adapters/shared.js +32 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/vue.d.ts +67 -0
- package/dist/adapters/vue.js +187 -0
- package/dist/adapters/vue.js.map +1 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/core/doc-anchors.d.ts +5 -0
- package/dist/core/doc-anchors.js +4 -3
- package/dist/core/doc-anchors.js.map +1 -1
- package/dist/core/editor-ui.js +57 -1
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/surface-session.js +139 -0
- package/dist/core/surface-session.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-op-review.d.ts +2 -2
- package/dist/features/ai/agent-op-review.js.map +1 -1
- package/dist/features/ai/agent-ops.d.ts +1 -1
- package/dist/features/ai/agent-ops.js +9 -6
- package/dist/features/ai/agent-ops.js.map +1 -1
- package/dist/features/ai/ai-apply.d.ts +1 -1
- package/dist/features/ai/ai-review.js +10 -1
- package/dist/features/ai/ai-review.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +78 -252
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
- package/dist/features/ai/components/AiSidebar.js +2 -2
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.js +1 -0
- package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/components/SettingsAiTab.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/document-chat.d.ts +3 -2
- package/dist/features/ai/document-chat.js +16 -19
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/index.d.ts +5 -3
- package/dist/features/ai/index.js +4 -2
- package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
- package/dist/features/ai/protocol/agent-ops.js +65 -39
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +89 -0
- package/dist/features/ai/protocol/block-lines.js +177 -0
- package/dist/features/ai/protocol/block-lines.js.map +1 -0
- package/dist/features/ai/protocol/chat.js +10 -10
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/index.js +3 -2
- package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
- package/dist/features/ai/react/AiSelectionMenu.js +64 -255
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/AiSidebar.js +2 -2
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -0
- package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
- package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
- package/dist/features/ai/selection-flow.d.ts +133 -0
- package/dist/features/ai/selection-flow.js +283 -0
- package/dist/features/ai/selection-flow.js.map +1 -0
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-document-chat.d.ts +1 -1
- package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
- package/dist/features/authors/feature.js +1 -1
- package/dist/features/authors/index.js +1 -1
- package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
- package/dist/features/comments/components/CommentBubble.js +1 -1
- package/dist/features/comments/components/CommentComposer.js +1 -1
- package/dist/features/comments/components/CommentThreadView.js +5 -2
- package/dist/features/comments/components/CommentThreadView.js.map +1 -1
- package/dist/features/comments/components/CommentsMargin.js +108 -230
- package/dist/features/comments/components/CommentsMargin.js.map +1 -1
- package/dist/features/comments/components/CommentsOverlay.js +1 -1
- package/dist/features/comments/feature.js +1 -1
- package/dist/features/comments/index.d.ts +4 -2
- package/dist/features/comments/index.js +4 -2
- package/dist/features/comments/margin-controller.d.ts +44 -0
- package/dist/features/comments/margin-controller.js +132 -0
- package/dist/features/comments/margin-controller.js.map +1 -0
- package/dist/features/comments/margin-layout.d.ts +55 -0
- package/dist/features/comments/margin-layout.js +79 -0
- package/dist/features/comments/margin-layout.js.map +1 -0
- package/dist/features/comments/model.d.ts +8 -0
- package/dist/features/comments/model.js +60 -34
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentBubble.js +1 -1
- package/dist/features/comments/react/CommentComposer.js +1 -1
- package/dist/features/comments/react/CommentThreadView.js +2 -2
- package/dist/features/comments/react/CommentThreadView.js.map +1 -1
- package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
- package/dist/features/comments/react/CommentsMargin.js +85 -251
- package/dist/features/comments/react/CommentsMargin.js.map +1 -1
- package/dist/features/comments/react/CommentsOverlay.js +1 -1
- package/dist/features/comments/react/use-comments.js +1 -1
- package/dist/features/comments/react/use-margin-layout.js +45 -0
- package/dist/features/comments/react/use-margin-layout.js.map +1 -0
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +23 -0
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js +40 -0
- package/dist/features/comments/snapshot-equality.js.map +1 -0
- package/dist/features/comments/thread-anchor.js +33 -0
- package/dist/features/comments/thread-anchor.js.map +1 -0
- package/dist/features/comments/use-comments.d.ts +3 -1
- package/dist/features/comments/use-comments.js +1 -0
- package/dist/features/comments/use-comments.js.map +1 -1
- package/dist/features/comments/use-margin-layout.js +29 -0
- package/dist/features/comments/use-margin-layout.js.map +1 -0
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +21 -0
- package/dist/features/composites/accepted-children.js +126 -0
- package/dist/features/composites/accepted-children.js.map +1 -0
- package/dist/features/composites/components/CompositeDetails.js +1 -1
- package/dist/features/composites/components/CompositeInspector.js +38 -164
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/feature.d.ts +2 -1
- package/dist/features/composites/feature.js +8 -2
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/index.d.ts +5 -3
- package/dist/features/composites/index.js +8 -6
- package/dist/features/composites/inspector-model.d.ts +97 -0
- package/dist/features/composites/inspector-model.js +255 -0
- package/dist/features/composites/inspector-model.js.map +1 -0
- package/dist/features/composites/kinds.d.ts +11 -0
- package/dist/features/composites/kinds.js +17 -2
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/outline.d.ts +18 -5
- package/dist/features/composites/outline.js +44 -12
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.js +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
- package/dist/features/composites/react/CompositeInspector.js +21 -180
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
- package/dist/features/composites/react/CompositeView.js +1 -1
- package/dist/features/composites/react/use-composite-inspector.js +29 -0
- package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/schema.d.ts +2 -2
- package/dist/features/composites/schema.js +2 -2
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/use-composite-anchors.d.ts +1 -1
- package/dist/features/composites/use-composite-inspector.js +41 -0
- package/dist/features/composites/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/use-composite-rows.d.ts +1 -1
- package/dist/features/media/index.d.ts +1 -1
- package/dist/features/media/vue.d.ts +1 -1
- package/dist/features/mentions/index.d.ts +3 -3
- package/dist/features/mentions/index.js +3 -3
- package/dist/features/mentions/schema.d.ts +1 -1
- package/dist/features/mentions/schema.js +1 -1
- package/dist/features/themes/index.d.ts +1 -1
- package/dist/features/themes/index.js +1 -1
- package/dist/features/transcript/feature.js +1 -1
- package/dist/features/transcript/index.js +2 -2
- package/dist/i18n/de/ai.js +2 -0
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/composites.js +8 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/tree.js +14 -1
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +22 -0
- package/dist/i18n/en/ai.js +2 -0
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +8 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/tree.js +14 -1
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.js +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +2 -2
- package/dist/integrations/composite-ai/index.js +2 -2
- package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
- package/dist/markdown/MarkdownSurface.d.ts +1 -1
- package/dist/markdown/MarkdownSurface.js +2 -2
- package/dist/markdown/MarkdownSurface.vue.d.ts +9 -9
- package/dist/markdown/index.d.ts +2 -2
- package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
- package/dist/react/EditorSurface.d.ts +1 -1
- package/dist/react/EditorSurface.js +43 -149
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.js +3 -40
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/schema/advanced.js +2 -2
- package/dist/server/adapter.d.ts +2 -2
- package/dist/server/collab.js +1 -1
- package/dist/server/handlers/ai.d.ts +4 -2
- package/dist/server/handlers/ai.js +11 -9
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +1 -1
- package/dist/server/handlers/editor.d.ts +1 -1
- package/dist/server/handlers/editor.js +2 -2
- package/dist/server/host.d.ts +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/settings/index.d.ts +1 -1
- package/dist/styles/tokens.css +1 -1
- package/dist/tree/DocumentTree.js +159 -387
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +37 -0
- package/dist/tree/drop-rules.js.map +1 -0
- package/dist/tree/react/DocumentTree.d.ts +2 -2
- package/dist/tree/react/DocumentTree.js +135 -397
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/react/use-tree-model.js +129 -0
- package/dist/tree/react/use-tree-model.js.map +1 -0
- package/dist/tree/react.js +1 -1
- package/dist/tree/tree-host.js +100 -0
- package/dist/tree/tree-host.js.map +1 -0
- package/dist/tree/tree-items.js +22 -43
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +580 -0
- package/dist/tree/tree-model.js.map +1 -0
- package/dist/tree/tree-rows.js +77 -0
- package/dist/tree/tree-rows.js.map +1 -0
- package/dist/tree/use-tree-model.js +94 -0
- package/dist/tree/use-tree-model.js.map +1 -0
- package/dist/tree/vue.js +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +6 -6
- package/dist/vue/SimpleEditor.vue.d.ts +2 -2
- package/dist/vue/components/EditorSurface.js +27 -87
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +6 -6
- package/dist/vue/components/EditorToolbar.js +7 -44
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/context.d.ts +1 -1
- package/dist/vue/context.js +1 -1
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/use-editor-ui.d.ts +1 -1
- package/dist/vue/use-notifier.d.ts +1 -1
- package/docs/integration.md +97 -4
- package/docs/llms.md +9 -1
- package/docs/usage.md +10 -5
- package/package.json +67 -5
|
@@ -1,453 +1,162 @@
|
|
|
1
1
|
import { useEditorI18n } from "../../react/use-editor-i18n.js";
|
|
2
2
|
import { cn } from "../../react/ui/utils.js";
|
|
3
3
|
import { Icon } from "../../react/Icon.js";
|
|
4
|
-
import { useStore } from "../../react/use-store.js";
|
|
5
|
-
import { useEditorSettings } from "../../react/settings.js";
|
|
6
4
|
import { Button } from "../../react/ui/button.js";
|
|
7
|
-
import { revealBlockInEditor } from "../../outline/heading-outline.js";
|
|
8
|
-
import { compositeJSON, compositeKindsOf } from "../../features/composites/nodes.js";
|
|
9
|
-
import { moveBlockInto } from "../../features/composites/outline.js";
|
|
10
|
-
import { pickFiles } from "../../features/media/upload.js";
|
|
11
|
-
import { useNotifier } from "../../react/use-notifier.js";
|
|
12
5
|
import { Input } from "../../react/ui/input.js";
|
|
13
6
|
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "../../react/ui/dropdown-menu.js";
|
|
14
|
-
import { useFileImport } from "../../features/media/react.js";
|
|
15
|
-
import { remapFolderPath } from "../folder-paths.js";
|
|
16
7
|
import { Collapsible, CollapsibleContent } from "../../react/ui/collapsible.js";
|
|
17
|
-
import { DRAG_MIME
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
8
|
+
import { DRAG_MIME } from "../tree-items.js";
|
|
9
|
+
import { rowOf } from "../tree-model.js";
|
|
10
|
+
import { useTreeModel } from "./use-tree-model.js";
|
|
11
|
+
import { useRef } from "react";
|
|
20
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
import { toast } from "sonner";
|
|
22
13
|
//#region src/tree/react/DocumentTree.tsx
|
|
23
|
-
const OUTLINE_DEBOUNCE_MS = 150;
|
|
24
|
-
const EMPTY_DOCUMENTS = [];
|
|
25
|
-
const EMPTY_KINDS = [];
|
|
26
14
|
const REVEAL_ON_HOVER = "opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100";
|
|
27
15
|
const isOutlineItem = (item) => item.type === "composite" || item.type === "heading";
|
|
16
|
+
/** The label with the part the title filter matched highlighted. */
|
|
17
|
+
function Label({ label, match }) {
|
|
18
|
+
if (!match) return /* @__PURE__ */ jsx(Fragment$1, { children: label });
|
|
19
|
+
const [at, length] = match;
|
|
20
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
21
|
+
label.slice(0, at),
|
|
22
|
+
/* @__PURE__ */ jsx("mark", {
|
|
23
|
+
className: "rounded-sm bg-primary/20 text-inherit",
|
|
24
|
+
children: label.slice(at, at + length)
|
|
25
|
+
}),
|
|
26
|
+
label.slice(at + length)
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
28
29
|
/** The file browser of a document store: folders and documents with their drag and drop, the open document's outline below it. */
|
|
29
|
-
function DocumentTree({ store, editor, selectedFolder
|
|
30
|
+
function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, documentActions, className }) {
|
|
30
31
|
const { t } = useEditorI18n();
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const { importFiles } = useFileImport(editor);
|
|
34
|
-
const notify = useNotifier(editor);
|
|
35
|
-
const tree = useStore(store.tree);
|
|
36
|
-
const documents = useStore(store.documents) ?? EMPTY_DOCUMENTS;
|
|
37
|
-
const activeId = useStore(store.activeId);
|
|
38
|
-
const structure = settings.blocks.structure;
|
|
39
|
-
const virtual = settings.blocks.treeRoot === "file" && structure === "composite" && !!editor;
|
|
40
|
-
const treeElement = useRef(null);
|
|
41
|
-
const [ownFolder, setOwnFolder] = useState("");
|
|
42
|
-
const selectedFolder = controlledFolder ?? ownFolder;
|
|
43
|
-
const selectedFolderRef = useRef(selectedFolder);
|
|
44
|
-
selectedFolderRef.current = selectedFolder;
|
|
45
|
-
const onSelectedFolderChangeRef = useRef(onSelectedFolderChange);
|
|
46
|
-
onSelectedFolderChangeRef.current = onSelectedFolderChange;
|
|
47
|
-
const setSelectedFolder = useCallback((folder) => {
|
|
48
|
-
selectedFolderRef.current = folder;
|
|
49
|
-
setOwnFolder(folder);
|
|
50
|
-
onSelectedFolderChangeRef.current?.(folder);
|
|
51
|
-
}, []);
|
|
52
|
-
/** Virtual mode: block ID of the selected `folder`/`document` composite; `null` = the document itself. */
|
|
53
|
-
const [virtualTarget, setVirtualTarget] = useState(null);
|
|
54
|
-
const [collapsedOutline, setCollapsedOutline] = useState([]);
|
|
55
|
-
const [dropKey, setDropKey] = useState(null);
|
|
56
|
-
const dragging = useRef(null);
|
|
57
|
-
const [removing, setRemoving] = useState(null);
|
|
58
|
-
const [renaming, setRenamingState] = useState(null);
|
|
59
|
-
const renamingRef = useRef(null);
|
|
60
|
-
const setRenaming = (next) => {
|
|
61
|
-
renamingRef.current = next;
|
|
62
|
-
setRenamingState(next);
|
|
63
|
-
};
|
|
64
|
-
const [creatingFolder, setCreatingFolderState] = useState(null);
|
|
65
|
-
const creatingFolderRef = useRef(null);
|
|
66
|
-
const setCreatingFolder = (next) => {
|
|
67
|
-
creatingFolderRef.current = next;
|
|
68
|
-
setCreatingFolderState(next);
|
|
69
|
-
};
|
|
70
|
-
const kinds = useMemo(() => editor ? compositeKindsOf(editor) : EMPTY_KINDS, [editor]);
|
|
71
|
-
const [outline, setOutline] = useState(EMPTY_OUTLINE);
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
setOutline(readOutline(editor, structure, t));
|
|
74
|
-
if (!editor) return;
|
|
75
|
-
let timer;
|
|
76
|
-
const onUpdate = () => {
|
|
77
|
-
clearTimeout(timer);
|
|
78
|
-
timer = setTimeout(() => setOutline(readOutline(editor, structure, t)), OUTLINE_DEBOUNCE_MS);
|
|
79
|
-
};
|
|
80
|
-
editor.on("update", onUpdate);
|
|
81
|
-
return () => {
|
|
82
|
-
clearTimeout(timer);
|
|
83
|
-
editor.off("update", onUpdate);
|
|
84
|
-
};
|
|
85
|
-
}, [
|
|
32
|
+
const binding = useTreeModel({
|
|
33
|
+
store,
|
|
86
34
|
editor,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
model.expand(ancestorKeys(activeFolder ?? ""));
|
|
96
|
-
if (activeFolder !== void 0) setSelectedFolder(activeFolder);
|
|
97
|
-
}, [
|
|
98
|
-
activeFolder,
|
|
99
|
-
model,
|
|
100
|
-
setSelectedFolder
|
|
101
|
-
]);
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
if (activeId) model.expand([`doc:${activeId}`]);
|
|
104
|
-
}, [activeId, model]);
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (!tree) return;
|
|
107
|
-
const paths = /* @__PURE__ */ new Set();
|
|
108
|
-
collectFolderPaths(tree, paths);
|
|
109
|
-
if (!paths.has(selectedFolderRef.current)) setSelectedFolder("");
|
|
110
|
-
}, [tree, setSelectedFolder]);
|
|
111
|
-
/** A folder moved from `from` to `to`: its open/selected state follows it. */
|
|
112
|
-
function followFolderMove(from, to) {
|
|
113
|
-
model.followFolderMove(from, to);
|
|
114
|
-
setSelectedFolder(remapFolderPath(selectedFolderRef.current, from, to));
|
|
115
|
-
model.expand(ancestorKeys(parentPath(to)));
|
|
116
|
-
}
|
|
117
|
-
/** Makes a composite's children visible in the outline again. */
|
|
118
|
-
function openOutline(blockId) {
|
|
119
|
-
setCollapsedOutline((current) => current.filter((key) => key !== `outline:${blockId}`));
|
|
120
|
-
}
|
|
121
|
-
function selectItem(item) {
|
|
122
|
-
if (renamingRef.current?.item.key === item.key) return;
|
|
123
|
-
if (item.key === "vroot") {
|
|
124
|
-
setVirtualTarget(null);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (item.type === "document") {
|
|
128
|
-
setSelectedFolder(item.folder ?? "");
|
|
129
|
-
store.open(item.id).catch(() => void 0);
|
|
130
|
-
} else setSelectedFolder(item.path ?? "");
|
|
131
|
-
}
|
|
132
|
-
function selectOutline(item) {
|
|
133
|
-
if (editor && !editor.isDestroyed) revealBlockInEditor(editor, {
|
|
134
|
-
id: item.blockId ?? null,
|
|
135
|
-
pos: item.pos
|
|
136
|
-
});
|
|
137
|
-
if (virtual && item.blockId && (item.kind === "folder" || item.kind === "document")) setVirtualTarget(item.blockId);
|
|
138
|
-
}
|
|
139
|
-
const outlineList = outlineItems(outline.nodes, kinds, selectOutline);
|
|
140
|
-
const unfilteredItems = virtual ? [virtualRootItem(documents.find((d) => d.id === activeId)?.title ?? t("tree.rootFallback"), outlineList, selectItem)] : tree ? [folderItem(tree, activeId, outlineList, selectItem)] : [];
|
|
141
|
-
const items = applyNodeFilters(unfilteredItems, nodeFilters);
|
|
142
|
-
const isEmpty = !virtual && !!tree && !tree.folders.length && !tree.documents.length;
|
|
143
|
-
const isOpen = (item) => isOutlineItem(item) ? !collapsedOutline.includes(item.key) : expanded.includes(item.key);
|
|
144
|
-
function setOpen(item, open) {
|
|
145
|
-
if (isOutlineItem(item)) setCollapsedOutline((current) => open ? current.filter((key) => key !== item.key) : current.includes(item.key) ? current : [...current, item.key]);
|
|
146
|
-
else if (open) model.expand([item.key]);
|
|
147
|
-
else model.expanded.set(model.expanded.get().filter((key) => key !== item.key));
|
|
148
|
-
}
|
|
35
|
+
selectedFolder,
|
|
36
|
+
onSelectedFolderChange
|
|
37
|
+
});
|
|
38
|
+
const treeElement = useRef(null);
|
|
39
|
+
if (!binding) return null;
|
|
40
|
+
const { model, state } = binding;
|
|
41
|
+
const { view, creatingFolder } = state;
|
|
149
42
|
function onChevronClick(event, item) {
|
|
150
43
|
if (item.type === "root" || item.type === "folder") return;
|
|
151
44
|
event.stopPropagation();
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
function activate(item) {
|
|
155
|
-
item.onSelect?.();
|
|
156
|
-
if (item.type === "root" || item.type === "folder") setOpen(item, !isOpen(item));
|
|
45
|
+
model.toggle(item.key);
|
|
157
46
|
}
|
|
158
47
|
function onRowKeyDown(event, item) {
|
|
159
48
|
if (event.target !== event.currentTarget) return;
|
|
160
|
-
const expandable
|
|
161
|
-
if (event.key === "Enter" || event.key === " ") activate(item);
|
|
162
|
-
else if (event.key === "ArrowRight" && expandable && !
|
|
163
|
-
else if (event.key === "ArrowLeft" && expandable &&
|
|
49
|
+
const { expandable, open } = rowOf(state, item, t);
|
|
50
|
+
if (event.key === "Enter" || event.key === " ") model.activate(item);
|
|
51
|
+
else if (event.key === "ArrowRight" && expandable && !open) model.setOpen(item.key, true);
|
|
52
|
+
else if (event.key === "ArrowLeft" && expandable && open) model.setOpen(item.key, false);
|
|
164
53
|
else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
165
54
|
const rows = [...treeElement.current?.querySelectorAll("[role=\"treeitem\"]") ?? []];
|
|
166
55
|
rows[rows.indexOf(event.currentTarget) + (event.key === "ArrowDown" ? 1 : -1)]?.focus();
|
|
167
56
|
} else return;
|
|
168
57
|
event.preventDefault();
|
|
169
58
|
}
|
|
170
|
-
const kindsOf = (item) => filterKinds(item, {
|
|
171
|
-
structure,
|
|
172
|
-
activeId,
|
|
173
|
-
kinds,
|
|
174
|
-
t
|
|
175
|
-
});
|
|
176
|
-
const hiddenBelow = (item) => nodeFilters[item.key] ?? [];
|
|
177
|
-
const isFiltered = (item) => hiddenBelow(item).length > 0;
|
|
178
|
-
const showFilter = (item) => kindsOf(item).length > 0 && (item.type === "root" || item.type === "folder" || !!item.children?.length || isFiltered(item));
|
|
179
|
-
const visibleKeys = collectKeys(unfilteredItems, /* @__PURE__ */ new Set());
|
|
180
|
-
const hasFilters = Object.entries(nodeFilters).some(([key, hidden]) => hidden.length > 0 && visibleKeys.has(key));
|
|
181
|
-
async function importIntoComposite(blockId) {
|
|
182
|
-
const files = await pickFiles("*/*");
|
|
183
|
-
if (!files.length || !editor || editor.isDestroyed) return;
|
|
184
|
-
await importFiles(editor, files, compositeInsertPos(editor, blockId));
|
|
185
|
-
if (blockId) openOutline(blockId);
|
|
186
|
-
}
|
|
187
|
-
function createCompositeFolder(blockId) {
|
|
188
|
-
if (!editor || editor.isDestroyed) return;
|
|
189
|
-
const pos = compositeInsertPos(editor, blockId);
|
|
190
|
-
editor.chain().insertContentAt(pos, compositeJSON("folder")).setTextSelection(pos + 2).focus().scrollIntoView().run();
|
|
191
|
-
if (blockId) openOutline(blockId);
|
|
192
|
-
}
|
|
193
|
-
async function createDocumentIn(folder) {
|
|
194
|
-
setSelectedFolder(folder);
|
|
195
|
-
const doc = await store.create(void 0, folder).catch(() => void 0);
|
|
196
|
-
if (!doc) return;
|
|
197
|
-
model.expand(ancestorKeys(folder));
|
|
198
|
-
await store.open(doc.id).catch(() => void 0);
|
|
199
|
-
}
|
|
200
|
-
const folderError = creatingFolder?.draft.trim() ? store.validateName(creatingFolder.draft, folderNamesIn(tree, creatingFolder.parent)) : null;
|
|
201
|
-
function startCreateFolder(parent) {
|
|
202
|
-
setSelectedFolder(parent);
|
|
203
|
-
setCreatingFolder({
|
|
204
|
-
parent,
|
|
205
|
-
draft: ""
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
async function commitCreateFolder() {
|
|
209
|
-
const current = creatingFolderRef.current;
|
|
210
|
-
if (!current) return;
|
|
211
|
-
if (!current.draft.trim()) {
|
|
212
|
-
setCreatingFolder(null);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
if (folderError) return;
|
|
216
|
-
setCreatingFolder(null);
|
|
217
|
-
if (await store.createFolder(joinPath(current.parent, current.draft.trim())).then(() => true, () => false)) model.expand(ancestorKeys(current.parent));
|
|
218
|
-
}
|
|
219
|
-
const renameErrorOf = (current) => !current || current.draft.trim() === current.item.label ? null : store.validateName(current.draft, folderNamesIn(tree, parentPath(current.item.path), current.item.path));
|
|
220
|
-
const renameError = renameErrorOf(renaming);
|
|
221
|
-
async function commitRename() {
|
|
222
|
-
const current = renamingRef.current;
|
|
223
|
-
if (!current) return;
|
|
224
|
-
if (renameErrorOf(current)) return;
|
|
225
|
-
const name = current.draft.trim();
|
|
226
|
-
setRenaming(null);
|
|
227
|
-
if (name === current.item.label) return;
|
|
228
|
-
const to = joinPath(parentPath(current.item.path), name);
|
|
229
|
-
if (await store.moveFolder(current.item.path, to).then(() => true, () => false)) followFolderMove(current.item.path, to);
|
|
230
|
-
}
|
|
231
|
-
function onRenameBlur() {
|
|
232
|
-
const current = renamingRef.current;
|
|
233
|
-
if (!current) return;
|
|
234
|
-
const error = renameErrorOf(current);
|
|
235
|
-
if (error) {
|
|
236
|
-
notify({
|
|
237
|
-
kind: "warning",
|
|
238
|
-
title: t("tree.folder.renameFailed"),
|
|
239
|
-
description: error
|
|
240
|
-
});
|
|
241
|
-
setRenaming(null);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
return commitRename();
|
|
245
|
-
}
|
|
246
|
-
async function restore(removed) {
|
|
247
|
-
const restored = await removed.restore().catch(() => void 0);
|
|
248
|
-
if (restored) await store.open(restored.id).catch(() => void 0);
|
|
249
|
-
}
|
|
250
|
-
async function removeWithUndo(id, folder) {
|
|
251
|
-
const removed = await store.remove(id, folder).catch(() => void 0);
|
|
252
|
-
if (!removed) return;
|
|
253
|
-
toast(t("tree.removed"), {
|
|
254
|
-
description: removed.title,
|
|
255
|
-
icon: /* @__PURE__ */ jsx(Icon, { name: "i-lucide-trash-2" }),
|
|
256
|
-
action: {
|
|
257
|
-
label: t("tree.undo"),
|
|
258
|
-
onClick: () => {
|
|
259
|
-
restore(removed);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
async function removeItem(item) {
|
|
265
|
-
setRemoving(item.key);
|
|
266
|
-
try {
|
|
267
|
-
if (item.type === "document") await removeWithUndo(item.id, item.folder ?? "");
|
|
268
|
-
else await store.removeFolder(item.path).catch(() => void 0);
|
|
269
|
-
} finally {
|
|
270
|
-
setRemoving(null);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* What can be created below `item`: folders of the store take documents and sub-folders, the open file (as the root or
|
|
275
|
-
* as a document row) and its Ordner/Dokument composites take Ordner and imported Dokument Bausteine. Other rows take
|
|
276
|
-
* nothing. Folders also list „Umbenennen“ here: renaming a document happens in the editor's title field.
|
|
277
|
-
*/
|
|
278
|
-
function addMenu(item) {
|
|
279
|
-
const groups = [];
|
|
280
|
-
if (item.key !== "vroot" && (item.type === "root" || item.type === "folder")) {
|
|
281
|
-
const path = item.path;
|
|
282
|
-
groups.push([{
|
|
283
|
-
label: t("tree.menu.newDocument"),
|
|
284
|
-
icon: "i-lucide-file-plus",
|
|
285
|
-
onSelect: () => void createDocumentIn(path)
|
|
286
|
-
}, {
|
|
287
|
-
label: item.type === "root" ? t("tree.menu.newFolder") : t("tree.menu.newSubfolder"),
|
|
288
|
-
icon: "i-lucide-folder-plus",
|
|
289
|
-
onSelect: () => startCreateFolder(path)
|
|
290
|
-
}]);
|
|
291
|
-
} else if (holdsComposites(item, {
|
|
292
|
-
structure,
|
|
293
|
-
editor,
|
|
294
|
-
activeId
|
|
295
|
-
})) {
|
|
296
|
-
const blockId = item.blockId ?? null;
|
|
297
|
-
groups.push([{
|
|
298
|
-
label: t("tree.menu.newCompositeFolder"),
|
|
299
|
-
icon: "i-lucide-folder-plus",
|
|
300
|
-
onSelect: () => {
|
|
301
|
-
createCompositeFolder(blockId);
|
|
302
|
-
if (!blockId) model.expand([item.key]);
|
|
303
|
-
}
|
|
304
|
-
}, {
|
|
305
|
-
label: t("tree.menu.importFile"),
|
|
306
|
-
icon: "i-lucide-file-up",
|
|
307
|
-
onSelect: () => {
|
|
308
|
-
importIntoComposite(blockId);
|
|
309
|
-
if (!blockId) model.expand([item.key]);
|
|
310
|
-
}
|
|
311
|
-
}]);
|
|
312
|
-
}
|
|
313
|
-
if (item.type === "folder") groups.push([{
|
|
314
|
-
label: t("tree.menu.rename"),
|
|
315
|
-
icon: "i-lucide-pencil",
|
|
316
|
-
onSelect: () => setRenaming({
|
|
317
|
-
item,
|
|
318
|
-
draft: item.label
|
|
319
|
-
})
|
|
320
|
-
}]);
|
|
321
|
-
return groups;
|
|
322
|
-
}
|
|
323
|
-
const isDraggable$1 = (item) => renaming?.item.key !== item.key && isDraggable(item, virtual);
|
|
324
59
|
function onDragStart(event, item) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
dragging.current = payload;
|
|
60
|
+
const payload = model.dragStart(item);
|
|
61
|
+
if (!payload) return event.preventDefault();
|
|
328
62
|
event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload));
|
|
329
63
|
event.dataTransfer.effectAllowed = "move";
|
|
330
64
|
}
|
|
331
65
|
function onDragOver(event, item) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
event.preventDefault();
|
|
337
|
-
event.dataTransfer.dropEffect = "move";
|
|
338
|
-
setDropKey(item.key);
|
|
66
|
+
const verdict = model.dragOver(item);
|
|
67
|
+
if (!verdict) return;
|
|
68
|
+
if (verdict.ok) event.preventDefault();
|
|
69
|
+
event.dataTransfer.dropEffect = verdict.ok ? "move" : "none";
|
|
339
70
|
}
|
|
340
71
|
function onDragLeave(event, item) {
|
|
341
|
-
if (
|
|
72
|
+
if (!event.currentTarget.contains(event.relatedTarget)) model.dragLeave(item);
|
|
342
73
|
}
|
|
343
|
-
function
|
|
344
|
-
|
|
345
|
-
setDropKey(null);
|
|
346
|
-
}
|
|
347
|
-
async function onDrop(event, target) {
|
|
348
|
-
const raw = event.dataTransfer.getData(DRAG_MIME);
|
|
349
|
-
endDrag();
|
|
350
|
-
if (!raw) return;
|
|
74
|
+
function onDrop(event, item) {
|
|
75
|
+
if (!state.drag) return;
|
|
351
76
|
event.preventDefault();
|
|
352
|
-
|
|
353
|
-
if (!canDrop(source, target, {
|
|
354
|
-
virtual,
|
|
355
|
-
editor
|
|
356
|
-
})) return;
|
|
357
|
-
if (source.type === "composite") {
|
|
358
|
-
const targetId = target.key === "vroot" ? null : target.blockId;
|
|
359
|
-
if (editor && moveBlockInto(editor, source.blockId, targetId) && targetId) openOutline(targetId);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const targetPath = target.path;
|
|
363
|
-
if (source.type === "document") {
|
|
364
|
-
if (await store.move(source.id, targetPath).then(() => true, () => false)) model.expand(ancestorKeys(targetPath));
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
const to = joinPath(targetPath, source.path.slice(source.path.lastIndexOf("/") + 1));
|
|
368
|
-
if (await store.moveFolder(source.path, to).then(() => true, () => false)) followFolderMove(source.path, to);
|
|
77
|
+
model.drop(item);
|
|
369
78
|
}
|
|
370
79
|
const filterMenu = (item) => {
|
|
371
|
-
const
|
|
80
|
+
const { filterKinds, hiddenKinds } = rowOf(state, item, t);
|
|
372
81
|
return /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
373
82
|
align: "start",
|
|
374
83
|
className: "w-auto",
|
|
375
84
|
children: [
|
|
376
85
|
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("tree.filter.show") }),
|
|
377
|
-
|
|
378
|
-
checked: !
|
|
86
|
+
filterKinds.map((kind) => /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
87
|
+
checked: !hiddenKinds.includes(kind.id),
|
|
379
88
|
onCheckedChange: (visible) => model.setKindHidden(item.key, kind.id, !visible),
|
|
380
89
|
onSelect: (event) => event.preventDefault(),
|
|
381
90
|
children: [/* @__PURE__ */ jsx(Icon, { name: kind.icon }), kind.label]
|
|
382
91
|
}, kind.id)),
|
|
383
|
-
|
|
92
|
+
hiddenKinds.length > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
384
93
|
onSelect: () => model.clearFilter(item.key),
|
|
385
94
|
children: [/* @__PURE__ */ jsx(Icon, { name: "i-lucide-filter-x" }), t("tree.filter.reset")]
|
|
386
95
|
})] })
|
|
387
96
|
]
|
|
388
97
|
});
|
|
389
98
|
};
|
|
390
|
-
const renderRow = (item,
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
99
|
+
const renderRow = (item, level) => {
|
|
100
|
+
const row = rowOf(state, item, t);
|
|
101
|
+
const add = model.addMenu(item);
|
|
102
|
+
const filtered = row.hiddenKinds.length > 0;
|
|
103
|
+
const refused = row.drop && !row.drop.ok ? row.drop.reason : null;
|
|
104
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
396
105
|
role: "treeitem",
|
|
397
106
|
tabIndex: 0,
|
|
398
107
|
"aria-level": level + 1,
|
|
399
|
-
"aria-expanded":
|
|
400
|
-
"aria-selected":
|
|
401
|
-
className: cn("group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring",
|
|
402
|
-
draggable: isDraggable
|
|
403
|
-
onClick: () => activate(item),
|
|
108
|
+
"aria-expanded": row.expandable ? row.open : void 0,
|
|
109
|
+
"aria-selected": row.active ? true : void 0,
|
|
110
|
+
className: cn("group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring", row.active && "bg-primary/10 font-medium text-primary", row.selected && "bg-muted", row.drop?.ok && "ring-1 ring-primary", refused && "bg-destructive/10 ring-1 ring-destructive"),
|
|
111
|
+
draggable: model.isDraggable(item),
|
|
112
|
+
onClick: () => model.activate(item),
|
|
404
113
|
onKeyDown: (event) => onRowKeyDown(event, item),
|
|
405
114
|
onDragStart: (event) => onDragStart(event, item),
|
|
406
115
|
onDragOver: (event) => onDragOver(event, item),
|
|
407
116
|
onDragLeave: (event) => onDragLeave(event, item),
|
|
408
|
-
onDrop: (event) =>
|
|
409
|
-
onDragEnd:
|
|
117
|
+
onDrop: (event) => onDrop(event, item),
|
|
118
|
+
onDragEnd: () => model.dragEnd(),
|
|
410
119
|
children: [
|
|
411
|
-
|
|
120
|
+
row.expandable ? /* @__PURE__ */ jsx("span", {
|
|
412
121
|
className: "flex shrink-0",
|
|
413
122
|
onClick: (event) => onChevronClick(event, item),
|
|
414
123
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
415
124
|
name: "i-lucide-chevron-right",
|
|
416
|
-
className: cn("size-3.5 text-muted-foreground transition-transform", open && "rotate-90")
|
|
125
|
+
className: cn("size-3.5 text-muted-foreground transition-transform", row.open && "rotate-90")
|
|
417
126
|
})
|
|
418
127
|
}) : isOutlineItem(item) && /* @__PURE__ */ jsx("span", { className: "size-3.5 shrink-0" }),
|
|
419
128
|
/* @__PURE__ */ jsx(Icon, { name: item.icon }),
|
|
420
|
-
renaming
|
|
129
|
+
row.renaming ? /* @__PURE__ */ jsxs("div", {
|
|
421
130
|
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
422
131
|
children: [/* @__PURE__ */ jsx(Input, {
|
|
423
|
-
value: renaming.draft,
|
|
132
|
+
value: row.renaming.draft,
|
|
424
133
|
className: "h-6 min-w-0 px-2 text-xs md:text-xs",
|
|
425
134
|
autoFocus: true,
|
|
426
135
|
"aria-label": t("tree.folder.name"),
|
|
427
|
-
"aria-invalid": !!
|
|
428
|
-
onChange: (event) =>
|
|
429
|
-
item: renaming.item,
|
|
430
|
-
draft: event.target.value
|
|
431
|
-
}),
|
|
136
|
+
"aria-invalid": !!row.renaming.error,
|
|
137
|
+
onChange: (event) => model.setRenameDraft(event.target.value),
|
|
432
138
|
onFocus: (event) => event.currentTarget.select(),
|
|
433
139
|
onClick: (event) => event.stopPropagation(),
|
|
434
140
|
onKeyDown: (event) => {
|
|
435
141
|
event.stopPropagation();
|
|
436
|
-
if (event.key === "Enter") commitRename();
|
|
437
|
-
else if (event.key === "Escape")
|
|
142
|
+
if (event.key === "Enter") model.commitRename();
|
|
143
|
+
else if (event.key === "Escape") model.cancelRename();
|
|
438
144
|
},
|
|
439
|
-
onBlur: () => void
|
|
440
|
-
}),
|
|
145
|
+
onBlur: () => void model.blurRename()
|
|
146
|
+
}), row.renaming.error && /* @__PURE__ */ jsx("p", {
|
|
441
147
|
className: "text-xs text-destructive",
|
|
442
148
|
role: "alert",
|
|
443
149
|
onClick: (event) => event.stopPropagation(),
|
|
444
|
-
children:
|
|
150
|
+
children: row.renaming.error
|
|
445
151
|
})]
|
|
446
152
|
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
447
153
|
/* @__PURE__ */ jsx("span", {
|
|
448
154
|
className: "min-w-0 flex-1 truncate",
|
|
449
155
|
title: item.label,
|
|
450
|
-
children:
|
|
156
|
+
children: /* @__PURE__ */ jsx(Label, {
|
|
157
|
+
label: item.label,
|
|
158
|
+
match: row.match
|
|
159
|
+
})
|
|
451
160
|
}),
|
|
452
161
|
item.hasSummary && /* @__PURE__ */ jsx(Icon, {
|
|
453
162
|
name: "i-lucide-text-quote",
|
|
@@ -456,7 +165,7 @@ function DocumentTree({ store, editor, selectedFolder: controlledFolder, onSelec
|
|
|
456
165
|
/* @__PURE__ */ jsxs("div", {
|
|
457
166
|
className: "flex shrink-0",
|
|
458
167
|
onClick: (event) => event.stopPropagation(),
|
|
459
|
-
children: [showFilter
|
|
168
|
+
children: [row.showFilter && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
460
169
|
asChild: true,
|
|
461
170
|
children: /* @__PURE__ */ jsx(Button, {
|
|
462
171
|
size: "icon-xs",
|
|
@@ -483,7 +192,7 @@ function DocumentTree({ store, editor, selectedFolder: controlledFolder, onSelec
|
|
|
483
192
|
className: "w-auto",
|
|
484
193
|
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
485
194
|
children: add.map((group, index) => /* @__PURE__ */ jsxs("div", { children: [index > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), group.map((entry) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
486
|
-
onSelect: entry.
|
|
195
|
+
onSelect: entry.run,
|
|
487
196
|
children: [/* @__PURE__ */ jsx(Icon, { name: entry.icon }), entry.label]
|
|
488
197
|
}, entry.label))] }, index))
|
|
489
198
|
})] }),
|
|
@@ -496,11 +205,11 @@ function DocumentTree({ store, editor, selectedFolder: controlledFolder, onSelec
|
|
|
496
205
|
variant: "ghost",
|
|
497
206
|
className: "text-destructive hover:text-destructive",
|
|
498
207
|
"aria-label": t("tree.delete"),
|
|
499
|
-
disabled: removing
|
|
500
|
-
onClick: () => void
|
|
208
|
+
disabled: row.removing,
|
|
209
|
+
onClick: () => void model.remove(item),
|
|
501
210
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
502
|
-
name: removing
|
|
503
|
-
className: cn(removing
|
|
211
|
+
name: row.removing ? "i-lucide-loader-circle" : "i-lucide-trash-2",
|
|
212
|
+
className: cn(row.removing && "animate-spin")
|
|
504
213
|
})
|
|
505
214
|
})
|
|
506
215
|
]
|
|
@@ -508,28 +217,56 @@ function DocumentTree({ store, editor, selectedFolder: controlledFolder, onSelec
|
|
|
508
217
|
})
|
|
509
218
|
] })
|
|
510
219
|
]
|
|
511
|
-
})
|
|
220
|
+
}), refused && /* @__PURE__ */ jsx("p", {
|
|
221
|
+
className: "px-2 pt-0.5 text-xs text-destructive",
|
|
222
|
+
role: "status",
|
|
223
|
+
children: refused
|
|
224
|
+
})] });
|
|
512
225
|
};
|
|
513
226
|
const renderItems = (list, level) => /* @__PURE__ */ jsx("ul", {
|
|
514
227
|
role: level === 0 ? "tree" : "group",
|
|
515
228
|
"aria-label": level === 0 ? t("tree.aria") : void 0,
|
|
516
229
|
ref: level === 0 ? treeElement : void 0,
|
|
517
230
|
className: cn("m-0 list-none p-0", level > 0 && "ml-3 border-l border-border pl-1"),
|
|
518
|
-
children: list.map((item) => {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
children: /* @__PURE__ */
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
})
|
|
526
|
-
}, item.key);
|
|
527
|
-
})
|
|
231
|
+
children: list.map((item) => /* @__PURE__ */ jsx("li", {
|
|
232
|
+
role: "none",
|
|
233
|
+
children: /* @__PURE__ */ jsxs(Collapsible, {
|
|
234
|
+
open: rowOf(state, item, t).open,
|
|
235
|
+
children: [renderRow(item, level), item.children?.length ? /* @__PURE__ */ jsx(CollapsibleContent, { children: renderItems(item.children, level + 1) }) : null]
|
|
236
|
+
})
|
|
237
|
+
}, item.key))
|
|
528
238
|
});
|
|
529
239
|
return /* @__PURE__ */ jsxs("div", {
|
|
530
240
|
className: cn("flex flex-col gap-2", className),
|
|
531
241
|
children: [
|
|
532
|
-
|
|
242
|
+
/* @__PURE__ */ jsxs("div", {
|
|
243
|
+
className: "relative",
|
|
244
|
+
children: [
|
|
245
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
246
|
+
name: "i-lucide-search",
|
|
247
|
+
className: "pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-muted-foreground"
|
|
248
|
+
}),
|
|
249
|
+
/* @__PURE__ */ jsx(Input, {
|
|
250
|
+
value: state.filterText,
|
|
251
|
+
className: "h-7 pr-7 pl-7 text-sm md:text-sm",
|
|
252
|
+
placeholder: t("tree.filterText.placeholder"),
|
|
253
|
+
"aria-label": t("tree.filterText.placeholder"),
|
|
254
|
+
onChange: (event) => model.setFilterText(event.target.value),
|
|
255
|
+
onKeyDown: (event) => {
|
|
256
|
+
if (event.key === "Escape") model.setFilterText("");
|
|
257
|
+
}
|
|
258
|
+
}),
|
|
259
|
+
state.filterText && /* @__PURE__ */ jsx(Button, {
|
|
260
|
+
size: "icon-xs",
|
|
261
|
+
variant: "ghost",
|
|
262
|
+
className: "absolute top-1/2 right-1 -translate-y-1/2",
|
|
263
|
+
"aria-label": t("tree.filterText.clear"),
|
|
264
|
+
onClick: () => model.setFilterText(""),
|
|
265
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "i-lucide-x" })
|
|
266
|
+
})
|
|
267
|
+
]
|
|
268
|
+
}),
|
|
269
|
+
view.hasKindFilters && /* @__PURE__ */ jsx("div", {
|
|
533
270
|
className: "flex items-center",
|
|
534
271
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
535
272
|
size: "xs",
|
|
@@ -546,27 +283,28 @@ function DocumentTree({ store, editor, selectedFolder: controlledFolder, onSelec
|
|
|
546
283
|
autoFocus: true,
|
|
547
284
|
placeholder: creatingFolder.parent ? t("tree.folder.placeholderIn", { parent: creatingFolder.parent }) : t("tree.folder.placeholder"),
|
|
548
285
|
"aria-label": t("tree.folder.name"),
|
|
549
|
-
"aria-invalid": !!
|
|
550
|
-
onChange: (event) =>
|
|
551
|
-
parent: creatingFolder.parent,
|
|
552
|
-
draft: event.target.value
|
|
553
|
-
}),
|
|
286
|
+
"aria-invalid": !!creatingFolder.error,
|
|
287
|
+
onChange: (event) => model.setFolderDraft(event.target.value),
|
|
554
288
|
onKeyDown: (event) => {
|
|
555
|
-
if (event.key === "Enter") commitCreateFolder();
|
|
556
|
-
else if (event.key === "Escape")
|
|
289
|
+
if (event.key === "Enter") model.commitCreateFolder();
|
|
290
|
+
else if (event.key === "Escape") model.cancelCreateFolder();
|
|
557
291
|
},
|
|
558
|
-
onBlur: () =>
|
|
559
|
-
}),
|
|
292
|
+
onBlur: () => model.cancelCreateFolder()
|
|
293
|
+
}), creatingFolder.error && /* @__PURE__ */ jsx("p", {
|
|
560
294
|
className: "px-0.5 text-xs text-destructive",
|
|
561
295
|
role: "alert",
|
|
562
|
-
children:
|
|
296
|
+
children: creatingFolder.error
|
|
563
297
|
})]
|
|
564
298
|
}),
|
|
565
|
-
!tree && !virtual ? /* @__PURE__ */ jsx("p", {
|
|
299
|
+
!state.tree && !state.virtual ? /* @__PURE__ */ jsx("p", {
|
|
566
300
|
className: "px-2.5 py-1.5 text-sm text-muted-foreground",
|
|
567
|
-
children: tree === null ? t("tree.loadFailed") : t("tree.loading")
|
|
568
|
-
}) : renderItems(items, 0),
|
|
569
|
-
|
|
301
|
+
children: state.tree === null ? t("tree.loadFailed") : t("tree.loading")
|
|
302
|
+
}) : renderItems(view.items, 0),
|
|
303
|
+
view.noMatches && /* @__PURE__ */ jsx("p", {
|
|
304
|
+
className: "px-2.5 py-1.5 text-sm text-muted-foreground",
|
|
305
|
+
children: t("tree.noMatches")
|
|
306
|
+
}),
|
|
307
|
+
view.isEmpty && /* @__PURE__ */ jsx("p", {
|
|
570
308
|
className: "px-2.5 py-1.5 text-sm text-muted-foreground",
|
|
571
309
|
children: t("tree.empty")
|
|
572
310
|
})
|