[{"data":1,"prerenderedAt":691},["ShallowReactive",2],{"msfe-doc:\u002Fen\u002Fextensions\u002Fmarkdown-plugins":3},{"id":4,"title":5,"body":6,"description":677,"extension":678,"keywords":679,"locale":684,"meta":685,"navigation":201,"order":185,"path":686,"section":687,"seo":688,"stem":689,"__hash__":690},"docs\u002Fextensions\u002Fmarkdown-plugins.en.md","Markdown plugins",{"type":7,"value":8,"toc":666},"minimark",[9,14,88,96,100,105,115,354,367,380,398,420,426,429,433,440,444,448,471,493,506,510,522,600,615,619,622,626,645,662],[10,11,13],"h2",{"id":12},"distinguish-three-plugin-responsibilities","Distinguish three plugin responsibilities",[15,16,17,33],"table",{},[18,19,20],"thead",{},[21,22,23,27,30],"tr",{},[24,25,26],"th",{},"Capability",[24,28,29],{},"MSFE entry",[24,31,32],{},"Mutates source?",[34,35,36,52,72],"tbody",{},[21,37,38,42,49],{},[39,40,41],"td",{},"Recognize syntax",[39,43,44,45],{},"Lezer extensions through ",[46,47,48],"code",{},"addMarkdownExtensions()",[39,50,51],{},"No",[21,53,54,57,69],{},[39,55,56],{},"Render recognized syntax",[39,58,59,62,63,62,66],{},[46,60,61],{},"addSource()",", ",[46,64,65],{},"addNodeView()",[46,67,68],{},"addProjectionLayers()",[39,70,71],{},"Rendering itself does not",[21,73,74,77,85],{},[39,75,76],{},"Normalize typed or pasted input",[39,78,79,62,82],{},[46,80,81],{},"InputRule",[46,83,84],{},"PasteRule",[39,86,87],{},"Yes, through transactions and history",[89,90,91,92,95],"p",{},"markdown-it plugins consume a token stream, rule chain and HTML renderer. MSFE uses Lezer's incremental tree. A plugin intended for ",[46,93,94],{},"md.use(plugin)"," cannot be installed directly as an MSFE extension. A third-party renderer may run inside an isolated non-editable preview, but source ranges, parsing and writes still need integration.",[10,97,99],{"id":98},"a-working-parser-plugin","A working parser plugin",[101,102,104],"h3",{"id":103},"optional-document-properties","Optional document properties",[89,106,107,110,111,114],{},[46,108,109],{},"Frontmatter"," recognizes source only. Explicitly install ",[46,112,113],{},"DocumentProperties"," to display a leading YAML\nheader as a property table. MD Editor does not install it by default. Vue consumers can reuse the public form:",[116,117,122],"pre",{"className":118,"code":119,"language":120,"meta":121,"style":121},"language-vue shiki shiki-themes github-light github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { MdEditor, MdDocumentProperties } from '@quirekit\u002Fmd-editor\u002Fvue'\nimport { DocumentProperties } from '@quirekit\u002Fmsfe-markdown-extras'\nimport { VueComponentViewRenderer } from '@quirekit\u002Fmsfe-vue'\n\nconst extensions = [DocumentProperties.configure({\n  render: VueComponentViewRenderer(MdDocumentProperties), locale: 'en',\n  defaultCollapsed: true,\n})]\nconst markdown = '---\\ntitle: Example\\ntags: [writing, editing]\\n---\\n\\nBody'\n\u003C\u002Fscript>\n\u003Ctemplate>\u003CMdEditor :markdown=\"markdown\" :extensions=\"extensions\" \u002F>\u003C\u002Ftemplate>\n","vue","",[46,123,124,154,170,183,196,203,225,243,254,260,295,305],{"__ignoreMap":121},[125,126,129,133,137,141,144,147,151],"span",{"class":127,"line":128},"line",1,[125,130,132],{"class":131},"sVPnS","\u003C",[125,134,136],{"class":135},"su0ou","script",[125,138,140],{"class":139},"sgQzM"," setup",[125,142,143],{"class":139}," lang",[125,145,146],{"class":131},"=",[125,148,150],{"class":149},"s-HEn","\"ts\"",[125,152,153],{"class":131},">\n",[125,155,157,161,164,167],{"class":127,"line":156},2,[125,158,160],{"class":159},"sDD9d","import",[125,162,163],{"class":131}," { MdEditor, MdDocumentProperties } ",[125,165,166],{"class":159},"from",[125,168,169],{"class":149}," '@quirekit\u002Fmd-editor\u002Fvue'\n",[125,171,173,175,178,180],{"class":127,"line":172},3,[125,174,160],{"class":159},[125,176,177],{"class":131}," { DocumentProperties } ",[125,179,166],{"class":159},[125,181,182],{"class":149}," '@quirekit\u002Fmsfe-markdown-extras'\n",[125,184,186,188,191,193],{"class":127,"line":185},4,[125,187,160],{"class":159},[125,189,190],{"class":131}," { VueComponentViewRenderer } ",[125,192,166],{"class":159},[125,194,195],{"class":149}," '@quirekit\u002Fmsfe-vue'\n",[125,197,199],{"class":127,"line":198},5,[125,200,202],{"emptyLinePlaceholder":201},true,"\n",[125,204,206,209,213,216,219,222],{"class":127,"line":205},6,[125,207,208],{"class":159},"const",[125,210,212],{"class":211},"sUNQB"," extensions",[125,214,215],{"class":159}," =",[125,217,218],{"class":131}," [DocumentProperties.",[125,220,221],{"class":139},"configure",[125,223,224],{"class":131},"({\n",[125,226,228,231,234,237,240],{"class":127,"line":227},7,[125,229,230],{"class":131},"  render: ",[125,232,233],{"class":139},"VueComponentViewRenderer",[125,235,236],{"class":131},"(MdDocumentProperties), locale: ",[125,238,239],{"class":149},"'en'",[125,241,242],{"class":131},",\n",[125,244,246,249,252],{"class":127,"line":245},8,[125,247,248],{"class":131},"  defaultCollapsed: ",[125,250,251],{"class":211},"true",[125,253,242],{"class":131},[125,255,257],{"class":127,"line":256},9,[125,258,259],{"class":131},"})]\n",[125,261,263,265,268,270,273,276,279,281,284,286,289,292],{"class":127,"line":262},10,[125,264,208],{"class":159},[125,266,267],{"class":211}," markdown",[125,269,215],{"class":159},[125,271,272],{"class":149}," '---",[125,274,275],{"class":211},"\\n",[125,277,278],{"class":149},"title: Example",[125,280,275],{"class":211},[125,282,283],{"class":149},"tags: [writing, editing]",[125,285,275],{"class":211},[125,287,288],{"class":149},"---",[125,290,291],{"class":211},"\\n\\n",[125,293,294],{"class":149},"Body'\n",[125,296,298,301,303],{"class":127,"line":297},11,[125,299,300],{"class":131},"\u003C\u002F",[125,302,136],{"class":135},[125,304,153],{"class":131},[125,306,308,310,313,316,319,322,325,327,330,332,334,336,339,341,343,345,347,350,352],{"class":127,"line":307},12,[125,309,132],{"class":131},[125,311,312],{"class":135},"template",[125,314,315],{"class":131},">\u003C",[125,317,318],{"class":135},"MdEditor",[125,320,321],{"class":131}," :",[125,323,324],{"class":139},"markdown",[125,326,146],{"class":131},[125,328,329],{"class":149},"\"",[125,331,324],{"class":131},[125,333,329],{"class":149},[125,335,321],{"class":131},[125,337,338],{"class":139},"extensions",[125,340,146],{"class":131},[125,342,329],{"class":149},[125,344,338],{"class":131},[125,346,329],{"class":149},[125,348,349],{"class":131}," \u002F>\u003C\u002F",[125,351,312],{"class":135},[125,353,153],{"class":131},[89,355,356,357,359,360,362,363,366],{},"MD Editor includes ",[46,358,109],{},"; direct MSFE Core consumers must install it alongside ",[46,361,113],{},".\nOmitting ",[46,364,365],{},"render"," uses a native property display and a source entry point.",[89,368,369,372,373,375,376,379],{},[46,370,371],{},"setDocumentPropertiesEnabled(editor, false)"," restores the literal Frontmatter; ",[46,374,251],{}," enables the table again.\nIt preserves the instance, undo history, body and selection. Read the applied preference with\n",[46,377,378],{},"documentPropertiesEnabled(editor.state)",". The example page uses this public API for its Document properties switch.",[89,381,382,385,386,389,390,393,394,397],{},[46,383,384],{},"defaultCollapsed: true"," initially shows only the heading and count, keeping the document body prominent.\nOmit it or use ",[46,387,388],{},"false"," to start expanded. Both the Vue panel and native renderer honor this setting.\nManual choices survive edits, mode changes, projection toggles and viewport remounts within the same document.\n",[46,391,392],{},"replaceDocument"," reapplies the default for the next document. ",[46,395,396],{},"enabled: false"," shows literal YAML instead\nof a collapsed property panel. Hosts can persist a preference across sessions and pass it as the next default.",[89,399,400,401,404,405,408,409,411,412,415,416,419],{},"Read the current state with ",[46,402,403],{},"documentPropertiesCollapsed(editor.state)",". Call\n",[46,406,407],{},"setDocumentPropertiesCollapsed(editor, true)"," to collapse or pass ",[46,410,388],{}," to expand. Custom renderers use\n",[46,413,414],{},"view.collapsed"," and ",[46,417,418],{},"view.setCollapsed()",". Readonly mode still allows folding without changing edit permissions.",[89,421,422,425],{},[46,423,424],{},"MdDocumentProperties"," initially previews six fields; long text and tag lists\nexpand individually. Click a value to edit it with confirmation and cancellation on the same row. Enter saves\na single-line field, Escape cancels, and ⌘\u002FCtrl + Enter saves multiline fields. Collapsing retains unsaved drafts;\nexpansion state is never written to Markdown.",[89,427,428],{},"Strings, numbers, booleans and string lists are editable through source transactions and undo. Other values\nremain literal. Invalid YAML, duplicate keys, aliases, anchors, explicit tags, flow or indented root maps,\nexplicit or multiline keys, TOML, unclosed headers and headers over 20,000 UTF-16 units fall back to source.\nNew property names are always encoded as string keys. Edits replace only the target range, retaining other fields,\ncomments and blank lines. Obsolete forms are rejected after a document or header change. Source mode always\nshows the original header; readonly editors expose no modification controls.",[101,430,432],{"id":431},"a-custom-badge","A custom badge",[89,434,435,436,439],{},"The example recognizes ",[46,437,438],{},":badge[label]:",", retains delimiters and adds theme color. Unclosed, oversized or nested-bracket forms remain ordinary source. The same spelling inside a code span is not a badge.",[441,442],"msfe-demo",{"name":443},"plugins\u002Feditor",[10,445,447],{"id":446},"_1-contribute-syntax","1. Contribute syntax",[89,449,450,451,454,455,458,459,462,463,466,467,470],{},"Depend on ",[46,452,453],{},"@quirekit\u002Flezer-markdown"," and return a ",[46,456,457],{},"MarkdownExtension",". ",[46,460,461],{},"plugins\u002Fsyntax.ts"," registers ",[46,464,465],{},"ExampleBadge"," through ",[46,468,469],{},"defineNodes",". Its inline parser checks the exact prefix at a colon and examines at most 82 following units for the closing form.",[89,472,473,476,477,480,481,484,485,488,489,492],{},[46,474,475],{},"context.elt()"," uses canonical source coordinates, and ",[46,478,479],{},"addElement()"," returns the consumed endpoint. Return ",[46,482,483],{},"-1"," when not applicable. ",[46,486,487],{},"before"," \u002F ",[46,490,491],{},"after"," controls parser ordering, not visual priority. Nesting is intentionally unsupported here. More complex syntax needs explicit block\u002Finline rules and fallback behavior.",[89,494,495,496,499,500,505],{},"For simple, non-nested, single-line literal delimiters, use ",[46,497,498],{},"defineSource({ syntax })"," as in the ",[501,502,504],"a",{"href":503},"\u002Fen\u002Fextensions\u002Fcustom-node","status node",". Avoid maintaining a second whole-document parser for a simple token.",[10,507,509],{"id":508},"_2-add-semantic-queries","2. Add semantic queries",[89,511,512,514,515,517,518,521],{},[46,513,61],{}," reads confirmed ",[46,516,465],{}," nodes as ",[46,519,520],{},"{ label }",". Read only the matched range. Add attribute validation and a precise patch if fields become editable. The semantic layer does not need HTML rendering.",[116,523,527],{"className":524,"code":525,"language":526,"meta":121,"style":121},"language-ts shiki shiki-themes github-light github-light github-dark","const query = editor.querySource('exampleBadge')\nif (query.status === 'ready') {\n  for (const match of query.matches) console.log(match.from, match.to, match.attrs)\n}\n","ts",[46,528,529,553,570,595],{"__ignoreMap":121},[125,530,531,533,536,538,541,544,547,550],{"class":127,"line":128},[125,532,208],{"class":159},[125,534,535],{"class":211}," query",[125,537,215],{"class":159},[125,539,540],{"class":131}," editor.",[125,542,543],{"class":139},"querySource",[125,545,546],{"class":131},"(",[125,548,549],{"class":149},"'exampleBadge'",[125,551,552],{"class":131},")\n",[125,554,555,558,561,564,567],{"class":127,"line":156},[125,556,557],{"class":159},"if",[125,559,560],{"class":131}," (query.status ",[125,562,563],{"class":159},"===",[125,565,566],{"class":149}," 'ready'",[125,568,569],{"class":131},") {\n",[125,571,572,575,578,580,583,586,589,592],{"class":127,"line":172},[125,573,574],{"class":159},"  for",[125,576,577],{"class":131}," (",[125,579,208],{"class":159},[125,581,582],{"class":211}," match",[125,584,585],{"class":159}," of",[125,587,588],{"class":131}," query.matches) console.",[125,590,591],{"class":139},"log",[125,593,594],{"class":131},"(match.from, match.to, match.attrs)\n",[125,596,597],{"class":127,"line":185},[125,598,599],{"class":131},"}\n",[89,601,602,603,606,607,610,611,614],{},"Queries default to the current selections or accept an explicit bounded range. ",[46,604,605],{},"pending"," means parsing is unconfirmed, not that syntax is absent; ",[46,608,609],{},"unsupported"," means the semantic entry is unavailable. Use ",[46,612,613],{},"requestMarkdownParse(editor.state)"," to register demand, then retry on accepted-state notification. Do not synchronously parse the whole document during input.",[10,616,618],{"id":617},"_3-choose-presentation-and-editing","3. Choose presentation and editing",[89,620,621],{},"The example's inline layer returns marks and uses a theme. It does not hide delimiters. Use a NodeView for interactive controls or a block layer for multiline replacements. A display plugin should not silently normalize source when loaded.",[10,623,625],{"id":624},"porting-a-markdown-it-plugin","Porting a markdown-it plugin",[627,628,629,633,636,639,642],"ol",{},[630,631,632],"li",{},"Describe syntax, input\u002Foutput, nesting, escapes and references.",[630,634,635],{},"Implement Lezer handling for incomplete input, code contexts and nesting.",[630,637,638],{},"Describe fields in a source codec; keep fields readonly until exact encoding exists.",[630,640,641],{},"Render through projections or components; implement HTML export independently.",[630,643,644],{},"Create source fixtures for unchanged reads, precise writes, undo, failures, readonly and incomplete parsing.",[89,646,647,648,652,653,415,657,661],{},"Many common plugins already have built-in equivalents. Check ",[501,649,651],{"href":650},"\u002Fen\u002Ffeatures\u002Fcatalog","Features and installation",", then ",[501,654,656],{"href":655},"\u002Fen\u002Fextensions\u002Finput-rules","Input rules",[501,658,660],{"href":659},"\u002Fen\u002Fextensions\u002Fprojections","Projection layers",".",[663,664,665],"style",{},"html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su0ou, html code.shiki .su0ou{--shiki-default:#22863A;--shiki-light:#22863A;--shiki-dark:#85E89D}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 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 .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);}",{"title":121,"searchDepth":156,"depth":156,"links":667},[668,669,673,674,675,676],{"id":12,"depth":156,"text":13},{"id":98,"depth":156,"text":99,"children":670},[671,672],{"id":103,"depth":172,"text":104},{"id":431,"depth":172,"text":432},{"id":446,"depth":156,"text":447},{"id":508,"depth":156,"text":509},{"id":617,"depth":156,"text":618},{"id":624,"depth":156,"text":625},"Connect new syntax to incremental parsing, semantic queries and source presentation.","md",[680,681,682,683,543],"Lezer","markdown-it","plugin","parseInline","en",{},"\u002Fen\u002Fextensions\u002Fmarkdown-plugins","Extensions",{"title":5,"description":677},"extensions\u002Fmarkdown-plugins.en","JVMzH9JMzN9QA4U_tX_8I027ui8fVPvGCeaL8gHO7JM",1793046312575]