zelune 0.0.0-stage → 1.4.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/CHANGELOG.md +57 -0
- package/DESIGN.md +90 -0
- package/PRODUCT.md +31 -0
- package/README.md +63 -2
- package/assets/logo.svg +8 -0
- package/bin/cli.mjs +243 -0
- package/index.html +18 -0
- package/mcp/server.ts +140 -0
- package/mcp_config.json +8 -0
- package/package.json +77 -4
- package/plugin.json +12 -0
- package/plugins/viteRnSourceInject.ts +117 -0
- package/plugins/viteVirtualScreensPlugin.ts +85 -0
- package/rules/AGENTS.md +21 -0
- package/skills/rn-canvas/SKILL.md +33 -0
- package/src/canvas/CanvasApp.tsx +1203 -0
- package/src/canvas/FlowArrowsLayer.tsx +197 -0
- package/src/canvas/InspectorPanel.tsx +979 -0
- package/src/canvas/ScreenArtboard.tsx +451 -0
- package/src/canvas/types.ts +23 -0
- package/src/main.tsx +10 -0
- package/src/runtime/MockNavigationContext.tsx +199 -0
- package/src/runtime/ScreenErrorBoundary.tsx +86 -0
- package/src/screens/CheckoutScreen.tsx +229 -0
- package/src/screens/HomeScreen.tsx +288 -0
- package/src/screens/LoginScreen.tsx +176 -0
- package/src/screens/ProductDetailScreen.tsx +234 -0
- package/src/screens/ProfileScreen.tsx +172 -0
- package/src/screens/index.ts +1 -0
- package/src/server/apiMiddleware.ts +154 -0
- package/src/server/astEngine.ts +877 -0
- package/src/styles.css +298 -0
- package/src/vite-env.d.ts +7 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +81 -0
|
@@ -0,0 +1,979 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Code2,
|
|
4
|
+
Sparkles,
|
|
5
|
+
Type,
|
|
6
|
+
Palette,
|
|
7
|
+
Box,
|
|
8
|
+
Navigation,
|
|
9
|
+
Copy,
|
|
10
|
+
Check,
|
|
11
|
+
Sliders,
|
|
12
|
+
X,
|
|
13
|
+
PanelRightClose,
|
|
14
|
+
} from 'lucide-react';
|
|
15
|
+
import type { InspectedElement, PatchPayload, StyleValue } from '../server/astEngine';
|
|
16
|
+
|
|
17
|
+
interface InspectorPanelProps {
|
|
18
|
+
inspected: InspectedElement | null;
|
|
19
|
+
isOpen: boolean;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
isSaving: boolean;
|
|
22
|
+
statusMessage: string | null;
|
|
23
|
+
onPatch: (payload: PatchPayload) => Promise<void>;
|
|
24
|
+
onClearSelection: () => void;
|
|
25
|
+
onCollapse: () => void;
|
|
26
|
+
onJumpToScreen: (routeName: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const COLOR_SWATCHES = [
|
|
30
|
+
'#0A0B0E',
|
|
31
|
+
'#13161E',
|
|
32
|
+
'#1E2433',
|
|
33
|
+
'#38BDF8',
|
|
34
|
+
'#6366F1',
|
|
35
|
+
'#10B981',
|
|
36
|
+
'#F59E0B',
|
|
37
|
+
'#F43F5E',
|
|
38
|
+
'#F8FAFC',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const FONT_WEIGHTS = ['400', '500', '600', '700', '800'];
|
|
42
|
+
|
|
43
|
+
export const InspectorPanel: React.FC<InspectorPanelProps> = ({
|
|
44
|
+
inspected,
|
|
45
|
+
isOpen,
|
|
46
|
+
isLoading,
|
|
47
|
+
isSaving,
|
|
48
|
+
statusMessage,
|
|
49
|
+
onPatch,
|
|
50
|
+
onClearSelection,
|
|
51
|
+
onCollapse,
|
|
52
|
+
onJumpToScreen,
|
|
53
|
+
}) => {
|
|
54
|
+
const [localText, setLocalText] = useState('');
|
|
55
|
+
const [localStyles, setLocalStyles] = useState<Record<string, StyleValue>>({});
|
|
56
|
+
const [agentInstruction, setAgentInstruction] = useState('');
|
|
57
|
+
const [copiedPrompt, setCopiedPrompt] = useState(false);
|
|
58
|
+
|
|
59
|
+
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
|
60
|
+
const debounceTimerRef = useRef<number | null>(null);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!inspected) {
|
|
64
|
+
setLocalText('');
|
|
65
|
+
setLocalStyles({});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setLocalText(inspected.textContent ?? '');
|
|
69
|
+
setLocalStyles(inspected.styles ?? {});
|
|
70
|
+
}, [inspected]);
|
|
71
|
+
|
|
72
|
+
// Modern Web Guidance fallback for <dialog closedby="any"> light-dismiss in browsers without native closedBy
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const dialog = dialogRef.current;
|
|
75
|
+
if (!dialog) return;
|
|
76
|
+
|
|
77
|
+
if (!('closedBy' in HTMLDialogElement.prototype)) {
|
|
78
|
+
const handleBackdropClick = (event: MouseEvent) => {
|
|
79
|
+
if (event.target !== dialog) return;
|
|
80
|
+
const rect = dialog.getBoundingClientRect();
|
|
81
|
+
const isDialogContent =
|
|
82
|
+
rect.top <= event.clientY &&
|
|
83
|
+
event.clientY <= rect.top + rect.height &&
|
|
84
|
+
rect.left <= event.clientX &&
|
|
85
|
+
event.clientX <= rect.left + rect.width;
|
|
86
|
+
if (isDialogContent) return;
|
|
87
|
+
dialog.close();
|
|
88
|
+
};
|
|
89
|
+
dialog.addEventListener('click', handleBackdropClick);
|
|
90
|
+
return () => dialog.removeEventListener('click', handleBackdropClick);
|
|
91
|
+
}
|
|
92
|
+
}, [inspected]);
|
|
93
|
+
|
|
94
|
+
const schedulePatch = (nextPatch: Omit<PatchPayload, 'file' | 'astPath'>) => {
|
|
95
|
+
if (!inspected) return;
|
|
96
|
+
if (debounceTimerRef.current) {
|
|
97
|
+
window.clearTimeout(debounceTimerRef.current);
|
|
98
|
+
}
|
|
99
|
+
debounceTimerRef.current = window.setTimeout(() => {
|
|
100
|
+
void onPatch({
|
|
101
|
+
file: inspected.file,
|
|
102
|
+
astPath: inspected.astPath,
|
|
103
|
+
line: inspected.line,
|
|
104
|
+
...nextPatch,
|
|
105
|
+
});
|
|
106
|
+
}, 220);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleTextChange = (val: string) => {
|
|
110
|
+
setLocalText(val);
|
|
111
|
+
schedulePatch({ text: val });
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleStyleChange = (key: string, rawVal: string | number) => {
|
|
115
|
+
const nextStyles = { ...localStyles, [key]: rawVal };
|
|
116
|
+
setLocalStyles(nextStyles);
|
|
117
|
+
schedulePatch({ styles: { [key]: rawVal } });
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleNumericStyleChange = (key: string, rawStr: string) => {
|
|
121
|
+
if (rawStr.trim() === '') {
|
|
122
|
+
const copy = { ...localStyles };
|
|
123
|
+
delete copy[key];
|
|
124
|
+
setLocalStyles(copy);
|
|
125
|
+
schedulePatch({ styles: { [key]: null } });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const parsed = Number(rawStr);
|
|
129
|
+
if (!Number.isNaN(parsed)) {
|
|
130
|
+
handleStyleChange(key, parsed);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const buildAntigravityPrompt = () => {
|
|
135
|
+
if (!inspected) return '';
|
|
136
|
+
return [
|
|
137
|
+
`Refactor the <${inspected.tagName}> element in \`${inspected.file}\` (around line ${inspected.line}, AST index ${inspected.astPath}).`,
|
|
138
|
+
inspected.styleSheetKey
|
|
139
|
+
? `Associated StyleSheet key: \`styles.${inspected.styleSheetKey}\`.`
|
|
140
|
+
: '',
|
|
141
|
+
'',
|
|
142
|
+
`Current JSX Snippet:`,
|
|
143
|
+
'```tsx',
|
|
144
|
+
inspected.codeSnippet,
|
|
145
|
+
'```',
|
|
146
|
+
'',
|
|
147
|
+
`Requested Change:`,
|
|
148
|
+
agentInstruction.trim() || 'Update this component layout and styling per DESIGN.md.',
|
|
149
|
+
]
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.join('\n');
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleCopyPrompt = async () => {
|
|
155
|
+
const text = buildAntigravityPrompt();
|
|
156
|
+
await navigator.clipboard.writeText(text);
|
|
157
|
+
setCopiedPrompt(true);
|
|
158
|
+
window.setTimeout(() => setCopiedPrompt(false), 2000);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const targetWidth = inspected ? 340 : 320;
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<aside
|
|
165
|
+
aria-hidden={!isOpen}
|
|
166
|
+
className="zelune-collapsible-panel"
|
|
167
|
+
style={{
|
|
168
|
+
width: isOpen ? targetWidth : 0,
|
|
169
|
+
opacity: isOpen ? 1 : 0,
|
|
170
|
+
pointerEvents: isOpen ? 'auto' : 'none',
|
|
171
|
+
backgroundColor: 'var(--studio-surface-1)',
|
|
172
|
+
borderLeft: isOpen ? '1px solid var(--studio-border-hairline)' : '0px solid transparent',
|
|
173
|
+
display: 'flex',
|
|
174
|
+
flexDirection: 'column',
|
|
175
|
+
height: '100%',
|
|
176
|
+
zIndex: 30,
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{!inspected ? (
|
|
180
|
+
<div
|
|
181
|
+
className="zelune-scroller"
|
|
182
|
+
style={{
|
|
183
|
+
width: 320,
|
|
184
|
+
minWidth: 320,
|
|
185
|
+
height: '100%',
|
|
186
|
+
display: 'flex',
|
|
187
|
+
flexDirection: 'column',
|
|
188
|
+
justifyContent: 'space-between',
|
|
189
|
+
padding: 18,
|
|
190
|
+
color: 'var(--studio-text-secondary)',
|
|
191
|
+
overflowY: 'auto',
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
|
195
|
+
<div
|
|
196
|
+
style={{
|
|
197
|
+
display: 'flex',
|
|
198
|
+
alignItems: 'center',
|
|
199
|
+
justifyContent: 'space-between',
|
|
200
|
+
color: '#F8FAFC',
|
|
201
|
+
fontWeight: 600,
|
|
202
|
+
fontSize: 13.5,
|
|
203
|
+
letterSpacing: '-0.01em',
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
207
|
+
<Sliders size={15} color="#38BDF8" />
|
|
208
|
+
<span>AST Visual Inspector</span>
|
|
209
|
+
</span>
|
|
210
|
+
<button
|
|
211
|
+
type="button"
|
|
212
|
+
className="zelune-interactive"
|
|
213
|
+
onClick={onCollapse}
|
|
214
|
+
title="Collapse Inspector Panel (])"
|
|
215
|
+
style={{
|
|
216
|
+
background: 'transparent',
|
|
217
|
+
border: '1px solid transparent',
|
|
218
|
+
borderRadius: 5,
|
|
219
|
+
color: 'var(--studio-text-muted)',
|
|
220
|
+
cursor: 'pointer',
|
|
221
|
+
padding: 4,
|
|
222
|
+
display: 'inline-flex',
|
|
223
|
+
alignItems: 'center',
|
|
224
|
+
justifyContent: 'center',
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
<PanelRightClose size={14} />
|
|
228
|
+
</button>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div
|
|
232
|
+
style={{
|
|
233
|
+
backgroundColor: 'var(--studio-surface-2)',
|
|
234
|
+
border: '1px solid var(--studio-border-hairline)',
|
|
235
|
+
borderRadius: 10,
|
|
236
|
+
padding: 14,
|
|
237
|
+
fontSize: 12.5,
|
|
238
|
+
lineHeight: 1.6,
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
Select any node on a <code style={{ color: '#38BDF8', fontFamily: 'var(--font-mono)' }}>393 × 852</code>{' '}
|
|
242
|
+
artboard to inspect and live-patch its{' '}
|
|
243
|
+
<strong style={{ color: '#E2E8F0' }}>StyleSheet.create</strong> rules or{' '}
|
|
244
|
+
<strong style={{ color: '#E2E8F0' }}>static copy</strong> directly in the{' '}
|
|
245
|
+
<code style={{ color: '#A5B4FC', fontFamily: 'var(--font-mono)' }}>.tsx</code> AST.
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 }}>
|
|
249
|
+
<div
|
|
250
|
+
style={{
|
|
251
|
+
fontSize: 10.5,
|
|
252
|
+
fontFamily: 'var(--font-mono)',
|
|
253
|
+
fontWeight: 600,
|
|
254
|
+
color: 'var(--studio-text-muted)',
|
|
255
|
+
letterSpacing: '0.06em',
|
|
256
|
+
}}
|
|
257
|
+
>
|
|
258
|
+
DESIGN & SYNC GOVERNANCE
|
|
259
|
+
</div>
|
|
260
|
+
<div style={{ fontSize: 12, color: 'var(--studio-text-secondary)', lineHeight: 1.5 }}>
|
|
261
|
+
• <strong style={{ color: '#CBD5E1' }}>Tier 1 (Surgical AST)</strong>: Instant write-back to colors, typography, spacing, and copy.
|
|
262
|
+
</div>
|
|
263
|
+
<div style={{ fontSize: 12, color: 'var(--studio-text-secondary)', lineHeight: 1.5 }}>
|
|
264
|
+
• <strong style={{ color: '#CBD5E1' }}>Tier 2 (Antigravity Bridge)</strong>: Dispatch structural refactors with exact file & AST path coordinates.
|
|
265
|
+
</div>
|
|
266
|
+
<div style={{ fontSize: 12, color: 'var(--studio-text-secondary)', lineHeight: 1.5 }}>
|
|
267
|
+
• <strong style={{ color: '#CBD5E1' }}>Impeccable & Taste Guardrails</strong>: Governed by{' '}
|
|
268
|
+
<code style={{ fontFamily: 'var(--font-mono)', color: '#38BDF8' }}>PRODUCT.md</code> &{' '}
|
|
269
|
+
<code style={{ fontFamily: 'var(--font-mono)', color: '#38BDF8' }}>DESIGN.md</code>.
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div
|
|
275
|
+
style={{
|
|
276
|
+
fontSize: 11,
|
|
277
|
+
fontFamily: 'var(--font-mono)',
|
|
278
|
+
color: 'var(--studio-text-muted)',
|
|
279
|
+
borderTop: '1px solid var(--studio-border-hairline)',
|
|
280
|
+
paddingTop: 12,
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
MCP Server: <span style={{ color: '#34D399' }}>Zelune Ready (mcp/server.ts)</span>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
) : (
|
|
287
|
+
<div
|
|
288
|
+
className="zelune-scroller"
|
|
289
|
+
style={{
|
|
290
|
+
width: 340,
|
|
291
|
+
minWidth: 340,
|
|
292
|
+
height: '100%',
|
|
293
|
+
display: 'flex',
|
|
294
|
+
flexDirection: 'column',
|
|
295
|
+
overflowY: 'auto',
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
{/* Inspector Header */}
|
|
299
|
+
<div
|
|
300
|
+
style={{
|
|
301
|
+
padding: '12px 16px',
|
|
302
|
+
borderBottom: '1px solid var(--studio-border-hairline)',
|
|
303
|
+
display: 'flex',
|
|
304
|
+
alignItems: 'center',
|
|
305
|
+
justifyContent: 'space-between',
|
|
306
|
+
backgroundColor: 'var(--studio-surface-2)',
|
|
307
|
+
}}
|
|
308
|
+
>
|
|
309
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
|
310
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
311
|
+
<span
|
|
312
|
+
style={{
|
|
313
|
+
backgroundColor: 'rgba(99, 102, 241, 0.18)',
|
|
314
|
+
border: '1px solid rgba(99, 102, 241, 0.4)',
|
|
315
|
+
color: '#C7D2FE',
|
|
316
|
+
fontSize: 11.5,
|
|
317
|
+
fontWeight: 700,
|
|
318
|
+
fontFamily: 'var(--font-mono)',
|
|
319
|
+
padding: '1px 6px',
|
|
320
|
+
borderRadius: 4,
|
|
321
|
+
}}
|
|
322
|
+
>
|
|
323
|
+
<{inspected.tagName}>
|
|
324
|
+
</span>
|
|
325
|
+
{inspected.styleSheetKey && (
|
|
326
|
+
<span
|
|
327
|
+
style={{
|
|
328
|
+
backgroundColor: 'rgba(16, 185, 129, 0.14)',
|
|
329
|
+
border: '1px solid rgba(16, 185, 129, 0.35)',
|
|
330
|
+
color: '#A7F3D0',
|
|
331
|
+
fontSize: 11,
|
|
332
|
+
fontFamily: 'var(--font-mono)',
|
|
333
|
+
padding: '1px 6px',
|
|
334
|
+
borderRadius: 4,
|
|
335
|
+
}}
|
|
336
|
+
>
|
|
337
|
+
styles.{inspected.styleSheetKey}
|
|
338
|
+
</span>
|
|
339
|
+
)}
|
|
340
|
+
</div>
|
|
341
|
+
<span
|
|
342
|
+
className="tabular-nums"
|
|
343
|
+
style={{
|
|
344
|
+
fontSize: 10.5,
|
|
345
|
+
fontFamily: 'var(--font-mono)',
|
|
346
|
+
color: 'var(--studio-text-muted)',
|
|
347
|
+
marginTop: 1,
|
|
348
|
+
}}
|
|
349
|
+
>
|
|
350
|
+
{inspected.file}:{inspected.line}:{inspected.col}
|
|
351
|
+
</span>
|
|
352
|
+
</div>
|
|
353
|
+
|
|
354
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
355
|
+
<button
|
|
356
|
+
type="button"
|
|
357
|
+
className="zelune-interactive"
|
|
358
|
+
onClick={onCollapse}
|
|
359
|
+
title="Collapse Inspector Panel (])"
|
|
360
|
+
style={{
|
|
361
|
+
background: 'transparent',
|
|
362
|
+
border: '1px solid transparent',
|
|
363
|
+
borderRadius: 5,
|
|
364
|
+
color: 'var(--studio-text-muted)',
|
|
365
|
+
cursor: 'pointer',
|
|
366
|
+
padding: 4,
|
|
367
|
+
display: 'inline-flex',
|
|
368
|
+
alignItems: 'center',
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
<PanelRightClose size={14} />
|
|
372
|
+
</button>
|
|
373
|
+
<button
|
|
374
|
+
type="button"
|
|
375
|
+
className="zelune-interactive"
|
|
376
|
+
onClick={onClearSelection}
|
|
377
|
+
title="Deselect element (Esc)"
|
|
378
|
+
style={{
|
|
379
|
+
background: 'transparent',
|
|
380
|
+
border: '1px solid transparent',
|
|
381
|
+
borderRadius: 5,
|
|
382
|
+
color: 'var(--studio-text-muted)',
|
|
383
|
+
cursor: 'pointer',
|
|
384
|
+
padding: 4,
|
|
385
|
+
display: 'inline-flex',
|
|
386
|
+
alignItems: 'center',
|
|
387
|
+
}}
|
|
388
|
+
>
|
|
389
|
+
<X size={15} />
|
|
390
|
+
</button>
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
{/* Live Save Status Indicator */}
|
|
395
|
+
{(isLoading || isSaving || statusMessage) && (
|
|
396
|
+
<div
|
|
397
|
+
style={{
|
|
398
|
+
padding: '6px 16px',
|
|
399
|
+
backgroundColor: 'rgba(30, 27, 75, 0.65)',
|
|
400
|
+
borderBottom: '1px solid rgba(99, 102, 241, 0.3)',
|
|
401
|
+
fontSize: 11,
|
|
402
|
+
fontFamily: 'var(--font-mono)',
|
|
403
|
+
color: '#A5B4FC',
|
|
404
|
+
display: 'flex',
|
|
405
|
+
alignItems: 'center',
|
|
406
|
+
justifyContent: 'space-between',
|
|
407
|
+
}}
|
|
408
|
+
>
|
|
409
|
+
<span>
|
|
410
|
+
{isSaving
|
|
411
|
+
? 'Patching AST & writing .tsx...'
|
|
412
|
+
: isLoading
|
|
413
|
+
? 'Inspecting AST node...'
|
|
414
|
+
: statusMessage}
|
|
415
|
+
</span>
|
|
416
|
+
<span style={{ color: '#34D399' }}>● AST Sync</span>
|
|
417
|
+
</div>
|
|
418
|
+
)}
|
|
419
|
+
|
|
420
|
+
{/* Navigation Flow Link Banner if element triggers navigation */}
|
|
421
|
+
{inspected.navigationTarget && (
|
|
422
|
+
<div
|
|
423
|
+
style={{
|
|
424
|
+
margin: '12px 14px 0',
|
|
425
|
+
padding: '9px 12px',
|
|
426
|
+
backgroundColor: 'rgba(6, 78, 59, 0.45)',
|
|
427
|
+
border: '1px solid rgba(16, 185, 129, 0.45)',
|
|
428
|
+
borderRadius: 8,
|
|
429
|
+
display: 'flex',
|
|
430
|
+
alignItems: 'center',
|
|
431
|
+
justifyContent: 'space-between',
|
|
432
|
+
}}
|
|
433
|
+
>
|
|
434
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: '#D1FAE5' }}>
|
|
435
|
+
<Navigation size={13} color="#34D399" />
|
|
436
|
+
<span>
|
|
437
|
+
Flow → <strong>{inspected.navigationTarget}</strong>
|
|
438
|
+
</span>
|
|
439
|
+
</div>
|
|
440
|
+
<button
|
|
441
|
+
type="button"
|
|
442
|
+
className="zelune-interactive"
|
|
443
|
+
onClick={() => onJumpToScreen(inspected.navigationTarget!)}
|
|
444
|
+
style={{
|
|
445
|
+
backgroundColor: '#10B981',
|
|
446
|
+
color: '#052E16',
|
|
447
|
+
border: 'none',
|
|
448
|
+
borderRadius: 5,
|
|
449
|
+
padding: '4px 8px',
|
|
450
|
+
fontSize: 11,
|
|
451
|
+
fontWeight: 700,
|
|
452
|
+
cursor: 'pointer',
|
|
453
|
+
}}
|
|
454
|
+
>
|
|
455
|
+
Focus Target →
|
|
456
|
+
</button>
|
|
457
|
+
</div>
|
|
458
|
+
)}
|
|
459
|
+
|
|
460
|
+
<div className="zelune-inspector-body" style={{ display: 'flex', flexDirection: 'column' }}>
|
|
461
|
+
{/* SECTION 1: Text Copy Editor */}
|
|
462
|
+
{(inspected.isTextEditable || inspected.hasDynamicText) && (
|
|
463
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
|
|
464
|
+
<div
|
|
465
|
+
style={{
|
|
466
|
+
display: 'flex',
|
|
467
|
+
alignItems: 'center',
|
|
468
|
+
gap: 6,
|
|
469
|
+
fontSize: 10.5,
|
|
470
|
+
fontFamily: 'var(--font-mono)',
|
|
471
|
+
fontWeight: 600,
|
|
472
|
+
color: 'var(--studio-text-muted)',
|
|
473
|
+
letterSpacing: '0.06em',
|
|
474
|
+
}}
|
|
475
|
+
>
|
|
476
|
+
<Type size={12} color="#38BDF8" />
|
|
477
|
+
<span>TEXT COPY</span>
|
|
478
|
+
</div>
|
|
479
|
+
|
|
480
|
+
{inspected.hasDynamicText ? (
|
|
481
|
+
<div
|
|
482
|
+
style={{
|
|
483
|
+
padding: 10,
|
|
484
|
+
backgroundColor: 'var(--studio-surface-2)',
|
|
485
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
486
|
+
borderRadius: 7,
|
|
487
|
+
fontSize: 11.5,
|
|
488
|
+
color: '#CBD5E1',
|
|
489
|
+
lineHeight: 1.5,
|
|
490
|
+
}}
|
|
491
|
+
>
|
|
492
|
+
Contains dynamic JSX expression (<code style={{ color: '#38BDF8' }}>{'{...}'}</code>). Protected from static overwrite—use <strong>Ask Antigravity</strong> below to refactor expression logic.
|
|
493
|
+
</div>
|
|
494
|
+
) : (
|
|
495
|
+
<textarea
|
|
496
|
+
rows={2}
|
|
497
|
+
value={localText}
|
|
498
|
+
onChange={(e) => handleTextChange(e.target.value)}
|
|
499
|
+
placeholder="Enter text content..."
|
|
500
|
+
className="zelune-input"
|
|
501
|
+
style={{
|
|
502
|
+
width: '100%',
|
|
503
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
504
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
505
|
+
borderRadius: 7,
|
|
506
|
+
paddingLeft: 10,
|
|
507
|
+
paddingRight: 10,
|
|
508
|
+
color: '#F8FAFC',
|
|
509
|
+
fontSize: 12.5,
|
|
510
|
+
resize: 'vertical',
|
|
511
|
+
fontFamily: 'var(--font-sans)',
|
|
512
|
+
}}
|
|
513
|
+
/>
|
|
514
|
+
)}
|
|
515
|
+
</section>
|
|
516
|
+
)}
|
|
517
|
+
|
|
518
|
+
{/* SECTION 2: Colors (Background & Text/Foreground) */}
|
|
519
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
520
|
+
<div
|
|
521
|
+
style={{
|
|
522
|
+
display: 'flex',
|
|
523
|
+
alignItems: 'center',
|
|
524
|
+
gap: 6,
|
|
525
|
+
fontSize: 10.5,
|
|
526
|
+
fontFamily: 'var(--font-mono)',
|
|
527
|
+
fontWeight: 600,
|
|
528
|
+
color: 'var(--studio-text-muted)',
|
|
529
|
+
letterSpacing: '0.06em',
|
|
530
|
+
}}
|
|
531
|
+
>
|
|
532
|
+
<Palette size={12} color="#38BDF8" />
|
|
533
|
+
<span>COLORS & SURFACES</span>
|
|
534
|
+
</div>
|
|
535
|
+
|
|
536
|
+
{/* Background Color */}
|
|
537
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
|
538
|
+
<label style={{ fontSize: 11.5, color: '#CBD5E1' }}>Background Color</label>
|
|
539
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
540
|
+
<input
|
|
541
|
+
type="color"
|
|
542
|
+
value={
|
|
543
|
+
typeof localStyles.backgroundColor === 'string' &&
|
|
544
|
+
localStyles.backgroundColor.startsWith('#') &&
|
|
545
|
+
localStyles.backgroundColor.length === 7
|
|
546
|
+
? localStyles.backgroundColor
|
|
547
|
+
: '#13161E'
|
|
548
|
+
}
|
|
549
|
+
onChange={(e) => handleStyleChange('backgroundColor', e.target.value)}
|
|
550
|
+
style={{
|
|
551
|
+
width: 30,
|
|
552
|
+
height: 30,
|
|
553
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
554
|
+
borderRadius: 6,
|
|
555
|
+
backgroundColor: 'transparent',
|
|
556
|
+
cursor: 'pointer',
|
|
557
|
+
}}
|
|
558
|
+
/>
|
|
559
|
+
<input
|
|
560
|
+
type="text"
|
|
561
|
+
value={String(localStyles.backgroundColor ?? '')}
|
|
562
|
+
placeholder="#13161E"
|
|
563
|
+
onChange={(e) => handleStyleChange('backgroundColor', e.target.value)}
|
|
564
|
+
className="zelune-input tabular-nums"
|
|
565
|
+
style={{
|
|
566
|
+
flex: 1,
|
|
567
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
568
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
569
|
+
borderRadius: 6,
|
|
570
|
+
paddingLeft: 10,
|
|
571
|
+
paddingRight: 10,
|
|
572
|
+
color: '#F8FAFC',
|
|
573
|
+
fontSize: 12,
|
|
574
|
+
fontFamily: 'var(--font-mono)',
|
|
575
|
+
}}
|
|
576
|
+
/>
|
|
577
|
+
</div>
|
|
578
|
+
<div style={{ display: 'flex', gap: 5, flexWrap: 'wrap', marginTop: 2 }}>
|
|
579
|
+
{COLOR_SWATCHES.map((hex) => (
|
|
580
|
+
<button
|
|
581
|
+
key={`bg-${hex}`}
|
|
582
|
+
type="button"
|
|
583
|
+
className="zelune-interactive"
|
|
584
|
+
onClick={() => handleStyleChange('backgroundColor', hex)}
|
|
585
|
+
title={`Set backgroundColor to ${hex}`}
|
|
586
|
+
style={{
|
|
587
|
+
width: 20,
|
|
588
|
+
height: 20,
|
|
589
|
+
borderRadius: 5,
|
|
590
|
+
backgroundColor: hex,
|
|
591
|
+
border:
|
|
592
|
+
localStyles.backgroundColor === hex
|
|
593
|
+
? '2px solid #38BDF8'
|
|
594
|
+
: '1px solid rgba(255, 255, 255, 0.14)',
|
|
595
|
+
cursor: 'pointer',
|
|
596
|
+
}}
|
|
597
|
+
/>
|
|
598
|
+
))}
|
|
599
|
+
</div>
|
|
600
|
+
</div>
|
|
601
|
+
|
|
602
|
+
{/* Text Color */}
|
|
603
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
|
604
|
+
<label style={{ fontSize: 11.5, color: '#CBD5E1' }}>Text Color</label>
|
|
605
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
606
|
+
<input
|
|
607
|
+
type="color"
|
|
608
|
+
value={
|
|
609
|
+
typeof localStyles.color === 'string' &&
|
|
610
|
+
localStyles.color.startsWith('#') &&
|
|
611
|
+
localStyles.color.length === 7
|
|
612
|
+
? localStyles.color
|
|
613
|
+
: '#F8FAFC'
|
|
614
|
+
}
|
|
615
|
+
onChange={(e) => handleStyleChange('color', e.target.value)}
|
|
616
|
+
style={{
|
|
617
|
+
width: 30,
|
|
618
|
+
height: 30,
|
|
619
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
620
|
+
borderRadius: 6,
|
|
621
|
+
backgroundColor: 'transparent',
|
|
622
|
+
cursor: 'pointer',
|
|
623
|
+
}}
|
|
624
|
+
/>
|
|
625
|
+
<input
|
|
626
|
+
type="text"
|
|
627
|
+
value={String(localStyles.color ?? '')}
|
|
628
|
+
placeholder="#F8FAFC"
|
|
629
|
+
onChange={(e) => handleStyleChange('color', e.target.value)}
|
|
630
|
+
className="zelune-input tabular-nums"
|
|
631
|
+
style={{
|
|
632
|
+
flex: 1,
|
|
633
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
634
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
635
|
+
borderRadius: 6,
|
|
636
|
+
paddingLeft: 10,
|
|
637
|
+
paddingRight: 10,
|
|
638
|
+
color: '#F8FAFC',
|
|
639
|
+
fontSize: 12,
|
|
640
|
+
fontFamily: 'var(--font-mono)',
|
|
641
|
+
}}
|
|
642
|
+
/>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
</section>
|
|
646
|
+
|
|
647
|
+
{/* SECTION 3: Typography */}
|
|
648
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
649
|
+
<div
|
|
650
|
+
style={{
|
|
651
|
+
display: 'flex',
|
|
652
|
+
alignItems: 'center',
|
|
653
|
+
gap: 6,
|
|
654
|
+
fontSize: 10.5,
|
|
655
|
+
fontFamily: 'var(--font-mono)',
|
|
656
|
+
fontWeight: 600,
|
|
657
|
+
color: 'var(--studio-text-muted)',
|
|
658
|
+
letterSpacing: '0.06em',
|
|
659
|
+
}}
|
|
660
|
+
>
|
|
661
|
+
<Type size={12} color="#38BDF8" />
|
|
662
|
+
<span>TYPOGRAPHY</span>
|
|
663
|
+
</div>
|
|
664
|
+
|
|
665
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
|
|
666
|
+
<div>
|
|
667
|
+
<label style={{ fontSize: 11, color: 'var(--studio-text-secondary)', display: 'block', marginBottom: 4 }}>
|
|
668
|
+
Font Size (px)
|
|
669
|
+
</label>
|
|
670
|
+
<input
|
|
671
|
+
type="number"
|
|
672
|
+
value={localStyles.fontSize ?? ''}
|
|
673
|
+
placeholder="15"
|
|
674
|
+
onChange={(e) => handleNumericStyleChange('fontSize', e.target.value)}
|
|
675
|
+
className="zelune-input tabular-nums"
|
|
676
|
+
style={{
|
|
677
|
+
width: '100%',
|
|
678
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
679
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
680
|
+
borderRadius: 6,
|
|
681
|
+
paddingLeft: 8,
|
|
682
|
+
paddingRight: 8,
|
|
683
|
+
color: '#F8FAFC',
|
|
684
|
+
fontSize: 12,
|
|
685
|
+
fontFamily: 'var(--font-mono)',
|
|
686
|
+
}}
|
|
687
|
+
/>
|
|
688
|
+
</div>
|
|
689
|
+
|
|
690
|
+
<div>
|
|
691
|
+
<label style={{ fontSize: 11, color: 'var(--studio-text-secondary)', display: 'block', marginBottom: 4 }}>
|
|
692
|
+
Font Weight
|
|
693
|
+
</label>
|
|
694
|
+
<select
|
|
695
|
+
value={String(localStyles.fontWeight ?? '600')}
|
|
696
|
+
onChange={(e) => handleStyleChange('fontWeight', e.target.value)}
|
|
697
|
+
className="zelune-input tabular-nums"
|
|
698
|
+
style={{
|
|
699
|
+
width: '100%',
|
|
700
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
701
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
702
|
+
borderRadius: 6,
|
|
703
|
+
paddingLeft: 8,
|
|
704
|
+
paddingRight: 8,
|
|
705
|
+
color: '#F8FAFC',
|
|
706
|
+
fontSize: 12,
|
|
707
|
+
fontFamily: 'var(--font-mono)',
|
|
708
|
+
}}
|
|
709
|
+
>
|
|
710
|
+
{FONT_WEIGHTS.map((w) => (
|
|
711
|
+
<option key={w} value={w}>
|
|
712
|
+
{w}
|
|
713
|
+
</option>
|
|
714
|
+
))}
|
|
715
|
+
</select>
|
|
716
|
+
</div>
|
|
717
|
+
</div>
|
|
718
|
+
</section>
|
|
719
|
+
|
|
720
|
+
{/* SECTION 4: Layout, Spacing & Radius */}
|
|
721
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
722
|
+
<div
|
|
723
|
+
style={{
|
|
724
|
+
display: 'flex',
|
|
725
|
+
alignItems: 'center',
|
|
726
|
+
gap: 6,
|
|
727
|
+
fontSize: 10.5,
|
|
728
|
+
fontFamily: 'var(--font-mono)',
|
|
729
|
+
fontWeight: 600,
|
|
730
|
+
color: 'var(--studio-text-muted)',
|
|
731
|
+
letterSpacing: '0.06em',
|
|
732
|
+
}}
|
|
733
|
+
>
|
|
734
|
+
<Box size={12} color="#38BDF8" />
|
|
735
|
+
<span>SPACING & GEOMETRY</span>
|
|
736
|
+
</div>
|
|
737
|
+
|
|
738
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
|
|
739
|
+
{[
|
|
740
|
+
{ key: 'borderRadius', label: 'Corner Radius' },
|
|
741
|
+
{ key: 'padding', label: 'Padding (All)' },
|
|
742
|
+
{ key: 'paddingHorizontal', label: 'Padding X' },
|
|
743
|
+
{ key: 'paddingVertical', label: 'Padding Y' },
|
|
744
|
+
{ key: 'gap', label: 'Flex Gap' },
|
|
745
|
+
{ key: 'borderWidth', label: 'Border Width' },
|
|
746
|
+
].map(({ key, label }) => (
|
|
747
|
+
<div key={key}>
|
|
748
|
+
<label style={{ fontSize: 11, color: 'var(--studio-text-secondary)', display: 'block', marginBottom: 4 }}>
|
|
749
|
+
{label}
|
|
750
|
+
</label>
|
|
751
|
+
<input
|
|
752
|
+
type="number"
|
|
753
|
+
value={localStyles[key] ?? ''}
|
|
754
|
+
placeholder="—"
|
|
755
|
+
onChange={(e) => handleNumericStyleChange(key, e.target.value)}
|
|
756
|
+
className="zelune-input tabular-nums"
|
|
757
|
+
style={{
|
|
758
|
+
width: '100%',
|
|
759
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
760
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
761
|
+
borderRadius: 6,
|
|
762
|
+
paddingLeft: 8,
|
|
763
|
+
paddingRight: 8,
|
|
764
|
+
color: '#F8FAFC',
|
|
765
|
+
fontSize: 12,
|
|
766
|
+
fontFamily: 'var(--font-mono)',
|
|
767
|
+
}}
|
|
768
|
+
/>
|
|
769
|
+
</div>
|
|
770
|
+
))}
|
|
771
|
+
</div>
|
|
772
|
+
</section>
|
|
773
|
+
|
|
774
|
+
{/* SECTION 5: Tier-2 Antigravity Structural Refactor Button */}
|
|
775
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
776
|
+
<button
|
|
777
|
+
type="button"
|
|
778
|
+
className="zelune-interactive"
|
|
779
|
+
onClick={() => dialogRef.current?.showModal()}
|
|
780
|
+
style={{
|
|
781
|
+
display: 'flex',
|
|
782
|
+
alignItems: 'center',
|
|
783
|
+
justifyContent: 'center',
|
|
784
|
+
gap: 8,
|
|
785
|
+
width: '100%',
|
|
786
|
+
padding: '10px 14px',
|
|
787
|
+
backgroundColor: 'var(--studio-surface-3)',
|
|
788
|
+
color: '#F8FAFC',
|
|
789
|
+
border: '1px solid rgba(56, 189, 248, 0.42)',
|
|
790
|
+
borderRadius: 8,
|
|
791
|
+
fontSize: 12.5,
|
|
792
|
+
fontWeight: 600,
|
|
793
|
+
cursor: 'pointer',
|
|
794
|
+
boxShadow: 'inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 8px 20px -8px rgba(0, 0, 0, 0.6)',
|
|
795
|
+
}}
|
|
796
|
+
>
|
|
797
|
+
<Sparkles size={14} color="#38BDF8" />
|
|
798
|
+
<span>Ask Antigravity to Refactor</span>
|
|
799
|
+
</button>
|
|
800
|
+
</section>
|
|
801
|
+
|
|
802
|
+
{/* SECTION 6: Live JSX Code Snippet */}
|
|
803
|
+
<section style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
804
|
+
<div
|
|
805
|
+
style={{
|
|
806
|
+
display: 'flex',
|
|
807
|
+
alignItems: 'center',
|
|
808
|
+
gap: 6,
|
|
809
|
+
fontSize: 10.5,
|
|
810
|
+
fontFamily: 'var(--font-mono)',
|
|
811
|
+
fontWeight: 600,
|
|
812
|
+
color: 'var(--studio-text-muted)',
|
|
813
|
+
letterSpacing: '0.06em',
|
|
814
|
+
}}
|
|
815
|
+
>
|
|
816
|
+
<Code2 size={12} color="#38BDF8" />
|
|
817
|
+
<span>LIVE JSX AST NODE</span>
|
|
818
|
+
</div>
|
|
819
|
+
<pre
|
|
820
|
+
className="zelune-scroller"
|
|
821
|
+
style={{
|
|
822
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
823
|
+
border: '1px solid var(--studio-border-hairline)',
|
|
824
|
+
borderRadius: 7,
|
|
825
|
+
padding: 10,
|
|
826
|
+
fontSize: 11,
|
|
827
|
+
fontFamily: 'var(--font-mono)',
|
|
828
|
+
color: '#CBD5E1',
|
|
829
|
+
overflowX: 'auto',
|
|
830
|
+
margin: 0,
|
|
831
|
+
lineHeight: 1.45,
|
|
832
|
+
}}
|
|
833
|
+
>
|
|
834
|
+
{inspected.codeSnippet}
|
|
835
|
+
</pre>
|
|
836
|
+
</section>
|
|
837
|
+
</div>
|
|
838
|
+
|
|
839
|
+
{/* Native Modal Dialog with closedby="any" + Emil Kowalski --ease-out-expo entry choreography */}
|
|
840
|
+
<dialog
|
|
841
|
+
ref={dialogRef}
|
|
842
|
+
closedby="any"
|
|
843
|
+
aria-labelledby="agyDialogTitle"
|
|
844
|
+
className="agy-modal-dialog"
|
|
845
|
+
>
|
|
846
|
+
<div
|
|
847
|
+
style={{
|
|
848
|
+
backgroundColor: 'var(--studio-surface-1)',
|
|
849
|
+
color: '#F8FAFC',
|
|
850
|
+
border: '1px solid var(--studio-border-strong)',
|
|
851
|
+
borderRadius: 14,
|
|
852
|
+
padding: 22,
|
|
853
|
+
width: 480,
|
|
854
|
+
maxWidth: '92vw',
|
|
855
|
+
display: 'flex',
|
|
856
|
+
flexDirection: 'column',
|
|
857
|
+
gap: 15,
|
|
858
|
+
boxShadow: '0 28px 60px -12px rgba(0, 0, 0, 0.85)',
|
|
859
|
+
}}
|
|
860
|
+
>
|
|
861
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
862
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
863
|
+
<Sparkles size={16} color="#38BDF8" />
|
|
864
|
+
<h2
|
|
865
|
+
id="agyDialogTitle"
|
|
866
|
+
className="balance-heading"
|
|
867
|
+
style={{ margin: 0, fontSize: 15, fontWeight: 700, letterSpacing: '-0.015em' }}
|
|
868
|
+
>
|
|
869
|
+
Ask Google Antigravity — Structural AST Edit
|
|
870
|
+
</h2>
|
|
871
|
+
</div>
|
|
872
|
+
<button
|
|
873
|
+
type="button"
|
|
874
|
+
className="zelune-interactive"
|
|
875
|
+
onClick={() => dialogRef.current?.close()}
|
|
876
|
+
style={{
|
|
877
|
+
background: 'transparent',
|
|
878
|
+
border: '1px solid transparent',
|
|
879
|
+
borderRadius: 5,
|
|
880
|
+
color: '#94A3B8',
|
|
881
|
+
cursor: 'pointer',
|
|
882
|
+
padding: 4,
|
|
883
|
+
}}
|
|
884
|
+
>
|
|
885
|
+
<X size={16} />
|
|
886
|
+
</button>
|
|
887
|
+
</div>
|
|
888
|
+
|
|
889
|
+
<p style={{ margin: 0, fontSize: 12.5, color: '#94A3B8', lineHeight: 1.55 }}>
|
|
890
|
+
Describe the structural or stateful change you want for{' '}
|
|
891
|
+
<code style={{ color: '#38BDF8', fontFamily: 'var(--font-mono)' }}><{inspected.tagName}></code> in{' '}
|
|
892
|
+
<code className="tabular-nums" style={{ color: '#A5B4FC', fontFamily: 'var(--font-mono)' }}>
|
|
893
|
+
{inspected.file}:{inspected.line}
|
|
894
|
+
</code>
|
|
895
|
+
.
|
|
896
|
+
</p>
|
|
897
|
+
|
|
898
|
+
<textarea
|
|
899
|
+
rows={3}
|
|
900
|
+
value={agentInstruction}
|
|
901
|
+
onChange={(e) => setAgentInstruction(e.target.value)}
|
|
902
|
+
placeholder="e.g., Convert this spec grid into an asymmetric 2-column acoustic telemetry readout..."
|
|
903
|
+
style={{
|
|
904
|
+
width: '100%',
|
|
905
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
906
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
907
|
+
borderRadius: 8,
|
|
908
|
+
padding: 11,
|
|
909
|
+
color: '#F8FAFC',
|
|
910
|
+
fontSize: 12.5,
|
|
911
|
+
fontFamily: 'var(--font-sans)',
|
|
912
|
+
}}
|
|
913
|
+
/>
|
|
914
|
+
|
|
915
|
+
<div
|
|
916
|
+
className="zelune-scroller"
|
|
917
|
+
style={{
|
|
918
|
+
backgroundColor: 'var(--studio-canvas-bg)',
|
|
919
|
+
border: '1px solid var(--studio-border-hairline)',
|
|
920
|
+
borderRadius: 7,
|
|
921
|
+
padding: 10,
|
|
922
|
+
maxHeight: 150,
|
|
923
|
+
overflowY: 'auto',
|
|
924
|
+
fontSize: 11,
|
|
925
|
+
fontFamily: 'var(--font-mono)',
|
|
926
|
+
color: '#94A3B8',
|
|
927
|
+
whiteSpace: 'pre-wrap',
|
|
928
|
+
}}
|
|
929
|
+
>
|
|
930
|
+
{buildAntigravityPrompt()}
|
|
931
|
+
</div>
|
|
932
|
+
|
|
933
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
|
|
934
|
+
<button
|
|
935
|
+
type="button"
|
|
936
|
+
className="zelune-interactive"
|
|
937
|
+
onClick={() => dialogRef.current?.close()}
|
|
938
|
+
style={{
|
|
939
|
+
padding: '8px 13px',
|
|
940
|
+
borderRadius: 7,
|
|
941
|
+
border: '1px solid var(--studio-border-subtle)',
|
|
942
|
+
backgroundColor: 'var(--studio-surface-2)',
|
|
943
|
+
color: '#CBD5E1',
|
|
944
|
+
fontSize: 12,
|
|
945
|
+
fontWeight: 600,
|
|
946
|
+
cursor: 'pointer',
|
|
947
|
+
}}
|
|
948
|
+
>
|
|
949
|
+
Cancel
|
|
950
|
+
</button>
|
|
951
|
+
<button
|
|
952
|
+
type="button"
|
|
953
|
+
className="zelune-interactive"
|
|
954
|
+
onClick={handleCopyPrompt}
|
|
955
|
+
style={{
|
|
956
|
+
display: 'inline-flex',
|
|
957
|
+
alignItems: 'center',
|
|
958
|
+
gap: 6,
|
|
959
|
+
padding: '8px 14px',
|
|
960
|
+
borderRadius: 7,
|
|
961
|
+
border: '1px solid rgba(56, 189, 248, 0.45)',
|
|
962
|
+
backgroundColor: 'var(--studio-surface-3)',
|
|
963
|
+
color: '#F8FAFC',
|
|
964
|
+
fontSize: 12,
|
|
965
|
+
fontWeight: 600,
|
|
966
|
+
cursor: 'pointer',
|
|
967
|
+
}}
|
|
968
|
+
>
|
|
969
|
+
{copiedPrompt ? <Check size={14} color="#34D399" /> : <Copy size={14} color="#38BDF8" />}
|
|
970
|
+
<span>{copiedPrompt ? 'Copied Context for Antigravity!' : 'Copy Agent Prompt Context'}</span>
|
|
971
|
+
</button>
|
|
972
|
+
</div>
|
|
973
|
+
</div>
|
|
974
|
+
</dialog>
|
|
975
|
+
</div>
|
|
976
|
+
)}
|
|
977
|
+
</aside>
|
|
978
|
+
);
|
|
979
|
+
};
|