nothumanallowed 16.0.44 → 16.0.46
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.46",
|
|
4
4
|
"description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '16.0.
|
|
8
|
+
export const VERSION = '16.0.46';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -229,6 +229,33 @@ class SandboxManager {
|
|
|
229
229
|
}
|
|
230
230
|
} catch {}
|
|
231
231
|
|
|
232
|
+
// Pre-flight: auto-extend CSS if coverage is below threshold.
|
|
233
|
+
// Detects "incomplete styling" (LLM truncated CSS generation) and calls
|
|
234
|
+
// the LLM automatically to extend the CSS file with rules for the missing
|
|
235
|
+
// HTML selectors. NO user intervention required.
|
|
236
|
+
// Iterates up to 2 times to handle large projects where a single extend
|
|
237
|
+
// might not cover all selectors (e.g. 200+ missing selectors).
|
|
238
|
+
try {
|
|
239
|
+
const projectName = path.basename(projectDir);
|
|
240
|
+
const cfg = loadConfig();
|
|
241
|
+
const maxPasses = 2;
|
|
242
|
+
let totalCovered = 0;
|
|
243
|
+
for (let pass = 1; pass <= maxPasses; pass++) {
|
|
244
|
+
const styleResult = await _autoExtendStylesIfNeeded(projectName, cfg, emit, { minCoverage: 0.85 });
|
|
245
|
+
if (!styleResult.extended) {
|
|
246
|
+
if (styleResult.reason === 'coverage acceptable') break;
|
|
247
|
+
// LLM failed or skipped — don't loop on a broken provider
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
const newlyCovered = (styleResult.missingBefore || 0) - (styleResult.missingAfter || 0);
|
|
251
|
+
totalCovered += newlyCovered;
|
|
252
|
+
emit({ type: 'status', msg: `Pass ${pass}/${maxPasses}: ${styleResult.file} extended — ${newlyCovered} new selectors covered (total ${totalCovered}).` });
|
|
253
|
+
if ((styleResult.coverageAfter || 0) >= 0.85) break;
|
|
254
|
+
}
|
|
255
|
+
} catch (e) {
|
|
256
|
+
emit({ type: 'warn', msg: `Auto-extend styles failed: ${(e.message || e).slice(0, 200)}` });
|
|
257
|
+
}
|
|
258
|
+
|
|
232
259
|
// Pre-flight: complete missing CSS/JS assets via sibling fill + LLM gen.
|
|
233
260
|
// Replaces the previous "empty placeholder" strategy with real content
|
|
234
261
|
// when available. LLM calls capped at 8 files per boot to keep latency
|
|
@@ -3460,6 +3487,17 @@ ${original.slice(0, 12_000)}`;
|
|
|
3460
3487
|
} catch (e) { console.error('[scan] CRASH:', e); sendError(res, 500, e.message); }
|
|
3461
3488
|
});
|
|
3462
3489
|
|
|
3490
|
+
// ── Auto-extend CSS when coverage is below threshold ──────────────────────
|
|
3491
|
+
router.post('/api/studio/webcraft/extend-styles', async (req, res) => {
|
|
3492
|
+
try {
|
|
3493
|
+
const { projectName, minCoverage } = await parseBody(req);
|
|
3494
|
+
if (!projectName) return sendError(res, 400, 'projectName required');
|
|
3495
|
+
const config = loadConfig();
|
|
3496
|
+
const result = await _autoExtendStylesIfNeeded(projectName, config, null, { minCoverage: minCoverage ?? 0.6 });
|
|
3497
|
+
sendJSON(res, 200, result);
|
|
3498
|
+
} catch (e) { sendError(res, 500, e.message); }
|
|
3499
|
+
});
|
|
3500
|
+
|
|
3463
3501
|
// ── Smart asset completion (sibling fill + LLM generation) ────────────────
|
|
3464
3502
|
// For missing CSS/JS referenced in HTML: first try copying from sibling
|
|
3465
3503
|
// files with real content (deterministic, instant), then LLM-generate
|
|
@@ -4058,6 +4096,259 @@ export async function _completeMissingAssets(projectName, config, emit) {
|
|
|
4058
4096
|
return report;
|
|
4059
4097
|
}
|
|
4060
4098
|
|
|
4099
|
+
/**
|
|
4100
|
+
* Analyze CSS coverage for an HTML project. Returns the percentage of HTML
|
|
4101
|
+
* classes/ids that have at least one matching CSS rule.
|
|
4102
|
+
*
|
|
4103
|
+
* Returns {
|
|
4104
|
+
* coverage: 0..1,
|
|
4105
|
+
* htmlSelectors: ['.cta', '.hero', '#main', ...], // all unique selectors in HTML
|
|
4106
|
+
* cssSelectors: Set of selectors that have CSS rules
|
|
4107
|
+
* missing: ['.testimonial', '.pricing-card', ...] // HTML selectors with no CSS
|
|
4108
|
+
* tagCount: number of HTML elements found
|
|
4109
|
+
* cssRuleCount: number of rules in all CSS files
|
|
4110
|
+
* imgCount: number of <img> tags
|
|
4111
|
+
* hasImgRule: boolean — does any CSS rule target `img` with max-width?
|
|
4112
|
+
* }
|
|
4113
|
+
*/
|
|
4114
|
+
export function _analyzeCssCoverage(projectDir) {
|
|
4115
|
+
const result = {
|
|
4116
|
+
coverage: 1,
|
|
4117
|
+
htmlSelectors: [],
|
|
4118
|
+
cssSelectors: new Set(),
|
|
4119
|
+
missing: [],
|
|
4120
|
+
tagCount: 0,
|
|
4121
|
+
cssRuleCount: 0,
|
|
4122
|
+
imgCount: 0,
|
|
4123
|
+
hasImgRule: false,
|
|
4124
|
+
cssFiles: [],
|
|
4125
|
+
htmlFiles: [],
|
|
4126
|
+
};
|
|
4127
|
+
|
|
4128
|
+
// Gather all HTML and CSS files
|
|
4129
|
+
const stack = [projectDir];
|
|
4130
|
+
const skipDirs = new Set(['node_modules', '.git', '.nha-shims', 'dist', 'build', '.next']);
|
|
4131
|
+
while (stack.length) {
|
|
4132
|
+
const cur = stack.pop();
|
|
4133
|
+
let entries;
|
|
4134
|
+
try { entries = fs.readdirSync(cur, { withFileTypes: true }); } catch { continue; }
|
|
4135
|
+
for (const ent of entries) {
|
|
4136
|
+
if (skipDirs.has(ent.name) || ent.name.startsWith('.')) continue;
|
|
4137
|
+
const abs = path.join(cur, ent.name);
|
|
4138
|
+
if (ent.isDirectory()) { stack.push(abs); continue; }
|
|
4139
|
+
const ext = path.extname(ent.name).toLowerCase();
|
|
4140
|
+
if (ext === '.html' || ext === '.htm') result.htmlFiles.push(abs);
|
|
4141
|
+
else if (ext === '.css') result.cssFiles.push(abs);
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
if (result.htmlFiles.length === 0) return result;
|
|
4146
|
+
|
|
4147
|
+
// Extract HTML classes/ids
|
|
4148
|
+
const htmlSelectorSet = new Set();
|
|
4149
|
+
for (const htmlAbs of result.htmlFiles) {
|
|
4150
|
+
let html;
|
|
4151
|
+
try { html = fs.readFileSync(htmlAbs, 'utf-8'); } catch { continue; }
|
|
4152
|
+
// Count tags
|
|
4153
|
+
const tagMatches = html.match(/<[a-zA-Z][a-zA-Z0-9-]*/g) || [];
|
|
4154
|
+
result.tagCount += tagMatches.length;
|
|
4155
|
+
// Count img tags specifically
|
|
4156
|
+
result.imgCount += (html.match(/<img\b/gi) || []).length;
|
|
4157
|
+
// Extract classes
|
|
4158
|
+
const classRe = /\bclass\s*=\s*["']([^"']+)["']/g;
|
|
4159
|
+
let m;
|
|
4160
|
+
while ((m = classRe.exec(html)) !== null) {
|
|
4161
|
+
for (const cls of m[1].split(/\s+/)) {
|
|
4162
|
+
if (cls.trim()) htmlSelectorSet.add('.' + cls.trim());
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
// Extract IDs
|
|
4166
|
+
const idRe = /\bid\s*=\s*["']([^"']+)["']/g;
|
|
4167
|
+
while ((m = idRe.exec(html)) !== null) {
|
|
4168
|
+
if (m[1].trim()) htmlSelectorSet.add('#' + m[1].trim());
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
result.htmlSelectors = [...htmlSelectorSet];
|
|
4172
|
+
|
|
4173
|
+
if (result.htmlSelectors.length === 0) {
|
|
4174
|
+
result.coverage = 1;
|
|
4175
|
+
return result;
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
// Extract CSS selectors (simple: split on { and look at preceding token)
|
|
4179
|
+
for (const cssAbs of result.cssFiles) {
|
|
4180
|
+
let css;
|
|
4181
|
+
try { css = fs.readFileSync(cssAbs, 'utf-8'); } catch { continue; }
|
|
4182
|
+
// Strip comments
|
|
4183
|
+
css = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
4184
|
+
// Find rule blocks: anything before { ... }
|
|
4185
|
+
const ruleRe = /([^{}]+)\{[^{}]*\}/g;
|
|
4186
|
+
let m;
|
|
4187
|
+
while ((m = ruleRe.exec(css)) !== null) {
|
|
4188
|
+
result.cssRuleCount++;
|
|
4189
|
+
const selectorList = m[1].trim();
|
|
4190
|
+
if (selectorList.startsWith('@')) continue; // @media, @keyframes etc.
|
|
4191
|
+
// Split combined selectors (.a, .b, .c) and extract bare class/id tokens
|
|
4192
|
+
for (const sel of selectorList.split(',')) {
|
|
4193
|
+
const trimmed = sel.trim();
|
|
4194
|
+
// Find all .classname and #id tokens in this selector
|
|
4195
|
+
const classM = trimmed.match(/\.[\w-]+/g) || [];
|
|
4196
|
+
const idM = trimmed.match(/#[\w-]+/g) || [];
|
|
4197
|
+
for (const t of [...classM, ...idM]) result.cssSelectors.add(t);
|
|
4198
|
+
// Track if any rule targets `img`
|
|
4199
|
+
if (/(^|[\s,>+~])img(\s*[.{#:]|\s*$)/.test(trimmed)) {
|
|
4200
|
+
if (css.slice(m.index, m.index + m[0].length).match(/max-width|object-fit/)) {
|
|
4201
|
+
result.hasImgRule = true;
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
|
|
4208
|
+
// Compute coverage
|
|
4209
|
+
for (const sel of result.htmlSelectors) {
|
|
4210
|
+
if (!result.cssSelectors.has(sel)) result.missing.push(sel);
|
|
4211
|
+
}
|
|
4212
|
+
result.coverage = (result.htmlSelectors.length - result.missing.length) / result.htmlSelectors.length;
|
|
4213
|
+
|
|
4214
|
+
return result;
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
/**
|
|
4218
|
+
* Auto-extend CSS by calling LLM when coverage is below threshold or layout
|
|
4219
|
+
* is clearly broken (e.g. <img> tags with no max-width rule). No user prompt;
|
|
4220
|
+
* triggers automatically in pre-flight.
|
|
4221
|
+
*/
|
|
4222
|
+
export async function _autoExtendStylesIfNeeded(projectName, config, emit, opts) {
|
|
4223
|
+
opts = opts || {};
|
|
4224
|
+
const minCoverage = opts.minCoverage ?? 0.6;
|
|
4225
|
+
const dir = ProjectStore.dir(projectName);
|
|
4226
|
+
if (!fs.existsSync(dir)) return { extended: false, reason: 'project not found' };
|
|
4227
|
+
|
|
4228
|
+
const analysis = _analyzeCssCoverage(dir);
|
|
4229
|
+
if (analysis.htmlSelectors.length === 0) return { extended: false, reason: 'no HTML selectors' };
|
|
4230
|
+
|
|
4231
|
+
const needsExtend =
|
|
4232
|
+
analysis.coverage < minCoverage ||
|
|
4233
|
+
(analysis.imgCount >= 2 && !analysis.hasImgRule) ||
|
|
4234
|
+
(analysis.tagCount > 50 && analysis.cssRuleCount < 20);
|
|
4235
|
+
|
|
4236
|
+
if (!needsExtend) {
|
|
4237
|
+
return {
|
|
4238
|
+
extended: false,
|
|
4239
|
+
reason: 'coverage acceptable',
|
|
4240
|
+
coverage: analysis.coverage,
|
|
4241
|
+
missing: analysis.missing.length,
|
|
4242
|
+
};
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
// Find the primary CSS file to extend (largest non-placeholder)
|
|
4246
|
+
const cssCandidates = [];
|
|
4247
|
+
for (const cssAbs of analysis.cssFiles) {
|
|
4248
|
+
try {
|
|
4249
|
+
const content = fs.readFileSync(cssAbs, 'utf-8');
|
|
4250
|
+
if (/nha-webcraft:.*placeholder/i.test(content.slice(0, 200))) continue;
|
|
4251
|
+
cssCandidates.push({ abs: cssAbs, size: content.length, content });
|
|
4252
|
+
} catch {}
|
|
4253
|
+
}
|
|
4254
|
+
cssCandidates.sort((a, b) => b.size - a.size);
|
|
4255
|
+
if (cssCandidates.length === 0) return { extended: false, reason: 'no real CSS files to extend' };
|
|
4256
|
+
|
|
4257
|
+
const target = cssCandidates[0];
|
|
4258
|
+
const targetRel = path.relative(dir, target.abs).replace(/\\/g, '/');
|
|
4259
|
+
|
|
4260
|
+
// Build prompt with HTML samples + current CSS + missing selectors
|
|
4261
|
+
let htmlSample = '';
|
|
4262
|
+
for (const htmlAbs of analysis.htmlFiles.slice(0, 3)) {
|
|
4263
|
+
try {
|
|
4264
|
+
const c = fs.readFileSync(htmlAbs, 'utf-8');
|
|
4265
|
+
htmlSample += `### ${path.relative(dir, htmlAbs)}\n${c.slice(0, 3500)}\n\n`;
|
|
4266
|
+
} catch {}
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
const sys = `You are an expert frontend designer producing PRODUCTION-QUALITY CSS.
|
|
4270
|
+
|
|
4271
|
+
DESIGN REQUIREMENTS (NON-NEGOTIABLE):
|
|
4272
|
+
- WCAG AA contrast: text-on-background ratio >= 4.5:1. NO washed-out pastels for text. Body text must be near-black on light bg, or near-white on dark bg.
|
|
4273
|
+
- Use VIBRANT accent colors with sufficient saturation (HSL S >= 60%, L between 35-65% for accents).
|
|
4274
|
+
- Cover EVERY selector listed — including footer, header, nav, hero, sections, cards, buttons, forms, modals, tooltips.
|
|
4275
|
+
- Use modern CSS: flex/grid layouts, custom properties for colors, smooth transitions (200-300ms), subtle shadows.
|
|
4276
|
+
|
|
4277
|
+
OUTPUT: ONLY the complete extended CSS file content. No markdown fences, no explanations, no preamble. The output will be written directly to disk.`;
|
|
4278
|
+
const user =
|
|
4279
|
+
`Extend this CSS file: \`${targetRel}\`\n\n` +
|
|
4280
|
+
`Current CSS (${target.size} bytes, ${analysis.cssRuleCount} rules):\n\`\`\`css\n${target.content.slice(0, 6000)}\n\`\`\`\n\n` +
|
|
4281
|
+
`ALL missing selectors (${analysis.missing.length} — cover every single one):\n${analysis.missing.join(', ')}\n\n` +
|
|
4282
|
+
`HTML files for context:\n${htmlSample}\n\n` +
|
|
4283
|
+
`Output the COMPLETE extended CSS file. Required additions:\n` +
|
|
4284
|
+
`- img { max-width: 100%; height: auto; object-fit: cover; display: block; }\n` +
|
|
4285
|
+
`- Footer, header, nav with proper layout and visible styling (NOT transparent backgrounds with low-contrast text)\n` +
|
|
4286
|
+
`- Rules for EVERY one of the ${analysis.missing.length} missing selectors above\n` +
|
|
4287
|
+
`- Responsive breakpoints at 1024px, 768px, 480px (mobile-first)\n` +
|
|
4288
|
+
`- Hover/focus/transition states for buttons, links, cards\n` +
|
|
4289
|
+
`- Color contrast must pass WCAG AA: text-on-bg >= 4.5:1`;
|
|
4290
|
+
|
|
4291
|
+
if (emit) emit({ type: 'status', msg: `CSS coverage ${(analysis.coverage * 100).toFixed(0)}% (${analysis.missing.length} selectors missing). Auto-extending ${targetRel} via LLM (timeout 60s)...` });
|
|
4292
|
+
|
|
4293
|
+
// Call LLM with timeout + progress heartbeat. Otherwise a slow/stuck Liara
|
|
4294
|
+
// leaves the user staring at "Auto-extending..." forever with no feedback.
|
|
4295
|
+
let body = '';
|
|
4296
|
+
let lastChunkAt = Date.now();
|
|
4297
|
+
const startedAt = Date.now();
|
|
4298
|
+
const timeoutMs = 60_000;
|
|
4299
|
+
const heartbeatInterval = setInterval(() => {
|
|
4300
|
+
if (!emit) return;
|
|
4301
|
+
const elapsed = ((Date.now() - startedAt) / 1000).toFixed(0);
|
|
4302
|
+
const sinceLast = ((Date.now() - lastChunkAt) / 1000).toFixed(0);
|
|
4303
|
+
emit({ type: 'status', msg: `LLM extend: ${elapsed}s elapsed, ${body.length} bytes received (${sinceLast}s since last chunk)` });
|
|
4304
|
+
}, 5_000);
|
|
4305
|
+
|
|
4306
|
+
try {
|
|
4307
|
+
await Promise.race([
|
|
4308
|
+
callLLMStream(config, sys, user, (chunk) => {
|
|
4309
|
+
body += chunk;
|
|
4310
|
+
lastChunkAt = Date.now();
|
|
4311
|
+
}, { max_tokens: 8192 }),
|
|
4312
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('LLM timeout after ' + (timeoutMs / 1000) + 's')), timeoutMs)),
|
|
4313
|
+
]);
|
|
4314
|
+
} catch (e) {
|
|
4315
|
+
clearInterval(heartbeatInterval);
|
|
4316
|
+
if (emit) emit({ type: 'warn', msg: `CSS extend failed: ${(e.message || e).slice(0, 200)}. Sandbox continues with current CSS — open chat to extend manually.` });
|
|
4317
|
+
return { extended: false, reason: 'llm_failed', error: e.message, partialBytes: body.length };
|
|
4318
|
+
}
|
|
4319
|
+
clearInterval(heartbeatInterval);
|
|
4320
|
+
|
|
4321
|
+
// Strip markdown fences
|
|
4322
|
+
body = body.replace(/^```[a-zA-Z]*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
|
|
4323
|
+
if (_looksLikeLLMError(body) || body.length < target.size * 0.5) {
|
|
4324
|
+
if (emit) emit({ type: 'warn', msg: `CSS extend produced suspicious output (${body.length} bytes vs ${target.size} original) — keeping original.` });
|
|
4325
|
+
return { extended: false, reason: 'output_too_short_or_error' };
|
|
4326
|
+
}
|
|
4327
|
+
|
|
4328
|
+
// Backup + write
|
|
4329
|
+
try {
|
|
4330
|
+
fs.writeFileSync(target.abs + '.before-extend-' + Date.now(), target.content, 'utf-8');
|
|
4331
|
+
fs.writeFileSync(target.abs, body, 'utf-8');
|
|
4332
|
+
} catch (e) {
|
|
4333
|
+
return { extended: false, reason: 'write_failed', error: e.message };
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
// Re-analyze to confirm improvement
|
|
4337
|
+
const after = _analyzeCssCoverage(dir);
|
|
4338
|
+
if (emit) emit({ type: 'status', msg: `CSS extended: ${targetRel} ${target.size} → ${body.length} bytes. Coverage ${(analysis.coverage * 100).toFixed(0)}% → ${(after.coverage * 100).toFixed(0)}%.` });
|
|
4339
|
+
|
|
4340
|
+
return {
|
|
4341
|
+
extended: true,
|
|
4342
|
+
file: targetRel,
|
|
4343
|
+
sizeBefore: target.size,
|
|
4344
|
+
sizeAfter: body.length,
|
|
4345
|
+
coverageBefore: analysis.coverage,
|
|
4346
|
+
coverageAfter: after.coverage,
|
|
4347
|
+
missingBefore: analysis.missing.length,
|
|
4348
|
+
missingAfter: after.missing.length,
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
|
|
4061
4352
|
export function autoRepairProject(projectName) {
|
|
4062
4353
|
const dir = ProjectStore.dir(projectName);
|
|
4063
4354
|
if (!fs.existsSync(dir)) throw new Error('project not found');
|
|
@@ -854,7 +854,7 @@ Errore: `+e.msg,L(!1))}catch{}}}}catch(e){e instanceof DOMException&&e.name===`A
|
|
|
854
854
|
[Piano approvato — procedi con le modifiche]`,null,[])}function tn(e){e&&Array.from(e).forEach(e=>{let t=new FileReader;t.onload=t=>{let n=(t.target?.result).split(`,`)[1];Ve(t=>[...t,{name:e.name,mimeType:e.type,base64:n,size:e.size}])},t.readAsDataURL(e)})}let nn=c&&g.length>0,rn=g[y],U=ze||N;return(0,k.jsxs)(`div`,{className:Q.root,children:[(0,k.jsxs)(`div`,{className:Q.header,children:[(0,k.jsxs)(`div`,{children:[(0,k.jsxs)(`div`,{className:Q.title,children:[`⚙ WebCraft`,c?` — ${c}`:``]}),!c&&(0,k.jsx)(`div`,{className:Q.subtitle,children:`Genera progetti web completi con agenti AI`})]}),(0,k.jsxs)(`div`,{className:Q.headerTabs,children:[(0,k.jsx)(`button`,{className:`${Q.tabBtn} ${t===`new`?Q.tabActive:``}`,onClick:async()=>{if(!(ee.size>0&&!confirm(`${ee.size} unsaved file(s). Discard changes and create new project?`))){if(ct){try{await fetch(`/api/studio/webcraft/sandbox`,{method:`DELETE`})}catch{}lt(null)}v([]),b(0),S(null),w(null),te(new Set),Ie([]),l(``),d(``),Re(``),Ge([]),qe(!1),Pe(null),Ce(!1),L(!1),oe(!1),ce(null),n(`new`)}},children:`+ Nuovo`}),(0,k.jsx)(`button`,{className:`${Q.tabBtn} ${t===`projects`?Q.tabActive:``}`,onClick:()=>{n(`projects`),Qt()},children:`📁 Progetti`})]})]}),(0,k.jsx)(`div`,{className:Q.body,children:t===`projects`?(0,k.jsx)(`div`,{className:Q.projectsList,children:ot.length===0?(0,k.jsxs)(`div`,{className:Q.emptyProjects,children:[(0,k.jsx)(`span`,{className:Q.emptyIcon,children:`📁`}),(0,k.jsx)(`span`,{children:e(`webcraft.noProjects`)}),(0,k.jsx)(`span`,{className:Q.emptyHint,children:`Crea un progetto nella tab Nuovo`})]}):ot.map(e=>(0,k.jsxs)(`div`,{className:Q.projectCard,children:[(0,k.jsxs)(`div`,{className:Q.projectInfo,children:[(0,k.jsx)(`div`,{className:Q.projectName,children:e.name}),(0,k.jsx)(`div`,{className:Q.projectDesc,children:e.description}),(0,k.jsxs)(`div`,{className:Q.projectMeta,children:[(0,k.jsxs)(`span`,{children:[`📄 `,e.fileCount,` file`]}),(0,k.jsxs)(`span`,{children:[`📅 `,e.createdAt?new Date(e.createdAt).toLocaleString():``]})]})]}),(0,k.jsx)(`button`,{className:Q.openBtn,onClick:()=>H(e),children:`↗ Apri`}),(0,k.jsx)(`button`,{className:Q.deleteBtn,onClick:()=>$t(e),children:`🗑`})]},e.name))}):(0,k.jsxs)(`div`,{className:Q.editor,"data-mobile-view":a?o:void 0,children:[(0,k.jsxs)(`div`,{className:Q.examples,children:[(0,k.jsx)(`div`,{className:Q.sectionLabel,children:`Esempi`}),(0,k.jsx)(`div`,{className:Q.examplePills,children:eE.map(e=>(0,k.jsx)(`button`,{className:Q.examplePill,onClick:()=>{l(e.name),d(e.desc),Re(e.desc)},children:e.name},e.name))})]}),(0,k.jsxs)(`div`,{className:Q.editorCols,children:[(0,k.jsxs)(`div`,{className:Q.leftSidebar,children:[(0,k.jsxs)(`div`,{className:Q.panel,children:[(0,k.jsx)(`div`,{className:Q.panelTitle,children:`Blocchi`}),tE.map(e=>(0,k.jsxs)(`label`,{className:Q.blockLabel,children:[(0,k.jsx)(`input`,{type:`checkbox`,checked:f[e.key],onChange:t=>p(n=>({...n,[e.key]:t.target.checked})),className:Q.blockCheck}),(0,k.jsx)(`span`,{children:e.icon}),(0,k.jsx)(`span`,{children:e.label})]},e.key))]}),f.auth&&(0,k.jsxs)(`div`,{className:Q.panel,children:[(0,k.jsxs)(`div`,{className:Q.panelHeader,children:[(0,k.jsx)(`div`,{className:Q.panelTitle,children:`Campi Auth`}),(0,k.jsx)(`button`,{className:Q.addBtn,onClick:()=>h(e=>[...e,{label:`New field`,type:`text`,required:!1}]),children:`+ Campo`})]}),m.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.authField,children:[(0,k.jsx)(`input`,{value:e.label,onChange:e=>h(n=>n.map((n,r)=>r===t?{...n,label:e.target.value}:n)),className:Q.authFieldInput}),(0,k.jsx)(`select`,{value:e.type,onChange:e=>h(n=>n.map((n,r)=>r===t?{...n,type:e.target.value}:n)),className:Q.authFieldSelect,children:[`text`,`email`,`password`,`tel`,`date`,`number`].map(e=>(0,k.jsx)(`option`,{value:e,children:e},e))}),(0,k.jsx)(`input`,{type:`checkbox`,checked:e.required,onChange:e=>h(n=>n.map((n,r)=>r===t?{...n,required:e.target.checked}:n)),title:`Required`,className:Q.authFieldReq}),(0,k.jsx)(`button`,{onClick:()=>h(e=>e.filter((e,n)=>n!==t)),className:Q.removeFieldBtn,children:`×`})]},t))]}),(0,k.jsxs)(`div`,{className:Q.panel,children:[(0,k.jsxs)(`div`,{className:Q.panelHeader,children:[(0,k.jsx)(`div`,{className:Q.panelTitle,children:`🗂 Contesto AI`}),(0,k.jsx)(`button`,{className:Q.addBtn,onClick:()=>Ye({mode:`new`,idx:null,name:``,content:``,type:`skill`,generating:!1}),children:`+ Skill`})]}),We.length>0?(0,k.jsx)(`div`,{className:Q.skillsList,children:We.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.skillRow,children:[(0,k.jsx)(`span`,{className:Q.skillIcon,children:aE(e.type)}),(0,k.jsx)(`span`,{className:Q.skillName,title:e.name,children:e.name}),(0,k.jsx)(`span`,{className:`${Q.skillBadge} ${Q[`skillBadge_`+e.type]}`,children:e.type}),!e.content&&e.type!==`log`&&(0,k.jsx)(`span`,{className:Q.skillEmpty,children:`⚠`}),(0,k.jsx)(`button`,{className:Q.skillBtn,onClick:()=>Ye({mode:e.type===`log`?`view`:`edit`,idx:t,name:e.name,content:e.content,type:e.type,generating:!1}),children:e.type===`log`?`👁`:`✏`}),e.type!==`memory`&&e.type!==`provider`&&e.type!==`log`&&(0,k.jsx)(`button`,{className:Q.skillBtn,onClick:()=>Zt(t),children:`🗑`}),e.type===`log`&&(0,k.jsx)(`button`,{className:Q.skillBtn,onClick:()=>Xt(t),children:`🗑`})]},t))}):(0,k.jsx)(`div`,{className:Q.skillsEmpty,children:Ke?`Nessun file di contesto. Clicca "+ Skill" per aggiungerne uno.`:`Crea o carica un progetto per i file di contesto.`})]}),Xe.length>0&&(0,k.jsxs)(`div`,{className:Q.panel,children:[(0,k.jsx)(`div`,{className:Q.panelTitle,children:`💾 Snapshot`}),Xe.slice(0,5).map(e=>{let t=e.ts.replace(`T`,` `).slice(0,16);return(0,k.jsxs)(`div`,{className:Q.snapshotRow,children:[(0,k.jsx)(`span`,{className:Q.snapshotTs,children:t}),(0,k.jsxs)(`span`,{className:Q.snapshotCount,children:[e.fileCount,`f`]}),(0,k.jsx)(`button`,{className:Q.snapshotBtn,onClick:()=>Vt(e.ts),children:`↺`})]},e.ts)})]}),N&&(0,k.jsx)(`div`,{className:Q.genStatus,children:`⏳ Generazione...`}),we&&(0,k.jsxs)(`div`,{className:Q.repairStatus,children:[(0,k.jsx)(`div`,{className:Q.repairStatusTitle,children:`🔧 Correzione automatica...`}),(0,k.jsxs)(`div`,{className:Q.repairStatusProg,children:[Ee,` / `,De,` file`]}),(0,k.jsx)(`div`,{className:Q.repairStatusFile,children:ke})]}),g.length>0&&!N&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`div`,{className:Q.actionRow,children:[(0,k.jsx)(`button`,{className:Q.actionBtn,onClick:Jt,children:`⬇ ZIP`}),(0,k.jsx)(`button`,{className:Q.actionBtnIcon,title:`Syntax check`,onClick:Ht,children:`✅`}),(0,k.jsx)(`button`,{className:`${Q.actionBtnIcon} ${Qe?Q.actionBtnActive:``}`,title:`Grep`,onClick:()=>$e(!Qe),children:`🔍`}),(0,k.jsx)(`button`,{className:Q.actionBtnIcon,title:`Snapshot`,onClick:zt,children:`💾`})]}),g.some(e=>e._error||e._syntaxError)&&!we&&(0,k.jsx)(`button`,{className:Q.repairBtn,onClick:V,children:`🔧 Correggi tutti i file rossi`}),(0,k.jsx)(`button`,{className:Q.sandboxBtn,onClick:()=>{c?Wt(c):i(`preview`)},children:ft?`⏳ Starting...`:ct?`🌐 Sandbox Live`:`▶ Sandbox`}),Ne&&(0,k.jsxs)(`div`,{className:Q.statsBar,children:[(0,k.jsxs)(`span`,{children:[`⏱ `,Ne.seconds>=60?`${Math.floor(Ne.seconds/60)}m ${Ne.seconds%60}s`:`${Ne.seconds}s`]}),(0,k.jsxs)(`span`,{children:[`↑ `,Ne.tokIn.toLocaleString(),` tok`]}),(0,k.jsxs)(`span`,{children:[`↓ `,Ne.tokOut.toLocaleString(),` tok`]}),(0,k.jsxs)(`span`,{children:[`📄 `,Ne.files,` file`]})]})]})]}),(0,k.jsxs)(`div`,{className:Q.rightPanel,children:[(0,k.jsxs)(`div`,{className:Q.rightTabBar,children:[(0,k.jsx)(`button`,{className:`${Q.rightTab} ${r===`preview`?``:Q.rightTabActive}`,onClick:()=>i(`files`),children:`📄 File`}),(0,k.jsx)(`button`,{className:`${Q.rightTab} ${r===`preview`?Q.rightTabActive:``}`,onClick:()=>i(`preview`),children:`🌐 Sandbox`})]}),we&&(0,k.jsxs)(`div`,{className:Q.repairBar,children:[(0,k.jsxs)(`div`,{className:Q.repairBarRow,children:[(0,k.jsx)(`span`,{className:Q.repairBarIcon,children:`🔧`}),(0,k.jsx)(`span`,{className:Q.repairBarLabel,children:`Auto-fix`}),(0,k.jsx)(`span`,{className:Q.repairBarFile,children:ke}),(0,k.jsxs)(`span`,{className:Q.repairBarCounter,children:[Ee,` / `,De]}),(0,k.jsx)(`span`,{className:Q.repairBarTime,children:R}),(0,k.jsx)(`button`,{className:Q.stopBtn,onClick:Lt,children:`⏹ Stop`})]}),(0,k.jsx)(`div`,{className:Q.progressTrack,children:(0,k.jsx)(`div`,{className:Q.repairProgress,style:{width:De>0?`${Math.round(Ee/De*100)}%`:`0%`}})})]}),N&&(0,k.jsxs)(`div`,{className:Q.genBar,children:[(0,k.jsxs)(`div`,{className:Q.genBarRow,children:[(0,k.jsx)(`span`,{className:Q.genBarRobot,children:`🤖`}),(0,k.jsx)(`span`,{className:Q.genBarLabel,children:I.total===0?`Pianificazione...`:`Generazione`}),(0,k.jsx)(`span`,{className:Q.genBarFile,children:(I.name||``).split(`,`)[0].trim()}),(0,k.jsx)(`span`,{className:Q.genBarCounter,children:I.total>0?`${I.fi} / ${I.total}`:``}),(0,k.jsx)(`span`,{className:Q.genBarCounter,children:je.tokIn+je.tokOut>0?`↑${jt(je.tokIn)} ↓${jt(je.tokOut)}`:``}),(0,k.jsx)(`span`,{className:Q.genBarTime,children:vt}),(0,k.jsxs)(`span`,{className:Q.genDots,children:[(0,k.jsx)(`span`,{className:`${Q.dot} ${Q.dot1}`}),(0,k.jsx)(`span`,{className:`${Q.dot} ${Q.dot2}`}),(0,k.jsx)(`span`,{className:`${Q.dot} ${Q.dot3}`})]})]}),(0,k.jsx)(`div`,{className:Q.progressTrack,children:(0,k.jsx)(`div`,{className:Q.genProgress,style:{width:I.total>0?`${Math.round(I.fi/I.total*100)}%`:`0%`}})})]}),r===`preview`?(0,k.jsxs)(`div`,{className:Q.sandboxWrap,children:[(0,k.jsxs)(`div`,{className:Q.sandboxStatusBar,children:[(0,k.jsx)(`span`,{className:Q.sandboxStatusDot,style:{background:ct?`#4ade80`:ft?`#facc15`:`#64748b`}}),(0,k.jsx)(`span`,{className:Q.sandboxStatusText,children:ct?`Live :${ct}`:ft?`Starting...`:`Stopped`}),ct&&(0,k.jsx)(`button`,{className:Q.sandboxReloadBtn,onClick:()=>{let e=document.querySelector(`iframe[title="WebCraft Sandbox"]`);e&&(e.src=e.src)},children:`↻`}),ct&&(0,k.jsx)(`button`,{className:Q.sandboxStopBtn,onClick:async()=>{lt(null),M([]);try{await fetch(`/api/studio/webcraft/sandbox`,{method:`DELETE`})}catch{}},children:`⏹`}),!ct&&!ft&&(0,k.jsx)(`button`,{className:Q.sandboxStartBtnSmall,onClick:()=>{c&&Wt(c)},children:`▶ Start`})]}),de.length>0&&(0,k.jsxs)(`div`,{className:Q.runtimeErrors,children:[(0,k.jsxs)(`div`,{className:Q.runtimeErrorsHeader,children:[(0,k.jsxs)(`span`,{children:[`❌ `,de.length,` runtime error`,de.length>1?`s`:``]}),(0,k.jsx)(`button`,{className:Q.runtimeErrorsFix,onClick:()=>{let e=`Fix these runtime errors. Use edit_file (or create_file if the file doesn't exist) to actually modify the source — do NOT respond with text descriptions. After each fix, call check_syntax to verify. If edit_file fails because old_text doesn't match, read_file again and retry with the exact text.\n\n${de.map(e=>`${e.message} (${e.source||``}:${e.line||0})`).join(`
|
|
855
855
|
`)}`;fetch(`/api/studio/webcraft/sandbox/errors`,{method:`DELETE`}),M([]),i(`files`),Pt(e)},children:`🔧 Auto-fix`}),(0,k.jsx)(`button`,{className:Q.runtimeErrorsDismiss,onClick:()=>{fetch(`/api/studio/webcraft/sandbox/errors`,{method:`DELETE`}),M([])},children:`✕`})]}),de.slice(0,3).map((e,t)=>(0,k.jsxs)(`div`,{className:Q.runtimeErrorLine,children:[e.message,e.source?` — ${e.source.split(`/`).pop()}:${e.line}`:``]},t))]}),ct?(0,k.jsx)(`iframe`,{src:`http://localhost:${ct}`,className:Q.sandboxFrame,title:`WebCraft Sandbox`,sandbox:`allow-scripts allow-same-origin allow-forms allow-popups`}):(0,k.jsxs)(`div`,{className:Q.sandboxEmpty,children:[(0,k.jsx)(`span`,{style:{fontSize:48},children:ft?`⏳`:mt?`❌`:`🌐`}),(0,k.jsx)(`span`,{style:{fontWeight:700,fontSize:16},children:ft?`Starting sandbox...`:mt?`Sandbox Error`:`Preview`}),mt&&(0,k.jsx)(`pre`,{style:{fontSize:11,maxWidth:600,textAlign:`left`,color:`#f87171`,background:`rgba(248,113,113,0.08)`,border:`1px solid rgba(248,113,113,0.2)`,borderRadius:6,padding:`8px 12px`,whiteSpace:`pre-wrap`,wordBreak:`break-word`,margin:`8px 0`,lineHeight:1.5,maxHeight:200,overflow:`auto`},children:mt}),gt.length>0&&(0,k.jsxs)(`div`,{style:{width:`100%`,maxWidth:720,margin:`8px 0`,textAlign:`left`},children:[(0,k.jsxs)(`div`,{style:{fontSize:10,color:`#94a3b8`,textTransform:`uppercase`,letterSpacing:`0.5px`,marginBottom:4},children:[`Sandbox log (`,gt.length,`)`]}),(0,k.jsx)(`pre`,{style:{fontSize:11,lineHeight:1.5,fontFamily:`SF Mono, Monaco, monospace`,background:`rgba(15, 23, 42, 0.6)`,border:`1px solid rgba(148, 163, 184, 0.18)`,borderRadius:6,padding:`8px 12px`,maxHeight:240,overflow:`auto`,whiteSpace:`pre-wrap`,wordBreak:`break-word`,color:`#cbd5e1`},children:gt.slice(-40).map((e,t)=>(0,k.jsx)(`div`,{style:{color:e.kind===`error`?`#f87171`:e.kind===`warn`?`#fbbf24`:e.kind===`phase`?`#60a5fa`:e.kind===`status`?`#34d399`:`#94a3b8`},children:`[${e.kind}] ${e.msg}`},t))})]}),!ft&&(0,k.jsxs)(`button`,{className:Q.sandboxStartBtn,onClick:()=>{c&&Wt(c)},children:[`▶ `,mt?`Retry`:`Start Sandbox`]})]})]}):(0,k.jsx)(`div`,{className:Q.codeArea,children:g.length===0&&N?(0,k.jsx)(`div`,{className:Q.noFiles,children:(0,k.jsxs)(`div`,{className:Q.noFilesHero,children:[(0,k.jsx)(`span`,{className:Q.noFilesIcon,children:`⏳`}),(0,k.jsx)(`span`,{className:Q.noFilesTitle,children:`Pianificazione...`}),(0,k.jsx)(`span`,{className:Q.noFilesTagline,children:I.name||`Analisi della struttura del progetto in corso`})]})}):g.length===0?(0,k.jsxs)(`div`,{className:Q.noFiles,children:[(0,k.jsxs)(`div`,{className:Q.noFilesHero,children:[(0,k.jsx)(`span`,{className:Q.noFilesIcon,children:`🔨`}),(0,k.jsx)(`span`,{className:Q.noFilesTitle,children:`WebCraft`}),(0,k.jsx)(`span`,{className:Q.noFilesTagline,children:`Genera progetti web completi con AI`})]}),(0,k.jsxs)(`div`,{className:Q.noFilesSteps,children:[(0,k.jsxs)(`div`,{className:Q.noFilesStep,children:[(0,k.jsx)(`span`,{className:Q.noFilesStepNum,children:`1`}),(0,k.jsx)(`span`,{children:`Scegli un esempio o scrivi una descrizione nel box in basso`})]}),(0,k.jsxs)(`div`,{className:Q.noFilesStep,children:[(0,k.jsx)(`span`,{className:Q.noFilesStepNum,children:`2`}),(0,k.jsxs)(`span`,{children:[`Premi `,(0,k.jsx)(`strong`,{children:`▶ Genera`}),` — l'AI crea tutti i file del progetto`]})]}),(0,k.jsxs)(`div`,{className:Q.noFilesStep,children:[(0,k.jsx)(`span`,{className:Q.noFilesStepNum,children:`3`}),(0,k.jsx)(`span`,{children:`Chiedi modifiche in chat, scarica lo ZIP o avvia il Sandbox`})]})]}),(0,k.jsxs)(`div`,{className:Q.noFilesExamplesHint,children:[`💡 Prova: `,(0,k.jsx)(`button`,{className:Q.noFilesExampleBtn,onClick:()=>{let e=eE[0];l(e.name),d(e.desc),Re(e.desc)},children:`MySaaS`}),(0,k.jsx)(`button`,{className:Q.noFilesExampleBtn,onClick:()=>{let e=eE[1];l(e.name),d(e.desc),Re(e.desc)},children:`MyShop`}),(0,k.jsx)(`button`,{className:Q.noFilesExampleBtn,onClick:()=>{let e=eE[3];l(e.name),d(e.desc),Re(e.desc)},children:`MyPortfolio`})]})]}):(0,k.jsxs)(`div`,{className:Q.codeLayout,children:[(0,k.jsx)(`div`,{className:Q.ideTabBar,children:g.map((e,t)=>{let n=e._error||!!e._syntaxError,r=t===y;return(0,k.jsxs)(`button`,{className:`${Q.ideTab} ${r?Q.ideTabActive:``} ${n?Q.ideTabError:``} ${e._pending?Q.ideTabPending:``}`,onClick:()=>{b(t),w(null),S(null),N&&me.current!==null&&t!==me.current&&(he.current&&clearTimeout(he.current),he.current=setTimeout(()=>{me.current!==null&&(b(me.current),S(null))},1e4))},title:e.name,children:[(0,k.jsx)(`span`,{className:Q.ideTabIcon,children:e._pending?`⌛`:n?`⚠`:rE(e.name)}),(0,k.jsx)(`span`,{className:Q.ideTabName,children:(e.name||``).split(`/`).pop()}),ee.has(e.name)&&(0,k.jsx)(`span`,{className:Q.ideTabUnsaved,children:`●`}),n&&(0,k.jsx)(`span`,{className:Q.ideTabDot})]},t)})}),xe&&(0,k.jsxs)(`div`,{className:Q.diffOverlay,children:[(0,k.jsxs)(`div`,{className:Q.diffOverlayHeader,children:[(0,k.jsxs)(`span`,{children:[`✏ Modifica proposta — `,(0,k.jsx)(`strong`,{children:xe.file})]}),(0,k.jsxs)(`div`,{className:Q.diffOverlayActions,children:[(0,k.jsx)(`button`,{className:Q.diffAcceptBtn,onClick:()=>{v(e=>e.map(e=>e.name===xe.file?{...e,content:xe.after}:e)),Se(null)},children:`✓ Accetta`}),(0,k.jsx)(`button`,{className:Q.diffRejectBtn,onClick:()=>Se(null),children:`✕ Rifiuta`})]})]}),(0,k.jsx)(`div`,{className:Q.diffOverlayBody,children:(0,k.jsx)(uE,{before:xe.before,after:xe.after})})]}),(0,k.jsxs)(`div`,{className:Q.codeRow,children:[(0,k.jsxs)(`div`,{className:Q.fileTreeWrap,children:[fe.length>0&&(0,k.jsxs)(`div`,{className:Q.scanBanner,children:[(0,k.jsx)(`span`,{className:Q.scanBannerIcon,children:`⚠`}),(0,k.jsxs)(`span`,{className:Q.scanBannerText,children:[fe.length,` issue`,fe.length>1?`s`:``]}),(0,k.jsx)(`button`,{className:Q.scanBannerFix,onClick:async()=>{i(`files`);try{let e=await E(`/api/studio/webcraft/auto-repair`,{projectName:c});if(e?.summary){let t=(e.repairs||[]).slice(0,20).map(e=>` • ${e.file} (${e.kind}${e.source?` from ${e.source}`:``})`).join(`
|
|
856
856
|
`);Ie(n=>[...n,{role:`agent`,text:`🔧 Auto-repair: ${e.summary}\n${t}`,tools:[]}])}let t=await E(`/api/studio/webcraft/complete`,{projectName:c}),n=t?.htmlRewrites?.length||0,r=t?.llmFills?.length||0,i=t?.stubFallbacks?.length||0;if(n+r+i>0){let e=[];n&&e.push(...(t.htmlRewrites||[]).map(e=>` • ${e.from}: ${e.oldHref} → ${e.newHref} (point to real file)`)),r&&e.push(...(t.llmFills||[]).map(e=>` • ${e.target} (${e.length} bytes, LLM-generated)`)),i&&e.push(...(t.stubFallbacks||[]).map(e=>` • ${e} (stub fallback — LLM unavailable)`)),Ie(t=>[...t,{role:`agent`,text:`✨ Asset completion: ${n} HTML rewrites, ${r} via LLM${i?`, ${i} as stubs`:``}\n${e.join(`
|
|
857
|
-
`)}`,tools:[]}])}await It([...e?.filesRepaired||[],...e?.filesCreated||[],...(t?.htmlRewrites||[]).map(e=>e.from),...(t?.llmFills||[]).map(e=>e.target)],{}),await kt(c)}catch(e){console.error(`[fix] failed:`,e)}},children:`Fix`})]}),(0,k.jsx)(WT,{files:g,activeIndex:y,unsavedFiles:ee,errorFiles:new Set(fe.filter(e=>e.severity===`error`).map(e=>e.file)),onSelect:e=>{b(e),w(null),S(null),N&&me.current!==null&&e!==me.current&&(he.current&&clearTimeout(he.current),he.current=setTimeout(()=>{me.current!==null&&(b(me.current),S(null))},1e4))}})]}),(0,k.jsx)(`div`,{className:Q.codeEditorWrap,children:rn&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`div`,{className:Q.codeHeader,children:[(0,k.jsx)(`span`,{className:Q.codeFileIcon,children:rE(rn.name)}),(0,k.jsx)(`span`,{className:Q.codeFileName,children:rn.name}),rn.content&&!rn._error&&(0,k.jsxs)(`span`,{className:Q.codeFileMeta,children:[(rn.content||``).split(`
|
|
857
|
+
`)}`,tools:[]}])}let a=await E(`/api/studio/webcraft/extend-styles`,{projectName:c});a?.extended&&Ie(e=>[...e,{role:`agent`,text:`🎨 Styles auto-extended: ${a.file} (${a.sizeBefore}→${a.sizeAfter} bytes, coverage ${((a.coverageBefore||0)*100).toFixed(0)}%→${((a.coverageAfter||0)*100).toFixed(0)}%, ${(a.missingBefore||0)-(a.missingAfter||0)} new selectors covered)`,tools:[]}]),await It([...e?.filesRepaired||[],...e?.filesCreated||[],...(t?.htmlRewrites||[]).map(e=>e.from),...(t?.llmFills||[]).map(e=>e.target),...a?.file?[a.file]:[]],{}),await kt(c)}catch(e){console.error(`[fix] failed:`,e)}},children:`Fix`})]}),(0,k.jsx)(WT,{files:g,activeIndex:y,unsavedFiles:ee,errorFiles:new Set(fe.filter(e=>e.severity===`error`).map(e=>e.file)),onSelect:e=>{b(e),w(null),S(null),N&&me.current!==null&&e!==me.current&&(he.current&&clearTimeout(he.current),he.current=setTimeout(()=>{me.current!==null&&(b(me.current),S(null))},1e4))}})]}),(0,k.jsx)(`div`,{className:Q.codeEditorWrap,children:rn&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`div`,{className:Q.codeHeader,children:[(0,k.jsx)(`span`,{className:Q.codeFileIcon,children:rE(rn.name)}),(0,k.jsx)(`span`,{className:Q.codeFileName,children:rn.name}),rn.content&&!rn._error&&(0,k.jsxs)(`span`,{className:Q.codeFileMeta,children:[(rn.content||``).split(`
|
|
858
858
|
`).length,` righe · `,iE(rn.content||``)]}),!rn._pending&&!rn._error&&rn.content&&(0,k.jsx)(`button`,{className:`${Q.editToggleBtn} ${C===null?``:Q.editToggleBtnActive}`,onClick:()=>{C===null?w(rn.content):(v(e=>e.map((e,t)=>t===y?{...e,content:C}:e)),E(`/api/studio/webcraft/file/write`,{projectName:c,path:rn.name,content:C}),w(null))},children:C===null?`✏ Modifica`:`💾 Salva`}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,title:`Split view`,onClick:()=>ce(se===null?+(y===0&&g.length>1):null),children:`⫼`}),(0,k.jsx)(`button`,{className:`${Q.headerIconBtn} ${A?Q.headerIconBtnActive:``}`,title:`Terminal`,onClick:()=>oe(!A),children:`⌨`}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,title:`Development Guide`,onClick:()=>ue(!0),children:`📖`})]}),ne&&(0,k.jsxs)(`div`,{className:Q.findBar,children:[(0,k.jsx)(`input`,{className:Q.findInput,value:D,onChange:e=>ie(e.target.value),placeholder:`Find...`,autoFocus:!0}),(0,k.jsx)(`input`,{className:Q.findInput,value:O,onChange:e=>ae(e.target.value),placeholder:`Replace...`}),(0,k.jsx)(`span`,{className:Q.findCount,children:D?((rn.content||``).match(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`gi`))?.length||0)+` found`:``}),(0,k.jsx)(`button`,{className:Q.findBtn,onClick:()=>{!D||C===null||w(C.replace(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`i`),O))},children:`Replace`}),(0,k.jsx)(`button`,{className:Q.findBtn,onClick:()=>{!D||C===null||w(C.replace(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`gi`),O))},children:`All`}),(0,k.jsx)(`button`,{className:Q.findClose,onClick:()=>re(!1),children:`×`})]}),rn._error&&(0,k.jsx)(`div`,{className:Q.fileError,children:`⚠ Generazione fallita — chiedi al modello di rigenerare questo file`}),rn._syntaxError&&!rn._error&&(0,k.jsxs)(`div`,{className:Q.fileSyntaxError,children:[`⚠ Syntax error: `,rn._syntaxError]}),N&&x!==null?(0,k.jsx)(`pre`,{className:Q.streamingPre,ref:e=>{e&&ge.current&&(e.scrollTop=e.scrollHeight)},onScroll:e=>{let t=e.currentTarget;t.scrollHeight-t.scrollTop-t.clientHeight<50?ge.current=!0:(ge.current=!1,_e.current&&clearTimeout(_e.current),_e.current=setTimeout(()=>{ge.current=!0},15e3))},dangerouslySetInnerHTML:{__html:QT(x||``,(rn.name.split(`.`).pop()||`js`).toLowerCase())+`<span class="`+Q.streamingCursor+`">▋</span>`}}):(0,k.jsx)(RT,{value:C===null?rn.content||``:C,filename:rn.name,readOnly:C===null,projectName:c,onChange:e=>{w(e),rn&&te(e=>new Set(e).add(rn.name))},onSave:e=>{v(t=>t.map((t,n)=>n===y?{...t,content:e}:t)),E(`/api/studio/webcraft/file/write`,{projectName:c,path:rn.name,content:e}),w(null),te(e=>{let t=new Set(e);return t.delete(rn.name),t})}})]})}),se!==null&&g[se]&&(0,k.jsxs)(`div`,{className:Q.codeEditorWrap,children:[(0,k.jsxs)(`div`,{className:Q.codeHeader,children:[(0,k.jsx)(`span`,{className:Q.codeFileIcon,children:rE(g[se].name)}),(0,k.jsx)(`span`,{className:Q.codeFileName,children:g[se].name}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,onClick:()=>ce(null),children:`✕`})]}),(0,k.jsx)(RT,{value:g[se].content||``,filename:g[se].name,readOnly:!0})]})]}),A&&(0,k.jsxs)(`div`,{className:Q.terminalPanel,children:[(0,k.jsxs)(`div`,{className:Q.terminalHeader,children:[(0,k.jsx)(`span`,{className:Q.terminalTitle,children:`Terminal`}),(0,k.jsx)(`button`,{className:Q.terminalClose,onClick:()=>oe(!1),children:`✕`})]}),(0,k.jsx)(YT,{projectDir:c||void 0})]})]})})]})]})]})}),He&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.planBanner,children:[(0,k.jsx)(`div`,{className:Q.planTitle,children:`📌 Piano proposto — approva per eseguire`}),(0,k.jsx)(`pre`,{className:Q.planText,children:He.plan}),(0,k.jsxs)(`div`,{className:Q.planActions,children:[(0,k.jsx)(`button`,{className:Q.planApprove,onClick:en,children:`✓ Esegui`}),(0,k.jsx)(`button`,{className:Q.planReject,onClick:()=>Ue(null),children:`✕ Annulla`})]})]}),Qe&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.grepPanel,children:[(0,k.jsxs)(`div`,{className:Q.grepRow,children:[(0,k.jsx)(`input`,{className:Q.grepInput,value:et,onChange:e=>tt(e.target.value),onKeyDown:e=>e.key===`Enter`&&Kt(),placeholder:`Cerca nel codice...`}),(0,k.jsx)(`button`,{className:Q.grepBtn,onClick:Kt,children:`🔍`}),(0,k.jsx)(`button`,{className:Q.grepClose,onClick:()=>$e(!1),children:`×`})]}),nt.length>0&&(0,k.jsxs)(`div`,{className:Q.grepCount,children:[nt.length,` risultati`]}),(0,k.jsx)(`div`,{className:Q.grepResults,children:nt.length===0?(0,k.jsx)(`div`,{className:Q.grepEmpty,children:`Nessun risultato.`}):nt.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.grepMatch,onClick:()=>qt(e.file),children:[(0,k.jsxs)(`span`,{className:Q.grepMatchFile,children:[e.file,`:`,e.lineNum]}),(0,k.jsx)(`pre`,{className:Q.grepMatchLine,children:e.line})]},t))})]}),it.length>0&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.diffPanel,children:[(0,k.jsxs)(`div`,{className:Q.diffHeader,children:[(0,k.jsxs)(`span`,{children:[`🔌 Diff — `,it.length,` file modificati`]}),(0,k.jsx)(`button`,{className:Q.diffClose,onClick:()=>at([]),children:`✕ Chiudi`})]}),it.map((e,t)=>{let n=(e.after||``).split(`
|
|
859
859
|
`).length-(e.before||``).split(`
|
|
860
860
|
`).length;return(0,k.jsxs)(`details`,{open:!0,className:Q.diffFile,children:[(0,k.jsxs)(`summary`,{className:Q.diffSummary,children:[(0,k.jsx)(`span`,{className:Q.diffArrow,children:`▲`}),(0,k.jsx)(`span`,{className:Q.diffFileName,children:e.file}),(0,k.jsxs)(`span`,{className:n>=0?Q.diffAdded:Q.diffRemoved,children:[n>=0?`+`:``,n,` linee`]})]}),(0,k.jsx)(`div`,{className:Q.diffContent,children:(0,k.jsx)(uE,{before:e.before,after:e.after})})]},t)})]}),t!==`projects`&&(0,k.jsxs)(`div`,{className:`${Q.chatPanel} ${ut?Q.chatPanelCollapsed:``}`,children:[(0,k.jsxs)(`button`,{className:Q.chatCollapseBtn,onClick:()=>dt(e=>!e),children:[(0,k.jsx)(`span`,{children:ut?`▲`:`▼`}),(0,k.jsx)(`span`,{children:ut?`Show Chat`:`Hide Chat`}),Fe.length>0&&(0,k.jsxs)(`span`,{style:{opacity:.5},children:[`(`,Fe.length,`)`]})]}),(0,k.jsxs)(`div`,{className:Q.chatMessages,ref:Tt,children:[Fe.length===0&&nn&&(0,k.jsxs)(`div`,{className:Q.chatWelcome,children:[`🤖 `,e(`webcraft.doctrine.title`),` — `,(0,k.jsx)(`button`,{className:Q.doctrineOpenBtn,onClick:()=>ue(!0),children:`📖 Open Guide`})]}),Fe.map((e,t)=>(0,k.jsxs)(`div`,{className:e.role===`user`?Q.chatUser:e.role===`system`?Q.chatSystem:Q.chatAgent,children:[e.role===`user`&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`div`,{className:Q.chatUserBubble,children:e.text}),e.attachments&&e.attachments.length>0&&(0,k.jsx)(`div`,{className:Q.chatAttachPreviews,children:e.attachments.map((e,t)=>(0,k.jsxs)(`span`,{className:Q.chatAttachBadge,children:[`📎 `,e.name]},t))})]}),e.role===`system`&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`div`,{className:Q.chatSystemBubble,children:e.text}),e.syntaxErrors?.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.chatSyntaxErr,children:[`✕ `,e.file,`: `,e.error]},t))]}),e.role===`agent`&&(()=>{let t=e.text.replace(/<tool>[\s\S]*?<\/tool>/g,``).replace(/<done\s*\/?>/g,``).trim(),n=(e.tools||[]).filter(e=>(e.op===`edit`||e.op===`write`)&&(e.result===`ok`||e.result===`ok_fuzzy`||e.result===`ok_repaired`)),r=(e.tools||[]).filter(e=>e.result?.includes(`not_found`)||e.result?.includes(`error`)||e.result===`blocked_use_edit`),a=t.match(/^(.{10,120}?)[.\n]/),o=a?a[1]+`.`:t.slice(0,120),s=t.length>130;return(0,k.jsxs)(`div`,{className:Q.chatAgentCard,children:[n.map((e,t)=>(0,k.jsxs)(`div`,{style:{margin:`6px 0`,borderRadius:8,overflow:`hidden`,border:`1px solid rgba(255,255,255,0.08)`},children:[(0,k.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,padding:`6px 10px`,background:`rgba(99,102,241,0.1)`,fontSize:11},children:[(0,k.jsxs)(`span`,{style:{fontWeight:600,color:`#818cf8`,cursor:`pointer`},onClick:()=>{let t=g.findIndex(t=>t.name===e.path);t>=0&&(b(t),i(`files`))},children:[`✏ `,e.path]}),(0,k.jsx)(`span`,{style:{color:`#4ade80`,fontSize:10,fontWeight:600},children:e.result===`ok_fuzzy`?`applied (fuzzy)`:e.result===`ok_repaired`?`applied (repaired)`:`✓ applied`})]}),e.oldSnippet||e.newSnippet?(0,k.jsx)(uE,{before:e.oldSnippet||``,after:e.newSnippet||``,contextLines:3}):(0,k.jsx)(`div`,{style:{padding:`6px 10px`,fontSize:11,color:`#4ade80`,background:`rgba(74,222,128,0.05)`},children:`File modified successfully`})]},t)),r.length>0&&(0,k.jsx)(`div`,{style:{margin:`6px 0`,padding:`6px 10px`,background:`rgba(248,113,113,0.08)`,borderRadius:6,fontSize:11,color:`#f87171`},children:r.map((e,t)=>(0,k.jsxs)(`div`,{children:[`❌ `,e.op,` `,e.path,`: `,typeof e.result==`string`?e.result.slice(0,100):``]},t))}),t&&(s?(0,k.jsxs)(`details`,{style:{margin:`6px 0`,fontSize:11},children:[(0,k.jsx)(`summary`,{style:{cursor:`pointer`,color:`var(--dim)`,padding:`4px 0`,userSelect:`none`},children:o.slice(0,100)}),(0,k.jsx)(`div`,{className:Q.chatAgentText,style:{fontSize:11,opacity:.8,marginTop:4},dangerouslySetInnerHTML:{__html:$T(t)}})]}):(0,k.jsx)(`div`,{className:Q.chatAgentText,style:{fontSize:11,opacity:.8},dangerouslySetInnerHTML:{__html:$T(t)}}))]})})()]},t)),ze&&(()=>{let e=Fe[Fe.length-1]?.tools??[],t=e[e.length-1],n=t?t.op===`read`?`Reading ${t.path}`:t.op===`edit`?`Editing ${t.path}`:t.op===`search`?`Searching...`:t.op===`lint`?`Linting ${t.path}`:t.op===`check`?`Checking ${t.path}`:t.op===`run`?`Running command...`:t.op===`sandbox`?`Starting sandbox...`:t.op:`Thinking...`;return(0,k.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8,padding:`8px 12px`,fontSize:12,color:`#818cf8`},children:[(0,k.jsx)(`span`,{className:Q.chatAgentRobotAnim,style:{fontSize:14},children:`⟳`}),(0,k.jsx)(`span`,{style:{fontWeight:500},children:n})]})})()]}),Be.length>0&&(0,k.jsx)(`div`,{className:Q.attachPreviews,children:Be.map((e,t)=>(0,k.jsxs)(`span`,{className:Q.attachBadge,children:[`📎 `,e.name,(0,k.jsx)(`button`,{className:Q.removeAttachBtn,onClick:()=>Ve(e=>e.filter((e,n)=>n!==t)),children:`×`})]},t))}),nn?(0,k.jsxs)(`div`,{className:Q.projActiveRow,children:[`📄 `,(0,k.jsx)(`strong`,{className:Q.projActiveName,children:c}),` — scrivi per modificare o migliorare il progetto`]}):(0,k.jsxs)(`div`,{className:Q.projNameRow,children:[(0,k.jsx)(`span`,{className:Q.projNameLabel,children:`Nome progetto:`}),(0,k.jsx)(`input`,{className:Q.projNameInput,value:c,onChange:e=>l(e.target.value),placeholder:`MioProgetto`})]}),(0,k.jsxs)(`div`,{className:Q.chatInputRow,children:[(0,k.jsxs)(`label`,{className:Q.attachLabel,title:`Allega immagine o PDF`,children:[`📎`,(0,k.jsx)(`input`,{ref:Et,type:`file`,multiple:!0,accept:`image/*,.pdf`,style:{display:`none`},onChange:e=>tn(e.target.files)})]}),(0,k.jsx)(`textarea`,{className:Q.chatTextarea,value:Le,onChange:e=>Re(e.target.value),placeholder:nn?`Parla con il tuo agente: chiedi correzioni, migliorie, nuove funzionalità...`:`Descrivi il progetto da creare, poi premi Genera...`,disabled:U,onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),Pt())},rows:4}),(0,k.jsxs)(`div`,{className:Q.chatSendCol,children:[(0,k.jsx)(`button`,{className:Q.chatSendBtn,onClick:()=>Pt(),disabled:U,children:N?`⏳`:nn?`▶`:`▶ Genera`}),U&&!we&&(0,k.jsx)(`button`,{className:Q.chatStopBtn,onClick:Lt,children:`⏹ Stop`})]})]})]}),le&&(0,k.jsx)(`div`,{className:Q.modalOverlay,onClick:()=>ue(!1),children:(0,k.jsxs)(`div`,{className:Q.modal,onClick:e=>e.stopPropagation(),style:{width:720,maxHeight:`90vh`},children:[(0,k.jsxs)(`div`,{className:Q.modalHeader,children:[(0,k.jsxs)(`span`,{className:Q.modalTitle,children:[`📖 `,e(`webcraft.doctrine.title`)]}),(0,k.jsx)(`span`,{className:Q.doctrineSubtitle,children:e(`webcraft.doctrine.subtitle`)}),(0,k.jsx)(`button`,{className:Q.modalClose,onClick:()=>ue(!1),children:`✕`})]}),(0,k.jsx)(`div`,{className:Q.modalBody,style:{gap:0},children:[`phase1`,`phase2`,`phase3`,`phase4`,`phase5`,`tools`,`golden`].map(t=>(0,k.jsxs)(`div`,{className:Q.doctrineSection,children:[(0,k.jsx)(`div`,{className:Q.doctrineSectionTitle,children:e(`webcraft.doctrine.${t}.title`)}),(0,k.jsx)(`div`,{className:Q.doctrineSectionBody,children:e(`webcraft.doctrine.${t}.desc`).split(`
|
package/src/ui-dist/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
10
|
<title>NHA — NotHumanAllowed</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CnfvvIP7.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-nUBdqB1O.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|