nothumanallowed 16.0.43 → 16.0.45
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.45",
|
|
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.45';
|
|
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,21 @@ class SandboxManager {
|
|
|
229
229
|
}
|
|
230
230
|
} catch {}
|
|
231
231
|
|
|
232
|
+
// Pre-flight: auto-extend CSS if coverage is below threshold.
|
|
233
|
+
// Detects "incomplete styling" (LLM truncated CSS generation) and calls
|
|
234
|
+
// the LLM automatically to extend the CSS file with rules for the missing
|
|
235
|
+
// HTML selectors. NO user intervention required.
|
|
236
|
+
try {
|
|
237
|
+
const projectName = path.basename(projectDir);
|
|
238
|
+
const cfg = loadConfig();
|
|
239
|
+
const styleResult = await _autoExtendStylesIfNeeded(projectName, cfg, emit, { minCoverage: 0.6 });
|
|
240
|
+
if (styleResult.extended) {
|
|
241
|
+
emit({ type: 'status', msg: `Auto-extended styles: ${styleResult.file} (${styleResult.missingBefore - styleResult.missingAfter} new selectors covered).` });
|
|
242
|
+
}
|
|
243
|
+
} catch (e) {
|
|
244
|
+
emit({ type: 'warn', msg: `Auto-extend styles failed: ${(e.message || e).slice(0, 200)}` });
|
|
245
|
+
}
|
|
246
|
+
|
|
232
247
|
// Pre-flight: complete missing CSS/JS assets via sibling fill + LLM gen.
|
|
233
248
|
// Replaces the previous "empty placeholder" strategy with real content
|
|
234
249
|
// when available. LLM calls capped at 8 files per boot to keep latency
|
|
@@ -3460,6 +3475,17 @@ ${original.slice(0, 12_000)}`;
|
|
|
3460
3475
|
} catch (e) { console.error('[scan] CRASH:', e); sendError(res, 500, e.message); }
|
|
3461
3476
|
});
|
|
3462
3477
|
|
|
3478
|
+
// ── Auto-extend CSS when coverage is below threshold ──────────────────────
|
|
3479
|
+
router.post('/api/studio/webcraft/extend-styles', async (req, res) => {
|
|
3480
|
+
try {
|
|
3481
|
+
const { projectName, minCoverage } = await parseBody(req);
|
|
3482
|
+
if (!projectName) return sendError(res, 400, 'projectName required');
|
|
3483
|
+
const config = loadConfig();
|
|
3484
|
+
const result = await _autoExtendStylesIfNeeded(projectName, config, null, { minCoverage: minCoverage ?? 0.6 });
|
|
3485
|
+
sendJSON(res, 200, result);
|
|
3486
|
+
} catch (e) { sendError(res, 500, e.message); }
|
|
3487
|
+
});
|
|
3488
|
+
|
|
3463
3489
|
// ── Smart asset completion (sibling fill + LLM generation) ────────────────
|
|
3464
3490
|
// For missing CSS/JS referenced in HTML: first try copying from sibling
|
|
3465
3491
|
// files with real content (deterministic, instant), then LLM-generate
|
|
@@ -3787,6 +3813,25 @@ export function _detectMissingDataFiles(projectDir) {
|
|
|
3787
3813
|
return created;
|
|
3788
3814
|
}
|
|
3789
3815
|
|
|
3816
|
+
// Names that imply specific functionality — sibling fill is forbidden because
|
|
3817
|
+
// the file MUST have specific content, not a generic copy of style.css.
|
|
3818
|
+
// LLM generation is preferred for these.
|
|
3819
|
+
const _SEMANTIC_FILE_NAMES = new Set([
|
|
3820
|
+
'animations', 'animation', 'transitions', 'transition',
|
|
3821
|
+
'theme', 'themes', 'dark', 'light',
|
|
3822
|
+
'charts', 'chart', 'graphs', 'graph', 'plot',
|
|
3823
|
+
'auth', 'authentication', 'login', 'signup', 'register',
|
|
3824
|
+
'dashboard', 'admin', 'profile',
|
|
3825
|
+
'portfolio', 'gallery', 'projects',
|
|
3826
|
+
'reset', 'normalize', 'print',
|
|
3827
|
+
'mobile', 'responsive', 'desktop',
|
|
3828
|
+
]);
|
|
3829
|
+
|
|
3830
|
+
function _hasSemanticName(filePath) {
|
|
3831
|
+
const base = path.basename(filePath).replace(/\.[^.]+$/, '').toLowerCase();
|
|
3832
|
+
return _SEMANTIC_FILE_NAMES.has(base);
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3790
3835
|
/**
|
|
3791
3836
|
* Score how similar two filenames are. Returns 0..1.
|
|
3792
3837
|
* "main.css" vs "style.css" → some score based on prefix/suffix shared chars.
|
|
@@ -3813,17 +3858,26 @@ function _fileSimilarity(a, b) {
|
|
|
3813
3858
|
* E.g. missing `public/css/main.css` but `public/css/style.css` exists with
|
|
3814
3859
|
* 8459 bytes — that's almost certainly what the LLM meant.
|
|
3815
3860
|
*/
|
|
3816
|
-
function _findSiblingFile(projectDir, missingRel) {
|
|
3861
|
+
function _findSiblingFile(projectDir, missingRel, exclude) {
|
|
3862
|
+
// Refuse sibling fill for semantic file names — they need real content
|
|
3863
|
+
// matching the name's intent, not a copy of style.css.
|
|
3864
|
+
if (_hasSemanticName(missingRel)) return null;
|
|
3817
3865
|
const missingAbs = path.join(projectDir, missingRel);
|
|
3818
3866
|
const dir = path.dirname(missingAbs);
|
|
3819
3867
|
const ext = path.extname(missingRel).toLowerCase();
|
|
3820
3868
|
if (!fs.existsSync(dir)) return null;
|
|
3821
3869
|
let entries;
|
|
3822
3870
|
try { entries = fs.readdirSync(dir); } catch { return null; }
|
|
3871
|
+
const excludeSet = exclude instanceof Set ? exclude : new Set();
|
|
3823
3872
|
const candidates = [];
|
|
3824
3873
|
for (const name of entries) {
|
|
3825
3874
|
if (path.extname(name).toLowerCase() !== ext) continue;
|
|
3875
|
+
if (name === path.basename(missingRel)) continue;
|
|
3826
3876
|
const abs = path.join(dir, name);
|
|
3877
|
+
// Skip files we just filled in this session (avoid filling A from B
|
|
3878
|
+
// when B was itself just filled from C — produces semantic duplicates)
|
|
3879
|
+
const relFromProject = path.relative(projectDir, abs).replace(/\\/g, '/');
|
|
3880
|
+
if (excludeSet.has(relFromProject)) continue;
|
|
3827
3881
|
let stat;
|
|
3828
3882
|
try { stat = fs.statSync(abs); } catch { continue; }
|
|
3829
3883
|
if (!stat.isFile() || stat.size < 200) continue;
|
|
@@ -3831,6 +3885,8 @@ function _findSiblingFile(projectDir, missingRel) {
|
|
|
3831
3885
|
try {
|
|
3832
3886
|
const head = fs.readFileSync(abs, 'utf-8').slice(0, 200);
|
|
3833
3887
|
if (/nha-webcraft:.*auto-created placeholder/i.test(head)) continue;
|
|
3888
|
+
// Skip files with semantic names — they have specific purpose
|
|
3889
|
+
if (_hasSemanticName(name)) continue;
|
|
3834
3890
|
} catch { continue; }
|
|
3835
3891
|
const sim = _fileSimilarity(path.basename(missingRel), name);
|
|
3836
3892
|
candidates.push({ name, abs, size: stat.size, similarity: sim });
|
|
@@ -3940,23 +3996,51 @@ export async function _completeMissingAssets(projectName, config, emit) {
|
|
|
3940
3996
|
|
|
3941
3997
|
if (emit) emit({ type: 'status', msg: 'Completing ' + missingByPath.size + ' missing/placeholder asset' + (missingByPath.size === 1 ? '' : 's') + '...' });
|
|
3942
3998
|
|
|
3943
|
-
// Phase 1:
|
|
3999
|
+
// Phase 1: HTML reference rewrite (NO file duplication).
|
|
4000
|
+
// When `main.css` is referenced but missing AND `style.css` exists in the
|
|
4001
|
+
// same dir with real content, the correct fix is NOT to copy style.css
|
|
4002
|
+
// into main.css (creates a duplicate with mismatched semantic name) but
|
|
4003
|
+
// to REWRITE the HTML <link href="main.css"> → <link href="style.css">.
|
|
4004
|
+
// This preserves the LLM's actual file structure.
|
|
4005
|
+
report.htmlRewrites = report.htmlRewrites || [];
|
|
4006
|
+
const referencedFromMap = new Map(); // htmlPath → list of {oldHref, newHref}
|
|
3944
4007
|
const stillMissing = [];
|
|
3945
4008
|
for (const [target, info] of missingByPath) {
|
|
3946
|
-
const sibling = _findSiblingFile(dir, target);
|
|
4009
|
+
const sibling = _findSiblingFile(dir, target, new Set());
|
|
3947
4010
|
if (sibling) {
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
4011
|
+
// Don't copy. Rewrite the HTML reference to point to the real file.
|
|
4012
|
+
const newRef = path.posix.relative(
|
|
4013
|
+
path.dirname(info.referencedFrom),
|
|
4014
|
+
path.relative(dir, sibling.abs).replace(/\\/g, '/')
|
|
4015
|
+
) || path.basename(sibling.name);
|
|
4016
|
+
const htmlRewrite = { from: info.referencedFrom, oldHref: target, newHref: newRef, sibling: sibling.name };
|
|
4017
|
+
if (!referencedFromMap.has(info.referencedFrom)) referencedFromMap.set(info.referencedFrom, []);
|
|
4018
|
+
referencedFromMap.get(info.referencedFrom).push(htmlRewrite);
|
|
4019
|
+
report.htmlRewrites.push(htmlRewrite);
|
|
4020
|
+
if (emit) emit({ type: 'status', msg: `HTML rewrite: ${info.referencedFrom} → ${target} now points to ${sibling.name} (${sibling.size} bytes, real content)` });
|
|
4021
|
+
continue;
|
|
3956
4022
|
}
|
|
3957
4023
|
stillMissing.push({ target, ...info });
|
|
3958
4024
|
}
|
|
3959
4025
|
|
|
4026
|
+
// Apply HTML rewrites (one pass per file)
|
|
4027
|
+
for (const [htmlRel, rewrites] of referencedFromMap) {
|
|
4028
|
+
try {
|
|
4029
|
+
const htmlAbs = path.join(dir, htmlRel);
|
|
4030
|
+
let html = fs.readFileSync(htmlAbs, 'utf-8');
|
|
4031
|
+
for (const r of rewrites) {
|
|
4032
|
+
// Rewrite both `href="X"` and `src="X"` for the old asset path.
|
|
4033
|
+
// Handle both relative ('css/main.css') and absolute ('/css/main.css').
|
|
4034
|
+
const oldBase = path.basename(r.oldHref);
|
|
4035
|
+
const re = new RegExp(`(href|src)=(["'])([^"']*${oldBase.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})\\2`, 'g');
|
|
4036
|
+
html = html.replace(re, (_m, attr, q) => `${attr}=${q}${r.newHref}${q}`);
|
|
4037
|
+
}
|
|
4038
|
+
fs.writeFileSync(htmlAbs, html, 'utf-8');
|
|
4039
|
+
} catch (e) {
|
|
4040
|
+
if (emit) emit({ type: 'warn', msg: `Failed to rewrite ${htmlRel}: ${e.message}` });
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
|
|
3960
4044
|
// Phase 2: LLM completion for remaining (one call per file, max 8 files)
|
|
3961
4045
|
const maxLLM = 8;
|
|
3962
4046
|
for (const m of stillMissing.slice(0, maxLLM)) {
|
|
@@ -4000,6 +4084,229 @@ export async function _completeMissingAssets(projectName, config, emit) {
|
|
|
4000
4084
|
return report;
|
|
4001
4085
|
}
|
|
4002
4086
|
|
|
4087
|
+
/**
|
|
4088
|
+
* Analyze CSS coverage for an HTML project. Returns the percentage of HTML
|
|
4089
|
+
* classes/ids that have at least one matching CSS rule.
|
|
4090
|
+
*
|
|
4091
|
+
* Returns {
|
|
4092
|
+
* coverage: 0..1,
|
|
4093
|
+
* htmlSelectors: ['.cta', '.hero', '#main', ...], // all unique selectors in HTML
|
|
4094
|
+
* cssSelectors: Set of selectors that have CSS rules
|
|
4095
|
+
* missing: ['.testimonial', '.pricing-card', ...] // HTML selectors with no CSS
|
|
4096
|
+
* tagCount: number of HTML elements found
|
|
4097
|
+
* cssRuleCount: number of rules in all CSS files
|
|
4098
|
+
* imgCount: number of <img> tags
|
|
4099
|
+
* hasImgRule: boolean — does any CSS rule target `img` with max-width?
|
|
4100
|
+
* }
|
|
4101
|
+
*/
|
|
4102
|
+
export function _analyzeCssCoverage(projectDir) {
|
|
4103
|
+
const result = {
|
|
4104
|
+
coverage: 1,
|
|
4105
|
+
htmlSelectors: [],
|
|
4106
|
+
cssSelectors: new Set(),
|
|
4107
|
+
missing: [],
|
|
4108
|
+
tagCount: 0,
|
|
4109
|
+
cssRuleCount: 0,
|
|
4110
|
+
imgCount: 0,
|
|
4111
|
+
hasImgRule: false,
|
|
4112
|
+
cssFiles: [],
|
|
4113
|
+
htmlFiles: [],
|
|
4114
|
+
};
|
|
4115
|
+
|
|
4116
|
+
// Gather all HTML and CSS files
|
|
4117
|
+
const stack = [projectDir];
|
|
4118
|
+
const skipDirs = new Set(['node_modules', '.git', '.nha-shims', 'dist', 'build', '.next']);
|
|
4119
|
+
while (stack.length) {
|
|
4120
|
+
const cur = stack.pop();
|
|
4121
|
+
let entries;
|
|
4122
|
+
try { entries = fs.readdirSync(cur, { withFileTypes: true }); } catch { continue; }
|
|
4123
|
+
for (const ent of entries) {
|
|
4124
|
+
if (skipDirs.has(ent.name) || ent.name.startsWith('.')) continue;
|
|
4125
|
+
const abs = path.join(cur, ent.name);
|
|
4126
|
+
if (ent.isDirectory()) { stack.push(abs); continue; }
|
|
4127
|
+
const ext = path.extname(ent.name).toLowerCase();
|
|
4128
|
+
if (ext === '.html' || ext === '.htm') result.htmlFiles.push(abs);
|
|
4129
|
+
else if (ext === '.css') result.cssFiles.push(abs);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
if (result.htmlFiles.length === 0) return result;
|
|
4134
|
+
|
|
4135
|
+
// Extract HTML classes/ids
|
|
4136
|
+
const htmlSelectorSet = new Set();
|
|
4137
|
+
for (const htmlAbs of result.htmlFiles) {
|
|
4138
|
+
let html;
|
|
4139
|
+
try { html = fs.readFileSync(htmlAbs, 'utf-8'); } catch { continue; }
|
|
4140
|
+
// Count tags
|
|
4141
|
+
const tagMatches = html.match(/<[a-zA-Z][a-zA-Z0-9-]*/g) || [];
|
|
4142
|
+
result.tagCount += tagMatches.length;
|
|
4143
|
+
// Count img tags specifically
|
|
4144
|
+
result.imgCount += (html.match(/<img\b/gi) || []).length;
|
|
4145
|
+
// Extract classes
|
|
4146
|
+
const classRe = /\bclass\s*=\s*["']([^"']+)["']/g;
|
|
4147
|
+
let m;
|
|
4148
|
+
while ((m = classRe.exec(html)) !== null) {
|
|
4149
|
+
for (const cls of m[1].split(/\s+/)) {
|
|
4150
|
+
if (cls.trim()) htmlSelectorSet.add('.' + cls.trim());
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
// Extract IDs
|
|
4154
|
+
const idRe = /\bid\s*=\s*["']([^"']+)["']/g;
|
|
4155
|
+
while ((m = idRe.exec(html)) !== null) {
|
|
4156
|
+
if (m[1].trim()) htmlSelectorSet.add('#' + m[1].trim());
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
result.htmlSelectors = [...htmlSelectorSet];
|
|
4160
|
+
|
|
4161
|
+
if (result.htmlSelectors.length === 0) {
|
|
4162
|
+
result.coverage = 1;
|
|
4163
|
+
return result;
|
|
4164
|
+
}
|
|
4165
|
+
|
|
4166
|
+
// Extract CSS selectors (simple: split on { and look at preceding token)
|
|
4167
|
+
for (const cssAbs of result.cssFiles) {
|
|
4168
|
+
let css;
|
|
4169
|
+
try { css = fs.readFileSync(cssAbs, 'utf-8'); } catch { continue; }
|
|
4170
|
+
// Strip comments
|
|
4171
|
+
css = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
4172
|
+
// Find rule blocks: anything before { ... }
|
|
4173
|
+
const ruleRe = /([^{}]+)\{[^{}]*\}/g;
|
|
4174
|
+
let m;
|
|
4175
|
+
while ((m = ruleRe.exec(css)) !== null) {
|
|
4176
|
+
result.cssRuleCount++;
|
|
4177
|
+
const selectorList = m[1].trim();
|
|
4178
|
+
if (selectorList.startsWith('@')) continue; // @media, @keyframes etc.
|
|
4179
|
+
// Split combined selectors (.a, .b, .c) and extract bare class/id tokens
|
|
4180
|
+
for (const sel of selectorList.split(',')) {
|
|
4181
|
+
const trimmed = sel.trim();
|
|
4182
|
+
// Find all .classname and #id tokens in this selector
|
|
4183
|
+
const classM = trimmed.match(/\.[\w-]+/g) || [];
|
|
4184
|
+
const idM = trimmed.match(/#[\w-]+/g) || [];
|
|
4185
|
+
for (const t of [...classM, ...idM]) result.cssSelectors.add(t);
|
|
4186
|
+
// Track if any rule targets `img`
|
|
4187
|
+
if (/(^|[\s,>+~])img(\s*[.{#:]|\s*$)/.test(trimmed)) {
|
|
4188
|
+
if (css.slice(m.index, m.index + m[0].length).match(/max-width|object-fit/)) {
|
|
4189
|
+
result.hasImgRule = true;
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
// Compute coverage
|
|
4197
|
+
for (const sel of result.htmlSelectors) {
|
|
4198
|
+
if (!result.cssSelectors.has(sel)) result.missing.push(sel);
|
|
4199
|
+
}
|
|
4200
|
+
result.coverage = (result.htmlSelectors.length - result.missing.length) / result.htmlSelectors.length;
|
|
4201
|
+
|
|
4202
|
+
return result;
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
/**
|
|
4206
|
+
* Auto-extend CSS by calling LLM when coverage is below threshold or layout
|
|
4207
|
+
* is clearly broken (e.g. <img> tags with no max-width rule). No user prompt;
|
|
4208
|
+
* triggers automatically in pre-flight.
|
|
4209
|
+
*/
|
|
4210
|
+
export async function _autoExtendStylesIfNeeded(projectName, config, emit, opts) {
|
|
4211
|
+
opts = opts || {};
|
|
4212
|
+
const minCoverage = opts.minCoverage ?? 0.6;
|
|
4213
|
+
const dir = ProjectStore.dir(projectName);
|
|
4214
|
+
if (!fs.existsSync(dir)) return { extended: false, reason: 'project not found' };
|
|
4215
|
+
|
|
4216
|
+
const analysis = _analyzeCssCoverage(dir);
|
|
4217
|
+
if (analysis.htmlSelectors.length === 0) return { extended: false, reason: 'no HTML selectors' };
|
|
4218
|
+
|
|
4219
|
+
const needsExtend =
|
|
4220
|
+
analysis.coverage < minCoverage ||
|
|
4221
|
+
(analysis.imgCount >= 2 && !analysis.hasImgRule) ||
|
|
4222
|
+
(analysis.tagCount > 50 && analysis.cssRuleCount < 20);
|
|
4223
|
+
|
|
4224
|
+
if (!needsExtend) {
|
|
4225
|
+
return {
|
|
4226
|
+
extended: false,
|
|
4227
|
+
reason: 'coverage acceptable',
|
|
4228
|
+
coverage: analysis.coverage,
|
|
4229
|
+
missing: analysis.missing.length,
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
// Find the primary CSS file to extend (largest non-placeholder)
|
|
4234
|
+
const cssCandidates = [];
|
|
4235
|
+
for (const cssAbs of analysis.cssFiles) {
|
|
4236
|
+
try {
|
|
4237
|
+
const content = fs.readFileSync(cssAbs, 'utf-8');
|
|
4238
|
+
if (/nha-webcraft:.*placeholder/i.test(content.slice(0, 200))) continue;
|
|
4239
|
+
cssCandidates.push({ abs: cssAbs, size: content.length, content });
|
|
4240
|
+
} catch {}
|
|
4241
|
+
}
|
|
4242
|
+
cssCandidates.sort((a, b) => b.size - a.size);
|
|
4243
|
+
if (cssCandidates.length === 0) return { extended: false, reason: 'no real CSS files to extend' };
|
|
4244
|
+
|
|
4245
|
+
const target = cssCandidates[0];
|
|
4246
|
+
const targetRel = path.relative(dir, target.abs).replace(/\\/g, '/');
|
|
4247
|
+
|
|
4248
|
+
// Build prompt with HTML samples + current CSS + missing selectors
|
|
4249
|
+
let htmlSample = '';
|
|
4250
|
+
for (const htmlAbs of analysis.htmlFiles.slice(0, 3)) {
|
|
4251
|
+
try {
|
|
4252
|
+
const c = fs.readFileSync(htmlAbs, 'utf-8');
|
|
4253
|
+
htmlSample += `### ${path.relative(dir, htmlAbs)}\n${c.slice(0, 3500)}\n\n`;
|
|
4254
|
+
} catch {}
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
const sys = 'You are an expert frontend designer. Extend the existing CSS to cover ALL classes/ids found in the HTML, maintaining the existing design language (colors, gradients, typography, spacing). Add responsive mobile-first rules. Output ONLY the complete extended CSS file content — no markdown fences, no explanations.';
|
|
4258
|
+
const user =
|
|
4259
|
+
`Extend this CSS file: \`${targetRel}\`\n\n` +
|
|
4260
|
+
`Current CSS (${target.size} bytes, ${analysis.cssRuleCount} rules):\n\`\`\`css\n${target.content.slice(0, 6000)}\n\`\`\`\n\n` +
|
|
4261
|
+
`Missing selectors (${analysis.missing.length}):\n${analysis.missing.slice(0, 50).join(', ')}\n\n` +
|
|
4262
|
+
`HTML files for context:\n${htmlSample}\n\n` +
|
|
4263
|
+
`Output the COMPLETE extended CSS file (existing rules + new rules for all missing selectors). Add:\n` +
|
|
4264
|
+
`- img { max-width: 100%; height: auto; object-fit: cover; }\n` +
|
|
4265
|
+
`- Layout for each missing class/id, coherent with the existing design language\n` +
|
|
4266
|
+
`- Responsive breakpoints at 768px and 480px\n` +
|
|
4267
|
+
`- Hover/transition states for interactive elements`;
|
|
4268
|
+
|
|
4269
|
+
if (emit) emit({ type: 'status', msg: `CSS coverage ${(analysis.coverage * 100).toFixed(0)}% (${analysis.missing.length} selectors missing). Auto-extending ${targetRel} via LLM...` });
|
|
4270
|
+
|
|
4271
|
+
let body = '';
|
|
4272
|
+
try {
|
|
4273
|
+
await callLLMStream(config, sys, user, (chunk) => { body += chunk; }, { max_tokens: 8192 });
|
|
4274
|
+
} catch (e) {
|
|
4275
|
+
if (emit) emit({ type: 'warn', msg: `CSS extend failed: ${(e.message || e).slice(0, 200)}` });
|
|
4276
|
+
return { extended: false, reason: 'llm_failed', error: e.message };
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
// Strip markdown fences
|
|
4280
|
+
body = body.replace(/^```[a-zA-Z]*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
|
|
4281
|
+
if (_looksLikeLLMError(body) || body.length < target.size * 0.5) {
|
|
4282
|
+
if (emit) emit({ type: 'warn', msg: `CSS extend produced suspicious output (${body.length} bytes vs ${target.size} original) — keeping original.` });
|
|
4283
|
+
return { extended: false, reason: 'output_too_short_or_error' };
|
|
4284
|
+
}
|
|
4285
|
+
|
|
4286
|
+
// Backup + write
|
|
4287
|
+
try {
|
|
4288
|
+
fs.writeFileSync(target.abs + '.before-extend-' + Date.now(), target.content, 'utf-8');
|
|
4289
|
+
fs.writeFileSync(target.abs, body, 'utf-8');
|
|
4290
|
+
} catch (e) {
|
|
4291
|
+
return { extended: false, reason: 'write_failed', error: e.message };
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
// Re-analyze to confirm improvement
|
|
4295
|
+
const after = _analyzeCssCoverage(dir);
|
|
4296
|
+
if (emit) emit({ type: 'status', msg: `CSS extended: ${targetRel} ${target.size} → ${body.length} bytes. Coverage ${(analysis.coverage * 100).toFixed(0)}% → ${(after.coverage * 100).toFixed(0)}%.` });
|
|
4297
|
+
|
|
4298
|
+
return {
|
|
4299
|
+
extended: true,
|
|
4300
|
+
file: targetRel,
|
|
4301
|
+
sizeBefore: target.size,
|
|
4302
|
+
sizeAfter: body.length,
|
|
4303
|
+
coverageBefore: analysis.coverage,
|
|
4304
|
+
coverageAfter: after.coverage,
|
|
4305
|
+
missingBefore: analysis.missing.length,
|
|
4306
|
+
missingAfter: after.missing.length,
|
|
4307
|
+
};
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4003
4310
|
export function autoRepairProject(projectName) {
|
|
4004
4311
|
const dir = ProjectStore.dir(projectName);
|
|
4005
4312
|
if (!fs.existsSync(dir)) throw new Error('project not found');
|
|
@@ -853,8 +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:[]}])}let t=await E(`/api/studio/webcraft/complete`,{projectName:c}),n=t?.
|
|
857
|
-
`)}`,tools:[]}])}await It([...e?.filesRepaired||[],...e?.filesCreated||[],...(t?.
|
|
856
|
+
`);Ie(n=>[...n,{role:`agent`,text:`🔧 Auto-repair: ${e.summary}\n${t}`,tools:[]}])}let t=await E(`/api/studio/webcraft/complete`,{projectName:c}),n=t?.htmlRewrites?.length||0,r=t?.llmFills?.length||0,i=t?.stubFallbacks?.length||0;if(n+r+i>0){let e=[];n&&e.push(...(t.htmlRewrites||[]).map(e=>` • ${e.from}: ${e.oldHref} → ${e.newHref} (point to real file)`)),r&&e.push(...(t.llmFills||[]).map(e=>` • ${e.target} (${e.length} bytes, LLM-generated)`)),i&&e.push(...(t.stubFallbacks||[]).map(e=>` • ${e} (stub fallback — LLM unavailable)`)),Ie(t=>[...t,{role:`agent`,text:`✨ Asset completion: ${n} HTML rewrites, ${r} via LLM${i?`, ${i} as stubs`:``}\n${e.join(`
|
|
857
|
+
`)}`,tools:[]}])}let a=await E(`/api/studio/webcraft/extend-styles`,{projectName:c});a?.extended&&Ie(e=>[...e,{role:`agent`,text:`🎨 Styles auto-extended: ${a.file} (${a.sizeBefore}→${a.sizeAfter} bytes, coverage ${((a.coverageBefore||0)*100).toFixed(0)}%→${((a.coverageAfter||0)*100).toFixed(0)}%, ${(a.missingBefore||0)-(a.missingAfter||0)} new selectors covered)`,tools:[]}]),await It([...e?.filesRepaired||[],...e?.filesCreated||[],...(t?.htmlRewrites||[]).map(e=>e.from),...(t?.llmFills||[]).map(e=>e.target),...a?.file?[a.file]:[]],{}),await kt(c)}catch(e){console.error(`[fix] failed:`,e)}},children:`Fix`})]}),(0,k.jsx)(WT,{files:g,activeIndex:y,unsavedFiles:ee,errorFiles:new Set(fe.filter(e=>e.severity===`error`).map(e=>e.file)),onSelect:e=>{b(e),w(null),S(null),N&&me.current!==null&&e!==me.current&&(he.current&&clearTimeout(he.current),he.current=setTimeout(()=>{me.current!==null&&(b(me.current),S(null))},1e4))}})]}),(0,k.jsx)(`div`,{className:Q.codeEditorWrap,children:rn&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`div`,{className:Q.codeHeader,children:[(0,k.jsx)(`span`,{className:Q.codeFileIcon,children:rE(rn.name)}),(0,k.jsx)(`span`,{className:Q.codeFileName,children:rn.name}),rn.content&&!rn._error&&(0,k.jsxs)(`span`,{className:Q.codeFileMeta,children:[(rn.content||``).split(`
|
|
858
858
|
`).length,` righe · `,iE(rn.content||``)]}),!rn._pending&&!rn._error&&rn.content&&(0,k.jsx)(`button`,{className:`${Q.editToggleBtn} ${C===null?``:Q.editToggleBtnActive}`,onClick:()=>{C===null?w(rn.content):(v(e=>e.map((e,t)=>t===y?{...e,content:C}:e)),E(`/api/studio/webcraft/file/write`,{projectName:c,path:rn.name,content:C}),w(null))},children:C===null?`✏ Modifica`:`💾 Salva`}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,title:`Split view`,onClick:()=>ce(se===null?+(y===0&&g.length>1):null),children:`⫼`}),(0,k.jsx)(`button`,{className:`${Q.headerIconBtn} ${A?Q.headerIconBtnActive:``}`,title:`Terminal`,onClick:()=>oe(!A),children:`⌨`}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,title:`Development Guide`,onClick:()=>ue(!0),children:`📖`})]}),ne&&(0,k.jsxs)(`div`,{className:Q.findBar,children:[(0,k.jsx)(`input`,{className:Q.findInput,value:D,onChange:e=>ie(e.target.value),placeholder:`Find...`,autoFocus:!0}),(0,k.jsx)(`input`,{className:Q.findInput,value:O,onChange:e=>ae(e.target.value),placeholder:`Replace...`}),(0,k.jsx)(`span`,{className:Q.findCount,children:D?((rn.content||``).match(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`gi`))?.length||0)+` found`:``}),(0,k.jsx)(`button`,{className:Q.findBtn,onClick:()=>{!D||C===null||w(C.replace(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`i`),O))},children:`Replace`}),(0,k.jsx)(`button`,{className:Q.findBtn,onClick:()=>{!D||C===null||w(C.replace(new RegExp(D.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),`gi`),O))},children:`All`}),(0,k.jsx)(`button`,{className:Q.findClose,onClick:()=>re(!1),children:`×`})]}),rn._error&&(0,k.jsx)(`div`,{className:Q.fileError,children:`⚠ Generazione fallita — chiedi al modello di rigenerare questo file`}),rn._syntaxError&&!rn._error&&(0,k.jsxs)(`div`,{className:Q.fileSyntaxError,children:[`⚠ Syntax error: `,rn._syntaxError]}),N&&x!==null?(0,k.jsx)(`pre`,{className:Q.streamingPre,ref:e=>{e&&ge.current&&(e.scrollTop=e.scrollHeight)},onScroll:e=>{let t=e.currentTarget;t.scrollHeight-t.scrollTop-t.clientHeight<50?ge.current=!0:(ge.current=!1,_e.current&&clearTimeout(_e.current),_e.current=setTimeout(()=>{ge.current=!0},15e3))},dangerouslySetInnerHTML:{__html:QT(x||``,(rn.name.split(`.`).pop()||`js`).toLowerCase())+`<span class="`+Q.streamingCursor+`">▋</span>`}}):(0,k.jsx)(RT,{value:C===null?rn.content||``:C,filename:rn.name,readOnly:C===null,projectName:c,onChange:e=>{w(e),rn&&te(e=>new Set(e).add(rn.name))},onSave:e=>{v(t=>t.map((t,n)=>n===y?{...t,content:e}:t)),E(`/api/studio/webcraft/file/write`,{projectName:c,path:rn.name,content:e}),w(null),te(e=>{let t=new Set(e);return t.delete(rn.name),t})}})]})}),se!==null&&g[se]&&(0,k.jsxs)(`div`,{className:Q.codeEditorWrap,children:[(0,k.jsxs)(`div`,{className:Q.codeHeader,children:[(0,k.jsx)(`span`,{className:Q.codeFileIcon,children:rE(g[se].name)}),(0,k.jsx)(`span`,{className:Q.codeFileName,children:g[se].name}),(0,k.jsx)(`button`,{className:Q.headerIconBtn,onClick:()=>ce(null),children:`✕`})]}),(0,k.jsx)(RT,{value:g[se].content||``,filename:g[se].name,readOnly:!0})]})]}),A&&(0,k.jsxs)(`div`,{className:Q.terminalPanel,children:[(0,k.jsxs)(`div`,{className:Q.terminalHeader,children:[(0,k.jsx)(`span`,{className:Q.terminalTitle,children:`Terminal`}),(0,k.jsx)(`button`,{className:Q.terminalClose,onClick:()=>oe(!1),children:`✕`})]}),(0,k.jsx)(YT,{projectDir:c||void 0})]})]})})]})]})]})}),He&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.planBanner,children:[(0,k.jsx)(`div`,{className:Q.planTitle,children:`📌 Piano proposto — approva per eseguire`}),(0,k.jsx)(`pre`,{className:Q.planText,children:He.plan}),(0,k.jsxs)(`div`,{className:Q.planActions,children:[(0,k.jsx)(`button`,{className:Q.planApprove,onClick:en,children:`✓ Esegui`}),(0,k.jsx)(`button`,{className:Q.planReject,onClick:()=>Ue(null),children:`✕ Annulla`})]})]}),Qe&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.grepPanel,children:[(0,k.jsxs)(`div`,{className:Q.grepRow,children:[(0,k.jsx)(`input`,{className:Q.grepInput,value:et,onChange:e=>tt(e.target.value),onKeyDown:e=>e.key===`Enter`&&Kt(),placeholder:`Cerca nel codice...`}),(0,k.jsx)(`button`,{className:Q.grepBtn,onClick:Kt,children:`🔍`}),(0,k.jsx)(`button`,{className:Q.grepClose,onClick:()=>$e(!1),children:`×`})]}),nt.length>0&&(0,k.jsxs)(`div`,{className:Q.grepCount,children:[nt.length,` risultati`]}),(0,k.jsx)(`div`,{className:Q.grepResults,children:nt.length===0?(0,k.jsx)(`div`,{className:Q.grepEmpty,children:`Nessun risultato.`}):nt.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.grepMatch,onClick:()=>qt(e.file),children:[(0,k.jsxs)(`span`,{className:Q.grepMatchFile,children:[e.file,`:`,e.lineNum]}),(0,k.jsx)(`pre`,{className:Q.grepMatchLine,children:e.line})]},t))})]}),it.length>0&&t!==`projects`&&(0,k.jsxs)(`div`,{className:Q.diffPanel,children:[(0,k.jsxs)(`div`,{className:Q.diffHeader,children:[(0,k.jsxs)(`span`,{children:[`🔌 Diff — `,it.length,` file modificati`]}),(0,k.jsx)(`button`,{className:Q.diffClose,onClick:()=>at([]),children:`✕ Chiudi`})]}),it.map((e,t)=>{let n=(e.after||``).split(`
|
|
859
859
|
`).length-(e.before||``).split(`
|
|
860
860
|
`).length;return(0,k.jsxs)(`details`,{open:!0,className:Q.diffFile,children:[(0,k.jsxs)(`summary`,{className:Q.diffSummary,children:[(0,k.jsx)(`span`,{className:Q.diffArrow,children:`▲`}),(0,k.jsx)(`span`,{className:Q.diffFileName,children:e.file}),(0,k.jsxs)(`span`,{className:n>=0?Q.diffAdded:Q.diffRemoved,children:[n>=0?`+`:``,n,` linee`]})]}),(0,k.jsx)(`div`,{className:Q.diffContent,children:(0,k.jsx)(uE,{before:e.before,after:e.after})})]},t)})]}),t!==`projects`&&(0,k.jsxs)(`div`,{className:`${Q.chatPanel} ${ut?Q.chatPanelCollapsed:``}`,children:[(0,k.jsxs)(`button`,{className:Q.chatCollapseBtn,onClick:()=>dt(e=>!e),children:[(0,k.jsx)(`span`,{children:ut?`▲`:`▼`}),(0,k.jsx)(`span`,{children:ut?`Show Chat`:`Hide Chat`}),Fe.length>0&&(0,k.jsxs)(`span`,{style:{opacity:.5},children:[`(`,Fe.length,`)`]})]}),(0,k.jsxs)(`div`,{className:Q.chatMessages,ref:Tt,children:[Fe.length===0&&nn&&(0,k.jsxs)(`div`,{className:Q.chatWelcome,children:[`🤖 `,e(`webcraft.doctrine.title`),` — `,(0,k.jsx)(`button`,{className:Q.doctrineOpenBtn,onClick:()=>ue(!0),children:`📖 Open Guide`})]}),Fe.map((e,t)=>(0,k.jsxs)(`div`,{className:e.role===`user`?Q.chatUser:e.role===`system`?Q.chatSystem:Q.chatAgent,children:[e.role===`user`&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`div`,{className:Q.chatUserBubble,children:e.text}),e.attachments&&e.attachments.length>0&&(0,k.jsx)(`div`,{className:Q.chatAttachPreviews,children:e.attachments.map((e,t)=>(0,k.jsxs)(`span`,{className:Q.chatAttachBadge,children:[`📎 `,e.name]},t))})]}),e.role===`system`&&(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`div`,{className:Q.chatSystemBubble,children:e.text}),e.syntaxErrors?.map((e,t)=>(0,k.jsxs)(`div`,{className:Q.chatSyntaxErr,children:[`✕ `,e.file,`: `,e.error]},t))]}),e.role===`agent`&&(()=>{let t=e.text.replace(/<tool>[\s\S]*?<\/tool>/g,``).replace(/<done\s*\/?>/g,``).trim(),n=(e.tools||[]).filter(e=>(e.op===`edit`||e.op===`write`)&&(e.result===`ok`||e.result===`ok_fuzzy`||e.result===`ok_repaired`)),r=(e.tools||[]).filter(e=>e.result?.includes(`not_found`)||e.result?.includes(`error`)||e.result===`blocked_use_edit`),a=t.match(/^(.{10,120}?)[.\n]/),o=a?a[1]+`.`:t.slice(0,120),s=t.length>130;return(0,k.jsxs)(`div`,{className:Q.chatAgentCard,children:[n.map((e,t)=>(0,k.jsxs)(`div`,{style:{margin:`6px 0`,borderRadius:8,overflow:`hidden`,border:`1px solid rgba(255,255,255,0.08)`},children:[(0,k.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,padding:`6px 10px`,background:`rgba(99,102,241,0.1)`,fontSize:11},children:[(0,k.jsxs)(`span`,{style:{fontWeight:600,color:`#818cf8`,cursor:`pointer`},onClick:()=>{let t=g.findIndex(t=>t.name===e.path);t>=0&&(b(t),i(`files`))},children:[`✏ `,e.path]}),(0,k.jsx)(`span`,{style:{color:`#4ade80`,fontSize:10,fontWeight:600},children:e.result===`ok_fuzzy`?`applied (fuzzy)`:e.result===`ok_repaired`?`applied (repaired)`:`✓ applied`})]}),e.oldSnippet||e.newSnippet?(0,k.jsx)(uE,{before:e.oldSnippet||``,after:e.newSnippet||``,contextLines:3}):(0,k.jsx)(`div`,{style:{padding:`6px 10px`,fontSize:11,color:`#4ade80`,background:`rgba(74,222,128,0.05)`},children:`File modified successfully`})]},t)),r.length>0&&(0,k.jsx)(`div`,{style:{margin:`6px 0`,padding:`6px 10px`,background:`rgba(248,113,113,0.08)`,borderRadius:6,fontSize:11,color:`#f87171`},children:r.map((e,t)=>(0,k.jsxs)(`div`,{children:[`❌ `,e.op,` `,e.path,`: `,typeof e.result==`string`?e.result.slice(0,100):``]},t))}),t&&(s?(0,k.jsxs)(`details`,{style:{margin:`6px 0`,fontSize:11},children:[(0,k.jsx)(`summary`,{style:{cursor:`pointer`,color:`var(--dim)`,padding:`4px 0`,userSelect:`none`},children:o.slice(0,100)}),(0,k.jsx)(`div`,{className:Q.chatAgentText,style:{fontSize:11,opacity:.8,marginTop:4},dangerouslySetInnerHTML:{__html:$T(t)}})]}):(0,k.jsx)(`div`,{className:Q.chatAgentText,style:{fontSize:11,opacity:.8},dangerouslySetInnerHTML:{__html:$T(t)}}))]})})()]},t)),ze&&(()=>{let e=Fe[Fe.length-1]?.tools??[],t=e[e.length-1],n=t?t.op===`read`?`Reading ${t.path}`:t.op===`edit`?`Editing ${t.path}`:t.op===`search`?`Searching...`:t.op===`lint`?`Linting ${t.path}`:t.op===`check`?`Checking ${t.path}`:t.op===`run`?`Running command...`:t.op===`sandbox`?`Starting sandbox...`:t.op:`Thinking...`;return(0,k.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8,padding:`8px 12px`,fontSize:12,color:`#818cf8`},children:[(0,k.jsx)(`span`,{className:Q.chatAgentRobotAnim,style:{fontSize:14},children:`⟳`}),(0,k.jsx)(`span`,{style:{fontWeight:500},children:n})]})})()]}),Be.length>0&&(0,k.jsx)(`div`,{className:Q.attachPreviews,children:Be.map((e,t)=>(0,k.jsxs)(`span`,{className:Q.attachBadge,children:[`📎 `,e.name,(0,k.jsx)(`button`,{className:Q.removeAttachBtn,onClick:()=>Ve(e=>e.filter((e,n)=>n!==t)),children:`×`})]},t))}),nn?(0,k.jsxs)(`div`,{className:Q.projActiveRow,children:[`📄 `,(0,k.jsx)(`strong`,{className:Q.projActiveName,children:c}),` — scrivi per modificare o migliorare il progetto`]}):(0,k.jsxs)(`div`,{className:Q.projNameRow,children:[(0,k.jsx)(`span`,{className:Q.projNameLabel,children:`Nome progetto:`}),(0,k.jsx)(`input`,{className:Q.projNameInput,value:c,onChange:e=>l(e.target.value),placeholder:`MioProgetto`})]}),(0,k.jsxs)(`div`,{className:Q.chatInputRow,children:[(0,k.jsxs)(`label`,{className:Q.attachLabel,title:`Allega immagine o PDF`,children:[`📎`,(0,k.jsx)(`input`,{ref:Et,type:`file`,multiple:!0,accept:`image/*,.pdf`,style:{display:`none`},onChange:e=>tn(e.target.files)})]}),(0,k.jsx)(`textarea`,{className:Q.chatTextarea,value:Le,onChange:e=>Re(e.target.value),placeholder:nn?`Parla con il tuo agente: chiedi correzioni, migliorie, nuove funzionalità...`:`Descrivi il progetto da creare, poi premi Genera...`,disabled:U,onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),Pt())},rows:4}),(0,k.jsxs)(`div`,{className:Q.chatSendCol,children:[(0,k.jsx)(`button`,{className:Q.chatSendBtn,onClick:()=>Pt(),disabled:U,children:N?`⏳`:nn?`▶`:`▶ Genera`}),U&&!we&&(0,k.jsx)(`button`,{className:Q.chatStopBtn,onClick:Lt,children:`⏹ Stop`})]})]})]}),le&&(0,k.jsx)(`div`,{className:Q.modalOverlay,onClick:()=>ue(!1),children:(0,k.jsxs)(`div`,{className:Q.modal,onClick:e=>e.stopPropagation(),style:{width:720,maxHeight:`90vh`},children:[(0,k.jsxs)(`div`,{className:Q.modalHeader,children:[(0,k.jsxs)(`span`,{className:Q.modalTitle,children:[`📖 `,e(`webcraft.doctrine.title`)]}),(0,k.jsx)(`span`,{className:Q.doctrineSubtitle,children:e(`webcraft.doctrine.subtitle`)}),(0,k.jsx)(`button`,{className:Q.modalClose,onClick:()=>ue(!1),children:`✕`})]}),(0,k.jsx)(`div`,{className:Q.modalBody,style:{gap:0},children:[`phase1`,`phase2`,`phase3`,`phase4`,`phase5`,`tools`,`golden`].map(t=>(0,k.jsxs)(`div`,{className:Q.doctrineSection,children:[(0,k.jsx)(`div`,{className:Q.doctrineSectionTitle,children:e(`webcraft.doctrine.${t}.title`)}),(0,k.jsx)(`div`,{className:Q.doctrineSectionBody,children:e(`webcraft.doctrine.${t}.desc`).split(`
|
package/src/ui-dist/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
10
|
<title>NHA — NotHumanAllowed</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CnfvvIP7.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-nUBdqB1O.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|