@vosjs/studio-core 0.35.4 → 0.37.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/dist/index.d.ts +158 -5
- package/dist/index.js +561 -132
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3239,6 +3239,169 @@ function htmlLayerPayload(clip) {
|
|
|
3239
3239
|
};
|
|
3240
3240
|
}
|
|
3241
3241
|
|
|
3242
|
+
// src/richText/layout.ts
|
|
3243
|
+
var sameStyle = (a, b) => (a.f || 0) === (b.f || 0) && a.c === b.c && a.u === b.u && a.s === b.s && a.h === b.h;
|
|
3244
|
+
function graphemesOf(text) {
|
|
3245
|
+
if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
|
|
3246
|
+
const seg = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
3247
|
+
const out = [];
|
|
3248
|
+
for (const s of seg.segment(text)) out.push(s.segment);
|
|
3249
|
+
return out;
|
|
3250
|
+
}
|
|
3251
|
+
return Array.from(text);
|
|
3252
|
+
}
|
|
3253
|
+
function tokenRanges(line) {
|
|
3254
|
+
const out = [];
|
|
3255
|
+
const re = /\S+\s*/g;
|
|
3256
|
+
let m;
|
|
3257
|
+
while ((m = re.exec(line)) !== null) {
|
|
3258
|
+
out.push([m.index, m.index + m[0].length]);
|
|
3259
|
+
}
|
|
3260
|
+
return out.length ? out : [[0, line.length]];
|
|
3261
|
+
}
|
|
3262
|
+
function layoutText(input, measure) {
|
|
3263
|
+
const memo = {};
|
|
3264
|
+
const adv = (t, f) => {
|
|
3265
|
+
if (!t) return 0;
|
|
3266
|
+
const key = f + "\0" + t;
|
|
3267
|
+
let w = memo[key];
|
|
3268
|
+
if (w === void 0) {
|
|
3269
|
+
w = measure(t, f);
|
|
3270
|
+
memo[key] = w;
|
|
3271
|
+
}
|
|
3272
|
+
return w;
|
|
3273
|
+
};
|
|
3274
|
+
const slice = (runs, a, b) => {
|
|
3275
|
+
const out = [];
|
|
3276
|
+
let at = 0;
|
|
3277
|
+
for (let i = 0; i < runs.length; i++) {
|
|
3278
|
+
const r = runs[i];
|
|
3279
|
+
const from = Math.max(a, at);
|
|
3280
|
+
const to = Math.min(b, at + r.t.length);
|
|
3281
|
+
if (to > from) {
|
|
3282
|
+
const t = r.t.slice(from - at, to - at);
|
|
3283
|
+
const last = out[out.length - 1];
|
|
3284
|
+
if (last && sameStyle(last, r)) last.t += t;
|
|
3285
|
+
else out.push({ ...r, t });
|
|
3286
|
+
}
|
|
3287
|
+
at += r.t.length;
|
|
3288
|
+
if (at >= b) break;
|
|
3289
|
+
}
|
|
3290
|
+
return out;
|
|
3291
|
+
};
|
|
3292
|
+
const widthOf = (runs) => {
|
|
3293
|
+
let w = 0;
|
|
3294
|
+
for (let i = 0; i < runs.length; i++) w += adv(runs[i].t, runs[i].f || 0);
|
|
3295
|
+
return w;
|
|
3296
|
+
};
|
|
3297
|
+
const partsIn = (frags, a, b) => {
|
|
3298
|
+
const out = [];
|
|
3299
|
+
for (let i = 0; i < frags.length; i++) {
|
|
3300
|
+
const fr = frags[i];
|
|
3301
|
+
const from = Math.max(a, fr.o);
|
|
3302
|
+
const to = Math.min(b, fr.o + fr.t.length);
|
|
3303
|
+
if (to <= from) continue;
|
|
3304
|
+
const f = fr.f || 0;
|
|
3305
|
+
const before = adv(fr.t.slice(0, from - fr.o), f);
|
|
3306
|
+
out.push({
|
|
3307
|
+
...fr,
|
|
3308
|
+
t: fr.t.slice(from - fr.o, to - fr.o),
|
|
3309
|
+
x: fr.x + before,
|
|
3310
|
+
w: adv(fr.t.slice(0, to - fr.o), f) - before,
|
|
3311
|
+
o: from
|
|
3312
|
+
});
|
|
3313
|
+
}
|
|
3314
|
+
return out;
|
|
3315
|
+
};
|
|
3316
|
+
const unitOf = (frags, a, b) => {
|
|
3317
|
+
const parts = partsIn(frags, a, b);
|
|
3318
|
+
if (!parts.length) return { x: 0, w: 0, parts };
|
|
3319
|
+
const last = parts[parts.length - 1];
|
|
3320
|
+
return { x: parts[0].x, w: last.x + last.w - parts[0].x, parts };
|
|
3321
|
+
};
|
|
3322
|
+
const lines = [];
|
|
3323
|
+
const max = input.maxWidth && input.maxWidth > 0 ? input.maxWidth : 0;
|
|
3324
|
+
let width = 0;
|
|
3325
|
+
let base = 0;
|
|
3326
|
+
const push2 = (runs, a, b) => {
|
|
3327
|
+
const cut = slice(runs, a, b);
|
|
3328
|
+
const frags = [];
|
|
3329
|
+
let x = 0;
|
|
3330
|
+
let o = 0;
|
|
3331
|
+
for (let i = 0; i < cut.length; i++) {
|
|
3332
|
+
const w = adv(cut[i].t, cut[i].f || 0);
|
|
3333
|
+
frags.push({ ...cut[i], x, w, o });
|
|
3334
|
+
x += w;
|
|
3335
|
+
o += cut[i].t.length;
|
|
3336
|
+
}
|
|
3337
|
+
const line = { w: x, frags, a: base + a, n: b - a };
|
|
3338
|
+
if (input.unit) {
|
|
3339
|
+
const text = cut.map((r) => r.t).join("");
|
|
3340
|
+
const units = [];
|
|
3341
|
+
if (input.unit === "line") units.push(unitOf(frags, 0, text.length));
|
|
3342
|
+
else if (input.unit === "word") {
|
|
3343
|
+
const toks = tokenRanges(text);
|
|
3344
|
+
for (let i = 0; i < toks.length; i++) {
|
|
3345
|
+
units.push(unitOf(frags, toks[i][0], toks[i][1]));
|
|
3346
|
+
}
|
|
3347
|
+
} else {
|
|
3348
|
+
let at = 0;
|
|
3349
|
+
const gs = graphemesOf(text);
|
|
3350
|
+
for (let i = 0; i < gs.length; i++) {
|
|
3351
|
+
units.push(unitOf(frags, at, at + gs[i].length));
|
|
3352
|
+
at += gs[i].length;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
line.units = units;
|
|
3356
|
+
}
|
|
3357
|
+
if (x > width) width = x;
|
|
3358
|
+
lines.push(line);
|
|
3359
|
+
};
|
|
3360
|
+
for (let li = 0; li < input.lines.length; li++) {
|
|
3361
|
+
const runs = input.lines[li];
|
|
3362
|
+
let len = 0;
|
|
3363
|
+
for (let i = 0; i < runs.length; i++) len += runs[i].t.length;
|
|
3364
|
+
if (!max || !len || widthOf(slice(runs, 0, len)) <= max) {
|
|
3365
|
+
push2(runs, 0, len);
|
|
3366
|
+
} else {
|
|
3367
|
+
const text = runs.map((r) => r.t).join("");
|
|
3368
|
+
const toks = tokenRanges(text);
|
|
3369
|
+
let a = 0;
|
|
3370
|
+
let b = toks[0][1];
|
|
3371
|
+
for (let i = 1; i < toks.length; i++) {
|
|
3372
|
+
if (widthOf(slice(runs, a, toks[i][1])) <= max) b = toks[i][1];
|
|
3373
|
+
else {
|
|
3374
|
+
push2(runs, a, b);
|
|
3375
|
+
a = toks[i][0];
|
|
3376
|
+
b = toks[i][1];
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
push2(runs, a, b);
|
|
3380
|
+
}
|
|
3381
|
+
base += len + 1;
|
|
3382
|
+
}
|
|
3383
|
+
if (!lines.length) lines.push({ w: 0, frags: [], a: 0, n: 0 });
|
|
3384
|
+
return { lines, width };
|
|
3385
|
+
}
|
|
3386
|
+
var UNDERLINE_OFFSET_EM = 0.42;
|
|
3387
|
+
var DECORATION_THICKNESS_EM = 0.065;
|
|
3388
|
+
var STRIKE_OFFSET_EM = 0.02;
|
|
3389
|
+
var HIGHLIGHT_HALF_EM = 0.58;
|
|
3390
|
+
function decorationRect(kind, x, w, y, px) {
|
|
3391
|
+
const x0 = Math.round(x);
|
|
3392
|
+
const x1 = Math.round(x + w);
|
|
3393
|
+
if (kind === "h") {
|
|
3394
|
+
const top2 = Math.round(y - HIGHLIGHT_HALF_EM * px);
|
|
3395
|
+
const bottom = Math.round(y + HIGHLIGHT_HALF_EM * px);
|
|
3396
|
+
return { x: x0, y: top2, w: x1 - x0, h: bottom - top2 };
|
|
3397
|
+
}
|
|
3398
|
+
const t = Math.max(1, Math.round(DECORATION_THICKNESS_EM * px));
|
|
3399
|
+
const top = Math.round(
|
|
3400
|
+
y + (kind === "u" ? UNDERLINE_OFFSET_EM : STRIKE_OFFSET_EM) * px
|
|
3401
|
+
);
|
|
3402
|
+
return { x: x0, y: top, w: x1 - x0, h: t };
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3242
3405
|
// src/overlayText.ts
|
|
3243
3406
|
var OVERLAY_BOX_PAD_X = 0.6;
|
|
3244
3407
|
var OVERLAY_BOX_PAD_Y = 0.35;
|
|
@@ -3445,19 +3608,44 @@ function resolveEmphasis(clip) {
|
|
|
3445
3608
|
} : {}
|
|
3446
3609
|
};
|
|
3447
3610
|
}
|
|
3448
|
-
function
|
|
3449
|
-
let
|
|
3450
|
-
let
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
if (c === EM_OPEN || c === EM_CLOSE) {
|
|
3454
|
-
if (run) w += measure(run, b ? emFont : font);
|
|
3455
|
-
run = "";
|
|
3456
|
-
b = c === EM_OPEN;
|
|
3457
|
-
} else run += c;
|
|
3611
|
+
function hashOf(text) {
|
|
3612
|
+
let h = 2166136261;
|
|
3613
|
+
for (let i = 0; i < text.length; i++) {
|
|
3614
|
+
h ^= text.charCodeAt(i);
|
|
3615
|
+
h = Math.imul(h, 16777619);
|
|
3458
3616
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3617
|
+
return (h >>> 0).toString(36);
|
|
3618
|
+
}
|
|
3619
|
+
function styledTextOf(clip) {
|
|
3620
|
+
const em = resolveEmphasis(clip);
|
|
3621
|
+
if (!em) return null;
|
|
3622
|
+
const l = overlayLines(overlayDisplayText(clip)).map((line) => {
|
|
3623
|
+
const runs = [];
|
|
3624
|
+
let run = "";
|
|
3625
|
+
let bold = false;
|
|
3626
|
+
const flush = () => {
|
|
3627
|
+
if (!run) return;
|
|
3628
|
+
runs.push(
|
|
3629
|
+
bold ? { t: run, f: 1, ...em.color ? { c: em.color } : {} } : { t: run }
|
|
3630
|
+
);
|
|
3631
|
+
run = "";
|
|
3632
|
+
};
|
|
3633
|
+
for (const c of line) {
|
|
3634
|
+
if (c === EM_OPEN || c === EM_CLOSE) {
|
|
3635
|
+
flush();
|
|
3636
|
+
bold = c === EM_OPEN;
|
|
3637
|
+
} else run += c;
|
|
3638
|
+
}
|
|
3639
|
+
flush();
|
|
3640
|
+
return runs;
|
|
3641
|
+
});
|
|
3642
|
+
const fs = [{ w: resolveOverlayStyle(clip).weight }, { w: em.weight }];
|
|
3643
|
+
return {
|
|
3644
|
+
l,
|
|
3645
|
+
fs,
|
|
3646
|
+
...em.face ? { faces: [{ f: em.face.family, w: em.face.weight, u: em.face.url }] } : {},
|
|
3647
|
+
h: hashOf(JSON.stringify([l, fs]))
|
|
3648
|
+
};
|
|
3461
3649
|
}
|
|
3462
3650
|
function overlayTokens(line) {
|
|
3463
3651
|
return line.match(/\S+\s*/g) ?? [line];
|
|
@@ -3487,7 +3675,7 @@ function wrapOverlayLines(lines, measure, maxPx) {
|
|
|
3487
3675
|
}
|
|
3488
3676
|
return out.length ? out : [""];
|
|
3489
3677
|
}
|
|
3490
|
-
function
|
|
3678
|
+
function graphemesOf2(line) {
|
|
3491
3679
|
if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
|
|
3492
3680
|
return [
|
|
3493
3681
|
...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(
|
|
@@ -3502,14 +3690,14 @@ function overlaySegments(text, unit) {
|
|
|
3502
3690
|
if (unit === "block") return [];
|
|
3503
3691
|
if (unit === "line") return lines.map((l) => [l]);
|
|
3504
3692
|
if (unit === "word") return lines.map(overlayTokens);
|
|
3505
|
-
return lines.map(
|
|
3693
|
+
return lines.map(graphemesOf2);
|
|
3506
3694
|
}
|
|
3507
3695
|
var FX_DIR = { forward: 0, reverse: 1, center: 2 };
|
|
3508
3696
|
function resolveOverlayFx(clip, clipDuration) {
|
|
3509
3697
|
const spec = overlayFxSpec(clip) ?? clip.fx ?? null;
|
|
3510
3698
|
if (!spec) return null;
|
|
3511
3699
|
const unit = spec.unit ?? "block";
|
|
3512
|
-
const text = overlayDisplayText(clip);
|
|
3700
|
+
const text = stripEmphasis(overlayDisplayText(clip));
|
|
3513
3701
|
const units = overlaySegments(text, unit);
|
|
3514
3702
|
const n = unit === "block" ? 1 : units.reduce((sum, line) => sum + line.length, 0);
|
|
3515
3703
|
const upperN = unit === "line" && clip.maxWidth ? overlayLines(text).reduce(
|
|
@@ -3559,13 +3747,25 @@ function overlayRect(clip, measure, frameW, frameH = 1080, mediaAspect) {
|
|
|
3559
3747
|
const style = resolveOverlayStyle(clip);
|
|
3560
3748
|
const font = overlayFontString(style, scale, 1);
|
|
3561
3749
|
const ls = style.letterSpacing * scale;
|
|
3562
|
-
const
|
|
3563
|
-
const emFont = em ? overlayFontString({ ...style, weight: em.weight }, scale, 1) : "";
|
|
3564
|
-
const width = (t) => em ? measureEmphasized(t, (run, f) => measure(run, f, ls), font, emFont) : measure(t, font, ls);
|
|
3565
|
-
const text = overlayDisplayText(clip);
|
|
3566
|
-
const lines = clip.maxWidth ? wrapOverlayLines(overlayLines(text), width, clip.maxWidth * frameW) : overlayLines(text);
|
|
3750
|
+
const styled = styledTextOf(clip);
|
|
3567
3751
|
let w = 0;
|
|
3568
|
-
|
|
3752
|
+
let lineCount = 0;
|
|
3753
|
+
if (styled) {
|
|
3754
|
+
const fonts = styled.fs.map(
|
|
3755
|
+
(f) => overlayFontString({ ...style, weight: f.w }, scale, 1)
|
|
3756
|
+
);
|
|
3757
|
+
const laid = layoutText(
|
|
3758
|
+
{ lines: styled.l, maxWidth: clip.maxWidth ? clip.maxWidth * frameW : 0 },
|
|
3759
|
+
(t, f) => measure(t, fonts[f], ls)
|
|
3760
|
+
);
|
|
3761
|
+
w = laid.width;
|
|
3762
|
+
lineCount = laid.lines.length;
|
|
3763
|
+
} else {
|
|
3764
|
+
const width = (t) => measure(t, font, ls);
|
|
3765
|
+
const lines = clip.maxWidth ? wrapOverlayLines(overlayLines(clip.text), width, clip.maxWidth * frameW) : overlayLines(clip.text);
|
|
3766
|
+
for (const line of lines) w = Math.max(w, width(line));
|
|
3767
|
+
lineCount = lines.length;
|
|
3768
|
+
}
|
|
3569
3769
|
const lineH = style.size * scale * style.lineHeight;
|
|
3570
3770
|
const box = resolveOverlayBox(clip);
|
|
3571
3771
|
const padX = box ? box.padX * scale : 0;
|
|
@@ -3574,7 +3774,7 @@ function overlayRect(clip, measure, frameW, frameH = 1080, mediaAspect) {
|
|
|
3574
3774
|
...base,
|
|
3575
3775
|
w: Math.max(w, style.size * scale * 0.6) + padX * 2,
|
|
3576
3776
|
// empty text still selectable
|
|
3577
|
-
h:
|
|
3777
|
+
h: lineCount * lineH + padY * 2
|
|
3578
3778
|
};
|
|
3579
3779
|
}
|
|
3580
3780
|
function overlayHit(rect, px, py, padPx = 8) {
|
|
@@ -4317,6 +4517,11 @@ function push(keys, t, a) {
|
|
|
4317
4517
|
|
|
4318
4518
|
// src/lower/studioEntry.ts
|
|
4319
4519
|
import { timelineRuntimeCode } from "@vosjs/timeline/bundle";
|
|
4520
|
+
|
|
4521
|
+
// src/richText/layoutCode.ts
|
|
4522
|
+
var textLayoutCode = '"use strict";var __vosTextLayout=(()=>{var E=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var W=(n,r)=>{for(var h in r)E(n,h,{get:r[h],enumerable:!0})},j=(n,r,h,l)=>{if(r&&typeof r=="object"||typeof r=="function")for(let f of A(r))!N.call(n,f)&&f!==h&&E(n,f,{get:()=>r[f],enumerable:!(l=k(r,f))||l.enumerable});return n};var C=n=>j(E({},"__esModule",{value:!0}),n);var v={};W(v,{DECORATION_THICKNESS_EM:()=>U,HIGHLIGHT_HALF_EM:()=>I,STRIKE_OFFSET_EM:()=>H,UNDERLINE_OFFSET_EM:()=>O,decorationRect:()=>K,graphemesOf:()=>_,layoutText:()=>G,tokenRanges:()=>F});var D=(n,r)=>(n.f||0)===(r.f||0)&&n.c===r.c&&n.u===r.u&&n.s===r.s&&n.h===r.h;function _(n){if(typeof Intl<"u"&&typeof Intl.Segmenter=="function"){let r=new Intl.Segmenter(void 0,{granularity:"grapheme"}),h=[];for(let l of r.segment(n))h.push(l.segment);return h}return Array.from(n)}function F(n){let r=[],h=/\\S+\\s*/g,l;for(;(l=h.exec(n))!==null;)r.push([l.index,l.index+l[0].length]);return r.length?r:[[0,n.length]]}function G(n,r){let h={},l=(i,u)=>{if(!i)return 0;let s=u+"\\0"+i,t=h[s];return t===void 0&&(t=r(i,u),h[s]=t),t},f=(i,u,s)=>{let t=[],o=0;for(let e=0;e<i.length;e++){let c=i[e],a=Math.max(u,o),m=Math.min(s,o+c.t.length);if(m>a){let g=c.t.slice(a-o,m-o),x=t[t.length-1];x&&D(x,c)?x.t+=g:t.push({...c,t:g})}if(o+=c.t.length,o>=s)break}return t},p=i=>{let u=0;for(let s=0;s<i.length;s++)u+=l(i[s].t,i[s].f||0);return u},R=(i,u,s)=>{let t=[];for(let o=0;o<i.length;o++){let e=i[o],c=Math.max(u,e.o),a=Math.min(s,e.o+e.t.length);if(a<=c)continue;let m=e.f||0,g=l(e.t.slice(0,c-e.o),m);t.push({...e,t:e.t.slice(c-e.o,a-e.o),x:e.x+g,w:l(e.t.slice(0,a-e.o),m)-g,o:c})}return t},L=(i,u,s)=>{let t=R(i,u,s);if(!t.length)return{x:0,w:0,parts:t};let o=t[t.length-1];return{x:t[0].x,w:o.x+o.w-t[0].x,parts:t}},y=[],d=n.maxWidth&&n.maxWidth>0?n.maxWidth:0,w=0,T=0,S=(i,u,s)=>{let t=f(i,u,s),o=[],e=0,c=0;for(let m=0;m<t.length;m++){let g=l(t[m].t,t[m].f||0);o.push({...t[m],x:e,w:g,o:c}),e+=g,c+=t[m].t.length}let a={w:e,frags:o,a:T+u,n:s-u};if(n.unit){let m=t.map(x=>x.t).join(""),g=[];if(n.unit==="line")g.push(L(o,0,m.length));else if(n.unit==="word"){let x=F(m);for(let b=0;b<x.length;b++)g.push(L(o,x[b][0],x[b][1]))}else{let x=0,b=_(m);for(let M=0;M<b.length;M++)g.push(L(o,x,x+b[M].length)),x+=b[M].length}a.units=g}e>w&&(w=e),y.push(a)};for(let i=0;i<n.lines.length;i++){let u=n.lines[i],s=0;for(let t=0;t<u.length;t++)s+=u[t].t.length;if(!d||!s||p(f(u,0,s))<=d)S(u,0,s);else{let t=u.map(a=>a.t).join(""),o=F(t),e=0,c=o[0][1];for(let a=1;a<o.length;a++)p(f(u,e,o[a][1]))<=d||(S(u,e,c),e=o[a][0]),c=o[a][1];S(u,e,c)}T+=s+1}return y.length||y.push({w:0,frags:[],a:0,n:0}),{lines:y,width:w}}var O=.42,U=.065,H=.02,I=.58;function K(n,r,h,l,f){let p=Math.round(r),R=Math.round(r+h);if(n==="h"){let d=Math.round(l-I*f),w=Math.round(l+I*f);return{x:p,y:d,w:R-p,h:w-d}}let L=Math.max(1,Math.round(U*f)),y=Math.round(l+(n==="u"?O:H)*f);return{x:p,y,w:R-p,h:L}}return C(v);})();';
|
|
4523
|
+
|
|
4524
|
+
// src/lower/studioEntry.ts
|
|
4320
4525
|
var STUDIO_ENTRY_ID = "vosso.studio";
|
|
4321
4526
|
function studioEntry(data) {
|
|
4322
4527
|
return {
|
|
@@ -4713,6 +4918,18 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
4713
4918
|
var s = H / 1080 // scale design-px controls to comp px
|
|
4714
4919
|
var ns = window.__vos__ || {}
|
|
4715
4920
|
var playing = ns.isPaused === false
|
|
4921
|
+
// The styled-text layout (richText/layout.ts, generated), installed once:
|
|
4922
|
+
// the page lays a text layer out with the code the host picks with.
|
|
4923
|
+
if (!ns.textLayout) { ${textLayoutCode}; ns.textLayout = __vosTextLayout }
|
|
4924
|
+
var TXL = ns.textLayout
|
|
4925
|
+
// The font epoch counts faces landing. A face changes what text measures
|
|
4926
|
+
// and what it paints, so the epoch is part of every layout's key and of
|
|
4927
|
+
// the layer's signature: a late face repaints a held layer once.
|
|
4928
|
+
var ofBump = function () { ns.fontEpoch = (ns.fontEpoch || 0) + 1 }
|
|
4929
|
+
if (!ns.fontWatch && typeof document !== 'undefined' && document.fonts && document.fonts.addEventListener) {
|
|
4930
|
+
ns.fontWatch = 1
|
|
4931
|
+
document.fonts.addEventListener('loadingdone', ofBump)
|
|
4932
|
+
}
|
|
4716
4933
|
|
|
4717
4934
|
// --- audio clips (music/SFX): Web Audio scheduler against the transport ---
|
|
4718
4935
|
// Buffer sources are one-shot: (re)schedule everything on play / clip-data change
|
|
@@ -4840,7 +5057,19 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
4840
5057
|
if (t < ol0.start || t > ol0.start + ol0.dur) continue
|
|
4841
5058
|
// A page that taints on foreignObject draws no HTML layer (setup's probe).
|
|
4842
5059
|
if (ol0.html && ns.htmlSafe === false) continue
|
|
4843
|
-
olVisSig += ol0.id + ':' + (ol0.text || ol0.key) + ':' + ol0.x + ',' + ol0.y + ',' + ol0.scale + ',' + ol0.rot + ',' + (ol0.fs || ol0.w) + ',' + (ol0.color || ol0.radius) + ',' + (ol0.opacity == null ? 1 : ol0.opacity) + (ol0.fx ? ',' + ol0.fx.k + ol0.fx.u + ol0.fx.d + ol0.fx.st : '') + (ol0.mw ? ',w' + ol0.mw : '')
|
|
5060
|
+
olVisSig += ol0.id + ':' + (ol0.text || ol0.key) + ':' + ol0.x + ',' + ol0.y + ',' + ol0.scale + ',' + ol0.rot + ',' + (ol0.fs || ol0.w) + ',' + (ol0.color || ol0.radius) + ',' + (ol0.opacity == null ? 1 : ol0.opacity) + (ol0.fx ? ',' + ol0.fx.k + ol0.fx.u + ol0.fx.d + ol0.fx.st : '') + (ol0.mw ? ',w' + ol0.mw : '')
|
|
5061
|
+
// Everything else a held layer's pixels depend on. The signature is the
|
|
5062
|
+
// ONLY thing that repaints a hold, so a field missing here is an edit
|
|
5063
|
+
// that does not show until something else moves.
|
|
5064
|
+
if (ol0.lines) {
|
|
5065
|
+
olVisSig += '|' + ol0.lines.join('\\n') + '|' + ol0.weight + '|' + ol0.stack + '|' + (ol0.sty || '') + '|' + (ol0.ls || 0) + '|' + (ol0.lh || 0) + '|' + (ol0.align || '') + '|' + ol0.shadow
|
|
5066
|
+
+ (ol0.stroke ? '|s' + ol0.stroke.c + ol0.stroke.w : '')
|
|
5067
|
+
+ (ol0.box ? '|b' + ol0.box.c + ol0.box.o + ',' + ol0.box.px + ',' + ol0.box.py + ',' + ol0.box.r : '')
|
|
5068
|
+
+ (ol0.rt ? '|r' + ol0.rt.h : '')
|
|
5069
|
+
} else {
|
|
5070
|
+
olVisSig += '|' + (ol0.shadow || '') + (ol0.border ? '|' + ol0.border.width + ol0.border.color : '') + (ol0.crop ? '|c' + ol0.crop.x + ',' + ol0.crop.y + ',' + ol0.crop.w + ',' + ol0.crop.h : '')
|
|
5071
|
+
}
|
|
5072
|
+
olVisSig += ';'
|
|
4844
5073
|
// fx widens the entrance window to the whole staggered span (tt);
|
|
4845
5074
|
// without fx it is the legacy enter transition window.
|
|
4846
5075
|
var olEW = ol0.fx ? ol0.fx.tt : olTD
|
|
@@ -4878,8 +5107,20 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
4878
5107
|
if (ns.htmlErrors) delete ns.htmlErrors[hlK]
|
|
4879
5108
|
}
|
|
4880
5109
|
}
|
|
4881
|
-
|
|
4882
|
-
|
|
5110
|
+
// A styled layer's cached layout leaves with its clip.
|
|
5111
|
+
if (ns.textLayouts) {
|
|
5112
|
+
for (var tlK in ns.textLayouts) {
|
|
5113
|
+
var tlSeen = false
|
|
5114
|
+
for (var tlI = 0; tlI < ols.length; tlI++) if (ols[tlI].id === tlK) { tlSeen = true; break }
|
|
5115
|
+
if (!tlSeen) delete ns.textLayouts[tlK]
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
var ovSig = W + 'x' + H + '|' + (ns.fontEpoch || 0) + '|' + (ns.htmlSafe === false ? 0 : 1) + '|' + olVisSig
|
|
5119
|
+
// The layer repaints when what it shows changed (the signature), while
|
|
5120
|
+
// something on it animates, and ONCE more after an animation's last frame:
|
|
5121
|
+
// that frame was drawn a moment short of settled, and a picture that
|
|
5122
|
+
// landed between two frames is drawn by it too. A hold costs nothing.
|
|
5123
|
+
var ovDirty = ov.sig !== ovSig || olAnim || ov.wasAnim
|
|
4883
5124
|
// The layer cards visible this frame, with their pose, for the card
|
|
4884
5125
|
// planes below (the overlay loop collects them instead of painting).
|
|
4885
5126
|
var lcList = []
|
|
@@ -5108,7 +5349,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5108
5349
|
try {
|
|
5109
5350
|
var ofBF = new FontFace(ofB.family, 'url(' + ofB.url + ')', { weight: String(ofB.weight) })
|
|
5110
5351
|
document.fonts.add(ofBF)
|
|
5111
|
-
ofBF.load().
|
|
5352
|
+
ofBF.load().then(ofBump, function () {})
|
|
5112
5353
|
} catch (e) {}
|
|
5113
5354
|
}
|
|
5114
5355
|
if (ol.face) {
|
|
@@ -5118,20 +5359,22 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5118
5359
|
try {
|
|
5119
5360
|
var ofFace = new FontFace(ol.face.f, 'url(' + ol.face.u + ')', { weight: String(ol.face.w) })
|
|
5120
5361
|
document.fonts.add(ofFace)
|
|
5121
|
-
ofFace.load().
|
|
5362
|
+
ofFace.load().then(ofBump, function () {})
|
|
5122
5363
|
} catch (e) {}
|
|
5123
5364
|
}
|
|
5124
5365
|
}
|
|
5125
|
-
//
|
|
5126
|
-
// first
|
|
5127
|
-
if (ol.
|
|
5128
|
-
var
|
|
5129
|
-
|
|
5130
|
-
|
|
5366
|
+
// A styled layer's other faces, the same way (a live edit that styles
|
|
5367
|
+
// its first run paints without a LOAD).
|
|
5368
|
+
if (ol.rt && ol.rt.faces) {
|
|
5369
|
+
for (var rfj = 0; rfj < ol.rt.faces.length; rfj++) {
|
|
5370
|
+
var rtF = ol.rt.faces[rfj]
|
|
5371
|
+
var rtFK = rtF.f + '|' + rtF.w
|
|
5372
|
+
if (ofSet[rtFK]) continue
|
|
5373
|
+
ofSet[rtFK] = 1
|
|
5131
5374
|
try {
|
|
5132
|
-
var
|
|
5133
|
-
document.fonts.add(
|
|
5134
|
-
|
|
5375
|
+
var rtFace = new FontFace(rtF.f, 'url(' + rtF.u + ')', { weight: String(rtF.w) })
|
|
5376
|
+
document.fonts.add(rtFace)
|
|
5377
|
+
rtFace.load().then(ofBump, function () {})
|
|
5135
5378
|
} catch (e) {}
|
|
5136
5379
|
}
|
|
5137
5380
|
}
|
|
@@ -5150,68 +5393,143 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5150
5393
|
ovC.textAlign = 'center'
|
|
5151
5394
|
ovC.textBaseline = 'middle'
|
|
5152
5395
|
ovC.letterSpacing = ((ol.ls || 0) * olMS * s) + 'px'
|
|
5153
|
-
//
|
|
5154
|
-
//
|
|
5155
|
-
//
|
|
5156
|
-
//
|
|
5157
|
-
//
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
var
|
|
5194
|
-
|
|
5396
|
+
// A STYLED layer (ol.rt: lines of runs, each set its own way) is laid
|
|
5397
|
+
// out by the layout module, ONCE: the layout is kept per clip under a
|
|
5398
|
+
// key of everything it was measured with, so a frame that only moves,
|
|
5399
|
+
// fades or reveals the layer measures nothing. Fragments draw left to
|
|
5400
|
+
// right from each line's left edge; a per-unit entrance animates the
|
|
5401
|
+
// layout's units, each a few parts of fragments.
|
|
5402
|
+
if (ol.rt) {
|
|
5403
|
+
var rtFonts = [ovC.font]
|
|
5404
|
+
for (var rfi = 1; rfi < ol.rt.fs.length; rfi++) {
|
|
5405
|
+
rtFonts.push((ol.sty ? ol.sty + ' ' : '') + ol.rt.fs[rfi].w + ' ' + olPx + 'px ' + ol.stack)
|
|
5406
|
+
}
|
|
5407
|
+
var rtFx = ol.fx && ol.fx.units.length ? ol.fx : null
|
|
5408
|
+
var rtMax = ol.mw ? ol.mw * W : 0
|
|
5409
|
+
var rtKey = ol.rt.h + '|' + rtFonts[0] + '|' + ((ol.ls || 0) * olMS * s) + '|' + rtMax + '|' + (rtFx ? rtFx.u : '') + '|' + (ns.fontEpoch || 0)
|
|
5410
|
+
var rtAll = ns.textLayouts || (ns.textLayouts = {})
|
|
5411
|
+
var rtHit = rtAll[ol.id]
|
|
5412
|
+
if (!rtHit || rtHit.k !== rtKey) {
|
|
5413
|
+
rtHit = rtAll[ol.id] = {
|
|
5414
|
+
k: rtKey,
|
|
5415
|
+
L: TXL.layoutText(
|
|
5416
|
+
{ lines: ol.rt.l, maxWidth: rtMax, unit: rtFx ? rtFx.u : undefined },
|
|
5417
|
+
function (mt, mf) { ovC.font = rtFonts[mf]; return ovC.measureText(mt).width }
|
|
5418
|
+
)
|
|
5419
|
+
}
|
|
5420
|
+
ovC.font = rtFonts[0]
|
|
5421
|
+
}
|
|
5422
|
+
var rtL = rtHit.L
|
|
5423
|
+
var rtN = rtL.lines.length
|
|
5424
|
+
var rtLH = olPx * (ol.lh || ${OVERLAY_LINE_HEIGHT})
|
|
5425
|
+
var rtY0 = -((rtN - 1) * rtLH) / 2
|
|
5426
|
+
// A line's left edge: lines sit centred on the anchor and align
|
|
5427
|
+
// against the widest, as the plain layer's do.
|
|
5428
|
+
var rtLeft = function (lw) {
|
|
5429
|
+
var xo = ol.align === 'left' ? (lw - rtL.width) / 2 : ol.align === 'right' ? (rtL.width - lw) / 2 : 0
|
|
5430
|
+
return xo - lw / 2
|
|
5431
|
+
}
|
|
5432
|
+
if (ol.box) {
|
|
5433
|
+
var rbPX = ol.box.px * olMS * s
|
|
5434
|
+
var rbPY = ol.box.py * olMS * s
|
|
5435
|
+
var rbW = rtL.width + rbPX * 2
|
|
5436
|
+
var rbH = rtN * rtLH + rbPY * 2
|
|
5437
|
+
ovC.save()
|
|
5438
|
+
ovC.globalAlpha = olA * ol.box.o
|
|
5439
|
+
ovC.fillStyle = ol.box.c
|
|
5440
|
+
rr(-rbW / 2, -rbH / 2, rbW, rbH, Math.min(ol.box.r * olMS * s, rbH / 2), ovC)
|
|
5441
|
+
ovC.fill()
|
|
5442
|
+
ovC.restore()
|
|
5443
|
+
}
|
|
5444
|
+
// Highlights sit behind the words and take no legibility shadow.
|
|
5445
|
+
for (var rhi = 0; rhi < rtN; rhi++) {
|
|
5446
|
+
var rhLine = rtL.lines[rhi]
|
|
5447
|
+
for (var rhj = 0; rhj < rhLine.frags.length; rhj++) {
|
|
5448
|
+
var rhF = rhLine.frags[rhj]
|
|
5449
|
+
if (!rhF.h) continue
|
|
5450
|
+
var rhR = TXL.decorationRect('h', rtLeft(rhLine.w) + rhF.x, rhF.w, rtY0 + rhi * rtLH, olPx)
|
|
5451
|
+
ovC.fillStyle = rhF.h
|
|
5452
|
+
ovC.fillRect(rhR.x, rhR.y, rhR.w, rhR.h)
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
if (ol.shadow > 0) {
|
|
5456
|
+
ovC.shadowColor = 'rgba(0,0,0,' + ol.shadow + ')'
|
|
5457
|
+
ovC.shadowBlur = olPx * 0.25
|
|
5458
|
+
ovC.shadowOffsetY = olPx * 0.04
|
|
5459
|
+
}
|
|
5460
|
+
ovC.textAlign = 'left'
|
|
5461
|
+
// One fragment (or the part of one a unit holds): the underline under
|
|
5462
|
+
// the glyphs, stroke under fill, the strikethrough over them.
|
|
5463
|
+
var rtPart = function (p, bx, y) {
|
|
5464
|
+
ovC.font = rtFonts[p.f || 0]
|
|
5465
|
+
ovC.fillStyle = p.c || ol.color
|
|
5466
|
+
if (p.u) {
|
|
5467
|
+
var ru = TXL.decorationRect('u', bx + p.x, p.w, y, olPx)
|
|
5468
|
+
ovC.fillRect(ru.x, ru.y, ru.w, ru.h)
|
|
5469
|
+
}
|
|
5195
5470
|
if (ol.stroke) {
|
|
5196
5471
|
ovC.strokeStyle = ol.stroke.c
|
|
5197
5472
|
ovC.lineWidth = ol.stroke.w * olMS * s
|
|
5198
5473
|
ovC.lineJoin = 'round'
|
|
5199
|
-
ovC.strokeText(
|
|
5474
|
+
ovC.strokeText(p.t, bx + p.x, y)
|
|
5475
|
+
}
|
|
5476
|
+
ovC.fillText(p.t, bx + p.x, y)
|
|
5477
|
+
if (p.s) {
|
|
5478
|
+
var rs = TXL.decorationRect('s', bx + p.x, p.w, y, olPx)
|
|
5479
|
+
ovC.fillRect(rs.x, rs.y, rs.w, rs.h)
|
|
5200
5480
|
}
|
|
5201
|
-
ovC.fillText(drun, dx, y)
|
|
5202
|
-
dx += ovC.measureText(drun).width
|
|
5203
|
-
ovC.fillStyle = dfs
|
|
5204
|
-
drun = ''
|
|
5205
5481
|
}
|
|
5206
|
-
|
|
5207
|
-
var
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
}
|
|
5482
|
+
if (!rtFx) {
|
|
5483
|
+
for (var rli = 0; rli < rtN; rli++) {
|
|
5484
|
+
var rLine = rtL.lines[rli]
|
|
5485
|
+
var rBX = rtLeft(rLine.w)
|
|
5486
|
+
for (var rfk = 0; rfk < rLine.frags.length; rfk++) rtPart(rLine.frags[rfk], rBX, rtY0 + rli * rtLH)
|
|
5487
|
+
}
|
|
5488
|
+
} else {
|
|
5489
|
+
// Per-unit entrance, the plain layer's own arithmetic over the
|
|
5490
|
+
// layout's units: delay = order(index) x st, eased over dur.
|
|
5491
|
+
var rtCount = 0
|
|
5492
|
+
for (var rci = 0; rci < rtN; rci++) rtCount += rtL.lines[rci].units.length
|
|
5493
|
+
var rtIdx = 0
|
|
5494
|
+
for (var rui = 0; rui < rtN; rui++) {
|
|
5495
|
+
var rULine = rtL.lines[rui]
|
|
5496
|
+
var rUBX = rtLeft(rULine.w)
|
|
5497
|
+
var rUY = rtY0 + rui * rtLH
|
|
5498
|
+
for (var ruj = 0; ruj < rULine.units.length; ruj++, rtIdx++) {
|
|
5499
|
+
var rU = rULine.units[ruj]
|
|
5500
|
+
var rOrd = rtFx.d === 1 ? (rtCount - 1 - rtIdx) : rtFx.d === 2 ? Math.abs(rtIdx - (rtCount - 1) / 2) : rtIdx
|
|
5501
|
+
var rT2 = olT - rOrd * rtFx.st
|
|
5502
|
+
var rUA = 1, rUu = 1
|
|
5503
|
+
if (rtFx.k === 'typewriter') {
|
|
5504
|
+
rUA = rT2 >= 0 ? 1 : 0
|
|
5505
|
+
} else {
|
|
5506
|
+
rUu = Math.max(0, Math.min(1, rT2 / rtFx.dur))
|
|
5507
|
+
var rUE = 1 - Math.pow(1 - rUu, 3)
|
|
5508
|
+
rUA = rtFx.k === 'pop' ? Math.min(1, rUu * 2) : rUE
|
|
5509
|
+
}
|
|
5510
|
+
if (rUA <= 0.004) continue
|
|
5511
|
+
ovC.save()
|
|
5512
|
+
ovC.globalAlpha = olA * rUA
|
|
5513
|
+
if (rUu < 1) {
|
|
5514
|
+
if (rtFx.k === 'rise') {
|
|
5515
|
+
ovC.translate(0, (1 - (1 - Math.pow(1 - rUu, 3))) * 24 * s)
|
|
5516
|
+
} else if (rtFx.k === 'pop') {
|
|
5517
|
+
var rPS = 1 + 2.70158 * Math.pow(rUu - 1, 3) + 1.70158 * Math.pow(rUu - 1, 2)
|
|
5518
|
+
var rCX = rUBX + rU.x + rU.w / 2
|
|
5519
|
+
ovC.translate(rCX, rUY)
|
|
5520
|
+
ovC.scale(rPS, rPS)
|
|
5521
|
+
ovC.translate(-rCX, -rUY)
|
|
5522
|
+
} else if (rtFx.k === 'blur') {
|
|
5523
|
+
ovC.filter = 'blur(' + ((1 - rUu) * olPx * 0.12).toFixed(2) + 'px)'
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
for (var rpk = 0; rpk < rU.parts.length; rpk++) rtPart(rU.parts[rpk], rUBX, rUY)
|
|
5527
|
+
ovC.restore()
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5212
5530
|
}
|
|
5213
|
-
|
|
5214
|
-
|
|
5531
|
+
ovC.restore()
|
|
5532
|
+
continue
|
|
5215
5533
|
}
|
|
5216
5534
|
var olLines = ol.lines || ['']
|
|
5217
5535
|
// maxWidth wrap (ol.mw = frame-width fraction): greedy over word tokens
|
|
@@ -5224,7 +5542,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5224
5542
|
var olWrapped = []
|
|
5225
5543
|
for (var olwl = 0; olwl < olLines.length; olwl++) {
|
|
5226
5544
|
var olWLine = olLines[olwl]
|
|
5227
|
-
if (!olWLine ||
|
|
5545
|
+
if (!olWLine || ovC.measureText(olWLine).width <= olWMax) {
|
|
5228
5546
|
olWrapped.push(olWLine)
|
|
5229
5547
|
continue
|
|
5230
5548
|
}
|
|
@@ -5232,7 +5550,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5232
5550
|
var olCur = ''
|
|
5233
5551
|
for (var olti = 0; olti < olToks.length; olti++) {
|
|
5234
5552
|
if (!olCur) { olCur = olToks[olti]; continue }
|
|
5235
|
-
if (
|
|
5553
|
+
if (ovC.measureText(olCur + olToks[olti]).width <= olWMax) {
|
|
5236
5554
|
olCur += olToks[olti]
|
|
5237
5555
|
} else {
|
|
5238
5556
|
olWrapped.push(olCur)
|
|
@@ -5282,10 +5600,10 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5282
5600
|
olFx = { k: olFx.k, u: olFx.u, d: olFx.d, st: olFx.st, dur: olFx.dur, tt: olFx.tt, units: olRe, n: olReN }
|
|
5283
5601
|
}
|
|
5284
5602
|
var olLWs = null, olMaxW = 0
|
|
5285
|
-
if (ol.box || ol.align || olFx
|
|
5603
|
+
if (ol.box || ol.align || olFx) {
|
|
5286
5604
|
olLWs = []
|
|
5287
5605
|
for (var olwi = 0; olwi < olLines.length; olwi++) {
|
|
5288
|
-
var olw =
|
|
5606
|
+
var olw = ovC.measureText(olLines[olwi]).width
|
|
5289
5607
|
olLWs.push(olw)
|
|
5290
5608
|
if (olw > olMaxW) olMaxW = olw
|
|
5291
5609
|
}
|
|
@@ -5321,12 +5639,6 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5321
5639
|
: (olMaxW - olLWs[ok]) / 2
|
|
5322
5640
|
}
|
|
5323
5641
|
var olLY = olY0 + ok * olLH
|
|
5324
|
-
if (olEm) {
|
|
5325
|
-
// Runs draw LEFT-aligned from the centred line's left edge.
|
|
5326
|
-
ovC.textAlign = 'left'
|
|
5327
|
-
olDraw(olLines[ok], olXof - olLWs[ok] / 2, olLY, false)
|
|
5328
|
-
continue
|
|
5329
|
-
}
|
|
5330
5642
|
if (ol.stroke) {
|
|
5331
5643
|
ovC.strokeStyle = ol.stroke.c
|
|
5332
5644
|
ovC.lineWidth = ol.stroke.w * olMS * s
|
|
@@ -5363,10 +5675,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5363
5675
|
: olIdx
|
|
5364
5676
|
var olT2 = olT - olOrd * olFx.st
|
|
5365
5677
|
var olNext = olPref + olUs[ou]
|
|
5366
|
-
var olNW =
|
|
5367
|
-
// The emphasis state this unit starts in (a char unit inside a
|
|
5368
|
-
// marked word carries no mark of its own).
|
|
5369
|
-
var olUB = olEm ? olStateAfter(olPref) : false
|
|
5678
|
+
var olNW = ovC.measureText(olNext).width
|
|
5370
5679
|
var olUW = olNW - olPW
|
|
5371
5680
|
var olUX = olXb + olPW
|
|
5372
5681
|
var olUA = 1, olUu = 1
|
|
@@ -5395,11 +5704,6 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5395
5704
|
ovC.filter = 'blur(' + ((1 - olUu) * olPx * 0.12).toFixed(2) + 'px)'
|
|
5396
5705
|
}
|
|
5397
5706
|
}
|
|
5398
|
-
if (olEm) {
|
|
5399
|
-
olDraw(olUnit, olUX, olLY2, olUB)
|
|
5400
|
-
ovC.restore()
|
|
5401
|
-
continue
|
|
5402
|
-
}
|
|
5403
5707
|
if (ol.stroke) {
|
|
5404
5708
|
ovC.strokeStyle = ol.stroke.c
|
|
5405
5709
|
ovC.lineWidth = ol.stroke.w * olMS * s
|
|
@@ -5414,7 +5718,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
5414
5718
|
ovC.restore()
|
|
5415
5719
|
}
|
|
5416
5720
|
ov.sig = ovSig
|
|
5417
|
-
ov.
|
|
5721
|
+
ov.wasAnim = olAnim
|
|
5418
5722
|
if (ov.texture) ov.texture.needsUpdate = true
|
|
5419
5723
|
}
|
|
5420
5724
|
|
|
@@ -6396,6 +6700,21 @@ var FRAME_STEP_SRC = `
|
|
|
6396
6700
|
}
|
|
6397
6701
|
function stepBg(bgEl, bgT) {
|
|
6398
6702
|
if (!bgEl.paused) bgEl.pause()
|
|
6703
|
+
// Capture pages: the loop's frame by PTS from its own provider, like the
|
|
6704
|
+
// footage. The loop clock only moves forward between wraps, so the walk
|
|
6705
|
+
// stays sequential and a wrap is one re-seek to the keyframe at 0.
|
|
6706
|
+
var bwc = bgEl.__voilaWc
|
|
6707
|
+
if (bwc) {
|
|
6708
|
+
var bwT = Math.min(bgT, bwc.duration || bgT)
|
|
6709
|
+
if (bwc.req !== bwT) {
|
|
6710
|
+
var bwp = bwc.seek(bwT)
|
|
6711
|
+
if (ns.pendingDecodes) {
|
|
6712
|
+
ns.pendingDecodes.add(bwp)
|
|
6713
|
+
bwp.finally(function () { ns.pendingDecodes.delete(bwp) })
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
return
|
|
6717
|
+
}
|
|
6399
6718
|
var bgTarget = Math.min(bgT, bgEl.duration || bgT)
|
|
6400
6719
|
if (bgEl.readyState >= 1 && !bgEl.seeking && Math.abs(bgEl.currentTime - bgTarget) > 0.02) {
|
|
6401
6720
|
if (ns.pendingDecodes) {
|
|
@@ -6479,6 +6798,7 @@ var SETUP = `async (ctx) => {
|
|
|
6479
6798
|
// page has a memory budget) and FAIL-OPEN: any fetch trouble degrades to
|
|
6480
6799
|
// the plain network element, never a dead LOAD.
|
|
6481
6800
|
const BLOB_FETCH_MAX = 400 * 1024 * 1024
|
|
6801
|
+
const BG_BLOB_MAX = 64 * 1024 * 1024
|
|
6482
6802
|
const toBlobUrl = async (src) => {
|
|
6483
6803
|
const resp = await fetch(src)
|
|
6484
6804
|
if (!resp.ok) throw new Error('[voila] blob fetch HTTP ' + resp.status)
|
|
@@ -6679,9 +6999,48 @@ var SETUP = `async (ctx) => {
|
|
|
6679
6999
|
if (bgm && bgm.key) {
|
|
6680
7000
|
try {
|
|
6681
7001
|
if (bgm.kind === 'image') await loadImage(bgm.key)
|
|
6682
|
-
else
|
|
7002
|
+
else {
|
|
7003
|
+
const bgV = await load(bgm.key, true)
|
|
7004
|
+
bgV.loop = true
|
|
7005
|
+
// Capture pages: the loop gets its own provider. An element seek
|
|
7006
|
+
// decodes from the previous keyframe on EVERY frame (a baked loop
|
|
7007
|
+
// has one every few seconds), and the capture waits for it; the
|
|
7008
|
+
// sequential walk decodes each packet once. The whole loop is read
|
|
7009
|
+
// into a Blob first when it is small enough (one request instead
|
|
7010
|
+
// of ranged reads against a CDN). FAIL-OPEN to the element at
|
|
7011
|
+
// every step, like the footage.
|
|
7012
|
+
if (ctx.data.videoDecodeMode === 'webcodecs' && !bgV.__voilaWc) {
|
|
7013
|
+
try {
|
|
7014
|
+
if (!(ns.videoBlobs && ns.videoBlobs.get(bgm.key)) && bgm.key.indexOf('blob:') !== 0 && bgm.key.indexOf('data:') !== 0) {
|
|
7015
|
+
try {
|
|
7016
|
+
const bgResp = await fetch(bgm.key)
|
|
7017
|
+
const bgLen = Number(bgResp.headers.get('content-length') || 0)
|
|
7018
|
+
if (bgResp.ok && bgLen <= BG_BLOB_MAX) {
|
|
7019
|
+
;(ns.videoBlobs || (ns.videoBlobs = new Map())).set(bgm.key, await bgResp.blob())
|
|
7020
|
+
} else {
|
|
7021
|
+
try { if (bgResp.body) await bgResp.body.cancel() } catch (e) { void e }
|
|
7022
|
+
}
|
|
7023
|
+
} catch (e) { void e }
|
|
7024
|
+
}
|
|
7025
|
+
const bgWc = await makeWcProvider(bgm.key)
|
|
7026
|
+
if (bgWc && (!bgV.videoWidth || (bgWc.width === bgV.videoWidth && bgWc.height === bgV.videoHeight))) bgV.__voilaWc = bgWc
|
|
7027
|
+
} catch (e) {
|
|
7028
|
+
console.warn('[voila] webcodecs provider unavailable for the background, seeks stay html5', e)
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
6683
7032
|
} catch (e) { console.warn('[voila] background media failed to load', e) }
|
|
6684
7033
|
}
|
|
7034
|
+
// Which path decodes what, for a host's telemetry: 'provider' is the
|
|
7035
|
+
// sequential WebCodecs walk, 'element' an HTMLVideoElement seek per frame.
|
|
7036
|
+
ns.voilaDecode = {
|
|
7037
|
+
recording: ctx.data.isImage ? 'none' : video.__voilaWc ? 'provider' : 'element',
|
|
7038
|
+
backdrop: (function () {
|
|
7039
|
+
if (!bgm || !bgm.key || bgm.kind === 'image') return 'none'
|
|
7040
|
+
const bgHit = cache.get(bgm.key)
|
|
7041
|
+
return bgHit && bgHit.__voilaWc ? 'provider' : 'element'
|
|
7042
|
+
})(),
|
|
7043
|
+
}
|
|
6685
7044
|
// Frame prep: a capture harness calls every ns.framePrep hook right after
|
|
6686
7045
|
// seeking the timeline and before the first paint, then awaits
|
|
6687
7046
|
// waitForVideosReady. The footage for the frame is requested HERE, so the
|
|
@@ -7310,7 +7669,14 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
7310
7669
|
// Blur: softens the media behind the card (design px \xD7 s).
|
|
7311
7670
|
var bgBlur = bgm.blur || 0
|
|
7312
7671
|
if (bgBlur > 0 && bgC.filter !== undefined) bgC.filter = 'blur(' + bgBlur * s + 'px)'
|
|
7313
|
-
|
|
7672
|
+
// The loop's provider draws its current sample (paused capture
|
|
7673
|
+
// pages); false \u21D2 the element (playback, or before the first ask).
|
|
7674
|
+
try {
|
|
7675
|
+
var bgWcp = !bgIsImg && !playing ? bgEl.__voilaWc : null
|
|
7676
|
+
if (!(bgWcp && bgWcp.draw(bgC, null, (W - bgDw) / 2 + bgOx, (H - bgDh) / 2 + bgOy, bgDw, bgDh))) {
|
|
7677
|
+
bgC.drawImage(bgEl, (W - bgDw) / 2 + bgOx, (H - bgDh) / 2 + bgOy, bgDw, bgDh)
|
|
7678
|
+
}
|
|
7679
|
+
} catch (e) {}
|
|
7314
7680
|
if (bgBlur > 0 && bgC.filter !== undefined) bgC.filter = 'none'
|
|
7315
7681
|
var bgDim = bgm.dim || 0
|
|
7316
7682
|
if (bgDim > 0) {
|
|
@@ -8305,27 +8671,16 @@ function studioLayerData(layers, duration, pins) {
|
|
|
8305
8671
|
}
|
|
8306
8672
|
const st = resolveOverlayStyle(o);
|
|
8307
8673
|
const bx = resolveOverlayBox(o);
|
|
8308
|
-
const
|
|
8674
|
+
const rt = styledTextOf(o);
|
|
8309
8675
|
return {
|
|
8310
8676
|
...base,
|
|
8311
8677
|
text: o.text,
|
|
8312
|
-
// The
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
//
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
w: em.weight,
|
|
8319
|
-
...em.color ? { c: em.color } : {},
|
|
8320
|
-
...em.face ? {
|
|
8321
|
-
face: {
|
|
8322
|
-
f: em.face.family,
|
|
8323
|
-
w: em.face.weight,
|
|
8324
|
-
u: em.face.url
|
|
8325
|
-
}
|
|
8326
|
-
} : {}
|
|
8327
|
-
}
|
|
8328
|
-
} : {},
|
|
8678
|
+
// The lines a person reads. A styled layer (rt) is painted from
|
|
8679
|
+
// its runs; these are then its words, for the signature.
|
|
8680
|
+
lines: overlayLines(stripEmphasis(overlayDisplayText(o))),
|
|
8681
|
+
// The styled payload, only when the layer is set in more than
|
|
8682
|
+
// one style: a plain clip's data is unchanged.
|
|
8683
|
+
...rt ? { rt } : {},
|
|
8329
8684
|
fs: st.size,
|
|
8330
8685
|
weight: st.weight,
|
|
8331
8686
|
stack: st.stack,
|
|
@@ -10458,6 +10813,68 @@ function applyLook(frame, look, size, video, placement) {
|
|
|
10458
10813
|
return out;
|
|
10459
10814
|
}
|
|
10460
10815
|
|
|
10816
|
+
// src/richText/caret.ts
|
|
10817
|
+
function caretStops(line, measure) {
|
|
10818
|
+
const stops = [{ o: line.a, x: 0 }];
|
|
10819
|
+
for (const fr of line.frags) {
|
|
10820
|
+
let at = 0;
|
|
10821
|
+
for (const g of graphemesOf(fr.t)) {
|
|
10822
|
+
at += g.length;
|
|
10823
|
+
stops.push({
|
|
10824
|
+
o: line.a + fr.o + at,
|
|
10825
|
+
x: fr.x + measure(fr.t.slice(0, at), fr.f || 0)
|
|
10826
|
+
});
|
|
10827
|
+
}
|
|
10828
|
+
}
|
|
10829
|
+
return stops;
|
|
10830
|
+
}
|
|
10831
|
+
function lineOfOffset(layout, offset) {
|
|
10832
|
+
const lines = layout.lines;
|
|
10833
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
10834
|
+
if (offset >= lines[i].a) return i;
|
|
10835
|
+
}
|
|
10836
|
+
return 0;
|
|
10837
|
+
}
|
|
10838
|
+
function caretAt(layout, offset, measure) {
|
|
10839
|
+
const li = lineOfOffset(layout, offset);
|
|
10840
|
+
const stops = caretStops(layout.lines[li], measure);
|
|
10841
|
+
let x = stops[stops.length - 1].x;
|
|
10842
|
+
for (const s of stops) {
|
|
10843
|
+
if (s.o >= offset) {
|
|
10844
|
+
x = s.x;
|
|
10845
|
+
break;
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
return { line: li, x };
|
|
10849
|
+
}
|
|
10850
|
+
function offsetAtX(line, x, measure) {
|
|
10851
|
+
const stops = caretStops(line, measure);
|
|
10852
|
+
let best = stops[0];
|
|
10853
|
+
for (const s of stops) {
|
|
10854
|
+
if (Math.abs(s.x - x) < Math.abs(best.x - x)) best = s;
|
|
10855
|
+
}
|
|
10856
|
+
return best.o;
|
|
10857
|
+
}
|
|
10858
|
+
function rangeRects(layout, start, end, measure, breakTail = 0) {
|
|
10859
|
+
const out = [];
|
|
10860
|
+
if (end <= start) return out;
|
|
10861
|
+
layout.lines.forEach((line, i) => {
|
|
10862
|
+
const lineEnd = line.a + line.n;
|
|
10863
|
+
if (end <= line.a || start > lineEnd) return;
|
|
10864
|
+
const stops = caretStops(line, measure);
|
|
10865
|
+
const xOf = (o) => {
|
|
10866
|
+
for (const s of stops) if (s.o >= o) return s.x;
|
|
10867
|
+
return stops[stops.length - 1].x;
|
|
10868
|
+
};
|
|
10869
|
+
const x0 = xOf(Math.max(start, line.a));
|
|
10870
|
+
const x1 = xOf(Math.min(end, lineEnd));
|
|
10871
|
+
const next = layout.lines[i + 1];
|
|
10872
|
+
const tail = next && next.a > lineEnd && end > lineEnd ? breakTail : 0;
|
|
10873
|
+
if (x1 + tail > x0) out.push({ line: i, x: x0, w: x1 - x0 + tail });
|
|
10874
|
+
});
|
|
10875
|
+
return out;
|
|
10876
|
+
}
|
|
10877
|
+
|
|
10461
10878
|
// src/timeline/lanes.ts
|
|
10462
10879
|
import {
|
|
10463
10880
|
mapTime as mapTime3,
|
|
@@ -11838,6 +12255,7 @@ export {
|
|
|
11838
12255
|
CURSOR_IDLE_FADE_OUT,
|
|
11839
12256
|
CURSOR_IDLE_HOLD,
|
|
11840
12257
|
DARK_GROUND,
|
|
12258
|
+
DECORATION_THICKNESS_EM,
|
|
11841
12259
|
DEFAULT_BROWSER_BAR,
|
|
11842
12260
|
DEFAULT_CAM_POSE,
|
|
11843
12261
|
DEFAULT_CAM_STYLE,
|
|
@@ -11875,6 +12293,7 @@ export {
|
|
|
11875
12293
|
FREEZE_DEFAULT_SECONDS,
|
|
11876
12294
|
FREEZE_SECONDS_MAX,
|
|
11877
12295
|
FREEZE_SECONDS_MIN,
|
|
12296
|
+
HIGHLIGHT_HALF_EM,
|
|
11878
12297
|
HOLD_DRIFT_MAX,
|
|
11879
12298
|
HOUSE_GRADIENT,
|
|
11880
12299
|
HOUSE_REGISTER,
|
|
@@ -11924,6 +12343,7 @@ export {
|
|
|
11924
12343
|
SPEED_SPAN_MIN,
|
|
11925
12344
|
STAGE_CLIP_PREFIX,
|
|
11926
12345
|
STILL_DEFAULT,
|
|
12346
|
+
STRIKE_OFFSET_EM,
|
|
11927
12347
|
STUDIO_ENTRY_ID,
|
|
11928
12348
|
STYLE_FIELDS,
|
|
11929
12349
|
TEXT3D_DEPTH_DEFAULT,
|
|
@@ -11948,6 +12368,7 @@ export {
|
|
|
11948
12368
|
TRANSITION_SECONDS_MAX,
|
|
11949
12369
|
TRANSITION_SECONDS_MIN,
|
|
11950
12370
|
TRANSITION_SPEED_MULT,
|
|
12371
|
+
UNDERLINE_OFFSET_EM,
|
|
11951
12372
|
WINDOW_FOCUS_MIN,
|
|
11952
12373
|
ZOOM_CHAIN_GAP,
|
|
11953
12374
|
ZOOM_EASE,
|
|
@@ -11991,6 +12412,8 @@ export {
|
|
|
11991
12412
|
cardInset,
|
|
11992
12413
|
cardPointToScreen,
|
|
11993
12414
|
cardPoseTrack,
|
|
12415
|
+
caretAt,
|
|
12416
|
+
caretStops,
|
|
11994
12417
|
clampCamFrac,
|
|
11995
12418
|
clampCamSize,
|
|
11996
12419
|
clampFocus,
|
|
@@ -12014,6 +12437,7 @@ export {
|
|
|
12014
12437
|
cursorFromRecords,
|
|
12015
12438
|
cursorFromTrace,
|
|
12016
12439
|
cursorIdleFade,
|
|
12440
|
+
decorationRect,
|
|
12017
12441
|
deltaE,
|
|
12018
12442
|
deriveViewportCrop,
|
|
12019
12443
|
destinationById,
|
|
@@ -12084,14 +12508,15 @@ export {
|
|
|
12084
12508
|
lastLayerEnd,
|
|
12085
12509
|
layerMedia,
|
|
12086
12510
|
layoutOf,
|
|
12511
|
+
layoutText,
|
|
12087
12512
|
levelForFocusFraction,
|
|
12513
|
+
lineOfOffset,
|
|
12088
12514
|
liveZoomStyleName,
|
|
12089
12515
|
lookFromBrand,
|
|
12090
12516
|
lookKindForGround,
|
|
12091
12517
|
lowerProgramDoc,
|
|
12092
12518
|
lowerStudioDoc,
|
|
12093
12519
|
lowerToComposition,
|
|
12094
|
-
measureEmphasized,
|
|
12095
12520
|
mediaAtOutput,
|
|
12096
12521
|
mediaDuration,
|
|
12097
12522
|
mediaFrame,
|
|
@@ -12111,6 +12536,7 @@ export {
|
|
|
12111
12536
|
objectMotionBase,
|
|
12112
12537
|
objectMotionPoseAt,
|
|
12113
12538
|
objectsLane,
|
|
12539
|
+
offsetAtX,
|
|
12114
12540
|
outputDurationOf,
|
|
12115
12541
|
outputEnd,
|
|
12116
12542
|
outputRangeToSource,
|
|
@@ -12151,6 +12577,7 @@ export {
|
|
|
12151
12577
|
programStillTime,
|
|
12152
12578
|
projectFromArtifact,
|
|
12153
12579
|
pumpFreeChainGap,
|
|
12580
|
+
rangeRects,
|
|
12154
12581
|
ratedSegments,
|
|
12155
12582
|
recommendedExportResolution,
|
|
12156
12583
|
refillAudioBeds,
|
|
@@ -12183,8 +12610,10 @@ export {
|
|
|
12183
12610
|
studioAudioPlan,
|
|
12184
12611
|
studioEntry,
|
|
12185
12612
|
studioLayerData,
|
|
12613
|
+
styledTextOf,
|
|
12186
12614
|
tiltLane,
|
|
12187
12615
|
tiltTrackFromDoc,
|
|
12616
|
+
tokenRanges,
|
|
12188
12617
|
traceKindOf,
|
|
12189
12618
|
transitionMult,
|
|
12190
12619
|
transitionSeconds,
|