@sobree/core 0.1.0
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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/__vite-browser-external-DYxpcVy9.js +5 -0
- package/dist/__vite-browser-external-DYxpcVy9.js.map +1 -0
- package/dist/blob/cache.d.ts +69 -0
- package/dist/blob/fetch.d.ts +18 -0
- package/dist/blob/hash.d.ts +13 -0
- package/dist/blob/index.d.ts +33 -0
- package/dist/blob/memory.d.ts +2 -0
- package/dist/blob/types.d.ts +80 -0
- package/dist/createSobree.d.ts +132 -0
- package/dist/doc/api.d.ts +132 -0
- package/dist/doc/builders.d.ts +42 -0
- package/dist/doc/pageSetupBridge.d.ts +26 -0
- package/dist/doc/parts.d.ts +18 -0
- package/dist/doc/runs.d.ts +47 -0
- package/dist/doc/styles.d.ts +19 -0
- package/dist/doc/types.d.ts +800 -0
- package/dist/doc/walk.d.ts +30 -0
- package/dist/docx/export/contentTypes.d.ts +35 -0
- package/dist/docx/export/context.d.ts +59 -0
- package/dist/docx/export/document.d.ts +19 -0
- package/dist/docx/export/drawings.d.ts +10 -0
- package/dist/docx/export/headers.d.ts +19 -0
- package/dist/docx/export/index.d.ts +14 -0
- package/dist/docx/export/runs.d.ts +8 -0
- package/dist/docx/export/styles.d.ts +8 -0
- package/dist/docx/export/zip.d.ts +13 -0
- package/dist/docx/import/anchoredFrames.d.ts +34 -0
- package/dist/docx/import/comments.d.ts +3 -0
- package/dist/docx/import/document.d.ts +57 -0
- package/dist/docx/import/flowFrames.d.ts +11 -0
- package/dist/docx/import/footnotes.d.ts +3 -0
- package/dist/docx/import/headers.d.ts +50 -0
- package/dist/docx/import/index.d.ts +12 -0
- package/dist/docx/import/inlineFrames.d.ts +62 -0
- package/dist/docx/import/numbering.d.ts +2 -0
- package/dist/docx/import/paragraph.d.ts +24 -0
- package/dist/docx/import/paragraphs.d.ts +27 -0
- package/dist/docx/import/rels.d.ts +5 -0
- package/dist/docx/import/runs.d.ts +64 -0
- package/dist/docx/import/settings.d.ts +48 -0
- package/dist/docx/import/styles.d.ts +3 -0
- package/dist/docx/import/tables.d.ts +12 -0
- package/dist/docx/import/unzip.d.ts +13 -0
- package/dist/docx/shared/namespaces.d.ts +31 -0
- package/dist/docx/shared/pageSize.d.ts +27 -0
- package/dist/docx/shared/shading.d.ts +2 -0
- package/dist/docx/shared/units.d.ts +35 -0
- package/dist/docx/shared/xml.d.ts +29 -0
- package/dist/docx/types.d.ts +98 -0
- package/dist/editor/index.d.ts +1078 -0
- package/dist/editor/internal/blockRegistry.d.ts +91 -0
- package/dist/editor/internal/mutations.d.ts +63 -0
- package/dist/editor/internal/positionMap.d.ts +35 -0
- package/dist/editor/table.d.ts +96 -0
- package/dist/editor/view/docRenderer/anchorLayer.d.ts +26 -0
- package/dist/editor/view/docRenderer/block.d.ts +13 -0
- package/dist/editor/view/docRenderer/fontFallback.d.ts +28 -0
- package/dist/editor/view/docRenderer/index.d.ts +18 -0
- package/dist/editor/view/docRenderer/inline.d.ts +15 -0
- package/dist/editor/view/docRenderer/inlineFrame.d.ts +4 -0
- package/dist/editor/view/docRenderer/lists.d.ts +28 -0
- package/dist/editor/view/docRenderer/paragraph.d.ts +2 -0
- package/dist/editor/view/docRenderer/properties.d.ts +2 -0
- package/dist/editor/view/docRenderer/table.d.ts +15 -0
- package/dist/editor/view/docRenderer/units.d.ts +48 -0
- package/dist/editor/view/docSerialize/block.d.ts +14 -0
- package/dist/editor/view/docSerialize/index.d.ts +8 -0
- package/dist/editor/view/docSerialize/inline.d.ts +11 -0
- package/dist/editor/view/docSerialize/table.d.ts +12 -0
- package/dist/editor/view/imageResize.d.ts +16 -0
- package/dist/embed/floatingCorner.d.ts +44 -0
- package/dist/embed/viewport.d.ts +133 -0
- package/dist/fonts/embedAPI.d.ts +33 -0
- package/dist/fonts/emit.d.ts +24 -0
- package/dist/fonts/fontFaceRegistry.d.ts +20 -0
- package/dist/fonts/fsType.d.ts +36 -0
- package/dist/fonts/index.d.ts +19 -0
- package/dist/fonts/liveness.d.ts +2 -0
- package/dist/fonts/odttf.d.ts +33 -0
- package/dist/fonts/parse.d.ts +29 -0
- package/dist/fonts/types.d.ts +52 -0
- package/dist/headless.d.ts +168 -0
- package/dist/history/history.d.ts +100 -0
- package/dist/history/index.d.ts +4 -0
- package/dist/history/types.d.ts +54 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +10561 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/parse.d.ts +6 -0
- package/dist/pagination/cost.d.ts +32 -0
- package/dist/pagination/index.d.ts +2 -0
- package/dist/pagination/paginate.d.ts +10 -0
- package/dist/pagination/postConditions.d.ts +10 -0
- package/dist/pagination/types.d.ts +94 -0
- package/dist/paperStack/pageSetup.d.ts +42 -0
- package/dist/paperStack/paginationAdapter/buildItems.d.ts +19 -0
- package/dist/paperStack/paginationAdapter/distribute.d.ts +23 -0
- package/dist/paperStack/paginationAdapter/index.d.ts +18 -0
- package/dist/paperStack/paginationAdapter/paragraphLines.d.ts +23 -0
- package/dist/paperStack/paginationAdapter/splitList.d.ts +19 -0
- package/dist/paperStack/paginationAdapter/splitParagraph.d.ts +21 -0
- package/dist/paperStack/paginationAdapter/types.d.ts +30 -0
- package/dist/paperStack/paper.d.ts +107 -0
- package/dist/paperStack/paperStack.d.ts +245 -0
- package/dist/plugin.d.ts +24 -0
- package/dist/plugins/marks.d.ts +49 -0
- package/dist/plugins/sections.d.ts +15 -0
- package/dist/presence/attach.d.ts +48 -0
- package/dist/presence/awareness.d.ts +28 -0
- package/dist/presence/index.d.ts +19 -0
- package/dist/presence/overlay.d.ts +28 -0
- package/dist/presence/state.d.ts +36 -0
- package/dist/sobree.d.ts +211 -0
- package/dist/tokens.css +144 -0
- package/dist/util/selection.d.ts +13 -0
- package/dist/ydoc/apply.d.ts +68 -0
- package/dist/ydoc/index.d.ts +18 -0
- package/dist/ydoc/project.d.ts +41 -0
- package/dist/ydoc/runs.d.ts +51 -0
- package/dist/ydoc/schema.d.ts +123 -0
- package/dist/ydoc/seed.d.ts +45 -0
- package/dist/ydoc/textDiff.d.ts +59 -0
- package/dist/zoneEdit/index.d.ts +22 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sobree.dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the “Software”), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @sobree/core
|
|
2
|
+
|
|
3
|
+
Embeddable, print-view-first WYSIWYG editor for `.docx`. Framework-free
|
|
4
|
+
core, plugin architecture, native OOXML round-trip.
|
|
5
|
+
|
|
6
|
+
→ Docs: **[docs.sobree.dev](https://docs.sobree.dev)**
|
|
7
|
+
→ Live editor: **[sobree.dev/try](https://sobree.dev/try)**
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
`@sobree/core` is the **minimal editor kernel** — AST + paginator + DOCX
|
|
12
|
+
I/O + history + fonts. It ships with **zero plugin packages**. Install
|
|
13
|
+
the plugins you want and pass them to `createSobree()`.
|
|
14
|
+
|
|
15
|
+
For an interactive editor with toolbar, keyboard shortcuts, and zoom dock:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add @sobree/core @sobree/keyboard @sobree/block-tools @sobree/zoom-controls
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For a headless / API-driven peer (LLM agent, automation):
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
pnpm add @sobree/core yjs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use `HeadlessSobree` — the no-DOM counterpart of the browser editor.
|
|
28
|
+
For multi-user collab, add `@sobree/collab-providers` (client glue)
|
|
29
|
+
and run `@sobree/collab-server` somewhere.
|
|
30
|
+
|
|
31
|
+
## Hello world
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { createSobree } from "@sobree/core";
|
|
35
|
+
import { keyboard } from "@sobree/keyboard";
|
|
36
|
+
import { blockTools } from "@sobree/block-tools";
|
|
37
|
+
import { zoomControls } from "@sobree/zoom-controls";
|
|
38
|
+
import "@sobree/core/tokens.css";
|
|
39
|
+
|
|
40
|
+
const editor = createSobree("#editor", {
|
|
41
|
+
content: "# Hello\n\nStart typing.",
|
|
42
|
+
plugins: [
|
|
43
|
+
keyboard(), // Cmd+B / Cmd+Z / …
|
|
44
|
+
blockTools(), // floating toolbar + gutter indicator
|
|
45
|
+
zoomControls(), // bottom-right zoom dock
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
editor.on("change", ({ doc }) => {
|
|
50
|
+
console.log("body has", doc.body.length, "blocks");
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`createSobree()` mounts the viewport, the paginated paper stack, runs each
|
|
55
|
+
plugin's `setup()` against a shared context, and returns a single handle.
|
|
56
|
+
Plugins are torn down in reverse-of-mount order on `editor.destroy()`.
|
|
57
|
+
|
|
58
|
+
## What's in the box
|
|
59
|
+
|
|
60
|
+
- **Native OOXML AST.** Every node maps 1:1 to a `<w:…>` element.
|
|
61
|
+
- **Y.Doc backed.** Every editor is backed by a Yjs `Y.Doc`; mutations mirror in via `Y.Doc.transact`. Embedders attach `y-websocket` / `y-indexeddb` / `y-webrtc` providers for persistence + collaboration. `editor.ydoc` is the escape hatch.
|
|
62
|
+
- **Pure paginator.** TeX-style break selection, widow / orphan, keep-with-next, multi-section. No DOM, no I/O.
|
|
63
|
+
- **Per-peer undo / redo.** Thin wrapper around `Y.UndoManager` — each peer's `Cmd+Z` reverses only its own edits via tracked Y origins; remote edits flow through but stay off the local stack. Selection restored from `stackItem.meta`.
|
|
64
|
+
- **OOXML font embedding.** `word/fontTable.xml` round-trip + ODTTF obfuscation + OS/2 fsType licence check + runtime `@font-face` registration. Note: `fsType` is an advisory gate — the embedder is responsible for ensuring they have rights to ship any font they pass to `editor.embedFont(...)`. Pass `{ allowRestricted: true }` only when you do.
|
|
65
|
+
- **Minimal core, opt-in plugins.** Toolbar (`@sobree/block-tools`), keyboard (`@sobree/keyboard`), zoom dock (`@sobree/zoom-controls`) ship as siblings. For multi-user collab: `@sobree/collab-providers` (client) + `@sobree/collab-server` (Node relay). `@sobree/core` has no plugin dependencies — only `fflate` for ZIP and `yjs` for the CRDT.
|
|
66
|
+
- **Y-protocol IS the wire.** No separate RPC plugin. Headless callers (LLMs, automation) participate via `HeadlessSobree` — same commands, same Y.Doc.
|
|
67
|
+
- **Wire-ready surface.** `editor.commands.execute(name, args)` is the single dispatch path used by keyboard, toolbar, and external transports.
|
|
68
|
+
- **JSON-clean projection.** `editor.getDocument()` returns a JSON-clean `SobreeDocument` snapshot of the underlying Y.Doc — survives `structuredClone` and any wire.
|
|
69
|
+
|
|
70
|
+
## Polymorphic content
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
createSobree("#editor", { content: "# Markdown" }); // seed string
|
|
74
|
+
createSobree("#editor", { content: docxBlob }); // .docx bytes
|
|
75
|
+
createSobree("#editor", { content: astLiteral }); // built with the doc builders
|
|
76
|
+
createSobree("#editor"); // empty
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For the `.docx` path, `await editor.ready` resolves once the import lands.
|
|
80
|
+
|
|
81
|
+
## Save back to `.docx`
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const { blob } = editor.toDocx();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or load a different file at runtime:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const { warnings } = await editor.loadDocx(file);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
- [Quick start](https://docs.sobree.dev/quick-start/)
|
|
96
|
+
- [`createSobree()` API](https://docs.sobree.dev/api/create-sobree/)
|
|
97
|
+
- [History (undo / redo)](https://docs.sobree.dev/api/history/)
|
|
98
|
+
- [Fonts (embedding + fontTable)](https://docs.sobree.dev/api/fonts/)
|
|
99
|
+
- [Architecture](https://docs.sobree.dev/concepts/architecture/)
|
|
100
|
+
- [Document model](https://docs.sobree.dev/concepts/document/)
|
|
101
|
+
- [Plugin model](https://docs.sobree.dev/concepts/plugins/)
|
|
102
|
+
- [Building your own plugin](https://docs.sobree.dev/plugins/build-your-own/)
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__vite-browser-external-DYxpcVy9.js","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"AAAA,MAAAA,IAAe,CAAA;"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BlobHash, BlobStore } from './types';
|
|
2
|
+
export interface BlobCacheOptions {
|
|
3
|
+
/** Underlying store. */
|
|
4
|
+
store: BlobStore;
|
|
5
|
+
/**
|
|
6
|
+
* Optional listener fired when a previously-missing hash arrives in
|
|
7
|
+
* the cache (background fetch landed). Editors use this to know
|
|
8
|
+
* when to re-render — a `<img>` whose bytes were pending now has
|
|
9
|
+
* something to show.
|
|
10
|
+
*/
|
|
11
|
+
onResolved?: (hash: BlobHash) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Optional listener fired when a fetch fails. The cache leaves the
|
|
14
|
+
* hash in "missing" state and lets a future `ensureLoaded` retry.
|
|
15
|
+
* Default: warn to console.
|
|
16
|
+
*/
|
|
17
|
+
onError?: (hash: BlobHash, err: unknown) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare class BlobCache {
|
|
20
|
+
private readonly store;
|
|
21
|
+
private readonly onResolved;
|
|
22
|
+
private readonly onError;
|
|
23
|
+
private readonly cache;
|
|
24
|
+
/** In-flight fetches, keyed by hash. Promise resolves to the bytes
|
|
25
|
+
* (cached) or `null` (not found / error). */
|
|
26
|
+
private readonly inflight;
|
|
27
|
+
constructor(opts: BlobCacheOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Synchronously read bytes for a hash. Returns `null` if not yet
|
|
30
|
+
* cached. Doesn't trigger a fetch — call `ensureLoaded` first if
|
|
31
|
+
* you need to wait.
|
|
32
|
+
*/
|
|
33
|
+
get(hash: BlobHash): Uint8Array | null;
|
|
34
|
+
/** Whether the hash is currently in the cache. */
|
|
35
|
+
has(hash: BlobHash): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Insert bytes into the cache directly. Used when the embedder
|
|
38
|
+
* already has the bytes in hand (paste image, font embed, DOCX
|
|
39
|
+
* import) and wants the local renderer to find them immediately.
|
|
40
|
+
*
|
|
41
|
+
* Returns the bytes' hash. Caller is responsible for uploading
|
|
42
|
+
* to the BlobStore separately (typically `await store.put(bytes)`
|
|
43
|
+
* with the same input).
|
|
44
|
+
*/
|
|
45
|
+
put(hash: BlobHash, bytes: Uint8Array): void;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure the given hashes are in the cache. Returns a Promise that
|
|
48
|
+
* resolves once every hash is either in the cache or has failed to
|
|
49
|
+
* fetch (failures don't reject the Promise — they're reported via
|
|
50
|
+
* `onError` and skipped, consistent with the "best-effort
|
|
51
|
+
* availability" model).
|
|
52
|
+
*
|
|
53
|
+
* Already-cached hashes resolve immediately. Already-in-flight
|
|
54
|
+
* fetches are deduplicated (multiple concurrent callers wait on
|
|
55
|
+
* the same Promise).
|
|
56
|
+
*/
|
|
57
|
+
ensureLoaded(hashes: readonly BlobHash[]): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Number of cached blobs. Diagnostic / test helper; production
|
|
60
|
+
* code shouldn't depend on this for correctness.
|
|
61
|
+
*/
|
|
62
|
+
size(): number;
|
|
63
|
+
/**
|
|
64
|
+
* Clear all cached blobs. Used by the Editor on `destroy` to free
|
|
65
|
+
* memory; future refinements may auto-evict.
|
|
66
|
+
*/
|
|
67
|
+
clear(): void;
|
|
68
|
+
private fetchOne;
|
|
69
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BlobStore } from './types';
|
|
2
|
+
export interface FetchBlobStoreOptions {
|
|
3
|
+
/** Base URL — bytes are addressed at `<baseUrl>/<hash>`. No trailing slash. */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/**
|
|
6
|
+
* Optional headers factory. Called per request — embed auth tokens,
|
|
7
|
+
* trace IDs, custom content types, etc. Defaults to no extra headers.
|
|
8
|
+
*/
|
|
9
|
+
headers?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
10
|
+
/** Pass through to the underlying `fetch` (CORS mode, signal, etc.). */
|
|
11
|
+
fetchInit?: RequestInit;
|
|
12
|
+
/**
|
|
13
|
+
* Override the global `fetch`. Useful for testing or running inside
|
|
14
|
+
* a runtime with a custom fetch impl. Default: `globalThis.fetch`.
|
|
15
|
+
*/
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
export declare function fetchBlobStore(opts: FetchBlobStoreOptions): BlobStore;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BlobHash } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Compute the SHA-256 hex digest of `bytes`. Lowercase hex, 64 chars.
|
|
4
|
+
*
|
|
5
|
+
* Always returns a Promise — the WebCrypto API is async, and we
|
|
6
|
+
* shape the Node path the same way for API consistency.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sha256Hex(bytes: Uint8Array): Promise<BlobHash>;
|
|
9
|
+
/**
|
|
10
|
+
* Validate that a string looks like a SHA-256 hex digest. Used as a
|
|
11
|
+
* defensive guard when bytes come from the wire.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isBlobHash(s: string): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sobree/core blob module — content-hashed binary parts.
|
|
3
|
+
*
|
|
4
|
+
* Without configuring a `BlobStore`, Sobree behaves as it always has:
|
|
5
|
+
* binary parts (images, fonts) live inline in `SobreeDocument.rawParts`
|
|
6
|
+
* and inside the Y.Doc's `parts: Y.Map<Uint8Array>`.
|
|
7
|
+
*
|
|
8
|
+
* With a `BlobStore` configured on `createSobree({ blobStore })`,
|
|
9
|
+
* Sobree shifts to *content-hashed* mode: bytes go to the BlobStore
|
|
10
|
+
* (the side channel) and the Y.Doc stores only SHA-256 hashes
|
|
11
|
+
* (`partRefs: Y.Map<string, string>`). Y updates stay small — a
|
|
12
|
+
* 5 MB image paste no longer means 5 MB on every peer's Y.Doc.
|
|
13
|
+
*
|
|
14
|
+
* Two reference implementations ship out of the box:
|
|
15
|
+
*
|
|
16
|
+
* - `inMemoryBlobStore()` — tests, local-only deployments
|
|
17
|
+
* - `fetchBlobStore({ baseUrl, headers })` — HTTP backend
|
|
18
|
+
*
|
|
19
|
+
* For production at scale, write a custom `BlobStore` against S3 /
|
|
20
|
+
* R2 / Postgres / Redis. The interface is three methods.
|
|
21
|
+
*
|
|
22
|
+
* The `BlobCache` class bridges the async `BlobStore` and the
|
|
23
|
+
* synchronous editor renderer — pre-fetch with `ensureLoaded`, read
|
|
24
|
+
* synchronously after.
|
|
25
|
+
*/
|
|
26
|
+
export type { BlobHash, BlobStore } from './types';
|
|
27
|
+
export { BlobStoreError } from './types';
|
|
28
|
+
export { sha256Hex, isBlobHash } from './hash';
|
|
29
|
+
export { inMemoryBlobStore } from './memory';
|
|
30
|
+
export { fetchBlobStore } from './fetch';
|
|
31
|
+
export type { FetchBlobStoreOptions } from './fetch';
|
|
32
|
+
export { BlobCache } from './cache';
|
|
33
|
+
export type { BlobCacheOptions } from './cache';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlobStore — the side-channel where binary parts live when an
|
|
3
|
+
* embedder configures Sobree for content-hashed assets (Phase 3.2+).
|
|
4
|
+
*
|
|
5
|
+
* # The contract
|
|
6
|
+
*
|
|
7
|
+
* Tiny, intentionally minimal. Three operations: `put` (returns the
|
|
8
|
+
* content hash under which bytes are stored), `get` (fetches by
|
|
9
|
+
* hash), and optional `has` (cheap existence check). Every reasonable
|
|
10
|
+
* backend — in-memory, IndexedDB, HTTP, S3, R2, Cloudflare Workers,
|
|
11
|
+
* Postgres LO, Redis — implements this.
|
|
12
|
+
*
|
|
13
|
+
* # Why content addressing
|
|
14
|
+
*
|
|
15
|
+
* Two reasons:
|
|
16
|
+
*
|
|
17
|
+
* 1. **Deduplication.** A 5 MB logo pasted into 100 docs lives once
|
|
18
|
+
* in the blob store, not 100 times. Hashes collide on identity,
|
|
19
|
+
* so identical bytes produce identical keys.
|
|
20
|
+
* 2. **Tamper resistance.** A peer can verify any fetched blob by
|
|
21
|
+
* re-hashing — no trust in the storage layer required for
|
|
22
|
+
* integrity. (Confidentiality is a separate concern; encrypt at
|
|
23
|
+
* the transport / storage layer if you need it.)
|
|
24
|
+
*
|
|
25
|
+
* # Hash algorithm
|
|
26
|
+
*
|
|
27
|
+
* Sobree uses SHA-256 hex (lowercase, no separators) for blob
|
|
28
|
+
* addressing. 64 chars per hash. Sized for the next decade or two of
|
|
29
|
+
* web content; not so long that hashes are awkward to log or shove
|
|
30
|
+
* into a URL path.
|
|
31
|
+
*
|
|
32
|
+
* # Concurrency
|
|
33
|
+
*
|
|
34
|
+
* Implementations should be safe for concurrent `put` of the same
|
|
35
|
+
* content (multiple callers, same bytes → same hash, idempotent).
|
|
36
|
+
* `get(hash)` must return the same bytes that were `put`.
|
|
37
|
+
*/
|
|
38
|
+
/** A SHA-256 hex digest. 64 lowercase hex chars. */
|
|
39
|
+
export type BlobHash = string;
|
|
40
|
+
export interface BlobStore {
|
|
41
|
+
/**
|
|
42
|
+
* Upload bytes and return their content hash. Idempotent — calling
|
|
43
|
+
* `put` with the same bytes returns the same hash and is a no-op
|
|
44
|
+
* after the first call.
|
|
45
|
+
*/
|
|
46
|
+
put(bytes: Uint8Array): Promise<BlobHash>;
|
|
47
|
+
/**
|
|
48
|
+
* Fetch bytes by hash. Returns `null` when the blob isn't present
|
|
49
|
+
* (e.g. another peer wrote the partRef but the bytes haven't
|
|
50
|
+
* propagated yet). Implementations should NOT throw on "not
|
|
51
|
+
* found" — return null.
|
|
52
|
+
*/
|
|
53
|
+
get(hash: BlobHash): Promise<Uint8Array | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Optional cheap existence check — returns `true` if a blob with
|
|
56
|
+
* the given hash exists, without transferring its content. Used by
|
|
57
|
+
* `BlobCache` to decide whether to schedule a fetch. Falls back to
|
|
58
|
+
* `(await get(hash)) !== null` if not provided.
|
|
59
|
+
*/
|
|
60
|
+
has?(hash: BlobHash): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Optional removal. Most production deployments don't expose this
|
|
63
|
+
* over the wire (blob deletion needs care: ref counting, garbage
|
|
64
|
+
* collection, distributed coordination). Provided for tests and
|
|
65
|
+
* local-only deployments where the embedder owns lifecycle.
|
|
66
|
+
*/
|
|
67
|
+
delete?(hash: BlobHash): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* BlobStoreError is thrown for low-level failures (transport,
|
|
71
|
+
* authorization, integrity check). "Not found" is *not* an error —
|
|
72
|
+
* `get` returns `null` for that case so callers can distinguish.
|
|
73
|
+
*/
|
|
74
|
+
export declare class BlobStoreError extends Error {
|
|
75
|
+
/** Originating exception, network response, etc. */
|
|
76
|
+
readonly cause?: unknown | undefined;
|
|
77
|
+
constructor(message: string,
|
|
78
|
+
/** Originating exception, network response, etc. */
|
|
79
|
+
cause?: unknown | undefined);
|
|
80
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Sobree, SobreeEvent, SobreeEventPayload, SobreeUnsubscribe } from './sobree';
|
|
2
|
+
import { Editor, CommandBus } from './editor';
|
|
3
|
+
import { Viewport } from './embed/viewport';
|
|
4
|
+
import { SobreePlugin } from './plugin';
|
|
5
|
+
import { PageSetup } from './paperStack/pageSetup';
|
|
6
|
+
import { SobreeDocument } from './doc/types';
|
|
7
|
+
/**
|
|
8
|
+
* Initial content. Type detection is automatic:
|
|
9
|
+
* - `string` → seed-quality Markdown (see `parseMarkdown`)
|
|
10
|
+
* - `Blob` / `File` → `.docx` bytes (loaded asynchronously)
|
|
11
|
+
* - `ArrayBuffer` → `.docx` bytes
|
|
12
|
+
* - `Uint8Array` → `.docx` bytes
|
|
13
|
+
* - `SobreeDocument` → AST literal (use the builders to construct)
|
|
14
|
+
*
|
|
15
|
+
* For a `.docx` source, the constructor returns synchronously with an
|
|
16
|
+
* empty editor and `.ready` resolves once the import lands. For all
|
|
17
|
+
* other source types, `.ready` is already resolved when the factory
|
|
18
|
+
* returns.
|
|
19
|
+
*/
|
|
20
|
+
export type SobreeContent = string | Blob | ArrayBuffer | Uint8Array | SobreeDocument;
|
|
21
|
+
/**
|
|
22
|
+
* How the viewport is fitted on initial mount.
|
|
23
|
+
* - `"width"` (default) — first paper fills the host width; you see
|
|
24
|
+
* the document the way you'd read it.
|
|
25
|
+
* - `"page"` — first paper is fully contained (whole page visible).
|
|
26
|
+
* - `"none"` — leave the viewport at 1:1, no auto-fit.
|
|
27
|
+
*/
|
|
28
|
+
export type FitOnMount = "width" | "page" | "none";
|
|
29
|
+
export interface CreateSobreeOptions {
|
|
30
|
+
/** Initial content. See `SobreeContent`. Default: empty document. */
|
|
31
|
+
content?: SobreeContent;
|
|
32
|
+
/** Page setup. Falls back to A4 portrait with 1in margins. */
|
|
33
|
+
pageSetup?: PageSetup;
|
|
34
|
+
/**
|
|
35
|
+
* Plugins to mount. Each receives a `PluginContext` (editor +
|
|
36
|
+
* viewport + sobree + host) on `setup()` and returns a destroyer.
|
|
37
|
+
* Mounted in array order; destroyed in reverse on
|
|
38
|
+
* `editor.destroy()`. See `@sobree/keyboard`, `@sobree/block-tools`,
|
|
39
|
+
* `@sobree/zoom-controls` for stock factories. (`@sobree/collab-providers`
|
|
40
|
+
* lands in Phase 2 for Yjs persistence / collaboration.)
|
|
41
|
+
*/
|
|
42
|
+
plugins?: SobreePlugin[];
|
|
43
|
+
/** Forwarded to the underlying Editor. */
|
|
44
|
+
changeDebounceMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Y.Doc backing the document. The editor mirrors every mutation into
|
|
47
|
+
* this Y.Doc; embedders attach providers (`y-websocket`,
|
|
48
|
+
* `y-indexeddb`, `y-webrtc`, …) for persistence / collaboration.
|
|
49
|
+
* If absent, the editor creates one internally — still observable
|
|
50
|
+
* via `editor.editor.ydoc`.
|
|
51
|
+
*/
|
|
52
|
+
ydoc?: import('yjs').Doc;
|
|
53
|
+
/**
|
|
54
|
+
* Optional content-hashed `BlobStore` for binary parts (Phase 3.2+).
|
|
55
|
+
* Without one, binary parts (images, fonts) ride inline in the
|
|
56
|
+
* Y.Doc; with one, they go through the BlobStore and the Y.Doc
|
|
57
|
+
* carries only hashes. See `EditorOptions.blobStore` for the full
|
|
58
|
+
* contract.
|
|
59
|
+
*/
|
|
60
|
+
blobStore?: import('./blob').BlobStore;
|
|
61
|
+
/**
|
|
62
|
+
* Auto-fit the viewport to the first paper after mount. Default
|
|
63
|
+
* `"width"` — what most embedders want for a "looks right out of the
|
|
64
|
+
* box" first impression. Pass `"none"` if you're driving the
|
|
65
|
+
* viewport yourself.
|
|
66
|
+
*/
|
|
67
|
+
fitOnMount?: FitOnMount;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The editor handle returned by `createSobree()`. Proxies the most-used
|
|
71
|
+
* methods of the underlying `Sobree` + `Editor` so embedders rarely
|
|
72
|
+
* need to reach through. Power users can use `.sobree` / `.editor` /
|
|
73
|
+
* `.viewport` directly.
|
|
74
|
+
*
|
|
75
|
+
* Plugin instances are NOT exposed on the handle — plugins self-manage
|
|
76
|
+
* after handoff. Reach through `.editor` for command bus / events.
|
|
77
|
+
*/
|
|
78
|
+
export interface SobreeHandle {
|
|
79
|
+
readonly sobree: Sobree;
|
|
80
|
+
readonly editor: Editor;
|
|
81
|
+
readonly viewport: Viewport;
|
|
82
|
+
/**
|
|
83
|
+
* The Y.Doc backing the document. Provided so embedders can attach
|
|
84
|
+
* Yjs providers (`y-websocket`, `y-indexeddb`, `y-webrtc`) for
|
|
85
|
+
* persistence / collaboration without reaching through `editor.editor`.
|
|
86
|
+
* Same reference as `editor.editor.ydoc`.
|
|
87
|
+
*/
|
|
88
|
+
readonly ydoc: import('yjs').Doc;
|
|
89
|
+
/**
|
|
90
|
+
* Resolves when the constructor's `content` has finished loading. For
|
|
91
|
+
* docx content this awaits the import; for everything else this is an
|
|
92
|
+
* already-resolved promise.
|
|
93
|
+
*/
|
|
94
|
+
readonly ready: Promise<{
|
|
95
|
+
warnings: string[];
|
|
96
|
+
}>;
|
|
97
|
+
getDocument(): SobreeDocument;
|
|
98
|
+
setDocument(doc: SobreeDocument): void;
|
|
99
|
+
/** Replace the document with one parsed from a Markdown string (seed-quality). */
|
|
100
|
+
loadMarkdown(md: string): void;
|
|
101
|
+
/** Load a `.docx` file. Resolves with any import warnings. */
|
|
102
|
+
loadDocx(src: File | Blob | ArrayBuffer | Uint8Array): Promise<{
|
|
103
|
+
warnings: string[];
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* Serialise the current document to a `.docx` Blob. Uses bytes
|
|
107
|
+
* currently cached locally — if a `blobStore` is configured and
|
|
108
|
+
* some referenced parts aren't yet cached, call
|
|
109
|
+
* `await handle.ensurePartsLoaded()` first to pre-fetch them.
|
|
110
|
+
* Missing parts appear in `warnings`.
|
|
111
|
+
*/
|
|
112
|
+
toDocx(): {
|
|
113
|
+
blob: Blob;
|
|
114
|
+
warnings: string[];
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Wait for every currently-referenced binary part to be available
|
|
118
|
+
* in the local cache. No-op when no `blobStore` is configured.
|
|
119
|
+
*/
|
|
120
|
+
ensurePartsLoaded(): Promise<void>;
|
|
121
|
+
getPageSetup(): PageSetup;
|
|
122
|
+
setPageSetup(partial: Partial<PageSetup>): void;
|
|
123
|
+
readonly commands: CommandBus;
|
|
124
|
+
on<E extends SobreeEvent>(event: E, cb: (payload: SobreeEventPayload[E]) => void): SobreeUnsubscribe;
|
|
125
|
+
destroy(): void;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Mount a fully-wired Sobree editor (Viewport + Sobree + user plugins)
|
|
129
|
+
* into `target` (CSS selector or HTMLElement). See `CreateSobreeOptions`
|
|
130
|
+
* and `SobreeHandle` for the full surface.
|
|
131
|
+
*/
|
|
132
|
+
export declare function createSobree(target: string | HTMLElement, options?: CreateSobreeOptions): SobreeHandle;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-level types for talking to the Editor.
|
|
3
|
+
*
|
|
4
|
+
* These describe HOW you address content (positions, ranges, selections)
|
|
5
|
+
* and what guarantees you get back (block versions, edit results). They
|
|
6
|
+
* are JSON-clean so the same shapes work for in-process callers, the
|
|
7
|
+
* forthcoming WebSocket / MCP adapter, and tests.
|
|
8
|
+
*
|
|
9
|
+
* Kept separate from `src/doc/types.ts` (the AST itself) because these
|
|
10
|
+
* are runtime concerns — none of them are persisted to .docx.
|
|
11
|
+
*
|
|
12
|
+
* Design note on why there's no polymorphic `Position` type:
|
|
13
|
+
* - For block-scoped operations (insert / replace / delete) the input
|
|
14
|
+
* is a `BlockRef` paired with a method name that spells out the
|
|
15
|
+
* intent (`insertBlockBefore`, `insertBlockAfter`).
|
|
16
|
+
* - For inline-scoped operations (insert a run, move caret, apply
|
|
17
|
+
* attributes across a span) the input is an `InlinePosition` — a
|
|
18
|
+
* block ref plus a character offset inside that block.
|
|
19
|
+
* This keeps method signatures self-describing for LLM / MCP callers,
|
|
20
|
+
* no discriminated unions to construct.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Stable handle to a block, valid for the lifetime of an Editor instance.
|
|
24
|
+
* `id` is allocated by the Editor on first sight (sequential strings like
|
|
25
|
+
* `"b1"`, `"b2"`); `version` bumps on every modification. Versions reset
|
|
26
|
+
* to 0 when the document is reloaded (`setDocument`, `openDocx`).
|
|
27
|
+
*
|
|
28
|
+
* Pass this to mutating operations to opt in to optimistic locking. The
|
|
29
|
+
* operation fails with `"optimistic-lock"` if the live version no longer
|
|
30
|
+
* matches.
|
|
31
|
+
*/
|
|
32
|
+
export interface BlockRef {
|
|
33
|
+
id: string;
|
|
34
|
+
version: number;
|
|
35
|
+
}
|
|
36
|
+
/** Version map for blocks an operation should also lock-check. */
|
|
37
|
+
export type BlockExpectations = Record<string, number>;
|
|
38
|
+
/**
|
|
39
|
+
* Address a point inside a block's content.
|
|
40
|
+
*
|
|
41
|
+
* `offset = 0` is the start of the block's content; `offset = blockLength`
|
|
42
|
+
* is the end. Non-text inlines (drawings, fields, hyperlinks) count as
|
|
43
|
+
* 1 each. A caret always lives inside a block — "before block N" is
|
|
44
|
+
* expressed as `{ block: blockN, offset: 0 }`; "after block N" is
|
|
45
|
+
* `{ block: blockN, offset: blockLength }`. For inserting NEW blocks
|
|
46
|
+
* adjacent to an existing one, use the `insertBlockBefore` /
|
|
47
|
+
* `insertBlockAfter` methods directly — they take a `BlockRef`, not a
|
|
48
|
+
* position.
|
|
49
|
+
*/
|
|
50
|
+
export interface InlinePosition {
|
|
51
|
+
block: BlockRef;
|
|
52
|
+
offset: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Inclusive range from `from` to `to` — both inline positions. Single-
|
|
56
|
+
* block ranges have `from.block.id === to.block.id`.
|
|
57
|
+
*
|
|
58
|
+
* For operations that span blocks, pass `opts.expect` with the middle
|
|
59
|
+
* blocks' expected versions — the Range itself only pins the endpoints.
|
|
60
|
+
*/
|
|
61
|
+
export interface Range {
|
|
62
|
+
from: InlinePosition;
|
|
63
|
+
to: InlinePosition;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The current cursor / selection state of the editor in model terms.
|
|
67
|
+
* `null` when nothing is selected (e.g. focus is outside the editor).
|
|
68
|
+
*/
|
|
69
|
+
export type Selection = null | {
|
|
70
|
+
kind: "caret";
|
|
71
|
+
at: InlinePosition;
|
|
72
|
+
} | {
|
|
73
|
+
kind: "range";
|
|
74
|
+
range: Range;
|
|
75
|
+
};
|
|
76
|
+
/** Result envelope returned by every mutating operation. */
|
|
77
|
+
export type EditResult<T = void> = {
|
|
78
|
+
ok: true;
|
|
79
|
+
value: T;
|
|
80
|
+
affected: BlockRef[];
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
error: EditError;
|
|
84
|
+
};
|
|
85
|
+
/** Why an edit didn't apply. Discriminated union — agents switch on `code`. */
|
|
86
|
+
export type EditError = OptimisticLockError | {
|
|
87
|
+
code: "invalid-position";
|
|
88
|
+
details: string;
|
|
89
|
+
} | {
|
|
90
|
+
code: "unknown-block";
|
|
91
|
+
blockId: string;
|
|
92
|
+
} | {
|
|
93
|
+
code: "range-empty";
|
|
94
|
+
details: string;
|
|
95
|
+
} | {
|
|
96
|
+
code: "range-out-of-order";
|
|
97
|
+
details: string;
|
|
98
|
+
} | {
|
|
99
|
+
code: "invalid-state";
|
|
100
|
+
details: string;
|
|
101
|
+
};
|
|
102
|
+
export interface OptimisticLockError {
|
|
103
|
+
code: "optimistic-lock";
|
|
104
|
+
/**
|
|
105
|
+
* Per-block conflict info. `actual` is `null` when the block has been
|
|
106
|
+
* deleted between read and write — distinguishable from a stale
|
|
107
|
+
* version on the same block.
|
|
108
|
+
*/
|
|
109
|
+
conflicts: Array<{
|
|
110
|
+
blockId: string;
|
|
111
|
+
expected: number;
|
|
112
|
+
actual: number | null;
|
|
113
|
+
}>;
|
|
114
|
+
}
|
|
115
|
+
/** Build an inline position inside `block` at the given character offset. */
|
|
116
|
+
export declare function inlineAt(block: BlockRef, offset: number): InlinePosition;
|
|
117
|
+
/** Construct a range from two positions. */
|
|
118
|
+
export declare function makeRange(from: InlinePosition, to: InlinePosition): Range;
|
|
119
|
+
/** Caret at a single position. */
|
|
120
|
+
export declare function caretAt(at: InlinePosition): Selection;
|
|
121
|
+
/** True if two inline positions are inside the same block (by id). */
|
|
122
|
+
export declare function sameBlock(a: InlinePosition, b: InlinePosition): boolean;
|
|
123
|
+
/** Zero-width range — `from` and `to` collapsed onto the same offset. */
|
|
124
|
+
export declare function isCollapsedRange(range: Range): boolean;
|
|
125
|
+
/** Caret selection OR collapsed range. */
|
|
126
|
+
export declare function isCaret(sel: Selection): boolean;
|
|
127
|
+
/** Build an `EditResult` for a successful operation. */
|
|
128
|
+
export declare function ok<T>(value: T, affected?: BlockRef[]): EditResult<T>;
|
|
129
|
+
/** Build an `EditResult` for a failed operation. */
|
|
130
|
+
export declare function fail(error: EditError): EditResult<never>;
|
|
131
|
+
/** Specialised builder for the common optimistic-lock failure case. */
|
|
132
|
+
export declare function lockConflict(conflicts: OptimisticLockError["conflicts"]): EditResult<never>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Block, HeaderFooterRef, InlineRun, NamedStyle, PageMargins, PageSize, Paragraph, ParagraphProperties, RunProperties, SectionProperties, SobreeDocument, Table, TextRun } from './types';
|
|
2
|
+
/** A new, blank document with an A4 portrait section and the standard styles. */
|
|
3
|
+
export declare function emptyDocument(): SobreeDocument;
|
|
4
|
+
export declare function defaultSection(): SectionProperties;
|
|
5
|
+
export declare function defaultPageSize(): PageSize;
|
|
6
|
+
export declare function defaultMargins(): PageMargins;
|
|
7
|
+
/** A paragraph with no runs and no properties beyond the defaults. */
|
|
8
|
+
export declare function paragraph(runs?: InlineRun[], properties?: ParagraphProperties): Paragraph;
|
|
9
|
+
/** Heading paragraph (`Heading{level}` style, level clamped to 1..6). */
|
|
10
|
+
export declare function heading(level: number, runs?: InlineRun[], extra?: ParagraphProperties): Paragraph;
|
|
11
|
+
/** Plain text run with optional formatting. */
|
|
12
|
+
export declare function text(value: string, properties?: RunProperties): TextRun;
|
|
13
|
+
/** Convenience: emphasised (bold + italic) text run. */
|
|
14
|
+
export declare function emphasis(value: string, properties?: RunProperties): TextRun;
|
|
15
|
+
export declare function strong(value: string, properties?: RunProperties): TextRun;
|
|
16
|
+
export declare function softBreak(): InlineRun;
|
|
17
|
+
export declare function pageBreak(): InlineRun;
|
|
18
|
+
/** Default Word styles every doc declares so headings render correctly.
|
|
19
|
+
*
|
|
20
|
+
* Carries WORD-HARDCODED-DEFAULT typography — i.e. what Word uses
|
|
21
|
+
* when a docx has bare/empty styles.xml entries. That means single
|
|
22
|
+
* line spacing (1.0×) and zero space-before / space-after on Normal.
|
|
23
|
+
* Headings keep their bold + size on `runDefaults` but DON'T add
|
|
24
|
+
* spacing-before/after either — the original docx tells the renderer
|
|
25
|
+
* via per-paragraph `spacing` properties when something needs to
|
|
26
|
+
* breathe.
|
|
27
|
+
*
|
|
28
|
+
* This is the load-bearing constraint: a docx round-trip must not
|
|
29
|
+
* silently add (or remove) vertical rhythm the original didn't ask
|
|
30
|
+
* for. Embedders that want a different baseline (e.g. markdown output
|
|
31
|
+
* with 8pt-after for visible paragraph separation) override the
|
|
32
|
+
* Normal style on the document they construct — see `parseMarkdown`.
|
|
33
|
+
*
|
|
34
|
+
* Heading scale steps down from a prominent H1 at 24pt to body-sized
|
|
35
|
+
* H6 at 11pt. */
|
|
36
|
+
export declare function defaultStyles(): NamedStyle[];
|
|
37
|
+
/** Push a block onto the document body. Mutates `doc` and returns it. */
|
|
38
|
+
export declare function appendBlock(doc: SobreeDocument, block: Block): SobreeDocument;
|
|
39
|
+
/** Allocate a new header/footer reference id. Pure helper. */
|
|
40
|
+
export declare function makeHeaderFooterRef(type: "default" | "first" | "even", partId: string): HeaderFooterRef;
|
|
41
|
+
export declare function isParagraph(block: Block): block is Paragraph;
|
|
42
|
+
export declare function isTable(block: Block): block is Table;
|