@barocss/editor-view-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +89 -0
  3. package/dist/editor-view-react/src/EditorView.d.ts +14 -0
  4. package/dist/editor-view-react/src/EditorView.d.ts.map +1 -0
  5. package/dist/editor-view-react/src/EditorViewContentLayer.d.ts +9 -0
  6. package/dist/editor-view-react/src/EditorViewContentLayer.d.ts.map +1 -0
  7. package/dist/editor-view-react/src/EditorViewContext.d.ts +43 -0
  8. package/dist/editor-view-react/src/EditorViewContext.d.ts.map +1 -0
  9. package/dist/editor-view-react/src/EditorViewLayer.d.ts +8 -0
  10. package/dist/editor-view-react/src/EditorViewLayer.d.ts.map +1 -0
  11. package/dist/editor-view-react/src/EditorViewOverlayLayerContent.d.ts +14 -0
  12. package/dist/editor-view-react/src/EditorViewOverlayLayerContent.d.ts.map +1 -0
  13. package/dist/editor-view-react/src/dom-sync/classify-c1.d.ts +45 -0
  14. package/dist/editor-view-react/src/dom-sync/classify-c1.d.ts.map +1 -0
  15. package/dist/editor-view-react/src/dom-sync/edit-position.d.ts +6 -0
  16. package/dist/editor-view-react/src/dom-sync/edit-position.d.ts.map +1 -0
  17. package/dist/editor-view-react/src/index.d.ts +12 -0
  18. package/dist/editor-view-react/src/index.d.ts.map +1 -0
  19. package/dist/editor-view-react/src/input-handler.d.ts +51 -0
  20. package/dist/editor-view-react/src/input-handler.d.ts.map +1 -0
  21. package/dist/editor-view-react/src/mutation-observer-manager.d.ts +13 -0
  22. package/dist/editor-view-react/src/mutation-observer-manager.d.ts.map +1 -0
  23. package/dist/editor-view-react/src/selection-handler.d.ts +56 -0
  24. package/dist/editor-view-react/src/selection-handler.d.ts.map +1 -0
  25. package/dist/editor-view-react/src/types.d.ts +103 -0
  26. package/dist/editor-view-react/src/types.d.ts.map +1 -0
  27. package/dist/index.cjs +4 -0
  28. package/dist/index.js +11882 -0
  29. package/docs/SPEC_VERIFICATION.md +109 -0
  30. package/docs/editor-view-react-spec.md +359 -0
  31. package/docs/improvement-opportunities.md +66 -0
  32. package/docs/layers-spec.md +97 -0
  33. package/package.json +53 -0
  34. package/src/EditorView.tsx +312 -0
  35. package/src/EditorViewContentLayer.tsx +90 -0
  36. package/src/EditorViewContext.tsx +228 -0
  37. package/src/EditorViewLayer.tsx +35 -0
  38. package/src/EditorViewOverlayLayerContent.tsx +42 -0
  39. package/src/dom-sync/classify-c1.ts +91 -0
  40. package/src/dom-sync/edit-position.ts +27 -0
  41. package/src/index.ts +33 -0
  42. package/src/input-handler.ts +716 -0
  43. package/src/mutation-observer-manager.ts +65 -0
  44. package/src/selection-handler.ts +450 -0
  45. package/src/types.ts +123 -0
  46. package/test/EditorView-decorator.test.tsx +198 -0
  47. package/test/EditorView-layers.test.tsx +352 -0
  48. package/test/EditorView.test.tsx +218 -0
  49. package/test/dom-sync.test.ts +49 -0
  50. package/test/mutation-observer-manager.test.ts +48 -0
  51. package/test/selection-handler.test.ts +86 -0
  52. package/tsconfig.json +12 -0
  53. package/vite.config.ts +26 -0
  54. package/vitest.config.ts +10 -0
