vatts 1.1.0 → 1.1.2

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.
@@ -3,147 +3,145 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = DevIndicator;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const react_dom_1 = require("react-dom");
6
7
  function DevIndicator({ hasBuildError = false, onClickBuildError, }) {
7
8
  const [isVisible, setIsVisible] = (0, react_1.useState)(true);
8
9
  const [hotState, setHotState] = (0, react_1.useState)('idle');
10
+ const [mounted, setMounted] = (0, react_1.useState)(false);
9
11
  (0, react_1.useEffect)(() => {
12
+ setMounted(true);
10
13
  const handler = (ev) => {
11
14
  const detail = ev?.detail;
12
15
  if (!detail || !detail.state)
13
16
  return;
14
- if (detail.state === 'reloading') {
17
+ if (detail.state === 'reloading' || detail.state === 'full-reload') {
15
18
  setHotState('reloading');
16
19
  }
17
20
  if (detail.state === 'idle') {
18
21
  setHotState('idle');
19
22
  }
20
- // Em casos de full reload, pode ficar como reloading até recarregar a página
21
- if (detail.state === 'full-reload') {
22
- setHotState('reloading');
23
- }
24
23
  };
25
24
  window.addEventListener('vatts:hotreload', handler);
26
25
  return () => window.removeEventListener('vatts:hotreload', handler);
27
26
  }, []);
28
- if (!isVisible)
27
+ if (!isVisible || !mounted)
29
28
  return null;
30
29
  const isReloading = hotState === 'reloading';
31
30
  const isError = !!hasBuildError;
32
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { children: `
33
- @keyframes vatts-pulse {
34
- 0% { opacity: 0.4; }
35
- 50% { opacity: 1; }
36
- 100% { opacity: 0.4; }
37
- }
31
+ // Criamos o elemento via Portal para injetar no final do <body>
32
+ return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { children: `
33
+ @keyframes vatts-pulse {
34
+ 0% { opacity: 0.4; }
35
+ 50% { opacity: 1; }
36
+ 100% { opacity: 0.4; }
37
+ }
38
+
39
+ @keyframes vatts-spin {
40
+ 0% { transform: rotate(0deg); }
41
+ 100% { transform: rotate(360deg); }
42
+ }
38
43
 
39
- @keyframes vatts-spin {
40
- 0% { transform: rotate(0deg); }
41
- 100% { transform: rotate(360deg); }
42
- }
44
+ .vatts-dev-badge {
45
+ position: fixed;
46
+ bottom: 20px;
47
+ right: 20px;
48
+ /* Z-index absurdo para garantir que fique acima de tudo */
49
+ z-index: 2147483647;
50
+
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 12px;
54
+ padding: 8px 14px;
55
+ background: rgba(0, 0, 0, 0.85);
56
+ backdrop-filter: blur(12px);
57
+ -webkit-backdrop-filter: blur(12px);
43
58
 
44
- .vatts-dev-badge {
45
- position: fixed;
46
- bottom: 20px;
47
- right: 20px;
48
- z-index: 999999;
49
-
50
- display: flex;
51
- align-items: center;
52
- gap: 12px;
53
- padding: 8px 14px;
54
- background: rgba(15, 15, 20, 0.8);
55
- backdrop-filter: blur(12px);
56
- -webkit-backdrop-filter: blur(12px);
59
+ border-radius: 10px;
60
+ color: #fff;
61
+ font-family: 'Inter', system-ui, sans-serif;
62
+ font-size: 11px;
63
+ font-weight: 600;
64
+ letter-spacing: 0.05em;
57
65
 
58
- border-radius: 10px;
59
- color: #fff;
60
- font-family: 'Inter', ui-monospace, SFMono-Regular, Menlo, monospace;
61
- font-size: 12px;
62
- font-weight: 600;
63
- letter-spacing: 0.05em;
66
+ /* Estilo Monocromático Next.js */
67
+ border: 1px solid rgba(255, 255, 255, 0.1);
68
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
69
+ transition: all 0.2s ease;
70
+ cursor: default;
71
+ user-select: none;
72
+ }
64
73
 
65
- border: 1px solid rgba(255, 107, 53, 0.1);
66
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
67
- transition: all 0.2s ease;
68
- cursor: default;
69
- user-select: none;
70
- }
74
+ .vatts-dev-badge.clickable {
75
+ cursor: pointer;
76
+ }
71
77
 
72
- .vatts-dev-badge.clickable {
73
- cursor: pointer;
74
- }
78
+ .vatts-dev-badge:hover {
79
+ border-color: rgba(255, 255, 255, 0.3);
80
+ transform: translateY(-2px);
81
+ background: rgba(10, 10, 10, 0.95);
82
+ }
75
83
 
76
- .vatts-dev-badge:hover {
77
- /* COR ALTERADA: Hover com brilho laranja */
78
- border-color: rgba(255, 107, 53, 0.5);
79
- transform: translateY(-2px);
80
- box-shadow: 0 10px 40px rgba(255, 107, 53, 0.15);
81
- }
84
+ .vatts-status-dot {
85
+ width: 7px;
86
+ height: 7px;
87
+ background: #ffffff; /* Branco para status OK (Estilo Vercel) */
88
+ border-radius: 50%;
89
+ box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
90
+ animation: vatts-pulse 2.5s infinite ease-in-out;
91
+ }
82
92
 
83
- /* Mantemos as cores semânticas (Verde = OK, Vermelho = Erro) pois são padrões de status */
84
- .vatts-status-dot {
85
- width: 8px;
86
- height: 8px;
87
- background: #10b981; /* Verde esmeralda */
88
- border-radius: 50%;
89
- box-shadow: 0 0 10px #10b981;
90
- animation: vatts-pulse 2s infinite ease-in-out;
91
- }
93
+ .vatts-status-dot.reloading {
94
+ background: #64748b; /* Slate */
95
+ box-shadow: none;
96
+ }
92
97
 
93
- .vatts-status-dot.reloading {
94
- background: #f59e0b; /* Amber */
95
- box-shadow: 0 0 10px #f59e0b;
96
- }
98
+ .vatts-status-dot.error {
99
+ background: #ef4444; /* Mantido vermelho por semântica de erro */
100
+ box-shadow: 0 0 10px #ef4444;
101
+ animation: vatts-pulse 1s infinite ease-in-out;
102
+ }
97
103
 
98
- .vatts-status-dot.error {
99
- background: #ef4444; /* Red */
100
- box-shadow: 0 0 12px #ef4444;
101
- animation: vatts-pulse 1.2s infinite ease-in-out;
102
- }
104
+ .vatts-spinner {
105
+ width: 10px;
106
+ height: 10px;
107
+ border-radius: 50%;
108
+ border: 2px solid rgba(255,255,255,0.1);
109
+ border-top-color: #ffffff;
110
+ animation: vatts-spin 0.8s linear infinite;
111
+ }
103
112
 
104
- .vatts-spinner {
105
- width: 10px;
106
- height: 10px;
107
- border-radius: 50%;
108
- border: 2px solid rgba(255,255,255,0.25);
109
- border-top-color: rgba(255,255,255,0.85);
110
- animation: vatts-spin 0.8s linear infinite;
111
- }
113
+ .vatts-logo {
114
+ color: #ffffff;
115
+ font-weight: 800;
116
+ display: flex;
117
+ align-items: center;
118
+ }
112
119
 
113
- .vatts-logo {
114
- /* COR ALTERADA: Gradiente da Logo para Vermelho/Laranja */
115
- background: linear-gradient(135deg, #ff6b35, #e85d04);
116
- -webkit-background-clip: text;
117
- -webkit-text-fill-color: transparent;
118
- font-weight: 800;
119
- }
120
+ .vatts-logo span {
121
+ color: #64748b;
122
+ }
120
123
 
121
- .vatts-error-pill {
122
- margin-left: 6px;
123
- padding: 2px 6px;
124
- border-radius: 999px;
125
- background: rgba(239, 68, 68, 0.18);
126
- border: 1px solid rgba(239, 68, 68, 0.35);
127
- color: rgba(255,255,255,0.9);
128
- font-size: 10px;
129
- font-weight: 800;
130
- letter-spacing: 0.08em;
131
- text-transform: uppercase;
132
- }
133
- ` }), (0, jsx_runtime_1.jsxs)("div", { className: `vatts-dev-badge${isError ? ' clickable' : ''}`, title: isError ? 'Build com erro — clique para ver detalhes' : undefined, onClick: () => {
134
- if (isError) {
135
- onClickBuildError?.();
136
- }
137
- }, children: [isReloading ? ((0, jsx_runtime_1.jsx)("div", { className: "vatts-spinner" })) : ((0, jsx_runtime_1.jsx)("div", { className: `vatts-status-dot${isReloading ? ' reloading' : ''}${isError ? ' error' : ''}` })), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "vatts-logo", children: "VATTS.JS" }), isError ? (0, jsx_runtime_1.jsx)("span", { className: "vatts-error-pill", children: "ERROR" }) : null] }), (0, jsx_runtime_1.jsx)("button", { onClick: (e) => {
124
+ .vatts-error-pill {
125
+ margin-left: 8px;
126
+ padding: 2px 6px;
127
+ border-radius: 4px;
128
+ background: #ffffff;
129
+ color: #000000;
130
+ font-size: 9px;
131
+ font-weight: 900;
132
+ }
133
+ ` }), (0, jsx_runtime_1.jsxs)("div", { className: `vatts-dev-badge${isError ? ' clickable' : ''}`, onClick: () => isError && onClickBuildError?.(), children: [isReloading ? ((0, jsx_runtime_1.jsx)("div", { className: "vatts-spinner" })) : ((0, jsx_runtime_1.jsx)("div", { className: `vatts-status-dot${isReloading ? ' reloading' : ''}${isError ? ' error' : ''}` })), (0, jsx_runtime_1.jsxs)("div", { className: "vatts-logo", children: ["VATTS", (0, jsx_runtime_1.jsx)("span", { children: ".JS" }), isError && (0, jsx_runtime_1.jsx)("span", { className: "vatts-error-pill", children: "ERROR" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: (e) => {
138
134
  e.stopPropagation();
139
135
  setIsVisible(false);
140
136
  }, style: {
141
137
  background: 'none',
142
138
  border: 'none',
143
- color: 'rgba(255,255,255,0.3)',
139
+ color: 'rgba(255,255,255,0.2)',
144
140
  cursor: 'pointer',
145
- fontSize: '14px',
141
+ fontSize: '16px',
146
142
  padding: '0 0 0 4px',
147
- marginLeft: '4px'
148
- }, title: "Fechar", children: "\u00D7" })] })] }));
143
+ marginLeft: '4px',
144
+ display: 'flex',
145
+ alignItems: 'center'
146
+ }, children: "\u00D7" })] })] }), document.body);
149
147
  }
