ghost-bridge 0.3.0 → 0.5.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.
- package/LICENSE +21 -0
- package/README.md +140 -66
- package/dist/cli.js +68 -37
- package/dist/server.js +71 -1
- package/extension/background.js +425 -39
- package/extension/icon-128.png +0 -0
- package/extension/icon-16.png +0 -0
- package/extension/icon-48.png +0 -0
- package/extension/manifest.json +1 -1
- package/extension/offscreen.js +1 -1
- package/extension/popup.html +416 -85
- package/extension/popup.js +128 -53
- package/package.json +24 -1
package/extension/popup.html
CHANGED
|
@@ -3,158 +3,489 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<style>
|
|
6
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
|
7
|
+
|
|
6
8
|
* {
|
|
7
9
|
margin: 0;
|
|
8
10
|
padding: 0;
|
|
9
11
|
box-sizing: border-box;
|
|
10
12
|
}
|
|
13
|
+
html {
|
|
14
|
+
height: fit-content;
|
|
15
|
+
width: fit-content;
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
body {
|
|
12
|
-
width:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
width: 320px;
|
|
20
|
+
height: fit-content;
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 20px;
|
|
23
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
24
|
+
background: #1f1f3a;
|
|
25
|
+
background: radial-gradient(circle at top right, #1f1f3a, #0b0c10);
|
|
26
|
+
color: #e0e6ed;
|
|
18
27
|
overflow: hidden;
|
|
19
|
-
|
|
28
|
+
position: relative;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body::before {
|
|
32
|
+
content: '';
|
|
33
|
+
position: absolute;
|
|
34
|
+
top: 0;
|
|
35
|
+
left: 0;
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
background: radial-gradient(circle at center, rgba(74, 158, 255, 0.05) 0%, transparent 80%);
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
z-index: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Full-screen ripple background container */
|
|
44
|
+
.bg-ripple-container {
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 0;
|
|
47
|
+
left: 0;
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 100%;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
pointer-events: none;
|
|
52
|
+
z-index: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.bg-ripple-container svg {
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.container {
|
|
61
|
+
position: relative;
|
|
62
|
+
z-index: 1;
|
|
20
63
|
}
|
|
64
|
+
|
|
21
65
|
.header {
|
|
22
66
|
display: flex;
|
|
23
67
|
align-items: center;
|
|
24
|
-
gap:
|
|
25
|
-
margin-bottom:
|
|
68
|
+
gap: 12px;
|
|
69
|
+
margin-bottom: 20px;
|
|
70
|
+
}
|
|
71
|
+
.ghost-wrapper {
|
|
72
|
+
position: relative;
|
|
73
|
+
width: 48px;
|
|
74
|
+
height: 48px;
|
|
75
|
+
flex-shrink: 0;
|
|
26
76
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
77
|
+
|
|
78
|
+
.ghost-wrapper svg {
|
|
79
|
+
width: 100%;
|
|
80
|
+
height: 100%;
|
|
81
|
+
overflow: visible;
|
|
82
|
+
filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.2));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ghost-body {
|
|
86
|
+
fill: #fff;
|
|
87
|
+
transition: fill 0.4s ease;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.bg-ripple {
|
|
91
|
+
fill: none;
|
|
92
|
+
stroke-width: 2;
|
|
93
|
+
opacity: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.ghost-eyes-normal, .ghost-eyes-closed, .ghost-eyes-star {
|
|
97
|
+
transition: opacity 0.3s ease;
|
|
98
|
+
opacity: 0;
|
|
30
99
|
}
|
|
100
|
+
|
|
101
|
+
/* Body/Ghost State Binding (Ripples now target .bg-ripple within body classes) */
|
|
102
|
+
.ghost-disconnected .ghost-body { fill: #d1d5db; }
|
|
103
|
+
.ghost-disconnected .ghost-eyes-closed { opacity: 1; }
|
|
104
|
+
.ghost-disconnected .ghost-mouth { opacity: 0.5; }
|
|
105
|
+
|
|
106
|
+
.ghost-connecting .ghost-body { fill: #a252ff; }
|
|
107
|
+
.ghost-connecting .ghost-eyes-normal { opacity: 1; }
|
|
108
|
+
|
|
109
|
+
body.connecting-state .bg-ripple {
|
|
110
|
+
stroke: #a252ff;
|
|
111
|
+
animation: bg-ripple-scan 2s infinite cubic-bezier(0, 0, 0.2, 1);
|
|
112
|
+
}
|
|
113
|
+
body.connecting-state .bg-ripple-2 { animation-delay: 1s; }
|
|
114
|
+
|
|
115
|
+
.ghost-connected .ghost-body { fill: #00d2ff; }
|
|
116
|
+
.ghost-connected .ghost-eyes-star { opacity: 1; }
|
|
117
|
+
|
|
118
|
+
body.connected-state .bg-ripple {
|
|
119
|
+
stroke: #00d2ff;
|
|
120
|
+
animation: bg-ripple-neon 2.5s infinite cubic-bezier(0, 0, 0.2, 1);
|
|
121
|
+
}
|
|
122
|
+
body.connected-state .bg-ripple-2 { animation-delay: 1.25s; }
|
|
123
|
+
|
|
124
|
+
@keyframes bg-ripple-scan {
|
|
125
|
+
0% { r: 10%; opacity: 0.8; stroke-width: 3; }
|
|
126
|
+
100% { r: 80%; opacity: 0; stroke-width: 0; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@keyframes bg-ripple-neon {
|
|
130
|
+
0% { r: 10%; opacity: 0.6; stroke-width: 4; }
|
|
131
|
+
100% { r: 100%; opacity: 0; stroke-width: 0; }
|
|
132
|
+
}
|
|
133
|
+
|
|
31
134
|
.header h1 {
|
|
32
|
-
font-size:
|
|
135
|
+
font-size: 18px;
|
|
33
136
|
font-weight: 600;
|
|
137
|
+
letter-spacing: 0.5px;
|
|
138
|
+
background: linear-gradient(90deg, #fff, #a0b0d0);
|
|
139
|
+
-webkit-background-clip: text;
|
|
140
|
+
background-clip: text;
|
|
141
|
+
-webkit-text-fill-color: transparent;
|
|
34
142
|
}
|
|
143
|
+
|
|
35
144
|
.status-card {
|
|
36
|
-
background:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
145
|
+
background: rgba(22, 33, 62, 0.6);
|
|
146
|
+
backdrop-filter: blur(12px);
|
|
147
|
+
-webkit-backdrop-filter: blur(12px);
|
|
148
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
149
|
+
border-radius: 12px;
|
|
150
|
+
padding: 16px;
|
|
151
|
+
margin-bottom: 16px;
|
|
152
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
153
|
+
transition: all 0.3s ease;
|
|
40
154
|
}
|
|
155
|
+
|
|
41
156
|
.status-row {
|
|
42
157
|
display: flex;
|
|
43
158
|
align-items: center;
|
|
44
|
-
gap:
|
|
159
|
+
gap: 12px;
|
|
45
160
|
}
|
|
161
|
+
|
|
162
|
+
/* Modern Ripple/Pulse Animations */
|
|
163
|
+
.status-dot-wrapper {
|
|
164
|
+
position: relative;
|
|
165
|
+
width: 12px;
|
|
166
|
+
height: 12px;
|
|
167
|
+
flex-shrink: 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
46
170
|
.status-dot {
|
|
47
|
-
width:
|
|
48
|
-
height:
|
|
171
|
+
width: 100%;
|
|
172
|
+
height: 100%;
|
|
49
173
|
border-radius: 50%;
|
|
50
|
-
|
|
174
|
+
transition: background 0.3s ease, box-shadow 0.3s ease;
|
|
175
|
+
position: relative;
|
|
176
|
+
z-index: 2;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.status-ping {
|
|
180
|
+
position: absolute;
|
|
181
|
+
top: 0;
|
|
182
|
+
left: 0;
|
|
183
|
+
width: 100%;
|
|
184
|
+
height: 100%;
|
|
185
|
+
border-radius: 50%;
|
|
186
|
+
z-index: 1;
|
|
187
|
+
opacity: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Connected State */
|
|
191
|
+
.connected .status-dot { background: #00d2ff; box-shadow: 0 0 10px #00d2ff; }
|
|
192
|
+
.connected .status-ping {
|
|
193
|
+
background: #00d2ff;
|
|
194
|
+
animation: ping-slow 3s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Connecting/Scanning State */
|
|
198
|
+
.connecting .status-dot { background: #a252ff; box-shadow: 0 0 12px #a252ff; }
|
|
199
|
+
.connecting .status-ping {
|
|
200
|
+
background: #a252ff;
|
|
201
|
+
animation: ping-fast 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
51
202
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.status-dot
|
|
55
|
-
.status-dot
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
203
|
+
|
|
204
|
+
/* Disconnected/Error State */
|
|
205
|
+
.disconnected .status-dot { background: #4b5563; }
|
|
206
|
+
.error .status-dot { background: #ef4444; box-shadow: 0 0 8px #ef4444; }
|
|
207
|
+
|
|
208
|
+
@keyframes ping-fast {
|
|
209
|
+
0% { transform: scale(1); opacity: 0.8; }
|
|
210
|
+
75%, 100% { transform: scale(2.5); opacity: 0; }
|
|
59
211
|
}
|
|
212
|
+
@keyframes ping-slow {
|
|
213
|
+
0% { transform: scale(1); opacity: 0.6; }
|
|
214
|
+
75%, 100% { transform: scale(2.2); opacity: 0; }
|
|
215
|
+
}
|
|
216
|
+
|
|
60
217
|
.status-text {
|
|
61
|
-
font-size:
|
|
62
|
-
font-weight:
|
|
218
|
+
font-size: 15px;
|
|
219
|
+
font-weight: 600;
|
|
220
|
+
letter-spacing: 0.3px;
|
|
221
|
+
transition: color 0.3s ease;
|
|
63
222
|
}
|
|
64
|
-
|
|
223
|
+
|
|
224
|
+
.status-detail-container {
|
|
225
|
+
margin-top: 12px;
|
|
226
|
+
padding-top: 12px;
|
|
227
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
gap: 6px;
|
|
231
|
+
max-height: 150px;
|
|
232
|
+
opacity: 1;
|
|
233
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
234
|
+
overflow: hidden;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.status-detail-container.collapsed {
|
|
238
|
+
max-height: 0;
|
|
239
|
+
opacity: 0;
|
|
240
|
+
margin-top: 0;
|
|
241
|
+
padding-top: 0;
|
|
242
|
+
border-top-color: transparent;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.detail-row {
|
|
246
|
+
display: flex;
|
|
247
|
+
justify-content: space-between;
|
|
65
248
|
font-size: 12px;
|
|
66
|
-
color: #888;
|
|
67
|
-
margin-top: 8px;
|
|
68
|
-
padding-left: 20px;
|
|
69
249
|
}
|
|
70
|
-
|
|
71
|
-
|
|
250
|
+
|
|
251
|
+
.detail-row.clickable {
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
transition: background 0.2s ease;
|
|
254
|
+
padding: 2px 4px;
|
|
255
|
+
margin: -2px -4px;
|
|
256
|
+
border-radius: 4px;
|
|
257
|
+
}
|
|
258
|
+
.detail-row.clickable:hover {
|
|
259
|
+
background: rgba(255, 255, 255, 0.05);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.detail-label {
|
|
263
|
+
color: #8b949e;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.detail-value {
|
|
267
|
+
color: #c9d1d9;
|
|
268
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
269
|
+
max-width: 180px;
|
|
270
|
+
white-space: nowrap;
|
|
271
|
+
overflow: hidden;
|
|
272
|
+
text-overflow: ellipsis;
|
|
273
|
+
text-align: right;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.detail-value.highlight {
|
|
277
|
+
color: #00d2ff;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.detail-value.warning {
|
|
281
|
+
color: #ff9f0a;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.error-list {
|
|
285
|
+
max-height: 180px;
|
|
286
|
+
overflow-y: auto;
|
|
287
|
+
margin-top: 12px;
|
|
288
|
+
padding: 8px;
|
|
289
|
+
background: rgba(0, 0, 0, 0.4);
|
|
72
290
|
border-radius: 8px;
|
|
73
|
-
|
|
74
|
-
|
|
291
|
+
border: 1px solid rgba(255, 59, 48, 0.2);
|
|
292
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
293
|
+
opacity: 1;
|
|
75
294
|
}
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
margin-
|
|
295
|
+
.error-list.collapsed {
|
|
296
|
+
max-height: 0;
|
|
297
|
+
opacity: 0;
|
|
298
|
+
margin-top: 0;
|
|
299
|
+
padding-top: 0;
|
|
300
|
+
padding-bottom: 0;
|
|
301
|
+
border-color: transparent;
|
|
302
|
+
overflow: hidden;
|
|
80
303
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
304
|
+
|
|
305
|
+
.error-item {
|
|
306
|
+
font-size: 11px;
|
|
307
|
+
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
308
|
+
padding: 6px;
|
|
309
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
310
|
+
word-break: break-work;
|
|
311
|
+
color: #e2e8f0;
|
|
312
|
+
display: flex;
|
|
313
|
+
flex-direction: column;
|
|
314
|
+
gap: 4px;
|
|
315
|
+
}
|
|
316
|
+
.error-item:last-child {
|
|
317
|
+
border-bottom: none;
|
|
90
318
|
}
|
|
91
|
-
.
|
|
92
|
-
|
|
319
|
+
.error-item .err-msg {
|
|
320
|
+
color: #ff8a8a;
|
|
321
|
+
white-space: pre-wrap;
|
|
322
|
+
}
|
|
323
|
+
.error-item .err-loc {
|
|
324
|
+
color: #64748b;
|
|
325
|
+
font-size: 10px;
|
|
93
326
|
}
|
|
94
327
|
|
|
95
328
|
.btn-row {
|
|
96
329
|
display: flex;
|
|
97
|
-
gap:
|
|
330
|
+
gap: 10px;
|
|
98
331
|
}
|
|
332
|
+
|
|
99
333
|
.btn {
|
|
100
334
|
flex: 1;
|
|
101
|
-
padding:
|
|
335
|
+
padding: 12px;
|
|
102
336
|
border: none;
|
|
103
|
-
border-radius:
|
|
104
|
-
font-size:
|
|
105
|
-
font-weight:
|
|
337
|
+
border-radius: 8px;
|
|
338
|
+
font-size: 14px;
|
|
339
|
+
font-weight: 600;
|
|
106
340
|
cursor: pointer;
|
|
107
|
-
transition: all 0.2s;
|
|
341
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
342
|
+
position: relative;
|
|
343
|
+
overflow: hidden;
|
|
108
344
|
}
|
|
345
|
+
|
|
109
346
|
.btn-primary {
|
|
110
|
-
background: #
|
|
347
|
+
background: linear-gradient(135deg, #00d2ff 0%, #3a7bd5 100%);
|
|
111
348
|
color: #fff;
|
|
349
|
+
box-shadow: 0 4px 12px rgba(0, 210, 255, 0.2);
|
|
112
350
|
}
|
|
113
|
-
|
|
114
|
-
|
|
351
|
+
|
|
352
|
+
.btn-primary:hover:not(:disabled) {
|
|
353
|
+
transform: translateY(-1px);
|
|
354
|
+
box-shadow: 0 6px 16px rgba(0, 210, 255, 0.3);
|
|
355
|
+
background: linear-gradient(135deg, #1ae0ff 0%, #4a8be5 100%);
|
|
115
356
|
}
|
|
357
|
+
|
|
358
|
+
.btn-primary:active:not(:disabled) {
|
|
359
|
+
transform: translateY(1px);
|
|
360
|
+
box-shadow: 0 2px 8px rgba(0, 210, 255, 0.2);
|
|
361
|
+
}
|
|
362
|
+
|
|
116
363
|
.btn-primary:disabled {
|
|
117
|
-
background: #
|
|
364
|
+
background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
|
|
365
|
+
color: #9ca3af;
|
|
366
|
+
box-shadow: none;
|
|
118
367
|
cursor: not-allowed;
|
|
119
368
|
}
|
|
369
|
+
|
|
120
370
|
.btn-secondary {
|
|
121
|
-
background:
|
|
122
|
-
|
|
371
|
+
background: rgba(45, 55, 72, 0.5);
|
|
372
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
373
|
+
color: #d1d5db;
|
|
123
374
|
}
|
|
124
|
-
|
|
125
|
-
|
|
375
|
+
|
|
376
|
+
.btn-secondary:hover:not(:disabled) {
|
|
377
|
+
background: rgba(74, 85, 104, 0.8);
|
|
378
|
+
color: #fff;
|
|
126
379
|
}
|
|
380
|
+
|
|
127
381
|
.scan-info {
|
|
128
|
-
font-size:
|
|
129
|
-
color: #
|
|
382
|
+
font-size: 12px;
|
|
383
|
+
color: #8b949e;
|
|
130
384
|
text-align: center;
|
|
131
|
-
margin-top:
|
|
385
|
+
margin-top: 12px;
|
|
386
|
+
max-height: 40px;
|
|
387
|
+
opacity: 1;
|
|
388
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
389
|
+
overflow: hidden;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.scan-info.collapsed {
|
|
393
|
+
max-height: 0;
|
|
394
|
+
opacity: 0;
|
|
395
|
+
margin-top: 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.hidden {
|
|
399
|
+
display: none !important;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.fade-enter {
|
|
403
|
+
opacity: 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.fade-enter-active {
|
|
407
|
+
opacity: 1;
|
|
408
|
+
transition: opacity 300ms ease-in;
|
|
132
409
|
}
|
|
133
410
|
</style>
|
|
134
411
|
</head>
|
|
135
|
-
<body>
|
|
136
|
-
<div class="
|
|
137
|
-
<
|
|
138
|
-
|
|
412
|
+
<body class="disconnected-state">
|
|
413
|
+
<div class="bg-ripple-container">
|
|
414
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
415
|
+
<!-- Centered on the Ghost Icon (20px padding + 24px icon center = 44px) -->
|
|
416
|
+
<circle class="bg-ripple bg-ripple-1" cx="44" cy="44" r="0" />
|
|
417
|
+
<circle class="bg-ripple bg-ripple-2" cx="44" cy="44" r="0" />
|
|
418
|
+
</svg>
|
|
139
419
|
</div>
|
|
140
420
|
|
|
141
|
-
<div class="
|
|
142
|
-
<div class="
|
|
143
|
-
<div class="
|
|
144
|
-
|
|
421
|
+
<div class="container">
|
|
422
|
+
<div class="header">
|
|
423
|
+
<div id="headerGhost" class="ghost-wrapper ghost-disconnected">
|
|
424
|
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
425
|
+
<!-- Ghost Body -->
|
|
426
|
+
<path class="ghost-body" d="M 25 50 C 25 36.19 36.19 25 50 25 C 63.81 25 75 36.19 75 50 V 75 Q 70 80 65 75 Q 60 70 55 75 Q 50 80 45 75 Q 40 70 35 75 Q 30 80 25 75 Z" />
|
|
427
|
+
|
|
428
|
+
<!-- Eyes Container -->
|
|
429
|
+
<g class="ghost-eyes-normal">
|
|
430
|
+
<!-- Normal Eyes -->
|
|
431
|
+
<circle cx="40" cy="45" r="4" fill="#0b0c10" />
|
|
432
|
+
<circle cx="60" cy="45" r="4" fill="#0b0c10" />
|
|
433
|
+
</g>
|
|
434
|
+
<g class="ghost-eyes-closed">
|
|
435
|
+
<!-- Closed Eyes (Arcs) -->
|
|
436
|
+
<path d="M 36 45 Q 40 48 44 45" stroke="#0b0c10" stroke-width="2.5" fill="none" stroke-linecap="round"/>
|
|
437
|
+
<path d="M 56 45 Q 60 48 64 45" stroke="#0b0c10" stroke-width="2.5" fill="none" stroke-linecap="round"/>
|
|
438
|
+
</g>
|
|
439
|
+
<g class="ghost-eyes-star">
|
|
440
|
+
<!-- Star Eyes -->
|
|
441
|
+
<path d="M 40 41 L 41.5 44 L 45 44.5 L 42 46.5 L 43 50 L 40 48 L 37 50 L 38 46.5 L 35 44.5 L 38.5 44 Z" fill="#0b0c10" />
|
|
442
|
+
<path d="M 60 41 L 61.5 44 L 65 44.5 L 62 46.5 L 63 50 L 60 48 L 57 50 L 58 46.5 L 55 44.5 L 58.5 44 Z" fill="#0b0c10" />
|
|
443
|
+
</g>
|
|
444
|
+
|
|
445
|
+
<!-- Mouth -->
|
|
446
|
+
<path class="ghost-mouth" d="M 46 55 H 54 V 60 H 46 Z" fill="#0b0c10" />
|
|
447
|
+
</svg>
|
|
448
|
+
</div>
|
|
449
|
+
<h1>Ghost Bridge</h1>
|
|
145
450
|
</div>
|
|
146
|
-
<div class="status-detail" id="statusDetail"></div>
|
|
147
|
-
</div>
|
|
148
451
|
|
|
452
|
+
<div class="status-card" id="statusCard">
|
|
453
|
+
<div class="status-row">
|
|
454
|
+
<div class="status-dot-wrapper" id="dotWrapper">
|
|
455
|
+
<div class="status-ping"></div>
|
|
456
|
+
<div class="status-dot"></div>
|
|
457
|
+
</div>
|
|
458
|
+
<span class="status-text" id="statusText">Checking...</span>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<div class="status-detail-container" id="detailContainer">
|
|
462
|
+
<div class="detail-row">
|
|
463
|
+
<span class="detail-label">Service</span>
|
|
464
|
+
<span class="detail-value" id="portVal">-</span>
|
|
465
|
+
</div>
|
|
466
|
+
<div class="detail-row" id="tabRow">
|
|
467
|
+
<span class="detail-label">Target Tab</span>
|
|
468
|
+
<span class="detail-value" id="tabVal" title="">-</span>
|
|
469
|
+
</div>
|
|
470
|
+
<div class="detail-row clickable" id="errorRow" title="Click to view recent errors">
|
|
471
|
+
<span class="detail-label">Rec. Errors</span>
|
|
472
|
+
<span class="detail-value warning" id="errorVal">0</span>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
<div id="errorList" class="error-list collapsed">
|
|
477
|
+
<!-- JS dynamically injects errors here -->
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
149
480
|
|
|
481
|
+
<div class="btn-row">
|
|
482
|
+
<button class="btn btn-primary" id="connectBtn">Connect</button>
|
|
483
|
+
<button class="btn btn-secondary" id="disconnectBtn">Stop</button>
|
|
484
|
+
</div>
|
|
150
485
|
|
|
151
|
-
|
|
152
|
-
<button class="btn btn-primary" id="connectBtn">连接</button>
|
|
153
|
-
<button class="btn btn-secondary" id="disconnectBtn">断开</button>
|
|
486
|
+
<div class="scan-info" id="scanInfo"></div>
|
|
154
487
|
</div>
|
|
155
488
|
|
|
156
|
-
<div class="scan-info" id="scanInfo"></div>
|
|
157
|
-
|
|
158
489
|
<script src="popup.js"></script>
|
|
159
490
|
</body>
|
|
160
491
|
</html>
|