vatts 2.2.6 → 2.3.0-canary.1

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.
Files changed (70) hide show
  1. package/LICENSE +12 -12
  2. package/README.md +65 -65
  3. package/dist/builder.js +79 -42
  4. package/dist/frameworks/FrontCore.d.ts +34 -0
  5. package/dist/frameworks/FrontCore.js +128 -0
  6. package/dist/{vue → frameworks/builds}/vue.build.js +1 -1
  7. package/dist/frameworks/react/client.d.ts +8 -0
  8. package/dist/{react → frameworks/react}/client.js +14 -14
  9. package/dist/{react → frameworks/react/components}/Link.js +2 -2
  10. package/dist/frameworks/react/entry.client.d.ts +14 -0
  11. package/dist/frameworks/react/entry.client.js +211 -0
  12. package/dist/frameworks/react/react-elements.d.ts +10 -0
  13. package/dist/{react → frameworks/renderers}/renderer-react.d.ts +2 -2
  14. package/dist/{react → frameworks/renderers}/renderer-react.js +53 -86
  15. package/dist/{vue → frameworks/renderers}/renderer.vue.d.ts +2 -2
  16. package/dist/frameworks/renderers/renderer.vue.js +193 -0
  17. package/dist/frameworks/themes/BuildingPage.d.ts +1 -0
  18. package/dist/frameworks/themes/BuildingPage.js +312 -0
  19. package/dist/frameworks/themes/DefaultNotFound.d.ts +1 -0
  20. package/dist/frameworks/themes/DefaultNotFound.js +330 -0
  21. package/dist/frameworks/themes/ErrorModal.d.ts +1 -0
  22. package/dist/frameworks/themes/ErrorModal.js +345 -0
  23. package/dist/frameworks/themes/ServerError.d.ts +1 -0
  24. package/dist/frameworks/themes/ServerError.js +401 -0
  25. package/dist/frameworks/themes/VattsDevBadge.d.ts +1 -0
  26. package/dist/frameworks/themes/VattsDevBadge.js +232 -0
  27. package/dist/frameworks/vue/App.vue +149 -0
  28. package/dist/frameworks/vue/client.d.ts +9 -0
  29. package/dist/{vue → frameworks/vue}/client.js +13 -11
  30. package/dist/{vue → frameworks/vue/components}/Link.vue +38 -38
  31. package/dist/{vue → frameworks/vue/components}/image/Image.vue +128 -128
  32. package/dist/frameworks/vue/entry.client.js +75 -0
  33. package/dist/global/global.d.ts +179 -180
  34. package/dist/hotReload.js +77 -77
  35. package/dist/index.js +12 -1
  36. package/dist/loaders.js +15 -15
  37. package/dist/renderer.js +2 -2
  38. package/dist/renderers/common.js +3 -3
  39. package/dist/utils/core-go.js +2 -2
  40. package/dist/utils/utils.js +5 -5
  41. package/package.json +10 -26
  42. package/dist/react/BuildingPage.d.ts +0 -2
  43. package/dist/react/BuildingPage.js +0 -270
  44. package/dist/react/DefaultNotFound.d.ts +0 -2
  45. package/dist/react/DefaultNotFound.js +0 -248
  46. package/dist/react/DevIndicator.d.ts +0 -5
  47. package/dist/react/DevIndicator.js +0 -203
  48. package/dist/react/ErrorModal.d.ts +0 -20
  49. package/dist/react/ErrorModal.js +0 -266
  50. package/dist/react/client.d.ts +0 -8
  51. package/dist/react/entry.client.d.ts +0 -6
  52. package/dist/react/entry.client.js +0 -325
  53. package/dist/react/server-error.d.ts +0 -8
  54. package/dist/react/server-error.js +0 -346
  55. package/dist/vue/App.vue +0 -199
  56. package/dist/vue/BuildingPage.vue +0 -281
  57. package/dist/vue/DefaultNotFound.vue +0 -329
  58. package/dist/vue/DevIndicator.vue +0 -226
  59. package/dist/vue/ErrorModal.vue +0 -317
  60. package/dist/vue/client.d.ts +0 -9
  61. package/dist/vue/entry.client.js +0 -110
  62. package/dist/vue/renderer.vue.js +0 -387
  63. package/dist/vue/server-error.vue +0 -351
  64. /package/dist/{react → frameworks/builds}/react.build.d.ts +0 -0
  65. /package/dist/{react → frameworks/builds}/react.build.js +0 -0
  66. /package/dist/{vue → frameworks/builds}/vue.build.d.ts +0 -0
  67. /package/dist/{react → frameworks/react/components}/Link.d.ts +0 -0
  68. /package/dist/{react → frameworks/react/components}/image/Image.d.ts +0 -0
  69. /package/dist/{react → frameworks/react/components}/image/Image.js +0 -0
  70. /package/dist/{vue → frameworks/vue}/entry.client.d.ts +0 -0
