nothumanallowed 16.0.41 → 16.0.43

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.41",
3
+ "version": "16.0.43",
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.41';
8
+ export const VERSION = '16.0.43';
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,22 @@ class SandboxManager {
229
229
  }
230
230
  } catch {}
231
231
 
232
+ // Pre-flight: complete missing CSS/JS assets via sibling fill + LLM gen.
233
+ // Replaces the previous "empty placeholder" strategy with real content
234
+ // when available. LLM calls capped at 8 files per boot to keep latency
235
+ // bounded; anything beyond falls back to stub (logged).
236
+ try {
237
+ const projectName = path.basename(projectDir);
238
+ const cfg = loadConfig();
239
+ const completionReport = await _completeMissingAssets(projectName, cfg, emit);
240
+ const total = completionReport.siblingFills.length + completionReport.llmFills.length + completionReport.stubFallbacks.length;
241
+ if (total > 0) {
242
+ emit({ type: 'status', msg: `Pre-flight asset completion: ${completionReport.siblingFills.length} from siblings, ${completionReport.llmFills.length} via LLM, ${completionReport.stubFallbacks.length} as stubs.` });
243
+ }
244
+ } catch (e) {
245
+ emit({ type: 'warn', msg: `Asset completion failed: ${(e.message || e).slice(0, 200)}` });
246
+ }
247
+
232
248
  // Pre-flight: repair unsafe err.X / error.X access in route handlers.
233
249
  // LLMs write `res.send(err.stack)` without null-checking err → 500 on
234
250
  // every request when error middleware signature is wrong (3 args not 4).
