@motionscript/ui 0.1.0-alpha.0 → 0.1.0-alpha.3

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/browser/chunks/chunk-SFBJ6J3M.js +2 -0
  3. package/dist/browser/chunks/chunk-SFBJ6J3M.js.map +7 -0
  4. package/dist/browser/index.js +1 -1
  5. package/dist/browser/index.js.map +3 -3
  6. package/dist/browser/kit.js +1 -1
  7. package/dist/browser/manifest.json +2 -2
  8. package/dist/button/button.d.ts +6 -4
  9. package/dist/button/button.d.ts.map +1 -1
  10. package/dist/button/button.js +10 -10
  11. package/dist/button/button.js.map +1 -1
  12. package/dist/cursor/cursor.d.ts +1 -1
  13. package/dist/cursor/cursor.d.ts.map +1 -1
  14. package/dist/cursor/cursor.js +1 -1
  15. package/dist/cursor/cursor.js.map +1 -1
  16. package/dist/kit/control-icons.d.ts +1 -1
  17. package/dist/kit/control-icons.d.ts.map +1 -1
  18. package/dist/kit/control-icons.js +1 -1
  19. package/dist/kit/control-icons.js.map +1 -1
  20. package/dist/kit/control-node.d.ts +1 -1
  21. package/dist/kit/control-node.d.ts.map +1 -1
  22. package/dist/kit/control-node.js +1 -1
  23. package/dist/kit/control-node.js.map +1 -1
  24. package/dist/kit/cursor-outline.d.ts +1 -1
  25. package/dist/kit/cursor-outline.d.ts.map +1 -1
  26. package/dist/kit/cursor-outline.js +1 -1
  27. package/dist/kit/cursor-outline.js.map +1 -1
  28. package/dist/nodes.d.ts +1 -1
  29. package/dist/switch/switch.d.ts +1 -1
  30. package/dist/switch/switch.d.ts.map +1 -1
  31. package/dist/switch/switch.js +2 -2
  32. package/dist/switch/switch.js.map +1 -1
  33. package/dist/window/window.d.ts +1 -1
  34. package/dist/window/window.d.ts.map +1 -1
  35. package/dist/window/window.js +1 -1
  36. package/dist/window/window.js.map +1 -1
  37. package/package.json +4 -3
  38. package/src/button/button.ts +15 -12
  39. package/src/cursor/cursor.ts +1 -1
  40. package/src/kit/control-icons.ts +1 -1
  41. package/src/kit/control-node.ts +1 -1
  42. package/src/kit/cursor-outline.ts +1 -1
  43. package/src/switch/switch.ts +2 -2
  44. package/src/window/window.ts +1 -1
  45. package/dist/browser/chunks/chunk-W7G2N4A4.js +0 -2
  46. package/dist/browser/chunks/chunk-W7G2N4A4.js.map +0 -7
@@ -9,7 +9,7 @@ import {
9
9
  type Node,
10
10
  type NodeConfig,
11
11
  type RenderContext2D,
12
- } from "@motionscript/core"
12
+ } from "@motionscript/sdk"
13
13
 
14
14
  import {
15
15
  CONTROL_ICONS,
@@ -24,7 +24,7 @@ import {
24
24
  switchLook,
25
25
  type ControlStyle,
26
26
  } from "../kit/control-style"
27
- import { pathOps } from "@motionscript/core"
27
+ import { pathOps } from "@motionscript/sdk"
28
28
  import { ControlNode, type ControlNodeProps } from "../kit/control-node"
29
29
 
30
30
  /** A switch's own props, over the box and the sound schedule every control has. */
@@ -8,7 +8,7 @@ import {
8
8
  type RenderContext2D,
9
9
  type ShadowResolved,
10
10
  Vector2,
11
- } from "@motionscript/core"
11
+ } from "@motionscript/sdk"
12
12
 
