[{"data":1,"prerenderedAt":4181},["ShallowReactive",2],{"navigation_docs":3,"-guides-composition":412,"-guides-composition-surround":4176},[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":90,"body":414,"description":4169,"extension":4170,"links":4171,"meta":4172,"navigation":4173,"path":91,"seo":4174,"stem":92,"__hash__":4175},"docs\u002F3.guides\u002F1.composition.md",{"type":415,"value":416,"toc":4160},"minimark",[417,422,489,641,665,704,769,819,875,999,1005,1009,1038,1155,1203,1303,1329,1333,1880,1927,2062,2066,2069,2224,2291,2579,2583,2586,2747,3096,3100,3315,3322,3326,3450,3490,3909,4036,4097,4156],[418,419,421],"h2",{"id":420},"dimensions-and-integration-depth","Dimensions and integration depth",[423,424,425,426,430,431,435,436,439,440,443,444,447,448,451,452,455,456,459,460,459,463,459,466,459,469,459,472,459,475,459,478,459,481,484,485,488],"p",{},"An editor is composed from three ",[427,428,429],"strong",{},"dimensions"," of features (",[432,433,434],"code",{},"composeEditor"," of ",[432,437,438],{},"@skaile\u002Fforge-editor\u002Fvue\u002Fadvanced"," or ",[432,441,442],{},"\u002Freact\u002Fadvanced","; framework-free core in ",[432,445,446],{},"@skaile\u002Fforge-editor\u002Fpresets\u002Fcompose","). The ",[427,449,450],{},"backend"," (",[432,453,454],{},"EditorBackend","; capabilities ",[432,457,458],{},"documents",", ",[432,461,462],{},"collaboration",[432,464,465],{},"assets",[432,467,468],{},"users",[432,470,471],{},"tags",[432,473,474],{},"settings",[432,476,477],{},"themes",[432,479,480],{},"ai",[432,482,483],{},"comments",") is a separate decision: it only prunes features whose capability it lacks, reported in ",[432,486,487],{},"omitted",".",[490,491,492,508],"table",{},[493,494,495],"thead",{},[496,497,498,502,505],"tr",{},[499,500,501],"th",{},"Dimension",[499,503,504],{},"Levels (or a list of feature ids)",[499,506,507],{},"Features",[509,510,511,561,601],"tbody",{},[496,512,513,520,556],{},[514,515,516,519],"td",{},[432,517,518],{},"content",": nodes, marks, structure; the server schema derives from it",[514,521,522,525,526,529,530,525,533,459,535,459,538,459,541,459,544,459,547,459,550,459,553],{},[432,523,524],{},"'basic'"," = ",[432,527,528],{},"core-content","; ",[432,531,532],{},"'rich'",[432,534,528],{},[432,536,537],{},"media",[432,539,540],{},"transcript",[432,542,543],{},"composites",[432,545,546],{},"mentions",[432,548,549],{},"authors",[432,551,552],{},"tables",[432,554,555],{},"composite-media",[514,557,558,560],{},[432,559,528],{}," is always installed",[496,562,563,569,596],{},[514,564,565,568],{},[432,566,567],{},"interaction",": editing UX without schema",[514,570,571,525,574,529,577,525,580,459,582,459,585,459,587,459,590,529,592,595],{},[432,572,573],{},"'minimal'",[432,575,576],{},"core-ui",[432,578,579],{},"'full'",[432,581,576],{},[432,583,584],{},"collab",[432,586,483],{},[432,588,589],{},"block-ui",[432,591,477],{},[432,593,594],{},"[]"," = none (a viewer)",[514,597,598,600],{},[432,599,576],{}," is prepended to every non-empty list",[496,602,603,607,632],{},[514,604,605],{},[432,606,480],{},[514,608,609,612,613,525,616,618,619,622,623,625,626,622,629,631],{},[432,610,611],{},"false",", or ",[432,614,615],{},"true",[432,617,480],{}," plus ",[432,620,621],{},"composite-ai"," (iff ",[432,624,543],{},") and ",[432,627,628],{},"transcript-ai",[432,630,540],{},")",[514,633,634,635,638,639],{},"an integration whose ",[432,636,637],{},"requires"," are missing lands in ",[432,640,487],{},[423,642,643,646,647,650,651,653,654,657,658,661,662,488],{},[432,644,645],{},"add"," appends host features after the catalog; ",[432,648,649],{},"remove"," drops catalog features and everything that ",[432,652,637],{}," them. Examples: viewer ",[432,655,656],{},"composeEditor({ content: 'rich', interaction: [], ai: false })","; advanced without AI ",[432,659,660],{},"composeEditor({ content: 'rich', interaction: 'full', ai: false })","; simple plus tables ",[432,663,664],{},"composeEditor({ content: ['tables'], interaction: 'minimal', ai: false })",[423,666,667,670,671,674,675,678,679,682,683,451,686,690,691,694,695,698,699,703],{},[432,668,669],{},"transfer"," (drag a Typed block from one editor or tree into another, with the copy transform and, with ",[432,672,673],{},"ai: true",", the summary and bullet transforms of ",[432,676,677],{},"integrations\u002Fcomposite-ai",") is in no level list, so a host opts in with ",[432,680,681],{},"interaction: [...INTERACTION.full, 'transfer']"," and configures it with ",[432,684,685],{},"transfer: { transforms?, defaultTransform? }",[687,688,155],"a",{"href":689},"\u002Fguides\u002Fcontent-tree#block-transfer","). ",[432,692,693],{},"compositeBaskets"," replaces the built-in colour Baskets like ",[432,696,697],{},"blockTypes"," replaces the Block types (",[687,700,702],{"href":701},"\u002Fguides\u002Fcontent-tree#baskets","Baskets",").",[423,705,706,708,709,712,713,716,717,720,721,459,724,727,728,459,731,459,734,737,738,612,741,744,745,748,749,752,753,756,757,760,761,764,765,768],{},[432,707,528],{}," is configurable: ",[432,710,711],{},"composeEditor({ content: 'basic', interaction: 'minimal', ai: false, coreContent: { headings: { levels: [1, 2] }, rule: false } })"," (or ",[432,714,715],{},"coreContentFeature(options)"," in a hand-built preset) drops optional StarterKit structures from the editor. Options (",[432,718,719],{},"CoreContentOptions","): ",[432,722,723],{},"headings: false | { levels }",[432,725,726],{},"lists: false"," (bullet and ordered lists with their items), ",[432,729,730],{},"blockquote: false",[432,732,733],{},"codeBlock: false",[432,735,736],{},"rule: false","; everything is on by default. A dropped structure is not in the schema, so the same options object also shapes the server schema (",[432,739,740],{},"schemaExtensionsOf(preset.features)",[432,742,743],{},"coreContentSchemaExtensions(options)"," from ",[432,746,747],{},"@skaile\u002Fforge-editor\u002Fcore\u002Fschema"," for a schema built by hand); pass the same options on both sides. Commands that create a dropped node (toolbar button, ",[432,750,751],{},"\u002F"," entry, ⌘K row) are hidden, not greyed out, and its shortcuts do not exist. A heading level outside ",[432,754,755],{},"levels"," hides just that level's command. Abilities are untouched: they stay a permission, not a schema switch. Content that holds a dropped structure (Markdown with ",[432,758,759],{},"# Titel"," and ",[432,762,763],{},"headings: false",", stored JSON with a ",[432,766,767],{},"heading"," node) has no node type to land in: do not pass such documents to an editor with that option.",[423,770,771,773,774,712,777,780,781,451,784,787,788,791,792,795,796,799,800,803,804,807,808,810,811,814,815,818],{},[432,772,589],{}," chooses where block actions appear: ",[432,775,776],{},"composeEditor({ content: 'rich', interaction: 'full', ai: false, blockUi: { actions: 'both' } })",[432,778,779],{},"blockUiFeature({ actions })"," in a hand-built preset). ",[432,782,783],{},"actions",[432,785,786],{},"BlockUiOptions",") is ",[432,789,790],{},"'hover'"," (default: the bar of the block under the pointer), ",[432,793,794],{},"'toolbar'"," (the bar of the block holding the selection, shown at its corner as soon as the caret is in it), ",[432,797,798],{},"'both'"," (one bar per block: where the pointer hovers the selected block only the toolbar shows) or ",[432,801,802],{},"'none'"," (drag handle and block targeting only, no action bar). Each action can narrow itself with ",[432,805,806],{},"placement: 'hover' | 'toolbar' | 'both'"," (default ",[432,809,798],{},"). Abilities are not touched: an ability stays a permission (\"can\"), ",[432,812,813],{},"blockUi.actions"," only chooses where the allowed actions are offered. The toolbar bar carries the class ",[432,816,817],{},"block-actions-toolbar"," for host styling.",[423,820,821,822,825,826,829,830,833,834,836,837,840,841,844,845,848,849,459,852,760,855,857,858,860,861,864,865,867,868,871,872,874],{},"The catalog behind these dimensions is one table of rows in ",[432,823,824],{},"presets\u002Fcatalog",": one row per feature with its dimension, the level lists that hold it and whether a UI layer binds it (",[432,827,828],{},"ui","), and ",[432,831,832],{},"presets\u002Fcompose"," adds how each is built from the ",[432,835,434],{}," options. Its key order is the canonical order (content, interaction, AI), which the composed feature list and the toolbar order follow (the schema order is the explicit ",[432,838,839],{},"SCHEMA_IDS"," list of ",[432,842,843],{},"presets\u002Fschema-ids",", a leaf the server's ",[432,846,847],{},"schema\u002Fadvanced"," reads without the catalog); ",[432,850,851],{},"CONTENT",[432,853,854],{},"INTERACTION",[432,856,113],{}," are derived from it. Adding a catalog feature takes one row there, plus its id in ",[432,859,843],{}," when it has a schema; every entry that holds a part of it is keyed by the rows or that list, so a missing part (the builder, a ",[432,862,863],{},"schema"," source, the Vue, React or test binding of a ",[432,866,828],{}," row) fails to compile, and ",[432,869,870],{},"UiBindings\u003CC>"," follows the ",[432,873,828],{}," rows. A framework-free feature without schema needs nothing else.",[490,876,877,893],{},[493,878,879],{},[496,880,881,884,887,890],{},[499,882,883],{},"Integration depth",[499,885,886],{},"What the host runs",[499,888,889],{},"Client call",[499,891,892],{},"Server",[509,894,895,930,980],{},[496,896,897,902,905,927],{},[514,898,899],{},[427,900,901],{},"No data layer",[514,903,904],{},"nothing; the browser keeps the data",[514,906,907,451,910,913,914,917,918,459,920,459,922,459,924,926],{},[432,908,909],{},"app.use(createEditor({ backend: createMemoryBackend({ documents, users, tags }) }))",[432,911,912],{},"@skaile\u002Fforge-editor\u002Fadapters\u002Fmemory","), then ",[432,915,916],{},"composeEditor({ content: 'rich', interaction: 'full', ai: true, backend })",": ",[432,919,584],{},[432,921,483],{},[432,923,477],{},[432,925,480],{}," and its integrations are omitted automatically",[514,928,929],{},"none",[496,931,932,937,944,964],{},[514,933,934],{},[427,935,936],{},"Nuxt, built-in",[514,938,939,940,943],{},"the Nuxt module with an ",[432,941,942],{},"EditorPorts"," bundle",[514,945,946,947,950,951,954,955,529,958,439,961],{},"module ",[432,948,949],{},"@skaile\u002Fforge-editor\u002Fnuxt"," (config key ",[432,952,953],{},"tiptapEditor","); the plugin installs ",[432,956,957],{},"createHttpBackend({ probe: true })",[432,959,960],{},"\u003CAdvancedEditor \u002F>",[432,962,963],{},"composeEditor({ …, backend })",[514,965,966,712,969,459,972,975,976],{},[432,967,968],{},"provideEditorServer({ ports: createJsonAdapter({ … }) })",[432,970,971],{},"createDrizzleAdapter",[432,973,974],{},"createForgeAdapter",") in a Nitro plugin, ",[687,977,978],{"href":196},[432,979,942],{},[496,981,982,987,990,993],{},[514,983,984],{},[427,985,986],{},"Nuxt, external data layer",[514,988,989],{},"the Nuxt module with ports of your own over the external system",[514,991,992],{},"same as the previous row",[514,994,995,998],{},[432,996,997],{},"provideEditorServer({ ports: { identity, documents, assets, … } })",": implement only the ports the external system serves; the probe omits the rest on the client",[423,1000,1001,1002,1004],{},"No Directus (or other CMS) adapter ships with the package: the seam for an external data layer is ",[432,1003,942],{},", unchanged.",[418,1006,1008],{"id":1007},"bundles","Bundles",[423,1010,1011,1012,1015,1016,1019,1020,439,1022,529,1024,1027,1028,1030,1031,1034,1035,1037],{},"A ",[427,1013,1014],{},"bundle"," is a named choice of the three dimensions, for hosts that want a starting point instead of picking levels. ",[432,1017,1018],{},"composeBundle(bundle, options?)"," (from ",[432,1021,438],{},[432,1023,442],{},[432,1025,1026],{},"composeBundle(ui, bundle, options?)"," in the framework-free ",[432,1029,446],{},") expands the bundle from ",[432,1032,1033],{},"BUNDLES"," and calls ",[432,1036,434],{},":",[490,1039,1040,1059],{},[493,1041,1042],{},[496,1043,1044,1047,1051,1055],{},[499,1045,1046],{},"Bundle",[499,1048,1049],{},[432,1050,518],{},[499,1052,1053],{},[432,1054,567],{},[499,1056,1057],{},[432,1058,480],{},[509,1060,1061,1080,1105,1136],{},[496,1062,1063,1068,1072,1076],{},[514,1064,1065],{},[432,1066,1067],{},"simple",[514,1069,1070],{},[432,1071,524],{},[514,1073,1074],{},[432,1075,573],{},[514,1077,1078],{},[432,1079,611],{},[496,1081,1082,1087,1095,1101],{},[514,1083,1084],{},[432,1085,1086],{},"writing",[514,1088,1089,459,1091,459,1093],{},[432,1090,537],{},[432,1092,552],{},[432,1094,546],{},[514,1096,1097,459,1099],{},[432,1098,589],{},[432,1100,477],{},[514,1102,1103],{},[432,1104,615],{},[496,1106,1107,1112,1122,1132],{},[514,1108,1109],{},[432,1110,1111],{},"collaborative",[514,1113,1114,459,1116,459,1118,459,1120],{},[432,1115,537],{},[432,1117,552],{},[432,1119,546],{},[432,1121,549],{},[514,1123,1124,459,1126,459,1128,459,1130],{},[432,1125,584],{},[432,1127,483],{},[432,1129,589],{},[432,1131,477],{},[514,1133,1134],{},[432,1135,611],{},[496,1137,1138,1143,1147,1151],{},[514,1139,1140],{},[432,1141,1142],{},"advanced",[514,1144,1145],{},[432,1146,532],{},[514,1148,1149],{},[432,1150,579],{},[514,1152,1153],{},[432,1154,615],{},[423,1156,1157,1160,1161,1163,1164,459,1166,459,1168,459,1170,459,1172,1175,1176,1179,1180,1182,1183,1186,1187,1189,1190,1193,1194,1196,1197,1199,1200,1202],{},[432,1158,1159],{},"BundleOptions"," are the options of ",[432,1162,434],{}," without the three dimensions (",[432,1165,450],{},[432,1167,645],{},[432,1169,649],{},[432,1171,697],{},[432,1173,1174],{},"blockUi",", …), plus two adjustments by catalog id: ",[432,1177,1178],{},"with"," adds features on top of the bundle (each lands in the dimension its catalog row names; an AI feature switches ",[432,1181,480],{}," on) and ",[432,1184,1185],{},"without"," takes features out, exactly like ",[432,1188,649],{}," (features that ",[432,1191,1192],{},"require"," them land in ",[432,1195,487],{},"). A ",[432,1198,1178],{}," id outside the catalog throws. Any other combination is a ",[432,1201,434],{}," call with explicit id lists.",[1204,1205,1210],"pre",{"className":1206,"code":1207,"language":1208,"meta":1209,"style":1209},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F The writing bundle plus the transfer menu, without tables.\nconst preset = composeBundle('writing', { with: ['transfer'], without: ['tables'], backend })\n","ts","",[432,1211,1212,1221],{"__ignoreMap":1209},[1213,1214,1217],"span",{"class":1215,"line":1216},"line",1,[1213,1218,1220],{"class":1219},"sHwdD","\u002F\u002F The writing bundle plus the transfer menu, without tables.\n",[1213,1222,1224,1228,1232,1236,1240,1243,1246,1249,1251,1254,1257,1261,1263,1266,1268,1270,1272,1275,1277,1280,1282,1284,1286,1288,1290,1292,1294,1297,1300],{"class":1215,"line":1223},2,[1213,1225,1227],{"class":1226},"spNyl","const",[1213,1229,1231],{"class":1230},"sTEyZ"," preset ",[1213,1233,1235],{"class":1234},"sMK4o","=",[1213,1237,1239],{"class":1238},"s2Zo4"," composeBundle",[1213,1241,1242],{"class":1230},"(",[1213,1244,1245],{"class":1234},"'",[1213,1247,1086],{"class":1248},"sfazB",[1213,1250,1245],{"class":1234},[1213,1252,1253],{"class":1234},",",[1213,1255,1256],{"class":1234}," {",[1213,1258,1260],{"class":1259},"swJcz"," with",[1213,1262,1037],{"class":1234},[1213,1264,1265],{"class":1230}," [",[1213,1267,1245],{"class":1234},[1213,1269,669],{"class":1248},[1213,1271,1245],{"class":1234},[1213,1273,1274],{"class":1230},"]",[1213,1276,1253],{"class":1234},[1213,1278,1279],{"class":1259}," without",[1213,1281,1037],{"class":1234},[1213,1283,1265],{"class":1230},[1213,1285,1245],{"class":1234},[1213,1287,552],{"class":1248},[1213,1289,1245],{"class":1234},[1213,1291,1274],{"class":1230},[1213,1293,1253],{"class":1234},[1213,1295,1296],{"class":1230}," backend ",[1213,1298,1299],{"class":1234},"}",[1213,1301,1302],{"class":1230},")\n",[423,1304,1305,1308,1309,760,1312,1308,1315,1318,1319,1322,1323,1325,1326,1328],{},[432,1306,1307],{},"simpleEditor()"," is ",[432,1310,1311],{},"composeBundle(…, 'simple')",[432,1313,1314],{},"advancedEditor(options)",[432,1316,1317],{},"composeBundle('advanced', options)","; the ",[432,1320,1321],{},"advancedEditor"," options are ",[432,1324,1159],{},", so the three dimensions are no longer overridable there (use ",[432,1327,434],{}," or a bundle for that).",[418,1330,1332],{"id":1331},"entries","Entries",[490,1334,1335,1348],{},[493,1336,1337],{},[496,1338,1339,1342,1345],{},[499,1340,1341],{},"Entry",[499,1343,1344],{},"Side",[499,1346,1347],{},"Contents",[509,1349,1350,1387,1432,1524,1590,1614,1643,1687,1728,1775,1833,1849],{},[496,1351,1352,1356,1359],{},[514,1353,1354],{},[432,1355,949],{},[514,1357,1358],{},"build",[514,1360,1361,1362,1365,1366,1368,1369,1372,1373,1376,1377,1380,1381,760,1384,488],{},"The Nuxt module (",[432,1363,1364],{},"defineNuxtModule",", config key ",[432,1367,953],{},"). Registers the Nitro scan dir, the client context plugin, AI runtime config, ",[432,1370,1371],{},"build.transpile",", the ",[432,1374,1375],{},"ui.css"," Tailwind ",[432,1378,1379],{},"@source",", Nitro ",[432,1382,1383],{},"externals.inline",[432,1385,1386],{},"experimental.websocket",[496,1388,1389,1394,1397],{},[514,1390,1391],{},[432,1392,1393],{},"@skaile\u002Fforge-editor\u002Fconfig",[514,1395,1396],{},"anywhere",[514,1398,1399,1400,459,1403,459,1406,1409,1410,459,1413,459,1416,459,1419,459,1422,459,1425,1428,1429,703],{},"Framework-free ",[432,1401,1402],{},"defineEditorConfig",[432,1404,1405],{},"serverConfig",[432,1407,1408],{},"publicConfig"," and the types ",[432,1411,1412],{},"EditorConfig",[432,1414,1415],{},"EditorConfigInput",[432,1417,1418],{},"EditorServerConfig",[432,1420,1421],{},"EditorPublicConfig",[432,1423,1424],{},"ModelPresetConfig",[432,1426,1427],{},"TranscriptionConfig",": the module options, the server options and the client options from one definition (see ",[687,1430,1431],{"href":293},"AI configuration",[496,1433,1434,1439,1442],{},[514,1435,1436],{},[432,1437,1438],{},"@skaile\u002Fforge-editor\u002Fserver",[514,1440,1441],{},"server",[514,1443,1444,1445,1448,1449,459,1452,459,1455,459,1458,1460,1461,459,1464,459,1467,459,1470,459,1473,459,1476,459,1479,459,1482,459,1485,459,1488,459,1491,1494,1495,459,1498,459,1501,435,1504,1507,1508,1511,1512,459,1515,459,1518,760,1521,488],{},"Framework-free (no Nuxt, h3, Nitro, Vue or React): ",[432,1446,1447],{},"createCollabServer({ ports, schema, hooks })"," → ",[432,1450,1451],{},"{ hocuspocus, handleConnection, forgetDocument, liveContent, contentChanged, stats, close }",[432,1453,1454],{},"createAiModels({ presets, apiKey, slots, transcription })",[432,1456,1457],{},"createRateLimiter({ max, windowMs })",[432,1459,942],{}," with ",[432,1462,1463],{},"AccessControl",[432,1465,1466],{},"DocumentContentStore",[432,1468,1469],{},"CollabStateStore",[432,1471,1472],{},"Directory",[432,1474,1475],{},"AssetStore",[432,1477,1478],{},"SettingsStore",[432,1480,1481],{},"ThemeStore",[432,1483,1484],{},"ModelProvider",[432,1486,1487],{},"PORT_CAPABILITY",[432,1489,1490],{},"serverCapabilities",[432,1492,1493],{},"HttpError",". The Nitro installers (",[432,1496,1497],{},"installEditorServer",[432,1499,1500],{},"provideEditorServer",[432,1502,1503],{},"useEditorServer",[432,1505,1506],{},"@skaile\u002Fforge-editor\u002Fserver\u002Fnitro",") return the ",[432,1509,1510],{},"EditorServer"," handle with ",[432,1513,1514],{},"forgetDocument",[432,1516,1517],{},"liveContent",[432,1519,1520],{},"enforceRateLimit",[432,1522,1523],{},"resolveModel",[496,1525,1526,1531,1533],{},[514,1527,1528],{},[432,1529,1530],{},"@skaile\u002Fforge-editor\u002Fserver\u002Fhandlers",[514,1532,1441],{},[514,1534,1535,1536,451,1539,459,1542,459,1545,459,1548,459,1551,459,1554,459,1557,459,1560,459,1563,459,1566,459,1569,459,1572,1575,1576,1579,1580,1583,1584,529,1587,1589],{},"One handler per editor\u002FAI route, ",[432,1537,1538],{},"(ctx, request) => Promise\u003CHandlerResponse>",[432,1540,1541],{},"usersGet",[432,1543,1544],{},"tagsGet",[432,1546,1547],{},"settingsGet\u002FPut",[432,1549,1550],{},"assetGet\u002FPost",[432,1552,1553],{},"themesGet",[432,1555,1556],{},"themeGet\u002FPost\u002FDelete",[432,1558,1559],{},"modelsGet",[432,1561,1562],{},"chatPost",[432,1564,1565],{},"generatePost",[432,1567,1568],{},"completePost",[432,1570,1571],{},"extractPost",[432,1573,1574],{},"transcriptCleanupPost",") and the zod schemas of their bodies (",[432,1577,1578],{},"chatBodySchema",", …). Map any HTTP server's request onto ",[432,1581,1582],{},"HandlerRequest"," and send the ",[432,1585,1586],{},"HandlerResponse",[432,1588,1493],{}," carries the status.",[496,1591,1592,1597,1600],{},[514,1593,1594],{},[432,1595,1596],{},"@skaile\u002Fforge-editor\u002Fserver\u002Fnode",[514,1598,1599],{},"server (Node)",[514,1601,1602,1605,1606,1609,1610,1613],{},[432,1603,1604],{},"listenCollab(collab, { port, host })",": a Node HTTP server that serves a ",[432,1607,1608],{},"createCollabServer"," over websockets, for a plain script (needs the ",[432,1611,1612],{},"crossws"," dependency; Node only).",[496,1615,1616,1627,1630],{},[514,1617,1618,459,1621,459,1624],{},[432,1619,1620],{},"@skaile\u002Fforge-editor\u002Fcollab",[432,1622,1623],{},"\u002Fcollab\u002Fvue",[432,1625,1626],{},"\u002Fcollab\u002Freact",[514,1628,1629],{},"browser",[514,1631,1632,1633,459,1636,529,1639,1642],{},"framework-free ",[432,1634,1635],{},"collabFeatureCore(ui?)",[432,1637,1638],{},"yjsAnchorResolver",[432,1640,1641],{},"collabFeature()"," per surface",[496,1644,1645,1656,1658],{},[514,1646,1647,459,1650,459,1653],{},[432,1648,1649],{},"@skaile\u002Fforge-editor\u002Fblock-ui",[432,1651,1652],{},"\u002Fblock-ui\u002Fvue",[432,1654,1655],{},"\u002Fblock-ui\u002Freact",[514,1657,1629],{},[514,1659,1632,1660,459,1663,459,1666,459,1669,459,1672,459,1675,529,1678,459,1681,459,1684,1642],{},[432,1661,1662],{},"blockUiFeatureCore(ui, options?)",[432,1664,1665],{},"blockActionsFor",[432,1667,1668],{},"selectedBlockOf",[432,1670,1671],{},"effectiveTarget",[432,1673,1674],{},"BLOCK_CORNER_ATTR",[432,1676,1677],{},"blockCornerOf",[432,1679,1680],{},"blockUiFeature(options?)",[432,1682,1683],{},"BlockUi",[432,1685,1686],{},"BlockActionsOverlay",[496,1688,1689,1700,1702],{},[514,1690,1691,459,1694,459,1697],{},[432,1692,1693],{},"@skaile\u002Fforge-editor\u002Fauthors",[432,1695,1696],{},"\u002Fauthors\u002Fvue",[432,1698,1699],{},"\u002Fauthors\u002Freact",[514,1701,1629],{},[514,1703,1632,1704,459,1707,459,1710,529,1713,618,1716,459,1719,459,1722,1725,1726,631],{},[432,1705,1706],{},"authorsFeatureCore(ui)",[432,1708,1709],{},"currentAuthor",[432,1711,1712],{},"documentAuthors",[432,1714,1715],{},"authorsFeature()",[432,1717,1718],{},"AuthorColorsButton",[432,1720,1721],{},"AuthorColorsPanel",[432,1723,1724],{},"SettingsAuthorsTab"," per surface (see ",[687,1727,99],{"href":100},[496,1729,1730,1741,1743],{},[514,1731,1732,459,1735,459,1738],{},[432,1733,1734],{},"@skaile\u002Fforge-editor\u002Fcomments",[432,1736,1737],{},"\u002Fcomments\u002Fvue",[432,1739,1740],{},"\u002Fcomments\u002Freact",[514,1742,1629],{},[514,1744,1632,1745,459,1748,529,1751,1754,1755,1757,1758,1761,1762,459,1765,459,1768,459,1771,1774],{},[432,1746,1747],{},"commentsFeatureCore(ui, { model? })",[432,1749,1750],{},"createCommentsModel",[432,1752,1753],{},"commentsFeature({ model? })"," (requires ",[432,1756,584],{}," unless a host ",[432,1759,1760],{},"model"," backs it), ",[432,1763,1764],{},"useComments",[432,1766,1767],{},"CommentsMargin",[432,1769,1770],{},"CommentsPopover",[432,1772,1773],{},"CommentsListMenu",", … per surface",[496,1776,1777,1788,1790],{},[514,1778,1779,459,1782,459,1785],{},[432,1780,1781],{},"@skaile\u002Fforge-editor\u002Fthemes",[432,1783,1784],{},"\u002Fthemes\u002Fvue",[432,1786,1787],{},"\u002Fthemes\u002Freact",[514,1789,1629],{},[514,1791,1632,1792,459,1795,459,1798,459,1801,459,1804,529,1807,1810,1811,625,1814,1817,1818,917,1821,459,1824,459,1827,459,1830],{},[432,1793,1794],{},"themesFeatureCore(ui)",[432,1796,1797],{},"startObsidianTheme",[432,1799,1800],{},"pinObsidianTheme",[432,1802,1803],{},"scopeThemeCss",[432,1805,1806],{},"THEME_SCOPE_CLASS",[432,1808,1809],{},"themesFeature()"," (site-wide Obsidian themes; see ",[687,1812,1813],{"href":105},"Themes",[432,1815,1816],{},"SettingsAppearanceTab"," per surface; Vue-free in ",[432,1819,1820],{},"\u002Fthemes\u002Fschema",[432,1822,1823],{},"ThemeRecord",[432,1825,1826],{},"THEME_ID",[432,1828,1829],{},"MAX_THEME_BYTES",[432,1831,1832],{},"themeManifestSchema",[496,1834,1835,1840,1842],{},[514,1836,1837],{},[432,1838,1839],{},"@skaile\u002Fforge-editor\u002Fschema\u002Fadvanced",[514,1841,1396],{},[514,1843,1844,1845,1848],{},"Vue-free ",[432,1846,1847],{},"advancedSchemaExtensions()","; imports no Vue, UI or browser code",[496,1850,1851,1868,1870],{},[514,1852,1853,459,1856,459,1859,459,1862,459,1865],{},[432,1854,1855],{},"@skaile\u002Fforge-editor\u002Fstyles\u002Fcollab.css",[432,1857,1858],{},"\u002Fblock-ui.css",[432,1860,1861],{},"\u002Fauthors.css",[432,1863,1864],{},"\u002Fcomments.css",[432,1866,1867],{},"\u002Fthemes.css",[514,1869,1629],{},[514,1871,1872,1873,451,1876,1879],{},"Remote carets, block-range selection, author colouring, comment highlights and cards, the Obsidian variable bridge. The Nuxt module adds every feature stylesheet to ",[432,1874,1875],{},"css",[432,1877,1878],{},"themes.css"," last).",[423,1881,1882,1883,1886,1887,459,1889,459,1892,1895,1896,451,1899,1902,1903,451,1906,1909,1910,451,1912,1914,1915,1917,1918,459,1920,459,1922,459,1924,1926],{},"The server logic (",[432,1884,1885],{},"src\u002Fserver\u002F",") runs in any Node process: ",[432,1888,1608],{},[432,1890,1891],{},"createAiModels",[432,1893,1894],{},"createRateLimiter"," and the route handlers take their ports and configuration explicitly. The HTTP routes are one table, ",[432,1897,1898],{},"API",[432,1900,1901],{},"src\u002Fcore\u002Fapi-routes.ts",": method, path, Port and request and response shape of every route), which the HTTP backend reads too; ",[432,1904,1905],{},"serverRoutesOf(ports, { ai })",[432,1907,1908],{},"src\u002Fserver\u002Froutes.ts",") pairs each row with its handler and serves a route only when its port is present. One installer, ",[432,1911,1497],{},[432,1913,1506],{},"), mounts that table and the collab websocket on a Nitro app and returns the ",[432,1916,1510],{}," handle (",[432,1919,1514],{},[432,1921,1517],{},[432,1923,1520],{},[432,1925,1523],{},"); the Nuxt module calls it from its server plugin and a plain Nitro app from a plugin of its own. A plain script needs no Nitro at all:",[1204,1928,1930],{"className":1206,"code":1929,"language":1208,"meta":1209,"style":1209},"import { createCollabServer } from '@skaile\u002Fforge-editor\u002Fserver'\nimport { listenCollab } from '@skaile\u002Fforge-editor\u002Fserver\u002Fnode'\n\nconst collab = createCollabServer({ ports }) \u002F\u002F ports.identity.authenticate({ headers, token }) decides who may connect; needs ports.collabState\nconst { url, close } = await listenCollab(collab, { port: 4000 })\n\u002F\u002F browser: new HocuspocusProvider({ url, name: documentId, document, token })\n",[432,1931,1932,1957,1976,1983,2011,2056],{"__ignoreMap":1209},[1213,1933,1934,1938,1940,1943,1946,1949,1952,1954],{"class":1215,"line":1216},[1213,1935,1937],{"class":1936},"s7zQu","import",[1213,1939,1256],{"class":1234},[1213,1941,1942],{"class":1230}," createCollabServer",[1213,1944,1945],{"class":1234}," }",[1213,1947,1948],{"class":1936}," from",[1213,1950,1951],{"class":1234}," '",[1213,1953,1438],{"class":1248},[1213,1955,1956],{"class":1234},"'\n",[1213,1958,1959,1961,1963,1966,1968,1970,1972,1974],{"class":1215,"line":1223},[1213,1960,1937],{"class":1936},[1213,1962,1256],{"class":1234},[1213,1964,1965],{"class":1230}," listenCollab",[1213,1967,1945],{"class":1234},[1213,1969,1948],{"class":1936},[1213,1971,1951],{"class":1234},[1213,1973,1596],{"class":1248},[1213,1975,1956],{"class":1234},[1213,1977,1979],{"class":1215,"line":1978},3,[1213,1980,1982],{"emptyLinePlaceholder":1981},true,"\n",[1213,1984,1986,1988,1991,1993,1995,1997,2000,2003,2005,2008],{"class":1215,"line":1985},4,[1213,1987,1227],{"class":1226},[1213,1989,1990],{"class":1230}," collab ",[1213,1992,1235],{"class":1234},[1213,1994,1942],{"class":1238},[1213,1996,1242],{"class":1230},[1213,1998,1999],{"class":1234},"{",[1213,2001,2002],{"class":1230}," ports ",[1213,2004,1299],{"class":1234},[1213,2006,2007],{"class":1230},") ",[1213,2009,2010],{"class":1219},"\u002F\u002F ports.identity.authenticate({ headers, token }) decides who may connect; needs ports.collabState\n",[1213,2012,2014,2016,2018,2021,2023,2026,2028,2031,2034,2036,2039,2041,2043,2046,2048,2052,2054],{"class":1215,"line":2013},5,[1213,2015,1227],{"class":1226},[1213,2017,1256],{"class":1234},[1213,2019,2020],{"class":1230}," url",[1213,2022,1253],{"class":1234},[1213,2024,2025],{"class":1230}," close ",[1213,2027,1299],{"class":1234},[1213,2029,2030],{"class":1234}," =",[1213,2032,2033],{"class":1936}," await",[1213,2035,1965],{"class":1238},[1213,2037,2038],{"class":1230},"(collab",[1213,2040,1253],{"class":1234},[1213,2042,1256],{"class":1234},[1213,2044,2045],{"class":1259}," port",[1213,2047,1037],{"class":1234},[1213,2049,2051],{"class":2050},"sbssI"," 4000",[1213,2053,1945],{"class":1234},[1213,2055,1302],{"class":1230},[1213,2057,2059],{"class":1215,"line":2058},6,[1213,2060,2061],{"class":1219},"\u002F\u002F browser: new HocuspocusProvider({ url, name: documentId, document, token })\n",[418,2063,2065],{"id":2064},"per-feature-entries-framework-free-vue-react","Per-feature entries: framework-free, Vue, React",[423,2067,2068],{},"Every feature that has a UI is split in the same four entries, so a host takes exactly the layers it uses:",[490,2070,2071,2082],{},[493,2072,2073],{},[496,2074,2075,2077,2079],{},[499,2076,1341],{},[499,2078,1347],{},[499,2080,2081],{},"May import",[509,2083,2084,2144,2185,2211],{},[496,2085,2086,2091,2122],{},[514,2087,2088],{},[432,2089,2090],{},".\u002F\u003Cx>",[514,2092,2093,2094,1372,2097,451,2100,459,2103,2106,2107,459,2109,459,2112,459,2115,459,2118,2121],{},"The feature's logic: ",[432,2095,2096],{},"\u003Cx>FeatureCore(ui)",[427,2098,2099],{},"models",[432,2101,2102],{},"create*",[432,2104,2105],{},"*Model",": framework-free stores such as ",[432,2108,1750],{},[432,2110,2111],{},"createCompositeRows",[432,2113,2114],{},"createAiAssistant",[432,2116,2117],{},"createDocumentChat",[432,2119,2120],{},"modelProfiles(app)","), Tiptap extensions and plugins, helpers",[514,2123,2124,2125,459,2128,2131,2132,459,2135,459,2138,439,2141],{},"Tiptap\u002FProseMirror, ",[432,2126,2127],{},"\u002Fcore",[432,2129,2130],{},"\u002Fsettings","; no ",[432,2133,2134],{},"vue",[432,2136,2137],{},"react",[432,2139,2140],{},"@nuxt\u002Fui",[432,2142,2143],{},"sonner",[496,2145,2146,2151,2175],{},[514,2147,2148],{},[432,2149,2150],{},".\u002F\u003Cx>\u002Fvue",[514,2152,2153,2156,2157,451,2160,459,2162,459,2165,459,2168,459,2171,2174],{},[432,2154,2155],{},"\u003Cx>Feature(): VueFeature",", the feature's Vue components and node views, thin Vue ",[427,2158,2159],{},"hooks",[432,2161,1764],{},[432,2163,2164],{},"useCompositeRows",[432,2166,2167],{},"useAiAssistant",[432,2169,2170],{},"useModelProfiles",[432,2172,2173],{},"useFileImport",", …) over the models",[514,2176,2177,459,2179,2182,2183],{},[432,2178,2134],{},[432,2180,2181],{},"@tiptap\u002Fvue-3",", Nuxt UI; never ",[432,2184,2137],{},[496,2186,2187,2192,2198],{},[514,2188,2189],{},[432,2190,2191],{},".\u002F\u003Cx>\u002Freact",[514,2193,2194,2197],{},[432,2195,2196],{},"\u003Cx>Feature(): ReactFeature",", the React components (shadcn\u002Fui) and node views, the React hooks over the same models",[514,2199,2200,459,2202,2205,2206,2208,2209],{},[432,2201,2137],{},[432,2203,2204],{},"@tiptap\u002Freact",", Radix, ",[432,2207,2143],{},"; never ",[432,2210,2134],{},[496,2212,2213,2218,2221],{},[514,2214,2215],{},[432,2216,2217],{},".\u002F\u003Cx>\u002Fschema",[514,2219,2220],{},"Node\u002Fmark specs, protocol schemas and limits for servers and validators",[514,2222,2223],{},"no UI, no DOM",[423,2225,2226,2227,459,2229,459,2231,459,2233,459,2235,459,2237,459,2239,459,2241,459,2243,2245,2246,2249,2250,760,2253,451,2256,459,2259,2262,2263,690,2266,2268,2269,2272,2273,459,2276,459,2278,2280,2281,459,2284,760,2287,2290],{},"Entries that exist per feature: ",[432,2228,537],{},[432,2230,540],{},[432,2232,543],{},[432,2234,549],{},[432,2236,483],{},[432,2238,480],{},[432,2240,584],{},[432,2242,589],{},[432,2244,477],{}," (with ",[432,2247,2248],{},"\u002Fschema"," where the feature owns document nodes or a protocol), ",[432,2251,2252],{},"outline",[432,2254,2255],{},"tree",[432,2257,2258],{},"\u002Fvue",[432,2260,2261],{},"\u002Freact",", no ",[432,2264,2265],{},"Core(ui)",[432,2267,546],{}," is framework-free alone (",[432,2270,2271],{},"mentionsFeature(): EditorFeature\u003Cnever>",", usable in both layers). The three integrations (",[432,2274,2275],{},"integrations\u002Fcomposite-media",[432,2277,621],{},[432,2279,628],{},") are framework-free alone too: they only wire other features' models, so ",[432,2282,2283],{},"compositeMediaFeature()",[432,2285,2286],{},"compositeAiFeature()",[432,2288,2289],{},"transcriptAiFeature()"," fit a Vue and a React preset unchanged.",[2292,2293,2294,2361,2460,2491],"ul",{},[2295,2296,2297,2300,2301,2304,2305,2308,2309,2312,2313,917,2316,459,2319,459,2322,529,2325,917,2328,459,2331,690,2334,2337,2338,2340,2341,2343,2344,2346,2347,2350,2351,459,2354,459,2357,2360],"li",{},[427,2298,2299],{},"The UI binding contract."," ",[432,2302,2303],{},"features\u002F\u003Cx>\u002Ffeature.ts"," exports ",[432,2306,2307],{},"\u003Cx>FeatureCore\u003CC>(ui: \u003CX>Ui\u003CC>): EditorFeature\u003CC>",": all feature behaviour (extensions, commands, toolbar items, slash commands, settings tabs, conversions) with the UI left open as the typed interface ",[432,2310,2311],{},"\u003CX>Ui\u003CC>"," (for example ",[432,2314,2315],{},"CommentsUi",[432,2317,2318],{},"toolbarButton",[432,2320,2321],{},"overlay",[432,2323,2324],{},"settingsTab",[432,2326,2327],{},"MediaUi",[432,2329,2330],{},"recorderModal",[432,2332,2333],{},"nodeViews",[432,2335,2336],{},"C"," is the framework's component type. ",[432,2339,2150],{}," fills it with Vue components, ",[432,2342,2191],{}," with React components; a host writing its own chrome (another UI kit, a test) calls ",[432,2345,2096],{}," with its own components. ",[432,2348,2349],{},"EditorFeature\u003Cnever>"," means “no components at all”: such a feature (",[432,2352,2353],{},"coreContentFeature()",[432,2355,2356],{},"coreUiFeature()",[432,2358,2359],{},"mentionsFeature()",", the integrations) is accepted by both presets.",[2295,2362,2363,2366,2367,451,2370,2373,2374,451,2377,2380,2381,529,2384,2387,2388,2390,2391,2394,2395,2398,2399,2402,2403,2406,2407,2410,2411,2414,2415,2418,2419,2423,2424,2427,2428,2431,2432,2435,2436,2439,2440,2443,2444,2447,2448,2451,2452,2455,2456,2459],{},[427,2364,2365],{},"Notices."," Features never import a toast library. A user-facing message is a ",[432,2368,2369],{},"Notice",[432,2371,2372],{},"{ id?, title, description?, kind?, icon?, duration? }",") sent to a ",[432,2375,2376],{},"Notifier",[432,2378,2379],{},"(notice) => void",", optional ",[432,2382,2383],{},"dismiss(id)",[432,2385,2386],{},"silentNotifier"," drops everything), from ",[432,2389,2127],{},". A feature reads it as ",[432,2392,2393],{},"ctx.notify"," in its factories (",[432,2396,2397],{},"FeatureContext.notify","), app-level models (model profiles, the rate-limit notice) as ",[432,2400,2401],{},"EditorContext.notify",", set with ",[432,2404,2405],{},"createEditorContext({ notify })",". Vue: ",[432,2408,2409],{},"vueNotifier(useToast())"," shows Nuxt UI toasts (the page needs ",[432,2412,2413],{},"\u003CUApp>","); ",[432,2416,2417],{},"createEditor()"," installs a ",[2420,2421,2422],"em",{},"queued"," notifier (",[432,2425,2426],{},"createQueuedNotifier()",") that holds notices raised before the first ",[432,2429,2430],{},"EditorSurface"," exists and shows them through that surface's ",[432,2433,2434],{},"useToast()",". React: ",[432,2437,2438],{},"reactNotifier"," shows sonner toasts; pass it as ",[432,2441,2442],{},"createEditorContext({ notify: reactNotifier })"," and mount sonner's ",[432,2445,2446],{},"Toaster"," (re-exported from ",[432,2449,2450],{},"@skaile\u002Fforge-editor\u002Freact",") once per page, otherwise no notice is visible. ",[432,2453,2454],{},"id"," replaces a visible toast, ",[432,2457,2458],{},"duration: 0"," keeps it until replaced or dismissed.",[2295,2461,2462,2468,2469,2472,2473,2476,2477,2479,2480,2482,2483,2486,2487,2490],{},[427,2463,2464,2467],{},[432,2465,2466],{},"setup"," and teardown."," A feature's ",[432,2470,2471],{},"setup(ctx)"," runs once per mounted surface and may return a teardown function that the surface runs when it unmounts. Per-app runtimes dedupe inside and hand back a release: ",[432,2474,2475],{},"startObsidianTheme(ctx)"," is reference-counted per app, so the React ",[432,2478,1809],{}," removes the theme when the last surface that started it unmounts, while the Vue ",[432,2481,1809],{}," (call it during component setup) keeps it for the life of the Vue app through the ",[432,2484,2485],{},"lifetime"," hook of ",[432,2488,2489],{},"ThemesUi",", because the theme styles the whole site. Run own per-app side effects there instead of in a component.",[2295,2492,2493,2496,2497,2500,2501,917,2503,459,2505,459,2508,2511,2512,1019,2515,2517,2518,2521,2522,529,2524,2527,2528,2531,2532,2535,2536,2539,2540,2543,2544,2547,2548,1019,2551,2553,2554,1019,2557,760,2559,2561,2562,2565,2566,2568,2569,459,2572,459,2575,2578],{},[427,2494,2495],{},"Models versus hooks."," State and logic are framework-free ",[432,2498,2499],{},"createStore"," stores (from ",[432,2502,2127],{},[432,2504,2499],{},[432,2506,2507],{},"derived",[432,2509,2510],{},"ReadonlyStore",") inside the models; the hooks only subscribe: React ",[432,2513,2514],{},"useStore(store)",[432,2516,2261],{},"), Vue ",[432,2519,2520],{},"useStoreRef(store)"," (a readonly ref, from ",[432,2523,2258],{},[432,2525,2526],{},"useWritableStoreRef(store)"," for two-way refs, ",[432,2529,2530],{},"trackedStore(store)"," for a store whose ",[432,2533,2534],{},"get()"," Vue tracks, ",[432,2537,2538],{},"storeFromRef(ref)"," the other way round), all from the one ",[432,2541,2542],{},"vue\u002Fstore-ref"," module, used inside the thin ",[432,2545,2546],{},"use*"," hooks of the feature. Notices follow one policy on both surfaces: ",[432,2549,2550],{},"notifierOf(editor, app)",[432,2552,2127],{},") picks the editor's surface notifier, else the app context's, when a notice is shown; ",[432,2555,2556],{},"useNotifier(editor?)",[432,2558,2258],{},[432,2560,2261],{},") is its hook form, so a host that passes ",[432,2563,2564],{},"createEditor({ notify })"," \u002F ",[432,2567,2405],{}," receives every notice of the hooks, including the AI rate-limit notice (",[432,2570,2571],{},"useAiAssistant(editor?)",[432,2573,2574],{},"useDocumentChat(editor?)",[432,2576,2577],{},"useFileImport(editor?)","). A host that wants a different framework (or a script) uses the model directly.",[418,2580,2582],{"id":2581},"feature-composition","Feature composition",[423,2584,2585],{},"An editor is a list of features. Every feature is imported from its own entry, so a feature you do not import is not in your bundle:",[1204,2587,2589],{"className":1206,"code":2588,"language":1208,"meta":1209,"style":1209},"import { compositeFeature } from '@skaile\u002Fforge-editor\u002Fcomposites\u002Fvue'\nimport { coreContentFeature, coreUiFeature, defineEditor } from '@skaile\u002Fforge-editor\u002Fcore'\nimport { compositeMediaFeature } from '@skaile\u002Fforge-editor\u002Fintegrations\u002Fcomposite-media'\nimport { mediaFeature } from '@skaile\u002Fforge-editor\u002Fmedia\u002Fvue'\n\n\u002F\u002F Order = order of toolbar groups, menu entries and the schema.\nconst preset = defineEditor({ features: [coreContentFeature(), coreUiFeature(), mediaFeature(), compositeFeature(), compositeMediaFeature()] })\n",[432,2590,2591,2611,2641,2661,2681,2685,2690],{"__ignoreMap":1209},[1213,2592,2593,2595,2597,2600,2602,2604,2606,2609],{"class":1215,"line":1216},[1213,2594,1937],{"class":1936},[1213,2596,1256],{"class":1234},[1213,2598,2599],{"class":1230}," compositeFeature",[1213,2601,1945],{"class":1234},[1213,2603,1948],{"class":1936},[1213,2605,1951],{"class":1234},[1213,2607,2608],{"class":1248},"@skaile\u002Fforge-editor\u002Fcomposites\u002Fvue",[1213,2610,1956],{"class":1234},[1213,2612,2613,2615,2617,2620,2622,2625,2627,2630,2632,2634,2636,2639],{"class":1215,"line":1223},[1213,2614,1937],{"class":1936},[1213,2616,1256],{"class":1234},[1213,2618,2619],{"class":1230}," coreContentFeature",[1213,2621,1253],{"class":1234},[1213,2623,2624],{"class":1230}," coreUiFeature",[1213,2626,1253],{"class":1234},[1213,2628,2629],{"class":1230}," defineEditor",[1213,2631,1945],{"class":1234},[1213,2633,1948],{"class":1936},[1213,2635,1951],{"class":1234},[1213,2637,2638],{"class":1248},"@skaile\u002Fforge-editor\u002Fcore",[1213,2640,1956],{"class":1234},[1213,2642,2643,2645,2647,2650,2652,2654,2656,2659],{"class":1215,"line":1978},[1213,2644,1937],{"class":1936},[1213,2646,1256],{"class":1234},[1213,2648,2649],{"class":1230}," compositeMediaFeature",[1213,2651,1945],{"class":1234},[1213,2653,1948],{"class":1936},[1213,2655,1951],{"class":1234},[1213,2657,2658],{"class":1248},"@skaile\u002Fforge-editor\u002Fintegrations\u002Fcomposite-media",[1213,2660,1956],{"class":1234},[1213,2662,2663,2665,2667,2670,2672,2674,2676,2679],{"class":1215,"line":1985},[1213,2664,1937],{"class":1936},[1213,2666,1256],{"class":1234},[1213,2668,2669],{"class":1230}," mediaFeature",[1213,2671,1945],{"class":1234},[1213,2673,1948],{"class":1936},[1213,2675,1951],{"class":1234},[1213,2677,2678],{"class":1248},"@skaile\u002Fforge-editor\u002Fmedia\u002Fvue",[1213,2680,1956],{"class":1234},[1213,2682,2683],{"class":1215,"line":2013},[1213,2684,1982],{"emptyLinePlaceholder":1981},[1213,2686,2687],{"class":1215,"line":2058},[1213,2688,2689],{"class":1219},"\u002F\u002F Order = order of toolbar groups, menu entries and the schema.\n",[1213,2691,2693,2695,2697,2699,2701,2703,2705,2708,2710,2712,2715,2718,2720,2722,2724,2726,2728,2730,2732,2734,2736,2738,2740,2743,2745],{"class":1215,"line":2692},7,[1213,2694,1227],{"class":1226},[1213,2696,1231],{"class":1230},[1213,2698,1235],{"class":1234},[1213,2700,2629],{"class":1238},[1213,2702,1242],{"class":1230},[1213,2704,1999],{"class":1234},[1213,2706,2707],{"class":1259}," features",[1213,2709,1037],{"class":1234},[1213,2711,1265],{"class":1230},[1213,2713,2714],{"class":1238},"coreContentFeature",[1213,2716,2717],{"class":1230},"()",[1213,2719,1253],{"class":1234},[1213,2721,2624],{"class":1238},[1213,2723,2717],{"class":1230},[1213,2725,1253],{"class":1234},[1213,2727,2669],{"class":1238},[1213,2729,2717],{"class":1230},[1213,2731,1253],{"class":1234},[1213,2733,2599],{"class":1238},[1213,2735,2717],{"class":1230},[1213,2737,1253],{"class":1234},[1213,2739,2649],{"class":1238},[1213,2741,2742],{"class":1230},"()] ",[1213,2744,1299],{"class":1234},[1213,2746,1302],{"class":1230},[2292,2748,2749,2789,2798,2900,2938,2951,3005,3046,3070],{},[2295,2750,2751,2754,2755,2757,2758,2761,2762,529,2764,2761,2766,760,2768,529,2770,2761,2772,459,2774,459,2776,529,2778,2761,2780,459,2782,2784,2785,2788],{},[432,2752,2753],{},"defineEditor"," checks ",[432,2756,637],{},": an integration listed without the features it connects (",[432,2759,2760],{},"commentsFeature()"," needs ",[432,2763,584],{},[432,2765,2283],{},[432,2767,543],{},[432,2769,537],{},[432,2771,2286],{},[432,2773,543],{},[432,2775,480],{},[432,2777,537],{},[432,2779,2289],{},[432,2781,540],{},[432,2783,480],{},") throws at composition time. ",[432,2786,2787],{},"bun run check:editor-composition"," keeps that behavior.",[2295,2790,2791,2792,760,2795,488],{},"Own blocks and features: see the tutorial chapters ",[687,2793,2794],{"href":61},"Your own block (path A)",[687,2796,2797],{"href":66},"Your own feature (path B)",[2295,2799,2800,2801,2804,2805,459,2807,2810,2811,2814,2815,2819,2820,917,2822,2824,2825,451,2827,2830,2831,2834,2835,690,2838,451,2841,2844,2845,451,2847,917,2849,2852,2853,451,2856,459,2859,625,2862,451,2865,459,2868,2870,2871,2873,2874,917,2877,459,2880,459,2882,459,2884,459,2886,459,2888,459,2891,459,2894,2896,2897,2899],{},"Prebuilt compositions: ",[432,2802,2803],{},"composeEditor(options)"," per surface (",[432,2806,438],{},[432,2808,2809],{},"@skaile\u002Fforge-editor\u002Freact\u002Fadvanced",") builds an ",[432,2812,2813],{},"EditorPreset"," from the three dimensions of the ",[687,2816,2818],{"href":2817},"#dimensions-and-integration-depth","table at the top"," plus an optional ",[432,2821,450],{},[432,2823,916],{}," returns the preset with ",[432,2826,487],{},[432,2828,2829],{},"{ id, reason }[]",": a feature dropped because it ",[432,2832,2833],{},"requires \"\u003Cid>\""," or because ",[432,2836,2837],{},"backend \"\u003Cname>\" lacks \"\u003Ccapability>\"",[432,2839,2840],{},"advancedEditor({ blockTypes?, …overrides })",[432,2842,2843],{},"{ content: 'rich', interaction: 'full', ai: true }",") is a one-line wrapper; the framework-free ",[432,2846,1307],{},[432,2848,446],{},[432,2850,2851],{},"{ content: 'basic', interaction: 'minimal', ai: false }",", no ⌘K palette) is the component-free simple preset; ",[432,2854,2855],{},"SimpleEditor",[432,2857,2858],{},"\u002Fvue\u002Fsimple",[432,2860,2861],{},"\u002Freact\u002Fsimple",[432,2863,2864],{},"AdvancedEditor",[432,2866,2867],{},"\u002Fvue\u002Fadvanced",[432,2869,442],{},") are the matching components. ",[432,2872,446],{}," is the framework-free assembly (",[432,2875,2876],{},"presets\u002Fcompose.ts",[432,2878,2879],{},"composeEditor(ui, options)",[432,2881,851],{},[432,2883,854],{},[432,2885,113],{},[432,2887,870],{},[432,2889,2890],{},"ComposeOptions\u003CC>",[432,2892,2893],{},"ComposedEditor\u003CC>",[432,2895,1307],{},"): a UI layer binds only the UI-bound features (core UI, media, transcript, composites, authors, comments, AI and its integrations, collab, block UI, themes), the framework-free ones (core content, mentions, tables, composite-media) are built there; it contains no component and is not a preset to mount. The order is canonical (content, then interaction, then AI, then ",[432,2898,645],{}," in the given order), so schema and toolbar order do not depend on how the options were written.",[2295,2901,2902,2903,2906,2907,2909,2910,2912,2913,1308,2916,459,2919,1308,2922,2925,2926,2928,2929,459,2932,2934,2935,703],{},"Both surfaces take the same features: a React preset is ",[432,2904,2905],{},"defineEditor({ features: [coreContentFeature(), coreUiFeature(), mediaFeature(), commentsFeature(), …] })"," with the factories from ",[432,2908,2191],{},", a Vue one with those from ",[432,2911,2150],{},". Mixing is a type error (",[432,2914,2915],{},"VueFeature",[432,2917,2918],{},"EditorFeature\u003CComponent>",[432,2920,2921],{},"ReactFeature",[432,2923,2924],{},"EditorFeature\u003CComponentType\u003C{ editor: Editor }>>","); only ",[432,2927,2349],{}," features (",[432,2930,2931],{},"coreContentFeature(), coreUiFeature()",[432,2933,2359],{}," and the integrations) fit either (",[687,2936,2937],{"href":30},"quick start for React",[2295,2939,2940,2941,2944,2945,451,2948,703],{},"The preset, and with it the schema, collaboration mode and slash commands, is read once when the surface mounts. Change it by remounting with a new ",[432,2942,2943],{},"key",". A simple editor cannot safely edit advanced JSON: unknown nodes are rejected through ",[432,2946,2947],{},"contentError",[687,2949,2950],{"href":51},"simple editor tutorial",[2295,2952,2953,2954,2957,2958,459,2961,459,2964,451,2967,2969,2970,451,2973,2969,2976,459,2979,451,2982,2969,2985,451,2988,2991,2992,2969,2995,451,2997,2969,2999,451,3002,703],{},"Components that belong ",[2420,2955,2956],{},"beside"," an editor are separate exports and take the editor as a prop (the toolbar and the settings components find its preset through it). Vue: ",[432,2959,2960],{},"EditorToolbar",[432,2962,2963],{},"EditorSettingsPanel",[432,2965,2966],{},"EditorSettingsModal",[432,2968,2258],{},"), ",[432,2971,2972],{},"EditorOutline",[432,2974,2975],{},"\u002Foutline\u002Fvue",[432,2977,2978],{},"CompositeTable",[432,2980,2981],{},"CompositeInspector",[432,2983,2984],{},"\u002Fcomposites\u002Fvue",[432,2986,2987],{},"AiSidebar",[432,2989,2990],{},"\u002Fai\u002Fvue\u002Fchat",", optional peer ",[432,2993,2994],{},"@skaile\u002Fforge-chat",[432,2996,1767],{},[432,2998,1737],{},[432,3000,3001],{},"DocumentTree",[432,3003,3004],{},"\u002Ftree\u002Fvue",[2295,3006,3007,3008,459,3010,459,3012,744,3014,529,3016,744,3018,529,3021,459,3023,744,3025,529,3028,744,3030,529,3033,744,3035,529,3037,744,3039,3042,3043,3045],{},"The React equivalents have the same names and props style: ",[432,3009,2960],{},[432,3011,2963],{},[432,3013,2966],{},[432,3015,2450],{},[432,3017,2972],{},[432,3019,3020],{},"\u002Foutline\u002Freact",[432,3022,2978],{},[432,3024,2981],{},[432,3026,3027],{},"\u002Fcomposites\u002Freact",[432,3029,2987],{},[432,3031,3032],{},"\u002Fai\u002Freact",[432,3034,1767],{},[432,3036,1740],{},[432,3038,3001],{},[432,3040,3041],{},"\u002Ftree\u002Freact",". The host mounts sonner's ",[432,3044,2446],{}," once.",[2295,3047,3048,3050,3051,917,3054,3057,3058,3061,3062,3065,3066,3069],{},[432,3049,2960],{}," (Vue and React) takes ",[432,3052,3053],{},"overflow",[432,3055,3056],{},"'wrap'"," (default) breaks its groups onto further lines; ",[432,3059,3060],{},"'collapse'"," keeps one line and folds the groups that do not fit, from the end, into one dropdown each (the first command's icon and a chevron; ",[432,3063,3064],{},"component"," commands, such as the comments and author-colour toggles, stay as buttons). Give the toolbar a bounded width, e.g. ",[432,3067,3068],{},"class=\"w-full\""," in a header row.",[2295,3071,3072,3073,3076,3077,3079,3080,3082,3083,3086,3087,3090,3091,3095],{},"A feature can run per-app side effects from every surface's setup without touching the surface markup: its ",[432,3074,3075],{},"setup: (ctx) => void | (() => void)"," contribution is called by ",[432,3078,2430],{}," for each mounted surface and its returned teardown when the surface unmounts (",[432,3081,1809],{}," starts its theme runtime this way; the runtime dedupes per app and puts ",[432,3084,3085],{},"obsidian-theme theme-dark|theme-light"," on ",[432,3088,3089],{},"\u003Chtml>"," so the whole app follows the theme; see ",[687,3092,3094],{"href":3093},"#per-feature-entries-framework-free-vue-react","Per-feature entries"," for its lifetime on each surface).",[418,3097,3099],{"id":3098},"browser-entries-versus-schema-and-server-entries","Browser entries versus schema and server entries",[490,3101,3102,3113],{},[493,3103,3104],{},[496,3105,3106,3109,3111],{},[499,3107,3108],{},"Entry group",[499,3110,1332],{},[499,3112,2081],{},[509,3114,3115,3170,3193,3250,3300],{},[496,3116,3117,3120,3164],{},[514,3118,3119],{},"Browser, with Vue and Nuxt UI",[514,3121,3122,459,3124,459,3126,459,3128,451,3131,459,3134,459,3137,459,3140,459,3143,459,3146,459,3149,459,3152,459,3155,459,3158,459,3161,631],{},[432,3123,2258],{},[432,3125,2858],{},[432,3127,2867],{},[432,3129,3130],{},"\u002F\u003Cfeature>\u002Fvue",[432,3132,3133],{},"\u002Fmedia",[432,3135,3136],{},"\u002Ftranscript",[432,3138,3139],{},"\u002Fauthors",[432,3141,3142],{},"\u002Fcomments",[432,3144,3145],{},"\u002Fcomposites",[432,3147,3148],{},"\u002Fai",[432,3150,3151],{},"\u002Fcollab",[432,3153,3154],{},"\u002Fblock-ui",[432,3156,3157],{},"\u002Fthemes",[432,3159,3160],{},"\u002Foutline",[432,3162,3163],{},"\u002Ftree",[514,3165,3166,3167,3169],{},"everything above them in the rules of ",[687,3168,369],{"href":370},"; never React",[496,3171,3172,3175,3187],{},[514,3173,3174],{},"Browser, with React and shadcn\u002Fui",[514,3176,3177,459,3179,459,3181,459,3183,3186],{},[432,3178,2261],{},[432,3180,2861],{},[432,3182,442],{},[432,3184,3185],{},"\u002F\u003Cfeature>\u002Freact"," (the same features)",[514,3188,3189,3190,3192],{},"the same rules; never Vue, ",[432,3191,2181],{}," or Nuxt UI",[496,3194,3195,3198,3232],{},[514,3196,3197],{},"Browser, framework-free",[514,3199,3200,451,3202,459,3204,3207,3208,3210,3211,760,3214,2969,3217,451,3220,3222,3223,459,3226,459,3229],{},[432,3201,2127],{},[432,3203,2753],{},[432,3205,3206],{},"createEditorContext",", the settings store, ",[432,3209,2499],{},", the notice seam, the suggestion renderer seam, descriptors such as ",[432,3212,3213],{},"EditorFeature\u003CC>",[432,3215,3216],{},"MenuItem",[432,3218,3219],{},"\u002F\u003Cfeature>",[432,3221,2096],{},", the models), ",[432,3224,3225],{},"\u002Fmentions",[432,3227,3228],{},"\u002Fintegrations\u002F*",[432,3230,3231],{},"\u002Fpresets\u002F*",[514,3233,2124,3234,760,3236,3238,3239,459,3241,459,3243,459,3245,439,3247,3249],{},[432,3235,2127],{},[432,3237,2130],{}," only (plus Yjs\u002FAI SDK where the feature needs them); no ",[432,3240,2134],{},[432,3242,2181],{},[432,3244,2140],{},[432,3246,2137],{},[432,3248,2143],{}," module",[496,3251,3252,3255,3297],{},[514,3253,3254],{},"Server-safe schema and protocol, usable in Node",[514,3256,3257,459,3260,459,3263,459,3266,459,3269,459,3272,459,3275,459,3278,459,3281,459,3284,459,3286,459,3289,459,3291,459,3294],{},[432,3258,3259],{},"\u002Fcore\u002Fschema",[432,3261,3262],{},"\u002Fschema\u002Fsimple",[432,3264,3265],{},"\u002Fschema\u002Fadvanced",[432,3267,3268],{},"\u002Fmedia\u002Fschema",[432,3270,3271],{},"\u002Fmentions\u002Fschema",[432,3273,3274],{},"\u002Ftranscript\u002Fschema",[432,3276,3277],{},"\u002Fauthors\u002Fschema",[432,3279,3280],{},"\u002Fcomments\u002Fschema",[432,3282,3283],{},"\u002Fcomposites\u002Fschema",[432,3285,1820],{},[432,3287,3288],{},"\u002Ftree\u002Ftypes",[432,3290,2130],{},[432,3292,3293],{},"\u002Fai\u002Fprotocol",[432,3295,3296],{},"\u002Fintegrations\u002Ftranscript-ai\u002Fprotocol",[514,3298,3299],{},"no UI framework, no DOM globals or feature factories",[496,3301,3302,3304,3312],{},[514,3303,892],{},[514,3305,3306,459,3309],{},[432,3307,3308],{},"\u002Fserver",[432,3310,3311],{},"\u002Fnuxt",[514,3313,3314],{},"Nitro\u002FNuxt build code; never imported by browser code",[423,3316,3317,3318,3321],{},"Servers and validators build their ProseMirror schema from the framework-free schema entries (",[432,3319,3320],{},"getSchema(advancedSchemaExtensions())","), never from a browser entry.",[418,3323,3325],{"id":3324},"choosing-a-preset-or-features","Choosing a preset or features",[490,3327,3328,3338],{},[493,3329,3330],{},[496,3331,3332,3335],{},[499,3333,3334],{},"Goal",[499,3336,3337],{},"Use",[509,3339,3340,3360,3372,3434],{},[496,3341,3342,3345],{},[514,3343,3344],{},"Rich text only, no backend, smallest bundle",[514,3346,3347,3349,3350,744,3352,3355,3356,459,3358],{},[432,3348,2855],{}," (with the ⌘K palette) \u002F ",[432,3351,1307],{},[432,3353,3354],{},"\u002Fpresets\u002Fcompose"," (without it); features ",[432,3357,528],{},[432,3359,576],{},[496,3361,3362,3365],{},[514,3363,3364],{},"Everything, Nuxt module as the backend",[514,3366,3367,2565,3369],{},[432,3368,2864],{},[432,3370,3371],{},"advancedEditor({ blockTypes? })",[496,3373,3374,3377],{},[514,3375,3376],{},"A level in between: advanced minus a feature, or a small editor with AI",[514,3378,3379,459,3382,917,3385,3387,3388,459,3390,459,3392,459,3394,459,3396,459,3398,459,3400,459,3402,3404,3405,459,3407,459,3409,459,3411,625,3413,3416,3417,3419,3420,3423,3424,690,3426,3428,3429,917,3431],{},[432,3380,3381],{},"\u003CAdvancedEditor :remove=\"['tables']\" \u002F>",[432,3383,3384],{},"\u003CAdvancedEditor content=\"basic\" interaction=\"minimal\" :ai=\"true\" \u002F>",[432,3386,2864],{}," takes ",[432,3389,518],{},[432,3391,567],{},[432,3393,480],{},[432,3395,645],{},[432,3397,649],{},[432,3399,450],{},[432,3401,697],{},[432,3403,1174],{}," (which surfaces show block actions: ",[432,3406,790],{},[432,3408,794],{},[432,3410,798],{},[432,3412,802],{},[432,3414,3415],{},"coreContent"," (headings, lists, blockquote, code block or rule left out of the schema), all read once at setup, builds the preset with ",[432,3418,434],{}," and exposes it (",[432,3421,3422],{},"preset",", with ",[432,3425,487],{},[432,3427,2855],{}," stays lean (core features only; its bundle holds no catalog feature) and takes just ",[432,3430,645],{},[432,3432,3433],{},"\u003CSimpleEditor :add=\"[aiFeature()]\" \u002F>",[496,3435,3436,3439],{},[514,3437,3438],{},"Something else, or the advanced set plus an own feature",[514,3440,3441,3444,3445,612,3447],{},[432,3442,3443],{},"defineEditor({ features: [...] })"," mounted with ",[432,3446,2430],{},[432,3448,3449],{},"advancedEditor({ add: [ownFeature()] })",[423,3451,3452,3453,459,3455,459,3457,459,3459,459,3461,459,3463,459,3465,459,3467,459,3469,459,3471,459,3473,459,3475,459,3477,459,3479,459,3481,3483,3484,3486,3487,3489],{},"Advanced list in schema order: ",[432,3454,528],{},[432,3456,576],{},[432,3458,537],{},[432,3460,540],{},[432,3462,543],{},[432,3464,546],{},[432,3466,549],{},[432,3468,483],{},[432,3470,480],{},[432,3472,584],{},[432,3474,589],{},[432,3476,555],{},[432,3478,621],{},[432,3480,628],{},[432,3482,477],{},". ",[432,3485,669],{}," is not part of it; add it through the ",[432,3488,567],{}," list as above.",[490,3491,3492,3507],{},[493,3493,3494],{},[496,3495,3496,3499,3504],{},[499,3497,3498],{},"Feature (factory, entries)",[499,3500,3501,3502,631],{},"Needs (",[432,3503,637],{},[499,3505,3506],{},"Backend",[509,3508,3509,3521,3562,3593,3617,3634,3652,3673,3699,3727,3755,3771,3792,3847,3866,3889],{},[496,3510,3511,3517,3519],{},[514,3512,3513,451,3515,631],{},[432,3514,2353],{},[432,3516,2127],{},[514,3518,929],{},[514,3520,929],{},[496,3522,3523,3558,3560],{},[514,3524,3525,451,3528,529,3530,3533,3534,3536,3537,751,3540,435,3543,760,3545,3547,3548,3551,3552,3554,3555,3557],{},[432,3526,3527],{},"coreUiFeature(ui?)",[432,3529,2127],{},[432,3531,3532],{},"ui = { palette }"," adds the ⌘K palette: the ",[432,3535,2855],{}," components and the ",[432,3538,3539],{},"composeEditor()",[432,3541,3542],{},"advancedEditor()",[432,3544,2867],{},[432,3546,442],{}," pass the framework's ",[432,3549,3550],{},"EditorCommandPalette","; the framework-free ",[432,3553,1307],{}," and a hand-built ",[432,3556,2753],{}," list have none unless they pass it themselves)",[514,3559,929],{},[514,3561,929],{},[496,3563,3564,3581,3583],{},[514,3565,3566,451,3569,459,3572,529,3575,3578,3579,631],{},[432,3567,3568],{},"mediaFeature()",[432,3570,3571],{},"\u002Fmedia\u002Fvue",[432,3573,3574],{},"\u002Fmedia\u002Freact",[432,3576,3577],{},"mediaFeatureCore"," in ",[432,3580,3133],{},[514,3582,929],{},[514,3584,3585,3586,3589,3590,631],{},"uploads (",[432,3587,3588],{},"backend.assets","; HTTP: ",[432,3591,3592],{},"\u002Fapi\u002Feditor\u002Fassets\u002F:id",[496,3594,3595,3602,3604],{},[514,3596,3597,451,3599,3601],{},[432,3598,2359],{},[432,3600,3225],{},", no components: both surfaces)",[514,3603,929],{},[514,3605,3606,459,3609,3612,3613,3616],{},[432,3607,3608],{},"\u002Fapi\u002Feditor\u002Fusers",[432,3610,3611],{},"\u002Fapi\u002Feditor\u002Ftags"," or host ",[432,3614,3615],{},"metadata"," lists",[496,3618,3619,3630,3632],{},[514,3620,3621,451,3624,459,3627,631],{},[432,3622,3623],{},"transcriptFeature()",[432,3625,3626],{},"\u002Ftranscript\u002Fvue",[432,3628,3629],{},"\u002Ftranscript\u002Freact",[514,3631,929],{},[514,3633,929],{},[496,3635,3636,3644,3646],{},[514,3637,3638,451,3640,459,3642,631],{},[432,3639,1715],{},[432,3641,1696],{},[432,3643,1699],{},[514,3645,929],{},[514,3647,3648,3649,631],{},"none (author comes from the collaboration session ",[432,3650,3651],{},"user",[496,3653,3654,3662,3664],{},[514,3655,3656,451,3658,459,3660,631],{},[432,3657,1641],{},[432,3659,1623],{},[432,3661,1626],{},[514,3663,929],{},[514,3665,3666,3669,3670,3672],{},[432,3667,3668],{},"\u002Fcollab\u002F:id"," websocket; active only when ",[432,3671,462],{}," is passed to the surface",[496,3674,3675,3683,3690],{},[514,3676,3677,451,3679,459,3681,631],{},[432,3678,1753],{},[432,3680,1737],{},[432,3682,1740],{},[514,3684,3685,3687,3688],{},[432,3686,584],{},", none with ",[432,3689,1760],{},[514,3691,3692,3693,451,3696,631],{},"comments live in the Y.Doc, or in the host's ",[432,3694,3695],{},"CommentsModel",[432,3697,3698],{},"model: getEditor => CommentsModel",[496,3700,3701,3710,3712],{},[514,3702,3703,451,3706,459,3708,631],{},[432,3704,3705],{},"compositeFeature({ blockTypes? })",[432,3707,2984],{},[432,3709,3027],{},[514,3711,929],{},[514,3713,3714,3715,3718,3719,3722,3723,3726],{},"none (also mounts the Typed block overlay: status\u002Ftag chips on each title line and, on the Block type icon, a popover with the Block type dropdown (warns before ",[432,3716,3717],{},"changeBlockType",", naming the Block property values that would be lost), the Block type's properties and the summary; also read-only: Block type as text, properties disabled; enforces the Block types' Accepted children, ",[432,3720,3721],{},"BlockType.accepts",": an edit that nests a Typed block into one that does not accept its Block type is refused with the notice ",[432,3724,3725],{},"composites.nesting.title"," („Verschachtelung nicht möglich“))",[496,3728,3729,3748,3750],{},[514,3730,3731,451,3734,459,3737,3739,3740,3578,3743,3745,3746,631],{},[432,3732,3733],{},"aiFeature()",[432,3735,3736],{},"\u002Fai\u002Fvue",[432,3738,3032],{},"; framework-free ",[432,3741,3742],{},"aiFeatureCore",[432,3744,3148],{},"; the Vue sidebar is the separate ",[432,3747,2990],{},[514,3749,929],{},[514,3751,3752],{},[432,3753,3754],{},"\u002Fapi\u002Fai\u002F{models,generate,chat,complete}",[496,3756,3757,3766,3768],{},[514,3758,3759,451,3762,459,3764,631],{},[432,3760,3761],{},"blockUiFeature()",[432,3763,1652],{},[432,3765,1655],{},[514,3767,929],{},[514,3769,3770],{},"none (drag handle, hover actions)",[496,3772,3773,3781,3783],{},[514,3774,3775,451,3777,459,3779,631],{},[432,3776,1809],{},[432,3778,1784],{},[432,3780,1787],{},[514,3782,929],{},[514,3784,3785,3786,751,3789],{},"adapter ",[432,3787,3788],{},"listThemes",[432,3790,3791],{},"loadTheme",[496,3793,3794,3811,3831],{},[514,3795,3796,451,3799,459,3802,3739,3805,3578,3808,631],{},[432,3797,3798],{},"transferFeature({ transforms?, defaultTransform? })",[432,3800,3801],{},"\u002Ftransfer\u002Fvue",[432,3803,3804],{},"\u002Ftransfer\u002Freact",[432,3806,3807],{},"transferFeatureCore",[432,3809,3810],{},"\u002Ftransfer",[514,3812,3813,451,3815,3817,3818,760,3821,435,3824,3826,3827,3830],{},[432,3814,543],{},[432,3816,637],{},"); none else (",[432,3819,3820],{},"summaryTransform",[432,3822,3823],{},"bulletsTransform",[432,3825,677],{}," are offered only with an AI backend and the ",[432,3828,3829],{},"typedBlock"," node in the schema)",[514,3832,3833,3834,529,3837,760,3840,3843,3844],{},"none for ",[432,3835,3836],{},"copy",[432,3838,3839],{},"summary",[432,3841,3842],{},"bullets"," use ",[432,3845,3846],{},"POST \u002Fapi\u002Fai\u002Fgenerate",[496,3848,3849,3857,3863],{},[514,3850,3851,451,3853,3856],{},[432,3852,2283],{},[432,3854,3855],{},"\u002Fintegrations\u002Fcomposite-media",", both surfaces)",[514,3858,3859,459,3861],{},[432,3860,543],{},[432,3862,537],{},[514,3864,3865],{},"uploads",[496,3867,3868,3875,3883],{},[514,3869,3870,451,3872,3856],{},[432,3871,2286],{},[432,3873,3874],{},"\u002Fintegrations\u002Fcomposite-ai",[514,3876,3877,459,3879,459,3881],{},[432,3878,543],{},[432,3880,480],{},[432,3882,537],{},[514,3884,3885,3886],{},"AI plus ",[432,3887,3888],{},"\u002Fapi\u002Fai\u002Fextract",[496,3890,3891,3898,3904],{},[514,3892,3893,451,3895,3856],{},[432,3894,2289],{},[432,3896,3897],{},"\u002Fintegrations\u002Ftranscript-ai",[514,3899,3900,459,3902],{},[432,3901,540],{},[432,3903,480],{},[514,3905,3885,3906],{},[432,3907,3908],{},"\u002Fapi\u002Fai\u002Ftranscript-cleanup",[1204,3910,3912],{"className":1206,"code":3911,"language":1208,"meta":1209,"style":1209},"import { coreContentFeature, coreUiFeature, defineEditor } from '@skaile\u002Fforge-editor\u002Fcore'\nimport { mediaFeature } from '@skaile\u002Fforge-editor\u002Fmedia\u002Fvue' \u002F\u002F or '\u002Fmedia\u002Freact' with a React surface\nimport { mentionsFeature } from '@skaile\u002Fforge-editor\u002Fmentions'\n\n\u002F\u002F The models come from the app context, so no component setup is needed for the AI feature (themesFeature() of the Vue binding is the exception: call it in setup).\nconst preset = defineEditor({ features: [coreContentFeature(), coreUiFeature(), mediaFeature(), mentionsFeature()] })\n",[432,3913,3914,3940,3961,3981,3985,3990],{"__ignoreMap":1209},[1213,3915,3916,3918,3920,3922,3924,3926,3928,3930,3932,3934,3936,3938],{"class":1215,"line":1216},[1213,3917,1937],{"class":1936},[1213,3919,1256],{"class":1234},[1213,3921,2619],{"class":1230},[1213,3923,1253],{"class":1234},[1213,3925,2624],{"class":1230},[1213,3927,1253],{"class":1234},[1213,3929,2629],{"class":1230},[1213,3931,1945],{"class":1234},[1213,3933,1948],{"class":1936},[1213,3935,1951],{"class":1234},[1213,3937,2638],{"class":1248},[1213,3939,1956],{"class":1234},[1213,3941,3942,3944,3946,3948,3950,3952,3954,3956,3958],{"class":1215,"line":1223},[1213,3943,1937],{"class":1936},[1213,3945,1256],{"class":1234},[1213,3947,2669],{"class":1230},[1213,3949,1945],{"class":1234},[1213,3951,1948],{"class":1936},[1213,3953,1951],{"class":1234},[1213,3955,2678],{"class":1248},[1213,3957,1245],{"class":1234},[1213,3959,3960],{"class":1219}," \u002F\u002F or '\u002Fmedia\u002Freact' with a React surface\n",[1213,3962,3963,3965,3967,3970,3972,3974,3976,3979],{"class":1215,"line":1978},[1213,3964,1937],{"class":1936},[1213,3966,1256],{"class":1234},[1213,3968,3969],{"class":1230}," mentionsFeature",[1213,3971,1945],{"class":1234},[1213,3973,1948],{"class":1936},[1213,3975,1951],{"class":1234},[1213,3977,3978],{"class":1248},"@skaile\u002Fforge-editor\u002Fmentions",[1213,3980,1956],{"class":1234},[1213,3982,3983],{"class":1215,"line":1985},[1213,3984,1982],{"emptyLinePlaceholder":1981},[1213,3986,3987],{"class":1215,"line":2013},[1213,3988,3989],{"class":1219},"\u002F\u002F The models come from the app context, so no component setup is needed for the AI feature (themesFeature() of the Vue binding is the exception: call it in setup).\n",[1213,3991,3992,3994,3996,3998,4000,4002,4004,4006,4008,4010,4012,4014,4016,4018,4020,4022,4024,4026,4028,4030,4032,4034],{"class":1215,"line":2058},[1213,3993,1227],{"class":1226},[1213,3995,1231],{"class":1230},[1213,3997,1235],{"class":1234},[1213,3999,2629],{"class":1238},[1213,4001,1242],{"class":1230},[1213,4003,1999],{"class":1234},[1213,4005,2707],{"class":1259},[1213,4007,1037],{"class":1234},[1213,4009,1265],{"class":1230},[1213,4011,2714],{"class":1238},[1213,4013,2717],{"class":1230},[1213,4015,1253],{"class":1234},[1213,4017,2624],{"class":1238},[1213,4019,2717],{"class":1230},[1213,4021,1253],{"class":1234},[1213,4023,2669],{"class":1238},[1213,4025,2717],{"class":1230},[1213,4027,1253],{"class":1234},[1213,4029,3969],{"class":1238},[1213,4031,2742],{"class":1230},[1213,4033,1299],{"class":1234},[1213,4035,1302],{"class":1230},[1204,4037,4040],{"className":4038,"code":4039,"language":2134,"meta":1209,"style":1209},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CEditorSurface v-model=\"content\" :preset=\"preset\" @ready=\"editor = $event\" \u002F>\n",[432,4041,4042],{"__ignoreMap":1209},[1213,4043,4044,4047,4049,4052,4054,4057,4059,4061,4064,4066,4068,4070,4072,4074,4077,4080,4082,4084,4087,4089,4092,4094],{"class":1215,"line":1216},[1213,4045,4046],{"class":1234},"\u003C",[1213,4048,2430],{"class":1259},[1213,4050,4051],{"class":1226}," v-model",[1213,4053,1235],{"class":1234},[1213,4055,4056],{"class":1234},"\"",[1213,4058,518],{"class":1230},[1213,4060,4056],{"class":1234},[1213,4062,4063],{"class":1234}," :",[1213,4065,3422],{"class":1226},[1213,4067,1235],{"class":1234},[1213,4069,4056],{"class":1234},[1213,4071,3422],{"class":1230},[1213,4073,4056],{"class":1234},[1213,4075,4076],{"class":1234}," @",[1213,4078,4079],{"class":1226},"ready",[1213,4081,1235],{"class":1234},[1213,4083,4056],{"class":1234},[1213,4085,4086],{"class":1230},"editor ",[1213,4088,1235],{"class":1234},[1213,4090,4091],{"class":1230}," $event",[1213,4093,4056],{"class":1234},[1213,4095,4096],{"class":1234}," \u002F>\n",[423,4098,4099,4101,4102,4104,4105,459,4108,459,4111,459,4114,459,4117,459,4120,4123,4124,4127,4128,4131,4132,1308,4134,459,4136,1308,4138,4141,4142,459,4144,459,4146,4148,4149,4151,4152,4155],{},[432,4100,2753],{}," keeps the given order (toolbar groups, menu entries, schema) and throws on a duplicate id, on a ",[432,4103,637],{}," entry that is not in the list and on a second contributor of a singleton hook (",[432,4106,4107],{},"parseMarkdown",[432,4109,4110],{},"segment",[432,4112,4113],{},"structure",[432,4115,4116],{},"migrateContent",[432,4118,4119],{},"anchorResolver",[432,4121,4122],{},"formatAiResult","). It installs nothing for you. A Vue list takes the factories from ",[432,4125,4126],{},"\u002F\u003Cx>\u002Fvue",", a React list those from ",[432,4129,4130],{},"\u002F\u003Cx>\u002Freact","; mixing them is a type error (",[432,4133,2915],{},[432,4135,2918],{},[432,4137,2921],{},[432,4139,4140],{},"EditorFeature\u003CComponentType\u003C{ editor }>>","), while features without components (",[432,4143,2353],{},[432,4145,2356],{},[432,4147,2359],{},", the integrations) fit both. A host that brings its own UI kit calls ",[432,4150,2096],{}," with its own components (",[687,4153,4154],{"href":3093},"per-feature entries","). When you use feature entries directly in a plain app, also import that feature's stylesheet. Per-feature usage: the other guides of this section; composition and entries: this page.",[4157,4158,4159],"style",{},"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 pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1209,"searchDepth":1223,"depth":1223,"links":4161},[4162,4163,4164,4165,4166,4167,4168],{"id":420,"depth":1223,"text":421},{"id":1007,"depth":1223,"text":1008},{"id":1331,"depth":1223,"text":1332},{"id":2064,"depth":1223,"text":2065},{"id":2581,"depth":1223,"text":2582},{"id":3098,"depth":1223,"text":3099},{"id":3324,"depth":1223,"text":3325},"Compose an editor from features and presets, pick the right entries, and choose the integration depth from no data layer to a Nuxt server.","md",null,{},{"icon":58},{"title":90,"description":4169},"IL6YyTuhoimL97sDG0xMWxL1GbG-bIZfUeOEdaHLkyk",[4177,4179],{"title":80,"path":81,"stem":82,"description":4178,"icon":83,"children":-1},"Run the meeting-notes editor on React: EditorContextProvider, EditorSurface, EditorToolbar, SimpleEditor and the Toaster, and where the callout would go.",{"title":94,"path":95,"stem":96,"description":4180,"icon":97,"children":-1},"Add image, audio, video and file blocks, @ mentions and # tags, and speaker transcripts as independent features, with uploads, metadata lists and server schemas.",1793213899182]