holosplat 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * HoloSplat mobile stats overlay.
3
+ * Injected by player.js when ?hs is in the URL on touch/narrow viewports —
4
+ * the full art-direction editor (editor.js) is desktop-only. Connects to
5
+ * window.__hsPlayers to read live performance numbers via viewer.getStats().
6
+ *
7
+ * Tap the panel to collapse it to a small dot.
8
+ */
9
+ (function () {
10
+ if (window.__hsStats) return;
11
+ window.__hsStats = true;
12
+
13
+ const CSS = `
14
+ #__hs-stats {
15
+ position:fixed;top:8px;left:8px;z-index:99999;
16
+ background:rgba(20,20,20,.78);border:1px solid rgba(255,255,255,.12);
17
+ border-radius:8px;padding:8px 10px;
18
+ font:11px/1.5 ui-monospace,Menlo,Consolas,monospace;color:#ddd;
19
+ backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
20
+ white-space:nowrap;user-select:none;-webkit-user-select:none;
21
+ }
22
+ #__hs-stats.collapsed {
23
+ padding:0;width:14px;height:14px;border-radius:50%;
24
+ background:rgba(255,255,255,.25);border:1px solid rgba(255,255,255,.3);
25
+ }
26
+ #__hs-stats.collapsed > * { display:none; }
27
+ #__hs-stats .row { display:flex;justify-content:space-between;gap:14px; }
28
+ #__hs-stats .lbl { color:#888; }
29
+ #__hs-stats .val { color:#fff;font-weight:600; }
30
+ #__hs-stats .val.fps-good { color:#5a9a5a; }
31
+ #__hs-stats .val.fps-ok { color:#d6b35a; }
32
+ #__hs-stats .val.fps-bad { color:#d65a5a; }
33
+ `;
34
+
35
+ function fmtCount(n) {
36
+ if (n >= 1e6) return (n / 1e6).toFixed(2) + 'M';
37
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
38
+ return String(n);
39
+ }
40
+
41
+ function fpsClass(fps) {
42
+ if (fps >= 50) return 'fps-good';
43
+ if (fps >= 27) return 'fps-ok';
44
+ return 'fps-bad';
45
+ }
46
+
47
+ function init() {
48
+ const style = document.createElement('style');
49
+ style.textContent = CSS;
50
+ document.head.appendChild(style);
51
+
52
+ const el = document.createElement('div');
53
+ el.id = '__hs-stats';
54
+ el.innerHTML = `
55
+ <div class="row"><span class="lbl">fps</span><span class="val" data-k="fps">–</span></div>
56
+ <div class="row"><span class="lbl">tier</span><span class="val" data-k="tier">–</span></div>
57
+ <div class="row"><span class="lbl">sort</span><span class="val" data-k="sort">–</span></div>
58
+ <div class="row"><span class="lbl">splats</span><span class="val" data-k="splats">–</span></div>
59
+ <div class="row"><span class="lbl">sh</span><span class="val" data-k="sh">–</span></div>
60
+ <div class="row"><span class="lbl">px ratio</span><span class="val" data-k="px">–</span></div>
61
+ <div class="row"><span class="lbl">scene</span><span class="val" data-k="scene">–</span></div>
62
+ `;
63
+ document.body.appendChild(el);
64
+
65
+ el.addEventListener('click', () => el.classList.toggle('collapsed'));
66
+
67
+ const fpsEl = el.querySelector('[data-k="fps"]');
68
+ const tierEl = el.querySelector('[data-k="tier"]');
69
+ const sortEl = el.querySelector('[data-k="sort"]');
70
+ const splatsEl = el.querySelector('[data-k="splats"]');
71
+ const shEl = el.querySelector('[data-k="sh"]');
72
+ const pxEl = el.querySelector('[data-k="px"]');
73
+ const sceneEl = el.querySelector('[data-k="scene"]');
74
+
75
+ function update() {
76
+ const entry = (window.__hsPlayers || [])[0];
77
+ const stats = entry?.viewer?.getStats?.();
78
+ if (stats) {
79
+ const fps = Math.round(stats.fps);
80
+ fpsEl.textContent = String(fps);
81
+ fpsEl.className = 'val ' + fpsClass(fps);
82
+ tierEl.textContent = stats.tier ?? '–';
83
+ sortEl.textContent = stats.gpuSortFailed ? 'CPU (gpu failed)' : (stats.gpuSort ? 'GPU' : 'CPU');
84
+ splatsEl.textContent = stats.activeSplats === stats.numSplats
85
+ ? fmtCount(stats.numSplats)
86
+ : `${fmtCount(stats.activeSplats)} / ${fmtCount(stats.numSplats)}`;
87
+ shEl.textContent = String(stats.shDegree);
88
+ pxEl.textContent = stats.pixelRatio.toFixed(2);
89
+ sceneEl.textContent = stats.sceneName ?? '–';
90
+ }
91
+ setTimeout(update, 250);
92
+ }
93
+ update();
94
+ }
95
+
96
+ if (document.readyState === 'loading') {
97
+ document.addEventListener('DOMContentLoaded', init);
98
+ } else {
99
+ init();
100
+ }
101
+ })();
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "holosplat",
3
+ "version": "0.6.0",
4
+ "description": "WebGPU Gaussian Splat viewer with scroll-driven animation and art direction editor",
5
+ "type": "module",
6
+ "main": "dist/holosplat.esm.js",
7
+ "module": "dist/holosplat.esm.js",
8
+ "exports": {
9
+ ".": "./dist/holosplat.esm.js",
10
+ "./server": "./src/server.js"
11
+ },
12
+ "bin": {
13
+ "holosplat": "./bin/holosplat.cjs"
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "holosplat/",
18
+ "bin/",
19
+ "src/server.js",
20
+ "server.py"
21
+ ],
22
+ "scripts": {
23
+ "build": "node build.js",
24
+ "build:py": "python bundle.py",
25
+ "dev": "node build.js --watch"
26
+ },
27
+ "devDependencies": {
28
+ "esbuild": "^0.24.2"
29
+ }
30
+ }