@sobree/core 0.1.28 → 0.1.29

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.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Sobree's internal document model.
3
+ *
4
+ * Every node here maps 1-to-1 to an OOXML construct so serialisation to
5
+ * `.docx` is mechanical (no decisions, no lossy translation). The names
6
+ * are JS-friendly — `Paragraph`, `RunProperties`, etc. — rather than
7
+ * `<w:p>`, `<w:rPr>` directly, but the shapes line up.
8
+ *
9
+ * Conventions:
10
+ * - All numeric measurements that originate in OOXML keep their native
11
+ * unit, suffixed in the field name: `wTwips`, `sizeHalfPt`, `widthEmu`.
12
+ * - All node objects are JSON-clean (no functions, classes, or
13
+ * references) so they cross any wire (Yjs sync messages, MCP,
14
+ * postMessage) untouched.
15
+ * - Optional fields are `?:` — absence means "not set", not "default".
16
+ * Defaults are applied at render time from the document's styles.
17
+ *
18
+ * The concept modules in this directory each own one slice of the model.
19
+ * This barrel re-exports every AST type so consumers keep importing from
20
+ * `doc/types` regardless of which concept file a type physically lives in.
21
+ */
22
+ export type { BorderSpec, Shading, TableBorders, TableCellBorders, TableCellMargins, } from '../formatting.types';
23
+ export type { TableConditionalType, TableLook, TableStyleCellFormat, TableStyleDefinition, } from '../tableStyle.types';
24
+ export type * from './block';
25
+ export type * from './document';
26
+ export type * from './drawing';
27
+ export type * from './headersFooters';
28
+ export type * from './numbering';
29
+ export type * from './paragraph';
30
+ export type * from './parts';
31
+ export type * from './revisions';
32
+ export type * from './runs';
33
+ export type * from './sections';
34
+ export type * from './styles';
@@ -0,0 +1,25 @@
1
+ import { ParagraphIndent } from './paragraph';
2
+ import { RunProperties } from './runs';
3
+ export interface NumberingDefinition {
4
+ /** `numId` referenced from `ParagraphProperties.numbering`. */
5
+ numId: number;
6
+ /** The abstract format definition. */
7
+ abstractFormat: AbstractNumberingFormat;
8
+ }
9
+ export interface AbstractNumberingFormat {
10
+ /** One per indent level (0..8 typically). */
11
+ levels: NumberingLevel[];
12
+ }
13
+ export interface NumberingLevel {
14
+ level: number;
15
+ /** Format: `bullet`, `decimal`, `lowerRoman`, `upperLetter`, … */
16
+ format: string;
17
+ /** Text template, e.g. `%1.` or a literal bullet character. */
18
+ text: string;
19
+ /** Restart numbering after this level. */
20
+ restart?: number;
21
+ /** Indentation of the numbered text. */
22
+ paragraphIndent?: ParagraphIndent;
23
+ /** Run properties for the bullet/number marker itself. */
24
+ runDefaults?: RunProperties;
25
+ }
@@ -0,0 +1,82 @@
1
+ import { BorderSpec, Shading } from '../formatting.types';
2
+ import { RevisionMark } from './revisions';
3
+ import { RunProperties } from './runs';
4
+ export interface ParagraphProperties {
5
+ /** Reference to a `NamedStyle.id` of type "paragraph". */
6
+ styleId?: string;
7
+ alignment?: ParagraphAlignment;
8
+ /** Numbered/bulleted list reference. */
9
+ numbering?: {
10
+ numId: number;
11
+ level: number;
12
+ };
13
+ spacing?: ParagraphSpacing;
14
+ indent?: ParagraphIndent;
15
+ borders?: ParagraphBorders;
16
+ shading?: Shading;
17
+ /** Keep this paragraph on the same page as the next one. */
18
+ keepNext?: boolean;
19
+ /** Don't allow this paragraph to break across pages. */
20
+ keepLines?: boolean;
21
+ /** Insert a page break before this paragraph. */
22
+ pageBreakBefore?: boolean;
23
+ /** Custom tab stops from `<w:pPr><w:tabs>`, positions in twips. The
24
+ * renderer uses the smallest stop's position to compute a CSS
25
+ * `tab-size` on the paragraph so `\t` characters in the text honour
26
+ * the document's tab geometry instead of the browser's 8-char
27
+ * default. Mixed alignments (right / decimal / leader) collapse to
28
+ * "left" for now — covering the common case (label-value columns
29
+ * in headers + form fields). */
30
+ tabStops?: readonly {
31
+ positionTwips: number;
32
+ alignment: string;
33
+ leader?: string;
34
+ }[];
35
+ /** Default run properties applied to runs that don't override. */
36
+ runDefaults?: RunProperties;
37
+ /**
38
+ * Tracked-change marker on the paragraph mark itself. Semantically:
39
+ * the *paragraph break that precedes this paragraph* is a tracked
40
+ * change. Word stores this as `<w:rPr><w:ins/></w:rPr>` inside
41
+ * `<w:pPr>` — see ECMA-376 §17.13.5.7.
42
+ *
43
+ * `ins` — pressing Enter created this paragraph (split the prior
44
+ * paragraph). Accepting keeps the split; rejecting merges
45
+ * the paragraph back into the previous one.
46
+ * `del` — the user has marked this paragraph break for deletion
47
+ * (e.g. Backspace at the start of this paragraph in
48
+ * tracked mode). Accepting merges into the previous;
49
+ * rejecting keeps the split.
50
+ *
51
+ * Accept/reject of this paragraph-level marker is tracked under
52
+ * follow-up task 26 (block-level revisions); v1 only adds the
53
+ * authoring path (via `Editor.splitBlock` in track-changes mode).
54
+ */
55
+ revision?: RevisionMark;
56
+ }
57
+ export type ParagraphAlignment = "left" | "center" | "right" | "both" | "distribute";
58
+ export interface ParagraphSpacing {
59
+ /** Twips before the paragraph. */
60
+ beforeTwips?: number;
61
+ /** Twips after the paragraph. */
62
+ afterTwips?: number;
63
+ /** Twips between lines (when `lineRule === "exact" | "atLeast"`) or
64
+ * 240ths of a multiplier (when `lineRule === "auto"`). */
65
+ line?: number;
66
+ lineRule?: "auto" | "exact" | "atLeast";
67
+ }
68
+ export interface ParagraphIndent {
69
+ leftTwips?: number;
70
+ rightTwips?: number;
71
+ /** Indent of the first line of the paragraph (positive = indent in). */
72
+ firstLineTwips?: number;
73
+ /** Hanging indent (offsets first line OUT of the rest of the para). */
74
+ hangingTwips?: number;
75
+ }
76
+ export interface ParagraphBorders {
77
+ top?: BorderSpec;
78
+ right?: BorderSpec;
79
+ bottom?: BorderSpec;
80
+ left?: BorderSpec;
81
+ between?: BorderSpec;
82
+ }
@@ -0,0 +1,18 @@
1
+ export type { FontDeclaration, FontEmbedRef } from '../../fonts/types';
2
+ /**
3
+ * Mirror of the `_rels/document.xml.rels` table — Sobree tracks
4
+ * relationships as data so headers, footers, images, hyperlinks all share
5
+ * one allocation strategy at export time.
6
+ */
7
+ export interface RelationshipManifest {
8
+ /** Map of `rId…` → relationship descriptor. */
9
+ byId: Record<string, Relationship>;
10
+ }
11
+ export interface Relationship {
12
+ id: string;
13
+ type: RelationshipType;
14
+ target: string;
15
+ /** External (true) means `target` is a URL; otherwise a part path. */
16
+ external?: boolean;
17
+ }
18
+ export type RelationshipType = "header" | "footer" | "image" | "hyperlink" | "styles" | "numbering" | "settings" | "fontTable" | "theme" | "comments" | "footnotes" | "endnotes";
@@ -0,0 +1,8 @@
1
+ export interface RevisionMark {
2
+ /** `ins` = insertion, `del` = deletion. */
3
+ type: "ins" | "del";
4
+ /** Author name as recorded in the docx (`<w:ins w:author="...">`). */
5
+ author?: string;
6
+ /** ISO-8601 timestamp string from the docx. */
7
+ date?: string;
8
+ }
@@ -0,0 +1,178 @@
1
+ import { Shading } from '../formatting.types';
2
+ import { RevisionMark } from './revisions';
3
+ export type InlineRun = TextRun | BreakRun | TabRun | FieldRun | DrawingRun | HyperlinkRun | FootnoteRefRun | CommentRefRun;
4
+ export interface TextRun {
5
+ kind: "text";
6
+ text: string;
7
+ properties: RunProperties;
8
+ }
9
+ export interface BreakRun {
10
+ kind: "break";
11
+ /**
12
+ * `line` — soft line break inside a paragraph (Shift-Enter).
13
+ * `page` — explicit page break.
14
+ * `column` — column break in a multi-column section.
15
+ */
16
+ type: "line" | "page" | "column";
17
+ properties?: RunProperties;
18
+ }
19
+ export interface TabRun {
20
+ kind: "tab";
21
+ properties?: RunProperties;
22
+ }
23
+ export interface FieldRun {
24
+ kind: "field";
25
+ /** Field instruction text — `PAGE`, `NUMPAGES`, `DATE`, `AUTHOR`, … */
26
+ instruction: string;
27
+ /**
28
+ * Cached value displayed if a viewer doesn't recalculate. Used as the
29
+ * preview text by Sobree's renderer.
30
+ */
31
+ cached?: string;
32
+ properties?: RunProperties;
33
+ }
34
+ /**
35
+ * Inline reference to a comment (`<w:commentReference w:id="N"/>`).
36
+ * Word renders a small balloon icon at the position; we mirror with
37
+ * a clickable inline span linking to the comment card in the aside.
38
+ */
39
+ export interface CommentRefRun {
40
+ kind: "commentRef";
41
+ /** ID matching a key in `SobreeDocument.comments`. */
42
+ id: number;
43
+ properties?: RunProperties;
44
+ }
45
+ /**
46
+ * Inline reference to a footnote (`<w:footnoteReference w:id="N"/>`).
47
+ * Renders as a clickable superscript number; the referenced footnote's
48
+ * body lives in `SobreeDocument.footnotes[id]` and is rendered at the
49
+ * end of the document. (True per-page pinning is a paginator feature
50
+ * deferred for now.)
51
+ */
52
+ export interface FootnoteRefRun {
53
+ kind: "footnoteRef";
54
+ /** ID matching a key in `SobreeDocument.footnotes`. */
55
+ id: number;
56
+ properties?: RunProperties;
57
+ }
58
+ export interface DrawingRun {
59
+ kind: "drawing";
60
+ /** Path of the embedded media part in `rawParts` (e.g. `word/media/image1.png`). */
61
+ partPath: string;
62
+ /** Rendered size. */
63
+ widthEmu: number;
64
+ heightEmu: number;
65
+ /** Accessibility text. */
66
+ altText?: string;
67
+ /**
68
+ * Where the image lays out:
69
+ * - "inline" — flows in the paragraph like a tall character.
70
+ * - "anchor" — positioned absolutely (`<wp:anchor>`); `anchor`
71
+ * carries the offset + frame-of-reference.
72
+ * - "floatLeft" / "floatRight" — a `<wp:anchor>` image with a
73
+ * displacing wrap (square/tight/through), converted to a
74
+ * CSS float at the head of its anchor paragraph so body
75
+ * text flows around it. `floatMarginsEmu` carries the
76
+ * `distT/B/L/R` clearance.
77
+ */
78
+ placement: "inline" | "anchor" | "floatLeft" | "floatRight";
79
+ /** Set when `placement === "anchor"`. */
80
+ anchor?: DrawingAnchor;
81
+ /** Set for `floatLeft` / `floatRight` — the text-clearance margins
82
+ * (from the frame's `distT/B/L/R`), applied as CSS margins. */
83
+ floatMarginsEmu?: {
84
+ topEmu: number;
85
+ rightEmu: number;
86
+ bottomEmu: number;
87
+ leftEmu: number;
88
+ };
89
+ /**
90
+ * Vertical alignment for an `inline` image relative to the text on
91
+ * its line. Defaults to the browser baseline (image bottom on the
92
+ * text baseline). `"middle"` centres the image on the text — used
93
+ * for a heading decoration (the flowed ► project arrow) that is
94
+ * taller than its label, so the label centres beside it as Word
95
+ * renders it. Omitted for ordinary inline images.
96
+ */
97
+ verticalAlign?: "baseline" | "middle";
98
+ }
99
+ export interface DrawingAnchor {
100
+ /** Horizontal offset in EMU (English Metric Units; 914400 EMU = 1 inch). */
101
+ offsetXEmu: number;
102
+ /** Vertical offset in EMU. */
103
+ offsetYEmu: number;
104
+ /** What `offsetXEmu` is measured from. */
105
+ relativeFromH: "page" | "margin" | "column" | "character";
106
+ /** What `offsetYEmu` is measured from. */
107
+ relativeFromV: "page" | "margin" | "paragraph" | "line";
108
+ /** True when the image renders *behind* text (z-index negative). */
109
+ behindDoc?: boolean;
110
+ }
111
+ export interface HyperlinkRun {
112
+ kind: "hyperlink";
113
+ /** Either an external URL or an internal anchor id. */
114
+ href: string;
115
+ /** Display text — itself a list of runs to allow nested formatting. */
116
+ children: InlineRun[];
117
+ properties?: RunProperties;
118
+ }
119
+ export interface RunProperties {
120
+ /** Reference to a `NamedStyle.id` of type "character". */
121
+ styleId?: string;
122
+ bold?: boolean;
123
+ italic?: boolean;
124
+ /** Underline style — most callers want `"single"`. */
125
+ underline?: "single" | "double" | "dotted" | "dashed" | "wave" | "none";
126
+ strike?: boolean;
127
+ doubleStrike?: boolean;
128
+ /** `#rrggbb`. */
129
+ color?: string;
130
+ /** Word highlight name (`yellow`, `green`, …) or `#rrggbb`. */
131
+ highlight?: string;
132
+ /** Cell-style shading (`<w:shd w:fill="…">`). */
133
+ shading?: Shading;
134
+ /** Font family name (Calibri, Georgia, …). */
135
+ fontFamily?: string;
136
+ /** Size in points (Word stores half-points; we expose pt for ergonomics). */
137
+ fontSizePt?: number;
138
+ verticalAlign?: "subscript" | "superscript";
139
+ /** Whether the text is uppercase / small caps. */
140
+ caps?: boolean;
141
+ smallCaps?: boolean;
142
+ /** Hidden text (`<w:vanish/>`). */
143
+ hidden?: boolean;
144
+ /**
145
+ * Tracked-change marker — set when the run is inside a `<w:ins>`
146
+ * (insertion) or `<w:del>` (deletion) wrapper. The renderer
147
+ * applies a visual revision style; the underlying text is preserved
148
+ * either way so the document round-trips faithfully.
149
+ */
150
+ revision?: RevisionMark;
151
+ /**
152
+ * Comment ids whose `<w:commentRangeStart>` … `<w:commentRangeEnd>`
153
+ * span includes this run. The renderer highlights ranges with any
154
+ * active comment. Multiple ids let nested/overlapping comments
155
+ * coexist on the same run.
156
+ */
157
+ commentIds?: readonly number[];
158
+ /**
159
+ * Tracked **format change** — a snapshot of this run's properties
160
+ * *before* the most recent tracked formatting edit. Word stores this
161
+ * as `<w:rPrChange>` (ECMA-376 §17.13.5.32).
162
+ *
163
+ * Accepting the format revision drops `revisionFormat` (the current
164
+ * `properties` stays). Rejecting it restores `properties` *to* the
165
+ * `before` snapshot. Repeated tracked format edits don't overwrite
166
+ * the snapshot — the *original* properties stay captured, so a
167
+ * reject always returns the run to its pre-tracking state.
168
+ *
169
+ * `before` is itself a `RunProperties` but `revisionFormat` doesn't
170
+ * recurse (the snapshot is "what the run looked like before we
171
+ * started tracking format changes").
172
+ */
173
+ revisionFormat?: {
174
+ before: RunProperties;
175
+ author?: string;
176
+ date?: string;
177
+ };
178
+ }
@@ -0,0 +1,67 @@
1
+ import { HeaderFooterRef } from './headersFooters';
2
+ export interface SectionProperties {
3
+ pageSize: PageSize;
4
+ pageMargins: PageMargins;
5
+ /** Header references. Most docs have one or two. */
6
+ headerRefs: HeaderFooterRef[];
7
+ footerRefs: HeaderFooterRef[];
8
+ /** Show the first-page header/footer slot if true. */
9
+ titlePage?: boolean;
10
+ /** Continuous, nextPage, etc. */
11
+ type?: "continuous" | "nextPage" | "evenPage" | "oddPage";
12
+ /**
13
+ * Vertical alignment of the body content on each page in this section.
14
+ * OOXML `<w:vAlign>` (ECMA-376 §17.6.21). Only visible on partial pages
15
+ * — full pages have no slack to redistribute. Default is `"top"` (the
16
+ * field is omitted in that case).
17
+ *
18
+ * - `top` — content anchored to top margin (default).
19
+ * - `center` — content centred between top and bottom margin.
20
+ * - `bottom` — content anchored to bottom margin.
21
+ * - `both` — paragraph spacing stretched to fill the page.
22
+ */
23
+ vAlign?: "top" | "center" | "bottom" | "both";
24
+ /**
25
+ * Multi-column layout for the section's content (`<w:cols>`).
26
+ * Absent or `count <= 1` → single column (the default; the renderer
27
+ * does not wrap in a column container in that case).
28
+ */
29
+ columns?: SectionColumns;
30
+ }
31
+ export interface SectionColumns {
32
+ /** Number of columns. */
33
+ count: number;
34
+ /** Default inter-column gap in twips (Word's `<w:cols w:space>`). Used
35
+ * for equal columns and as the fallback gap when a per-column space
36
+ * is absent. */
37
+ spaceTwips?: number;
38
+ /** `false` when the section declares explicit per-column widths
39
+ * (Word's `<w:cols w:equalWidth="0">`). Absent/`true` → equal columns,
40
+ * which the renderer flows with CSS multi-column. */
41
+ equalWidth?: boolean;
42
+ /** Per-column geometry from `<w:col w:w w:space>`, present only for the
43
+ * unequal case. `length === count`. Each entry's `spaceTwips` is the
44
+ * gap AFTER that column (the last column's is usually absent). The
45
+ * renderer flows blocks across these tracks at their true widths. */
46
+ columns?: SectionColumn[];
47
+ }
48
+ export interface SectionColumn {
49
+ /** Column width in twips (`<w:col w:w>`). */
50
+ widthTwips: number;
51
+ /** Trailing gap after this column in twips (`<w:col w:space>`). */
52
+ spaceTwips?: number;
53
+ }
54
+ export interface PageSize {
55
+ wTwips: number;
56
+ hTwips: number;
57
+ orientation: "portrait" | "landscape";
58
+ }
59
+ export interface PageMargins {
60
+ topTwips: number;
61
+ rightTwips: number;
62
+ bottomTwips: number;
63
+ leftTwips: number;
64
+ headerTwips: number;
65
+ footerTwips: number;
66
+ gutterTwips: number;
67
+ }
@@ -0,0 +1,32 @@
1
+ import { TableStyleDefinition } from '../tableStyle.types';
2
+ import { TableProperties } from './block';
3
+ import { ParagraphProperties } from './paragraph';
4
+ import { RunProperties } from './runs';
5
+ export interface NamedStyle {
6
+ id: string;
7
+ type: "paragraph" | "character" | "table" | "numbering";
8
+ /** Display name shown in Word's style picker. */
9
+ displayName: string;
10
+ /** Inherits from another style id. */
11
+ basedOn?: string;
12
+ /** The style applied to the next paragraph after this one (for headings). */
13
+ nextStyleId?: string;
14
+ /** Default run properties. */
15
+ runDefaults?: RunProperties;
16
+ /** Default paragraph properties. */
17
+ paragraphDefaults?: ParagraphProperties;
18
+ /** Numbering linked via the style's `<w:numPr>` — the source of heading
19
+ * outline numbers ("1", "1.1", "1.2"). `numId` references a
20
+ * `NumberingDefinition`; `level` is the outline level this style sits at.
21
+ * Distinct from `ParagraphProperties.numbering` (a paragraph's OWN list
22
+ * membership); a style's numbering applies to every paragraph using it. */
23
+ numbering?: {
24
+ numId: number;
25
+ level: number;
26
+ };
27
+ /** Default table properties (only for table styles). */
28
+ tableDefaults?: TableProperties;
29
+ /** Table-style borders + conditional formatting (only for table
30
+ * styles). Resolved per cell at render time. */
31
+ tableStyle?: TableStyleDefinition;
32
+ }