nothumanallowed 13.5.153 → 13.5.155

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": "13.5.153",
3
+ "version": "13.5.155",
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/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.153';
8
+ export const VERSION = '13.5.155';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -7384,38 +7384,32 @@ function wcGenElapsed() {
7384
7384
  function wcStartGenTimer() {
7385
7385
  if (_wcTimerInterval) clearInterval(_wcTimerInterval);
7386
7386
  _wcTimerInterval = setInterval(function() {
7387
- if (!wcState.running) { clearInterval(_wcTimerInterval); _wcTimerInterval = null; return; }
7388
- if (_wcOverlayMinimized) {
7389
- var pill = document.getElementById('wcPillLabel');
7390
- if (pill) pill.textContent = _wcGenOverlayState.name || 'Generando...';
7391
- var ov = document.getElementById('wcGenOverlay');
7392
- if (ov) { var spans = ov.querySelectorAll('span'); if (spans[1]) spans[1].textContent = wcGenElapsed(); }
7393
- } else {
7394
- wcUpdateGenOverlay(_wcGenOverlayState.fi, _wcGenOverlayState.total, _wcGenOverlayState.name);
7395
- }
7387
+ if (!wcState.running && !wcState.repairing) { clearInterval(_wcTimerInterval); _wcTimerInterval = null; return; }
7388
+ // Always update time in-place — never re-render the whole component
7389
+ wcUpdateGenOverlay(_wcGenOverlayState.fi, _wcGenOverlayState.total, _wcGenOverlayState.name);
7390
+ var repairTime = document.getElementById('wcRepairTime');
7391
+ if (repairTime) repairTime.textContent = wcGenElapsed();
7396
7392
  }, 1000);
7397
7393
  }
7398
7394
 
7399
7395
  function wcUpdateGenOverlay(fi2, total, name) {
7400
7396
  _wcGenOverlayState = { fi: fi2, total: total, name: name };
7401
- if (_wcOverlayMinimized) return;
7402
- var ov = document.getElementById('wcGenOverlay');
7403
- if (!ov) return;
7404
- var pct = Math.round((fi2 / total) * 100);
7405
- var tokLabel = (_wcTokIn + _wcTokOut) > 0
7406
- ? '<div style="font-size:10px;color:var(--dim);margin-top:4px;font-family:var(--mono)">&#8679;' + _wcTokIn.toLocaleString() + ' &#8681;' + _wcTokOut.toLocaleString() + ' tok</div>'
7407
- : '';
7408
- ov.innerHTML =
7409
- '<div style="font-size:38px;animation:wcRobotBob 1s ease-in-out infinite">&#129302;</div>' +
7410
- '<div style="font-size:13px;font-weight:700;color:var(--green);margin-top:12px">' + (name.indexOf('Retry:') === 0 ? 'Retry in corso...' : name.indexOf('Fix:') === 0 ? 'Correzione in corso...' : 'Generazione in corso...') + '</div>' +
7411
- '<div style="font-size:10px;color:var(--dim);margin-top:2px">Clicca per navigare i file</div>' +
7412
- '<div style="font-size:11px;color:'+(name.indexOf('Retry:')===0?'#fb923c':name.indexOf('Fix:')===0?'#facc15':'var(--dim)')+';font-family:var(--mono);max-width:300px;text-align:center;word-break:break-all;margin-top:8px">'+wcEsc(name)+'</div>' +
7413
- '<div style="width:220px;height:4px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden;margin-top:12px">' +
7414
- '<div style="height:100%;width:'+pct+'%;background:var(--green);border-radius:2px;transition:width .4s ease;animation:wcBarPulse 1.5s ease-in-out infinite"></div>' +
7415
- '</div>' +
7416
- '<div style="font-size:10px;color:var(--dim);margin-top:6px">'+fi2+' / '+total+' file &nbsp;&#183;&nbsp; '+wcGenElapsed()+'</div>' +
7417
- tokLabel +
7418
- '<div style="display:flex;gap:4px;margin-top:10px">'+[0,1,2,3,4].map(function(_,idx){ return '<div style="width:6px;height:6px;border-radius:50%;background:var(--green);animation:wcDot 1.1s ease-in-out infinite '+(idx*0.14)+'s"></div>'; }).join('')+'</div>';
7397
+ // Update only the specific DOM nodes — no innerHTML rewrite, no flicker
7398
+ var pct = total > 0 ? Math.round((fi2 / total) * 100) : 0;
7399
+ var counterEl = document.getElementById('wcGenCounter');
7400
+ var barEl = document.getElementById('wcGenBar');
7401
+ var nameEl = document.getElementById('wcGenFileName');
7402
+ var timeEl = document.getElementById('wcGenTime');
7403
+ var pillLabel = document.getElementById('wcPillLabel');
7404
+ var pillCount = document.getElementById('wcPillCount');
7405
+ var pillTime = document.getElementById('wcPillTime');
7406
+ if (counterEl) counterEl.textContent = fi2 + ' / ' + total;
7407
+ if (barEl) barEl.style.width = pct + '%';
7408
+ if (nameEl) nameEl.textContent = name || '';
7409
+ if (timeEl) timeEl.textContent = wcGenElapsed();
7410
+ if (pillLabel) pillLabel.textContent = name ? name.split(',')[0].trim() : 'Generando...';
7411
+ if (pillCount) pillCount.textContent = fi2 + '/' + total;
7412
+ if (pillTime) pillTime.textContent = wcGenElapsed();
7419
7413
  }
7420
7414
 
7421
7415
  // Skills state
@@ -7479,8 +7473,8 @@ function renderWebCraft(el) {
7479
7473
  '</div>' +
7480
7474
  (_activeFile._error ? '<div style="padding:8px 14px;background:rgba(239,68,68,0.12);border-bottom:1px solid rgba(239,68,68,0.3);font-size:11px;color:#f87171;display:flex;align-items:center;gap:6px">&#9888; Generazione fallita — chiedi al modello di rigenerare questo file</div>' :
7481
7475
  _activeFile._syntaxError ? '<div style="padding:8px 14px;background:rgba(234,179,8,0.1);border-bottom:1px solid rgba(234,179,8,0.3);font-size:11px;color:#facc15;display:flex;align-items:center;gap:6px">&#9888; Syntax error: '+wcEsc(_activeFile._syntaxError)+'</div>' : '') +
7482
- (_activeFile._pending ? '<div style="display:flex;align-items:center;justify-content:center;height:120px;color:var(--dim);font-size:12px;gap:8px">&#8987; In generazione...</div>' :
7483
- '<pre style="margin:0;padding:14px 16px;font-size:11px;line-height:1.6;color:'+(_activeFile._error?'#f87171':_activeFile._syntaxError?'#fde68a':'var(--text)')+';font-family:var(--mono);white-space:pre-wrap;word-break:break-all">'+wcEsc(_activeFile.content)+'</pre>') +
7476
+ (_activeFile._pending ? '<div id="wcLivePending" style="display:flex;align-items:center;justify-content:center;height:120px;color:var(--dim);font-size:12px;gap:8px">&#8987; In generazione...</div>' :
7477
+ '<pre id="wcLiveCode" style="margin:0;padding:14px 16px;font-size:11px;line-height:1.6;color:'+(_activeFile._error?'#f87171':_activeFile._syntaxError?'#fde68a':'var(--text)')+';font-family:var(--mono);white-space:pre-wrap;word-break:break-all">'+wcEsc(_activeFile.content)+'</pre>') +
7484
7478
  '</div>' +
7485
7479
  fileSidebarHtml +
7486
7480
  '</div>'
@@ -7591,36 +7585,47 @@ function renderWebCraft(el) {
7591
7585
  '<div data-wc-files style="position:relative;flex:1;min-width:0;background:var(--bg2);border:1px solid var(--border);border-radius:10px;display:flex;flex-direction:column;height:100%;overflow:hidden">' +
7592
7586
  (wcState.repairing ?
7593
7587
  (_wcOverlayMinimized
7594
- ? '<div id="wcRepairOverlay" onclick="wcOverlayRestore()" style="position:absolute;bottom:12px;right:12px;z-index:50;background:rgba(0,0,0,0.85);border:1px solid rgba(234,179,8,0.6);border-radius:20px;padding:5px 12px;display:flex;align-items:center;gap:7px;cursor:pointer;animation:wcBubbleIn .2s ease;backdrop-filter:blur(4px)">'
7595
- +'<span style="font-size:16px;animation:wcRobotBob .9s ease-in-out infinite">&#128295;</span>'
7596
- +'<span style="font-size:10px;color:#facc15;font-weight:700;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+wcEsc(wcState.repairCurrent || 'Correzione...')+'</span>'
7597
- +'<span style="font-size:9px;color:var(--dim)">'+wcState.repairDone+'/'+wcState.repairTotal+'</span>'
7588
+ // Minimized repair: pill bottom-right
7589
+ ? '<div id="wcRepairOverlay" onclick="wcOverlayRestore()" style="position:absolute;bottom:12px;right:12px;z-index:50;background:rgba(0,0,0,0.85);border:1px solid rgba(234,179,8,0.6);border-radius:20px;padding:5px 12px;display:flex;align-items:center;gap:7px;cursor:pointer;backdrop-filter:blur(4px)">'
7590
+ +'<span style="font-size:14px;animation:wcRobotBob .9s ease-in-out infinite">&#128295;</span>'
7591
+ +'<span style="font-size:10px;color:#facc15;font-weight:700;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" id="wcRepairFile">'+wcEsc(wcState.repairCurrent || 'Correzione...')+'</span>'
7592
+ +'<span style="font-size:9px;color:var(--dim)" id="wcRepairCounter">'+wcState.repairDone+'/'+wcState.repairTotal+'</span>'
7598
7593
  +'<span style="display:flex;gap:3px">'+[0,1,2].map(function(_,idx){ return '<span style="width:4px;height:4px;border-radius:50%;background:#facc15;animation:wcDot 1.1s ease-in-out infinite '+(idx*0.18)+'s"></span>'; }).join('')+'</span>'
7599
7594
  +'</div>'
7600
- : '<div id="wcRepairOverlay" onclick="wcOverlayMinimize()" title="Clicca per navigare i file" style="position:absolute;inset:0;background:rgba(0,0,0,0.75);backdrop-filter:blur(4px);border-radius:10px;z-index:50;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;animation:wcBubbleIn .3s ease">'
7601
- +'<div style="font-size:38px;animation:wcRobotBob 1s ease-in-out infinite">&#128295;</div>'
7602
- +'<div style="font-size:13px;font-weight:700;color:#facc15;margin-top:12px">Correzione automatica in corso...</div>'
7603
- +'<div style="font-size:10px;color:var(--dim);margin-top:4px">Clicca per navigare i file</div>'
7604
- +'<div id="wcRepairCounter" style="font-size:11px;color:var(--dim);margin-top:6px">'+wcState.repairDone+' / '+wcState.repairTotal+' file</div>'
7605
- +'<div id="wcRepairFile" style="font-size:10px;color:#fde68a;font-family:var(--mono);margin-top:4px;max-width:280px;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+wcEsc(wcState.repairCurrent)+'</div>'
7606
- +'<div style="display:flex;gap:4px;margin-top:16px">'+[0,1,2,3,4].map(function(_,idx){ return '<div style="width:6px;height:6px;border-radius:50%;background:#facc15;animation:wcDot 1.1s ease-in-out infinite '+(idx*0.14)+'s"></div>'; }).join('')+'</div>'
7595
+ // Full repair: sticky header bar code stays visible below
7596
+ : '<div id="wcRepairOverlay" style="position:absolute;top:0;left:0;right:0;z-index:50;background:rgba(20,16,0,0.92);backdrop-filter:blur(6px);border-bottom:1px solid rgba(234,179,8,0.5);padding:8px 16px;display:flex;align-items:center;gap:10px">'
7597
+ +'<span style="font-size:16px;animation:wcRobotBob .9s ease-in-out infinite;flex-shrink:0">&#128295;</span>'
7598
+ +'<span style="font-size:11px;font-weight:700;color:#facc15;flex-shrink:0">Correzione automatica</span>'
7599
+ +'<span style="font-size:10px;color:#fde68a;font-family:var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1" id="wcRepairFile">'+wcEsc(wcState.repairCurrent || '')+'</span>'
7600
+ +'<span style="font-size:10px;color:var(--dim);flex-shrink:0" id="wcRepairCounter">'+wcState.repairDone+' / '+wcState.repairTotal+' file</span>'
7601
+ +'<span onclick="wcOverlayMinimize()" style="font-size:10px;color:var(--dim);cursor:pointer;flex-shrink:0;padding:2px 6px;border:1px solid var(--border2);border-radius:4px" title="Minimizza">&#8211;</span>'
7602
+ +'<span style="display:flex;gap:3px">'+[0,1,2].map(function(_,idx){ return '<span style="width:4px;height:4px;border-radius:50%;background:#facc15;animation:wcDot 1.1s ease-in-out infinite '+(idx*0.18)+'s"></span>'; }).join('')+'</span>'
7607
7603
  +'</div>'
7608
7604
  )
7609
7605
  : wcState.running ? (
7610
7606
  _wcOverlayMinimized
7611
- // Minimized: small pill in bottom-right corner
7612
- ? '<div id="wcGenOverlay" onclick="wcOverlayRestore()" style="position:absolute;bottom:12px;right:12px;z-index:50;background:rgba(0,0,0,0.85);border:1px solid var(--green3);border-radius:20px;padding:5px 12px;display:flex;align-items:center;gap:7px;cursor:pointer;animation:wcBubbleIn .2s ease;backdrop-filter:blur(4px)">'
7613
- +'<span style="font-size:16px;animation:wcRobotBob .9s ease-in-out infinite">&#129302;</span>'
7614
- +'<span id="wcPillLabel" style="font-size:10px;color:var(--green);font-weight:700;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+wcEsc(_wcGenOverlayState.name || 'Generando...')+'</span>'
7615
- +'<span style="font-size:9px;color:var(--dim)">'+wcGenElapsed()+'</span>'
7607
+ // Minimized gen: pill bottom-right
7608
+ ? '<div id="wcGenOverlay" onclick="wcOverlayRestore()" style="position:absolute;bottom:12px;right:12px;z-index:50;background:rgba(0,0,0,0.85);border:1px solid var(--green3);border-radius:20px;padding:5px 12px;display:flex;align-items:center;gap:7px;cursor:pointer;backdrop-filter:blur(4px)">'
7609
+ +'<span style="font-size:14px;animation:wcRobotBob .9s ease-in-out infinite">&#129302;</span>'
7610
+ +'<span id="wcPillLabel" style="font-size:10px;color:var(--green);font-weight:700;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+wcEsc((_wcGenOverlayState.name||'').split(',')[0].trim() || 'Generando...')+'</span>'
7611
+ +'<span id="wcPillCount" style="font-size:9px;color:var(--dim)">'+_wcGenOverlayState.fi+'/'+_wcGenOverlayState.total+'</span>'
7612
+ +'<span id="wcPillTime" style="font-size:9px;color:var(--dim)">'+wcGenElapsed()+'</span>'
7616
7613
  +'<span style="display:flex;gap:3px">'+[0,1,2].map(function(_,idx){ return '<span style="width:4px;height:4px;border-radius:50%;background:var(--green);animation:wcDot 1.1s ease-in-out infinite '+(idx*0.18)+'s"></span>'; }).join('')+'</span>'
7617
7614
  +'</div>'
7618
- // Full overlay
7619
- : '<div id="wcGenOverlay" onclick="wcOverlayMinimize()" title="Clicca per nascondere e navigare i file" style="position:absolute;inset:0;background:rgba(0,0,0,0.75);backdrop-filter:blur(4px);border-radius:10px;z-index:50;display:flex;flex-direction:column;align-items:center;justify-content:center;animation:wcBubbleIn .3s ease;cursor:pointer">'
7620
- +'<div style="font-size:38px;animation:wcRobotBob 1s ease-in-out infinite">&#129302;</div>'
7621
- +'<div style="font-size:13px;font-weight:700;color:var(--green);margin-top:12px">Generazione in corso...</div>'
7622
- +'<div style="font-size:10px;color:var(--dim);margin-top:4px">Clicca per navigare i file</div>'
7623
- +'<div style="display:flex;gap:4px;margin-top:16px">'+[0,1,2,3,4].map(function(_,idx){ return '<div style="width:6px;height:6px;border-radius:50%;background:var(--green);animation:wcDot 1.1s ease-in-out infinite '+(idx*0.14)+'s"></div>'; }).join('')+'</div>'
7615
+ // Full gen: sticky header bar — code streams visibly below
7616
+ : '<div id="wcGenOverlay" style="position:absolute;top:0;left:0;right:0;z-index:50;background:rgba(0,14,0,0.90);backdrop-filter:blur(6px);border-bottom:1px solid var(--green3);padding:8px 16px;display:flex;flex-direction:column;gap:4px">'
7617
+ +'<div style="display:flex;align-items:center;gap:10px">'
7618
+ +'<span style="font-size:16px;animation:wcRobotBob .9s ease-in-out infinite;flex-shrink:0">&#129302;</span>'
7619
+ +'<span style="font-size:11px;font-weight:700;color:var(--green);flex-shrink:0">Generazione in corso</span>'
7620
+ +'<span id="wcGenFileName" style="font-size:10px;color:var(--dim);font-family:var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1">'+wcEsc((_wcGenOverlayState.name||'').split(',')[0].trim())+'</span>'
7621
+ +'<span id="wcGenCounter" style="font-size:10px;color:var(--dim);flex-shrink:0">'+_wcGenOverlayState.fi+' / '+_wcGenOverlayState.total+'</span>'
7622
+ +'<span id="wcGenTime" style="font-size:10px;color:var(--dim);flex-shrink:0">'+wcGenElapsed()+'</span>'
7623
+ +'<span onclick="wcOverlayMinimize()" style="font-size:10px;color:var(--dim);cursor:pointer;flex-shrink:0;padding:2px 6px;border:1px solid var(--border2);border-radius:4px" title="Minimizza">&#8211;</span>'
7624
+ +'<span style="display:flex;gap:3px">'+[0,1,2].map(function(_,idx){ return '<span style="width:4px;height:4px;border-radius:50%;background:var(--green);animation:wcDot 1.1s ease-in-out infinite '+(idx*0.18)+'s"></span>'; }).join('')+'</span>'
7625
+ +'</div>'
7626
+ +'<div style="height:2px;background:rgba(255,255,255,0.07);border-radius:1px;overflow:hidden">'
7627
+ +'<div id="wcGenBar" style="height:100%;width:'+Math.round((_wcGenOverlayState.fi/_wcGenOverlayState.total||0)*100)+'%;background:var(--green);border-radius:1px;transition:width .3s ease"></div>'
7628
+ +'</div>'
7624
7629
  +'</div>'
7625
7630
  ) : '') +
7626
7631
  '<div style="display:flex;border-bottom:1px solid var(--border);flex-shrink:0">' +
@@ -8116,12 +8121,17 @@ function wcStopAll() {
8116
8121
  }
8117
8122
 
8118
8123
  function wcOverlayMinimize() {
8124
+ if (_wcOverlayTimer) clearTimeout(_wcOverlayTimer);
8119
8125
  _wcOverlayMinimized = true;
8120
8126
  renderWebCraft(document.getElementById('content'));
8121
- if (_wcOverlayTimer) clearTimeout(_wcOverlayTimer);
8127
+ // Auto-restore after 30s of inactivity so user doesn't miss progress
8122
8128
  _wcOverlayTimer = setTimeout(function() {
8123
- if (wcState.running || wcState.repairing) { _wcOverlayMinimized = false; renderWebCraft(document.getElementById('content')); }
8124
- }, 10000);
8129
+ if (wcState.running || wcState.repairing) {
8130
+ _wcOverlayMinimized = false;
8131
+ renderWebCraft(document.getElementById('content'));
8132
+ }
8133
+ _wcOverlayTimer = null;
8134
+ }, 30000);
8125
8135
  }
8126
8136
 
8127
8137
  function wcOverlayRestore() {
@@ -8834,7 +8844,7 @@ async function wcGenerate() {
8834
8844
  }
8835
8845
 
8836
8846
  var _nl = String.fromCharCode(10);
8837
- var sysPreamble = 'You are an expert full-stack engineer generating production-quality code.' + _nl + _nl + 'SECURITY RULES (non-negotiable):' + _nl + SECURITY_RULES + _nl + _nl + 'Project: ' + projName + _nl + 'Description: ' + desc + _nl + 'Enabled blocks: ' + blocksEnabled + _nl + _nl + 'Generate ONLY the file content requested. No explanations, no markdown code fences, no comments like "here is the file". Output raw file content only.';
8847
+ var sysPreamble = 'You are an expert full-stack engineer generating production-quality code.' + _nl + _nl + 'SECURITY RULES (non-negotiable):' + _nl + SECURITY_RULES + _nl + _nl + 'JSON FILES RULES (non-negotiable):' + _nl + '- NEVER add spaces inside JSON keys or string values that are identifiers (package names, field names).' + _nl + '- package.json "name" must be lowercase with no spaces. All dependency names must match exactly the npm package name (no spaces, no leading/trailing spaces).' + _nl + '- NEVER duplicate dependency entries. Each package name appears exactly once.' + _nl + '- "devDependencies" key has NO trailing space. All JSON keys are exact.' + _nl + _nl + 'Project: ' + projName + _nl + 'Description: ' + desc + _nl + 'Enabled blocks: ' + blocksEnabled + _nl + _nl + 'Generate ONLY the file content requested. No explanations, no markdown code fences, no comments like "here is the file". Output raw file content only.';
8838
8848
  _wcLastFilePlan = filePlan;
8839
8849
  _wcLastSysPreamble = sysPreamble;
8840
8850
 
@@ -8878,7 +8888,27 @@ async function wcGenerate() {
8878
8888
  return part1 + _nl2 + _nl2 + part2;
8879
8889
  }
8880
8890
  var content = await wcCallLLM(sysPreamble, fp.prompt + _nl2 + _nl2 + 'File to generate: ' + fp.name, signal, fp.lang, undefined, onLiveUpdate);
8881
- return wcStripFences(content);
8891
+ content = wcStripFences(content);
8892
+ // Post-process package.json: fix common LLM mistakes (spaces in keys/names, duplicates)
8893
+ if (fp.name === 'package.json' && fp.lang === 'json') {
8894
+ try {
8895
+ var pkg = JSON.parse(content);
8896
+ // Fix name field
8897
+ if (typeof pkg.name === 'string') pkg.name = pkg.name.trim().toLowerCase().replace(/\s+/g, '-');
8898
+ // Fix all dependency sections: strip spaces from keys, deduplicate
8899
+ ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].forEach(function(section) {
8900
+ if (!pkg[section] || typeof pkg[section] !== 'object') return;
8901
+ var clean = {};
8902
+ Object.keys(pkg[section]).forEach(function(k) {
8903
+ var cleanKey = k.trim();
8904
+ if (cleanKey && !clean[cleanKey]) clean[cleanKey] = pkg[section][k];
8905
+ });
8906
+ pkg[section] = clean;
8907
+ });
8908
+ content = JSON.stringify(pkg, null, 2);
8909
+ } catch(_) { /* if JSON is already broken, leave as-is for repair to handle */ }
8910
+ }
8911
+ return content;
8882
8912
  }
