@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
@@ -17,3 +17,15 @@ export declare function resolveStyleCascade(styles: readonly NamedStyle[] | Sobr
17
17
  runDefaults: RunProperties;
18
18
  paragraphDefaults: ParagraphProperties;
19
19
  };
20
+ /**
21
+ * Resolve a RUN character style (`<w:rStyle>`) to the run properties it
22
+ * contributes: its own rPr merged up its `basedOn` chain — but WITHOUT
23
+ * the Normal / DocDefaults anchor that `resolveStyleCascade` appends.
24
+ *
25
+ * A character style layers on top of the run's INHERITED paragraph
26
+ * formatting; folding the document defaults back in here would reset the
27
+ * run's font / size to the doc default (e.g. a colour-only "Blue" char
28
+ * style must not drag Times/12pt onto a Helvetica/10pt contact line). So
29
+ * we walk only the explicit `basedOn` chain and stop — no Normal anchor.
30
+ */
31
+ export declare function resolveRunStyle(styles: readonly NamedStyle[], styleId: string): RunProperties;
@@ -30,6 +30,15 @@ export interface SobreeDocument {
30
30
  * emits each body to its own OOXML part at export time.
31
31
  */
32
32
  headerFooterBodies: Record<string, Block[]>;
33
+ /**
34
+ * Floating objects that live inside a header/footer part, keyed by the
35
+ * SAME `HeaderFooterRef.partId` as `headerFooterBodies`. A header part
36
+ * is a self-contained sub-document: its flow blocks live in
37
+ * `headerFooterBodies[partId]`, its anchored frames here. The renderer
38
+ * paints these into a per-zone overlay exactly like body `anchoredFrames`.
39
+ * Empty/absent for the common header-without-floats case.
40
+ */
41
+ headerFooterFrames?: Record<string, AnchoredFrame[]>;
33
42
  /** Named styles (Heading1, Quote, Body Text, …) defined at the doc level. */
34
43
  styles: NamedStyle[];
35
44
  /** List/numbering definitions referenced by `Paragraph.properties.numbering`. */
@@ -129,6 +138,20 @@ export interface AnchoredFrame {
129
138
  * floats over text and reserves nothing. Absent ⇒ unknown (treated
130
139
  * as non-displacing). */
131
140
  wrap?: "square" | "topAndBottom" | "tight" | "through" | "none";
141
+ /** `wrapText` side from `<wp:wrapSquare|Tight|Through wrapText="…">` —
142
+ * which sides of the frame body text flows on. Default `bothSides`.
143
+ * Only meaningful for the displacing wrap modes; drives whether a
144
+ * floated image goes `float: left` (text on the right) or `right`. */
145
+ wrapText?: "bothSides" | "left" | "right" | "largest";
146
+ /** Text-distance insets — `distT/B/L/R` on `<wp:anchor>`, in EMU. The
147
+ * gap Word keeps between the frame and the text wrapping around it;
148
+ * rendered as margins on the floated frame. */
149
+ textDistancesEmu?: {
150
+ topEmu: number;
151
+ rightEmu: number;
152
+ bottomEmu: number;
153
+ leftEmu: number;
154
+ };
132
155
  /** What this frame contains. */
133
156
  content: AnchoredContent;
134
157
  }
@@ -271,6 +294,31 @@ export interface SectionBreak {
271
294
  * **Status**: type declared (Phase 1.0). Importer does not yet emit
272
295
  * this; renderer treats it as a no-op. Wiring lands in Phase 1.1-1.2.
273
296
  */
297
+ /** One textbox shape inside an inline-frame group: its intra-group
298
+ * position + size and recursive body, plus optional chrome (fill /
299
+ * border / text insets) and vertical text anchor. */
300
+ export interface InlineFrameTextbox {
301
+ offsetEmu: {
302
+ xEmu: number;
303
+ yEmu: number;
304
+ };
305
+ sizeEmu: {
306
+ wEmu: number;
307
+ hEmu: number;
308
+ };
309
+ body: Block[];
310
+ fill?: string;
311
+ border?: FrameBorder;
312
+ /** `<wps:bodyPr>` text insets (lIns/tIns/rIns/bIns) → CSS padding. */
313
+ padding?: {
314
+ topEmu: number;
315
+ rightEmu: number;
316
+ bottomEmu: number;
317
+ leftEmu: number;
318
+ };
319
+ /** Vertical text anchor from `<wps:bodyPr anchor>`; defaults to "top". */
320
+ vAlign?: "top" | "center" | "bottom";
321
+ }
274
322
  export interface InlineFrame {
275
323
  kind: "inline_frame";
276
324
  /** From the containing `<w:p>`'s `<w:pPr>`. The paginator emits a
@@ -304,36 +352,12 @@ export interface InlineFrame {
304
352
  wEmu: number;
305
353
  hEmu: number;
306
354
  };
307
- /** The textbox SHAPE's intra-group position + size + body. The
308
- * renderer places `body` at the EXACT coordinates the author
309
- * intended. Optional because some drawings have only pictures /
310
- * shapes (no textbox content). */
311
- textbox?: {
312
- offsetEmu: {
313
- xEmu: number;
314
- yEmu: number;
315
- };
316
- sizeEmu: {
317
- wEmu: number;
318
- hEmu: number;
319
- };
320
- body: Block[];
321
- fill?: string;
322
- border?: FrameBorder;
323
- /** `<wps:bodyPr>` text insets (lIns/tIns/rIns/bIns). The renderer
324
- * paints these as the region's CSS padding. */
325
- padding?: {
326
- topEmu: number;
327
- rightEmu: number;
328
- bottomEmu: number;
329
- leftEmu: number;
330
- };
331
- /** Vertical text anchor from `<wps:bodyPr anchor>`. Word centers a
332
- * single heading line by sizing a short textbox and centering its
333
- * text; this carries that intent into the renderer's flex column.
334
- * Defaults to "top" when the attribute is absent. */
335
- vAlign?: "top" | "center" | "bottom";
336
- };
355
+ /** The group's textbox SHAPES, in document (child) order. Most groups
356
+ * have one (a section "pill" heading: a centred line over a background
357
+ * picture), but a "Project: X" entry has two — a title textbox and a
358
+ * details textbox — and the renderer must show BOTH. Empty when the
359
+ * group carries only pictures / shapes (no textbox content). */
360
+ textboxes: InlineFrameTextbox[];
337
361
  /** Decorative pictures inside the group. Each carries its own
338
362
  * intra-group position. The renderer paints them as
339
363
  * absolute-positioned `<img>` children of the frame wrapper,
@@ -443,10 +467,23 @@ export interface DrawingRun {
443
467
  * - "inline" — flows in the paragraph like a tall character.
444
468
  * - "anchor" — positioned absolutely (`<wp:anchor>`); `anchor`
445
469
  * carries the offset + frame-of-reference.
470
+ * - "floatLeft" / "floatRight" — a `<wp:anchor>` image with a
471
+ * displacing wrap (square/tight/through), converted to a
472
+ * CSS float at the head of its anchor paragraph so body
473
+ * text flows around it. `floatMarginsEmu` carries the
474
+ * `distT/B/L/R` clearance.
446
475
  */
447
- placement: "inline" | "anchor";
476
+ placement: "inline" | "anchor" | "floatLeft" | "floatRight";
448
477
  /** Set when `placement === "anchor"`. */
449
478
  anchor?: DrawingAnchor;
479
+ /** Set for `floatLeft` / `floatRight` — the text-clearance margins
480
+ * (from the frame's `distT/B/L/R`), applied as CSS margins. */
481
+ floatMarginsEmu?: {
482
+ topEmu: number;
483
+ rightEmu: number;
484
+ bottomEmu: number;
485
+ leftEmu: number;
486
+ };
450
487
  /**
451
488
  * Vertical alignment for an `inline` image relative to the text on
452
489
  * its line. Defaults to the browser baseline (image bottom on the
@@ -4,6 +4,7 @@ declare const REL_TYPES: {
4
4
  readonly image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
5
5
  readonly hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
6
6
  readonly fontTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable";
7
+ readonly numbering: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering";
7
8
  readonly font: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font";
8
9
  };
9
10
  type RelKind = keyof typeof REL_TYPES;
@@ -13,7 +13,7 @@ export interface ExportContext {
13
13
  /** Rels to append to `word/_rels/document.xml.rels`. */
14
14
  relationships: Array<{
15
15
  id: string;
16
- type: "header" | "footer" | "image" | "hyperlink" | "fontTable";
16
+ type: "header" | "footer" | "image" | "hyperlink" | "fontTable" | "numbering";
17
17
  target: string;
18
18
  /** External targets (URLs) need `TargetMode="External"`. */
19
19
  external?: boolean;
@@ -0,0 +1,2 @@
1
+ import { NumberingDefinition } from '../../doc/types';
2
+ export declare function renderNumberingXml(numbering: readonly NumberingDefinition[]): string | null;
@@ -1,4 +1,5 @@
1
1
  import { AnchoredFrame, Block } from '../../doc/types';
2
+ import { ThemePalette } from '../shared/drawingColor';
2
3
  export interface AnchoredFramesContext {
3
4
  /** RelationshipId → part path lookup, e.g. `"rId4" → "media/image1.png"`. */
4
5
  rels: Map<string, string>;
@@ -20,6 +21,9 @@ export interface AnchoredFramesContext {
20
21
  * its true layout. When absent, falls back to flat text (tests).
21
22
  */
22
23
  parseBlockBody?: (txbxContent: Element) => Block[];
24
+ /** Theme colour palette (from `word/theme/theme1.xml`) so shape fills /
25
+ * strokes declared as `<a:schemeClr>` resolve instead of vanishing. */
26
+ theme?: ThemePalette;
23
27
  }
24
28
  /**
25
29
  * Walk every `<w:drawing>/<wp:anchor>` in the document and return one
@@ -0,0 +1,12 @@
1
+ import { ParagraphBorders } from '../../doc/types';
2
+ /**
3
+ * Read a `<w:pBdr>` paragraph-border element into `ParagraphBorders`.
4
+ *
5
+ * Shared by the direct-paragraph parser AND the named-style parser: Word
6
+ * puts the divider rules of letterhead / résumé headers on a STYLE (e.g. a
7
+ * "Name" style's `<w:top>` rule), not on each paragraph. Reading it only
8
+ * for direct paragraphs dropped those rules entirely (the renderer's
9
+ * `effective.borders` cascade had nothing to apply). `none` / `nil` sides
10
+ * are skipped (Word's explicit "no border on this side").
11
+ */
12
+ export declare function readParagraphBorders(pPr: Element): ParagraphBorders | undefined;
@@ -0,0 +1,11 @@
1
+ import { AnchoredFrame, Block, SectionProperties } from '../../doc/types';
2
+ /**
3
+ * Prepend a float `DrawingRun` to each floatable frame's anchor paragraph
4
+ * and drop those frames from the overlay set. Pure — inputs untouched.
5
+ * Body indices are stable (we modify paragraphs in place, never insert),
6
+ * so it composes after `flowDisplacingTextboxes` without an index remap.
7
+ */
8
+ export declare function floatWrappingImages(body: readonly Block[], frames: readonly AnchoredFrame[], sections: readonly SectionProperties[]): {
9
+ body: Block[];
10
+ frames: AnchoredFrame[];
11
+ };
@@ -1,4 +1,5 @@
1
1
  import { Block, InlineFrame } from '../../doc/types';
2
+ import { ThemePalette } from '../shared/drawingColor';
2
3
  export interface InlineFramesContext {
3
4
  /** RelationshipId → part path lookup. */
4
5
  rels: Map<string, string>;
@@ -26,6 +27,9 @@ export interface InlineFramesContext {
26
27
  * Without this flag, only explicit directives count.
27
28
  */
28
29
  honorLastRenderedPageBreaks?: boolean;
30
+ /** Theme colour palette (from `word/theme/theme1.xml`) so textbox /
31
+ * shape fills declared as `<a:schemeClr>` resolve instead of vanishing. */
32
+ theme?: ThemePalette;
29
33
  }
30
34
  /**
31
35
  * One InlineFrame plus the source DOM nodes it came from.
@@ -4,9 +4,14 @@ import { ParagraphFormat } from '../types';
4
4
  export type ImportedItem = {
5
5
  kind: "run";
6
6
  run: ImportedRun;
7
- } | {
7
+ }
8
+ /** `href` is set for HYPERLINK *fields* (the target lives in the field
9
+ * instruction); `relId` for `<w:hyperlink r:id>` elements (resolved
10
+ * against the rels table downstream). */
11
+ | {
8
12
  kind: "hyperlink";
9
13
  relId?: string;
14
+ href?: string;
10
15
  runs: ImportedRun[];
11
16
  };
12
17
  export interface ImportedParagraph {
@@ -1,3 +1,16 @@
1
1
  import { NamedStyle } from '../../doc/types';
2
2
  import { DocSettings } from './settings';
3
+ /**
4
+ * Canonicalise a heading style id to `HeadingN`.
5
+ *
6
+ * Word and OpenOffice name the heading styles inconsistently across docs:
7
+ * `Heading2`, `Heading 2`, `heading 2`. The paragraph importer already
8
+ * maps a heading PARAGRAPH's styleId to the canonical `HeadingN` (so it
9
+ * renders as `<hN>` and joins the `HeadingN` convention used by builders /
10
+ * serialize / markdown). The STYLE definition must canonicalise the same
11
+ * way, or `resolveStyleCascade` looks up `HeadingN` and misses the
12
+ * actual style — dropping its colour, caps, etc. Non-heading ids pass
13
+ * through unchanged.
14
+ */
15
+ export declare function canonicalStyleId(id: string): string;
3
16
  export declare function parseStylesXml(xml: string | undefined, settings?: DocSettings): NamedStyle[] | null;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * DrawingML colour resolution — literal AND theme colours.
3
+ *
4
+ * A DrawingML colour container (`<a:solidFill>`, the children of
5
+ * `<a:ln>`, …) holds either a literal `<a:srgbClr val="RRGGBB">` or a
6
+ * theme reference `<a:schemeClr val="accent1">`. Both can carry child
7
+ * TRANSFORM elements that adjust the base colour (`hueOff`, `satOff`,
8
+ * `lumOff`, `lumMod`, `shade`, `tint`, …). Word resolves the scheme slot
9
+ * against `word/theme/theme1.xml`'s `<a:clrScheme>` and applies the
10
+ * transforms in document order — reading only `srgbClr` renders every
11
+ * theme-coloured shape invisible (no fill, no stroke).
12
+ *
13
+ * Transform units (ECMA-376 §20.1.2.3):
14
+ * - `hueOff` — 60000ths of a degree, added to the hue.
15
+ * - `satOff` / `lumOff` — 1000ths of a percent-POINT, added to S / L.
16
+ * - `satMod` / `lumMod` — 100000ths, multiplied onto S / L.
17
+ * - `shade` — scale toward black (val/100000).
18
+ * - `tint` — scale toward white (val/100000).
19
+ */
20
+ /** Theme slot → `#RRGGBB`. Slots: dk1/lt1/dk2/lt2/accent1-6/hlink/folHlink. */
21
+ export type ThemePalette = Record<string, string>;
22
+ /** Parse `word/theme/theme1.xml` into the colour-scheme palette.
23
+ * Returns undefined when the part is absent or malformed. */
24
+ export declare function parseThemeXml(xml: string | undefined): ThemePalette | undefined;
25
+ /**
26
+ * Resolve the colour child of `parent` (an `<a:solidFill>` or `<a:ln>`-
27
+ * style container): literal `srgbClr` or theme `schemeClr`, transforms
28
+ * applied. Returns `#RRGGBB` or undefined when no resolvable colour.
29
+ */
30
+ export declare function readDrawingColor(parent: Element, theme?: ThemePalette): string | undefined;
@@ -28,8 +28,9 @@ export declare const twipsToPt: (t: number) => number;
28
28
  /** pt → twips. */
29
29
  export declare const ptToTwips: (pt: number) => number;
30
30
  /**
31
- * `w:line` for line spacing in "auto" mode: 240 twips = single-spacing.
32
- * So `line-height: 1.5` → `240 * 1.5 = 360`.
31
+ * `w:line` value (in "auto" line-rule mode) for single line spacing.
32
+ * Word treats 240 as 1.0×, so `line-height: 1.5` → `240 * 1.5 = 360`.
33
33
  */
34
+ export declare const SINGLE_SPACING_LINE = 240;
34
35
  export declare const lineHeightToOoxml: (lineHeight: number) => number;
35
36
  export declare const ooxmlLineHeightToCss: (line: number) => number;
@@ -17,6 +17,18 @@ export declare function wChildren(parent: Element, localName: string): Element[]
17
17
  * serialised documents differ.
18
18
  */
19
19
  export declare function wVal(el: Element | null): string | null;
20
+ /**
21
+ * Read an OOXML on/off toggle property (`CT_OnOff`: `<w:pageBreakBefore>`,
22
+ * `<w:b>`, `<w:keepNext>`, …). Absent → false. Present with no `w:val` →
23
+ * true (a bare element means "on"). Present with `w:val` →
24
+ * "false"/"0"/"off" mean OFF; anything else ON.
25
+ *
26
+ * Reading these by mere presence is a classic OOXML bug: Word writes the
27
+ * explicit-off form (`<w:pageBreakBefore w:val="0"/>`) in DocDefaults and
28
+ * styles, so presence-only flips the property ON for every consumer — e.g.
29
+ * a page break before every paragraph.
30
+ */
31
+ export declare function wOnOff(root: Document | Element, localName: string): boolean;
20
32
  /** Build an XML declaration header + root element. Used by the exporter. */
21
33
  export declare function xmlDocument(rootXml: string): string;
22
34
  /**
@@ -18,6 +18,10 @@ export interface DocxExportResult {
18
18
  }
19
19
  /** A single inline run's formatting flags — what `<w:rPr>` tells us. */
20
20
  export interface RunFormat {
21
+ /** `<w:rStyle w:val="…">` — a character style applied to the run. Its
22
+ * rPr (colour, underline, …) is resolved against the style cascade at
23
+ * render time, under any direct run formatting. */
24
+ styleId?: string;
21
25
  bold?: boolean;
22
26
  italic?: boolean;
23
27
  underline?: boolean;
@@ -0,0 +1,14 @@
1
+ import { CommandBus, CommandDefinition, CommandSnapshot } from './types';
2
+ /**
3
+ * Default {@link CommandBus} implementation. Plain in-memory map; no
4
+ * editor coupling beyond the closure plugins use when registering.
5
+ * Replacing it would mean swapping a field on Editor — the rest of
6
+ * the surface stays the same.
7
+ */
8
+ export declare class EditorCommands implements CommandBus {
9
+ private readonly commands;
10
+ register<Args = void>(def: CommandDefinition<Args>): () => void;
11
+ execute<Args = void>(name: string, args?: Args): void;
12
+ list(): CommandSnapshot[];
13
+ has(name: string): boolean;
14
+ }
@@ -0,0 +1,71 @@
1
+ import { BlobCache, BlobStore } from '../blob';
2
+ import { Range as ApiRange, BlockRef, EditResult } from '../doc/api';
3
+ import { SobreeDocument } from '../doc/types';
4
+ import { FontFaceRegistry } from '../fonts';
5
+ import { History } from '../history';
6
+ import { BlockRegistry } from './internal/blockRegistry';
7
+ import { Mutation } from './internal/mutations';
8
+ import { EditorSelection } from './selection';
9
+ import { TrackChangesState } from './types';
10
+ import type * as Y from "yjs";
11
+ /**
12
+ * The kernel seam between the `Editor` (which owns mutable state, the
13
+ * `commit()` transaction pipeline, DOM-listener wiring, and Y.Doc
14
+ * mirroring) and the behaviour modules (`ops/*`, `query`) that implement
15
+ * each editing concern as free functions over this context.
16
+ *
17
+ * The `Editor` builds one `EditorContext` once in its constructor,
18
+ * closing over its own privates — so `commit` / `checkRefs` / `ensureCurrent`
19
+ * stay private to the class while the behaviour modules get exactly the
20
+ * surface they need and nothing more. This is the state/behaviour split
21
+ * that lets the central editor decompose without Host-seam slicing
22
+ * (which would have to expose nearly everything).
23
+ *
24
+ * Members are named to match the `Editor`'s own field/method names so a
25
+ * behaviour body reads identically whether it lives on the class or in a
26
+ * module: `this.commit(...)` becomes `ctx.commit(...)`.
27
+ */
28
+ export interface EditorContext {
29
+ readonly host: HTMLElement;
30
+ readonly selection: EditorSelection;
31
+ readonly registry: BlockRegistry;
32
+ readonly history: History;
33
+ readonly ydoc: Y.Doc;
34
+ readonly blobStore: BlobStore | null;
35
+ readonly blobCache: BlobCache | null;
36
+ readonly fontFaces: FontFaceRegistry;
37
+ /** Live view of the editor's current document (re-read on every access). */
38
+ readonly doc: SobreeDocument;
39
+ /** Replace the cached document wholesale (rare — most paths use `commit`). */
40
+ setDoc(doc: SobreeDocument): void;
41
+ /** Full document replace: reset registry, re-render, mirror, emit change. */
42
+ setDocument(doc: SobreeDocument): void;
43
+ /** Re-render the current `doc` into the hosts (no selection restore, no emit). */
44
+ renderCurrent(): void;
45
+ /**
46
+ * Soft-revert the in-memory doc to `snapshot` and re-render (resets the
47
+ * serialised-block cache + dom-dirty flag; no registry reset, mirror, or
48
+ * emit). Used to roll back the browser's native IME mutations before a
49
+ * tracked re-insert.
50
+ */
51
+ restoreSnapshot(snapshot: SobreeDocument): void;
52
+ /** The content host(s) the renderer paints into (may differ from `host`). */
53
+ getContentHosts(): HTMLElement[];
54
+ /** The host(s) a DOM selection may live in. */
55
+ _hosts(): HTMLElement[];
56
+ readonly trackChanges: TrackChangesState;
57
+ /** Set tracked-changes state WITHOUT firing the change event (internal). */
58
+ setTrackChangesRaw(state: TrackChangesState): void;
59
+ readonly lastPartRefs: Record<string, string>;
60
+ setLastPartRefs(refs: Record<string, string>): void;
61
+ readonly pendingPartRefMigrations: Set<string>;
62
+ commit<T = void>(update: Partial<SobreeDocument>, mutations: readonly Mutation[], value?: T, reason?: string): EditResult<T>;
63
+ ensureCurrent(): SobreeDocument;
64
+ syncFromDom(): SobreeDocument;
65
+ checkRefs(refs: readonly BlockRef[]): EditResult<never> | null;
66
+ checkRange(range: ApiRange, expect: Record<string, number> | undefined): EditResult<never> | null;
67
+ emitChangeNow(): void;
68
+ mirrorToYDoc(): void;
69
+ scheduleChange(): void;
70
+ setDomDirty(value: boolean): void;
71
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * DOM utilities for the editor — selection geometry, block-element
3
+ * lookup, drag/drop image detection, image sizing. Browser-only
4
+ * (no model state); kept apart from `editor/index.ts` so the editor
5
+ * module stays about editing, not DOM plumbing.
6
+ */
7
+ /** Nearest ancestor (or self) that is a direct block child of a host. */
8
+ export declare function closestBlockElement(node: Node, hosts: HTMLElement[]): HTMLElement | null;
9
+ /** The live DOM selection range, but only if both ends are inside a host. */
10
+ export declare function currentDomRangeInsideHosts(hosts: HTMLElement[]): Range | null;
11
+ /** True when a drag/paste DataTransfer carries at least one image file. */
12
+ export declare function hasImageInDataTransfer(dt: DataTransfer | null): boolean;
13
+ /** Cross-browser caret range at viewport (x, y) — drop-point resolution. */
14
+ export declare function caretRangeFromPoint(x: number, y: number): Range | null;
15
+ /** Decode an image file's natural dimensions (falls back to 200×150). */
16
+ export declare function readImageDimensions(file: File): Promise<{
17
+ width: number;
18
+ height: number;
19
+ }>;
20
+ /** Replace an element with its children (lift contents up one level). */
21
+ export declare function unwrap(el: HTMLElement): void;
@@ -0,0 +1,29 @@
1
+ import { Selection } from '../doc/api';
2
+ import { ChangePayload, EditorEvent, EditorEventPayload, TrackChangesState, Unsubscribe } from './types';
3
+ /**
4
+ * The editor's observable event surface — `change`, `selection`,
5
+ * `keydown`, `track-changes-change`. Owns the four listener sets and the
6
+ * dispatch loops; the Editor keeps the *triggers* (the DOM listeners and
7
+ * the commit pipeline) and forwards built payloads here. Each dispatch
8
+ * isolates listener exceptions so one bad subscriber can't break the
9
+ * others or the edit that fired the event.
10
+ */
11
+ export declare class EditorEvents {
12
+ private readonly listeners;
13
+ on<E extends EditorEvent>(event: E, cb: (p: EditorEventPayload[E]) => void): Unsubscribe;
14
+ /** Whether any `change` subscriber exists — lets the caller skip
15
+ * building the (cloned) payload when nobody's listening. */
16
+ hasChangeListeners(): boolean;
17
+ emitChange(payload: ChangePayload): void;
18
+ /** Compose a {@link SelectionPayload} from `sel` and dispatch. */
19
+ emitSelection(sel: Selection): void;
20
+ /**
21
+ * Normalise a DOM `KeyboardEvent` into a {@link KeyDownPayload} and
22
+ * dispatch in registration order. Subscribers can `preventDefault()`
23
+ * (browser default) and/or `stopPropagation()` (further subscribers).
24
+ */
25
+ emitKeyDown(e: KeyboardEvent): void;
26
+ emitTrackChanges(state: TrackChangesState): void;
27
+ /** Drop all subscribers on editor destroy. */
28
+ clear(): void;
29
+ }