[{"data":1,"prerenderedAt":3683},["ShallowReactive",2],{"navigation_docs":3,"-guides-image-studio":412,"-guides-image-studio-surround":3678},[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":180,"body":414,"description":3672,"extension":999,"links":3673,"meta":3674,"navigation":3675,"path":181,"seo":3676,"stem":182,"__hash__":3677},"docs\u002F3.guides\u002F27.image-studio.md",{"type":415,"value":416,"toc":3659},"minimark",[417,430,515,522,525,609,614,624,761,771,774,816,819,921,925,960,995,1024,1040,1044,1047,1262,1268,1382,1402,1406,1424,1627,1641,1747,1776,1790,1795,1814,2209,2233,2237,2254,2318,2336,2343,2553,2574,2606,2613,3192,3196,3243,3397,3427,3439,3443,3473,3624,3630,3634,3655],[418,419,420,421,425,426,429],"p",{},"The ",[422,423,424],"strong",{},"Bild-Studio"," is a block, not a mode. It holds the text that describes the picture (a placeholder until you write one), opens into a dialog with a chat, and keeps every reference image and every previous generation. One of them is ",[422,427,428],{},"selected"," and is the block's picture.",[431,432,433,446],"table",{},[434,435,436],"thead",{},[437,438,439,443],"tr",{},[440,441,442],"th",{},"Part",[440,444,445],{},"What it is",[447,448,449,458,470,483,494,507],"tbody",{},[437,450,451,455],{},[452,453,454],"td",{},"Description",[452,456,457],{},"The text describing the image. Empty shows „Noch keine Beschreibung“ in the block and „Beschreibe das Bild …“ in the dialog.",[437,459,460,463],{},[452,461,462],{},"Prompt blocks",[452,464,465,466,469],{},"A list of style, brand, … texts that ",[422,467,468],{},"the host manages"," (label, text, order). Each can be switched on or off per block.",[437,471,472,475],{},[452,473,474],{},"References",[452,476,477,478,482],{},"Uploaded or picked images. An ",[479,480,481],"em",{},"active"," one goes into the next generation.",[437,484,485,488],{},[452,486,487],{},"Previous generations",[452,489,490,491,493],{},"Every image the chat produced. An ",[479,492,481],{}," one goes into the next generation as a reference.",[437,495,496,499],{},[452,497,498],{},"Selected image",[452,500,501,502,506],{},"The one reference or generation that is the block's picture (",[503,504,505],"code",{},"src"," of the node).",[437,508,509,512],{},[452,510,511],{},"Chat",[452,513,514],{},"The log of instructions, each with the image it produced or its problem.",[418,516,517,518,521],{},"Removing a reference or a generation removes it ",[422,519,520],{},"from this block only","; the host keeps the file. The package never deletes files.",[418,523,524],{},"This guide covers the feature, the node and its Markdown form, the state and its reducers, the backend port, and the dialog for a host that owns the state itself. The framework-free and Vue entries ship since 3.5.0, the React entry (dialog and node view) since 3.5.1.",[431,526,527,537],{},[434,528,529],{},[437,530,531,534],{},[440,532,533],{},"Entry",[440,535,536],{},"Contents",[447,538,539,559,578,591],{},[437,540,541,546],{},[452,542,543],{},[503,544,545],{},"@skaile\u002Fforge-editor\u002Fimage-studio\u002Fvue",[452,547,548,551,552,551,555,558],{},[503,549,550],{},"imageStudioFeature()",", ",[503,553,554],{},"ImageStudioDialog",[503,556,557],{},"createImageStudioNodeStore",", the state and reducers",[437,560,561,566],{},[452,562,563],{},[503,564,565],{},"@skaile\u002Fforge-editor\u002Fimage-studio\u002Freact",[452,567,568,551,570,551,572,551,575,558],{},[503,569,550],{},[503,571,554],{},[503,573,574],{},"useImageStudioNodeStore",[503,576,577],{},"ImageStudioReactStore",[437,579,580,585],{},[452,581,582],{},[503,583,584],{},"@skaile\u002Fforge-editor\u002Fimage-studio",[452,586,587,588,558],{},"Framework-free: ",[503,589,590],{},"imageStudioFeatureCore",[437,592,593,598],{},[452,594,595],{},[503,596,597],{},"@skaile\u002Fforge-editor\u002Fimage-studio\u002Fschema",[452,599,600,601,604,605,608],{},"Vue-free ",[503,602,603],{},"imageStudioSchemaExtensions()"," and ",[503,606,607],{},"ImageStudioNode"," for servers",[610,611,613],"h2",{"id":612},"enabling-it","Enabling it",[418,615,616,617,619,620,623],{},"Add ",[503,618,550],{}," to ",[503,621,622],{},"defineEditor({ features })"," next to the core features. The feature has no other dependency and imports no other feature.",[625,626,631],"pre",{"className":627,"code":628,"language":629,"meta":630,"style":630},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { coreContentFeature, coreUiFeature, defineEditor } from '@skaile\u002Fforge-editor\u002Fcore'\nimport { imageStudioFeature } from '@skaile\u002Fforge-editor\u002Fimage-studio\u002Fvue'\n\nconst preset = defineEditor({ features: [coreContentFeature(), coreUiFeature(), imageStudioFeature()] })\n","ts","",[503,632,633,677,697,704],{"__ignoreMap":630},[634,635,638,642,646,650,653,656,658,661,664,667,670,674],"span",{"class":636,"line":637},"line",1,[634,639,641],{"class":640},"s7zQu","import",[634,643,645],{"class":644},"sMK4o"," {",[634,647,649],{"class":648},"sTEyZ"," coreContentFeature",[634,651,652],{"class":644},",",[634,654,655],{"class":648}," coreUiFeature",[634,657,652],{"class":644},[634,659,660],{"class":648}," defineEditor",[634,662,663],{"class":644}," }",[634,665,666],{"class":640}," from",[634,668,669],{"class":644}," '",[634,671,673],{"class":672},"sfazB","@skaile\u002Fforge-editor\u002Fcore",[634,675,676],{"class":644},"'\n",[634,678,680,682,684,687,689,691,693,695],{"class":636,"line":679},2,[634,681,641],{"class":640},[634,683,645],{"class":644},[634,685,686],{"class":648}," imageStudioFeature",[634,688,663],{"class":644},[634,690,666],{"class":640},[634,692,669],{"class":644},[634,694,545],{"class":672},[634,696,676],{"class":644},[634,698,700],{"class":636,"line":699},3,[634,701,703],{"emptyLinePlaceholder":702},true,"\n",[634,705,707,711,714,717,720,723,726,730,733,736,739,742,744,746,748,750,752,755,758],{"class":636,"line":706},4,[634,708,710],{"class":709},"spNyl","const",[634,712,713],{"class":648}," preset ",[634,715,716],{"class":644},"=",[634,718,660],{"class":719},"s2Zo4",[634,721,722],{"class":648},"(",[634,724,725],{"class":644},"{",[634,727,729],{"class":728},"swJcz"," features",[634,731,732],{"class":644},":",[634,734,735],{"class":648}," [",[634,737,738],{"class":719},"coreContentFeature",[634,740,741],{"class":648},"()",[634,743,652],{"class":644},[634,745,655],{"class":719},[634,747,741],{"class":648},[634,749,652],{"class":644},[634,751,686],{"class":719},[634,753,754],{"class":648},"()] ",[634,756,757],{"class":644},"}",[634,759,760],{"class":648},")\n",[418,762,763,764,767,768,770],{},"On the React surface (shadcn) import ",[503,765,766],{},"imageStudioFeature"," from ",[503,769,565],{}," instead; everything else, the block, the Markdown form, the backend port and the behaviour described below, is the same.",[418,772,773],{},"The feature adds:",[775,776,777,785,809],"ul",{},[778,779,780,781,784],"li",{},"the ",[503,782,783],{},"imageStudio"," block (an atom, draggable; it revokes the text abilities, so no marks or text commands apply inside it),",[778,786,780,787,790,791,793,794,797,798,801,802,805,806,652],{},[503,788,789],{},"\u002F"," entry „Bild-Studio“ (command id ",[503,792,783],{},", slash id ",[503,795,796],{},"bild-studio","), offered where the caret has the ability ",[503,799,800],{},"insert.imageStudio"," (",[503,803,804],{},"ABILITY.imageStudio","), and the command ",[503,807,808],{},"insertImageStudio(attrs?)",[778,810,811,812,815],{},"the block action ",[503,813,814],{},"image-studio.open"," („Im Bild-Studio öffnen“, in the block menu and on the block's chrome), which asks the block's view to open the dialog. The view also opens it on double-click and from its „Bild-Studio öffnen“ button.",[418,817,818],{},"A server that validates or stores documents containing the node builds its schema from the schema entry:",[625,820,822],{"className":627,"code":821,"language":629,"meta":630,"style":630},"import { getSchema } from '@tiptap\u002Fcore'\nimport { coreSchemaExtensions } from '@skaile\u002Fforge-editor\u002Fcore\u002Fschema'\nimport { imageStudioSchemaExtensions } from '@skaile\u002Fforge-editor\u002Fimage-studio\u002Fschema'\n\nconst schema = getSchema([...coreSchemaExtensions(), ...imageStudioSchemaExtensions()])\n",[503,823,824,844,864,883,887],{"__ignoreMap":630},[634,825,826,828,830,833,835,837,839,842],{"class":636,"line":637},[634,827,641],{"class":640},[634,829,645],{"class":644},[634,831,832],{"class":648}," getSchema",[634,834,663],{"class":644},[634,836,666],{"class":640},[634,838,669],{"class":644},[634,840,841],{"class":672},"@tiptap\u002Fcore",[634,843,676],{"class":644},[634,845,846,848,850,853,855,857,859,862],{"class":636,"line":679},[634,847,641],{"class":640},[634,849,645],{"class":644},[634,851,852],{"class":648}," coreSchemaExtensions",[634,854,663],{"class":644},[634,856,666],{"class":640},[634,858,669],{"class":644},[634,860,861],{"class":672},"@skaile\u002Fforge-editor\u002Fcore\u002Fschema",[634,863,676],{"class":644},[634,865,866,868,870,873,875,877,879,881],{"class":636,"line":699},[634,867,641],{"class":640},[634,869,645],{"class":644},[634,871,872],{"class":648}," imageStudioSchemaExtensions",[634,874,663],{"class":644},[634,876,666],{"class":640},[634,878,669],{"class":644},[634,880,597],{"class":672},[634,882,676],{"class":644},[634,884,885],{"class":636,"line":706},[634,886,703],{"emptyLinePlaceholder":702},[634,888,890,892,895,897,899,902,905,908,910,912,915,918],{"class":636,"line":889},5,[634,891,710],{"class":709},[634,893,894],{"class":648}," schema ",[634,896,716],{"class":644},[634,898,832],{"class":719},[634,900,901],{"class":648},"([",[634,903,904],{"class":644},"...",[634,906,907],{"class":719},"coreSchemaExtensions",[634,909,741],{"class":648},[634,911,652],{"class":644},[634,913,914],{"class":644}," ...",[634,916,917],{"class":719},"imageStudioSchemaExtensions",[634,919,920],{"class":648},"()])\n",[610,922,924],{"id":923},"the-node-and-its-markdown-form","The node and its Markdown form",[418,926,927,928,930,931,933,934,937,938,604,941,944,945,948,949,952,953,956,957,959],{},"The node ",[503,929,783],{}," has the attributes ",[503,932,505],{}," (the url of the selected image, ",[503,935,936],{},"null"," without one), ",[503,939,940],{},"alt",[503,942,943],{},"studio"," (the whole ",[503,946,947],{},"ImageStudioState","). ",[503,950,951],{},"setImageStudioState(pos, state)"," replaces the state of the block at ",[503,954,955],{},"pos"," in one transaction and sets ",[503,958,505],{}," from the selected image, so undo takes both back together.",[431,961,962,972],{},[434,963,964],{},[437,965,966,969],{},[440,967,968],{},"Form",[440,970,971],{},"Shape",[447,973,974,984],{},[437,975,976,979],{},[452,977,978],{},"HTML",[452,980,981],{},[503,982,983],{},"\u003Cimg data-image-studio src alt data-studio=\"{json}\">",[437,985,986,989],{},[452,987,988],{},"Markdown",[452,990,991,994],{},[503,992,993],{},"[Bild-Studio: \u003Calt>](\u003Csrc>) \u003C!-- {\"studio\": …} -->"," on its own line",[625,996,1000],{"className":997,"code":998,"language":999,"meta":630,"style":630},"language-md shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[Bild-Studio: Segelboot im Abendlicht](\u002Ffiles\u002Fa1.png) \u003C!-- {\"studio\":{\"description\":\"Ein Segelboot im Abendlicht\",\"promptBlockIds\":[\"style\"],\"references\":[],\"generations\":[{\"id\":\"a1\",\"url\":\"\u002Ffiles\u002Fa1.png\",\"title\":\"Version 1\",\"active\":false}],\"selected\":\"a1\",\"turns\":[{\"id\":\"t1\",\"instruction\":\"\",\"imageId\":\"a1\",\"createdAt\":\"2026-10-21T09:00:00.000Z\"}]}} -->\n","md",[503,1001,1002],{"__ignoreMap":630},[634,1003,1004,1007,1010,1013,1017,1020],{"class":636,"line":637},[634,1005,1006],{"class":644},"[",[634,1008,1009],{"class":672},"Bild-Studio: Segelboot im Abendlicht",[634,1011,1012],{"class":644},"](",[634,1014,1016],{"class":1015},"sDpXG","\u002Ffiles\u002Fa1.png",[634,1018,1019],{"class":644},")",[634,1021,1023],{"class":1022},"sHwdD"," \u003C!-- {\"studio\":{\"description\":\"Ein Segelboot im Abendlicht\",\"promptBlockIds\":[\"style\"],\"references\":[],\"generations\":[{\"id\":\"a1\",\"url\":\"\u002Ffiles\u002Fa1.png\",\"title\":\"Version 1\",\"active\":false}],\"selected\":\"a1\",\"turns\":[{\"id\":\"t1\",\"instruction\":\"\",\"imageId\":\"a1\",\"createdAt\":\"2026-10-21T09:00:00.000Z\"}]}} -->\n",[418,1025,1026,1027,1029,1030,1033,1034,1036,1037,1039],{},"The label is the literal ",[503,1028,424],{}," in every UI language, because parsing looks for it. The comment is left out while the studio is empty (no description, prompt block, image or turn). Reading a document normalises the state: a missing or mistyped field falls back to its empty value, an entry without an ",[503,1031,1032],{},"id"," is dropped and a ",[503,1035,428],{}," that points at no image becomes ",[503,1038,936],{},". A valid state round-trips unchanged.",[610,1041,1043],{"id":1042},"the-state-and-its-reducers","The state and its reducers",[418,1045,1046],{},"The state is plain JSON and knows nothing about the host: image ids are opaque (a host uses its own asset ids).",[625,1048,1050],{"className":627,"code":1049,"language":629,"meta":630,"style":630},"interface ImageStudioImage { id: string, url: string, title: string, active: boolean }\ninterface ImageStudioTurn { id: string, instruction: string, imageId: string | null, createdAt: string, problem?: string }\ninterface ImageStudioState {\n  description: string \u002F\u002F '' shows the placeholder\n  promptBlockIds: string[] \u002F\u002F active prompt blocks, by the host's ids\n  references: ImageStudioImage[]\n  generations: ImageStudioImage[]\n  selected: string | null \u002F\u002F id of the generation (or reference) that is the block's image\n  turns: ImageStudioTurn[] \u002F\u002F the chat log: instruction → generation\n}\n",[503,1051,1052,1102,1162,1172,1184,1199,1212,1224,1241,1256],{"__ignoreMap":630},[634,1053,1054,1057,1061,1063,1066,1068,1071,1073,1076,1078,1080,1082,1085,1087,1089,1091,1094,1096,1099],{"class":636,"line":637},[634,1055,1056],{"class":709},"interface",[634,1058,1060],{"class":1059},"sBMFI"," ImageStudioImage",[634,1062,645],{"class":644},[634,1064,1065],{"class":728}," id",[634,1067,732],{"class":644},[634,1069,1070],{"class":1059}," string",[634,1072,652],{"class":644},[634,1074,1075],{"class":728}," url",[634,1077,732],{"class":644},[634,1079,1070],{"class":1059},[634,1081,652],{"class":644},[634,1083,1084],{"class":728}," title",[634,1086,732],{"class":644},[634,1088,1070],{"class":1059},[634,1090,652],{"class":644},[634,1092,1093],{"class":728}," active",[634,1095,732],{"class":644},[634,1097,1098],{"class":1059}," boolean",[634,1100,1101],{"class":644}," }\n",[634,1103,1104,1106,1109,1111,1113,1115,1117,1119,1122,1124,1126,1128,1131,1133,1135,1138,1141,1143,1146,1148,1150,1152,1155,1158,1160],{"class":636,"line":679},[634,1105,1056],{"class":709},[634,1107,1108],{"class":1059}," ImageStudioTurn",[634,1110,645],{"class":644},[634,1112,1065],{"class":728},[634,1114,732],{"class":644},[634,1116,1070],{"class":1059},[634,1118,652],{"class":644},[634,1120,1121],{"class":728}," instruction",[634,1123,732],{"class":644},[634,1125,1070],{"class":1059},[634,1127,652],{"class":644},[634,1129,1130],{"class":728}," imageId",[634,1132,732],{"class":644},[634,1134,1070],{"class":1059},[634,1136,1137],{"class":644}," |",[634,1139,1140],{"class":1059}," null",[634,1142,652],{"class":644},[634,1144,1145],{"class":728}," createdAt",[634,1147,732],{"class":644},[634,1149,1070],{"class":1059},[634,1151,652],{"class":644},[634,1153,1154],{"class":728}," problem",[634,1156,1157],{"class":644},"?:",[634,1159,1070],{"class":1059},[634,1161,1101],{"class":644},[634,1163,1164,1166,1169],{"class":636,"line":699},[634,1165,1056],{"class":709},[634,1167,1168],{"class":1059}," ImageStudioState",[634,1170,1171],{"class":644}," {\n",[634,1173,1174,1177,1179,1181],{"class":636,"line":706},[634,1175,1176],{"class":728},"  description",[634,1178,732],{"class":644},[634,1180,1070],{"class":1059},[634,1182,1183],{"class":1022}," \u002F\u002F '' shows the placeholder\n",[634,1185,1186,1189,1191,1193,1196],{"class":636,"line":889},[634,1187,1188],{"class":728},"  promptBlockIds",[634,1190,732],{"class":644},[634,1192,1070],{"class":1059},[634,1194,1195],{"class":648},"[] ",[634,1197,1198],{"class":1022},"\u002F\u002F active prompt blocks, by the host's ids\n",[634,1200,1202,1205,1207,1209],{"class":636,"line":1201},6,[634,1203,1204],{"class":728},"  references",[634,1206,732],{"class":644},[634,1208,1060],{"class":1059},[634,1210,1211],{"class":648},"[]\n",[634,1213,1215,1218,1220,1222],{"class":636,"line":1214},7,[634,1216,1217],{"class":728},"  generations",[634,1219,732],{"class":644},[634,1221,1060],{"class":1059},[634,1223,1211],{"class":648},[634,1225,1227,1230,1232,1234,1236,1238],{"class":636,"line":1226},8,[634,1228,1229],{"class":728},"  selected",[634,1231,732],{"class":644},[634,1233,1070],{"class":1059},[634,1235,1137],{"class":644},[634,1237,1140],{"class":1059},[634,1239,1240],{"class":1022}," \u002F\u002F id of the generation (or reference) that is the block's image\n",[634,1242,1244,1247,1249,1251,1253],{"class":636,"line":1243},9,[634,1245,1246],{"class":728},"  turns",[634,1248,732],{"class":644},[634,1250,1108],{"class":1059},[634,1252,1195],{"class":648},[634,1254,1255],{"class":1022},"\u002F\u002F the chat log: instruction → generation\n",[634,1257,1259],{"class":636,"line":1258},10,[634,1260,1261],{"class":644},"}\n",[418,1263,1264,1267],{},[503,1265,1266],{},"EMPTY_IMAGE_STUDIO_STATE"," is the state of a new block. The reducers are pure: each returns a new state and never touches its argument.",[431,1269,1270,1280],{},[434,1271,1272],{},[437,1273,1274,1277],{},[440,1275,1276],{},"Reducer",[440,1278,1279],{},"Effect",[447,1281,1282,1292,1302,1315,1328,1348,1358,1368],{},[437,1283,1284,1289],{},[452,1285,1286],{},[503,1287,1288],{},"setDescription(state, text)",[452,1290,1291],{},"Replaces the description.",[437,1293,1294,1299],{},[452,1295,1296],{},[503,1297,1298],{},"togglePromptBlock(state, id)",[452,1300,1301],{},"Adds the prompt block to the active ones, or removes it when it is active.",[437,1303,1304,1309],{},[452,1305,1306],{},[503,1307,1308],{},"toggleImage(state, id)",[452,1310,1311,1312,1314],{},"Flips ",[503,1313,481],{}," of the reference or generation; an unknown id changes nothing.",[437,1316,1317,1322],{},[452,1318,1319],{},[503,1320,1321],{},"select(state, id | null)",[452,1323,1324,1325,1327],{},"Makes the image the block's picture; ",[503,1326,936],{}," clears the choice; an unknown id changes nothing.",[437,1329,1330,1335],{},[452,1331,1332],{},[503,1333,1334],{},"removeImage(state, id)",[452,1336,1337,1338,604,1341,1344,1345,1347],{},"Drops the image from ",[503,1339,1340],{},"references",[503,1342,1343],{},"generations","; clears ",[503,1346,428],{}," when it was selected. The chat log keeps its turns.",[437,1349,1350,1355],{},[452,1351,1352],{},[503,1353,1354],{},"addReference(state, image)",[452,1356,1357],{},"Appends a reference; one with the same id is replaced where it stands.",[437,1359,1360,1365],{},[452,1361,1362],{},[503,1363,1364],{},"adoptImage(state, image)",[452,1366,1367],{},"Makes the image the block's picture: unchanged when it already is; an image the block holds is only selected; any other is added as a reference first.",[437,1369,1370,1375],{},[452,1371,1372],{},[503,1373,1374],{},"addGeneration(state, image, turn)",[452,1376,1377,1378,1381],{},"Appends the generation and its turn (pointing at the image) and ",[422,1379,1380],{},"selects it",": a chat generate means „this is the picture now“; an earlier one can be selected again.",[418,1383,1384,1385,1388,1389,551,1392,1395,1396,604,1398,1401],{},"Helpers: ",[503,1386,1387],{},"selectedImage(state)"," (the selected image, if the selection still points at one), ",[503,1390,1391],{},"isEmptyImageStudioState(state)",[503,1393,1394],{},"normalizeImageStudioState(value)"," (for stored JSON from outside). All of them, with the types, are exported from ",[503,1397,584],{},[503,1399,1400],{},"\u002Fimage-studio\u002Fvue",".",[610,1403,1405],{"id":1404},"the-backend-port","The backend port",[418,1407,1408,1409,1412,1413,1420,1421,732],{},"Generation, uploads and the list of prompt blocks go through the ",[503,1410,1411],{},"images"," capability of the ",[1414,1415,1417],"a",{"href":1416},"\u002Fguides\u002Feditor-ports#capability-probe",[503,1418,1419],{},"EditorBackend",", the last entry of ",[503,1422,1423],{},"BACKEND_CAPABILITIES",[625,1425,1427],{"className":627,"code":1426,"language":629,"meta":630,"style":630},"interface ImagesPort { \u002F\u002F EditorBackend['images']\n  promptBlocks: () => Promise\u003CImageStudioPromptBlock[]> \u002F\u002F { id, label, text }, in the host's order\n  generate: (body: ImageStudioGenerateBody, signal?: AbortSignal) => Promise\u003CImageStudioImage> \u002F\u002F resolves with active: false\n  upload: (documentId: string, file: File) => Promise\u003CImageStudioImage> \u002F\u002F resolves with active: true\n}\n\ninterface ImageStudioGenerateBody {\n  description: string\n  instruction: string \u002F\u002F '' generates from the description alone\n  promptBlockIds: string[] \u002F\u002F the active prompt blocks\n  referenceUrls: string[] \u002F\u002F urls of the active references and generations\n}\n",[503,1428,1429,1441,1472,1516,1557,1561,1565,1573,1582,1594,1607,1622],{"__ignoreMap":630},[634,1430,1431,1433,1436,1438],{"class":636,"line":637},[634,1432,1056],{"class":709},[634,1434,1435],{"class":1059}," ImagesPort",[634,1437,645],{"class":644},[634,1439,1440],{"class":1022}," \u002F\u002F EditorBackend['images']\n",[634,1442,1443,1446,1448,1451,1454,1457,1460,1463,1466,1469],{"class":636,"line":679},[634,1444,1445],{"class":728},"  promptBlocks",[634,1447,732],{"class":644},[634,1449,1450],{"class":644}," ()",[634,1452,1453],{"class":709}," =>",[634,1455,1456],{"class":1059}," Promise",[634,1458,1459],{"class":644},"\u003C",[634,1461,1462],{"class":1059},"ImageStudioPromptBlock",[634,1464,1465],{"class":648},"[]",[634,1467,1468],{"class":644},">",[634,1470,1471],{"class":1022}," \u002F\u002F { id, label, text }, in the host's order\n",[634,1473,1474,1477,1479,1481,1485,1487,1490,1492,1495,1497,1500,1502,1504,1506,1508,1511,1513],{"class":636,"line":699},[634,1475,1476],{"class":728},"  generate",[634,1478,732],{"class":644},[634,1480,801],{"class":644},[634,1482,1484],{"class":1483},"sHdIc","body",[634,1486,732],{"class":644},[634,1488,1489],{"class":1059}," ImageStudioGenerateBody",[634,1491,652],{"class":644},[634,1493,1494],{"class":1483}," signal",[634,1496,1157],{"class":644},[634,1498,1499],{"class":1059}," AbortSignal",[634,1501,1019],{"class":644},[634,1503,1453],{"class":709},[634,1505,1456],{"class":1059},[634,1507,1459],{"class":644},[634,1509,1510],{"class":1059},"ImageStudioImage",[634,1512,1468],{"class":644},[634,1514,1515],{"class":1022}," \u002F\u002F resolves with active: false\n",[634,1517,1518,1521,1523,1525,1528,1530,1532,1534,1537,1539,1542,1544,1546,1548,1550,1552,1554],{"class":636,"line":706},[634,1519,1520],{"class":728},"  upload",[634,1522,732],{"class":644},[634,1524,801],{"class":644},[634,1526,1527],{"class":1483},"documentId",[634,1529,732],{"class":644},[634,1531,1070],{"class":1059},[634,1533,652],{"class":644},[634,1535,1536],{"class":1483}," file",[634,1538,732],{"class":644},[634,1540,1541],{"class":1059}," File",[634,1543,1019],{"class":644},[634,1545,1453],{"class":709},[634,1547,1456],{"class":1059},[634,1549,1459],{"class":644},[634,1551,1510],{"class":1059},[634,1553,1468],{"class":644},[634,1555,1556],{"class":1022}," \u002F\u002F resolves with active: true\n",[634,1558,1559],{"class":636,"line":889},[634,1560,1261],{"class":644},[634,1562,1563],{"class":636,"line":1201},[634,1564,703],{"emptyLinePlaceholder":702},[634,1566,1567,1569,1571],{"class":636,"line":1214},[634,1568,1056],{"class":709},[634,1570,1489],{"class":1059},[634,1572,1171],{"class":644},[634,1574,1575,1577,1579],{"class":636,"line":1226},[634,1576,1176],{"class":728},[634,1578,732],{"class":644},[634,1580,1581],{"class":1059}," string\n",[634,1583,1584,1587,1589,1591],{"class":636,"line":1243},[634,1585,1586],{"class":728},"  instruction",[634,1588,732],{"class":644},[634,1590,1070],{"class":1059},[634,1592,1593],{"class":1022}," \u002F\u002F '' generates from the description alone\n",[634,1595,1596,1598,1600,1602,1604],{"class":636,"line":1258},[634,1597,1188],{"class":728},[634,1599,732],{"class":644},[634,1601,1070],{"class":1059},[634,1603,1195],{"class":648},[634,1605,1606],{"class":1022},"\u002F\u002F the active prompt blocks\n",[634,1608,1610,1613,1615,1617,1619],{"class":636,"line":1609},11,[634,1611,1612],{"class":728},"  referenceUrls",[634,1614,732],{"class":644},[634,1616,1070],{"class":1059},[634,1618,1195],{"class":648},[634,1620,1621],{"class":1022},"\u002F\u002F urls of the active references and generations\n",[634,1623,1625],{"class":636,"line":1624},12,[634,1626,1261],{"class":644},[418,1628,1629,1630,1633,1634,1637,1638,1640],{},"The HTTP backend (",[503,1631,1632],{},"createHttpBackend()",") maps the three members to routes. With ",[503,1635,1636],{},"probe: true"," it drops ",[503,1639,1411],{}," when the server does not list the capability.",[431,1642,1643,1659],{},[434,1644,1645],{},[437,1646,1647,1650,1653,1656],{},[440,1648,1649],{},"Member",[440,1651,1652],{},"Route",[440,1654,1655],{},"Body",[440,1657,1658],{},"Answer",[447,1660,1661,1681,1718],{},[437,1662,1663,1668,1673,1676],{},[452,1664,1665],{},[503,1666,1667],{},"promptBlocks()",[452,1669,1670],{},[503,1671,1672],{},"GET \u002Fapi\u002Feditor\u002Fimages\u002Fprompt-blocks",[452,1674,1675],{},"none",[452,1677,1678],{},[503,1679,1680],{},"ImageStudioPromptBlock[]",[437,1682,1683,1688,1693,1714],{},[452,1684,1685],{},[503,1686,1687],{},"generate(body, signal)",[452,1689,1690],{},[503,1691,1692],{},"POST \u002Fapi\u002Feditor\u002Fimages\u002Fgenerate",[452,1694,1695,1696,801,1699,1702,1703,1706,1707,1710,1711,1019],{},"JSON ",[503,1697,1698],{},"ImageStudioGenerateBody",[503,1700,1701],{},"description"," ≤ 10 000, ",[503,1704,1705],{},"instruction"," ≤ 4 000 characters, ≤ 50 ",[503,1708,1709],{},"promptBlockIds",", ≤ 20 ",[503,1712,1713],{},"referenceUrls",[452,1715,1716],{},[503,1717,1510],{},[437,1719,1720,1725,1730,1743],{},[452,1721,1722],{},[503,1723,1724],{},"upload(documentId, file)",[452,1726,1727],{},[503,1728,1729],{},"POST \u002Fapi\u002Feditor\u002Fimages\u002Fupload",[452,1731,1732,1733,1735,1736,1739,1740],{},"multipart: ",[503,1734,1527],{}," (a document id or ",[503,1737,1738],{},"scratch",") and ",[503,1741,1742],{},"file",[452,1744,1745],{},[503,1746,1510],{},[418,1748,1749,1750,1753,1754,1756,1757,1760,1761,1764,1765,1768,1769,1772,1773,1775],{},"The routes authenticate like every other editor route. ",[503,1751,1752],{},"400"," answers a malformed body, a bad ",[503,1755,1527],{}," or a missing file; ",[503,1758,1759],{},"413"," a file over the image limit (",[503,1762,1763],{},"MAX_MEDIA_BYTES.image","); ",[503,1766,1767],{},"415"," a file that is no image; ",[503,1770,1771],{},"501"," any of the three when the server has no ",[503,1774,1411],{}," port.",[418,1777,1778,1779,1782,1783,1786,1787,1789],{},"The memory backend (",[503,1780,1781],{},"createMemoryBackend()",") has ",[422,1784,1785],{},"no"," ",[503,1788,1411],{}," member: image generation needs a server that holds the model key and stores the files. In such a client the block still works as a picture holder, but opening the studio (the block action, double-click or the button) shows the notice „Bildgenerierung ist nicht eingerichtet“ instead of the dialog.",[1791,1792,1794],"h3",{"id":1793},"the-server-port","The server port",[418,1796,1797,1798,1801,1802,1805,1806,1809,1810,1813],{},"On the server the host supplies ",[503,1799,1800],{},"EditorPorts.images",", an ",[503,1803,1804],{},"ImageProvider",". The package owns authentication, validation and the routes; the provider owns the prompt, the model and the storage. ",[503,1807,1808],{},"user"," is what ",[503,1811,1812],{},"identity.authenticate"," returned.",[625,1815,1817],{"className":627,"code":1816,"language":629,"meta":630,"style":630},"import { provideEditorServer } from '@skaile\u002Fforge-editor\u002Fserver\u002Fnitro'\n\nprovideEditorServer({\n  ports: {\n    identity,\n    documents,\n    images: {\n      \u002F\u002F The managed list (label, text, order), e.g. rows of your own table.\n      promptBlocks: user => listPromptBlocks(user),\n      \u002F\u002F Build the prompt from description + instruction + the texts of body.promptBlockIds,\n      \u002F\u002F pass body.referenceUrls to the model, store the result and return it with active: false.\n      generate: async (body, user) => {\n        const file = await runImageModel(buildPrompt(body), body.referenceUrls)\n        const stored = await storeImage(file, user)\n        return { id: stored.id, url: stored.url, title: stored.title, active: false }\n      },\n      \u002F\u002F The route has already checked documentId, size and that the file is an image.\n      upload: async (documentId, file, user) => {\n        const stored = await storeUpload(documentId, file, user)\n        return { id: stored.id, url: stored.url, title: stored.title, active: true }\n      },\n    },\n  },\n})\n",[503,1818,1819,1839,1843,1853,1862,1870,1877,1886,1891,1911,1916,1921,1945,1984,2009,2065,2071,2077,2105,2133,2185,2190,2196,2202],{"__ignoreMap":630},[634,1820,1821,1823,1825,1828,1830,1832,1834,1837],{"class":636,"line":637},[634,1822,641],{"class":640},[634,1824,645],{"class":644},[634,1826,1827],{"class":648}," provideEditorServer",[634,1829,663],{"class":644},[634,1831,666],{"class":640},[634,1833,669],{"class":644},[634,1835,1836],{"class":672},"@skaile\u002Fforge-editor\u002Fserver\u002Fnitro",[634,1838,676],{"class":644},[634,1840,1841],{"class":636,"line":679},[634,1842,703],{"emptyLinePlaceholder":702},[634,1844,1845,1848,1850],{"class":636,"line":699},[634,1846,1847],{"class":719},"provideEditorServer",[634,1849,722],{"class":648},[634,1851,1852],{"class":644},"{\n",[634,1854,1855,1858,1860],{"class":636,"line":706},[634,1856,1857],{"class":728},"  ports",[634,1859,732],{"class":644},[634,1861,1171],{"class":644},[634,1863,1864,1867],{"class":636,"line":889},[634,1865,1866],{"class":648},"    identity",[634,1868,1869],{"class":644},",\n",[634,1871,1872,1875],{"class":636,"line":1201},[634,1873,1874],{"class":648},"    documents",[634,1876,1869],{"class":644},[634,1878,1879,1882,1884],{"class":636,"line":1214},[634,1880,1881],{"class":728},"    images",[634,1883,732],{"class":644},[634,1885,1171],{"class":644},[634,1887,1888],{"class":636,"line":1226},[634,1889,1890],{"class":1022},"      \u002F\u002F The managed list (label, text, order), e.g. rows of your own table.\n",[634,1892,1893,1896,1898,1901,1903,1906,1909],{"class":636,"line":1243},[634,1894,1895],{"class":719},"      promptBlocks",[634,1897,732],{"class":644},[634,1899,1900],{"class":1483}," user",[634,1902,1453],{"class":709},[634,1904,1905],{"class":719}," listPromptBlocks",[634,1907,1908],{"class":648},"(user)",[634,1910,1869],{"class":644},[634,1912,1913],{"class":636,"line":1258},[634,1914,1915],{"class":1022},"      \u002F\u002F Build the prompt from description + instruction + the texts of body.promptBlockIds,\n",[634,1917,1918],{"class":636,"line":1609},[634,1919,1920],{"class":1022},"      \u002F\u002F pass body.referenceUrls to the model, store the result and return it with active: false.\n",[634,1922,1923,1926,1928,1931,1933,1935,1937,1939,1941,1943],{"class":636,"line":1624},[634,1924,1925],{"class":719},"      generate",[634,1927,732],{"class":644},[634,1929,1930],{"class":709}," async",[634,1932,801],{"class":644},[634,1934,1484],{"class":1483},[634,1936,652],{"class":644},[634,1938,1900],{"class":1483},[634,1940,1019],{"class":644},[634,1942,1453],{"class":709},[634,1944,1171],{"class":644},[634,1946,1948,1951,1953,1956,1959,1962,1964,1967,1969,1971,1973,1975,1978,1980,1982],{"class":636,"line":1947},13,[634,1949,1950],{"class":709},"        const",[634,1952,1536],{"class":648},[634,1954,1955],{"class":644}," =",[634,1957,1958],{"class":640}," await",[634,1960,1961],{"class":719}," runImageModel",[634,1963,722],{"class":728},[634,1965,1966],{"class":719},"buildPrompt",[634,1968,722],{"class":728},[634,1970,1484],{"class":648},[634,1972,1019],{"class":728},[634,1974,652],{"class":644},[634,1976,1977],{"class":648}," body",[634,1979,1401],{"class":644},[634,1981,1713],{"class":648},[634,1983,760],{"class":728},[634,1985,1987,1989,1992,1994,1996,1999,2001,2003,2005,2007],{"class":636,"line":1986},14,[634,1988,1950],{"class":709},[634,1990,1991],{"class":648}," stored",[634,1993,1955],{"class":644},[634,1995,1958],{"class":640},[634,1997,1998],{"class":719}," storeImage",[634,2000,722],{"class":728},[634,2002,1742],{"class":648},[634,2004,652],{"class":644},[634,2006,1900],{"class":648},[634,2008,760],{"class":728},[634,2010,2012,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033,2035,2037,2040,2042,2044,2046,2048,2050,2053,2055,2057,2059,2063],{"class":636,"line":2011},15,[634,2013,2014],{"class":640},"        return",[634,2016,645],{"class":644},[634,2018,1065],{"class":728},[634,2020,732],{"class":644},[634,2022,1991],{"class":648},[634,2024,1401],{"class":644},[634,2026,1032],{"class":648},[634,2028,652],{"class":644},[634,2030,1075],{"class":728},[634,2032,732],{"class":644},[634,2034,1991],{"class":648},[634,2036,1401],{"class":644},[634,2038,2039],{"class":648},"url",[634,2041,652],{"class":644},[634,2043,1084],{"class":728},[634,2045,732],{"class":644},[634,2047,1991],{"class":648},[634,2049,1401],{"class":644},[634,2051,2052],{"class":648},"title",[634,2054,652],{"class":644},[634,2056,1093],{"class":728},[634,2058,732],{"class":644},[634,2060,2062],{"class":2061},"sfNiH"," false",[634,2064,1101],{"class":644},[634,2066,2068],{"class":636,"line":2067},16,[634,2069,2070],{"class":644},"      },\n",[634,2072,2074],{"class":636,"line":2073},17,[634,2075,2076],{"class":1022},"      \u002F\u002F The route has already checked documentId, size and that the file is an image.\n",[634,2078,2080,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103],{"class":636,"line":2079},18,[634,2081,2082],{"class":719},"      upload",[634,2084,732],{"class":644},[634,2086,1930],{"class":709},[634,2088,801],{"class":644},[634,2090,1527],{"class":1483},[634,2092,652],{"class":644},[634,2094,1536],{"class":1483},[634,2096,652],{"class":644},[634,2098,1900],{"class":1483},[634,2100,1019],{"class":644},[634,2102,1453],{"class":709},[634,2104,1171],{"class":644},[634,2106,2108,2110,2112,2114,2116,2119,2121,2123,2125,2127,2129,2131],{"class":636,"line":2107},19,[634,2109,1950],{"class":709},[634,2111,1991],{"class":648},[634,2113,1955],{"class":644},[634,2115,1958],{"class":640},[634,2117,2118],{"class":719}," storeUpload",[634,2120,722],{"class":728},[634,2122,1527],{"class":648},[634,2124,652],{"class":644},[634,2126,1536],{"class":648},[634,2128,652],{"class":644},[634,2130,1900],{"class":648},[634,2132,760],{"class":728},[634,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2160,2162,2164,2166,2168,2170,2172,2174,2176,2178,2180,2183],{"class":636,"line":2135},20,[634,2137,2014],{"class":640},[634,2139,645],{"class":644},[634,2141,1065],{"class":728},[634,2143,732],{"class":644},[634,2145,1991],{"class":648},[634,2147,1401],{"class":644},[634,2149,1032],{"class":648},[634,2151,652],{"class":644},[634,2153,1075],{"class":728},[634,2155,732],{"class":644},[634,2157,1991],{"class":648},[634,2159,1401],{"class":644},[634,2161,2039],{"class":648},[634,2163,652],{"class":644},[634,2165,1084],{"class":728},[634,2167,732],{"class":644},[634,2169,1991],{"class":648},[634,2171,1401],{"class":644},[634,2173,2052],{"class":648},[634,2175,652],{"class":644},[634,2177,1093],{"class":728},[634,2179,732],{"class":644},[634,2181,2182],{"class":2061}," true",[634,2184,1101],{"class":644},[634,2186,2188],{"class":636,"line":2187},21,[634,2189,2070],{"class":644},[634,2191,2193],{"class":636,"line":2192},22,[634,2194,2195],{"class":644},"    },\n",[634,2197,2199],{"class":636,"line":2198},23,[634,2200,2201],{"class":644},"  },\n",[634,2203,2205,2207],{"class":636,"line":2204},24,[634,2206,757],{"class":644},[634,2208,760],{"class":648},[418,2210,2211,2213,2214,801,2216,2219,2220,604,2223,2226,2227,2229,2230,1401],{},[503,2212,1411],{}," maps to the capability ",[503,2215,1411],{},[503,2217,2218],{},"PORT_CAPABILITY","), so ",[503,2221,2222],{},"serverCapabilities(ports)",[503,2224,2225],{},"GET \u002Fapi\u002Feditor\u002Fcapabilities"," list it only when the port is given. Without it the three routes answer ",[503,2228,1771],{},". The port is described with the others in ",[1414,2231,2232],{"href":196},"EditorPorts",[610,2234,2236],{"id":2235},"the-dialog-and-your-own-store","The dialog and your own store",[418,2238,2239,2241,2242,2245,2246,2249,2250,2253],{},[503,2240,554],{}," is exported for hosts; the block's view uses the same component. The Vue dialog is a modal (",[503,2243,2244],{},"useUiKit().Modal","), the React one a shadcn ",[503,2247,2248],{},"Dialog","; both have three columns: description and prompt blocks on the left (a switch per prompt block, its text underneath), references and previous generations in the middle, the selected image and the chat on the right. They use the same texts (",[503,2251,2252],{},"imageStudio.*",").",[431,2255,2256,2266],{},[434,2257,2258],{},[437,2259,2260,2263],{},[440,2261,2262],{},"Prop \u002F event",[440,2264,2265],{},"Meaning",[447,2267,2268,2295,2308],{},[437,2269,2270,2285],{},[452,2271,2272,801,2275,2278,2279,604,2282,1019],{},[503,2273,2274],{},"open: boolean",[503,2276,2277],{},"v-model:open","; React: ",[503,2280,2281],{},"open",[503,2283,2284],{},"onOpenChange(open)",[452,2286,2287,2288,2291,2292,1401],{},"Whether the dialog is shown; Vue emits ",[503,2289,2290],{},"update:open",", React calls ",[503,2293,2294],{},"onOpenChange",[437,2296,2297,2305],{},[452,2298,2299,2302,2303,1019],{},[503,2300,2301],{},"store: ImageStudioStore"," (React: ",[503,2304,577],{},[452,2306,2307],{},"What the dialog drives, see below.",[437,2309,2310,2315],{},[452,2311,2312],{},[503,2313,2314],{},"title?: string",[452,2316,2317],{},"Dialog title; default „Bild-Studio“.",[418,2319,2320,2321,2323,2324,2327,2328,2331,2332,2335],{},"In the middle column a click on a tile toggles ",[503,2322,481],{}," (a ring marks it), „Als Bild verwenden“ selects it and „Entfernen“ removes it from the block. A reference is added by the upload button or by dropping files on the zone. In the chat, ",[503,2325,2326],{},"Enter"," sends the instruction, ",[503,2329,2330],{},"Shift+Enter"," adds a line, and „Erzeugen“ is disabled while a generation runs. Closing the dialog aborts a running generation; an aborted generation adds nothing, a failed one adds a turn with ",[503,2333,2334],{},"problem"," set. The description is committed 500 ms after the last keystroke and at once on blur, on „Erzeugen“ and on close, so a prompt is never built from a stale text.",[418,2337,2338,2339,2342],{},"The dialog applies the reducers itself and hands every resulting state to ",[503,2340,2341],{},"store.update",". The store only persists, generates and uploads:",[625,2344,2346],{"className":627,"code":2345,"language":629,"meta":630,"style":630},"interface ImageStudioStore {\n  readonly state: Ref\u003CImageStudioState>\n  readonly promptBlocks: Ref\u003Creadonly ImageStudioPromptBlock[]> \u002F\u002F the managed list, loaded by the host\n  update: (next: ImageStudioState) => void \u002F\u002F after a reducer; the host persists it\n  generate: (instruction: string, signal: AbortSignal) => Promise\u003CImageStudioImage> \u002F\u002F the host builds the prompt\n  upload: (file: File) => Promise\u003CImageStudioImage> \u002F\u002F the dialog then calls addReference\n  readonly problem: Ref\u003Cstring | undefined> \u002F\u002F a problem of the store itself, shown as a notice\n  readonly library?: ImageStudioLibrary \u002F\u002F the host's gallery; a «Galerie» tab appears only when set\n  readonly placeholderUrl?: string \u002F\u002F shown in the preview while no image is selected\n}\n",[503,2347,2348,2357,2377,2403,2429,2466,2495,2520,2535,2549],{"__ignoreMap":630},[634,2349,2350,2352,2355],{"class":636,"line":637},[634,2351,1056],{"class":709},[634,2353,2354],{"class":1059}," ImageStudioStore",[634,2356,1171],{"class":644},[634,2358,2359,2362,2365,2367,2370,2372,2374],{"class":636,"line":679},[634,2360,2361],{"class":709},"  readonly",[634,2363,2364],{"class":728}," state",[634,2366,732],{"class":644},[634,2368,2369],{"class":1059}," Ref",[634,2371,1459],{"class":644},[634,2373,947],{"class":1059},[634,2375,2376],{"class":644},">\n",[634,2378,2379,2381,2384,2386,2388,2390,2393,2396,2398,2400],{"class":636,"line":699},[634,2380,2361],{"class":709},[634,2382,2383],{"class":728}," promptBlocks",[634,2385,732],{"class":644},[634,2387,2369],{"class":1059},[634,2389,1459],{"class":644},[634,2391,2392],{"class":709},"readonly",[634,2394,2395],{"class":1059}," ImageStudioPromptBlock",[634,2397,1465],{"class":648},[634,2399,1468],{"class":644},[634,2401,2402],{"class":1022}," \u002F\u002F the managed list, loaded by the host\n",[634,2404,2405,2408,2410,2412,2415,2417,2419,2421,2423,2426],{"class":636,"line":706},[634,2406,2407],{"class":728},"  update",[634,2409,732],{"class":644},[634,2411,801],{"class":644},[634,2413,2414],{"class":1483},"next",[634,2416,732],{"class":644},[634,2418,1168],{"class":1059},[634,2420,1019],{"class":644},[634,2422,1453],{"class":709},[634,2424,2425],{"class":1059}," void",[634,2427,2428],{"class":1022}," \u002F\u002F after a reducer; the host persists it\n",[634,2430,2431,2433,2435,2437,2439,2441,2443,2445,2447,2449,2451,2453,2455,2457,2459,2461,2463],{"class":636,"line":889},[634,2432,1476],{"class":728},[634,2434,732],{"class":644},[634,2436,801],{"class":644},[634,2438,1705],{"class":1483},[634,2440,732],{"class":644},[634,2442,1070],{"class":1059},[634,2444,652],{"class":644},[634,2446,1494],{"class":1483},[634,2448,732],{"class":644},[634,2450,1499],{"class":1059},[634,2452,1019],{"class":644},[634,2454,1453],{"class":709},[634,2456,1456],{"class":1059},[634,2458,1459],{"class":644},[634,2460,1510],{"class":1059},[634,2462,1468],{"class":644},[634,2464,2465],{"class":1022}," \u002F\u002F the host builds the prompt\n",[634,2467,2468,2470,2472,2474,2476,2478,2480,2482,2484,2486,2488,2490,2492],{"class":636,"line":1201},[634,2469,1520],{"class":728},[634,2471,732],{"class":644},[634,2473,801],{"class":644},[634,2475,1742],{"class":1483},[634,2477,732],{"class":644},[634,2479,1541],{"class":1059},[634,2481,1019],{"class":644},[634,2483,1453],{"class":709},[634,2485,1456],{"class":1059},[634,2487,1459],{"class":644},[634,2489,1510],{"class":1059},[634,2491,1468],{"class":644},[634,2493,2494],{"class":1022}," \u002F\u002F the dialog then calls addReference\n",[634,2496,2497,2499,2501,2503,2505,2507,2510,2512,2515,2517],{"class":636,"line":1214},[634,2498,2361],{"class":709},[634,2500,1154],{"class":728},[634,2502,732],{"class":644},[634,2504,2369],{"class":1059},[634,2506,1459],{"class":644},[634,2508,2509],{"class":1059},"string",[634,2511,1137],{"class":644},[634,2513,2514],{"class":1059}," undefined",[634,2516,1468],{"class":644},[634,2518,2519],{"class":1022}," \u002F\u002F a problem of the store itself, shown as a notice\n",[634,2521,2522,2524,2527,2529,2532],{"class":636,"line":1226},[634,2523,2361],{"class":709},[634,2525,2526],{"class":728}," library",[634,2528,1157],{"class":644},[634,2530,2531],{"class":1059}," ImageStudioLibrary",[634,2533,2534],{"class":1022}," \u002F\u002F the host's gallery; a «Galerie» tab appears only when set\n",[634,2536,2537,2539,2542,2544,2546],{"class":636,"line":1243},[634,2538,2361],{"class":709},[634,2540,2541],{"class":728}," placeholderUrl",[634,2543,1157],{"class":644},[634,2545,1070],{"class":1059},[634,2547,2548],{"class":1022}," \u002F\u002F shown in the preview while no image is selected\n",[634,2550,2551],{"class":636,"line":1258},[634,2552,1261],{"class":644},[418,2554,2555,2558,2559,2562,2563,2566,2567,2570,2571,2573],{},[503,2556,2557],{},"generate"," reads the description, the active prompt blocks and the active images from ",[503,2560,2561],{},"state"," and returns ",[422,2564,2565],{},"one"," image; the dialog calls ",[503,2568,2569],{},"addGeneration"," with it. Its store methods never write ",[503,2572,2561],{}," themselves.",[418,2575,2576,2577,2580,2581,2583,2584,2586,2587,2589,2590,2593,2594,2597,2598,2601,2602,2605],{},"For a studio inside the editor, ",[503,2578,2579],{},"createImageStudioNodeStore({ editor, getPos, backend, documentId })"," builds the store of one ",[503,2582,783],{}," node: ",[503,2585,2561],{}," follows the node's ",[503,2588,943],{}," attribute (so undo, collaboration and other writers show), ",[503,2591,2592],{},"update"," writes it back with ",[503,2595,2596],{},"setImageStudioState",", and the rest goes to ",[503,2599,2600],{},"backend.images",". Call its ",[503,2603,2604],{},"dispose()"," when the view goes.",[418,2607,2608,2609,2612],{},"A host that keeps the state ",[422,2610,2611],{},"outside the document"," (a sidecar next to the block, a database row) writes its own store and uses the dialog directly:",[625,2614,2618],{"className":2615,"code":2616,"language":2617,"meta":630,"style":630},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport { ImageStudioDialog, type ImageStudioStore, normalizeImageStudioState } from '@skaile\u002Fforge-editor\u002Fimage-studio\u002Fvue'\nimport { ref, shallowRef } from 'vue'\n\nconst open = ref(false)\n\n\u002F\u002F The block's studio state lives in the host's own record.\nconst state = shallowRef(normalizeImageStudioState(record.studio))\nconst promptBlocks = shallowRef(await fetchPromptBlocks())\nconst problem = shallowRef\u003Cstring>()\n\nconst store: ImageStudioStore = {\n  state,\n  promptBlocks,\n  problem,\n  update(next) {\n    state.value = next\n    void saveStudio(record.id, next) \u002F\u002F persist; also mirror the selected image into the block here\n  },\n  async generate(instruction, signal) {\n    const { description, promptBlockIds, references, generations } = state.value\n    const referenceIds = [...references, ...generations].filter(image => image.active).map(image => image.id)\n    return await requestImage({ description, instruction, promptBlockIds, referenceIds }, signal)\n  },\n  upload: file => uploadReference(file),\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUButton label=\"Bild-Studio\" @click=\"open = true\" \u002F>\n  \u003CImageStudioDialog v-model:open=\"open\" :store=\"store\" \u002F>\n\u003C\u002Ftemplate>\n","vue",[503,2619,2620,2644,2675,2699,2703,2721,2725,2730,2754,2776,2796,2800,2815,2822,2828,2835,2847,2862,2890,2894,2914,2950,3014,3049,3053,3072,3077,3087,3092,3102,3145,3183],{"__ignoreMap":630},[634,2621,2622,2624,2627,2630,2633,2635,2638,2640,2642],{"class":636,"line":637},[634,2623,1459],{"class":644},[634,2625,2626],{"class":728},"script",[634,2628,2629],{"class":709}," setup",[634,2631,2632],{"class":709}," lang",[634,2634,716],{"class":644},[634,2636,2637],{"class":644},"\"",[634,2639,629],{"class":672},[634,2641,2637],{"class":644},[634,2643,2376],{"class":644},[634,2645,2646,2648,2650,2653,2655,2658,2660,2662,2665,2667,2669,2671,2673],{"class":636,"line":679},[634,2647,641],{"class":640},[634,2649,645],{"class":644},[634,2651,2652],{"class":648}," ImageStudioDialog",[634,2654,652],{"class":644},[634,2656,2657],{"class":640}," type",[634,2659,2354],{"class":648},[634,2661,652],{"class":644},[634,2663,2664],{"class":648}," normalizeImageStudioState",[634,2666,663],{"class":644},[634,2668,666],{"class":640},[634,2670,669],{"class":644},[634,2672,545],{"class":672},[634,2674,676],{"class":644},[634,2676,2677,2679,2681,2684,2686,2689,2691,2693,2695,2697],{"class":636,"line":699},[634,2678,641],{"class":640},[634,2680,645],{"class":644},[634,2682,2683],{"class":648}," ref",[634,2685,652],{"class":644},[634,2687,2688],{"class":648}," shallowRef",[634,2690,663],{"class":644},[634,2692,666],{"class":640},[634,2694,669],{"class":644},[634,2696,2617],{"class":672},[634,2698,676],{"class":644},[634,2700,2701],{"class":636,"line":706},[634,2702,703],{"emptyLinePlaceholder":702},[634,2704,2705,2707,2710,2712,2714,2716,2719],{"class":636,"line":889},[634,2706,710],{"class":709},[634,2708,2709],{"class":648}," open ",[634,2711,716],{"class":644},[634,2713,2683],{"class":719},[634,2715,722],{"class":648},[634,2717,2718],{"class":2061},"false",[634,2720,760],{"class":648},[634,2722,2723],{"class":636,"line":1201},[634,2724,703],{"emptyLinePlaceholder":702},[634,2726,2727],{"class":636,"line":1214},[634,2728,2729],{"class":1022},"\u002F\u002F The block's studio state lives in the host's own record.\n",[634,2731,2732,2734,2737,2739,2741,2743,2746,2749,2751],{"class":636,"line":1226},[634,2733,710],{"class":709},[634,2735,2736],{"class":648}," state ",[634,2738,716],{"class":644},[634,2740,2688],{"class":719},[634,2742,722],{"class":648},[634,2744,2745],{"class":719},"normalizeImageStudioState",[634,2747,2748],{"class":648},"(record",[634,2750,1401],{"class":644},[634,2752,2753],{"class":648},"studio))\n",[634,2755,2756,2758,2761,2763,2765,2767,2770,2773],{"class":636,"line":1243},[634,2757,710],{"class":709},[634,2759,2760],{"class":648}," promptBlocks ",[634,2762,716],{"class":644},[634,2764,2688],{"class":719},[634,2766,722],{"class":648},[634,2768,2769],{"class":640},"await",[634,2771,2772],{"class":719}," fetchPromptBlocks",[634,2774,2775],{"class":648},"())\n",[634,2777,2778,2780,2783,2785,2787,2789,2791,2793],{"class":636,"line":1258},[634,2779,710],{"class":709},[634,2781,2782],{"class":648}," problem ",[634,2784,716],{"class":644},[634,2786,2688],{"class":719},[634,2788,1459],{"class":644},[634,2790,2509],{"class":1059},[634,2792,1468],{"class":644},[634,2794,2795],{"class":648},"()\n",[634,2797,2798],{"class":636,"line":1609},[634,2799,703],{"emptyLinePlaceholder":702},[634,2801,2802,2804,2807,2809,2811,2813],{"class":636,"line":1624},[634,2803,710],{"class":709},[634,2805,2806],{"class":648}," store",[634,2808,732],{"class":644},[634,2810,2354],{"class":1059},[634,2812,1955],{"class":644},[634,2814,1171],{"class":644},[634,2816,2817,2820],{"class":636,"line":1947},[634,2818,2819],{"class":648},"  state",[634,2821,1869],{"class":644},[634,2823,2824,2826],{"class":636,"line":1986},[634,2825,1445],{"class":648},[634,2827,1869],{"class":644},[634,2829,2830,2833],{"class":636,"line":2011},[634,2831,2832],{"class":648},"  problem",[634,2834,1869],{"class":644},[634,2836,2837,2839,2841,2843,2845],{"class":636,"line":2067},[634,2838,2407],{"class":728},[634,2840,722],{"class":644},[634,2842,2414],{"class":1483},[634,2844,1019],{"class":644},[634,2846,1171],{"class":644},[634,2848,2849,2852,2854,2857,2859],{"class":636,"line":2073},[634,2850,2851],{"class":648},"    state",[634,2853,1401],{"class":644},[634,2855,2856],{"class":648},"value",[634,2858,1955],{"class":644},[634,2860,2861],{"class":648}," next\n",[634,2863,2864,2867,2870,2872,2875,2877,2879,2881,2884,2887],{"class":636,"line":2079},[634,2865,2866],{"class":644},"    void",[634,2868,2869],{"class":719}," saveStudio",[634,2871,722],{"class":728},[634,2873,2874],{"class":648},"record",[634,2876,1401],{"class":644},[634,2878,1032],{"class":648},[634,2880,652],{"class":644},[634,2882,2883],{"class":648}," next",[634,2885,2886],{"class":728},") ",[634,2888,2889],{"class":1022},"\u002F\u002F persist; also mirror the selected image into the block here\n",[634,2891,2892],{"class":636,"line":2107},[634,2893,2201],{"class":644},[634,2895,2896,2899,2902,2904,2906,2908,2910,2912],{"class":636,"line":2135},[634,2897,2898],{"class":709},"  async",[634,2900,2901],{"class":728}," generate",[634,2903,722],{"class":644},[634,2905,1705],{"class":1483},[634,2907,652],{"class":644},[634,2909,1494],{"class":1483},[634,2911,1019],{"class":644},[634,2913,1171],{"class":644},[634,2915,2916,2919,2921,2924,2926,2929,2931,2934,2936,2939,2941,2943,2945,2947],{"class":636,"line":2187},[634,2917,2918],{"class":709},"    const",[634,2920,645],{"class":644},[634,2922,2923],{"class":648}," description",[634,2925,652],{"class":644},[634,2927,2928],{"class":648}," promptBlockIds",[634,2930,652],{"class":644},[634,2932,2933],{"class":648}," references",[634,2935,652],{"class":644},[634,2937,2938],{"class":648}," generations",[634,2940,663],{"class":644},[634,2942,1955],{"class":644},[634,2944,2364],{"class":648},[634,2946,1401],{"class":644},[634,2948,2949],{"class":648},"value\n",[634,2951,2952,2954,2957,2959,2961,2963,2965,2967,2969,2971,2974,2976,2979,2981,2984,2986,2989,2991,2993,2995,2997,3000,3002,3004,3006,3008,3010,3012],{"class":636,"line":2192},[634,2953,2918],{"class":709},[634,2955,2956],{"class":648}," referenceIds",[634,2958,1955],{"class":644},[634,2960,735],{"class":728},[634,2962,904],{"class":644},[634,2964,1340],{"class":648},[634,2966,652],{"class":644},[634,2968,914],{"class":644},[634,2970,1343],{"class":648},[634,2972,2973],{"class":728},"]",[634,2975,1401],{"class":644},[634,2977,2978],{"class":719},"filter",[634,2980,722],{"class":728},[634,2982,2983],{"class":1483},"image",[634,2985,1453],{"class":709},[634,2987,2988],{"class":648}," image",[634,2990,1401],{"class":644},[634,2992,481],{"class":648},[634,2994,1019],{"class":728},[634,2996,1401],{"class":644},[634,2998,2999],{"class":719},"map",[634,3001,722],{"class":728},[634,3003,2983],{"class":1483},[634,3005,1453],{"class":709},[634,3007,2988],{"class":648},[634,3009,1401],{"class":644},[634,3011,1032],{"class":648},[634,3013,760],{"class":728},[634,3015,3016,3019,3021,3024,3026,3028,3030,3032,3034,3036,3038,3040,3042,3045,3047],{"class":636,"line":2198},[634,3017,3018],{"class":640},"    return",[634,3020,1958],{"class":640},[634,3022,3023],{"class":719}," requestImage",[634,3025,722],{"class":728},[634,3027,725],{"class":644},[634,3029,2923],{"class":648},[634,3031,652],{"class":644},[634,3033,1121],{"class":648},[634,3035,652],{"class":644},[634,3037,2928],{"class":648},[634,3039,652],{"class":644},[634,3041,2956],{"class":648},[634,3043,3044],{"class":644}," },",[634,3046,1494],{"class":648},[634,3048,760],{"class":728},[634,3050,3051],{"class":636,"line":2204},[634,3052,2201],{"class":644},[634,3054,3056,3058,3060,3062,3064,3067,3070],{"class":636,"line":3055},25,[634,3057,1520],{"class":719},[634,3059,732],{"class":644},[634,3061,1536],{"class":1483},[634,3063,1453],{"class":709},[634,3065,3066],{"class":719}," uploadReference",[634,3068,3069],{"class":648},"(file)",[634,3071,1869],{"class":644},[634,3073,3075],{"class":636,"line":3074},26,[634,3076,1261],{"class":644},[634,3078,3080,3083,3085],{"class":636,"line":3079},27,[634,3081,3082],{"class":644},"\u003C\u002F",[634,3084,2626],{"class":728},[634,3086,2376],{"class":644},[634,3088,3090],{"class":636,"line":3089},28,[634,3091,703],{"emptyLinePlaceholder":702},[634,3093,3095,3097,3100],{"class":636,"line":3094},29,[634,3096,1459],{"class":644},[634,3098,3099],{"class":728},"template",[634,3101,2376],{"class":644},[634,3103,3105,3108,3111,3114,3116,3118,3120,3122,3125,3128,3130,3132,3134,3137,3140,3142],{"class":636,"line":3104},30,[634,3106,3107],{"class":644},"  \u003C",[634,3109,3110],{"class":728},"UButton",[634,3112,3113],{"class":709}," label",[634,3115,716],{"class":644},[634,3117,2637],{"class":644},[634,3119,424],{"class":672},[634,3121,2637],{"class":644},[634,3123,3124],{"class":644}," @",[634,3126,3127],{"class":709},"click",[634,3129,716],{"class":644},[634,3131,2637],{"class":644},[634,3133,2281],{"class":648},[634,3135,3136],{"class":644}," = ",[634,3138,3139],{"class":2061},"true",[634,3141,2637],{"class":644},[634,3143,3144],{"class":644}," \u002F>\n",[634,3146,3148,3150,3152,3155,3157,3159,3161,3163,3165,3167,3170,3173,3175,3177,3179,3181],{"class":636,"line":3147},31,[634,3149,3107],{"class":644},[634,3151,554],{"class":728},[634,3153,3154],{"class":709}," v-model",[634,3156,732],{"class":644},[634,3158,2281],{"class":709},[634,3160,716],{"class":644},[634,3162,2637],{"class":644},[634,3164,2281],{"class":648},[634,3166,2637],{"class":644},[634,3168,3169],{"class":644}," :",[634,3171,3172],{"class":709},"store",[634,3174,716],{"class":644},[634,3176,2637],{"class":644},[634,3178,3172],{"class":648},[634,3180,2637],{"class":644},[634,3182,3144],{"class":644},[634,3184,3186,3188,3190],{"class":636,"line":3185},32,[634,3187,3082],{"class":644},[634,3189,3099],{"class":728},[634,3191,2376],{"class":644},[1791,3193,3195],{"id":3194},"react","React",[418,3197,3198,3199,3201,3202,551,3204,604,3207,3209,3210,551,3212,604,3214,3217,3218,3220,3221,3223,3224,3226,3227,3229,3230,3232,3233,3236,3237,3239,3240,732],{},"The React dialog takes the same contract with plain values instead of refs, ",[503,3200,577],{},": ",[503,3203,2561],{},[503,3205,3206],{},"promptBlocks",[503,3208,2334],{}," are values, ",[503,3211,2592],{},[503,3213,2557],{},[503,3215,3216],{},"upload"," are the same functions. The host re-renders the dialog with the new ",[503,3219,2561],{}," after ",[503,3222,2592],{},"; until ",[503,3225,2561],{}," changes the dialog keeps building on the state it handed to ",[503,3228,2592],{},", so several uploads that finish together all land. The identity of ",[503,3231,2592],{}," is the identity of the store: keep it stable while the dialog edits one image (",[503,3234,3235],{},"useCallback","), and hand the open dialog another ",[503,3238,2592],{}," only with another image, so a pending description edit is committed to the image it was typed for. A host that keeps the state in a record writes it with ",[503,3241,3242],{},"useState",[625,3244,3248],{"className":3245,"code":3246,"language":3247,"meta":630,"style":630},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { ImageStudioDialog, type ImageStudioPromptBlock, type ImageStudioReactStore, type ImageStudioState, normalizeImageStudioState } from '@skaile\u002Fforge-editor\u002Fimage-studio\u002Freact'\nimport { useCallback, useMemo, useState } from 'react'\n\nfunction StudioButton({ record, promptBlocks }: { record: { id: string, studio: unknown }, promptBlocks: ImageStudioPromptBlock[] }) {\n  const [open, setOpen] = useState(false)\n  const [state, setState] = useState(() => normalizeImageStudioState(record.studio))\n  \u002F\u002F Stable per record: a new `update` tells the dialog that another image is open.\n  const update = useCallback((next: ImageStudioState) => {\n    setState(next)\n    void saveStudio(record.id, next) \u002F\u002F persist; also mirror the selected image into the block here\n  }, [record.id])\n  const store: ImageStudioReactStore = useMemo(() => ({\n    state,\n    promptBlocks,\n    problem: undefined,\n    update,\n    async generate(instruction, signal) {\n      const { description, promptBlockIds, references, generations } = state\n      const referenceIds = [...references, ...generations].filter(image => image.active).map(image => image.id)\n      return await requestImage({ description, instruction, promptBlockIds, referenceIds }, signal)\n    },\n    upload: file => uploadReference(file),\n  }), [state, promptBlocks, update])\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => setOpen(true)}>Bild-Studio\u003C\u002Fbutton>\n      \u003CImageStudioDialog open={open} onOpenChange={setOpen} store={store} \u002F>\n    \u003C\u002F>\n  )\n}\n","tsx",[503,3249,3250,3255,3260,3264,3269,3274,3279,3284,3289,3294,3299,3304,3309,3314,3319,3324,3329,3334,3339,3344,3349,3353,3358,3363,3368,3373,3378,3383,3388,3393],{"__ignoreMap":630},[634,3251,3252],{"class":636,"line":637},[634,3253,3254],{},"import { ImageStudioDialog, type ImageStudioPromptBlock, type ImageStudioReactStore, type ImageStudioState, normalizeImageStudioState } from '@skaile\u002Fforge-editor\u002Fimage-studio\u002Freact'\n",[634,3256,3257],{"class":636,"line":679},[634,3258,3259],{},"import { useCallback, useMemo, useState } from 'react'\n",[634,3261,3262],{"class":636,"line":699},[634,3263,703],{"emptyLinePlaceholder":702},[634,3265,3266],{"class":636,"line":706},[634,3267,3268],{},"function StudioButton({ record, promptBlocks }: { record: { id: string, studio: unknown }, promptBlocks: ImageStudioPromptBlock[] }) {\n",[634,3270,3271],{"class":636,"line":889},[634,3272,3273],{},"  const [open, setOpen] = useState(false)\n",[634,3275,3276],{"class":636,"line":1201},[634,3277,3278],{},"  const [state, setState] = useState(() => normalizeImageStudioState(record.studio))\n",[634,3280,3281],{"class":636,"line":1214},[634,3282,3283],{},"  \u002F\u002F Stable per record: a new `update` tells the dialog that another image is open.\n",[634,3285,3286],{"class":636,"line":1226},[634,3287,3288],{},"  const update = useCallback((next: ImageStudioState) => {\n",[634,3290,3291],{"class":636,"line":1243},[634,3292,3293],{},"    setState(next)\n",[634,3295,3296],{"class":636,"line":1258},[634,3297,3298],{},"    void saveStudio(record.id, next) \u002F\u002F persist; also mirror the selected image into the block here\n",[634,3300,3301],{"class":636,"line":1609},[634,3302,3303],{},"  }, [record.id])\n",[634,3305,3306],{"class":636,"line":1624},[634,3307,3308],{},"  const store: ImageStudioReactStore = useMemo(() => ({\n",[634,3310,3311],{"class":636,"line":1947},[634,3312,3313],{},"    state,\n",[634,3315,3316],{"class":636,"line":1986},[634,3317,3318],{},"    promptBlocks,\n",[634,3320,3321],{"class":636,"line":2011},[634,3322,3323],{},"    problem: undefined,\n",[634,3325,3326],{"class":636,"line":2067},[634,3327,3328],{},"    update,\n",[634,3330,3331],{"class":636,"line":2073},[634,3332,3333],{},"    async generate(instruction, signal) {\n",[634,3335,3336],{"class":636,"line":2079},[634,3337,3338],{},"      const { description, promptBlockIds, references, generations } = state\n",[634,3340,3341],{"class":636,"line":2107},[634,3342,3343],{},"      const referenceIds = [...references, ...generations].filter(image => image.active).map(image => image.id)\n",[634,3345,3346],{"class":636,"line":2135},[634,3347,3348],{},"      return await requestImage({ description, instruction, promptBlockIds, referenceIds }, signal)\n",[634,3350,3351],{"class":636,"line":2187},[634,3352,2195],{},[634,3354,3355],{"class":636,"line":2192},[634,3356,3357],{},"    upload: file => uploadReference(file),\n",[634,3359,3360],{"class":636,"line":2198},[634,3361,3362],{},"  }), [state, promptBlocks, update])\n",[634,3364,3365],{"class":636,"line":2204},[634,3366,3367],{},"  return (\n",[634,3369,3370],{"class":636,"line":3055},[634,3371,3372],{},"    \u003C>\n",[634,3374,3375],{"class":636,"line":3074},[634,3376,3377],{},"      \u003Cbutton onClick={() => setOpen(true)}>Bild-Studio\u003C\u002Fbutton>\n",[634,3379,3380],{"class":636,"line":3079},[634,3381,3382],{},"      \u003CImageStudioDialog open={open} onOpenChange={setOpen} store={store} \u002F>\n",[634,3384,3385],{"class":636,"line":3089},[634,3386,3387],{},"    \u003C\u002F>\n",[634,3389,3390],{"class":636,"line":3094},[634,3391,3392],{},"  )\n",[634,3394,3395],{"class":636,"line":3104},[634,3396,1261],{},[418,3398,3399,3400,3403,3404,3407,3408,551,3411,551,3414,551,3416,551,3418,551,3420,3422,3423,3426],{},"For the node's own store use ",[503,3401,3402],{},"useImageStudioNodeStore({ editor, getPos, backend, documentId })",", which wraps ",[503,3405,3406],{},"createImageStudioNodeCore"," (the framework-free observable the Vue store wraps as well: ",[503,3409,3410],{},"get()",[503,3412,3413],{},"subscribe(listener)",[503,3415,2592],{},[503,3417,2557],{},[503,3419,3216],{},[503,3421,2604],{},"). It returns ",[503,3424,3425],{},"undefined"," on the first render, because the core is built in an effect; render the dialog once there is a store.",[418,3428,3429,3430,3432,3433,3435,3436,3438],{},"Image ",[503,3431,1032],{},"s can be the host's own ids (an asset id, say) and ",[503,3434,2039],{}," any url the page can show; the library only compares ids. The host decides what a removed image means for its files, and whether ",[503,3437,428],{}," is also written to wherever the block's picture is read from.",[1791,3440,3442],{"id":3441},"the-gallery-tab","The gallery tab",[418,3444,3445,3446,3449,3450,3453,3454,3457,3458,3461,3462,3465,3466,3469,3470,3472],{},"With ",[503,3447,3448],{},"store.library"," the middle column gets two tabs: „Dieser Block“ (references and previous generations, as above) and „Galerie“ (the host's images). The tab re-reads ",[503,3451,3452],{},"facets()"," each time it opens, lists with the first option of every facet and an empty search, and lists again after a facet change and 300 ms after the last keystroke of the search; a newer read aborts the running one. A tile offers „Als Bild verwenden“ (",[503,3455,3456],{},"adoptImage","), „Als Referenz“ (",[503,3459,3460],{},"addReference",", disabled once the block holds the image), and, when the host gives ",[503,3463,3464],{},"rename"," \u002F ",[503,3467,3468],{},"remove",", a pencil (Enter commits, Escape or blur cancels) and a trash button. A refused ",[503,3471,3468],{}," shows the error's message in the tab and keeps the tile; a granted one also drops the image from the block when it holds it.",[625,3474,3476],{"className":627,"code":3475,"language":629,"meta":630,"style":630},"interface ImageStudioLibrary {\n  facets: () => readonly ImageStudioLibraryFacet[] \u002F\u002F { id, label, options: { value, label }[] }; the first option is the default\n  list: (query: ImageStudioLibraryQuery, signal: AbortSignal) => Promise\u003Creadonly ImageStudioLibraryItem[]> \u002F\u002F query: { search, facets: { [facetId]: value } }; item: an ImageStudioImage plus an optional subtitle\n  rename?: (id: string, title: string) => Promise\u003CImageStudioLibraryItem>\n  remove?: (id: string) => Promise\u003Cvoid> \u002F\u002F reject with an Error whose message the dialog shows\n}\n",[503,3477,3478,3486,3508,3553,3589,3620],{"__ignoreMap":630},[634,3479,3480,3482,3484],{"class":636,"line":637},[634,3481,1056],{"class":709},[634,3483,2531],{"class":1059},[634,3485,1171],{"class":644},[634,3487,3488,3491,3493,3495,3497,3500,3503,3505],{"class":636,"line":679},[634,3489,3490],{"class":728},"  facets",[634,3492,732],{"class":644},[634,3494,1450],{"class":644},[634,3496,1453],{"class":709},[634,3498,3499],{"class":709}," readonly",[634,3501,3502],{"class":1059}," ImageStudioLibraryFacet",[634,3504,1195],{"class":648},[634,3506,3507],{"class":1022},"\u002F\u002F { id, label, options: { value, label }[] }; the first option is the default\n",[634,3509,3510,3513,3515,3517,3520,3522,3525,3527,3529,3531,3533,3535,3537,3539,3541,3543,3546,3548,3550],{"class":636,"line":699},[634,3511,3512],{"class":728},"  list",[634,3514,732],{"class":644},[634,3516,801],{"class":644},[634,3518,3519],{"class":1483},"query",[634,3521,732],{"class":644},[634,3523,3524],{"class":1059}," ImageStudioLibraryQuery",[634,3526,652],{"class":644},[634,3528,1494],{"class":1483},[634,3530,732],{"class":644},[634,3532,1499],{"class":1059},[634,3534,1019],{"class":644},[634,3536,1453],{"class":709},[634,3538,1456],{"class":1059},[634,3540,1459],{"class":644},[634,3542,2392],{"class":709},[634,3544,3545],{"class":1059}," ImageStudioLibraryItem",[634,3547,1465],{"class":648},[634,3549,1468],{"class":644},[634,3551,3552],{"class":1022}," \u002F\u002F query: { search, facets: { [facetId]: value } }; item: an ImageStudioImage plus an optional subtitle\n",[634,3554,3555,3558,3560,3562,3564,3566,3568,3570,3572,3574,3576,3578,3580,3582,3584,3587],{"class":636,"line":706},[634,3556,3557],{"class":728},"  rename",[634,3559,1157],{"class":644},[634,3561,801],{"class":644},[634,3563,1032],{"class":1483},[634,3565,732],{"class":644},[634,3567,1070],{"class":1059},[634,3569,652],{"class":644},[634,3571,1084],{"class":1483},[634,3573,732],{"class":644},[634,3575,1070],{"class":1059},[634,3577,1019],{"class":644},[634,3579,1453],{"class":709},[634,3581,1456],{"class":1059},[634,3583,1459],{"class":644},[634,3585,3586],{"class":1059},"ImageStudioLibraryItem",[634,3588,2376],{"class":644},[634,3590,3591,3594,3596,3598,3600,3602,3604,3606,3608,3610,3612,3615,3617],{"class":636,"line":889},[634,3592,3593],{"class":728},"  remove",[634,3595,1157],{"class":644},[634,3597,801],{"class":644},[634,3599,1032],{"class":1483},[634,3601,732],{"class":644},[634,3603,1070],{"class":1059},[634,3605,1019],{"class":644},[634,3607,1453],{"class":709},[634,3609,1456],{"class":1059},[634,3611,1459],{"class":644},[634,3613,3614],{"class":1059},"void",[634,3616,1468],{"class":644},[634,3618,3619],{"class":1022}," \u002F\u002F reject with an Error whose message the dialog shows\n",[634,3621,3622],{"class":636,"line":1201},[634,3623,1261],{"class":644},[418,3625,3626,3629],{},[503,3627,3628],{},"placeholderUrl"," replaces the text „Noch kein Bild“ in the preview with that picture while no image is selected. The node store sets neither member.",[610,3631,3633],{"id":3632},"see-also","See also",[775,3635,3636,3641,3646],{},[778,3637,3638,3640],{},[1414,3639,332],{"href":333}," lists the three entries.",[778,3642,3643,3645],{},[1414,3644,2232],{"href":196}," explains the ports, the capability probe and the capability classes.",[778,3647,3648,3651,3652,1775],{},[1414,3649,3650],{"href":95},"Media, mentions and transcript"," covers the plain image block and uploads through the ",[503,3653,3654],{},"assets",[3656,3657,3658],"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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html 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}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sDpXG, html code.shiki .sDpXG{--shiki-light:#E53935;--shiki-light-text-decoration:underline;--shiki-default:#F07178;--shiki-default-text-decoration:underline;--shiki-dark:#F07178;--shiki-dark-text-decoration:underline}",{"title":630,"searchDepth":679,"depth":679,"links":3660},[3661,3662,3663,3664,3667,3671],{"id":612,"depth":679,"text":613},{"id":923,"depth":679,"text":924},{"id":1042,"depth":679,"text":1043},{"id":1404,"depth":679,"text":1405,"children":3665},[3666],{"id":1793,"depth":699,"text":1794},{"id":2235,"depth":679,"text":2236,"children":3668},[3669,3670],{"id":3194,"depth":699,"text":3195},{"id":3441,"depth":699,"text":3442},{"id":3632,"depth":679,"text":3633},"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.",null,{},{"icon":183},{"title":180,"description":3672},"7psnxpzJqFRSaVRHt-_ZjzT9Kvnk6xgX7LmQQEeNuI0",[3679,3681],{"title":175,"path":176,"stem":177,"description":3680,"icon":178,"children":-1},"Define Block properties with Property definitions, give them to a Block type with the properties capability and read and write them.",{"title":185,"path":186,"stem":187,"description":3682,"icon":188,"children":-1},"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.",1793178233162]