agentic-documents 0.5.0 → 0.6.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/CHANGELOG.md +21 -0
- package/README.md +51 -8
- package/dist/{chunk-7R7ZPPRF.js → chunk-34C47PDC.js} +1 -1
- package/dist/{chunk-UPEGODUV.js → chunk-5IQX2W52.js} +1 -1
- package/dist/{chunk-S6MQDWFK.js → chunk-6UYJV2EJ.js} +19 -6
- package/dist/{chunk-E7B6T5WN.js → chunk-CN6UAW7B.js} +5 -3
- package/dist/chunk-DWVJIKVT.js +8 -0
- package/dist/{chunk-6QOYS63L.js → chunk-GQ5E6T7Z.js} +4 -2
- package/dist/{chunk-YNHZCIGO.js → chunk-JBAQEIOH.js} +5 -3
- package/dist/chunk-KC5KFB5R.js +354 -0
- package/dist/{chunk-FX755I7V.js → chunk-V6TBTOHW.js} +11 -8
- package/dist/cli.js +16 -14
- package/dist/client.d.ts +5 -5
- package/dist/{doc-mJPajXEQ.d.ts → doc-BXG9qG5X.d.ts} +4 -2
- package/dist/editor.css +37 -0
- package/dist/embed.d.ts +86 -0
- package/dist/embed.js +431 -0
- package/dist/formats.d.ts +6 -3
- package/dist/formats.js +14 -224
- package/dist/index.d.ts +12 -11
- package/dist/index.js +14 -12
- package/dist/live-worker.d.ts +2 -2
- package/dist/live-worker.js +7 -6
- package/dist/live.d.ts +2 -2
- package/dist/live.js +5 -4
- package/dist/{mcp-ESSEG46C.js → mcp-ZTUTC4CS.js} +5 -4
- package/dist/{export-Cwx276UP.d.ts → render-463xHb-x.d.ts} +3 -4
- package/dist/{review-Hgq8OgvD.d.ts → review-Ds-i5Vnc.d.ts} +2 -2
- package/dist/{service-CqKE9DKz.d.ts → service-Ck6oX-Kq.d.ts} +2 -2
- package/dist/ui/app.css +1 -1
- package/dist/ui/app.js +65 -65
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +7 -6
- package/package.json +28 -11
- package/dist/chunk-L6W2AQUF.js +0 -258
package/dist/worker.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { Region } from 'agentic-identity';
|
|
3
3
|
import { D1Like, R2Like } from 'agentic-identity/cloudflare';
|
|
4
4
|
import { Realtime, Role } from 'agentic-realtime';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { P as Principal } from './doc-BXG9qG5X.js';
|
|
6
|
+
import { c as DocsService } from './service-Ck6oX-Kq.js';
|
|
7
7
|
import 'yjs';
|
|
8
8
|
|
|
9
9
|
interface WorkerEnv {
|
package/dist/worker.js
CHANGED
|
@@ -3,12 +3,13 @@ import {
|
|
|
3
3
|
regionOf,
|
|
4
4
|
serviceFor,
|
|
5
5
|
worker_default
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-V6TBTOHW.js";
|
|
7
|
+
import "./chunk-6UYJV2EJ.js";
|
|
8
|
+
import "./chunk-JBAQEIOH.js";
|
|
9
|
+
import "./chunk-5IQX2W52.js";
|
|
10
|
+
import "./chunk-KC5KFB5R.js";
|
|
11
|
+
import "./chunk-GQ5E6T7Z.js";
|
|
12
|
+
import "./chunk-DWVJIKVT.js";
|
|
12
13
|
export {
|
|
13
14
|
createApp,
|
|
14
15
|
worker_default as default,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-documents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Write documents with people and agents in real time.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"documents",
|
|
@@ -62,6 +62,11 @@
|
|
|
62
62
|
"types": "./dist/formats.d.ts",
|
|
63
63
|
"import": "./dist/formats.js"
|
|
64
64
|
},
|
|
65
|
+
"./ui": {
|
|
66
|
+
"types": "./dist/embed.d.ts",
|
|
67
|
+
"import": "./dist/embed.js"
|
|
68
|
+
},
|
|
69
|
+
"./ui.css": "./dist/editor.css",
|
|
65
70
|
"./live-worker": {
|
|
66
71
|
"types": "./dist/live-worker.d.ts",
|
|
67
72
|
"import": "./dist/live-worker.js"
|
|
@@ -78,10 +83,18 @@
|
|
|
78
83
|
},
|
|
79
84
|
"dependencies": {
|
|
80
85
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
86
|
+
"@tiptap/core": "^3.32.1",
|
|
87
|
+
"@tiptap/extension-collaboration": "^3.32.1",
|
|
88
|
+
"@tiptap/extension-image": "^3.32.1",
|
|
89
|
+
"@tiptap/extension-list": "^3.32.1",
|
|
90
|
+
"@tiptap/extension-table": "^3.32.1",
|
|
91
|
+
"@tiptap/pm": "^3.32.1",
|
|
92
|
+
"@tiptap/starter-kit": "^3.32.1",
|
|
93
|
+
"@tiptap/y-tiptap": "^3.0.11",
|
|
94
|
+
"agentic-design": "^0.1.0",
|
|
81
95
|
"agentic-formats": "^0.2.0",
|
|
82
96
|
"agentic-identity": "^0.4.0",
|
|
83
97
|
"agentic-realtime": "^0.1.0",
|
|
84
|
-
"fflate": "^0.8.3",
|
|
85
98
|
"hono": "^4.13.14",
|
|
86
99
|
"lib0": "^0.2.119",
|
|
87
100
|
"mdast-util-from-markdown": "^2.1.0",
|
|
@@ -99,20 +112,12 @@
|
|
|
99
112
|
"devDependencies": {
|
|
100
113
|
"@axe-core/playwright": "^4.14.0",
|
|
101
114
|
"@playwright/test": "^1.64.1",
|
|
102
|
-
"@tiptap/core": "^3.32.1",
|
|
103
|
-
"@tiptap/extension-collaboration": "^3.32.1",
|
|
104
|
-
"@tiptap/extension-image": "^3.32.1",
|
|
105
|
-
"@tiptap/extension-list": "^3.32.1",
|
|
106
|
-
"@tiptap/extension-table": "^3.32.1",
|
|
107
|
-
"@tiptap/pm": "^3.32.1",
|
|
108
|
-
"@tiptap/starter-kit": "^3.32.1",
|
|
109
|
-
"@tiptap/y-tiptap": "^3.0.11",
|
|
110
115
|
"@types/mdast": "^4.0.4",
|
|
111
116
|
"@types/node": "^22.0.0",
|
|
112
117
|
"@types/react": "^19.3.0",
|
|
113
118
|
"@types/react-dom": "^19.3.0",
|
|
114
|
-
"agentic-design": "^0.1.0",
|
|
115
119
|
"esbuild": "^0.28.2",
|
|
120
|
+
"jsdom": "^26.1.0",
|
|
116
121
|
"react": "^19.3.1",
|
|
117
122
|
"react-dom": "^19.3.1",
|
|
118
123
|
"tsup": "^8.0.0",
|
|
@@ -120,6 +125,18 @@
|
|
|
120
125
|
"vitest": "^3.0.0",
|
|
121
126
|
"wrangler": "^4.155.0"
|
|
122
127
|
},
|
|
128
|
+
"peerDependencies": {
|
|
129
|
+
"react": "^19.0.0",
|
|
130
|
+
"react-dom": "^19.0.0"
|
|
131
|
+
},
|
|
132
|
+
"peerDependenciesMeta": {
|
|
133
|
+
"react": {
|
|
134
|
+
"optional": true
|
|
135
|
+
},
|
|
136
|
+
"react-dom": {
|
|
137
|
+
"optional": true
|
|
138
|
+
}
|
|
139
|
+
},
|
|
123
140
|
"bin": {
|
|
124
141
|
"agentic-documents": "./dist/cli.js"
|
|
125
142
|
}
|
package/dist/chunk-L6W2AQUF.js
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
// src/package.ts
|
|
2
|
-
var IMAGE_TYPES = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp" };
|
|
3
|
-
var DATA_IMAGE = /^data:(image\/(?:png|jpeg|gif|webp));base64,([a-z0-9+/=]+)$/i;
|
|
4
|
-
var FIXED_DATE = new Date(2e3, 0, 1);
|
|
5
|
-
function toBase64(bytes) {
|
|
6
|
-
let text = "";
|
|
7
|
-
for (let i = 0; i < bytes.length; i += 32768) text += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
8
|
-
return btoa(text);
|
|
9
|
-
}
|
|
10
|
-
var fromBase64 = (value) => Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
|
|
11
|
-
function imageSize(b) {
|
|
12
|
-
const v = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
13
|
-
if (b.length > 24 && v.getUint32(0) === 2303741511) return [v.getUint32(16), v.getUint32(20)];
|
|
14
|
-
if (b.length > 10 && v.getUint32(0) === 1195984440) return [v.getUint16(6, true), v.getUint16(8, true)];
|
|
15
|
-
if (b.length > 4 && v.getUint16(0) === 65496) {
|
|
16
|
-
for (let i = 2; i + 9 < b.length && b[i] === 255; i += 2 + v.getUint16(i + 2)) {
|
|
17
|
-
if (b[i + 1] >= 192 && b[i + 1] <= 207 && ![196, 200, 204].includes(b[i + 1])) return [v.getUint16(i + 7), v.getUint16(i + 5)];
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return void 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// src/xml.ts
|
|
24
|
-
var xmlEscape = (value) => String(value ?? "").replace(/[^\u0009\u000A\u000D --�\u{10000}-\u{10FFFF}]/gu, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
25
|
-
var safeHref = (value) => {
|
|
26
|
-
const href = String(value ?? "").trim();
|
|
27
|
-
return /^(https?:|mailto:|#|[^:]*$)/i.test(href) ? href : void 0;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// src/odt.ts
|
|
31
|
-
import { strToU8, zipSync } from "fflate";
|
|
32
|
-
var ODT_MIME = "application/vnd.oasis.opendocument.text";
|
|
33
|
-
var NS = 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" office:version="1.3"';
|
|
34
|
-
var XML = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
35
|
-
var SPANS = { bold: "B", italic: "I", strike: "S", code: "Source_20_Text" };
|
|
36
|
-
var TASK = ["\u2610 ", "\u2611 "];
|
|
37
|
-
var LEVELS = (kind) => Array.from(
|
|
38
|
-
{ length: 10 },
|
|
39
|
-
(_, i) => kind === "bullet" ? `<text:list-level-style-bullet text:level="${i + 1}" text:bullet-char="\u2022"><style:list-level-properties text:list-level-position-and-space-mode="label-alignment"><style:list-level-label-alignment text:label-followed-by="listtab" fo:margin-left="${(i + 1) * 0.63}cm" fo:text-indent="-0.63cm"/></style:list-level-properties></text:list-level-style-bullet>` : `<text:list-level-style-number text:level="${i + 1}" style:num-suffix="." style:num-format="1"><style:list-level-properties text:list-level-position-and-space-mode="label-alignment"><style:list-level-label-alignment text:label-followed-by="listtab" fo:margin-left="${(i + 1) * 0.63}cm" fo:text-indent="-0.63cm"/></style:list-level-properties></text:list-level-style-number>`
|
|
40
|
-
).join("");
|
|
41
|
-
var SIZES = [24, 18, 14, 12, 11, 10];
|
|
42
|
-
var STYLES = `${XML}<office:document-styles ${NS}><office:styles>
|
|
43
|
-
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
|
44
|
-
${SIZES.map((s, i) => `<style:style style:name="Heading_20_${i + 1}" style:display-name="Heading ${i + 1}" style:family="paragraph" style:parent-style-name="Standard" style:class="text"><style:text-properties fo:font-size="${s}pt" fo:font-weight="bold"/></style:style>`).join("\n")}
|
|
45
|
-
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html"><style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm"/></style:style>
|
|
46
|
-
<style:style style:name="Preformatted_20_Text" style:display-name="Preformatted Text" style:family="paragraph" style:parent-style-name="Standard" style:class="html"><style:text-properties fo:font-family="Liberation Mono, Courier New, monospace" fo:font-size="10pt"/></style:style>
|
|
47
|
-
<style:style style:name="Horizontal_20_Line" style:display-name="Horizontal Line" style:family="paragraph" style:parent-style-name="Standard" style:class="html"><style:paragraph-properties fo:border-bottom="0.5pt solid #808080" fo:padding="0cm" fo:margin-bottom="0.3cm"/></style:style>
|
|
48
|
-
<style:style style:name="Source_20_Text" style:display-name="Source Text" style:family="text"><style:text-properties fo:font-family="Liberation Mono, Courier New, monospace"/></style:style>
|
|
49
|
-
</office:styles></office:document-styles>`;
|
|
50
|
-
var AUTO = `<style:style style:name="B" style:family="text"><style:text-properties fo:font-weight="bold"/></style:style>
|
|
51
|
-
<style:style style:name="I" style:family="text"><style:text-properties fo:font-style="italic"/></style:style>
|
|
52
|
-
<style:style style:name="S" style:family="text"><style:text-properties style:text-line-through-style="solid"/></style:style>
|
|
53
|
-
<text:list-style style:name="Bullets">${LEVELS("bullet")}</text:list-style>
|
|
54
|
-
<text:list-style style:name="Numbering">${LEVELS("number")}</text:list-style>`;
|
|
55
|
-
var spaced = (s) => xmlEscape(s).replace(/\t/g, "<text:tab/>").replace(/\n/g, "<text:line-break/>").replace(/ +/g, (run, at, all) => at === 0 || at + run.length === all.length ? `<text:s text:c="${run.length}"/>` : run.length > 1 ? ` <text:s text:c="${run.length - 1}"/>` : run);
|
|
56
|
-
function image(node, out) {
|
|
57
|
-
const alt = String(node.attrs?.alt ?? "");
|
|
58
|
-
const found = DATA_IMAGE.exec(String(node.attrs?.src ?? "").trim());
|
|
59
|
-
let data;
|
|
60
|
-
try {
|
|
61
|
-
data = found ? fromBase64(found[2]) : void 0;
|
|
62
|
-
} catch {
|
|
63
|
-
data = void 0;
|
|
64
|
-
}
|
|
65
|
-
if (!found || !data) return spaced(`[image${alt ? `: ${alt}` : ""}]`);
|
|
66
|
-
const ext = Object.keys(IMAGE_TYPES).find((k) => IMAGE_TYPES[k] === found[1].toLowerCase());
|
|
67
|
-
const name = `Image${out.images.length + 1}`;
|
|
68
|
-
out.images.push({ name: `Pictures/${name}.${ext}`, type: found[1].toLowerCase(), data });
|
|
69
|
-
const [w, h] = imageSize(data) ?? [320, 240];
|
|
70
|
-
const scale = Math.min(1, 16 / (w * 2.54 / 96));
|
|
71
|
-
const cm = (px) => (px * 2.54 * scale / 96).toFixed(2);
|
|
72
|
-
return `<draw:frame draw:name="${name}" text:anchor-type="as-char" svg:width="${cm(w)}cm" svg:height="${cm(h)}cm"><draw:image xlink:href="Pictures/${name}.${ext}" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/><svg:title>${xmlEscape(alt)}</svg:title></draw:frame>`;
|
|
73
|
-
}
|
|
74
|
-
function inline(nodes, out) {
|
|
75
|
-
return nodes.map((node) => {
|
|
76
|
-
if (node.type === "hardBreak") return "<text:line-break/>";
|
|
77
|
-
if (node.type === "image") return image(node, out);
|
|
78
|
-
return (node.marks ?? []).reduce((xml, mark) => {
|
|
79
|
-
if (mark.type === "link") {
|
|
80
|
-
const href = safeHref(mark.attrs?.href);
|
|
81
|
-
return href ? `<text:a xlink:type="simple" xlink:href="${xmlEscape(href)}">${xml}</text:a>` : xml;
|
|
82
|
-
}
|
|
83
|
-
return SPANS[mark.type] ? `<text:span text:style-name="${SPANS[mark.type]}">${xml}</text:span>` : xml;
|
|
84
|
-
}, spaced(node.text ?? ""));
|
|
85
|
-
}).join("");
|
|
86
|
-
}
|
|
87
|
-
function block(node, out, style = "Standard") {
|
|
88
|
-
const kids = node.content ?? [];
|
|
89
|
-
const p = (name, inner) => `<text:p text:style-name="${name}">${inner}</text:p>`;
|
|
90
|
-
switch (node.type) {
|
|
91
|
-
case "paragraph":
|
|
92
|
-
return p(style, inline(kids, out));
|
|
93
|
-
case "heading": {
|
|
94
|
-
const level = Math.min(6, Math.max(1, Math.trunc(Number(node.attrs?.level)) || 1));
|
|
95
|
-
return `<text:h text:style-name="Heading_20_${level}" text:outline-level="${level}">${inline(kids, out)}</text:h>`;
|
|
96
|
-
}
|
|
97
|
-
case "blockquote":
|
|
98
|
-
return kids.map((k) => block(k, out, "Quotations")).join("");
|
|
99
|
-
case "codeBlock":
|
|
100
|
-
return p("Preformatted_20_Text", spaced(kids.map((k) => k.text ?? "").join("")));
|
|
101
|
-
case "horizontalRule":
|
|
102
|
-
return `<text:p text:style-name="Horizontal_20_Line"/>`;
|
|
103
|
-
case "bulletList":
|
|
104
|
-
case "orderedList":
|
|
105
|
-
case "taskList": {
|
|
106
|
-
const ordered = node.type === "orderedList";
|
|
107
|
-
const start = Number(node.attrs?.start ?? 1);
|
|
108
|
-
const items = kids.map((item, i) => {
|
|
109
|
-
const [first, ...rest] = item.content ?? [];
|
|
110
|
-
const mark = item.type === "taskItem" && first ? { ...first, content: [{ type: "text", text: TASK[item.attrs?.checked ? 1 : 0] }, ...first.content ?? []] } : first;
|
|
111
|
-
const attr = ordered && i === 0 && start !== 1 ? ` text:start-value="${Math.trunc(start) || 1}"` : "";
|
|
112
|
-
return `<text:list-item${attr}>${[mark, ...rest].filter(Boolean).map((k) => block(k, out)).join("")}</text:list-item>`;
|
|
113
|
-
});
|
|
114
|
-
return `<text:list text:style-name="${ordered ? "Numbering" : "Bullets"}">${items.join("")}</text:list>`;
|
|
115
|
-
}
|
|
116
|
-
case "table": {
|
|
117
|
-
const cols = Math.max(0, ...kids.map((r) => r.content?.length ?? 0));
|
|
118
|
-
const row = (r) => `<table:table-row>${(r.content ?? []).map((c) => `<table:table-cell office:value-type="string">${(c.content ?? []).map((b) => block(b, out)).join("")}</table:table-cell>`).join("")}</table:table-row>`;
|
|
119
|
-
const header = kids[0]?.content?.length && kids[0].content.every((c) => c.type === "tableHeader");
|
|
120
|
-
const rows = header ? `<table:table-header-rows>${row(kids[0])}</table:table-header-rows>${kids.slice(1).map(row).join("")}` : kids.map(row).join("");
|
|
121
|
-
return `<table:table table:name="Table${++out.tables}">${cols ? `<table:table-column table:number-columns-repeated="${cols}"/>` : ""}${rows}</table:table>`;
|
|
122
|
-
}
|
|
123
|
-
default:
|
|
124
|
-
return "";
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function exportOdt(blocks) {
|
|
128
|
-
const out = { images: [], tables: 0 };
|
|
129
|
-
const body = blocks.map((b) => block(b, out)).join("\n");
|
|
130
|
-
const content = `${XML}<office:document-content ${NS}><office:automatic-styles>${AUTO}</office:automatic-styles><office:body><office:text>${body}</office:text></office:body></office:document-content>`;
|
|
131
|
-
const entries = [
|
|
132
|
-
`<manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="${ODT_MIME}"/>`,
|
|
133
|
-
...["content.xml", "styles.xml", "meta.xml"].map((n) => `<manifest:file-entry manifest:full-path="${n}" manifest:media-type="text/xml"/>`),
|
|
134
|
-
...out.images.map((i) => `<manifest:file-entry manifest:full-path="${i.name}" manifest:media-type="${i.type}"/>`)
|
|
135
|
-
];
|
|
136
|
-
const meta = `${XML}<office:document-meta ${NS}><office:meta><meta:generator>agentic-documents</meta:generator></office:meta></office:document-meta>`;
|
|
137
|
-
const part = (text) => [strToU8(text), { mtime: FIXED_DATE }];
|
|
138
|
-
return zipSync({
|
|
139
|
-
mimetype: [strToU8(ODT_MIME), { level: 0, mtime: FIXED_DATE }],
|
|
140
|
-
// first and stored, so file sniffers can read it
|
|
141
|
-
"META-INF/manifest.xml": part(`${XML}<manifest:manifest ${NS}>${entries.join("")}</manifest:manifest>`),
|
|
142
|
-
"content.xml": part(content),
|
|
143
|
-
"styles.xml": part(STYLES),
|
|
144
|
-
"meta.xml": part(meta),
|
|
145
|
-
...Object.fromEntries(out.images.map((i) => [i.name, [i.data, { mtime: FIXED_DATE }]]))
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// src/export.ts
|
|
150
|
-
var EXPORT_FORMATS = ["md", "html", "odt"];
|
|
151
|
-
var esc = (s) => String(s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
152
|
-
var url = (value) => /^(https?:|mailto:|[^:]*$)/i.test(String(value ?? "").trim()) ? esc(String(value).trim()) : "#";
|
|
153
|
-
var integer = (value, fallback) => Number.isFinite(Number(value)) ? Math.trunc(Number(value)) : fallback;
|
|
154
|
-
var INLINE_IMAGE = /^data:image\/(png|jpeg|gif|webp);base64,[a-z0-9+/=]+$/i;
|
|
155
|
-
var textOf = (node) => node.text ?? (node.content ?? []).map(textOf).join("");
|
|
156
|
-
var MARK_TAGS = { bold: "strong", italic: "em", strike: "s", code: "code" };
|
|
157
|
-
function image2(node, remote) {
|
|
158
|
-
const src = String(node.attrs?.src ?? "").trim();
|
|
159
|
-
const alt = esc(node.attrs?.alt);
|
|
160
|
-
if (INLINE_IMAGE.test(src)) return `<img src="${src}" alt="${alt}">`;
|
|
161
|
-
if (remote && url(src) !== "#") return `<img src="${url(src)}" alt="${alt}">`;
|
|
162
|
-
return `<span class="image">[image${alt ? `: ${alt}` : ""}]</span>`;
|
|
163
|
-
}
|
|
164
|
-
function inline2(node, remote) {
|
|
165
|
-
if (node.type === "hardBreak") return "<br>";
|
|
166
|
-
if (node.type === "image") return image2(node, remote);
|
|
167
|
-
return (node.marks ?? []).reduce((html, mark) => {
|
|
168
|
-
if (mark.type === "link") return `<a href="${url(mark.attrs?.href)}">${html}</a>`;
|
|
169
|
-
const tag = MARK_TAGS[mark.type];
|
|
170
|
-
return tag ? `<${tag}>${html}</${tag}>` : html;
|
|
171
|
-
}, esc(node.text));
|
|
172
|
-
}
|
|
173
|
-
function block2(node, remote) {
|
|
174
|
-
const kids = (node.content ?? []).map((n) => block2(n, remote)).join("");
|
|
175
|
-
const text = (node.content ?? []).map((n) => inline2(n, remote)).join("");
|
|
176
|
-
switch (node.type) {
|
|
177
|
-
case "paragraph":
|
|
178
|
-
return `<p>${text}</p>
|
|
179
|
-
`;
|
|
180
|
-
case "heading": {
|
|
181
|
-
const level = Math.min(6, Math.max(1, integer(node.attrs?.level, 1)));
|
|
182
|
-
return `<h${level}>${text}</h${level}>
|
|
183
|
-
`;
|
|
184
|
-
}
|
|
185
|
-
case "blockquote":
|
|
186
|
-
return `<blockquote>
|
|
187
|
-
${kids}</blockquote>
|
|
188
|
-
`;
|
|
189
|
-
case "codeBlock": {
|
|
190
|
-
const lang = node.attrs?.language ? ` class="language-${esc(node.attrs.language)}"` : "";
|
|
191
|
-
return `<pre><code${lang}>${esc(textOf(node))}</code></pre>
|
|
192
|
-
`;
|
|
193
|
-
}
|
|
194
|
-
case "horizontalRule":
|
|
195
|
-
return "<hr>\n";
|
|
196
|
-
case "bulletList":
|
|
197
|
-
case "taskList":
|
|
198
|
-
return `<ul>
|
|
199
|
-
${kids}</ul>
|
|
200
|
-
`;
|
|
201
|
-
case "orderedList":
|
|
202
|
-
return `<ol start="${integer(node.attrs?.start, 1)}">
|
|
203
|
-
${kids}</ol>
|
|
204
|
-
`;
|
|
205
|
-
case "listItem":
|
|
206
|
-
return `<li>${kids}</li>
|
|
207
|
-
`;
|
|
208
|
-
case "taskItem":
|
|
209
|
-
return `<li><input type="checkbox" disabled${node.attrs?.checked ? " checked" : ""}> ${kids}</li>
|
|
210
|
-
`;
|
|
211
|
-
case "table":
|
|
212
|
-
return `<table>
|
|
213
|
-
${kids}</table>
|
|
214
|
-
`;
|
|
215
|
-
case "tableRow":
|
|
216
|
-
return `<tr>${kids}</tr>
|
|
217
|
-
`;
|
|
218
|
-
case "tableHeader":
|
|
219
|
-
return `<th>${kids}</th>`;
|
|
220
|
-
case "tableCell":
|
|
221
|
-
return `<td>${kids}</td>`;
|
|
222
|
-
default:
|
|
223
|
-
return "";
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
function toHtml(blocks, remote) {
|
|
227
|
-
const title = textOf(blocks.find((b) => b.type === "heading") ?? { type: "doc" }) || "Document";
|
|
228
|
-
const css = "body{font-family:system-ui,sans-serif;max-width:46rem;margin:2rem auto;padding:0 1rem;line-height:1.5}table{border-collapse:collapse}td,th{border:1px solid #888;padding:.25rem .5rem}pre{overflow:auto}";
|
|
229
|
-
return `<!doctype html>
|
|
230
|
-
<html lang="en">
|
|
231
|
-
<head>
|
|
232
|
-
<meta charset="utf-8">
|
|
233
|
-
<title>${esc(title)}</title>
|
|
234
|
-
<style>${css}</style>
|
|
235
|
-
</head>
|
|
236
|
-
<body>
|
|
237
|
-
${blocks.map((b) => block2(b, remote)).join("")}</body>
|
|
238
|
-
</html>
|
|
239
|
-
`;
|
|
240
|
-
}
|
|
241
|
-
function exportDoc(doc, format, options = {}) {
|
|
242
|
-
if (format === "html") {
|
|
243
|
-
return { format, contentType: "text/html; charset=utf-8", encoding: "utf8", data: toHtml(doc.toJSON().content ?? [], options.remoteImages === true) };
|
|
244
|
-
}
|
|
245
|
-
if (format === "odt") return { format, contentType: ODT_MIME, encoding: "base64", data: toBase64(exportOdt(doc.toJSON().content ?? [])) };
|
|
246
|
-
return { format, contentType: "text/markdown; charset=utf-8", encoding: "utf8", data: doc.toMarkdown() };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export {
|
|
250
|
-
DATA_IMAGE,
|
|
251
|
-
toBase64,
|
|
252
|
-
fromBase64,
|
|
253
|
-
safeHref,
|
|
254
|
-
ODT_MIME,
|
|
255
|
-
exportOdt,
|
|
256
|
-
EXPORT_FORMATS,
|
|
257
|
-
exportDoc
|
|
258
|
-
};
|