@sobree/core 0.1.31 → 0.1.33

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.
@@ -1,5 +1,5 @@
1
1
  import { AnchoredFrame, Block } from '../../doc/types';
2
- import { ThemePalette } from '../shared/drawingColor';
2
+ import { ThemePalette } from './colors';
3
3
  export interface AnchoredFramesContext {
4
4
  /** RelationshipId → part path lookup, e.g. `"rId4" → "media/image1.png"`. */
5
5
  rels: Map<string, string>;
@@ -14,7 +14,7 @@ export interface AnchoredFramesContext {
14
14
  bodyParagraphIndexByElement?: Map<Element, number>;
15
15
  /**
16
16
  * Recursive body walker for `<w:txbxContent>`, injected by the caller
17
- * to avoid an `anchoredFrames ↔ document` import cycle. When present,
17
+ * to avoid an `anchored ↔ document` import cycle. When present,
18
18
  * textbox bodies parse through the SAME pipeline as the document body
19
19
  * — real run formatting, paragraph spacing, lists, tables — so a
20
20
  * frame whose content flows into the body (see `flowFrames`) keeps
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Namespace-aware XML traversal helpers shared by every DrawingML reader.
3
+ * Pure plumbing — no DrawingML semantics live here, only the
4
+ * `namespaceURI`/`localName` matching the DOM's `getElementsByTagName`
5
+ * can't do directly. Numeric/EMU attribute reads live in `extents.ts`.
6
+ */
7
+ /** First DIRECT child of `parent` in `ns` with `localName === local`. */
8
+ export declare function firstChildNS(parent: Element, ns: string, local: string): Element | null;
9
+ /** First DESCENDANT (any depth) of `root` in `ns` with `localName === local`. */
10
+ export declare function firstNS(root: Element, ns: string, local: string): Element | null;
11
+ /** All DIRECT children of `parent` in `ns` with `localName === local`. */
12
+ export declare function directChildrenNS(parent: Element, ns: string, local: string): Element[];
13
+ /** Nearest ANCESTOR of `start` in `ns` with `localName === local`. */
14
+ export declare function findAncestor(start: Element, ns: string, local: string): Element | null;
@@ -0,0 +1,12 @@
1
+ import { EmuExtent } from './model';
2
+ /** Read a numeric XML attribute; `0` when absent, missing, or non-finite. */
3
+ export declare function numAttr(el: Element | undefined | null, name: string): number;
4
+ /** Read a numeric XML attribute, falling back to `fallback` when ABSENT —
5
+ * used for `<wps:bodyPr>` insets whose OOXML defaults are non-zero. */
6
+ export declare function numAttrOr(el: Element, name: string, fallback: number): number;
7
+ /** Read an EMU attribute that the caller has already null-checked; `0`
8
+ * for an explicitly-absent (`null`) value. Distinct from `numAttr` only
9
+ * in taking the raw attribute string rather than the element. */
10
+ export declare function emuAttr(v: string | null): number;
11
+ /** Read an `<…:extent>` / `<…:ext>` element's `cx`/`cy` into EMU. */
12
+ export declare function readExtent(el: Element | undefined | null): EmuExtent;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * DrawingML concept modules — the single home for OOXML `<w:drawing>`
3
+ * translation, split by concept (extents, position, wrap, margins,
4
+ * relationships, colours, shape properties). The import dispatch
5
+ * (`docx/import/{anchored,inline,float,flow}Frames`) and serialization
6
+ * (`docx/export/drawings`) call into these; they do not re-implement EMU
7
+ * math or `relativeFrom` coercion. See `OWNERSHIP.md`.
8
+ */
9
+ export { directChildrenNS, findAncestor, firstChildNS, firstNS, } from './dom';
10
+ export { emuAttr, numAttr, numAttrOr, readExtent } from './extents';
11
+ export { coerceHRelativeFrom, coerceVRelativeFrom, readPosOffset } from './position';
12
+ export { readWrapText, readWrapType } from './wrap';
13
+ export type { WrapText, WrapType } from './wrap';
14
+ export { readTextDistances } from './margins';
15
+ export type { TextDistancesEmu } from './margins';
16
+ export { normalizePartPath, readBlipEmbedPart } from './relationships';
17
+ export { readBorder, readGeometry, readSolidFill } from './shapeProps';
18
+ export { parseThemeXml, readDrawingColor } from './colors';
19
+ export type { ThemePalette } from './colors';
20
+ export type { EmuExtent, EmuOffset, RelativeFromH, RelativeFromV, XfrmBox, } from './model';
@@ -1,5 +1,5 @@
1
1
  import { Block, InlineFrame } from '../../doc/types';
2
- import { ThemePalette } from '../shared/drawingColor';
2
+ import { ThemePalette } from './colors';
3
3
  export interface InlineFramesContext {
4
4
  /** RelationshipId → part path lookup. */
5
5
  rels: Map<string, string>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Owns DrawingML text-distance insets: the `distT/B/L/R` attributes on
3
+ * `<wp:anchor>` — the clearance Word keeps between a wrapped frame and the
4
+ * text flowing around it. Maps to `AnchoredFrame.textDistancesEmu` /
5
+ * (after the float pass) `DrawingRun.floatMarginsEmu`.
6
+ */
7
+ /** Text-distance insets in EMU, all four sides. */
8
+ export interface TextDistancesEmu {
9
+ topEmu: number;
10
+ rightEmu: number;
11
+ bottomEmu: number;
12
+ leftEmu: number;
13
+ }
14
+ /**
15
+ * `distT/B/L/R` are attributes of `<wp:anchor>` itself (not the wrap
16
+ * child). Returns `undefined` when ALL four are absent — a frame with no
17
+ * declared clearance models none; a partially-declared one zero-fills the
18
+ * omitted sides (Word's default distance is 0 for the wrap modes).
19
+ */
20
+ export declare function readTextDistances(anchor: Element): TextDistancesEmu | undefined;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared structural vocabulary for the DrawingML concept readers. These
3
+ * are the small intermediate shapes the per-concept modules traffic in —
4
+ * deliberately separate from the final Sobree AST (`AnchoredFrame`,
5
+ * `InlineFrame`, `DrawingRun`) so importer and (future) exporter agree on
6
+ * one EMU-coordinate language without coupling to persisted editor types.
7
+ */
8
+ /** A DrawingML `<…:ext cx cy>` / `<wp:extent>` size, in EMU. */
9
+ export interface EmuExtent {
10
+ cx: number;
11
+ cy: number;
12
+ }
13
+ /** A DrawingML `<a:off x y>` position, in EMU. */
14
+ export interface EmuOffset {
15
+ x: number;
16
+ y: number;
17
+ }
18
+ /** An `<a:xfrm>` offset+extent pair. Either may be absent in the source. */
19
+ export interface XfrmBox {
20
+ off?: EmuOffset;
21
+ ext?: EmuExtent;
22
+ }
23
+ /** `<wp:positionH relativeFrom>` enum (horizontal frame origin). */
24
+ export type RelativeFromH = "page" | "margin" | "column";
25
+ /** `<wp:positionV relativeFrom>` enum (vertical frame origin). */
26
+ export type RelativeFromV = "page" | "margin" | "paragraph";
@@ -0,0 +1,8 @@
1
+ import { RelativeFromH, RelativeFromV } from './model';
2
+ /** Coerce `<wp:positionH relativeFrom>` to the AST enum; `page` default. */
3
+ export declare function coerceHRelativeFrom(v: string | null): RelativeFromH;
4
+ /** Coerce `<wp:positionV relativeFrom>` to the AST enum; `page` default. */
5
+ export declare function coerceVRelativeFrom(v: string | null): RelativeFromV;
6
+ /** Read the `<wp:posOffset>` EMU offset of a `<wp:positionH/V>` element;
7
+ * `0` when the element or its offset child is absent. */
8
+ export declare function readPosOffset(positionEl: Element | null): number;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Owns relationship → media-part resolution for drawings: reading
3
+ * `r:embed` off an `<a:blip>`, looking it up in the document's rels map,
4
+ * and normalising the resulting part path. The one place that knows how a
5
+ * drawing points at its image bytes.
6
+ */
7
+ /**
8
+ * Normalise a relationship target to a `word/…`-rooted part path.
9
+ * - a package-absolute `/word/media/x.png` drops the leading slash,
10
+ * - an already-`word/`-rooted path is left as-is,
11
+ * - a document-relative `media/x.png` is rooted under `word/`.
12
+ */
13
+ export declare function normalizePartPath(target: string): string;
14
+ /**
15
+ * Resolve an `<a:blip r:embed>` to its normalised media part path, or
16
+ * `null` when the blip has no embed id or the id isn't in `rels`.
17
+ */
18
+ export declare function readBlipEmbedPart(blip: Element, rels: Map<string, string>): string | null;
@@ -0,0 +1,16 @@
1
+ import { FrameBorder } from '../../doc/types';
2
+ import { ThemePalette } from './colors';
3
+ /** Map `<a:prstGeom prst>` to the AST's preset geometry enum; unknown
4
+ * presets fall back to `rect`. (Custom geometry is handled separately by
5
+ * the anchored reader via `customGeometry`.) */
6
+ export declare function readGeometry(wsp: Element): "rect" | "ellipse" | "roundedRect" | "line";
7
+ /**
8
+ * First `<a:solidFill>` directly inside the shape's `spPr` (wps or pic) —
9
+ * literal `srgbClr` or theme `schemeClr` (+ transforms), resolved by
10
+ * `readDrawingColor`. Direct-child traversal so a fill nested deeper
11
+ * inside a child shape isn't picked up by mistake.
12
+ */
13
+ export declare function readSolidFill(shape: Element, theme?: ThemePalette): string | undefined;
14
+ /** Read the shape outline `<a:ln>` (width + colour + dash) into a
15
+ * `FrameBorder`; `undefined` when there's no outline or no stroke colour. */
16
+ export declare function readBorder(shape: Element, theme?: ThemePalette): FrameBorder | undefined;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Test-only helper: parse a single namespaced DrawingML fragment and
3
+ * return its root element, with all the OOXML prefixes the concept
4
+ * readers expect already declared. Keeps the per-concept `.test.ts`
5
+ * files free of namespace boilerplate.
6
+ */
7
+ /** Parse `<prefix:tag …>…</…>` and return the root element. The namespace
8
+ * declarations are injected onto the root so any prefix resolves. */
9
+ export declare function el(fragment: string): Element;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Owns DrawingML text-wrapping: the `<wp:wrap*>` child of `<wp:anchor>`
3
+ * that decides whether a frame DISPLACES body flow, and the `wrapText`
4
+ * side that decides which way a floated frame goes. Wrap is an
5
+ * anchored-drawing concept; inline drawings are in flow and carry none.
6
+ */
7
+ /** The mapped wrap mode, or the AST's value for an `<wp:anchor>` with no
8
+ * recognised wrap child. */
9
+ export type WrapType = "square" | "topAndBottom" | "tight" | "through" | "none";
10
+ /** Which sides of the frame body text flows on (`<wp:wrap* wrapText>`). */
11
+ export type WrapText = "bothSides" | "left" | "right" | "largest";
12
+ /**
13
+ * The wrap mode lives as a dedicated child of `<wp:anchor>`:
14
+ * `<wp:wrapSquare>`, `<wp:wrapTopAndBottom>`, `<wp:wrapTight>`,
15
+ * `<wp:wrapThrough>`, or `<wp:wrapNone>`. Returns the mapped enum or
16
+ * `undefined` when no wrap element is present.
17
+ */
18
+ export declare function readWrapType(anchor: Element): WrapType | undefined;
19
+ /**
20
+ * `wrapText` (`bothSides` / `left` / `right` / `largest`) lives on the
21
+ * displacing wrap child (`<wp:wrapSquare|Tight|Through>`) and says which
22
+ * sides of the frame text flows on. `topAndBottom` / `none` don't carry it.
23
+ */
24
+ export declare function readWrapText(anchor: Element): WrapText | undefined;
@@ -8,6 +8,17 @@ import { BlockRegistry } from './blockRegistry';
8
8
  * `offset = 0` — block-internal table addressing is a future extension.
9
9
  */
10
10
  export declare function positionFromDomPoint(hosts: readonly HTMLElement[], registry: BlockRegistry, node: Node, domOffset: number): InlinePosition | null;
11
+ /**
12
+ * Registry-free `(node, offset)` → block descriptor (`blockId` + offset, plus a
13
+ * cell address inside a table). The id-based core of {@link positionFromDomPoint},
14
+ * also used to save/restore a selection across a DOM rebuild (repagination)
15
+ * where raw node references don't survive.
16
+ */
17
+ interface BlockPoint {
18
+ blockId: string;
19
+ offset: number;
20
+ cell?: CellAddress;
21
+ }
11
22
  /** Build an API `Range` from a live DOM `Range`. */
12
23
  export declare function rangeFromDomRange(hosts: readonly HTMLElement[], registry: BlockRegistry, range: Range): ApiRange | null;
13
24
  /** Read `window.getSelection()` as a model `Selection`. */
@@ -21,6 +32,15 @@ export declare function domPointFromPosition(hosts: readonly HTMLElement[], pos:
21
32
  } | null;
22
33
  /** Apply a model `Selection` to `window.getSelection()`. */
23
34
  export declare function applySelectionToDom(hosts: readonly HTMLElement[], selection: Selection): boolean;
35
+ export interface SelectionDescriptor {
36
+ start: BlockPoint;
37
+ end: BlockPoint;
38
+ collapsed: boolean;
39
+ }
40
+ /** Capture the live selection as a {@link SelectionDescriptor}, or null. */
41
+ export declare function captureSelectionDescriptor(hosts: readonly HTMLElement[]): SelectionDescriptor | null;
42
+ /** Restore a {@link SelectionDescriptor} to the live DOM (after a rebuild). */
43
+ export declare function applySelectionDescriptor(hosts: readonly HTMLElement[], desc: SelectionDescriptor | null): boolean;
24
44
  /** Total character-count length of a block, per the counting rules above. */
25
45
  export declare function blockLength(blockEl: Element): number;
26
46
  /** The DOM element for body block `index` via the positional `data-block-index`
@@ -31,3 +51,5 @@ export declare function blockElementAtIndex(hosts: readonly HTMLElement[], index
31
51
  /** Count distinct blocks in the rendered DOM. Dedups by id so a block split
32
52
  * across a page boundary (two fragments, one id) counts once. */
33
53
  export declare function countBlocks(hosts: readonly HTMLElement[]): number;
54
+ type CellAddress = NonNullable<InlinePosition["cell"]>;
55
+ export {};