8883
8913
 
8884
8914
  wcStartGenTimer();
@@ -8890,35 +8920,67 @@ async function wcGenerate() {
8890
8920
  wcState.activeFile = 0;
8891
8921
  renderWebCraft(document.getElementById('content'));
8892
8922
 
8893
- // Live render throttle update UI at most every 120ms per file during streaming
8894
- var _wcLiveRenderTimers = {};
8923
+ // Live update: write token directly into DOM zero re-render, zero flicker
8924
+ var _wcLiveDomTimers = {};
8895
8925
  function wcLiveUpdateFile(fpName, fpLang, partialContent) {
8926
+ // Always update state
8927
+ var fileIdx = -1;
8896
8928
  for (var li = 0; li < wcState.generatedFiles.length; li++) {
8897
8929
  if (wcState.generatedFiles[li].name === fpName) {
8898
8930
  wcState.generatedFiles[li].content = partialContent;
8899
8931
  wcState.generatedFiles[li]._pending = false;
8932
+ fileIdx = li;
8900
8933
  break;
8901
8934
  }
8902
8935
  }
8903
- if (_wcLiveRenderTimers[fpName]) return; // throttle
8904
- _wcLiveRenderTimers[fpName] = setTimeout(function() {
8905
- delete _wcLiveRenderTimers[fpName];
8906
- renderWebCraft(document.getElementById('content'));
8907
- }, 120);
8936
+ // If this file is the active one, update the <pre> directly (throttled 80ms)
8937
+ if (fileIdx !== wcState.activeFile) return;
8938
+ if (_wcLiveDomTimers[fpName]) return;
8939
+ _wcLiveDomTimers[fpName] = setTimeout(function() {
8940
+ delete _wcLiveDomTimers[fpName];
8941
+ // Replace pending placeholder with live <pre> if needed
8942
+ var pending = document.getElementById('wcLivePending');
8943
+ var pre = document.getElementById('wcLiveCode');
8944
+ var wrap = document.getElementById('wcCodeWrap');
8945
+ if (pending && wrap) {
8946
+ // First token: swap placeholder for <pre>
8947
+ var newPre = document.createElement('pre');
8948
+ newPre.id = 'wcLiveCode';
8949
+ newPre.style.cssText = 'margin:0;padding:14px 16px;font-size:11px;line-height:1.6;color:var(--text);font-family:var(--mono);white-space:pre-wrap;word-break:break-all';
8950
+ newPre.textContent = partialContent;
8951
+ pending.parentNode.replaceChild(newPre, pending);
8952
+ return;
8953
+ }
8954
+ if (pre) {
8955
+ pre.textContent = partialContent;
8956
+ // Auto-scroll to bottom so user sees latest tokens
8957
+ var codeWrap = document.getElementById('wcCodeWrap');
8958
+ if (codeWrap) codeWrap.scrollTop = codeWrap.scrollHeight;
8959
+ }
8960
+ }, 80);
8908
8961
  }
8909
8962
 
8910
8963
  // Generate in parallel batches of 4 — each call is independent/fresh to Liara
8964
+ // Counter increments 1 per file as it completes, not per batch
8911
8965
  var BATCH = 4;
8912
8966
  var doneCount = 0;
8967
+ wcUpdateGenOverlay(0, filePlan.length, '');
8913
8968
  for (var bi = 0; bi < filePlan.length; bi += BATCH) {
8914
8969
  if (_wcGenAbortCtrl && _wcGenAbortCtrl.signal.aborted) break;
8915
8970
  var batch = filePlan.slice(bi, bi + BATCH);
8916
- wcUpdateGenOverlay(doneCount, filePlan.length, batch.map(function(f){ return f.name; }).join(', '));
8917
- var results = await Promise.allSettled(batch.map(function(fp) {
8971
+ var results = await Promise.allSettled(batch.map(function(fp, bii) {
8972
+ // Point activeFile at the first file of this batch so tokens appear immediately
8973
+ if (bii === 0) {
8974
+ var firstIdx = wcState.generatedFiles.findIndex(function(f){ return f.name === fp.name; });
8975
+ if (firstIdx >= 0) wcState.activeFile = firstIdx;
8976
+ }
8977
+ // Show file name in overlay as soon as it starts
8978
+ wcUpdateGenOverlay(doneCount, filePlan.length, fp.name);
8918
8979
  var liveCallback = function(partial) { wcLiveUpdateFile(fp.name, fp.lang, partial); };
8919
8980
  return wcGenOneFile(fp, _wcGenAbortCtrl ? _wcGenAbortCtrl.signal : null, liveCallback).then(function(c){ return { fp: fp, content: c }; });
8920
8981
  }));
8921
- results.forEach(function(r) {
8982
+ results.forEach(function(r, ri) {
8983
+ var batchIdx = ri;
8922
8984
  if (r.status === 'fulfilled') {
8923
8985
  var fp = r.value.fp;
8924
8986
  for (var gi = 0; gi < wcState.generatedFiles.length; gi++) {
@@ -8928,20 +8990,20 @@ async function wcGenerate() {
8928
8990
  }
8929
8991
  }
8930
8992
  } else if (r.reason && r.reason.name !== 'AbortError') {
8931
- var fpName = batch[results.indexOf(r)] ? batch[results.indexOf(r)].name : '?';
8932
- // find by matching position
8933
- var batchIdx = results.indexOf(r);
8934
- if (batch[batchIdx]) fpName = batch[batchIdx].name;
8935
- for (var gi2 = 0; gi2 < wcState.generatedFiles.length; gi2++) {
8936
- if (wcState.generatedFiles[gi2].name === fpName) {
8937
- wcState.generatedFiles[gi2] = { name: fpName, content: '// Error generating this file: ' + (r.reason.message || 'unknown error'), lang: (batch[batchIdx] || {}).lang || '', _error: true };
8938
- break;
8993
+ var fpErr = batch[batchIdx];
8994
+ if (fpErr) {
8995
+ for (var gi2 = 0; gi2 < wcState.generatedFiles.length; gi2++) {
8996
+ if (wcState.generatedFiles[gi2].name === fpErr.name) {
8997
+ wcState.generatedFiles[gi2] = { name: fpErr.name, content: '// Error generating this file: ' + (r.reason.message || 'unknown error'), lang: fpErr.lang || '', _error: true };
8998
+ break;
8999
+ }
8939
9000
  }
8940
9001
  }
8941
9002
  }
8942
9003
  doneCount++;
9004
+ // Update counter immediately on each file completion — no full re-render
9005
+ wcUpdateGenOverlay(doneCount, filePlan.length, doneCount < filePlan.length ? (batch[Math.min(batchIdx+1, batch.length-1)] || batch[batchIdx] || {}).name || '' : '');
8943
9006
  });
8944
- renderWebCraft(document.getElementById('content'));
8945
9007
  }
8946
9008
 
8947
9009
  if (_wcTimerInterval) { clearInterval(_wcTimerInterval); _wcTimerInterval = null; }