@overglaze/web 0.0.0-stage → 0.9.1

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 (51) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +63 -2
  3. package/desktops/desktop.js +40 -0
  4. package/desktops/fonts/DMMono-Medium.woff2 +0 -0
  5. package/desktops/fonts/DMMono-Regular.woff2 +0 -0
  6. package/desktops/fonts/NotoSerif-VF.woff2 +0 -0
  7. package/desktops/fonts/OFL-DMMono.txt +93 -0
  8. package/desktops/fonts/OFL-NotoSerif.txt +93 -0
  9. package/desktops/fonts/OFL-SpaceGrotesk.txt +93 -0
  10. package/desktops/fonts/SpaceGrotesk-Bold.woff2 +0 -0
  11. package/desktops/fonts/SpaceGrotesk-Medium.woff2 +0 -0
  12. package/desktops/fonts/SpaceGrotesk-Regular.woff2 +0 -0
  13. package/desktops/wallpapers.js +227 -0
  14. package/embed.css +53 -0
  15. package/embed.js +6 -0
  16. package/engine/core.js +6 -0
  17. package/engine/cursor.js +6 -0
  18. package/engine/descriptor.js +6 -0
  19. package/engine/files.js +6 -0
  20. package/engine/globals.js +6 -0
  21. package/engine/golden-globals.js +6 -0
  22. package/engine/imgui-ops.js +6 -0
  23. package/engine/keys.js +6 -0
  24. package/engine/modules.js +6 -0
  25. package/engine/naga.wasm +0 -0
  26. package/engine/naga.wasm.notices.txt +2344 -0
  27. package/engine/ops.js +6 -0
  28. package/engine/param-store.js +6 -0
  29. package/engine/plugin-js.wasm +0 -0
  30. package/engine/plugin-js.wasm.notices.txt +4411 -0
  31. package/engine/plugin-storage.js +6 -0
  32. package/engine/session.js +9 -0
  33. package/engine/shaders.js +7 -0
  34. package/engine/tex.js +6 -0
  35. package/engine/uniforms.js +6 -0
  36. package/engine/worker.js +74 -0
  37. package/host/api.js +30 -0
  38. package/host/assets.js +6 -0
  39. package/host/bootstrap.js +7 -0
  40. package/host/imgui.js +6 -0
  41. package/host/params.js +6 -0
  42. package/host/seeded_random.js +6 -0
  43. package/host/storage.js +6 -0
  44. package/host/webio.js +6 -0
  45. package/host/webshim.js +6 -0
  46. package/package.json +19 -4
  47. package/player/fill.js +6 -0
  48. package/player/panel.css +92 -0
  49. package/player/panel.js +6 -0
  50. package/player/player.css +210 -0
  51. package/player/player.js +6 -0
