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/LICENSE +21 -21
- package/README.md +311 -311
- package/README_CN.md +323 -323
- package/dist/bin/cli.js +13 -13
- package/dist/src/hooks/write-scripts.js +21 -21
- package/dist/src/renderer/index.html +264 -264
- package/package.json +48 -48
- package/src/hooks/bridge.ps1 +11 -11
- package/src/hooks/bridge.sh +9 -9
- package/dist/src/renderer/wander.d.ts +0 -8
- package/dist/src/renderer/wander.d.ts.map +0 -1
- package/dist/src/renderer/wander.js +0 -137
- package/dist/src/renderer/wander.js.map +0 -1
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>
|