@sobree/core 0.1.1 → 0.1.3

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 (53) hide show
  1. package/dist/doc/styles.d.ts +12 -0
  2. package/dist/doc/types.d.ts +68 -31
  3. package/dist/docx/export/contentTypes.d.ts +1 -0
  4. package/dist/docx/export/context.d.ts +1 -1
  5. package/dist/docx/export/numbering.d.ts +2 -0
  6. package/dist/docx/import/anchoredFrames.d.ts +4 -0
  7. package/dist/docx/import/borders.d.ts +12 -0
  8. package/dist/docx/import/floatFrames.d.ts +11 -0
  9. package/dist/docx/import/inlineFrames.d.ts +4 -0
  10. package/dist/docx/import/paragraphs.d.ts +6 -1
  11. package/dist/docx/import/styles.d.ts +13 -0
  12. package/dist/docx/shared/drawingColor.d.ts +30 -0
  13. package/dist/docx/shared/units.d.ts +3 -2
  14. package/dist/docx/shared/xml.d.ts +12 -0
  15. package/dist/docx/types.d.ts +4 -0
  16. package/dist/editor/commands.d.ts +14 -0
  17. package/dist/editor/context.d.ts +71 -0
  18. package/dist/editor/dom.d.ts +21 -0
  19. package/dist/editor/events.d.ts +29 -0
  20. package/dist/editor/index.d.ts +85 -663
  21. package/dist/editor/internal/mutations.d.ts +1 -1
  22. package/dist/editor/ops/blocks.d.ts +42 -0
  23. package/dist/editor/ops/comments.d.ts +12 -0
  24. package/dist/editor/ops/parts.d.ts +71 -0
  25. package/dist/editor/ops/review.d.ts +67 -0
  26. package/dist/editor/ops/runs.d.ts +83 -0
  27. package/dist/editor/ops/trackedInput.d.ts +44 -0
  28. package/dist/editor/query.d.ts +22 -0
  29. package/dist/editor/revisionRuns.d.ts +56 -0
  30. package/dist/editor/selection.d.ts +34 -0
  31. package/dist/editor/types.d.ts +292 -0
  32. package/dist/editor/view/docRenderer/anchorPosition.d.ts +18 -0
  33. package/dist/editor/view/docRenderer/fontFallback.d.ts +18 -5
  34. package/dist/editor/view/docRenderer/inline.d.ts +2 -2
  35. package/dist/editor/view/docRenderer/lists.d.ts +22 -11
  36. package/dist/editor/view/docRenderer/sectionFlow.d.ts +23 -0
  37. package/dist/editor/view/docRenderer/table.d.ts +11 -2
  38. package/dist/index.css +1 -1
  39. package/dist/index.js +5481 -5060
  40. package/dist/index.js.map +1 -1
  41. package/dist/paperStack/paginationV2/apply.d.ts +18 -0
  42. package/dist/paperStack/paginationV2/engine.d.ts +7 -0
  43. package/dist/paperStack/paginationV2/measure.d.ts +9 -0
  44. package/dist/paperStack/paginationV2/types.d.ts +273 -0
  45. package/dist/paperStack/paper.d.ts +31 -9
  46. package/dist/paperStack/paperStack.d.ts +13 -1
  47. package/dist/paperStack/paperZone.d.ts +35 -0
  48. package/dist/plugins/marks.d.ts +4 -4
  49. package/dist/ydoc/runs.d.ts +14 -18
  50. package/dist/ydoc/schema.d.ts +5 -0
  51. package/package.json +1 -1
  52. package/dist/__vite-browser-external-DYxpcVy9.js +0 -5
  53. package/dist/__vite-browser-external-DYxpcVy9.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ import { PaginatedDoc } from './types';
