petdex-cc 0.1.6 → 0.1.7
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 +287 -112
- package/README_CN.md +283 -110
- 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/dist/src/renderer/wander.d.ts +8 -0
- package/dist/src/renderer/wander.d.ts.map +1 -0
- package/dist/src/renderer/wander.js +137 -0
- package/dist/src/renderer/wander.js.map +1 -0
- package/package.json +12 -2
- package/src/hooks/bridge.ps1 +11 -11
- package/src/hooks/bridge.sh +9 -9
|
@@ -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>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BrowserWindow } from "electron";
|
|
2
|
+
type Screen = Electron.Screen;
|
|
3
|
+
export declare function startWander(window: BrowserWindow, screenObjParam?: Screen): void;
|
|
4
|
+
export declare function stopWander(): void;
|
|
5
|
+
export declare function pauseWander(): void;
|
|
6
|
+
export declare function resumeWander(): void;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=wander.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wander.d.ts","sourceRoot":"","sources":["../../../src/renderer/wander.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;AA8G9B,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAKhF;AAED,wBAAgB,UAAU,IAAI,IAAI,CAKjC;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAInC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { setPetAction } from "./pet-sprite.js";
|
|
2
|
+
let wanderRafId = null;
|
|
3
|
+
let paused = false;
|
|
4
|
+
let dx = 0;
|
|
5
|
+
let dy = 0;
|
|
6
|
+
let moveFrames = 0;
|
|
7
|
+
const SPEED = 1;
|
|
8
|
+
const MOVE_DURATION_MIN = 5 * 60; // 5 seconds at 60fps
|
|
9
|
+
const MOVE_DURATION_MAX = 12 * 60;
|
|
10
|
+
const IDLE_DURATION_MIN = 2 * 60;
|
|
11
|
+
const IDLE_DURATION_MAX = 4 * 60;
|
|
12
|
+
let screenObj = null;
|
|
13
|
+
function randomInt(min, max) {
|
|
14
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
15
|
+
}
|
|
16
|
+
/** Get usable screen area (safe bounds for the pet). */
|
|
17
|
+
function getScreenBounds() {
|
|
18
|
+
if (screenObj) {
|
|
19
|
+
try {
|
|
20
|
+
const displays = screenObj.getAllDisplays();
|
|
21
|
+
if (displays.length > 0) {
|
|
22
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
23
|
+
for (const d of displays) {
|
|
24
|
+
const w = d.workArea;
|
|
25
|
+
minX = Math.min(minX, w.x);
|
|
26
|
+
minY = Math.min(minY, w.y);
|
|
27
|
+
maxX = Math.max(maxX, w.x + w.width);
|
|
28
|
+
maxY = Math.max(maxY, w.y + w.height);
|
|
29
|
+
}
|
|
30
|
+
return { minX, minY, maxX, maxY };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
}
|
|
35
|
+
// Fallback: use primary screen from web API
|
|
36
|
+
return {
|
|
37
|
+
minX: 0,
|
|
38
|
+
minY: 0,
|
|
39
|
+
maxX: window.screen.width,
|
|
40
|
+
maxY: window.screen.height,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function pickNewDirection() {
|
|
44
|
+
dx = randomInt(-1, 1);
|
|
45
|
+
dy = randomInt(-1, 1);
|
|
46
|
+
if (dx === 0 && dy === 0)
|
|
47
|
+
dx = Math.random() < 0.5 ? -1 : 1;
|
|
48
|
+
moveFrames = randomInt(MOVE_DURATION_MIN, MOVE_DURATION_MAX);
|
|
49
|
+
if (dx > 0)
|
|
50
|
+
setPetAction("running-right");
|
|
51
|
+
else if (dx < 0)
|
|
52
|
+
setPetAction("running-left");
|
|
53
|
+
}
|
|
54
|
+
function pickIdle() {
|
|
55
|
+
dx = 0;
|
|
56
|
+
dy = 0;
|
|
57
|
+
moveFrames = randomInt(IDLE_DURATION_MIN, IDLE_DURATION_MAX);
|
|
58
|
+
const actions = ["idle", "waiting", "waving"];
|
|
59
|
+
setPetAction(actions[randomInt(0, actions.length - 1)]);
|
|
60
|
+
}
|
|
61
|
+
function wanderTick(window) {
|
|
62
|
+
if (paused) {
|
|
63
|
+
wanderRafId = requestAnimationFrame(() => wanderTick(window));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Segment finished → pick next action
|
|
67
|
+
if (moveFrames <= 0) {
|
|
68
|
+
if (Math.random() < 0.4) {
|
|
69
|
+
pickIdle();
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
pickNewDirection();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Only move if we have a direction
|
|
76
|
+
if (dx !== 0 || dy !== 0) {
|
|
77
|
+
const [wx, wy] = window.getPosition();
|
|
78
|
+
const [ww, wh] = window.getSize();
|
|
79
|
+
const { minX, minY, maxX, maxY } = getScreenBounds();
|
|
80
|
+
let nx = wx + dx * SPEED;
|
|
81
|
+
let ny = wy + dy * SPEED;
|
|
82
|
+
// Bounce: clamp position AND reverse direction
|
|
83
|
+
let bounced = false;
|
|
84
|
+
if (nx < minX) {
|
|
85
|
+
nx = minX;
|
|
86
|
+
dx = 1;
|
|
87
|
+
bounced = true;
|
|
88
|
+
}
|
|
89
|
+
if (nx + ww > maxX) {
|
|
90
|
+
nx = maxX - ww;
|
|
91
|
+
dx = -1;
|
|
92
|
+
bounced = true;
|
|
93
|
+
}
|
|
94
|
+
if (ny < minY) {
|
|
95
|
+
ny = minY;
|
|
96
|
+
dy = 1;
|
|
97
|
+
bounced = true;
|
|
98
|
+
}
|
|
99
|
+
if (ny + wh > maxY) {
|
|
100
|
+
ny = maxY - wh;
|
|
101
|
+
dy = -1;
|
|
102
|
+
bounced = true;
|
|
103
|
+
}
|
|
104
|
+
if (bounced) {
|
|
105
|
+
// Give a fresh segment after bounce so the pet walks away clearly
|
|
106
|
+
moveFrames = randomInt(MOVE_DURATION_MIN, MOVE_DURATION_MAX);
|
|
107
|
+
if (dx > 0)
|
|
108
|
+
setPetAction("running-right");
|
|
109
|
+
else if (dx < 0)
|
|
110
|
+
setPetAction("running-left");
|
|
111
|
+
}
|
|
112
|
+
window.setPosition(Math.round(nx), Math.round(ny));
|
|
113
|
+
}
|
|
114
|
+
moveFrames--;
|
|
115
|
+
wanderRafId = requestAnimationFrame(() => wanderTick(window));
|
|
116
|
+
}
|
|
117
|
+
export function startWander(window, screenObjParam) {
|
|
118
|
+
screenObj = screenObjParam ?? null;
|
|
119
|
+
paused = false;
|
|
120
|
+
pickNewDirection();
|
|
121
|
+
wanderRafId = requestAnimationFrame(() => wanderTick(window));
|
|
122
|
+
}
|
|
123
|
+
export function stopWander() {
|
|
124
|
+
if (wanderRafId !== null) {
|
|
125
|
+
cancelAnimationFrame(wanderRafId);
|
|
126
|
+
wanderRafId = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function pauseWander() {
|
|
130
|
+
paused = true;
|
|
131
|
+
}
|
|
132
|
+
export function resumeWander() {
|
|
133
|
+
paused = false;
|
|
134
|
+
// Pick a fresh direction from current position so pet doesn't jump back
|
|
135
|
+
pickNewDirection();
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=wander.js.map
|