diff --git a/CLAUDE.md b/CLAUDE.md index 060090530d..951c0f86dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,10 +4,17 @@ A document editing and rendering library for the web. ## Architecture: Rendering -SuperDoc uses its own rendering pipeline — **ProseMirror is NOT used for visual output**. +SuperDoc uses its own rendering pipeline. ProseMirror stores document state; +it is not the visual renderer. ``` -PM Doc (hidden) → pm-adapter → FlowBlock[] → layout-engine → Layout[] → DomPainter → DOM +.docx + → super-converter parses OOXML into the hidden PM doc + → pm-adapter reads PM state and resolved styles + → FlowBlock[] + → layout-engine paginates + → ResolvedLayout + → DomPainter paints DOM ``` - `PresentationEditor` wraps a hidden ProseMirror `Editor` instance for document state and editing commands @@ -15,15 +22,37 @@ PM Doc (hidden) → pm-adapter → FlowBlock[] → layout-engine → Layout[] - **DomPainter** (`layout-engine/painters/dom/`) owns all visual rendering - Style-resolved properties (backgrounds, fonts, borders, etc.) must flow through `pm-adapter` → DomPainter, not through PM decorations -### Where visual changes go +### Where To Put Your Change -| Change | Where | -|--------|-------| -| How something looks | `pm-adapter/` (data) + `painters/dom/` (rendering) | -| Style resolution | `style-engine/` | -| Editing behavior | `super-editor/src/editors/v1/extensions/` | +| Concern | Where | Rule | +|---------|-------|------| +| 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. | +| Style cascade | `layout-engine/style-engine/` | Own defaults, styles, conditional formatting, and inline override resolution. | +| 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. | +| 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`. | +| 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. | +| 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`). | +| 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. | +| Geometry and pagination | `layout-engine/layout-engine/` | Compute layout from `FlowBlock[]`; do not read rendered DOM to recover missing data. | +| Final DOM rendering | `layout-engine/painters/dom/` | Render `ResolvedLayout`. Paint-time visual transforms, such as the `w:bidiVisual` mirror, belong here. | +| Presentation state bridge | `PresentationEditor.ts` | Bridge editor events into layout and paint state. Do not resolve OOXML semantics here. | +| New document-API operation | `packages/document-api/src/contract/operation-definitions.ts` | Contract-first; touches 4 files — see `packages/document-api/README.md`. | +| New consumer SDK / CLI surface | `apps/cli/src/` + regenerate SDK | Run `pnpm run generate:all` after. | -**Do NOT** add ProseMirror decoration plugins for visual styling — DomPainter handles rendering. +### Quick Boundary Checks + +Use these searches before adding a new visual or direction-aware path: + +```bash +# Painter should not import upstream packages. +rg "@superdoc/(pm-adapter|style-engine|layout-bridge|layout-resolved)" packages/layout-engine/painters/dom/src + +# pm-adapter should not do DOM work. +rg "getBoundingClientRect|clientWidth|offsetWidth|document\\.|window\\." packages/layout-engine/pm-adapter/src + +# Import should not bake style cascade into direct attrs. +rg "referencedStyles|resolve.*Properties|resolve.*Style" packages/super-editor/src/editors/v1/core/super-converter +``` ### State Communication @@ -76,7 +105,7 @@ tests/visual/ Visual regression tests (Playwright + R2 baselines) **The importer stores raw OOXML properties. The style-engine resolves them at render time.** -- 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. +- 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. - 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. - Both rendering systems call the style-engine to compute final visual properties. @@ -99,7 +128,7 @@ The `packages/document-api/` package uses a contract-first pattern with a single 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. -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`. +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`. ## JSDoc types diff --git a/packages/layout-engine/AGENTS.md b/packages/layout-engine/AGENTS.md index ca08b49fdd..08b4f72446 100644 --- a/packages/layout-engine/AGENTS.md +++ b/packages/layout-engine/AGENTS.md @@ -20,12 +20,12 @@ ProseMirror Doc → pm-adapter → FlowBlock[] → layout-engine → Layout[] | `style-engine/` | OOXML style resolution | `src/index.ts` | | `geometry-utils/` | Math utilities for layout | `src/index.ts` | -## Key Insight: DomPainter is "Dumb" +## Key Insight: DomPainter Receives Paint-Ready Data DomPainter receives a single paint-ready input — `ResolvedLayout` — and -renders the result to DOM. It does NOT do layout logic, measurement, or -PM-adapter conversion (that's upstream in `layout-engine/` / -`layout-resolved/` / `pm-adapter/`). +renders it to DOM. It does not do layout logic, measurement, or pm-adapter +conversion. Those decisions happen upstream in `layout-engine/`, +`layout-resolved/`, and `pm-adapter/`. This is enforced as two hard invariants, not aspirational language: @@ -74,7 +74,7 @@ When adding style resolution for a new property type (e.g., `tableCellProperties 3. Cascade using `combineProperties()` (low → high priority) 4. Inline properties always win last -See root CLAUDE.md "Style Resolution Boundary" for why this must NOT be done in the importer. +See root CLAUDE.md "Style Resolution Boundary" for why this must not be done in the importer. ## Important Patterns @@ -138,12 +138,23 @@ Rendering logic for specific OOXML features is extracted into **feature modules* - `layout-bridge/src/incrementalLayout.ts` - Layout orchestration (called by PresentationEditor) - `pm-adapter/src/internal.ts` - PM → FlowBlock conversion -## Rendering Ownership - -**DomPainter owns ALL visual rendering.** ProseMirror is hidden — its DOM is never shown to the user. - -- Style-resolved properties flow through: `style-engine` → `pm-adapter` (sets attrs on FlowBlocks) → `DomPainter` (renders to DOM) -- Do NOT add ProseMirror decoration plugins for visual styling — that bypasses the rendering pipeline -- Editing behavior (commands, keybindings) stays in `super-editor/src/editors/v1/extensions/` - -See root CLAUDE.md for full architecture. +## Layer Ownership + +See root `CLAUDE.md` for the full placement map. This package owns the +layout and rendering pipeline. + +- Style-resolved properties flow through `style-engine` → `pm-adapter` → + DomPainter. +- Static document visuals belong in layout data plus DomPainter rendering, not + ProseMirror decorations. +- Editing behavior, including commands and keybindings, stays in + `super-editor/src/editors/v1/extensions/`. +- `PresentationEditor` bridges editor state into layout and paint state. It + should not resolve OOXML semantics. +- Direction work keeps OOXML axes separate. `style-engine` resolves cascades, + `pm-adapter` writes typed direction/table attrs, and DomPainter owns + paint-time visual mirroring. For `w:bidiVisual`, upstream layers keep table + sides in LTR-default form and DomPainter mirrors once. + +For the full direction taxonomy, see +`pm-adapter/src/direction/README.md`.