@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.
- package/dist/doc/styles.d.ts +12 -0
- package/dist/doc/types.d.ts +68 -31
- package/dist/docx/export/contentTypes.d.ts +1 -0
- package/dist/docx/export/context.d.ts +1 -1
- package/dist/docx/export/numbering.d.ts +2 -0
- package/dist/docx/import/anchoredFrames.d.ts +4 -0
- package/dist/docx/import/borders.d.ts +12 -0
- package/dist/docx/import/floatFrames.d.ts +11 -0
- package/dist/docx/import/inlineFrames.d.ts +4 -0
- package/dist/docx/import/paragraphs.d.ts +6 -1
- package/dist/docx/import/styles.d.ts +13 -0
- package/dist/docx/shared/drawingColor.d.ts +30 -0
- package/dist/docx/shared/units.d.ts +3 -2
- package/dist/docx/shared/xml.d.ts +12 -0
- package/dist/docx/types.d.ts +4 -0
- package/dist/editor/commands.d.ts +14 -0
- package/dist/editor/context.d.ts +71 -0
- package/dist/editor/dom.d.ts +21 -0
- package/dist/editor/events.d.ts +29 -0
- package/dist/editor/index.d.ts +85 -663
- package/dist/editor/internal/mutations.d.ts +1 -1
- package/dist/editor/ops/blocks.d.ts +42 -0
- package/dist/editor/ops/comments.d.ts +12 -0
- package/dist/editor/ops/parts.d.ts +71 -0
- package/dist/editor/ops/review.d.ts +67 -0
- package/dist/editor/ops/runs.d.ts +83 -0
- package/dist/editor/ops/trackedInput.d.ts +44 -0
- package/dist/editor/query.d.ts +22 -0
- package/dist/editor/revisionRuns.d.ts +56 -0
- package/dist/editor/selection.d.ts +34 -0
- package/dist/editor/types.d.ts +292 -0
- package/dist/editor/view/docRenderer/anchorPosition.d.ts +18 -0
- package/dist/editor/view/docRenderer/fontFallback.d.ts +18 -5
- package/dist/editor/view/docRenderer/inline.d.ts +2 -2
- package/dist/editor/view/docRenderer/lists.d.ts +22 -11
- package/dist/editor/view/docRenderer/sectionFlow.d.ts +23 -0
- package/dist/editor/view/docRenderer/table.d.ts +11 -2
- package/dist/index.css +1 -1
- package/dist/index.js +5481 -5060
- package/dist/index.js.map +1 -1
- package/dist/paperStack/paginationV2/apply.d.ts +18 -0
- package/dist/paperStack/paginationV2/engine.d.ts +7 -0
- package/dist/paperStack/paginationV2/measure.d.ts +9 -0
- package/dist/paperStack/paginationV2/types.d.ts +273 -0
- package/dist/paperStack/paper.d.ts +31 -9
- package/dist/paperStack/paperStack.d.ts +13 -1
- package/dist/paperStack/paperZone.d.ts +35 -0
- package/dist/plugins/marks.d.ts +4 -4
- package/dist/ydoc/runs.d.ts +14 -18
- package/dist/ydoc/schema.d.ts +5 -0
- package/package.json +1 -1
- package/dist/__vite-browser-external-DYxpcVy9.js +0 -5
- package/dist/__vite-browser-external-DYxpcVy9.js.map +0 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { BlobStore } from '../blob';
|
|
2
|
+
import { Range as ApiRange, BlockRef, EditResult, InlinePosition, Selection } from '../doc/api';
|
|
3
|
+
import { RunPropertiesPatch } from '../doc/runs';
|
|
4
|
+
import { Block, ParagraphAlignment, ParagraphProperties, SobreeDocument } from '../doc/types';
|
|
5
|
+
/**
|
|
6
|
+
* Editor-surface types — the public type vocabulary of the `Editor`
|
|
7
|
+
* façade (events, payloads, command bus, options, tracked-change spans).
|
|
8
|
+
*
|
|
9
|
+
* Extracted from `editor/index.ts` so that:
|
|
10
|
+
* 1. the 3000+-line editor module stays focused on behaviour, and
|
|
11
|
+
* 2. helper modules (`internal/mutations`, `plugins/marks`) can import
|
|
12
|
+
* these shared types from a leaf module instead of reaching back
|
|
13
|
+
* into `editor/index.ts` — which created an import cycle.
|
|
14
|
+
*
|
|
15
|
+
* Re-exported from `editor/index.ts`, so the public surface is unchanged.
|
|
16
|
+
*/
|
|
17
|
+
import type * as Y from "yjs";
|
|
18
|
+
export type ApiRangeType = ApiRange;
|
|
19
|
+
export type { CellRef, InsertAt, InsertColumnOpts, InsertRowOpts, MergeCellsOpts, } from './table';
|
|
20
|
+
/**
|
|
21
|
+
* One logical tracked change — a maximal run of consecutive inline
|
|
22
|
+
* runs that all carry a `revision` marker by the same author.
|
|
23
|
+
* `getRevisions()` returns these; pass `range` straight to
|
|
24
|
+
* `acceptRevision` / `rejectRevision`.
|
|
25
|
+
*
|
|
26
|
+
* `kinds` is the set of revision types in the span: `["ins"]` or
|
|
27
|
+
* `["del"]` for a plain change, both for a delete-then-insert
|
|
28
|
+
* replacement (which accepts/rejects as a single unit).
|
|
29
|
+
*/
|
|
30
|
+
export interface RevisionSpan {
|
|
31
|
+
range: ApiRange;
|
|
32
|
+
author?: string;
|
|
33
|
+
kinds: ("ins" | "del")[];
|
|
34
|
+
/** ISO date of the span's first revision run, if recorded. */
|
|
35
|
+
date?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Discriminator between revision levels:
|
|
38
|
+
* `"inline"` (default for backwards compat) — the span covers
|
|
39
|
+
* `ins`/`del` text runs inside a block. Pass `range` to
|
|
40
|
+
* `acceptRevision` / `rejectRevision`.
|
|
41
|
+
* `"paragraph"` — the span flags the *paragraph mark* itself on
|
|
42
|
+
* `range.from.block`. The range covers offset `[0, length]` of
|
|
43
|
+
* the block so it still selects the right element for UIs, but
|
|
44
|
+
* accept/reject must go through `acceptParagraphRevision` /
|
|
45
|
+
* `rejectParagraphRevision`.
|
|
46
|
+
* `"format"` — the span flags a tracked format change
|
|
47
|
+
* (`<w:rPrChange>`) on contiguous runs by the same author.
|
|
48
|
+
* `kinds` always reports `["ins"]` (the marker is binary: a
|
|
49
|
+
* format change exists or not). Pass `range` to
|
|
50
|
+
* `acceptFormatRevision` / `rejectFormatRevision`.
|
|
51
|
+
*/
|
|
52
|
+
level?: "inline" | "paragraph" | "format";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Track-changes mode. When `enabled` is true, the editor reinterprets
|
|
56
|
+
* authoring mutations as tracked revisions rather than direct edits:
|
|
57
|
+
*
|
|
58
|
+
* - `insertRun` stamps `revision: { type: "ins", author }` on the
|
|
59
|
+
* inserted run instead of merging it in plainly.
|
|
60
|
+
* - `deleteRange` stamps `revision: { type: "del", author }` on the
|
|
61
|
+
* plain text runs in range instead of dropping them. A run that's
|
|
62
|
+
* already an `ins` *by the same author* is dropped instead — the
|
|
63
|
+
* author cancelling their own pending insert — and runs already
|
|
64
|
+
* carrying a peer's revision are left untouched (the API user must
|
|
65
|
+
* resolve those via `acceptRevision` / `rejectRevision` first).
|
|
66
|
+
* - All other mutations (`applyRunProperties`, block-level ops, etc.)
|
|
67
|
+
* pass through unchanged in this first cut; format-change tracking
|
|
68
|
+
* (`<w:rPrChange>`) and paragraph-mark tracking will land later.
|
|
69
|
+
*
|
|
70
|
+
* `author` is the human-readable name written into the revision
|
|
71
|
+
* marker. Optional — falls back to no `author` field, mirroring the
|
|
72
|
+
* Word semantics for anonymous-author tracked changes.
|
|
73
|
+
*
|
|
74
|
+
* This is the *authoring* side of the review feature. `getRevisions`
|
|
75
|
+
* / `acceptRevision` / `rejectRevision` are the *consumption* side
|
|
76
|
+
* and work the same regardless of this flag.
|
|
77
|
+
*/
|
|
78
|
+
export interface TrackChangesState {
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
author?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Payload delivered to `change` subscribers. Plain data — safe to
|
|
84
|
+
* JSON-stringify and ship over a wire. `rawParts` is stripped from
|
|
85
|
+
* `document` so the payload never carries binary Uint8Arrays.
|
|
86
|
+
*/
|
|
87
|
+
export interface ChangePayload {
|
|
88
|
+
doc: SobreeDocument;
|
|
89
|
+
/**
|
|
90
|
+
* @deprecated Use `doc` instead. This alias is kept for backwards
|
|
91
|
+
* compatibility within the pre-1.0 line and will be removed before
|
|
92
|
+
* v1. Same reference as `doc`.
|
|
93
|
+
*/
|
|
94
|
+
document: SobreeDocument;
|
|
95
|
+
revision: number;
|
|
96
|
+
documentVersion: number;
|
|
97
|
+
}
|
|
98
|
+
/** Summary of a top-level block, for `getBlocks()` and list-style UIs. */
|
|
99
|
+
export interface BlockInfo {
|
|
100
|
+
/** Current position in the body (unstable across edits). */
|
|
101
|
+
index: number;
|
|
102
|
+
/** Stable id — pair with `version` to form a `BlockRef`. */
|
|
103
|
+
id: string;
|
|
104
|
+
/** Bumps on every modification of this block. */
|
|
105
|
+
version: number;
|
|
106
|
+
kind: Block["kind"];
|
|
107
|
+
styleId?: string;
|
|
108
|
+
alignment?: ParagraphAlignment;
|
|
109
|
+
/** Plain-text preview. */
|
|
110
|
+
text: string;
|
|
111
|
+
/** Total character-length of the block's content (see `runsLength`). */
|
|
112
|
+
length: number;
|
|
113
|
+
}
|
|
114
|
+
/** Outline entry — one per heading in document order. */
|
|
115
|
+
export interface OutlineItem {
|
|
116
|
+
level: number;
|
|
117
|
+
text: string;
|
|
118
|
+
blockIndex: number;
|
|
119
|
+
block: BlockRef;
|
|
120
|
+
}
|
|
121
|
+
export type ParagraphPropertiesPatch = {
|
|
122
|
+
[K in keyof ParagraphProperties]?: ParagraphProperties[K] | undefined;
|
|
123
|
+
};
|
|
124
|
+
export type WrapTag = "sup" | "sub" | "strong" | "em" | "u" | "s" | "mark";
|
|
125
|
+
/** The slice of selection state plugins read (see {@link EditorLike}). */
|
|
126
|
+
export interface EditorSelectionLike {
|
|
127
|
+
currentRange(): ApiRange | null;
|
|
128
|
+
currentCaret(): InlinePosition | null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The minimal `Editor` contract that framework-free plugins depend on
|
|
132
|
+
* (mark toggles, etc.). Plugins type against this instead of the
|
|
133
|
+
* concrete `Editor` class so they don't import `editor/index.ts` — that
|
|
134
|
+
* kept a (type-only) import cycle alive and coupled plugins to the
|
|
135
|
+
* whole editor module. The `Editor` class structurally satisfies it.
|
|
136
|
+
*/
|
|
137
|
+
export interface EditorLike {
|
|
138
|
+
getDocument(): SobreeDocument;
|
|
139
|
+
getBlocks(): BlockInfo[];
|
|
140
|
+
getBlockById(id: string): BlockInfo | null;
|
|
141
|
+
applyRunProperties(range: ApiRange, patch: RunPropertiesPatch, opts?: {
|
|
142
|
+
expect?: Record<string, number>;
|
|
143
|
+
}): EditResult<void>;
|
|
144
|
+
wrapRange(range: ApiRange, tag: WrapTag, opts?: {
|
|
145
|
+
expect?: Record<string, number>;
|
|
146
|
+
}): EditResult<void>;
|
|
147
|
+
readonly selection: EditorSelectionLike;
|
|
148
|
+
}
|
|
149
|
+
export type EditorEvent = "change" | "selection" | "keydown" | "track-changes-change";
|
|
150
|
+
export type EditorEventPayload = {
|
|
151
|
+
change: ChangePayload;
|
|
152
|
+
selection: SelectionPayload;
|
|
153
|
+
keydown: KeyDownPayload;
|
|
154
|
+
"track-changes-change": TrackChangesState;
|
|
155
|
+
};
|
|
156
|
+
export type Unsubscribe = () => void;
|
|
157
|
+
/**
|
|
158
|
+
* Payload delivered to `selection` subscribers. Fires whenever the live
|
|
159
|
+
* DOM selection changes — typing, clicking, arrow-key navigation, focus
|
|
160
|
+
* loss, programmatic restore. Subscribers should subscribe through the
|
|
161
|
+
* editor rather than `document.addEventListener("selectionchange")`
|
|
162
|
+
* directly so cleanup is centralised and the editor can later add
|
|
163
|
+
* dedup / throttling.
|
|
164
|
+
*
|
|
165
|
+
* `selection` is the model shape (`null` when focus is outside the
|
|
166
|
+
* editor). The convenience fields below mirror what `EditorSelection`
|
|
167
|
+
* exposes for ergonomics — read whichever one you need.
|
|
168
|
+
*/
|
|
169
|
+
export interface SelectionPayload {
|
|
170
|
+
selection: Selection;
|
|
171
|
+
range: ApiRange | null;
|
|
172
|
+
caret: InlinePosition | null;
|
|
173
|
+
block: BlockRef | null;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Payload delivered to `keydown` subscribers. Fires for every key press
|
|
177
|
+
* inside the editor host. The editor binds NO shortcuts itself —
|
|
178
|
+
* plugins map keys to API calls via `preventDefault()` (stops the
|
|
179
|
+
* browser's default action) and `stopPropagation()` (stops the chain
|
|
180
|
+
* of remaining subscribers). Subscribers fire in registration order.
|
|
181
|
+
*/
|
|
182
|
+
export interface KeyDownPayload {
|
|
183
|
+
/** `KeyboardEvent.key` — `"b"`, `"Enter"`, `"ArrowLeft"`, … (lowercased for letters). */
|
|
184
|
+
key: string;
|
|
185
|
+
/** `KeyboardEvent.code` — `"KeyB"`, `"Enter"`, `"ArrowLeft"`, … (layout-independent). */
|
|
186
|
+
code: string;
|
|
187
|
+
ctrl: boolean;
|
|
188
|
+
shift: boolean;
|
|
189
|
+
alt: boolean;
|
|
190
|
+
meta: boolean;
|
|
191
|
+
/** Stop the browser's default for this key (insertion, navigation, …). */
|
|
192
|
+
preventDefault(): void;
|
|
193
|
+
/** Stop further subscribers from receiving this key. */
|
|
194
|
+
stopPropagation(): void;
|
|
195
|
+
/** Underlying DOM event — for advanced needs (`isComposing`, repeat, …). */
|
|
196
|
+
originalEvent: KeyboardEvent;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A registered command — a named, callable unit of editor work that
|
|
200
|
+
* plugins coordinate around. Same definition gets reached by a
|
|
201
|
+
* keyboard shortcut, a toolbar click, a programmatic call from an
|
|
202
|
+
* agent, or an MCP request.
|
|
203
|
+
*
|
|
204
|
+
* The `run` function is the one place the work happens. `isActive` and
|
|
205
|
+
* `isAvailable` let UI plugins paint toggle / disabled state without
|
|
206
|
+
* understanding what the command actually does.
|
|
207
|
+
*/
|
|
208
|
+
export interface CommandDefinition<Args = void> {
|
|
209
|
+
/** Dotted, namespaced — `"mark.toggle.bold"`, `"section.insertBreak"`, … */
|
|
210
|
+
name: string;
|
|
211
|
+
/** Short human label for tooltips / command palettes. */
|
|
212
|
+
title?: string;
|
|
213
|
+
/** Perform the work. Should be idempotent w.r.t. selection (a second
|
|
214
|
+
* invocation on an already-bold selection clears bold, etc.). */
|
|
215
|
+
run: (args: Args) => void;
|
|
216
|
+
/** True when the command represents an active state (mark already on,
|
|
217
|
+
* block is already a heading, …). Drives toolbar `is-active`. */
|
|
218
|
+
isActive?: () => boolean;
|
|
219
|
+
/** False when the command can't run (e.g. selection is wrong shape).
|
|
220
|
+
* Defaults to true. Drives toolbar `disabled`. */
|
|
221
|
+
isAvailable?: () => boolean;
|
|
222
|
+
}
|
|
223
|
+
/** Snapshot of one registered command — what `commands.list()` returns. */
|
|
224
|
+
export interface CommandSnapshot {
|
|
225
|
+
name: string;
|
|
226
|
+
title: string;
|
|
227
|
+
isActive: boolean;
|
|
228
|
+
isAvailable: boolean;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Registry every plugin uses to talk to every other plugin. The
|
|
232
|
+
* editor owns it; plugins register commands on attach and unregister
|
|
233
|
+
* on detach. Keyboard plugins, toolbar plugins, and a future MCP
|
|
234
|
+
* adapter all share the same dispatch path: `editor.commands.execute(name)`.
|
|
235
|
+
*/
|
|
236
|
+
export interface CommandBus {
|
|
237
|
+
/** Register a command. Returns an unsubscribe that removes it. */
|
|
238
|
+
register<Args = void>(def: CommandDefinition<Args>): () => void;
|
|
239
|
+
/** Run a registered command. No-op (with a warning) if unknown. */
|
|
240
|
+
execute<Args = void>(name: string, args?: Args): void;
|
|
241
|
+
/** Snapshot every registered command — for command palettes,
|
|
242
|
+
* toolbars rendering toggle states, accessibility audits. */
|
|
243
|
+
list(): CommandSnapshot[];
|
|
244
|
+
/** Whether the named command is currently registered. */
|
|
245
|
+
has(name: string): boolean;
|
|
246
|
+
}
|
|
247
|
+
export interface EditorOptions {
|
|
248
|
+
initialDocument?: SobreeDocument;
|
|
249
|
+
changeDebounceMs?: number;
|
|
250
|
+
/**
|
|
251
|
+
* Elements whose children are editable blocks, in document order. Called
|
|
252
|
+
* fresh each time — the list can grow/shrink (e.g. during pagination).
|
|
253
|
+
*/
|
|
254
|
+
contentHosts?: () => HTMLElement[];
|
|
255
|
+
/**
|
|
256
|
+
* Y.Doc backing the document. Optional — if absent, the editor creates
|
|
257
|
+
* one internally. Embedders pass their own when they need to attach
|
|
258
|
+
* a provider (`y-websocket`, `y-indexeddb`, `y-webrtc`, …) for
|
|
259
|
+
* persistence or collaboration.
|
|
260
|
+
*
|
|
261
|
+
* When supplied, the editor checks whether the Y.Doc already has body
|
|
262
|
+
* content. If empty, it seeds from `initialDocument`. If non-empty
|
|
263
|
+
* (Phase 2+: a peer joined an active room), the existing Y.Doc state
|
|
264
|
+
* wins and `initialDocument` is ignored. See `editor.ydoc` for the
|
|
265
|
+
* public escape hatch.
|
|
266
|
+
*/
|
|
267
|
+
ydoc?: Y.Doc;
|
|
268
|
+
/**
|
|
269
|
+
* Optional content-hashed `BlobStore` for binary parts (images, fonts).
|
|
270
|
+
*
|
|
271
|
+
* Without one (default): bytes live inline in the Y.Doc's `parts`
|
|
272
|
+
* Y.Map and replicate to every peer through Y updates. Fine for
|
|
273
|
+
* small docs.
|
|
274
|
+
*
|
|
275
|
+
* With one: the editor hashes binary parts, uploads the bytes to the
|
|
276
|
+
* store, and writes only the hash into the Y.Doc's `partRefs` Y.Map.
|
|
277
|
+
* Y updates stay small regardless of image size. The editor maintains
|
|
278
|
+
* a local `BlobCache` that synchronously serves already-fetched bytes
|
|
279
|
+
* to the renderer; `editor.ensurePartsLoaded()` is the async hook for
|
|
280
|
+
* explicit pre-fetching (e.g. before `toDocx()`).
|
|
281
|
+
*
|
|
282
|
+
* See `@sobree/core/blob` for the interface + reference impls
|
|
283
|
+
* (`inMemoryBlobStore`, `fetchBlobStore`).
|
|
284
|
+
*/
|
|
285
|
+
blobStore?: BlobStore;
|
|
286
|
+
/**
|
|
287
|
+
* Initial track-changes mode. When omitted, the editor starts in
|
|
288
|
+
* direct-edit mode (`{ enabled: false }`) and embedders can flip it
|
|
289
|
+
* later via `editor.setTrackChanges`. See `TrackChangesState`.
|
|
290
|
+
*/
|
|
291
|
+
trackChanges?: TrackChangesState;
|
|
292
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AnchoredFrame } from '../../../doc/types';
|
|
2
|
+
export interface AnchorGeometry {
|
|
3
|
+
/** Page top margin (`<w:pgMar w:top>`) in EMU, card-relative. */
|
|
4
|
+
marginTopEmu: number;
|
|
5
|
+
/** Page left margin (`<w:pgMar w:left>`) in EMU, card-relative. */
|
|
6
|
+
marginLeftEmu: number;
|
|
7
|
+
/**
|
|
8
|
+
* Rendered top of the anchor paragraph, card-relative, in EMU.
|
|
9
|
+
* Required when `verticalFrom === "paragraph"`; when absent (the
|
|
10
|
+
* paragraph couldn't be located) the frame falls back to margin-top —
|
|
11
|
+
* a paragraph-anchored frame is never page-relative.
|
|
12
|
+
*/
|
|
13
|
+
anchorParaTopEmu?: number | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function resolveAnchorPosition(frame: AnchoredFrame, geom: AnchorGeometry): {
|
|
16
|
+
xEmu: number;
|
|
17
|
+
yEmu: number;
|
|
18
|
+
};
|
|
@@ -19,10 +19,23 @@
|
|
|
19
19
|
* `pnpm fixtures:compare --pages` on user-contract.docx (Bookman not
|
|
20
20
|
* installed on macOS by default). See [diagnosis notes].
|
|
21
21
|
*/
|
|
22
|
+
/** A face name resolved for CSS: the family fallback stack, plus the
|
|
23
|
+
* weight / italic the face name implied (absent when the name carried
|
|
24
|
+
* no face tokens — the run's own bold/italic then fully decide). */
|
|
25
|
+
export interface ResolvedFontFace {
|
|
26
|
+
stack: string;
|
|
27
|
+
weight?: number;
|
|
28
|
+
italic?: boolean;
|
|
29
|
+
}
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* Resolve an OOXML font NAME into a CSS family stack + implied weight.
|
|
32
|
+
*
|
|
33
|
+
* Curated whole-name chains win first ("Calibri Light" is a real family
|
|
34
|
+
* with its own calibrated chain — corpus baselines depend on it). Then
|
|
35
|
+
* trailing face tokens are stripped ("Helvetica Neue Light" → base
|
|
36
|
+
* "Helvetica Neue" + weight 300) and the BASE resolves through the same
|
|
37
|
+
* curated chains; the full face name stays first in the stack so hosts
|
|
38
|
+
* that do ship the exact face still use it. A name with no tokens and no
|
|
39
|
+
* curated chain keeps the documented unknown-font default (`, serif`).
|
|
27
40
|
*/
|
|
28
|
-
export declare function
|
|
41
|
+
export declare function resolveFontFace(fontFamily: string): ResolvedFontFace;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InlineRun } from '../../../doc/types';
|
|
1
|
+
import { InlineRun, NamedStyle } from '../../../doc/types';
|
|
2
2
|
/**
|
|
3
3
|
* Render a list of InlineRuns into DOM children of `parent`. Empty run
|
|
4
4
|
* lists produce a `<br>` placeholder so contenteditable can place a
|
|
@@ -7,7 +7,7 @@ import { InlineRun } from '../../../doc/types';
|
|
|
7
7
|
* `rawParts` is threaded through so `DrawingRun` can resolve its
|
|
8
8
|
* `partPath` to an `<img src>` via a blob URL / data URI.
|
|
9
9
|
*/
|
|
10
|
-
export declare function appendInlineRuns(parent: HTMLElement, runs: readonly InlineRun[], rawParts?: Record<string, Uint8Array
|
|
10
|
+
export declare function appendInlineRuns(parent: HTMLElement, runs: readonly InlineRun[], rawParts?: Record<string, Uint8Array>, styles?: readonly NamedStyle[]): void;
|
|
11
11
|
/** Convert a raw part's bytes (from `doc.rawParts`) into a blob URL
|
|
12
12
|
* the browser can render as `<img src>` / `background-image`. Exported
|
|
13
13
|
* for the section-frame renderer in `block.ts` which paints the
|
|
@@ -2,16 +2,27 @@ import { Block, NumberingDefinition } from '../../../doc/types';
|
|
|
2
2
|
export interface ListInfo {
|
|
3
3
|
numId: number;
|
|
4
4
|
ordered: boolean;
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
* Applied as `padding-left` on the OL / UL so wrapped text lands at
|
|
8
|
-
* the right position and the marker hangs to its left. */
|
|
5
|
+
/** Text-column indent (`<w:ind w:left>`), twips → the OL/UL
|
|
6
|
+
* `padding-left`. First line and wraps both align here. */
|
|
9
7
|
leftTwips?: number;
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
8
|
+
/** Marker hang (`<w:ind w:hanging>`), twips → the width of the
|
|
9
|
+
* `::before` marker box; the marker sits at `leftTwips - hangingTwips`. */
|
|
12
10
|
hangingTwips?: number;
|
|
13
|
-
/**
|
|
11
|
+
/** Bullet glyph (post-Wingdings remapping), for unordered lists. */
|
|
14
12
|
bulletGlyph?: string;
|
|
13
|
+
/** Marker glyph's own run formatting (`<w:lvl><w:rPr>`): colour, font,
|
|
14
|
+
* size. The font also matters for layout — Word lets the marker
|
|
15
|
+
* font's strut set the bullet line height (see paperStack.css). */
|
|
16
|
+
markerColor?: string;
|
|
17
|
+
markerFont?: string;
|
|
18
|
+
markerSizePt?: number;
|
|
19
|
+
/** CSS counter-style class suffix for ordered lists (`decimal`,
|
|
20
|
+
* `lower-latin`, …) — selects the matching `::before` rule. */
|
|
21
|
+
counterStyle?: string;
|
|
22
|
+
/** Literal text around the number in `lvlText` (`%1.` → suffix `.`;
|
|
23
|
+
* `(%1)` → prefix `(`, suffix `)`). */
|
|
24
|
+
markerPrefix?: string;
|
|
25
|
+
markerSuffix?: string;
|
|
15
26
|
}
|
|
16
27
|
/**
|
|
17
28
|
* Resolve a paragraph's list membership from the numbering table.
|
|
@@ -20,9 +31,9 @@ export interface ListInfo {
|
|
|
20
31
|
*/
|
|
21
32
|
export declare function paragraphListInfo(block: Block, numbering: readonly NumberingDefinition[]): ListInfo | null;
|
|
22
33
|
/**
|
|
23
|
-
* Build the `<ol>` / `<ul>` container for a run of list items sharing
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
34
|
+
* Build the `<ol>` / `<ul>` container for a run of list items sharing a
|
|
35
|
+
* `numId`. Sets the text-column `padding-left`, the `--sobree-list-hang`
|
|
36
|
+
* marker-box width, and the marker content (glyph or counter format) the
|
|
37
|
+
* CSS `::before` rules consume.
|
|
27
38
|
*/
|
|
28
39
|
export declare function createListContainer(info: ListInfo, sectionIndex: number): HTMLElement;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SectionProperties } from '../../../doc/types';
|
|
2
|
+
/**
|
|
3
|
+
* If `section.columns.count > 1`, append a column container to `host`
|
|
4
|
+
* and return it as the new append target. Otherwise return `host` —
|
|
5
|
+
* single-column sections write directly to it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function openColumnContainerIfNeeded(host: HTMLElement, section: SectionProperties | undefined): HTMLElement;
|
|
8
|
+
/**
|
|
9
|
+
* If `container` is a column-flow container, move any trailing
|
|
10
|
+
* visually-empty paragraphs out of it and append them to `host`. Keeps
|
|
11
|
+
* Word's column balancing semantics: empties between content count,
|
|
12
|
+
* trailing empties do not.
|
|
13
|
+
*/
|
|
14
|
+
export declare function evictTrailingEmptyParagraphs(container: HTMLElement, host: HTMLElement): void;
|
|
15
|
+
/**
|
|
16
|
+
* Visually collapse the empty paragraph immediately preceding the
|
|
17
|
+
* just-appended section_break. The paragraph stays in the DOM (and the
|
|
18
|
+
* AST) so round-trip and caret behaviour stay intact; CSS just zeros
|
|
19
|
+
* its height. Pulls back from the last element (which is the section
|
|
20
|
+
* break itself) by one position and checks if it's a visually-empty
|
|
21
|
+
* paragraph (no text, no images).
|
|
22
|
+
*/
|
|
23
|
+
export declare function collapseSectionTrailerEmpty(host: HTMLElement): void;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import { NamedStyle, NumberingDefinition, Table } from '../../../doc/types';
|
|
1
|
+
import { Block, NamedStyle, NumberingDefinition, Table } from '../../../doc/types';
|
|
2
|
+
/**
|
|
3
|
+
* Renderer for a table cell's block content, injected by the caller
|
|
4
|
+
* (`block.ts`) instead of imported — so `table.ts` never imports
|
|
5
|
+
* `block.ts`, breaking the block ↔ table render cycle. The recursion is
|
|
6
|
+
* genuine (a cell holds blocks; a block may be a nested table), but the
|
|
7
|
+
* *import* cycle isn't: the sole caller already owns `renderBlocks` and
|
|
8
|
+
* hands it in.
|
|
9
|
+
*/
|
|
10
|
+
export type RenderCellBlocks = (blocks: readonly Block[], host: HTMLElement, numbering: readonly NumberingDefinition[], styles: readonly NamedStyle[], rawParts: Record<string, Uint8Array>) => void;
|
|
2
11
|
/**
|
|
3
12
|
* Render a Table to a `<table>` element. `vMerge: "restart"` cells
|
|
4
13
|
* emit with `rowspan=N` computed from the following `"continue"` cells
|
|
@@ -12,4 +21,4 @@ import { NamedStyle, NumberingDefinition, Table } from '../../../doc/types';
|
|
|
12
21
|
* 1.5× line-height because the cell's own renderer ignored per-paragraph
|
|
13
22
|
* properties).
|
|
14
23
|
*/
|
|
15
|
-
export declare function renderTable(table: Table, numbering?: readonly NumberingDefinition[], styles?: readonly NamedStyle[], rawParts?: Record<string, Uint8Array>): HTMLElement;
|
|
24
|
+
export declare function renderTable(table: Table, renderCellBlocks: RenderCellBlocks, numbering?: readonly NumberingDefinition[], styles?: readonly NamedStyle[], rawParts?: Record<string, Uint8Array>): HTMLElement;
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:
|
|
1
|
+
.paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:1}.paper-anchors.is-empty{display:none}.paper-zone-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:2}.paper-zone-anchors.is-empty{display:none}.paper-anchor{position:absolute;box-sizing:border-box;overflow:hidden}.paper-header>p,.paper-footer>p,.paper-header>ul,.paper-header>ol,.paper-footer>ul,.paper-footer>ol{margin:0}.paper-footnotes{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);bottom:var(--margin-bottom, 25mm);font-size:.85em;padding-top:.5em;border-top:1pt solid currentColor;background:#fffffff5;z-index:1;max-height:40%;overflow:hidden}.paper-footnotes.is-empty{display:none}.paper-comments{flex:0 0 70mm;max-width:70mm;font-size:.85em;padding:0 .5em;align-self:stretch;overflow-y:auto}.paper-comments.is-empty{display:none}.paper-footnotes .sobree-footnotes__list{margin:0;padding-left:1.5em}.paper-footnotes .sobree-footnotes__item{margin:.25em 0}.paper-content a{color:inherit;text-decoration:none}.paper-content{position:relative;height:100%;min-height:100px;display:flow-root}.paper-content td p:not([style*=line-height]),.paper-content td li:not([style*=line-height]),.paper-content th p:not([style*=line-height]),.paper-content th li:not([style*=line-height]){line-height:1}.paper-content td p:not([style*=margin]),.paper-content th p:not([style*=margin]){margin:0}.paper-content .sobree-hang>li{list-style:none}.paper-content .sobree-hang>li:before{display:inline-block;box-sizing:border-box;width:var(--sobree-list-hang, 0);margin-left:calc(-1 * var(--sobree-list-hang, 0));white-space:nowrap;color:var(--sobree-marker-color, currentColor);font-family:var(--sobree-marker-font, inherit);font-size:var(--sobree-marker-size, inherit)}.paper-content .sobree-hang.lst-bullet>li:before{content:var(--sobree-bullet, "•")}.paper-content .sobree-hang.lst-decimal>li:before{content:var(--mk-pre, "") counter(list-item,decimal) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-decimal-zero>li:before{content:var(--mk-pre, "") counter(list-item,decimal-leading-zero) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-latin>li:before{content:var(--mk-pre, "") counter(list-item,lower-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-latin>li:before{content:var(--mk-pre, "") counter(list-item,upper-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-roman>li:before{content:var(--mk-pre, "") counter(list-item,lower-roman) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-roman>li:before{content:var(--mk-pre, "") counter(list-item,upper-roman) var(--mk-suf, ".")}.paper-content .sobree-tab-spread{display:flex;justify-content:space-between;align-items:baseline;gap:1em;white-space:nowrap}.paper-content .sobree-tab-spread .sobree-tab-spread__before,.paper-content .sobree-tab-spread .sobree-tab-spread__after{white-space:pre-wrap;min-width:0}.paper-content table.sobree-table-bordered td,.paper-content table.sobree-table-bordered th{border:var(--table-cell-border, none);padding:0 4px}.sobree-editor{position:relative;outline:none}.sobree-editor>:first-child{margin-top:0}.sobree-editor p,.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6,.sobree-editor ul,.sobree-editor ol,.sobree-editor li,.sobree-editor blockquote,.sobree-editor pre{margin:0;padding:0}.sobree-editor ul,.sobree-editor ol{padding-left:1.5em}.sobree-editor li.sobree-li-continuation{list-style-type:none}.sobree-editor li.sobree-li-continuation::marker{content:""}.sobree-editor .sobree-fragment-continued{text-align-last:justify}.sobree-editor .sobree-footnote-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-footnote-ref a:hover{text-decoration:underline}.sobree-editor .sobree-footnotes{margin-top:2em;padding-top:.5em;border-top:1pt solid currentColor;font-size:.85em}.sobree-editor .sobree-footnotes__list{padding-left:1.5em}.sobree-editor .sobree-footnotes__item{margin:.25em 0}.sobree-editor ins.sobree-revision-ins{text-decoration:underline}.sobree-editor del.sobree-revision-del{text-decoration:line-through}.sobree-editor .sobree-revision-format{text-decoration:underline dashed var(--sobree-format-revision-color, currentColor);text-underline-offset:3px}.sobree-editor [data-block-revision=ins]:after,.sobree-editor [data-block-revision=del]:after{content:" ¶";color:var(--sobree-block-revision-color, currentColor);opacity:.65;font-weight:600;-webkit-user-select:none;user-select:none}.sobree-editor [data-block-revision=del]:after{text-decoration:line-through}.sobree-editor .sobree-comment-range{background:var(--sobree-comment-range-bg, rgba(255, 217, 0, .25));border-bottom:1px dotted var(--sobree-comment-range-border, rgba(180, 130, 0, .5))}.sobree-editor .sobree-comment-ref{display:inline;font-size:.85em;margin:0 .1em}.sobree-editor .sobree-comment-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-comment-ref a:hover{filter:brightness(.7)}.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6{font-size:inherit;font-weight:inherit}.sobree-editor table{border-collapse:collapse;width:100%;position:relative}.sobree-editor th,.sobree-editor td{border:none;padding:0 .08in;vertical-align:top;transition:border-color .12s ease}.sobree-editor .sobree-section-break{display:flex;align-items:center;gap:8px;margin:8px 0;color:var(--fg-subtle, #7c7764);font-size:11px;letter-spacing:.04em;text-transform:uppercase;-webkit-user-select:none;user-select:none}.sobree-editor .sobree-section-break--continuous{visibility:hidden;height:0;margin:0;padding:0;border:none;overflow:hidden;font-size:0;line-height:0}.sobree-editor .sobree-section-break:before,.sobree-editor .sobree-section-break:after{content:"";flex:1;border-top:1px dashed var(--border, #dedbd0)}.sobree-editor .sobree-section-break__label{flex:none;padding:0 6px;background:var(--bg-elevated, #fff)}.sobree-editor .sobree-textbox-frame p{line-height:1.2}.sobree-editor .paper-content p,.sobree-editor .paper-content li,.sobree-editor .paper-header p,.sobree-editor .paper-header li,.sobree-editor .paper-footer p,.sobree-editor .paper-footer li,.sobree-editor .sobree-textbox-frame p{white-space:pre-wrap}.sobree-editor .sobree-section-trailer-empty{height:0;line-height:0;font-size:0;margin:0;padding:0;overflow:hidden}.sobree-editor .sobree-textbox-frame--placeholder{border:1px solid var(--border, #c8c4b8)}.sobree-editor .sobree-textbox-frame--placeholder.sobree-textbox-frame--filled{border:none;z-index:-1}.sobree-editor img.is-selected{outline:2px solid var(--sobree-primary, #d4521f);outline-offset:2px}.sobree-image-resize-handle{position:absolute;width:16px;height:16px;background:var(--sobree-primary, #d4521f);border:2px solid #fff;border-radius:3px;cursor:nwse-resize;z-index:1000;-webkit-user-select:none;user-select:none}.sobree-viewport{position:relative;overflow:hidden;overscroll-behavior:contain;touch-action:none;background:#ececee}.sobree-viewport__stage{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.sobree-viewport__stage.is-animating{transition:transform .32s cubic-bezier(.2,.7,.2,1)}.sobree-viewport__slot{display:block}.paper-stack .paper-content,.paper-stack .paper-header,.paper-stack .paper-footer{transition:opacity .2s ease}.paper-stack.is-zone-editing .paper-content,.paper-stack.is-zone-editing .paper-header,.paper-stack.is-zone-editing .paper-footer{opacity:.2}.paper-stack.is-zone-editing .paper-header[contenteditable=true],.paper-stack.is-zone-editing .paper-footer[contenteditable=true]{opacity:1}.paper-header[contenteditable=true],.paper-footer[contenteditable=true]{outline:2px dotted var(--primary, #c96f22);outline-offset:4px;background:var(--primary-soft, #fdf6ee);color:var(--fg-strong, #14130f);border-radius:2px}
|