2
+ /**
3
+ * Apply a PaginatedDoc to the source DOM. The `sourceBlocks` array
4
+ * must be the SAME elements (same `data-meas-id` stamps) the measure
5
+ * pass walked — the applicator uses the id to resolve a `PageSegment`
6
+ * back to the originating element.
7
+ *
8
+ * Returns one `HTMLElement[]` per page, in the order the page should
9
+ * render. The caller (paperStack) appends these into paper-content boxes.
10
+ *
11
+ * SIDE EFFECTS on the source DOM:
12
+ * - Paragraph / list-item splits clone the source element, replacing
13
+ * it with a head fragment in place.
14
+ * - List / table clones MOVE their LI / TR children out of the source
15
+ * into per-page clones; the source container is removed if emptied.
16
+ * Source DOM should be treated as consumed after this call.
17
+ */
18
+ export declare function applyPaginatedDoc(doc: PaginatedDoc, sourceBlocks: readonly HTMLElement[]): HTMLElement[][];
@@ -0,0 +1,7 @@
1
+ import { BlockMeasurement, PaginatedDoc, PaginationConstraints } from './types';
2
+ /**
3
+ * Pure paginator over the new typed contract. Determinism, totality,
4
+ * forced-break semantics — all from the existing engine; this wrapper
5
+ * just translates.
6
+ */
7
+ export declare function paginateMeasurements(measurements: ReadonlyArray<BlockMeasurement>, constraints: PaginationConstraints): PaginatedDoc;
@@ -0,0 +1,9 @@
1
+ import { BlockMeasurement } from './types';
2
+ /**
3
+ * Measure a flat list of top-level block elements.
4
+ *
5
+ * `blocks` is the same `HTMLElement[]` shape `paginateBlocks` takes
6
+ * today — caller's responsibility to filter to direct flow children of
7
+ * the source paper-content.
8
+ */
9
+ export declare function measureBlocks(blocks: readonly HTMLElement[]): BlockMeasurement[];
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Phase 2 paginator contract — the typed boundary between the renderer
3
+ * and the pagination engine.
4
+ *
5
+ * Today's flow (the layer this replaces):
6
+ *
7
+ * blocks: HTMLElement[]
8
+ * → buildItems() (reads live DOM `offsetHeight`,
9
+ * stamps `data-pag-*` ids,
10
+ * emits an Item[] stream)
11
+ * → paginate() (the pure engine — already first-class)
12
+ * → distributePages() (mutates DOM: splits <p>/<ol>/<table>)
13
+ * → collapse*Pages() (post-process passes that re-read heights
14
+ * from blocks in the WRONG DOM context,
15
+ * source of the worst recurring bugs)
16
+ *
17
+ * Why that's wrong (recorded in `packages/core/docs/SESSION_HANDOFF.md`):
18
+ * - Forced breaks are smuggled in as `Penalty(-Infinity)` inside the
19
+ * Item stream, NOT as first-class constraints. The engine can
20
+ * refuse to grow the page array when honouring them would create
21
+ * overflow — so a `<w:pageBreakBefore/>` that doesn't fit silently
22
+ * shortens the document.
23
+ * - Inter-module signalling is ad-hoc: `data-page-break-before`,
24
+ * `data-keep-next`, `data-pag-pid`, `data-pag-lid`, `data-pag-tid`,
25
+ * `.keep-together`, etc. — none of it typed.
26
+ * - The post-process passes re-measure block heights after some
27
+ * blocks have moved and others haven't, producing nonsense values
28
+ * (a 268px table measured as 36px in firstContent → mistakenly
29
+ * classified as a widow → absorbed onto the previous page → 268px
30
+ * overflow into the footer band).
31
+ *
32
+ * The new contract:
33
+ *
34
+ * BlockMeasurement[] ← computed ONCE from source DOM
35
+ * PaginationConstraints ← typed budget + rules
36
+ * → purePaginate() ← engine: pure (measurements,
37
+ * constraints) → PaginatedDoc
38
+ * PaginatedDoc ← partition, NOT a DOM mutation
39
+ * → applyPaginatedDoc() ← single DOM pass: splits + moves
40
+ *
41
+ * Pure. Typed. Forced-breaks-as-boundaries. One DOM mutation pass.
42
+ *
43
+ * This file is types only. The measurement pass, engine wrapper, and
44
+ * DOM applicator land in sibling files in subsequent steps and are
45
+ * gated behind a flag until per-fixture verification passes.
46
+ */
47
+ /**
48
+ * One block as the paginator sees it. All quantities are pre-measured;
49
+ * the engine NEVER reads the DOM.
50
+ *
51
+ * Multi-line paragraphs, multi-item lists, multi-row tables are SINGLE
52
+ * BlockMeasurements with non-empty `splitPoints` — the engine breaks
53
+ * within the block when it chooses one of those points.
54
+ */
55
+ export interface BlockMeasurement {
56
+ /**
57
+ * Stable identifier. The engine refers to blocks (and segments within
58
+ * blocks) only by id — never by DOM reference. The DOM applicator
59
+ * holds the `blockId → HTMLElement` map.
60
+ *
61
+ * Ids must be stable across pagination retries (footnote-zone budget
62
+ * recalc, e.g.). Caller's choice; typically derived from the source
63
+ * AST's `block-index` or from the rendered DOM's `data-block-index`.
64
+ */
65
+ blockId: string;
66
+ /**
67
+ * Natural rendered height of this block in CSS px when placed in its
68
+ * canonical context (the source paper's `.paper-content`). Measured
69
+ * once by the measurement pass.
70
+ *
71
+ * For a splittable block (paragraph with multiple lines, list with
72
+ * multiple items, table with multiple rows), this is the height when
73
+ * the WHOLE block lands on one page. When the engine chooses to split
74
+ * at a `splitPoint`, the page-end segment's height is
75
+ * `splitPoint.yOffset`; the next-page segment's height is
76
+ * `height - splitPoint.yOffset`.
77
+ */
78
+ height: number;
79
+ /**
80
+ * Inter-block gap that appears BEFORE this block in flow — the
81
+ * post-margin-collapse vertical distance between the previous block's
82
+ * bottom and this block's top. Encoded as Glue in the engine's
83
+ * stream; counted against page height only between non-trailing
84
+ * blocks (trailing glue discards at page boundaries).
85
+ *
86
+ * 0 for the first block in the stream.
87
+ */
88
+ gapBefore: number;
89
+ /**
90
+ * Points at which the engine may break this block across pages.
91
+ * Empty / undefined → the block is monolithic (whole thing moves
92
+ * together). For paragraphs, one entry per line break. For lists,
93
+ * one per `<li>`. For tables, one per `<tr>`.
94
+ *
95
+ * The yOffset is the height consumed by the on-page side of the
96
+ * split, measured from the block's top edge. The breakpoint after
97
+ * the LAST line/item/row is implicit (it's the block's bottom edge)
98
+ * and not included.
99
+ */
100
+ splitPoints?: ReadonlyArray<SplitPoint>;
101
+ /**
102
+ * The block carries an explicit forced page break BEFORE it
103
+ * (`<w:pageBreakBefore/>` on the paragraph, or
104
+ * `<w:br w:type="page"/>` in its runs). The engine MUST start a new
105
+ * page at this block — and MUST grow the page array if the previous
106
+ * page is already full.
107
+ *
108
+ * This is the field forced-break semantics hang on. The engine
109
+ * doesn't read `data-page-break-before` from DOM; it reads this.
110
+ */
111
+ pageBreakBefore?: boolean;
112
+ /**
113
+ * Keep on the same page as the NEXT block in the stream. Heading
114
+ * styles set this so the heading stays with its first content
115
+ * paragraph. If both don't fit, the break moves to BEFORE this
116
+ * block.
117
+ */
118
+ keepWithNext?: boolean;
119
+ /**
120
+ * Keep all of this block's split points together (i.e. the whole
121
+ * block stays on one page). Stronger than monolithic: a
122
+ * `keepTogether` block CAN have splitPoints (so the engine knows
123
+ * the block's internal structure for cost estimation) but is
124
+ * penalised heavily for actually breaking. Used for figures /
125
+ * `.keep-together` groups.
126
+ */
127
+ keepTogether?: boolean;
128
+ /**
129
+ * Out-of-flow blocks (`position: absolute` / `fixed`) contribute 0
130
+ * to the page budget. The engine still emits them in document order
131
+ * so the DOM applicator can route them to the right page, but their
132
+ * height doesn't compete with in-flow content for space.
133
+ *
134
+ * Anchored frames are intended to render via the floating layer
135
+ * (anchorLayer.ts) and not appear here at all; this flag covers the
136
+ * fallback where an out-of-flow block still ended up in the body
137
+ * block stream.
138
+ */
139
+ outOfFlow?: boolean;
140
+ }
141
+ /**
142
+ * A position within a block where the engine may break across a page
143
+ * boundary. The block stays as one DOM element when not broken; when
144
+ * the engine chooses to break here, the DOM applicator slices it.
145
+ */
146
+ export interface SplitPoint {
147
+ /**
148
+ * Height (px) consumed by the on-page portion if the engine breaks
149
+ * AT this point. Equivalently: the position of this break within
150
+ * the block's natural rendered height.
151
+ */
152
+ yOffset: number;
153
+ /**
154
+ * Stable identifier for the on-page segment ending here. For
155
+ * paragraphs, the line index. For lists, the `<li>` index. For
156
+ * tables, the `<tr>` index. The DOM applicator uses this to know
157
+ * where to cut.
158
+ */
159
+ segmentId: string;
160
+ /**
161
+ * Cost added if the engine breaks at this point. 0 = neutral,
162
+ * `Number.POSITIVE_INFINITY` = forbidden. Used for widow / orphan
163
+ * penalties (e.g. forbidding a break that leaves 1 line of a
164
+ * 3-line paragraph alone).
165
+ */
166
+ penalty?: number;
167
+ }
168
+ /**
169
+ * Engine constraints — the budget and rules the engine optimises
170
+ * within. Separate from `BlockMeasurement[]` so the same stream can
171
+ * be re-paginated under different budgets (footnote-zone recalc)
172
+ * without re-measuring.
173
+ */
174
+ export interface PaginationConstraints {
175
+ /**
176
+ * Body-area height for page index `i`, in px. The engine uses
177
+ * `pageHeights[i]` when present, falling back to
178
+ * `defaultPageHeight` for pages beyond the array's length.
179
+ *
180
+ * Today's paginator computes per-page heights iteratively because
181
+ * footnote zones eat budget on specific pages; that signal flows in
182
+ * here once, instead of being re-discovered through retry loops.
183
+ */
184
+ pageHeights: ReadonlyArray<number>;
185
+ /** Default body-area height for pages past `pageHeights.length`. */
186
+ defaultPageHeight: number;
187
+ /** Minimum lines of a paragraph that may sit on the new page. Default 1. */
188
+ widows?: number;
189
+ /** Minimum lines of a paragraph that may sit on the current page. Default 1. */
190
+ orphans?: number;
191
+ /**
192
+ * Penalty cost added to breaks that would create a widow or orphan
193
+ * inside `widows` / `orphans` limits. Default 10000 — high enough
194
+ * to win against natural breaks but finite so it doesn't escalate
195
+ * to `Infinity` and create unsatisfiable constraints.
196
+ */
197
+ widowOrphanPenalty?: number;
198
+ /**
199
+ * Penalty cost added to breaks inside a `keepWithNext` adjacency or
200
+ * across a `keepTogether` block. Default 10000.
201
+ */
202
+ keepPenalty?: number;
203
+ }
204
+ /**
205
+ * The paginator's output: a partition of the input stream into pages,
206
+ * expressed as data. NO DOM mutation has happened yet.
207
+ *
208
+ * The DOM applicator reads this and performs the necessary splits +
209
+ * moves in a single pass. Snapshot tests bite at THAT layer.
210
+ */
211
+ export interface PaginatedDoc {
212
+ pages: ReadonlyArray<PaginatedPage>;
213
+ /**
214
+ * Sum of penalty costs the engine accepted for this partition.
215
+ * Diagnostic: a high value means widow/orphan/keep rules were
216
+ * violated to satisfy a forced break.
217
+ */
218
+ totalCost: number;
219
+ /**
220
+ * Whether the engine had to grow the page array beyond what the
221
+ * `pageHeights` array sized. True when a forced break landed on a
222
+ * page that wasn't accounted for in the constraints — the caller
223
+ * may want to re-measure footnote zones for the new page.
224
+ */
225
+ grewPageArray: boolean;
226
+ }
227
+ /**
228
+ * One page's assignment, in document order.
229
+ */
230
+ export interface PaginatedPage {
231
+ segments: ReadonlyArray<PageSegment>;
232
+ /**
233
+ * Total height the engine packed onto this page, excluding trailing
234
+ * glue. Useful for the DOM applicator to know how much space the
235
+ * placed content needs (e.g. for vertical alignment within the
236
+ * paper-content box).
237
+ */
238
+ usedHeight: number;
239
+ }
240
+ /**
241
+ * One block's contribution to a page. When `range` is absent the
242
+ * whole block lands here. When present, only the slice from
243
+ * `startSegmentId` (exclusive of any earlier-page slice) to
244
+ * `endSegmentId` (last segment on this page) lands here.
245
+ *
246
+ * Continuation across pages of the same block is recognised by
247
+ * matching `blockId` between adjacent pages — both halves carry the
248
+ * same id; the segment ids tell the DOM applicator where to cut.
249
+ */
250
+ export interface PageSegment {
251
+ blockId: string;
252
+ range?: SegmentRange;
253
+ }
254
+ export interface SegmentRange {
255
+ /** First on-page segment (inclusive). */
256
+ startSegmentId: string;
257
+ /** Last on-page segment (inclusive). */
258
+ endSegmentId: string;
259
+ }
260
+ /**
261
+ * The pure paginator: typed in, typed out, no I/O, no globals.
262
+ *
263
+ * - Determinism: same inputs ⇒ same `PaginatedDoc` (modulo undefined
264
+ * behaviour on tied costs, which the engine MUST resolve in a
265
+ * stable, documented way — typically by preferring earlier breaks).
266
+ * - Totality: defined for every legal input. Empty `measurements` ⇒
267
+ * `{ pages: [], totalCost: 0, grewPageArray: false }`.
268
+ * - Forced-break correctness: if any `BlockMeasurement` has
269
+ * `pageBreakBefore: true`, the returned page assignment places it
270
+ * at the START of some page — never mid-page. The page array
271
+ * grows as needed.
272
+ */
273
+ export type PurePaginate = (measurements: ReadonlyArray<BlockMeasurement>, constraints: PaginationConstraints) => PaginatedDoc;
@@ -1,14 +1,8 @@
1
1
  import { PageSetup } from './pageSetup';
