[{"data":1,"prerenderedAt":696},["ShallowReactive",2],{"msfe-doc:\u002Fen\u002Fextensions\u002Fprojections":3},{"id":4,"title":5,"body":6,"description":683,"extension":684,"keywords":685,"locale":688,"meta":689,"navigation":690,"order":226,"path":691,"section":692,"seo":693,"stem":694,"__hash__":695},"docs\u002Fextensions\u002Fprojections.en.md","Projection layers and component lifetime",{"type":7,"value":8,"toc":674},"minimark",[9,14,23,117,121,124,140,144,310,316,331,342,353,357,364,367,404,419,517,524,547,551,601,604,637,641,652,659,663,670],[10,11,13],"h2",{"id":12},"source-descriptions-and-dom","Source, descriptions and DOM",[15,16,17,18,22],"p",{},"A codec interprets source; a projection builds declarative descriptions from accepted inputs; a renderer creates DOM or framework components. Never initialize charts, fetch data or dispatch transactions from ",[19,20,21],"code",{},"build()",".",[24,25,26,42],"table",{},[27,28,29],"thead",{},[30,31,32,36,39],"tr",{},[33,34,35],"th",{},"Output",[33,37,38],{},"Typical use",[33,40,41],{},"Constraint",[43,44,45,59,72,85,102],"tbody",{},[30,46,47,53,56],{},[48,49,50],"td",{},[19,51,52],{},"mark",[48,54,55],{},"Highlighting and annotations",[48,57,58],{},"Canonical source coordinates",[30,60,61,66,69],{},[48,62,63],{},[19,64,65],{},"line",[48,67,68],{},"Native paragraph\u002Fcontainer styles",[48,70,71],{},"Anchor at the line start",[30,73,74,79,82],{},[48,75,76],{},[19,77,78],{},"widget",[48,80,81],{},"Controls and hints",[48,83,84],{},"Must not own body input",[30,86,87,96,99],{},[48,88,89,92,93],{},[19,90,91],{},"replace"," + ",[19,94,95],{},"text",[48,97,98],{},"Source-neutral literal display",[48,100,101],{},"Text is not interpreted as HTML",[30,103,104,111,114],{},[48,105,106,92,108],{},[19,107,91],{},[19,109,110],{},"view",[48,112,113],{},"Diagrams and previews",[48,115,116],{},"Multiline structure requires a block layer",[10,118,120],{"id":119},"inline-versus-block","Inline versus block",[15,122,123],{},"An inline layer works within bounded visible source and must not return multiline structural replacements. Block output is provided before CodeMirror computes its viewport and may affect multiline geometry.",[15,125,126,127,130,131,134,135,22],{},"A source NodeView's ",[19,128,129],{},"placement: 'replace' | 'before'"," is an inline view contract, not an arbitrary block editing container. Images may use the dedicated ",[19,132,133],{},"measureHeight"," protocol. Use a block layer for large fence previews, as in the ",[136,137,139],"a",{"href":138},"\u002Fen\u002Fextensions\u002Fdiagrams","diagram example",[10,141,143],{"id":142},"dependencies-and-stable-identity","Dependencies and stable identity",[145,146,151],"pre",{"className":147,"code":148,"language":149,"meta":150,"style":150},"language-ts shiki shiki-themes github-light github-light github-dark","addProjectionLayers() {\n  const name = this.name\n  return [{\n    key: `${name}.highlight`,\n    placement: 'inline',\n    build(input) {\n      return input.query(name).matches.map(match => ({\n        type: 'mark', from: match.from, to: match.to, class: 'my-source-mark',\n      }))\n    },\n  }]\n}\n","ts","",[19,152,153,165,185,194,213,224,240,269,286,292,298,304],{"__ignoreMap":150},[154,155,157,161],"span",{"class":65,"line":156},1,[154,158,160],{"class":159},"sgQzM","addProjectionLayers",[154,162,164],{"class":163},"sVPnS","() {\n",[154,166,168,172,176,179,182],{"class":65,"line":167},2,[154,169,171],{"class":170},"sDD9d","  const",[154,173,175],{"class":174},"sUNQB"," name",[154,177,178],{"class":170}," =",[154,180,181],{"class":174}," this",[154,183,184],{"class":163},".name\n",[154,186,188,191],{"class":65,"line":187},3,[154,189,190],{"class":170},"  return",[154,192,193],{"class":163}," [{\n",[154,195,197,200,204,207,210],{"class":65,"line":196},4,[154,198,199],{"class":163},"    key: ",[154,201,203],{"class":202},"s-HEn","`${",[154,205,206],{"class":163},"name",[154,208,209],{"class":202},"}.highlight`",[154,211,212],{"class":163},",\n",[154,214,216,219,222],{"class":65,"line":215},5,[154,217,218],{"class":163},"    placement: ",[154,220,221],{"class":202},"'inline'",[154,223,212],{"class":163},[154,225,227,230,233,237],{"class":65,"line":226},6,[154,228,229],{"class":159},"    build",[154,231,232],{"class":163},"(",[154,234,236],{"class":235},"sde8i","input",[154,238,239],{"class":163},") {\n",[154,241,243,246,249,252,255,258,260,263,266],{"class":65,"line":242},7,[154,244,245],{"class":170},"      return",[154,247,248],{"class":163}," input.",[154,250,251],{"class":159},"query",[154,253,254],{"class":163},"(name).matches.",[154,256,257],{"class":159},"map",[154,259,232],{"class":163},[154,261,262],{"class":235},"match",[154,264,265],{"class":170}," =>",[154,267,268],{"class":163}," ({\n",[154,270,272,275,278,281,284],{"class":65,"line":271},8,[154,273,274],{"class":163},"        type: ",[154,276,277],{"class":202},"'mark'",[154,279,280],{"class":163},", from: match.from, to: match.to, class: ",[154,282,283],{"class":202},"'my-source-mark'",[154,285,212],{"class":163},[154,287,289],{"class":65,"line":288},9,[154,290,291],{"class":163},"      }))\n",[154,293,295],{"class":65,"line":294},10,[154,296,297],{"class":163},"    },\n",[154,299,301],{"class":65,"line":300},11,[154,302,303],{"class":163},"  }]\n",[154,305,307],{"class":65,"line":306},12,[154,308,309],{"class":163},"}\n",[15,311,312,313,315],{},"Layer keys are globally unique; view keys are stable within a layer. Do not create new renderer functions inside ",[19,314,21],{},", which would remount components unnecessarily. A module-level renderer may be shared while all instance resources remain scoped.",[15,317,318,319,322,323,326,327,330],{},"Set ",[19,320,321],{},"selection: true"," if the builder reads ",[19,324,325],{},"input.selection",". List state slots read by a layer in ",[19,328,329],{},"states",". Ordinary source reveal is already handled separately; it does not require rebuilding every chart on cursor movement.",[15,332,333,334,337,338,341],{},"Modes default to live and locked. ",[19,335,336],{},"suppressDuringComposition"," defaults to true: in Markdown editing, only descriptions intersecting the IME selection temporarily reveal source. Replacements use their complete ",[19,339,340],{},"revealRange","; line descriptions use the source line. Other blocks retain presentation and component identity. Hidden-syntax document editing keeps projections while CodeMirror owns composition in native text.",[15,343,344,345,348,349,352],{},"Composition boundaries rematerialize existing descriptions without rebuilding every layer. Even when previews remain mounted, edit actions must check ",[19,346,347],{},"readSourcePresentation(editor.state).composing"," before moving selection or opening an editor, so they cannot take IME focus. Keep input protection enabled. Competing replacements may fall back to source; inspect ",[19,350,351],{},"editor.work.projectionConflicts"," for ownership conflicts.",[10,354,356],{"id":355},"updating-affected-blocks-during-ordinary-input","Updating affected blocks during ordinary input",[15,358,359,360,363],{},"Declare ",[19,361,362],{},"incremental: true"," when output depends only on its syntax block. Block layers receive changed complete syntax blocks; untouched descriptions move with the source and retain component identity. Inline layers still query visible ranges and reuse mapped component identities. Omitting the option keeps complete builds for plugins that depend on the whole document.",[15,365,366],{},"Before opting in, satisfy these requirements:",[368,369,370,381,387,397],"ul",{},[371,372,373,374,377,378,380],"li",{},"Use ",[19,375,376],{},"input.query(name)"," or source within the current range. Output and ",[19,379,340],{}," must belong to the queried blocks. Do not scan the entire document, number every block or build a document-wide outline in this path.",[371,382,359,383,386],{},[19,384,385],{},"references: true"," when links depend on definitions elsewhere. Adding, deleting or changing a definition rebuilds the layer. Keep conservative builds for other global dependencies.",[371,388,389,390,393,394,396],{},"Continue declaring ",[19,391,392],{},"selection"," and ",[19,395,329],{},". Permission, mode or declared dependency changes trigger complete evaluation.",[371,398,399,400,403],{},"Within one build, each ",[19,401,402],{},"key"," identifies one source placement. Keep the renderer stable. The runtime maps component identity through source changes; replacing or deleting the entire block ends its old identity.",[15,405,406,407,410,411,414,415,418],{},"Map source positions in component props explicitly with the third argument of ",[19,408,409],{},"componentView",". This view fragment belongs inside ",[19,412,413],{},"input.query(name).matches.map(...)","; ",[19,416,417],{},"renderPreview"," is a module-level renderer:",[145,420,422],{"className":147,"code":421,"language":149,"meta":150,"style":150},"view: componentView(renderPreview, {\n  source: input.document.slice(match.from, match.to),\n  range: { from: match.from, to: match.to },\n}, (props, offset) => ({\n  ...props,\n  range: {\n    from: props.range.from + offset,\n    to: props.range.to + offset,\n  },\n}))\n",[19,423,424,436,447,452,474,482,487,498,507,512],{"__ignoreMap":150},[154,425,426,428,431,433],{"class":65,"line":156},[154,427,110],{"class":159},[154,429,430],{"class":163},": ",[154,432,409],{"class":159},[154,434,435],{"class":163},"(renderPreview, {\n",[154,437,438,441,444],{"class":65,"line":167},[154,439,440],{"class":163},"  source: input.document.",[154,442,443],{"class":159},"slice",[154,445,446],{"class":163},"(match.from, match.to),\n",[154,448,449],{"class":65,"line":187},[154,450,451],{"class":163},"  range: { from: match.from, to: match.to },\n",[154,453,454,457,460,463,466,469,472],{"class":65,"line":196},[154,455,456],{"class":163},"}, (",[154,458,459],{"class":235},"props",[154,461,462],{"class":163},", ",[154,464,465],{"class":235},"offset",[154,467,468],{"class":163},") ",[154,470,471],{"class":170},"=>",[154,473,268],{"class":163},[154,475,476,479],{"class":65,"line":215},[154,477,478],{"class":170},"  ...",[154,480,481],{"class":163},"props,\n",[154,483,484],{"class":65,"line":226},[154,485,486],{"class":163},"  range: {\n",[154,488,489,492,495],{"class":65,"line":242},[154,490,491],{"class":163},"    from: props.range.from ",[154,493,494],{"class":170},"+",[154,496,497],{"class":163}," offset,\n",[154,499,500,503,505],{"class":65,"line":271},[154,501,502],{"class":163},"    to: props.range.to ",[154,504,494],{"class":170},[154,506,497],{"class":163},[154,508,509],{"class":65,"line":288},[154,510,511],{"class":163},"  },\n",[154,513,514],{"class":65,"line":294},[154,515,516],{"class":163},"}))\n",[15,518,519,520,523],{},"The mapper only returns new props. It must not create DOM, acquire resources or dispatch transactions. The renderer's ",[19,521,522],{},"update(next)"," must retain the latest props; click handlers must not capture the initial source position. Reuse the chart instance when preview content is unchanged. After typing before a preview, its edit action then targets the correct block even if that preview mounts only after scrolling into view.",[15,525,526,527,462,530,462,533,462,536,393,539,542,543,546],{},"Local updates do not mean parsing only the caret line. Changing fences, list\u002Fcontainer boundaries, table delimiters or reference definitions can legitimately affect surrounding blocks. The runtime expands invalidation according to accepted syntax changes. Compare ",[19,528,529],{},"syntaxDiffBuilds",[19,531,532],{},"syntaxDiffChunks",[19,534,535],{},"syntaxDiffComparisons",[19,537,538],{},"layerVisitedNodes",[19,540,541],{},"blockBuilds"," in ",[19,544,545],{},"editor.work",", and verify DOM\u002Fresource reuse. Existing descriptions still need mapping and decoration materialization; constant cost for every input is not guaranteed.",[10,548,550],{"id":549},"componentview-versus-nodeview","ComponentView versus NodeView",[24,552,553,566],{},[27,554,555],{},[30,556,557,560,563],{},[33,558,559],{},"Contract",[33,561,562],{},"Input",[33,564,565],{},"Use",[43,567,568,587],{},[30,569,570,573,584],{},[48,571,572],{},"NodeView",[48,574,575,462,578,462,581],{},[19,576,577],{},"nodeView.attrs",[19,579,580],{},"updateAttributes",[19,582,583],{},"getRange",[48,585,586],{},"Source-field controls",[30,588,589,592,598],{},[48,590,591],{},"ComponentView",[48,593,594,595,597],{},"Feature-specific ",[19,596,110],{}," props",[48,599,600],{},"Search, disclosure and chart UI",[15,602,603],{},"Use the corresponding Vue or React renderer and a Host inside the existing Provider tree. Do not create an independent framework app per node.",[15,605,606,607,610,611,462,614,462,616,462,619,393,622,625,626,462,629,632,633,636],{},"A native ",[19,608,609],{},"ComponentViewRenderer\u003CProps>"," receives ",[19,612,613],{},"dom",[19,615,459],{},[19,617,618],{},"host",[19,620,621],{},"scope",[19,623,624],{},"requestMeasure",", and returns ",[19,627,628],{},"update",[19,630,631],{},"destroy"," and optional ",[19,634,635],{},"ignoreEvent",". Event ownership defaults to the component; return false only for events intentionally delegated to CodeMirror.",[10,638,640],{"id":639},"measurement-and-teardown","Measurement and teardown",[15,642,643,644,647,648,651],{},"The runtime owns the mount container. Renderers own their child DOM, listeners, observers and external instances. Never mutate ",[19,645,646],{},".cm-content",". Call the coalesced ",[19,649,650],{},"requestMeasure()"," after asynchronous geometry changes.",[15,653,654,655,22],{},"Reuse resources for permission-only updates. Cancel obsolete work when source or rendering parameters change. Destroy framework entries and release scope-owned resources, including unfinished loads. See ",[136,656,658],{"href":657},"\u002Fen\u002Fextensions\u002Fresources","Resource lifetime",[10,660,662],{"id":661},"interaction-verification","Interaction verification",[15,664,665,666,22],{},"Cover drag selection across previews, keyboard reveal, search, readonly copying, composition, viewport unmounts, rapid updates and document replacement. Inspect work-count deltas and resource counts as well as final appearance. Cursor movement should not parse the whole document or initialize every chart again. See ",[136,667,669],{"href":668},"\u002Fen\u002Fapi\u002Frendering","Rendering API",[671,672,673],"style",{},"html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}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 .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sde8i, html code.shiki .sde8i{--shiki-default:#E36209;--shiki-light:#E36209;--shiki-dark:#FFAB70}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":150,"searchDepth":167,"depth":167,"links":675},[676,677,678,679,680,681,682],{"id":12,"depth":167,"text":13},{"id":119,"depth":167,"text":120},{"id":142,"depth":167,"text":143},{"id":355,"depth":167,"text":356},{"id":549,"depth":167,"text":550},{"id":639,"depth":167,"text":640},{"id":661,"depth":167,"text":662},"Implement inline decoration, multiline previews, declared dependencies and measurement.","md",[686,591,572,624,687],"ProjectionLayer","block","en",{},true,"\u002Fen\u002Fextensions\u002Fprojections","Extensions",{"title":5,"description":683},"extensions\u002Fprojections.en","ZebCilwsYO3TC4OUMfjHqev3xA5XYGIjudUlOW42ABw",1793046312619]