nothumanallowed 13.5.44 → 13.5.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 +1 -1
- package/src/commands/ui.mjs +75 -0
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +144 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.45",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -3865,6 +3865,81 @@ ${completedHeadings ? `## SECTIONS ALREADY WRITTEN (headings only):\n${completed
|
|
|
3865
3865
|
return;
|
|
3866
3866
|
}
|
|
3867
3867
|
|
|
3868
|
+
// ── WebCraft Projects — list and delete saved projects ──────────────────
|
|
3869
|
+
// GET /api/studio/webcraft/projects → { projects: [{name, description, fileCount, createdAt, dir}] }
|
|
3870
|
+
// DELETE /api/studio/webcraft/projects/:name → { ok }
|
|
3871
|
+
// POST /api/studio/webcraft/projects/save → saves meta+files, { ok, dir }
|
|
3872
|
+
if (pathname === '/api/studio/webcraft/projects' && method === 'GET') {
|
|
3873
|
+
const wcBaseDir = path.join(os.homedir(), '.nha', 'webcraft');
|
|
3874
|
+
const projects = [];
|
|
3875
|
+
if (fs.existsSync(wcBaseDir)) {
|
|
3876
|
+
for (const entry of fs.readdirSync(wcBaseDir, { withFileTypes: true })) {
|
|
3877
|
+
if (!entry.isDirectory()) continue;
|
|
3878
|
+
const metaPath = path.join(wcBaseDir, entry.name, 'webcraft-meta.json');
|
|
3879
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
3880
|
+
try {
|
|
3881
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
3882
|
+
projects.push({ name: entry.name, description: meta.description || '', fileCount: (meta.files || []).length, createdAt: meta.createdAt || '', dir: path.join(wcBaseDir, entry.name) });
|
|
3883
|
+
} catch(_) {}
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
projects.sort((a, b) => (b.createdAt > a.createdAt ? 1 : -1));
|
|
3887
|
+
sendJSON(res, 200, { projects });
|
|
3888
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3889
|
+
return;
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
if (pathname.startsWith('/api/studio/webcraft/projects/') && method === 'DELETE') {
|
|
3893
|
+
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/projects/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
3894
|
+
if (!projName) { sendJSON(res, 400, { error: 'invalid name' }); return; }
|
|
3895
|
+
const projDir = path.join(os.homedir(), '.nha', 'webcraft', projName);
|
|
3896
|
+
if (fs.existsSync(projDir)) fs.rmSync(projDir, { recursive: true, force: true });
|
|
3897
|
+
sendJSON(res, 200, { ok: true });
|
|
3898
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
if (pathname === '/api/studio/webcraft/projects/save' && method === 'POST') {
|
|
3903
|
+
const body = await parseBody(req, 16 * 1024 * 1024); // 16MB
|
|
3904
|
+
const projName = (body.projectName || 'webcraft').replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
3905
|
+
const projDir = path.join(os.homedir(), '.nha', 'webcraft', projName);
|
|
3906
|
+
fs.mkdirSync(projDir, { recursive: true });
|
|
3907
|
+
// Write each file with full folder structure
|
|
3908
|
+
for (const f of (body.files || [])) {
|
|
3909
|
+
const fp = path.join(projDir, f.name);
|
|
3910
|
+
fs.mkdirSync(path.dirname(fp), { recursive: true });
|
|
3911
|
+
fs.writeFileSync(fp, f.content, 'utf8');
|
|
3912
|
+
}
|
|
3913
|
+
// Write meta
|
|
3914
|
+
const meta = { projectName: projName, description: body.description || '', files: (body.files || []).map(f => f.name), createdAt: new Date().toISOString() };
|
|
3915
|
+
fs.writeFileSync(path.join(projDir, 'webcraft-meta.json'), JSON.stringify(meta, null, 2), 'utf8');
|
|
3916
|
+
sendJSON(res, 200, { ok: true, dir: projDir });
|
|
3917
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3918
|
+
return;
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
// GET /api/studio/webcraft/projects/load/:name → { projectName, description, files[] }
|
|
3922
|
+
if (pathname.startsWith('/api/studio/webcraft/projects/load/') && method === 'GET') {
|
|
3923
|
+
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/projects/load/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
3924
|
+
const projDir = path.join(os.homedir(), '.nha', 'webcraft', projName);
|
|
3925
|
+
const metaPath = path.join(projDir, 'webcraft-meta.json');
|
|
3926
|
+
if (!fs.existsSync(metaPath)) { sendJSON(res, 404, { error: 'not found' }); return; }
|
|
3927
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
3928
|
+
const files = [];
|
|
3929
|
+
for (const fname of (meta.files || [])) {
|
|
3930
|
+
const fp = path.join(projDir, fname);
|
|
3931
|
+
if (fs.existsSync(fp)) {
|
|
3932
|
+
const content = fs.readFileSync(fp, 'utf8');
|
|
3933
|
+
const ext = fname.split('.').pop();
|
|
3934
|
+
const langMap = { js:'javascript', mjs:'javascript', ts:'typescript', json:'json', html:'html', css:'css', sql:'sql', md:'markdown', sh:'bash', env:'bash', conf:'nginx' };
|
|
3935
|
+
files.push({ name: fname, content, lang: langMap[ext] || 'text' });
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
sendJSON(res, 200, { projectName: meta.projectName, description: meta.description, files });
|
|
3939
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3940
|
+
return;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3868
3943
|
// ── WebCraft Sandbox — fullstack preview in ~/.nha/webcraft/<project> ──
|
|
3869
3944
|
// POST /api/studio/webcraft/sandbox/start { projectName, files[] }
|
|
3870
3945
|
// → SSE stream with log lines, ends with { type:'ready', port, dir }
|
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 = '13.5.
|
|
8
|
+
export const VERSION = '13.5.45';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -3149,6 +3149,9 @@ var I18N = {
|
|
|
3149
3149
|
wc_no_files:'Describe your project and click Generate',
|
|
3150
3150
|
wc_examples_label:'Examples',
|
|
3151
3151
|
wc_sandbox_start:'Launch Sandbox',
|
|
3152
|
+
wc_projects:'Projects',
|
|
3153
|
+
wc_no_projects:'No saved projects yet',
|
|
3154
|
+
wc_no_projects_hint:'Generate a project and it will be saved automatically',
|
|
3152
3155
|
},
|
|
3153
3156
|
it: {
|
|
3154
3157
|
chat:'Chat', studio:'Studio', settings:'Impostazioni', agents:'Agenti',
|
|
@@ -3186,6 +3189,9 @@ var I18N = {
|
|
|
3186
3189
|
wc_no_files:'Descrivi il progetto e clicca Genera',
|
|
3187
3190
|
wc_examples_label:'Esempi',
|
|
3188
3191
|
wc_sandbox_start:'Avvia Sandbox',
|
|
3192
|
+
wc_projects:'Progetti',
|
|
3193
|
+
wc_no_projects:'Nessun progetto salvato',
|
|
3194
|
+
wc_no_projects_hint:'Genera un progetto e verr\u00e0 salvato automaticamente',
|
|
3189
3195
|
},
|
|
3190
3196
|
es: {
|
|
3191
3197
|
chat:'Chat', studio:'Studio', settings:'Configuración', agents:'Agentes',
|
|
@@ -3222,6 +3228,9 @@ var I18N = {
|
|
|
3222
3228
|
wc_no_files:'Describe el proyecto y haz clic en Generar',
|
|
3223
3229
|
wc_examples_label:'Ejemplos',
|
|
3224
3230
|
wc_sandbox_start:'Iniciar Sandbox',
|
|
3231
|
+
wc_projects:'Proyectos',
|
|
3232
|
+
wc_no_projects:'No hay proyectos guardados',
|
|
3233
|
+
wc_no_projects_hint:'Genera un proyecto y se guardar\u00e1 autom\u00e1ticamente',
|
|
3225
3234
|
},
|
|
3226
3235
|
fr: {
|
|
3227
3236
|
chat:'Chat', studio:'Studio', settings:'Paramètres', agents:'Agents',
|
|
@@ -3258,6 +3267,9 @@ var I18N = {
|
|
|
3258
3267
|
wc_no_files:'D\u00e9crivez le projet et cliquez sur G\u00e9n\u00e9rer',
|
|
3259
3268
|
wc_examples_label:'Exemples',
|
|
3260
3269
|
wc_sandbox_start:'Lancer Sandbox',
|
|
3270
|
+
wc_projects:'Projets',
|
|
3271
|
+
wc_no_projects:'Aucun projet sauvegard\u00e9',
|
|
3272
|
+
wc_no_projects_hint:'G\u00e9n\u00e9rez un projet et il sera sauvegard\u00e9 automatiquement',
|
|
3261
3273
|
},
|
|
3262
3274
|
de: {
|
|
3263
3275
|
chat:'Chat', studio:'Studio', settings:'Einstellungen', agents:'Agenten',
|
|
@@ -3294,6 +3306,9 @@ var I18N = {
|
|
|
3294
3306
|
wc_no_files:'Beschreibe das Projekt und klicke auf Generieren',
|
|
3295
3307
|
wc_examples_label:'Beispiele',
|
|
3296
3308
|
wc_sandbox_start:'Sandbox starten',
|
|
3309
|
+
wc_projects:'Projekte',
|
|
3310
|
+
wc_no_projects:'Keine gespeicherten Projekte',
|
|
3311
|
+
wc_no_projects_hint:'Generiere ein Projekt und es wird automatisch gespeichert',
|
|
3297
3312
|
},
|
|
3298
3313
|
};
|
|
3299
3314
|
// Fallback to 'en' for unmapped languages
|
|
@@ -6228,6 +6243,8 @@ var wcState = {
|
|
|
6228
6243
|
}
|
|
6229
6244
|
};
|
|
6230
6245
|
var wcRightTab = 'files';
|
|
6246
|
+
var wcMainTab = 'new'; // 'new' | 'projects'
|
|
6247
|
+
var wcProjectsList = []; // cached list from server
|
|
6231
6248
|
|
|
6232
6249
|
function wcEsc(s){return s?String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'):''}
|
|
6233
6250
|
|
|
@@ -6276,74 +6293,68 @@ function renderWebCraft(el) {
|
|
|
6276
6293
|
}).join('') +
|
|
6277
6294
|
'</div></div>';
|
|
6278
6295
|
|
|
6279
|
-
|
|
6280
|
-
'<div style="display:flex;
|
|
6281
|
-
'<div
|
|
6282
|
-
'<h2 style="font-size:15px;color:var(--green);margin-bottom:
|
|
6296
|
+
var headerHtml =
|
|
6297
|
+
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;flex-shrink:0">' +
|
|
6298
|
+
'<div>' +
|
|
6299
|
+
'<h2 style="font-size:15px;color:var(--green);margin-bottom:2px">🛠 '+t('wc_title')+'</h2>' +
|
|
6283
6300
|
'<p style="font-size:11px;color:var(--dim);line-height:1.5">'+t('wc_subtitle')+'</p>' +
|
|
6284
6301
|
'</div>' +
|
|
6302
|
+
'<div style="display:flex;gap:6px;flex-shrink:0">' +
|
|
6303
|
+
'<button onclick="wcMainTabNew()" style="padding:5px 14px;border-radius:6px;border:1px solid var(--border2);background:'+(wcMainTab==='new'?'var(--green3)':'var(--bg3)')+';color:'+(wcMainTab==='new'?'var(--bg)':'var(--dim)')+';font-size:11px;font-weight:600;cursor:pointer">+ Nuovo</button>' +
|
|
6304
|
+
'<button onclick="wcMainTabProjects()" style="padding:5px 14px;border-radius:6px;border:1px solid var(--border2);background:'+(wcMainTab==='projects'?'var(--green3)':'var(--bg3)')+';color:'+(wcMainTab==='projects'?'var(--bg)':'var(--dim)')+';font-size:11px;font-weight:600;cursor:pointer">📁 '+t('wc_projects')+'</button>' +
|
|
6305
|
+
'</div>' +
|
|
6306
|
+
'</div>';
|
|
6285
6307
|
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
'<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px">' +
|
|
6295
|
-
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px;margin-bottom:8px">'+t('wc_project')+'</div>' +
|
|
6296
|
-
'<input id="wcProjectName" placeholder="'+t('wc_project_name')+'" value="'+wcEsc(wcState.projectName)+'" oninput="wcState.projectName=this.value" style="width:100%;padding:8px 10px;font-size:12px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);margin-bottom:8px;box-sizing:border-box">' +
|
|
6297
|
-
'<textarea id="wcDesc" placeholder="'+t('wc_desc')+' e.g. SaaS landing page with user registration, dashboard, Stripe-ready pricing section" rows="4" oninput="wcState.description=this.value" style="width:100%;padding:8px 10px;font-size:12px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);resize:vertical;box-sizing:border-box;line-height:1.5">'+wcEsc(wcState.description)+'</textarea>' +
|
|
6298
|
-
'</div>' +
|
|
6299
|
-
|
|
6300
|
-
// Blocks
|
|
6301
|
-
'<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px">' +
|
|
6302
|
-
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">'+t('wc_blocks')+'</div>' +
|
|
6303
|
-
['auth','cookieBanner','securityMiddleware','emailVerification'].map(function(b){
|
|
6304
|
-
var labels = {auth:'Auth (register/login/JWT)',cookieBanner:'GDPR Cookie Banner',securityMiddleware:'Security Middleware',emailVerification:'Email Verification'};
|
|
6305
|
-
var icons = {auth:'🔒',cookieBanner:'🍪',securityMiddleware:'🛡',emailVerification:'✉'};
|
|
6306
|
-
return '<label style="display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;font-size:11px;color:var(--text)">' +
|
|
6307
|
-
'<input type="checkbox"'+(wcState.blocks[b]?' checked':'')+' onchange="wcState.blocks['+JSON.stringify(b)+']=this.checked" style="accent-color:var(--green3);width:14px;height:14px">' +
|
|
6308
|
-
'<span>'+icons[b]+'</span><span>'+labels[b]+'</span>' +
|
|
6309
|
-
'</label>';
|
|
6310
|
-
}).join('') +
|
|
6311
|
-
'</div>' +
|
|
6312
|
-
|
|
6313
|
-
// Auth fields configurator
|
|
6314
|
-
'<div id="wcAuthFieldsPanel" style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px;'+(wcState.blocks.auth?'':'display:none')+'">' +
|
|
6315
|
-
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">' +
|
|
6316
|
-
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px">'+t('wc_auth_fields')+'</div>' +
|
|
6317
|
-
'<button onclick="wcAddField()" style="font-size:10px;padding:3px 8px;background:var(--bg3);border:1px solid var(--border2);border-radius:5px;color:var(--green);cursor:pointer">'+t('wc_add_field')+'</button>' +
|
|
6318
|
-
'</div>' +
|
|
6319
|
-
'<div id="wcFieldsList">'+authFieldsHtml+'</div>' +
|
|
6320
|
-
'<div style="font-size:9px;color:var(--dim);margin-top:4px">'+t('wc_required_hint')+'</div>' +
|
|
6321
|
-
'</div>' +
|
|
6322
|
-
|
|
6323
|
-
// Generate button
|
|
6324
|
-
'<button id="wcRunBtn" onclick="wcGenerate()" style="width:100%;padding:12px;background:var(--green3);border:none;border-radius:8px;color:var(--bg);font-size:13px;font-weight:700;cursor:pointer;letter-spacing:.2px"'+(wcState.running?' disabled':'')+'>'+
|
|
6325
|
-
(wcState.running ? '⏳ '+t('wc_generating')+'...' : '▶ '+t('wc_generate')) +
|
|
6326
|
-
'</button>' +
|
|
6327
|
-
|
|
6328
|
-
(wcState.generatedFiles.length > 0 ?
|
|
6329
|
-
'<button onclick="wcDownloadZip()" style="width:100%;padding:10px;background:var(--bg3);border:1px solid var(--border2);border-radius:8px;color:var(--text);font-size:12px;font-weight:600;cursor:pointer">⇩ '+t('wc_download')+'</button>' +
|
|
6330
|
-
'<button onclick="wcStartSandbox()" id="wcSandboxBtn" style="width:100%;padding:10px;background:var(--bg3);border:1px solid var(--green3);border-radius:8px;color:var(--green);font-size:12px;font-weight:600;cursor:pointer">▶ '+t('wc_sandbox_start')+'</button>'
|
|
6331
|
-
: '') +
|
|
6332
|
-
|
|
6308
|
+
var editorHtml =
|
|
6309
|
+
wcExHtml +
|
|
6310
|
+
'<div style="display:flex;gap:14px;align-items:flex-start;flex:1;min-height:0">' +
|
|
6311
|
+
'<div style="width:280px;flex-shrink:0;display:flex;flex-direction:column;gap:10px;overflow-y:auto;max-height:calc(100vh - 120px)">' +
|
|
6312
|
+
'<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px">' +
|
|
6313
|
+
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px;margin-bottom:8px">'+t('wc_project')+'</div>' +
|
|
6314
|
+
'<input id="wcProjectName" placeholder="'+t('wc_project_name')+'" value="'+wcEsc(wcState.projectName)+'" oninput="wcState.projectName=this.value" style="width:100%;padding:8px 10px;font-size:12px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);margin-bottom:8px;box-sizing:border-box">' +
|
|
6315
|
+
'<textarea id="wcDesc" placeholder="'+t('wc_desc')+'" rows="4" oninput="wcState.description=this.value" style="width:100%;padding:8px 10px;font-size:12px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);resize:vertical;box-sizing:border-box;line-height:1.5">'+wcEsc(wcState.description)+'</textarea>' +
|
|
6333
6316
|
'</div>' +
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
'<
|
|
6340
|
-
|
|
6317
|
+
'<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px">' +
|
|
6318
|
+
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">'+t('wc_blocks')+'</div>' +
|
|
6319
|
+
['auth','cookieBanner','securityMiddleware','emailVerification'].map(function(b){
|
|
6320
|
+
var labels = {auth:'Auth (register/login/JWT)',cookieBanner:'GDPR Cookie Banner',securityMiddleware:'Security Middleware',emailVerification:'Email Verification'};
|
|
6321
|
+
var icons = {auth:'🔒',cookieBanner:'🍪',securityMiddleware:'🛡',emailVerification:'✉'};
|
|
6322
|
+
return '<label style="display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;font-size:11px;color:var(--text)">' +
|
|
6323
|
+
'<input type="checkbox"'+(wcState.blocks[b]?' checked':'')+' onchange="wcState.blocks['+JSON.stringify(b)+']=this.checked" style="accent-color:var(--green3);width:14px;height:14px">' +
|
|
6324
|
+
'<span>'+icons[b]+'</span><span>'+labels[b]+'</span>' +
|
|
6325
|
+
'</label>';
|
|
6326
|
+
}).join('') +
|
|
6327
|
+
'</div>' +
|
|
6328
|
+
'<div id="wcAuthFieldsPanel" style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px;'+(wcState.blocks.auth?'':'display:none')+'">' +
|
|
6329
|
+
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">' +
|
|
6330
|
+
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px">'+t('wc_auth_fields')+'</div>' +
|
|
6331
|
+
'<button onclick="wcAddField()" style="font-size:10px;padding:3px 8px;background:var(--bg3);border:1px solid var(--border2);border-radius:5px;color:var(--green);cursor:pointer">'+t('wc_add_field')+'</button>' +
|
|
6341
6332
|
'</div>' +
|
|
6342
|
-
|
|
6333
|
+
'<div id="wcFieldsList">'+authFieldsHtml+'</div>' +
|
|
6334
|
+
'<div style="font-size:9px;color:var(--dim);margin-top:4px">'+t('wc_required_hint')+'</div>' +
|
|
6343
6335
|
'</div>' +
|
|
6344
|
-
|
|
6336
|
+
'<button id="wcRunBtn" onclick="wcGenerate()" style="width:100%;padding:12px;background:var(--green3);border:none;border-radius:8px;color:var(--bg);font-size:13px;font-weight:700;cursor:pointer;letter-spacing:.2px"'+(wcState.running?' disabled':'')+'>'+
|
|
6337
|
+
(wcState.running ? '⏳ '+t('wc_generating')+'...' : '▶ '+t('wc_generate')) +
|
|
6338
|
+
'</button>' +
|
|
6339
|
+
(wcState.generatedFiles.length > 0 ?
|
|
6340
|
+
'<button onclick="wcDownloadZip()" style="width:100%;padding:10px;background:var(--bg3);border:1px solid var(--border2);border-radius:8px;color:var(--text);font-size:12px;font-weight:600;cursor:pointer">⇩ '+t('wc_download')+'</button>' +
|
|
6341
|
+
'<button onclick="wcStartSandbox()" id="wcSandboxBtn" style="width:100%;padding:10px;background:var(--bg3);border:1px solid var(--green3);border-radius:8px;color:var(--green);font-size:12px;font-weight:600;cursor:pointer">▶ '+t('wc_sandbox_start')+'</button>'
|
|
6342
|
+
: '') +
|
|
6343
|
+
'</div>' +
|
|
6344
|
+
'<div style="flex:1;min-width:0;background:var(--bg2);border:1px solid var(--border);border-radius:10px;display:flex;flex-direction:column;height:calc(100vh - 120px);overflow:hidden">' +
|
|
6345
|
+
'<div style="display:flex;border-bottom:1px solid var(--border);flex-shrink:0">' +
|
|
6346
|
+
'<button onclick="wcTabFiles()" style="padding:8px 16px;background:'+(wcRightTab==='preview'?'transparent':'var(--bg3)')+';border:none;border-right:1px solid var(--border);color:'+(wcRightTab==='preview'?'var(--dim)':'var(--text)')+';font-size:11px;font-weight:600;cursor:pointer">📄 File</button>' +
|
|
6347
|
+
'<button onclick="wcTabPreview()" style="padding:8px 16px;background:'+(wcRightTab==='preview'?'var(--bg3)':'transparent')+';border:none;color:'+(wcRightTab==='preview'?'var(--text)':'var(--dim)')+';font-size:11px;font-weight:600;cursor:pointer">🌐 Sandbox</button>' +
|
|
6348
|
+
'</div>' +
|
|
6349
|
+
(wcRightTab === 'preview' ? wcSandboxPanelHtml() : (fileTabsHtml + codeHtml)) +
|
|
6345
6350
|
'</div>' +
|
|
6346
6351
|
'</div>';
|
|
6352
|
+
|
|
6353
|
+
el.innerHTML =
|
|
6354
|
+
'<div style="display:flex;flex-direction:column;height:100%;min-height:0;padding:0 4px">' +
|
|
6355
|
+
headerHtml +
|
|
6356
|
+
(wcMainTab === 'projects' ? wcProjectsPanelHtml() : editorHtml) +
|
|
6357
|
+
'</div>';
|
|
6347
6358
|
}
|
|
6348
6359
|
|
|
6349
6360
|
function wcPickExample(i) {
|
|
@@ -6364,6 +6375,69 @@ function wcPickExample(i) {
|
|
|
6364
6375
|
function wcTabFiles() { wcRightTab = 'files'; renderWebCraft(document.getElementById('content')); }
|
|
6365
6376
|
function wcTabPreview() { wcRightTab = 'preview'; renderWebCraft(document.getElementById('content')); }
|
|
6366
6377
|
function wcOpenSandbox() { if (wcState.sandbox.port) window.open('http://127.0.0.1:' + wcState.sandbox.port, '_blank'); }
|
|
6378
|
+
|
|
6379
|
+
function wcMainTabNew() { wcMainTab = 'new'; renderWebCraft(document.getElementById('content')); }
|
|
6380
|
+
function wcMainTabProjects() {
|
|
6381
|
+
wcMainTab = 'projects';
|
|
6382
|
+
renderWebCraft(document.getElementById('content'));
|
|
6383
|
+
// Load projects list from server
|
|
6384
|
+
fetch(API + '/api/studio/webcraft/projects').then(function(r){ return r.json(); }).then(function(d){
|
|
6385
|
+
wcProjectsList = d.projects || [];
|
|
6386
|
+
renderWebCraft(document.getElementById('content'));
|
|
6387
|
+
}).catch(function(){});
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6390
|
+
function wcProjectsPanelHtml() {
|
|
6391
|
+
if (!wcProjectsList.length) {
|
|
6392
|
+
return '<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1;gap:10px;padding:40px;color:var(--dim)">' +
|
|
6393
|
+
'<div style="font-size:32px">📁</div>' +
|
|
6394
|
+
'<div style="font-size:13px">'+t('wc_no_projects')+'</div>' +
|
|
6395
|
+
'<div style="font-size:11px">'+t('wc_no_projects_hint')+'</div>' +
|
|
6396
|
+
'</div>';
|
|
6397
|
+
}
|
|
6398
|
+
return '<div style="flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:10px;padding:4px 0">' +
|
|
6399
|
+
wcProjectsList.map(function(p, pi){
|
|
6400
|
+
var date = p.createdAt ? new Date(p.createdAt).toLocaleString() : '';
|
|
6401
|
+
return '<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px 16px;display:flex;align-items:center;gap:12px">' +
|
|
6402
|
+
'<div style="flex:1;min-width:0">' +
|
|
6403
|
+
'<div style="font-size:13px;font-weight:700;color:var(--text);margin-bottom:2px">'+wcEsc(p.name)+'</div>' +
|
|
6404
|
+
'<div style="font-size:10px;color:var(--dim);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+wcEsc(p.description||'')+'</div>' +
|
|
6405
|
+
'<div style="display:flex;gap:10px;font-size:10px;color:var(--dim)">' +
|
|
6406
|
+
'<span>📄 '+p.fileCount+' file</span>' +
|
|
6407
|
+
'<span>📅 '+date+'</span>' +
|
|
6408
|
+
'<span style="font-family:var(--mono);font-size:9px">'+wcEsc(p.dir||'')+'</span>' +
|
|
6409
|
+
'</div>' +
|
|
6410
|
+
'</div>' +
|
|
6411
|
+
'<button onclick="wcLoadProject('+pi+')" style="padding:6px 14px;background:var(--green3);border:none;border-radius:6px;color:var(--bg);font-size:11px;font-weight:700;cursor:pointer;flex-shrink:0">↗ Apri</button>' +
|
|
6412
|
+
'<button onclick="wcDeleteProject('+pi+')" style="padding:6px 10px;background:var(--bg3);border:1px solid var(--border2);border-radius:6px;color:var(--red);font-size:11px;cursor:pointer;flex-shrink:0">🗑</button>' +
|
|
6413
|
+
'</div>';
|
|
6414
|
+
}).join('') +
|
|
6415
|
+
'</div>';
|
|
6416
|
+
}
|
|
6417
|
+
|
|
6418
|
+
async function wcLoadProject(pi) {
|
|
6419
|
+
var p = wcProjectsList[pi];
|
|
6420
|
+
if (!p) return;
|
|
6421
|
+
var r = await fetch(API + '/api/studio/webcraft/projects/load/' + encodeURIComponent(p.name));
|
|
6422
|
+
if (!r.ok) return;
|
|
6423
|
+
var d = await r.json();
|
|
6424
|
+
wcState.projectName = d.projectName || p.name;
|
|
6425
|
+
wcState.description = d.description || '';
|
|
6426
|
+
wcState.generatedFiles = d.files || [];
|
|
6427
|
+
wcState.activeFile = 0;
|
|
6428
|
+
wcMainTab = 'new';
|
|
6429
|
+
wcRightTab = 'files';
|
|
6430
|
+
renderWebCraft(document.getElementById('content'));
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6433
|
+
async function wcDeleteProject(pi) {
|
|
6434
|
+
var p = wcProjectsList[pi];
|
|
6435
|
+
if (!p) return;
|
|
6436
|
+
if (!confirm('Eliminare: ' + p.name + ' - ' + p.dir + ' ?')) return;
|
|
6437
|
+
await fetch(API + '/api/studio/webcraft/projects/' + encodeURIComponent(p.name), {method:'DELETE'});
|
|
6438
|
+
wcProjectsList.splice(pi, 1);
|
|
6439
|
+
renderWebCraft(document.getElementById('content'));
|
|
6440
|
+
}
|
|
6367
6441
|
function wcUpdateField(i, val) { wcState.authFields[i].label = val; }
|
|
6368
6442
|
function wcUpdateFieldType(i, t) { wcState.authFields[i].type = t; }
|
|
6369
6443
|
function wcToggleRequired(i, v) { wcState.authFields[i].required = v; }
|
|
@@ -6465,6 +6539,16 @@ async function wcGenerate() {
|
|
|
6465
6539
|
}
|
|
6466
6540
|
|
|
6467
6541
|
wcState.running = false;
|
|
6542
|
+
|
|
6543
|
+
// Auto-save project to ~/.nha/webcraft/<projectName>/
|
|
6544
|
+
try {
|
|
6545
|
+
await fetch(API + '/api/studio/webcraft/projects/save', {
|
|
6546
|
+
method: 'POST',
|
|
6547
|
+
headers: {'Content-Type':'application/json'},
|
|
6548
|
+
body: JSON.stringify({ projectName: wcState.projectName, description: wcState.description, files: wcState.generatedFiles })
|
|
6549
|
+
});
|
|
6550
|
+
} catch(_) {}
|
|
6551
|
+
|
|
6468
6552
|
renderWebCraft(document.getElementById('content'));
|
|
6469
6553
|
}
|
|
6470
6554
|
|