2
2
  import { AnchorLayerContext } from '../editor/view/docRenderer/anchorLayer';
3
- import { AnchoredFrame, Block, NamedStyle, NumberingDefinition, SectionProperties } from '../doc/types';
4
- export interface ZoneRenderContext {
5
- blocks: readonly Block[];
6
- numbering: readonly NumberingDefinition[];
7
- styles: readonly NamedStyle[];
8
- rawParts: Record<string, Uint8Array>;
9
- pageNumber: number;
10
- totalPages: number;
11
- }
3
+ import { ZoneRenderContext } from './paperZone';
4
+ import { AnchoredFrame, SectionProperties } from '../doc/types';
5
+ export type { ZoneRenderContext } from './paperZone';
12
6
  /**
13
7
  * A single paper — exactly one page. Fixed width and height from the page
14
8
  * setup. Has a header zone, a content area, and a footer zone. The content
@@ -53,6 +47,15 @@ export declare class Paper {
53
47
  readonly comments: HTMLElement;
54
48
  private readonly header;
55
49
  private readonly footer;
50
+ /**
51
+ * Floating overlays for the header / footer zones. Unlike `.paper-
52
+ * anchors` (which overlays only the content rectangle), these span the
53
+ * whole paper card so a header frame's page/margin-relative offsets
54
+ * land at the right spot above the content area. Painted from
55
+ * `headerFooterFrames[partId]` — a zone is flow + floats, like the body.
56
+ */
57
+ private readonly headerAnchors;
58
+ private readonly footerAnchors;
56
59
  constructor(container: HTMLElement, setup: PageSetup);
