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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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(`
|
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-L_v81SPj.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-nUBdqB1O.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|