@vosjs/studio-core 0.19.1 → 0.21.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 +66 -11
- package/dist/index.js +258 -69
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -61,7 +61,13 @@ interface ZoomStyleParams {
|
|
|
61
61
|
autoZoom: boolean;
|
|
62
62
|
/** clicks within this many seconds merge into one span (session merge). */
|
|
63
63
|
clusterGap: number;
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Minimum clicks for a cluster to earn a zoom. Every style is 1: the
|
|
66
|
+
* Cursorful ≥2 rule glide and keynote carried was never in effect on a
|
|
67
|
+
* recorded take (the recorder counted each press twice, so a lone click
|
|
68
|
+
* was a pair on most pages and a stray on the rest), and the lone click on
|
|
69
|
+
* a button is the zoom people miss when it goes.
|
|
70
|
+
*/
|
|
65
71
|
minClusterClicks: number;
|
|
66
72
|
/** zoom so the clicked element fills ~this fraction of the frame. */
|
|
67
73
|
targetFill: number;
|
|
@@ -1353,6 +1359,18 @@ interface HtmlOverlayClip extends OverlayClipBase {
|
|
|
1353
1359
|
* component designed at 452 px lands at 452 px in a 1080p frame.
|
|
1354
1360
|
*/
|
|
1355
1361
|
width?: number;
|
|
1362
|
+
/**
|
|
1363
|
+
* A LIVE layer: the picture is a function of clip-local time. CSS
|
|
1364
|
+
* `@keyframes` inside it are scrubbed to t (paused, delayed by -t), and
|
|
1365
|
+
* `{{t}}` / `{{data.<name>}}` placeholders in the markup and CSS fill per
|
|
1366
|
+
* frame, so a counter, a progress bar or a typed line come out on the
|
|
1367
|
+
* timeline's clock in the preview and in every export chunk alike. One
|
|
1368
|
+
* rasterize per frame while on screen. Absent = a still, byte-identical
|
|
1369
|
+
* to before the field existed.
|
|
1370
|
+
*/
|
|
1371
|
+
live?: boolean;
|
|
1372
|
+
/** Values for a live layer's `{{data.<name>}}` placeholders. */
|
|
1373
|
+
data?: Record<string, string | number | boolean>;
|
|
1356
1374
|
/** Opacity 0..1. Absent = 1. */
|
|
1357
1375
|
opacity?: number;
|
|
1358
1376
|
}
|
|
@@ -2011,11 +2029,18 @@ interface SmoothOptions {
|
|
|
2011
2029
|
declare function smoothCursor(track: CursorTrack, options?: SmoothOptions): SmoothPoint[];
|
|
2012
2030
|
|
|
2013
2031
|
/**
|
|
2014
|
-
* A
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
*
|
|
2018
|
-
*
|
|
2032
|
+
* A press whose element FITS the frame at less than this level is not a
|
|
2033
|
+
* target: it is a drag (aiming, scrubbing, moving a thing across the canvas)
|
|
2034
|
+
* or a frame-sized surface (a canvas, a panel, a modal clicked to focus or
|
|
2035
|
+
* dismiss it), and a zoom on it says nothing. Five real takes (2026-08-25)
|
|
2036
|
+
* each carried 1-4 clusters of these, planned at the floor level for 10-25s;
|
|
2037
|
+
* every one was dropped by hand. Now they plan nothing.
|
|
2038
|
+
*
|
|
2039
|
+
* The rule is PER PRESS, never per cluster: a target cluster's level came
|
|
2040
|
+
* from its LARGEST element, so one press on a panel inside a chain of button
|
|
2041
|
+
* clicks read the whole chain as a drag and threw every zoom away — two real
|
|
2042
|
+
* takes (2026-09-11) lost 5 of 8 and 4 of 4 clicks that way. A surface press
|
|
2043
|
+
* is set aside before clustering; the targets around it keep their zoom.
|
|
2019
2044
|
*/
|
|
2020
2045
|
declare const DRAG_FIT_LEVEL = 1.15;
|
|
2021
2046
|
interface PlanOptions {
|
|
@@ -2105,6 +2130,12 @@ interface Moment {
|
|
|
2105
2130
|
/** Normalized target bounds (element rect union), when the events had one. */
|
|
2106
2131
|
rect: NormRect | null;
|
|
2107
2132
|
clicks?: number;
|
|
2133
|
+
/**
|
|
2134
|
+
* A click cluster on a frame-sized element (a canvas, a panel, a drag):
|
|
2135
|
+
* the planner frames nothing on it (DRAG_FIT_LEVEL), so a zoom here is
|
|
2136
|
+
* the agent's call, never a proposal.
|
|
2137
|
+
*/
|
|
2138
|
+
surface?: boolean;
|
|
2108
2139
|
pings?: number;
|
|
2109
2140
|
/** Motion in the window, 0..1 (fraction of changed pixels), null without frames. */
|
|
2110
2141
|
activity: number | null;
|
|
@@ -3090,7 +3121,7 @@ declare function studioEntry(data: Record<string, unknown>): StudioEntry;
|
|
|
3090
3121
|
* import, so the composer is carried twice and `htmlOverlay.test.ts` pins
|
|
3091
3122
|
* the two byte-for-byte. Change them together.
|
|
3092
3123
|
*/
|
|
3093
|
-
declare const HTML_LAYER_SVG_CODE = "(src, faces) => {\n const bleed = Math.max(0, Math.round(src.bleed || 0))\n const boxW = src.box.width + bleed * 2\n const boxH = src.box.height + bleed * 2\n let fontCss = ''\n for (const f of faces || []) fontCss += \"@font-face{font-family:'\" + f.family + \"';src:url(\" + f.dataUri + \") format('woff2');font-weight:\" + f.weight + \";font-style:\" + f.style + \";font-display:block}\"\n const wrapper = '.vos-html-layer{width:' + boxW + 'px;height:' + boxH + 'px;box-sizing:border-box;margin:0;padding:' + bleed + 'px;font-family:-apple-system,system-ui,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:geometricPrecision}'\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"' + boxW + '\" height=\"' + boxH + '\" viewBox=\"0 0 ' + boxW + ' ' + boxH + '\">' +\n '<foreignObject x=\"0\" y=\"0\" width=\"' + boxW + '\" height=\"' + boxH + '\">' +\n '<div xmlns=\"http://www.w3.org/1999/xhtml\" class=\"vos-html-layer\">' +\n '<style>' + fontCss + wrapper + (src.css || '') + '</style>' + src.html +\n '</div></foreignObject></svg>'\n}";
|
|
3124
|
+
declare const HTML_LAYER_SVG_CODE = "(src, faces, assets) => {\n const bleed = Math.max(0, Math.round(src.bleed || 0))\n const boxW = src.box.width + bleed * 2\n const boxH = src.box.height + bleed * 2\n let fontCss = ''\n for (const f of faces || []) fontCss += \"@font-face{font-family:'\" + f.family + \"';src:url(\" + f.dataUri + \") format('woff2');font-weight:\" + f.weight + \";font-style:\" + f.style + \";font-display:block}\"\n const t = src.t || 0\n const fmtT = (n) => String(Math.round(n * 1000) / 1000)\n const esc = (v) => String(v).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n const fill = (text) => {\n let out = text\n for (const a of assets || []) out = out.split(a.url).join(a.dataUri)\n if (src.live) {\n out = out.replace(/\\{\\{\\s*t\\s*\\}\\}/g, fmtT(t))\n out = out.replace(/\\{\\{\\s*data\\.([A-Za-z0-9_.-]+)\\s*\\}\\}/g, (m, name) => { const v = src.data ? src.data[name] : undefined; return v == null ? '' : esc(v) })\n }\n return out\n }\n const scrub = src.live ? '.vos-html-layer{--vos-t:' + fmtT(t) + '}.vos-html-layer,.vos-html-layer *{animation-play-state:paused!important;animation-delay:calc(var(--vos-t) * -1s)!important}' : ''\n const wrapper = '.vos-html-layer{width:' + boxW + 'px;height:' + boxH + 'px;box-sizing:border-box;margin:0;padding:' + bleed + 'px;font-family:-apple-system,system-ui,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:geometricPrecision}'\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"' + boxW + '\" height=\"' + boxH + '\" viewBox=\"0 0 ' + boxW + ' ' + boxH + '\">' +\n '<foreignObject x=\"0\" y=\"0\" width=\"' + boxW + '\" height=\"' + boxH + '\">' +\n '<div xmlns=\"http://www.w3.org/1999/xhtml\" class=\"vos-html-layer\">' +\n '<style>' + fontCss + wrapper + fill(src.css || '') + scrub + '</style>' + fill(src.html) +\n '</div></foreignObject></svg>'\n}";
|
|
3094
3125
|
|
|
3095
3126
|
/** The design frame an HTML layer's `box` is measured against (1080p). */
|
|
3096
3127
|
declare const DESIGN_FRAME_WIDTH = 1920;
|
|
@@ -3113,6 +3144,14 @@ interface HtmlLayerSource {
|
|
|
3113
3144
|
height: number;
|
|
3114
3145
|
};
|
|
3115
3146
|
bleed?: number;
|
|
3147
|
+
/**
|
|
3148
|
+
* A LIVE layer (§3.13): the picture is a function of clip-local time.
|
|
3149
|
+
* `t` is the moment being drawn, `data` the clip's own values for the
|
|
3150
|
+
* `{{data.<name>}}` placeholders. Absent = a still.
|
|
3151
|
+
*/
|
|
3152
|
+
live?: boolean;
|
|
3153
|
+
t?: number;
|
|
3154
|
+
data?: Record<string, string | number | boolean>;
|
|
3116
3155
|
}
|
|
3117
3156
|
interface HtmlLayerInlineFace {
|
|
3118
3157
|
family: string;
|
|
@@ -3120,6 +3159,11 @@ interface HtmlLayerInlineFace {
|
|
|
3120
3159
|
style: 'normal' | 'italic';
|
|
3121
3160
|
dataUri: string;
|
|
3122
3161
|
}
|
|
3162
|
+
/** An image the source names by URL, fetched and inlined by the page. */
|
|
3163
|
+
interface HtmlLayerInlineAsset {
|
|
3164
|
+
url: string;
|
|
3165
|
+
dataUri: string;
|
|
3166
|
+
}
|
|
3123
3167
|
declare function cssFamilies(css: string | undefined): string[];
|
|
3124
3168
|
/**
|
|
3125
3169
|
* The weights the CSS and the markup ask for: `font-weight` numbers and
|
|
@@ -3180,7 +3224,7 @@ declare function htmlLayerWidth(clip: Pick<HtmlOverlayClip, 'width' | 'box' | 'c
|
|
|
3180
3224
|
declare function htmlLayerText(html: string): string;
|
|
3181
3225
|
/** The timeline item's label: the first words, else the kind. */
|
|
3182
3226
|
declare function htmlLayerLabel(clip: Pick<HtmlOverlayClip, 'html'>): string;
|
|
3183
|
-
type HtmlLayerProblemCode = 'empty' | 'unclosed-void' | 'bare-ampersand' | 'unquoted-attr' | 'too-large' | 'foreign-field' | 'shadow-clipped' | 'external-resource' | 'wall-clock' | 'unhosted-family';
|
|
3227
|
+
type HtmlLayerProblemCode = 'empty' | 'unclosed-void' | 'bare-ampersand' | 'unquoted-attr' | 'too-large' | 'foreign-field' | 'shadow-clipped' | 'external-resource' | 'wall-clock' | 'live-delay' | 'placeholder-still' | 'unhosted-family';
|
|
3184
3228
|
interface HtmlLayerProblem {
|
|
3185
3229
|
code: HtmlLayerProblemCode;
|
|
3186
3230
|
/** A problem stops the layer painting; a warning is a layer that paints wrong. */
|
|
@@ -3196,7 +3240,7 @@ declare const HTML_LAYER_FOREIGN_FIELDS: readonly ["key", "radius", "shadow", "b
|
|
|
3196
3240
|
* author is told instead, the same answer in the studio, in `vos validate`
|
|
3197
3241
|
* and in a test.
|
|
3198
3242
|
*/
|
|
3199
|
-
declare function htmlLayerProblems(clip: Pick<HtmlOverlayClip, 'html' | 'css' | 'bleed' | 'fonts'>): HtmlLayerProblem[];
|
|
3243
|
+
declare function htmlLayerProblems(clip: Pick<HtmlOverlayClip, 'html' | 'css' | 'bleed' | 'fonts' | 'live'>): HtmlLayerProblem[];
|
|
3200
3244
|
/**
|
|
3201
3245
|
* The real XML question, asked of a real parser: the studio hands in
|
|
3202
3246
|
* `DOMParser`, a test hands in a stub. The CLI does not call this (Node
|
|
@@ -3220,9 +3264,15 @@ declare function wellFormedXml(svg: string, parseXml: (text: string) => {
|
|
|
3220
3264
|
* resets the two properties an SVG document does not inherit sensibly;
|
|
3221
3265
|
* everything else is the author's.
|
|
3222
3266
|
*
|
|
3267
|
+
* Assets the source names by URL are swapped for their bytes. A LIVE layer
|
|
3268
|
+
* is composed for one moment: `{{t}}` and `{{data.<name>}}` fill, and a
|
|
3269
|
+
* scrub block after the author's CSS pauses every animation and delays it
|
|
3270
|
+
* by -t, so the browser lays the frame out at exactly that offset (the
|
|
3271
|
+
* standard way to scrub a CSS animation, here applied per picture).
|
|
3272
|
+
*
|
|
3223
3273
|
* MIRRORED by `HTML_LAYER_SVG_CODE` in `lower/studioEntry.ts`. Change both.
|
|
3224
3274
|
*/
|
|
3225
|
-
declare function htmlLayerSvg(src: HtmlLayerSource, faces?: readonly HtmlLayerInlineFace[]): string;
|
|
3275
|
+
declare function htmlLayerSvg(src: HtmlLayerSource, faces?: readonly HtmlLayerInlineFace[], assets?: readonly HtmlLayerInlineAsset[]): string;
|
|
3226
3276
|
/**
|
|
3227
3277
|
* The layer's CACHE KEY: content-addressed over everything the picture is
|
|
3228
3278
|
* made of (markup, CSS, box, bleed, the resolved faces), so an edit to the
|
|
@@ -3231,7 +3281,7 @@ declare function htmlLayerSvg(src: HtmlLayerSource, faces?: readonly HtmlLayerIn
|
|
|
3231
3281
|
* scaling or animating the clip does not re-key it: none of those repaint.
|
|
3232
3282
|
* The key is a handle, never a URL: nothing may fetch it.
|
|
3233
3283
|
*/
|
|
3234
|
-
declare function htmlLayerKey(clip: Pick<HtmlOverlayClip, 'id' | 'html' | 'css' | 'box'>, faces: readonly HtmlLayerFace[], bleed: number): string;
|
|
3284
|
+
declare function htmlLayerKey(clip: Pick<HtmlOverlayClip, 'id' | 'html' | 'css' | 'box' | 'live' | 'data'>, faces: readonly HtmlLayerFace[], bleed: number, assets?: readonly string[]): string;
|
|
3235
3285
|
/** The shape the page is handed for a layer: the source plus resolved faces. */
|
|
3236
3286
|
interface HtmlLayerPayload {
|
|
3237
3287
|
markup: string;
|
|
@@ -3242,6 +3292,11 @@ interface HtmlLayerPayload {
|
|
|
3242
3292
|
};
|
|
3243
3293
|
bleed: number;
|
|
3244
3294
|
faces: HtmlLayerFace[];
|
|
3295
|
+
/** Images the source names by URL, for the page to fetch and inline. */
|
|
3296
|
+
assets: string[];
|
|
3297
|
+
/** A live layer: the page composes the picture per frame at clip-local t. */
|
|
3298
|
+
live?: true;
|
|
3299
|
+
data?: Record<string, string | number | boolean>;
|
|
3245
3300
|
}
|
|
3246
3301
|
/** Everything the lowering emits for a layer, from the clip alone. */
|
|
3247
3302
|
declare function htmlLayerPayload(clip: HtmlOverlayClip): {
|