[{"data":1,"prerenderedAt":774},["ShallowReactive",2],{"msfe-doc:\u002Fen\u002Fextensions\u002Fcustom-node":3},{"id":4,"title":5,"body":6,"description":756,"extension":757,"keywords":758,"locale":766,"meta":767,"navigation":328,"order":768,"path":769,"section":770,"seo":771,"stem":772,"__hash__":773},"docs\u002Fextensions\u002Fcustom-node.en.md","Custom nodes",{"type":7,"value":8,"toc":741},"minimark",[9,14,23,27,31,53,67,71,77,81,85,115,203,207,230,237,241,263,420,446,463,467,493,496,519,642,646,649,653,662,665,668,672,678,681,685,709,713,730,734,737],[10,11,13],"h2",{"id":12},"try-a-field-update","Try a field update",[15,16,17,18,22],"p",{},"Click Pending to change it to Done; the reading preview updates below. Read the Markdown to confirm that only the value inside ",[19,20,21],"code",{},"[[todo]]"," changed. Undo and redo update both views. Locked mode prevents field changes.",[24,25],"msfe-demo",{"name":26},"status\u002Feditor",[10,28,30],{"id":29},"define-syntax-and-fields","Define syntax and fields",[15,32,33,36,37,40,41,44,45,48,49,52],{},[19,34,35],{},"status.ts"," uses ",[19,38,39],{},"Node.create()"," to declare semantics. ",[19,42,43],{},"addAttributes()"," admits the ",[19,46,47],{},"value"," field and validates its values. The literal-delimiter syntax in ",[19,50,51],{},"defineSource()"," is suitable for single-line, non-nested tokens.",[15,54,55,58,59,62,63,66],{},[19,56,57],{},"read(document, node)"," reads only the confirmed range. Invalid values return ",[19,60,61],{},"null"," and keep the source visible. For complex or nested syntax, supply parsing rules through ",[19,64,65],{},"addMarkdownExtensions()",".",[10,68,70],{"id":69},"write-precise-changes","Write precise changes",[15,72,73,76],{},[19,74,75],{},"patch(document, node, patch)"," returns source edits. This example preserves the surrounding brackets and replaces only the inner value. The editor handles field validation, read-only access, stale targets, and undo centrally.",[78,79],"msfe-api",{"name":80},"SourceDefinition",[10,82,84],{"id":83},"add-commands","Add commands",[15,86,87,90,91,94,95,98,99,102,103,106,107,110,111,114],{},[19,88,89],{},"addCommands()"," defines ",[19,92,93],{},"insertStatus()",". Module augmentation in ",[19,96,97],{},"types.ts"," provides type information for ",[19,100,101],{},"editor.commands",", ",[19,104,105],{},"editor.can()",", and ",[19,108,109],{},"editor.chain()",". Commands use the supplied ",[19,112,113],{},"tr"," instead of dispatching a separate transaction.",[116,117,122],"pre",{"className":118,"code":119,"language":120,"meta":121,"style":121},"language-ts shiki shiki-themes github-light github-light github-dark","editor.commands.insertStatus('todo')\neditor.can().insertStatus('done')\neditor.chain().insertStatus('todo').insertText(' ').run()\n","ts","",[19,123,124,147,168],{"__ignoreMap":121},[125,126,129,133,137,140,144],"span",{"class":127,"line":128},"line",1,[125,130,132],{"class":131},"sVPnS","editor.commands.",[125,134,136],{"class":135},"sgQzM","insertStatus",[125,138,139],{"class":131},"(",[125,141,143],{"class":142},"s-HEn","'todo'",[125,145,146],{"class":131},")\n",[125,148,150,153,156,159,161,163,166],{"class":127,"line":149},2,[125,151,152],{"class":131},"editor.",[125,154,155],{"class":135},"can",[125,157,158],{"class":131},"().",[125,160,136],{"class":135},[125,162,139],{"class":131},[125,164,165],{"class":142},"'done'",[125,167,146],{"class":131},[125,169,171,173,176,178,180,182,184,187,190,192,195,197,200],{"class":127,"line":170},3,[125,172,152],{"class":131},[125,174,175],{"class":135},"chain",[125,177,158],{"class":131},[125,179,136],{"class":135},[125,181,139],{"class":131},[125,183,143],{"class":142},[125,185,186],{"class":131},").",[125,188,189],{"class":135},"insertText",[125,191,139],{"class":131},[125,193,194],{"class":142},"' '",[125,196,186],{"class":131},[125,198,199],{"class":135},"run",[125,201,202],{"class":131},"()\n",[10,204,206],{"id":205},"replace-the-component","Replace the component",[15,208,209,210,213,214,217,218,221,222,225,226,229],{},"The application uses ",[19,211,212],{},".extend({ addNodeView() { ... } })"," to install ",[19,215,216],{},"VueNodeViewRenderer()",". ",[19,219,220],{},"status-view.vue"," receives ",[19,223,224],{},"nodeView.attrs"," and ",[19,227,228],{},"updateAttributes()",", so it does not calculate source offsets itself.",[15,231,232,233,236],{},"The component tree must include ",[19,234,235],{},"VueNodeViewHost",". When moving to React, retain the same syntax extension and use the corresponding renderer and host.",[10,238,240],{"id":239},"add-static-reading","Add static reading",[15,242,243,246,247,250,251,254,255,258,259,262],{},[19,244,245],{},"MdProse"," accepts third-party reading contributions through ",[19,248,249],{},"withMdReading(extension, definition)",".\nFinish ",[19,252,253],{},"configure()"," \u002F ",[19,256,257],{},"extend()"," first, then wrap and install the extension in the same editor.\nThe public entry is ",[19,260,261],{},"@quirekit\u002Fmd-editor\u002Fvue","; no changes to built-in syntax branches are needed.",[116,264,266],{"className":118,"code":265,"language":120,"meta":121,"style":121},"import { h } from 'vue'\nimport { withMdReading } from '@quirekit\u002Fmd-editor\u002Fvue'\nimport { Status } from '.\u002Fstatus'\nimport type { StatusAttributes } from '.\u002Ftypes'\n\nconst ReadableStatus = withMdReading\u003CStatusAttributes>(Status, {\n  render({ attrs }) {\n    return h('strong', attrs.value === 'done' ? 'Done' : 'Pending')\n  },\n})\n",[19,267,268,283,295,307,323,330,355,371,408,414],{"__ignoreMap":121},[125,269,270,274,277,280],{"class":127,"line":128},[125,271,273],{"class":272},"sDD9d","import",[125,275,276],{"class":131}," { h } ",[125,278,279],{"class":272},"from",[125,281,282],{"class":142}," 'vue'\n",[125,284,285,287,290,292],{"class":127,"line":149},[125,286,273],{"class":272},[125,288,289],{"class":131}," { withMdReading } ",[125,291,279],{"class":272},[125,293,294],{"class":142}," '@quirekit\u002Fmd-editor\u002Fvue'\n",[125,296,297,299,302,304],{"class":127,"line":170},[125,298,273],{"class":272},[125,300,301],{"class":131}," { Status } ",[125,303,279],{"class":272},[125,305,306],{"class":142}," '.\u002Fstatus'\n",[125,308,310,312,315,318,320],{"class":127,"line":309},4,[125,311,273],{"class":272},[125,313,314],{"class":272}," type",[125,316,317],{"class":131}," { StatusAttributes } ",[125,319,279],{"class":272},[125,321,322],{"class":142}," '.\u002Ftypes'\n",[125,324,326],{"class":127,"line":325},5,[125,327,329],{"emptyLinePlaceholder":328},true,"\n",[125,331,333,336,340,343,346,349,352],{"class":127,"line":332},6,[125,334,335],{"class":272},"const",[125,337,339],{"class":338},"sUNQB"," ReadableStatus",[125,341,342],{"class":272}," =",[125,344,345],{"class":135}," withMdReading",[125,347,348],{"class":131},"\u003C",[125,350,351],{"class":135},"StatusAttributes",[125,353,354],{"class":131},">(Status, {\n",[125,356,358,361,364,368],{"class":127,"line":357},7,[125,359,360],{"class":135},"  render",[125,362,363],{"class":131},"({ ",[125,365,367],{"class":366},"sde8i","attrs",[125,369,370],{"class":131}," }) {\n",[125,372,374,377,380,382,385,388,391,394,397,400,403,406],{"class":127,"line":373},8,[125,375,376],{"class":272},"    return",[125,378,379],{"class":135}," h",[125,381,139],{"class":131},[125,383,384],{"class":142},"'strong'",[125,386,387],{"class":131},", attrs.value ",[125,389,390],{"class":272},"===",[125,392,393],{"class":142}," 'done'",[125,395,396],{"class":272}," ?",[125,398,399],{"class":142}," 'Done'",[125,401,402],{"class":272}," :",[125,404,405],{"class":142}," 'Pending'",[125,407,146],{"class":131},[125,409,411],{"class":127,"line":410},9,[125,412,413],{"class":131},"  },\n",[125,415,417],{"class":127,"line":416},10,[125,418,419],{"class":131},"})\n",[15,421,422,425,426,102,429,102,432,225,435,438,439,225,442,445],{},[19,423,424],{},"render(context, resource)"," receives attrs from the installed extension's original codec, sharing\nits defaults and validation. Context also provides ",[19,427,428],{},"name",[19,430,431],{},"node",[19,433,434],{},"document",[19,436,437],{},"locale",", without\nediting actions. ",[19,440,441],{},"renderInline(from?, to?)",[19,443,444],{},"renderBlocks(omitted?)"," delegate children to the\nshared reader. Inline bounds must stay inside the node without cutting a child syntax node;\ninvalid bounds trigger local source fallback.",[15,447,448,449,452,453,456,457,462],{},"Pass the original editor's immutable snapshot to ",[19,450,451],{},"MdProse.state",". The example uses\n",[19,454,455],{},"const state = useMsfeState(editor)"," with automatic subscription cleanup; see\n",[458,459,461],"a",{"href":460},"\u002Fen\u002Fguide\u002Fvue#observe-editor-state-in-vue","Vue state subscriptions",". No Markdown serialization runs\non each input. Selection and focus changes do not repeat decoding or loading. Reading depends on source, syntax,\ninstalled contributions, presentation configuration and locale; codecs should not derive static\ncontent from selection or mutable external state. Document changes may rebuild the reading snapshot\nconservatively; this API does not promise incremental node updates.",[10,464,466],{"id":465},"reading-resources-and-fallback","Reading resources and fallback",[15,468,469,470,473,474,477,478,481,482,485,486,489,490,66],{},"For IO, declare ",[19,471,472],{},"load(context, signal)"," returning ",[19,475,476],{},"ResourceLease\u003CResource>"," (",[19,479,480],{},"{ value, release? }",")\nor a Promise of it. ",[19,483,484],{},"render"," runs when the resource is ready. Snapshot replacement and unmount abort\nthe signal. Late resources still call ",[19,487,488],{},"release()"," even if the loader ignores cancellation. Reading\nand editing NodeViews own separate lifetimes; the adapter does not automatically reuse ",[19,491,492],{},"addSource().load",[15,494,495],{},"Pending or failed loads retain exact source, including during server rendering. SSR never starts\nloading; synchronous renderers can render on the server. Vue children inherit host providers, and\nclient-side child errors stay local to the node. Renderers are trusted host code; raw Markdown is\nnever executed as HTML.",[15,497,498,499,502,503,506,507,254,510,254,513,254,515,518],{},"Listen with ",[19,500,501],{},"\u003CMdProse @error=\"reportReadingError\" \u002F>"," for ",[19,504,505],{},"MdReadingError",", including owner,\n",[19,508,509],{},"read",[19,511,512],{},"load",[19,514,484],{},[19,516,517],{},"dispose"," phase, original error and UTF-16 bounds in the captured snapshot.\nThese diagnostic bounds are not safe asynchronous write targets. Without an error listener, the Vue\nentry preserves visible source; hosts can add logging. Disposal failures are reported without\nreplacing successfully rendered new content.",[520,521,522,537],"table",{},[523,524,525],"thead",{},[113,526,527,531,534],{},[528,529,530],"th",{},"Scenario",[528,532,533],{},"Editing \u002F source",[528,535,536],{},"Static reading",[538,539,540,552,563,574,585,598,609,620,631],"tbody",{},[113,541,542,546,549],{},[543,544,545],"td",{},"Valid custom fields",[543,547,548],{},"Original patch makes precise, undoable changes",[543,550,551],{},"Same codec defaults and validation",[113,553,554,557,560],{},[543,555,556],{},"Invalid fields, unknown or unclosed syntax",[543,558,559],{},"Original text retained",[543,561,562],{},"Built-in rendering or source when no contribution claims it",[113,564,565,568,571],{},[543,566,567],{},"Tokens in inline or fenced code",[543,569,570],{},"Remain code text",[543,572,573],{},"Not parsed again as custom nodes",[113,575,576,579,582],{},[543,577,578],{},"Multiple semantic candidates for one syntax",[543,580,581],{},"Existing installation order",[543,583,584],{},"First codec returning non-null owns the node",[113,586,587,592,595],{},[543,588,589,591],{},[19,590,484],{}," returns undefined \u002F null",[543,593,594],{},"Source unchanged",[543,596,597],{},"Undefined retains exact source; null explicitly produces no content",[113,599,600,603,606],{},[543,601,602],{},"Pending\u002Frejected loads or render errors",[543,604,605],{},"Source unchanged, surrounding blocks intact",[543,607,608],{},"Only the failing node falls back; errors can be observed",[113,610,611,614,617],{},[543,612,613],{},"Locked mode",[543,615,616],{},"Field and history writes rejected",[543,618,619],{},"Same content remains readable",[113,621,622,625,628],{},[543,623,624],{},"Missing source codec \u002F duplicate reading owner",[543,626,627],{},"Installation explicitly rejected",[543,629,630],{},"No invalid reading contribution",[113,632,633,636,639],{},[543,634,635],{},"React or Word\u002FPDF\u002FOFD export",[543,637,638],{},"Framework-neutral semantics retained",[543,640,641],{},"This adapter serves Vue reading; exports need separate conversion contributions",[10,643,645],{"id":644},"lifecycle-constraints","Lifecycle constraints",[15,647,648],{},"A node component may unmount when it leaves the viewport, reveals source, switches documents, or is destroyed. Write persistent fields back to source and choose suitable lifetimes for transient UI state; do not assume the component stays mounted forever.",[10,650,652],{"id":651},"read-patch-and-validation-contracts","Read, patch and validation contracts",[15,654,655,657,658,661],{},[19,656,43],{}," admits writable fields. Defaults and validation do not replace source encoding. ",[19,659,660],{},"read()"," returning null means the confirmed syntax is not a supported semantic form; preserve it rather than normalizing it.",[15,663,664],{},"A patch receives partial fields, not necessarily every attribute. Return one edit, disjoint edits or null. Replace only intended field ranges and preserve spacing, quote style and delimiters. Encode or reject values containing delimiter syntax instead of interpolating them blindly.",[15,666,667],{},"Unknown or invalid fields are rejected. For several source spellings, define reversible writes first. Unsupported writable forms may remain readable; do not claim editing merely because rendering works.",[10,669,671],{"id":670},"source-fingerprints","Source fingerprints",[15,673,674,677],{},[19,675,676],{},"source(document, node)"," may define a smaller exact fingerprint, but it must include every patchable field. A code-language control can fingerprint the header; a body editor cannot use that same narrow fingerprint.",[15,679,680],{},"Fingerprints prevent stale components from editing different content. They are not durable business IDs. Neither are viewport view IDs. Persist required identifiers as explicitly validated source fields.",[10,682,684],{"id":683},"asynchronous-components","Asynchronous components",[15,686,687,690,691,694,695,102,697,225,700,703,704,708],{},[19,688,689],{},"defineSource\u003CAttrs, Resource>()"," can add ",[19,692,693],{},"load(attrs, signal)",". The view receives loading\u002Fready\u002Ferror status and an optional resource. Keep source writes behind guarded actions such as ",[19,696,228],{},[19,698,699],{},"deleteNode()",[19,701,702],{},"revealSource()",". Read ",[458,705,707],{"href":706},"\u002Fen\u002Fextensions\u002Fresources","Resources"," before adding uploads or server queries.",[10,710,712],{"id":711},"choosing-block-behavior","Choosing block behavior",[15,714,715,716,720,721,725,726,66],{},"This example's literal syntax is a single-line, non-nested token. The helper is unsuitable for arbitrary multiline syntax or nested containers. Use native Markdown body ownership for ",[458,717,719],{"href":718},"\u002Fen\u002Fextensions\u002Fcontainers","containers",", block layers for ",[458,722,724],{"href":723},"\u002Fen\u002Fextensions\u002Fdiagrams","diagrams",", and explicit grammar for ",[458,727,729],{"href":728},"\u002Fen\u002Fextensions\u002Fmarkdown-plugins","parser plugins",[10,731,733],{"id":732},"completion-criteria","Completion criteria",[15,735,736],{},"Cover valid, invalid, unclosed and escaped forms; exact field writes; undo; readonly rejection; obsolete resource cleanup; and actions after unmount. Check component baseline, native selection, focus and both themes at narrow widths.",[738,739,740],"style",{},"html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}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 .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 .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 .sDD9d, html code.shiki .sDD9d{--shiki-default:#D73A49;--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sUNQB, html code.shiki .sUNQB{--shiki-default:#005CC5;--shiki-light:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sde8i, html code.shiki .sde8i{--shiki-default:#E36209;--shiki-light:#E36209;--shiki-dark:#FFAB70}",{"title":121,"searchDepth":149,"depth":149,"links":742},[743,744,745,746,747,748,749,750,751,752,753,754,755],{"id":12,"depth":149,"text":13},{"id":29,"depth":149,"text":30},{"id":69,"depth":149,"text":70},{"id":83,"depth":149,"text":84},{"id":205,"depth":149,"text":206},{"id":239,"depth":149,"text":240},{"id":465,"depth":149,"text":466},{"id":644,"depth":149,"text":645},{"id":651,"depth":149,"text":652},{"id":670,"depth":149,"text":671},{"id":683,"depth":149,"text":684},{"id":711,"depth":149,"text":712},{"id":732,"depth":149,"text":733},"Follow a clickable status token through syntax, fields, commands, and component integration.","md",[759,760,761,762,763,764,765],"Node","defineSource","patch","addNodeView","withMdReading","custom UI","status","en",{},0,"\u002Fen\u002Fextensions\u002Fcustom-node","Extensions",{"title":5,"description":756},"extensions\u002Fcustom-node.en","l6a9Z2Yg5jWNIwhndcoNx3PN79UdKw8y4SN9Y2PpdSU",1793046312424]