@@ -20,16 +20,16 @@ const jsx_runtime_1 = require("react/jsx-runtime");
20
20
  */
21
21
  const react_1 = require("react");
22
22
  const react_dom_1 = require("react-dom");
23
- // --- ANSI PARSER LOGIC ---
23
+ // --- ANSI PARSER LOGIC (Monochrome Adjusted) ---
24
24
  const ANSI_COLORS = {
25
- '30': '#94a3b8',
26
- '31': '#f87171',
27
- '32': '#4ade80',
28
- '33': '#facc15',
29
- '34': '#60a5fa',
30
- '35': '#c084fc',
31
- '36': '#ff6b35', // Alterado de Cyan para Laranja
32
- '37': '#e2e8f0',
25
+ '30': '#475569',
26
+ '31': '#ef4444', // Mantido um vermelho sutil para erros no log
27
+ '32': '#ffffff', // Sucesso vira Branco
28
+ '33': '#94a3b8', // Warning vira Cinza
29
+ '34': '#cbd5e1',
30
+ '35': '#e2e8f0',
31
+ '36': '#ffffff', // Accent vira Branco
32
+ '37': '#ffffff',
33
33
  '90': '#64748b',
34
34
  };
35
35
  function AnsiText({ text }) {
@@ -67,10 +67,10 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
67
67
  const [isHoveringClose, setIsHoveringClose] = (0, react_1.useState)(false);
68
68
  const [isHoveringCopy, setIsHoveringCopy] = (0, react_1.useState)(false);
69
69
  const [mounted, setMounted] = (0, react_1.useState)(false);
70
- // Definição das novas cores
71
- const primaryColor = '#ff6b35';
72
- const primaryColorDark = '#e85d04';
73
- const primaryRgb = '255, 107, 53';
70
+ // Paleta Next.js
71
+ const primaryColor = '#ffffff';
72
+ const primaryRgb = '255, 255, 255';
73
+ const borderColor = 'rgba(255, 255, 255, 0.1)';
74
74
  (0, react_1.useEffect)(() => {
75
75
  setMounted(true);
76
76
  return () => setMounted(false);
@@ -108,14 +108,14 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
108
108
  width: '100vw',
109
109
  height: '100vh',
110
110
  zIndex: 2147483647,
111
- background: visible ? 'rgba(5, 5, 5, 0.98)' : 'rgba(5, 5, 5, 0)',
112
- backdropFilter: 'blur(10px)',
113
- WebkitBackdropFilter: 'blur(10px)',
111
+ background: visible ? 'rgba(0, 0, 0, 0.95)' : 'rgba(0, 0, 0, 0)',
112
+ backdropFilter: 'blur(12px)',
113
+ WebkitBackdropFilter: 'blur(12px)',
114
114
  display: 'flex',
115
115
  alignItems: 'center',
116
116
  justifyContent: 'center',
117
117
  padding: 24,
118
- transition: 'background 0.3s ease, opacity 0.3s ease',
118
+ transition: 'all 0.3s ease',
119
119
  opacity: visible ? 1 : 0,
120
120
  boxSizing: 'border-box',
121
121
  };
@@ -125,21 +125,19 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
125
125
  maxHeight: '90vh',
126
126
  display: 'flex',
127
127
  flexDirection: 'column',
128
- background: 'rgba(10, 10, 12, 1)',
129
- // COR ALTERADA: Sombra laranja
130
- boxShadow: `0 0 0 1px rgba(${primaryRgb}, 0.15), 0 50px 100px -20px rgba(0, 0, 0, 1)`,
128
+ background: '#0a0a0a',
129
+ boxShadow: `0 0 0 1px ${borderColor}, 0 50px 100px -20px rgba(0, 0, 0, 1)`,
131
130
  borderRadius: 16,
132
131
  overflow: 'hidden',
133
132
  transform: visible ? 'scale(1) translateY(0)' : 'scale(0.98) translateY(10px)',
134
- transition: 'transform 0.35s cubic-bezier(0.16, 1, 0.3, 1)',
133
+ transition: 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
135
134
  position: 'relative',
136
135
  };
137
136
  const neonLine = {
138
137
  height: '1px',
139
138
  width: '100%',
140
- // COR ALTERADA: Gradiente Laranja/Vermelho
141
- background: `linear-gradient(90deg, transparent, ${primaryColorDark}, ${primaryColor}, transparent)`,
142
- boxShadow: `0 0 15px rgba(${primaryRgb}, 0.6)`,
139
+ background: `linear-gradient(90deg, transparent, #334155, #ffffff, #334155, transparent)`,
140
+ boxShadow: `0 0 15px rgba(255, 255, 255, 0.05)`,
143
141
  };
144
142
  const headerStyle = {
145
143
  padding: '16px 24px',
@@ -153,7 +151,7 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
153
151
  padding: 24,
154
152
  overflow: 'auto',
155
153
  flex: 1,
156
- fontFamily: '"JetBrains Mono", "Fira Code", monospace',
154
+ fontFamily: '"JetBrains Mono", monospace',
157
155
  fontSize: 13,
158
156
  lineHeight: 1.6,
159
157
  color: '#e2e8f0',
@@ -162,7 +160,7 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
162
160
  };
163
161
  const getBtnStyle = (kind, hovering) => {
164
162
  const base = {
165
- padding: '8px 14px',
163
+ padding: '8px 16px',
166
164
  borderRadius: 8,
167
165
  fontSize: 11,
168
166
  fontWeight: 700,
@@ -172,44 +170,49 @@ function ErrorModal({ error, isOpen, onClose, onCopy, }) {
172
170
  letterSpacing: '0.05em',
173
171
  border: 'none',
174
172
  outline: 'none',
175
- fontFamily: 'system-ui, sans-serif'
173
+ fontFamily: 'Inter, system-ui, sans-serif'
176
174
  };
177
175
  if (kind === 'primary') {
178
176
  return {
179
177
  ...base,
180
- // COR ALTERADA: Botão primário laranja
181
- background: hovering ? `rgba(${primaryRgb}, 0.15)` : 'transparent',
182
- color: primaryColor,
183
- border: `1px solid rgba(${primaryRgb}, 0.3)`,
184
- boxShadow: hovering ? `0 0 10px rgba(${primaryRgb}, 0.2)` : 'none',
178
+ background: hovering ? '#ffffff' : '#f1f5f9',
179
+ color: '#000000',
180
+ boxShadow: hovering ? `0 0 15px rgba(255, 255, 255, 0.2)` : 'none',
185
181
  };
186
182
  }
187
183
  return {
188
184
  ...base,
189
- background: 'transparent',
190
- color: hovering ? '#fff' : 'rgba(255,255,255,0.5)',
185
+ background: hovering ? 'rgba(255, 255, 255, 0.08)' : 'transparent',
186
+ color: hovering ? '#fff' : 'rgba(255, 255, 255, 0.4)',
187
+ border: '1px solid transparent',
188
+ borderColor: hovering ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
191
189
  };
192
190
  };
193
191
  const modalContent = ((0, jsx_runtime_1.jsx)("div", { style: overlayStyle, onMouseDown: onClose, children: (0, jsx_runtime_1.jsxs)("div", { style: cardStyle, onMouseDown: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("div", { style: neonLine }), (0, jsx_runtime_1.jsxs)("div", { style: headerStyle, children: [(0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [(0, jsx_runtime_1.jsx)("span", { style: {
194
- fontSize: 12,
195
- fontWeight: 800,
196
- color: '#f87171',
197
- letterSpacing: '0.1em'
192
+ fontSize: 11,
193
+ fontWeight: 900,
194
+ color: '#ffffff',
195
+ background: '#ef4444',
196
+ padding: '2px 8px',
197
+ borderRadius: 4,
198
+ letterSpacing: '0.05em'
198
199
  }, children: "ERROR" }), error.plugin && ((0, jsx_runtime_1.jsx)("span", { style: {
199
200
  fontSize: 11,
200
- color: 'rgba(255,255,255,0.3)',
201
- background: 'rgba(255,255,255,0.05)',
202
- padding: '2px 6px',
201
+ color: '#64748b',
202
+ background: 'rgba(255, 255, 255, 0.03)',
203
+ padding: '2px 8px',
203
204
  borderRadius: 4,
204
- fontFamily: 'monospace'
205
- }, children: error.plugin }))] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: 8 }, children: [onCopy && ((0, jsx_runtime_1.jsx)("button", { onClick: onCopy, onMouseEnter: () => setIsHoveringCopy(true), onMouseLeave: () => setIsHoveringCopy(false), style: getBtnStyle('secondary', isHoveringCopy), children: "Copy Log" })), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, onMouseEnter: () => setIsHoveringClose(true), onMouseLeave: () => setIsHoveringClose(false), style: getBtnStyle('primary', isHoveringClose), children: "Close" })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: terminalContent, children: [(0, jsx_runtime_1.jsx)(AnsiText, { text: rawOutput }), stackOutput && ((0, jsx_runtime_1.jsx)("div", { style: { marginTop: 24, opacity: 0.6, borderTop: '1px dashed rgba(255,255,255,0.1)', paddingTop: 16 }, children: (0, jsx_runtime_1.jsx)(AnsiText, { text: stackOutput }) }))] }), (0, jsx_runtime_1.jsxs)("div", { style: {
206
- padding: '8px 24px',
207
- background: 'rgba(0,0,0,0.3)',
205
+ fontFamily: 'monospace',
206
+ border: '1px solid rgba(255, 255, 255, 0.05)'
207
+ }, children: error.plugin }))] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: 8 }, children: [onCopy && ((0, jsx_runtime_1.jsx)("button", { onClick: onCopy, onMouseEnter: () => setIsHoveringCopy(true), onMouseLeave: () => setIsHoveringCopy(false), style: getBtnStyle('secondary', isHoveringCopy), children: "Copy Log" })), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, onMouseEnter: () => setIsHoveringClose(true), onMouseLeave: () => setIsHoveringClose(false), style: getBtnStyle('primary', isHoveringClose), children: "Close" })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: terminalContent, children: [(0, jsx_runtime_1.jsx)(AnsiText, { text: rawOutput }), stackOutput && ((0, jsx_runtime_1.jsx)("div", { style: { marginTop: 24, opacity: 0.4, borderTop: '1px dashed rgba(255,255,255,0.1)', paddingTop: 16 }, children: (0, jsx_runtime_1.jsx)(AnsiText, { text: stackOutput }) }))] }), (0, jsx_runtime_1.jsxs)("div", { style: {
208
+ padding: '10px 24px',
209
+ background: 'rgba(255,255,255,0.01)',
208
210
  borderTop: '1px solid rgba(255,255,255,0.03)',
209
211
  display: 'flex',
210
212
  justifyContent: 'space-between',
211
213
  fontSize: 11,
212
- color: 'rgba(255,255,255,0.2)'
213
- }, children: [(0, jsx_runtime_1.jsx)("span", { children: "vatts-cli" }), (0, jsx_runtime_1.jsx)("span", { style: { color: primaryColor }, children: "Watching for changes..." })] })] }) }));
214
+ color: 'rgba(255,255,255,0.3)',
215
+ fontFamily: 'Inter, sans-serif'
216
+ }, children: [(0, jsx_runtime_1.jsx)("span", { children: "vatts-cli" }), (0, jsx_runtime_1.jsx)("span", { style: { color: '#64748b', fontWeight: 500 }, children: "Watching for changes..." })] })] }) }));
214
217
  return (0, react_dom_1.createPortal)(modalContent, document.body);
