petdex-cc 0.1.2 → 0.1.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.
package/dist/bin/cli.js CHANGED
@@ -56,19 +56,19 @@ async function main() {
56
56
  }
57
57
  }
58
58
  function printHelp() {
59
- console.log(`
60
- petdex-cc - Desktop pet companion for Claude Code
61
-
62
- Commands:
63
- install <slug> Install a pet from Petdex and configure hooks
64
- start Start the desktop pet
65
- stop Stop the desktop pet
66
- list List available pets from Petdex
67
- switch <slug> Switch to a different pet
68
- status Show current pet status and level
69
- uninstall Remove petdex-cc hooks and data
70
- config Configure API key and settings
71
- help Show this help message
59
+ console.log(`
60
+ petdex-cc - Desktop pet companion for Claude Code
61
+
62
+ Commands:
63
+ install <slug> Install a pet from Petdex and configure hooks
64
+ start Start the desktop pet
65
+ stop Stop the desktop pet
66
+ list List available pets from Petdex
67
+ switch <slug> Switch to a different pet
68
+ status Show current pet status and level
69
+ uninstall Remove petdex-cc hooks and data
70
+ config Configure API key and settings
71
+ help Show this help message
72
72
  `);
73
73
  }
74
74
  main().catch((err) => {
@@ -1,28 +1,28 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { homedir } from "node:os";
4
- const BRIDGE_SH = `#!/bin/bash
5
- INPUT=$(cat)
6
- PORT_FILE="$HOME/.petdex-cc/data/port.lock"
7
- PORT=17321
8
- if [ -f "$PORT_FILE" ]; then
9
- PORT=$(cat "$PORT_FILE" 2>/dev/null || echo "17321")
10
- fi
11
- curl -s -X POST "http://localhost:$PORT/event" \\
12
- -H "Content-Type: application/json" \\
13
- -d "$INPUT" > /dev/null 2>&1 &
4
+ const BRIDGE_SH = `#!/bin/bash
5
+ INPUT=$(cat)
6
+ PORT_FILE="$HOME/.petdex-cc/data/port.lock"
7
+ PORT=17321
8
+ if [ -f "$PORT_FILE" ]; then
9
+ PORT=$(cat "$PORT_FILE" 2>/dev/null || echo "17321")
10
+ fi
11
+ curl -s -X POST "http://localhost:$PORT/event" \\
12
+ -H "Content-Type: application/json" \\
13
+ -d "$INPUT" > /dev/null 2>&1 &
14
14
  `;
15
- const BRIDGE_PS1 = `$inputJson = [System.Console]::In.ReadToEnd()
16
- $portFile = Join-Path $env:USERPROFILE ".petdex-cc\\data\\port.lock"
17
- $port = 17321
18
- if (Test-Path $portFile) {
19
- $port = Get-Content $portFile -ErrorAction SilentlyContinue
20
- if (-not $port) { $port = 17321 }
21
- }
22
- $url = "http://localhost:$port/event"
23
- try {
24
- Invoke-WebRequest -Uri $url -Method POST -ContentType "application/json" -Body $inputJson -UseBasicParsing -TimeoutSec 5 | Out-Null
25
- } catch {}
15
+ const BRIDGE_PS1 = `$inputJson = [System.Console]::In.ReadToEnd()
16
+ $portFile = Join-Path $env:USERPROFILE ".petdex-cc\\data\\port.lock"
17
+ $port = 17321
18
+ if (Test-Path $portFile) {
19
+ $port = Get-Content $portFile -ErrorAction SilentlyContinue
20
+ if (-not $port) { $port = 17321 }
21
+ }
22
+ $url = "http://localhost:$port/event"
23
+ try {
24
+ Invoke-WebRequest -Uri $url -Method POST -ContentType "application/json" -Body $inputJson -UseBasicParsing -TimeoutSec 5 | Out-Null
25
+ } catch {}
26
26
  `;
27
27
  export function writeBridgeScripts() {
28
28
  const hooksDir = path.join(homedir(), ".petdex-cc", "hooks");
@@ -1,264 +1,264 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <!-- CSP removed for local Electron renderer -->
7
- <title>petdex-cc</title>
8
- <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- html, body {
16
- width: 100%;
17
- height: 100%;
18
- overflow: hidden;
19
- background: transparent;
20
- user-select: none;
21
- -webkit-user-select: none;
22
- }
23
-
24
- #pet-container {
25
- position: absolute;
26
- width: 300px;
27
- height: 320px;
28
- display: flex;
29
- flex-direction: column;
30
- align-items: center;
31
- justify-content: flex-end;
32
- }
33
-
34
- #bubble-container {
35
- position: relative;
36
- width: 100%;
37
- display: flex;
38
- justify-content: center;
39
- margin-bottom: 4px;
40
- pointer-events: none;
41
- z-index: 10;
42
- }
43
-
44
- #bubble {
45
- position: absolute;
46
- bottom: calc(100% + 10px);
47
- max-width: 220px;
48
- padding: 8px 12px;
49
- border-radius: 12px;
50
- background: rgba(255, 255, 255, 0.9);
51
- font: 13px sans-serif;
52
- color: #333;
53
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
54
- opacity: 1;
55
- transition: opacity 200ms ease-in, opacity 500ms ease-out;
56
- pointer-events: none;
57
- word-wrap: break-word;
58
- line-height: 1.4;
59
- }
60
-
61
- #bubble.bubble-hidden {
62
- opacity: 0;
63
- }
64
-
65
- #bubble::after {
66
- content: '';
67
- position: absolute;
68
- bottom: -10px;
69
- left: 50%;
70
- transform: translateX(-50%);
71
- width: 0;
72
- height: 0;
73
- border-left: 10px solid transparent;
74
- border-right: 10px solid transparent;
75
- border-top: 10px solid rgba(255, 255, 255, 0.9);
76
- }
77
-
78
- #pet-sprite {
79
- width: 192px;
80
- height: 208px;
81
- position: relative;
82
- background-repeat: no-repeat;
83
- background-size: 1536px 1872px;
84
- image-rendering: pixelated;
85
- background-image: var(--sprite-url);
86
- animation: pet-state var(--sprite-duration) steps(var(--sprite-frames)) infinite;
87
- --sprite-url: none;
88
- --sprite-row: 0;
89
- --sprite-frames: 6;
90
- --sprite-duration: 1100ms;
91
- --sprite-y: calc(var(--sprite-row) * -208px);
92
- --sprite-end-x: calc(var(--sprite-frames) * -192px);
93
- }
94
-
95
- @keyframes pet-state {
96
- from {
97
- background-position: 0 var(--sprite-y);
98
- }
99
- to {
100
- background-position: var(--sprite-end-x) var(--sprite-y);
101
- }
102
- }
103
-
104
- /* ── Level badge ── */
105
- #level-badge {
106
- position: absolute;
107
- top: 4px;
108
- right: 4px;
109
- padding: 2px 8px;
110
- border-radius: 8px;
111
- background: linear-gradient(145deg, rgba(255,255,255,0.14), rgba(255,255,255,0.04));
112
- border: 1px solid rgba(255,255,255,0.2);
113
- color: #fff;
114
- font: 600 10px/1.4 'Cascadia Code', 'SF Mono', 'Fira Code', Consolas, monospace;
115
- letter-spacing: 0.4px;
116
- pointer-events: none;
117
- z-index: 20;
118
- white-space: nowrap;
119
- display: none;
120
- text-shadow: 0 0 8px var(--lv-color, #fff), 0 1px 2px rgba(0,0,0,0.5);
121
- box-shadow: 0 0 8px -2px var(--lv-color, transparent),
122
- 0 2px 4px rgba(0,0,0,0.2),
123
- inset 0 1px 0 rgba(255,255,255,0.1);
124
- overflow: hidden;
125
- }
126
-
127
- #level-badge::after {
128
- content: '';
129
- position: absolute;
130
- top: 0; left: -60%;
131
- width: 40%; height: 100%;
132
- background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
133
- transform: skewX(-25deg);
134
- animation: badge-shine 5s ease-in-out infinite;
135
- }
136
-
137
- @keyframes badge-shine {
138
- 0%, 75%, 100% { left: -60%; }
139
- 90% { left: 140%; }
140
- }
141
-
142
- /* ── Level effect layers ── */
143
- #level-glow, #level-aura, #level-halo {
144
- position: absolute;
145
- top: -16px; left: -16px;
146
- width: 224px; height: 240px;
147
- pointer-events: none;
148
- }
149
-
150
- #level-particles {
151
- position: absolute;
152
- top: 0; left: 0;
153
- width: 192px; height: 208px;
154
- overflow: visible;
155
- pointer-events: none;
156
- }
157
-
158
- /* Glow — soft breathing radial */
159
- #level-glow {
160
- border-radius: 50%;
161
- filter: blur(18px);
162
- opacity: 0;
163
- }
164
-
165
- #level-glow.active {
166
- background: radial-gradient(circle, var(--lv-color, #4ade80) 0%, transparent 60%);
167
- animation: glow-breathe 4s ease-in-out infinite;
168
- }
169
-
170
- @keyframes glow-breathe {
171
- 0%, 100% { opacity: 0.1; transform: scale(0.9); }
172
- 50% { opacity: 0.3; transform: scale(1.08); }
173
- }
174
-
175
- /* Aura — rotating conic-gradient ring */
176
- #level-aura {
177
- border-radius: 50%;
178
- opacity: 0;
179
- }
180
-
181
- #level-aura.active {
182
- background: conic-gradient(
183
- from 0deg,
184
- transparent 0%, var(--lv-color, #60a5fa) 6%, transparent 14%,
185
- transparent 44%, var(--lv-color, #60a5fa) 50%, transparent 58%,
186
- transparent 100%
187
- );
188
- -webkit-mask: radial-gradient(circle, transparent 58%, black 60%, black 78%, transparent 80%);
189
- mask: radial-gradient(circle, transparent 58%, black 60%, black 78%, transparent 80%);
190
- animation: aura-spin 10s linear infinite, aura-pulse 4s ease-in-out infinite;
191
- }
192
-
193
- @keyframes aura-spin { to { transform: rotate(360deg); } }
194
- @keyframes aura-pulse {
195
- 0%, 100% { opacity: 0.45; }
196
- 50% { opacity: 0.85; }
197
- }
198
-
199
- /* Particles — floating light motes */
200
- .level-particle {
201
- position: absolute;
202
- width: 3px; height: 3px;
203
- border-radius: 50%;
204
- background: var(--lv-color, #f59e0b);
205
- box-shadow: 0 0 6px 1px var(--lv-color, #f59e0b);
206
- opacity: 0;
207
- pointer-events: none;
208
- }
209
-
210
- .level-particle.active {
211
- animation: p-float var(--p-dur, 3s) ease-out infinite var(--p-del, 0s);
212
- }
213
-
214
- @keyframes p-float {
215
- 0% { opacity: 0; transform: translateY(0) scale(0.4); }
216
- 10% { opacity: 0.9; transform: translateY(-5px) scale(1); }
217
- 100% { opacity: 0; transform: translateY(var(--p-dy, -80px)) translateX(var(--p-dx, 0px)) scale(0.2); }
218
- }
219
-
220
- /* Halo — golden shimmer ring */
221
- #level-halo {
222
- border-radius: 50%;
223
- border: 2px solid transparent;
224
- opacity: 0;
225
- }
226
-
227
- #level-halo.active {
228
- border-color: #fbbf24;
229
- animation: halo-glow 3s ease-in-out infinite;
230
- }
231
-
232
- @keyframes halo-glow {
233
- 0%, 100% { opacity: 0.45; box-shadow: 0 0 10px #fbbf2444, inset 0 0 6px #fbbf2422; }
234
- 50% { opacity: 0.85; box-shadow: 0 0 24px #fbbf2488, inset 0 0 12px #fbbf2444, 0 0 40px #fbbf2422; }
235
- }
236
- </style>
237
- </head>
238
- <body>
239
- <div id="pet-container">
240
- <div id="bubble-container">
241
- <div id="bubble" class="bubble-hidden"></div>
242
- </div>
243
- <div id="pet-sprite-wrapper" style="position: relative;">
244
- <div id="pet-sprite"></div>
245
- <div id="level-glow"></div>
246
- <div id="level-aura"></div>
247
- <div id="level-particles">
248
- <div class="level-particle" style="left:18%;top:78%;--p-dur:3.2s;--p-del:0s;--p-dy:-85px;--p-dx:-8px"></div>
249
- <div class="level-particle" style="left:38%;top:88%;--p-dur:2.7s;--p-del:.5s;--p-dy:-75px;--p-dx:6px"></div>
250
- <div class="level-particle" style="left:58%;top:82%;--p-dur:3.4s;--p-del:.9s;--p-dy:-90px;--p-dx:-4px"></div>
251
- <div class="level-particle" style="left:78%;top:72%;--p-dur:2.9s;--p-del:1.4s;--p-dy:-70px;--p-dx:10px"></div>
252
- <div class="level-particle" style="left:12%;top:62%;--p-dur:3.1s;--p-del:1.8s;--p-dy:-80px;--p-dx:-12px"></div>
253
- <div class="level-particle" style="left:48%;top:92%;--p-dur:3.5s;--p-del:2.2s;--p-dy:-95px;--p-dx:3px"></div>
254
- <div class="level-particle" style="left:72%;top:68%;--p-dur:2.8s;--p-del:2.6s;--p-dy:-72px;--p-dx:8px"></div>
255
- <div class="level-particle" style="left:28%;top:58%;--p-dur:3.3s;--p-del:3s;--p-dy:-65px;--p-dx:-6px"></div>
256
- </div>
257
- <div id="level-halo"></div>
258
- <div id="level-badge"></div>
259
- </div>
260
- </div>
261
- <script src="./renderer.js"></script>
262
- <!-- In dev, renderer.ts compiles to renderer.js via tsc -->
263
- </body>
264
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <!-- CSP removed for local Electron renderer -->
7
+ <title>petdex-cc</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ html, body {
16
+ width: 100%;
17
+ height: 100%;
18
+ overflow: hidden;
19
+ background: transparent;
20
+ user-select: none;
21
+ -webkit-user-select: none;
22
+ }
23
+
24
+ #pet-container {
25
+ position: absolute;
26
+ width: 300px;
27
+ height: 320px;
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: center;
31
+ justify-content: flex-end;
32
+ }
33
+
34
+ #bubble-container {
35
+ position: relative;
36
+ width: 100%;
37
+ display: flex;
38
+ justify-content: center;
39
+ margin-bottom: 4px;
40
+ pointer-events: none;
41
+ z-index: 10;
42
+ }
43
+
44
+ #bubble {
45
+ position: absolute;
46
+ bottom: calc(100% + 10px);
47
+ max-width: 220px;
48
+ padding: 8px 12px;
49
+ border-radius: 12px;
50
+ background: rgba(255, 255, 255, 0.9);
51
+ font: 13px sans-serif;
52
+ color: #333;
53
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
54
+ opacity: 1;
55
+ transition: opacity 200ms ease-in, opacity 500ms ease-out;
56
+ pointer-events: none;
57
+ word-wrap: break-word;
58
+ line-height: 1.4;
59
+ }
60
+
61
+ #bubble.bubble-hidden {
62
+ opacity: 0;
63
+ }
64
+
65
+ #bubble::after {
66
+ content: '';
67
+ position: absolute;
68
+ bottom: -10px;
69
+ left: 50%;
70
+ transform: translateX(-50%);
71
+ width: 0;
72
+ height: 0;
73
+ border-left: 10px solid transparent;
74
+ border-right: 10px solid transparent;
75
+ border-top: 10px solid rgba(255, 255, 255, 0.9);
76
+ }
77
+
78
+ #pet-sprite {
79
+ width: 192px;
80
+ height: 208px;
81
+ position: relative;
82
+ background-repeat: no-repeat;
83
+ background-size: 1536px 1872px;
84
+ image-rendering: pixelated;
85
+ background-image: var(--sprite-url);
86
+ animation: pet-state var(--sprite-duration) steps(var(--sprite-frames)) infinite;
87
+ --sprite-url: none;
88
+ --sprite-row: 0;
89
+ --sprite-frames: 6;
90
+ --sprite-duration: 1100ms;
91
+ --sprite-y: calc(var(--sprite-row) * -208px);
92
+ --sprite-end-x: calc(var(--sprite-frames) * -192px);
93
+ }
94
+
95
+ @keyframes pet-state {
96
+ from {
97
+ background-position: 0 var(--sprite-y);
98
+ }
99
+ to {
100
+ background-position: var(--sprite-end-x) var(--sprite-y);
101
+ }
102
+ }
103
+
104
+ /* ── Level badge ── */
105
+ #level-badge {
106
+ position: absolute;
107
+ top: 4px;
108
+ right: 4px;
109
+ padding: 2px 8px;
110
+ border-radius: 8px;
111
+ background: linear-gradient(145deg, rgba(255,255,255,0.14), rgba(255,255,255,0.04));
112
+ border: 1px solid rgba(255,255,255,0.2);
113
+ color: #fff;
114
+ font: 600 10px/1.4 'Cascadia Code', 'SF Mono', 'Fira Code', Consolas, monospace;
115
+ letter-spacing: 0.4px;
116
+ pointer-events: none;
117
+ z-index: 20;
118
+ white-space: nowrap;
119
+ display: none;
120
+ text-shadow: 0 0 8px var(--lv-color, #fff), 0 1px 2px rgba(0,0,0,0.5);
121
+ box-shadow: 0 0 8px -2px var(--lv-color, transparent),
122
+ 0 2px 4px rgba(0,0,0,0.2),
123
+ inset 0 1px 0 rgba(255,255,255,0.1);
124
+ overflow: hidden;
125
+ }
126
+
127
+ #level-badge::after {
128
+ content: '';
129
+ position: absolute;
130
+ top: 0; left: -60%;
131
+ width: 40%; height: 100%;
132
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
133
+ transform: skewX(-25deg);
134
+ animation: badge-shine 5s ease-in-out infinite;
135
+ }
136
+
137
+ @keyframes badge-shine {
138
+ 0%, 75%, 100% { left: -60%; }
139
+ 90% { left: 140%; }
140
+ }
141
+
142
+ /* ── Level effect layers ── */
143
+ #level-glow, #level-aura, #level-halo {
144
+ position: absolute;
145
+ top: -16px; left: -16px;
146
+ width: 224px; height: 240px;
147
+ pointer-events: none;
148
+ }
149
+
150
+ #level-particles {
151
+ position: absolute;
152
+ top: 0; left: 0;
153
+ width: 192px; height: 208px;
154
+ overflow: visible;
155
+ pointer-events: none;
156
+ }
157
+
158
+ /* Glow — soft breathing radial */
159
+ #level-glow {
160
+ border-radius: 50%;
161
+ filter: blur(18px);
162
+ opacity: 0;
163
+ }
164
+
165
+ #level-glow.active {
166
+ background: radial-gradient(circle, var(--lv-color, #4ade80) 0%, transparent 60%);
167
+ animation: glow-breathe 4s ease-in-out infinite;
168
+ }
169
+
170
+ @keyframes glow-breathe {
171
+ 0%, 100% { opacity: 0.1; transform: scale(0.9); }
172
+ 50% { opacity: 0.3; transform: scale(1.08); }
173
+ }
174
+
175
+ /* Aura — rotating conic-gradient ring */
176
+ #level-aura {
177
+ border-radius: 50%;
178
+ opacity: 0;
179
+ }
180
+
181
+ #level-aura.active {
182
+ background: conic-gradient(
183
+ from 0deg,
184
+ transparent 0%, var(--lv-color, #60a5fa) 6%, transparent 14%,
185
+ transparent 44%, var(--lv-color, #60a5fa) 50%, transparent 58%,
186
+ transparent 100%
187
+ );
188
+ -webkit-mask: radial-gradient(circle, transparent 58%, black 60%, black 78%, transparent 80%);
189
+ mask: radial-gradient(circle, transparent 58%, black 60%, black 78%, transparent 80%);
190
+ animation: aura-spin 10s linear infinite, aura-pulse 4s ease-in-out infinite;
191
+ }
192
+
193
+ @keyframes aura-spin { to { transform: rotate(360deg); } }
194
+ @keyframes aura-pulse {
195
+ 0%, 100% { opacity: 0.45; }
196
+ 50% { opacity: 0.85; }
197
+ }
198
+
199
+ /* Particles — floating light motes */
200
+ .level-particle {
201
+ position: absolute;
202
+ width: 3px; height: 3px;
203
+ border-radius: 50%;
204
+ background: var(--lv-color, #f59e0b);
205
+ box-shadow: 0 0 6px 1px var(--lv-color, #f59e0b);
206
+ opacity: 0;
207
+ pointer-events: none;
208
+ }
209
+
210
+ .level-particle.active {
211
+ animation: p-float var(--p-dur, 3s) ease-out infinite var(--p-del, 0s);
212
+ }
213
+
214
+ @keyframes p-float {
215
+ 0% { opacity: 0; transform: translateY(0) scale(0.4); }
216
+ 10% { opacity: 0.9; transform: translateY(-5px) scale(1); }
217
+ 100% { opacity: 0; transform: translateY(var(--p-dy, -80px)) translateX(var(--p-dx, 0px)) scale(0.2); }
218
+ }
219
+
220
+ /* Halo — golden shimmer ring */
221
+ #level-halo {
222
+ border-radius: 50%;
223
+ border: 2px solid transparent;
224
+ opacity: 0;
225
+ }
226
+
227
+ #level-halo.active {
228
+ border-color: #fbbf24;
229
+ animation: halo-glow 3s ease-in-out infinite;
230
+ }
231
+
232
+ @keyframes halo-glow {
233
+ 0%, 100% { opacity: 0.45; box-shadow: 0 0 10px #fbbf2444, inset 0 0 6px #fbbf2422; }
234
+ 50% { opacity: 0.85; box-shadow: 0 0 24px #fbbf2488, inset 0 0 12px #fbbf2444, 0 0 40px #fbbf2422; }
235
+ }
236
+ </style>
237
+ </head>
238
+ <body>
239
+ <div id="pet-container">
240
+ <div id="bubble-container">
241
+ <div id="bubble" class="bubble-hidden"></div>
242
+ </div>
243
+ <div id="pet-sprite-wrapper" style="position: relative;">
244
+ <div id="pet-sprite"></div>
245
+ <div id="level-glow"></div>
246
+ <div id="level-aura"></div>
247
+ <div id="level-particles">
248
+ <div class="level-particle" style="left:18%;top:78%;--p-dur:3.2s;--p-del:0s;--p-dy:-85px;--p-dx:-8px"></div>
249
+ <div class="level-particle" style="left:38%;top:88%;--p-dur:2.7s;--p-del:.5s;--p-dy:-75px;--p-dx:6px"></div>
250
+ <div class="level-particle" style="left:58%;top:82%;--p-dur:3.4s;--p-del:.9s;--p-dy:-90px;--p-dx:-4px"></div>
251
+ <div class="level-particle" style="left:78%;top:72%;--p-dur:2.9s;--p-del:1.4s;--p-dy:-70px;--p-dx:10px"></div>
252
+ <div class="level-particle" style="left:12%;top:62%;--p-dur:3.1s;--p-del:1.8s;--p-dy:-80px;--p-dx:-12px"></div>
253
+ <div class="level-particle" style="left:48%;top:92%;--p-dur:3.5s;--p-del:2.2s;--p-dy:-95px;--p-dx:3px"></div>
254
+ <div class="level-particle" style="left:72%;top:68%;--p-dur:2.8s;--p-del:2.6s;--p-dy:-72px;--p-dx:8px"></div>
255
+ <div class="level-particle" style="left:28%;top:58%;--p-dur:3.3s;--p-del:3s;--p-dy:-65px;--p-dx:-6px"></div>
256
+ </div>
257
+ <div id="level-halo"></div>
258
+ <div id="level-badge"></div>
259
+ </div>
260
+ </div>
261
+ <script src="./renderer.js"></script>
262
+ <!-- In dev, renderer.ts compiles to renderer.js via tsc -->
263
+ </body>
264
+ </html>