[{"data":1,"prerenderedAt":2322},["ShallowReactive",2],{"navigation_docs":3,"-guides-document-tree":412,"-guides-document-tree-surround":2317},[4,38,84,326,386],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":37},"Getting started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20,24,28,32],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-book-open",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":6},"Quick start: Nuxt","\u002Fgetting-started\u002Fquick-start-nuxt","1.getting-started\u002F3.quick-start-nuxt",{"title":25,"path":26,"stem":27,"icon":6},"Quick start: plain Vue","\u002Fgetting-started\u002Fquick-start-vue","1.getting-started\u002F4.quick-start-vue",{"title":29,"path":30,"stem":31,"icon":6},"Quick start: React","\u002Fgetting-started\u002Fquick-start-react","1.getting-started\u002F5.quick-start-react",{"title":33,"path":34,"stem":35,"icon":36},"Glossary","\u002Fgetting-started\u002Fglossary","1.getting-started\u002F6.glossary","i-lucide-book-a",false,{"title":39,"icon":40,"path":41,"stem":42,"children":43,"page":37},"Tutorial","i-lucide-graduation-cap","\u002Ftutorial","2.tutorial",[44,49,54,59,64,69,74,79],{"title":45,"path":46,"stem":47,"icon":48},"Tutorial: overview","\u002Ftutorial\u002Foverview","2.tutorial\u002F1.overview","i-lucide-map",{"title":50,"path":51,"stem":52,"icon":53},"Chapter 2: A simple editor","\u002Ftutorial\u002Fsimple-editor","2.tutorial\u002F2.simple-editor","i-lucide-pencil",{"title":55,"path":56,"stem":57,"icon":58},"Chapter 3: Compose features","\u002Ftutorial\u002Fcompose-features","2.tutorial\u002F3.compose-features","i-lucide-blocks",{"title":60,"path":61,"stem":62,"icon":63},"Chapter 4: Your own block, path A","\u002Ftutorial\u002Fown-block-path-a","2.tutorial\u002F4.own-block-path-a","i-lucide-square-plus",{"title":65,"path":66,"stem":67,"icon":68},"Chapter 5: Your own feature, path B","\u002Ftutorial\u002Fown-feature-path-b","2.tutorial\u002F5.own-feature-path-b","i-lucide-puzzle",{"title":70,"path":71,"stem":72,"icon":73},"Chapter 6: Backend and collaboration","\u002Ftutorial\u002Fbackend-and-collaboration","2.tutorial\u002F6.backend-and-collaboration","i-lucide-users",{"title":75,"path":76,"stem":77,"icon":78},"Chapter 7: AI","\u002Ftutorial\u002Fai","2.tutorial\u002F7.ai","i-lucide-sparkles",{"title":80,"path":81,"stem":82,"icon":83},"Chapter 8: The React surface","\u002Ftutorial\u002Freact-surface","2.tutorial\u002F8.react-surface","i-lucide-atom",{"title":85,"icon":14,"path":86,"stem":87,"children":88,"page":37},"Guides","\u002Fguides","3.guides",[89,93,98,103,108,112,116,121,126,130,134,139,144,149,154,159,164,169,174,179,184,189,194,199,204,209,213,218,223,228,233,237,242,247,251,256,261,266,271,276,281,286,291,295,302,307,311,316,321],{"title":90,"path":91,"stem":92,"icon":58},"Feature composition and entries","\u002Fguides\u002Fcomposition","3.guides\u002F1.composition",{"title":94,"path":95,"stem":96,"icon":97},"Media, mentions and transcript features","\u002Fguides\u002Fmedia-mentions-transcript","3.guides\u002F10.media-mentions-transcript","i-lucide-image",{"title":99,"path":100,"stem":101,"icon":102},"Authors and comments","\u002Fguides\u002Fauthors-and-comments","3.guides\u002F11.authors-and-comments","i-lucide-message-square-text",{"title":104,"path":105,"stem":106,"icon":107},"Themes (Obsidian)","\u002Fguides\u002Fthemes","3.guides\u002F12.themes","i-lucide-palette",{"title":109,"path":110,"stem":111,"icon":58},"Composites and the settings UI","\u002Fguides\u002Fcomposites","3.guides\u002F13.composites",{"title":113,"path":114,"stem":115,"icon":78},"AI","\u002Fguides\u002Fai","3.guides\u002F14.ai",{"title":117,"path":118,"stem":119,"icon":120},"Document tree","\u002Fguides\u002Fdocument-tree","3.guides\u002F15.document-tree","i-lucide-folder-tree",{"title":122,"path":123,"stem":124,"icon":125},"Markdown and code","\u002Fguides\u002Fmarkdown-and-code","3.guides\u002F16.markdown-and-code","i-lucide-file-code",{"title":127,"path":128,"stem":129,"icon":83},"Advanced editor in React","\u002Fguides\u002Fadvanced-react","3.guides\u002F17.advanced-react",{"title":131,"path":132,"stem":133,"icon":6},"Kitchen sink: learning path","\u002Fguides\u002Fadvanced-nuxt-example","3.guides\u002F18.advanced-nuxt-example",{"title":135,"path":136,"stem":137,"icon":138},"Production","\u002Fguides\u002Fproduction","3.guides\u002F19.production","i-lucide-server",{"title":140,"path":141,"stem":142,"icon":143},"Register the Nuxt module","\u002Fguides\u002Fnuxt-module","3.guides\u002F2.nuxt-module","i-lucide-package",{"title":145,"path":146,"stem":147,"icon":148},"Storage adapters, comment stores, Nitro host","\u002Fguides\u002Fstorage-adapters","3.guides\u002F20.storage-adapters","i-lucide-database",{"title":150,"path":151,"stem":152,"icon":153},"Migrating from #tiptap-editor","\u002Fguides\u002Fmigration","3.guides\u002F21.migration","i-lucide-arrow-right-left",{"title":155,"path":156,"stem":157,"icon":158},"Content tree mechanics","\u002Fguides\u002Fcontent-tree","3.guides\u002F22.content-tree","i-lucide-git-fork",{"title":160,"path":161,"stem":162,"icon":163},"Block capabilities, abilities and actions","\u002Fguides\u002Fblock-capabilities","3.guides\u002F23.block-capabilities","i-lucide-shield-check",{"title":165,"path":166,"stem":167,"icon":168},"Block types in code and JSON","\u002Fguides\u002Fblock-types","3.guides\u002F24.block-types","i-lucide-shapes",{"title":170,"path":171,"stem":172,"icon":173},"Derive methods and the Derivation index","\u002Fguides\u002Fderive-methods","3.guides\u002F25.derive-methods","i-lucide-git-branch-plus",{"title":175,"path":176,"stem":177,"icon":178},"Block properties and Property definitions","\u002Fguides\u002Fblock-properties","3.guides\u002F26.block-properties","i-lucide-sliders-horizontal",{"title":180,"path":181,"stem":182,"icon":183},"Image studio","\u002Fguides\u002Fimage-studio","3.guides\u002F27.image-studio","i-lucide-image-plus",{"title":185,"path":186,"stem":187,"icon":188},"Prüfung and Freigabestatus","\u002Fguides\u002Fpruefung","3.guides\u002F28.pruefung","i-lucide-stamp",{"title":190,"path":191,"stem":192,"icon":193},"Workspace: find, read, reference and embed Blocks across Documents","\u002Fguides\u002Fworkspace","3.guides\u002F29.workspace","i-lucide-network",{"title":195,"path":196,"stem":197,"icon":198},"EditorPorts: auth and storage","\u002Fguides\u002Feditor-ports","3.guides\u002F3.editor-ports","i-lucide-plug",{"title":200,"path":201,"stem":202,"icon":203},"Collections: an ordered list of Blocks","\u002Fguides\u002Fcollections","3.guides\u002F30.collections","i-lucide-list-checks",{"title":205,"path":206,"stem":207,"icon":208},"Data views: a query shown as table, list or board","\u002Fguides\u002Fdata-views","3.guides\u002F31.data-views","i-lucide-table-properties",{"title":210,"path":211,"stem":212,"icon":193},"Explore: backlinks, related Blocks and the trail","\u002Fguides\u002Fexplore","3.guides\u002F32.explore",{"title":214,"path":215,"stem":216,"icon":217},"Render: export a Block or a Document to a file","\u002Fguides\u002Frender","3.guides\u002F33.render","i-lucide-file-output",{"title":219,"path":220,"stem":221,"icon":222},"Importers: what a dropped file becomes","\u002Fguides\u002Fimporters","3.guides\u002F34.importers","i-lucide-file-input",{"title":224,"path":225,"stem":226,"icon":227},"Templates: start a Document from a Dokumentvorlage","\u002Fguides\u002Ftemplates","3.guides\u002F35.templates","i-lucide-file-stack",{"title":229,"path":230,"stem":231,"icon":232},"Citations","\u002Fguides\u002Fcitations","3.guides\u002F36.citations","i-lucide-quote",{"title":234,"path":235,"stem":236,"icon":163},"AI proposals and the export check","\u002Fguides\u002Fexport-check","3.guides\u002F37.export-check",{"title":238,"path":239,"stem":240,"icon":241},"Query: filter, sort, group and total Blocks","\u002Fguides\u002Fquery","3.guides\u002F38.query","i-lucide-list-filter",{"title":243,"path":244,"stem":245,"icon":246},"Edges: backlinks and authored connections","\u002Fguides\u002Fedges","3.guides\u002F39.edges","i-lucide-link-2",{"title":248,"path":249,"stem":250,"icon":163},"Server routes and authentication","\u002Fguides\u002Froutes-and-auth","3.guides\u002F4.routes-and-auth",{"title":252,"path":253,"stem":254,"icon":255},"Speech: text to audio, and subtitle files","\u002Fguides\u002Fspeech","3.guides\u002F41.speech","i-lucide-audio-lines",{"title":257,"path":258,"stem":259,"icon":260},"Graph: the Edges around a Block","\u002Fguides\u002Fgraph","3.guides\u002F42.graph","i-lucide-waypoints",{"title":262,"path":263,"stem":264,"icon":265},"PDF viewer: passages, anchors and quotes","\u002Fguides\u002Fpdf-viewer","3.guides\u002F43.pdf-viewer","i-lucide-file-search",{"title":267,"path":268,"stem":269,"icon":270},"Obsidian: import and export a vault","\u002Fguides\u002Fobsidian","3.guides\u002F44.obsidian","i-lucide-folder-input",{"title":272,"path":273,"stem":274,"icon":275},"Typst: render a Document as a PDF with citations","\u002Fguides\u002Ftypst","3.guides\u002F45.typst","i-lucide-file-text",{"title":277,"path":278,"stem":279,"icon":280},"Audio: read aloud, mixdown and ElevenLabs","\u002Fguides\u002Faudio","3.guides\u002F46.audio","i-lucide-audio-waveform",{"title":282,"path":283,"stem":284,"icon":285},"Slides: decks, slide layouts and exports","\u002Fguides\u002Fslides","3.guides\u002F47.slides","i-lucide-presentation",{"title":287,"path":288,"stem":289,"icon":290},"Knowledge graph: sources and concepts beside the Documents","\u002Fguides\u002Fknowledge-graph","3.guides\u002F49.knowledge-graph","i-lucide-share-2",{"title":292,"path":293,"stem":294,"icon":78},"AI configuration and secrets","\u002Fguides\u002Fai-configuration","3.guides\u002F5.ai-configuration",{"title":296,"path":297,"stem":298,"children":299,"icon":301},"Index on Postgres: typos, meaning and SQL pushdown","\u002Fguides\u002Findex-pg","3.guides\u002F50.index-pg",[300],{"title":296,"path":297,"stem":298,"icon":301},"i-lucide-database-zap",{"title":303,"path":304,"stem":305,"icon":306},"Charts: bar, line, pie and scatter over a query","\u002Fguides\u002Fcharts","3.guides\u002F51.charts","i-lucide-chart-column",{"title":308,"path":309,"stem":310,"icon":73},"Collaboration on the client","\u002Fguides\u002Fcollaboration","3.guides\u002F6.collaboration",{"title":312,"path":313,"stem":314,"icon":315},"Block UI (drag handle, hover actions)","\u002Fguides\u002Fblock-ui","3.guides\u002F7.block-ui","i-lucide-grip-vertical",{"title":317,"path":318,"stem":319,"icon":320},"Settings and app lifetime","\u002Fguides\u002Fsettings","3.guides\u002F8.settings","i-lucide-settings",{"title":322,"path":323,"stem":324,"icon":325},"Language","\u002Fguides\u002Flanguage","3.guides\u002F9.language","i-lucide-languages",{"title":327,"icon":14,"path":328,"stem":329,"children":330,"page":37},"Reference","\u002Freference","4.reference",[331,335,339,344,348,352,356,360,364,368,373,378,382],{"title":332,"path":333,"stem":334,"icon":143},"Public exports","\u002Freference\u002Fexports","4.reference\u002F1.exports",{"title":336,"path":337,"stem":338,"icon":173},"Derive method contract","\u002Freference\u002Fderive-methods","4.reference\u002F10.derive-methods",{"title":340,"path":341,"stem":342,"icon":343},"Changelog","\u002Freference\u002Fchangelog","4.reference\u002F11.changelog","i-lucide-history",{"title":345,"path":346,"stem":347,"icon":217},"Renderer port","\u002Freference\u002Frenderer-port","4.reference\u002F12.renderer-port",{"title":349,"path":350,"stem":351,"icon":255},"Speech port","\u002Freference\u002Fspeech-port","4.reference\u002F13.speech-port",{"title":353,"path":354,"stem":355,"icon":58},"Presets and features","\u002Freference\u002Fpresets-and-features","4.reference\u002F2.presets-and-features",{"title":357,"path":358,"stem":359,"icon":68},"Feature contract","\u002Freference\u002Ffeature-contract","4.reference\u002F3.feature-contract",{"title":361,"path":362,"stem":363,"icon":107},"CSS and UI-provider requirements","\u002Freference\u002Fcss-and-ui","4.reference\u002F4.css-and-ui",{"title":365,"path":366,"stem":367,"icon":148},"Vue-free advanced schema","\u002Freference\u002Fadvanced-schema","4.reference\u002F5.advanced-schema",{"title":369,"path":370,"stem":371,"icon":372},"Dependency rules","\u002Freference\u002Fdependency-rules","4.reference\u002F6.dependency-rules","i-lucide-git-branch",{"title":374,"path":375,"stem":376,"icon":377},"Commands","\u002Freference\u002Fcommands","4.reference\u002F7.commands","i-lucide-terminal",{"title":379,"path":380,"stem":381,"icon":163},"Block capability contract","\u002Freference\u002Fblock-capabilities","4.reference\u002F8.block-capabilities",{"title":383,"path":384,"stem":385,"icon":168},"Block type contract","\u002Freference\u002Fblock-types","4.reference\u002F9.block-types",{"title":387,"path":388,"stem":389,"children":390,"icon":393},"AI agents","\u002Fai-agents","5.ai-agents\u002F1.index",[391,394,398,402,407],{"title":392,"path":388,"stem":389,"icon":393},"Overview","i-lucide-bot",{"title":395,"path":396,"stem":397,"icon":198},"Integrate the editor","\u002Fai-agents\u002Fintegrate","5.ai-agents\u002F2.integrate",{"title":399,"path":400,"stem":401,"icon":68},"Add a block or feature","\u002Fai-agents\u002Fextend","5.ai-agents\u002F3.extend",{"title":403,"path":404,"stem":405,"icon":406},"Pitfalls and invariants","\u002Fai-agents\u002Finvariants","5.ai-agents\u002F4.invariants","i-lucide-shield-alert",{"title":408,"path":409,"stem":410,"icon":411},"Verify","\u002Fai-agents\u002Fverify","5.ai-agents\u002F5.verify","i-lucide-check-check",{"id":413,"title":117,"body":414,"description":2310,"extension":2311,"links":2312,"meta":2313,"navigation":2314,"path":118,"seo":2315,"stem":119,"__hash__":2316},"docs\u002F3.guides\u002F15.document-tree.md",{"type":415,"value":416,"toc":2302},"minimark",[417,484,496,501,855,861,955,1104,1124,1128,1166,1189,1252,1295,1340,1345,1723,1749,1802,1805,1929,1933,2100,2104,2110,2256,2283,2287,2298],[418,419,420,424,425,429,430,433,434,437,438,441,442,445,446,449,450,453,454,457,458,461,462,461,465,468,469,472,473,476,477,480,481,483],"p",{},[421,422,423],"code",{},"DocumentTree"," is the document drawer: folders and documents of ",[426,427,428],"em",{},"your"," storage, with create, rename, move (drag and drop), delete with undo, per-item Block type filters and the Block outline of the open document. It exists for both surfaces: ",[421,431,432],{},"@skaile\u002Fforge-editor\u002Ftree\u002Fvue"," (Nuxt UI) and ",[421,435,436],{},"@skaile\u002Fforge-editor\u002Ftree\u002Freact"," (shadcn\u002Fui; needs ",[421,439,440],{},"EditorContextProvider"," and a mounted ",[421,443,444],{},"Toaster","). The framework-free ",[421,447,448],{},"@skaile\u002Fforge-editor\u002Ftree"," holds the ",[421,451,452],{},"DocumentStore"," contract (the tree's own interface, not the server port ",[421,455,456],{},"DocumentContentStore",") and the view-state model (",[421,459,460],{},"createTreeView",", ",[421,463,464],{},"treeView(context)",[421,466,467],{},"TreeViewModel",") that both components and their ",[421,470,471],{},"useTreeView()"," hooks sit on. It needs the settings context (",[421,474,475],{},"createEditor()"," \u002F ",[421,478,479],{},"createEditorContext()","), the Typed blocks and media features' code (it reads the Block outline of the Typed blocks and imports files) and a ",[421,482,452],{}," that you implement over your storage and router; it does no HTTP or routing of its own.",[418,485,486,487,490,491,441,493,495],{},"The drawer shows a „Dokumente“ root with folders and documents, create\u002Frename\u002Fmove\u002Fdelete, drag and drop, Block type filters per item, a title filter („Titel filtern“), and the Block outline of the open document (its headings and Typed blocks as one tree, in Flat and Structure mode) listed under it. The tree's own messages (delete with undo) are notices: Vue needs ",[421,488,489],{},"\u003CUApp>",", React needs ",[421,492,440],{},[421,494,444],{},".",[497,498,500],"h2",{"id":499},"mount-the-tree","Mount the tree",[502,503,508],"pre",{"className":504,"code":505,"language":506,"meta":507,"style":507},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport { DocumentTree } from '@skaile\u002Fforge-editor\u002Ftree\u002Fvue'\nimport type { DocumentStore } from '@skaile\u002Fforge-editor\u002Ftree'\nimport type { Editor } from '@tiptap\u002Fcore'\n\ndefineProps\u003C{ store: DocumentStore, editor: Editor | undefined }>()\nconst selectedFolder = defineModel\u003Cstring>('selectedFolder', { default: '' })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CDocumentTree v-model:selected-folder=\"selectedFolder\" :store=\"store\" :editor=\"editor\">\n    \u003Ctemplate #document-actions=\"{ id }\">\u003C!-- optional host buttons per document -->\u003C\u002Ftemplate>\n  \u003C\u002FDocumentTree>\n\u003C\u002Ftemplate>\n","vue","",[421,509,510,545,573,596,619,626,667,717,727,732,742,795,836,846],{"__ignoreMap":507},[511,512,515,519,523,527,530,533,536,540,542],"span",{"class":513,"line":514},"line",1,[511,516,518],{"class":517},"sMK4o","\u003C",[511,520,522],{"class":521},"swJcz","script",[511,524,526],{"class":525},"spNyl"," setup",[511,528,529],{"class":525}," lang",[511,531,532],{"class":517},"=",[511,534,535],{"class":517},"\"",[511,537,539],{"class":538},"sfazB","ts",[511,541,535],{"class":517},[511,543,544],{"class":517},">\n",[511,546,548,552,555,559,562,565,568,570],{"class":513,"line":547},2,[511,549,551],{"class":550},"s7zQu","import",[511,553,554],{"class":517}," {",[511,556,558],{"class":557},"sTEyZ"," DocumentTree",[511,560,561],{"class":517}," }",[511,563,564],{"class":550}," from",[511,566,567],{"class":517}," '",[511,569,432],{"class":538},[511,571,572],{"class":517},"'\n",[511,574,576,578,581,583,586,588,590,592,594],{"class":513,"line":575},3,[511,577,551],{"class":550},[511,579,580],{"class":550}," type",[511,582,554],{"class":517},[511,584,585],{"class":557}," DocumentStore",[511,587,561],{"class":517},[511,589,564],{"class":550},[511,591,567],{"class":517},[511,593,448],{"class":538},[511,595,572],{"class":517},[511,597,599,601,603,605,608,610,612,614,617],{"class":513,"line":598},4,[511,600,551],{"class":550},[511,602,580],{"class":550},[511,604,554],{"class":517},[511,606,607],{"class":557}," Editor",[511,609,561],{"class":517},[511,611,564],{"class":550},[511,613,567],{"class":517},[511,615,616],{"class":538},"@tiptap\u002Fcore",[511,618,572],{"class":517},[511,620,622],{"class":513,"line":621},5,[511,623,625],{"emptyLinePlaceholder":624},true,"\n",[511,627,629,633,636,639,642,645,648,651,653,655,658,661,664],{"class":513,"line":628},6,[511,630,632],{"class":631},"s2Zo4","defineProps",[511,634,635],{"class":517},"\u003C{",[511,637,638],{"class":521}," store",[511,640,641],{"class":517},":",[511,643,585],{"class":644},"sBMFI",[511,646,647],{"class":517},",",[511,649,650],{"class":521}," editor",[511,652,641],{"class":517},[511,654,607],{"class":644},[511,656,657],{"class":517}," |",[511,659,660],{"class":644}," undefined",[511,662,663],{"class":517}," }>",[511,665,666],{"class":557},"()\n",[511,668,670,673,676,678,681,683,686,689,692,695,698,700,702,704,707,709,712,714],{"class":513,"line":669},7,[511,671,672],{"class":525},"const",[511,674,675],{"class":557}," selectedFolder ",[511,677,532],{"class":517},[511,679,680],{"class":631}," defineModel",[511,682,518],{"class":517},[511,684,685],{"class":644},"string",[511,687,688],{"class":517},">",[511,690,691],{"class":557},"(",[511,693,694],{"class":517},"'",[511,696,697],{"class":538},"selectedFolder",[511,699,694],{"class":517},[511,701,647],{"class":517},[511,703,554],{"class":517},[511,705,706],{"class":521}," default",[511,708,641],{"class":517},[511,710,711],{"class":517}," ''",[511,713,561],{"class":517},[511,715,716],{"class":557},")\n",[511,718,720,723,725],{"class":513,"line":719},8,[511,721,722],{"class":517},"\u003C\u002F",[511,724,522],{"class":521},[511,726,544],{"class":517},[511,728,730],{"class":513,"line":729},9,[511,731,625],{"emptyLinePlaceholder":624},[511,733,735,737,740],{"class":513,"line":734},10,[511,736,518],{"class":517},[511,738,739],{"class":521},"template",[511,741,544],{"class":517},[511,743,745,748,750,753,755,758,760,762,764,766,769,772,774,776,778,780,782,785,787,789,791,793],{"class":513,"line":744},11,[511,746,747],{"class":517},"  \u003C",[511,749,423],{"class":521},[511,751,752],{"class":525}," v-model",[511,754,641],{"class":517},[511,756,757],{"class":525},"selected-folder",[511,759,532],{"class":517},[511,761,535],{"class":517},[511,763,697],{"class":557},[511,765,535],{"class":517},[511,767,768],{"class":517}," :",[511,770,771],{"class":525},"store",[511,773,532],{"class":517},[511,775,535],{"class":517},[511,777,771],{"class":557},[511,779,535],{"class":517},[511,781,768],{"class":517},[511,783,784],{"class":525},"editor",[511,786,532],{"class":517},[511,788,535],{"class":517},[511,790,784],{"class":557},[511,792,535],{"class":517},[511,794,544],{"class":517},[511,796,798,801,803,806,809,811,813,816,819,822,824,826,830,832,834],{"class":513,"line":797},12,[511,799,800],{"class":517},"    \u003C",[511,802,739],{"class":521},[511,804,805],{"class":517}," #",[511,807,808],{"class":525},"document-actions",[511,810,532],{"class":517},[511,812,535],{"class":517},[511,814,815],{"class":517},"{",[511,817,818],{"class":557}," id ",[511,820,821],{"class":517},"}",[511,823,535],{"class":517},[511,825,688],{"class":517},[511,827,829],{"class":828},"sHwdD","\u003C!-- optional host buttons per document -->",[511,831,722],{"class":517},[511,833,739],{"class":521},[511,835,544],{"class":517},[511,837,839,842,844],{"class":513,"line":838},13,[511,840,841],{"class":517},"  \u003C\u002F",[511,843,423],{"class":521},[511,845,544],{"class":517},[511,847,849,851,853],{"class":513,"line":848},14,[511,850,722],{"class":517},[511,852,739],{"class":521},[511,854,544],{"class":517},[418,856,857,858,860],{},"React, with the same ",[421,859,771],{}," and a controlled folder:",[502,862,866],{"className":863,"code":864,"language":865,"meta":507,"style":507},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import type { DocumentStore } from '@skaile\u002Fforge-editor\u002Ftree'\nimport { DocumentTree } from '@skaile\u002Fforge-editor\u002Ftree\u002Freact'\nimport type { Editor } from '@tiptap\u002Fcore'\nimport { useState } from 'react'\n\nexport function Drawer({ store, editor }: { store: DocumentStore, editor: Editor | undefined }) {\n  const [selectedFolder, setSelectedFolder] = useState('')\n  return (\n    \u003CDocumentTree\n      store={store}\n      editor={editor}\n      selectedFolder={selectedFolder}\n      onSelectedFolderChange={setSelectedFolder}\n      documentActions={({ id }) => null \u002F* optional host buttons per document *\u002F}\n    \u002F>\n  )\n}\n","tsx",[421,867,868,873,878,883,888,892,897,902,907,912,917,922,927,932,937,943,949],{"__ignoreMap":507},[511,869,870],{"class":513,"line":514},[511,871,872],{},"import type { DocumentStore } from '@skaile\u002Fforge-editor\u002Ftree'\n",[511,874,875],{"class":513,"line":547},[511,876,877],{},"import { DocumentTree } from '@skaile\u002Fforge-editor\u002Ftree\u002Freact'\n",[511,879,880],{"class":513,"line":575},[511,881,882],{},"import type { Editor } from '@tiptap\u002Fcore'\n",[511,884,885],{"class":513,"line":598},[511,886,887],{},"import { useState } from 'react'\n",[511,889,890],{"class":513,"line":621},[511,891,625],{"emptyLinePlaceholder":624},[511,893,894],{"class":513,"line":628},[511,895,896],{},"export function Drawer({ store, editor }: { store: DocumentStore, editor: Editor | undefined }) {\n",[511,898,899],{"class":513,"line":669},[511,900,901],{},"  const [selectedFolder, setSelectedFolder] = useState('')\n",[511,903,904],{"class":513,"line":719},[511,905,906],{},"  return (\n",[511,908,909],{"class":513,"line":729},[511,910,911],{},"    \u003CDocumentTree\n",[511,913,914],{"class":513,"line":734},[511,915,916],{},"      store={store}\n",[511,918,919],{"class":513,"line":744},[511,920,921],{},"      editor={editor}\n",[511,923,924],{"class":513,"line":797},[511,925,926],{},"      selectedFolder={selectedFolder}\n",[511,928,929],{"class":513,"line":838},[511,930,931],{},"      onSelectedFolderChange={setSelectedFolder}\n",[511,933,934],{"class":513,"line":848},[511,935,936],{},"      documentActions={({ id }) => null \u002F* optional host buttons per document *\u002F}\n",[511,938,940],{"class":513,"line":939},15,[511,941,942],{},"    \u002F>\n",[511,944,946],{"class":513,"line":945},16,[511,947,948],{},"  )\n",[511,950,952],{"class":513,"line":951},17,[511,953,954],{},"}\n",[956,957,958,972,995,1027,1038,1066,1076,1089],"ul",{},[959,960,961,963,964,967,968,971],"li",{},[421,962,784],{}," is the open page's editor (",[421,965,966],{},"undefined"," when no document is open); take it from your page through a shared composable, as ",[421,969,970],{},"useActiveEditor()"," does in the Nuxt app.",[959,973,974,977,978,476,980,983,984,987,988,990,991,994],{},[421,975,976],{},"v-model:selectedFolder"," (React: ",[421,979,697],{},[421,981,982],{},"onSelectedFolderChange",", uncontrolled when omitted) is where new documents, new folders and your own import actions go. Bind the same state in your import button (the Nuxt app binds it in the layout and passes it to ",[421,985,986],{},"AppDocumentTree","). The Vue ",[421,989,808],{}," slot is ",[421,992,993],{},"documentActions"," on React.",[959,996,997,998,1000,1001,1003,1004,1007,1008,461,1011,1014,1015,1018,1019,1022,1023,1026],{},"Place ",[421,999,423],{}," anywhere (a sidebar, a drawer); several can coexist, sharing one filter state per app (",[421,1002,471],{}," from ",[421,1005,1006],{},"\u002Ftree\u002Fvue"," returns the refs ",[421,1009,1010],{},"nodeFilters",[421,1012,1013],{},"expanded"," and ",[421,1016,1017],{},"collapsedOutline",", from ",[421,1020,1021],{},"\u002Ftree\u002Freact"," the plain values, both with the framework-free ",[421,1024,1025],{},"model",").",[959,1028,1029,1033,1034,1037],{},[1030,1031,1032],"strong",{},"Title filter."," The field „Titel filtern“ above the tree (clear button, ",[421,1035,1036],{},"Esc"," clears too) filters by title (for a Typed block without a title: by the label derived from its body, see the next but one point), case-insensitive substring, over folders, documents and the outline entries: only matching rows and their parents stay, the matching part is highlighted, and „Keine Treffer“ shows when nothing matches. While the filter is set the parents of matches are shown open without touching the stored open state; clearing the filter restores the rows that were open before (a row opened or closed by hand meanwhile counts only until then). The title filter belongs to one tree; the Block type filters are the shared per-app state.",[959,1039,1040,1043,1044,1047,1048,1051,1052,1054,1055,1058,1059,1062,1063,1065],{},[1030,1041,1042],{},"Refused drops."," While a drag hovers a row, the tree asks whether that drop is allowed (",[421,1045,1046],{},"dropVerdict",", with a reason in the editor's language). An allowed target gets a primary outline and takes the drop; a refused one is outlined red, shows the reason under the row („Notiz nimmt keine Aufgabe auf.“, „Ein Ordner lässt sich nicht in sich selbst oder einen seiner Unterordner verschieben.“, …) and the browser shows „not allowed“. Moving a Typed block is judged by the open document's editor (",[421,1049,1050],{},"moveIntoVerdict",", so the Block types' Accepted children apply); documents and folders follow the folder rules of the ",[421,1053,452],{}," contract. Both components render one framework-free model (",[421,1056,1057],{},"createTreeModel",", internal, not exported from ",[421,1060,1061],{},".\u002Ftree","); the props of ",[421,1064,423],{}," are unchanged.",[959,1067,1068,1069,1072,1073,495],{},"The outline entries under the open document are its headings and Typed blocks in both modes; in Structure mode with ",[421,1070,1071],{},"blocks.treeRoot: 'file'"," the open document is the ",[426,1074,1075],{},"virtual root",[959,1077,1078,1081,1082,461,1085,1088],{},[1030,1079,1080],{},"Label of an untitled Typed block."," A Typed block without a title is labelled by its own body, so a document of many untitled Typed blocks does not read „(ohne Titel)“ in every row: the text of the first heading of its body, else the first non-empty text of its body cut at 60 characters with „…“. Text inside a nested Typed block is not used, and only a Typed block with an empty body keeps „(ohne Titel)“. The row shows such a derived label muted and italic (",[421,1083,1084],{},"TreeItem.labelFromBody",[421,1086,1087],{},"data-label-from-body"," on the label), so it does not pass for a title; the title filter matches it like a title. A title always wins.",[959,1090,1091,1092,1095,1096,1099,1100,1103],{},"The tree header has a „Ordner“ \u002F „Datei als Wurzel“ toggle that writes ",[421,1093,1094],{},"blocks.treeRoot"," (the same value as the radio group in the settings tab „Bearbeiten“). It is disabled, with an explanation, while no Typed block file is open or ",[421,1097,1098],{},"blocks.structure"," is ",[421,1101,1102],{},"'flat'"," (Flat mode); the tree then shows the folders.",[418,1105,1106,1107,1116,1117,1119,1120,495],{},"The host implementation of the store is ",[1108,1109,1113],"a",{"href":1110,"rel":1111},"https:\u002F\u002Fgithub.com\u002Fskaile-ai\u002Fforge-editor\u002Fblob\u002Fmain\u002Fapp\u002Fcomposables\u002FuseDocumentStore.ts",[1112],"nofollow",[421,1114,1115],{},"app\u002Fcomposables\u002FuseDocumentStore.ts","; the ",[421,1118,452],{}," contract it implements is ",[1108,1121,1123],{"href":1122},"#documentstore","below",[497,1125,1127],{"id":1126},"props","Props",[418,1129,1130,1131,1133,1134,1136,1137,1140,1141,1143,1144,1147,1148,1150,1151,1153,1154,1157,1158,1161,1162,1165],{},"Vue props: ",[421,1132,771],{}," (required) and ",[421,1135,784],{}," (",[421,1138,1139],{},"Editor | undefined",", required). ",[421,1142,976],{}," is the folder (path, ",[421,1145,1146],{},"''"," = root) that new documents, new folders and host import actions go to; the tree resets it to ",[421,1149,1146],{}," when that folder is deleted or moved away. The slot ",[421,1152,808],{}," receives ",[421,1155,1156],{},"{ id, title }",". Further props: ",[421,1159,1160],{},"reorder"," (a ",[421,1163,1164],{},"RowReorder",", see below).",[418,1167,1168,1171,1172,1175,1176,1178,1179,461,1182,1014,1185,1188],{},[1030,1169,1170],{},"Host row reorder and Typed block slots (4.3)."," The tree moves a Typed block row by the library's own drop rules (into another Typed block, or to the end of the document). A host that orders the rows among each other (e.g. the root blocks of a virtual-root document, where a drop on a row means \"take its place\") passes ",[421,1173,1174],{},"reorder: RowReorder"," (exported from ",[421,1177,448],{}," with ",[421,1180,1181],{},"TreeItem",[421,1183,1184],{},"DragPayload",[421,1186,1187],{},"DropVerdict","):",[956,1190,1191,1209,1222],{},[959,1192,1193,1196,1197,1200,1201,1204,1205,1208],{},[421,1194,1195],{},"verdict(source, target): DropVerdict | null"," is asked first for every drag of the tree's own rows; ",[421,1198,1199],{},"{ ok: true }"," or ",[421,1202,1203],{},"{ ok: false, reason }"," decides (the row shows the reason like any refused drop), ",[421,1206,1207],{},"null"," leaves it to the library rules.",[959,1210,1211,1214,1215,1218,1219,1026],{},[421,1212,1213],{},"drop(source, target)"," runs when the host's verdict was ",[421,1216,1217],{},"ok","; the host moves the rows (the tree emits no ",[421,1220,1221],{},"moveBlock",[959,1223,1224,1227,1228,1239,1240,1243,1244,1247,1248,1251],{},[421,1225,1226],{},"step?(item, direction)"," runs on ",[1030,1229,1230,1231,1234,1235,1238],{},"Ctrl+↑ (",[421,1232,1233],{},"-1",") \u002F Ctrl+↓ (",[421,1236,1237],{},"1",")"," on a focused row and moves it one place among its siblings; it returns ",[421,1241,1242],{},"false"," when the row is not the host's (the tree does nothing else with Ctrl+arrow); after a ",[421,1245,1246],{},"true"," the keyboard focus follows the row to its new place, so the next press moves the same row on. Without ",[421,1249,1250],{},"step"," the shortcut does nothing.",[418,1253,1254,1255,1136,1258,1261,1262,1136,1265,1267,1268,1271,1272,461,1274,1014,1277,1280,1281,1288,1289,1291,1292,495],{},"Two slots decorate Typed block rows only: ",[421,1256,1257],{},"block-badges",[421,1259,1260],{},"{ item: TreeItem }","; always visible, right after the label, e.g. a warning icon) and ",[421,1263,1264],{},"block-actions",[421,1266,1260],{},"; in the controls that show on hover or focus, after the add menu, e.g. a status menu). The row element carries ",[421,1269,1270],{},"data-tree-key"," (the item key). In React the props are ",[421,1273,1160],{},[421,1275,1276],{},"blockBadges(item)",[421,1278,1279],{},"blockActions(item)",". The runnable adapter is ",[1108,1282,1285],{"href":1283,"rel":1284},"https:\u002F\u002Fgithub.com\u002Fskaile-ai\u002Fforge-editor\u002Fblob\u002Fmain\u002Fapp\u002Fcomponents\u002FAppDocumentTree.vue",[1112],[421,1286,1287],{},"app\u002Fcomponents\u002FAppDocumentTree.vue","; the layout holds ",[421,1290,697],{}," and binds it with ",[421,1293,1294],{},"v-model:selected-folder",[418,1296,1297,1014,1299,1301,1302,1304,1305,1307,1308,1310,1311,1313,1314,1316,1317,1319,1320,1322,1323,1326,1327,1329,1330,1099,1333,495],{},[421,1298,771],{},[421,1300,784],{}," are required (",[421,1303,784],{}," may be ",[421,1306,966],{},"); ",[421,1309,697],{},"\u002F",[421,1312,982],{}," are optional (the tree keeps the folder itself when ",[421,1315,697],{}," is omitted); ",[421,1318,993],{}," is a render function receiving ",[421,1321,1156],{},"; ",[421,1324,1325],{},"className"," styles the root. The runnable adapter of a ",[421,1328,452],{}," over the host's ",[421,1331,1332],{},"\u002Fapi",[1108,1334,1337],{"href":1335,"rel":1336},"https:\u002F\u002Fgithub.com\u002Fskaile-ai\u002Fforge-editor\u002Fblob\u002Fmain\u002Fexamples\u002Freact-advanced\u002Fsrc\u002Fdocument-store.ts",[1112],[421,1338,1339],{},"examples\u002Freact-advanced\u002Fsrc\u002Fdocument-store.ts",[497,1341,1343],{"id":1342},"documentstore",[421,1344,452],{},[502,1346,1349],{"className":1347,"code":1348,"language":539,"meta":507,"style":507},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","interface DocumentStore {\n  tree: ReadonlyStore\u003CTreeFolder | null | undefined>               \u002F\u002F undefined = loading, null = loading failed\n  documents: ReadonlyStore\u003Creadonly DocumentSummary[] | undefined> \u002F\u002F undefined = loading\n  activeId: ReadonlyStore\u003Cstring | undefined>\n  open(id: string): Promise\u003Cvoid>\n  create(title?: string, folder?: string): Promise\u003C{ id: string }>\n  move(id: string, folder: string): Promise\u003Cvoid>\n  remove(id: string, folder: string): Promise\u003C{ title: string, restore: () => Promise\u003C{ id: string }> }>\n  createFolder(path: string): Promise\u003Cvoid>\n  moveFolder(path: string, to: string): Promise\u003Cvoid>   \u002F\u002F `to` is the complete new path: moves and renames\n  removeFolder(path: string): Promise\u003Cvoid>\n  validateName(name: string, siblings: readonly string[]): string | null\n}\n",[421,1350,1351,1361,1390,1420,1439,1467,1507,1538,1599,1623,1658,1681,1719],{"__ignoreMap":507},[511,1352,1353,1356,1358],{"class":513,"line":514},[511,1354,1355],{"class":525},"interface",[511,1357,585],{"class":644},[511,1359,1360],{"class":517}," {\n",[511,1362,1363,1366,1368,1371,1373,1376,1378,1381,1383,1385,1387],{"class":513,"line":547},[511,1364,1365],{"class":521},"  tree",[511,1367,641],{"class":517},[511,1369,1370],{"class":644}," ReadonlyStore",[511,1372,518],{"class":517},[511,1374,1375],{"class":644},"TreeFolder",[511,1377,657],{"class":517},[511,1379,1380],{"class":644}," null",[511,1382,657],{"class":517},[511,1384,660],{"class":644},[511,1386,688],{"class":517},[511,1388,1389],{"class":828},"               \u002F\u002F undefined = loading, null = loading failed\n",[511,1391,1392,1395,1397,1399,1401,1404,1407,1410,1413,1415,1417],{"class":513,"line":575},[511,1393,1394],{"class":521},"  documents",[511,1396,641],{"class":517},[511,1398,1370],{"class":644},[511,1400,518],{"class":517},[511,1402,1403],{"class":525},"readonly",[511,1405,1406],{"class":644}," DocumentSummary",[511,1408,1409],{"class":557},"[] ",[511,1411,1412],{"class":517},"|",[511,1414,660],{"class":644},[511,1416,688],{"class":517},[511,1418,1419],{"class":828}," \u002F\u002F undefined = loading\n",[511,1421,1422,1425,1427,1429,1431,1433,1435,1437],{"class":513,"line":598},[511,1423,1424],{"class":521},"  activeId",[511,1426,641],{"class":517},[511,1428,1370],{"class":644},[511,1430,518],{"class":517},[511,1432,685],{"class":644},[511,1434,657],{"class":517},[511,1436,660],{"class":644},[511,1438,544],{"class":517},[511,1440,1441,1444,1446,1450,1452,1455,1457,1460,1462,1465],{"class":513,"line":621},[511,1442,1443],{"class":521},"  open",[511,1445,691],{"class":517},[511,1447,1449],{"class":1448},"sHdIc","id",[511,1451,641],{"class":517},[511,1453,1454],{"class":644}," string",[511,1456,1188],{"class":517},[511,1458,1459],{"class":644}," Promise",[511,1461,518],{"class":517},[511,1463,1464],{"class":644},"void",[511,1466,544],{"class":517},[511,1468,1469,1472,1474,1477,1480,1482,1484,1487,1489,1491,1493,1495,1497,1500,1502,1504],{"class":513,"line":628},[511,1470,1471],{"class":521},"  create",[511,1473,691],{"class":517},[511,1475,1476],{"class":1448},"title",[511,1478,1479],{"class":517},"?:",[511,1481,1454],{"class":644},[511,1483,647],{"class":517},[511,1485,1486],{"class":1448}," folder",[511,1488,1479],{"class":517},[511,1490,1454],{"class":644},[511,1492,1188],{"class":517},[511,1494,1459],{"class":644},[511,1496,635],{"class":517},[511,1498,1499],{"class":521}," id",[511,1501,641],{"class":517},[511,1503,1454],{"class":644},[511,1505,1506],{"class":517}," }>\n",[511,1508,1509,1512,1514,1516,1518,1520,1522,1524,1526,1528,1530,1532,1534,1536],{"class":513,"line":669},[511,1510,1511],{"class":521},"  move",[511,1513,691],{"class":517},[511,1515,1449],{"class":1448},[511,1517,641],{"class":517},[511,1519,1454],{"class":644},[511,1521,647],{"class":517},[511,1523,1486],{"class":1448},[511,1525,641],{"class":517},[511,1527,1454],{"class":644},[511,1529,1188],{"class":517},[511,1531,1459],{"class":644},[511,1533,518],{"class":517},[511,1535,1464],{"class":644},[511,1537,544],{"class":517},[511,1539,1540,1543,1545,1547,1549,1551,1553,1555,1557,1559,1561,1563,1565,1568,1570,1572,1574,1577,1579,1582,1585,1587,1589,1591,1593,1595,1597],{"class":513,"line":719},[511,1541,1542],{"class":521},"  remove",[511,1544,691],{"class":517},[511,1546,1449],{"class":1448},[511,1548,641],{"class":517},[511,1550,1454],{"class":644},[511,1552,647],{"class":517},[511,1554,1486],{"class":1448},[511,1556,641],{"class":517},[511,1558,1454],{"class":644},[511,1560,1188],{"class":517},[511,1562,1459],{"class":644},[511,1564,635],{"class":517},[511,1566,1567],{"class":521}," title",[511,1569,641],{"class":517},[511,1571,1454],{"class":644},[511,1573,647],{"class":517},[511,1575,1576],{"class":521}," restore",[511,1578,641],{"class":517},[511,1580,1581],{"class":517}," ()",[511,1583,1584],{"class":525}," =>",[511,1586,1459],{"class":644},[511,1588,635],{"class":517},[511,1590,1499],{"class":521},[511,1592,641],{"class":517},[511,1594,1454],{"class":644},[511,1596,663],{"class":517},[511,1598,1506],{"class":517},[511,1600,1601,1604,1606,1609,1611,1613,1615,1617,1619,1621],{"class":513,"line":729},[511,1602,1603],{"class":521},"  createFolder",[511,1605,691],{"class":517},[511,1607,1608],{"class":1448},"path",[511,1610,641],{"class":517},[511,1612,1454],{"class":644},[511,1614,1188],{"class":517},[511,1616,1459],{"class":644},[511,1618,518],{"class":517},[511,1620,1464],{"class":644},[511,1622,544],{"class":517},[511,1624,1625,1628,1630,1632,1634,1636,1638,1641,1643,1645,1647,1649,1651,1653,1655],{"class":513,"line":734},[511,1626,1627],{"class":521},"  moveFolder",[511,1629,691],{"class":517},[511,1631,1608],{"class":1448},[511,1633,641],{"class":517},[511,1635,1454],{"class":644},[511,1637,647],{"class":517},[511,1639,1640],{"class":1448}," to",[511,1642,641],{"class":517},[511,1644,1454],{"class":644},[511,1646,1188],{"class":517},[511,1648,1459],{"class":644},[511,1650,518],{"class":517},[511,1652,1464],{"class":644},[511,1654,688],{"class":517},[511,1656,1657],{"class":828},"   \u002F\u002F `to` is the complete new path: moves and renames\n",[511,1659,1660,1663,1665,1667,1669,1671,1673,1675,1677,1679],{"class":513,"line":744},[511,1661,1662],{"class":521},"  removeFolder",[511,1664,691],{"class":517},[511,1666,1608],{"class":1448},[511,1668,641],{"class":517},[511,1670,1454],{"class":644},[511,1672,1188],{"class":517},[511,1674,1459],{"class":644},[511,1676,518],{"class":517},[511,1678,1464],{"class":644},[511,1680,544],{"class":517},[511,1682,1683,1686,1688,1691,1693,1695,1697,1700,1702,1705,1707,1710,1712,1714,1716],{"class":513,"line":797},[511,1684,1685],{"class":521},"  validateName",[511,1687,691],{"class":517},[511,1689,1690],{"class":1448},"name",[511,1692,641],{"class":517},[511,1694,1454],{"class":644},[511,1696,647],{"class":517},[511,1698,1699],{"class":1448}," siblings",[511,1701,641],{"class":517},[511,1703,1704],{"class":525}," readonly",[511,1706,1454],{"class":644},[511,1708,1709],{"class":557},"[]",[511,1711,1188],{"class":517},[511,1713,1454],{"class":644},[511,1715,657],{"class":517},[511,1717,1718],{"class":644}," null\n",[511,1720,1721],{"class":513,"line":838},[511,1722,954],{"class":517},[418,1724,1725,1726,1729,1730,1003,1733,1736,1737,1014,1740,1003,1743,1746,1747,495],{},"The observable values are ",[421,1727,1728],{},"ReadonlyStore","s (",[421,1731,1732],{},"createStore",[421,1734,1735],{},"@skaile\u002Fforge-editor\u002Fcore","), so the same store drives both components. A Vue host that keeps refs adapts them with ",[421,1738,1739],{},"storeFromRef(ref)",[421,1741,1742],{},"useStoreRef(storeOrGetter)",[421,1744,1745],{},"@skaile\u002Fforge-editor\u002Fvue","; a React host holds its data in its own state or data layer and publishes it with ",[421,1748,1732],{},[418,1750,1751,1099,1753,1756,1757,1099,1759,1761,1762,1099,1765,1768,1769,1772,1773,1776,1777,1014,1779,1782,1783,1786,1787,1790,1791,1794,1795,1797,1798,1801],{},[421,1752,1375],{},[421,1754,1755],{},"{ type: 'folder', name, path, folders, documents }"," (the root is named „Dokumente“, ",[421,1758,1608],{},[421,1760,1146],{},"), ",[421,1763,1764],{},"DocumentSummary",[421,1766,1767],{},"{ id, title, updatedAt, folder }","; both are exported from ",[421,1770,1771],{},"\u002Ftree",". The editor serves them: ",[421,1774,1775],{},"GET \u002Fapi\u002Feditor\u002Ftree"," answers a ",[421,1778,1375],{},[421,1780,1781],{},"GET \u002Fapi\u002Feditor\u002Fdocuments"," the ",[421,1784,1785],{},"DocumentSummary[]"," (from the documents port's ",[421,1788,1789],{},"DocumentAdmin.listTree","). A host adapter that builds the tree imports the types from ",[421,1792,1793],{},"@skaile\u002Fforge-editor\u002Ftree\u002Ftypes"," (the ",[421,1796,1771],{}," entry also carries the store and the view model, which a server's typecheck should not pull in); this repository's ",[421,1799,1800],{},"server\u002Futils\u002Fdocuments.ts"," does.",[418,1803,1804],{},"Semantics every implementation must keep:",[956,1806,1807,1820,1833,1847,1855,1879,1896,1917],{},[959,1808,1809,1812,1813,1816,1817,1819],{},[1030,1810,1811],{},"Failure ownership."," The host reports failures (its toast, its wording) and then ",[426,1814,1815],{},"rejects",". ",[421,1818,423],{}," shows no second message; it only stops the flow it was running (for example it keeps the folder-name input open or does not expand a folder).",[959,1821,1822,1825,1826,1014,1829,1832],{},[1030,1823,1824],{},"Refresh."," Every mutation resolves only after both ",[421,1827,1828],{},"tree",[421,1830,1831],{},"documents"," show its result, so the tree can select and expand the new item right away.",[959,1834,1835,1838,1839,1099,1841,1843,1844,1846],{},[1030,1836,1837],{},"Loading."," While ",[421,1840,1828],{},[421,1842,966],{}," the tree shows „Lädt…“, for ",[421,1845,1207],{}," „Dokumente konnten nicht geladen werden“; the virtual root (below) is not affected by either.",[959,1848,1849,1854],{},[1030,1850,1851],{},[421,1852,1853],{},"create"," returns the new document's ID. Omitted title and folder mean the host's default title and the root.",[959,1856,1857,1863,1864,1867,1868,1871,1872,1875,1876,1878],{},[1030,1858,1859,1862],{},[421,1860,1861],{},"remove"," with undo."," The tree shows the notification „Dokument gelöscht“ (description: the title) with a „Rückgängig“ action built from the resolved value. The implementation must (1) leave the document first when it is the open one and wait for its page to be torn down, so the collaboration provider is destroyed before the server forgets the document, (2) snapshot title and content, (3) delete, and (4) return ",[421,1865,1866],{},"restore",", which creates a ",[1030,1869,1870],{},"new"," document (new ID) with the old title in ",[421,1873,1874],{},"folder"," and writes the JSON content back. Assets, IDs and collaboration history are not restored; the tree opens the restored document by its new ID. If ",[421,1877,1866],{}," rejects, the host has already reported it.",[959,1880,1881,1884,1885,1887,1888,1891,1892,1895],{},[1030,1882,1883],{},"Folders."," Paths are ",[421,1886,1310],{},"-joined segments. ",[421,1889,1890],{},"removeFolder"," must reject for a non-empty folder (the host's server says „Ordner ist nicht leer“); the tree itself refuses to drop a folder onto itself or a descendant and never calls ",[421,1893,1894],{},"moveFolder"," for it. A selected folder that no longer exists falls back to the root.",[959,1897,1898,1903,1904,1322,1906,1909,1910,1003,1913,1916],{},[1030,1899,1900],{},[421,1901,1902],{},"validateName(name, siblings)"," returns a German error message or ",[421,1905,1207],{},[421,1907,1908],{},"siblings"," are the names already taken in the target folder. The tree shows the message inline under the input (on create and rename) and only submits valid names. The host passes its server's rules (here ",[421,1911,1912],{},"fileNameError",[421,1914,1915],{},"shared\u002Futils\u002Ffile-name.ts",") so client and server never disagree.",[959,1918,1919,1924,1925,1928],{},[1030,1920,1921],{},[421,1922,1923],{},"open(id)"," navigates the host to the document; ",[421,1926,1927],{},"activeId"," follows the host's route.",[497,1930,1932],{"id":1931},"filters-virtual-root-outline","Filters, virtual root, outline",[956,1934,1935,1996,2010,2035,2073],{},[959,1936,1937,1938,1941,1942,1945,1946,1949,1950,461,1952,461,1954,461,1956,461,1959,461,1962,461,1965,461,1968,1761,1971,1973,1974,1977,1978,1136,1980,1982,1983,461,1985,1014,1987,1989,1990,1992,1993,1026],{},"Filters hide Block types below a tree item (per item key); they persist in ",[421,1939,1940],{},"localStorage"," under ",[421,1943,1944],{},"tip-tree:node-filters",". The view state is a framework-free model: ",[421,1947,1948],{},"createTreeView()"," builds one (",[421,1951,1010],{},[421,1953,1013],{},[421,1955,1017],{},[421,1957,1958],{},"expand",[421,1960,1961],{},"setKindHidden",[421,1963,1964],{},"clearFilter",[421,1966,1967],{},"clearAllFilters",[421,1969,1970],{},"followFolderMove",[421,1972,464],{}," returns the one of an ",[421,1975,1976],{},"EditorContext","'s app, and ",[421,1979,471],{},[421,1981,1006],{},": ",[421,1984,1010],{},[421,1986,1013],{},[421,1988,1017],{}," as refs; ",[421,1991,1021],{},": as plain values that re-render the component) reads it, so several trees of one app share it (see ",[1108,1994,1995],{"href":318},"§11",[959,1997,1998,1999,2002,2003,2005,2006,2009],{},"Outline rows are open unless their key (",[421,2000,2001],{},"outline:\u003Cblock id>",") is in ",[421,2004,1017],{},"; it lasts for the session, so a tree mounted again shows the rows as they were left. A host sets it to start with rows closed, e.g. ",[421,2007,2008],{},"treeView(context).collapsedOutline.set(ids.map(id => `outline:${id}`))"," for the top Typed blocks.",[959,2011,2012,2013,2016,2017,2019,2020,1761,2023,1014,2026,2029,2030,1136,2032,1026],{},"The basket filter belongs to the same view state: ",[421,2014,2015],{},"basketFilter"," (the basket ids to show, ",[421,2018,1709],{}," = all; persists under ",[421,2021,2022],{},"tip-tree:basket-filter",[421,2024,2025],{},"setBasketShown(id, shown)",[421,2027,2028],{},"clearBasketFilter()"," on ",[421,2031,467],{},[1108,2033,1123],{"href":2034},"#baskets-focus-and-foreign-drops",[959,2036,2037,2038,1099,2040,2043,2044,2047,2048,2050,2051,2054,2055,2057,2058,2061,2062,2065,2066,2069,2070,2072],{},"The settings value ",[421,2039,1094],{},[421,2041,2042],{},"'folders'"," (default: folders with their documents, the open document is an entry among them) or ",[421,2045,2046],{},"'file'",". With ",[421,2049,2046],{},", an open document in Structure mode (",[421,2052,2053],{},"blocks.structure === 'structured'",") is the ",[426,2056,1075],{}," of the tree: its Typed blocks are the nested items and dragging one moves the block (",[421,2059,2060],{},"moveBlockInto",") unless the target Typed block's Block type does not accept it (",[421,2063,2064],{},"acceptsChild",", from the Block type's ",[421,2067,2068],{},"accepts","): the drop is then refused, outlined red with the reason of ",[421,2071,1050],{}," under the row. With no open document or in Flat mode the tree shows the folders instead. Switching it in the settings panel re-roots the tree immediately.",[959,2074,2075,2076,1136,2078,1018,2081,2084,2085,1003,2088,2090,2091,1816,2094,2096,2097,2099],{},"The Block outline of the open document is read from ",[421,2077,784],{},[421,2079,2080],{},"documentOutline",[421,2082,2083],{},"\u002Fcomposites","): its headings and Typed blocks as one tree in document order, the same in Flat and Structure mode. A heading nests under the nearest shallower heading before it, a Typed block under the heading it follows, and the headings in a Typed block's body start their own levels below it, so converting headings into Typed blocks („Überschriften in Blöcke“) or back keeps every entry. The Block type filter of the open document and of its rows lists the Block types plus „Überschriften“ (",[421,2086,2087],{},"HEADING_KIND",[421,2089,1771],{},"); hiding the headings lifts the entries below them. The basket filter hides a heading unless a shown Typed block is below it, which then keeps it dimmed. The outline is refreshed, debounced, on ",[421,2092,2093],{},"update",[421,2095,423],{}," re-subscribes when the ",[421,2098,784],{}," prop changes and removes its listener when the editor changes or the tree unmounts, so switching documents never leaves a stale listener on the previous editor.",[497,2101,2103],{"id":2102},"baskets-focus-and-foreign-drops","Baskets, focus and foreign drops",[418,2105,2106,2107,2109],{},"Three additions for a tree of Typed blocks; the editor side of each is in ",[1108,2108,155],{"href":156},". The German labels of the basket controls still use a legacy word, so this section quotes their English catalog labels (English label).",[956,2111,2112,2139,2171,2199],{},[959,2113,2114,2117,2118,2121,2122,2125,2126,2129,2130,2133,2134,1941,2136,2138],{},[1030,2115,2116],{},"Basket dot and filter."," A Typed block row shows the dot of its basket (",[421,2119,2120],{},"meta.basket","; the baskets are the ones the open editor offers, ",[421,2123,2124],{},"compositeFeature({ baskets })","). With a live editor that offers baskets, a paint-bucket button (“Filter by basket”) next to the title filter opens a menu of baskets to check; the tree then shows only the Typed blocks of the checked baskets (none checked = all) plus their ancestors, which are dimmed (",[421,2127,2128],{},"TreeRowState.dimmed","). Documents, folders and headings are never hidden; “Reset basket filter” clears it. The choice is ",[421,2131,2132],{},"TreeViewModel.basketFilter",", shared by the trees of one app and stored in ",[421,2135,1940],{},[421,2137,2022],{},"; an id the open editor does not offer never filters.",[959,2140,2141,2144,2145,461,2148,2151,2152,2155,2156,2159,2160,2163,2164,2166,2167,2170],{},[1030,2142,2143],{},"Focus."," Every Typed block row has a focus button („Fokussieren“). A focused row lists only its children, under a breadcrumb whose first crumb is the document („Fokus aufheben“ leaves the focus) and whose other crumbs refocus that level; the Block type filters of the rows on the way down still apply to the children (",[421,2146,2147],{},"TreeState.focusKey",[421,2149,2150],{},"focusPath","). The tree and the editor stay in step: focusing a row sends the ",[421,2153,2154],{},"TreeCommand"," ",[421,2157,2158],{},"{ type: 'focusBlock', id }",", whose executor runs ",[421,2161,2162],{},"focusComposite(id)"," on the ",[421,2165,784],{}," prop (",[421,2168,2169],{},"id: null"," leaves it), and a focus started in the editor (block action „Fokussieren“, the focus chip) is mirrored into the tree. A focused Typed block that disappears, or another editor, resets the focus. Only a Typed block with a block id can be focused.",[959,2172,2173,2176,2177,2155,2179,2159,2182,2163,2185,2166,2187,1307,2191,2194,2195,2198],{},[1030,2174,2175],{},"Merge."," A Typed block row whose Typed block the open editor can merge with the Typed block after it shows a merge button („Mit nächstem zusammenführen“, next to the focus button). It sends the ",[421,2178,2154],{},[421,2180,2181],{},"{ type: 'mergeBlock', id }",[421,2183,2184],{},"mergeCompositeWithNext",[421,2186,784],{},[1108,2188,2190],{"href":2189},"\u002Fguides\u002Fcontent-tree#merging-with-the-next-typed-block","Merging with the next Typed block",[421,2192,2193],{},"TreeModel.canMerge(item)"," says whether to show it, ",[421,2196,2197],{},"merge(item)"," sends it.",[959,2200,2201,2204,2205,461,2208,2212,2213,2215,2216,2219,2220,2155,2222,2225,2226,2155,2228,2231,2232,2235,2236,1136,2239,2242,2243,2246,2247,2250,2251,2255],{},[1030,2202,2203],{},"Foreign drops."," A Typed block dragged in from another editor surface or tree (",[421,2206,2207],{},"BLOCK_MIME",[1108,2209,2211],{"href":2210},"\u002Fguides\u002Fcontent-tree#the-payload","payload",") is accepted in the ",[426,2214,1075],{}," mode (",[421,2217,2218],{},"blocks.treeRoot = 'file'",") on the document root and on a Typed block row that has a block id (",[421,2221,1184],{},[421,2223,2224],{},"{ type: 'foreign-block' }","); any other row, or the folder mode, refuses it with „Hier lassen sich keine Blöcke ablegen.“. The model emits ",[421,2227,2154],{},[421,2229,2230],{},"{ type: 'transferBlock', payload, targetId, menu, x, y }",", and the executor runs ",[421,2233,2234],{},"editor.commands.transferBlock"," at the ",[1030,2237,2238],{},"end of the target Typed block",[421,2240,2241],{},"targetId: null"," = the end of the document); with Shift held at the drop (",[421,2244,2245],{},"menu: true",") the transform menu opens at the pointer first. This needs ",[421,2248,2249],{},"transferFeature"," in the open editor's preset (",[1108,2252,2254],{"href":2253},"\u002Fguides\u002Fcontent-tree#block-transfer","Block transfer","); without it the tree refuses every foreign drop („Dieser Editor nimmt keine Blöcke von außen an.“). The rows write their own payload when dragged, so a tree row can be dropped on another surface or tree.",[418,2257,2258,2259,2261,2262,461,2265,1014,2268,2271,2272,2274,2275,2277,2278,1200,2280,2282],{},"The ",[421,2260,2154],{}," union gained ",[421,2263,2264],{},"transferBlock",[421,2266,2267],{},"focusBlock",[421,2269,2270],{},"mergeBlock",", and ",[421,2273,1184],{}," gained ",[421,2276,2224],{},"; the components run these commands through the open editor, so a host does not handle them. A host that switches exhaustively over ",[421,2279,2154],{},[421,2281,1184],{}," needs the new cases.",[497,2284,2286],{"id":2285},"not-in-the-library","Not in the library",[418,2288,2289,2290,2293,2294,1136,2296,1026],{},"Storage, routing, authentication, toasts, and the host's own file formats. In this repository the ",[421,2291,2292],{},".cdoc"," (package) export stays in the host: the document row's export link comes in through ",[421,2295,808],{},[421,2297,1287],{},[2299,2300,2301],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":507,"searchDepth":547,"depth":547,"links":2303},[2304,2305,2306,2307,2308,2309],{"id":499,"depth":547,"text":500},{"id":1126,"depth":547,"text":1127},{"id":1342,"depth":547,"text":452},{"id":1931,"depth":547,"text":1932},{"id":2102,"depth":547,"text":2103},{"id":2285,"depth":547,"text":2286},"Show folders, documents and the open document's outline in a file drawer over your own DocumentStore, on Vue and React.","md",null,{},{"icon":120},{"title":117,"description":2310},"brPDlwJg-PqDd8I7fdoZb5NRcbRt5MaicAtqJ4cRdpg",[2318,2320],{"title":113,"path":114,"stem":115,"description":2319,"icon":78,"children":-1},"Add AI to an editor: selection menu, sidebar, ghost text, backend endpoints, integrations with Typed blocks and transcripts, agent operations and fence formats.",{"title":122,"path":123,"stem":124,"description":2321,"icon":125,"children":-1},"Edit Markdown files as blocks with frontmatter and unknown fences preserved, edit source files in CodeMirror, and pick a surface per file type.",1793213902194]