@pixerate/editor 0.24.0 → 0.26.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 +101 -1
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent/index.cjs +175 -11
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -4
- package/dist/agent/index.d.ts +18 -4
- package/dist/agent/index.js +168 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
- package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
- package/dist/builtins-CdPcLwwF.d.cts +176 -0
- package/dist/builtins-Chj6zv_O.d.ts +176 -0
- package/dist/canvas/index.cjs +8588 -1857
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +1847 -9
- package/dist/canvas/index.d.ts +1847 -9
- package/dist/canvas/index.js +8577 -1846
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-3CC7OPUQ.cjs +831 -0
- package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
- package/dist/chunk-CS6NZWWA.cjs.map +1 -0
- package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
- package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
- package/dist/chunk-HDYMLS3X.cjs +409 -0
- package/dist/chunk-HDYMLS3X.cjs.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
- package/dist/chunk-IFHKKVXH.cjs.map +1 -0
- package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
- package/dist/chunk-JOIREKL7.js.map +1 -0
- package/dist/chunk-KRGXUQ2Y.js +1696 -0
- package/dist/chunk-KRGXUQ2Y.js.map +1 -0
- package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
- package/dist/chunk-MR2764X2.js +831 -0
- package/dist/chunk-MR2764X2.js.map +1 -0
- package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
- package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
- package/dist/chunk-NQF32I6Z.cjs +6478 -0
- package/dist/chunk-NQF32I6Z.cjs.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
- package/dist/chunk-Q2OHWD2A.js.map +1 -0
- package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
- package/dist/chunk-QJSOZFCZ.js.map +1 -0
- package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
- package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
- package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
- package/dist/chunk-SWE3NPOU.js.map +1 -0
- package/dist/chunk-TXXNBDQX.cjs +1696 -0
- package/dist/chunk-TXXNBDQX.cjs.map +1 -0
- package/dist/chunk-UBCFIABU.js +409 -0
- package/dist/chunk-UBCFIABU.js.map +1 -0
- package/dist/chunk-X7HXL2SB.js +6478 -0
- package/dist/chunk-X7HXL2SB.js.map +1 -0
- package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
- package/dist/chunk-YOMYSFJU.cjs.map +1 -0
- package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
- package/dist/chunk-ZRNHGAQT.js.map +1 -0
- package/dist/commands/index.cjs +425 -4
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +39 -77
- package/dist/commands/index.d.ts +39 -77
- package/dist/commands/index.js +424 -3
- package/dist/commands/index.js.map +1 -1
- package/dist/compare/index.cjs +3 -3
- package/dist/compare/index.d.cts +1 -1
- package/dist/compare/index.d.ts +1 -1
- package/dist/compare/index.js +2 -2
- package/dist/controller-CxXGOEmy.d.cts +2038 -0
- package/dist/controller-CxXGOEmy.d.ts +2038 -0
- package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
- package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
- package/dist/export/index.cjs +36 -0
- package/dist/export/index.cjs.map +1 -0
- package/dist/export/index.d.cts +243 -0
- package/dist/export/index.d.ts +243 -0
- package/dist/export/index.js +36 -0
- package/dist/export/index.js.map +1 -0
- package/dist/extensions/index.cjs +101 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +18 -5
- package/dist/extensions/index.d.ts +18 -5
- package/dist/extensions/index.js +101 -6
- package/dist/generation/index.cjs +1013 -69
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +474 -13
- package/dist/generation/index.d.ts +474 -13
- package/dist/generation/index.js +976 -32
- package/dist/generation/index.js.map +1 -1
- package/dist/image-editor/index.cjs +3385 -1275
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +693 -128
- package/dist/image-editor/index.d.ts +693 -128
- package/dist/image-editor/index.js +3325 -1215
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
- package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
- package/dist/index.cjs +1603 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -27
- package/dist/index.d.ts +154 -27
- package/dist/index.js +1584 -26
- package/dist/index.js.map +1 -1
- package/dist/mention-text-CDnCFd3c.d.cts +53 -0
- package/dist/mention-text-CDnCFd3c.d.ts +53 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.d.cts +3 -120
- package/dist/persistence/index.d.ts +3 -120
- package/dist/registry-CED5C0lA.d.cts +204 -0
- package/dist/registry-CED5C0lA.d.ts +204 -0
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
- package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
- package/dist/serializers/index.cjs +12 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +41 -13
- package/dist/serializers/index.d.ts +41 -13
- package/dist/serializers/index.js +12 -4
- package/dist/spreadsheet/index.cjs +157 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +534 -11
- package/dist/spreadsheet/index.d.ts +534 -11
- package/dist/spreadsheet/index.js +157 -3
- package/dist/storage-BpFfspHm.d.cts +120 -0
- package/dist/storage-BpFfspHm.d.ts +120 -0
- package/dist/tokens/index.cjs +115 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +212 -0
- package/dist/tokens/index.d.ts +212 -0
- package/dist/tokens/index.js +115 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
- package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
- package/package.json +15 -1
- package/dist/chunk-3CN2WD3X.js.map +0 -1
- package/dist/chunk-5S3Z4PPF.js.map +0 -1
- package/dist/chunk-D27YFC46.cjs.map +0 -1
- package/dist/chunk-FODNLX5N.js.map +0 -1
- package/dist/chunk-ITBSKI26.js.map +0 -1
- package/dist/chunk-MA2F5KHF.cjs +0 -3223
- package/dist/chunk-MA2F5KHF.cjs.map +0 -1
- package/dist/chunk-MCSJFWAO.cjs.map +0 -1
- package/dist/chunk-OLZJXL4R.cjs.map +0 -1
- package/dist/chunk-PGNZKFQS.js.map +0 -1
- package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
- package/dist/chunk-U5KWZU4X.js +0 -3223
- package/dist/chunk-U5KWZU4X.js.map +0 -1
- package/dist/chunk-XNORY7M6.cjs.map +0 -1
- package/dist/controller-BhLLAoi8.d.cts +0 -1000
- package/dist/controller-BhLLAoi8.d.ts +0 -1000
- package/dist/types-DX4Mh4SL.d.cts +0 -217
- package/dist/types-DX4Mh4SL.d.ts +0 -217
- /package/dist/{chunk-3LV6RRPH.js.map → chunk-MHEPLBFB.js.map} +0 -0
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
// src/extensions/paragraph-styles/registry.ts
|
|
2
|
+
var MAX_CHAIN = 32;
|
|
3
|
+
var STYLE_ID = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
4
|
+
var UNSAFE_CSS = /[;{}<>\\]|\/\*|url\s*\(|expression\s*\(|@import|javascript:/i;
|
|
5
|
+
var NORMAL_STYLE_ID = "Normal";
|
|
6
|
+
var PREVIEW_PROPERTIES = /* @__PURE__ */ new Set([
|
|
7
|
+
"font-family",
|
|
8
|
+
"font-weight",
|
|
9
|
+
"font-style",
|
|
10
|
+
"color",
|
|
11
|
+
"text-decoration-line",
|
|
12
|
+
"text-transform",
|
|
13
|
+
"letter-spacing",
|
|
14
|
+
"font-variant-caps"
|
|
15
|
+
]);
|
|
16
|
+
function isValidStyleId(id) {
|
|
17
|
+
return typeof id === "string" && STYLE_ID.test(id);
|
|
18
|
+
}
|
|
19
|
+
var MUTED = "var(--pixerate-style-muted, #6b7280)";
|
|
20
|
+
var HEADING_SIZES = ["2em", "1.5em", "1.25em", "1.1em", "1em", "0.9em"];
|
|
21
|
+
function builtinStyles() {
|
|
22
|
+
const styles = [
|
|
23
|
+
{
|
|
24
|
+
id: NORMAL_STYLE_ID,
|
|
25
|
+
name: "Normal",
|
|
26
|
+
kind: "paragraph",
|
|
27
|
+
block: { type: "paragraph" },
|
|
28
|
+
priority: 0,
|
|
29
|
+
quick: true,
|
|
30
|
+
builtin: true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "Title",
|
|
34
|
+
name: "Title",
|
|
35
|
+
kind: "paragraph",
|
|
36
|
+
basedOn: NORMAL_STYLE_ID,
|
|
37
|
+
next: NORMAL_STYLE_ID,
|
|
38
|
+
linked: "TitleChar",
|
|
39
|
+
paragraph: { spaceAfter: "0.25em", lineHeight: "1.15" },
|
|
40
|
+
character: { fontSize: "2.5em", bold: true, letterSpacing: "-0.01em" },
|
|
41
|
+
priority: 10,
|
|
42
|
+
quick: true,
|
|
43
|
+
builtin: true
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "Subtitle",
|
|
47
|
+
name: "Subtitle",
|
|
48
|
+
kind: "paragraph",
|
|
49
|
+
basedOn: NORMAL_STYLE_ID,
|
|
50
|
+
next: NORMAL_STYLE_ID,
|
|
51
|
+
linked: "SubtitleChar",
|
|
52
|
+
paragraph: { spaceAfter: "0.75em" },
|
|
53
|
+
character: { fontSize: "1.25em", color: MUTED },
|
|
54
|
+
priority: 11,
|
|
55
|
+
quick: true,
|
|
56
|
+
builtin: true
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
HEADING_SIZES.forEach((fontSize, i) => {
|
|
60
|
+
const level = i + 1;
|
|
61
|
+
styles.push({
|
|
62
|
+
id: `Heading${level}`,
|
|
63
|
+
name: `Heading ${level}`,
|
|
64
|
+
kind: "paragraph",
|
|
65
|
+
basedOn: NORMAL_STYLE_ID,
|
|
66
|
+
next: NORMAL_STYLE_ID,
|
|
67
|
+
linked: `Heading${level}Char`,
|
|
68
|
+
block: { type: "heading", attrs: { level } },
|
|
69
|
+
paragraph: { spaceBefore: level <= 2 ? "1em" : "0.75em", spaceAfter: "0.25em" },
|
|
70
|
+
character: { fontSize, bold: true },
|
|
71
|
+
priority: 11 + level,
|
|
72
|
+
quick: level <= 3,
|
|
73
|
+
builtin: true
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
styles.push(
|
|
77
|
+
{
|
|
78
|
+
id: "Quote",
|
|
79
|
+
name: "Quote",
|
|
80
|
+
kind: "paragraph",
|
|
81
|
+
basedOn: NORMAL_STYLE_ID,
|
|
82
|
+
next: NORMAL_STYLE_ID,
|
|
83
|
+
linked: "QuoteChar",
|
|
84
|
+
paragraph: { indentLeft: "1em", paddingLeft: "0.75em", borderLeft: "3px solid currentColor" },
|
|
85
|
+
character: { italic: true, color: MUTED },
|
|
86
|
+
priority: 29,
|
|
87
|
+
quick: true,
|
|
88
|
+
builtin: true
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "Code",
|
|
92
|
+
name: "Code",
|
|
93
|
+
kind: "paragraph",
|
|
94
|
+
basedOn: NORMAL_STYLE_ID,
|
|
95
|
+
next: NORMAL_STYLE_ID,
|
|
96
|
+
block: { type: "codeBlock" },
|
|
97
|
+
character: {
|
|
98
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
99
|
+
fontSize: "0.9em"
|
|
100
|
+
},
|
|
101
|
+
priority: 30,
|
|
102
|
+
quick: true,
|
|
103
|
+
builtin: true
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "ListParagraph",
|
|
107
|
+
name: "List Paragraph",
|
|
108
|
+
kind: "paragraph",
|
|
109
|
+
basedOn: NORMAL_STYLE_ID,
|
|
110
|
+
paragraph: { indentLeft: "2em" },
|
|
111
|
+
priority: 34,
|
|
112
|
+
quick: true,
|
|
113
|
+
builtin: true
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
const linked = ["Title", "Subtitle", "Quote", ...HEADING_SIZES.map((_, i) => `Heading${i + 1}`)];
|
|
117
|
+
linked.forEach((paragraphId) => {
|
|
118
|
+
const name = styles.find((s) => s.id === paragraphId).name;
|
|
119
|
+
styles.push({
|
|
120
|
+
id: `${paragraphId}Char`,
|
|
121
|
+
name: `${name} Char`,
|
|
122
|
+
kind: "character",
|
|
123
|
+
linked: paragraphId,
|
|
124
|
+
priority: 99,
|
|
125
|
+
hidden: true,
|
|
126
|
+
builtin: true
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
styles.push(
|
|
130
|
+
{
|
|
131
|
+
id: "Emphasis",
|
|
132
|
+
name: "Emphasis",
|
|
133
|
+
kind: "character",
|
|
134
|
+
character: { italic: true },
|
|
135
|
+
priority: 20,
|
|
136
|
+
quick: true,
|
|
137
|
+
builtin: true
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "Strong",
|
|
141
|
+
name: "Strong",
|
|
142
|
+
kind: "character",
|
|
143
|
+
character: { bold: true },
|
|
144
|
+
priority: 22,
|
|
145
|
+
quick: true,
|
|
146
|
+
builtin: true
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
return styles;
|
|
150
|
+
}
|
|
151
|
+
var DEFAULT_STYLES = Object.freeze(builtinStyles());
|
|
152
|
+
var clone = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
|
|
153
|
+
function overlay(base, patch) {
|
|
154
|
+
if (!patch) return base;
|
|
155
|
+
const out = { ...base };
|
|
156
|
+
Object.entries(patch).forEach(([key, value]) => {
|
|
157
|
+
if (value !== void 0 && value !== null) out[key] = value;
|
|
158
|
+
});
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function sameBlock(a, type, attrs) {
|
|
162
|
+
if (a.type !== type) return false;
|
|
163
|
+
return Object.entries(a.attrs ?? {}).every(([key, value]) => attrs[key] === value);
|
|
164
|
+
}
|
|
165
|
+
var StyleRegistry = class {
|
|
166
|
+
styles = /* @__PURE__ */ new Map();
|
|
167
|
+
defaults;
|
|
168
|
+
version = 0;
|
|
169
|
+
snapshot = null;
|
|
170
|
+
listeners = /* @__PURE__ */ new Set();
|
|
171
|
+
constructor(options = {}) {
|
|
172
|
+
this.defaults = clone(options.defaults ?? {});
|
|
173
|
+
if (options.builtins !== false) DEFAULT_STYLES.forEach((style) => this.styles.set(style.id, clone(style)));
|
|
174
|
+
(options.styles ?? []).forEach((style) => this.put(style));
|
|
175
|
+
}
|
|
176
|
+
// -- Reading --------------------------------------------------------------
|
|
177
|
+
get(id) {
|
|
178
|
+
return id ? this.styles.get(id) : void 0;
|
|
179
|
+
}
|
|
180
|
+
/** Finds a style by id or display name (case-insensitive). */
|
|
181
|
+
find(nameOrId) {
|
|
182
|
+
const direct = this.styles.get(nameOrId);
|
|
183
|
+
if (direct) return direct;
|
|
184
|
+
const lower = nameOrId.toLowerCase();
|
|
185
|
+
return this.list().find((style) => style.name.toLowerCase() === lower);
|
|
186
|
+
}
|
|
187
|
+
/** All styles (optionally of one kind) in gallery order. */
|
|
188
|
+
list(kind) {
|
|
189
|
+
return Array.from(this.styles.values()).filter((style) => !kind || style.kind === kind).sort((a, b) => (a.priority ?? 99) - (b.priority ?? 99) || a.name.localeCompare(b.name));
|
|
190
|
+
}
|
|
191
|
+
/** Styles shown in the gallery (`quick`, not hidden), paragraph styles by default. */
|
|
192
|
+
gallery(kind = "paragraph") {
|
|
193
|
+
return this.list(kind).filter((style) => style.quick && !style.hidden);
|
|
194
|
+
}
|
|
195
|
+
/** The `basedOn` chain from the root style down to `id` (cycle-safe, bounded). */
|
|
196
|
+
chain(id) {
|
|
197
|
+
const out = [];
|
|
198
|
+
let current = this.styles.get(id);
|
|
199
|
+
while (current && out.length < MAX_CHAIN && !out.includes(current)) {
|
|
200
|
+
out.push(current);
|
|
201
|
+
current = current.basedOn ? this.styles.get(current.basedOn) : void 0;
|
|
202
|
+
}
|
|
203
|
+
return out.reverse();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Resolves a paragraph style: defaults, then each style of the chain (base
|
|
207
|
+
* first). Unknown ids resolve to the defaults in a `paragraph` block.
|
|
208
|
+
*/
|
|
209
|
+
resolve(id) {
|
|
210
|
+
let paragraph = overlay({}, this.defaults.paragraph);
|
|
211
|
+
let character = overlay({}, this.defaults.character);
|
|
212
|
+
let block = { type: "paragraph" };
|
|
213
|
+
this.chain(id).forEach((style) => {
|
|
214
|
+
if (style.kind !== "paragraph") return;
|
|
215
|
+
paragraph = overlay(paragraph, style.paragraph);
|
|
216
|
+
character = overlay(character, style.character);
|
|
217
|
+
if (style.block) block = clone(style.block);
|
|
218
|
+
});
|
|
219
|
+
return { id, paragraph, character, block, next: this.nextStyleId(id) };
|
|
220
|
+
}
|
|
221
|
+
/** Character formatting a paragraph style's chain sets (without defaults). */
|
|
222
|
+
paragraphCharacter(id) {
|
|
223
|
+
let character = {};
|
|
224
|
+
this.chain(id).forEach((style) => {
|
|
225
|
+
if (style.kind === "paragraph") character = overlay(character, style.character);
|
|
226
|
+
});
|
|
227
|
+
return character;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Character formatting of a character style's chain. A style linked to a
|
|
231
|
+
* paragraph style takes that style's character formatting first.
|
|
232
|
+
*/
|
|
233
|
+
resolveCharacterStyle(id) {
|
|
234
|
+
let character = {};
|
|
235
|
+
this.chain(id).forEach((style) => {
|
|
236
|
+
if (style.kind === "character" && style.linked && this.get(style.linked)?.kind === "paragraph") {
|
|
237
|
+
character = overlay(character, this.paragraphCharacter(style.linked));
|
|
238
|
+
}
|
|
239
|
+
character = overlay(character, style.character);
|
|
240
|
+
});
|
|
241
|
+
return character;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Character formatting of text: defaults → paragraph style chain →
|
|
245
|
+
* character style chain → direct formatting.
|
|
246
|
+
*/
|
|
247
|
+
resolveText(input) {
|
|
248
|
+
let character = this.resolve(input.paragraphStyle || NORMAL_STYLE_ID).character;
|
|
249
|
+
if (input.characterStyle) character = overlay(character, this.resolveCharacterStyle(input.characterStyle));
|
|
250
|
+
return overlay(character, input.direct);
|
|
251
|
+
}
|
|
252
|
+
/** The style after Enter at the end of a paragraph of `id` (itself when unset or unknown). */
|
|
253
|
+
nextStyleId(id) {
|
|
254
|
+
const next = this.styles.get(id)?.next;
|
|
255
|
+
return next && this.styles.get(next)?.kind === "paragraph" ? next : id;
|
|
256
|
+
}
|
|
257
|
+
/** The linked character style of a paragraph style, if any. */
|
|
258
|
+
linkedCharacterStyle(id) {
|
|
259
|
+
const style = this.styles.get(id);
|
|
260
|
+
const linked = style?.linked ? this.styles.get(style.linked) : void 0;
|
|
261
|
+
return style?.kind === "paragraph" && linked?.kind === "character" ? linked : void 0;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* The style a block uses when it names none: Normal for paragraphs,
|
|
265
|
+
* Heading N for headings of level N, Code for code blocks (the built-in
|
|
266
|
+
* style whose own `block` matches).
|
|
267
|
+
*/
|
|
268
|
+
defaultStyleFor(type, attrs = {}) {
|
|
269
|
+
if (type === "paragraph" && this.styles.get(NORMAL_STYLE_ID)) return NORMAL_STYLE_ID;
|
|
270
|
+
const candidates = this.list("paragraph").filter(
|
|
271
|
+
(style) => style.block && sameBlock(style.block, type, attrs)
|
|
272
|
+
);
|
|
273
|
+
return (candidates.find((s) => s.builtin) ?? candidates[0])?.id ?? null;
|
|
274
|
+
}
|
|
275
|
+
/** The style of a block: its `styleId` when known, otherwise the block's default style. */
|
|
276
|
+
styleOfBlock(type, attrs = {}) {
|
|
277
|
+
const id = attrs.styleId;
|
|
278
|
+
if (id && this.styles.get(id)?.kind === "paragraph") return id;
|
|
279
|
+
return this.defaultStyleFor(type, attrs);
|
|
280
|
+
}
|
|
281
|
+
getSnapshot = () => {
|
|
282
|
+
if (!this.snapshot) {
|
|
283
|
+
this.snapshot = Object.freeze({
|
|
284
|
+
styles: Object.freeze(this.list()),
|
|
285
|
+
defaults: this.defaults,
|
|
286
|
+
version: this.version
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return this.snapshot;
|
|
290
|
+
};
|
|
291
|
+
subscribe = (listener) => {
|
|
292
|
+
this.listeners.add(listener);
|
|
293
|
+
listener(this.getSnapshot());
|
|
294
|
+
return () => {
|
|
295
|
+
this.listeners.delete(listener);
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
toJSON() {
|
|
299
|
+
return { styles: clone(Array.from(this.styles.values())), defaults: clone(this.defaults) };
|
|
300
|
+
}
|
|
301
|
+
// -- Writing --------------------------------------------------------------
|
|
302
|
+
/** Adds or replaces a style. Throws on an invalid id. */
|
|
303
|
+
upsert(style) {
|
|
304
|
+
const stored = this.put(style);
|
|
305
|
+
this.changed();
|
|
306
|
+
return stored;
|
|
307
|
+
}
|
|
308
|
+
/** Merges `patch` into a style (formats are merged key by key). Returns `undefined` for unknown ids. */
|
|
309
|
+
update(id, patch) {
|
|
310
|
+
const current = this.styles.get(id);
|
|
311
|
+
if (!current) return void 0;
|
|
312
|
+
const next = {
|
|
313
|
+
...current,
|
|
314
|
+
...patch,
|
|
315
|
+
id,
|
|
316
|
+
paragraph: patch.paragraph ? { ...current.paragraph, ...patch.paragraph } : current.paragraph,
|
|
317
|
+
character: patch.character ? { ...current.character, ...patch.character } : current.character
|
|
318
|
+
};
|
|
319
|
+
return this.upsert(next);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Removes a style and returns it. Built-in styles cannot be removed. Styles
|
|
323
|
+
* based on it are rebased onto its own base, and `next`/`linked`
|
|
324
|
+
* references to it are dropped.
|
|
325
|
+
*/
|
|
326
|
+
remove(id) {
|
|
327
|
+
const gone = this.styles.get(id);
|
|
328
|
+
if (!gone || gone.builtin) return void 0;
|
|
329
|
+
this.styles.delete(id);
|
|
330
|
+
this.styles.forEach((style, key) => {
|
|
331
|
+
let changed = style;
|
|
332
|
+
if (style.basedOn === id) {
|
|
333
|
+
changed = { ...changed, basedOn: gone.basedOn && gone.basedOn !== key ? gone.basedOn : null };
|
|
334
|
+
}
|
|
335
|
+
if (style.next === id) changed = { ...changed, next: null };
|
|
336
|
+
if (style.linked === id) changed = { ...changed, linked: null };
|
|
337
|
+
if (changed !== style) this.styles.set(key, changed);
|
|
338
|
+
});
|
|
339
|
+
this.changed();
|
|
340
|
+
return gone;
|
|
341
|
+
}
|
|
342
|
+
/** Replaces the document defaults. */
|
|
343
|
+
setDefaults(defaults) {
|
|
344
|
+
this.defaults = clone(defaults);
|
|
345
|
+
this.changed();
|
|
346
|
+
}
|
|
347
|
+
/** An unused id derived from a display name (`My Style` → `MyStyle`, then `MyStyle1`, …). */
|
|
348
|
+
newId(name) {
|
|
349
|
+
let base = name.replace(/[^A-Za-z0-9_-]+/g, "").replace(/^[^A-Za-z]+/, "").slice(0, 56);
|
|
350
|
+
if (!base) base = "Style";
|
|
351
|
+
if (!this.styles.has(base)) return base;
|
|
352
|
+
for (let i = 1; i < 1e5; i++) {
|
|
353
|
+
const id = `${base}${i}`;
|
|
354
|
+
if (!this.styles.has(id)) return id;
|
|
355
|
+
}
|
|
356
|
+
return `${base}${Date.now()}`;
|
|
357
|
+
}
|
|
358
|
+
// -- CSS --------------------------------------------------------------------
|
|
359
|
+
/** CSS declarations (`property: value`) of a resolved paragraph or character style. */
|
|
360
|
+
cssDeclarations(id) {
|
|
361
|
+
const style = this.styles.get(id);
|
|
362
|
+
if (style?.kind === "character") return characterCSS(this.resolveCharacterStyle(id));
|
|
363
|
+
const resolved = this.resolve(id);
|
|
364
|
+
return { ...paragraphCSS(resolved.paragraph), ...characterCSS(resolved.character) };
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Declarations for previewing a style in a menu or gallery: font family,
|
|
368
|
+
* weight, style, color, decoration, case and letter spacing (no sizes,
|
|
369
|
+
* margins or borders).
|
|
370
|
+
*/
|
|
371
|
+
previewDeclarations(id) {
|
|
372
|
+
return Object.fromEntries(
|
|
373
|
+
Object.entries(this.cssDeclarations(id)).filter(([property]) => PREVIEW_PROPERTIES.has(property))
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* A stylesheet for the styles: `[data-style="Id"]` rules for paragraph
|
|
378
|
+
* styles (plus the plain element of blocks that use their default style,
|
|
379
|
+
* e.g. `h2:not([data-style])`), `[data-char-style="Id"]` for character
|
|
380
|
+
* styles and the defaults on the scope element.
|
|
381
|
+
*/
|
|
382
|
+
toCSS(options = {}) {
|
|
383
|
+
const scope = options.scope ?? ".ProseMirror";
|
|
384
|
+
const tagFor = options.tagFor ?? defaultTagFor;
|
|
385
|
+
const rules = [];
|
|
386
|
+
const rule = (selectors, declarations) => {
|
|
387
|
+
const body = Object.entries(declarations).map(([property, value]) => `${property}: ${value};`).join(" ");
|
|
388
|
+
if (body) rules.push(`${selectors.join(", ")} { ${body} }`);
|
|
389
|
+
};
|
|
390
|
+
rule([scope], {
|
|
391
|
+
...paragraphCSS(this.defaults.paragraph ?? {}),
|
|
392
|
+
...characterCSS(this.defaults.character ?? {})
|
|
393
|
+
});
|
|
394
|
+
this.list().forEach((style) => {
|
|
395
|
+
if (style.kind === "character") {
|
|
396
|
+
rule([`${scope} [data-char-style="${style.id}"]`], this.cssDeclarations(style.id));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const selectors = [`${scope} [data-style="${style.id}"]`];
|
|
400
|
+
const block = this.resolve(style.id).block;
|
|
401
|
+
if (this.defaultStyleFor(block.type, block.attrs ?? {}) === style.id) {
|
|
402
|
+
const tag = tagFor(block);
|
|
403
|
+
if (tag) selectors.push(`${scope} ${tag}:not([data-style])`);
|
|
404
|
+
}
|
|
405
|
+
const resolved = this.resolve(style.id);
|
|
406
|
+
const own = {
|
|
407
|
+
...paragraphCSS(withoutDefaults(resolved.paragraph, this.defaults.paragraph)),
|
|
408
|
+
...characterCSS(withoutDefaults(resolved.character, this.defaults.character))
|
|
409
|
+
};
|
|
410
|
+
rule(selectors, own);
|
|
411
|
+
});
|
|
412
|
+
return rules.join("\n");
|
|
413
|
+
}
|
|
414
|
+
// -- Internals --------------------------------------------------------------
|
|
415
|
+
put(style) {
|
|
416
|
+
if (!isValidStyleId(style.id)) throw new Error(`Invalid style id: ${String(style.id)}`);
|
|
417
|
+
if (style.kind !== "paragraph" && style.kind !== "character") {
|
|
418
|
+
throw new Error(`Invalid style kind: ${String(style.kind)}`);
|
|
419
|
+
}
|
|
420
|
+
const stored = clone({ ...style, name: style.name || style.id });
|
|
421
|
+
this.styles.set(style.id, stored);
|
|
422
|
+
return stored;
|
|
423
|
+
}
|
|
424
|
+
changed() {
|
|
425
|
+
this.version++;
|
|
426
|
+
this.snapshot = null;
|
|
427
|
+
const snapshot = this.getSnapshot();
|
|
428
|
+
this.listeners.forEach((listener) => {
|
|
429
|
+
try {
|
|
430
|
+
listener(snapshot);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
console.error("[StyleRegistry] Error in listener:", err);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
function withoutDefaults(format, defaults) {
|
|
438
|
+
if (!defaults) return format;
|
|
439
|
+
const out = {};
|
|
440
|
+
Object.entries(format).forEach(([key, value]) => {
|
|
441
|
+
if (defaults[key] !== value) out[key] = value;
|
|
442
|
+
});
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
function defaultTagFor(block) {
|
|
446
|
+
if (block.type === "paragraph") return "p";
|
|
447
|
+
if (block.type === "heading") {
|
|
448
|
+
const level = Number(block.attrs?.level);
|
|
449
|
+
return level >= 1 && level <= 6 ? `h${level}` : null;
|
|
450
|
+
}
|
|
451
|
+
if (block.type === "codeBlock") return "pre";
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
function safeCSSValue(value) {
|
|
455
|
+
if (typeof value !== "string") return void 0;
|
|
456
|
+
const trimmed = value.trim();
|
|
457
|
+
if (!trimmed || trimmed.length > 200 || UNSAFE_CSS.test(trimmed)) return void 0;
|
|
458
|
+
return trimmed;
|
|
459
|
+
}
|
|
460
|
+
function paragraphCSS(format) {
|
|
461
|
+
const out = {};
|
|
462
|
+
const set = (property, value) => {
|
|
463
|
+
const safe = safeCSSValue(value);
|
|
464
|
+
if (safe) out[property] = safe;
|
|
465
|
+
};
|
|
466
|
+
set("text-align", format.textAlign);
|
|
467
|
+
set("line-height", format.lineHeight);
|
|
468
|
+
set("margin-top", format.spaceBefore);
|
|
469
|
+
set("margin-bottom", format.spaceAfter);
|
|
470
|
+
set("margin-left", format.indentLeft);
|
|
471
|
+
set("margin-right", format.indentRight);
|
|
472
|
+
set("text-indent", format.indentFirstLine);
|
|
473
|
+
set("border-left", format.borderLeft);
|
|
474
|
+
set("padding-left", format.paddingLeft);
|
|
475
|
+
set("background-color", format.backgroundColor);
|
|
476
|
+
return out;
|
|
477
|
+
}
|
|
478
|
+
function characterCSS(format) {
|
|
479
|
+
const out = {};
|
|
480
|
+
const set = (property, value) => {
|
|
481
|
+
const safe = safeCSSValue(value);
|
|
482
|
+
if (safe) out[property] = safe;
|
|
483
|
+
};
|
|
484
|
+
set("font-family", format.fontFamily);
|
|
485
|
+
set("font-size", format.fontSize);
|
|
486
|
+
if (format.bold !== void 0) out["font-weight"] = format.bold ? "700" : "400";
|
|
487
|
+
if (format.italic !== void 0) out["font-style"] = format.italic ? "italic" : "normal";
|
|
488
|
+
const lines = [format.underline && "underline", format.strike && "line-through"].filter(Boolean);
|
|
489
|
+
if (format.underline !== void 0 || format.strike !== void 0) {
|
|
490
|
+
out["text-decoration-line"] = lines.length ? lines.join(" ") : "none";
|
|
491
|
+
}
|
|
492
|
+
set("color", format.color);
|
|
493
|
+
set("background-color", format.backgroundColor);
|
|
494
|
+
set("letter-spacing", format.letterSpacing);
|
|
495
|
+
set("text-transform", format.textTransform);
|
|
496
|
+
if (format.smallCaps !== void 0) out["font-variant-caps"] = format.smallCaps ? "small-caps" : "normal";
|
|
497
|
+
return out;
|
|
498
|
+
}
|
|
499
|
+
function createStyleRegistry(options = {}) {
|
|
500
|
+
return new StyleRegistry(options);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// src/import/zip.ts
|
|
504
|
+
var ZipError = class extends Error {
|
|
505
|
+
code;
|
|
506
|
+
entryName;
|
|
507
|
+
constructor(code, message, details = {}) {
|
|
508
|
+
super(message, details.cause !== void 0 ? { cause: details.cause } : void 0);
|
|
509
|
+
this.name = "ZipError";
|
|
510
|
+
this.code = code;
|
|
511
|
+
this.entryName = details.entryName;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
var DEFAULT_ZIP_MAX_ENTRIES = 1e4;
|
|
515
|
+
var DEFAULT_ZIP_MAX_TOTAL_SIZE = 256 * 1024 * 1024;
|
|
516
|
+
var SIG_EOCD = 101010256;
|
|
517
|
+
var SIG_ZIP64_EOCD = 101075792;
|
|
518
|
+
var SIG_ZIP64_LOCATOR = 117853008;
|
|
519
|
+
var SIG_CENTRAL = 33639248;
|
|
520
|
+
var SIG_LOCAL = 67324752;
|
|
521
|
+
var EOCD_SIZE = 22;
|
|
522
|
+
var MAX_COMMENT = 65535;
|
|
523
|
+
var crcTable = null;
|
|
524
|
+
function zipCrc32(data) {
|
|
525
|
+
if (!crcTable) {
|
|
526
|
+
crcTable = new Uint32Array(256);
|
|
527
|
+
for (let n = 0; n < 256; n++) {
|
|
528
|
+
let c = n;
|
|
529
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
530
|
+
crcTable[n] = c >>> 0;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
let crc = 4294967295;
|
|
534
|
+
for (let i = 0; i < data.length; i++) crc = crcTable[(crc ^ data[i]) & 255] ^ crc >>> 8;
|
|
535
|
+
return (crc ^ 4294967295) >>> 0;
|
|
536
|
+
}
|
|
537
|
+
function toBytes(data) {
|
|
538
|
+
if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
539
|
+
return new Uint8Array(data);
|
|
540
|
+
}
|
|
541
|
+
function isUnsafeZipPath(name) {
|
|
542
|
+
if (!name || name.includes("\0")) return true;
|
|
543
|
+
const normalized = name.replace(/\\/g, "/");
|
|
544
|
+
if (normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized)) return true;
|
|
545
|
+
return normalized.split("/").some((segment) => segment === "..");
|
|
546
|
+
}
|
|
547
|
+
var utf8 = new TextDecoder("utf-8");
|
|
548
|
+
var CP437_HIGH = "\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0";
|
|
549
|
+
function decodeName(bytes, isUtf8) {
|
|
550
|
+
if (isUtf8 || bytes.every((b) => b < 128)) return utf8.decode(bytes);
|
|
551
|
+
let out = "";
|
|
552
|
+
for (const b of bytes) out += b < 128 ? String.fromCharCode(b) : CP437_HIGH[b - 128];
|
|
553
|
+
return out;
|
|
554
|
+
}
|
|
555
|
+
var ZipArchive = class {
|
|
556
|
+
constructor(bytes, entries, maxTotalSize) {
|
|
557
|
+
this.bytes = bytes;
|
|
558
|
+
this.maxTotalSize = maxTotalSize;
|
|
559
|
+
this.entries = entries;
|
|
560
|
+
entries.forEach((entry) => {
|
|
561
|
+
if (!this.byName.has(entry.name)) this.byName.set(entry.name, entry);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
bytes;
|
|
565
|
+
maxTotalSize;
|
|
566
|
+
/** Entries in central-directory order. */
|
|
567
|
+
entries;
|
|
568
|
+
byName = /* @__PURE__ */ new Map();
|
|
569
|
+
extracted = 0;
|
|
570
|
+
/** Looks up an entry by exact name (a leading `/` is ignored). */
|
|
571
|
+
get(name) {
|
|
572
|
+
return this.byName.get(name.replace(/^\/+/, ""));
|
|
573
|
+
}
|
|
574
|
+
has(name) {
|
|
575
|
+
return !!this.get(name);
|
|
576
|
+
}
|
|
577
|
+
/** Uncompressed bytes extracted so far (counted against `maxTotalSize`). */
|
|
578
|
+
get extractedSize() {
|
|
579
|
+
return this.extracted;
|
|
580
|
+
}
|
|
581
|
+
/** Extracts an entry's data, verifying its size and CRC-32. */
|
|
582
|
+
async read(entryOrName) {
|
|
583
|
+
const entry = typeof entryOrName === "string" ? this.get(entryOrName) : entryOrName;
|
|
584
|
+
if (!entry) {
|
|
585
|
+
throw new ZipError("entry_not_found", `ZIP entry "${String(entryOrName)}" not found.`, {
|
|
586
|
+
entryName: String(entryOrName)
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
const name = entry.name;
|
|
590
|
+
if (entry.encrypted) {
|
|
591
|
+
throw new ZipError("encrypted", `ZIP entry "${name}" is encrypted; encrypted archives are not supported.`, {
|
|
592
|
+
entryName: name
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
if (entry.isDirectory) return new Uint8Array(0);
|
|
596
|
+
const remaining = this.maxTotalSize - this.extracted;
|
|
597
|
+
if (entry.size > remaining) {
|
|
598
|
+
throw new ZipError(
|
|
599
|
+
"too_large",
|
|
600
|
+
`Extracting "${name}" (${entry.size} bytes) would exceed the ${this.maxTotalSize}-byte limit.`,
|
|
601
|
+
{ entryName: name }
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
const { bytes } = this;
|
|
605
|
+
const offset = entry.localHeaderOffset;
|
|
606
|
+
if (offset + 30 > bytes.length || readU32(bytes, offset) !== SIG_LOCAL) {
|
|
607
|
+
throw new ZipError("corrupt_entry", `ZIP entry "${name}" has no valid local header.`, { entryName: name });
|
|
608
|
+
}
|
|
609
|
+
const dataStart = offset + 30 + readU16(bytes, offset + 26) + readU16(bytes, offset + 28);
|
|
610
|
+
const dataEnd = dataStart + entry.compressedSize;
|
|
611
|
+
if (dataEnd > bytes.length) {
|
|
612
|
+
throw new ZipError("corrupt_entry", `ZIP entry "${name}" is truncated.`, { entryName: name });
|
|
613
|
+
}
|
|
614
|
+
const raw = bytes.subarray(dataStart, dataEnd);
|
|
615
|
+
let data;
|
|
616
|
+
if (entry.method === 0) {
|
|
617
|
+
if (entry.compressedSize !== entry.size) {
|
|
618
|
+
throw new ZipError("corrupt_entry", `Stored ZIP entry "${name}" has inconsistent sizes.`, { entryName: name });
|
|
619
|
+
}
|
|
620
|
+
data = raw.slice();
|
|
621
|
+
} else if (entry.method === 8) {
|
|
622
|
+
data = await inflateRaw(raw, entry.size, name);
|
|
623
|
+
} else {
|
|
624
|
+
throw new ZipError(
|
|
625
|
+
"unsupported_feature",
|
|
626
|
+
`ZIP entry "${name}" uses unsupported compression method ${entry.method}; only stored and deflate are supported.`,
|
|
627
|
+
{ entryName: name }
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
if (data.length !== entry.size) {
|
|
631
|
+
throw new ZipError("corrupt_entry", `ZIP entry "${name}" size does not match the directory.`, { entryName: name });
|
|
632
|
+
}
|
|
633
|
+
this.extracted += data.length;
|
|
634
|
+
if (zipCrc32(data) !== entry.crc32) {
|
|
635
|
+
throw new ZipError("corrupt_entry", `ZIP entry "${name}" failed its CRC-32 check.`, { entryName: name });
|
|
636
|
+
}
|
|
637
|
+
return data;
|
|
638
|
+
}
|
|
639
|
+
/** Extracts an entry and decodes it as UTF-8 (a leading BOM is removed). */
|
|
640
|
+
async readText(entryOrName) {
|
|
641
|
+
const text = utf8.decode(await this.read(entryOrName));
|
|
642
|
+
return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
function readU16(bytes, offset) {
|
|
646
|
+
return bytes[offset] | bytes[offset + 1] << 8;
|
|
647
|
+
}
|
|
648
|
+
function readU32(bytes, offset) {
|
|
649
|
+
return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;
|
|
650
|
+
}
|
|
651
|
+
function readU64(bytes, offset) {
|
|
652
|
+
const value = readU32(bytes, offset) + readU32(bytes, offset + 4) * 4294967296;
|
|
653
|
+
if (!Number.isSafeInteger(value)) {
|
|
654
|
+
throw new ZipError("unsupported_feature", "ZIP64 value exceeds the supported range.");
|
|
655
|
+
}
|
|
656
|
+
return value;
|
|
657
|
+
}
|
|
658
|
+
async function inflateRaw(raw, limit, entryName) {
|
|
659
|
+
const Stream = globalThis.DecompressionStream;
|
|
660
|
+
if (typeof Stream !== "function") {
|
|
661
|
+
throw new ZipError(
|
|
662
|
+
"unsupported_environment",
|
|
663
|
+
"Inflating ZIP entries needs DecompressionStream (modern browsers, Node 18+).",
|
|
664
|
+
{ entryName }
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
const stream = new Stream("deflate-raw");
|
|
668
|
+
const writer = stream.writable.getWriter();
|
|
669
|
+
const reader = stream.readable.getReader();
|
|
670
|
+
const CHUNK = 64 * 1024;
|
|
671
|
+
const feeding = (async () => {
|
|
672
|
+
for (let i = 0; i < raw.length; i += CHUNK) await writer.write(raw.subarray(i, i + CHUNK));
|
|
673
|
+
await writer.close();
|
|
674
|
+
})();
|
|
675
|
+
feeding.catch(() => {
|
|
676
|
+
});
|
|
677
|
+
const chunks = [];
|
|
678
|
+
let total = 0;
|
|
679
|
+
try {
|
|
680
|
+
for (; ; ) {
|
|
681
|
+
const { done, value } = await reader.read();
|
|
682
|
+
if (done) break;
|
|
683
|
+
total += value.length;
|
|
684
|
+
if (total > limit) {
|
|
685
|
+
throw new ZipError(
|
|
686
|
+
"corrupt_entry",
|
|
687
|
+
`ZIP entry "${entryName}" inflates beyond its declared size of ${limit} bytes.`,
|
|
688
|
+
{ entryName }
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
chunks.push(value);
|
|
692
|
+
}
|
|
693
|
+
} catch (error) {
|
|
694
|
+
reader.cancel().catch(() => {
|
|
695
|
+
});
|
|
696
|
+
writer.abort().catch(() => {
|
|
697
|
+
});
|
|
698
|
+
if (error instanceof ZipError) throw error;
|
|
699
|
+
throw new ZipError("corrupt_entry", `ZIP entry "${entryName}" has an invalid deflate stream.`, {
|
|
700
|
+
entryName,
|
|
701
|
+
cause: error
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
await feeding.catch(() => {
|
|
705
|
+
});
|
|
706
|
+
const out = new Uint8Array(total);
|
|
707
|
+
let pos = 0;
|
|
708
|
+
for (const chunk of chunks) {
|
|
709
|
+
out.set(chunk, pos);
|
|
710
|
+
pos += chunk.length;
|
|
711
|
+
}
|
|
712
|
+
return out;
|
|
713
|
+
}
|
|
714
|
+
function findEndOfCentralDirectory(bytes) {
|
|
715
|
+
const min = Math.max(0, bytes.length - EOCD_SIZE - MAX_COMMENT);
|
|
716
|
+
for (let i = bytes.length - EOCD_SIZE; i >= min; i--) {
|
|
717
|
+
if (readU32(bytes, i) === SIG_EOCD && i + EOCD_SIZE + readU16(bytes, i + 20) <= bytes.length) return i;
|
|
718
|
+
}
|
|
719
|
+
return -1;
|
|
720
|
+
}
|
|
721
|
+
async function readZip(data, options = {}) {
|
|
722
|
+
const bytes = toBytes(data);
|
|
723
|
+
const maxEntries = options.maxEntries ?? DEFAULT_ZIP_MAX_ENTRIES;
|
|
724
|
+
const maxTotalSize = options.maxTotalSize ?? DEFAULT_ZIP_MAX_TOTAL_SIZE;
|
|
725
|
+
const eocd = findEndOfCentralDirectory(bytes);
|
|
726
|
+
if (eocd < 0) throw new ZipError("invalid_archive", "Not a ZIP archive (no end of central directory record).");
|
|
727
|
+
let diskNumber = readU16(bytes, eocd + 4);
|
|
728
|
+
let cdDisk = readU16(bytes, eocd + 6);
|
|
729
|
+
let count = readU16(bytes, eocd + 10);
|
|
730
|
+
let cdSize = readU32(bytes, eocd + 12);
|
|
731
|
+
let cdOffset = readU32(bytes, eocd + 16);
|
|
732
|
+
if (count === 65535 || cdSize === 4294967295 || cdOffset === 4294967295) {
|
|
733
|
+
const locator = eocd - 20;
|
|
734
|
+
if (locator < 0 || readU32(bytes, locator) !== SIG_ZIP64_LOCATOR) {
|
|
735
|
+
throw new ZipError("invalid_archive", "ZIP64 archive is missing its end of central directory locator.");
|
|
736
|
+
}
|
|
737
|
+
const record = readU64(bytes, locator + 8);
|
|
738
|
+
if (record + 56 > bytes.length || readU32(bytes, record) !== SIG_ZIP64_EOCD) {
|
|
739
|
+
throw new ZipError("invalid_archive", "ZIP64 end of central directory record not found.");
|
|
740
|
+
}
|
|
741
|
+
diskNumber = readU32(bytes, record + 16);
|
|
742
|
+
cdDisk = readU32(bytes, record + 20);
|
|
743
|
+
count = readU64(bytes, record + 32);
|
|
744
|
+
cdSize = readU64(bytes, record + 40);
|
|
745
|
+
cdOffset = readU64(bytes, record + 48);
|
|
746
|
+
}
|
|
747
|
+
if (diskNumber !== 0 || cdDisk !== 0) {
|
|
748
|
+
throw new ZipError("unsupported_feature", "Multi-disk (spanned) ZIP archives are not supported.");
|
|
749
|
+
}
|
|
750
|
+
if (count > maxEntries) {
|
|
751
|
+
throw new ZipError("too_many_entries", `ZIP archive has ${count} entries; the limit is ${maxEntries}.`);
|
|
752
|
+
}
|
|
753
|
+
if (cdOffset + cdSize > bytes.length) {
|
|
754
|
+
throw new ZipError("invalid_archive", "ZIP central directory lies outside the file.");
|
|
755
|
+
}
|
|
756
|
+
const entries = [];
|
|
757
|
+
let p = cdOffset;
|
|
758
|
+
for (let i = 0; i < count; i++) {
|
|
759
|
+
if (p + 46 > bytes.length || readU32(bytes, p) !== SIG_CENTRAL) {
|
|
760
|
+
throw new ZipError("invalid_archive", "ZIP central directory is corrupt.");
|
|
761
|
+
}
|
|
762
|
+
const flags = readU16(bytes, p + 8);
|
|
763
|
+
const method = readU16(bytes, p + 10);
|
|
764
|
+
const crc32 = readU32(bytes, p + 16);
|
|
765
|
+
let compressedSize = readU32(bytes, p + 20);
|
|
766
|
+
let size = readU32(bytes, p + 24);
|
|
767
|
+
const nameLength = readU16(bytes, p + 28);
|
|
768
|
+
const extraLength = readU16(bytes, p + 30);
|
|
769
|
+
const commentLength = readU16(bytes, p + 32);
|
|
770
|
+
let localHeaderOffset = readU32(bytes, p + 42);
|
|
771
|
+
const nameStart = p + 46;
|
|
772
|
+
const extraStart = nameStart + nameLength;
|
|
773
|
+
const next = extraStart + extraLength + commentLength;
|
|
774
|
+
if (next > bytes.length) throw new ZipError("invalid_archive", "ZIP central directory is truncated.");
|
|
775
|
+
for (let e = extraStart; e + 4 <= extraStart + extraLength; ) {
|
|
776
|
+
const id = readU16(bytes, e);
|
|
777
|
+
const len = readU16(bytes, e + 2);
|
|
778
|
+
if (id === 1) {
|
|
779
|
+
let f = e + 4;
|
|
780
|
+
const end = f + len;
|
|
781
|
+
if (size === 4294967295 && f + 8 <= end) {
|
|
782
|
+
size = readU64(bytes, f);
|
|
783
|
+
f += 8;
|
|
784
|
+
}
|
|
785
|
+
if (compressedSize === 4294967295 && f + 8 <= end) {
|
|
786
|
+
compressedSize = readU64(bytes, f);
|
|
787
|
+
f += 8;
|
|
788
|
+
}
|
|
789
|
+
if (localHeaderOffset === 4294967295 && f + 8 <= end) localHeaderOffset = readU64(bytes, f);
|
|
790
|
+
}
|
|
791
|
+
e += 4 + len;
|
|
792
|
+
}
|
|
793
|
+
const rawName = decodeName(bytes.subarray(nameStart, extraStart), (flags & 2048) !== 0);
|
|
794
|
+
const name = rawName.replace(/\\/g, "/");
|
|
795
|
+
p = next;
|
|
796
|
+
if (isUnsafeZipPath(rawName)) {
|
|
797
|
+
if (options.unsafePaths === "skip") continue;
|
|
798
|
+
throw new ZipError("unsafe_path", `ZIP entry name "${rawName}" is absolute or escapes the archive root.`, {
|
|
799
|
+
entryName: rawName
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
entries.push({
|
|
803
|
+
name,
|
|
804
|
+
isDirectory: name.endsWith("/"),
|
|
805
|
+
encrypted: (flags & 1) !== 0,
|
|
806
|
+
method,
|
|
807
|
+
compressedSize,
|
|
808
|
+
size,
|
|
809
|
+
crc32,
|
|
810
|
+
localHeaderOffset
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
return new ZipArchive(bytes, entries, maxTotalSize);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
export {
|
|
817
|
+
NORMAL_STYLE_ID,
|
|
818
|
+
isValidStyleId,
|
|
819
|
+
DEFAULT_STYLES,
|
|
820
|
+
StyleRegistry,
|
|
821
|
+
safeCSSValue,
|
|
822
|
+
createStyleRegistry,
|
|
823
|
+
ZipError,
|
|
824
|
+
DEFAULT_ZIP_MAX_ENTRIES,
|
|
825
|
+
DEFAULT_ZIP_MAX_TOTAL_SIZE,
|
|
826
|
+
zipCrc32,
|
|
827
|
+
isUnsafeZipPath,
|
|
828
|
+
ZipArchive,
|
|
829
|
+
readZip
|
|
830
|
+
};
|
|
831
|
+
//# sourceMappingURL=chunk-MR2764X2.js.map
|