@bendyline/squisq 2.11.7 → 2.11.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Doc-BkxT7G80.d.ts → Doc-CynsGqL4.d.ts} +208 -1
- package/dist/{ImageEditDoc-C8ZB54Vv.d.ts → ImageEditDoc-BVZe_5y4.d.ts} +1 -1
- package/dist/{chunk-SKKBRLLM.js → chunk-5FBI3ZW4.js} +7 -42
- package/dist/{chunk-YGGYYBJS.js → chunk-JJO6JFNZ.js} +1 -1
- package/dist/{chunk-SOIV7KDO.js → chunk-JM2QARY2.js} +42 -10
- package/dist/chunk-JSGIWW35.js +70 -0
- package/dist/{chunk-IYZTF4VO.js → chunk-KFZOCWPK.js} +2 -2
- package/dist/chunk-L7QZORLB.js +230 -0
- package/dist/chunk-NEWNNLWN.js +64 -0
- package/dist/{chunk-6ECMNUIP.js → chunk-OU5FYBOB.js} +170 -4
- package/dist/{chunk-3YNFMFNK.js → chunk-RMCUZDW5.js} +17 -8
- package/dist/{chunk-XJXBI3N5.js → chunk-XZVBR5BP.js} +1 -1
- package/dist/{chunk-GAZKTT4R.js → chunk-YNO6JV36.js} +96 -33
- package/dist/{chunk-EF353KGH.js → chunk-ZEONJCP3.js} +1 -1
- package/dist/doc/index.d.ts +5 -5
- package/dist/doc/index.js +22 -20
- package/dist/fence/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/imageEdit/index.d.ts +47 -4
- package/dist/imageEdit/index.js +5 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +48 -39
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/jsonForm/index.js +4 -2
- package/dist/markdown/index.js +3 -3
- package/dist/{materializePageSection-Dp2aHFbc.d.ts → materializePageSection-CAJoa8fh.d.ts} +1 -1
- package/dist/mediaEdit/index.d.ts +467 -0
- package/dist/mediaEdit/index.js +990 -0
- package/dist/narration/index.d.ts +20 -12
- package/dist/narration/index.js +6 -4
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/index.js +7 -3
- package/dist/{themeLibrary-BbSjnGmd.d.ts → themeLibrary-z7t3x5CU.d.ts} +1 -1
- package/dist/timing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +2 -2
- package/dist/transform/index.js +6 -5
- package/package.json +6 -1
- package/dist/{chunk-CW24CH7I.js → chunk-HUXK7EOT.js} +3 -3
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// src/mediaEdit/recipe.ts
|
|
2
|
+
var MEDIA_FX_ORDER = ["highpass", "denoise", "debreath", "loudness"];
|
|
3
|
+
var MEDIA_FX_SPECS = Object.freeze({
|
|
4
|
+
highpass: { defaultValue: 80, min: 20, max: 300 },
|
|
5
|
+
denoise: { defaultValue: 0.8, min: 0, max: 1 },
|
|
6
|
+
debreath: { defaultValue: -15, min: -40, max: 0 },
|
|
7
|
+
loudness: { defaultValue: -16, min: -31, max: -5 }
|
|
8
|
+
});
|
|
9
|
+
var MEDIA_EDIT_PARAMS = [
|
|
10
|
+
{ key: "fx", htmlSuffix: "fx" },
|
|
11
|
+
{ key: "cuts", htmlSuffix: "cuts" },
|
|
12
|
+
{ key: "gain", htmlSuffix: "gain" },
|
|
13
|
+
{ key: "fadeIn", htmlSuffix: "fade-in" },
|
|
14
|
+
{ key: "fadeOut", htmlSuffix: "fade-out" },
|
|
15
|
+
{ key: "crop", htmlSuffix: "crop" },
|
|
16
|
+
{ key: "group", htmlSuffix: "group" }
|
|
17
|
+
];
|
|
18
|
+
function mediaEditHtmlAttribute(kind, key) {
|
|
19
|
+
const param = MEDIA_EDIT_PARAMS.find((p) => p.key === key);
|
|
20
|
+
return `data-squisq-${kind}-${param?.htmlSuffix ?? key}`;
|
|
21
|
+
}
|
|
22
|
+
function formatMediaNumber(value, digits = 3) {
|
|
23
|
+
const rounded = Number(value.toFixed(digits));
|
|
24
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
25
|
+
}
|
|
26
|
+
function finiteNumber(raw) {
|
|
27
|
+
if (raw == null) return null;
|
|
28
|
+
const trimmed = raw.trim();
|
|
29
|
+
if (!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(trimmed)) return null;
|
|
30
|
+
const n = Number(trimmed);
|
|
31
|
+
return Number.isFinite(n) ? n : null;
|
|
32
|
+
}
|
|
33
|
+
function clamp(value, min, max) {
|
|
34
|
+
return Math.min(max, Math.max(min, value));
|
|
35
|
+
}
|
|
36
|
+
function parseSeconds(raw) {
|
|
37
|
+
const trimmed = raw.trim();
|
|
38
|
+
const bare = finiteNumber(trimmed);
|
|
39
|
+
if (bare != null) return bare >= 0 ? bare : null;
|
|
40
|
+
const ms = trimmed.match(/^(\d+(?:\.\d+)?)ms$/i);
|
|
41
|
+
if (ms) return Number(ms[1]) / 1e3;
|
|
42
|
+
const mmss = trimmed.match(/^(\d+):([0-5]?\d)(?:\.(\d+))?$/);
|
|
43
|
+
if (mmss) return Number(mmss[1]) * 60 + Number(mmss[2]) + Number(`0.${mmss[3] ?? "0"}`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function isFxOpId(name) {
|
|
47
|
+
return MEDIA_FX_ORDER.includes(name);
|
|
48
|
+
}
|
|
49
|
+
function parseMediaFx(raw) {
|
|
50
|
+
if (raw == null) return null;
|
|
51
|
+
const tokens = raw.split(/[\s,]+/).filter(Boolean);
|
|
52
|
+
if (tokens.length === 0) return null;
|
|
53
|
+
const byId = /* @__PURE__ */ new Map();
|
|
54
|
+
const unknown = [];
|
|
55
|
+
for (const token of tokens) {
|
|
56
|
+
const colon = token.indexOf(":");
|
|
57
|
+
const name = (colon === -1 ? token : token.slice(0, colon)).toLowerCase();
|
|
58
|
+
if (!isFxOpId(name)) {
|
|
59
|
+
unknown.push(token);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const spec = MEDIA_FX_SPECS[name];
|
|
63
|
+
let value = spec.defaultValue;
|
|
64
|
+
if (colon !== -1) {
|
|
65
|
+
value = finiteNumber(token.slice(colon + 1));
|
|
66
|
+
if (value != null && name === "debreath") value = -Math.abs(value);
|
|
67
|
+
}
|
|
68
|
+
if (value == null) {
|
|
69
|
+
unknown.push(token);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
byId.set(name, clamp(value, spec.min, spec.max));
|
|
73
|
+
}
|
|
74
|
+
const ops = MEDIA_FX_ORDER.filter((id) => byId.has(id)).map((id) => ({
|
|
75
|
+
id,
|
|
76
|
+
value: byId.get(id)
|
|
77
|
+
}));
|
|
78
|
+
return { ops, unknown };
|
|
79
|
+
}
|
|
80
|
+
function serializeMediaFx(chain) {
|
|
81
|
+
return [
|
|
82
|
+
...chain.ops.map((op) => `${op.id}:${formatMediaNumber(op.value)}`),
|
|
83
|
+
...chain.unknown
|
|
84
|
+
].join(" ");
|
|
85
|
+
}
|
|
86
|
+
function hasRenderableFx(chain) {
|
|
87
|
+
return chain != null && chain.ops.length > 0;
|
|
88
|
+
}
|
|
89
|
+
var CUT_EPSILON = 1e-3;
|
|
90
|
+
function normalizeMediaCuts(cuts, windowStart = 0, windowEnd = Number.POSITIVE_INFINITY) {
|
|
91
|
+
const clamped = cuts.filter((c) => Number.isFinite(c.start) && Number.isFinite(c.end)).map((c) => ({
|
|
92
|
+
start: Math.max(windowStart, Math.min(c.start, c.end)),
|
|
93
|
+
end: Math.min(windowEnd, Math.max(c.start, c.end))
|
|
94
|
+
})).filter((c) => c.end - c.start >= CUT_EPSILON).sort((a, b) => a.start - b.start);
|
|
95
|
+
const merged = [];
|
|
96
|
+
for (const cut of clamped) {
|
|
97
|
+
const last = merged[merged.length - 1];
|
|
98
|
+
if (last && cut.start <= last.end + CUT_EPSILON) {
|
|
99
|
+
last.end = Math.max(last.end, cut.end);
|
|
100
|
+
} else {
|
|
101
|
+
merged.push({ ...cut });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return merged;
|
|
105
|
+
}
|
|
106
|
+
function parseMediaCuts(raw) {
|
|
107
|
+
if (raw == null) return null;
|
|
108
|
+
const cuts = [];
|
|
109
|
+
for (const token of raw.split(/[\s,]+/).filter(Boolean)) {
|
|
110
|
+
const dash = token.indexOf("-", 1);
|
|
111
|
+
if (dash === -1) continue;
|
|
112
|
+
const start = parseSeconds(token.slice(0, dash));
|
|
113
|
+
const end = parseSeconds(token.slice(dash + 1));
|
|
114
|
+
if (start == null || end == null || end <= start) continue;
|
|
115
|
+
cuts.push({ start, end });
|
|
116
|
+
}
|
|
117
|
+
const normalized = normalizeMediaCuts(cuts);
|
|
118
|
+
return normalized.length > 0 ? normalized : null;
|
|
119
|
+
}
|
|
120
|
+
function serializeMediaCuts(cuts) {
|
|
121
|
+
return normalizeMediaCuts(cuts).map((c) => `${formatMediaNumber(c.start)}-${formatMediaNumber(c.end)}`).join(" ");
|
|
122
|
+
}
|
|
123
|
+
var MIN_CROP_EDGE = 0.02;
|
|
124
|
+
function parseMediaCrop(raw) {
|
|
125
|
+
if (raw == null) return null;
|
|
126
|
+
const parts = raw.split(/[\s,]+/).filter(Boolean).map((p) => finiteNumber(p));
|
|
127
|
+
if (parts.length !== 4 || parts.some((p) => p == null)) return null;
|
|
128
|
+
return normalizeMediaCrop({
|
|
129
|
+
x: parts[0],
|
|
130
|
+
y: parts[1],
|
|
131
|
+
w: parts[2],
|
|
132
|
+
h: parts[3]
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function normalizeMediaCrop(crop) {
|
|
136
|
+
const x = clamp(crop.x, 0, 1 - MIN_CROP_EDGE);
|
|
137
|
+
const y = clamp(crop.y, 0, 1 - MIN_CROP_EDGE);
|
|
138
|
+
const w = clamp(crop.w, MIN_CROP_EDGE, 1 - x);
|
|
139
|
+
const h = clamp(crop.h, MIN_CROP_EDGE, 1 - y);
|
|
140
|
+
const full = x < 1e-4 && y < 1e-4 && w > 1 - 1e-4 && h > 1 - 1e-4;
|
|
141
|
+
return full ? null : { x, y, w, h };
|
|
142
|
+
}
|
|
143
|
+
function serializeMediaCrop(crop) {
|
|
144
|
+
return [crop.x, crop.y, crop.w, crop.h].map((n) => formatMediaNumber(n, 4)).join(" ");
|
|
145
|
+
}
|
|
146
|
+
var GAIN_RANGE = { min: -60, max: 24 };
|
|
147
|
+
var GROUP_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
148
|
+
function parseMediaEdits(values) {
|
|
149
|
+
const edits = {};
|
|
150
|
+
const fx = parseMediaFx(values.fx);
|
|
151
|
+
if (fx && (fx.ops.length > 0 || fx.unknown.length > 0)) edits.fx = fx;
|
|
152
|
+
const cuts = parseMediaCuts(values.cuts);
|
|
153
|
+
if (cuts) edits.cuts = cuts;
|
|
154
|
+
const gain = finiteNumber(values.gain);
|
|
155
|
+
if (gain != null && gain !== 0) edits.gain = clamp(gain, GAIN_RANGE.min, GAIN_RANGE.max);
|
|
156
|
+
const fadeIn = values.fadeIn == null ? null : parseSeconds(values.fadeIn);
|
|
157
|
+
if (fadeIn != null && fadeIn > 0) edits.fadeIn = fadeIn;
|
|
158
|
+
const fadeOut = values.fadeOut == null ? null : parseSeconds(values.fadeOut);
|
|
159
|
+
if (fadeOut != null && fadeOut > 0) edits.fadeOut = fadeOut;
|
|
160
|
+
const crop = parseMediaCrop(values.crop);
|
|
161
|
+
if (crop) edits.crop = crop;
|
|
162
|
+
const group = values.group?.trim();
|
|
163
|
+
if (group && GROUP_RE.test(group)) edits.group = group;
|
|
164
|
+
return Object.keys(edits).length > 0 ? edits : void 0;
|
|
165
|
+
}
|
|
166
|
+
function serializeMediaEdits(edits) {
|
|
167
|
+
const fx = edits?.fx && (edits.fx.ops.length > 0 || edits.fx.unknown.length > 0);
|
|
168
|
+
return {
|
|
169
|
+
fx: fx && edits?.fx ? serializeMediaFx(edits.fx) : null,
|
|
170
|
+
cuts: edits?.cuts && edits.cuts.length > 0 ? serializeMediaCuts(edits.cuts) : null,
|
|
171
|
+
gain: edits?.gain ? formatMediaNumber(edits.gain, 2) : null,
|
|
172
|
+
fadeIn: edits?.fadeIn ? formatMediaNumber(edits.fadeIn) : null,
|
|
173
|
+
fadeOut: edits?.fadeOut ? formatMediaNumber(edits.fadeOut) : null,
|
|
174
|
+
crop: edits?.crop ? serializeMediaCrop(edits.crop) : null,
|
|
175
|
+
group: edits?.group ?? null
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function mediaEditValuesFromParams(params) {
|
|
179
|
+
const out = {};
|
|
180
|
+
for (const { key } of MEDIA_EDIT_PARAMS) {
|
|
181
|
+
if (params[key] != null) out[key] = params[key];
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
function mediaEditValuesFromAttributes(kind, attributes) {
|
|
186
|
+
const out = {};
|
|
187
|
+
for (const { key } of MEDIA_EDIT_PARAMS) {
|
|
188
|
+
const value = attributes[mediaEditHtmlAttribute(kind, key)];
|
|
189
|
+
if (value != null) out[key] = value;
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function fnv1a64Hex(text) {
|
|
194
|
+
let h1 = 2166136261;
|
|
195
|
+
let h2 = 16777619 ^ 1540483477;
|
|
196
|
+
for (let i = 0; i < text.length; i++) {
|
|
197
|
+
const c = text.charCodeAt(i);
|
|
198
|
+
h1 = Math.imul(h1 ^ c, 16777619);
|
|
199
|
+
h2 = Math.imul(h2 ^ c, 1540483477);
|
|
200
|
+
h2 ^= h2 >>> 15;
|
|
201
|
+
}
|
|
202
|
+
return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0");
|
|
203
|
+
}
|
|
204
|
+
function mediaRenderKey(src, fx) {
|
|
205
|
+
const canonical = fx.ops.map((op) => `${op.id}:${formatMediaNumber(op.value)}`).join(" ");
|
|
206
|
+
return fnv1a64Hex(`${src}
|
|
207
|
+
${canonical}`).slice(0, 12);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
MEDIA_FX_ORDER,
|
|
212
|
+
MEDIA_FX_SPECS,
|
|
213
|
+
MEDIA_EDIT_PARAMS,
|
|
214
|
+
mediaEditHtmlAttribute,
|
|
215
|
+
formatMediaNumber,
|
|
216
|
+
parseMediaFx,
|
|
217
|
+
serializeMediaFx,
|
|
218
|
+
hasRenderableFx,
|
|
219
|
+
normalizeMediaCuts,
|
|
220
|
+
parseMediaCuts,
|
|
221
|
+
serializeMediaCuts,
|
|
222
|
+
parseMediaCrop,
|
|
223
|
+
normalizeMediaCrop,
|
|
224
|
+
serializeMediaCrop,
|
|
225
|
+
parseMediaEdits,
|
|
226
|
+
serializeMediaEdits,
|
|
227
|
+
mediaEditValuesFromParams,
|
|
228
|
+
mediaEditValuesFromAttributes,
|
|
229
|
+
mediaRenderKey
|
|
230
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/mediaEdit/dsp/biquad.ts
|
|
2
|
+
var BUTTERWORTH_Q = Math.SQRT1_2;
|
|
3
|
+
function createBiquadState() {
|
|
4
|
+
return { x1: 0, x2: 0, y1: 0, y2: 0 };
|
|
5
|
+
}
|
|
6
|
+
function omega(sampleRate, freqHz) {
|
|
7
|
+
const w0 = 2 * Math.PI * freqHz / sampleRate;
|
|
8
|
+
return { cosW0: Math.cos(w0), sinW0: Math.sin(w0) };
|
|
9
|
+
}
|
|
10
|
+
function highpassCoeffs(sampleRate, freqHz, q = BUTTERWORTH_Q) {
|
|
11
|
+
const { cosW0, sinW0 } = omega(sampleRate, freqHz);
|
|
12
|
+
const alpha = sinW0 / (2 * q);
|
|
13
|
+
const a0 = 1 + alpha;
|
|
14
|
+
return {
|
|
15
|
+
b0: (1 + cosW0) / 2 / a0,
|
|
16
|
+
b1: -(1 + cosW0) / a0,
|
|
17
|
+
b2: (1 + cosW0) / 2 / a0,
|
|
18
|
+
a1: -2 * cosW0 / a0,
|
|
19
|
+
a2: (1 - alpha) / a0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function lowpassCoeffs(sampleRate, freqHz, q = BUTTERWORTH_Q) {
|
|
23
|
+
const { cosW0, sinW0 } = omega(sampleRate, freqHz);
|
|
24
|
+
const alpha = sinW0 / (2 * q);
|
|
25
|
+
const a0 = 1 + alpha;
|
|
26
|
+
return {
|
|
27
|
+
b0: (1 - cosW0) / 2 / a0,
|
|
28
|
+
b1: (1 - cosW0) / a0,
|
|
29
|
+
b2: (1 - cosW0) / 2 / a0,
|
|
30
|
+
a1: -2 * cosW0 / a0,
|
|
31
|
+
a2: (1 - alpha) / a0
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function biquadRun(coeffs, state, input, output) {
|
|
35
|
+
let { x1, x2, y1, y2 } = state;
|
|
36
|
+
const { b0, b1, b2, a1, a2 } = coeffs;
|
|
37
|
+
for (let i = 0; i < input.length; i++) {
|
|
38
|
+
const x0 = input[i];
|
|
39
|
+
const y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
|
|
40
|
+
output[i] = y0;
|
|
41
|
+
x2 = x1;
|
|
42
|
+
x1 = x0;
|
|
43
|
+
y2 = y1;
|
|
44
|
+
y1 = y0;
|
|
45
|
+
}
|
|
46
|
+
return { x1, x2, y1, y2 };
|
|
47
|
+
}
|
|
48
|
+
function createBiquadCascade(stages) {
|
|
49
|
+
const states = stages.map(() => createBiquadState());
|
|
50
|
+
return {
|
|
51
|
+
process(block) {
|
|
52
|
+
for (let s = 0; s < stages.length; s++) {
|
|
53
|
+
states[s] = biquadRun(stages[s], states[s], block, block);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
highpassCoeffs,
|
|
61
|
+
lowpassCoeffs,
|
|
62
|
+
biquadRun,
|
|
63
|
+
createBiquadCascade
|
|
64
|
+
};
|
|
@@ -216,11 +216,11 @@ async function revertToImageEditVersion(container, version, options = {}) {
|
|
|
216
216
|
if (options.snapshotCurrent !== false) {
|
|
217
217
|
const saveOptions = { stateFilename: options.stateFilename };
|
|
218
218
|
if (options.doc !== void 0) saveOptions.doc = options.doc;
|
|
219
|
-
const
|
|
220
|
-
if (!
|
|
219
|
+
const result2 = await saveImageEditVersion(container, saveOptions);
|
|
220
|
+
if (!result2.saved && result2.reason !== "unchanged") {
|
|
221
221
|
return { reverted: false, snapshotted: null, reason: "snapshot-failed" };
|
|
222
222
|
}
|
|
223
|
-
snapshotted =
|
|
223
|
+
snapshotted = result2.version;
|
|
224
224
|
}
|
|
225
225
|
await writeImageEditDoc(container, doc, options.stateFilename);
|
|
226
226
|
return { reverted: true, snapshotted };
|
|
@@ -482,6 +482,170 @@ function escapeText(s) {
|
|
|
482
482
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
// src/imageEdit/animatedImage.ts
|
|
486
|
+
var MIN_FRAME_DELAY_MS = 10;
|
|
487
|
+
var CLAMPED_DELAY_MS = 100;
|
|
488
|
+
function inspectAnimatedImage(input) {
|
|
489
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
490
|
+
return inspectGif(bytes) ?? inspectPng(bytes) ?? inspectWebp(bytes);
|
|
491
|
+
}
|
|
492
|
+
var ANIMATABLE_EXTENSIONS = /* @__PURE__ */ new Set(["gif", "webp", "apng"]);
|
|
493
|
+
var ANIMATABLE_MIME = /^image\/(?:gif|webp|apng)(?=$|[;,\s])/i;
|
|
494
|
+
function mayBeAnimatedImage(srcOrMime) {
|
|
495
|
+
const value = srcOrMime.trim();
|
|
496
|
+
if (!value) return false;
|
|
497
|
+
if (/^data:/i.test(value)) return ANIMATABLE_MIME.test(value.slice(5));
|
|
498
|
+
if (ANIMATABLE_MIME.test(value)) return true;
|
|
499
|
+
const path = value.split(/[?#]/, 1)[0] ?? "";
|
|
500
|
+
const dot = path.lastIndexOf(".");
|
|
501
|
+
if (dot < 0 || dot < path.lastIndexOf("/")) return false;
|
|
502
|
+
return ANIMATABLE_EXTENSIONS.has(path.slice(dot + 1).toLowerCase());
|
|
503
|
+
}
|
|
504
|
+
function inspectGif(b) {
|
|
505
|
+
if (b.length < 13) return null;
|
|
506
|
+
if (b[0] !== 71 || b[1] !== 73 || b[2] !== 70 || b[3] !== 56) return null;
|
|
507
|
+
if (b[4] !== 55 && b[4] !== 57 || b[5] !== 97) return null;
|
|
508
|
+
let pos = 13 + colorTableSize(b[10]);
|
|
509
|
+
let frames = 0;
|
|
510
|
+
let duration = 0;
|
|
511
|
+
let pendingDelayMs = 0;
|
|
512
|
+
let playCount = 1;
|
|
513
|
+
while (pos < b.length) {
|
|
514
|
+
const marker = b[pos++];
|
|
515
|
+
if (marker === 59) break;
|
|
516
|
+
if (marker === 33) {
|
|
517
|
+
if (pos >= b.length) break;
|
|
518
|
+
const label = b[pos++];
|
|
519
|
+
if (label === 249 && pos + 3 < b.length && b[pos] >= 4) {
|
|
520
|
+
pendingDelayMs = (b[pos + 2] | b[pos + 3] << 8) * 10;
|
|
521
|
+
} else if (label === 255 && b[pos] === 11 && pos + 12 <= b.length) {
|
|
522
|
+
const id = ascii(b, pos + 1, 11);
|
|
523
|
+
const sub = pos + 12;
|
|
524
|
+
if ((id === "NETSCAPE2.0" || id === "ANIMEXTS1.0") && sub + 3 < b.length && b[sub] >= 3 && b[sub + 1] === 1) {
|
|
525
|
+
const loops = b[sub + 2] | b[sub + 3] << 8;
|
|
526
|
+
playCount = loops === 0 ? 0 : loops + 1;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
pos = skipSubBlocks(b, pos);
|
|
530
|
+
if (pos < 0) break;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (marker === 44) {
|
|
534
|
+
if (pos + 9 > b.length) break;
|
|
535
|
+
const packed = b[pos + 8];
|
|
536
|
+
pos += 9 + colorTableSize(packed);
|
|
537
|
+
pos += 1;
|
|
538
|
+
frames++;
|
|
539
|
+
duration += playedDelay(pendingDelayMs);
|
|
540
|
+
pendingDelayMs = 0;
|
|
541
|
+
pos = skipSubBlocks(b, pos);
|
|
542
|
+
if (pos < 0) break;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
return result("gif", frames, duration, playCount);
|
|
548
|
+
}
|
|
549
|
+
function colorTableSize(packed) {
|
|
550
|
+
return packed & 128 ? 3 * (1 << (packed & 7) + 1) : 0;
|
|
551
|
+
}
|
|
552
|
+
function skipSubBlocks(b, start) {
|
|
553
|
+
let pos = start;
|
|
554
|
+
while (pos < b.length) {
|
|
555
|
+
const size = b[pos];
|
|
556
|
+
pos += 1;
|
|
557
|
+
if (size === 0) return pos;
|
|
558
|
+
pos += size;
|
|
559
|
+
}
|
|
560
|
+
return -1;
|
|
561
|
+
}
|
|
562
|
+
var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
563
|
+
function inspectPng(b) {
|
|
564
|
+
if (b.length < PNG_SIGNATURE.length) return null;
|
|
565
|
+
for (let i = 0; i < PNG_SIGNATURE.length; i++) {
|
|
566
|
+
if (b[i] !== PNG_SIGNATURE[i]) return null;
|
|
567
|
+
}
|
|
568
|
+
let pos = PNG_SIGNATURE.length;
|
|
569
|
+
let hasAnimationControl = false;
|
|
570
|
+
let frames = 1;
|
|
571
|
+
let playCount = 1;
|
|
572
|
+
let duration = 0;
|
|
573
|
+
while (pos + 8 <= b.length) {
|
|
574
|
+
const length = readU32BE(b, pos);
|
|
575
|
+
const type = ascii(b, pos + 4, 4);
|
|
576
|
+
const data = pos + 8;
|
|
577
|
+
if (type === "acTL" && length >= 8 && data + 8 <= b.length) {
|
|
578
|
+
hasAnimationControl = true;
|
|
579
|
+
frames = readU32BE(b, data);
|
|
580
|
+
playCount = readU32BE(b, data + 4);
|
|
581
|
+
} else if (type === "fcTL" && length >= 26 && data + 26 <= b.length) {
|
|
582
|
+
const num = readU16BE(b, data + 20);
|
|
583
|
+
const den = readU16BE(b, data + 22) || 100;
|
|
584
|
+
duration += playedDelay(num * 1e3 / den);
|
|
585
|
+
} else if (type === "IDAT" && !hasAnimationControl) {
|
|
586
|
+
break;
|
|
587
|
+
} else if (type === "IEND") {
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
pos = data + length + 4;
|
|
591
|
+
}
|
|
592
|
+
return result("png", hasAnimationControl ? frames : 1, duration, playCount);
|
|
593
|
+
}
|
|
594
|
+
function inspectWebp(b) {
|
|
595
|
+
if (b.length < 12 || ascii(b, 0, 4) !== "RIFF" || ascii(b, 8, 4) !== "WEBP") return null;
|
|
596
|
+
let pos = 12;
|
|
597
|
+
let animationFlag = false;
|
|
598
|
+
let frames = 0;
|
|
599
|
+
let duration = 0;
|
|
600
|
+
let loopCount = null;
|
|
601
|
+
while (pos + 8 <= b.length) {
|
|
602
|
+
const type = ascii(b, pos, 4);
|
|
603
|
+
const size = readU32LE(b, pos + 4);
|
|
604
|
+
const data = pos + 8;
|
|
605
|
+
if (type === "VP8X" && size >= 10 && data < b.length) {
|
|
606
|
+
animationFlag = (b[data] & 2) !== 0;
|
|
607
|
+
if (!animationFlag) break;
|
|
608
|
+
} else if (type === "ANIM" && size >= 6 && data + 6 <= b.length) {
|
|
609
|
+
loopCount = b[data + 4] | b[data + 5] << 8;
|
|
610
|
+
} else if (type === "ANMF" && size >= 16 && data + 15 <= b.length) {
|
|
611
|
+
frames++;
|
|
612
|
+
duration += playedDelay(b[data + 12] | b[data + 13] << 8 | b[data + 14] << 16);
|
|
613
|
+
} else if ((type === "VP8 " || type === "VP8L") && !animationFlag) {
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
pos = data + size + (size & 1);
|
|
617
|
+
}
|
|
618
|
+
const frameCount = animationFlag ? frames : 1;
|
|
619
|
+
return result("webp", frameCount, duration, loopCount ?? 0);
|
|
620
|
+
}
|
|
621
|
+
function result(format, frameCount, durationMs, playCount) {
|
|
622
|
+
const animated = frameCount > 1;
|
|
623
|
+
return {
|
|
624
|
+
format,
|
|
625
|
+
animated,
|
|
626
|
+
frameCount: Math.max(frameCount, 1),
|
|
627
|
+
durationMs: animated ? Math.round(durationMs) : 0,
|
|
628
|
+
playCount: animated ? playCount : 1
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function playedDelay(ms) {
|
|
632
|
+
return ms <= MIN_FRAME_DELAY_MS ? CLAMPED_DELAY_MS : ms;
|
|
633
|
+
}
|
|
634
|
+
function ascii(b, start, length) {
|
|
635
|
+
let out = "";
|
|
636
|
+
for (let i = start; i < start + length && i < b.length; i++) out += String.fromCharCode(b[i]);
|
|
637
|
+
return out;
|
|
638
|
+
}
|
|
639
|
+
function readU16BE(b, pos) {
|
|
640
|
+
return b[pos] << 8 | b[pos + 1];
|
|
641
|
+
}
|
|
642
|
+
function readU32BE(b, pos) {
|
|
643
|
+
return (b[pos] << 24 | b[pos + 1] << 16 | b[pos + 2] << 8 | b[pos + 3]) >>> 0;
|
|
644
|
+
}
|
|
645
|
+
function readU32LE(b, pos) {
|
|
646
|
+
return (b[pos] | b[pos + 1] << 8 | b[pos + 2] << 16 | b[pos + 3] << 24) >>> 0;
|
|
647
|
+
}
|
|
648
|
+
|
|
485
649
|
export {
|
|
486
650
|
IMAGE_EDIT_STATE_FILENAME,
|
|
487
651
|
IMAGE_EDIT_ASSETS_PREFIX,
|
|
@@ -507,5 +671,7 @@ export {
|
|
|
507
671
|
coalesceImageEditVersions,
|
|
508
672
|
ImageEditVersionManager,
|
|
509
673
|
exportImageEditDoc,
|
|
510
|
-
buildSvgString
|
|
674
|
+
buildSvgString,
|
|
675
|
+
inspectAnimatedImage,
|
|
676
|
+
mayBeAnimatedImage
|
|
511
677
|
};
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
anchorPoint,
|
|
3
|
-
base64ToUtf8,
|
|
4
|
-
connectorPath,
|
|
5
|
-
lineStyleDasharray,
|
|
6
|
-
shapePath,
|
|
7
|
-
snapEndpoints
|
|
8
|
-
} from "./chunk-PUS54YU6.js";
|
|
9
1
|
import {
|
|
10
2
|
detectAsciiDiagram,
|
|
11
3
|
detectAsciiTimeline,
|
|
@@ -22,6 +14,14 @@ import {
|
|
|
22
14
|
pickAutoTemplate,
|
|
23
15
|
profileBlockContents
|
|
24
16
|
} from "./chunk-C3NAY5YP.js";
|
|
17
|
+
import {
|
|
18
|
+
anchorPoint,
|
|
19
|
+
base64ToUtf8,
|
|
20
|
+
connectorPath,
|
|
21
|
+
lineStyleDasharray,
|
|
22
|
+
shapePath,
|
|
23
|
+
snapEndpoints
|
|
24
|
+
} from "./chunk-PUS54YU6.js";
|
|
25
25
|
import {
|
|
26
26
|
DEFAULT_THEME,
|
|
27
27
|
FRONTMATTER_CUSTOM_TEMPLATES_KEY,
|
|
@@ -42,6 +42,11 @@ import {
|
|
|
42
42
|
validateTheme,
|
|
43
43
|
withAlpha
|
|
44
44
|
} from "./chunk-Y4D6TDZT.js";
|
|
45
|
+
import {
|
|
46
|
+
mediaEditValuesFromAttributes,
|
|
47
|
+
mediaEditValuesFromParams,
|
|
48
|
+
parseMediaEdits
|
|
49
|
+
} from "./chunk-L7QZORLB.js";
|
|
45
50
|
import {
|
|
46
51
|
KNOWN_BLOCK_META_KEYS,
|
|
47
52
|
TABLE_FED_TEMPLATES,
|
|
@@ -11133,6 +11138,8 @@ function toMediaClip(parsed, id) {
|
|
|
11133
11138
|
if (clipEnd != null) clip.clipEnd = clipEnd;
|
|
11134
11139
|
if (clip.lockToBlock === true) delete clip.clipEnd;
|
|
11135
11140
|
if (params.spillover === "true" || params.spillover === "") clip.spillover = true;
|
|
11141
|
+
const edits = parseMediaEdits(mediaEditValuesFromParams(params));
|
|
11142
|
+
if (edits) clip.edits = edits;
|
|
11136
11143
|
return clip;
|
|
11137
11144
|
}
|
|
11138
11145
|
function firstVideoElement(children) {
|
|
@@ -11195,6 +11202,8 @@ function htmlVideoClip(node, id) {
|
|
|
11195
11202
|
const clipEnd = time(element.attributes["data-squisq-video-clip-end"]);
|
|
11196
11203
|
if (clipStart != null) clip.clipStart = clipStart;
|
|
11197
11204
|
if (!locked && clipEnd != null) clip.clipEnd = clipEnd;
|
|
11205
|
+
const edits = parseMediaEdits(mediaEditValuesFromAttributes("video", element.attributes));
|
|
11206
|
+
if (edits) clip.edits = edits;
|
|
11198
11207
|
return clip;
|
|
11199
11208
|
}
|
|
11200
11209
|
function extractMediaFromContents(contents, makeId, blockId) {
|