13
13
  import {
14
14
  DEFAULT_WINDOW_STYLE,
@@ -1,2 +0,0 @@
1
- import{pathOps as g}from"@motionscript/core";var j=["none","check","close","dot","plus","minus","play","pause","power","sun","moon","star","heart","lock","bolt","arrowRight"],c1="none",X=24,_={strokes:[],fills:[],weight:0};function o(n,t){return{x:n,y:t}}function l(n,t=!1){return{anchors:n.map(e=>({point:e,inHandle:null,outHandle:null})),closed:t}}function L(n,t,e,r,c,s=!1){let i=r*Math.PI/180,a=c*Math.PI/180-i,x=Math.max(1,Math.ceil(Math.abs(a)/(Math.PI/2))),k=a/x,y=4/3*Math.tan(k/4)*e,m=u=>o(n+e*Math.cos(u),t+e*Math.sin(u)),b=u=>o(-Math.sin(u),Math.cos(u)),w=[];for(let u=0;u<=x;u++){let C=i+k*u,h=m(C),O=b(C);w.push({point:h,inHandle:u===0?null:o(h.x-O.x*y,h.y-O.y*y),outHandle:u===x?null:o(h.x+O.x*y,h.y+O.y*y)})}return{anchors:w,closed:s}}function H(n,t,e){let r=L(n,t,e,0,360,!0),c=r.anchors.slice(0,-1),s=r.anchors[r.anchors.length-1];return c[0]={...c[0],inHandle:s.inHandle},{anchors:c,closed:!0}}function B(n,t){let e=n.anchors.slice(0,-1),r=n.anchors[n.anchors.length-1],[c,...s]=t.anchors;return{anchors:[...e,{...r,outHandle:c.outHandle},...s],closed:!0}}function z(n,t,e,r,c){let s=[];for(let i=0;i<c*2;i++){let f=Math.PI*i/c-Math.PI/2,a=i%2===0?e:r;s.push(o(n+a*Math.cos(f),t+a*Math.sin(f)))}return l(s,!0)}function G(){let c=C=>{let h=C*Math.PI/180;return o(12+8.5*Math.cos(h),12+8.5*Math.sin(h))},s=c(-60),i=c(150),f=(s.x+i.x)/2,a=(s.y+i.y)/2,x=Math.hypot(i.x-s.x,i.y-s.y),k=-(i.y-s.y)/x,y=(i.x-s.x)/x,m=f+k*3.5,b=a+y*3.5,w=Math.hypot(s.x-m,s.y-b),u=C=>Math.atan2(C.y-b,C.x-m)*180/Math.PI;return B(L(12,12,8.5,-60,150),L(m,b,w,u(i),u(s)))}var d=2.2,Z={none:_,check:{strokes:[l([o(5,12.5),o(9.75,17.5),o(19,6.5)])],fills:[],weight:d},close:{strokes:[l([o(6.5,6.5),o(17.5,17.5)]),l([o(17.5,6.5),o(6.5,17.5)])],fills:[],weight:d},dot:{strokes:[],fills:[H(12,12,4.5)],weight:0},plus:{strokes:[l([o(12,5.5),o(12,18.5)]),l([o(5.5,12),o(18.5,12)])],fills:[],weight:d},minus:{strokes:[l([o(5.5,12),o(18.5,12)])],fills:[],weight:d},play:{strokes:[],fills:[l([o(7.5,5),o(19,12),o(7.5,19)],!0)],weight:0},pause:{strokes:[],fills:[l([o(8,5.5),o(11,5.5),o(11,18.5),o(8,18.5)],!0),l([o(13,5.5),o(16,5.5),o(16,18.5),o(13,18.5)],!0)],weight:0},power:{strokes:[L(12,12.5,7,-60,240),l([o(12,3.5),o(12,11.5)])],fills:[],weight:d},sun:{strokes:[H(12,12,4.5),...Array.from({length:8},(n,t)=>{let e=Math.PI*t/4,r=Math.cos(e),c=Math.sin(e);return l([o(12+r*7.5,12+c*7.5),o(12+r*9.75,12+c*9.75)])})],fills:[],weight:d},moon:{strokes:[],fills:[G()],weight:0},star:{strokes:[],fills:[z(12,12,8.5,3.6,5)],weight:0},heart:{strokes:[],fills:[{anchors:[{point:o(12,20),inHandle:o(15.6,17),outHandle:o(8.4,17)},{point:o(4,11.6),inHandle:o(5.4,15.4),outHandle:o(2.2,9.6)},{point:o(7.8,5.5),inHandle:o(4.2,5.5),outHandle:o(10.4,5.5)},{point:o(12,8.6),inHandle:o(11.2,6.6),outHandle:o(12.8,6.6)},{point:o(16.2,5.5),inHandle:o(13.6,5.5),outHandle:o(19.8,5.5)},{point:o(20,11.6),inHandle:o(21.8,9.6),outHandle:o(18.6,15.4)}],closed:!0}],weight:0},lock:{strokes:[L(12,10,4,180,360),{anchors:[{point:o(6.25,10.5),inHandle:o(6.25,10.9),outHandle:null},{point:o(17.75,10.5),inHandle:null,outHandle:o(18.5,10.5)},{point:o(18.5,11.25),inHandle:o(18.5,10.85),outHandle:null},{point:o(18.5,18.75),inHandle:null,outHandle:o(18.5,19.5)},{point:o(17.75,19.5),inHandle:o(18.15,19.5),outHandle:null},{point:o(6.25,19.5),inHandle:null,outHandle:o(5.5,19.5)},{point:o(5.5,18.75),inHandle:o(5.5,19.15),outHandle:null},{point:o(5.5,11.25),inHandle:null,outHandle:o(5.5,10.5)}],closed:!0}],fills:[],weight:d},bolt:{strokes:[],fills:[l([o(13.5,2.5),o(5,13.5),o(11,13.5),o(10.5,21.5),o(19,10.5),o(13,10.5)],!0)],weight:0},arrowRight:{strokes:[l([o(4.5,12),o(19,12)]),l([o(13,6),o(19,12),o(13,18)])],fills:[],weight:d}};function I(n){return Z[n]??_}function i1(n,t){let e=I(n);if(!(t>0))return{strokes:[],fills:[],weight:0};let r=t/X,c=s=>({x:s.x*r-t/2,y:t/2-s.y*r});return{strokes:g.map(e.strokes,c),fills:g.map(e.fills,c),weight:e.weight*r}}function f1(n){let t=I(n);return{strokes:g.fromAnchors(t.strokes),fills:g.fromAnchors(t.fills),weight:t.weight}}function a1(n){return typeof n=="string"&&j.includes(n)}import{Node2D as v,property as q}from"@motionscript/core";var $=function(n,t,e,r){var c=arguments.length,s=c<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,e):r,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(n,t,e,r);else for(var f=n.length-1;f>=0;f--)(i=n[f])&&(s=(c<3?i(s):c>3?i(t,e,s):i(t,e))||s);return c>3&&s&&Object.defineProperty(t,e,s),s},S=class extends v{declareAudio(t){for(let e of this.sounds){if(!e.src)continue;let c=Number.isFinite(e.trimEnd)&&e.trimEnd>0?e.trimEnd:t.catalog.getMediaDuration(e.src);t.clip({src:e.src,at:e.at,until:1/0,volume:e.volume,trimStart:e.trimStart,...Number.isFinite(c)&&c>0?{trimEnd:c}:{}})}}};$([q({default:[]})],S.prototype,"sounds",void 0);var J=["ios","material","fluent","custom"],h1="ios";function d1(n){return typeof n=="string"&&J.includes(n)}var E={ios:{track:1,radius:1,knobOff:27/31,knobOn:27/31,knobRadius:.5,outline:0,shadow:.16,offTrack:"#e9e9eb",offOutline:"#00000000",offKnob:"#ffffff",onKnob:"#ffffff",offIcon:"#8e8e93",accent:"#34c759"},material:{track:1,radius:1,knobOff:16/32,knobOn:24/32,knobRadius:.5,outline:2/32,shadow:0,offTrack:"#e6e0e9",offOutline:"#79747e",offKnob:"#79747e",onKnob:"#ffffff",offIcon:"#e6e0e9",accent:"#6750a4"},fluent:{track:1,radius:1,knobOff:14/20,knobOn:14/20,knobRadius:.5,outline:1/20,shadow:0,offTrack:"#ffffff",offOutline:"#8a8886",offKnob:"#605e5c",onKnob:"#ffffff",offIcon:"#ffffff",accent:"#0f6cbd"},custom:{track:1,radius:1,knobOff:27/31,knobOn:27/31,knobRadius:.5,outline:0,shadow:.16,offTrack:"#e9e9eb",offOutline:"#00000000",offKnob:"#ffffff",onKnob:"#ffffff",offIcon:"#8e8e93",accent:"#34c759"}};function x1(n){return E[n]??E.ios}var C1=51/31,A={ios:{radius:12/25,outline:0,shadow:0,fontSize:17/50,fontWeight:600,letterSpacing:0,iconGap:.4,iconSize:1.15,pressScale:0,pressDim:.18,accent:"#007aff"},material:{radius:1,outline:0,shadow:0,fontSize:14/40,fontWeight:500,letterSpacing:.1/14,iconGap:.6,iconSize:1.3,pressScale:0,pressDim:.12,accent:"#6750a4"},fluent:{radius:4/16,outline:1/32,shadow:0,fontSize:14/32,fontWeight:600,letterSpacing:0,iconGap:.5,iconSize:1.2,pressScale:.03,pressDim:.14,accent:"#0f6cbd"},custom:{radius:12/25,outline:0,shadow:0,fontSize:17/50,fontWeight:600,letterSpacing:0,iconGap:.4,iconSize:1.15,pressScale:0,pressDim:.18,accent:"#007aff"}};function y1(n){return A[n]??A.ios}var m1=160/50;function T(n){let t=n.trim().replace(/^#/,"");if(/^[0-9a-f]{3}$/i.test(t))return[parseInt(t[0]+t[0],16),parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16)];if(!/^[0-9a-f]{6}$/i.test(t))return null;let e=parseInt(t,16);return[e>>16&255,e>>8&255,e&255]}function M(n){return Math.round(Math.min(255,Math.max(0,n))).toString(16).padStart(2,"0")}function b1(n,t,e){let r=T(n),c=T(t);if(!r||!c)return n;let s=Math.min(1,Math.max(0,e));return`#${M(r[0]+(c[0]-r[0])*s)}${M(r[1]+(c[1]-r[1])*s)}${M(r[2]+(c[2]-r[2])*s)}`}function L1(n){let t=T(n);return t&&(.2126*t[0]+.7152*t[1]+.0722*t[2])/255>.6?"#1c1c1e":"#ffffff"}function w1(n){let t=n-Math.floor(n);if(t<=0)return 0;let e=1/3;return t<e?N(t/e):N(1-(t-e)/(1-e))}function N(n){let t=Math.min(1,Math.max(0,n));return t*t*(3-2*t)}import{pathOps as p}from"@motionscript/core";var Q=["default","pointer"],V=["windows"],n1="default",D="windows",R=32,S1=32,P={windows:{body:"#ffffff",keyline:"#000000"}};function M1(n){return P[n]??P[D]}var t1={windows:{default:{keyline:"M 14.78125 5 C 14.75 5.007813 14.71875 5.019531 14.6875 5.03125 C 14.644531 5.050781 14.601563 5.070313 14.5625 5.09375 C 14.550781 5.09375 14.542969 5.09375 14.53125 5.09375 C 14.511719 5.101563 14.488281 5.113281 14.46875 5.125 C 14.457031 5.136719 14.449219 5.144531 14.4375 5.15625 C 14.425781 5.167969 14.417969 5.175781 14.40625 5.1875 C 14.375 5.207031 14.34375 5.226563 14.3125 5.25 C 14.289063 5.269531 14.269531 5.289063 14.25 5.3125 C 14.238281 5.332031 14.226563 5.355469 14.21875 5.375 C 14.183594 5.414063 14.152344 5.457031 14.125 5.5 C 14.113281 5.511719 14.105469 5.519531 14.09375 5.53125 C 14.09375 5.542969 14.09375 5.550781 14.09375 5.5625 C 14.082031 5.582031 14.070313 5.605469 14.0625 5.625 C 14.050781 5.636719 14.042969 5.644531 14.03125 5.65625 C 14.03125 5.675781 14.03125 5.699219 14.03125 5.71875 C 14.019531 5.757813 14.007813 5.800781 14 5.84375 C 14 5.875 14 5.90625 14 5.9375 C 14 5.949219 14 5.957031 14 5.96875 C 14 5.980469 14 5.988281 14 6 C 13.996094 6.050781 13.996094 6.105469 14 6.15625 L 14 39 C 14.003906 39.398438 14.242188 39.757813 14.609375 39.914063 C 14.972656 40.070313 15.398438 39.992188 15.6875 39.71875 L 22.9375 32.90625 L 28.78125 46.40625 C 28.890625 46.652344 29.09375 46.847656 29.347656 46.941406 C 29.601563 47.035156 29.882813 47.023438 30.125 46.90625 L 34.5 44.90625 C 34.996094 44.679688 35.21875 44.09375 35 43.59375 L 28.90625 30.28125 L 39.09375 29.40625 C 39.496094 29.378906 39.84375 29.113281 39.976563 28.730469 C 40.105469 28.347656 39.992188 27.921875 39.6875 27.65625 L 15.84375 5.4375 C 15.796875 5.378906 15.746094 5.328125 15.6875 5.28125 C 15.648438 5.234375 15.609375 5.195313 15.5625 5.15625 C 15.550781 5.15625 15.542969 5.15625 15.53125 5.15625 C 15.511719 5.132813 15.492188 5.113281 15.46875 5.09375 C 15.457031 5.09375 15.449219 5.09375 15.4375 5.09375 C 15.386719 5.070313 15.335938 5.046875 15.28125 5.03125 C 15.269531 5.03125 15.261719 5.03125 15.25 5.03125 C 15.230469 5.019531 15.207031 5.007813 15.1875 5 C 15.175781 5 15.167969 5 15.15625 5 C 15.136719 5 15.113281 5 15.09375 5 C 15.082031 5 15.074219 5 15.0625 5 C 15.042969 5 15.019531 5 15 5 C 14.988281 5 14.980469 5 14.96875 5 C 14.9375 5 14.90625 5 14.875 5 C 14.84375 5 14.8125 5 14.78125 5 Z",body:"M 16 8.28125 L 36.6875 27.59375 L 27.3125 28.40625 C 26.992188 28.4375 26.707031 28.621094 26.546875 28.902344 C 26.382813 29.179688 26.367188 29.519531 26.5 29.8125 L 32.78125 43.5 L 30.21875 44.65625 L 24.21875 30.8125 C 24.089844 30.515625 23.828125 30.296875 23.511719 30.230469 C 23.195313 30.160156 22.863281 30.25 22.625 30.46875 L 16 36.6875 Z"},pointer:{keyline:"M 13 2 C 11.355469 2 10 3.355469 10 5 L 10 16.8125 L 9.34375 16.125 L 9.09375 15.90625 C 7.941406 14.753906 6.058594 14.753906 4.90625 15.90625 C 3.753906 17.058594 3.753906 18.941406 4.90625 20.09375 L 4.90625 20.125 L 13.09375 28.21875 L 13.15625 28.25 L 13.1875 28.3125 C 14.535156 29.324219 16.253906 30 18.1875 30 L 19.90625 30 C 24.441406 30 28.09375 26.347656 28.09375 21.8125 L 28.09375 14 C 28.09375 12.355469 26.738281 11 25.09375 11 C 24.667969 11 24.273438 11.117188 23.90625 11.28125 C 23.578125 9.980469 22.394531 9 21 9 C 20.234375 9 19.53125 9.300781 19 9.78125 C 18.46875 9.300781 17.765625 9 17 9 C 16.648438 9 16.316406 9.074219 16 9.1875 L 16 5 C 16 3.355469 14.644531 2 13 2 Z",body:"M 13 4 C 13.554688 4 14 4.445313 14 5 L 14 16 L 16 16 L 16 12 C 16 11.445313 16.445313 11 17 11 C 17.554688 11 18 11.445313 18 12 L 18 16 L 20 16 L 20 12 C 20 11.445313 20.445313 11 21 11 C 21.554688 11 22 11.445313 22 12 L 22 16 L 24.09375 16 L 24.09375 14 C 24.09375 13.445313 24.539063 13 25.09375 13 C 25.648438 13 26.09375 13.445313 26.09375 14 L 26.09375 21.8125 C 26.09375 25.277344 23.371094 28 19.90625 28 L 18.1875 28 C 16.722656 28 15.457031 27.476563 14.40625 26.6875 L 6.3125 18.6875 C 5.867188 18.242188 5.867188 17.757813 6.3125 17.3125 C 6.757813 16.867188 7.242188 16.867188 7.6875 17.3125 L 12 21.625 L 12 5 C 12 4.445313 12.445313 4 13 4 Z"}}};function o1(n){let t=p.anchors(n.keyline),e=p.anchors(n.body),r=p.anchorBounds([...t,...e]);if(!r)return{keyline:t,body:e};let c=r.maxX-r.minX,s=r.maxY-r.minY,i=R/Math.max(c,s,1e-9),f=a=>({x:(a.x-r.minX)*i,y:(a.y-r.minY)*i});return{keyline:p.map(t,f),body:p.map(e,f)}}var U=Object.fromEntries(Object.entries(t1).map(([n,t])=>[n,Object.fromEntries(Object.entries(t).map(([e,r])=>[e,o1(r)]))]));function K(n,t){let e=U[t]??U[D];return e[n]??e[n1]}function T1(n,t,e,r){if(!(e>0)||!(r>0))return{keyline:[],body:[]};let c=Math.min(e,r)/R,s=R*c,i=a=>({x:a.x*c-s/2,y:a.y*c-s/2}),f=K(n,t);return{keyline:p.map(f.keyline,i),body:p.map(f.body,i)}}function R1(n,t){let e=K(n,t);return{keyline:p.fromAnchors(e.keyline),body:p.fromAnchors(e.body)}}function H1(n){return typeof n=="string"&&Q.includes(n)}function _1(n){return typeof n=="string"&&V.includes(n)}var Y={bow:.15,lean:.5},e1=0,r1=1;function E1(n){if(!n||typeof n!="object")return null;let t=n,e=typeof t.bow=="number"?t.bow:null,r=typeof t.lean=="number"?t.lean:null;return e===null&&r===null?null:{bow:e??Y.bow,lean:W(r??Y.lean)}}function F(n,t,e){let r=t.x-n.x,c=t.y-n.y,s=Math.hypot(r,c);if(s===0)return{x:n.x,y:n.y};let i={x:n.x+r*e.lean,y:n.y+c*e.lean},f=s*e.bow*2;return{x:i.x+-c/s*f,y:i.y+r/s*f}}function A1(n,t,e){let r=F(n,t,e);return{x:.25*n.x+.5*r.x+.25*t.x,y:.25*n.y+.5*r.y+.25*t.y}}function N1(n,t,e){let r=t.x-n.x,c=t.y-n.y,s=Math.hypot(r,c);if(s===0)return null;let i=e.x-n.x,f=e.y-n.y,a=(i*r+f*c)/(s*s);return{bow:(i*-c+f*r)/(s*s),lean:W(a*2-.5)}}function P1(n,t,e,r){if(!e||e.bow===0)return{x:n.x+(t.x-n.x)*r,y:n.y+(t.y-n.y)*r};let c=F(n,t,e),s=1-r;return{x:s*s*n.x+2*s*r*c.x+r*r*t.x,y:s*s*n.y+2*s*r*c.y+r*r*t.y}}function W(n){return Math.min(r1,Math.max(e1,n))}export{j as a,c1 as b,X as c,i1 as d,f1 as e,a1 as f,J as g,h1 as h,d1 as i,E as j,x1 as k,C1 as l,A as m,y1 as n,m1 as o,b1 as p,L1 as q,S as r,w1 as s,Q as t,V as u,n1 as v,D as w,R as x,S1 as y,P as z,M1 as A,T1 as B,R1 as C,H1 as D,_1 as E,Y as F,e1 as G,r1 as H,E1 as I,F as J,A1 as K,N1 as L,P1 as M};
2
- //# sourceMappingURL=chunk-W7G2N4A4.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/kit/control-icons.ts", "../../../src/kit/control-node.ts", "../../../src/kit/control-style.ts", "../../../src/kit/press.ts", "../../../src/kit/cursor-outline.ts", "../../../src/kit/click-path.ts"],
4
- "sourcesContent": ["/**\n * The glyph catalogue the UI-control nodes draw inside themselves — the mark a\n * Switch wears in its knob, and the one a Button wears beside its label.\n *\n * ## Why a catalogue rather than an asset field\n *\n * The obvious alternative is to let an author point each slot at an image in the\n * library, and it is the wrong shape twice over. A control's icon is drawn at\n * twelve pixels inside a moving knob, so it has to be a *line* that thickens\n * with the control rather than a bitmap that blurs; and the icons people\n * actually want here — a tick, a cross, a power symbol — are the same dozen in\n * every product ever shipped, so asking someone to find and upload one is asking\n * them to do work whose only possible answer is already known.\n *\n * The same argument `model/cursor-outline` makes for the pointer, and this\n * module is deliberately its sibling: fixed artwork, normalized once into an\n * author box, handed out mapped onto whatever box the node has.\n *\n * ## Built rather than pasted\n *\n * Unlike the cursor's, these contours are **constructed** — {@link circle},\n * {@link arc} and {@link polyline} rather than verbatim `d` strings. Two\n * reasons, and the second is the load-bearing one:\n *\n * - Half of them are circles and arcs, and `parseVectorPath` deliberately\n * understands no `A` command (see its `COMMAND_LETTERS`), so a pasted circle\n * would have arrived as somebody's hand-expanded cubics anyway.\n * - A glyph built from named geometry is one somebody can *correct*. \"The power\n * ring opens 60° at the top\" is a line of code; the same fact spelled as eight\n * control points is a drawing nobody will ever touch again.\n *\n * ## Two kinds of contour\n *\n * A glyph carries a stroked list and a filled list, and most carry only one. It\n * is not a stylistic choice: a tick has no interior and a play triangle has\n * nothing else, so drawing either the other way round is drawing a different\n * mark. {@link ControlIconArtwork.weight} is in box units, so it scales with the\n * glyph exactly as the cursor's keyline does — a pixel weight would be a\n * hairline on a large switch and a blob on a small one.\n */\n\nimport { pathOps, type PathSubpath, type VectorPath, type Vector2 } from \"@motionscript/core\"\n\n/**\n * The marks a control can wear, in the order the dropdown offers them.\n *\n * `none` leads and is the default for both slots, because the commonest switch\n * in the world has no icon in it at all — the position *is* the state. What the\n * rest are for is the switch whose two states are not obviously opposite (\"mic\n * live / mic muted\"), where the mark is what saves a caption.\n *\n * Ordered by what they answer rather than alphabetically: the yes/no pair, the\n * arithmetic pair, the transport pair, then the ones that name a *thing* being\n * switched — power, light, sound, security.\n */\nexport const CONTROL_ICONS = [\n \"none\",\n \"check\",\n \"close\",\n \"dot\",\n \"plus\",\n \"minus\",\n \"play\",\n \"pause\",\n \"power\",\n \"sun\",\n \"moon\",\n \"star\",\n \"heart\",\n \"lock\",\n \"bolt\",\n \"arrowRight\",\n] as const\n\nexport type ControlIcon = (typeof CONTROL_ICONS)[number]\n\n/** What both icon slots open on: nothing. See {@link CONTROL_ICONS}. */\nexport const DEFAULT_CONTROL_ICON: ControlIcon = \"none\"\n\n/** The side of the square box every glyph is authored in, y-down, origin top-left. */\nexport const CONTROL_ICON_UNITS = 24\n\n/** One glyph: what to stroke, what to fill, and how thick the stroke is. */\nexport interface ControlIconArtwork {\n /** Open (or closed) contours drawn as lines. */\n strokes: VectorPath\n /** Closed contours painted solid. */\n fills: VectorPath\n /** Stroke weight, in units of the {@link CONTROL_ICON_UNITS} box. */\n weight: number\n}\n\n/** The empty glyph — what `none` resolves to, and the fallback for a bad key. */\nconst EMPTY_ARTWORK: ControlIconArtwork = {\n strokes: [],\n fills: [],\n weight: 0,\n}\n\n// --- Construction helpers --------------------------------------------------\n\nfunction point(x: number, y: number): Vector2 {\n return { x, y }\n}\n\n/** A run of straight segments through `points`, optionally closed. */\nfunction polyline(points: Vector2[], closed = false): PathSubpath {\n return {\n anchors: points.map((p) => ({ point: p, inHandle: null, outHandle: null })),\n closed,\n }\n}\n\n/**\n * A circular arc as cubics, swept **clockwise** in the y-down box from `from`\n * degrees to `to` degrees (0° is due east, 90° due south).\n *\n * Split so no segment spans more than a quarter turn, which is the point past\n * which a single cubic's error stops being invisible. The handle length is the\n * standard `(4/3)·tan(Δ/4)·r`, exact at the endpoints and within a thousandth of\n * the radius in between — far below the half-pixel anyone could see at the size\n * these are drawn.\n */\nfunction arc(\n cx: number,\n cy: number,\n radius: number,\n from: number,\n to: number,\n closed = false\n): PathSubpath {\n const start = (from * Math.PI) / 180\n const end = (to * Math.PI) / 180\n const sweep = end - start\n const steps = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)))\n const step = sweep / steps\n const handle = (4 / 3) * Math.tan(step / 4) * radius\n\n const at = (angle: number): Vector2 =>\n point(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle))\n /** The unit tangent in the direction of increasing angle. */\n const tangent = (angle: number): Vector2 =>\n point(-Math.sin(angle), Math.cos(angle))\n\n const anchors = []\n for (let i = 0; i <= steps; i++) {\n const angle = start + step * i\n const p = at(angle)\n const t = tangent(angle)\n anchors.push({\n point: p,\n // The first anchor has nothing arriving at it and the last nothing\n // leaving, which is what makes this an arc rather than a closed ring.\n inHandle: i === 0 ? null : point(p.x - t.x * handle, p.y - t.y * handle),\n outHandle:\n i === steps ? null : point(p.x + t.x * handle, p.y + t.y * handle),\n })\n }\n return { anchors, closed }\n}\n\n/** A full ring, as the closed four-segment arc. */\nfunction circle(cx: number, cy: number, radius: number): PathSubpath {\n const ring = arc(cx, cy, radius, 0, 360, true)\n // The sweep lands exactly back on the first anchor, so the last one is that\n // same point twice — dropped, with its incoming handle moved onto the anchor\n // it duplicates, which is what closes the ring smoothly rather than with a\n // corner.\n const anchors = ring.anchors.slice(0, -1)\n const last = ring.anchors[ring.anchors.length - 1]\n anchors[0] = { ...anchors[0], inHandle: last.inHandle }\n return { anchors, closed: true }\n}\n\n/**\n * Two arcs welded end to end into one contour.\n *\n * The second's first anchor is the same *point* as the first's last — that is\n * what makes the pair a shape rather than two curves — so it is dropped and its\n * outgoing handle moved onto the anchor it duplicated. Leaving it in would put a\n * zero-length segment at the seam, which strokes as a dot and fills as nothing.\n */\nfunction joinArcs(first: PathSubpath, second: PathSubpath): PathSubpath {\n const head = first.anchors.slice(0, -1)\n const seam = first.anchors[first.anchors.length - 1]\n const [start, ...rest] = second.anchors\n return {\n anchors: [...head, { ...seam, outHandle: start.outHandle }, ...rest],\n closed: true,\n }\n}\n\n/** A regular star, `points` long, alternating between the two radii. */\nfunction star(\n cx: number,\n cy: number,\n outer: number,\n inner: number,\n points: number\n): PathSubpath {\n const vertices: Vector2[] = []\n for (let i = 0; i < points * 2; i++) {\n // Starts at due north, which is the only orientation anyone pictures.\n const angle = (Math.PI * i) / points - Math.PI / 2\n const radius = i % 2 === 0 ? outer : inner\n vertices.push(\n point(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle))\n )\n }\n return polyline(vertices, true)\n}\n\n/**\n * The crescent's outline: the fat edge of one circle, bitten by another.\n *\n * The two **tips** are chosen first — at −60° and 150° on the outer ring, which\n * is the tilt every moon glyph is drawn at — and the biting circle is then\n * derived from them: its centre is the point on their perpendicular bisector\n * that sits {@link BITE} away from their midpoint, on the side the bite comes\n * from. So the two arcs meet exactly, by construction rather than by adjustment,\n * and moving the tips or deepening the bite keeps them meeting.\n */\nfunction crescent(): PathSubpath {\n const cx = 12\n const cy = 12\n const radius = 8.5\n /** How far the biting circle's centre sits off the chord's midpoint. */\n const BITE = 3.5\n\n const tip = (degrees: number): Vector2 => {\n const angle = (degrees * Math.PI) / 180\n return point(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle))\n }\n const first = tip(-60)\n const second = tip(150)\n\n // The chord's midpoint, pushed along its own perpendicular toward the\n // up-and-left, which is where a crescent's bite comes from.\n const midX = (first.x + second.x) / 2\n const midY = (first.y + second.y) / 2\n const span = Math.hypot(second.x - first.x, second.y - first.y)\n const normalX = -(second.y - first.y) / span\n const normalY = (second.x - first.x) / span\n const biteX = midX + normalX * BITE\n const biteY = midY + normalY * BITE\n const biteRadius = Math.hypot(first.x - biteX, first.y - biteY)\n const angleOf = (p: Vector2): number =>\n (Math.atan2(p.y - biteY, p.x - biteX) * 180) / Math.PI\n\n return joinArcs(\n // The outer edge: the long way round, through the east and the south.\n arc(cx, cy, radius, -60, 150),\n // …and back through the biting circle's own east and south, which lies\n // inside the ring above and is therefore the concave side.\n arc(biteX, biteY, biteRadius, angleOf(second), angleOf(first))\n )\n}\n\n// --- The catalogue ---------------------------------------------------------\n\n/** The default stroke weight: two units of twenty-four, as lucide draws. */\nconst WEIGHT = 2.2\n\n/**\n * Every glyph, in the 24-unit author box.\n *\n * Built at module load rather than per draw, exactly as the cursor's artwork is:\n * these are fixed drawings with nothing to recompute, so re-deriving a few dozen\n * control points every frame would buy nothing.\n */\nconst ARTWORK: Record<ControlIcon, ControlIconArtwork> = {\n none: EMPTY_ARTWORK,\n\n check: {\n strokes: [polyline([point(5, 12.5), point(9.75, 17.5), point(19, 6.5)])],\n fills: [],\n weight: WEIGHT,\n },\n\n close: {\n strokes: [\n polyline([point(6.5, 6.5), point(17.5, 17.5)]),\n polyline([point(17.5, 6.5), point(6.5, 17.5)]),\n ],\n fills: [],\n weight: WEIGHT,\n },\n\n // A filled disc — the mark for a switch whose states are \"armed\" and \"not\",\n // where a tick would claim something was completed.\n dot: { strokes: [], fills: [circle(12, 12, 4.5)], weight: 0 },\n\n plus: {\n strokes: [\n polyline([point(12, 5.5), point(12, 18.5)]),\n polyline([point(5.5, 12), point(18.5, 12)]),\n ],\n fills: [],\n weight: WEIGHT,\n },\n\n minus: {\n strokes: [polyline([point(5.5, 12), point(18.5, 12)])],\n fills: [],\n weight: WEIGHT,\n },\n\n // Filled, because a play triangle has no interior to leave empty — an\n // outlined one reads as a \"next\" chevron rather than as play.\n play: {\n strokes: [],\n fills: [polyline([point(7.5, 5), point(19, 12), point(7.5, 19)], true)],\n weight: 0,\n },\n\n pause: {\n strokes: [],\n fills: [\n polyline(\n [point(8, 5.5), point(11, 5.5), point(11, 18.5), point(8, 18.5)],\n true\n ),\n polyline(\n [point(13, 5.5), point(16, 5.5), point(16, 18.5), point(13, 18.5)],\n true\n ),\n ],\n weight: 0,\n },\n\n // The ring opens 60° at the top, centred on the stem — the IEC 5009 symbol\n // every device on earth wears, and the reason the gap is stated in degrees\n // rather than drawn: it is a fact about the symbol, not about this drawing.\n power: {\n strokes: [\n arc(12, 12.5, 7, -60, 240),\n polyline([point(12, 3.5), point(12, 11.5)]),\n ],\n fills: [],\n weight: WEIGHT,\n },\n\n sun: {\n strokes: [\n circle(12, 12, 4.5),\n ...Array.from({ length: 8 }, (_, i) => {\n const angle = (Math.PI * i) / 4\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n return polyline([\n point(12 + cos * 7.5, 12 + sin * 7.5),\n point(12 + cos * 9.75, 12 + sin * 9.75),\n ])\n }),\n ],\n fills: [],\n weight: WEIGHT,\n },\n\n // A crescent as two arcs of two different circles rather than one circle cut\n // out of another: a cut needs an even-odd fill the renderer would have to be\n // told about, and the outline is the same shape either way.\n //\n // The second circle is *derived* from where the two tips sit rather than\n // eyeballed, which is the only way the two arcs meet exactly — a crescent\n // whose halves miss each other by a unit closes with a straight nick across\n // its own point. See {@link crescent}.\n moon: { strokes: [], fills: [crescent()], weight: 0 },\n\n star: { strokes: [], fills: [star(12, 12, 8.5, 3.6, 5)], weight: 0 },\n\n // Two lobes meeting at a point. Written as one closed contour of four cubics\n // because a heart is a *shape*, and splitting it into halves would leave a\n // seam down the middle of any stroke drawn on it.\n heart: {\n strokes: [],\n fills: [\n {\n anchors: [\n {\n point: point(12, 20),\n inHandle: point(15.6, 17),\n outHandle: point(8.4, 17),\n },\n {\n point: point(4, 11.6),\n inHandle: point(5.4, 15.4),\n outHandle: point(2.2, 9.6),\n },\n {\n point: point(7.8, 5.5),\n inHandle: point(4.2, 5.5),\n outHandle: point(10.4, 5.5),\n },\n {\n point: point(12, 8.6),\n inHandle: point(11.2, 6.6),\n outHandle: point(12.8, 6.6),\n },\n {\n point: point(16.2, 5.5),\n inHandle: point(13.6, 5.5),\n outHandle: point(19.8, 5.5),\n },\n {\n point: point(20, 11.6),\n inHandle: point(21.8, 9.6),\n outHandle: point(18.6, 15.4),\n },\n ],\n closed: true,\n },\n ],\n weight: 0,\n },\n\n lock: {\n strokes: [\n // The shackle: a half turn, open at the bottom where it meets the body.\n arc(12, 10, 4, 180, 360),\n // The body, as a rounded box drawn from its four corners.\n {\n anchors: [\n {\n point: point(6.25, 10.5),\n inHandle: point(6.25, 10.9),\n outHandle: null,\n },\n {\n point: point(17.75, 10.5),\n inHandle: null,\n outHandle: point(18.5, 10.5),\n },\n {\n point: point(18.5, 11.25),\n inHandle: point(18.5, 10.85),\n outHandle: null,\n },\n {\n point: point(18.5, 18.75),\n inHandle: null,\n outHandle: point(18.5, 19.5),\n },\n {\n point: point(17.75, 19.5),\n inHandle: point(18.15, 19.5),\n outHandle: null,\n },\n {\n point: point(6.25, 19.5),\n inHandle: null,\n outHandle: point(5.5, 19.5),\n },\n {\n point: point(5.5, 18.75),\n inHandle: point(5.5, 19.15),\n outHandle: null,\n },\n {\n point: point(5.5, 11.25),\n inHandle: null,\n outHandle: point(5.5, 10.5),\n },\n ],\n closed: true,\n },\n ],\n fills: [],\n weight: WEIGHT,\n },\n\n bolt: {\n strokes: [],\n fills: [\n polyline(\n [\n point(13.5, 2.5),\n point(5, 13.5),\n point(11, 13.5),\n point(10.5, 21.5),\n point(19, 10.5),\n point(13, 10.5),\n ],\n true\n ),\n ],\n weight: 0,\n },\n\n arrowRight: {\n strokes: [\n polyline([point(4.5, 12), point(19, 12)]),\n polyline([point(13, 6), point(19, 12), point(13, 18)]),\n ],\n fills: [],\n weight: WEIGHT,\n },\n}\n\n/** The glyph a key names, or the empty one for a key this build doesn't know. */\nfunction artworkFor(icon: ControlIcon): ControlIconArtwork {\n return ARTWORK[icon] ?? EMPTY_ARTWORK\n}\n\n/**\n * The glyph mapped onto a `size` × `size` box **centred on the origin and\n * y-up** — the space a node draws in.\n *\n * The flip is the whole of the difference between the author box and the\n * renderer's, exactly as it is for the cursor: glyphs are drawn y-down because\n * that is what every SVG box and every icon set uses, and a node's own space\n * counts upward.\n *\n * The weight comes back in pixels so the caller can hand it straight to a\n * stroke. It scales with the glyph, which is the point: an icon in a switch knob\n * is drawn at whatever size the knob came out, and a fixed pixel weight would be\n * a hairline on a big control and a smudge on a small one.\n */\nexport function controlIconPixels(\n icon: ControlIcon,\n size: number\n): { strokes: VectorPath; fills: VectorPath; weight: number } {\n const artwork = artworkFor(icon)\n if (!(size > 0)) return { strokes: [], fills: [], weight: 0 }\n const scale = size / CONTROL_ICON_UNITS\n const onto = (p: Vector2): Vector2 => ({\n x: p.x * scale - size / 2,\n y: size / 2 - p.y * scale,\n })\n return {\n strokes: pathOps.map(artwork.strokes, onto),\n fills: pathOps.map(artwork.fills, onto),\n weight: artwork.weight * scale,\n }\n}\n\n/**\n * The glyph as SVG `d` strings in the 24-unit box, for the studio's own chrome.\n *\n * What the icon dropdown draws its options with, so a mark corrected here is\n * corrected in the panel too — the same bargain `cursorArtworkData` strikes, and\n * for the same reason: a hand-drawn approximation in the picker is a picture of\n * an icon the node does not draw.\n */\nexport function controlIconData(icon: ControlIcon): {\n strokes: string\n fills: string\n weight: number\n} {\n const artwork = artworkFor(icon)\n return {\n strokes: pathOps.fromAnchors(artwork.strokes),\n fills: pathOps.fromAnchors(artwork.fills),\n weight: artwork.weight,\n }\n}\n\n/** Whether a stored value names a glyph this build can draw. */\nexport function isControlIcon(value: unknown): value is ControlIcon {\n return (\n typeof value === \"string\" &&\n (CONTROL_ICONS as readonly string[]).includes(value)\n )\n}\n", "/**\n * The base the two UI controls share: a node that can **make a noise when a\n * command runs**.\n *\n * ## Declared, not fired\n *\n * Playing the sound from the command's own step cannot work: a seek jumps\n * straight to a frame without running the steps before it, and an export renders\n * the same frame many times, so a step that started a clip would play the click\n * zero times, once, or forty times depending on how the playhead arrived. So a\n * node is handed its whole schedule at construction — \"at this second of the\n * clip, play this file\" — and declares it, which keeps the mix a pure function\n * of the document.\n *\n * The cues are built by the builder rather than here, because only it holds the\n * resolved asset URLs and the node's spawn time (see `nodes/ui-kit/sound-cues.ts`).\n *\n * ## Why the base class exists at all\n *\n * Two nodes need this and neither is a kind of the other — a switch is not a\n * button — so the alternative is the same code twice. What they genuinely share\n * is exactly this: a box, a style, and a schedule of sounds.\n */\n\nimport {\n Node2D,\n property,\n type AudioScope,\n type Node2DProps,\n} from \"@motionscript/core\"\n\n/**\n * One sound a node's chain fires: a moment, a file, and the window of it played.\n *\n * `at` is a second of the **clip's** clock rather than of the node's own, because\n * that is what an `AudioRequest.startAt` is. The builder adds the node's spawn\n * delay for exactly this reason.\n */\nexport interface SoundCue {\n /** Second of the clip's clock the effect fires at. */\n at: number\n /** The resolved source URL. Never an asset id: the node cannot resolve one. */\n src: string\n /** In-point inside the source, in seconds. */\n trimStart: number\n /**\n * Out-point, or `0` for \"to the end of the source\" — the same sentinel\n * {@link CommandSound.trimEnd} carries, passed through unresolved because the\n * length is a fact only the asset catalog holds.\n */\n trimEnd: number\n /** Linear gain, `0`–`1`. */\n volume: number\n}\n\nexport interface ControlNodeProps extends Node2DProps {\n /** The node's whole audio schedule — see {@link SoundCue}. */\n sounds: SoundCue[]\n}\n\n/**\n * A node with a sound schedule. Abstract: what it *draws* is its subclass's\n * business, and there is no sensible drawing for \"a control\".\n */\nexport abstract class ControlNode<\n P extends ControlNodeProps,\n> extends Node2D<P> {\n @property({ default: [] }) declare sounds: SoundCue[]\n\n override declareAudio(audio: AudioScope): void {\n for (const cue of this.sounds) {\n if (!cue.src) continue\n const trimmed = Number.isFinite(cue.trimEnd) && cue.trimEnd > 0\n const length = trimmed ? cue.trimEnd : audio.catalog.getMediaDuration(cue.src)\n audio.clip({\n src: cue.src,\n at: cue.at,\n // A click outlives the control that made it.\n until: Infinity,\n volume: cue.volume,\n trimStart: cue.trimStart,\n // An unknown length leaves the clip open rather than pinning it to zero.\n ...(Number.isFinite(length) && length > 0 ? { trimEnd: length } : {}),\n })\n }\n }\n}\n", "/**\n * The looks the UI-control nodes come in — and the argument for there being a\n * *look* at all rather than thirty controls.\n *\n * A switch has a track height, a track radius, a knob diameter, a knob inset, a\n * knob radius, an outline weight, three resting colours, a travel curve and a\n * shadow. Every one of those is a real decision, and not one of them is a\n * decision the person making a video about an app wants to make: what they want\n * is *the iOS switch*, because the video is about an iPhone. Exposing the thirty\n * would be handing them a kit to rebuild, from memory, a thing that has one\n * correct answer.\n *\n * So the control asks one question — whose switch? — and the answers are the\n * three design languages a viewer can actually recognise, plus **Custom** for\n * the case none of them covers. Custom is not a fourth look: it is the same\n * geometry with its colours and its corner unlocked, which is what the schema's\n * `showWhen` gating is for (see `model/appearance/schema.ts`).\n *\n * ## What a look does *not* decide\n *\n * The **accent** — the colour a switch turns when it is on, and a button's\n * fill — is a field on the node in every style, because it is the one thing here\n * that is genuinely about the product being demonstrated rather than about the\n * platform it runs on. A look ships the accent its platform is *usually* seen\n * with as the field's starting value, and nothing more.\n *\n * Nor the **size**. Every measurement below is a fraction of the node's own box,\n * so a switch is resized with the same handle as a rectangle and the drawing\n * keeps its proportions. {@link SWITCH_ASPECT} is what the node is *seeded* at,\n * not what it is held to.\n */\n\n/** The looks, in the order the dropdown offers them. */\nexport const CONTROL_STYLES = [\"ios\", \"material\", \"fluent\", \"custom\"] as const\n\nexport type ControlStyle = (typeof CONTROL_STYLES)[number]\n\n/**\n * What a new control opens as.\n *\n * iOS, and not because it is better: it is the switch the largest number of\n * people can name on sight, so it is the one most likely to be recognised as\n * \"the thing I meant\" by an author who has not yet found this dropdown.\n */\nexport const DEFAULT_CONTROL_STYLE: ControlStyle = \"ios\"\n\n/** Whether a stored value names a look this build knows. */\nexport function isControlStyle(value: unknown): value is ControlStyle {\n return (\n typeof value === \"string\" &&\n (CONTROL_STYLES as readonly string[]).includes(value)\n )\n}\n\n// --- The switch ------------------------------------------------------------\n\n/**\n * One switch look, stated as fractions of the node's own box.\n *\n * Fractions rather than pixels throughout, which is what lets the resize handle\n * work on this node at all: a look expressed in points would be correct at one\n * size and wrong at every other, and the node would need a \"scale\" field\n * duplicating the box it already has.\n */\nexport interface SwitchLook {\n /**\n * How much of the node's height the track occupies, `0`–`1`.\n *\n * One today, and the field exists because it is the first thing a fourth look\n * would need: several design systems draw a *thin* track with a knob standing\n * proud of it, which is a switch whose track is a third of its box.\n */\n track: number\n /** Track corner radius as a fraction of half the track's height — `1` is a pill. */\n radius: number\n /** Knob diameter as a fraction of the track's height, when **off**. */\n knobOff: number\n /** …and when **on**. Differing is the Material 3 knob, which grows. */\n knobOn: number\n /** Knob corner radius as a fraction of its own diameter — `0.5` is a circle. */\n knobRadius: number\n /** Track outline weight as a fraction of the track's height. `0` draws none. */\n outline: number\n /** How far the knob's shadow spreads, as a fraction of the track's height. */\n shadow: number\n /** The track when off. */\n offTrack: string\n /** The track's outline when off — unused where {@link outline} is `0`. */\n offOutline: string\n /** The knob when off. */\n offKnob: string\n /** The knob when on. Off-white or white in every look shipped so far. */\n onKnob: string\n /** The mark inside the knob when off. */\n offIcon: string\n /**\n * The accent a **new** switch in this look starts with — the platform's own\n * \"on\" colour, and nothing more than a starting value: it is copied into the\n * node's `accent` field at creation and never read again.\n */\n accent: string\n}\n\n/**\n * The three looks, measured off the real controls.\n *\n * The numbers are ratios taken from each platform's published metrics rather\n * than from a screenshot, which matters most for the knob: iOS's stands almost\n * flush in its track (2px of a 31pt switch), Material 3's is inset by a quarter\n * and *grows* when it turns on, and Fluent's is a small pill floating in a\n * hollow track. Those three facts are what make each recognisable at a glance,\n * far more than the colours are.\n */\nexport const SWITCH_LOOKS: Record<ControlStyle, SwitchLook> = {\n // 51 × 31 pt, knob 27pt, inset 2pt each side, soft shadow under it.\n ios: {\n track: 1,\n radius: 1,\n knobOff: 27 / 31,\n knobOn: 27 / 31,\n knobRadius: 0.5,\n outline: 0,\n shadow: 0.16,\n offTrack: \"#e9e9eb\",\n offOutline: \"#00000000\",\n offKnob: \"#ffffff\",\n onKnob: \"#ffffff\",\n offIcon: \"#8e8e93\",\n accent: \"#34c759\",\n },\n // 52 × 32 dp. Off: a 2dp outline, a 16dp knob in the outline's own colour.\n // On: no outline, a 24dp knob. The growth is the whole gesture.\n material: {\n track: 1,\n radius: 1,\n knobOff: 16 / 32,\n knobOn: 24 / 32,\n knobRadius: 0.5,\n outline: 2 / 32,\n shadow: 0,\n offTrack: \"#e6e0e9\",\n offOutline: \"#79747e\",\n offKnob: \"#79747e\",\n onKnob: \"#ffffff\",\n offIcon: \"#e6e0e9\",\n accent: \"#6750a4\",\n },\n // 40 × 20 px, 1px border, a 14px knob that keeps its size and changes colour\n // — the hollow track is Fluent's signature and the reason `offTrack` here is\n // the page rather than a grey.\n fluent: {\n track: 1,\n radius: 1,\n knobOff: 14 / 20,\n knobOn: 14 / 20,\n knobRadius: 0.5,\n outline: 1 / 20,\n shadow: 0,\n offTrack: \"#ffffff\",\n offOutline: \"#8a8886\",\n offKnob: \"#605e5c\",\n onKnob: \"#ffffff\",\n offIcon: \"#ffffff\",\n accent: \"#0f6cbd\",\n },\n // Custom starts *as* iOS and unlocks the four fields the schema hides on the\n // others (see `showWhen`), so choosing it changes nothing until something is\n // changed — which is the only honest behaviour for a mode named \"custom\".\n custom: {\n track: 1,\n radius: 1,\n knobOff: 27 / 31,\n knobOn: 27 / 31,\n knobRadius: 0.5,\n outline: 0,\n shadow: 0.16,\n offTrack: \"#e9e9eb\",\n offOutline: \"#00000000\",\n offKnob: \"#ffffff\",\n onKnob: \"#ffffff\",\n offIcon: \"#8e8e93\",\n accent: \"#34c759\",\n },\n}\n\n/** The look a style names, falling back for a value this build doesn't know. */\nexport function switchLook(style: ControlStyle): SwitchLook {\n return SWITCH_LOOKS[style] ?? SWITCH_LOOKS[DEFAULT_CONTROL_STYLE]\n}\n\n/**\n * The proportions a new Switch node is seeded at — near enough the real\n * control's, so one dropped on the canvas already looks like the thing.\n *\n * iOS's 51 : 31 rather than an average of the three, on the same reasoning\n * {@link DEFAULT_CONTROL_STYLE} picks iOS: it is the shape most people picture,\n * and the other two are within a few percent of it anyway.\n */\nexport const SWITCH_ASPECT = 51 / 31\n\n// --- The button ------------------------------------------------------------\n\n/** One button look, on the same terms as {@link SwitchLook}. */\nexport interface ButtonLook {\n /** Corner radius as a fraction of half the node's height — `1` is a pill. */\n radius: number\n /** Border weight as a fraction of the node's height. `0` draws none. */\n outline: number\n /** Drop-shadow spread as a fraction of the node's height. `0` draws none. */\n shadow: number\n /** Label size as a fraction of the node's height. */\n fontSize: number\n /** Label weight, as a CSS numeric weight. */\n fontWeight: number\n /** Tracking, as a fraction of the label's own size. */\n letterSpacing: number\n /** Gap between a leading icon and the label, as a fraction of the label size. */\n iconGap: number\n /** Icon size as a fraction of the label size. */\n iconSize: number\n /**\n * How far the button shrinks while pressed, as a fraction of its own size.\n *\n * Zero on the two platforms that answer a press with colour alone — which is\n * a real difference between them and not a detail: iOS dims, Material lays a\n * state layer over, and Fluent does both and moves.\n */\n pressScale: number\n /** How far the fill darkens while pressed, `0`–`1`. */\n pressDim: number\n /** The accent a **new** button in this look starts with. See {@link SwitchLook.accent}. */\n accent: string\n}\n\n/** The three looks, measured the same way {@link SWITCH_LOOKS} is. */\nexport const BUTTON_LOOKS: Record<ControlStyle, ButtonLook> = {\n // A 50pt filled button: 12pt corner, 17pt semibold label, no border, no\n // shadow. Pressing dims it and moves nothing.\n ios: {\n radius: 12 / 25,\n outline: 0,\n shadow: 0,\n fontSize: 17 / 50,\n fontWeight: 600,\n letterSpacing: 0,\n iconGap: 0.4,\n iconSize: 1.15,\n pressScale: 0,\n pressDim: 0.18,\n accent: \"#007aff\",\n },\n // A 40dp filled button: fully rounded, 14dp medium label with M3's own\n // tracking. The press is a state layer, so it darkens and holds still.\n material: {\n radius: 1,\n outline: 0,\n shadow: 0,\n fontSize: 14 / 40,\n fontWeight: 500,\n letterSpacing: 0.1 / 14,\n iconGap: 0.6,\n iconSize: 1.3,\n pressScale: 0,\n pressDim: 0.12,\n accent: \"#6750a4\",\n },\n // A 32px button: a 4px corner, a 14px semibold label, a hairline border. The\n // press both darkens and settles, which is Fluent's own depress.\n fluent: {\n radius: 4 / 16,\n outline: 1 / 32,\n shadow: 0,\n fontSize: 14 / 32,\n fontWeight: 600,\n letterSpacing: 0,\n iconGap: 0.5,\n iconSize: 1.2,\n pressScale: 0.03,\n pressDim: 0.14,\n accent: \"#0f6cbd\",\n },\n // As iOS, for the reason `SWITCH_LOOKS.custom` is.\n custom: {\n radius: 12 / 25,\n outline: 0,\n shadow: 0,\n fontSize: 17 / 50,\n fontWeight: 600,\n letterSpacing: 0,\n iconGap: 0.4,\n iconSize: 1.15,\n pressScale: 0,\n pressDim: 0.18,\n accent: \"#007aff\",\n },\n}\n\n/** The look a style names, falling back for a value this build doesn't know. */\nexport function buttonLook(style: ControlStyle): ButtonLook {\n return BUTTON_LOOKS[style] ?? BUTTON_LOOKS[DEFAULT_CONTROL_STYLE]\n}\n\n/** The proportions a new Button node is seeded at — a comfortable tap target. */\nexport const BUTTON_ASPECT = 160 / 50\n\n// --- Colour ----------------------------------------------------------------\n\n/** A `#rrggbb` (or `#rgb`) string as its three channels, or `null` if unreadable. */\nfunction channels(hex: string): [number, number, number] | null {\n const value = hex.trim().replace(/^#/, \"\")\n if (/^[0-9a-f]{3}$/i.test(value)) {\n return [\n parseInt(value[0] + value[0], 16),\n parseInt(value[1] + value[1], 16),\n parseInt(value[2] + value[2], 16),\n ]\n }\n if (!/^[0-9a-f]{6}$/i.test(value)) return null\n const n = parseInt(value, 16)\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255]\n}\n\nfunction hex2(value: number): string {\n return Math.round(Math.min(255, Math.max(0, value)))\n .toString(16)\n .padStart(2, \"0\")\n}\n\n/**\n * `amount` of the way from `from` toward `to`, as a `#rrggbb`.\n *\n * Straight linear RGB-in-sRGB, which is the wrong colour space and the right\n * choice: what this is used for is a press state and a disabled wash — small\n * moves toward black or white where the perceptual argument for OKLab buys a\n * difference nobody can see, at the cost of a conversion in a render loop. The\n * grade pipeline, which makes *large* moves, does it properly (see `model/lut`).\n *\n * An unreadable colour comes back unchanged rather than black: it is far more\n * likely to be a `rgba(...)` or a var this function was never meant to parse\n * than a typo, and a control that turns black on a press is a worse failure than\n * one that doesn't react.\n */\nexport function mixHex(from: string, to: string, amount: number): string {\n const a = channels(from)\n const b = channels(to)\n if (!a || !b) return from\n const t = Math.min(1, Math.max(0, amount))\n return `#${hex2(a[0] + (b[0] - a[0]) * t)}${hex2(a[1] + (b[1] - a[1]) * t)}${hex2(a[2] + (b[2] - a[2]) * t)}`\n}\n\n/**\n * Black or white, whichever is legible on `background`.\n *\n * Weighted by the sRGB luminance coefficients rather than a plain average,\n * because green carries most of the perceived brightness and a plain average\n * puts white text on a mid-green button. The threshold is 0.6 rather than 0.5:\n * the eye tolerates white-on-mid far better than black-on-mid, so the tie is\n * broken toward white.\n *\n * Derived rather than offered as a field on purpose — see the module note. A\n * label colour is not a decision, it is a consequence of the fill, and the one\n * style where it might not be exposes it explicitly.\n */\nexport function readableInk(background: string): string {\n const rgb = channels(background)\n if (!rgb) return \"#ffffff\"\n const luminance = (0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]) / 255\n return luminance > 0.6 ? \"#1c1c1e\" : \"#ffffff\"\n}\n", "/**\n * How far into a press the button is, `0`–`1`, given the command's own progress.\n *\n * The envelope rather than the phase: a press is *down and back up*, which no\n * single tween can state, so the node holds a monotonic phase a host can seek\n * and turns it into a depression here. Down over the first third and back over\n * the remaining two — the asymmetry every platform's own press animation has,\n * because a finger lands faster than it lifts.\n *\n * Both ends are exactly zero, which is what lets the phase keep climbing past\n * one press into the next: a command that finishes leaves the button at rest by\n * arithmetic rather than by a reset somebody has to remember to write.\n *\n * It lived beside the *command* that drives it, which put a piece of the\n * button's drawing in a file about authoring. The node needs it to render at\n * all, so it comes with the node.\n */\nexport function pressDepth(phase: number): number {\n const t = phase - Math.floor(phase)\n if (t <= 0) return 0\n const DOWN = 1 / 3\n if (t < DOWN) return smooth(t / DOWN)\n return smooth(1 - (t - DOWN) / (1 - DOWN))\n}\n\n/** Smoothstep — the S-curve, so a press has no corners at either end. */\nfunction smooth(t: number): number {\n const x = Math.min(1, Math.max(0, t))\n return x * x * (3 - 2 * x)\n}\n", "/**\n * The artwork behind the Cursor node: two pointers, drawn the way an operating\n * system draws them.\n *\n * ## Two contours, not one shape and a stroke\n *\n * A real cursor is a **white body inside a black keyline** — that is what makes\n * it legible over a dark window and a light one both, and it is not decoration\n * anybody chose. So each glyph here is two closed contours: the outer\n * silhouette (body *plus* keyline) and the body inside it. The node paints the\n * outer one in the keyline colour and the inner one over it in the body colour,\n * and the keyline is whatever gap that leaves.\n *\n * That is deliberately **not** a fill with a stroke round it, and the reason is\n * the resize handle. A stroke's weight is a pixel count, and this glyph is\n * stretched onto whatever box the node has — so a weight that looked right at\n * 32px would be a hairline at 400 and a blob at 16. Baking the keyline into the\n * geometry makes it a *fraction of the drawing*, which is what it is on screen,\n * and it scales with everything else for free.\n *\n * ## The author box\n *\n * Both glyphs are normalized into one **32 × 32 box, y-down, origin top-left**\n * on the way in ({@link prepare}), fitted uniformly and anchored at the\n * top-left corner. Three things follow, all of them the point:\n *\n * - The two share a box, so changing the type dropdown swaps the drawing\n * without moving or resizing the node.\n * - The fit is *uniform*, so neither is distorted to fill a box it doesn't have\n * the proportions of. A glyph narrower than the box leaves the slack on its\n * right, which is empty space inside the selection rectangle and nothing more.\n * - The anchor is the top-left, which is where a pointer's **hotspot** is. A\n * cursor placed at (x, y) points at (x, y), the way the real one does.\n *\n * Normalizing at load rather than by hand is what lets the sources below stay\n * *verbatim* — they are the artwork as drawn, in their own viewBoxes, and\n * nobody has to re-derive a coordinate to move one into this module.\n */\n\nimport { pathOps, type VectorPath, type Vector2 } from \"@motionscript/core\"\n\n/**\n * The pointers, in the order the dropdown offers them.\n *\n * Two, and named for the CSS keywords rather than for what they look like:\n * `default` is the arrow and `pointer` is the hand you get over a link. That\n * vocabulary is the one anybody reaching for this node already has, and it is\n * the one the list grows in — `text`, `grab`, `crosshair` and the rest are each\n * a pair of contours away.\n */\nexport const CURSOR_TYPES = [\"default\", \"pointer\"] as const\n\nexport type CursorType = (typeof CURSOR_TYPES)[number]\n\n/**\n * The looks a cursor can be drawn in.\n *\n * One today, and the axis exists anyway because a second is a different\n * *drawing* rather than a setting: the macOS arrow is black with a white\n * keyline and has a silhouette of its own, so it arrives as another pair of\n * contours in {@link SOURCES} plus a row in {@link CURSOR_LOOKS} — not as a\n * colour control on the node. Keeping the axis is what makes that an addition\n * instead of a migration.\n *\n * While there is only one, the studio renders no dropdown for it: a control\n * with a single answer is a click that asks nothing. See the schema entry in\n * `model/appearance/schema.ts`, which says where to put it back.\n */\nexport const CURSOR_STYLES = [\"windows\"] as const\n\nexport type CursorStyle = (typeof CURSOR_STYLES)[number]\n\n/** The glyph a new Cursor node opens on: the arrow everybody pictures. */\nexport const DEFAULT_CURSOR_TYPE: CursorType = \"default\"\n\n/** …drawn the way the machine most people are looking at draws it. */\nexport const DEFAULT_CURSOR_STYLE: CursorStyle = \"windows\"\n\n/** The side of the square box both glyphs are normalized into. */\nexport const CURSOR_UNITS = 32\n\n/**\n * The side of the box a cursor node is **born at**, in scene units.\n *\n * A real pointer's own size: both Windows and macOS ship their cursors as 32×32\n * logical pixels, so a node this size in a 1920×1080 composition is the size the\n * thing actually is on a 1080p screen. That matters more than it sounds — a\n * cursor drawn four times life size doesn't read as a big cursor, it reads as a\n * scene shot at a different resolution from the interface under it.\n *\n * It is deliberately the *same number* as {@link CURSOR_UNITS} rather than a\n * multiple of it, and the coincidence is the point: the author box was chosen at\n * 32 because that is what a cursor is. See `model/natural-size.ts`, which is\n * what puts a node back to this after it has been dragged.\n */\nexport const CURSOR_SIZE = 32\n\n/** What a style paints its two contours with. */\nexport interface CursorLook {\n /** The body inside the keyline. */\n body: string\n /** The outline round it, which is the outer contour showing through. */\n keyline: string\n}\n\n/**\n * The colours each look is drawn in.\n *\n * Not defaults an author can change: a cursor's colours are what make it that\n * cursor, and a white-on-black Windows arrow repainted pink is not a stylized\n * cursor but a mistake. Hence the node exposes no Fills or Strokes section at\n * all — see `CURSOR_NODE_KEY`.\n */\nexport const CURSOR_LOOKS: Record<CursorStyle, CursorLook> = {\n windows: { body: \"#ffffff\", keyline: \"#000000\" },\n}\n\n/**\n * The look a style names, falling back to the default for one this build\n * doesn't know — a row written by a newer client, which should draw a cursor\n * rather than nothing.\n */\nexport function cursorLook(style: CursorStyle): CursorLook {\n return CURSOR_LOOKS[style] ?? CURSOR_LOOKS[DEFAULT_CURSOR_STYLE]\n}\n\n/** A cursor's two contours: the outer silhouette, and the body inside it. */\nexport interface CursorArtwork {\n /** Body *and* keyline — the whole shape, painted in the keyline colour. */\n keyline: VectorPath\n /** The body, painted over it. What is left showing is the keyline. */\n body: VectorPath\n}\n\n/**\n * The artwork, verbatim, in whatever viewBox it was drawn in.\n *\n * Each entry is one SVG `<path>` split at its second `M`: these are drawn as a\n * single two-subpath path with an even-odd fill, which paints the keyline and\n * leaves the body transparent. Splitting them is what lets the body be painted\n * too, and it is the only edit made to either.\n *\n * Keyed by style first, because a second style is a second drawing rather than\n * a recolouring of this one.\n */\nconst SOURCES: Record<\n CursorStyle,\n Record<CursorType, { keyline: string; body: string }>\n> = {\n windows: {\n // The arrow, drawn in a 50 x 50 box. The run of near-identical curves round\n // (14.8, 5) is the trace of its tip and is left exactly as it came: it is a\n // few dozen bytes, and \"tidying\" a traced outline is how a shape stops\n // matching the thing it was traced from.\n default: {\n keyline:\n \"M 14.78125 5 C 14.75 5.007813 14.71875 5.019531 14.6875 5.03125 C 14.644531 5.050781 14.601563 5.070313 14.5625 5.09375 C 14.550781 5.09375 14.542969 5.09375 14.53125 5.09375 C 14.511719 5.101563 14.488281 5.113281 14.46875 5.125 C 14.457031 5.136719 14.449219 5.144531 14.4375 5.15625 C 14.425781 5.167969 14.417969 5.175781 14.40625 5.1875 C 14.375 5.207031 14.34375 5.226563 14.3125 5.25 C 14.289063 5.269531 14.269531 5.289063 14.25 5.3125 C 14.238281 5.332031 14.226563 5.355469 14.21875 5.375 C 14.183594 5.414063 14.152344 5.457031 14.125 5.5 C 14.113281 5.511719 14.105469 5.519531 14.09375 5.53125 C 14.09375 5.542969 14.09375 5.550781 14.09375 5.5625 C 14.082031 5.582031 14.070313 5.605469 14.0625 5.625 C 14.050781 5.636719 14.042969 5.644531 14.03125 5.65625 C 14.03125 5.675781 14.03125 5.699219 14.03125 5.71875 C 14.019531 5.757813 14.007813 5.800781 14 5.84375 C 14 5.875 14 5.90625 14 5.9375 C 14 5.949219 14 5.957031 14 5.96875 C 14 5.980469 14 5.988281 14 6 C 13.996094 6.050781 13.996094 6.105469 14 6.15625 L 14 39 C 14.003906 39.398438 14.242188 39.757813 14.609375 39.914063 C 14.972656 40.070313 15.398438 39.992188 15.6875 39.71875 L 22.9375 32.90625 L 28.78125 46.40625 C 28.890625 46.652344 29.09375 46.847656 29.347656 46.941406 C 29.601563 47.035156 29.882813 47.023438 30.125 46.90625 L 34.5 44.90625 C 34.996094 44.679688 35.21875 44.09375 35 43.59375 L 28.90625 30.28125 L 39.09375 29.40625 C 39.496094 29.378906 39.84375 29.113281 39.976563 28.730469 C 40.105469 28.347656 39.992188 27.921875 39.6875 27.65625 L 15.84375 5.4375 C 15.796875 5.378906 15.746094 5.328125 15.6875 5.28125 C 15.648438 5.234375 15.609375 5.195313 15.5625 5.15625 C 15.550781 5.15625 15.542969 5.15625 15.53125 5.15625 C 15.511719 5.132813 15.492188 5.113281 15.46875 5.09375 C 15.457031 5.09375 15.449219 5.09375 15.4375 5.09375 C 15.386719 5.070313 15.335938 5.046875 15.28125 5.03125 C 15.269531 5.03125 15.261719 5.03125 15.25 5.03125 C 15.230469 5.019531 15.207031 5.007813 15.1875 5 C 15.175781 5 15.167969 5 15.15625 5 C 15.136719 5 15.113281 5 15.09375 5 C 15.082031 5 15.074219 5 15.0625 5 C 15.042969 5 15.019531 5 15 5 C 14.988281 5 14.980469 5 14.96875 5 C 14.9375 5 14.90625 5 14.875 5 C 14.84375 5 14.8125 5 14.78125 5 Z\",\n body: \"M 16 8.28125 L 36.6875 27.59375 L 27.3125 28.40625 C 26.992188 28.4375 26.707031 28.621094 26.546875 28.902344 C 26.382813 29.179688 26.367188 29.519531 26.5 29.8125 L 32.78125 43.5 L 30.21875 44.65625 L 24.21875 30.8125 C 24.089844 30.515625 23.828125 30.296875 23.511719 30.230469 C 23.195313 30.160156 22.863281 30.25 22.625 30.46875 L 16 36.6875 Z\",\n },\n // The pointing hand, drawn in a 32 x 32 box. The notches along the top of\n // the body contour are the gaps between the folded fingers — they are part\n // of the *body*, which is why the keyline shows through them.\n pointer: {\n keyline:\n \"M 13 2 C 11.355469 2 10 3.355469 10 5 L 10 16.8125 L 9.34375 16.125 L 9.09375 15.90625 C 7.941406 14.753906 6.058594 14.753906 4.90625 15.90625 C 3.753906 17.058594 3.753906 18.941406 4.90625 20.09375 L 4.90625 20.125 L 13.09375 28.21875 L 13.15625 28.25 L 13.1875 28.3125 C 14.535156 29.324219 16.253906 30 18.1875 30 L 19.90625 30 C 24.441406 30 28.09375 26.347656 28.09375 21.8125 L 28.09375 14 C 28.09375 12.355469 26.738281 11 25.09375 11 C 24.667969 11 24.273438 11.117188 23.90625 11.28125 C 23.578125 9.980469 22.394531 9 21 9 C 20.234375 9 19.53125 9.300781 19 9.78125 C 18.46875 9.300781 17.765625 9 17 9 C 16.648438 9 16.316406 9.074219 16 9.1875 L 16 5 C 16 3.355469 14.644531 2 13 2 Z\",\n body: \"M 13 4 C 13.554688 4 14 4.445313 14 5 L 14 16 L 16 16 L 16 12 C 16 11.445313 16.445313 11 17 11 C 17.554688 11 18 11.445313 18 12 L 18 16 L 20 16 L 20 12 C 20 11.445313 20.445313 11 21 11 C 21.554688 11 22 11.445313 22 12 L 22 16 L 24.09375 16 L 24.09375 14 C 24.09375 13.445313 24.539063 13 25.09375 13 C 25.648438 13 26.09375 13.445313 26.09375 14 L 26.09375 21.8125 C 26.09375 25.277344 23.371094 28 19.90625 28 L 18.1875 28 C 16.722656 28 15.457031 27.476563 14.40625 26.6875 L 6.3125 18.6875 C 5.867188 18.242188 5.867188 17.757813 6.3125 17.3125 C 6.757813 16.867188 7.242188 16.867188 7.6875 17.3125 L 12 21.625 L 12 5 C 12 4.445313 12.445313 4 13 4 Z\",\n },\n },\n}\n\n/**\n * Moves one glyph out of the viewBox it was drawn in and into the author box:\n * scaled uniformly to fit, anchored at the top-left corner.\n *\n * The two contours are measured **together** and mapped by the same transform,\n * which is the whole of what makes them still fit each other afterwards.\n */\nfunction prepare(source: { keyline: string; body: string }): CursorArtwork {\n const keyline = pathOps.anchors(source.keyline)\n const body = pathOps.anchors(source.body)\n // The outer contour contains the inner one by construction, so its bounds are\n // the glyph's — measured across both anyway, because a glyph pasted in with\n // its two halves the wrong way round would otherwise be silently cropped.\n const bounds = pathOps.anchorBounds([...keyline, ...body])\n if (!bounds) return { keyline, body }\n const width = bounds.maxX - bounds.minX\n const height = bounds.maxY - bounds.minY\n const scale = CURSOR_UNITS / Math.max(width, height, 1e-9)\n const move = (p: Vector2) => ({\n x: (p.x - bounds.minX) * scale,\n y: (p.y - bounds.minY) * scale,\n })\n return {\n keyline: pathOps.map(keyline, move),\n body: pathOps.map(body, move),\n }\n}\n\n/**\n * Every glyph, parsed and moved into the author box once, at module load.\n *\n * Once rather than per draw, unlike the parametric shapes next door: these are\n * fixed drawings with nothing to recompute, so parsing a few hundred curve\n * commands on every frame would buy nothing at all.\n */\nconst ARTWORK: Record<\n CursorStyle,\n Record<CursorType, CursorArtwork>\n> = Object.fromEntries(\n Object.entries(SOURCES).map(([style, byType]) => [\n style,\n Object.fromEntries(\n Object.entries(byType).map(([type, source]) => [type, prepare(source)])\n ),\n ])\n) as Record<CursorStyle, Record<CursorType, CursorArtwork>>\n\n/** The glyph in the author box, or the arrow for a type this build doesn't know. */\nfunction artworkFor(type: CursorType, style: CursorStyle): CursorArtwork {\n const byType = ARTWORK[style] ?? ARTWORK[DEFAULT_CURSOR_STYLE]\n return byType[type] ?? byType[DEFAULT_CURSOR_TYPE]\n}\n\n/**\n * The glyph mapped onto a node's box: centred on the origin, **fitted** into\n * `width` × `height`, still y-down.\n *\n * The same contract `shapeOutlinePixels` has, so the node class that draws this\n * is the same shape of thing as the ones that draw a hexagon — see\n * `nodes/cursor`.\n *\n * A cursor is **fitted** rather than stretched, which is the opposite of the\n * bargain every other node in this app makes and is the one case that earns it.\n * A rectangle dragged tall is a tall rectangle; a pointer dragged tall is not a\n * tall pointer, it is a drawing that has gone wrong — the shape is the whole of\n * what makes an arrow read as *the* arrow.\n *\n * In practice the two mappings agree, because the node's box is aspect-locked\n * square (see `model/natural-size.ts`) and a uniform fit into a square box is a\n * stretch. What this buys is what happens when the box arrives at some other\n * shape anyway — a scene authored before the lock existed, a paste, an agent, a\n * newer client — where the honest answer is a correctly-shaped cursor with slack\n * around it rather than a squashed one. The same failure posture the builder\n * takes everywhere else: draw something right rather than something wrong.\n */\nexport function cursorArtworkPixels(\n type: CursorType,\n style: CursorStyle,\n width: number,\n height: number\n): CursorArtwork {\n if (!(width > 0) || !(height > 0)) return { keyline: [], body: [] }\n // One scale for both axes — that is the fit — taken from the tighter of them,\n // and the leftover slack split either side so the glyph sits in the middle of\n // whatever box it was given.\n const k = Math.min(width, height) / CURSOR_UNITS\n const span = CURSOR_UNITS * k\n const onto = (p: Vector2) => ({\n x: p.x * k - span / 2,\n y: p.y * k - span / 2,\n })\n const artwork = artworkFor(type, style)\n return {\n keyline: pathOps.map(artwork.keyline, onto),\n body: pathOps.map(artwork.body, onto),\n }\n}\n\n/**\n * The glyph's two contours as SVG `d` strings in the 32-unit box.\n *\n * What the studio's own chrome draws with: the Components panel's card shows\n * the *actual* cursor rather than a hand-drawn approximation of it, so artwork\n * corrected here is corrected everywhere at once.\n */\nexport function cursorArtworkData(\n type: CursorType,\n style: CursorStyle\n): { keyline: string; body: string } {\n const artwork = artworkFor(type, style)\n return {\n keyline: pathOps.fromAnchors(artwork.keyline),\n body: pathOps.fromAnchors(artwork.body),\n }\n}\n\n/** Whether a stored value names a cursor this build can draw. */\nexport function isCursorType(value: unknown): value is CursorType {\n return (\n typeof value === \"string\" &&\n (CURSOR_TYPES as readonly string[]).includes(value)\n )\n}\n\n/** Whether a stored value names a look this build can draw. */\nexport function isCursorStyle(value: unknown): value is CursorStyle {\n return (\n typeof value === \"string\" &&\n (CURSOR_STYLES as readonly string[]).includes(value)\n )\n}\n", "/**\n * The curve a pointer travels on its way somewhere: a quadratic bow over the\n * straight line between two points, stated as two numbers that survive either\n * end moving.\n *\n * **Why the shape and not the path.** A curve baked to absolute coordinates is\n * only correct for the two points it was baked against, so a control moved on\n * the canvas leaves the animation aimed at where it used to be — which is the\n * whole cost {@link Cursor.click} exists to remove. `bow` and `lean` are\n * relative to the chord, so the same pair re-derives a correct curve from\n * wherever the two ends are when the command is built.\n *\n * It lives in the kit rather than in the editor because both ends need it and\n * they must agree: the node derives the path it actually travels, and a host\n * draws a handle on the crest. Two copies of this is a gizmo that points at a\n * curve the render does not follow.\n */\n\nexport interface ClickArc {\n /** Signed fraction of the chord's length the curve's crest stands off it. */\n bow: number\n /** Where along the chord the bow is pulled from, `0`–`1`. */\n lean: number\n}\n\nexport const CLICK_ARC_DEFAULT: ClickArc = { bow: 0.15, lean: 0.5 }\nexport const CLICK_ARC_MIN_LEAN = 0\nexport const CLICK_ARC_MAX_LEAN = 1\n\nexport interface ClickPoint {\n x: number\n y: number\n}\n\n/** A stored `clickShape`/`moveShape` narrowed to a usable arc, or `null`. */\nexport function resolveClickArc(value: unknown): ClickArc | null {\n if (!value || typeof value !== \"object\") return null\n const bag = value as Partial<ClickArc>\n const bow = typeof bag.bow === \"number\" ? bag.bow : null\n const lean = typeof bag.lean === \"number\" ? bag.lean : null\n if (bow === null && lean === null) return null\n return {\n bow: bow ?? CLICK_ARC_DEFAULT.bow,\n lean: clampLean(lean ?? CLICK_ARC_DEFAULT.lean),\n }\n}\n\n/**\n * The quadratic control point for an arc from `start` to `end`.\n *\n * `bow` states the curve's own crest offset, doubled here because a quadratic\n * only travels half way to its control.\n */\nexport function arcControlPoint(\n start: ClickPoint,\n end: ClickPoint,\n arc: ClickArc\n): ClickPoint {\n const dx = end.x - start.x\n const dy = end.y - start.y\n const length = Math.hypot(dx, dy)\n if (length === 0) return { x: start.x, y: start.y }\n const along = { x: start.x + dx * arc.lean, y: start.y + dy * arc.lean }\n const offset = length * arc.bow * 2\n return {\n x: along.x + (-dy / length) * offset,\n y: along.y + (dx / length) * offset,\n }\n}\n\n/**\n * The crest of the arc — the curve's point furthest from the chord, and the one\n * a canvas handle sits on. Always the halfway parameter whatever the lean, so\n * it stands exactly {@link ClickArc.bow} of the chord's length off it and\n * `0.25 + lean / 2` along it: the pair of facts that lets one handle state both\n * numbers. {@link arcFromCrest} is the way back.\n */\nexport function arcCrestPoint(\n start: ClickPoint,\n end: ClickPoint,\n arc: ClickArc\n): ClickPoint {\n const control = arcControlPoint(start, end, arc)\n // Quadratic Bézier at t = ½: ¼·P0 + ½·C + ¼·P1.\n return {\n x: 0.25 * start.x + 0.5 * control.x + 0.25 * end.x,\n y: 0.25 * start.y + 0.5 * control.y + 0.25 * end.y,\n }\n}\n\n/**\n * The arc whose crest is at `point` — the exact inverse of\n * {@link arcCrestPoint}, which is what makes a handle follow the pointer rather\n * than merely respond to it. The lean is clamped, so dragging past the middle\n * half of the chord slides the crest no further and only the bow keeps\n * answering. `null` for a move with no chord to measure against.\n */\nexport function arcFromCrest(\n start: ClickPoint,\n end: ClickPoint,\n point: ClickPoint\n): ClickArc | null {\n const dx = end.x - start.x\n const dy = end.y - start.y\n const length = Math.hypot(dx, dy)\n if (length === 0) return null\n const rx = point.x - start.x\n const ry = point.y - start.y\n // In the chord's own frame: along it, then a quarter turn on — the same\n // perpendicular `arcControlPoint` pushes the control out along.\n const along = (rx * dx + ry * dy) / (length * length)\n const off = (rx * -dy + ry * dx) / (length * length)\n return { bow: off, lean: clampLean(along * 2 - 0.5) }\n}\n\n/**\n * The point at `t` along the arc, by the curve's **parameter**.\n *\n * Not by arc length, deliberately: a pointer's travel is eased over the whole\n * gesture and a bow this shallow makes the two indistinguishable, while\n * sampling by length would mean measuring the curve on every build for a\n * difference of a pixel or two. `moveTo` walks by length because a path there\n * may be an arbitrary authored shape; this one is always a gentle quadratic\n * between two points.\n */\nexport function arcPointAt(\n start: ClickPoint,\n end: ClickPoint,\n arc: ClickArc | null,\n t: number\n): ClickPoint {\n if (!arc || arc.bow === 0) {\n return { x: start.x + (end.x - start.x) * t, y: start.y + (end.y - start.y) * t }\n }\n const c = arcControlPoint(start, end, arc)\n const u = 1 - t\n return {\n x: u * u * start.x + 2 * u * t * c.x + t * t * end.x,\n y: u * u * start.y + 2 * u * t * c.y + t * t * end.y,\n }\n}\n\nfunction clampLean(lean: number): number {\n return Math.min(CLICK_ARC_MAX_LEAN, Math.max(CLICK_ARC_MIN_LEAN, lean))\n}\n"],
5
- "mappings": "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",
6
- "names": ["pathOps", "CONTROL_ICONS", "DEFAULT_CONTROL_ICON", "CONTROL_ICON_UNITS", "EMPTY_ARTWORK", "point", "x", "y", "polyline", "points", "closed", "p", "arc", "cx", "cy", "radius", "from", "to", "start", "sweep", "steps", "step", "handle", "at", "angle", "tangent", "anchors", "i", "t", "circle", "ring", "last", "joinArcs", "first", "second", "head", "seam", "rest", "star", "outer", "inner", "vertices", "crescent", "tip", "degrees", "midX", "midY", "span", "normalX", "normalY", "biteX", "biteY", "biteRadius", "angleOf", "WEIGHT", "ARTWORK", "_", "cos", "sin", "artworkFor", "icon", "controlIconPixels", "size", "artwork", "scale", "onto", "controlIconData", "isControlIcon", "value", "Node2D", "property", "ControlNode", "audio", "cue", "length", "__decorate", "CONTROL_STYLES", "DEFAULT_CONTROL_STYLE", "isControlStyle", "value", "SWITCH_LOOKS", "switchLook", "style", "SWITCH_ASPECT", "BUTTON_LOOKS", "buttonLook", "BUTTON_ASPECT", "channels", "hex", "n", "hex2", "mixHex", "from", "to", "amount", "a", "b", "t", "readableInk", "background", "rgb", "pressDepth", "phase", "DOWN", "smooth", "t", "x", "pathOps", "CURSOR_TYPES", "CURSOR_STYLES", "DEFAULT_CURSOR_TYPE", "DEFAULT_CURSOR_STYLE", "CURSOR_UNITS", "CURSOR_SIZE", "CURSOR_LOOKS", "cursorLook", "style", "SOURCES", "prepare", "source", "keyline", "body", "bounds", "width", "height", "scale", "move", "p", "ARTWORK", "byType", "type", "artworkFor", "cursorArtworkPixels", "k", "span", "onto", "artwork", "cursorArtworkData", "isCursorType", "value", "isCursorStyle", "CLICK_ARC_DEFAULT", "CLICK_ARC_MIN_LEAN", "CLICK_ARC_MAX_LEAN", "resolveClickArc", "value", "bag", "bow", "lean", "clampLean", "arcControlPoint", "start", "end", "arc", "dx", "dy", "length", "along", "offset", "arcCrestPoint", "control", "arcFromCrest", "point", "rx", "ry", "arcPointAt", "t", "u"]
7
- }