@@ -0,0 +1,109 @@
1
+ # editor-view-react: Spec vs Implementation Verification
2
+
3
+ This document records the result of verifying the implementation against `editor-view-react-spec.md`.
4
+
5
+ **Verification date**: 2026-02-01
6
+ **Test run**: `pnpm --filter @barocss/editor-view-react test:run` — 23 tests, 4 files.
7
+
8
+ ---
9
+
10
+ ## §1–2 Goals, Architecture
11
+
12
+ | Spec section | Verified | Notes |
13
+ |--------------|----------|--------|
14
+ | §1 Goals and Scope | ✅ | Same Editor/DataStore; renderer-react; no editor-view-dom dependency. |
15
+ | §2 Architecture pipeline | ✅ | EditorViewContextProvider → selectionHandler, inputHandler, mutationObserverManager; EditorView → ContentLayer + overlay layers. |
16
+ | §2 Dependencies | ✅ | dsl, editor-core, renderer-react, shared, text-analyzer (text-run-index API from shared); no editor-view-dom. |
17
+
18
+ ---
19
+
20
+ ## §3 API Contract
21
+
22
+ | Spec section | Verified | Notes |
23
+ |--------------|----------|--------|
24
+ | §3.1 Exports | ✅ | EditorView, EditorViewContentLayer, EditorViewLayer, EditorViewContextProvider, useEditorViewContext, useOptionalEditorViewContext, createMutationObserverManager, types. |
25
+ | §3.2 EditorView props | ✅ | editor, options (registry, className, layers), children. Test: options.className applied; layers.* control overlay presence; children in CustomLayer. |
26
+ | §3.3 EditorViewContentLayer props | ✅ | options (registry, className, editable). Editor from context. |
27
+ | §3.4 EditorViewLayer props | ✅ | layer, className, style, children. Test: data-bc-layer, position absolute, pointer-events none, default classNames/zIndex. |
28
+ | §3.5 EditorView static subcomponents | ✅ | ContentLayer, Layer, DecoratorLayer, SelectionLayer, ContextLayer, CustomLayer. |
29
+
30
+ ---
31
+
32
+ ## §4 Context and View State
33
+
34
+ | Spec section | Verified | Notes |
35
+ |--------------|----------|--------|
36
+ | §4.1 EditorViewContextValue | ✅ | Test: Consumer inside Provider receives editor, selectionHandler, inputHandler, mutationObserverManager, setContentEditableElement. |
37
+ | §4.2 EditorViewViewState | ✅ | viewStateRef with isModelDrivenChange, isRendering, isComposing, skipNextRenderFromMO, skipApplyModelSelectionToDOM (used in implementation). |
38
+ | §4.3 Lifecycle | ✅ | Provider creates handlers and manager; ContentLayer subscribes and setContentEditableElement (implementation only; subscription not asserted in tests). |
39
+
40
+ ---
41
+
42
+ ## §5–6 Layers, Content Layer
43
+
44
+ | Spec section | Verified | Notes |
45
+ |--------------|----------|--------|
46
+ | §5.1 Content layer | ✅ | Test: content layer has data-bc-layer="content", data-testid="editor-content". |
47
+ | §5.2 Overlay layers | ✅ | Test: decorator/selection/custom layers render when options.layers.* set; position absolute, pointer-events none. |
48
+ | §5.3 Conditional rendering | ✅ | Test: overlay layers only when layers.* set; children in CustomLayer. |
49
+ | §6 Document snapshot, ReactRenderer, contenteditable | ✅ | Implementation: getDocumentProxy, editor:content.change, ReactRenderer.build; contentEditable prop. Not asserted in unit tests (would require Editor mock with getDocumentProxy returning model). |
50
+
51
+ ---
52
+
53
+ ## §7 Selection Flow
54
+
55
+ | Spec section | Verified | Notes |
56
+ |--------------|----------|--------|
57
+ | §7.1 DOM → Model | ✅ | Test: setProgrammaticChange(true) causes handleSelectionChange to skip updateSelection. |
58
+ | §7.2 Model → DOM | ✅ | Implementation: editor:selection.model → requestAnimationFrame ×2 → convertModelSelectionToDOM. |
59
+ | §7.3 ReactSelectionHandler | ✅ | Test: isSelectionInsideEditableText returns false when empty, true when inside inline-text node; setProgrammaticChange behavior. convertDOMSelectionToModel not tested (requires full DOM + shared text-run-index). |
60
+
61
+ ---
62
+
63
+ ## §8 Input and DOM Sync
64
+
65
+ | Spec section | Verified | Notes |
66
+ |--------------|----------|--------|
67
+ | §8.1 ReactInputHandler | ✅ | Implementation: beforeinput/keydown, setComposing, syncFocusedTextNodeAfterComposition. Not unit tested. |
68
+ | §8.2 handleDomMutations, C1 | ✅ | Implementation: classifyDomChangeC1, replaceText. classifyDomChangeC1 not unit tested (requires Editor + mutations). |
69
+ | §8.3 dom-sync | ✅ | Test: findClosestInlineTextNode (element, child, no ancestor, null); reconstructModelTextFromDOM (concatenated text, empty). |
70
+
71
+ ---
72
+
73
+ ## §9 MutationObserver
74
+
75
+ | Spec section | Verified | Notes |
76
+ |--------------|----------|--------|
77
+ | §9.1 ReactMutationObserverManager | ✅ | Test: setup(element) observes and invokes callback with batched mutations (setTimeout 0); disconnect() stops observing and callback not called after disconnect. |
78
+ | §9.2 Wiring | ✅ | Implementation: Provider creates manager with onMutations = inputHandler.handleDomMutations; ContentLayer setContentEditableElement. |
79
+
80
+ ---
81
+
82
+ ## §10 Test Strategy
83
+
84
+ | Spec section | Verified | Notes |
85
+ |--------------|----------|--------|
86
+ | §10.2 Required test categories | ✅ | EditorViewContextProvider (value, useEditorViewContext throw, useOptionalEditorViewContext null). EditorView (root, content layer, options.className, overlay layers, children). EditorViewContentLayer (presence and attributes; subscription behavior not asserted). EditorViewLayer (data-bc-layer, style, default classNames/zIndex). ReactSelectionHandler (instantiate, isSelectionInsideEditableText, setProgrammaticChange). ReactMutationObserverManager (setup/disconnect, batch). dom-sync (findClosestInlineTextNode, reconstructModelTextFromDOM). |
87
+ | §10.3 Test environment | ✅ | Vitest, jsdom, @testing-library/react. Mock Editor used in tests. |
88
+
89
+ ---
90
+
91
+ ## Checklist (Concrete) — Spec §11
92
+
93
+ - [x] EditorViewContextProvider provides editor, viewStateRef, selectionHandler, inputHandler, mutationObserverManager, setContentEditableElement.
94
+ - [x] useEditorViewContext throws outside Provider; useOptionalEditorViewContext returns null outside.
95
+ - [x] EditorView renders content layer; overlay layers only when options.layers.* set; children in CustomLayer.
96
+ - [x] EditorViewContentLayer subscribes to editor:content.change and editor:selection.model; setContentEditableElement(ref) on mount/unmount. (Implementation verified; not asserted in tests.)
97
+ - [x] EditorViewLayer renders with data-bc-layer, position absolute, pointer-events none, default classNames/zIndex.
98
+ - [x] ReactSelectionHandler convertDOMSelectionToModel / isSelectionInsideEditableText / setProgrammaticChange behave as spec. (isSelectionInsideEditableText and setProgrammaticChange tested; convertDOMSelectionToModel not.)
99
+ - [x] ReactMutationObserverManager setup/disconnect and batch callback.
100
+ - [x] findClosestInlineTextNode, reconstructModelTextFromDOM behave as spec. (classifyDomChangeC1 not tested.)
101
+
102
+ ---
103
+
104
+ ## Gaps (optional future tests)
105
+
106
+ - **EditorViewContentLayer**: Assert editor.on('editor:content.change') and editor.on('editor:selection.model') called; setContentEditableElement called with ref on mount and null on unmount (with mock Editor and spy).
107
+ - **classifyDomChangeC1**: Unit test with mock Editor, mutations array, and options; assert C1 or UNKNOWN.
108
+ - **ReactSelectionHandler.convertDOMSelectionToModel**: Unit test with full DOM (data-bc-sid nodes, text runs) and mock Editor.dataStore.getNode; assert ModelSelection shape.
109
+ - **ReactInputHandler**: beforeinput/keydown handling with mock Editor and executeCommand spy.
@@ -0,0 +1,359 @@
1
+ # editor-view-react Specification
2
+
3
+ This document defines the spec for `@barocss/editor-view-react`: goals, architecture, API, context, layers, selection/input flow, DOM sync, and test strategy.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Goals and Scope](#1-goals-and-scope)
10
+ 2. [Architecture](#2-architecture)
11
+ 3. [API Contract](#3-api-contract)
12
+ 4. [Context and View State](#4-context-and-view-state)
13
+ 5. [Layers](#5-layers)
14
+ 6. [Content Layer and Renderer](#6-content-layer-and-renderer)
15
+ 7. [Selection Flow](#7-selection-flow)
16
+ 8. [Input and DOM Sync](#8-input-and-dom-sync)
17
+ 9. [MutationObserver](#9-mutationobserver)
18
+ 10. [Test Strategy and Required Tests](#10-test-strategy-and-required-tests)
19
+ 11. [Out of Scope and Future](#11-out-of-scope-and-future)
20
+
21
+ ---
22
+
23
+ ## 1. Goals and Scope
24
+
25
+ ### 1.1 Goals
26
+
27
+ - **React view layer for Barocss Editor**: Renders the editor document with **renderer-react** (DSL → ReactNode) and re-renders on `editor:content.change`. React counterpart of **editor-view-dom**.
28
+ - **Same Editor/DataStore model**: Uses `Editor` from `@barocss/editor-core` (getDocumentProxy, on/off for events). Same registry and model shape as editor-view-dom for sid/stype semantics.
29
+ - **Selection and input**: DOM selection ↔ model selection via `ReactSelectionHandler`; beforeinput/keydown and DOM mutations via `ReactInputHandler` and `ReactMutationObserverManager`. No dependency on editor-view-dom.
30
+
31
+ ### 1.2 Scope
32
+
33
+ | In scope | Out of scope |
34
+ |----------|--------------|
35
+ | EditorView composite, EditorViewContentLayer, EditorViewLayer | Full E2E typing tests (apps/editor-react) |
36
+ | EditorViewContext (editor, selectionHandler, inputHandler, mutationObserverManager, viewState) | Decorator/selection overlay rendering (layer slots only) |
37
+ | ReactRenderer + contenteditable div, editor:content.change subscription | renderer-react internals |
38
+ | editor:selection.model → convertModelSelectionToDOM (requestAnimationFrame) | editor-core selection internals |
39
+ | selectionchange → convertDOMSelectionToModel → editor.updateSelection | DOM reconciliation (React) |
40
+ | beforeinput/keydown → commands (replaceText, insertParagraph, etc.) | Command implementations (extensions) |
41
+ | MutationObserver → C1 classification → replaceText / structural commands | when()/each() in templates |
42
+ | data-bc-sid, data-bc-stype on content (from renderer-react) | Portals, decorator DOM |
43
+
44
+ ---
45
+
46
+ ## 2. Architecture
47
+
48
+ ### 2.1 Pipeline
49
+
50
+ ```
51
+ Editor (editor-core)
52
+
53
+ EditorViewContextProvider
54
+ → viewStateRef (isModelDrivenChange, isRendering, isComposing, skipNextRenderFromMO, skipApplyModelSelectionToDOM)
55
+ → ReactSelectionHandler(editor, getContentEditableElement)
56
+ → ReactInputHandler(editor, selectionHandler, viewStateRef)
57
+ → createMutationObserverManager(mutations => inputHandler.handleDomMutations(mutations))
58
+ → setContentEditableElement(el) — connect/disconnect observer
59
+
60
+ EditorView (composite)
61
+ → EditorViewContentLayer (contenteditable, ReactRenderer.build(documentSnapshot), editor:content.change, editor:selection.model)
62
+ → EditorView.DecoratorLayer | SelectionLayer | ContextLayer | CustomLayer (EditorViewLayer overlay)
63
+ ```
64
+
65
+ ### 2.2 Dependencies
66
+
67
+ - **@barocss/dsl**: RendererRegistry, getGlobalRegistry.
68
+ - **@barocss/editor-core**: Editor, fromDOMSelection.
69
+ - **@barocss/renderer-react**: ReactRenderer.
70
+ - **@barocss/shared**: getKeyString.
71
+ - **@barocss/text-analyzer**: analyzeTextChanges.
72
+ - **@barocss/shared**: buildTextRunIndex, binarySearchRun, ContainerRuns (text-run-index API).
73
+ - **react**, **react-dom**: Context, hooks, DOM refs.
74
+
75
+ No dependency on **editor-view-dom** or **renderer-dom**.
76
+
77
+ ### 2.3 Data Flow
78
+
79
+ - **Content**: editor.getDocumentProxy() → documentSnapshot state → ReactRenderer.build(model) → contenteditable div children. On editor:content.change → setDocumentSnapshot(e.content ?? getDocumentProxy()).
80
+ - **Selection (model → DOM)**: editor:selection.model → skipApplyModelSelectionToDOM check → requestAnimationFrame ×2 → selectionHandler.convertModelSelectionToDOM(sel).
81
+ - **Selection (DOM → model)**: document selectionchange → selectionHandler.handleSelectionChange → convertDOMSelectionToModel → editor.updateSelection(modelSelection).
82
+ - **Input**: contenteditable receives input → beforeinput/keydown captured (if wired) → ReactInputHandler runs commands; or MutationObserver → handleDomMutations → C1 classify → replaceText.
83
+
84
+ ---
85
+
86
+ ## 3. API Contract
87
+
88
+ ### 3.1 Exports
89
+
90
+ | Export | Type | Description |
91
+ |--------|------|-------------|
92
+ | EditorView | Component | Composite view; provides EditorViewContextProvider and content + overlay layers. |
93
+ | EditorViewContentLayer | Component | Renders document with ReactRenderer in contenteditable div. Must be inside EditorViewContext. |
94
+ | EditorViewLayer | Component | Overlay layer wrapper (layer prop: decorator \| selection \| context \| custom). |
95
+ | EditorViewContextProvider | Component | Provides editor, viewStateRef, selectionHandler, inputHandler, mutationObserverManager, setContentEditableElement. |
96
+ | useEditorViewContext | Hook | Returns EditorViewContextValue; throws if not inside Provider. |
97
+ | useOptionalEditorViewContext | Hook | Returns value or null. |
98
+ | createMutationObserverManager | Function | (onMutations) => ReactMutationObserverManager. |
99
+ | Types | — | EditorViewOptions, EditorViewProps, EditorViewRef, EditorViewHandle, EditorViewContentLayerOptions, EditorViewLayerOptions, EditorViewLayersConfig, EditorViewLayerType, DecoratorExportData, LoadDecoratorsPatternFunctions, DecoratorQueryOptions, DecoratorTypeSchema, ModelSelection, EditorViewViewState, EditorViewContextValue, ReactMutationObserverManager. |
100
+
101
+ ### 3.2 EditorView Props
102
+
103
+ - **editor**: Editor (required).
104
+ - **options**: Optional.
105
+ - **registry**: RendererRegistry (for content layer).
106
+ - **className**: string (root container).
107
+ - **layers**: EditorViewLayersConfig — content?, decorator?, selection?, context?, custom? (optional className/style overrides; all four overlay layers are always rendered).
108
+ - **children**: ReactNode (rendered inside CustomLayer after overlay decorators).
109
+
110
+ Decorators are managed internally; use the ref API (EditorViewHandle) when using a ref on EditorView.
111
+
112
+ ### 3.3 EditorView ref (EditorViewHandle)
113
+
114
+ When using `ref` on EditorView, the following are exposed (parity with editor-view-dom where applicable):
115
+
116
+ - **addDecorator(decorator: Decorator | DecoratorGenerator)** — Add a target decorator or register a DecoratorGenerator (function-based decorators). Generators are registered with `decoratorGeneratorManager` and their output is included in merged decorators.
117
+ - **removeDecorator(id)**, **updateDecorator(id, updates)** — Remove or update a decorator by id.
118
+ - **getDecorators(options?: DecoratorQueryOptions)** — Return merged decorators (local + remote + pattern-generated + generator-generated), optionally filtered/sorted by category, type, nodeId, enabledOnly, sortBy, sortOrder.
119
+ - **getDecorator(id)** — Return a single decorator or generator by id (searches merged, then local, then remote).
120
+ - **exportDecorators()**, **loadDecorators(data, patternFunctions?)** — Serialize/restore target and pattern decorators; pattern functions provided at load time for pattern logic.
121
+ - **contentEditableElement** — HTMLElement | null (content-editable root).
122
+ - **convertModelSelectionToDOM(sel)**, **convertDOMSelectionToModel(selection)**, **convertStaticRangeToModel(staticRange)** — Selection conversion between model and DOM/StaticRange.
123
+ - **defineDecoratorType(type, category, schema)** — Register a decorator type schema (DecoratorTypeSchema) for validation and defaults when adding decorators; uses shared DecoratorSchemaRegistry.
124
+ - **decoratorManager**, **remoteDecoratorManager**, **patternDecoratorConfigManager**, **decoratorGeneratorManager** — Refs to the shared manager instances (nullable).
125
+
126
+ Types: `DecoratorExportData`, `LoadDecoratorsPatternFunctions`, `DecoratorQueryOptions`, `DecoratorTypeSchema`, `ModelSelection` are exported from the package (re-exported from `@barocss/shared` where applicable).
127
+
128
+ ### 3.4 EditorViewContentLayer Props
129
+
130
+ - **options**: Optional.
131
+ - **registry**: RendererRegistry (default getGlobalRegistry()).
132
+ - **className**: string (contenteditable wrapper).
133
+ - **editable**: boolean (default true).
134
+
135
+ Decorators are taken from internal DecoratorManager (context); no options.decorators or getDecorators. Editor is taken from EditorViewContext only.
136
+
137
+ ### 3.5 EditorViewLayer Props
138
+
139
+ - **layer**: 'decorator' | 'selection' | 'context' | 'custom'.
140
+ - **className**: Optional string.
141
+ - **style**: Optional React.CSSProperties.
142
+ - **children**: Optional ReactNode.
143
+
144
+ Default classNames and zIndex per layer: decorator (barocss-editor-decorators, 10), selection (barocss-editor-selection, 100), context (barocss-editor-context, 200), custom (barocss-editor-custom, 1000). Position absolute, pointer-events: none.
145
+
146
+ ### 3.6 EditorView Static Subcomponents
147
+
148
+ - **EditorView.ContentLayer** = EditorViewContentLayer.
149
+ - **EditorView.Layer** = EditorViewLayer (generic; pass layer prop).
150
+ - **EditorView.DecoratorLayer**, **EditorView.SelectionLayer**, **EditorView.ContextLayer**, **EditorView.CustomLayer** = overlay layers with fixed layer type.
151
+
152
+ ---
153
+
154
+ ## 4. Context and View State
155
+
156
+ ### 4.1 EditorViewContextValue
157
+
158
+ - **editor**: Editor.
159
+ - **viewStateRef**: MutableRefObject<EditorViewViewState>.
160
+ - **selectionHandler**: ReactSelectionHandler.
161
+ - **inputHandler**: ReactInputHandler.
162
+ - **mutationObserverManager**: ReactMutationObserverManager.
163
+ - **setContentEditableElement**: (el: HTMLElement | null) => void.
164
+
165
+ ### 4.2 EditorViewViewState
166
+
167
+ - **isModelDrivenChange**: boolean.
168
+ - **isRendering**: boolean.
169
+ - **isComposing**: boolean.
170
+ - **skipNextRenderFromMO**: boolean — when true, next editor:content.change (from model commit during MO C1) must not trigger refresh (data-only update).
171
+ - **skipApplyModelSelectionToDOM**: boolean — when true, editor:selection.model must not call convertModelSelectionToDOM (selection came from DOM input; leave DOM selection as-is).
172
+
173
+ ### 4.3 Lifecycle
174
+
175
+ - EditorViewContextProvider creates viewStateRef, contentEditableRef, selectionHandler (useMemo), inputHandler (useMemo), mutationObserverManager (useMemo), setContentEditableElement (useCallback). Subscribes to document selectionchange for selectionHandler.handleSelectionChange.
176
+ - EditorViewContentLayer: on mount/update calls setContentEditableElement(contentRef.current); on unmount setContentEditableElement(null). Subscribes to editor:content.change and editor:selection.model.
177
+
178
+ ---
179
+
180
+ ## 5. Layers
181
+
182
+ See **layers-spec.md** for layerTarget semantics and how selection/context/custom layers are intended to be used.
183
+
184
+ ### 5.1 Content Layer
185
+
186
+ - Single contenteditable div. data-bc-layer="content", data-testid="editor-content".
187
+ - Renders documentSnapshot via ReactRenderer.build(model, decorators). Decorators come from internal DecoratorManager; inline/block (and layerTarget content) are rendered in the same tree as content. Subscribes to editor:content.change to update documentSnapshot.
188
+ - Ref passed to setContentEditableElement so MutationObserver and selection resolution use the same root.
189
+
190
+ ### 5.2 Overlay Layers
191
+
192
+ - **decorator**, **selection**, **context**, **custom**: Each is a div with position absolute, full inset, pointer-events: none, data-bc-layer={layer}. All four are always rendered. Content: decorators with layerTarget === that layer are rendered via ReactRenderer.buildOverlayDecorators(filtered). Custom layer also renders EditorView children after overlay decorators.
193
+
194
+ ### 5.3 Rendering in EditorView
195
+
196
+ - Content layer always rendered (with merged options from options.layers?.content).
197
+ - All four overlay layers (decorator, selection, context, custom) are always rendered. options.layers?.decorator|selection|context|custom supply optional className and style only. Children are rendered inside CustomLayer after overlay decorators.
198
+
199
+ ---
200
+
201
+ ## 6. Content Layer and Renderer
202
+
203
+ ### 6.1 Document Snapshot
204
+
205
+ - Initial state: editor.getDocumentProxy?.() ?? null.
206
+ - On editor:content.change: setDocumentSnapshot(e?.content ?? editor.getDocumentProxy?.() ?? null).
207
+ - If documentSnapshot is null or has no stype, content layer renders null (empty div).
208
+
209
+ ### 6.2 ReactRenderer
210
+
211
+ - useMemo(() => new ReactRenderer(registry ?? getGlobalRegistry()), [registry]).
212
+ - decorators = getMergedDecorators(documentSnapshot) (local + remote + pattern-from-model + generator-from-model).
213
+ - content = useMemo(() => documentSnapshot != null && model.stype ? renderer.build(model, decorators) : null, [documentSnapshot, renderer, decorators]).
214
+ - Same registry and model shape as renderer-dom for parity (sid, stype, content, text, marks, decorators).
215
+
216
+ ### 6.3 Decorator source and updates (vs editor-view-dom)
217
+
218
+ - **editor-view-dom**: Decorators live **inside** the view (DecoratorManager, RemoteDecoratorManager, DecoratorGeneratorManager, PatternDecoratorConfigManager). When render() is called, the view reads decorators from these managers and passes them to DOMRenderer.render(container, model, allDecorators, …).
219
+ - **editor-view-react**: Same: decorators live **inside** the view. Context holds DecoratorManager, RemoteDecoratorManager, PatternDecoratorConfigManager, DecoratorGeneratorManager. Content and overlay layers use **getMergedDecorators(model)** (local + remote + pattern-from-model + generator-from-model). Ref exposes addDecorator, removeDecorator, updateDecorator, getDecorators(), and the four managers. No decorators passed from outside via options.
220
+
221
+ ### 6.4 Contenteditable
222
+
223
+ - contentEditable={editable} (default true). suppressContentEditableWarning. className and options from props.
224
+
225
+ ---
226
+
227
+ ## 7. Selection Flow
228
+
229
+ ### 7.1 DOM → Model
230
+
231
+ - **selectionchange** (document): selectionHandler.handleSelectionChange(). If not programmatic and selection inside contentEditable, convertDOMSelectionToModel(selection) → editor.updateSelection(modelSelection).
232
+ - **convertDOMSelectionToModel**: Uses data-bc-sid nodes and text-run-index (buildTextRunIndex, offset conversion) to produce ModelSelection (none | range | node). Skips nodes with data-devtool.
233
+
234
+ ### 7.2 Model → DOM
235
+
236
+ - **editor:selection.model**: If !skipApplyModelSelectionToDOM, requestAnimationFrame ×2 then selectionHandler.convertModelSelectionToDOM(sel). Converts model range to DOM range and sets selection.
237
+
238
+ ### 7.3 ReactSelectionHandler
239
+
240
+ - **isSelectionInsideEditableText(domSelection?)**: Returns true if selection is entirely inside inline-text nodes (data-bc-sid + model.stype === 'inline-text').
241
+ - **setProgrammaticChange(value)**: When true, handleSelectionChange no-ops (avoids feedback loop when applying model selection to DOM).
242
+ - **convertModelSelectionToDOM**: Resolves sid + offset to DOM node + offset; uses text-run-index and buildTextRunIndex. Sets window.getSelection() range.
243
+
244
+ ---
245
+
246
+ ## 8. Input and DOM Sync
247
+
248
+ ### 8.1 ReactInputHandler
249
+
250
+ - **beforeinput / keydown**: Dispatches to replaceText, insertParagraph, deleteContentBackward, etc., based on inputType and key. Uses selectionHandler for current model selection and isSelectionInsideEditableText.
251
+ - **setComposing(isComposing)**: Updates viewStateRef.current.isComposing (and viewState for consumers).
252
+ - **syncFocusedTextNodeAfterComposition**: After compositionend, reconstructs DOM text of focused inline-text node, compares to model; if different, executeCommand('replaceText', …) and update model selection; sets skipApplyModelSelectionToDOM during apply.
253
+
254
+ ### 8.2 handleDomMutations
255
+
256
+ - MutationObserver batches mutations (setTimeout 0) then calls inputHandler.handleDomMutations(mutations).
257
+ - **classifyDomChangeC1(mutations, options)**: If mutations are confined to a single inline-text node (data-bc-sid, stype === 'inline-text') and no block-like nodes added/removed, returns ClassifiedChangeC1 { nodeId, prevText, newText, … }. Otherwise UNKNOWN.
258
+ - **C1 path**: replaceText command with range and text from classification. Optionally skipNextRenderFromMO to avoid double refresh.
259
+ - **Structural / UNKNOWN**: Can trigger other commands or no-op; behavior is implementation-specific.
260
+
261
+ ### 8.3 DOM Sync Utilities (dom-sync)
262
+
263
+ - **findClosestInlineTextNode(node)**: Walk up to find Element with data-bc-sid (any; caller checks model stype if needed).
264
+ - **reconstructModelTextFromDOM(inlineTextNode)**: buildTextRunIndex(inner text runs) and concatenate text. Used for C1 newText and syncFocusedTextNodeAfterComposition.
265
+ - **classifyDomChangeC1**: Ported from editor-view-dom C1; uses editor, selection, modelSelection, inputHint, isComposing.
266
+
267
+ ---
268
+
269
+ ## 9. MutationObserver
270
+
271
+ ### 9.1 ReactMutationObserverManager
272
+
273
+ - **setup(contentEditableElement)**: Disconnect previous observer; create MutationObserver with observe(..., { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['data-bc-edit', 'data-bc-value', 'data-bc-sid', 'data-bc-stype'], characterDataOldValue: true, attributeOldValue: true }). On mutation, push to pending; setTimeout(0) then invoke onMutations(pending) and clear pending.
274
+ - **disconnect()**: Disconnect observer, clear timer and pending.
275
+
276
+ ### 9.2 Wiring
277
+
278
+ - EditorViewContextProvider creates manager with onMutations = (mutations) => inputHandler.handleDomMutations(mutations).
279
+ - EditorViewContentLayer sets ref → setContentEditableElement(el). Context setContentEditableElement: on change, disconnect from old element, assign ref, setup(new element).
280
+
281
+ ---
282
+
283
+ ## 10. Test Strategy and Required Tests
284
+
285
+ ### 10.1 Principles
286
+
287
+ - **Spec-first**: Tests assert behavior described in this spec (context, layers, selection/input wiring, DOM sync).
288
+ - **Unit where possible**: ReactSelectionHandler, ReactInputHandler, createMutationObserverManager, classifyDomChangeC1, findClosestInlineTextNode, reconstructModelTextFromDOM can be tested with mocks.
289
+ - **Integration**: EditorView mount with mock Editor (getDocumentProxy, on/off), assert content layer renders, context provides handlers, layers render when configured.
290
+
291
+ ### 10.2 Required Test Categories
292
+
293
+ 1. **EditorViewContextProvider**
294
+ - Provides editor, selectionHandler, inputHandler, mutationObserverManager, setContentEditableElement, viewStateRef.
295
+ - useEditorViewContext throws when outside Provider; useOptionalEditorViewContext returns null.
296
+
297
+ 2. **EditorView**
298
+ - Renders content layer with merged options (registry, className, editable).
299
+ - Renders overlay layers only when options.layers?.decorator etc. are set.
300
+ - Renders children inside CustomLayer when layers.custom or children present.
301
+ - Root div has data-editor-view="true", position relative.
302
+
303
+ 3. **EditorViewContentLayer**
304
+ - Subscribes to editor:content.change; documentSnapshot updates; renderer.build called with snapshot.
305
+ - Subscribes to editor:selection.model; when !skipApplyModelSelectionToDOM, convertModelSelectionToDOM called (e.g. in rAF).
306
+ - setContentEditableElement called with ref on mount and null on unmount.
307
+ - contenteditable div has data-bc-layer="content", data-testid="editor-content".
308
+
309
+ 4. **EditorViewLayer**
310
+ - Renders div with data-bc-layer={layer}, position absolute, pointer-events none, correct default className and zIndex per layer type.
311
+
312
+ 5. **ReactSelectionHandler**
313
+ - convertDOMSelectionToModel with mock Editor and DOM (data-bc-sid nodes) returns expected ModelSelection.
314
+ - isSelectionInsideEditableText returns true/false for selection inside/outside inline-text.
315
+ - setProgrammaticChange(true) causes handleSelectionChange to skip updateSelection.
316
+
317
+ 6. **ReactMutationObserverManager**
318
+ - setup(element) observes element; disconnect() stops observing. Callback invoked with batched mutations (setTimeout 0).
319
+
320
+ 7. **dom-sync**
321
+ - findClosestInlineTextNode returns closest element with data-bc-sid.
322
+ - reconstructModelTextFromDOM returns concatenated text from text runs.
323
+ - classifyDomChangeC1 returns C1 or null for given mutations and options.
324
+
325
+ ### 10.3 Test Environment
326
+
327
+ - **Vitest** for unit tests. **React Testing Library** (or @testing-library/react) for component tests if needed. JSDOM for DOM/selection tests.
328
+ - Mock Editor: getDocumentProxy(), on(), off(), updateSelection(), executeCommand(), dataStore.getNode().
329
+
330
+ ---
331
+
332
+ ## 11. References and analysis
333
+
334
+ - **React editing view analysis**: `packages/renderer-react/docs/editing-view-react-analysis.md` — comparison with ProseMirror/Lexical/TipTap, risks (cursor, IME, reconciliation), and improvements (skipNextRenderFromMO, E2E).
335
+
336
+ ---
337
+
338
+ ## 12. Out of Scope and Future
339
+
340
+ | Feature | Current | Future |
341
+ |---------|---------|--------|
342
+ | Decorator DOM rendering | Layer slot only; no built-in decorator DOM | Optional decorator list and overlay DOM |
343
+ | E2E typing/selection in app | Manual or apps/editor-react E2E | Playwright E2E for editor-react |
344
+ | when()/each() in templates | Handled by renderer-react (unsupported) | — |
345
+ | IME composition edge cases | syncFocusedTextNodeAfterComposition; composition state in viewState | More tests and edge cases |
346
+ | Portal / overlay positioning | Layers are absolute full-size | Fine-grained positioning if needed |
347
+
348
+ ---
349
+
350
+ ## Checklist (Concrete)
351
+
352
+ - [x] EditorViewContextProvider provides editor, viewStateRef, selectionHandler, inputHandler, mutationObserverManager, setContentEditableElement.
353
+ - [x] useEditorViewContext throws outside Provider; useOptionalEditorViewContext returns null outside.
354
+ - [x] EditorView renders content layer; overlay layers only when options.layers.* set; children in CustomLayer.
355
+ - [x] EditorViewContentLayer subscribes to editor:content.change and editor:selection.model; setContentEditableElement(ref) on mount/unmount (implementation; see SPEC_VERIFICATION.md).
356
+ - [x] EditorViewLayer renders with data-bc-layer, position absolute, pointer-events none, default classNames/zIndex.
357
+ - [x] ReactSelectionHandler isSelectionInsideEditableText / setProgrammaticChange behave as spec (convertDOMSelectionToModel covered by implementation).
358
+ - [x] ReactMutationObserverManager setup/disconnect and batch callback.
359
+ - [x] findClosestInlineTextNode, reconstructModelTextFromDOM behave as spec (classifyDomChangeC1 implementation verified; optional unit test).
@@ -0,0 +1,66 @@
1
+ # editor-view-dom / editor-view-react 개선 후보
2
+
3
+ editor-view-dom과 editor-view-react를 비교·분석한 결과, 아래 개선이 가능하다.
4
+
5
+ **적용 완료 (1–4번)**: DecoratorExportData/LoadDecoratorsPatternFunctions shared 통일, addDecorator(DecoratorGenerator) 지원, IEditorViewDOM 타입 정리, 스펙/README 업데이트.
6
+
7
+ ---
8
+
9
+ ## 1. editor-view-dom
10
+
11
+ ### 1.1 DecoratorExportData·LoadDecoratorsPatternFunctions를 shared에서 사용
12
+
13
+ - **현재**: `packages/editor-view-dom/src/types.ts`에 `DecoratorExportData`가 로컬로 정의되어 있고, `loadDecorators`의 `patternFunctions`는 인라인 타입으로만 정의됨.
14
+ - **개선**: `DecoratorExportData`, `LoadDecoratorsPatternFunctions`를 `@barocss/shared`에서 import하고, editor-view-dom의 types에서 해당 정의 제거. `loadDecorators` 시그니처에 `LoadDecoratorsPatternFunctions` 사용.
15
+ - **효과**: 타입 중복 제거, shared와 시그니처 일치.
16
+
17
+ ### 1.2 IEditorViewDOM 타입 정리
18
+
19
+ - **현재**: `getDecorators?(options?: any): any[]`로 되어 있음.
20
+ - **개선**: `getDecorators(options?: DecoratorQueryOptions): (Decorator | DecoratorGenerator)[]`로 명시. `defineDecoratorType`의 `schema` 인자 타입을 shared의 `DecoratorTypeSchema`로 통일.
21
+ - **효과**: 타입 안정성·자동완성 개선, editor-view-react와 개념적 일치.
22
+
23
+ ---
24
+
25
+ ## 2. editor-view-react
26
+
27
+ ### 2.1 addDecorator에 DecoratorGenerator 지원
28
+
29
+ - **현재**: `addDecorator(decorator: Decorator)`만 받음. editor-view-dom은 `addDecorator(decorator: Decorator | DecoratorGenerator)`를 지원함.
30
+ - **개선**: `EditorViewHandle.addDecorator(decorator: Decorator | DecoratorGenerator)`로 확장. `DecoratorGenerator`인 경우 `decoratorGeneratorManagerRef.current?.registerGenerator(generator, bumpDecoratorVersion)` 호출 후 `bumpDecoratorVersion()` 호출.
31
+ - **효과**: DOM 뷰와 동일하게 generator 기반 데코레이터 등록 가능, API 일치.
32
+
33
+ ### 2.2 ref.destroy() (선택)
34
+
35
+ - **현재**: editor-view-dom은 `view.destroy()`로 이벤트/observer/레이어 정리. editor-view-react는 언마운트 시 `useEffect` cleanup으로 observer 해제·이벤트 해제.
36
+ - **개선**: ref에 `destroy?(): void`를 두고, 호출 시 데코레이터 매니저 clear·버전 bump 등 선택적 정리만 수행. React는 언마운트가 곧 정리이므로 필수는 아님.
37
+ - **효과**: DOM과 동일한 “명시적 정리” API를 쓰고 싶을 때 사용 가능. 우선순위는 낮음.
38
+
39
+ ---
40
+
41
+ ## 3. 공통·문서
42
+
43
+ ### 3.1 스펙/README 반영
44
+
45
+ - **editor-view-react**: `editor-view-react-spec.md`, `README.md`에 최근 추가된 ref API 반영
46
+ (`defineDecoratorType`, `convertStaticRangeToModel`, `getDecorators(options?)`, `DecoratorSchemaRegistry` 사용).
47
+ - **editor-view-dom**: README/문서에 `LoadDecoratorsPatternFunctions`, `DecoratorQueryOptions` 등 shared 타입 사용 여부 명시.
48
+
49
+ ### 3.2 테스트
50
+
51
+ - **editor-view-react**: `addDecorator(DecoratorGenerator)` 지원 시, generator 등록 후 `getDecorators()`에 반영되는지 한 번 더 검증하는 테스트 추가.
52
+ - **editor-view-dom**: `DecoratorExportData`를 shared에서 쓰도록 바꾼 뒤, 기존 export/load 관련 테스트가 그대로 통과하는지 확인.
53
+
54
+ ---
55
+
56
+ ## 우선순위 제안
57
+
58
+ | 순서 | 항목 | 패키지 | 비고 |
59
+ |------|------|--------|------|
60
+ | 1 | DecoratorExportData·LoadDecoratorsPatternFunctions를 shared로 통일 | editor-view-dom | 타입 일원화 |
61
+ | 2 | addDecorator(DecoratorGenerator) 지원 | editor-view-react | DOM과 API parity |
62
+ | 3 | IEditorViewDOM getDecorators·defineDecoratorType 타입 정리 | editor-view-dom | 타입 품질 |
63
+ | 4 | 스펙/README 업데이트 | 둘 다 | 유지보수성 |
64
+ | 5 | ref.destroy() (선택) | editor-view-react | 필요 시 추가 |
65
+
66
+ 원하면 위 항목별로 이슈/PR 단위로 나눠서 적용할 수 있다.
@@ -0,0 +1,97 @@
1
+ # Editor View Layers Specification
2
+
3
+ This document defines the five layers used by editor-view-dom and editor-view-react: content, decorator, selection, context, and custom. It describes how `layerTarget` routes decorators to layers and how each layer is intended to be used.
4
+
5
+ ---
6
+
7
+ ## 1. Layer Overview
8
+
9
+ | Layer | data-bc-layer | Purpose |
10
+ |-----------|----------------|---------|
11
+ | content | content | Document + inline/block decorators (content flow). Single contenteditable root. |
12
+ | decorator | decorator | Overlay decorators (e.g. comment bubbles, floating annotations). |
13
+ | selection | selection | Selection/cursor indicators (range highlight, caret). |
14
+ | context | context | Context UI (tooltips, autocomplete, menus). |
15
+ | custom | custom | App-defined overlay + optional children. |
16
+
17
+ All overlay layers (decorator, selection, context, custom) are positioned absolute over the content layer, with `pointer-events: none` by default so the content layer receives input. Default z-index: decorator 10, selection 100, context 200, custom 1000.
18
+
19
+ ---
20
+
21
+ ## 2. layerTarget and Routing
22
+
23
+ Decorators have an optional `layerTarget?: 'content' | 'decorator' | 'selection' | 'context' | 'custom'`.
24
+
25
+ - **content** (default for inline/block): Rendered inside the content layer in the document tree (inline/block slots). Do not render in overlay layers.
26
+ - **decorator**: Rendered only in the decorator overlay layer (e.g. comment popover, annotation).
27
+ - **selection**: Rendered only in the selection overlay layer (e.g. cursor div, range highlight).
28
+ - **context**: Rendered only in the context overlay layer (e.g. tooltip, autocomplete popup).
29
+ - **custom**: Rendered only in the custom overlay layer (e.g. custom floating UI).
30
+
31
+ When `layerTarget` is omitted, inline/block decorators default to content; layer-category decorators default to decorator (editor-view-dom DecoratorPrebuilder). In editor-view-react, overlay content is built with `ReactRenderer.buildOverlayDecorators(decorators)` per layer, with decorators filtered by `layerTarget === layer`.
32
+
33
+ ---
34
+
35
+ ## 3. Content Layer
36
+
37
+ - **Role**: Document body and inline/block decorators that participate in the content flow.
38
+ - **Rendering**: Single contenteditable div. Document model is rendered via ReactRenderer.build(model, decorators). Inline and block decorators (and layer decorators with layerTarget content) are rendered in the same tree (before/after/overlay slots).
39
+ - **Usage**: No app code; view manages document and decorators internally.
40
+
41
+ ---
42
+
43
+ ## 4. Decorator Layer
44
+
45
+ - **Role**: Overlay decorators that float above content (e.g. comment bubbles, annotations) without being part of the content flow.
46
+ - **Usage**: Add a decorator with `category: 'layer'` and `layerTarget: 'decorator'` (or omit layerTarget for layer category). The view renders it in the decorator overlay via buildOverlayDecorators. Position/size can be set in decorator.data (e.g. data.position for absolute coordinates).
47
+
48
+ ---
49
+
50
+ ## 5. Selection Layer
51
+
52
+ - **Role**: Visual representation of the current selection or cursor (caret, range highlight).
53
+ - **Usage**: Add decorators with `layerTarget: 'selection'`. Typical use: cursor (collapsed range) or range highlight. In editor-view-dom tests, the selection layer is also used by appending DOM (e.g. a cursor div) directly to `view.layers.selection`. In editor-view-react, selection visuals are driven by decorators with layerTarget 'selection' rendered via buildOverlayDecorators. Future: model selection could be mapped to one or more selection-layer decorators by the view or app.
54
+
55
+ ---
56
+
57
+ ## 6. Context Layer
58
+
59
+ - **Role**: Contextual UI that appears near the selection or a target (tooltips, autocomplete, inline menus).
60
+ - **Usage**: Add decorators with `layerTarget: 'context'`. Example: tooltip decorator with data.text, or autocomplete popup. Position in data.position. In editor-view-dom tests, context layer is used for tooltips and autocomplete popup.
61
+
62
+ ---
63
+
64
+ ## 7. Custom Layer
65
+
66
+ - **Role**: App-defined overlay content plus optional React children.
67
+ - **Usage**:
68
+ - **Decorators**: Add decorators with `layerTarget: 'custom'`; they are rendered in the custom overlay.
69
+ - **Children**: Pass children to EditorView; they are rendered inside the custom layer after the overlay decorators. Use for custom UI (floating buttons, panels) that are not driven by the decorator model.
70
+
71
+ ---
72
+
73
+ ## 8. Parity with editor-view-dom
74
+
75
+ - editor-view-dom: Content layer uses DOMRenderer.render(); overlay layers use DecoratorPrebuilder to build DecoratorModels, split by layerTarget, then _decoratorRenderer/_selectionRenderer/_contextRenderer/_customRenderer.renderChildren(layerElement, models).
76
+ - editor-view-react: Content layer uses ReactRenderer.build(model, decorators); overlay layers use EditorViewOverlayLayerContent per layer, which filters decorators by layerTarget and calls ReactRenderer.buildOverlayDecorators(filtered). Same layerTarget semantics and layer order (content, then decorator, selection, context, custom).
77
+
78
+ ---
79
+
80
+ ## 9. References
81
+
82
+ - editor-view-dom: `packages/editor-view-dom/src/editor-view-dom.ts` (_renderLayers, layerTarget switch), `packages/editor-view-dom/docs/decorator-guide.md`, `packages/editor-view-dom/test/core/layered-api.test.ts`, `packages/editor-view-dom/test/integration/pattern-custom-decorator-render.test.ts`.
83
+ - editor-view-react: EditorViewOverlayLayerContent, EditorView.tsx (DecoratorLayerSlot, SelectionLayerSlot, ContextLayerSlot, CustomLayerSlot), editor-view-react-spec.md (Layers section).
84
+
85
+ ---
86
+
87
+ ## 10. Decorator improvements (future)
88
+
89
+ Possible improvements to align behavior and reduce drift:
90
+
91
+ | Item | Description |
92
+ |------|-------------|
93
+ | **Decorator range adjustment on text edit** | When the user types, inline decorator ranges (startOffset/endOffset) should be adjusted so they stay correct. editor-view-dom computes `adjustedDecorators` in handleEfficientEdit (via dataStore.decorators.adjustRanges or edit-position-converter) but applying them back to DecoratorManager is still TODO. editor-view-react does not yet run any adjust step after replaceText. Adding this in both views would keep decorators in sync with content. |
94
+ | **Single Decorator type source** | Decorator is defined in editor-view-dom, shared, and renderer-react. Unifying on shared (and re-exporting or extending in the other packages) would avoid drift. |
95
+ | **ref.updateDecorator(id, updates)** | editor-view-dom exposes updateDecorator(id, updates). editor-view-react ref currently has addDecorator, removeDecorator, getDecorators; adding updateDecorator would call DecoratorManager.update for parity. |
96
+ | **Optional DecoratorRegistry in editor-view-react** | editor-view-dom uses DecoratorRegistry for validation and default values when adding decorators. editor-view-react uses DecoratorManager without a validator; an optional registry would allow the same validation/defaults. |
97
+ | **Remote / Pattern / Generator** | editor-view-react provides RemoteDecoratorManager, PatternDecoratorConfigManager, and DecoratorGeneratorManager via ref. Merged decorators (local + remote + pattern-from-model + generator-from-model) are used for content and overlay rendering. |