@@ -0,0 +1,401 @@
1
+ "use strict";
2
+ /*
3
+ * This file is part of the Vatts.js Project.
4
+ * Copyright (c) 2026 mfraz
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ function formatUnknownError(error) {
19
+ if (!error)
20
+ return { message: 'Erro desconhecido no SSR.' };
21
+ if (error instanceof Error) {
22
+ return { message: error.message || String(error), stack: error.stack };
23
+ }
24
+ if (typeof error === 'string') {
25
+ return { message: error };
26
+ }
27
+ try {
28
+ return { message: JSON.stringify(error, null, 2) };
29
+ }
30
+ catch {
31
+ return { message: String(error) };
32
+ }
33
+ }
34
+ function escapeHtml(unsafe) {
35
+ if (!unsafe)
36
+ return '';
37
+ return unsafe.toString()
38
+ .replace(/&/g, "&")
39
+ .replace(/</g, "&lt;")
40
+ .replace(/>/g, "&gt;")
41
+ .replace(/"/g, "&quot;")
42
+ .replace(/'/g, "&#039;");
43
+ }
44
+ function getServerErrorHtml(options = {}) {
45
+ const title = options.title || 'SSR Error';
46
+ const { message, stack } = formatUnknownError(options.error);
47
+ const hint = options.hint;
48
+ const requestUrl = options.requestUrl;
49
+ const safeTitle = escapeHtml(title);
50
+ const safeMessage = escapeHtml(message);
51
+ const safeStack = escapeHtml(stack);
52
+ const safeHint = escapeHtml(hint);
53
+ const safeUrl = escapeHtml(requestUrl);
54
+ return `
55
+ <!DOCTYPE html>
56
+ <html lang="en">
57
+ <head>
58
+ <meta charset="UTF-8" />
59
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
60
+ <title>Vatts.js | ${safeTitle}</title>
61
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
62
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
63
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
64
+ <style>
65
+ :root {
66
+ --bg-base-start: #050507;
67
+ --bg-base-end: #09090b;
68
+
69
+ --bg-card: #0a0a0c;
70
+ --bg-terminal: #050505;
71
+
72
+ --text-main: #ffffff;
73
+ --text-dim: #71717a;
74
+ --color-accent: #0ea5e9;
75
+ --error-red: #ef4444;
76
+ }
77
+
78
+ body {
79
+ margin: 0;
80
+ padding: 0;
81
+ padding: 10px;
82
+ background-color: var(--bg-base-start);
83
+ background-image: linear-gradient(135deg, var(--bg-base-start), var(--bg-base-end));
84
+ font-family: 'Inter', system-ui, sans-serif;
85
+ color: var(--text-main);
86
+ box-sizing: border-box;
87
+ display: flex;
88
+ justify-content: center;
89
+ align-items: center;
90
+ }
91
+
92
+ .container {
93
+ display: flex;
94
+ flex-direction: column;
95
+ align-items: center;
96
+ width: 100%;
97
+ z-index: 2;
98
+ padding: 24px;
99
+ box-sizing: border-box;
100
+ }
101
+
102
+ /* BORDA FAKE DE 3PX COM GRADIENTE ESCURO */
103
+ .card-wrapper {
104
+ width: 100%;
105
+ max-width: 1024px; /* Bem largo pra caber as logs do SSR */
106
+ max-height: 90vh;
107
+ padding: 3px;
108
+ border-radius: 16px;
109
+ background: linear-gradient(135deg, rgb(24, 24, 27), rgb(39, 39, 42), transparent);
110
+ box-sizing: border-box;
111
+ display: flex;
112
+ flex-direction: column;
113
+ }
114
+
115
+ /* CARD SEM BORDAS REAIS E SEM SHADOWS */
116
+ .card {
117
+ background-color: var(--bg-card);
118
+ border-radius: 13px;
119
+ padding: 32px 40px;
120
+ width: 100%;
121
+ display: flex;
122
+ flex-direction: column;
123
+ box-sizing: border-box;
124
+ border: none;
125
+ box-shadow: none;
126
+ flex: 1;
127
+ min-height: 0; /* Essencial pra deixar o terminal scrollar */
128
+ }
129
+
130
+ .header-section {
131
+ margin-bottom: 24px;
132
+ display: flex;
133
+ flex-direction: column;
134
+ gap: 16px;
135
+ }
136
+
137
+ .title-row {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 16px;
141
+ }
142
+
143
+ .badge-error {
144
+ font-size: 11px;
145
+ font-weight: 900;
146
+ color: #ffffff;
147
+ background: var(--error-red);
148
+ padding: 4px 10px;
149
+ border-radius: 6px;
150
+ letter-spacing: 0.05em;
151
+ text-transform: uppercase;
152
+ }
153
+
154
+ h1 {
155
+ margin: 0;
156
+ font-size: 1.8rem;
157
+ font-weight: 800;
158
+ letter-spacing: -0.04em;
159
+ background: linear-gradient(to right, #fff, var(--text-dim));
160
+ -webkit-background-clip: text;
161
+ -webkit-text-fill-color: transparent;
162
+ }
163
+
164
+ .meta-info {
165
+ display: flex;
166
+ flex-direction: column;
167
+ gap: 8px;
168
+ padding: 16px;
169
+ background: rgba(255, 255, 255, 0.02);
170
+ border-radius: 8px;
171
+ }
172
+
173
+ .meta-row {
174
+ display: flex;
175
+ align-items: baseline;
176
+ gap: 12px;
177
+ font-size: 0.85rem;
178
+ }
179
+
180
+ .meta-label {
181
+ color: var(--text-dim);
182
+ font-weight: 700;
183
+ font-size: 0.75rem;
184
+ letter-spacing: 0.05em;
185
+ min-width: 80px;
186
+ }
187
+
188
+ .meta-value {
189
+ color: var(--color-accent);
190
+ font-family: 'JetBrains Mono', monospace;
191
+ word-break: break-all;
192
+ }
193
+
194
+ .terminal-container {
195
+ display: flex;
196
+ flex-direction: column;
197
+ flex: 1;
198
+ min-height: 0;
199
+ background: var(--bg-terminal);
200
+ border-radius: 8px;
201
+ padding: 24px;
202
+ }
203
+
204
+ .terminal-header {
205
+ display: flex;
206
+ align-items: center;
207
+ gap: 12px;
208
+ margin-bottom: 16px;
209
+ padding-bottom: 16px;
210
+ border-bottom: 1px dashed rgba(255, 255, 255, 0.05);
211
+ }
212
+
213
+ .terminal-dots {
214
+ display: flex;
215
+ gap: 6px;
216
+ opacity: 0.5;
217
+ }
218
+
219
+ .dot {
220
+ width: 8px;
221
+ height: 8px;
222
+ border-radius: 50%;
223
+ background: var(--text-dim);
224
+ }
225
+
226
+ .terminal-title {
227
+ font-size: 0.75rem;
228
+ color: var(--text-dim);
229
+ text-transform: uppercase;
230
+ letter-spacing: 0.05em;
231
+ font-weight: 600;
232
+ }
233
+
234
+ .terminal-body {
235
+ overflow-y: auto;
236
+ color: #f4f4f5;
237
+ font-family: 'JetBrains Mono', monospace;
238
+ font-size: 0.85rem;
239
+ line-height: 1.6;
240
+ padding-right: 12px;
241
+ }
242
+
243
+ .log-entry {
244
+ color: #ef4444;
245
+ font-weight: 600;
246
+ display: flex;
247
+ gap: 10px;
248
+ white-space: pre-wrap;
249
+ word-break: break-word;
250
+ }
251
+
252
+ .arrow {
253
+ color: var(--text-dim);
254
+ user-select: none;
255
+ }
256
+
257
+ .stack-trace {
258
+ color: #a1a1aa;
259
+ white-space: pre-wrap;
260
+ word-break: break-all;
261
+ margin-top: 16px;
262
+ padding-top: 16px;
263
+ border-top: 1px dashed rgba(255, 255, 255, 0.05);
264
+ }
265
+
266
+ /* Custom Scrollbar pro Terminal SSR */
267
+ .terminal-body::-webkit-scrollbar {
268
+ width: 10px;
269
+ }
270
+ .terminal-body::-webkit-scrollbar-track {
271
+ background: transparent;
272
+ }
273
+ .terminal-body::-webkit-scrollbar-thumb {
274
+ background: #27272a;
275
+ border-radius: 10px;
276
+ border: 3px solid var(--bg-terminal);
277
+ }
278
+ .terminal-body::-webkit-scrollbar-thumb:hover {
279
+ background: #3f3f46;
280
+ }
281
+
282
+ .footer {
283
+ margin-top: 24px;
284
+ display: flex;
285
+ justify-content: space-between;
286
+ align-items: center;
287
+ font-size: 0.75rem;
288
+ color: var(--text-dim);
289
+ font-weight: 500;
290
+ width: 100%;
291
+ border-top: 1px solid rgba(255, 255, 255, 0.02);
292
+ padding-top: 16px;
293
+ }
294
+
295
+ .status {
296
+ display: flex;
297
+ align-items: center;
298
+ gap: 6px;
299
+ }
300
+
301
+ .status-dot {
302
+ width: 6px;
303
+ height: 6px;
304
+ background-color: var(--error-red);
305
+ border-radius: 50%;
306
+ animation: pulse-dot 1.5s infinite;
307
+ }
308
+
309
+ @keyframes pulse-dot {
310
+ 0%, 100% { opacity: 0.3; }
311
+ 50% { opacity: 1; box-shadow: 0 0 8px rgba(239, 68, 68, 0.4); }
312
+ }
313
+
314
+ .brand-link {
315
+ margin-top: 32px;
316
+ display: flex;
317
+ align-items: center;
318
+ gap: 10px;
319
+ opacity: 0.5;
320
+ transition: opacity 0.3s ease;
321
+ text-decoration: none;
322
+ color: var(--text-main);
323
+ }
324
+
325
+ .brand-link:hover {
326
+ opacity: 1;
327
+ }
328
+ </style>
329
+ </head>
330
+ <body>
331
+ <div class="container">
332
+ <div class="card-wrapper">
333
+ <div class="card">
334
+
335
+ <div class="header-section">
336
+ <div class="title-row">
337
+ <span class="badge-error">SSR FAILED</span>
338
+ <h1>${safeTitle}</h1>
339
+ </div>
340
+
341
+ ${(safeUrl || safeHint) ? `
342
+ <div class="meta-info">
343
+ ${safeUrl ? `
344
+ <div class="meta-row">
345
+ <span class="meta-label">ROUTE</span>
346
+ <span class="meta-value">${safeUrl}</span>
347
+ </div>
348
+ ` : ''}
349
+ ${safeHint ? `
350
+ <div class="meta-row">
351
+ <span class="meta-label">HINT</span>
352
+ <span class="meta-value" style="color: #f4f4f5;">${safeHint}</span>
353
+ </div>
354
+ ` : ''}
355
+ </div>
356
+ ` : ''}
357
+ </div>
358
+
359
+ <div class="terminal-container">
360
+ <div class="terminal-header">
361
+ <div class="terminal-dots">
362
+ <div class="dot"></div>
363
+ <div class="dot"></div>
364
+ <div class="dot"></div>
365
+ </div>
366
+ <span class="terminal-title">Server Exception Trace</span>
367
+ </div>
368
+
369
+ <div class="terminal-body">
370
+ <div class="log-entry">
371
+ <span class="arrow">></span>
372
+ <span>${safeMessage}</span>
373
+ </div>
374
+
375
+ ${safeStack ? `
376
+ <div class="stack-trace">${safeStack}</div>
377
+ ` : ''}
378
+ </div>
379
+ </div>
380
+
381
+ <div class="footer">
382
+ <span>Vatts.js</span>
383
+ <div class="status">
384
+ <div class="status-dot"></div>
385
+ <span style="color: var(--text-main);">Render Offline</span>
386
+ </div>
387
+ </div>
388
+
389
+ </div>
390
+ </div>
391
+
392
+ <a href="https://npmjs.com/package/vatts" target="_blank" rel="noopener noreferrer" class="brand-link">
393
+ <img src="https://raw.githubusercontent.com/murillo-frazao-cunha/vatts-docs/master/public/logo.png" alt="Vatts Logo" style="width: 20px; height: 20px;" />
394
+ <span style="font-size: 13px; font-weight: 600; letter-spacing: 0.02em;">Vatts.js</span>
395
+ </a>
396
+ </div>
397
+ </body>
398
+ </html>
399
+ `;
400
+ }
401
+ module.exports = { getServerErrorHtml };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /*
3
+ * This file is part of the Vatts.js Project.
4
+ * Copyright (c) 2026 mfraz
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ class VattsDevBadge extends HTMLElement {
20
+ constructor() {
21
+ super();
22
+ this.isVisible = true;
23
+ this.hotState = 'idle';
24
+ this.hasBuildError = false;
25
+ // Bind dos métodos
26
+ this.handleHotReload = this.handleHotReload.bind(this);
27
+ this.closeBadge = this.closeBadge.bind(this);
28
+ this.handleBadgeClick = this.handleBadgeClick.bind(this);
29
+ }
30
+ static get observedAttributes() {
31
+ return ['has-build-error'];
32
+ }
33
+ attributeChangedCallback(name, oldValue, newValue) {
34
+ if (name === 'has-build-error') {
35
+ this.hasBuildError = newValue === 'true';
36
+ this.render();
37
+ }
38
+ }
39
+ connectedCallback() {
40
+ if (typeof window !== 'undefined') {
41
+ window.addEventListener('vatts:hotreload', this.handleHotReload);
42
+ }
43
+ this.render();
44
+ }
45
+ disconnectedCallback() {
46
+ if (typeof window !== 'undefined') {
47
+ window.removeEventListener('vatts:hotreload', this.handleHotReload);
48
+ }
49
+ }
50
+ handleHotReload(ev) {
51
+ const detail = ev?.detail;
52
+ if (!detail || !detail.state)
53
+ return;
54
+ if (detail.state === 'reloading' || detail.state === 'full-reload') {
55
+ this.hotState = 'reloading';
56
+ }
57
+ else if (detail.state === 'idle') {
58
+ this.hotState = 'idle';
59
+ }
60
+ this.render();
61
+ }
62
+ closeBadge(e) {
63
+ e.stopPropagation();
64
+ this.isVisible = false;
65
+ this.render();
66
+ }
67
+ handleBadgeClick() {
68
+ if (this.hasBuildError) {
69
+ this.dispatchEvent(new CustomEvent('click-build-error', { bubbles: true, composed: true }));
70
+ }
71
+ }
72
+ render() {
73
+ if (!this.isVisible) {
74
+ this.innerHTML = '';
75
+ return;
76
+ }
77
+ const isReloading = this.hotState === 'reloading';
78
+ const isError = this.hasBuildError;
79
+ this.innerHTML = `
80
+ <style>
81
+ @keyframes vatts-pulse {
82
+ 0% { opacity: 0.3; transform: scale(0.95); }
83
+ 50% { opacity: 1; transform: scale(1.05); }
84
+ 100% { opacity: 0.3; transform: scale(0.95); }
85
+ }
86
+
87
+ @keyframes vatts-spin {
88
+ 0% { transform: rotate(0deg); }
89
+ 100% { transform: rotate(360deg); }
90
+ }
91
+
92
+ /* WRAPPER DA BORDA FAKE COM PADDING DE 3PX E GRADIENTE */
93
+ .vatts-badge-wrapper {
94
+ position: fixed;
95
+ bottom: 24px;
96
+ right: 24px;
97
+ z-index: 2147483647;
98
+
99
+ padding: 2px; /* Padding mágico para a borda */
100
+ border-radius: 9999px;
101
+ /* O gradiente exato to-br com zinc-900, zinc-800 e transparent */
102
+ background: linear-gradient(135deg, rgb(24, 24, 27), rgb(39, 39, 42), transparent);
103
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
104
+ }
105
+
106
+ .vatts-badge-wrapper:hover {
107
+ transform: translateY(-3px) scale(1.02);
108
+ /* Acende levemente o gradiente de fundo no hover */
109
+ background: linear-gradient(135deg, rgb(39, 39, 42), rgb(63, 63, 70), transparent);
110
+ }
111
+
112
+ .vatts-dev-badge {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 12px;
116
+ padding: 8px 16px 8px 14px;
117
+ background: #0a0a0c; /* Fundo do card interno */
118
+ border-radius: 9999px;
119
+ font-family: 'Inter', system-ui, sans-serif;
120
+ font-size: 12px;
121
+ letter-spacing: 0.04em;
122
+ border: none;
123
+ box-shadow: none;
124
+ cursor: default;
125
+ user-select: none;
126
+ }
127
+
128
+ .vatts-dev-badge.clickable {
129
+ cursor: pointer;
130
+ }
131
+
132
+ .vatts-status-dot {
133
+ width: 8px;
134
+ height: 8px;
135
+ background: #0ea5e9;
136
+ border-radius: 50%;
137
+ border: none;
138
+ animation: vatts-pulse 2s infinite ease-in-out;
139
+ }
140
+
141
+ .vatts-status-dot.reloading {
142
+ background: #71717a;
143
+ animation: none;
144
+ }
145
+
146
+ .vatts-status-dot.error {
147
+ background: #ef4444;
148
+ animation: vatts-pulse 1s infinite ease-in-out;
149
+ }
150
+
151
+ .vatts-spinner {
152
+ width: 12px;
153
+ height: 12px;
154
+ border-radius: 50%;
155
+ border: 2px solid rgba(14, 165, 233, 0.15);
156
+ border-top-color: #0ea5e9;
157
+ animation: vatts-spin 0.7s linear infinite;
158
+ }
159
+
160
+ .vatts-logo {
161
+ font-weight: 900;
162
+ display: flex;
163
+ align-items: center;
164
+ background: linear-gradient(to right, #6366f1, #0ea5e9);
165
+ -webkit-background-clip: text;
166
+ -webkit-text-fill-color: transparent;
167
+ }
168
+
169
+ .vatts-logo span.suffix {
170
+ color: #71717a;
171
+ -webkit-text-fill-color: #71717a;
172
+ font-weight: 600;
173
+ }
174
+
175
+ .vatts-error-pill {
176
+ margin-left: 10px;
177
+ padding: 2px 8px;
178
+ border-radius: 6px;
179
+ background: #ef4444;
180
+ color: #ffffff;
181
+ -webkit-text-fill-color: #ffffff;
182
+ font-size: 10px;
183
+ font-weight: 800;
184
+ letter-spacing: 0.1em;
185
+ }
186
+
187
+ .close-btn {
188
+ background: none;
189
+ border: none;
190
+ color: #52525b;
191
+ cursor: pointer;
192
+ font-size: 16px;
193
+ font-weight: 400;
194
+ padding: 0;
195
+ margin-left: 6px;
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: center;
199
+ transition: color 0.2s ease;
200
+ }
201
+
202
+ .close-btn:hover {
203
+ color: #ffffff;
204
+ }
205
+ </style>
206
+
207
+ <div class="vatts-badge-wrapper">
208
+ <div class="vatts-dev-badge ${isError ? 'clickable' : ''}" id="vatts-badge-container">
209
+ ${isReloading ? '<div class="vatts-spinner"></div>' : `<div class="vatts-status-dot ${isError ? 'error' : ''}"></div>`}
210
+
211
+ <div class="vatts-logo">
212
+ VATTS<span class="suffix">.JS</span>
213
+ ${isError ? '<span class="vatts-error-pill">ERROR</span>' : ''}
214
+ </div>
215
+
216
+ <button class="close-btn" id="vatts-badge-close" aria-label="Close">×</button>
217
+ </div>
218
+ </div>
219
+ `;
220
+ const container = this.querySelector('#vatts-badge-container');
221
+ const closeBtn = this.querySelector('#vatts-badge-close');
222
+ if (container) {
223
+ container.addEventListener('click', this.handleBadgeClick);
224
+ }
225
+ if (closeBtn) {
226
+ closeBtn.addEventListener('click', this.closeBadge);
227
+ }
228
+ }
229
+ }
230
+ if (typeof window !== 'undefined' && !customElements.get('vatts-dev-badge')) {
231
+ customElements.define('vatts-dev-badge', VattsDevBadge);
232
+ }