[{"data":1,"prerenderedAt":2458},["ShallowReactive",2],{"navigation_docs":3,"-guides-pruefung":412,"-guides-pruefung-surround":2453},[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":185,"body":414,"description":2446,"extension":2447,"links":2448,"meta":2449,"navigation":2450,"path":186,"seo":2451,"stem":187,"__hash__":2452},"docs\u002F3.guides\u002F28.pruefung.md",{"type":415,"value":416,"toc":2434},"minimark",[417,451,458,564,587,592,626,711,724,738,1239,1252,1256,1263,1337,1341,1359,1363,1366,1423,1429,1544,1555,1559,1575,1613,1636,1639,1680,1696,1701,1716,1844,1863,1872,1874,1886,1904,1908,1963,1967,1976,2069,2103,2384,2430],[418,419,420,421,425,426,429,430,434,435,438,439,442,443,446,447,450],"p",{},"A ",[422,423,424],"strong",{},"Prüfung"," is one immutable record that a user checked one Typed block at one content state, with a ",[422,427,428],{},"kind"," such as ",[431,432,433],"code",{},"redaktionell"," or ",[431,436,437],{},"fachlich",". The record stores the block ID, the SHA-256 hash of the checked text, the kind, who recorded it (",[431,440,441],{},"by","), when (",[431,444,445],{},"at","), an optional note and the checked text itself (",[431,448,449],{},"snapshot","). A Prüfung stays valid while the hash of its block is unchanged. When the block changes, the hash changes, and the old record no longer matches.",[418,452,453,454,457],{},"The ",[422,455,456],{},"Freigabestatus"," is derived from the records and the current hashes. It is never stored:",[459,460,461,477],"table",{},[462,463,464],"thead",{},[465,466,467,471,474],"tr",{},[468,469,470],"th",{},"Level",[468,472,473],{},"Status",[468,475,476],{},"Meaning",[478,479,480,494,506,521,533,553],"tbody",{},[465,481,482,486,491],{},[483,484,485],"td",{},"Block, per kind",[483,487,488],{},[431,489,490],{},"geprueft",[483,492,493],{},"a record of this kind exists at the current hash",[465,495,496,498,503],{},[483,497],{},[483,499,500],{},[431,501,502],{},"veraltet",[483,504,505],{},"records of this kind exist, none at the current hash",[465,507,508,510,515],{},[483,509],{},[483,511,512],{},[431,513,514],{},"vorgeschlagen",[483,516,517,518],{},"no record of this kind, and the block is an AI proposal (a Derivation by the model, body not edited since); see ",[519,520,234],"a",{"href":235},[465,522,523,525,530],{},[483,524],{},[483,526,527],{},[431,528,529],{},"offen",[483,531,532],{},"no record of this kind",[465,534,535,538,543],{},[483,536,537],{},"Document",[483,539,540],{},[431,541,542],{},"freigegeben",[483,544,545,546,549,550,552],{},"every Typed block whose Block type lists the ",[431,547,548],{},"pruefung"," capability is ",[431,551,490],{}," for all its kinds",[465,554,555,557,561],{},[483,556],{},[483,558,559],{},[431,560,529],{},[483,562,563],{},"at least one such block is not",[418,565,566,567,570,571,577,578,583,584,586],{},"A Prüfung is not a Block property. A property such as ",[431,568,569],{},"status"," is part of the document and any user who may edit the Block can write it. A Prüfung lives on the server outside the document, is written through its own route after a check of the user's right, and is pinned to the content (",[519,572,576],{"href":573,"rel":574},"https:\u002F\u002Fgithub.com\u002Fskaile-ai\u002Fforge-editor\u002Fblob\u002Fmain\u002Fdocs\u002Fadr\u002F0002-pruefung-records-outside-the-document.md",[575],"nofollow","ADR-0002","). Records key on block IDs, which live in the database; the stored document is canonical and Markdown is an import\u002Fexport format (",[519,579,582],{"href":580,"rel":581},"https:\u002F\u002Fgithub.com\u002Fskaile-ai\u002Fforge-editor\u002Fblob\u002Fmain\u002Fdocs\u002Fadr\u002F0001-database-canonical-markdown-export.md",[575],"ADR-0001","). A block that is copied or imported from Markdown gets a new ID and starts ",[431,585,529],{},".",[588,589,591],"h2",{"id":590},"enable-it","Enable it",[418,593,453,594,596,597,600,601,604,605,608,609,612,613,616,617,619,620,622,623,586],{},[431,595,548],{}," Feature is in no level list of the presets. A host that composes with ",[431,598,599],{},"composeEditor"," opts in by id, next to the framework binding of the preset: ",[431,602,603],{},"interaction: [...INTERACTION.full, 'pruefung']",". A host that lists features itself adds ",[431,606,607],{},"pruefungFeature()",". The Feature ",[431,610,611],{},"requires"," the ",[431,614,615],{},"composites"," Feature and the backend capability ",[431,618,548],{},"; a surface over a backend without that capability throws, and ",[431,621,599],{}," reports the feature in ",[431,624,625],{},"omitted",[459,627,628,638],{},[462,629,630],{},[465,631,632,635],{},[468,633,634],{},"Entry",[468,636,637],{},"Content",[478,639,640,682,692],{},[465,641,642,647],{},[483,643,644],{},[431,645,646],{},"@skaile\u002Fforge-editor\u002Fpruefung",[483,648,649,650,653,654,653,657,653,660,653,663,653,666,653,669,653,672,653,675,653,678,681],{},"framework-free: ",[431,651,652],{},"pruefungFeature",", ",[431,655,656],{},"pruefungFeatureCore",[431,658,659],{},"pruefungCapability",[431,661,662],{},"pruefungHash",[431,664,665],{},"pruefungMarkdown",[431,667,668],{},"isBlockLocked",[431,670,671],{},"UNLOCK_KIND",[431,673,674],{},"createPruefungModel",[431,676,677],{},"createRestPruefungModel",[431,679,680],{},"pruefungModelOf"," and the types",[465,683,684,689],{},[483,685,686],{},[431,687,688],{},"@skaile\u002Fforge-editor\u002Fpruefung\u002Fschema",[483,690,691],{},"Vue-free, server-importable: the record, route and store types and the capability config schema",[465,693,694,702],{},[483,695,696,653,699],{},[431,697,698],{},"@skaile\u002Fforge-editor\u002Fpruefung\u002Fvue",[431,700,701],{},"@skaile\u002Fforge-editor\u002Fpruefung\u002Freact",[483,703,704,706,707,710],{},[431,705,652],{}," with the component of the Prüfstand section (",[431,708,709],{},"PruefungBadge",")",[418,712,713,714,716,717,434,720,723],{},"The chips (Block, document) need no component: they come from the framework-free Feature. Only the inspector section is drawn by the binding, so import ",[431,715,652],{}," from the ",[431,718,719],{},"\u002Fvue",[431,721,722],{},"\u002Freact"," entry to get it.",[418,725,726,727,729,730,733,734,737],{},"A Block type opts in by listing the ",[431,728,548],{}," capability in ",[431,731,732],{},"capabilities",". The example gives the Block type ",[431,735,736],{},"contract"," two kinds, and adds the Feature to a preset:",[739,740,746],"pre",{"className":741,"code":742,"filename":743,"language":744,"meta":745,"style":745},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { compositeFeature } from '@skaile\u002Fforge-editor\u002Fcomposites\u002Fvue'\nimport { type BlockType, defaultBlockTypes } from '@skaile\u002Fforge-editor\u002Fcomposites\u002Fschema'\nimport { coreContentFeature, coreUiFeature, createTranslator, defineEditor } from '@skaile\u002Fforge-editor\u002Fcore'\nimport { pruefungFeature } from '@skaile\u002Fforge-editor\u002Fpruefung'\n\n\u002F\u002F A Block type opts into the capability and configures it.\nconst contract: BlockType = {\n  id: 'contract',\n  label: 'Vertrag',\n  icon: 'i-lucide-file-signature',\n  capabilities: [\n    { id: 'container', config: { accepts: 'all' } },\n    { id: 'pruefung', config: { kinds: ['redaktionell', 'fachlich'] } },\n  ],\n}\n\nconst blockTypes = [...defaultBlockTypes(createTranslator('de')), contract]\n\n\u002F\u002F A Block type that lists a capability no Feature declares throws when the editor is created; so does a block action that\n\u002F\u002F `requires` an undeclared id, when the actions are collected.\nexport const preset = defineEditor({\n  features: [\n    coreContentFeature(),\n    coreUiFeature(),\n    compositeFeature({ blockTypes }),\n    pruefungFeature(),\n  ],\n})\n","docs\u002Fsnippets\u002Fpruefung-preset.ts","ts","",[431,747,748,781,811,847,867,874,881,903,922,939,956,967,1011,1064,1072,1078,1083,1126,1131,1137,1143,1164,1174,1185,1195,1214,1224,1231],{"__ignoreMap":745},[749,750,753,757,761,765,768,771,774,778],"span",{"class":751,"line":752},"line",1,[749,754,756],{"class":755},"s7zQu","import",[749,758,760],{"class":759},"sMK4o"," {",[749,762,764],{"class":763},"sTEyZ"," compositeFeature",[749,766,767],{"class":759}," }",[749,769,770],{"class":755}," from",[749,772,773],{"class":759}," '",[749,775,777],{"class":776},"sfazB","@skaile\u002Fforge-editor\u002Fcomposites\u002Fvue",[749,779,780],{"class":759},"'\n",[749,782,784,786,788,791,794,797,800,802,804,806,809],{"class":751,"line":783},2,[749,785,756],{"class":755},[749,787,760],{"class":759},[749,789,790],{"class":755}," type",[749,792,793],{"class":763}," BlockType",[749,795,796],{"class":759},",",[749,798,799],{"class":763}," defaultBlockTypes",[749,801,767],{"class":759},[749,803,770],{"class":755},[749,805,773],{"class":759},[749,807,808],{"class":776},"@skaile\u002Fforge-editor\u002Fcomposites\u002Fschema",[749,810,780],{"class":759},[749,812,814,816,818,821,823,826,828,831,833,836,838,840,842,845],{"class":751,"line":813},3,[749,815,756],{"class":755},[749,817,760],{"class":759},[749,819,820],{"class":763}," coreContentFeature",[749,822,796],{"class":759},[749,824,825],{"class":763}," coreUiFeature",[749,827,796],{"class":759},[749,829,830],{"class":763}," createTranslator",[749,832,796],{"class":759},[749,834,835],{"class":763}," defineEditor",[749,837,767],{"class":759},[749,839,770],{"class":755},[749,841,773],{"class":759},[749,843,844],{"class":776},"@skaile\u002Fforge-editor\u002Fcore",[749,846,780],{"class":759},[749,848,850,852,854,857,859,861,863,865],{"class":751,"line":849},4,[749,851,756],{"class":755},[749,853,760],{"class":759},[749,855,856],{"class":763}," pruefungFeature",[749,858,767],{"class":759},[749,860,770],{"class":755},[749,862,773],{"class":759},[749,864,646],{"class":776},[749,866,780],{"class":759},[749,868,870],{"class":751,"line":869},5,[749,871,873],{"emptyLinePlaceholder":872},true,"\n",[749,875,877],{"class":751,"line":876},6,[749,878,880],{"class":879},"sHwdD","\u002F\u002F A Block type opts into the capability and configures it.\n",[749,882,884,888,891,894,897,900],{"class":751,"line":883},7,[749,885,887],{"class":886},"spNyl","const",[749,889,890],{"class":763}," contract",[749,892,893],{"class":759},":",[749,895,793],{"class":896},"sBMFI",[749,898,899],{"class":759}," =",[749,901,902],{"class":759}," {\n",[749,904,906,910,912,914,916,919],{"class":751,"line":905},8,[749,907,909],{"class":908},"swJcz","  id",[749,911,893],{"class":759},[749,913,773],{"class":759},[749,915,736],{"class":776},[749,917,918],{"class":759},"'",[749,920,921],{"class":759},",\n",[749,923,925,928,930,932,935,937],{"class":751,"line":924},9,[749,926,927],{"class":908},"  label",[749,929,893],{"class":759},[749,931,773],{"class":759},[749,933,934],{"class":776},"Vertrag",[749,936,918],{"class":759},[749,938,921],{"class":759},[749,940,942,945,947,949,952,954],{"class":751,"line":941},10,[749,943,944],{"class":908},"  icon",[749,946,893],{"class":759},[749,948,773],{"class":759},[749,950,951],{"class":776},"i-lucide-file-signature",[749,953,918],{"class":759},[749,955,921],{"class":759},[749,957,959,962,964],{"class":751,"line":958},11,[749,960,961],{"class":908},"  capabilities",[749,963,893],{"class":759},[749,965,966],{"class":763}," [\n",[749,968,970,973,976,978,980,983,985,987,990,992,994,997,999,1001,1004,1006,1008],{"class":751,"line":969},12,[749,971,972],{"class":759},"    {",[749,974,975],{"class":908}," id",[749,977,893],{"class":759},[749,979,773],{"class":759},[749,981,982],{"class":776},"container",[749,984,918],{"class":759},[749,986,796],{"class":759},[749,988,989],{"class":908}," config",[749,991,893],{"class":759},[749,993,760],{"class":759},[749,995,996],{"class":908}," accepts",[749,998,893],{"class":759},[749,1000,773],{"class":759},[749,1002,1003],{"class":776},"all",[749,1005,918],{"class":759},[749,1007,767],{"class":759},[749,1009,1010],{"class":759}," },\n",[749,1012,1014,1016,1018,1020,1022,1024,1026,1028,1030,1032,1034,1037,1039,1042,1044,1046,1048,1050,1052,1054,1056,1059,1062],{"class":751,"line":1013},13,[749,1015,972],{"class":759},[749,1017,975],{"class":908},[749,1019,893],{"class":759},[749,1021,773],{"class":759},[749,1023,548],{"class":776},[749,1025,918],{"class":759},[749,1027,796],{"class":759},[749,1029,989],{"class":908},[749,1031,893],{"class":759},[749,1033,760],{"class":759},[749,1035,1036],{"class":908}," kinds",[749,1038,893],{"class":759},[749,1040,1041],{"class":763}," [",[749,1043,918],{"class":759},[749,1045,433],{"class":776},[749,1047,918],{"class":759},[749,1049,796],{"class":759},[749,1051,773],{"class":759},[749,1053,437],{"class":776},[749,1055,918],{"class":759},[749,1057,1058],{"class":763},"] ",[749,1060,1061],{"class":759},"}",[749,1063,1010],{"class":759},[749,1065,1067,1070],{"class":751,"line":1066},14,[749,1068,1069],{"class":763},"  ]",[749,1071,921],{"class":759},[749,1073,1075],{"class":751,"line":1074},15,[749,1076,1077],{"class":759},"}\n",[749,1079,1081],{"class":751,"line":1080},16,[749,1082,873],{"emptyLinePlaceholder":872},[749,1084,1086,1088,1091,1094,1096,1099,1103,1106,1109,1111,1113,1116,1118,1121,1123],{"class":751,"line":1085},17,[749,1087,887],{"class":886},[749,1089,1090],{"class":763}," blockTypes ",[749,1092,1093],{"class":759},"=",[749,1095,1041],{"class":763},[749,1097,1098],{"class":759},"...",[749,1100,1102],{"class":1101},"s2Zo4","defaultBlockTypes",[749,1104,1105],{"class":763},"(",[749,1107,1108],{"class":1101},"createTranslator",[749,1110,1105],{"class":763},[749,1112,918],{"class":759},[749,1114,1115],{"class":776},"de",[749,1117,918],{"class":759},[749,1119,1120],{"class":763},"))",[749,1122,796],{"class":759},[749,1124,1125],{"class":763}," contract]\n",[749,1127,1129],{"class":751,"line":1128},18,[749,1130,873],{"emptyLinePlaceholder":872},[749,1132,1134],{"class":751,"line":1133},19,[749,1135,1136],{"class":879},"\u002F\u002F A Block type that lists a capability no Feature declares throws when the editor is created; so does a block action that\n",[749,1138,1140],{"class":751,"line":1139},20,[749,1141,1142],{"class":879},"\u002F\u002F `requires` an undeclared id, when the actions are collected.\n",[749,1144,1146,1149,1152,1155,1157,1159,1161],{"class":751,"line":1145},21,[749,1147,1148],{"class":755},"export",[749,1150,1151],{"class":886}," const",[749,1153,1154],{"class":763}," preset ",[749,1156,1093],{"class":759},[749,1158,835],{"class":1101},[749,1160,1105],{"class":763},[749,1162,1163],{"class":759},"{\n",[749,1165,1167,1170,1172],{"class":751,"line":1166},22,[749,1168,1169],{"class":908},"  features",[749,1171,893],{"class":759},[749,1173,966],{"class":763},[749,1175,1177,1180,1183],{"class":751,"line":1176},23,[749,1178,1179],{"class":1101},"    coreContentFeature",[749,1181,1182],{"class":763},"()",[749,1184,921],{"class":759},[749,1186,1188,1191,1193],{"class":751,"line":1187},24,[749,1189,1190],{"class":1101},"    coreUiFeature",[749,1192,1182],{"class":763},[749,1194,921],{"class":759},[749,1196,1198,1201,1203,1206,1208,1210,1212],{"class":751,"line":1197},25,[749,1199,1200],{"class":1101},"    compositeFeature",[749,1202,1105],{"class":763},[749,1204,1205],{"class":759},"{",[749,1207,1090],{"class":763},[749,1209,1061],{"class":759},[749,1211,710],{"class":763},[749,1213,921],{"class":759},[749,1215,1217,1220,1222],{"class":751,"line":1216},26,[749,1218,1219],{"class":1101},"    pruefungFeature",[749,1221,1182],{"class":763},[749,1223,921],{"class":759},[749,1225,1227,1229],{"class":751,"line":1226},27,[749,1228,1069],{"class":763},[749,1230,921],{"class":759},[749,1232,1234,1236],{"class":751,"line":1233},28,[749,1235,1061],{"class":759},[749,1237,1238],{"class":763},")\n",[418,1240,1241,1242,1245,1246,1248,1249,1251],{},"Block capabilities in general are described in ",[519,1243,1244],{"href":161},"Block capabilities","; the shape of ",[431,1247,659],{}," is the worked example there. A Block type that does not list ",[431,1250,548],{}," has no Prüfung: no action, no chip, no section.",[588,1253,1255],{"id":1254},"the-capability-config","The capability config",[418,1257,1258,1259,1262],{},"The config is validated when the editor is created (",[431,1260,1261],{},"z.strictObject",": an unknown key throws).",[459,1264,1265,1277],{},[462,1266,1267],{},[465,1268,1269,1272,1275],{},[468,1270,1271],{},"Key",[468,1273,1274],{},"Type",[468,1276,476],{},[478,1278,1279,1295,1314],{},[465,1280,1281,1286,1292],{},[483,1282,1283],{},[431,1284,1285],{},"kinds",[483,1287,1288,1291],{},[431,1289,1290],{},"string[]",", at least one",[483,1293,1294],{},"the kinds a block of this type takes, in order",[465,1296,1297,1302,1311],{},[483,1298,1299],{},[431,1300,1301],{},"chain",[483,1303,1304,1307,1308],{},[431,1305,1306],{},"boolean",", default ",[431,1309,1310],{},"false",[483,1312,1313],{},"a kind can be recorded only when every kind before it has a record at the block's current hash",[465,1315,1316,1321,1327],{},[483,1317,1318],{},[431,1319,1320],{},"lock",[483,1322,1323,1326],{},[431,1324,1325],{},"string",", optional",[483,1328,1329,1330,1332,1333,710],{},"the kind whose Prüfung locks the block against edits while it is ",[431,1331,490],{}," (",[519,1334,1336],{"href":1335},"#lock","Lock",[588,1338,1340],{"id":1339},"what-the-hash-covers","What the hash covers",[418,1342,1343,1344,653,1346,1348,1349,1351,1352,1354,1355,1358],{},"The hash is the SHA-256 (hex) of the title line, a blank line and the body Markdown of the block (",[431,1345,665],{},[431,1347,662],{},"). The same text is the ",[431,1350,449],{}," of the record. A changed title or body makes the Prüfung ",[431,1353,502],{},". These are not covered, so changing them leaves a Prüfung valid: ",[431,1356,1357],{},"meta"," (Block properties, Derivation), tags, description, Baskets and the title visibility.",[588,1360,1362],{"id":1361},"who-may-record","Who may record",[418,1364,1365],{},"Recording needs two things.",[1367,1368,1369,1394],"ul",{},[1370,1371,1372,1332,1378,1381,1382,1385,1386,1389,1390,1393],"li",{},[422,1373,1374,1375],{},"The ability ",[431,1376,1377],{},"pruefung.record",[431,1379,1380],{},"ABILITY.pruefungRecord",", from ",[431,1383,1384],{},"@skaile\u002Fforge-editor\u002Fcore\u002Fschema","). No ability list contains it by default. A host grants it per mount through the ",[431,1387,1388],{},"abilities"," prop, for example ",[431,1391,1392],{},"{ root: [...ROOT_ABILITIES, ABILITY.pruefungRecord] }",". Without it the block actions „Prüfung eintragen“ and „Sperre aufheben“ do not show.",[1370,1395,1396,1399,1400,1403,1404,1407,1408,1410,1411,1414,1415,1418,1419,1422],{},[422,1397,1398],{},"The right of the user on the server."," ",[431,1401,1402],{},"GET"," and ",[431,1405,1406],{},"POST \u002Fapi\u002Feditor\u002Fpruefung\u002F:id"," answer ",[431,1409,1285],{},", the kinds that the requesting user may record. A read-only user gets ",[431,1412,1413],{},"[]",". Otherwise ",[431,1416,1417],{},"AccessControl.pruefungKinds(user, id)"," decides; when the host does not define it, every user who may edit gets the sentinel ",[431,1420,1421],{},"['*']",", which the client reads as every configured kind.",[418,1424,1425,1428],{},[431,1426,1427],{},"AccessControl.pruefungKinds"," returns the kind names that the user may record in that document:",[739,1430,1432],{"className":741,"code":1431,"language":744,"meta":745,"style":745},"const identity: AccessControl = {\n  \u002F\u002F ...authenticate, authorize\n  async pruefungKinds(user, id) {\n    return isEditorialStaff(user) ? ['redaktionell'] : ['redaktionell', 'fachlich', 'entsperrt']\n  },\n}\n",[431,1433,1434,1450,1455,1477,1535,1540],{"__ignoreMap":745},[749,1435,1436,1438,1441,1443,1446,1448],{"class":751,"line":752},[749,1437,887],{"class":886},[749,1439,1440],{"class":763}," identity",[749,1442,893],{"class":759},[749,1444,1445],{"class":896}," AccessControl",[749,1447,899],{"class":759},[749,1449,902],{"class":759},[749,1451,1452],{"class":751,"line":783},[749,1453,1454],{"class":879},"  \u002F\u002F ...authenticate, authorize\n",[749,1456,1457,1460,1463,1465,1469,1471,1473,1475],{"class":751,"line":813},[749,1458,1459],{"class":886},"  async",[749,1461,1462],{"class":908}," pruefungKinds",[749,1464,1105],{"class":759},[749,1466,1468],{"class":1467},"sHdIc","user",[749,1470,796],{"class":759},[749,1472,975],{"class":1467},[749,1474,710],{"class":759},[749,1476,902],{"class":759},[749,1478,1479,1482,1485,1487,1489,1492,1495,1497,1499,1501,1503,1505,1507,1509,1511,1513,1515,1517,1519,1521,1523,1525,1527,1530,1532],{"class":751,"line":849},[749,1480,1481],{"class":755},"    return",[749,1483,1484],{"class":1101}," isEditorialStaff",[749,1486,1105],{"class":908},[749,1488,1468],{"class":763},[749,1490,1491],{"class":908},") ",[749,1493,1494],{"class":759},"?",[749,1496,1041],{"class":908},[749,1498,918],{"class":759},[749,1500,433],{"class":776},[749,1502,918],{"class":759},[749,1504,1058],{"class":908},[749,1506,893],{"class":759},[749,1508,1041],{"class":908},[749,1510,918],{"class":759},[749,1512,433],{"class":776},[749,1514,918],{"class":759},[749,1516,796],{"class":759},[749,1518,773],{"class":759},[749,1520,437],{"class":776},[749,1522,918],{"class":759},[749,1524,796],{"class":759},[749,1526,773],{"class":759},[749,1528,1529],{"class":776},"entsperrt",[749,1531,918],{"class":759},[749,1533,1534],{"class":908},"]\n",[749,1536,1537],{"class":751,"line":869},[749,1538,1539],{"class":759},"  },\n",[749,1541,1542],{"class":751,"line":876},[749,1543,1077],{"class":759},[418,1545,1546,1547,1332,1549,1551,1552,1554],{},"A list that names kinds explicitly applies exactly. A user who may lift a lock needs ",[431,1548,1529],{},[431,1550,671],{},") in it, because „Sperre aufheben“ records that kind. A client shows an action only for a kind in ",[431,1553,1285],{},"; the server enforces it again.",[588,1556,1558],{"id":1557},"the-server-port","The server port",[418,1560,1561,1562,1564,1565,1568,1569,1332,1572,1574],{},"The route group needs the port ",[431,1563,548],{}," of ",[431,1566,1567],{},"EditorPorts",", a ",[431,1570,1571],{},"PruefungStore",[431,1573,688],{},"):",[459,1576,1577,1587],{},[462,1578,1579],{},[465,1580,1581,1584],{},[468,1582,1583],{},"Method",[468,1585,1586],{},"Does",[478,1588,1589,1599],{},[465,1590,1591,1596],{},[483,1592,1593],{},[431,1594,1595],{},"list(documentId)",[483,1597,1598],{},"the records of one document, oldest first; empty for an unknown document",[465,1600,1601,1606],{},[483,1602,1603],{},[431,1604,1605],{},"record(entry)",[483,1607,1608,1609,1612],{},"stores ",[431,1610,1611],{},"entry"," under a new ID and returns the record",[418,1614,1615,1616,1618,1619,1332,1622,1625,1626,1629,1630,1632,1633,586],{},"Records are never changed or deleted: a later check is a new record. The port provides the backend capability ",[431,1617,548],{},"; without it the server mounts no Prüfung route and the Feature is not available. ",[431,1620,1621],{},"createJsonAdapter",[431,1623,1624],{},"@skaile\u002Fforge-editor\u002Fadapters\u002Fjson",") wires a JSON store that keeps the records of all documents in one file, ",[431,1627,1628],{},".pruefung.json",", in the adapter's data directory (a dot-named file, so it is not read as a document). A host with its own database implements ",[431,1631,1571],{}," and passes it as ",[431,1634,1635],{},"ports.pruefung",[418,1637,1638],{},"The routes:",[459,1640,1641,1650],{},[462,1642,1643],{},[465,1644,1645,1648],{},[468,1646,1647],{},"Route",[468,1649,1586],{},[478,1651,1652,1665],{},[465,1653,1654,1659],{},[483,1655,1656],{},[431,1657,1658],{},"GET \u002Fapi\u002Feditor\u002Fpruefung\u002F:id",[483,1660,1661,1662],{},"answers ",[431,1663,1664],{},"{ records, kinds }",[465,1666,1667,1671],{},[483,1668,1669],{},[431,1670,1406],{},[483,1672,1673,1674,1677,1678],{},"body ",[431,1675,1676],{},"{ blockId, hash, kind, note?, snapshot, kinds, chain }","; records one Prüfung and answers ",[431,1679,1664],{},[418,1681,1682,1403,1684,1686,1687,1689,1690,1693,1694,586],{},[431,1683,1285],{},[431,1685,1301],{}," in the body echo the capability config. The server uses only their order to apply ",[431,1688,1301],{},"; it never takes a right from them. It computes the stored ",[431,1691,1692],{},"hash"," itself, from ",[431,1695,449],{},[1697,1698,1700],"h3",{"id":1699},"refusals","Refusals",[418,1702,1703,1704,1707,1708,1711,1712,1715],{},"A refused ",[431,1705,1706],{},"POST"," answers an error whose data is ",[431,1709,1710],{},"{ reason }",". A client switches on ",[431,1713,1714],{},"reason",", not on the message. The checks run in this order:",[459,1717,1718,1734],{},[462,1719,1720],{},[465,1721,1722,1725,1728,1731],{},[468,1723,1724],{},"Reason",[468,1726,1727],{},"HTTP",[468,1729,1730],{},"Message",[468,1732,1733],{},"When",[478,1735,1736,1760,1778,1801,1818],{},[465,1737,1738,1741,1744,1749],{},[483,1739,1740],{},"(none)",[483,1742,1743],{},"400",[483,1745,1746],{},[431,1747,1748],{},"Ungültiger JSON-Body",[483,1750,1751,1752,653,1755,434,1757,1759],{},"the body is not JSON or a member has the wrong type, or ",[431,1753,1754],{},"blockId",[431,1756,1692],{},[431,1758,428],{}," is empty",[465,1761,1762,1767,1770,1775],{},[483,1763,1764],{},[431,1765,1766],{},"role",[483,1768,1769],{},"403",[483,1771,1772],{},[431,1773,1774],{},"Keine Berechtigung für diese Prüfung",[483,1776,1777],{},"the user is read-only, or may not record this kind",[465,1779,1780,1785,1788,1793],{},[483,1781,1782],{},[431,1783,1784],{},"stale",[483,1786,1787],{},"409",[483,1789,1790],{},[431,1791,1792],{},"Der Block hat sich seit der Prüfung geändert",[483,1794,1795,1796,1798,1799],{},"the SHA-256 of ",[431,1797,449],{}," is not ",[431,1800,1692],{},[465,1802,1803,1808,1810,1815],{},[483,1804,1805],{},[431,1806,1807],{},"duplicate",[483,1809,1787],{},[483,1811,1812],{},[431,1813,1814],{},"Diese Prüfung liegt für diesen Stand bereits vor",[483,1816,1817],{},"the block already has a record of this kind at this hash",[465,1819,1820,1825,1827,1832],{},[483,1821,1822],{},[431,1823,1824],{},"needs-\u003Ckind>",[483,1826,1787],{},[483,1828,1829],{},[431,1830,1831],{},"Zuerst ist die Prüfung „\u003Ckind>“ nötig",[483,1833,1834,1836,1837,1840,1841,1843],{},[431,1835,1301],{}," is on and ",[431,1838,1839],{},"\u003Ckind>",", an earlier kind in ",[431,1842,1285],{},", has no record at this hash",[418,1845,1846,1847,1850,1851,1853,1854,1403,1856,1858,1859,1862],{},"The REST model shows the message of a refused ",[431,1848,1849],{},"record"," as a warning notice. ",[431,1852,1784],{}," is weaker than its name: the server has no Markdown serializer for a block, so it cannot rebuild the current text from the document. It proves only that ",[431,1855,449],{},[431,1857,1692],{}," belong together. The client builds both from the live block, so a block that changed between the click and the answer is the client's case (",[519,1860,576],{"href":573,"rel":1861},[575],", Consequences).",[418,1864,1865,1866,1868,1869,1871],{},"A kind that is not in ",[431,1867,1285],{}," (the unlock record ",[431,1870,1529],{},") is not part of the chain.",[588,1873,1336],{"id":1320},[418,1875,1876,1879,1880,1882,1883,1885],{},[431,1877,1878],{},"lock: '\u003Ckind>'"," in the capability config locks a block while that kind is ",[431,1881,490],{}," at the block's current hash. The lock is a transaction guard, not a stored flag. A local edit inside a locked block is dropped whole and the editor shows the notice „Block ist freigegeben und gesperrt“. The guard hashes only the blocks that a transaction touches and only those whose Block type sets ",[431,1884,1320],{},". Transactions that carry what another user wrote (Yjs sync) pass, because refusing them would make this client differ from the shared document.",[418,1887,1888,1889,1891,1892,1894,1895,1897,1898,1900,1901,1903],{},"The block action „Sperre aufheben“ (ability ",[431,1890,1377],{},"; visible only on a locked block, for a user whose ",[431,1893,1285],{}," include ",[431,1896,1529],{},") records a Prüfung of kind ",[431,1899,1529],{}," at the current hash. A block with such a record at its current hash is not locked, and the history of who lifted the lock stays in the records. After the next edit the hash changes, the kind is ",[431,1902,502],{}," and the lock is off until someone records the kind again. Any change of the hash lifts the lock, including a collaborator's edit or an import.",[588,1905,1907],{"id":1906},"what-the-user-sees","What the user sees",[459,1909,1910,1920],{},[462,1911,1912],{},[465,1913,1914,1917],{},[468,1915,1916],{},"Where",[468,1918,1919],{},"What",[478,1921,1922,1933,1947,1955],{},[465,1923,1924,1927],{},[483,1925,1926],{},"Block action „Prüfung eintragen“",[483,1928,1929,1930,1932],{},"with one recordable kind, a click records it; with several, a menu lists the kinds and shows „Für diesen Stand geprüft“ for kinds that are already ",[431,1931,490],{},". Kinds that the user may not record are not listed",[465,1934,1935,1938],{},[483,1936,1937],{},"Chip after the title of a block",[483,1939,1940,1941,1943,1944,1946],{},"„Geprüft“ (success) when every kind is ",[431,1942,490],{},"; „Prüfung veraltet“ (warning) when a kind is ",[431,1945,502],{},"; nothing otherwise",[465,1948,1949,1952],{},[483,1950,1951],{},"Inspector and details card, section „Prüfstand“",[483,1953,1954],{},"the kinds with their status, the records (kind, user, date, note), and „Geprüften Stand anzeigen“, which shows the checked text and the current text in two read-only columns, without a diff",[465,1956,1957,1960],{},[483,1958,1959],{},"„Dokument“ header of the inspector, and the open document's row in the Block outline of the Document tree",[483,1961,1962],{},"the Freigabestatus: „Freigegeben“, or „Offen (n Blöcke)“; nothing while no block opts in",[588,1964,1966],{"id":1965},"add-your-own-chips-and-sections","Add your own chips and sections",[418,1968,1969,1970,1332,1973,1975],{},"The composites Feature draws the chips and sections of other Features and knows nothing about Prüfung. Three optional members of ",[431,1971,1972],{},"EditorFeature",[431,1974,844],{},") carry them; all three are additive in Feature order and the factory runs once per surface:",[459,1977,1978,1991],{},[462,1979,1980],{},[465,1981,1982,1985,1988],{},[468,1983,1984],{},"Member",[468,1986,1987],{},"Contribution",[468,1989,1990],{},"Drawn",[478,1992,1993,2027,2047],{},[465,1994,1995,2000,2024],{},[483,1996,1997],{},[431,1998,1999],{},"rowBadges(ctx)",[483,2001,2002,2005,2006,2009,2010,2013,2014,1332,2017,2020,2021],{},[431,2003,2004],{},"RowBadge[]",": ",[431,2007,2008],{},"{ id, of(editor, row), subscribe? }","; ",[431,2011,2012],{},"of"," returns a ",[431,2015,2016],{},"BadgeChip",[431,2018,2019],{},"{ label, tone: 'success' | 'warning' }",", label already translated) or ",[431,2022,2023],{},"null",[483,2025,2026],{},"after the title of a Typed block",[465,2028,2029,2034,2044],{},[483,2030,2031],{},[431,2032,2033],{},"documentBadges(ctx)",[483,2035,2036,2039,2040,2043],{},[431,2037,2038],{},"DocumentBadge[]",": the same, ",[431,2041,2042],{},"of(editor)"," without a row",[483,2045,2046],{},"after the „Dokument“ heading of the inspector and on the open document's row of the Block outline",[465,2048,2049,2054,2066],{},[483,2050,2051],{},[431,2052,2053],{},"inspectorSections(ctx)",[483,2055,2056,2005,2059,2062,2063],{},[431,2057,2058],{},"InspectorSection\u003CC>[]",[431,2060,2061],{},"{ id, title, component, appliesTo? }","; the component of the framework layer receives ",[431,2064,2065],{},"{ editor, node }",[483,2067,2068],{},"below the fields of a Typed block in the inspector and the details card",[418,2070,2071,2073,2074,2077,2078,2081,2082,2084,2085,2087,2088,2091,2092,2095,2096,2099,2100,893],{},[431,2072,2012],{}," runs for every row on every redraw, so it must stay cheap: look at ",[431,2075,2076],{},"row.capabilities"," first. ",[431,2079,2080],{},"subscribe(editor, listener)"," reports changes of what ",[431,2083,2012],{}," reads that are not editor transactions (the Prüfung model loads its records later); the chrome calls ",[431,2086,2012],{}," again after each ",[431,2089,2090],{},"listener"," call, and ",[431,2093,2094],{},"subscribe"," returns the unsubscribe function. This Feature puts a chip on every block whose ",[431,2097,2098],{},"meta.status"," is ",[431,2101,2102],{},"entwurf",[739,2104,2106],{"className":741,"code":2105,"language":744,"meta":745,"style":745},"import type { EditorFeature, RowBadge } from '@skaile\u002Fforge-editor\u002Fcore'\n\nconst draftChip: RowBadge = {\n  id: 'draft',\n  of: (editor, row) =>\n    editor.state.doc.nodeAt(row.pos)?.attrs.meta?.status === 'entwurf'\n      ? { label: 'Entwurf', tone: 'warning' }\n      : null,\n}\n\nexport const draftFeature: EditorFeature = {\n  id: 'draft-chip',\n  dimension: 'interaction',\n  requires: ['composites'],\n  rowBadges: () => [draftChip],\n}\n",[431,2107,2108,2134,2138,2153,2168,2190,2242,2278,2286,2290,2294,2311,2326,2342,2362,2380],{"__ignoreMap":745},[749,2109,2110,2112,2114,2116,2119,2121,2124,2126,2128,2130,2132],{"class":751,"line":752},[749,2111,756],{"class":755},[749,2113,790],{"class":755},[749,2115,760],{"class":759},[749,2117,2118],{"class":763}," EditorFeature",[749,2120,796],{"class":759},[749,2122,2123],{"class":763}," RowBadge",[749,2125,767],{"class":759},[749,2127,770],{"class":755},[749,2129,773],{"class":759},[749,2131,844],{"class":776},[749,2133,780],{"class":759},[749,2135,2136],{"class":751,"line":783},[749,2137,873],{"emptyLinePlaceholder":872},[749,2139,2140,2142,2145,2147,2149,2151],{"class":751,"line":813},[749,2141,887],{"class":886},[749,2143,2144],{"class":763}," draftChip",[749,2146,893],{"class":759},[749,2148,2123],{"class":896},[749,2150,899],{"class":759},[749,2152,902],{"class":759},[749,2154,2155,2157,2159,2161,2164,2166],{"class":751,"line":849},[749,2156,909],{"class":908},[749,2158,893],{"class":759},[749,2160,773],{"class":759},[749,2162,2163],{"class":776},"draft",[749,2165,918],{"class":759},[749,2167,921],{"class":759},[749,2169,2170,2173,2175,2177,2180,2182,2185,2187],{"class":751,"line":869},[749,2171,2172],{"class":1101},"  of",[749,2174,893],{"class":759},[749,2176,1332],{"class":759},[749,2178,2179],{"class":1467},"editor",[749,2181,796],{"class":759},[749,2183,2184],{"class":1467}," row",[749,2186,710],{"class":759},[749,2188,2189],{"class":886}," =>\n",[749,2191,2192,2195,2197,2200,2202,2205,2207,2210,2213,2215,2218,2221,2224,2226,2228,2230,2233,2236,2238,2240],{"class":751,"line":876},[749,2193,2194],{"class":763},"    editor",[749,2196,586],{"class":759},[749,2198,2199],{"class":763},"state",[749,2201,586],{"class":759},[749,2203,2204],{"class":763},"doc",[749,2206,586],{"class":759},[749,2208,2209],{"class":1101},"nodeAt",[749,2211,2212],{"class":763},"(row",[749,2214,586],{"class":759},[749,2216,2217],{"class":763},"pos)",[749,2219,2220],{"class":759},"?.",[749,2222,2223],{"class":763},"attrs",[749,2225,586],{"class":759},[749,2227,1357],{"class":763},[749,2229,2220],{"class":759},[749,2231,2232],{"class":763},"status ",[749,2234,2235],{"class":759},"===",[749,2237,773],{"class":759},[749,2239,2102],{"class":776},[749,2241,780],{"class":759},[749,2243,2244,2247,2249,2252,2254,2256,2259,2261,2263,2266,2268,2270,2273,2275],{"class":751,"line":883},[749,2245,2246],{"class":759},"      ?",[749,2248,760],{"class":759},[749,2250,2251],{"class":908}," label",[749,2253,893],{"class":759},[749,2255,773],{"class":759},[749,2257,2258],{"class":776},"Entwurf",[749,2260,918],{"class":759},[749,2262,796],{"class":759},[749,2264,2265],{"class":908}," tone",[749,2267,893],{"class":759},[749,2269,773],{"class":759},[749,2271,2272],{"class":776},"warning",[749,2274,918],{"class":759},[749,2276,2277],{"class":759}," }\n",[749,2279,2280,2283],{"class":751,"line":905},[749,2281,2282],{"class":759},"      :",[749,2284,2285],{"class":759}," null,\n",[749,2287,2288],{"class":751,"line":924},[749,2289,1077],{"class":759},[749,2291,2292],{"class":751,"line":941},[749,2293,873],{"emptyLinePlaceholder":872},[749,2295,2296,2298,2300,2303,2305,2307,2309],{"class":751,"line":958},[749,2297,1148],{"class":755},[749,2299,1151],{"class":886},[749,2301,2302],{"class":763}," draftFeature",[749,2304,893],{"class":759},[749,2306,2118],{"class":896},[749,2308,899],{"class":759},[749,2310,902],{"class":759},[749,2312,2313,2315,2317,2319,2322,2324],{"class":751,"line":969},[749,2314,909],{"class":908},[749,2316,893],{"class":759},[749,2318,773],{"class":759},[749,2320,2321],{"class":776},"draft-chip",[749,2323,918],{"class":759},[749,2325,921],{"class":759},[749,2327,2328,2331,2333,2335,2338,2340],{"class":751,"line":1013},[749,2329,2330],{"class":908},"  dimension",[749,2332,893],{"class":759},[749,2334,773],{"class":759},[749,2336,2337],{"class":776},"interaction",[749,2339,918],{"class":759},[749,2341,921],{"class":759},[749,2343,2344,2347,2349,2351,2353,2355,2357,2360],{"class":751,"line":1066},[749,2345,2346],{"class":908},"  requires",[749,2348,893],{"class":759},[749,2350,1041],{"class":763},[749,2352,918],{"class":759},[749,2354,615],{"class":776},[749,2356,918],{"class":759},[749,2358,2359],{"class":763},"]",[749,2361,921],{"class":759},[749,2363,2364,2367,2369,2372,2375,2378],{"class":751,"line":1074},[749,2365,2366],{"class":1101},"  rowBadges",[749,2368,893],{"class":759},[749,2370,2371],{"class":759}," ()",[749,2373,2374],{"class":886}," =>",[749,2376,2377],{"class":763}," [draftChip]",[749,2379,921],{"class":759},[749,2381,2382],{"class":751,"line":1080},[749,2383,1077],{"class":759},[418,2385,2386,2389,2390,2393,2394,2397,2398,2401,2402,2404,2405,2408,2409,653,2411,653,2414,653,2417,653,2420,1403,2423,1332,2426,2429],{},[431,2387,2388],{},"pruefungFeatureCore(ui, options)"," is the same Feature without a framework: ",[431,2391,2392],{},"ui.section"," is the component of the Prüfstand, and without ",[431,2395,2396],{},"ui"," the Feature adds no section. ",[431,2399,2400],{},"PruefungFeatureOptions.model"," replaces the REST model with a factory for a host's own store; the Feature then needs no ",[431,2403,548],{}," backend capability. For the model, ",[431,2406,2407],{},"pruefungModelOf(editor)"," gives ",[431,2410,2199],{},[431,2412,2413],{},"refresh",[431,2415,2416],{},"record(blockId, kind, note?)",[431,2418,2419],{},"mayRecord(kind)",[431,2421,2422],{},"statusOf(node)",[431,2424,2425],{},"freigabeOf(editor)",[431,2427,2428],{},"{ status, open, total }",").",[2431,2432,2433],"style",{},"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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":745,"searchDepth":783,"depth":783,"links":2435},[2436,2437,2438,2439,2440,2443,2444,2445],{"id":590,"depth":783,"text":591},{"id":1254,"depth":783,"text":1255},{"id":1339,"depth":783,"text":1340},{"id":1361,"depth":783,"text":1362},{"id":1557,"depth":783,"text":1558,"children":2441},[2442],{"id":1699,"depth":813,"text":1700},{"id":1320,"depth":783,"text":1336},{"id":1906,"depth":783,"text":1907},{"id":1965,"depth":783,"text":1966},"Record that a user checked a Typed block at one content state, derive the Freigabestatus, optionally lock checked blocks, and add your own chips and inspector sections.","md",null,{},{"icon":188},{"title":185,"description":2446},"wzU8LdlRxuUipkCPmZhFVi8gOLj_tMyLwcWoWN69SPU",[2454,2456],{"title":180,"path":181,"stem":182,"description":2455,"icon":183,"children":-1},"The Bild-Studio block: a picture with its description, prompt blocks, references, previous generations and chat. The node, its Markdown form, the state reducers, the images backend port and the dialog for your own store.",{"title":190,"path":191,"stem":192,"description":2457,"icon":193,"children":-1},"The Block index, Workspace search, Block references and Embeds that name a Block address, Document routes with the DocumentMutator, Tool providers for the document chat, and change sets for edits to other Documents.",1793178233318]