versioncam 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +82 -2
- package/dist/cli/commands/check.js +25 -0
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/doctor.js +54 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/record.d.ts +6 -0
- package/dist/cli/commands/record.js +24 -3
- package/dist/cli/commands/record.js.map +1 -1
- package/dist/cli/commands/stability.js +10 -3
- package/dist/cli/commands/stability.js.map +1 -1
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +2 -0
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +37 -1
- package/dist/config.js +115 -3
- package/dist/config.js.map +1 -1
- package/dist/core/cursor.d.ts +22 -0
- package/dist/core/cursor.js +6 -0
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/motion-defaults.d.ts +8 -0
- package/dist/core/motion-defaults.js +8 -0
- package/dist/core/motion-defaults.js.map +1 -1
- package/dist/core/timeline.d.ts +24 -0
- package/dist/core/timeline.js.map +1 -1
- package/dist/driver/clip.d.ts +6 -0
- package/dist/driver/clip.js +33 -2
- package/dist/driver/clip.js.map +1 -1
- package/dist/driver/file-window.d.ts +66 -0
- package/dist/driver/file-window.js +245 -0
- package/dist/driver/file-window.js.map +1 -0
- package/dist/driver/page-hooks.d.ts +23 -1
- package/dist/driver/page-hooks.js +36 -1
- package/dist/driver/page-hooks.js.map +1 -1
- package/dist/driver/replay.d.ts +263 -0
- package/dist/driver/replay.js +558 -0
- package/dist/driver/replay.js.map +1 -0
- package/dist/driver/session.d.ts +70 -4
- package/dist/driver/session.js +267 -29
- package/dist/driver/session.js.map +1 -1
- package/dist/driver/settle.js +6 -0
- package/dist/driver/settle.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js.map +1 -1
- package/dist/page/assets/index-CnRDAiUz.js +2 -0
- package/dist/page/index.html +1 -1
- package/dist/publish/protocol.d.ts +12 -1
- package/dist/publish/protocol.js.map +1 -1
- package/dsl.md +68 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/versioncam/authoring.md +6 -1
- package/plugin/skills/versioncam/onboarding.md +9 -3
- package/dist/page/assets/index-BtUbUEdN.js +0 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export type { AppHandle } from "./app-server.js";
|
|
|
5
5
|
export { clip, runClip, RECORDER_VERSION } from "./driver/clip.js";
|
|
6
6
|
export type { RunClipOptions } from "./driver/clip.js";
|
|
7
7
|
export type { ClipBody, ClipDefinition, ClipOptions, RunClipResult, } from "./driver/clip.js";
|
|
8
|
-
export type { RecordingSession, SessionOptions } from "./driver/session.js";
|
|
8
|
+
export type { DragOptions, FileWindow, Modifier, RecordingSession, ScrollOptions, ScrollWhere, SessionOptions, } from "./driver/session.js";
|
|
9
|
+
export type { FileWindowOptions, StagedFile } from "./driver/file-window.js";
|
|
9
10
|
export { createGate } from "./driver/gate.js";
|
|
10
11
|
export type { Gate } from "./driver/gate.js";
|
|
11
12
|
export type { SettleOptions, SettleReport } from "./driver/settle.js";
|
|
@@ -17,7 +18,8 @@ export type { LoopPlanner, LoopOptions } from "./core/loop.js";
|
|
|
17
18
|
export { sseBody } from "./core/sse.js";
|
|
18
19
|
export type { SseFixtureEvent } from "./core/sse.js";
|
|
19
20
|
export { MOTION } from "./core/motion-defaults.js";
|
|
20
|
-
export type {
|
|
21
|
+
export type { ReplayConfig, ReplayMode, ReplayPattern, } from "./driver/replay.js";
|
|
22
|
+
export type { Caption, CameraKey, Highlight, HighlightStyle, Mark, PointerAction, Rect, Scroll, Timeline, Vec2, } from "./core/timeline.js";
|
|
21
23
|
export type { Scene, Size } from "./scene.js";
|
|
22
24
|
export { loadClips, loadConfig, loadSceneIds, bundleScenes } from "./loader.js";
|
|
23
25
|
export { contactSheet, renderFrame, renderRecording, renderScene, withBrowser, readTimeline, } from "./render/render.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiBxE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiBxE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAkBnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE7E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAqBnD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,cAAc,GACf,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EACL,qBAAqB,EACrB,QAAQ,EACR,OAAO,EACP,SAAS,GACV,MAAM,uBAAuB,CAAC;AAE/B,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){let n=t(e);return n*n*n*(10+n*(-15+6*n))}function t(e){return e<=0?0:e>=1?1:e}function n(e,t,n){return e+(t-e)*n}function r(e,t,r){let i=e.x+e.w/2,a=e.y+e.h/2,o=t.x+t.w/2,s=t.y+t.h/2,c=e.w*(t.w/e.w)**+r,l=e.h*(t.h/e.h)**+r,u=n(i,o,r),d=n(a,s,r);return{x:u-c/2,y:d-l/2,w:c,h:l}}function i(e,t,r){return{x:n(e.x,t.x,r),y:n(e.y,t.y,r),w:n(e.w,t.w,r),h:n(e.h,t.h,r)}}function a(t,n){if(t.length===0)throw Error(`evalCamera called with no keys`);if(n<=t[0].t)return t[0].rect;for(let a=1;a<t.length;a+=1){let o=t[a-1],s=t[a];if(n>=s.t)continue;if(s.ease===`hold`)return o.rect;let c=s.t-o.t,l=c<=0?1:(n-o.t)/c;return s.ease===`linear`?i(o.rect,s.rect,l):r(o.rect,s.rect,e(l))}return t[t.length-1].rect}function o(e,t,n,r){let i=1-r,a=i*i,o=2*i*r,s=r*r;return[a*e[0]+o*t[0]+s*n[0],a*e[1]+o*t[1]+s*n[1]]}function s(t,n){if(t.length===0)return[0,0];let r=t[0];if(n<=r.t0)return r.kind===`move`?r.from:r.at;for(let r of t){if(n>r.t1)continue;if(r.kind===`hold`)return r.at;let t=r.t1-r.t0,i=t<=0?1:(n-r.t0)/t;return o(r.from,r.ctrl,r.to,e(i))}let i=t[t.length-1];return i.kind===`move`?i.to:i.at}var c={style:`light`,ripple:!0};function l(e,t,n){let r=n.w/t.w;return[n.x+(e[0]-t.x)*r,n.y+(e[1]-t.y)*r]}function u(e,t,n){let r=n.w/t.w;return{x:n.x+(e.x-t.x)*r,y:n.y+(e.y-t.y)*r,w:e.w*r,h:e.h*r}}function d(e,t){return{x:e.x*t,y:e.y*t,w:e.w*t,h:e.h*t}}function f(e,t,n){let r=Math.min(e.width,e.height)*n,i=e.width-r*2,a=e.height-r*2,o=t.width/t.height,s=i,c=s/o;return c>a&&(c=a,s=c*o),{x:(e.width-s)/2,y:(e.height-c)/2,w:s,h:c}}function p(e,n,r){return r<=0?1:t((e-n+1)/r)}function m(e,t,n){let r=e.frames[t],i=null;for(let a of e.cuts)t>a&&t<=a+n&&e.frames[a]!==r&&(i===null||a>i)&&(i=a);return i===null?{current:r,previous:null,alpha:1}:{current:r,previous:e.frames[i],alpha:p(t,i+1,n)}}function h(e,n,r,i){return e<=n||e>=r?0:i<=0?1:Math.min(t((e-n)/i),t((r-e)/i))}function g(e,t){return e/t}function _(e,t,n){let r=n?.padding??0;return!n||r<=0?{x:0,y:0,w:e.width,h:e.height}:f(e,t,r)}function v(e){let t=e?.caption??`auto`;return t===`none`?`none`:t===`below`||t===`overlay`?t:(e?.padding??0)>0?`below`:`overlay`}function y(e,t){let n=e?.captionInset;return n?.right===void 0?{x:n?.left??40,align:`left`}:{x:t-n.right,align:`right`}}var b={radius:16,hairline:`rgba(255, 255, 255, 0.16)`,shadow:`rgba(0, 0, 0, 0.45)`,shadowBlur:60,shadowOffsetY:18,captionFont:`600 30px "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif`,captionColor:`rgba(255, 255, 255, 0.94)`,captionShadow:`rgba(0, 0, 0, 0.5)`,captionScrim:`rgba(0, 0, 0, 0.45)`,captionScrimHeight:86,ringWidth:3,ringRadius:10,spotlightDim:.55,spotlightTint:`6, 2, 16`},x={light:{fill:`#ffffff`,stroke:`rgba(0, 0, 0, 0.55)`,shadow:`rgba(0, 0, 0, 0.35)`},dark:{fill:`#000000`,stroke:`rgba(255, 255, 255, 0.9)`,shadow:`rgba(0, 0, 0, 0.3)`}};function ee(e,t){let n=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!n)return e;let r=Number.parseInt(n[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${t})`}function S(e,t,n){let r=Math.min(n,t.w/2,t.h/2);e.beginPath(),e.moveTo(t.x+r,t.y),e.arcTo(t.x+t.w,t.y,t.x+t.w,t.y+t.h,r),e.arcTo(t.x+t.w,t.y+t.h,t.x,t.y+t.h,r),e.arcTo(t.x,t.y+t.h,t.x,t.y,r),e.arcTo(t.x,t.y,t.x+t.w,t.y,r),e.closePath()}function te(e,t,n){if(typeof n==`string`)e.fillStyle=n;else{let r=e.createLinearGradient(0,0,t.width,t.height);r.addColorStop(0,n[0]),r.addColorStop(1,n[1]),e.fillStyle=r}e.fillRect(0,0,t.width,t.height)}function ne(e,t,n,r,i,a,o){let s=d(i,t.viewport.dpr),{current:c,previous:l,alpha:u}=m(t,r,o.motion.crossfadeFrames),f=o.theme.presentation?.frame??!1;e.save(),f&&(S(e,a,b.radius),e.clip());let p=(t,r)=>{let i=n.images[t];i&&(e.globalAlpha=r,e.drawImage(i,s.x,s.y,s.w,s.h,a.x,a.y,a.w,a.h))};l!==null&&p(l,1),p(c,l===null?1:u),e.globalAlpha=1,e.restore()}function C(e,t){e.save(),e.shadowColor=b.shadow,e.shadowBlur=b.shadowBlur,e.shadowOffsetY=b.shadowOffsetY,e.fillStyle=`rgba(0,0,0,1)`,S(e,t,b.radius),e.fill(),e.restore()}function w(e,t){e.save(),e.strokeStyle=b.hairline,e.lineWidth=1,S(e,t,b.radius),e.stroke(),e.restore()}function T(e,t){return{x:e.x-t,y:e.y-t,w:e.w+t*2,h:e.h+t*2}}function E(e,t,n,r,i,a){let o=a.theme.presentation?.frame??!1;for(let s of t.highlights){let t=h(n,s.t0,s.t1,.25);if(t<=0)continue;let c=u(s.rect,r,i);e.save(),e.globalAlpha=t,s.style===`spotlight`?(e.save(),S(e,i,o?b.radius:0),e.clip(),e.fillStyle=`rgba(${b.spotlightTint}, ${b.spotlightDim})`,e.fillRect(i.x,i.y,i.w,i.h),e.globalCompositeOperation=`destination-out`,S(e,T(c,8),b.ringRadius),e.fill(),e.restore()):(e.strokeStyle=a.theme.accent,e.lineWidth=b.ringWidth,e.shadowColor=ee(a.theme.accent,.35),e.shadowBlur=18,S(e,T(c,6),b.ringRadius),e.stroke()),e.restore()}}function D(e,t,n){let r=!1,i=[],a=0;for(let o of e.pointer){if(o.t>t)break;if(o.kind===`down`)r=!0,a=o.t;else{r=!1;let e=t-o.t;e>=0&&e<=n.motion.rippleMs/1e3&&i.push({at:o.at,age:e})}}let o=r?t-a:0,s=r?Math.min(1,o/(n.motion.pressScaleMs/1e3)):0;return{scale:1-(1-n.motion.pressScale)*s,ripples:i}}function re(e,t,n,r,i){for(let a of t){let t=a.age/(i.motion.rippleMs/1e3),[o,s]=l(a.at,n,r);e.save(),e.globalAlpha=(1-t)*.55,e.strokeStyle=i.theme.accent,e.lineWidth=2.5,e.beginPath(),e.arc(o,s,6+t*i.motion.rippleRadius,0,Math.PI*2),e.stroke(),e.restore()}}function ie(e,t,n,r,i){let a=r*n;e.save(),e.translate(t[0],t[1]),e.scale(a,a),e.shadowColor=i.shadow,e.shadowBlur=6/a,e.shadowOffsetY=2/a,e.beginPath(),e.moveTo(0,0),e.lineTo(0,16.6),e.lineTo(4.1,12.9),e.lineTo(6.9,18.9),e.lineTo(9.6,17.6),e.lineTo(6.9,11.8),e.lineTo(12.1,11.5),e.closePath(),e.fillStyle=i.fill,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=i.stroke,e.lineWidth=1/a,e.stroke(),e.restore()}function O(e,t,n,r,i,a){let o=v(a.theme.presentation);if(o!==`none`)for(let s of t.captions){let t=h(n,s.t0,s.t1,a.motion.captionFadeS);if(t<=0)continue;if(e.save(),e.globalAlpha=t,o===`overlay`){let t=b.captionScrimHeight,n=e.createLinearGradient(0,i.height-t,0,i.height);n.addColorStop(0,`rgba(0, 0, 0, 0)`),n.addColorStop(1,b.captionScrim),e.fillStyle=n,e.fillRect(0,i.height-t,i.width,t)}e.font=b.captionFont,e.fillStyle=b.captionColor,e.textBaseline=`middle`,e.shadowColor=b.captionShadow,e.shadowBlur=8;let c=r.x,l=(r.y+r.h+i.height)/2;if(o===`overlay`){let t=y(a.theme.presentation,i.width);e.textAlign=t.align,c=t.x,l=i.height-b.captionScrimHeight/2+4}e.fillText(s.text,c,l),e.restore()}}function k(e,t,n,r,i,o){let u=g(r,t.fps),d=a(t.camera,u),f=o.theme.presentation,p=_(i,t.viewport,f);f?.backdrop&&te(e,i,f.backdrop),f?.frame&&C(e,p),ne(e,t,n,r,d,p,o),f?.frame&&w(e,p),E(e,t,u,d,p,o);let m=o.theme.cursor??c,{scale:h,ripples:v}=D(t,u,o);m.ripple&&re(e,v,d,p,o),ie(e,l(s(t.cursor,u),d,p),h,o.theme.cursorScale,x[m.style]),O(e,t,u,p,i,o)}var A="versioncam reads scenes from a module's default export: a Record<string, Scene> keyed by scene id, as in `export default { [intro.id]: intro }`.";function j(e){return e==null?String(e):Array.isArray(e)?`an array`:`a ${typeof e}`}function M(e){return typeof e==`string`?JSON.stringify(e):String(e)}function N(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ae(e,t){if(!N(t))return`"${e}" is ${j(t)}, not a scene`;let{draw:n,durationFrames:r,fps:i}=t;return typeof n==`function`?!Number.isInteger(r)||r<1?`"${e}" has durationFrames ${M(r)}, where a whole number of frames, 1 or more, belongs`:typeof i!=`number`||!Number.isFinite(i)||i<=0?`"${e}" has fps ${M(i)}, where a number above 0 belongs`:null:`"${e}" has no draw(ctx, frame, canvas)`}function P(e){if(!N(e))return j(e);if(typeof e.draw==`function`)return`a single scene rather than a record of them`;for(let[t,n]of Object.entries(e)){let e=ae(t,n);if(e)return`a record in which ${e}`}return null}function oe(e,t){if(e.default===void 0){let n=Object.keys(e).filter(e=>e!=="default"),r=[`${t} has no default export.`+(n.length>0?` It exports ${n.join(`, `)}.`:``),A],i=n.filter(t=>P(e[t])===null&&Object.keys(e[t]).length>0);return i.length===1&&r.push(`${i[0]} is one: add \`export default ${i[0]}\`.`),{error:r.join(`
|
|
2
|
+
`)}}let n=P(e.default);return n?{error:`${t} default-exports ${n}.\n${A}`}:{scenes:e.default}}function F(e,t,n){let r=e.map(e=>`"${e}"`).join(`, `),i=`No scene${e.length===1?``:`s`} called ${r}.`;return n===null?`${i} The config names no \`scenes\` module.`:`${i} ${n} defines ${t.length>0?t.join(`, `):`none`}.`}var I=`modulepreload`,L=function(e,t){return new URL(e,t).href},R={},z=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=L(t,n),t=s(t),t in R)return;R[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:I,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},B=new URLSearchParams(location.search),V=B.has(`recording`),H=B.get(`scene`),U=B.get(`headless`)===`1`;U&&document.body.classList.add(`headless`);var W=document.getElementById(`stage`),G=document.getElementById(`scrub`),K=document.getElementById(`status`),q=document.getElementById(`play`),se=document.getElementById(`error`);function J(e){throw se.textContent=e,Error(e)}async function Y(e,t){let n=await fetch(e);return n.ok||J(`Could not load ${t} (${e}): ${n.status}`),await n.json()}async function ce(e){let t=await fetch(e);return t.ok||J(`Could not load ${e}: ${t.status}`),createImageBitmap(await t.blob())}function X(e){W.width=e.output.width,W.height=e.output.height,U?(W.style.width=`${e.output.width}px`,W.style.height=`${e.output.height}px`):(W.style.width=`min(100%, 1280px)`,W.style.height=`auto`);let t=W.getContext(`2d`,{alpha:!1});return t||J(`No 2D context`),t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`,t}function Z(e,t,n,r){let i=!1;q.addEventListener(`click`,()=>{i=!i,q.textContent=i?`Pause`:`Play`;let a=performance.now(),o=s=>{i&&(s-a>=1e3/e&&(a=s,r((n()+1)%t)),requestAnimationFrame(o))};requestAnimationFrame(o)})}function Q(e,t,n,r){e.durationFrames=t,e.fps=n,e.setFrame=e=>new Promise(t=>{r(e),requestAnimationFrame(()=>requestAnimationFrame(()=>t()))})}async function le(e,t){let n=await Y(`/recording/timeline.json`,`the recording`),r=X(t),i={images:await Promise.all(n.states.map(e=>ce(`/recording/states/${e}`)))};await document.fonts.ready;let a=-1,o=e=>{let o=Math.max(0,Math.min(n.durationFrames-1,e));k(r,n,i,o,W,t),a=o,U||(G.value=String(o),K.textContent=`${o+1} / ${n.durationFrames} · ${(o/n.fps).toFixed(2)}s · state ${n.frames[o]}`)};G.max=String(n.durationFrames-1),G.addEventListener(`input`,()=>o(Number(G.value))),Z(n.fps,n.durationFrames,()=>a,o),o(Number(B.get(`frame`)??0)),Q(e,n.durationFrames,n.fps,o)}async function ue(e,t,n){t.scenes===null&&J(F([n],[],null));let r=oe(await z(()=>import(`/scenes.js`),[],import.meta.url),t.scenes);`error`in r&&J(r.error);let i=r.scenes[n];i||J(F([n],Object.keys(r.scenes),t.scenes));let a=X(t);await document.fonts.ready;let o=-1,s=e=>{let t=Math.max(0,Math.min(i.durationFrames-1,e));i.draw(a,t,W),o=t,U||(G.value=String(t),K.textContent=`${t+1} / ${i.durationFrames} · ${(t/i.fps).toFixed(2)}s`)};G.max=String(i.durationFrames-1),G.addEventListener(`input`,()=>s(Number(G.value))),Z(i.fps,i.durationFrames,()=>o,s),s(Number(B.get(`frame`)??0)),Q(e,i.durationFrames,i.fps,s)}async function de(e){let t=await Y(`/render-config.json`,`the render config`);if(H)return ue(e,t,H);if(V)return le(e,t);J(`Pass ?recording or ?scene=<id>.`)}var $={ready:Promise.resolve(),setFrame:async()=>{},durationFrames:0,fps:60};window.__render=$,$.ready=de($);
|
package/dist/page/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<title>versioncam render</title>
|
|
6
6
|
<style>*{box-sizing:border-box;margin:0;padding:0;transition:none!important;animation:none!important}body{color:#e8e2f5;background:#0b0614;flex-direction:column;align-items:center;min-height:100vh;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;display:flex}body.headless{background:0 0;display:block}#stage{display:block}body.headless #stage{position:fixed;inset:0}#controls{align-items:center;gap:14px;width:min(100%,1280px);padding:14px 18px 28px;font-size:13px;display:flex}body.headless #controls{display:none}#scrub{flex:1}#status{font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.75}button{font:inherit;color:#fff;cursor:pointer;background:#2f6fed;border:0;border-radius:6px;padding:6px 14px}#error{color:#ff9a9a;white-space:pre-wrap;padding:24px;font-family:ui-monospace,Menlo,monospace}</style>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-CnRDAiUz.js"></script>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<canvas id="stage"></canvas>
|
|
@@ -71,10 +71,21 @@ export type PublishedRenderConfig = {
|
|
|
71
71
|
theme: {
|
|
72
72
|
accent: string;
|
|
73
73
|
cursorScale: number;
|
|
74
|
+
/**
|
|
75
|
+
* The config's `theme.cursor`, from 0.6.0, and only when it is not the
|
|
76
|
+
* default; a renderer draws the default without it.
|
|
77
|
+
*/
|
|
78
|
+
cursor?: {
|
|
79
|
+
style: "light" | "dark";
|
|
80
|
+
ripple: boolean;
|
|
81
|
+
};
|
|
74
82
|
/** The config's `theme.presentation`, or null when it is off. */
|
|
75
83
|
presentation: Record<string, unknown> | null;
|
|
76
84
|
};
|
|
77
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* The config's motion, every default filled in, but for the settings
|
|
87
|
+
* added since 0.5.3, which are sent only when they are not the default.
|
|
88
|
+
*/
|
|
78
89
|
motion: Record<string, unknown>;
|
|
79
90
|
clock: {
|
|
80
91
|
timezoneId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/publish/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,gBAAgB,EAAE,GAAG;IACrB,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC3B,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC9B,YAAY,EAAE,GAAG,GAAG,IAAI;IACxB,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC7B,gBAAgB,EAAE,EAAE;CACZ,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/publish/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,gBAAgB,EAAE,GAAG;IACrB,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC3B,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC9B,YAAY,EAAE,GAAG,GAAG,IAAI;IACxB,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC7B,gBAAgB,EAAE,EAAE;CACZ,CAAC;AA4JX,2CAA2C;AAC3C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,EAAE,mBAAmB;IAC1B,KAAK,EAAE,CAAC,EAAU,EAAE,IAAY,EAAE,EAAE,CAAC,eAAe,EAAE,UAAU,IAAI,EAAE;IACtE,QAAQ,EAAE,CAAC,EAAU,EAAE,IAAY,EAAE,EAAE,CACrC,eAAe,EAAE,aAAa,kBAAkB,CAAC,IAAI,CAAC,EAAE;IAC1D,OAAO,EAAE,CAAC,EAAU,EAAE,IAAY,EAAE,EAAE,CACpC,eAAe,EAAE,YAAY,kBAAkB,CAAC,IAAI,CAAC,EAAE;IACzD,OAAO,EAAE,CAAC,EAAU,EAAE,IAAY,EAAE,EAAE,CACpC,eAAe,EAAE,YAAY,kBAAkB,CAAC,IAAI,CAAC,EAAE;IACzD,MAAM,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS;CAC1C,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAEjC;IACC,MAAM,EACJ,UAAU,EAAE,WAAW,EACvB,SAAS,EAAE,UAAU,EACrB,GAAG,IAAI,EACR,GAAG,QAAQ,CAAC,IAAI,CAAC;IAClB,OAAO,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
|
package/dsl.md
CHANGED
|
@@ -129,6 +129,7 @@ already is. The press lasts 70 to 100 milliseconds, seeded.
|
|
|
129
129
|
| Option | Default | What it sets |
|
|
130
130
|
|---|---|---|
|
|
131
131
|
| `dwell` | `80` to `150`, seeded | How long to rest on the target before pressing. |
|
|
132
|
+
| `modifiers` | none | Keys held through the press: `["Shift"]` for a shift-click, `["Meta"]`, `["Alt"]`, `["Control"]`. |
|
|
132
133
|
|
|
133
134
|
### `drag(target, from, to, options?)`
|
|
134
135
|
|
|
@@ -141,10 +142,15 @@ the drag survives a layout change: `{ x: 0.18, y: 0.5 }` to
|
|
|
141
142
|
Mind the edges. A chart's axis labels live inside its own box, and a drag
|
|
142
143
|
that starts at x 0.08 selects that text instead of the control.
|
|
143
144
|
|
|
145
|
+
`drag(from, to, options?)` is the other form: from one thing to another,
|
|
146
|
+
each a locator or a point, such as a file onto a dropzone or a card onto a
|
|
147
|
+
column. It is told apart by its second argument, which is a locator or a
|
|
148
|
+
point there, and a fraction `{ x, y }` in the first form.
|
|
149
|
+
|
|
144
150
|
| Option | Default | What it sets |
|
|
145
151
|
|---|---|---|
|
|
146
152
|
| `duration` | `1400` | How long the stroke takes, from press to release. |
|
|
147
|
-
| `modifiers` | none | Keys held through the stroke: `["Shift"]`, `["Alt"]`,
|
|
153
|
+
| `modifiers` | none | Keys held through the stroke: `["Shift"]`, `["Alt"]`, `["Meta"]`, `["Control"]`. |
|
|
148
154
|
|
|
149
155
|
### `lasso(target, planner, options?)`
|
|
150
156
|
|
|
@@ -163,6 +169,67 @@ selection from a frame after the loop closes.
|
|
|
163
169
|
| `duration` | `1400` | How long the stroke takes, from press to release. |
|
|
164
170
|
| `modifiers` | none | Keys held through the stroke: `["Shift"]`, `["Alt"]`, or both. |
|
|
165
171
|
|
|
172
|
+
### `scroll(target, where, options?)`
|
|
173
|
+
|
|
174
|
+
Scroll a pane, or the page, on the clip's clock: one eased step a frame, so
|
|
175
|
+
the frames inside the scroll are the same on every run. `target` is the
|
|
176
|
+
element that scrolls, as a locator, or `"page"` for the document. `where` is
|
|
177
|
+
one of three:
|
|
178
|
+
|
|
179
|
+
- `{ by: px }` from where it is now: `{ by: 400 }` goes down, `{ by: -400 }`
|
|
180
|
+
back up.
|
|
181
|
+
- `{ to: "top" | "bottom" | px }` to one end, or to an offset.
|
|
182
|
+
- `{ into: locator, block? }` until that element sits at the `"start"`,
|
|
183
|
+
`"center"` or `"end"` of the pane's visible box. The default is
|
|
184
|
+
`"center"`.
|
|
185
|
+
|
|
186
|
+
The end is clamped to what the element can scroll, and the recording keeps
|
|
187
|
+
the end it reached. An element with nothing to scroll is refused: it is
|
|
188
|
+
nearly always a wrapper around the one that scrolls, which `inspect` shows.
|
|
189
|
+
|
|
190
|
+
The cursor stays where it is. For a scroll that reads as a mouse wheel, move
|
|
191
|
+
the cursor over the pane first. Never scroll with `offCamera` and
|
|
192
|
+
`scrollIntoView` or `mouse.wheel`: the browser animates those on its own
|
|
193
|
+
clock, and the frames inside them differ between runs. While recording, a
|
|
194
|
+
page's own smooth scrolling, from its stylesheet or from its script, is
|
|
195
|
+
instant.
|
|
196
|
+
|
|
197
|
+
| Option | Default | What it sets |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `duration` | `700` | How long the scroll takes. Two frames at the least. `motion.scrollDurationMs` sets the default. |
|
|
200
|
+
| `axis` | `"y"` | `"x"` scrolls sideways. `"top"` is then the left end and `"bottom"` the right. |
|
|
201
|
+
|
|
202
|
+
### `fileWindow(files, options?)`
|
|
203
|
+
|
|
204
|
+
Draw a file window over the page, listing `files`, to drag onto the app's
|
|
205
|
+
dropzone: the browser's own file dialog is outside the page, and nothing can
|
|
206
|
+
film it. It looks like a Finder window. Returns `{ item(i), close() }`:
|
|
207
|
+
`item(i)` is row `i` as a locator, for `click`, `hover` and `drag`.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const files = await s.fileWindow(
|
|
211
|
+
[{ name: "cases.csv", content: csv }, { name: "sites.csv", content: sites }],
|
|
212
|
+
{ title: "Case data" },
|
|
213
|
+
);
|
|
214
|
+
await s.click(files.item(0));
|
|
215
|
+
await s.click(files.item(1), { modifiers: ["Shift"] });
|
|
216
|
+
await s.drag(files.item(0), s.byTestId("dropzone"), { duration: 1000 });
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
A click selects one row, a click with Shift extends to it, and one with
|
|
220
|
+
Meta adds or removes it. A press on a selected row drags the whole
|
|
221
|
+
selection, and letting go over the page sends the element under the pointer
|
|
222
|
+
the drag events a real drag sends, carrying the files as `File` objects, so
|
|
223
|
+
the app takes them through its own dropzone. The window then closes. A file
|
|
224
|
+
is `{ name, content?, type?, size? }`; the type and the Size and Kind columns
|
|
225
|
+
follow from the name and the content unless given. The window is staged:
|
|
226
|
+
say so wherever the clip is shown.
|
|
227
|
+
|
|
228
|
+
| Option | Default | What it sets |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `title` | `"Files"` | The title bar. |
|
|
231
|
+
| `at` | `{ x: 0.52, y: 0.42 }` | The window's top left corner, as fractions of the viewport. |
|
|
232
|
+
|
|
166
233
|
## Keyboard
|
|
167
234
|
|
|
168
235
|
### `typeInto(target, text)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "versioncam",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Record a short, true demo clip of a web app from one sentence: a skill that sets the repository up on its first run, writes the clip, and has a reviewer pass it from a contact sheet.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Peter Bulovec"
|
|
@@ -193,9 +193,14 @@ happened again the first time an agent used it.
|
|
|
193
193
|
|---|---|
|
|
194
194
|
| locator not found at step N | `inspect` at that moment; pick a control that exists; if none does, the beat is wrong |
|
|
195
195
|
| beat produced no new state | missing `settle`, wrong target, or effect off-screen; check cuts and states |
|
|
196
|
-
| settle timed out on `X` | add a `ready` hook, raise the timeout, or the source is live
|
|
196
|
+
| settle timed out on `X` | add a `ready` hook, raise the timeout, or the source is live: name it in `replay.record` so its answer is recorded once and served after |
|
|
197
|
+
| two runs differ where the app fetched something (tiles, a count, an answer) | `replay.record` the request; `replay.pace` it too if the page reads it over time |
|
|
198
|
+
| the clip fails with "its replay does not hold" | the app fetches something new; `versioncam record <id> --replay record` and look at the diff of `replay/<id>.har` |
|
|
197
199
|
| document reloaded mid-clip | something navigated; find the trigger and avoid it |
|
|
198
200
|
| drag or lasso selected nothing | `measure`, re-author geometry as fractions of the real box |
|
|
201
|
+
| the frames inside a scroll differ between runs, or the page scrolls on its own | `s.scroll(pane, { by })`, `{ to }` or `{ into }`; never `scrollIntoView` or `mouse.wheel` from `offCamera`, which the browser animates on its own clock |
|
|
202
|
+
| the clip has to show files being uploaded | `s.fileWindow([...])`, click the rows (Shift to extend), then `s.drag(files.item(0), dropzone)`; never `setInputFiles` from `offCamera`, which shows the files arriving from nowhere. Say in the summary that the window is staged |
|
|
203
|
+
| `scroll` says the element does not scroll | it is a wrapper; `inspect` at that moment and scroll the child with `overflow: auto` |
|
|
199
204
|
| duration drift | adjust holds, never the beats |
|
|
200
205
|
| tooltip / menu open in a frame it should not be | move the cursor start; add a hold before the next action |
|
|
201
206
|
| the caption covers something a beat points at | `theme.presentation.captionInset: { right: 40 }` in the config moves it to the bottom-right corner; it moves every clip's caption, so say so in what you write down |
|
|
@@ -160,9 +160,15 @@ What you may fix, in the config:
|
|
|
160
160
|
already fixed (`clock.time`), so a timestamp that still moves came from an
|
|
161
161
|
API, and that is the next item.
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
answer.
|
|
165
|
-
|
|
163
|
+
- **Live data**: a feed, a count that changes, a map's tiles, a model's
|
|
164
|
+
answer. When the two pictures differ where the app fetched something, name
|
|
165
|
+
the requests in the config's `replay.record` (a URL glob each, as the
|
|
166
|
+
README's *Live data* shows) and run `stability` again: the first run
|
|
167
|
+
records the answers into `replay/`, and every run after serves them. A
|
|
168
|
+
stream the page reads over time also goes in `replay.pace`. Say which
|
|
169
|
+
requests you named and why. The replay holds the server's answers of the
|
|
170
|
+
day it was recorded; tell the person it belongs in the repository, and
|
|
171
|
+
that it may hold what those answers held.
|
|
166
172
|
|
|
167
173
|
**Never author on an unstable app without saying so** — in the report now,
|
|
168
174
|
and in `summary.json` at the end, as `"stable": false`. A clip written against
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){let n=t(e);return n*n*n*(10+n*(-15+6*n))}function t(e){return e<=0?0:e>=1?1:e}function n(e,t,n){return e+(t-e)*n}function r(e,t,r){let i=e.x+e.w/2,a=e.y+e.h/2,o=t.x+t.w/2,s=t.y+t.h/2,c=e.w*(t.w/e.w)**+r,l=e.h*(t.h/e.h)**+r,u=n(i,o,r),d=n(a,s,r);return{x:u-c/2,y:d-l/2,w:c,h:l}}function i(e,t,r){return{x:n(e.x,t.x,r),y:n(e.y,t.y,r),w:n(e.w,t.w,r),h:n(e.h,t.h,r)}}function a(t,n){if(t.length===0)throw Error(`evalCamera called with no keys`);if(n<=t[0].t)return t[0].rect;for(let a=1;a<t.length;a+=1){let o=t[a-1],s=t[a];if(n>=s.t)continue;if(s.ease===`hold`)return o.rect;let c=s.t-o.t,l=c<=0?1:(n-o.t)/c;return s.ease===`linear`?i(o.rect,s.rect,l):r(o.rect,s.rect,e(l))}return t[t.length-1].rect}function o(e,t,n,r){let i=1-r,a=i*i,o=2*i*r,s=r*r;return[a*e[0]+o*t[0]+s*n[0],a*e[1]+o*t[1]+s*n[1]]}function s(t,n){if(t.length===0)return[0,0];let r=t[0];if(n<=r.t0)return r.kind===`move`?r.from:r.at;for(let r of t){if(n>r.t1)continue;if(r.kind===`hold`)return r.at;let t=r.t1-r.t0,i=t<=0?1:(n-r.t0)/t;return o(r.from,r.ctrl,r.to,e(i))}let i=t[t.length-1];return i.kind===`move`?i.to:i.at}function c(e,t,n){let r=n.w/t.w;return[n.x+(e[0]-t.x)*r,n.y+(e[1]-t.y)*r]}function l(e,t,n){let r=n.w/t.w;return{x:n.x+(e.x-t.x)*r,y:n.y+(e.y-t.y)*r,w:e.w*r,h:e.h*r}}function u(e,t){return{x:e.x*t,y:e.y*t,w:e.w*t,h:e.h*t}}function d(e,t,n){let r=Math.min(e.width,e.height)*n,i=e.width-r*2,a=e.height-r*2,o=t.width/t.height,s=i,c=s/o;return c>a&&(c=a,s=c*o),{x:(e.width-s)/2,y:(e.height-c)/2,w:s,h:c}}function f(e,n,r){return r<=0?1:t((e-n+1)/r)}function p(e,t,n){let r=e.frames[t],i=null;for(let a of e.cuts)t>a&&t<=a+n&&e.frames[a]!==r&&(i===null||a>i)&&(i=a);return i===null?{current:r,previous:null,alpha:1}:{current:r,previous:e.frames[i],alpha:f(t,i+1,n)}}function m(e,n,r,i){return e<=n||e>=r?0:i<=0?1:Math.min(t((e-n)/i),t((r-e)/i))}function ee(e,t){return e/t}function te(e,t,n){let r=n?.padding??0;return!n||r<=0?{x:0,y:0,w:e.width,h:e.height}:d(e,t,r)}function h(e){let t=e?.caption??`auto`;return t===`none`?`none`:t===`below`||t===`overlay`?t:(e?.padding??0)>0?`below`:`overlay`}function ne(e,t){let n=e?.captionInset;return n?.right===void 0?{x:n?.left??40,align:`left`}:{x:t-n.right,align:`right`}}var g={radius:16,hairline:`rgba(255, 255, 255, 0.16)`,shadow:`rgba(0, 0, 0, 0.45)`,shadowBlur:60,shadowOffsetY:18,captionFont:`600 30px "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif`,captionColor:`rgba(255, 255, 255, 0.94)`,captionShadow:`rgba(0, 0, 0, 0.5)`,captionScrim:`rgba(0, 0, 0, 0.45)`,captionScrimHeight:86,cursorFill:`#ffffff`,cursorStroke:`rgba(0, 0, 0, 0.55)`,cursorShadow:`rgba(0, 0, 0, 0.35)`,ringWidth:3,ringRadius:10,spotlightDim:.55,spotlightTint:`6, 2, 16`};function re(e,t){let n=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!n)return e;let r=Number.parseInt(n[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${t})`}function _(e,t,n){let r=Math.min(n,t.w/2,t.h/2);e.beginPath(),e.moveTo(t.x+r,t.y),e.arcTo(t.x+t.w,t.y,t.x+t.w,t.y+t.h,r),e.arcTo(t.x+t.w,t.y+t.h,t.x,t.y+t.h,r),e.arcTo(t.x,t.y+t.h,t.x,t.y,r),e.arcTo(t.x,t.y,t.x+t.w,t.y,r),e.closePath()}function ie(e,t,n){if(typeof n==`string`)e.fillStyle=n;else{let r=e.createLinearGradient(0,0,t.width,t.height);r.addColorStop(0,n[0]),r.addColorStop(1,n[1]),e.fillStyle=r}e.fillRect(0,0,t.width,t.height)}function v(e,t,n,r,i,a,o){let s=u(i,t.viewport.dpr),{current:c,previous:l,alpha:d}=p(t,r,o.motion.crossfadeFrames),f=o.theme.presentation?.frame??!1;e.save(),f&&(_(e,a,g.radius),e.clip());let m=(t,r)=>{let i=n.images[t];i&&(e.globalAlpha=r,e.drawImage(i,s.x,s.y,s.w,s.h,a.x,a.y,a.w,a.h))};l!==null&&m(l,1),m(c,l===null?1:d),e.globalAlpha=1,e.restore()}function y(e,t){e.save(),e.shadowColor=g.shadow,e.shadowBlur=g.shadowBlur,e.shadowOffsetY=g.shadowOffsetY,e.fillStyle=`rgba(0,0,0,1)`,_(e,t,g.radius),e.fill(),e.restore()}function b(e,t){e.save(),e.strokeStyle=g.hairline,e.lineWidth=1,_(e,t,g.radius),e.stroke(),e.restore()}function x(e,t){return{x:e.x-t,y:e.y-t,w:e.w+t*2,h:e.h+t*2}}function S(e,t,n,r,i,a){let o=a.theme.presentation?.frame??!1;for(let s of t.highlights){let t=m(n,s.t0,s.t1,.25);if(t<=0)continue;let c=l(s.rect,r,i);e.save(),e.globalAlpha=t,s.style===`spotlight`?(e.save(),_(e,i,o?g.radius:0),e.clip(),e.fillStyle=`rgba(${g.spotlightTint}, ${g.spotlightDim})`,e.fillRect(i.x,i.y,i.w,i.h),e.globalCompositeOperation=`destination-out`,_(e,x(c,8),g.ringRadius),e.fill(),e.restore()):(e.strokeStyle=a.theme.accent,e.lineWidth=g.ringWidth,e.shadowColor=re(a.theme.accent,.35),e.shadowBlur=18,_(e,x(c,6),g.ringRadius),e.stroke()),e.restore()}}function C(e,t,n){let r=!1,i=[],a=0;for(let o of e.pointer){if(o.t>t)break;if(o.kind===`down`)r=!0,a=o.t;else{r=!1;let e=t-o.t;e>=0&&e<=n.motion.rippleMs/1e3&&i.push({at:o.at,age:e})}}let o=r?t-a:0,s=r?Math.min(1,o/(n.motion.pressScaleMs/1e3)):0;return{scale:1-(1-n.motion.pressScale)*s,ripples:i}}function w(e,t,n,r,i){for(let a of t){let t=a.age/(i.motion.rippleMs/1e3),[o,s]=c(a.at,n,r);e.save(),e.globalAlpha=(1-t)*.55,e.strokeStyle=i.theme.accent,e.lineWidth=2.5,e.beginPath(),e.arc(o,s,6+t*i.motion.rippleRadius,0,Math.PI*2),e.stroke(),e.restore()}}function T(e,t,n,r){let i=r*n;e.save(),e.translate(t[0],t[1]),e.scale(i,i),e.shadowColor=g.cursorShadow,e.shadowBlur=6/i,e.shadowOffsetY=2/i,e.beginPath(),e.moveTo(0,0),e.lineTo(0,16.6),e.lineTo(4.1,12.9),e.lineTo(6.9,18.9),e.lineTo(9.6,17.6),e.lineTo(6.9,11.8),e.lineTo(12.1,11.5),e.closePath(),e.fillStyle=g.cursorFill,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=g.cursorStroke,e.lineWidth=1/i,e.stroke(),e.restore()}function E(e,t,n,r,i,a){let o=h(a.theme.presentation);if(o!==`none`)for(let s of t.captions){let t=m(n,s.t0,s.t1,a.motion.captionFadeS);if(t<=0)continue;if(e.save(),e.globalAlpha=t,o===`overlay`){let t=g.captionScrimHeight,n=e.createLinearGradient(0,i.height-t,0,i.height);n.addColorStop(0,`rgba(0, 0, 0, 0)`),n.addColorStop(1,g.captionScrim),e.fillStyle=n,e.fillRect(0,i.height-t,i.width,t)}e.font=g.captionFont,e.fillStyle=g.captionColor,e.textBaseline=`middle`,e.shadowColor=g.captionShadow,e.shadowBlur=8;let c=r.x,l=(r.y+r.h+i.height)/2;if(o===`overlay`){let t=ne(a.theme.presentation,i.width);e.textAlign=t.align,c=t.x,l=i.height-g.captionScrimHeight/2+4}e.fillText(s.text,c,l),e.restore()}}function ae(e,t,n,r,i,o){let l=ee(r,t.fps),u=a(t.camera,l),d=o.theme.presentation,f=te(i,t.viewport,d);d?.backdrop&&ie(e,i,d.backdrop),d?.frame&&y(e,f),v(e,t,n,r,u,f,o),d?.frame&&b(e,f),S(e,t,l,u,f,o);let{scale:p,ripples:m}=C(t,l,o);w(e,m,u,f,o),T(e,c(s(t.cursor,l),u,f),p,o.theme.cursorScale),E(e,t,l,f,i,o)}var D="versioncam reads scenes from a module's default export: a Record<string, Scene> keyed by scene id, as in `export default { [intro.id]: intro }`.";function O(e){return e==null?String(e):Array.isArray(e)?`an array`:`a ${typeof e}`}function k(e){return typeof e==`string`?JSON.stringify(e):String(e)}function A(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oe(e,t){if(!A(t))return`"${e}" is ${O(t)}, not a scene`;let{draw:n,durationFrames:r,fps:i}=t;return typeof n==`function`?!Number.isInteger(r)||r<1?`"${e}" has durationFrames ${k(r)}, where a whole number of frames, 1 or more, belongs`:typeof i!=`number`||!Number.isFinite(i)||i<=0?`"${e}" has fps ${k(i)}, where a number above 0 belongs`:null:`"${e}" has no draw(ctx, frame, canvas)`}function j(e){if(!A(e))return O(e);if(typeof e.draw==`function`)return`a single scene rather than a record of them`;for(let[t,n]of Object.entries(e)){let e=oe(t,n);if(e)return`a record in which ${e}`}return null}function M(e,t){if(e.default===void 0){let n=Object.keys(e).filter(e=>e!=="default"),r=[`${t} has no default export.`+(n.length>0?` It exports ${n.join(`, `)}.`:``),D],i=n.filter(t=>j(e[t])===null&&Object.keys(e[t]).length>0);return i.length===1&&r.push(`${i[0]} is one: add \`export default ${i[0]}\`.`),{error:r.join(`
|
|
2
|
-
`)}}let n=j(e.default);return n?{error:`${t} default-exports ${n}.\n${D}`}:{scenes:e.default}}function N(e,t,n){let r=e.map(e=>`"${e}"`).join(`, `),i=`No scene${e.length===1?``:`s`} called ${r}.`;return n===null?`${i} The config names no \`scenes\` module.`:`${i} ${n} defines ${t.length>0?t.join(`, `):`none`}.`}var P=`modulepreload`,F=function(e,t){return new URL(e,t).href},I={},L=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=F(t,n),t=s(t),t in I)return;I[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:P,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},R=new URLSearchParams(location.search),z=R.has(`recording`),B=R.get(`scene`),V=R.get(`headless`)===`1`;V&&document.body.classList.add(`headless`);var H=document.getElementById(`stage`),U=document.getElementById(`scrub`),W=document.getElementById(`status`),G=document.getElementById(`play`),K=document.getElementById(`error`);function q(e){throw K.textContent=e,Error(e)}async function J(e,t){let n=await fetch(e);return n.ok||q(`Could not load ${t} (${e}): ${n.status}`),await n.json()}async function se(e){let t=await fetch(e);return t.ok||q(`Could not load ${e}: ${t.status}`),createImageBitmap(await t.blob())}function Y(e){H.width=e.output.width,H.height=e.output.height,V?(H.style.width=`${e.output.width}px`,H.style.height=`${e.output.height}px`):(H.style.width=`min(100%, 1280px)`,H.style.height=`auto`);let t=H.getContext(`2d`,{alpha:!1});return t||q(`No 2D context`),t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`,t}function X(e,t,n,r){let i=!1;G.addEventListener(`click`,()=>{i=!i,G.textContent=i?`Pause`:`Play`;let a=performance.now(),o=s=>{i&&(s-a>=1e3/e&&(a=s,r((n()+1)%t)),requestAnimationFrame(o))};requestAnimationFrame(o)})}function Z(e,t,n,r){e.durationFrames=t,e.fps=n,e.setFrame=e=>new Promise(t=>{r(e),requestAnimationFrame(()=>requestAnimationFrame(()=>t()))})}async function Q(e,t){let n=await J(`/recording/timeline.json`,`the recording`),r=Y(t),i={images:await Promise.all(n.states.map(e=>se(`/recording/states/${e}`)))};await document.fonts.ready;let a=-1,o=e=>{let o=Math.max(0,Math.min(n.durationFrames-1,e));ae(r,n,i,o,H,t),a=o,V||(U.value=String(o),W.textContent=`${o+1} / ${n.durationFrames} · ${(o/n.fps).toFixed(2)}s · state ${n.frames[o]}`)};U.max=String(n.durationFrames-1),U.addEventListener(`input`,()=>o(Number(U.value))),X(n.fps,n.durationFrames,()=>a,o),o(Number(R.get(`frame`)??0)),Z(e,n.durationFrames,n.fps,o)}async function ce(e,t,n){t.scenes===null&&q(N([n],[],null));let r=M(await L(()=>import(`/scenes.js`),[],import.meta.url),t.scenes);`error`in r&&q(r.error);let i=r.scenes[n];i||q(N([n],Object.keys(r.scenes),t.scenes));let a=Y(t);await document.fonts.ready;let o=-1,s=e=>{let t=Math.max(0,Math.min(i.durationFrames-1,e));i.draw(a,t,H),o=t,V||(U.value=String(t),W.textContent=`${t+1} / ${i.durationFrames} · ${(t/i.fps).toFixed(2)}s`)};U.max=String(i.durationFrames-1),U.addEventListener(`input`,()=>s(Number(U.value))),X(i.fps,i.durationFrames,()=>o,s),s(Number(R.get(`frame`)??0)),Z(e,i.durationFrames,i.fps,s)}async function le(e){let t=await J(`/render-config.json`,`the render config`);if(B)return ce(e,t,B);if(z)return Q(e,t);q(`Pass ?recording or ?scene=<id>.`)}var $={ready:Promise.resolve(),setFrame:async()=>{},durationFrames:0,fps:60};window.__render=$,$.ready=le($);
|