@p10node/office-engine 0.1.0 → 0.2.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/README.md +1 -0
- package/dist/{DocViewer-Cv4LaMB_.js → DocViewer-PJieqbIR.js} +3254 -2027
- package/dist/DocViewer-PJieqbIR.js.map +1 -0
- package/dist/assets/worker-B1gMpGFN.js.map +1 -0
- package/dist/{collab-BzN1ZI-z.js → collab-BvjXvfJ9.js} +529 -232
- package/dist/collab-BvjXvfJ9.js.map +1 -0
- package/dist/collab.js +1 -1
- package/dist/core.js +1 -1
- package/dist/{default-worker-B5CGBnic.js → default-worker-DfpaJUjf.js} +1053 -1030
- package/dist/default-worker-DfpaJUjf.js.map +1 -0
- package/dist/embed.js +13 -3
- package/dist/embed.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/office-engine.embed.js +76 -24
- package/dist/office-engine.worker.js +26 -6
- package/dist/types/collab/compact.d.ts +23 -0
- package/dist/types/collab/hub.d.ts +35 -4
- package/dist/types/collab/index.d.ts +9 -2
- package/dist/types/collab/protocol.d.ts +29 -2
- package/dist/types/collab/session.d.ts +5 -1
- package/dist/types/collab/store.d.ts +94 -0
- package/dist/types/core/convert.d.ts +29 -0
- package/dist/types/core/zipwrite.d.ts +3 -1
- package/dist/types/embed.d.ts +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/react/DocViewer.d.ts +18 -0
- package/dist/types/react/common/Dialog.d.ts +8 -0
- package/dist/types/react/common/Slots.d.ts +20 -0
- package/dist/types/react/docx/DocxToolbar.d.ts +2 -1
- package/dist/types/react/edit/Toolbar.d.ts +4 -0
- package/dist/types/react/file/DetailsDialog.d.ts +7 -0
- package/dist/types/react/file/FileMenu.d.ts +48 -0
- package/dist/types/react/file/importFile.d.ts +10 -0
- package/dist/types/react/file/print.d.ts +16 -0
- package/dist/types/react/file/text.d.ts +10 -0
- package/dist/types/react/xlsx/XlsxView.d.ts +3 -1
- package/dist/types/react/xlsx/fit.d.ts +4 -0
- package/dist/types/react/xlsx/print.d.ts +17 -0
- package/package.json +2 -1
- package/dist/DocViewer-Cv4LaMB_.js.map +0 -1
- package/dist/assets/worker-CJfZ05I7.js.map +0 -1
- package/dist/collab-BzN1ZI-z.js.map +0 -1
- package/dist/default-worker-B5CGBnic.js.map +0 -1
|
@@ -344,7 +344,7 @@ async function ce(e, t, n = "application/zip") {
|
|
|
344
344
|
let n = await ae(oe(t)), { time: r, date: i } = se();
|
|
345
345
|
l(e, 8, n.crc, n.csize, n.size, r, i, n.chunks);
|
|
346
346
|
};
|
|
347
|
-
for (let t of e
|
|
347
|
+
for (let t of e?.list() ?? []) {
|
|
348
348
|
let n = E(t.name), r = c.get(n);
|
|
349
349
|
if (r) {
|
|
350
350
|
c.delete(n), r.data !== null && await u(t.name, r.data);
|
|
@@ -6222,7 +6222,7 @@ var Wa = (e, t) => `${e}:${t}`, Ga = class {
|
|
|
6222
6222
|
let a = this.sheetIndex(n, this.cur);
|
|
6223
6223
|
if (a < 0) return new I("#REF!");
|
|
6224
6224
|
let o = Wa(a, r * Q + i);
|
|
6225
|
-
return e.has(o) && this.compute(o, e, t), this.value(a, r, i);
|
|
6225
|
+
return t.has(o) ? new I("#REF!") : (e.has(o) && this.compute(o, e, t), this.value(a, r, i));
|
|
6226
6226
|
},
|
|
6227
6227
|
bounds: (e) => {
|
|
6228
6228
|
let t = this.wb.sheetsData.get(this.sheetIndex(e, this.cur));
|
|
@@ -7479,6 +7479,7 @@ var zo = class {
|
|
|
7479
7479
|
root;
|
|
7480
7480
|
P;
|
|
7481
7481
|
cache = /* @__PURE__ */ new Map();
|
|
7482
|
+
found = /* @__PURE__ */ new Map();
|
|
7482
7483
|
changed = !1;
|
|
7483
7484
|
constructor(e) {
|
|
7484
7485
|
this.nodes = t(e), this.root = a(this.nodes), this.P = i(this.root.q);
|
|
@@ -7493,6 +7494,23 @@ var zo = class {
|
|
|
7493
7494
|
let r = o(n).length;
|
|
7494
7495
|
return u(n, "count", String(r)), this.changed = !0, r - 1;
|
|
7495
7496
|
}
|
|
7497
|
+
lookup(e) {
|
|
7498
|
+
let t = this.found.get(e);
|
|
7499
|
+
if (!t) {
|
|
7500
|
+
let r = /* @__PURE__ */ new Map();
|
|
7501
|
+
o(s(this.root, e)).forEach((e, t) => {
|
|
7502
|
+
let i = n([e]);
|
|
7503
|
+
r.has(i) || r.set(i, t);
|
|
7504
|
+
}), this.found.set(e, t = r);
|
|
7505
|
+
}
|
|
7506
|
+
return t;
|
|
7507
|
+
}
|
|
7508
|
+
intern(e, t) {
|
|
7509
|
+
let r = n([t]), i = this.lookup(e), a = i.get(r);
|
|
7510
|
+
if (a !== void 0) return a;
|
|
7511
|
+
let o = this.append(e, t);
|
|
7512
|
+
return i.set(r, o), o;
|
|
7513
|
+
}
|
|
7496
7514
|
numFmtId(e) {
|
|
7497
7515
|
for (let [t, n] of Object.entries(qa)) if (n === e) return Number(t);
|
|
7498
7516
|
let t = s(this.root, "numFmts"), n = 163;
|
|
@@ -7525,13 +7543,13 @@ var zo = class {
|
|
|
7525
7543
|
"font"
|
|
7526
7544
|
].some((e) => e in n)) {
|
|
7527
7545
|
let e = o(this.section("fonts"), "font"), t = f(e[Number(l(p, "fontId") ?? 0)] ?? e[0] ?? d(a + "font")), r = (e, n) => m(t, n, e, Mo);
|
|
7528
|
-
n.b !== void 0 && r("b", n.b ? d(a + "b") : void 0), n.i !== void 0 && r("i", n.i ? d(a + "i") : void 0), n.strike !== void 0 && r("strike", n.strike ? d(a + "strike") : void 0), n.u !== void 0 && r("u", n.u ? d(a + "u") : void 0), n.sz !== void 0 && r("sz", d(a + "sz", { val: String(n.sz) })), "color" in n && r("color", n.color ? d(a + "color", { rgb: "FF" + n.color.replace("#", "").toUpperCase() }) : void 0), n.font && (r("name", d(a + "name", { val: n.font })), r("scheme", void 0)), u(p, "fontId", String(this.
|
|
7546
|
+
n.b !== void 0 && r("b", n.b ? d(a + "b") : void 0), n.i !== void 0 && r("i", n.i ? d(a + "i") : void 0), n.strike !== void 0 && r("strike", n.strike ? d(a + "strike") : void 0), n.u !== void 0 && r("u", n.u ? d(a + "u") : void 0), n.sz !== void 0 && r("sz", d(a + "sz", { val: String(n.sz) })), "color" in n && r("color", n.color ? d(a + "color", { rgb: "FF" + n.color.replace("#", "").toUpperCase() }) : void 0), n.font && (r("name", d(a + "name", { val: n.font })), r("scheme", void 0)), u(p, "fontId", String(this.intern("fonts", t))), u(p, "applyFont", "1");
|
|
7529
7547
|
}
|
|
7530
7548
|
if ("fill" in n) {
|
|
7531
7549
|
if (!n.fill) u(p, "fillId", "0");
|
|
7532
7550
|
else {
|
|
7533
7551
|
let e = t(`<${a}fill><${a}patternFill patternType="solid"><${a}fgColor rgb="FF${n.fill.replace("#", "").toUpperCase()}"/><${a}bgColor indexed="64"/></${a}patternFill></${a}fill>`)[0];
|
|
7534
|
-
u(p, "fillId", String(this.
|
|
7552
|
+
u(p, "fillId", String(this.intern("fills", e)));
|
|
7535
7553
|
}
|
|
7536
7554
|
u(p, "applyFill", "1");
|
|
7537
7555
|
}
|
|
@@ -7553,7 +7571,7 @@ var zo = class {
|
|
|
7553
7571
|
"top",
|
|
7554
7572
|
"bottom"
|
|
7555
7573
|
]) t in e && m(r, e[t] ? d(a + t, { style: n.borderStyle ?? "thin" }, [d(a + "color", { rgb: "FF" + i })]) : d(a + t), t, c);
|
|
7556
|
-
s(r, "diagonal") || m(r, d(a + "diagonal"), "diagonal", c), u(p, "borderId", String(this.
|
|
7574
|
+
s(r, "diagonal") || m(r, d(a + "diagonal"), "diagonal", c), u(p, "borderId", String(this.intern("borders", r))), u(p, "applyBorder", "1");
|
|
7557
7575
|
}
|
|
7558
7576
|
if ("align" in n || "wrap" in n || "valign" in n || "rotate" in n) {
|
|
7559
7577
|
let e = f(s(p, "alignment") ?? d(a + "alignment"));
|
|
@@ -7568,7 +7586,7 @@ var zo = class {
|
|
|
7568
7586
|
]), u(p, "applyAlignment", "1");
|
|
7569
7587
|
}
|
|
7570
7588
|
n.numFmt !== void 0 && (u(p, "numFmtId", String(this.numFmtId(n.numFmt))), u(p, "applyNumberFormat", "1"));
|
|
7571
|
-
let h = this.
|
|
7589
|
+
let h = this.intern("cellXfs", p);
|
|
7572
7590
|
return this.cache.set(r, h), h;
|
|
7573
7591
|
}
|
|
7574
7592
|
xml() {
|
|
@@ -7591,6 +7609,8 @@ var zo = class {
|
|
|
7591
7609
|
if (!e || r.length <= n) continue;
|
|
7592
7610
|
let i = new Set(r.slice(n));
|
|
7593
7611
|
e.c = e.c.filter((e) => !i.has(e)), u(e, "count", String(n));
|
|
7612
|
+
let a = this.found.get(t);
|
|
7613
|
+
if (a) for (let [e, t] of a) t >= n && a.delete(e);
|
|
7594
7614
|
}
|
|
7595
7615
|
for (let [t, n] of this.cache) n >= e.cellXfs && this.cache.delete(t);
|
|
7596
7616
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side snapshots: replays shared history onto a document headlessly, exactly the way a
|
|
3
|
+
* collaborator applies it, so the result can be saved as the room's new base file.
|
|
4
|
+
*
|
|
5
|
+
* const client = await DocumentClient.open(snapshotFile, { worker: false })
|
|
6
|
+
* const snap = await compact(client, { from: snapshotMeta, batches })
|
|
7
|
+
* const file = await client.call('save') // store it, then hub.snapshot(room, snap)
|
|
8
|
+
*/
|
|
9
|
+
import type { DocumentClient } from '../core/worker/client';
|
|
10
|
+
import type { Batch, Snapshot } from './protocol';
|
|
11
|
+
export interface CompactOptions {
|
|
12
|
+
/** the snapshot `client` was opened from (default: the original upload, seq 0) */
|
|
13
|
+
from?: Snapshot;
|
|
14
|
+
/** batches to apply: every batch after `from.seq`, in order (older ones are ignored) */
|
|
15
|
+
batches: Batch[];
|
|
16
|
+
/** canonical batches kept as the new snapshot's context (default 1000) */
|
|
17
|
+
window?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Applies `batches` to `client` (opened on the file of snapshot `from`) and returns the snapshot it
|
|
21
|
+
* now holds. Save the document afterwards with `client.call('save')`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function compact(client: DocumentClient, opts: CompactOptions): Promise<Snapshot>;
|
|
@@ -5,26 +5,46 @@
|
|
|
5
5
|
* const conn = hub.connect((msg) => socket.send(encode(msg)))
|
|
6
6
|
* socket.onmessage = (e) => conn.receive(decode(e.data))
|
|
7
7
|
* socket.onclose = () => conn.close()
|
|
8
|
+
*
|
|
9
|
+
* Snapshots (see `compact`) bound the history: register one with `hub.snapshot()` and new clients
|
|
10
|
+
* open its file and only replay the batches after it, while the hub drops older batches.
|
|
8
11
|
*/
|
|
9
|
-
import { type Batch, type ClientMsg, type HubMsg, type PeerInfo } from './protocol';
|
|
12
|
+
import { type Batch, type ClientMsg, type Entry, type HubMsg, type PeerInfo, type Snapshot } from './protocol';
|
|
10
13
|
export interface RoomState {
|
|
11
14
|
id: string;
|
|
12
15
|
seq: number;
|
|
16
|
+
/** every batch with seq > `start`, in order */
|
|
13
17
|
log: Batch[];
|
|
18
|
+
/** batches older than this were dropped (after a snapshot) */
|
|
19
|
+
start: number;
|
|
14
20
|
ids: Set<string>;
|
|
15
21
|
peers: Map<number, {
|
|
16
22
|
info: PeerInfo;
|
|
17
23
|
send: (m: HubMsg) => void;
|
|
18
24
|
}>;
|
|
19
25
|
nextSite: number;
|
|
26
|
+
/** snapshots clients can join from: seq → context */
|
|
27
|
+
snaps: Map<number, Entry[]>;
|
|
28
|
+
}
|
|
29
|
+
/** A persisted room: its latest snapshot and the batches after it (and possibly some before). */
|
|
30
|
+
export interface RoomLog {
|
|
31
|
+
log: Batch[];
|
|
32
|
+
snapshot?: Snapshot & {
|
|
33
|
+
sites?: number;
|
|
34
|
+
};
|
|
20
35
|
}
|
|
36
|
+
type Loaded = Batch[] | RoomLog | undefined;
|
|
21
37
|
export interface HubOptions {
|
|
22
38
|
/** called for every sequenced batch (persist it to rebuild rooms after a restart) */
|
|
23
39
|
onBatch?: (room: string, batch: Batch) => void;
|
|
24
|
-
/** rebuilds a room
|
|
25
|
-
loadLog?: (room: string) =>
|
|
40
|
+
/** rebuilds a room (e.g. from storage) the first time it is used */
|
|
41
|
+
loadLog?: (room: string) => Loaded | Promise<Loaded>;
|
|
26
42
|
/** called when the last peer left a room */
|
|
27
43
|
onIdle?: (room: string) => void;
|
|
44
|
+
/** batches kept below the latest snapshot for clients reconnecting from older states (default 1000) */
|
|
45
|
+
retain?: number;
|
|
46
|
+
/** most recent snapshots clients can still join from (default 3) */
|
|
47
|
+
keep?: number;
|
|
28
48
|
}
|
|
29
49
|
export interface HubConnection {
|
|
30
50
|
receive(msg: ClientMsg): void;
|
|
@@ -33,9 +53,20 @@ export interface HubConnection {
|
|
|
33
53
|
export declare class CollabHub {
|
|
34
54
|
private opts;
|
|
35
55
|
readonly rooms: Map<string, RoomState>;
|
|
56
|
+
private loading;
|
|
36
57
|
constructor(opts?: HubOptions);
|
|
37
|
-
room
|
|
58
|
+
/** the room, loaded through `loadLog` the first time */
|
|
59
|
+
room(id: string): Promise<RoomState>;
|
|
38
60
|
/** forgets a room (its log and peers) — e.g. when a new base file is uploaded */
|
|
39
61
|
reset(id: string): void;
|
|
62
|
+
/** drops an idle room from memory (it is loaded again on the next join); false while peers are connected */
|
|
63
|
+
unload(id: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Registers a snapshot of a loaded room (`compact` output, its file stored where clients can open
|
|
66
|
+
* it): clients may join from it, and batches older than `retain` below it are dropped.
|
|
67
|
+
* Returns false when the snapshot doesn't fit the room (e.g. it was reset meanwhile).
|
|
68
|
+
*/
|
|
69
|
+
snapshot(id: string, snap: Snapshot): boolean;
|
|
40
70
|
connect(send: (m: HubMsg) => void): HubConnection;
|
|
41
71
|
}
|
|
72
|
+
export {};
|
|
@@ -19,6 +19,11 @@ export interface CollabOptions {
|
|
|
19
19
|
/** shared document id */
|
|
20
20
|
room: string;
|
|
21
21
|
user: UserInfo;
|
|
22
|
+
/**
|
|
23
|
+
* Sequence number of the snapshot `src` was opened from (see `compact`); default 0, the original
|
|
24
|
+
* upload. The hub then only sends the batches after it.
|
|
25
|
+
*/
|
|
26
|
+
base?: number;
|
|
22
27
|
}
|
|
23
28
|
export interface Collab {
|
|
24
29
|
readonly session: CollabSession<unknown>;
|
|
@@ -36,8 +41,10 @@ export interface Collab {
|
|
|
36
41
|
close(): void;
|
|
37
42
|
}
|
|
38
43
|
export declare function startCollab(client: DocumentClient, opts: CollabOptions): Collab;
|
|
39
|
-
export { CollabHub, type HubOptions, type HubConnection } from './hub';
|
|
44
|
+
export { CollabHub, type HubOptions, type HubConnection, type RoomLog, type RoomState } from './hub';
|
|
45
|
+
export { compact, type CompactOptions } from './compact';
|
|
46
|
+
export { RoomStore, MemoryBlobStore, type BlobStore, type RoomHead, type RoomStoreOptions, type Version } from './store';
|
|
40
47
|
export { CollabSession, type Adapter, type CollabStatus } from './session';
|
|
41
48
|
export { MemoryTransport, WebSocketTransport, type Transport, type TransportStatus } from './transport';
|
|
42
|
-
export type { Batch, ClientMsg, HubMsg, PeerInfo, UserInfo } from './protocol';
|
|
49
|
+
export type { Batch, ClientMsg, Entry, HubMsg, PeerInfo, Snapshot, UserInfo } from './protocol';
|
|
43
50
|
export { encode, decode } from './codec';
|
|
@@ -28,15 +28,38 @@ export interface Batch<Op = unknown> {
|
|
|
28
28
|
/** client generated id (dedupes resends after reconnects) */
|
|
29
29
|
id: string;
|
|
30
30
|
ops: Op[];
|
|
31
|
+
/** when the hub sequenced it (ms since epoch) */
|
|
32
|
+
time?: number;
|
|
33
|
+
/** author: user id, else name */
|
|
34
|
+
user?: string;
|
|
35
|
+
}
|
|
36
|
+
/** A batch the way every client applies it: its ops transformed against the concurrent batches sequenced before it. */
|
|
37
|
+
export interface Entry<Op = unknown> {
|
|
38
|
+
seq: number;
|
|
39
|
+
site: number;
|
|
40
|
+
ops: Op[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The document materialized at `seq` (see `compact`): a file containing every batch up to `seq`,
|
|
44
|
+
* plus the canonical batches right before it (`ctx`) that clients joining from that file need to
|
|
45
|
+
* merge batches still based on older states.
|
|
46
|
+
*/
|
|
47
|
+
export interface Snapshot<Op = unknown> {
|
|
48
|
+
seq: number;
|
|
49
|
+
ctx: Entry<Op>[];
|
|
31
50
|
}
|
|
32
51
|
export type ClientMsg =
|
|
33
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* join a room; `have`/`site` on reconnect. `base`: on the first join, the snapshot the client
|
|
54
|
+
* opened (default 0, the original upload)
|
|
55
|
+
*/
|
|
34
56
|
{
|
|
35
57
|
t: 'hello';
|
|
36
58
|
room: string;
|
|
37
59
|
user: UserInfo;
|
|
38
60
|
have?: number;
|
|
39
61
|
site?: number;
|
|
62
|
+
base?: number;
|
|
40
63
|
} | {
|
|
41
64
|
t: 'ops';
|
|
42
65
|
id: string;
|
|
@@ -47,7 +70,10 @@ export type ClientMsg =
|
|
|
47
70
|
data: unknown;
|
|
48
71
|
};
|
|
49
72
|
export type HubMsg =
|
|
50
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* `log` holds every batch with seq > `from`; `ctx` the joined snapshot's context (first join from a
|
|
75
|
+
* snapshot); `reset` when the hub can't continue from the client's state (history lost or dropped)
|
|
76
|
+
*/
|
|
51
77
|
{
|
|
52
78
|
t: 'welcome';
|
|
53
79
|
site: number;
|
|
@@ -56,6 +82,7 @@ export type HubMsg =
|
|
|
56
82
|
log: Batch[];
|
|
57
83
|
peers: PeerInfo[];
|
|
58
84
|
reset?: boolean;
|
|
85
|
+
ctx?: Entry[];
|
|
59
86
|
} | {
|
|
60
87
|
t: 'batch';
|
|
61
88
|
b: Batch;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* to rebase, like deleting a sheet) are not applied locally until the hub has
|
|
15
15
|
* sequenced them.
|
|
16
16
|
*/
|
|
17
|
-
import type { PeerInfo, UserInfo } from './protocol';
|
|
17
|
+
import type { Entry, PeerInfo, UserInfo } from './protocol';
|
|
18
18
|
import type { Transport } from './transport';
|
|
19
19
|
export interface ApplyResult {
|
|
20
20
|
/** whatever `revert` needs to undo the op exactly (local ops only) */
|
|
@@ -60,6 +60,8 @@ export interface SessionOptions {
|
|
|
60
60
|
user: UserInfo;
|
|
61
61
|
/** canonical batches kept to transform late batches (default 5000) */
|
|
62
62
|
logWindow?: number;
|
|
63
|
+
/** sequence number the opened file already contains: a snapshot (default 0, the original upload) */
|
|
64
|
+
base?: number;
|
|
63
65
|
}
|
|
64
66
|
export declare class CollabSession<Op = unknown> {
|
|
65
67
|
private transport;
|
|
@@ -94,6 +96,8 @@ export declare class CollabSession<Op = unknown> {
|
|
|
94
96
|
push(op: Op, undo: unknown): void;
|
|
95
97
|
setPresence(data: unknown): void;
|
|
96
98
|
on<K extends keyof SessionEvents>(event: K, fn: (d: SessionEvents[K]) => void): () => void;
|
|
99
|
+
/** the last `limit` batches as applied here (the context of a snapshot taken at `rev`) */
|
|
100
|
+
context(limit?: number): Entry<Op>[];
|
|
97
101
|
/** resolves once everything queued so far was processed */
|
|
98
102
|
idle(): Promise<void>;
|
|
99
103
|
close(): void;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { RoomLog } from './hub';
|
|
2
|
+
import type { Batch, Snapshot } from './protocol';
|
|
3
|
+
/** Minimal object storage. Keys use `/` separators. */
|
|
4
|
+
export interface BlobStore {
|
|
5
|
+
/** null when missing */
|
|
6
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
7
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
8
|
+
/** every key starting with `prefix` (any order) */
|
|
9
|
+
list(prefix: string): Promise<string[]>;
|
|
10
|
+
delete(key: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare class MemoryBlobStore implements BlobStore {
|
|
13
|
+
readonly data: Map<string, Uint8Array<ArrayBufferLike>>;
|
|
14
|
+
get(key: string): Promise<Uint8Array<ArrayBufferLike> | null>;
|
|
15
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
16
|
+
list(prefix: string): Promise<string[]>;
|
|
17
|
+
delete(key: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface RoomHead {
|
|
20
|
+
/** changes with every upload: stale writes of an older upload are ignored */
|
|
21
|
+
epoch: string;
|
|
22
|
+
/** latest snapshot */
|
|
23
|
+
seq: number;
|
|
24
|
+
size: number;
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
time: number;
|
|
28
|
+
}
|
|
29
|
+
/** a stored snapshot */
|
|
30
|
+
export interface Version {
|
|
31
|
+
seq: number;
|
|
32
|
+
size: number;
|
|
33
|
+
time: number;
|
|
34
|
+
}
|
|
35
|
+
export interface RoomStoreOptions {
|
|
36
|
+
/** key prefix, e.g. `collab/` */
|
|
37
|
+
prefix?: string;
|
|
38
|
+
/** batches are buffered and written as one segment at most this often (ms, default 250) */
|
|
39
|
+
flushMs?: number;
|
|
40
|
+
/** snapshots kept per room (default: all — the version history); older ones and their batches are deleted */
|
|
41
|
+
keep?: number;
|
|
42
|
+
/** batches below the latest snapshot loaded for clients reconnecting from older states (default 1000) */
|
|
43
|
+
retain?: number;
|
|
44
|
+
/** a write failed (it is retried) */
|
|
45
|
+
onError?: (error: Error, room: string) => void;
|
|
46
|
+
}
|
|
47
|
+
type SnapMeta = Version & {
|
|
48
|
+
sites?: number;
|
|
49
|
+
};
|
|
50
|
+
export declare class RoomStore {
|
|
51
|
+
readonly blobs: BlobStore;
|
|
52
|
+
private opts;
|
|
53
|
+
private heads;
|
|
54
|
+
private buffers;
|
|
55
|
+
private queues;
|
|
56
|
+
constructor(blobs: BlobStore, opts?: RoomStoreOptions);
|
|
57
|
+
private dir;
|
|
58
|
+
private epochDir;
|
|
59
|
+
/** runs `fn` after every earlier operation on `room` */
|
|
60
|
+
private run;
|
|
61
|
+
private readHead;
|
|
62
|
+
/** sets a room's document (snapshot 0 of a new epoch) and forgets its history */
|
|
63
|
+
create(room: string, file: Uint8Array, info: {
|
|
64
|
+
name: string;
|
|
65
|
+
type: string;
|
|
66
|
+
}): Promise<RoomHead>;
|
|
67
|
+
/** latest snapshot of the room (null: no such room) */
|
|
68
|
+
head(room: string): Promise<RoomHead | null>;
|
|
69
|
+
/** the document of snapshot `seq` */
|
|
70
|
+
file(room: string, seq: number, epoch?: string): Promise<Uint8Array | null>;
|
|
71
|
+
/** snapshot `seq` with its merge context */
|
|
72
|
+
snapshot(room: string, seq: number, epoch?: string): Promise<(Snapshot & SnapMeta) | null>;
|
|
73
|
+
/** stored snapshots, oldest first */
|
|
74
|
+
versions(room: string, epoch?: string): Promise<Version[]>;
|
|
75
|
+
/** stored batches with `after` < seq ≤ `upTo` (flushed ones only), in order */
|
|
76
|
+
batches(room: string, after: number, upTo?: number, epoch?: string): Promise<Batch[]>;
|
|
77
|
+
/** hub `onBatch`: buffers the batch, written with the next segment */
|
|
78
|
+
append(room: string, batch: Batch): void;
|
|
79
|
+
/** writes buffered batches now (all rooms when `room` is omitted) */
|
|
80
|
+
flush(room?: string): Promise<void>;
|
|
81
|
+
private flushRoom;
|
|
82
|
+
/** hub `loadLog`: the latest snapshot and the batches after it (plus `retain` before it) */
|
|
83
|
+
load(room: string): Promise<RoomLog | undefined>;
|
|
84
|
+
/**
|
|
85
|
+
* Stores a snapshot taken from `base` (the head it was compacted from) and makes it the latest.
|
|
86
|
+
* Returns the new head, or null when the room changed meanwhile (new upload, newer snapshot).
|
|
87
|
+
*/
|
|
88
|
+
save(room: string, base: RoomHead, snap: Snapshot & {
|
|
89
|
+
sites?: number;
|
|
90
|
+
}, file: Uint8Array): Promise<RoomHead | null>;
|
|
91
|
+
/** drops snapshots beyond `keep` and the segments no remaining snapshot needs */
|
|
92
|
+
private prune;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RunSet } from './docx/types';
|
|
2
|
+
/** Separator of a delimited text file: tab, semicolon (European CSV) or comma. */
|
|
3
|
+
export declare function detectSeparator(text: string): string;
|
|
4
|
+
/** RFC 4180 parser (quoted fields, escaped quotes, CRLF / LF, newlines inside quotes). */
|
|
5
|
+
export declare function parseCsv(text: string, sep?: string): string[][];
|
|
6
|
+
/**
|
|
7
|
+
* Builds a one-sheet workbook from rows of text.
|
|
8
|
+
* `decimalComma`: "12,5" is a number (semicolon-separated files from comma-decimal locales).
|
|
9
|
+
*/
|
|
10
|
+
export declare function rowsToXlsx(rows: string[][], sheetName?: string, decimalComma?: boolean): Promise<Blob>;
|
|
11
|
+
export interface ImportRun {
|
|
12
|
+
text: string;
|
|
13
|
+
set?: RunSet;
|
|
14
|
+
href?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ImportPara {
|
|
17
|
+
runs: ImportRun[];
|
|
18
|
+
/** heading level 1-6 */
|
|
19
|
+
heading?: number;
|
|
20
|
+
list?: 'bullet' | 'number';
|
|
21
|
+
}
|
|
22
|
+
/** One paragraph per line. */
|
|
23
|
+
export declare function textToParas(text: string): ImportPara[];
|
|
24
|
+
/** Small CommonMark subset → HTML: headings, paragraphs, lists, quotes, code blocks, rules, tables. */
|
|
25
|
+
export declare function markdownToHtml(md: string): string;
|
|
26
|
+
/** Builds a document (Letter, 1" margins, Calibri 11) from paragraphs of formatted runs. */
|
|
27
|
+
export declare function parasToDocx(paras: ImportPara[]): Promise<Blob>;
|
|
28
|
+
/** One empty title slide, 16:9, Office theme; layouts: title, title and content, blank. */
|
|
29
|
+
export declare function blankPptx(): Promise<Blob>;
|
|
@@ -6,4 +6,6 @@ export type PartData = Uint8Array | string | (() => ReadableStream<Uint8Array>);
|
|
|
6
6
|
* zero-copy when the source is a Blob); changed/added entries are deflated.
|
|
7
7
|
* `changes`: path → new data, or null to delete.
|
|
8
8
|
*/
|
|
9
|
-
export declare function rebuildZip(zip: ZipArchive, changes: Map<string, PartData | null>, type?: string): Promise<Blob>;
|
|
9
|
+
export declare function rebuildZip(zip: ZipArchive | null, changes: Map<string, PartData | null>, type?: string): Promise<Blob>;
|
|
10
|
+
/** Writes a new archive (all entries deflated). */
|
|
11
|
+
export declare function writeZip(entries: Map<string, PartData>, type?: string): Promise<Blob>;
|
package/dist/types/embed.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export declare function mount(el: HTMLElement, options: MountOptions): MountedVi
|
|
|
10
10
|
/**
|
|
11
11
|
* `<office-viewer src="/file.xlsx" style="height:600px"></office-viewer>`
|
|
12
12
|
* Rendered inside a shadow root so host page CSS cannot leak in.
|
|
13
|
-
* Attributes: src, name, editable, toolbar
|
|
14
|
-
* real-time collaboration: collab (hub WebSocket URL), room, user (display name).
|
|
13
|
+
* Attributes: src, name, editable, toolbar / thumbnails / save / download / file-menu / open ("false" to hide);
|
|
14
|
+
* real-time collaboration: collab (hub WebSocket URL), room, user (display name), base (snapshot seq `src` was opened at).
|
|
15
15
|
* Property: `file` (File/Blob/ArrayBuffer) for local data.
|
|
16
16
|
*/
|
|
17
17
|
export declare class OfficeViewerElement extends HTMLElement {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import './core/default-worker';
|
|
2
2
|
export { DocViewer, type DocViewerProps } from './react/DocViewer';
|
|
3
|
+
export type { FileMenuItem } from './react/file/FileMenu';
|
|
3
4
|
export { DocxView, type DocxViewProps } from './react/docx/DocxView';
|
|
4
5
|
export { XlsxView, type XlsxViewProps } from './react/xlsx/XlsxView';
|
|
5
6
|
export { PptxView, type PptxViewProps } from './react/pptx/PptxView';
|
|
@@ -2,6 +2,7 @@ import { type CSSProperties, type ReactNode } from 'react';
|
|
|
2
2
|
import type { DocSource } from '../core/source';
|
|
3
3
|
import type { DocumentClient, OpenOptions } from '../core/worker/client';
|
|
4
4
|
import type { Zoom } from './common/ZoomControl';
|
|
5
|
+
import { type FileMenuItem } from './file/FileMenu';
|
|
5
6
|
import type { CollabOptions } from '../collab';
|
|
6
7
|
export interface DocViewerProps {
|
|
7
8
|
/** File, Blob, ArrayBuffer, Uint8Array or URL (HTTP Range requests are used when the server supports them). */
|
|
@@ -30,6 +31,23 @@ export interface DocViewerProps {
|
|
|
30
31
|
editable?: boolean;
|
|
31
32
|
/** Show the Edit/View toggle. Default true. */
|
|
32
33
|
allowEdit?: boolean;
|
|
34
|
+
/** Show the Save/Download button and enable Ctrl+S. Default true. */
|
|
35
|
+
allowSave?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Allow downloading files to the user's device (the save fallback without `onSave`, "Download image").
|
|
38
|
+
* With `onSave`, saving still works when this is false. Default true.
|
|
39
|
+
*/
|
|
40
|
+
allowDownload?: boolean;
|
|
41
|
+
/** Show the File menu (new, open, save, rename, download as PDF / HTML / text…, details, print) and its shortcuts. Default true. */
|
|
42
|
+
fileMenu?: boolean;
|
|
43
|
+
/** File → New / Open: replace the document with a blank one or a local file (Office, CSV, text, Markdown, HTML). Default true. */
|
|
44
|
+
allowOpen?: boolean;
|
|
45
|
+
/** Extra File menu entries (Share, Version history…), shown before Close. */
|
|
46
|
+
fileMenuItems?: FileMenuItem[];
|
|
47
|
+
/** File → Rename: called with the new name (extension included). Rename is shown when `fileMenu` is on. */
|
|
48
|
+
onRename?: (name: string) => void;
|
|
49
|
+
/** Adds File → Close. */
|
|
50
|
+
onClose?: () => void;
|
|
33
51
|
/** Receives the saved file instead of downloading it. */
|
|
34
52
|
onSave?: (file: Blob, name: string) => void | Promise<void>;
|
|
35
53
|
/** Called when the document gets / loses unsaved changes. */
|
|
@@ -20,6 +20,14 @@ export declare function PromptDialog({ spec, onClose }: {
|
|
|
20
20
|
spec: PromptSpec;
|
|
21
21
|
onClose: () => void;
|
|
22
22
|
}): import("react").JSX.Element;
|
|
23
|
+
/** Yes / no question (no window.confirm: works in sandboxed / embedded pages). */
|
|
24
|
+
export declare function ConfirmDialog(props: {
|
|
25
|
+
title: string;
|
|
26
|
+
message: ReactNode;
|
|
27
|
+
ok: string;
|
|
28
|
+
onOk: () => void;
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
}): import("react").JSX.Element;
|
|
23
31
|
/** Short-lived message at the bottom of the viewer. */
|
|
24
32
|
export declare function Toast({ text, onDone }: {
|
|
25
33
|
text: string;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { DocumentClient } from '../../core/worker/client';
|
|
3
|
+
import type { Printable } from '../file/print';
|
|
4
|
+
/** What the active view offers to the File menu. */
|
|
5
|
+
export interface FileOps {
|
|
6
|
+
/** all pages, for PDF / print and HTML */
|
|
7
|
+
printable(): Promise<Printable>;
|
|
8
|
+
/** text formats: 'txt' / 'md' (documents, presentations), 'csv' / 'tsv' (current sheet) */
|
|
9
|
+
text(format: string): Promise<string>;
|
|
10
|
+
/** File → Document details: [label, value] rows */
|
|
11
|
+
stats(): [string, string][];
|
|
12
|
+
/** File → Import (spreadsheets): rows into a new sheet */
|
|
13
|
+
importTable?(rows: string[][], name: string): Promise<void>;
|
|
14
|
+
}
|
|
3
15
|
export interface ViewerApi {
|
|
4
16
|
/** editor bar mount point (null when the viewer toolbar is hidden) */
|
|
5
17
|
bar: HTMLElement | null;
|
|
@@ -8,10 +20,18 @@ export interface ViewerApi {
|
|
|
8
20
|
setDirty: (dirty: boolean) => void;
|
|
9
21
|
/** build + hand off the edited file */
|
|
10
22
|
save: () => Promise<void>;
|
|
23
|
+
/** whether files may be downloaded to the user's device */
|
|
24
|
+
canDownload: boolean;
|
|
11
25
|
client: DocumentClient | null;
|
|
26
|
+
/** set by the mounted view (see useFileOps) */
|
|
27
|
+
fileOps: {
|
|
28
|
+
current: FileOps | null;
|
|
29
|
+
};
|
|
12
30
|
}
|
|
13
31
|
export declare const ViewerContext: import("react").Context<ViewerApi>;
|
|
14
32
|
export declare const useViewer: () => ViewerApi;
|
|
33
|
+
/** Publishes the view's file operations (latest closure each render) to the File menu. */
|
|
34
|
+
export declare function useFileOps(ops: FileOps): void;
|
|
15
35
|
/** Renders the editor bar into the viewer's toolbar row. */
|
|
16
36
|
export declare function EditorBar({ children }: {
|
|
17
37
|
children: ReactNode;
|
|
@@ -4,7 +4,8 @@ export interface ImageActions {
|
|
|
4
4
|
original: () => void;
|
|
5
5
|
replace: () => void;
|
|
6
6
|
alt: () => void;
|
|
7
|
-
|
|
7
|
+
/** undefined when downloads are disabled */
|
|
8
|
+
download?: () => void;
|
|
8
9
|
}
|
|
9
10
|
export declare function DocxToolbar(props: {
|
|
10
11
|
editor: DocxEditor;
|
|
@@ -26,6 +26,8 @@ export interface DropOption<T> {
|
|
|
26
26
|
disabled?: boolean;
|
|
27
27
|
/** separator above this option */
|
|
28
28
|
sep?: boolean;
|
|
29
|
+
/** group heading above this option */
|
|
30
|
+
head?: string;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Styled dropdown (listbox). Never takes focus from the editor: mouse down is prevented and,
|
|
@@ -46,6 +48,8 @@ export declare function Dropdown<T extends string | number>(props: {
|
|
|
46
48
|
disabled?: boolean;
|
|
47
49
|
/** popup aligned to the trigger's right edge */
|
|
48
50
|
alignRight?: boolean;
|
|
51
|
+
/** extra class of the trigger button */
|
|
52
|
+
className?: string;
|
|
49
53
|
}): import("react").JSX.Element;
|
|
50
54
|
/** Value picker (fonts, styles, formats…). */
|
|
51
55
|
export declare function Select<T extends string | number>(props: {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DocKind } from '../../core/opc';
|
|
2
|
+
import type { FileOps } from '../common/Slots';
|
|
3
|
+
import { type DropOption } from '../edit/Toolbar';
|
|
4
|
+
export declare const KIND_LABEL: Record<DocKind, string>;
|
|
5
|
+
/** Host-provided File menu entry (Share, Version history…). */
|
|
6
|
+
export interface FileMenuItem {
|
|
7
|
+
label: string;
|
|
8
|
+
/** built-in icon name */
|
|
9
|
+
icon?: string;
|
|
10
|
+
/** shortcut / note on the right */
|
|
11
|
+
hint?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onClick: () => void;
|
|
14
|
+
}
|
|
15
|
+
export interface FileActionsOptions {
|
|
16
|
+
kind: DocKind | undefined;
|
|
17
|
+
/** file name without extension */
|
|
18
|
+
base: string;
|
|
19
|
+
/** New / Open (replace the document) */
|
|
20
|
+
canOpen: boolean;
|
|
21
|
+
/** Import into the document (spreadsheets) */
|
|
22
|
+
canImport: boolean;
|
|
23
|
+
canSave: boolean;
|
|
24
|
+
canDownload: boolean;
|
|
25
|
+
ops: {
|
|
26
|
+
current: FileOps | null;
|
|
27
|
+
};
|
|
28
|
+
create: (kind: DocKind) => void;
|
|
29
|
+
open: (file: File) => void;
|
|
30
|
+
save: () => Promise<void>;
|
|
31
|
+
/** the document in its own format */
|
|
32
|
+
downloadOriginal: () => Promise<void>;
|
|
33
|
+
rename?: () => void;
|
|
34
|
+
details?: () => void;
|
|
35
|
+
close?: () => void;
|
|
36
|
+
extra: FileMenuItem[];
|
|
37
|
+
notify: (message: string) => void;
|
|
38
|
+
}
|
|
39
|
+
export interface FileActions {
|
|
40
|
+
options: DropOption<string>[];
|
|
41
|
+
busy: boolean;
|
|
42
|
+
run: (action: string) => void;
|
|
43
|
+
}
|
|
44
|
+
/** File menu entries and their actions (also used by the Ctrl+O / Ctrl+P shortcuts). */
|
|
45
|
+
export declare function useFileActions(o: FileActionsOptions): FileActions;
|
|
46
|
+
export declare function FileMenu({ actions }: {
|
|
47
|
+
actions: FileActions;
|
|
48
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `accept` of the File → Open picker */
|
|
2
|
+
export declare const OPEN_ACCEPT: string;
|
|
3
|
+
export interface Imported {
|
|
4
|
+
src: Blob;
|
|
5
|
+
name: string;
|
|
6
|
+
/** built from another format: not saved as an Office file yet */
|
|
7
|
+
converted: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Office files open as they are; CSV / TSV become a workbook, text / Markdown / HTML a document. */
|
|
10
|
+
export declare function importFile(file: File): Promise<Imported>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** Content laid out for paper: PDF / print and standalone HTML. */
|
|
3
|
+
export interface Printable {
|
|
4
|
+
/** the pages (`.de-print-page` elements) or flowing content */
|
|
5
|
+
node: ReactNode;
|
|
6
|
+
/** CSS `@page` size: '612pt 792pt', 'landscape', 'auto' */
|
|
7
|
+
size: string;
|
|
8
|
+
/** CSS `@page` margin */
|
|
9
|
+
margin: string;
|
|
10
|
+
/** shown to the user (e.g. output was truncated) */
|
|
11
|
+
notice?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Opens the browser print dialog for the content (choose "Save as PDF" there). */
|
|
14
|
+
export declare function printDocument(p: Printable, title: string): Promise<void>;
|
|
15
|
+
/** Standalone web page of the content (pictures embedded). */
|
|
16
|
+
export declare function exportHtml(p: Printable, title: string): Promise<Blob>;
|