@@ -0,0 +1,92 @@
1
+ /* The parameter panel's own look: a row per parameter, its label beside its
2
+ control, and each group under a heading rule. It takes the player's button
3
+ style for "Reset all" (player.css). A surface restyles it through the custom
4
+ properties below. */
5
+
6
+ .og-params {
7
+ --og-params-line: rgba(127, 127, 127, 0.35);
8
+ --og-params-quiet: 0.75;
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 8px;
12
+ min-width: 0;
13
+ }
14
+
15
+ .og-params-note {
16
+ margin: 0;
17
+ opacity: var(--og-params-quiet);
18
+ }
19
+
20
+ .og-param {
21
+ display: grid;
22
+ grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
23
+ gap: 2px 10px;
24
+ align-items: center;
25
+ }
26
+
27
+ .og-param-label {
28
+ overflow-wrap: anywhere;
29
+ }
30
+
31
+ .og-param-control {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 4px;
35
+ min-width: 0;
36
+ }
37
+
38
+ .og-param-pair {
39
+ display: flex;
40
+ gap: 6px;
41
+ align-items: center;
42
+ min-width: 0;
43
+ }
44
+
45
+ .og-param-pair input[type="range"] {
46
+ flex: 1 1 auto;
47
+ min-width: 0;
48
+ }
49
+
50
+ .og-params input,
51
+ .og-params select {
52
+ font: inherit;
53
+ }
54
+
55
+ .og-param-number {
56
+ width: 6.5em;
57
+ }
58
+
59
+ .og-param-control > .og-param-number,
60
+ .og-param-control > select {
61
+ width: 100%;
62
+ }
63
+
64
+ /* A hint stays on screen under its control, a line per value. */
65
+ .og-param-hint {
66
+ grid-column: 2;
67
+ margin: 0;
68
+ padding-left: 1.2em;
69
+ font-size: 0.9em;
70
+ opacity: var(--og-params-quiet);
71
+ }
72
+
73
+ .og-params-group {
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: 8px;
77
+ min-width: 0;
78
+ margin: 4px 0 0;
79
+ padding: 6px 0 0;
80
+ border: 0;
81
+ border-top: 1px solid var(--og-params-line);
82
+ }
83
+
84
+ .og-params-group > legend {
85
+ padding: 0 8px 0 0;
86
+ font-weight: 600;
87
+ }
88
+
89
+ .og-params-foot {
90
+ padding-top: 8px;
91
+ border-top: 1px solid var(--og-params-line);
92
+ }
@@ -0,0 +1,6 @@
1
+ /*! Overglaze web engine. Licensed under the PolyForm Noncommercial License 1.0.0
2
+ * <https://polyformproject.org/licenses/noncommercial/1.0.0>
3
+ * Required Notice: Copyright (c) 2026 Svetlozar Kostadinov
4
+ * With an added permission to run it to develop and preview Overglaze plugins: see LICENSE in @overglaze/web
5
+ */
6
+ var E=Object.defineProperty;var h=(d,e)=>E(d,"name",{value:e,configurable:!0});const y=["x","y","z","w"],k="Put every parameter back to the value this plugin declares, and forget what was saved.",N="This effect has no parameters to adjust.";let S=0;function o(d,e,a={}){const i=document.createElement(d);e&&(i.className=e);for(const[s,f]of Object.entries(a))i.setAttribute(s,f);return i}h(o,"el");function x(d,e){return Number.isFinite(e)?d.int||d.bool?String(Math.round(e)):String(Number(e.toPrecision(6))):""}h(x,"shown");const C=h(d=>Math.round(Math.min(1,Math.max(0,Number.isFinite(d)?d:0))*255),"channel");function L(d){return"#"+[0,1,2].map(e=>C(d[e]).toString(16).padStart(2,"0")).join("")}h(L,"hex");function M(d){const e=parseInt(d.slice(1),16);return[(e>>16&255)/255,(e>>8&255)/255,(e&255)/255]}h(M,"unhex");class P{static{h(this,"ParamPanel")}constructor(e,{write:a,reset:i}){this.root=e,this.write=a,this.reset=i,this.prefix="og-param-"+ ++S+"-",this.rows=new Map,e.classList.add("og-params"),this.clear("")}clear(e){this.rows.clear();const a=o("p","og-params-note");a.textContent=e,this.root.replaceChildren(a)}model({resettable:e,rows:a}){const i=this.#i();this.rows.clear();const s=[];if(!a.length){const t=o("p","og-params-note");t.textContent=N,s.push(t)}let f,c=null;if(a.forEach((t,r)=>{if(t.group===void 0)c=null;else if(c===null||t.group!==f){c=o("fieldset","og-params-group");const n=o("legend");n.textContent=t.group,c.append(n),s.push(c)}f=t.group;const v=this.#s(t,r);c?c.append(v):s.push(v)}),e&&a.length){const t=o("div","og-params-foot"),r=o("button","og-button",{type:"button",title:k});r.textContent="Reset all",r.addEventListener("click",()=>this.reset()),t.append(r),s.push(t)}if(this.root.replaceChildren(...s),i){const t=this.rows.get(i.key),r=t&&t.inputs[i.n];r&&r.el.focus({preventScroll:!0})}}values(e){for(const[a,i]of e){const s=this.rows.get(a);if(!s||i.length!==s.value.length)continue;const f=this.#n(s);s.value=i.map((c,t)=>f.has(t)?s.value[t]:c),this.#t(s)}}#n(e){const a=new Set;for(const i of e.inputs)if(i.el===document.activeElement)for(const s of i.comps)a.add(s);return a}#i(){for(const[e,a]of this.rows){const i=a.inputs.findIndex(s=>s.el===document.activeElement);if(i>=0)return{key:e,n:i}}return null}#t(e){for(const a of e.inputs)a.el!==document.activeElement&&a.refresh()}#e(e,a,i){Number.isFinite(i)&&(e.value[a]=i,this.write(e.row.key,e.value.slice()))}#s(e,a){const i=this.prefix+a,s=o("div","og-param");s.dataset.key=e.key,e.tooltip!==void 0&&(s.title=e.tooltip);const f=o("label","og-param-label",{for:i+"-0"});f.textContent=e.label;const c=o("div","og-param-control"),t={row:e,value:e.value.slice(),inputs:[]};this.rows.set(e.key,t);const r=h((n,l,p)=>t.inputs.push({el:n,comps:l,refresh:p}),"add"),v=h((n,l)=>e.comps>1||l?{"aria-label":e.label+(e.comps>1?" "+y[n]:"")+(l?" "+l:"")}:{},"name");if(e.widget==="checkbox"){const n=o("input",null,{type:"checkbox",id:i+"-0"});n.addEventListener("change",()=>this.#e(t,0,n.checked?1:0)),r(n,[0],()=>{n.checked=t.value[0]!==0&&!Number.isNaN(t.value[0])}),c.append(n)}else if(e.widget==="combo"){const n=o("select",null,{id:i+"-0"});e.choices.forEach((l,p)=>{const u=o("option",null,{value:String(p)});u.textContent=l,n.append(u)}),n.addEventListener("change",()=>this.#e(t,0,Number(n.value))),r(n,[0],()=>{const l=Math.round(t.value[0]);n.selectedIndex=l>=0&&l<e.choices.length?l:-1}),c.append(n)}else if(e.widget==="color"){const n=o("input",null,{type:"color",id:i+"-0"});if(n.addEventListener("input",()=>{const l=M(n.value);for(let p=0;p<3;p++)t.value[p]=l[p];this.write(e.key,t.value.slice())}),r(n,[0,1,2],()=>{n.value=L(t.value)}),c.append(n),e.comps===4){const l=o("input",null,{type:"range",min:"0",max:"1",step:"any","aria-label":e.label+" alpha"});l.addEventListener("input",()=>this.#e(t,3,l.valueAsNumber)),r(l,[3],()=>{l.value=String(t.value[3])}),c.append(l)}}else{const n=e.widget==="slider",l=n?e.int?"1":"any":String(e.step),p={};e.min!==void 0?p.min=String(e.min):e.unsigned&&(p.min="0"),e.max!==void 0&&(p.max=String(e.max));for(let u=0;u<e.comps;u++){const m=n?o("input",null,{type:"range",id:i+"-"+u,step:l,...p,...v(u)}):null,g=o("input","og-param-number",{type:"number",step:l,...p,...n?v(u,"value"):{id:i+"-"+u,...v(u)}});if(g.addEventListener("input",()=>{this.#e(t,u,g.valueAsNumber),m&&(m.value=String(t.value[u]))}),r(g,[u],()=>{g.value=x(e,t.value[u])}),!m){c.append(g);continue}m.addEventListener("input",()=>{this.#e(t,u,m.valueAsNumber),g.value=x(e,t.value[u])}),r(m,[u],()=>{m.value=String(t.value[u])});const b=o("div","og-param-pair");b.append(m,g),c.append(b)}}if(e.tooltip!==void 0)for(const n of t.inputs)n.el.title=e.tooltip;if(s.append(f,c),e.hint!==void 0){const n=o("ul","og-param-hint");for(const l of e.hint){const p=o("li");p.textContent=l,n.append(p)}s.append(n)}return s.addEventListener("focusout",n=>{s.contains(n.relatedTarget)||this.#t(t)}),this.#t(t),s}}export{P as ParamPanel};
@@ -0,0 +1,210 @@
1
+ /* The player's own look: the box, its bar, the before/after divider. A surface
2
+ restyles it through the custom properties below. */
3
+
4
+ .og-player {
5
+ --og-bar-bg: transparent;
6
+ --og-bar-fg: inherit;
7
+ --og-button-bg: rgba(127, 127, 127, 0.12);
8
+ --og-button-line: rgba(127, 127, 127, 0.35);
9
+ --og-focus: #3d8bfd;
10
+ --og-divider: #ffffff;
11
+ --og-tag-bg: rgba(0, 0, 0, 0.55);
12
+ --og-tag-fg: #ffffff;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: 8px;
16
+ max-width: 100%;
17
+ }
18
+
19
+ .og-stage {
20
+ position: relative;
21
+ width: 100%;
22
+ background: #000;
23
+ overflow: hidden;
24
+ container-type: size;
25
+ }
26
+
27
+ /* The scene at its own shape (--og-aspect, width over height), as large as the
28
+ box allows: the whole box at the box's shape, letterboxed at another. */
29
+ .og-frame {
30
+ position: absolute;
31
+ inset: 0;
32
+ margin: auto;
33
+ width: min(100cqw, 100cqh * var(--og-aspect, 1.7778));
34
+ height: min(100cqh, 100cqw / var(--og-aspect, 1.7778));
35
+ }
36
+
37
+ .og-screen,
38
+ .og-screen canvas {
39
+ position: absolute;
40
+ inset: 0;
41
+ width: 100%;
42
+ height: 100%;
43
+ }
44
+
45
+ .og-screen canvas {
46
+ display: block;
47
+ outline: none;
48
+ }
49
+
50
+ .og-screen canvas:focus-visible {
51
+ outline: 2px solid var(--og-focus);
52
+ outline-offset: -2px;
53
+ }
54
+
55
+ /* The poster over the box: the stock desktop, and the still over it, cut at the
56
+ divider. */
57
+ .og-poster,
58
+ .og-poster img {
59
+ position: absolute;
60
+ inset: 0;
61
+ width: 100%;
62
+ height: 100%;
63
+ }
64
+
65
+ .og-poster[hidden] {
66
+ display: none;
67
+ }
68
+
69
+ .og-poster img {
70
+ display: block;
71
+ user-select: none;
72
+ }
73
+
74
+ .og-poster-before:not([src]) {
75
+ visibility: hidden;
76
+ }
77
+
78
+ .og-poster-after {
79
+ clip-path: inset(0 0 0 var(--og-split, 0%));
80
+ }
81
+
82
+ .og-bar {
83
+ display: flex;
84
+ flex-wrap: wrap;
85
+ gap: 8px;
86
+ align-items: center;
87
+ background: var(--og-bar-bg);
88
+ color: var(--og-bar-fg);
89
+ }
90
+
91
+ .og-button {
92
+ font: inherit;
93
+ padding: 4px 10px;
94
+ border: 1px solid var(--og-button-line);
95
+ border-radius: 6px;
96
+ background: var(--og-button-bg);
97
+ color: inherit;
98
+ cursor: pointer;
99
+ }
100
+
101
+ .og-button:disabled {
102
+ opacity: 0.5;
103
+ cursor: default;
104
+ }
105
+
106
+ .og-button[aria-pressed="true"] {
107
+ border-color: var(--og-focus);
108
+ }
109
+
110
+ .og-button:focus-visible,
111
+ .og-grip:focus-visible {
112
+ outline: 2px solid var(--og-focus);
113
+ outline-offset: 2px;
114
+ }
115
+
116
+ /* The divider: a hairline the height of the box with a grip half way down. Its
117
+ hit area is wider than the line, so it can be grabbed. */
118
+ .og-divider {
119
+ position: absolute;
120
+ top: 0;
121
+ bottom: 0;
122
+ width: 24px;
123
+ margin-left: -12px;
124
+ cursor: ew-resize;
125
+ touch-action: none;
126
+ z-index: 1;
127
+ }
128
+
129
+ .og-divider[hidden] {
130
+ display: none;
131
+ }
132
+
133
+ .og-divider-line {
134
+ position: absolute;
135
+ top: 0;
136
+ bottom: 0;
137
+ left: 11px;
138
+ width: 2px;
139
+ background: var(--og-divider);
140
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
141
+ }
142
+
143
+ .og-grip {
144
+ position: absolute;
145
+ top: 50%;
146
+ left: 0;
147
+ width: 24px;
148
+ height: 24px;
149
+ margin-top: -12px;
150
+ border-radius: 50%;
151
+ background: var(--og-divider);
152
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.6);
153
+ }
154
+
155
+ /* The two sides' names, at the box's top corners whatever the divider's place. */
156
+ .og-tags {
157
+ position: absolute;
158
+ top: 8px;
159
+ left: 12px;
160
+ pointer-events: none;
161
+ }
162
+
163
+ .og-tag {
164
+ position: absolute;
165
+ top: 0;
166
+ padding: 2px 6px;
167
+ border-radius: 4px;
168
+ background: var(--og-tag-bg);
169
+ color: var(--og-tag-fg);
170
+ font-size: 12px;
171
+ white-space: nowrap;
172
+ }
173
+
174
+ .og-tag:first-child {
175
+ right: 8px;
176
+ }
177
+
178
+ .og-tag:last-child {
179
+ left: 8px;
180
+ }
181
+
182
+ /* Fullscreen: the box is the screen, and the bar lies over its foot. */
183
+ .og-player:fullscreen {
184
+ display: block;
185
+ background: #000;
186
+ }
187
+
188
+ .og-player:fullscreen .og-stage {
189
+ position: fixed;
190
+ inset: 0;
191
+ width: auto;
192
+ }
193
+
194
+ .og-player:fullscreen .og-bar {
195
+ position: fixed;
196
+ left: 0;
197
+ right: 0;
198
+ bottom: 0;
199
+ padding: 10px 16px;
200
+ justify-content: center;
201
+ --og-bar-bg: rgba(0, 0, 0, 0.55);
202
+ --og-bar-fg: #fff;
203
+ z-index: 2;
204
+ transition: opacity 0.2s;
205
+ }
206
+
207
+ .og-player:fullscreen.og-idle .og-bar {
208
+ opacity: 0;
209
+ pointer-events: none;
210
+ }
@@ -0,0 +1,6 @@
1
+ /*! Overglaze web engine. Licensed under the PolyForm Noncommercial License 1.0.0
2
+ * <https://polyformproject.org/licenses/noncommercial/1.0.0>
3
+ * Required Notice: Copyright (c) 2026 Svetlozar Kostadinov
4
+ * With an added permission to run it to develop and preview Overglaze plugins: see LICENSE in @overglaze/web
5
+ */
6
+ var I=Object.defineProperty;var o=(c,t)=>I(c,"name",{value:t,configurable:!0});import{fill as W}from"./fill.js";const b=[1920,1080],N=["pause","compare","theme","fullscreen"],U=["pause","compare","theme","preset","screen","rotate","wallpaper","fullscreen"],k=["preset","screen","rotate","wallpaper"],d=[[1920,1080],[2560,1440],[3840,2160],[3440,1440]],_="\xD7",j=40,F=3,H=2,V=2500,f=.02,P=.1,y=2;function S(c,t,s){const e=s[0]>1920||s[1]>1080;return c+t+"-"+(e?"3840x2160":"1920x1080")+".png"}o(S,"stockDesktop");const m=o(c=>[Math.max(c[0],c[1]),Math.min(c[0],c[1])],"landscape"),L=o((c,t)=>c[0]===t[0]&&c[1]===t[1],"sameSize");function l(c,t,s={}){const e=document.createElement(c);t&&(e.className=t);for(const[i,h]of Object.entries(s))e.setAttribute(i,h);return e}o(l,"el");class J{static{o(this,"Player")}constructor(t,{plugin:s,modules:e,folder:i,files:h=null,descriptor:r=null,keep:n=null,desktops:a="desktops/",pictures:p=!0,desktop:u="stock",pace:E=y,picture:x=null,poster:g=null,theme:A=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",scene:w=b,controls:C=null,remember:v=null,params:D={},persistParams:M=!1,watchdog:O=!0,clock:z="wall",refreshHz:R=60,minFps:B=j,on:T={}}){this.root=t,this.source={plugin:s,modules:e,folder:i,files:h,descriptor:r,keep:n},this.desktops=a,this.pictures=p!==!1,this.desktopMode=u==="live"?"live":"stock",this.pace=Math.max(1,Math.round(Number(E))||y),this.live=null,this.liveLoad=null,this.liveDirty=!1,this.livePump=null,this.inflight=[],this.customPicture=x,this.theme=A,this.surfaceScene=w,this.windowed=w,this.scene=w,this.remember=typeof v=="string"&&v?v:null,this.pick=this.#k(),this.presets=[],this.wallpapers=[],this.sceneQueue=Promise.resolve(),this.opening=Promise.resolve(),this.params=D,this.persistParams=M,this.watchdog=O,this.clock=z,this.refreshHz=R,this.minFps=B,this.on=T,this.session=null,this.full=!1,this.userPaused=!1,this.inView=!0,this.sessionPaused=!1,this.split=null,this.health={warm:0,slow:0,told:new Set},this.idleTimer=0,this.posterUrls=g,this.stilled=!1,this.#S(C||(this.desktopMode==="live"?U:N)),this.#x(),this.#o(!!g)}load({plugin:t,modules:s,folder:e,files:i=null,descriptor:h=null,keep:r=null,params:n={},poster:a=null}){this.session&&this.session.stop(),this.session=null,this.source={plugin:t,modules:s,folder:e,files:i,descriptor:h,keep:r},this.params=n,this.posterUrls=a,this.stilled=!1,this.#o(!!a),this.#i()}async start(){this.session&&this.session.stop(),this.stilled=!1,this.userPaused=!1,this.sessionPaused=!1,this.health={warm:0,slow:0,told:new Set},this.#i(),await this.#w(),await this.opening;const t=await this.#s(this.scene),s=matchMedia("(prefers-reduced-motion: reduce)").matches,e=this.on,{Session:i}=await import("../engine/session.js");this.session&&this.session.stop(),this.session=new i({box:this.screen,...this.source,picture:t,scene:this.scene,params:this.params,persistParams:this.persistParams,watchdog:this.watchdog,clock:this.clock,refreshHz:this.refreshHz,pointer:[this.scene[0]/2,this.scene[1]/2],hold:s&&this.clock!=="stepped",on:{...e,ready:o(h=>{h.fallback&&this.#c("fallback",h.adapter);const r=this.session.gpuFeatures.filter(n=>!h.features.includes(n));r.length&&this.#c("features",r.join(", ")),e.ready&&e.ready(h)},"ready"),stats:o(h=>{this.#P(h),e.stats&&e.stats(h)},"stats"),held:o(()=>{this.userPaused=!0,this.sessionPaused=!0,this.#i(),e.held&&e.held()},"held"),stopped:o((h,r)=>{h==="unsupported"&&this.#c("unsupported",r),e.stopped&&e.stopped(h,r)},"stopped")}}),this.split!==null&&this.session.split(this.split),this.#l(),await this.session.start()}stop(){this.session&&this.session.stop()}runOn(t){this.session&&this.session.runOn(t)}reveal(){this.#o(!1)}still(){this.session&&this.session.stop(),this.stilled=!0,this.#o(!!this.posterUrls),this.posterUrls&&this.#a(this.surfaceScene),this.#i()}pause(){this.userPaused=!0,this.#l()}resume(){this.userPaused=!1,this.#l()}get paused(){return this.userPaused}async usePicture(t){this.customPicture=t,this.#t(),this.session&&await this.session.setPicture(await this.#s(this.scene))}async setScreen({size:t=this.pick.size,portrait:s=this.pick.portrait}={}){if(!this.live)return;const e=m(t.map(i=>Math.max(1,Math.round(Number(i))||1)));this.pick={...this.pick,size:e,portrait:!!s},this.#n(),this.windowed=this.#p(),this.#t(),this.full||await this.#u(this.windowed)}async setPreset(t){const s=this.presets.find(e=>e.id===t);!this.live||!s||(this.pick={...this.pick,preset:t,wallpaper:s.wallpaper},this.#n(),this.#t(),await this.#r(()=>this.live.setPreset(t)),this.session&&await this.session.setPicture(await this.#s(this.scene)))}resetDesktop(){const t=this.presets[0];this.pick={size:m(this.surfaceScene),portrait:this.surfaceScene[1]>this.surfaceScene[0],preset:t?t.id:null,wallpaper:t?t.wallpaper:null},this.#n(),this.windowed=this.#p(),this.#t();const s=(async()=>{this.full||await this.#u(this.windowed),this.live&&await this.#r(()=>this.live.open({scene:this.scene})),this.session&&await this.session.setPicture(await this.#s(this.scene))})();return this.opening=s.catch(()=>{}),this.opening}async setWallpaper(t){!this.live||!this.wallpapers.some(s=>s.id===t)||(this.pick={...this.pick,wallpaper:t},this.#n(),this.#t(),await this.#r(()=>this.live.setWallpaper(t)),this.session&&await this.session.setPicture(await this.#s(this.scene)))}async setTheme(t){this.theme=t==="dark"?"dark":"light",this.#m(),this.#d(),this.live&&await this.#r(()=>this.live.setTheme(this.theme)),this.session&&await this.session.setPicture(await this.#s(this.scene)),this.#e("theme",this.theme)}compare(t){this.split=t===null?null:Math.min(1,Math.max(0,t));const s=this.split!==null;if(this.divider.hidden=!s,this.buttons.compare&&this.buttons.compare.setAttribute("aria-pressed",String(s)),s){const e=Math.round(this.split*100);this.divider.style.left=this.split*100+"%",this.grip.setAttribute("aria-valuenow",String(e)),this.grip.setAttribute("aria-valuetext",e+"% before, "+(100-e)+"% after")}this.poster.style.setProperty("--og-split",s?this.split*100+"%":"0%"),this.#d(),this.session&&this.session.split(this.split),this.#e("split",this.split)}fullscreen(){document.fullscreenElement===this.root?document.exitFullscreen():this.root.requestFullscreen().catch(()=>{})}async#s(t){if(this.customPicture){const i=this.customPicture;return typeof i=="function"?i({theme:this.theme,scene:t}):i}if(this.live&&(this.live.scene[0]!==t[0]||this.live.scene[1]!==t[1])&&await this.#r(()=>this.live.setScene(t)),this.live)try{return this.live.render()}catch(i){this.#h(i)}if(!this.pictures){const i=W(this.theme,t[0],t[1]);return new ImageData(i.data,i.width,i.height)}const s=S(this.desktops,this.theme,t),e=await fetch(s);if(!e.ok)throw new Error("no stock desktop at "+s+" ("+e.status+")");return e.blob()}#w(){return this.desktopMode!=="live"||this.live?Promise.resolve():(this.liveLoad||(this.liveLoad=(async()=>{try{const t=new URL(this.desktops,location.href),{Desktop:s,PRESETS:e,WALLPAPERS:i}=await import(new URL("desktop.js",t).href);this.presets=e,this.wallpapers=i;const h=e.find(a=>a.id===this.pick.preset)||e[0];this.pick.preset=h.id,i.some(a=>a.id===this.pick.wallpaper)||(this.pick.wallpaper=h.wallpaper);const r=this.#p(),n=new s({theme:this.theme,scene:this.full?this.scene:r,preset:this.pick.preset,wallpaper:this.pick.wallpaper});await n.ready(),this.live=n,this.windowed=r,this.full||(this.scene=r,this.#a()),this.#L(),this.#t(),this.#v(),this.#e("desktop","live","")}catch(t){this.#h(t)}})()),this.liveLoad)}async#r(t){try{await t()}catch(s){this.#h(s)}}#h(t){this.live=null,this.desktopMode="stock",this.screen.style.cursor="",this.#t(),this.#e("desktop",this.pictures?"stock":"fill",String(t&&t.message||t))}#v(){if(this.pointerWired)return;this.pointerWired=!0;let t=!1,s=!1;const e=o((i,h)=>{const r=this.live;if(!r||this.customPicture)return;const n=this.screen.getBoundingClientRect();if(!n.width||!n.height)return;const a=(h.clientX-n.left)/n.width,p=(h.clientY-n.top)/n.height;if(i==="down"){if(h.button!==0)return;const u=this.session;if(s=!!(u&&u.claims(a*this.scene[0],p*this.scene[1])),t=!0,s)return}else if(s){(i==="up"||i==="leave")&&(s=t=!1);return}else if(i==="up"){if(!t)return;t=!1}else if(i==="leave"&&t)return;r.pointer(i,a*r.size[0],p*r.size[1])&&this.#g(),this.screen.style.cursor=r.cursor},"feed");this.screen.addEventListener("pointerdown",i=>e("down",i)),this.screen.addEventListener("pointermove",i=>e("move",i)),this.screen.addEventListener("pointerup",i=>e("up",i)),this.screen.addEventListener("pointercancel",i=>e("up",i)),this.screen.addEventListener("pointerleave",i=>e("leave",i))}#g(){this.liveDirty=!0,this.livePump||(this.livePump=this.#b().finally(()=>this.livePump=null))}async#b(){for(;this.liveDirty&&this.live;){const t=this.session;for(;this.inflight.length>=this.pace;)await this.inflight.shift();if(!this.liveDirty||!this.live)break;this.liveDirty=!1;let s;try{s=this.live.render()}catch(e){this.#h(e);break}!t||t!==this.session||(await t.setPicture(s),this.inflight.push(t.pictureIn()))}}#k(){let t=null;try{t=this.remember?JSON.parse(localStorage.getItem(this.remember)||"null"):null}catch{t=null}const s=m(this.surfaceScene);return{size:(t&&Array.isArray(t.size)?[...d,s].find(i=>L(i,t.size)):null)||s,portrait:t&&typeof t.portrait=="boolean"?t.portrait:this.surfaceScene[1]>this.surfaceScene[0],preset:t&&typeof t.preset=="string"?t.preset:null,wallpaper:t&&typeof t.wallpaper=="string"?t.wallpaper:null}}#n(){if(this.remember)try{localStorage.setItem(this.remember,JSON.stringify(this.pick))}catch{}}#p(){const[t,s]=this.pick.size;return this.pick.portrait?[s,t]:[t,s]}#u(t){const s=this.sceneQueue.then(async()=>{this.scene=t,this.#a(),this.session&&(await this.session.resize(t,await this.#s(t)),this.inflight=[])});return this.sceneQueue=s.catch(()=>{}),s}#a(t=this.scene){this.frame.style.setProperty("--og-aspect",String(t[0]/t[1]))}#o(t){this.poster.hidden=!t,this.#d(),this.#t()}#d(){const t=this.posterUrls;if(!t||this.poster.hidden)return;const s=new URL(t[this.theme],location.href).href;if(this.posterAfter.src!==s&&(this.posterAfter.src=s),this.posterAfter.alt=t.alt||"",this.split===null||!this.pictures)return;const e=new URL(S(this.desktops,this.theme,b),location.href).href;this.posterBefore.src!==e&&(this.posterBefore.src=e)}#l(){const t=this.userPaused||!this.inView;this.session&&t!==this.sessionPaused&&(t?this.session.pause():(this.health.warm=0,this.session.resume())),this.sessionPaused=t,this.#i()}#i(){const t=this.buttons&&this.buttons.pause;t&&(t.textContent=this.userPaused?"Play":"Pause",t.setAttribute("aria-label",this.userPaused?"Play the effect":"Pause the effect"),t.hidden=this.stilled),this.root.classList.toggle("og-paused",this.userPaused),this.#e("paused",this.userPaused)}#P(t){const s=this.health;if(this.sessionPaused||document.hidden||!this.session||this.session.phase!=="running"){s.warm=0;return}if(s.warm<H){s.warm++;return}s.slow=t.fps<this.minFps?s.slow+1:0,s.slow>=F&&this.#c("slow",t.fps)}#c(t,s){this.health.told.has(t)||(this.health.told.add(t),this.#e("degraded",t,s))}async#y(){const t=document.fullscreenElement===this.root;if(t===this.full)return;this.full=t,this.#m(),this.#t();const s=t?[Math.round(screen.width*devicePixelRatio),Math.round(screen.height*devicePixelRatio)]:this.windowed;this.stage.style.aspectRatio=t?"":this.surfaceScene[0]+" / "+this.surfaceScene[1],await this.#u(s);const e=this.screen.querySelector("canvas");this.session&&t&&e&&e.focus({preventScroll:!0}),this.#f(),this.#e("fullscreen",t)}#f(){this.root.classList.remove("og-idle"),clearTimeout(this.idleTimer),this.full&&(this.idleTimer=setTimeout(()=>{this.bar.contains(document.activeElement)||this.root.classList.add("og-idle")},V))}#S(t){const s=this.root;s.classList.add("og-player"),this.stage=l("div","og-stage"),this.stage.style.aspectRatio=this.scene[0]+" / "+this.scene[1],this.frame=l("div","og-frame"),this.screen=l("div","og-screen"),this.divider=l("div","og-divider"),this.divider.hidden=!0,this.grip=l("div","og-grip",{role:"slider",tabindex:"0","aria-label":"Before and after","aria-valuemin":"0","aria-valuemax":"100"}),this.divider.append(l("div","og-divider-line"),this.grip);const e=l("div","og-tags");e.append(Object.assign(l("span","og-tag"),{textContent:"Before"}),Object.assign(l("span","og-tag"),{textContent:"After"})),this.divider.append(e),this.poster=l("div","og-poster"),this.posterBefore=l("img","og-poster-before",{alt:"",draggable:"false"}),this.posterAfter=l("img","og-poster-after",{alt:"",draggable:"false"}),this.poster.append(this.posterBefore,this.posterAfter),this.frame.append(this.screen,this.poster,this.divider),this.stage.append(this.frame),this.#a(),this.bar=l("div","og-bar"),this.buttons={};const i={pause:o(()=>this.userPaused?this.resume():this.pause(),"pause"),compare:o(()=>this.compare(this.split===null?.5:null),"compare"),theme:o(()=>this.setTheme(this.theme==="dark"?"light":"dark"),"theme"),fullscreen:o(()=>this.fullscreen(),"fullscreen"),rotate:o(()=>this.setScreen({portrait:!this.pick.portrait}),"rotate")},h={preset:["Desktop",r=>this.setPreset(r)],screen:["Screen size",r=>this.setScreen({size:r.split("x").map(Number)})],wallpaper:["Wallpaper",r=>this.setWallpaper(r)]};for(const r of t){let n;if(h[r]){const[a,p]=h[r];n=l("select","og-button og-select",{"data-control":r,"aria-label":a}),n.addEventListener("change",()=>p(n.value))}else if(i[r])n=l("button","og-button",{type:"button","data-control":r}),n.addEventListener("click",i[r]);else continue;k.includes(r)&&(n.hidden=!0),this.buttons[r]=n,this.bar.append(n)}if(this.buttons.screen){const r=m(this.surfaceScene),n=d.some(a=>L(a,r))?d:[...d,r];for(const[a,p]of n)this.buttons.screen.append(Object.assign(l("option","",{value:a+"x"+p}),{textContent:a+_+p}))}this.buttons.compare&&this.buttons.compare.setAttribute("aria-pressed","false"),s.replaceChildren(this.stage,this.bar),this.#m(),this.#i(),this.#E()}#m(){const t=this.buttons;if(t.compare&&(t.compare.textContent="Before/after"),t.theme){const s=this.theme==="dark"?"light":"dark";t.theme.textContent=s==="dark"?"Dark desktop":"Light desktop",t.theme.setAttribute("aria-label","Show the "+s+" desktop")}t.fullscreen&&(t.fullscreen.textContent=this.full?"Exit fullscreen":"Fullscreen")}#L(){for(const[t,s]of[["preset",this.presets],["wallpaper",this.wallpapers]]){const e=this.buttons[t];e&&e.replaceChildren(...s.map(({id:i,name:h})=>Object.assign(l("option","",{value:i}),{textContent:h})))}}#t(){const t=this.buttons,s=!!this.live&&!this.customPicture&&this.poster.hidden;for(const e of k)t[e]&&(t[e].hidden=!s);if(t.screen&&(t.screen.value=this.pick.size.join("x"),t.screen.disabled=this.full),t.rotate){const e=this.pick.portrait?"landscape":"portrait";t.rotate.textContent=e==="portrait"?"Portrait":"Landscape",t.rotate.setAttribute("aria-label","Turn the screen to "+e),t.rotate.disabled=this.full}t.preset&&this.live&&(t.preset.value=this.pick.preset),t.wallpaper&&this.live&&(t.wallpaper.value=this.pick.wallpaper)}#E(){const t=o(s=>{const e=this.frame.getBoundingClientRect();return(s.clientX-e.left)/e.width},"at");this.divider.addEventListener("pointerdown",s=>{s.button===0&&(s.preventDefault(),this.divider.setPointerCapture(s.pointerId),this.grip.focus({preventScroll:!0}),this.compare(t(s)))}),this.divider.addEventListener("pointermove",s=>{this.divider.hasPointerCapture(s.pointerId)&&this.compare(t(s))}),this.grip.addEventListener("keydown",s=>{const e={ArrowLeft:-f,ArrowDown:-f,ArrowRight:f,ArrowUp:f,PageDown:-P,PageUp:P};let i=null;s.key in e?i=this.split+e[s.key]:s.key==="Home"?i=0:s.key==="End"&&(i=1),i!==null&&(s.preventDefault(),this.compare(i))})}#x(){new IntersectionObserver(t=>{const s=t[t.length-1];this.inView=s.isIntersecting,this.#l()}).observe(this.stage),document.addEventListener("fullscreenchange",()=>this.#y()),this.root.addEventListener("pointermove",()=>this.#f()),this.root.addEventListener("focusin",()=>this.#f())}#e(t,...s){const e=this.on[t];e&&e(...s)}}export{J as Player,S as stockDesktop};