Skip to content

Commit c04e785

Browse files
authored
docs: add contributor placement guide for SuperDoc layers (#3278)
A central routing map for "where do I put my change?" so contributors and agents land in the right layer without learning the boundaries by trial. Root CLAUDE.md (which root AGENTS.md follows via symlink): - Rewrite the rendering-flow arrow to include super-converter as the entry (.docx -> super-converter -> hidden PM -> pm-adapter+style-engine -> ...). - Replace the 3-row "Where visual changes go" table with an 11-row "Where To Put Your Change" routing table covering: DOCX import/export, style cascade, static visuals, direction-aware properties, editing behavior, ephemeral editor UI, interaction mapping, geometry/pagination, final DOM rendering, presentation state bridge, document-API operations, consumer SDK/CLI surface. - Add a "Quick Boundary Checks" section with rg commands that surface the three most common layer-leak patterns (painter upstream imports, pm-adapter DOM work, importer baking style cascade). packages/layout-engine/AGENTS.md (CLAUDE.md follows via symlink): - Rename the "Key Insight" header to "DomPainter Receives Paint-Ready Data" (the existing 'dumb' wording was a tone choice the new copy drops). - Replace the "Rendering Ownership" block with a "Layer Ownership" section that cross-references root CLAUDE.md and adds direction-axes + bidiVisual single-mirror reminders local to the package. packages/superdoc/AGENTS.md is intentionally untouched: it is consumer- facing and ships with the npm package. No behavior change. Docs only.
1 parent ed407d7 commit c04e785

2 files changed

Lines changed: 65 additions & 25 deletions

File tree

CLAUDE.md

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,55 @@ A document editing and rendering library for the web.
44

55
## Architecture: Rendering
66

7-
SuperDoc uses its own rendering pipeline — **ProseMirror is NOT used for visual output**.
7+
SuperDoc uses its own rendering pipeline. ProseMirror stores document state;
8+
it is not the visual renderer.
89

910
```
10-
PM Doc (hidden) → pm-adapter → FlowBlock[] → layout-engine → Layout[] → DomPainter → DOM
11+
.docx
12+
→ super-converter parses OOXML into the hidden PM doc
13+
→ pm-adapter reads PM state and resolved styles
14+
→ FlowBlock[]
15+
→ layout-engine paginates
16+
→ ResolvedLayout
17+
→ DomPainter paints DOM
1118
```
1219

1320
- `PresentationEditor` wraps a hidden ProseMirror `Editor` instance for document state and editing commands
1421
- The hidden Editor's contenteditable DOM is never shown to the user
1522
- **DomPainter** (`layout-engine/painters/dom/`) owns all visual rendering
1623
- Style-resolved properties (backgrounds, fonts, borders, etc.) must flow through `pm-adapter` → DomPainter, not through PM decorations
1724

18-
### Where visual changes go
25+
### Where To Put Your Change
1926

20-
| Change | Where |
21-
|--------|-------|
22-
| How something looks | `pm-adapter/` (data) + `painters/dom/` (rendering) |
23-
| Style resolution | `style-engine/` |
24-
| Editing behavior | `super-editor/src/editors/v1/extensions/` |
27+
| Concern | Where | Rule |
28+
|---------|-------|------|
29+
| DOCX import/export | `super-editor/src/editors/v1/core/super-converter/` | Parse and preserve OOXML, style refs, and inline properties. Do not bake inherited or style-resolved formatting into direct attrs. |
30+
| Style cascade | `layout-engine/style-engine/` | Own defaults, styles, conditional formatting, and inline override resolution. |
31+
| Static document visuals | `pm-adapter/` data + `layout-engine/painters/dom/` rendering | Feed typed data into DomPainter. Do not style static document content with PM decorations. |
32+
| Direction-aware properties | One layer makes the flip; pm-adapter stores logical sides LTR-default | For `w:bidiVisual` table-visual properties, DomPainter mirrors once at paint time. Pre-mirroring upstream is a double-swap. Full taxonomy: `pm-adapter/src/direction/README.md`. |
33+
| Editing behavior | `super-editor/src/editors/v1/extensions/` | Commands, keybindings, editor plugins, and active interaction state live here. Do not duplicate cascade logic or render document visuals here. |
34+
| Ephemeral editor UI | `PresentationEditor` post-paint pipeline + overlay components | Selections, active comments, proofing marks, search highlights, resize handles, and other overlays. PM decorations are bridged to painted DOM only for eligible plugins; comments use `CommentHighlightDecorator` (painter metadata), search is excluded from the bridge, resize handles are Vue overlay components. When adding a plugin that emits decorations, decide whether to bridge (default) or stay PM-side (add a prefix to `EXCLUDED_PLUGIN_KEY_PREFIXES` in `DecorationBridge.ts`). |
35+
| Interaction mapping | `layout-engine/layout-bridge/` | Map clicks, selections, resize handles, and visual coordinates back to document positions. RTL-aware as an inverse mapping (visual → logical), not a pre-mirror. |
36+
| Geometry and pagination | `layout-engine/layout-engine/` | Compute layout from `FlowBlock[]`; do not read rendered DOM to recover missing data. |
37+
| Final DOM rendering | `layout-engine/painters/dom/` | Render `ResolvedLayout`. Paint-time visual transforms, such as the `w:bidiVisual` mirror, belong here. |
38+
| Presentation state bridge | `PresentationEditor.ts` | Bridge editor events into layout and paint state. Do not resolve OOXML semantics here. |
39+
| New document-API operation | `packages/document-api/src/contract/operation-definitions.ts` | Contract-first; touches 4 files — see `packages/document-api/README.md`. |
40+
| New consumer SDK / CLI surface | `apps/cli/src/` + regenerate SDK | Run `pnpm run generate:all` after. |
2541

26-
**Do NOT** add ProseMirror decoration plugins for visual styling — DomPainter handles rendering.
42+
### Quick Boundary Checks
43+
44+
Use these searches before adding a new visual or direction-aware path:
45+
46+
```bash
47+
# Painter should not import upstream packages.
48+
rg "@superdoc/(pm-adapter|style-engine|layout-bridge|layout-resolved)" packages/layout-engine/painters/dom/src
49+
50+
# pm-adapter should not do DOM work.
51+
rg "getBoundingClientRect|clientWidth|offsetWidth|document\\.|window\\." packages/layout-engine/pm-adapter/src
52+
53+
# Import should not bake style cascade into direct attrs.
54+
rg "referencedStyles|resolve.*Properties|resolve.*Style" packages/super-editor/src/editors/v1/core/super-converter
55+
```
2756

2857
### State Communication
2958

@@ -76,7 +105,7 @@ tests/visual/ Visual regression tests (Playwright + R2 baselines)
76105

77106
**The importer stores raw OOXML properties. The style-engine resolves them at render time.**
78107

79-
- The converter (`super-converter/`) should only parse and store what is explicitly in the XML (inline properties, style references). It must NOT resolve style cascades, conditional formatting, or inherited properties.
108+
- The converter (`super-converter/`) should only parse and store what is explicitly in the XML (inline properties, style references). It must not resolve style cascades, conditional formatting, or inherited properties.
80109
- The style-engine (`layout-engine/style-engine/`) is the single source of truth for cascade logic. All style resolution (defaults → table style → conditional formatting → inline overrides) happens here.
81110
- Both rendering systems call the style-engine to compute final visual properties.
82111

@@ -99,7 +128,7 @@ The `packages/document-api/` package uses a contract-first pattern with a single
99128

100129
Adding a new operation touches 4 files: `operation-definitions.ts`, `operation-registry.ts`, `invoke.ts` (dispatch table), and the implementation. See `packages/document-api/README.md` for the full guide.
101130

102-
Do NOT hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFERENCE_DOC_PATH_MAP`, or `REFERENCE_OPERATION_GROUPS` — they are derived from `OPERATION_DEFINITIONS`.
131+
Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFERENCE_DOC_PATH_MAP`, or `REFERENCE_OPERATION_GROUPS` — they are derived from `OPERATION_DEFINITIONS`.
103132

104133
## JSDoc types
105134

packages/layout-engine/AGENTS.md

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ ProseMirror Doc → pm-adapter → FlowBlock[] → layout-engine → Layout[]
2020
| `style-engine/` | OOXML style resolution | `src/index.ts` |
2121
| `geometry-utils/` | Math utilities for layout | `src/index.ts` |
2222

23-
## Key Insight: DomPainter is "Dumb"
23+
## Key Insight: DomPainter Receives Paint-Ready Data
2424

2525
DomPainter receives a single paint-ready input — `ResolvedLayout` — and
26-
renders the result to DOM. It does NOT do layout logic, measurement, or
27-
PM-adapter conversion (that's upstream in `layout-engine/` /
28-
`layout-resolved/` / `pm-adapter/`).
26+
renders it to DOM. It does not do layout logic, measurement, or pm-adapter
27+
conversion. Those decisions happen upstream in `layout-engine/`,
28+
`layout-resolved/`, and `pm-adapter/`.
2929

3030
This is enforced as two hard invariants, not aspirational language:
3131

@@ -74,7 +74,7 @@ When adding style resolution for a new property type (e.g., `tableCellProperties
7474
3. Cascade using `combineProperties()` (low → high priority)
7575
4. Inline properties always win last
7676

77-
See root CLAUDE.md "Style Resolution Boundary" for why this must NOT be done in the importer.
77+
See root CLAUDE.md "Style Resolution Boundary" for why this must not be done in the importer.
7878

7979
## Important Patterns
8080

@@ -138,12 +138,23 @@ Rendering logic for specific OOXML features is extracted into **feature modules*
138138
- `layout-bridge/src/incrementalLayout.ts` - Layout orchestration (called by PresentationEditor)
139139
- `pm-adapter/src/internal.ts` - PM → FlowBlock conversion
140140

141-
## Rendering Ownership
142-
143-
**DomPainter owns ALL visual rendering.** ProseMirror is hidden — its DOM is never shown to the user.
144-
145-
- Style-resolved properties flow through: `style-engine``pm-adapter` (sets attrs on FlowBlocks) → `DomPainter` (renders to DOM)
146-
- Do NOT add ProseMirror decoration plugins for visual styling — that bypasses the rendering pipeline
147-
- Editing behavior (commands, keybindings) stays in `super-editor/src/editors/v1/extensions/`
148-
149-
See root CLAUDE.md for full architecture.
141+
## Layer Ownership
142+
143+
See root `CLAUDE.md` for the full placement map. This package owns the
144+
layout and rendering pipeline.
145+
146+
- Style-resolved properties flow through `style-engine``pm-adapter`
147+
DomPainter.
148+
- Static document visuals belong in layout data plus DomPainter rendering, not
149+
ProseMirror decorations.
150+
- Editing behavior, including commands and keybindings, stays in
151+
`super-editor/src/editors/v1/extensions/`.
152+
- `PresentationEditor` bridges editor state into layout and paint state. It
153+
should not resolve OOXML semantics.
154+
- Direction work keeps OOXML axes separate. `style-engine` resolves cascades,
155+
`pm-adapter` writes typed direction/table attrs, and DomPainter owns
156+
paint-time visual mirroring. For `w:bidiVisual`, upstream layers keep table
157+
sides in LTR-default form and DomPainter mirrors once.
158+
159+
For the full direction taxonomy, see
160+
`pm-adapter/src/direction/README.md`.

0 commit comments

Comments
 (0)