215
218
  }
@@ -2,3 +2,5 @@ export { Link } from '../components/Link';
2
2
  export { RouteConfig, Metadata } from "../types";
3
3
  export { router } from './clientRouter';
4
4
  export { importServer } from './rpc';
5
+ export { default as Image } from "./image/Image";
6
+ export { default as VattsImage } from "./image/Image";
@@ -15,8 +15,11 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
+ var __importDefault = (this && this.__importDefault) || function (mod) {
19
+ return (mod && mod.__esModule) ? mod : { "default": mod };
20
+ };
18
21
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.importServer = exports.router = exports.Link = void 0;
22
+ exports.VattsImage = exports.Image = exports.importServer = exports.router = exports.Link = void 0;
20
23
  // Este arquivo exporta apenas código seguro para o cliente (navegador)
21
24
  var Link_1 = require("../components/Link");
22
25
  Object.defineProperty(exports, "Link", { enumerable: true, get: function () { return Link_1.Link; } });
@@ -25,3 +28,7 @@ Object.defineProperty(exports, "router", { enumerable: true, get: function () {
25
28
  // RPC (client-side)
26
29
  var rpc_1 = require("./rpc");
27
30
  Object.defineProperty(exports, "importServer", { enumerable: true, get: function () { return rpc_1.importServer; } });
31
+ var Image_1 = require("./image/Image");
32
+ Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return __importDefault(Image_1).default; } });
33
+ var Image_2 = require("./image/Image");
34
+ Object.defineProperty(exports, "VattsImage", { enumerable: true, get: function () { return __importDefault(Image_2).default; } });
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface VattsImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
3
+ src: string;
4
+ width?: number | string;
5
+ height?: number | string;
6
+ quality?: number;
7
+ priority?: boolean;
8
+ }
9
+ declare const Image: React.FC<VattsImageProps>;
10
+ export default Image;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const Image = ({ src, width, height, quality = 75, priority = false, className, style, alt = "", ...props }) => {
5
+ // Se a imagem for Base64 (pequena) ou externa (http), não otimizamos via backend local
6
+ const isOptimizable = src && !src.startsWith('data:') && !src.startsWith('http');
7
+ let optimizedSrc = src;
8
+ if (isOptimizable) {
9
+ const params = new URLSearchParams();
10
+ params.set('url', src);
11
+ // Tratamento inteligente para remover "px" se o usuário passar string
12
+ if (width) {
13
+ const w = String(width).replace('px', '');
14
+ if (!isNaN(Number(w)))
15
+ params.set('w', w);
16
+ }
17
+ if (height) {
18
+ const h = String(height).replace('px', '');
19
+ if (!isNaN(Number(h)))
20
+ params.set('h', h);
21
+ }
22
+ if (quality)
23
+ params.set('q', quality.toString());
24
+ optimizedSrc = `/_vatts/image?${params.toString()}`;
25
+ }
26
+ // Estilos base para prevenir layout shift se dimensões forem fornecidas
27
+ const baseStyle = {
28
+ // Se width for numérico, assume pixels, senão usa o valor string (ex: 100%)
29
+ width: width ? (typeof width === 'number' ? `${width}px` : width) : 'auto',
30
+ height: height ? (typeof height === 'number' ? `${height}px` : height) : 'auto',
31
+ ...style,
32
+ };
33
+ return ((0, jsx_runtime_1.jsx)("img", { ...props, src: optimizedSrc, alt: alt, loading: priority ? 'eager' : 'lazy', decoding: priority ? 'sync' : 'async', width: typeof width === 'string' ? width.replace('px', '') : width, height: typeof height === 'string' ? height.replace('px', '') : height, className: `vatts-image ${className || ''}`, style: baseStyle }));
34
+ };
35
+ exports.default = Image;