57
60
  applySetup(setup: PageSetup): void;
58
61
  /**
@@ -70,6 +73,25 @@ export declare class Paper {
70
73
  */
71
74
  setHeaderBlocks(ctx: ZoneRenderContext): void;
72
75
  setFooterBlocks(ctx: ZoneRenderContext): void;
76
+ /**
77
+ * Paint the header zone's floating frames. Resolved against the header
78
+ * flow (so `verticalFrom="paragraph"` anchors to a header paragraph).
79
+ * Pass `[]` to clear. Mirrors `setAnchoredFrames` for body.
80
+ */
81
+ setHeaderFrames(frames: readonly AnchoredFrame[], ctx: AnchorLayerContext): void;
82
+ setFooterFrames(frames: readonly AnchoredFrame[], ctx: AnchorLayerContext): void;
83
+ /**
84
+ * Resolve each frame's `relativeFrom` origin to an absolute card-relative
85
+ * position, returning clones whose `offsetX/YEmu` are the final
86
+ * coordinates the (full-card, `inset:0`) overlay paints at. The pure
87
+ * `resolveAnchorPosition` owns the semantics; here we just supply the
88
+ * measured geometry: page margins (from the CSS vars) and — for
89
+ * `verticalFrom="paragraph"` — the anchor paragraph's rendered top,
90
+ * located within `zoneFlow` by the `data-block-index` `renderBlocks`
91
+ * stamps. `offsetTop` is zoom-invariant in Chromium, so the measurement
92
+ * stays logical at any render tier.
93
+ */
94
+ private resolveFrames;
73
95
  /**
74
96
  * When a rich header (or footer) renders taller than the page's
75
97
  * reserved margin, push body content out of the overlap.
@@ -10,6 +10,9 @@ import { AnchoredFrame, Block, NamedStyle, NumberingDefinition, SectionPropertie
10
10
  */
