@sobree/core 0.1.1 → 0.1.2

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 (37) hide show
  1. package/dist/doc/types.d.ts +40 -30
  2. package/dist/docx/shared/units.d.ts +3 -2
  3. package/dist/docx/shared/xml.d.ts +12 -0
  4. package/dist/editor/commands.d.ts +14 -0
  5. package/dist/editor/context.d.ts +71 -0
  6. package/dist/editor/dom.d.ts +21 -0
  7. package/dist/editor/events.d.ts +29 -0
  8. package/dist/editor/index.d.ts +85 -663
  9. package/dist/editor/internal/mutations.d.ts +1 -1
  10. package/dist/editor/ops/blocks.d.ts +42 -0
  11. package/dist/editor/ops/comments.d.ts +12 -0
  12. package/dist/editor/ops/parts.d.ts +71 -0
  13. package/dist/editor/ops/review.d.ts +67 -0
  14. package/dist/editor/ops/runs.d.ts +83 -0
  15. package/dist/editor/ops/trackedInput.d.ts +44 -0
  16. package/dist/editor/query.d.ts +22 -0
  17. package/dist/editor/revisionRuns.d.ts +56 -0
  18. package/dist/editor/selection.d.ts +34 -0
  19. package/dist/editor/types.d.ts +292 -0
  20. package/dist/editor/view/docRenderer/anchorPosition.d.ts +18 -0
  21. package/dist/editor/view/docRenderer/sectionFlow.d.ts +23 -0
  22. package/dist/editor/view/docRenderer/table.d.ts +11 -2
  23. package/dist/index.css +1 -1
  24. package/dist/index.js +6099 -6020
  25. package/dist/index.js.map +1 -1
  26. package/dist/paperStack/paginationV2/apply.d.ts +18 -0
  27. package/dist/paperStack/paginationV2/engine.d.ts +7 -0
  28. package/dist/paperStack/paginationV2/measure.d.ts +9 -0
  29. package/dist/paperStack/paginationV2/types.d.ts +273 -0
  30. package/dist/paperStack/paper.d.ts +31 -9
  31. package/dist/paperStack/paperStack.d.ts +13 -1
  32. package/dist/paperStack/paperZone.d.ts +35 -0
  33. package/dist/plugins/marks.d.ts +4 -4
  34. package/dist/ydoc/schema.d.ts +5 -0
  35. package/package.json +1 -1
  36. package/dist/__vite-browser-external-DYxpcVy9.js +0 -5
  37. package/dist/__vite-browser-external-DYxpcVy9.js.map +0 -1
@@ -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`. */
@@ -271,6 +280,31 @@ export interface SectionBreak {
271
280
  * **Status**: type declared (Phase 1.0). Importer does not yet emit
272
281
  * this; renderer treats it as a no-op. Wiring lands in Phase 1.1-1.2.
273
282
  */
283
+ /** One textbox shape inside an inline-frame group: its intra-group
284
+ * position + size and recursive body, plus optional chrome (fill /
285
+ * border / text insets) and vertical text anchor. */
286
+ export interface InlineFrameTextbox {
287
+ offsetEmu: {
288
+ xEmu: number;
289
+ yEmu: number;
290
+ };
291
+ sizeEmu: {
292
+ wEmu: number;
293
+ hEmu: number;
294
+ };
295
+ body: Block[];
296
+ fill?: string;
297
+ border?: FrameBorder;
298
+ /** `<wps:bodyPr>` text insets (lIns/tIns/rIns/bIns) → CSS padding. */
299
+ padding?: {
300
+ topEmu: number;
301
+ rightEmu: number;
302
+ bottomEmu: number;
303
+ leftEmu: number;
304
+ };
305
+ /** Vertical text anchor from `<wps:bodyPr anchor>`; defaults to "top". */
306
+ vAlign?: "top" | "center" | "bottom";
307
+ }
274
308
  export interface InlineFrame {
275
309
  kind: "inline_frame";
276
310
  /** From the containing `<w:p>`'s `<w:pPr>`. The paginator emits a
@@ -304,36 +338,12 @@ export interface InlineFrame {
304
338
  wEmu: number;
305
339
  hEmu: number;
306
340
  };
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
- };
341
+ /** The group's textbox SHAPES, in document (child) order. Most groups
342
+ * have one (a section "pill" heading: a centred line over a background
343
+ * picture), but a "Project: X" entry has two — a title textbox and a
344
+ * details textbox — and the renderer must show BOTH. Empty when the
345
+ * group carries only pictures / shapes (no textbox content). */
346
+ textboxes: InlineFrameTextbox[];
337
347
  /** Decorative pictures inside the group. Each carries its own
338
348
  * intra-group position. The renderer paints them as
339
349
  * absolute-positioned `<img>` children of the frame wrapper,
@@ -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
  /**
@@ -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
+ }