Skip to content

Commit bd5c875

Browse files
committed
refactor: move pm adapter out of layout engine
1 parent 83ea57d commit bd5c875

213 files changed

Lines changed: 535 additions & 287 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/risk-label.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { readFileSync } from 'node:fs';
77
const CRITICAL_PATHS = [
88
'packages/layout-engine/style-engine/',
99
'packages/layout-engine/layout-engine/',
10-
'packages/layout-engine/pm-adapter/',
10+
'packages/pm-adapter/',
1111
'packages/layout-engine/layout-bridge/',
1212
'packages/layout-engine/measuring/',
1313
'packages/layout-engine/painters/',

.github/scripts/risk-label.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('classify', () => {
8585

8686
it('critical: pm-adapter', () => {
8787
assert.equal(
88-
classify(['packages/layout-engine/pm-adapter/src/foo.js']).level,
88+
classify(['packages/pm-adapter/src/foo.js']).level,
8989
'critical',
9090
);
9191
});

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ SuperDoc uses its own rendering pipeline. ProseMirror stores document state; it
2727
| DOCX import/export | `super-editor/src/editors/v1/core/super-converter/` | Parse and preserve OOXML, style refs, inline properties. Do not bake resolved formatting into direct attrs. |
2828
| Style cascade | `layout-engine/style-engine/` | Single source of truth for defaults, styles, conditional formatting, inline overrides. |
2929
| Static document visuals | `pm-adapter/` data + `layout-engine/painters/dom/` rendering | Feed typed data into DomPainter. Do not style static content with PM decorations. |
30-
| Direction-aware properties | `layout-engine/painters/dom/` | DomPainter mirrors at paint time for `w:bidiVisual`. pm-adapter stores logical sides LTR-default. Pre-mirroring upstream is a double-swap. See `packages/layout-engine/pm-adapter/src/direction/README.md`. |
30+
| Direction-aware properties | `layout-engine/painters/dom/` | DomPainter mirrors at paint time for `w:bidiVisual`. pm-adapter stores logical sides LTR-default. Pre-mirroring upstream is a double-swap. See `packages/pm-adapter/src/direction/README.md`. |
3131
| Editing behavior | `super-editor/src/editors/v1/extensions/` | Commands, keybindings, editor plugins. Do not duplicate cascade or render document visuals here. |
3232
| Final DOM rendering | `layout-engine/painters/dom/` | Render `ResolvedLayout`. Paint-time transforms (e.g. RTL mirror) live here. |
3333
| New doc-api operation | `packages/document-api/src/contract/operation-definitions.ts` | Contract-first; touches 4 files. See `packages/document-api/README.md`. |

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ tests/visual/ Visual regression tests (Playwright)
8484
|--------------------------|---------------|
8585
| How something looks (visual rendering) | `layout-engine/painters/dom/` |
8686
| Style resolution (fonts, colors, borders) | `layout-engine/style-engine/` |
87-
| Data flowing from editor to renderer | `layout-engine/pm-adapter/` |
87+
| Data flowing from editor to renderer | `packages/pm-adapter/` |
8888
| Editing behavior (keyboard, commands) | `super-editor/src/editors/v1/extensions/` |
8989
| DOCX import/export | `super-editor/src/editors/v1/core/super-converter/` |
9090
| React integration | `packages/react/` |

docs/architecture/package-boundaries.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ For any entry classified as legacy public:
6565
| `packages/layout-engine/dom-contract` | `@superdoc/dom-contract` | Internal implementation | DOM rendering contracts |
6666
| `packages/layout-engine/painters/dom` | `@superdoc/painter-dom` | Internal implementation | DOM rendering pipeline |
6767
| `packages/layout-engine/measuring/dom` | `@superdoc/measuring-dom` | Internal implementation | Measurement pipeline |
68-
| `packages/layout-engine/pm-adapter` | `@superdoc/pm-adapter` | Internal implementation | ProseMirror to FlowBlock bridge |
68+
| `packages/pm-adapter` | `@superdoc/pm-adapter` | Internal implementation | ProseMirror to FlowBlock bridge |
6969
| `packages/layout-engine/style-engine` | `@superdoc/style-engine` | Internal implementation | OOXML cascade resolution |
7070
| `packages/layout-engine/layout-bridge` | `@superdoc/layout-bridge` | Internal implementation | Pipeline orchestration |
7171
| `packages/layout-engine/layout-engine` | `@superdoc/layout-engine` | Internal implementation | Pagination algorithms |

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test:behavior:html": "pnpm --filter @superdoc-testing/behavior test:html",
2626
"type-check": "pnpm run check:types",
2727
"type-check:force": "tsc -b --force tsconfig.references.json",
28-
"rebuild:types": "pnpm --workspace-concurrency=1 run --filter=@superdoc/common --filter=@superdoc/word-layout --filter=@superdoc/contracts --filter=@superdoc/dom-contract --filter=@superdoc/layout-resolved --filter=@superdoc/geometry-utils --filter=@superdoc/style-engine --filter=@superdoc/pm-adapter --filter=@superdoc/measuring-dom --filter=@superdoc/layout-engine --filter=@superdoc/layout-bridge build && pnpm --filter=@superdoc/painter-dom build",
28+
"rebuild:types": "pnpm --workspace-concurrency=1 run --filter=@superdoc/common --filter=@superdoc/word-layout --filter=@superdoc/contracts --filter=@superdoc/dom-contract --filter=@superdoc/layout-resolved --filter=@superdoc/geometry-utils --filter=@superdoc/style-engine --filter=@superdoc/layout-adapter --filter=@superdoc/pm-adapter --filter=@superdoc/measuring-dom --filter=@superdoc/layout-engine --filter=@superdoc/layout-bridge build && pnpm --filter=@superdoc/painter-dom build",
2929
"validate:commands": "node scripts/validate-command-types.mjs",
3030
"unzip": "bash packages/super-editor/src/editors/v1/tests/helpers/unzip.sh",
3131
"dev": "pnpm --prefix packages/superdoc run dev",

packages/layout-adapter/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# @superdoc/layout-adapter
2+
3+
Adapter-agnostic contract between document sources (ProseMirror today, custom transaction models later) and the SuperDoc layout pipeline.
4+
5+
## SD-3222 acceptance checklist
6+
7+
- [x] `@superdoc/pm-adapter` lives outside `packages/layout-engine/` (`packages/pm-adapter/`)
8+
- [x] Layout runtime packages (`layout-engine`, `layout-bridge`, `painters/dom`, `contracts`) do not import concrete adapters at runtime (Guard H)
9+
- [x] Super-editor presentation path uses `@superdoc/layout-adapter` registry instead of direct `toFlowBlocks` imports
10+
- [x] Default ProseMirror adapter registers via `@superdoc/pm-adapter/register`
11+
- [x] Layout integration tests convert documents through the adapter contract helper
12+
13+
## Usage
14+
15+
Register the default PM adapter once at application startup:
16+
17+
```ts
18+
import '@superdoc/pm-adapter/register';
19+
```
20+
21+
Convert documents through the contract:
22+
23+
```ts
24+
import { getLayoutDocumentAdapter } from '@superdoc/layout-adapter';
25+
26+
const { blocks, bookmarks } = getLayoutDocumentAdapter().toFlowBlocks(docJson, {
27+
emitSectionBreaks: true,
28+
converterContext,
29+
});
30+
```
31+
32+
Implement a new adapter by satisfying `LayoutDocumentAdapter` and calling `registerLayoutDocumentAdapter`.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "@superdoc/layout-adapter",
3+
"version": "0.0.0",
4+
"description": "Adapter-agnostic contract between document sources and the SuperDoc layout pipeline.",
5+
"type": "module",
6+
"private": true,
7+
"main": "./src/index.ts",
8+
"types": "./src/index.ts",
9+
"exports": {
10+
".": {
11+
"types": "./src/index.ts",
12+
"source": "./src/index.ts",
13+
"default": "./src/index.ts"
14+
},
15+
"./sections": {
16+
"types": "./src/sections.ts",
17+
"source": "./src/sections.ts",
18+
"default": "./src/sections.ts"
19+
},
20+
"./*.js": {
21+
"types": "./src/*.ts",
22+
"source": "./src/*.ts",
23+
"default": "./src/*.ts"
24+
},
25+
"./*": {
26+
"types": "./src/*",
27+
"source": "./src/*",
28+
"default": "./src/*"
29+
}
30+
},
31+
"scripts": {
32+
"build": "tsc --project tsconfig.json --noEmit",
33+
"type-check": "tsc --noEmit"
34+
},
35+
"dependencies": {
36+
"@superdoc/contracts": "workspace:*"
37+
},
38+
"devDependencies": {
39+
"typescript": "catalog:"
40+
}
41+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { FlowBlock, SectionMetadata, TrackedChangesMode } from '@superdoc/contracts';
2+
3+
import type { SectionRange } from './sections.js';
4+
5+
/** Opaque document input for a concrete adapter (e.g. PM JSON). */
6+
export type DocumentAdapterInput = unknown;
7+
8+
export type FlowBlocksResult = {
9+
blocks: FlowBlock[];
10+
bookmarks: Map<string, number>;
11+
};
12+
13+
export interface FlowBlockCacheLike {
14+
clear(): void;
15+
setHasExternalChanges?(value: boolean): void;
16+
}
17+
18+
export interface DocumentAdapterConvertOptions {
19+
defaultFont?: string;
20+
defaultSize?: number;
21+
blockIdPrefix?: string;
22+
storyKey?: string;
23+
atomNodeTypes?: Iterable<string>;
24+
positions?: unknown;
25+
mediaFiles?: Record<string, string>;
26+
emitSectionBreaks?: boolean;
27+
showBookmarks?: boolean;
28+
trackedChangesMode?: TrackedChangesMode;
29+
enableTrackedChanges?: boolean;
30+
enableRichHyperlinks?: boolean;
31+
enableComments?: boolean;
32+
themeColors?: Record<string, string>;
33+
sectionMetadata?: SectionMetadata[];
34+
/** Adapter-specific style/converter payload (e.g. PM ConverterContext). */
35+
converterContext?: unknown;
36+
flowBlockCache?: FlowBlockCacheLike;
37+
}
38+
39+
export interface DocumentAdapter {
40+
readonly id: string;
41+
toFlowBlocks(input: DocumentAdapterInput, options?: DocumentAdapterConvertOptions): FlowBlocksResult;
42+
createFlowBlockCache?(): FlowBlockCacheLike;
43+
}
44+
45+
export interface SectionAnalysisAdapter {
46+
analyzeSectionRanges(doc: DocumentAdapterInput, bodySectPr?: unknown): SectionRange[];
47+
}
48+
49+
export interface LayoutDocumentAdapter extends DocumentAdapter, SectionAnalysisAdapter {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export type {
2+
DocumentAdapterInput,
3+
DocumentAdapterConvertOptions,
4+
DocumentAdapter,
5+
FlowBlockCacheLike,
6+
FlowBlocksResult,
7+
LayoutDocumentAdapter,
8+
SectionAnalysisAdapter,
9+
} from './document-adapter.js';
10+
11+
export {
12+
registerLayoutDocumentAdapter,
13+
getLayoutDocumentAdapter,
14+
resetLayoutDocumentAdapterForTests,
15+
} from './registry.js';
16+
17+
export {
18+
SectionType,
19+
DEFAULT_PARAGRAPH_SECTION_TYPE,
20+
DEFAULT_BODY_SECTION_TYPE,
21+
type SectPrElement,
22+
type SectPrChildElement,
23+
type ParagraphProperties,
24+
type SectPrLikeObject,
25+
type SectionSignature,
26+
type SectionVerticalAlign,
27+
type SectionRange,
28+
} from './sections.js';

0 commit comments

Comments
 (0)