11
11
  export interface RichZonesSource {
12
12
  headerFooterBodies: Record<string, Block[]>;
13
+ /** Floating frames per header/footer part, keyed by the same partId as
14
+ * `headerFooterBodies`. Absent for docs whose zones have no floats. */
15
+ headerFooterFrames?: Record<string, AnchoredFrame[]>;
13
16
  numbering: readonly NumberingDefinition[];
14
17
  styles: readonly NamedStyle[];
15
18
  rawParts: Record<string, Uint8Array>;
@@ -171,6 +174,15 @@ export declare class PaperStack {
171
174
  private collectAllBlocks;
172
175
  private ensurePaperCount;
173
176
  private renderAllZones;
177
+ /**
178
+ * Build the `AnchorLayerContext` shared by body + zone overlays. The
179
+ * injected `renderBody` routes textbox bodies through the full
180
+ * `renderBlocks` pipeline so anchored text matches body formatting,
181
+ * keeping `anchorLayer` decoupled from `block.ts`.
182
+ */
183
+ private anchorLayerCtx;
184
+ /** Context for clearing an overlay to empty (no rich zones to draw). */
185
+ private emptyAnchorCtx;
174
186
  /**
175
187
  * Filter the document's `anchoredFrames` per paper and ask each Paper
176
188
  * to swap its anchor-layer DOM accordingly. A frame's destination
@@ -190,7 +202,7 @@ export declare class PaperStack {
190
202
  * this is the only piece of mutual knowledge between body flow and
191
203
  * the floating layer needed for paragraph anchoring.
192
204
  */
193
- private buildParagraphPageIndex;
205
+ private buildParagraphIndex;
194
206
  /**
195
207
  * Section index a paper belongs to. Read off the first content
196
208
  * block's `data-section-index` (stamped by `renderBlocks`). Empty
@@ -0,0 +1,35 @@
1
+ import { AnchorLayerContext } from '../editor/view/docRenderer/anchorLayer';
2
+ import { AnchoredFrame, Block, NamedStyle, NumberingDefinition } from '../doc/types';
3
+ /**
4
+ * Everything a header/footer zone needs to render its FLOW content. The
5
+ * floating layer (anchored frames) is painted separately via
6
+ * {@link paintZoneFrames} — a zone is a flow layer + a float layer, the
7
+ * same two-layer model the body uses.
8
+ */
9
+ export interface ZoneRenderContext {
10
+ blocks: readonly Block[];
11
+ numbering: readonly NumberingDefinition[];
12
+ styles: readonly NamedStyle[];
13
+ rawParts: Record<string, Uint8Array>;
14
+ pageNumber: number;
15
+ totalPages: number;
16
+ }
17
+ /**
18
+ * Render a zone's flow blocks. Uses the same `renderBlocks` walker as
19
+ * body content so drawings, formatting, hyperlinks and tables all carry
20
+ * through. `PAGE` / `NUMPAGES` field nodes are substituted with this
21
+ * paper's page number / the total count.
22
+ *
23
+ * "Empty" here = no rendered text anywhere; an image-only header still
24
+ * counts as non-empty so the `is-empty` CSS doesn't collapse its space.
25
+ */
26
+ export declare function renderZone(zone: HTMLElement, ctx: ZoneRenderContext): void;
27
+ /**
28
+ * Paint a zone's floating frames into its overlay element. Mirrors
29
+ * `Paper.setAnchoredFrames` for the body: `renderAnchorLayer` builds a
30
+ * fresh layer, we copy its children into the persistent overlay node so
31
+ * external observers keep a stable reference, and `is-empty` collapses
32
+ * the overlay when there are no frames.
33
+ */
34
+ export declare function paintZoneFrames(overlay: HTMLElement, frames: readonly AnchoredFrame[], ctx: AnchorLayerContext): void;
35
+ export declare function setZoneText(zone: HTMLElement, text: string): void;
@@ -1,6 +1,6 @@
1
1
  import { Range as ApiRange } from '../doc/api';
2
2
  import { RunProperties } from '../doc/types';
3
- import { Editor, WrapTag } from '../editor';
3
+ import { EditorLike, WrapTag } from '../editor/types';
4
4
  /**
5
5
  * Mark toggle helpers — shared by the floating toolbar's mark buttons
6
6
  * and the keyboard plugin (Ctrl+B / I / U / …).
@@ -34,16 +34,16 @@ export declare const MARK_COMMAND_DEFS: readonly MarkCommandDef[];
34
34
  * `mark` (highlight) is delegated straight to `wrapRange` — it's not a
35
35
  * toggle since highlight is a colour, not a boolean.
36
36
  */
37
- export declare function toggleMark(editor: Editor, range: ApiRange, tag: WrapTag): void;
37
+ export declare function toggleMark(editor: EditorLike, range: ApiRange, tag: WrapTag): void;
38
38
  /**
39
39
  * True when every non-empty text run inside `range` has the mark's "on"
40
40
  * value. Walks hyperlink children. Multi-block ranges always read as
41
41
  * inactive so a click sets the mark before clearing it.
42
42
  */
43
- export declare function isMarkActive(editor: Editor, range: ApiRange, tag: string): boolean;
43
+ export declare function isMarkActive(editor: EditorLike, range: ApiRange, tag: string): boolean;
44
44
  /**
45
45
  * Pick the right range to operate on: live DOM selection if any,
46
46
  * otherwise the full extent of the block at the caret. Matches the
47
47
  * "press Ctrl+B with caret only → bold the whole paragraph" UX.
48
48
  */
49
- export declare function rangeAtSelection(editor: Editor): ApiRange | null;
49
+ export declare function rangeAtSelection(editor: EditorLike): ApiRange | null;
@@ -1,4 +1,4 @@
1
- import { BreakRun, DrawingRun, InlineRun, RunProperties } from '../doc/types';
1
+ import { BreakRun, CommentRefRun, DrawingRun, FieldRun, FootnoteRefRun, HyperlinkRun, InlineRun, RunProperties, TabRun, TextRun } from '../doc/types';
2
2
  export interface DeltaOp {
3
3
  /** Text content (string) or embed (object literal). Y.Text differentiates by typeof. */
4
4
  insert: string | EmbedContent;
@@ -6,24 +6,19 @@ export interface DeltaOp {
6
6
  * no marks apply (Yjs preference: omit rather than set empty). */
7
7
  attributes?: Record<string, unknown>;
8
8
  }
9
- /** All non-text run kinds become embed objects in the delta. */
10
- export type EmbedContent = {
11
- __sobree: "break";
12
- type: BreakRun["type"];
13
- } | {
14
- __sobree: "tab";
15
- } | {
16
- __sobree: "field";
17
- instruction: string;
18
- cached?: string;
19
- } | {
20
- __sobree: "drawing";
21
- partPath: string;
22
- widthEmu: number;
23
- heightEmu: number;
24
- placement: DrawingRun["placement"];
25
- altText?: string;
9
+ /** Run kinds that travel as atomic embeds (everything except text,
10
+ * which is the Y.Text string itself, and hyperlink, which flattens to
11
+ * a `link` mark on its children). */
12
+ type EmbedRun = Exclude<InlineRun, TextRun | HyperlinkRun>;
13
+ /** An embed is the run itself, structurally: `kind` becomes the
14
+ * `__sobree` discriminator and `properties` move to the op's
15
+ * attributes. DERIVED from the AST type — a new field on any embed
16
+ * run kind is carried automatically; there is no per-field whitelist
17
+ * that could silently drop it. */
18
+ type EmbedOf<R extends EmbedRun> = Omit<R, "kind" | "properties"> & {
19
+ __sobree: R["kind"];
26
20
  };
21
+ export type EmbedContent = EmbedOf<BreakRun> | EmbedOf<TabRun> | EmbedOf<FieldRun> | EmbedOf<DrawingRun> | EmbedOf<FootnoteRefRun> | EmbedOf<CommentRefRun>;
27
22
  /** The `link` mark — stamped on every char inside a HyperlinkRun. */
28
23
  export interface LinkMark {
29
24
  href: string;
@@ -49,3 +44,4 @@ export declare function attrsToRunProps(attrs: Record<string, unknown> | undefin
49
44
  * Y.Text diff to detect cells that haven't changed.
50
45
  */
51
46
  export declare function deepEqual(a: unknown, b: unknown): boolean;
47
+ export {};
@@ -117,6 +117,11 @@ export declare const Y_BLOCK_AST_KEY = "_ast";
117
117
  export declare const Y_META_FIELDS: {
118
118
  readonly sections: "sections";
119
119
  readonly headerFooterBodies: "headerFooterBodies";
120
+ readonly anchoredFrames: "anchoredFrames";
121
+ readonly headerFooterFrames: "headerFooterFrames";
122
+ readonly footnotes: "footnotes";
123
+ readonly comments: "comments";
124
+ readonly settings: "settings";
120
125
  readonly styles: "styles";
121
126
  readonly numbering: "numbering";
122
127
  readonly fonts: "fonts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sobree/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,5 +0,0 @@
1
- const e = {};
2
- export {
3
- e as default
4
- };
5
- //# sourceMappingURL=__vite-browser-external-DYxpcVy9.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"__vite-browser-external-DYxpcVy9.js","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"AAAA,MAAAA,IAAe,CAAA;"}