juxscript 1.1.404 → 1.1.408
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/dist/components/button.d.ts +1 -0
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/button.js +37 -0
- package/dist/components/button.js.map +1 -1
- package/dist/components/c.d.ts +53 -0
- package/dist/components/c.d.ts.map +1 -0
- package/dist/components/c.js +127 -0
- package/dist/components/c.js.map +1 -0
- package/dist/components/charts/barChart.d.ts +119 -0
- package/dist/components/charts/barChart.d.ts.map +1 -0
- package/dist/components/charts/barChart.js +644 -0
- package/dist/components/charts/barChart.js.map +1 -0
- package/dist/components/charts/lineChart.d.ts +104 -0
- package/dist/components/charts/lineChart.d.ts.map +1 -0
- package/dist/components/charts/lineChart.js +466 -0
- package/dist/components/charts/lineChart.js.map +1 -0
- package/dist/components/charts/pieChart.d.ts +93 -0
- package/dist/components/charts/pieChart.d.ts.map +1 -0
- package/dist/components/charts/pieChart.js +397 -0
- package/dist/components/charts/pieChart.js.map +1 -0
- package/dist/components/checkbox.d.ts +2 -0
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +47 -0
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/flex.d.ts +91 -0
- package/dist/components/flex.d.ts.map +1 -0
- package/dist/components/flex.js +166 -0
- package/dist/components/flex.js.map +1 -0
- package/dist/components/g.d.ts +21 -0
- package/dist/components/g.d.ts.map +1 -0
- package/dist/components/g.js +52 -0
- package/dist/components/g.js.map +1 -0
- package/dist/components/input.d.ts +2 -0
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +21 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/jtable.d.ts +47 -0
- package/dist/components/jtable.d.ts.map +1 -0
- package/dist/components/jtable.js +307 -0
- package/dist/components/jtable.js.map +1 -0
- package/dist/components/link.d.ts +1 -0
- package/dist/components/link.d.ts.map +1 -1
- package/dist/components/link.js +17 -0
- package/dist/components/link.js.map +1 -1
- package/dist/components/list.d.ts +1 -0
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +18 -0
- package/dist/components/list.js.map +1 -1
- package/dist/components/menu.d.ts +108 -0
- package/dist/components/menu.d.ts.map +1 -0
- package/dist/components/menu.js +665 -0
- package/dist/components/menu.js.map +1 -0
- package/dist/components/nav.d.ts +1 -0
- package/dist/components/nav.d.ts.map +1 -1
- package/dist/components/nav.js +19 -0
- package/dist/components/nav.js.map +1 -1
- package/dist/components/radio.d.ts +1 -0
- package/dist/components/radio.d.ts.map +1 -1
- package/dist/components/radio.js +23 -0
- package/dist/components/radio.js.map +1 -1
- package/dist/components/routes.d.ts +17 -0
- package/dist/components/routes.d.ts.map +1 -1
- package/dist/components/routes.js +86 -0
- package/dist/components/routes.js.map +1 -1
- package/dist/components/select.d.ts +1 -0
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +17 -0
- package/dist/components/select.js.map +1 -1
- package/dist/components/table.d.ts +1 -0
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +20 -0
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.d.ts +17 -1
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +50 -8
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tag.d.ts +1 -0
- package/dist/components/tag.d.ts.map +1 -1
- package/dist/components/tag.js +16 -0
- package/dist/components/tag.js.map +1 -1
- package/dist/components/widgets/calendar.d.ts +74 -0
- package/dist/components/widgets/calendar.d.ts.map +1 -0
- package/dist/components/widgets/calendar.js +308 -0
- package/dist/components/widgets/calendar.js.map +1 -0
- package/dist/components/widgets/canvas-ai.d.ts +12 -0
- package/dist/components/widgets/canvas-ai.d.ts.map +1 -0
- package/dist/components/widgets/canvas-ai.js +97 -0
- package/dist/components/widgets/canvas-ai.js.map +1 -0
- package/dist/components/widgets/canvas-compile.d.ts +36 -0
- package/dist/components/widgets/canvas-compile.d.ts.map +1 -0
- package/dist/components/widgets/canvas-compile.js +379 -0
- package/dist/components/widgets/canvas-compile.js.map +1 -0
- package/dist/components/widgets/canvas-persist.d.ts +11 -0
- package/dist/components/widgets/canvas-persist.d.ts.map +1 -0
- package/dist/components/widgets/canvas-persist.js +60 -0
- package/dist/components/widgets/canvas-persist.js.map +1 -0
- package/dist/components/widgets/canvas-registry.d.ts +42 -0
- package/dist/components/widgets/canvas-registry.d.ts.map +1 -0
- package/dist/components/widgets/canvas-registry.js +338 -0
- package/dist/components/widgets/canvas-registry.js.map +1 -0
- package/dist/components/widgets/canvas-styles.d.ts +2 -0
- package/dist/components/widgets/canvas-styles.d.ts.map +1 -0
- package/dist/components/widgets/canvas-styles.js +215 -0
- package/dist/components/widgets/canvas-styles.js.map +1 -0
- package/dist/components/widgets/canvas.d.ts +125 -0
- package/dist/components/widgets/canvas.d.ts.map +1 -0
- package/dist/components/widgets/canvas.js +1359 -0
- package/dist/components/widgets/canvas.js.map +1 -0
- package/dist/components/widgets/sidebar.d.ts +100 -0
- package/dist/components/widgets/sidebar.d.ts.map +1 -0
- package/dist/components/widgets/sidebar.js +434 -0
- package/dist/components/widgets/sidebar.js.map +1 -0
- package/dist/components/widgets/stepper.d.ts +87 -0
- package/dist/components/widgets/stepper.d.ts.map +1 -0
- package/dist/components/widgets/stepper.js +388 -0
- package/dist/components/widgets/stepper.js.map +1 -0
- package/dist/generated/jux-registry.d.ts +24 -0
- package/dist/generated/jux-registry.d.ts.map +1 -0
- package/dist/generated/jux-registry.js +90 -0
- package/dist/generated/jux-registry.js.map +1 -0
- package/dist/index.d.ts +39 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -24
- package/dist/index.js.map +1 -1
- package/dist/state/pageState.d.ts +6 -0
- package/dist/state/pageState.d.ts.map +1 -1
- package/dist/state/pageState.js +24 -16
- package/dist/state/pageState.js.map +1 -1
- package/dist/styles/layout-regions-observer.d.ts +7 -0
- package/dist/styles/layout-regions-observer.d.ts.map +1 -0
- package/dist/styles/layout-regions-observer.js +52 -0
- package/dist/styles/layout-regions-observer.js.map +1 -0
- package/dist/utils/colors.d.ts +0 -3
- package/dist/utils/colors.d.ts.map +1 -1
- package/dist/utils/colors.js +20 -6
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/resolveContent.d.ts +11 -0
- package/dist/utils/resolveContent.d.ts.map +1 -0
- package/dist/utils/resolveContent.js +37 -0
- package/dist/utils/resolveContent.js.map +1 -0
- package/dist/utils/theme.d.ts +58 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +172 -0
- package/dist/utils/theme.js.map +1 -0
- package/dist/widgets/canvas.d.ts +3 -69
- package/dist/widgets/canvas.d.ts.map +1 -1
- package/dist/widgets/canvas.js +3 -791
- package/dist/widgets/canvas.js.map +1 -1
- package/juxconfig.example.js +19 -0
- package/machinery/compiler4.js +103 -9
- package/machinery/errors-client.js +171 -67
- package/machinery/jux-errors.js +218 -0
- package/machinery/serve.js +67 -0
- package/package.json +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JUX Theme System
|
|
3
|
+
*
|
|
4
|
+
* Provides top-level CSS variable injection for swappable aesthetics.
|
|
5
|
+
* All jux components reference these CSS custom properties; changing the
|
|
6
|
+
* active theme instantly updates every rendered component.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* jux.theme('gemini'); // apply a built-in preset
|
|
10
|
+
* jux.theme('anthropic'); // swap to Anthropic aesthetic
|
|
11
|
+
* jux.theme({ primary: '252 75% 60%', radius: '8px' }); // custom overrides
|
|
12
|
+
* jux.getTheme(); // → current theme name
|
|
13
|
+
* jux.getThemeNames(); // → ['gemini','anthropic','render','shadcn']
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ── Token interface ──────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface ThemeTokens {
|
|
19
|
+
/* ── Semantic colors (HSL channel values, no `hsl()` wrapper) ── */
|
|
20
|
+
primary: string;
|
|
21
|
+
'primary-foreground': string;
|
|
22
|
+
secondary: string;
|
|
23
|
+
'secondary-foreground': string;
|
|
24
|
+
accent: string;
|
|
25
|
+
'accent-foreground': string;
|
|
26
|
+
destructive: string;
|
|
27
|
+
'destructive-foreground': string;
|
|
28
|
+
muted: string;
|
|
29
|
+
'muted-foreground': string;
|
|
30
|
+
background: string;
|
|
31
|
+
foreground: string;
|
|
32
|
+
card: string;
|
|
33
|
+
'card-foreground': string;
|
|
34
|
+
popover: string;
|
|
35
|
+
'popover-foreground': string;
|
|
36
|
+
border: string;
|
|
37
|
+
input: string;
|
|
38
|
+
ring: string;
|
|
39
|
+
|
|
40
|
+
/* ── Structural tokens ── */
|
|
41
|
+
radius: string;
|
|
42
|
+
'font-sans': string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Built-in presets ─────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
const gemini: ThemeTokens = {
|
|
48
|
+
primary: '217 89% 51%',
|
|
49
|
+
'primary-foreground': '0 0% 100%',
|
|
50
|
+
secondary: '220 14% 96%',
|
|
51
|
+
'secondary-foreground': '220 13% 18%',
|
|
52
|
+
accent: '217 91% 97%',
|
|
53
|
+
'accent-foreground': '217 89% 41%',
|
|
54
|
+
destructive: '0 72% 51%',
|
|
55
|
+
'destructive-foreground': '0 0% 100%',
|
|
56
|
+
muted: '220 14% 96%',
|
|
57
|
+
'muted-foreground': '220 9% 46%',
|
|
58
|
+
background: '0 0% 100%',
|
|
59
|
+
foreground: '220 13% 18%',
|
|
60
|
+
card: '0 0% 100%',
|
|
61
|
+
'card-foreground': '220 13% 18%',
|
|
62
|
+
popover: '220 13% 18%',
|
|
63
|
+
'popover-foreground': '0 0% 98%',
|
|
64
|
+
border: '220 13% 91%',
|
|
65
|
+
input: '220 13% 91%',
|
|
66
|
+
ring: '217 89% 61%',
|
|
67
|
+
radius: '12px',
|
|
68
|
+
'font-sans': "'Google Sans','Inter',system-ui,-apple-system,sans-serif",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const anthropic: ThemeTokens = {
|
|
72
|
+
primary: '24 75% 50%',
|
|
73
|
+
'primary-foreground': '0 0% 100%',
|
|
74
|
+
secondary: '30 15% 95%',
|
|
75
|
+
'secondary-foreground': '24 5% 20%',
|
|
76
|
+
accent: '30 30% 95%',
|
|
77
|
+
'accent-foreground': '24 75% 40%',
|
|
78
|
+
destructive: '0 72% 51%',
|
|
79
|
+
'destructive-foreground': '0 0% 100%',
|
|
80
|
+
muted: '30 15% 95%',
|
|
81
|
+
'muted-foreground': '24 5% 46%',
|
|
82
|
+
background: '40 33% 99%',
|
|
83
|
+
foreground: '24 5% 20%',
|
|
84
|
+
card: '40 33% 99%',
|
|
85
|
+
'card-foreground': '24 5% 20%',
|
|
86
|
+
popover: '24 5% 15%',
|
|
87
|
+
'popover-foreground': '40 33% 98%',
|
|
88
|
+
border: '30 15% 88%',
|
|
89
|
+
input: '30 15% 88%',
|
|
90
|
+
ring: '24 75% 60%',
|
|
91
|
+
radius: '8px',
|
|
92
|
+
'font-sans': "'Inter',system-ui,-apple-system,sans-serif",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const render: ThemeTokens = {
|
|
96
|
+
primary: '252 75% 60%',
|
|
97
|
+
'primary-foreground': '0 0% 100%',
|
|
98
|
+
secondary: '230 10% 96%',
|
|
99
|
+
'secondary-foreground': '230 10% 15%',
|
|
100
|
+
accent: '252 60% 97%',
|
|
101
|
+
'accent-foreground': '252 75% 50%',
|
|
102
|
+
destructive: '0 72% 51%',
|
|
103
|
+
'destructive-foreground': '0 0% 100%',
|
|
104
|
+
muted: '230 10% 96%',
|
|
105
|
+
'muted-foreground': '230 10% 46%',
|
|
106
|
+
background: '0 0% 100%',
|
|
107
|
+
foreground: '230 10% 15%',
|
|
108
|
+
card: '0 0% 100%',
|
|
109
|
+
'card-foreground': '230 10% 15%',
|
|
110
|
+
popover: '230 10% 12%',
|
|
111
|
+
'popover-foreground': '0 0% 98%',
|
|
112
|
+
border: '230 10% 90%',
|
|
113
|
+
input: '230 10% 90%',
|
|
114
|
+
ring: '252 75% 70%',
|
|
115
|
+
radius: '8px',
|
|
116
|
+
'font-sans': "'Inter',system-ui,-apple-system,sans-serif",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const shadcn: ThemeTokens = {
|
|
120
|
+
primary: '222.2 47.4% 11.2%',
|
|
121
|
+
'primary-foreground': '210 40% 98%',
|
|
122
|
+
secondary: '210 40% 96.1%',
|
|
123
|
+
'secondary-foreground': '222.2 47.4% 11.2%',
|
|
124
|
+
accent: '210 40% 96.1%',
|
|
125
|
+
'accent-foreground': '222.2 47.4% 11.2%',
|
|
126
|
+
destructive: '0 84.2% 60.2%',
|
|
127
|
+
'destructive-foreground': '210 40% 98%',
|
|
128
|
+
muted: '210 40% 96.1%',
|
|
129
|
+
'muted-foreground': '215.4 16.3% 46.9%',
|
|
130
|
+
background: '0 0% 100%',
|
|
131
|
+
foreground: '222.2 84% 4.9%',
|
|
132
|
+
card: '0 0% 100%',
|
|
133
|
+
'card-foreground': '222.2 84% 4.9%',
|
|
134
|
+
popover: '0 0% 100%',
|
|
135
|
+
'popover-foreground': '222.2 84% 4.9%',
|
|
136
|
+
border: '214.3 31.8% 91.4%',
|
|
137
|
+
input: '214.3 31.8% 91.4%',
|
|
138
|
+
ring: '215 20.2% 65.1%',
|
|
139
|
+
radius: '6px',
|
|
140
|
+
'font-sans': "system-ui,-apple-system,sans-serif",
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ── Preset registry ──────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
const PRESETS: Record<string, ThemeTokens> = { gemini, anthropic, render, shadcn };
|
|
146
|
+
|
|
147
|
+
// ── State ────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
const STYLE_ID = 'jux-theme-tokens';
|
|
150
|
+
let _currentTheme = 'gemini';
|
|
151
|
+
|
|
152
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
function buildCSS(tokens: ThemeTokens): string {
|
|
155
|
+
const lines = Object.entries(tokens).map(([key, val]) => ` --${key}:${val};`);
|
|
156
|
+
return `:root{\n${lines.join('\n')}\n}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function inject(css: string): void {
|
|
160
|
+
if (typeof document === 'undefined') return; // SSR / Node guard
|
|
161
|
+
let el = document.getElementById(STYLE_ID) as HTMLStyleElement | null;
|
|
162
|
+
if (!el) {
|
|
163
|
+
el = document.createElement('style');
|
|
164
|
+
el.id = STYLE_ID;
|
|
165
|
+
document.head.prepend(el); // first <style> → highest base priority
|
|
166
|
+
}
|
|
167
|
+
el.textContent = css;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Apply a theme by name or by providing a partial/full token object.
|
|
174
|
+
*
|
|
175
|
+
* ```ts
|
|
176
|
+
* jux.theme('gemini');
|
|
177
|
+
* jux.theme('anthropic');
|
|
178
|
+
* jux.theme({ primary: '252 75% 60%', radius: '16px' });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function theme(nameOrTokens: string | Partial<ThemeTokens> = 'gemini'): void {
|
|
182
|
+
if (typeof nameOrTokens === 'string') {
|
|
183
|
+
const preset = PRESETS[nameOrTokens];
|
|
184
|
+
if (!preset) throw new Error(`[jux] Unknown theme "${nameOrTokens}". Available: ${Object.keys(PRESETS).join(', ')}`);
|
|
185
|
+
_currentTheme = nameOrTokens;
|
|
186
|
+
inject(buildCSS(preset));
|
|
187
|
+
} else {
|
|
188
|
+
// Merge overrides onto current base
|
|
189
|
+
const base = PRESETS[_currentTheme] ?? gemini;
|
|
190
|
+
const merged: ThemeTokens = { ...base, ...nameOrTokens };
|
|
191
|
+
_currentTheme = 'custom';
|
|
192
|
+
inject(buildCSS(merged));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Alias for `theme()` — set the active theme. */
|
|
197
|
+
export const setTheme = theme;
|
|
198
|
+
|
|
199
|
+
/** Return the name of the currently active theme. */
|
|
200
|
+
export function getTheme(): string { return _currentTheme; }
|
|
201
|
+
|
|
202
|
+
/** Return the list of built-in preset names. */
|
|
203
|
+
export function getThemeNames(): string[] { return Object.keys(PRESETS); }
|
|
204
|
+
|
|
205
|
+
/** Return a copy of a preset's tokens (or the current custom tokens). */
|
|
206
|
+
export function getThemeTokens(name?: string): ThemeTokens {
|
|
207
|
+
const key = name ?? _currentTheme;
|
|
208
|
+
return { ...(PRESETS[key] ?? PRESETS.gemini) };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Register a custom preset so it can be referenced by name. */
|
|
212
|
+
export function registerTheme(name: string, tokens: ThemeTokens): void {
|
|
213
|
+
PRESETS[name] = tokens;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── Auto-inject default theme on first import ────────────────────────────────
|
|
217
|
+
|
|
218
|
+
theme('gemini');
|
package/machinery/serve.js
CHANGED
|
@@ -112,6 +112,8 @@ app.use((req, res, next) => {
|
|
|
112
112
|
next();
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
+
app.use(express.json());
|
|
116
|
+
|
|
115
117
|
app.get('/favicon.ico', (req, res) => res.status(204).end());
|
|
116
118
|
app.get('/favicon.png', (req, res) => res.status(204).end());
|
|
117
119
|
|
|
@@ -148,6 +150,71 @@ app.get('/__jux_sources.json', (req, res) => {
|
|
|
148
150
|
}
|
|
149
151
|
});
|
|
150
152
|
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════
|
|
154
|
+
// AI ASSIST — Claude proxy endpoint (Anthropic Messages API)
|
|
155
|
+
// ═══════════════════════════════════════════════════════════════
|
|
156
|
+
const aiConfig = rawConfig.ai || {};
|
|
157
|
+
|
|
158
|
+
app.post('/api/ai/suggest', async (req, res) => {
|
|
159
|
+
const apiKey = aiConfig.apiKey || process.env.ANTHROPIC_API_KEY || '';
|
|
160
|
+
if (!apiKey) {
|
|
161
|
+
return res.status(400).json({
|
|
162
|
+
error: 'No Anthropic API key configured. Set ai.apiKey in juxconfig.js or the ANTHROPIC_API_KEY environment variable.'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { prompt, code, cursorLine, systemPrompt } = req.body || {};
|
|
167
|
+
if (!prompt) {
|
|
168
|
+
return res.status(400).json({ error: 'Missing "prompt" in request body.' });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const model = aiConfig.model || 'claude-sonnet-4-20250514';
|
|
172
|
+
const maxTokens = aiConfig.maxTokens || 1024;
|
|
173
|
+
|
|
174
|
+
const userMessage = [
|
|
175
|
+
code ? `Current code:\n\`\`\`js\n${code}\n\`\`\`` : '',
|
|
176
|
+
cursorLine != null ? `Cursor is on line ${cursorLine}.` : '',
|
|
177
|
+
`Request: ${prompt}`,
|
|
178
|
+
].filter(Boolean).join('\n\n');
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const anthropicRes = await fetch('https://api.anthropic.com/v1/messages', {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: {
|
|
184
|
+
'Content-Type': 'application/json',
|
|
185
|
+
'x-api-key': apiKey,
|
|
186
|
+
'anthropic-version': '2023-06-01',
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify({
|
|
189
|
+
model,
|
|
190
|
+
max_tokens: maxTokens,
|
|
191
|
+
system: systemPrompt || 'You are a helpful JUX framework coding assistant.',
|
|
192
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!anthropicRes.ok) {
|
|
197
|
+
const errBody = await anthropicRes.text();
|
|
198
|
+
console.error('Anthropic API error:', anthropicRes.status, errBody);
|
|
199
|
+
return res.status(anthropicRes.status).json({
|
|
200
|
+
error: `Anthropic API error: ${anthropicRes.status}`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const data = await anthropicRes.json();
|
|
205
|
+
const text = data.content?.[0]?.text || '';
|
|
206
|
+
|
|
207
|
+
// Extract code from markdown fences if present
|
|
208
|
+
const codeMatch = text.match(/```(?:js|javascript)?\n([\s\S]*?)```/);
|
|
209
|
+
const suggestion = codeMatch ? codeMatch[1].trim() : text.trim();
|
|
210
|
+
|
|
211
|
+
res.json({ suggestion });
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.error('AI suggest error:', err.message);
|
|
214
|
+
res.status(500).json({ error: err.message });
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
151
218
|
const hotReloadScript = `
|
|
152
219
|
<script>
|
|
153
220
|
(function() {
|