@@ -3444,6 +3460,20 @@ ${original.slice(0, 12_000)}`;
3444
3460
  } catch (e) { console.error('[scan] CRASH:', e); sendError(res, 500, e.message); }
3445
3461
  });
3446
3462
 
3463
+ // ── Smart asset completion (sibling fill + LLM generation) ────────────────
3464
+ // For missing CSS/JS referenced in HTML: first try copying from sibling
3465
+ // files with real content (deterministic, instant), then LLM-generate
3466
+ // anything still missing. Falls back to stub only when both fail.
3467
+ router.post('/api/studio/webcraft/complete', async (req, res) => {
3468
+ try {
3469
+ const { projectName } = await parseBody(req);
3470
+ if (!projectName) return sendError(res, 400, 'projectName required');
3471
+ const config = loadConfig();
3472
+ const report = await _completeMissingAssets(projectName, config, null);
3473
+ sendJSON(res, 200, report);
3474
+ } catch (e) { sendError(res, 500, e.message); }
3475
+ });
3476
+
3447
3477
  // ── Deterministic auto-repair (zero LLM, pure code) ────────────────────────
3448
3478
  // Handles the common bug classes that the LLM gets wrong:
3449
3479
  // 1. Mismatched/unclosed HTML tags → balance via tag stack
@@ -3757,6 +3787,219 @@ export function _detectMissingDataFiles(projectDir) {
3757
3787
  return created;
3758
3788
  }
3759
3789
 
3790
+ /**
3791
+ * Score how similar two filenames are. Returns 0..1.
3792
+ * "main.css" vs "style.css" → some score based on prefix/suffix shared chars.
3793
+ */
3794
+ function _fileSimilarity(a, b) {
3795
+ const aBase = a.replace(/\.[^.]+$/, '').toLowerCase();
3796
+ const bBase = b.replace(/\.[^.]+$/, '').toLowerCase();
3797
+ if (aBase === bBase) return 1;
3798
+ // Common CSS naming variants get a boost
3799
+ const cssAliases = ['main', 'style', 'styles', 'app', 'index', 'global'];
3800
+ const jsAliases = ['main', 'app', 'index', 'script', 'scripts', 'bundle'];
3801
+ const aliases = a.endsWith('.css') ? cssAliases : a.endsWith('.js') ? jsAliases : [];
3802
+ if (aliases.includes(aBase) && aliases.includes(bBase)) return 0.8;
3803
+ // Simple Levenshtein-ish: count common chars at start/end
3804
+ let prefix = 0;
3805
+ while (prefix < aBase.length && prefix < bBase.length && aBase[prefix] === bBase[prefix]) prefix++;
3806
+ let suffix = 0;
3807
+ while (suffix < aBase.length - prefix && suffix < bBase.length - prefix && aBase[aBase.length - 1 - suffix] === bBase[bBase.length - 1 - suffix]) suffix++;
3808
+ return (prefix + suffix) / Math.max(aBase.length, bBase.length);
3809
+ }
3810
+
3811
+ /**
3812
+ * Find a sibling file with real content that could fill in for a missing asset.
3813
+ * E.g. missing `public/css/main.css` but `public/css/style.css` exists with
3814
+ * 8459 bytes — that's almost certainly what the LLM meant.
3815
+ */
3816
+ function _findSiblingFile(projectDir, missingRel) {
3817
+ const missingAbs = path.join(projectDir, missingRel);
3818
+ const dir = path.dirname(missingAbs);
3819
+ const ext = path.extname(missingRel).toLowerCase();
3820
+ if (!fs.existsSync(dir)) return null;
3821
+ let entries;
3822
+ try { entries = fs.readdirSync(dir); } catch { return null; }
3823
+ const candidates = [];
3824
+ for (const name of entries) {
3825
+ if (path.extname(name).toLowerCase() !== ext) continue;
3826
+ const abs = path.join(dir, name);
3827
+ let stat;
3828
+ try { stat = fs.statSync(abs); } catch { continue; }
3829
+ if (!stat.isFile() || stat.size < 200) continue;
3830
+ // Skip nha-generated placeholders (small + commented)
3831
+ try {
3832
+ const head = fs.readFileSync(abs, 'utf-8').slice(0, 200);
3833
+ if (/nha-webcraft:.*auto-created placeholder/i.test(head)) continue;
3834
+ } catch { continue; }
3835
+ const sim = _fileSimilarity(path.basename(missingRel), name);
3836
+ candidates.push({ name, abs, size: stat.size, similarity: sim });
3837
+ }
3838
+ if (candidates.length === 0) return null;
3839
+ candidates.sort((a, b) => (b.similarity * b.size) - (a.similarity * a.size));
3840
+ return candidates[0];
3841
+ }
3842
+
3843
+ /**
3844
+ * Build a focused LLM prompt to generate a single missing file. Includes the
3845
+ * HTML that references it (for context) and other files in the project as
3846
+ * style reference. Returns the prompt strings; caller invokes callLLM.
3847
+ */
3848
+ function _buildCompletionPrompt(projectDir, missingRel, referencedFrom) {
3849
+ const ext = path.extname(missingRel).toLowerCase();
3850
+ const kind = ext === '.css' ? 'CSS stylesheet' : ext === '.js' ? 'JavaScript file' : 'asset file';
3851
+ let htmlSnippet = '';
3852
+ try {
3853
+ if (referencedFrom) {
3854
+ const html = fs.readFileSync(path.join(projectDir, referencedFrom), 'utf-8');
3855
+ htmlSnippet = html.slice(0, 4000);
3856
+ }
3857
+ } catch {}
3858
+ // Find sibling files of same kind for style reference
3859
+ const siblings = [];
3860
+ try {
3861
+ const dir = path.dirname(path.join(projectDir, missingRel));
3862
+ if (fs.existsSync(dir)) {
3863
+ for (const name of fs.readdirSync(dir).slice(0, 5)) {
3864
+ if (path.extname(name).toLowerCase() !== ext) continue;
3865
+ const abs = path.join(dir, name);
3866
+ try {
3867
+ const content = fs.readFileSync(abs, 'utf-8');
3868
+ if (/nha-webcraft:.*placeholder/i.test(content.slice(0, 200))) continue;
3869
+ siblings.push({ name, preview: content.slice(0, 1500) });
3870
+ } catch {}
3871
+ }
3872
+ }
3873
+ } catch {}
3874
+ const siblingCtx = siblings.length
3875
+ ? '\n\nOther ' + kind + ' files in the project (for style/convention reference):\n' +
3876
+ siblings.map(s => '### ' + s.name + '\n' + s.preview).join('\n\n')
3877
+ : '';
3878
+ const sys = 'You are an expert frontend developer. Generate the complete contents of a single file. Output ONLY the file content — no markdown fences, no explanations, no preamble. The output will be written directly to disk.';
3879
+ const user = 'Generate the full contents of `' + missingRel + '` for a web project.\n\n' +
3880
+ (htmlSnippet ? 'The file is referenced by `' + referencedFrom + '`:\n```\n' + htmlSnippet + '\n```' : '') +
3881
+ siblingCtx +
3882
+ '\n\nProduce production-quality ' + kind + ' that is coherent with the rest of the project.';
3883
+ return { sys, user };
3884
+ }
3885
+
3886
+ /**
3887
+ * Complete missing assets in a project: first try sibling fill (deterministic),
3888
+ * then fall back to LLM generation. Returns a report of what was filled and how.
3889
+ */
3890
+ export async function _completeMissingAssets(projectName, config, emit) {
3891
+ const dir = ProjectStore.dir(projectName);
3892
+ if (!fs.existsSync(dir)) throw new Error('project not found');
3893
+ const report = { siblingFills: [], llmFills: [], stillMissing: [], stubFallbacks: [] };
3894
+
3895
+ // Discover every HTML asset reference and whether the target exists
3896
+ const htmlFiles = [];
3897
+ const stack = [dir];
3898
+ const skipDirs = new Set(['node_modules', '.git', '.nha-shims', 'dist', 'build', '.next']);
3899
+ while (stack.length) {
3900
+ const cur = stack.pop();
3901
+ let entries;
3902
+ try { entries = fs.readdirSync(cur, { withFileTypes: true }); } catch { continue; }
3903
+ for (const ent of entries) {
3904
+ if (skipDirs.has(ent.name) || ent.name.startsWith('.')) continue;
3905
+ const abs = path.join(cur, ent.name);
3906
+ if (ent.isDirectory()) { stack.push(abs); continue; }
3907
+ const ext = path.extname(ent.name).toLowerCase();
3908
+ if (ext === '.html' || ext === '.htm') htmlFiles.push(abs);
3909
+ }
3910
+ }
3911
+
3912
+ const missingByPath = new Map();
3913
+ for (const htmlAbs of htmlFiles) {
3914
+ let html;
3915
+ try { html = fs.readFileSync(htmlAbs, 'utf-8'); } catch { continue; }
3916
+ const htmlRel = path.relative(dir, htmlAbs).replace(/\\/g, '/');
3917
+ const refs = _extractHtmlAssetRefs(html);
3918
+ for (const ref of refs) {
3919
+ const target = _normalizeAssetPath(ref.href, htmlRel);
3920
+ if (!target) continue;
3921
+ const targetAbs = path.join(dir, target);
3922
+ // Treat existing-but-placeholder as missing
3923
+ let isPlaceholder = false;
3924
+ if (fs.existsSync(targetAbs)) {
3925
+ try {
3926
+ const head = fs.readFileSync(targetAbs, 'utf-8').slice(0, 200);
3927
+ isPlaceholder = /nha-webcraft:.*placeholder/i.test(head);
3928
+ } catch {}
3929
+ if (!isPlaceholder) continue;
3930
+ }
3931
+ if (!missingByPath.has(target)) {
3932
+ missingByPath.set(target, { kind: ref.kind, referencedFrom: htmlRel, isPlaceholder });
3933
+ }
3934
+ }
3935
+ }
3936
+
3937
+ if (missingByPath.size === 0) {
3938
+ return report;
3939
+ }
3940
+
3941
+ if (emit) emit({ type: 'status', msg: 'Completing ' + missingByPath.size + ' missing/placeholder asset' + (missingByPath.size === 1 ? '' : 's') + '...' });
3942
+
3943
+ // Phase 1: sibling fill (deterministic, instant)
3944
+ const stillMissing = [];
3945
+ for (const [target, info] of missingByPath) {
3946
+ const sibling = _findSiblingFile(dir, target);
3947
+ if (sibling) {
3948
+ try {
3949
+ const targetAbs = path.join(dir, target);
3950
+ ensureDir(path.dirname(targetAbs));
3951
+ fs.copyFileSync(sibling.abs, targetAbs);
3952
+ report.siblingFills.push({ target, source: sibling.name, size: sibling.size });
3953
+ if (emit) emit({ type: 'status', msg: 'Sibling-filled: ' + target + ' ← ' + sibling.name + ' (' + sibling.size + ' bytes)' });
3954
+ continue;
3955
+ } catch {}
3956
+ }
3957
+ stillMissing.push({ target, ...info });
3958
+ }
3959
+
3960
+ // Phase 2: LLM completion for remaining (one call per file, max 8 files)
3961
+ const maxLLM = 8;
3962
+ for (const m of stillMissing.slice(0, maxLLM)) {
3963
+ try {
3964
+ const { sys, user } = _buildCompletionPrompt(dir, m.target, m.referencedFrom);
3965
+ if (emit) emit({ type: 'status', msg: 'LLM-completing: ' + m.target + ' (referenced by ' + m.referencedFrom + ')' });
3966
+ let body = '';
3967
+ await callLLMStream(config, sys, user, (chunk) => { body += chunk; }, { max_tokens: 4096 });
3968
+ // Strip markdown fences if LLM leaked them
3969
+ body = body.replace(/^```[a-zA-Z]*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
3970
+ // Reject LLM error responses leaked as content
3971
+ if (_looksLikeLLMError(body) || body.length < 50) {
3972
+ report.stillMissing.push(m.target);
3973
+ // Fallback to stub placeholder
3974
+ const targetAbs = path.join(dir, m.target);
3975
+ ensureDir(path.dirname(targetAbs));
3976
+ fs.writeFileSync(targetAbs, _placeholderContent(m.kind, m.target), 'utf-8');
3977
+ report.stubFallbacks.push(m.target);
3978
+ continue;
3979
+ }
3980
+ const targetAbs = path.join(dir, m.target);
3981
+ ensureDir(path.dirname(targetAbs));
3982
+ fs.writeFileSync(targetAbs, body, 'utf-8');
3983
+ report.llmFills.push({ target: m.target, length: body.length });
3984
+ } catch (e) {
3985
+ report.stillMissing.push(m.target);
3986
+ if (emit) emit({ type: 'warn', msg: 'LLM completion failed for ' + m.target + ': ' + (e.message || e).slice(0, 100) });
3987
+ }
3988
+ }
3989
+
3990
+ // Phase 3: anything beyond the LLM cap → stub
3991
+ for (const m of stillMissing.slice(maxLLM)) {
3992
+ try {
3993
+ const targetAbs = path.join(dir, m.target);
3994
+ ensureDir(path.dirname(targetAbs));
3995
+ fs.writeFileSync(targetAbs, _placeholderContent(m.kind, m.target), 'utf-8');
3996
+ report.stubFallbacks.push(m.target);
3997
+ } catch {}
3998
+ }
3999
+
4000
+ return report;
4001
+ }
4002
+
3760
4003
  export function autoRepairProject(projectName) {
3761
4004
  const dir = ProjectStore.dir(projectName);
3762
4005
  if (!fs.existsSync(dir)) throw new Error('project not found');
@@ -5136,7 +5379,76 @@ function createApp() {
5136
5379
  const express = createApp;
5137
5380
  express.json = function (opts) { return async (req, res, next) => { if (/json/i.test(req.headers['content-type'] || '')) req.body = await parseBody(req); next(); }; };
5138
5381
  express.urlencoded = function (opts) { return async (req, res, next) => { if (/urlencoded/i.test(req.headers['content-type'] || '')) req.body = await parseBody(req); next(); }; };
5139
- express.static = function (root) { return (req, res, next) => next(); };
5382
+
5383
+ // MIME type table for static files
5384
+ const _MIME = {
5385
+ '.html': 'text/html; charset=utf-8',
5386
+ '.htm': 'text/html; charset=utf-8',
5387
+ '.css': 'text/css; charset=utf-8',
5388
+ '.js': 'application/javascript; charset=utf-8',
5389
+ '.mjs': 'application/javascript; charset=utf-8',
5390
+ '.json': 'application/json; charset=utf-8',
5391
+ '.png': 'image/png',
5392
+ '.jpg': 'image/jpeg',
5393
+ '.jpeg': 'image/jpeg',
5394
+ '.gif': 'image/gif',
5395
+ '.webp': 'image/webp',
5396
+ '.svg': 'image/svg+xml',
5397
+ '.ico': 'image/x-icon',
5398
+ '.woff': 'font/woff',
5399
+ '.woff2': 'font/woff2',
5400
+ '.ttf': 'font/ttf',
5401
+ '.otf': 'font/otf',
5402
+ '.eot': 'application/vnd.ms-fontobject',
5403
+ '.txt': 'text/plain; charset=utf-8',
5404
+ '.xml': 'application/xml',
5405
+ '.pdf': 'application/pdf',
5406
+ '.map': 'application/json',
5407
+ '.mp4': 'video/mp4',
5408
+ '.webm': 'video/webm',
5409
+ '.mp3': 'audio/mpeg',
5410
+ '.wav': 'audio/wav',
5411
+ };
5412
+
5413
+ // Real express.static implementation — serves files from disk with proper
5414
+ // MIME types. Supports index.html for directory requests, path traversal
5415
+ // protection. Falls through to next() when the file doesn't exist.
5416
+ express.static = function (root, opts) {
5417
+ opts = opts || {};
5418
+ const indexFile = opts.index === false ? null : (opts.index || 'index.html');
5419
+ const rootAbs = require('path').resolve(root);
5420
+ return function (req, res, next) {
5421
+ if (req.method !== 'GET' && req.method !== 'HEAD') return next();
5422
+ try {
5423
+ let relPath = decodeURIComponent((req.path || req.url || '/').split('?')[0]);
5424
+ // Path traversal protection
5425
+ if (relPath.includes('\\\\0') || relPath.includes('..')) return next();
5426
+ if (relPath.startsWith('/')) relPath = relPath.slice(1);
5427
+ const abs = require('path').resolve(rootAbs, relPath);
5428
+ // Ensure resolved path stays within root
5429
+ if (!abs.startsWith(rootAbs)) return next();
5430
+ let stat;
5431
+ try { stat = require('fs').statSync(abs); } catch { return next(); }
5432
+ let filePath = abs;
5433
+ if (stat.isDirectory()) {
5434
+ if (!indexFile) return next();
5435
+ filePath = require('path').join(abs, indexFile);
5436
+ try { stat = require('fs').statSync(filePath); } catch { return next(); }
5437
+ if (!stat.isFile()) return next();
5438
+ }
5439
+ const ext = require('path').extname(filePath).toLowerCase();
5440
+ const mime = _MIME[ext] || 'application/octet-stream';
5441
+ res.setHeader('Content-Type', mime);
5442
+ res.setHeader('Content-Length', String(stat.size));
5443
+ res.setHeader('Cache-Control', 'public, max-age=0');
5444
+ if (req.method === 'HEAD') return res.end();
5445
+ require('fs').createReadStream(filePath).pipe(res);
5446
+ } catch (e) {
5447
+ return next();
5448
+ }
5449
+ };
5450
+ };
5451
+
5140
5452
  express.Router = function () { const r = createApp(); return r; };
5141
5453
  module.exports = express;
5142
5454
  module.exports.default = express;
@@ -853,7 +853,8 @@ Errore: `+e.msg,L(!1))}catch{}}}}catch(e){e instanceof DOMException&&e.name===`A
853
853
  `);i=a.pop()??``;for(let e of a){let t=e.replace(/^data: /,``).trim();if(t)try{let e=JSON.parse(t);(e.type===`phase`||e.type===`status`||e.type===`log`||e.type===`warn`||e.type===`error`)&&_t(t=>[...t.slice(-49),{kind:e.type,msg:String(e.msg||``),ts:Date.now()}]),e.type===`ready`&&e.port?(lt(e.port),pt(!1)):e.type===`error`&&ht(e.msg)}catch{}}}}catch(e){ht(e.message||`Connection failed`)}pt(!1)}}async function Gt(){c&&await Wt(c)}(0,_.useEffect)(()=>{z.current=V,Ot.current=Gt});async function Kt(){if(!et||!c)return;let e=await E(`/api/studio/webcraft/grep`,{projectName:c,query:et});e?.matches&&rt(e.matches)}function qt(e){let t=g.findIndex(t=>t.name===e);t>=0&&(b(t),i(`files`))}function Jt(){window.open(`/api/studio/webcraft/download/${encodeURIComponent(c)}`,`_blank`)}async function Yt(e,t,n,r){t.endsWith(`.md`)||(t+=`.md`);let i={name:t,content:n,type:r},a;a=e.mode===`edit`&&e.idx!==null?We.map((t,n)=>n===e.idx?i:t):[...We,i],Ge(a),Ye(null);let o=c||`MyProject`;c||l(o),await E(`/api/studio/webcraft/skills/${encodeURIComponent(o)}`,{skills:a})}async function Xt(e){let t=We[e];!t||!confirm(`Eliminare "${t.name}"?`)||(await E(`/api/studio/webcraft/skills/${encodeURIComponent(c)}/delete`,{name:t.name}),Ge(We.filter((t,n)=>n!==e)))}async function Zt(e){let t=We[e];if(!t||!confirm(`Svuotare "${t.name}"? Il file rimane ma il contenuto viene cancellato.`))return;let n=We.map((t,n)=>n===e?{...t,content:``}:t);Ge(n),await E(`/api/studio/webcraft/skills/${encodeURIComponent(c)}`,{skills:n})}async function Qt(){let e=await T(`/api/studio/webcraft/projects`);e?.projects&&st(e.projects)}async function H(e){let t=await T(`/api/studio/webcraft/projects/load/${encodeURIComponent(e.name)}`);if(!t)return;let r=t.projectName??e.name;l(r),d(t.description??``),v(t.files??[]),b(0),n(`new`),i(`files`),Ie([]),Ge([]),qe(!1),pe([]),kt(r);let a=await T(`/api/studio/webcraft/projects/chat/load/${encodeURIComponent(r)}`);a?.chat&&Ie(a.chat);let o=await T(`/api/studio/webcraft/skills/${encodeURIComponent(r)}`);o?.skills&&(Ge(o.skills),qe(!0))}async function $t(e){confirm(`Eliminare: ${e.name} - ${e.dir}?`)&&(await E(`/api/studio/webcraft/projects/${encodeURIComponent(e.name)}`,{},`DELETE`),st(ot.filter(t=>t.name!==e.name)),c===e.name&&(l(``),v([]),Ie([]),d(``)))}async function en(){if(!He)return;let e=He.originalMessage;Ue(null),await Ft(e+`
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
- `);Ie(n=>[...n,{role:`agent`,text:`🔧 Auto-repair: ${e.summary}\n${t}`,tools:[]}])}await It([...e?.filesRepaired||[],...e?.filesCreated||[]],{}),await kt(c)}catch(e){console.error(`[auto-repair] 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(`
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?.siblingFills?.length||0,r=t?.llmFills?.length||0,i=t?.stubFallbacks?.length||0;if(n+r+i>0){let e=[];n&&e.push(...(t.siblingFills||[]).map(e=>` • ${e.target} ← ${e.source} (${e.size} bytes, copied)`)),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} from siblings, ${r} via LLM${i?`, ${i} as stubs`:``}\n${e.join(`
857
+ `)}`,tools:[]}])}await It([...e?.filesRepaired||[],...e?.filesCreated||[],...(t?.siblingFills||[]).map(e=>e.target),...(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
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(`
858
859
  `).length-(e.before||``).split(`
859
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(`
@@ -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-Dy-GFsjK.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-L_v81SPj.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-nUBdqB1O.css">
13
13
  </head>
14
14
  <body>