nothumanallowed 13.3.2 → 13.4.7
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/README.md +23 -2
- package/package.json +1 -1
- package/src/commands/ui.mjs +9 -4
- package/src/constants.mjs +1 -1
- package/src/services/tool-executor.mjs +217 -0
- package/src/services/web-ui.mjs +900 -115
package/src/services/web-ui.mjs
CHANGED
|
@@ -48,8 +48,8 @@ function renderMd(raw) {
|
|
|
48
48
|
// Already inside pre block
|
|
49
49
|
if (l.indexOf('<pre class="md-code">') !== -1) { inPre = true; }
|
|
50
50
|
if (inPre) { out.push(l); if (l.indexOf('</pre>') !== -1) inPre = false; continue; }
|
|
51
|
-
// H1-
|
|
52
|
-
var hm = l.match(/^(#{1,
|
|
51
|
+
// H1-H4
|
|
52
|
+
var hm = l.match(/^(#{1,4}) (.+)/);
|
|
53
53
|
if (hm) {
|
|
54
54
|
if (inUl) { out.push('</ul>'); inUl = false; }
|
|
55
55
|
if (inOl) { out.push('</ol>'); inOl = false; }
|
|
@@ -529,6 +529,7 @@ var canvasIdx=-1;
|
|
|
529
529
|
var browserIdx=-1;
|
|
530
530
|
var canvasMode='canvas';
|
|
531
531
|
var browserViewIdx=-1; // -1=gallery, >=0=detail page view
|
|
532
|
+
var _canvasFrameLoadedHtml=null; // tracks what is currently loaded in the canvas iframe — avoids reload flicker
|
|
532
533
|
|
|
533
534
|
function getConvCanvasData(){
|
|
534
535
|
var id=activeConvId||'_default';
|
|
@@ -600,7 +601,11 @@ function renderCanvasPanel(){
|
|
|
600
601
|
var d=getConvCanvasData();
|
|
601
602
|
var list=canvasMode==='browser'?d.browsers:d.canvases;
|
|
602
603
|
var idx=canvasMode==='browser'?browserIdx:canvasIdx;
|
|
604
|
+
// In canvas mode: if list is empty but studioState.canvas exists, use it as a virtual item
|
|
603
605
|
var item=list[idx];
|
|
606
|
+
if(!item && canvasMode==='canvas' && studioState.canvas){
|
|
607
|
+
item={html:studioState.canvas,title:'Studio Report',ts:''};
|
|
608
|
+
}
|
|
604
609
|
// Header title
|
|
605
610
|
var t=document.getElementById('canvasTitle');
|
|
606
611
|
if(t){
|
|
@@ -609,7 +614,7 @@ function renderCanvasPanel(){
|
|
|
609
614
|
try{var u=new URL(bvTitle);bvTitle=u.hostname+(u.pathname!=='/'?u.pathname:'');}catch(e){}
|
|
610
615
|
t.textContent=bvTitle;
|
|
611
616
|
} else if(canvasMode==='browser'){t.textContent=d.browsers.length>0?d.browsers.length+' pages visited':'No pages visited';}
|
|
612
|
-
else if(!item){t.textContent='
|
|
617
|
+
else if(!item){t.textContent='Canvas';}
|
|
613
618
|
else{t.textContent=(item.title||'Canvas')+(d.canvases.length>1?' ('+(canvasIdx+1)+'/'+d.canvases.length+')':'');}
|
|
614
619
|
}
|
|
615
620
|
// Nav arrows - only for canvas mode (browser uses gallery grid)
|
|
@@ -621,11 +626,17 @@ function renderCanvasPanel(){
|
|
|
621
626
|
if(tabC)tabC.style.borderBottom=canvasMode==='canvas'?'2px solid var(--green)':'none';
|
|
622
627
|
if(tabB)tabB.style.borderBottom=canvasMode==='browser'?'2px solid var(--green)':'none';
|
|
623
628
|
// Render iframe content via srcdoc (no allow-same-origin needed)
|
|
629
|
+
// _canvasFrameLoadedHtml: skip assignment if content unchanged — prevents Safari black flash on tab switch
|
|
624
630
|
var f=document.getElementById('canvasFrame');if(!f)return;
|
|
631
|
+
function setFrameSrcdoc(html){
|
|
632
|
+
if(_canvasFrameLoadedHtml===html)return; // identical content — skip to avoid flicker
|
|
633
|
+
_canvasFrameLoadedHtml=html;
|
|
634
|
+
f.srcdoc=html;
|
|
635
|
+
}
|
|
625
636
|
if(canvasMode==='browser'){
|
|
626
637
|
var d=getConvCanvasData();
|
|
627
638
|
if(d.browsers.length===0){
|
|
628
|
-
|
|
639
|
+
setFrameSrcdoc('<html><body style="margin:0;background:#111;display:flex;align-items:center;justify-content:center;height:100vh;font-family:monospace;color:#555"><div style="text-align:center"><div style="font-size:48px;margin-bottom:12px">🌐</div><div>No pages visited yet</div><div style="font-size:11px;margin-top:8px;color:#333">in this conversation</div><div style="margin-top:16px;font-size:11px;color:#888">Ask me to search or open a page</div></div></body></html>');
|
|
629
640
|
} else if(browserViewIdx>=0&&d.browsers[browserViewIdx]){
|
|
630
641
|
// Detail view: URL bar + screenshot + prev/next + back button
|
|
631
642
|
var bv=d.browsers[browserViewIdx];
|
|
@@ -642,7 +653,7 @@ function renderCanvasPanel(){
|
|
|
642
653
|
detail+='</div>';
|
|
643
654
|
detail+='<div class="content">'+(imgSrc?'<img src="'+imgSrc+'" alt="screenshot"/>':'<div class="no-img">No screenshot available</div>')+'</div>';
|
|
644
655
|
detail+='</body></html>';
|
|
645
|
-
|
|
656
|
+
setFrameSrcdoc(detail);
|
|
646
657
|
} else {
|
|
647
658
|
// Gallery view
|
|
648
659
|
var apiBase=window.API||'';
|
|
@@ -653,12 +664,17 @@ function renderCanvasPanel(){
|
|
|
653
664
|
gallery+='<div class="card" onclick="window.parent.postMessage({type:\\x27selectBrowser\\x27,index:'+bi+'},\\x27*\\x27)">'+(imgSrc?'<img src="'+imgSrc+'" alt="'+b.url+'"/>':'<div style="height:120px;display:flex;align-items:center;justify-content:center;color:#555">No preview</div>')+'<div class="info"><div class="url">'+b.url+'</div><div class="time">'+(b.ts||'')+'</div></div></div>';
|
|
654
665
|
}
|
|
655
666
|
gallery+='</div></body></html>';
|
|
656
|
-
|
|
667
|
+
setFrameSrcdoc(gallery);
|
|
657
668
|
}
|
|
658
669
|
} else if(!item){
|
|
659
|
-
|
|
670
|
+
// Fallback: if studioState has a canvas (loaded but not yet in allCanvasData), show it directly
|
|
671
|
+
if(studioState.canvas){
|
|
672
|
+
setFrameSrcdoc(studioState.canvas);
|
|
673
|
+
} else {
|
|
674
|
+
setFrameSrcdoc('<html><body style="margin:0;background:#111;display:flex;align-items:center;justify-content:center;height:100vh;font-family:monospace;color:#555"><div style="text-align:center"><div style="font-size:48px;margin-bottom:12px">▣</div><div>No canvas content</div><div style="font-size:11px;margin-top:8px;color:#333">in this conversation</div></div></body></html>');
|
|
675
|
+
}
|
|
660
676
|
} else {
|
|
661
|
-
|
|
677
|
+
setFrameSrcdoc(item.html);
|
|
662
678
|
}
|
|
663
679
|
}
|
|
664
680
|
|
|
@@ -686,11 +702,19 @@ window.addEventListener('message',function(e){
|
|
|
686
702
|
function selectBrowserPage(i){
|
|
687
703
|
browserIdx=i;browserViewIdx=i;canvasMode='browser';renderCanvasPanel();
|
|
688
704
|
}
|
|
689
|
-
function canvasShowBrowser(){var d=getConvCanvasData();browserIdx=d.browsers.length-1;browserViewIdx=-1;canvasMode='browser';renderCanvasPanel();}
|
|
690
|
-
function canvasShowCanvas(){
|
|
705
|
+
function canvasShowBrowser(){_canvasFrameLoadedHtml=null;var d=getConvCanvasData();browserIdx=d.browsers.length-1;browserViewIdx=-1;canvasMode='browser';renderCanvasPanel();}
|
|
706
|
+
function canvasShowCanvas(){
|
|
707
|
+
// Reset iframe tracker so switching back from Browser always reloads Canvas content
|
|
708
|
+
_canvasFrameLoadedHtml=null;
|
|
709
|
+
canvasMode='canvas';
|
|
710
|
+
var d=getConvCanvasData();
|
|
711
|
+
canvasIdx=Math.max(0,d.canvases.length-1);
|
|
712
|
+
renderCanvasPanel();
|
|
713
|
+
}
|
|
691
714
|
|
|
692
715
|
function onConversationSwitch(){
|
|
693
716
|
// Called when user switches conversation - update canvas panel
|
|
717
|
+
_canvasFrameLoadedHtml = null; // new conversation = new iframe content
|
|
694
718
|
var p=document.getElementById('canvasPanel');
|
|
695
719
|
if(p&&p.classList.contains('open')){
|
|
696
720
|
var d=getConvCanvasData();
|
|
@@ -705,24 +729,12 @@ function openCanvasPanel(){
|
|
|
705
729
|
var cp = document.getElementById('canvasPanel');
|
|
706
730
|
if (!cp) return;
|
|
707
731
|
cp.classList.add('open');
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
} else {
|
|
715
|
-
// Check allCanvasData for current conversation
|
|
716
|
-
var d = getConvCanvasData();
|
|
717
|
-
if (d.canvases.length > 0) {
|
|
718
|
-
canvasMode = 'canvas';
|
|
719
|
-
canvasIdx = d.canvases.length - 1;
|
|
720
|
-
renderCanvasPanel();
|
|
721
|
-
} else if (cf) {
|
|
722
|
-
var tip = '<!DOCTYPE html><html><body style="background:#0a0a0a;color:#6b7280;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;text-align:center;padding:20px"><div><div style="font-size:32px;margin-bottom:16px">■</div><div style="font-size:13px;line-height:1.6">Nessun Canvas in questo workflow.<br>Aggiungi <strong style="color:#4ade80">html</strong>, <strong style="color:#4ade80">dashboard</strong> o <strong style="color:#4ade80">visual</strong> al prompt,<br>oppure usa un task con 2+ agenti specialisti.</div></div></body></html>';
|
|
723
|
-
cf.srcdoc = tip;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
732
|
+
// Always reset tracker so renderCanvasPanel reloads fresh content
|
|
733
|
+
_canvasFrameLoadedHtml = null;
|
|
734
|
+
canvasMode = 'canvas';
|
|
735
|
+
var d = getConvCanvasData();
|
|
736
|
+
canvasIdx = Math.max(0, d.canvases.length - 1);
|
|
737
|
+
renderCanvasPanel();
|
|
726
738
|
}
|
|
727
739
|
|
|
728
740
|
function reopenCanvas(){
|
|
@@ -3330,6 +3342,12 @@ function studioReset() {
|
|
|
3330
3342
|
if (tb) tb.textContent = '';
|
|
3331
3343
|
var inlinePdfBtn = document.getElementById('studioInlinePdfBtn');
|
|
3332
3344
|
if (inlinePdfBtn) inlinePdfBtn.style.display = 'none';
|
|
3345
|
+
var parlEl = document.getElementById('studioParliamentBlock');
|
|
3346
|
+
if (parlEl) { parlEl.innerHTML = ''; parlEl.style.display = 'none'; }
|
|
3347
|
+
var canvasBtn = document.getElementById('studioCanvasBtn');
|
|
3348
|
+
if (canvasBtn) { canvasBtn.style.background = ''; canvasBtn.style.borderColor = ''; canvasBtn.style.color = 'var(--dim)'; }
|
|
3349
|
+
_canvasFrameLoadedHtml = null; // reset iframe tracker so next open always loads fresh
|
|
3350
|
+
closeCanvas();
|
|
3333
3351
|
renderStudioNodes();
|
|
3334
3352
|
renderStudioLog();
|
|
3335
3353
|
renderStudioResult();
|
|
@@ -3390,6 +3408,137 @@ function studioLog(agent, icon, text, type, update) {
|
|
|
3390
3408
|
renderStudioLog();
|
|
3391
3409
|
}
|
|
3392
3410
|
|
|
3411
|
+
// ── Workflow node character SVG ──────────────────────────────────────────────
|
|
3412
|
+
// Generates a compact animated "agent at desk" illustration for each pipeline node card.
|
|
3413
|
+
// isRunning = prl-arm/prl-head animations active; isDone = green checkmark.
|
|
3414
|
+
function buildWorkflowChar(n) {
|
|
3415
|
+
var lbl = n.label || n.agent || \x27?\x27;
|
|
3416
|
+
var ico = n.icon || \x27■\x27;
|
|
3417
|
+
var isActive = n.status === \x27running\x27;
|
|
3418
|
+
var isDone = n.status === \x27done\x27;
|
|
3419
|
+
var skinColors = [\x27#fbbf24\x27,\x27#f97316\x27,\x27#a78bfa\x27,\x27#34d399\x27,\x27#60a5fa\x27,\x27#f472b6\x27];
|
|
3420
|
+
var skinIdx = Math.abs((lbl.charCodeAt(0)||65)+(lbl.charCodeAt(lbl.length-1)||90)) % skinColors.length;
|
|
3421
|
+
var skin = skinColors[skinIdx];
|
|
3422
|
+
var shirtColors = [\x27#4f46e5\x27,\x27#0891b2\x27,\x27#7c3aed\x27,\x27#059669\x27,\x27#dc2626\x27,\x27#d97706\x27];
|
|
3423
|
+
var shirt = shirtColors[skinIdx];
|
|
3424
|
+
var hairColors = [\x27#1a1a1a\x27,\x27#4a3728\x27,\x27#c4a35a\x27,\x27#8b0000\x27,\x27#2c4a7c\x27,\x27#3d2b1f\x27];
|
|
3425
|
+
var hair = hairColors[skinIdx];
|
|
3426
|
+
var accentColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : \x27#333360\x27);
|
|
3427
|
+
var deskBg = isDone ? \x27#1a3a1a\x27 : (isActive ? \x27#1a1a3e\x27 : \x27#1a1a2a\x27);
|
|
3428
|
+
var monGlow = isActive ? \x27filter:drop-shadow(0 0 4px #6366f1)\x27 : \x27\x27;
|
|
3429
|
+
var armCls = isActive ? \x27class="prl-arm"\x27 : \x27\x27;
|
|
3430
|
+
var headCls = isActive ? \x27class="prl-head"\x27 : \x27\x27;
|
|
3431
|
+
var glowStyle = isActive ? \x27filter:drop-shadow(0 0 5px #6366f1)\x27 : \x27\x27;
|
|
3432
|
+
var svg = \x27<svg viewBox="0 0 80 96" width="70" height="84" xmlns="http://www.w3.org/2000/svg" style="\x27+glowStyle+\x27;display:block;margin:0 auto">\x27+
|
|
3433
|
+
// Desk
|
|
3434
|
+
\x27<path d="M4 55 L76 55 L76 63 L4 63 Z" fill="\x27+deskBg+\x27" stroke="\x27+accentColor+\x27" stroke-width="1.2"/>\x27+
|
|
3435
|
+
\x27<path d="M4 63 L76 63 L76 70 L4 70 Z" fill="#0e0e1c"/>\x27+
|
|
3436
|
+
\x27<line x1="4" y1="63" x2="76" y2="63" stroke="\x27+accentColor+\x2760" stroke-width=".8"/>\x27+
|
|
3437
|
+
\x27<path d="M10 70 C10 70 9 82 9 84 C9 86 11 87 13 87 C15 87 17 86 17 84 C17 82 16 70 16 70 Z" fill="#111128"/>\x27+
|
|
3438
|
+
\x27<path d="M63 70 C63 70 62 82 62 84 C62 86 64 87 66 87 C68 87 70 86 70 84 C70 82 69 70 69 70 Z" fill="#111128"/>\x27+
|
|
3439
|
+
\x27<rect x="17" y="79" width="46" height="3" rx="1.5" fill="#161626"/>\x27+
|
|
3440
|
+
// Monitor
|
|
3441
|
+
\x27<ellipse cx="35" cy="56" rx="14" ry="2" fill="rgba(0,0,0,.4)"/>\x27+
|
|
3442
|
+
\x27<ellipse cx="35" cy="57" rx="7" ry="1.5" fill="#1a1a2e"/>\x27+
|
|
3443
|
+
\x27<rect x="33" y="50" width="4" height="6" rx="1" fill="#1a1a2e"/>\x27+
|
|
3444
|
+
\x27<rect x="17" y="26" width="36" height="25" rx="4" fill="#050510"/>\x27+
|
|
3445
|
+
\x27<rect x="18" y="27" width="34" height="23" rx="3" fill="#0d0d20" stroke="\x27+accentColor+\x27" stroke-width="\x27+(isActive?\x272\x27:\x271\x27)+\x27" style="\x27+monGlow+\x27"/>\x27+
|
|
3446
|
+
\x27<rect x="20" y="29" width="30" height="18" rx="2" fill="#0a0a18"/>\x27+
|
|
3447
|
+
(isActive ?
|
|
3448
|
+
\x27<defs><linearGradient id="wsg\x27+skinIdx+\x27" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#6366f122"/><stop offset="1" stop-color="#6366f108"/></linearGradient></defs>\x27+
|
|
3449
|
+
\x27<rect x="20" y="29" width="30" height="18" rx="2" fill="url(#wsg\x27+skinIdx+\x27)"/>\x27+
|
|
3450
|
+
\x27<line x1="22" y1="32" x2="48" y2="32" stroke="#6366f1ee" stroke-width="1.2" stroke-linecap="round"/>\x27+
|
|
3451
|
+
\x27<line x1="22" y1="35" x2="44" y2="35" stroke="#6366f1aa" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3452
|
+
\x27<line x1="22" y1="38" x2="46" y2="38" stroke="#6366f188" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3453
|
+
\x27<line x1="22" y1="41" x2="40" y2="41" stroke="#6366f166" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3454
|
+
\x27<line x1="22" y1="44" x2="43" y2="44" stroke="#6366f144" stroke-width="1" stroke-linecap="round"/>\x27
|
|
3455
|
+
:
|
|
3456
|
+
\x27<line x1="22" y1="33" x2="46" y2="33" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3457
|
+
\x27<line x1="22" y1="36" x2="42" y2="36" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3458
|
+
\x27<line x1="22" y1="39" x2="44" y2="39" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
3459
|
+
\x27<line x1="22" y1="42" x2="38" y2="42" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27
|
|
3460
|
+
)+
|
|
3461
|
+
\x27<circle cx="35" cy="28.2" r=".9" fill="\x27+(isActive?\x27#6366f1\x27:\x27#2a2a40\x27)+\x27"/>\x27+
|
|
3462
|
+
// Keyboard
|
|
3463
|
+
\x27<rect x="13" y="48" width="36" height="7" rx="2.5" fill="#0c0c1e" stroke="#202036" stroke-width="1"/>\x27+
|
|
3464
|
+
\x27<rect x="14" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3465
|
+
\x27<rect x="18" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3466
|
+
\x27<rect x="22" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3467
|
+
\x27<rect x="26" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3468
|
+
\x27<rect x="30" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3469
|
+
\x27<rect x="34" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3470
|
+
\x27<rect x="38" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3471
|
+
\x27<rect x="15" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3472
|
+
\x27<rect x="21" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3473
|
+
\x27<rect x="27" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3474
|
+
\x27<rect x="33" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
3475
|
+
\x27<rect x="19" y="55" width="24" height="1.8" rx=".9" fill="#181830"/>\x27+
|
|
3476
|
+
// Chair
|
|
3477
|
+
\x27<ellipse cx="34" cy="72" rx="12" ry="4" fill="#111124"/>\x27+
|
|
3478
|
+
\x27<rect x="32" y="65" width="4" height="8" rx="1" fill="#1a1a2c"/>\x27+
|
|
3479
|
+
\x27<path d="M22 60 Q22 56 34 56 Q46 56 46 60 L46 66 Q46 68 34 68 Q22 68 22 66 Z" fill="#1c1c2c" stroke="#2a2a3e" stroke-width="1"/>\x27+
|
|
3480
|
+
\x27<path d="M24 44 Q23 38 34 37 Q45 38 44 44 L44 58 Q44 60 34 60 Q24 60 24 58 Z" fill="#191928" stroke="#2a2a3c" stroke-width="1"/>\x27+
|
|
3481
|
+
\x27<path d="M26 46 Q26 41 34 40 Q42 41 42 46 L42 57 Q42 58 34 58 Q26 58 26 57 Z" fill="#1e1e30"/>\x27+
|
|
3482
|
+
// Shirt
|
|
3483
|
+
\x27<path d="M27 44 Q27 42 34 41 Q41 42 41 44 L42 58 L26 58 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
3484
|
+
\x27<path d="M27 44 Q27 42 34 41 L34 58 L26 58 Z" fill="rgba(0,0,0,.12)"/>\x27+
|
|
3485
|
+
\x27<path d="M34 41 L31 46 L34 44.5 L37 46 Z" fill="\x27+skin+\x27ee"/>\x27+
|
|
3486
|
+
// Arms
|
|
3487
|
+
\x27<g \x27+armCls+\x27>\x27+
|
|
3488
|
+
\x27<path d="M28 45 C24 47 22 50 21 53 C21 55 23 56 25 55 C27 54 27 52 28 49 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
3489
|
+
\x27<path d="M21 53 C19 55 18 57 18 59 C18 61 20 62 22 61 C24 60 24 58 25 55 Z" fill="\x27+skin+\x27"/>\x27+
|
|
3490
|
+
\x27<ellipse cx="19" cy="60" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(-10 19 60)"/>\x27+
|
|
3491
|
+
\x27<path d="M40 45 C44 47 46 50 47 53 C47 55 45 56 43 55 C41 54 41 52 40 49 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
3492
|
+
\x27<path d="M47 53 C49 55 50 57 50 59 C50 61 48 62 46 61 C44 60 44 58 43 55 Z" fill="\x27+skin+\x27"/>\x27+
|
|
3493
|
+
\x27<ellipse cx="49" cy="60" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(10 49 60)"/>\x27+
|
|
3494
|
+
\x27</g>\x27+
|
|
3495
|
+
// Head
|
|
3496
|
+
\x27<g \x27+headCls+\x27>\x27+
|
|
3497
|
+
\x27<path d="M30 40 L38 40 L38 43 Q38 45 34 45 Q30 45 30 43 Z" fill="\x27+skin+\x27"/>\x27+
|
|
3498
|
+
\x27<ellipse cx="34" cy="29" rx="11" ry="12.5" fill="\x27+skin+\x27"/>\x27+
|
|
3499
|
+
\x27<path d="M23 28 C21 28 20 30 20 31.5 C20 33 21 34.5 23 34.5 C24 34.5 24.5 33.5 24 31.5 C24.5 29.5 24 28 23 28" fill="\x27+skin+\x27"/>\x27+
|
|
3500
|
+
\x27<path d="M45 28 C47 28 48 30 48 31.5 C48 33 47 34.5 45 34.5 C44 34.5 43.5 33.5 44 31.5 C43.5 29.5 44 28 45 28" fill="\x27+skin+\x27"/>\x27+
|
|
3501
|
+
\x27<path d="M23 28 C22 22 24 16 34 15 C44 16 46 22 45 28 C44 22 42 18 34 17 C26 18 24 22 23 28" fill="\x27+hair+\x27"/>\x27+
|
|
3502
|
+
\x27<path d="M28 17 C30 15 33 15 36 16 C33 14 29 15 28 17" fill="rgba(255,255,255,.12)"/>\x27+
|
|
3503
|
+
\x27<path d="M27 23 Q29.5 21.5 32 23" stroke="\x27+hair+\x27" stroke-width="1.6" fill="none" stroke-linecap="round"/>\x27+
|
|
3504
|
+
\x27<path d="M36 23 Q38.5 21.5 41 23" stroke="\x27+hair+\x27" stroke-width="1.6" fill="none" stroke-linecap="round"/>\x27+
|
|
3505
|
+
\x27<ellipse cx="30" cy="27.5" rx="3" ry="3.5" fill="#fff"/>\x27+
|
|
3506
|
+
\x27<ellipse cx="38" cy="27.5" rx="3" ry="3.5" fill="#fff"/>\x27+
|
|
3507
|
+
\x27<circle cx="30" cy="28" r="2.2" fill="#3d4a6b"/>\x27+
|
|
3508
|
+
\x27<circle cx="38" cy="28" r="2.2" fill="#3d4a6b"/>\x27+
|
|
3509
|
+
\x27<circle cx="30" cy="28" r="1.3" fill="#0a0a14"/>\x27+
|
|
3510
|
+
\x27<circle cx="38" cy="28" r="1.3" fill="#0a0a14"/>\x27+
|
|
3511
|
+
\x27<circle cx="31" cy="27" r=".8" fill="rgba(255,255,255,.9)"/>\x27+
|
|
3512
|
+
\x27<circle cx="39" cy="27" r=".8" fill="rgba(255,255,255,.9)"/>\x27+
|
|
3513
|
+
\x27<path d="M33 31 Q33 33 34 33.5 Q35 34 35 33 Q36 33 36 31" stroke="\x27+skin+\x27" stroke-width="1.1" fill="none" opacity=".7"/>\x27+
|
|
3514
|
+
(isDone ?
|
|
3515
|
+
\x27<path d="M29 37 Q34 42 39 37" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
|
|
3516
|
+
\x27<path d="M30 37.5 Q34 41 38 37.5 Q34 40 30 37.5" fill="#fff" opacity=".8"/>\x27
|
|
3517
|
+
:
|
|
3518
|
+
\x27<path d="M30.5 37 Q34 38.5 37.5 37" stroke="#8b4513" stroke-width="1.4" fill="none" stroke-linecap="round"/>\x27
|
|
3519
|
+
)+
|
|
3520
|
+
\x27<circle cx="38.5" cy="48" r="5.5" fill="#0f0f1e" stroke="\x27+shirt+\x2780" stroke-width="1.2"/>\x27+
|
|
3521
|
+
\x27<circle cx="38.5" cy="48" r="4" fill="#161622"/>\x27+
|
|
3522
|
+
\x27<text x="38.5" y="51" text-anchor="middle" font-size="6">\x27+ico+\x27</text>\x27+
|
|
3523
|
+
\x27</g>\x27+
|
|
3524
|
+
(isDone ?
|
|
3525
|
+
\x27<circle cx="67" cy="9" r="9" fill="#0a2010"/>\x27+
|
|
3526
|
+
\x27<circle cx="67" cy="9" r="7" fill="#16a34a"/>\x27+
|
|
3527
|
+
\x27<circle cx="67" cy="9" r="5.5" fill="#22c55e"/>\x27+
|
|
3528
|
+
\x27<path d="M63 9 L66 12 L71 5" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>\x27
|
|
3529
|
+
: \x27\x27)+
|
|
3530
|
+
// Flying papers when running
|
|
3531
|
+
(isActive ?
|
|
3532
|
+
\x27<g class="prl-fly-doc" style="animation-delay:.2s;animation-duration:1.6s;transform-origin:35px 50px">\x27+
|
|
3533
|
+
\x27<path d="M0,0 L11,0 L14,3 L14,18 L0,18 Z" fill="#0d0d20" stroke="#6366f1" stroke-width="1.2" transform="translate(35,50)"/>\x27+
|
|
3534
|
+
\x27<line x1="37" y1="53" x2="47" y2="53" stroke="#6366f1" stroke-width=".8" opacity=".7"/>\x27+
|
|
3535
|
+
\x27<line x1="37" y1="56" x2="47" y2="56" stroke="#6366f1" stroke-width=".8" opacity=".5"/>\x27+
|
|
3536
|
+
\x27</g>\x27
|
|
3537
|
+
: \x27\x27)+
|
|
3538
|
+
\x27</svg>\x27;
|
|
3539
|
+
return svg;
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3393
3542
|
function renderStudioNodes() {
|
|
3394
3543
|
var el = document.getElementById('studioNodes');
|
|
3395
3544
|
if (!el) return;
|
|
@@ -3399,29 +3548,41 @@ function renderStudioNodes() {
|
|
|
3399
3548
|
}
|
|
3400
3549
|
var html = '<div class="studio-nodes">';
|
|
3401
3550
|
studioState.nodes.forEach(function(n, i) {
|
|
3551
|
+
var isActive = n.status === 'running';
|
|
3552
|
+
var isDone = n.status === 'done';
|
|
3553
|
+
var isErr = n.status === 'error';
|
|
3554
|
+
var isWait = !isActive && !isDone && !isErr;
|
|
3402
3555
|
var cls = 'studio-node';
|
|
3403
|
-
if (
|
|
3404
|
-
else if (
|
|
3405
|
-
else if (
|
|
3406
|
-
|
|
3407
|
-
// Only animate nodes that haven't been rendered yet (first appearance)
|
|
3556
|
+
if (isActive) cls += ' studio-node--active';
|
|
3557
|
+
else if (isDone) cls += ' studio-node--done';
|
|
3558
|
+
else if (isErr) cls += ' studio-node--error';
|
|
3559
|
+
// Only animate entrance on first appearance
|
|
3408
3560
|
var style = n._rendered ? '' : 'animation-delay:' + (i * 110) + 'ms';
|
|
3409
|
-
html += '<div class="' + cls + '" data-agent-label="' + esc(n.label || n.agent) + '" style="' + style + ';cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))" title="
|
|
3410
|
-
|
|
3411
|
-
|
|
3561
|
+
html += '<div class="' + cls + '" data-agent-label="' + esc(n.label || n.agent) + '" style="' + style + ';cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))" title="' + esc(n.label || n.agent) + '">';
|
|
3562
|
+
if (isActive || isDone) {
|
|
3563
|
+
// Show animated office character
|
|
3564
|
+
html += '<div class="studio-node__char">' + buildWorkflowChar(n) + '</div>';
|
|
3565
|
+
if (isActive) {
|
|
3566
|
+
var desc = n.label || n.agent;
|
|
3567
|
+
html += '<div class="studio-node__bubble prl-action-bubble prl-action-bubble--active">...analizza</div>';
|
|
3568
|
+
} else {
|
|
3569
|
+
html += '<div class="studio-node__bubble prl-action-bubble" style="background:#0a2010;border-color:#22c55e;color:#4ade80">\u2714 completato</div>';
|
|
3570
|
+
}
|
|
3571
|
+
html += '<div class="studio-node__label studio-node__label--char">' + esc(n.label) + '</div>';
|
|
3572
|
+
} else {
|
|
3573
|
+
// Waiting / error: keep original compact pill
|
|
3574
|
+
html += '<div class="studio-node__circle">' + n.icon + '</div>';
|
|
3575
|
+
html += '<div class="studio-node__label">' + esc(n.label) + '</div>';
|
|
3576
|
+
}
|
|
3412
3577
|
if (n.reason) {
|
|
3413
3578
|
html += '<div class="studio-node__reason" onclick="event.stopPropagation();this.classList.toggle(String.fromCharCode(111,112,101,110))" title="' + esc(n.reason) + '">ℹ<span class="studio-node__reason-tip">' + esc(n.reason) + '</span></div>';
|
|
3414
3579
|
}
|
|
3415
|
-
html += '<div class="studio-node__status studio-node__status--' + n.status + '">' + statusLabel + '</div>';
|
|
3416
|
-
if (n.status === 'running') {
|
|
3417
|
-
html += '<div class="studio-node__progress"><span></span><span></span><span></span></div>';
|
|
3418
|
-
}
|
|
3419
3580
|
html += '</div>';
|
|
3420
3581
|
if (i < studioState.nodes.length - 1) {
|
|
3421
3582
|
var next = studioState.nodes[i + 1];
|
|
3422
3583
|
var arrowCls = 'studio-arrow';
|
|
3423
|
-
if (
|
|
3424
|
-
else if (
|
|
3584
|
+
if (isDone && next.status === 'running') arrowCls += ' studio-arrow--active';
|
|
3585
|
+
else if (isDone) arrowCls += ' studio-arrow--done';
|
|
3425
3586
|
var arrowStyle = n._rendered ? '' : 'opacity:0;animation:stNodeIn .3s ease ' + (i * 110 + 55) + 'ms forwards';
|
|
3426
3587
|
html += '<div class="' + arrowCls + '" style="' + arrowStyle + '">→</div>';
|
|
3427
3588
|
}
|
|
@@ -3522,7 +3683,8 @@ function downloadStudioPDF() {
|
|
|
3522
3683
|
for (var li = 0; li < lines.length; li++) {
|
|
3523
3684
|
var line = lines[li];
|
|
3524
3685
|
var trimmed = line.trim();
|
|
3525
|
-
// Headers
|
|
3686
|
+
// Headers (H1-H4)
|
|
3687
|
+
if (trimmed.slice(0,5) === '#### ') { closeAll(); out += '<h4>' + inlineFormat(trimmed.slice(5)) + '</h4>'; continue; }
|
|
3526
3688
|
if (trimmed.slice(0,4) === '### ') { closeAll(); out += '<h3>' + inlineFormat(trimmed.slice(4)) + '</h3>'; continue; }
|
|
3527
3689
|
if (trimmed.slice(0,3) === '## ') { closeAll(); out += '<h2>' + inlineFormat(trimmed.slice(3)) + '</h2>'; continue; }
|
|
3528
3690
|
if (trimmed.slice(0,2) === '# ') { closeAll(); out += '<h1>' + inlineFormat(trimmed.slice(2)) + '</h1>'; continue; }
|
|
@@ -3584,16 +3746,59 @@ function downloadStudioPDF() {
|
|
|
3584
3746
|
var agentNames = activeNodes.map(function(n){ return (n.icon||'') + ' ' + esc(n.label||n.agent); });
|
|
3585
3747
|
var nowTime = new Date().toLocaleTimeString('it-IT', {hour:'2-digit',minute:'2-digit'});
|
|
3586
3748
|
|
|
3749
|
+
// ── Helper: detect if a markdown string has real body content (not just headings/blanks)
|
|
3750
|
+
function hasBodyContent(raw) {
|
|
3751
|
+
var NL3 = String.fromCharCode(10);
|
|
3752
|
+
var lines = raw.split(NL3);
|
|
3753
|
+
for (var bi = 0; bi < lines.length; bi++) {
|
|
3754
|
+
var t = lines[bi].trim();
|
|
3755
|
+
if (!t) continue;
|
|
3756
|
+
if (/^#{1,6}\s/.test(t)) continue; // heading-only
|
|
3757
|
+
if (/^---+$/.test(t)) continue; // hr
|
|
3758
|
+
return true; // has real content
|
|
3759
|
+
}
|
|
3760
|
+
return false;
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3587
3763
|
// ── Section HTML ──────────────────────────────────────────────────────────
|
|
3588
3764
|
var sectionsHtml = activeNodes.map(function(n, idx) {
|
|
3589
3765
|
var agentColor = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#0284c7'][idx % 7];
|
|
3766
|
+
var tokIn = n.tokensIn || 0; var tokOut = n.tokensOut || 0; var tokTotal = tokIn + tokOut;
|
|
3767
|
+
var tokBadge = tokTotal > 0
|
|
3768
|
+
? '<span style="margin-left:auto;font-size:10px;color:#9ca3af;font-family:monospace;white-space:nowrap">⬆' + tokIn.toLocaleString() + ' ⬇' + tokOut.toLocaleString() + ' tok</span>'
|
|
3769
|
+
: '';
|
|
3770
|
+
// Strip empty sub-sections: find heading lines that have no body content before the next heading
|
|
3771
|
+
var NL3 = String.fromCharCode(10);
|
|
3772
|
+
var rawLines = (n.output || '').split(NL3);
|
|
3773
|
+
var cleanedLines = [];
|
|
3774
|
+
var pendingHeadings = [];
|
|
3775
|
+
for (var si = 0; si < rawLines.length; si++) {
|
|
3776
|
+
var sl = rawLines[si].trim();
|
|
3777
|
+
if (/^#{1,6}\s/.test(sl) || /^---+$/.test(sl)) {
|
|
3778
|
+
pendingHeadings.push(rawLines[si]);
|
|
3779
|
+
} else if (sl !== '') {
|
|
3780
|
+
// Real content — flush pending headings then add this line
|
|
3781
|
+
for (var ph = 0; ph < pendingHeadings.length; ph++) { cleanedLines.push(pendingHeadings[ph]); }
|
|
3782
|
+
pendingHeadings = [];
|
|
3783
|
+
cleanedLines.push(rawLines[si]);
|
|
3784
|
+
} else {
|
|
3785
|
+
// Blank line — flush pending headings only if we already have content
|
|
3786
|
+
if (cleanedLines.length > 0) {
|
|
3787
|
+
for (var ph2 = 0; ph2 < pendingHeadings.length; ph2++) { cleanedLines.push(pendingHeadings[ph2]); }
|
|
3788
|
+
pendingHeadings = [];
|
|
3789
|
+
cleanedLines.push(rawLines[si]);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
var cleanedOutput = cleanedLines.join(NL3);
|
|
3590
3794
|
return '<div class="section">' +
|
|
3591
3795
|
'<div class="agent-header" style="border-left-color:' + agentColor + '">' +
|
|
3592
3796
|
'<span class="agent-icon">' + (n.icon||'■') + '</span>' +
|
|
3593
|
-
'<div><div class="agent-name">' + esc(n.label||n.agent) + '</div>' +
|
|
3797
|
+
'<div style="flex:1"><div class="agent-name">' + esc(n.label||n.agent) + '</div>' +
|
|
3594
3798
|
'<div class="agent-sub">' + esc(n.agent) + ' · Step ' + (idx+1) + ' di ' + activeNodes.length + '</div></div>' +
|
|
3799
|
+
tokBadge +
|
|
3595
3800
|
'</div>' +
|
|
3596
|
-
'<div class="section-body">' + mdToPdfHtml(
|
|
3801
|
+
'<div class="section-body">' + mdToPdfHtml(cleanedOutput) + '</div>' +
|
|
3597
3802
|
'</div>';
|
|
3598
3803
|
}).join('');
|
|
3599
3804
|
|
|
@@ -3647,6 +3852,7 @@ function downloadStudioPDF() {
|
|
|
3647
3852
|
'.section-body h1{font-size:18px;font-weight:700;color:#1e1e2e;margin:20px 0 10px;border-bottom:1px solid #e5e7eb;padding-bottom:6px}' +
|
|
3648
3853
|
'.section-body h2{font-size:15px;font-weight:700;color:#1e1e2e;margin:18px 0 8px}' +
|
|
3649
3854
|
'.section-body h3{font-size:13px;font-weight:600;color:#4f46e5;margin:14px 0 6px}' +
|
|
3855
|
+
'.section-body h4{font-size:12px;font-weight:600;color:#6366f1;margin:10px 0 4px;text-transform:uppercase;letter-spacing:.4px}' +
|
|
3650
3856
|
'.section-body p{margin:0 0 10px}' +
|
|
3651
3857
|
'.section-body ul{margin:8px 0 10px 18px;list-style:disc}' +
|
|
3652
3858
|
'.section-body ol{margin:8px 0 10px 18px}' +
|
|
@@ -3761,6 +3967,7 @@ function downloadStudioPDF() {
|
|
|
3761
3967
|
'.section,.card,.priority-item,.source-item,.bar-row{break-inside:avoid;page-break-inside:avoid}' +
|
|
3762
3968
|
'h1,h2,h3,h4{break-after:avoid;page-break-after:avoid}' +
|
|
3763
3969
|
'.header{break-after:avoid;page-break-after:avoid}' +
|
|
3970
|
+
'.section-body h4{font-size:12px;font-weight:600;color:#6366f1!important;margin:10px 0 4px;text-transform:uppercase;letter-spacing:.4px}' +
|
|
3764
3971
|
'</style>';
|
|
3765
3972
|
var pdfHtml2 = html.replace('</head>', lightOverride + '</head>');
|
|
3766
3973
|
|
|
@@ -3774,6 +3981,24 @@ function downloadStudioPDF() {
|
|
|
3774
3981
|
var ifrBody = ifrDoc.body;
|
|
3775
3982
|
var totalH = Math.max(ifrBody.scrollHeight, ifrBody.offsetHeight, ifrDoc.documentElement.scrollHeight);
|
|
3776
3983
|
iframe.style.height = totalH + 'px';
|
|
3984
|
+
|
|
3985
|
+
// Collect smart page-break candidates using offsetTop (document-relative, not viewport-relative).
|
|
3986
|
+
// We use offsetTop + offsetHeight of each .section element so breaks land after each agent section.
|
|
3987
|
+
var breakCandidates = [0];
|
|
3988
|
+
var sectionEls = ifrDoc.querySelectorAll('.section,.cover,.toc,.workflow-bar');
|
|
3989
|
+
for (var si2 = 0; si2 < sectionEls.length; si2++) {
|
|
3990
|
+
var el2 = sectionEls[si2];
|
|
3991
|
+
// Walk up to get absolute offsetTop within the iframe document
|
|
3992
|
+
var absTop = 0; var cur = el2;
|
|
3993
|
+
while (cur && cur !== ifrDoc.body) { absTop += cur.offsetTop; cur = cur.offsetParent; }
|
|
3994
|
+
breakCandidates.push(absTop); // start of section (new page begins here)
|
|
3995
|
+
breakCandidates.push(absTop + el2.offsetHeight); // end of section
|
|
3996
|
+
}
|
|
3997
|
+
breakCandidates.push(totalH);
|
|
3998
|
+
breakCandidates.sort(function(a,b){ return a-b; });
|
|
3999
|
+
// Deduplicate
|
|
4000
|
+
breakCandidates = breakCandidates.filter(function(v,i,a){ return i===0||v!==a[i-1]; });
|
|
4001
|
+
|
|
3777
4002
|
// Scale: 3x on HiDPI screens, minimum 2.5x for sharp text at A4
|
|
3778
4003
|
var renderScale = Math.max(2.5, Math.min(3, window.devicePixelRatio * 1.5));
|
|
3779
4004
|
window.html2canvas(ifrBody, {
|
|
@@ -3796,16 +4021,33 @@ function downloadStudioPDF() {
|
|
|
3796
4021
|
var usableW = pageW - margin * 2;
|
|
3797
4022
|
var usableH = pageH - margin * 2;
|
|
3798
4023
|
|
|
3799
|
-
// px per rendered page
|
|
4024
|
+
// px per rendered page: canvas.width / usableW gives canvas-px per pt;
|
|
4025
|
+
// usableH (pt) * that ratio = canvas px per A4 usable page height
|
|
3800
4026
|
var pxPerPt = canvas.width / usableW;
|
|
3801
|
-
var
|
|
4027
|
+
var maxSliceH = Math.floor(usableH * pxPerPt); // max canvas px per page
|
|
3802
4028
|
|
|
4029
|
+
// Convert DOM break candidates to canvas px coordinates
|
|
4030
|
+
// (DOM px * renderScale because html2canvas renders at renderScale)
|
|
4031
|
+
var canvasBreaks = breakCandidates.map(function(domPx){ return Math.round(domPx * renderScale); });
|
|
4032
|
+
|
|
4033
|
+
// Smart page-break slicer: each slice ends at the nearest break candidate that fits within maxSliceH.
|
|
4034
|
+
// Falls back to hard-cut only when a single section is taller than one full page.
|
|
3803
4035
|
var yCanvas = 0;
|
|
3804
4036
|
var pageNum = 0;
|
|
3805
4037
|
while (yCanvas < canvas.height) {
|
|
3806
4038
|
if (pageNum > 0) pdf.addPage();
|
|
3807
|
-
var
|
|
3808
|
-
|
|
4039
|
+
var maxEnd = yCanvas + maxSliceH;
|
|
4040
|
+
// Find the largest break candidate <= maxEnd (that is also > yCanvas)
|
|
4041
|
+
var bestBreak = -1;
|
|
4042
|
+
for (var bi2 = 0; bi2 < canvasBreaks.length; bi2++) {
|
|
4043
|
+
var bp = canvasBreaks[bi2];
|
|
4044
|
+
if (bp > yCanvas && bp <= maxEnd) { bestBreak = bp; }
|
|
4045
|
+
}
|
|
4046
|
+
// If no break found (section taller than a page), hard-cut at maxEnd
|
|
4047
|
+
var sliceEnd = (bestBreak > yCanvas) ? bestBreak : Math.min(maxEnd, canvas.height);
|
|
4048
|
+
var thisSlice = sliceEnd - yCanvas;
|
|
4049
|
+
if (thisSlice <= 0) break; // safety guard
|
|
4050
|
+
|
|
3809
4051
|
var sliceCanvas = document.createElement('canvas');
|
|
3810
4052
|
sliceCanvas.width = canvas.width;
|
|
3811
4053
|
sliceCanvas.height = thisSlice;
|
|
@@ -3817,7 +4059,7 @@ function downloadStudioPDF() {
|
|
|
3817
4059
|
// Proportional height in pt: thisSlice / pxPerPt
|
|
3818
4060
|
var sliceImgH = thisSlice / pxPerPt;
|
|
3819
4061
|
pdf.addImage(sliceData, 'PNG', margin, margin, usableW, sliceImgH, '', 'FAST');
|
|
3820
|
-
yCanvas
|
|
4062
|
+
yCanvas = sliceEnd;
|
|
3821
4063
|
pageNum++;
|
|
3822
4064
|
}
|
|
3823
4065
|
pdf.save(pdfFileName);
|
|
@@ -3997,18 +4239,11 @@ async function runStudio() {
|
|
|
3997
4239
|
studioSetNodeStatus(i, 'done');
|
|
3998
4240
|
var realOutput = (stepResult.output && stepResult.output !== '(no output)') ? stepResult.output : null;
|
|
3999
4241
|
studioState.nodes[i].output = realOutput || '';
|
|
4242
|
+
studioState.nodes[i].tokensIn = stepResult.tokensIn || 0;
|
|
4243
|
+
studioState.nodes[i].tokensOut = stepResult.tokensOut || 0;
|
|
4000
4244
|
studioLog(node.label, node.icon, realOutput || (stepResult.canvas ? '[Canvas report generated]' : '(done)'), 'agent', true);
|
|
4001
|
-
//
|
|
4002
|
-
|
|
4003
|
-
var cf = document.getElementById('canvasFrame');
|
|
4004
|
-
var cp = document.getElementById('canvasPanel');
|
|
4005
|
-
var ct = document.getElementById('canvasTitle');
|
|
4006
|
-
if (cf && cp) {
|
|
4007
|
-
cf.srcdoc = stepResult.canvas;
|
|
4008
|
-
cp.classList.add('open');
|
|
4009
|
-
if (ct) ct.textContent = node.label + ' Report';
|
|
4010
|
-
}
|
|
4011
|
-
}
|
|
4245
|
+
// allCanvasData already updated inside runStudioStep streaming handler.
|
|
4246
|
+
// Keep _canvasFrameLoadedHtml in sync so openCanvasPanel knows what\x27s loaded.
|
|
4012
4247
|
// Accumulate context: append each step's output so specialist agents see ALL previous data
|
|
4013
4248
|
if (realOutput) {
|
|
4014
4249
|
context = context
|
|
@@ -4054,27 +4289,464 @@ async function runStudio() {
|
|
|
4054
4289
|
var pb = document.getElementById(\x27studioParliamentBlock\x27);
|
|
4055
4290
|
if (!pb) return;
|
|
4056
4291
|
pb.style.display = \x27block\x27;
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
var phaseLabel =
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4292
|
+
if (convergence != null) {
|
|
4293
|
+
pb.style.position = \x27\x27; pb.style.top = \x27\x27; pb.style.zIndex = \x27\x27; pb.style.boxShadow = \x27\x27;
|
|
4294
|
+
} else {
|
|
4295
|
+
pb.style.position = \x27sticky\x27; pb.style.top = \x278px\x27; pb.style.zIndex = \x27200\x27;
|
|
4296
|
+
pb.style.boxShadow = \x270 4px 32px rgba(99,102,241,.35)\x27;
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
// ── OFFICE CARTOON ANIMATION ─────────────────────────────────────────
|
|
4300
|
+
// Each agent = a character at a desk doing visible work.
|
|
4301
|
+
// Documents fly between agents during R2. MASTER walks around in R1.
|
|
4302
|
+
var phaseColor = {r1:\x27#6366f1\x27,r2:\x27#22d3ee\x27,r3:\x27#f59e0b\x27,done:\x27#22c55e\x27}[phase]||\x27#6366f1\x27;
|
|
4303
|
+
var phaseLabel = {
|
|
4304
|
+
r1:\x27Round 1 \u2014 Ogni agente analizza il task in autonomia\x27,
|
|
4305
|
+
r2:\x27Round 2 \u2014 Gli agenti si scambiano le analisi e le raffinano\x27,
|
|
4306
|
+
r3:\x27Round 3 \u2014 HERALD media le posizioni divergenti\x27,
|
|
4307
|
+
done:\x27Deliberazione completata\x27
|
|
4308
|
+
}[phase]||\x27\x27;
|
|
4309
|
+
|
|
4310
|
+
var n = proposals.length;
|
|
4311
|
+
var doneCount = Object.keys(parlDoneAgents).length;
|
|
4312
|
+
var progressPct = n > 0 ? Math.round(doneCount / n * 100) : 0;
|
|
4313
|
+
|
|
4314
|
+
// ── Build one desk+character card per agent ───────────────────────
|
|
4315
|
+
// Character is SVG-drawn inline: head + body + arms + desk.
|
|
4316
|
+
// Active = character types (arms animate). Done = character leans back, checkmark.
|
|
4317
|
+
// R2 active = character holds document up and turns head.
|
|
4318
|
+
|
|
4319
|
+
function buildChar(prop, isDone, isActive, isOrchestratorTarget) {
|
|
4320
|
+
var lbl = prop.label || prop.agent;
|
|
4321
|
+
var ico = prop.icon || String.fromCharCode(9632);
|
|
4322
|
+
// skin tones cycle through a palette for visual variety
|
|
4323
|
+
var skinColors = [\x27#fbbf24\x27,\x27#f97316\x27,\x27#a78bfa\x27,\x27#34d399\x27,\x27#60a5fa\x27,\x27#f472b6\x27];
|
|
4324
|
+
var skinIdx = Math.abs(lbl.charCodeAt(0)+lbl.charCodeAt(lbl.length-1)) % skinColors.length;
|
|
4325
|
+
var skin = skinColors[skinIdx];
|
|
4326
|
+
var deskColor = isDone ? \x27#1a3a1a\x27 : (isActive ? \x27#1a1a3e\x27 : \x27#1a1a2a\x27);
|
|
4327
|
+
var deskBorder = isDone ? \x27#22c55e\x27 : (isActive ? phaseColor : \x27#333360\x27);
|
|
4328
|
+
var shadow = isActive ? (\x270 0 18px \x27+phaseColor+\x2744\x27) : \x27none\x27;
|
|
4329
|
+
|
|
4330
|
+
// Action text shown above character
|
|
4331
|
+
var actionStr = \x27\x27;
|
|
4332
|
+
if (phase===\x27r1\x27 && isActive) actionStr = \x27...analizza\x27;
|
|
4333
|
+
else if (phase===\x27r1\x27 && isDone) actionStr = \x27\u2714 bozza pronta\x27;
|
|
4334
|
+
else if (phase===\x27r2\x27 && isActive) actionStr = \x27...legge + raffina\x27;
|
|
4335
|
+
else if (phase===\x27r2\x27 && isDone) actionStr = \x27\u2714 raffinato\x27;
|
|
4336
|
+
else if (phase===\x27r3\x27 && isActive) actionStr = \x27...media\x27;
|
|
4337
|
+
else if (phase===\x27done\x27) actionStr = \x27\u2714 consenso\x27;
|
|
4338
|
+
|
|
4339
|
+
// SVG character (80px wide, 70px tall)
|
|
4340
|
+
// Desk: rectangle at bottom. Chair back. Body. Head. Arms animated.
|
|
4341
|
+
var armAnim = (isActive && phase!==\x27done\x27) ? \x27class="prl-arm"\x27 : \x27\x27;
|
|
4342
|
+
var headAnim = (isActive && phase!==\x27done\x27) ? \x27class="prl-head"\x27 : \x27\x27;
|
|
4343
|
+
var glowStyle = isActive ? (\x27filter:drop-shadow(0 0 6px \x27+phaseColor+\x27)\x27) : \x27\x27;
|
|
4344
|
+
|
|
4345
|
+
// Document held up during R2 cross-reading
|
|
4346
|
+
var docHtml = \x27\x27;
|
|
4347
|
+
if (phase===\x27r2\x27 && isActive) {
|
|
4348
|
+
docHtml = \x27<rect x="46" y="6" width="14" height="18" rx="2" fill="#0f0f1e" stroke="#22d3ee" stroke-width="1.5" class="prl-doc-hold"/>\x27+
|
|
4349
|
+
\x27<line x1="49" y1="11" x2="57" y2="11" stroke="#22d3ee" stroke-width="1" opacity=".7"/>\x27+
|
|
4350
|
+
\x27<line x1="49" y1="14" x2="57" y2="14" stroke="#22d3ee" stroke-width="1" opacity=".5"/>\x27+
|
|
4351
|
+
\x27<line x1="49" y1="17" x2="54" y2="17" stroke="#22d3ee" stroke-width="1" opacity=".4"/>\x27;
|
|
4352
|
+
}
|
|
4353
|
+
if (phase===\x27r3\x27 && isActive) {
|
|
4354
|
+
docHtml = \x27<rect x="46" y="6" width="14" height="18" rx="2" fill="#0f0f1e" stroke="#f59e0b" stroke-width="1.5" class="prl-doc-hold"/>\x27+
|
|
4355
|
+
\x27<line x1="49" y1="11" x2="57" y2="11" stroke="#f59e0b" stroke-width="1" opacity=".7"/>\x27+
|
|
4356
|
+
\x27<line x1="49" y1="14" x2="57" y2="14" stroke="#f59e0b" stroke-width="1" opacity=".5"/>\x27;
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
// Shirt colors — vibrant, professional palette
|
|
4360
|
+
var shirtColors = [\x27#4f46e5\x27,\x27#0891b2\x27,\x27#7c3aed\x27,\x27#059669\x27,\x27#dc2626\x27,\x27#d97706\x27];
|
|
4361
|
+
var shirt = shirtColors[skinIdx];
|
|
4362
|
+
// Hair colors — varied and realistic
|
|
4363
|
+
var hairColors = [\x27#1a1a1a\x27,\x27#4a3728\x27,\x27#c4a35a\x27,\x27#8b0000\x27,\x27#2c4a7c\x27,\x27#3d2b1f\x27];
|
|
4364
|
+
var hair = hairColors[skinIdx];
|
|
4365
|
+
var monitorGlow = isActive ? (\x27filter:drop-shadow(0 0 5px \x27+phaseColor+\x2780)\x27) : \x27\x27;
|
|
4366
|
+
|
|
4367
|
+
var svgChar = \x27<svg viewBox="0 0 80 96" width="76" height="90" xmlns="http://www.w3.org/2000/svg" style="\x27+glowStyle+\x27;display:block;margin:0 auto">\x27+
|
|
4368
|
+
// ════ ISOMETRIC-STYLE DESK ════
|
|
4369
|
+
// Desk top — parallelogram for 3D feel (top face)
|
|
4370
|
+
\x27<path d="M4 55 L76 55 L76 63 L4 63 Z" fill="\x27+deskColor+\x27" stroke="\x27+deskBorder+\x27" stroke-width="1.2"/>\x27+
|
|
4371
|
+
// Desk front face (darker) — 3D depth
|
|
4372
|
+
\x27<path d="M4 63 L76 63 L76 70 L4 70 Z" fill="\x27+(isDone?\x27#0d2010\x27:(isActive?\x27#0c0c22\x27:\x27#0e0e1c\x27))+\x27"/>\x27+
|
|
4373
|
+
// Desk left side face
|
|
4374
|
+
\x27<path d="M4 55 L4 70 L4 70" fill="none"/>\x27+
|
|
4375
|
+
// Desk front edge highlight
|
|
4376
|
+
\x27<line x1="4" y1="63" x2="76" y2="63" stroke="\x27+deskBorder+\x2760" stroke-width=".8"/>\x27+
|
|
4377
|
+
// Desk legs — rounded, tapered
|
|
4378
|
+
\x27<path d="M10 70 C10 70 9 82 9 84 C9 86 11 87 13 87 C15 87 17 86 17 84 C17 82 16 70 16 70 Z" fill="#111128"/>\x27+
|
|
4379
|
+
\x27<path d="M63 70 C63 70 62 82 62 84 C62 86 64 87 66 87 C68 87 70 86 70 84 C70 82 69 70 69 70 Z" fill="#111128"/>\x27+
|
|
4380
|
+
// Desk shelf between legs
|
|
4381
|
+
\x27<rect x="17" y="79" width="46" height="3" rx="1.5" fill="#161626"/>\x27+
|
|
4382
|
+
// ════ MONITOR (sleek, thin-bezel) ════
|
|
4383
|
+
// Monitor shadow on desk
|
|
4384
|
+
\x27<ellipse cx="35" cy="56" rx="14" ry="2" fill="rgba(0,0,0,.4)"/>\x27+
|
|
4385
|
+
// Monitor stand base
|
|
4386
|
+
\x27<ellipse cx="35" cy="57" rx="7" ry="1.5" fill="#1a1a2e"/>\x27+
|
|
4387
|
+
// Monitor stand pole
|
|
4388
|
+
\x27<rect x="33" y="50" width="4" height="6" rx="1" fill="#1a1a2e"/>\x27+
|
|
4389
|
+
// Monitor outer bezel — shadow/depth
|
|
4390
|
+
\x27<rect x="17" y="26" width="36" height="25" rx="4" fill="#050510"/>\x27+
|
|
4391
|
+
// Monitor bezel
|
|
4392
|
+
\x27<rect x="18" y="27" width="34" height="23" rx="3" fill="#0d0d20" stroke="\x27+(isActive?phaseColor:\x27#252535\x27)+\x27" stroke-width="\x27+(isActive?\x272\x27:\x271\x27)+\x27" style="\x27+monitorGlow+\x27"/>\x27+
|
|
4393
|
+
// Screen glass — subtle gradient
|
|
4394
|
+
\x27<rect x="20" y="29" width="30" height="18" rx="2" fill="#0a0a18"/>\x27+
|
|
4395
|
+
// Screen content
|
|
4396
|
+
(isActive ?
|
|
4397
|
+
// Active: glowing code/data on screen
|
|
4398
|
+
\x27<defs><linearGradient id="sg\x27+skinIdx+\x27" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="\x27+phaseColor+\x2722"/><stop offset="1" stop-color="\x27+phaseColor+\x2708"/></linearGradient></defs>\x27+
|
|
4399
|
+
\x27<rect x="20" y="29" width="30" height="18" rx="2" fill="url(#sg\x27+skinIdx+\x27)"/>\x27+
|
|
4400
|
+
\x27<line x1="22" y1="32" x2="48" y2="32" stroke="\x27+phaseColor+\x27ee" stroke-width="1.2" stroke-linecap="round"/>\x27+
|
|
4401
|
+
\x27<line x1="22" y1="35" x2="44" y2="35" stroke="\x27+phaseColor+\x27aa" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4402
|
+
\x27<line x1="22" y1="38" x2="46" y2="38" stroke="\x27+phaseColor+\x2788" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4403
|
+
\x27<line x1="22" y1="41" x2="40" y2="41" stroke="\x27+phaseColor+\x2766" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4404
|
+
\x27<line x1="22" y1="44" x2="43" y2="44" stroke="\x27+phaseColor+\x2744" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4405
|
+
\x27<rect x="22" y="30" width="10" height="2.5" rx="1" fill="\x27+phaseColor+\x2733"/>\x27
|
|
4406
|
+
:
|
|
4407
|
+
// Idle: dim screen with faint lines
|
|
4408
|
+
\x27<line x1="22" y1="33" x2="46" y2="33" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4409
|
+
\x27<line x1="22" y1="36" x2="42" y2="36" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4410
|
+
\x27<line x1="22" y1="39" x2="44" y2="39" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4411
|
+
\x27<line x1="22" y1="42" x2="38" y2="42" stroke="#1e1e30" stroke-width="1" stroke-linecap="round"/>\x27
|
|
4412
|
+
)+
|
|
4413
|
+
// Monitor camera dot
|
|
4414
|
+
\x27<circle cx="35" cy="28.2" r=".9" fill="\x27+(isActive?phaseColor:\x27#2a2a40\x27)+\x27"/>\x27+
|
|
4415
|
+
// ════ KEYBOARD (detailed, realistic) ════
|
|
4416
|
+
\x27<rect x="13" y="48" width="36" height="7" rx="2.5" fill="#0c0c1e" stroke="#202036" stroke-width="1"/>\x27+
|
|
4417
|
+
// Key rows
|
|
4418
|
+
\x27<rect x="14" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4419
|
+
\x27<rect x="18" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4420
|
+
\x27<rect x="22" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4421
|
+
\x27<rect x="26" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4422
|
+
\x27<rect x="30" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4423
|
+
\x27<rect x="34" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4424
|
+
\x27<rect x="38" y="49.5" width="3" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4425
|
+
\x27<rect x="15" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4426
|
+
\x27<rect x="21" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4427
|
+
\x27<rect x="27" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4428
|
+
\x27<rect x="33" y="52.5" width="5" height="2" rx=".5" fill="#181830"/>\x27+
|
|
4429
|
+
// Spacebar
|
|
4430
|
+
\x27<rect x="19" y="55" width="24" height="1.8" rx=".9" fill="#181830"/>\x27+
|
|
4431
|
+
// ════ ERGONOMIC CHAIR ════
|
|
4432
|
+
// Chair base
|
|
4433
|
+
\x27<ellipse cx="34" cy="72" rx="12" ry="4" fill="#111124"/>\x27+
|
|
4434
|
+
// Chair gas lift
|
|
4435
|
+
\x27<rect x="32" y="65" width="4" height="8" rx="1" fill="#1a1a2c"/>\x27+
|
|
4436
|
+
// Chair seat
|
|
4437
|
+
\x27<path d="M22 60 Q22 56 34 56 Q46 56 46 60 L46 66 Q46 68 34 68 Q22 68 22 66 Z" fill="#1c1c2c" stroke="#2a2a3e" stroke-width="1"/>\x27+
|
|
4438
|
+
// Chair backrest
|
|
4439
|
+
\x27<path d="M24 44 Q23 38 34 37 Q45 38 44 44 L44 58 Q44 60 34 60 Q24 60 24 58 Z" fill="#191928" stroke="#2a2a3c" stroke-width="1"/>\x27+
|
|
4440
|
+
// Chair backrest cushion
|
|
4441
|
+
\x27<path d="M26 46 Q26 41 34 40 Q42 41 42 46 L42 57 Q42 58 34 58 Q26 58 26 57 Z" fill="#1e1e30"/>\x27+
|
|
4442
|
+
// Chair headrest
|
|
4443
|
+
\x27<path d="M28 37 Q28 33 34 33 Q40 33 40 37 L40 39 Q40 40 34 40 Q28 40 28 39 Z" fill="#191928" stroke="#2a2a3c" stroke-width="1"/>\x27+
|
|
4444
|
+
// Chair armrests
|
|
4445
|
+
\x27<path d="M22 55 L18 55 Q16 55 16 57 L16 60 Q16 62 18 62 L22 62 Q24 62 24 60 L24 55 Z" fill="#1c1c2c"/>\x27+
|
|
4446
|
+
\x27<path d="M46 55 L50 55 Q52 55 52 57 L52 60 Q52 62 50 62 L46 62 Q44 62 44 60 L44 55 Z" fill="#1c1c2c"/>\x27+
|
|
4447
|
+
// ════ TORSO / SHIRT ════
|
|
4448
|
+
// shirt back visible above chair
|
|
4449
|
+
\x27<path d="M27 58 Q27 54 34 53 Q41 54 41 58 L42 65 L26 65 Z" fill="\x27+shirt+\x27cc"/>\x27+
|
|
4450
|
+
// shirt body
|
|
4451
|
+
\x27<path d="M27 44 Q27 42 34 41 Q41 42 41 44 L42 58 L26 58 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
4452
|
+
// shirt shading (left side)
|
|
4453
|
+
\x27<path d="M27 44 Q27 42 34 41 L34 58 L26 58 Z" fill="rgba(0,0,0,.12)"/>\x27+
|
|
4454
|
+
// collar / V-neck
|
|
4455
|
+
\x27<path d="M34 41 L31 46 L34 44.5 L37 46 Z" fill="\x27+skin+\x27ee"/>\x27+
|
|
4456
|
+
// shirt wrinkle detail
|
|
4457
|
+
\x27<line x1="34" y1="46" x2="34" y2="57" stroke="rgba(0,0,0,.08)" stroke-width="2" stroke-linecap="round"/>\x27+
|
|
4458
|
+
// ════ ARMS (typing position) ════
|
|
4459
|
+
\x27<g \x27+armAnim+\x27>\x27+
|
|
4460
|
+
// Left upper arm
|
|
4461
|
+
\x27<path d="M28 45 C24 47 22 50 21 53 C21 55 23 56 25 55 C27 54 27 52 28 49 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
4462
|
+
// Left forearm
|
|
4463
|
+
\x27<path d="M21 53 C19 55 18 57 18 59 C18 61 20 62 22 61 C24 60 24 58 25 55 Z" fill="\x27+skin+\x27"/>\x27+
|
|
4464
|
+
// Left hand
|
|
4465
|
+
\x27<ellipse cx="19" cy="60" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(-10 19 60)"/>\x27+
|
|
4466
|
+
// Right upper arm
|
|
4467
|
+
\x27<path d="M40 45 C44 47 46 50 47 53 C47 55 45 56 43 55 C41 54 41 52 40 49 Z" fill="\x27+shirt+\x27"/>\x27+
|
|
4468
|
+
// Right forearm
|
|
4469
|
+
\x27<path d="M47 53 C49 55 50 57 50 59 C50 61 48 62 46 61 C44 60 44 58 43 55 Z" fill="\x27+skin+\x27"/>\x27+
|
|
4470
|
+
// Right hand
|
|
4471
|
+
\x27<ellipse cx="49" cy="60" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(10 49 60)"/>\x27+
|
|
4472
|
+
\x27</g>\x27+
|
|
4473
|
+
// ════ HEAD (smooth, expressive) ════
|
|
4474
|
+
\x27<g \x27+headAnim+\x27>\x27+
|
|
4475
|
+
// Neck with shadow
|
|
4476
|
+
\x27<path d="M30 40 L38 40 L38 43 Q38 45 34 45 Q30 45 30 43 Z" fill="\x27+skin+\x27"/>\x27+
|
|
4477
|
+
\x27<path d="M30 40 L34 40 L34 45 Q30 45 30 43 Z" fill="rgba(0,0,0,.08)"/>\x27+
|
|
4478
|
+
// Head — well-proportioned ellipse
|
|
4479
|
+
\x27<ellipse cx="34" cy="29" rx="11" ry="12.5" fill="\x27+skin+\x27"/>\x27+
|
|
4480
|
+
// Cheek blush (subtle)
|
|
4481
|
+
\x27<ellipse cx="26" cy="32" rx="3.5" ry="2" fill="\x27+skin+\x27" opacity=".6"/>\x27+
|
|
4482
|
+
\x27<ellipse cx="42" cy="32" rx="3.5" ry="2" fill="\x27+skin+\x27" opacity=".6"/>\x27+
|
|
4483
|
+
// Head shadow (right)
|
|
4484
|
+
\x27<ellipse cx="41" cy="29" rx="5" ry="11" fill="rgba(0,0,0,.06)"/>\x27+
|
|
4485
|
+
// Ears — detailed
|
|
4486
|
+
\x27<path d="M23 28 C21 28 20 30 20 31.5 C20 33 21 34.5 23 34.5 C24 34.5 24.5 33.5 24 31.5 C24.5 29.5 24 28 23 28" fill="\x27+skin+\x27"/>\x27+
|
|
4487
|
+
\x27<path d="M45 28 C47 28 48 30 48 31.5 C48 33 47 34.5 45 34.5 C44 34.5 43.5 33.5 44 31.5 C43.5 29.5 44 28 45 28" fill="\x27+skin+\x27"/>\x27+
|
|
4488
|
+
\x27<path d="M23.5 30 C22.5 30.5 22.5 32.5 23.5 33" stroke="\x27+skin+\x27" stroke-width="1" fill="none" opacity=".5"/>\x27+
|
|
4489
|
+
// Hair — styled, voluminous
|
|
4490
|
+
\x27<path d="M23 28 C22 22 24 16 34 15 C44 16 46 22 45 28 C44 22 42 18 34 17 C26 18 24 22 23 28" fill="\x27+hair+\x27"/>\x27+
|
|
4491
|
+
\x27<path d="M23 27 C22 24 23 19 26 17 C24 20 23 24 24 27" fill="\x27+hair+\x2788"/>\x27+
|
|
4492
|
+
// Hair highlight
|
|
4493
|
+
\x27<path d="M28 17 C30 15 33 15 36 16 C33 14 29 15 28 17" fill="rgba(255,255,255,.12)"/>\x27+
|
|
4494
|
+
// Eyebrows — expressive
|
|
4495
|
+
\x27<path d="M27 23 Q29.5 21.5 32 23" stroke="\x27+hair+\x27" stroke-width="1.6" fill="none" stroke-linecap="round"/>\x27+
|
|
4496
|
+
\x27<path d="M36 23 Q38.5 21.5 41 23" stroke="\x27+hair+\x27" stroke-width="1.6" fill="none" stroke-linecap="round"/>\x27+
|
|
4497
|
+
// Eyes — full detail: white + iris + pupil + highlight
|
|
4498
|
+
\x27<ellipse cx="30" cy="27.5" rx="3" ry="3.5" fill="#fff" stroke="\x27+skin+\x2740" stroke-width=".5"/>\x27+
|
|
4499
|
+
\x27<ellipse cx="38" cy="27.5" rx="3" ry="3.5" fill="#fff" stroke="\x27+skin+\x2740" stroke-width=".5"/>\x27+
|
|
4500
|
+
// Iris
|
|
4501
|
+
\x27<circle cx="30" cy="28" r="2.2" fill="#3d4a6b"/>\x27+
|
|
4502
|
+
\x27<circle cx="38" cy="28" r="2.2" fill="#3d4a6b"/>\x27+
|
|
4503
|
+
// Pupil
|
|
4504
|
+
\x27<circle cx="30" cy="28" r="1.3" fill="#0a0a14"/>\x27+
|
|
4505
|
+
\x27<circle cx="38" cy="28" r="1.3" fill="#0a0a14"/>\x27+
|
|
4506
|
+
// Eye shine
|
|
4507
|
+
\x27<circle cx="31" cy="27" r=".8" fill="rgba(255,255,255,.9)"/>\x27+
|
|
4508
|
+
\x27<circle cx="39" cy="27" r=".8" fill="rgba(255,255,255,.9)"/>\x27+
|
|
4509
|
+
\x27<circle cx="29.5" cy="29" r=".35" fill="rgba(255,255,255,.4)"/>\x27+
|
|
4510
|
+
// Lower eyelid line
|
|
4511
|
+
\x27<path d="M27 30 Q30 31.5 33 30" stroke="\x27+skin+\x27" stroke-width=".7" fill="none" opacity=".5"/>\x27+
|
|
4512
|
+
\x27<path d="M35 30 Q38 31.5 41 30" stroke="\x27+skin+\x27" stroke-width=".7" fill="none" opacity=".5"/>\x27+
|
|
4513
|
+
// Nose — soft curved
|
|
4514
|
+
\x27<path d="M33 31 Q33 33 34 33.5 Q35 34 35 33 Q36 33 36 31" stroke="\x27+skin+\x27" stroke-width="1.1" fill="none" stroke-linecap="round" opacity=".7"/>\x27+
|
|
4515
|
+
\x27<ellipse cx="31.5" cy="33.5" rx="1.2" ry=".7" fill="rgba(0,0,0,.08)"/>\x27+
|
|
4516
|
+
\x27<ellipse cx="36.5" cy="33.5" rx="1.2" ry=".7" fill="rgba(0,0,0,.08)"/>\x27+
|
|
4517
|
+
// Mouth — expressive
|
|
4518
|
+
(isDone ?
|
|
4519
|
+
// Big smile when done
|
|
4520
|
+
\x27<path d="M29 37 Q34 42 39 37" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
|
|
4521
|
+
\x27<path d="M29 37 Q34 41 39 37" stroke="rgba(255,255,255,.3)" stroke-width=".5" fill="none" stroke-linecap="round"/>\x27+
|
|
4522
|
+
// Teeth
|
|
4523
|
+
\x27<path d="M30 37.5 Q34 41 38 37.5 Q34 40 30 37.5" fill="#fff" opacity=".8"/>\x27
|
|
4524
|
+
:
|
|
4525
|
+
// Focused expression
|
|
4526
|
+
\x27<path d="M30.5 37 Q34 38.5 37.5 37" stroke="#8b4513" stroke-width="1.4" fill="none" stroke-linecap="round"/>\x27
|
|
4527
|
+
)+
|
|
4528
|
+
// Agent badge/pin on shirt
|
|
4529
|
+
\x27<circle cx="38.5" cy="48" r="5.5" fill="#0f0f1e" stroke="\x27+shirt+\x2780" stroke-width="1.2"/>\x27+
|
|
4530
|
+
\x27<circle cx="38.5" cy="48" r="4" fill="#161622"/>\x27+
|
|
4531
|
+
\x27<text x="38.5" y="51" text-anchor="middle" font-size="6">\x27+ico+\x27</text>\x27+
|
|
4532
|
+
\x27</g>\x27+
|
|
4533
|
+
// ════ DONE BADGE (top right corner, polished) ════
|
|
4534
|
+
(isDone ?
|
|
4535
|
+
\x27<circle cx="67" cy="9" r="10" fill="#0a2010"/>\x27+
|
|
4536
|
+
\x27<circle cx="67" cy="9" r="8" fill="#16a34a"/>\x27+
|
|
4537
|
+
\x27<circle cx="67" cy="9" r="6" fill="#22c55e"/>\x27+
|
|
4538
|
+
\x27<path d="M62.5 9 L65.5 12 L71.5 5" stroke="#fff" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>\x27
|
|
4539
|
+
: \x27\x27)+
|
|
4540
|
+
docHtml+
|
|
4541
|
+
\x27</svg>\x27;
|
|
4542
|
+
|
|
4543
|
+
return \x27<div class="prl-desk\x27+(isActive?\x27 prl-desk--active\x27:\x27\x27)+(isDone?\x27 prl-desk--done\x27:\x27\x27)+\x27" style="--dc:\x27+phaseColor+\x27;box-shadow:\x27+shadow+\x27">\x27+
|
|
4544
|
+
(actionStr ? \x27<div class="prl-action-bubble\x27+(isActive?\x27 prl-action-bubble--active\x27:\x27\x27)+\x27">\x27+actionStr+\x27</div>\x27 : \x27\x27)+
|
|
4545
|
+
svgChar+
|
|
4546
|
+
\x27<div class="prl-desk-name" style="color:\x27+(isDone?\x27#4ade80\x27:(isActive?phaseColor:\x27#6b7280\x27))+\x27">\x27+esc(lbl.slice(0,14))+\x27</div>\x27+
|
|
4547
|
+
\x27</div>\x27;
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
// ── MASTER ORCHESTRATOR walking animation ─────────────────────────
|
|
4551
|
+
// In R1: walks left-right between desks (CSS animation).
|
|
4552
|
+
// In R2: stands at the active agent's desk.
|
|
4553
|
+
// In R3: stands center with lightning bolt.
|
|
4554
|
+
var masterIcon = phase===\x27r3\x27 ? \x27\u26a1\x27 : (phase===\x27done\x27 ? \x27\u2714\x27 : \x27\u2666\x27);
|
|
4555
|
+
var masterColor2 = {r1:\x27#818cf8\x27,r2:\x27#818cf8\x27,r3:\x27#f59e0b\x27,done:\x27#22c55e\x27}[phase]||\x27#818cf8\x27;
|
|
4556
|
+
var masterAnim = (phase===\x27r1\x27) ? \x27prl-master-walk\x27 : (phase===\x27r2\x27 ? \x27prl-master-supervise\x27 : \x27\x27);
|
|
4557
|
+
var masterSvg = \x27<svg viewBox="0 0 60 90" width="56" height="86" xmlns="http://www.w3.org/2000/svg" style="filter:drop-shadow(0 0 12px \x27+masterColor2+\x27aa)">\x27+
|
|
4558
|
+
// ════ LEGS (walking when R1) ════
|
|
4559
|
+
// Left leg — trouser
|
|
4560
|
+
\x27<path d="M22 55 C21 63 19 72 18 77 C17 80 18 82 21 82 C23 82 24 80 24 77 C25 71 25 62 26 55 Z" fill="#1e1c4a" class="prl-master-leg-l"/>\x27+
|
|
4561
|
+
// Right leg — trouser
|
|
4562
|
+
\x27<path d="M28 55 C29 63 31 72 32 77 C33 80 32 82 29 82 C27 82 26 80 26 77 C25 71 25 62 24 55 Z" fill="#1e1c4a" class="prl-master-leg-r"/>\x27+
|
|
4563
|
+
// Left shoe — detailed
|
|
4564
|
+
\x27<path d="M16 79 C14 79 13 81 14 83 C15 85 18 85 21 84 C23 83 24 82 23 80 C22 79 19 79 16 79" fill="#0a0a14"/>\x27+
|
|
4565
|
+
\x27<path d="M16 79 C15 80 14 82 15 83" stroke="#1a1a2e" stroke-width=".8" fill="none"/>\x27+
|
|
4566
|
+
// Right shoe
|
|
4567
|
+
\x27<path d="M34 79 C36 79 37 81 36 83 C35 85 32 85 29 84 C27 83 26 82 27 80 C28 79 31 79 34 79" fill="#0a0a14"/>\x27+
|
|
4568
|
+
// Trouser crease
|
|
4569
|
+
\x27<line x1="23" y1="55" x2="21" y2="77" stroke="rgba(255,255,255,.08)" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4570
|
+
\x27<line x1="27" y1="55" x2="29" y2="77" stroke="rgba(255,255,255,.08)" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4571
|
+
// ════ TORSO — Premium dark suit ════
|
|
4572
|
+
// Suit jacket base
|
|
4573
|
+
\x27<path d="M13 32 C12 30 15 27 25 25 C35 27 38 30 37 32 L38 55 L12 55 Z" fill="#252450"/>\x27+
|
|
4574
|
+
// Left suit front panel
|
|
4575
|
+
\x27<path d="M13 32 C12 30 15 27 25 25 L25 55 L12 55 Z" fill="#1e1d44"/>\x27+
|
|
4576
|
+
// Right suit front panel (lighter)
|
|
4577
|
+
\x27<path d="M25 25 C35 27 38 30 37 32 L38 55 L25 55 Z" fill="#272660"/>\x27+
|
|
4578
|
+
// Suit lapels — left
|
|
4579
|
+
\x27<path d="M25 25 L19 33 L22 36 L25 29 Z" fill="#1a1940" stroke="#252450" stroke-width=".5"/>\x27+
|
|
4580
|
+
// Suit lapels — right
|
|
4581
|
+
\x27<path d="M25 25 L31 33 L28 36 L25 29 Z" fill="#1a1940" stroke="#252450" stroke-width=".5"/>\x27+
|
|
4582
|
+
// White shirt / tie visible between lapels
|
|
4583
|
+
\x27<path d="M25 29 L22 36 L25 34 L28 36 Z" fill="#f0f0fa"/>\x27+
|
|
4584
|
+
// Tie — authority color
|
|
4585
|
+
\x27<path d="M25 33 L24 44 L25 48 L26 44 Z" fill="\x27+masterColor2+\x27"/>\x27+
|
|
4586
|
+
\x27<path d="M24 44 L25 48 L26 44 L25 43 Z" fill="\x27+masterColor2+\x27cc"/>\x27+
|
|
4587
|
+
// Tie knot
|
|
4588
|
+
\x27<path d="M23.5 32 L26.5 32 L25 34 Z" fill="\x27+masterColor2+\x27"/>\x27+
|
|
4589
|
+
// Suit pocket square
|
|
4590
|
+
\x27<path d="M33 35 L36 33 L37 36 L34 37 Z" fill="\x27+masterColor2+\x2799"/>\x27+
|
|
4591
|
+
// Suit buttons
|
|
4592
|
+
\x27<circle cx="25" cy="42" r="1.2" fill="\x27+masterColor2+\x27aa"/>\x27+
|
|
4593
|
+
\x27<circle cx="25" cy="46" r="1.2" fill="\x27+masterColor2+\x27aa"/>\x27+
|
|
4594
|
+
// Suit lapel badge / NHA logo
|
|
4595
|
+
\x27<circle cx="20" cy="36" r="2.5" fill="#0d0d1e" stroke="\x27+masterColor2+\x2799" stroke-width="1"/>\x27+
|
|
4596
|
+
\x27<text x="20" y="39" text-anchor="middle" font-size="4" fill="\x27+masterColor2+\x27">N</text>\x27+
|
|
4597
|
+
// ════ ARMS ════
|
|
4598
|
+
\x27<g class="prl-master-arm-l">\x27+
|
|
4599
|
+
// Left upper arm
|
|
4600
|
+
\x27<path d="M13 34 C8 37 6 42 6 46 C6 49 9 50 11 49 C13 48 13 45 14 41 C15 38 14 35 13 34" fill="#252450"/>\x27+
|
|
4601
|
+
// Left forearm
|
|
4602
|
+
\x27<path d="M6 46 C4 48 4 51 5 53 C6 55 9 55 10 53 C11 51 10 48 10 46 Z" fill="#d4a97a"/>\x27+
|
|
4603
|
+
// Left hand
|
|
4604
|
+
\x27<ellipse cx="7" cy="54" rx="4.5" ry="3.5" fill="#d4a97a" transform="rotate(-15 7 54)"/>\x27+
|
|
4605
|
+
\x27</g>\x27+
|
|
4606
|
+
// Right arm — holds clipboard
|
|
4607
|
+
\x27<g class="prl-master-arm-r">\x27+
|
|
4608
|
+
// Right upper arm
|
|
4609
|
+
\x27<path d="M37 34 C42 37 44 41 44 45 C44 48 41 49 39 48 C37 47 37 44 37 40 C37 37 37 35 37 34" fill="#252450"/>\x27+
|
|
4610
|
+
// Right forearm
|
|
4611
|
+
\x27<path d="M44 44 C46 46 47 49 46 52 C45 54 42 54 41 52 C40 50 41 47 41 45 Z" fill="#d4a97a"/>\x27+
|
|
4612
|
+
// Right hand
|
|
4613
|
+
\x27<ellipse cx="45" cy="52" rx="4" ry="3" fill="#d4a97a" transform="rotate(15 45 52)"/>\x27+
|
|
4614
|
+
// Clipboard — premium design
|
|
4615
|
+
\x27<rect x="43" y="32" width="14" height="19" rx="2.5" fill="#1a1a2e" stroke="\x27+masterColor2+\x2799" stroke-width="1.5"/>\x27+
|
|
4616
|
+
// Clipboard clip
|
|
4617
|
+
\x27<rect x="47" y="30" width="6" height="5" rx="1.5" fill="\x27+masterColor2+\x27" stroke="\x27+masterColor2+\x27" stroke-width="1"/>\x27+
|
|
4618
|
+
\x27<rect x="48" y="31" width="4" height="3" rx="1" fill="#0f0f1e"/>\x27+
|
|
4619
|
+
// Clipboard lines
|
|
4620
|
+
\x27<line x1="46" y1="36" x2="54" y2="36" stroke="\x27+masterColor2+\x27cc" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4621
|
+
\x27<line x1="46" y1="39" x2="54" y2="39" stroke="\x27+masterColor2+\x27aa" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4622
|
+
\x27<line x1="46" y1="42" x2="54" y2="42" stroke="\x27+masterColor2+\x2788" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4623
|
+
\x27<line x1="46" y1="45" x2="50" y2="45" stroke="\x27+masterColor2+\x2766" stroke-width="1" stroke-linecap="round"/>\x27+
|
|
4624
|
+
// Data chart on clipboard
|
|
4625
|
+
\x27<rect x="46" y="37" width="3" height="2" rx=".5" fill="\x27+masterColor2+\x2744"/>\x27+
|
|
4626
|
+
\x27<rect x="50" y="36" width="3" height="3" rx=".5" fill="\x27+masterColor2+\x2766"/>\x27+
|
|
4627
|
+
\x27</g>\x27+
|
|
4628
|
+
// ════ HEAD — authoritative, confident ════
|
|
4629
|
+
// Neck
|
|
4630
|
+
\x27<path d="M22 25 L28 25 L28 29 Q28 31 25 31 Q22 31 22 29 Z" fill="#d4a97a"/>\x27+
|
|
4631
|
+
\x27<path d="M22 25 L25 25 L25 31 Q22 31 22 29 Z" fill="rgba(0,0,0,.1)"/>\x27+
|
|
4632
|
+
// Head shape
|
|
4633
|
+
\x27<ellipse cx="25" cy="15" rx="12" ry="13" fill="#d4a97a"/>\x27+
|
|
4634
|
+
// Jaw/chin
|
|
4635
|
+
\x27<path d="M14 15 C14 22 18 26 25 27 C32 26 36 22 36 15" fill="#d4a97a"/>\x27+
|
|
4636
|
+
// Head shading
|
|
4637
|
+
\x27<ellipse cx="32" cy="15" rx="6" ry="11" fill="rgba(0,0,0,.07)"/>\x27+
|
|
4638
|
+
// Ears
|
|
4639
|
+
\x27<path d="M13 13 C11 13 10 15 10 17 C10 19 11 20 13 20 C14 20 14.5 19 14 17 C14.5 15 14 13 13 13" fill="#d4a97a"/>\x27+
|
|
4640
|
+
\x27<path d="M37 13 C39 13 40 15 40 17 C40 19 39 20 37 20 C36 20 35.5 19 36 17 C35.5 15 36 13 37 13" fill="#d4a97a"/>\x27+
|
|
4641
|
+
\x27<path d="M13.5 15 C12.5 16 12.5 18 13.5 19" stroke="#c4935a" stroke-width="1" fill="none"/>\x27+
|
|
4642
|
+
// Hair — executive styled, neat
|
|
4643
|
+
\x27<path d="M13 14 C12 7 16 2 25 1 C34 2 38 7 37 14 C36 7 32 4 25 3 C18 4 14 7 13 14" fill="#1a0e08"/>\x27+
|
|
4644
|
+
// Side part
|
|
4645
|
+
\x27<path d="M22 3 C21 4 21 6 22 8" stroke="rgba(255,255,255,.15)" stroke-width="1.5" fill="none" stroke-linecap="round"/>\x27+
|
|
4646
|
+
// Hair sheen
|
|
4647
|
+
\x27<path d="M20 3 C22 1 27 1 30 2 C27 0 22 1 20 3" fill="rgba(255,255,255,.1)"/>\x27+
|
|
4648
|
+
// Eyebrows — thick, authoritative
|
|
4649
|
+
\x27<path d="M16.5 11 Q19 9.5 21.5 11" stroke="#1a0e08" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
|
|
4650
|
+
\x27<path d="M28.5 11 Q31 9.5 33.5 11" stroke="#1a0e08" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
|
|
4651
|
+
// Eyes — confident, forward-looking
|
|
4652
|
+
\x27<ellipse cx="19.5" cy="14.5" rx="3.2" ry="3.5" fill="#fff" stroke="#d4a97a" stroke-width=".4"/>\x27+
|
|
4653
|
+
\x27<ellipse cx="30.5" cy="14.5" rx="3.2" ry="3.5" fill="#fff" stroke="#d4a97a" stroke-width=".4"/>\x27+
|
|
4654
|
+
\x27<circle cx="19.5" cy="15" r="2.3" fill="#1e3a6e"/>\x27+
|
|
4655
|
+
\x27<circle cx="30.5" cy="15" r="2.3" fill="#1e3a6e"/>\x27+
|
|
4656
|
+
\x27<circle cx="19.5" cy="15" r="1.3" fill="#0a0a18"/>\x27+
|
|
4657
|
+
\x27<circle cx="30.5" cy="15" r="1.3" fill="#0a0a18"/>\x27+
|
|
4658
|
+
\x27<circle cx="20.5" cy="13.7" r=".9" fill="rgba(255,255,255,.95)"/>\x27+
|
|
4659
|
+
\x27<circle cx="31.5" cy="13.7" r=".9" fill="rgba(255,255,255,.95)"/>\x27+
|
|
4660
|
+
// Nose — subtle
|
|
4661
|
+
\x27<path d="M24 18 Q24 20 25 20.5 Q26 21 26 19.5" stroke="#c4935a" stroke-width="1.1" fill="none" stroke-linecap="round" opacity=".8"/>\x27+
|
|
4662
|
+
\x27<ellipse cx="22.5" cy="20.5" rx="1.3" ry=".8" fill="rgba(0,0,0,.1)"/>\x27+
|
|
4663
|
+
\x27<ellipse cx="27.5" cy="20.5" rx="1.3" ry=".8" fill="rgba(0,0,0,.1)"/>\x27+
|
|
4664
|
+
// Confident smile
|
|
4665
|
+
\x27<path d="M19.5 24 Q25 27.5 30.5 24" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
|
|
4666
|
+
\x27<path d="M20 24.5 Q25 27 30 24.5 Q25 26.5 20 24.5" fill="#fff" opacity=".7"/>\x27+
|
|
4667
|
+
// Crown / authority icon above head
|
|
4668
|
+
\x27<text x="25" y="-1" text-anchor="middle" font-size="11" style="filter:drop-shadow(0 0 4px \x27+masterColor2+\x27)">\x27+masterIcon+\x27</text>\x27+
|
|
4669
|
+
// Subtle glow ring around crown icon
|
|
4670
|
+
\x27<circle cx="25" cy="-2" r="8" fill="none" stroke="\x27+masterColor2+\x2730" stroke-width="1.5"/>\x27+
|
|
4671
|
+
\x27</svg>\x27;
|
|
4672
|
+
|
|
4673
|
+
var masterLabel2 = {r1:\x27Orchestratore\x27,r2:\x27Coordina\x27,r3:\x27HERALD\x27,done:\x27Completato\x27}[phase]||\x27MASTER\x27;
|
|
4674
|
+
var masterHtml = \x27<div class="prl-master \x27+masterAnim+\x27">\x27+masterSvg+\x27<div class="prl-master-label" style="color:\x27+masterColor2+\x27">\x27+masterLabel2+\x27</div></div>\x27;
|
|
4675
|
+
|
|
4676
|
+
// ── Flying document animation for R2 (agent-to-agent) ────────────
|
|
4677
|
+
// One flying doc per active cross-reading event, CSS keyframe arc.
|
|
4678
|
+
var flyingDocHtml = \x27\x27;
|
|
4679
|
+
if (phase===\x27r2\x27 && activeLabel) {
|
|
4680
|
+
var others2 = proposals.filter(function(x){return (x.label||x.agent)!==activeLabel;});
|
|
4681
|
+
flyingDocHtml = \x27<div class="prl-fly-container" aria-hidden="true">\x27;
|
|
4682
|
+
others2.forEach(function(other, oi) {
|
|
4683
|
+
var delay = (oi * 0.35).toFixed(2);
|
|
4684
|
+
flyingDocHtml += \x27<div class="prl-fly-doc" style="animation-delay:\x27+delay+\x27s;animation-duration:\x27+(1.5+oi*0.2).toFixed(1)+\x27s">\x27+
|
|
4685
|
+
// Document — dog-ear corner, realistic paper look
|
|
4686
|
+
\x27<svg viewBox="0 0 22 28" width="22" height="28">\x27+
|
|
4687
|
+
\x27<defs><filter id="dsf\x27+oi+\x27" x="-20%" y="-20%" width="140%" height="140%"><feDropShadow dx="1" dy="2" stdDeviation="2" flood-color="#22d3ee" flood-opacity=".5"/></filter></defs>\x27+
|
|
4688
|
+
// Paper body
|
|
4689
|
+
\x27<path d="M2 1 L16 1 L21 6 L21 27 Q21 28 20 28 L2 28 Q1 28 1 27 L1 2 Q1 1 2 1" fill="#0c0c1e" stroke="#22d3ee" stroke-width="1.5" filter="url(#dsf\x27+oi+\x27)"/>\x27+
|
|
4690
|
+
// Dog-ear fold
|
|
4691
|
+
\x27<path d="M16 1 L16 6 L21 6" fill="none" stroke="#22d3ee" stroke-width="1.2"/>\x27+
|
|
4692
|
+
\x27<path d="M16 1 L21 6 L16 6 Z" fill="#0f0f28"/>\x27+
|
|
4693
|
+
// Header bar (colored)
|
|
4694
|
+
\x27<rect x="2" y="2" width="13" height="3" rx="1" fill="#22d3ee22"/>\x27+
|
|
4695
|
+
// Text lines
|
|
4696
|
+
\x27<line x1="3" y1="9" x2="19" y2="9" stroke="#22d3ee" stroke-width="1" opacity=".8" stroke-linecap="round"/>\x27+
|
|
4697
|
+
\x27<line x1="3" y1="12" x2="17" y2="12" stroke="#22d3ee" stroke-width="1" opacity=".6" stroke-linecap="round"/>\x27+
|
|
4698
|
+
\x27<line x1="3" y1="15" x2="19" y2="15" stroke="#22d3ee" stroke-width="1" opacity=".5" stroke-linecap="round"/>\x27+
|
|
4699
|
+
\x27<line x1="3" y1="18" x2="14" y2="18" stroke="#22d3ee" stroke-width="1" opacity=".4" stroke-linecap="round"/>\x27+
|
|
4700
|
+
// Chart bar (mini)
|
|
4701
|
+
\x27<rect x="3" y="21" width="3" height="5" rx=".5" fill="#22d3ee44"/>\x27+
|
|
4702
|
+
\x27<rect x="7" y="19" width="3" height="7" rx=".5" fill="#22d3ee66"/>\x27+
|
|
4703
|
+
\x27<rect x="11" y="22" width="3" height="4" rx=".5" fill="#22d3ee44"/>\x27+
|
|
4704
|
+
\x27<rect x="15" y="20" width="3" height="6" rx=".5" fill="#22d3ee55"/>\x27+
|
|
4705
|
+
\x27</svg></div>\x27;
|
|
4706
|
+
});
|
|
4707
|
+
flyingDocHtml += \x27</div>\x27;
|
|
4708
|
+
}
|
|
4709
|
+
|
|
4710
|
+
// ── Assemble agent desks row ──────────────────────────────────────
|
|
4711
|
+
var desksHtml = proposals.map(function(prop) {
|
|
4712
|
+
var lbl = prop.label || prop.agent;
|
|
4713
|
+
return buildChar(prop, !!parlDoneAgents[lbl], lbl===activeLabel, false);
|
|
4714
|
+
}).join(\x27\x27);
|
|
4715
|
+
|
|
4716
|
+
var convergenceHtml = convergence != null
|
|
4717
|
+
? (\x27<div class="prl-conv-wrap"><div class="prl-conv-bar-outer"><div class="prl-conv-bar-inner" style="width:\x27+Math.min(convergence,100)+\x27%"></div></div>\x27+
|
|
4718
|
+
\x27<div class="prl-conv-text"><strong>\u2714 Convergenza \x27+convergence+\x27%</strong> — le analisi condividono il \x27+convergence+\x27% dei concetti chiave (Jaccard). HERALD ha sintetizzato il consenso finale.</div></div>\x27)
|
|
4719
|
+
: (\x27<div class="prl-progress"><div class="prl-progress__bar" style="width:\x27+progressPct+\x27%"></div></div>\x27);
|
|
4720
|
+
|
|
4721
|
+
pb.innerHTML =
|
|
4722
|
+
\x27<div class="prl-wrap">\x27+
|
|
4723
|
+
\x27<div class="prl-header"><span class="prl-phase-chip" style="--pc:\x27+phaseColor+\x27">\x27+phaseLabel+\x27</span></div>\x27+
|
|
4724
|
+
\x27<div class="prl-office">\x27+
|
|
4725
|
+
\x27<div class="prl-office-floor"></div>\x27+
|
|
4726
|
+
\x27<div class="prl-desks-row">\x27+desksHtml+\x27</div>\x27+
|
|
4727
|
+
masterHtml+
|
|
4728
|
+
flyingDocHtml+
|
|
4729
|
+
\x27</div>\x27+
|
|
4730
|
+
convergenceHtml+
|
|
4731
|
+
\x27</div>\x27;
|
|
4732
|
+
|
|
4733
|
+
// Force Safari compositing repaint
|
|
4734
|
+
void pb.offsetHeight;
|
|
4735
|
+
pb.style.transform = \x27translateZ(0)\x27;
|
|
4736
|
+
requestAnimationFrame(function(){
|
|
4737
|
+
pb.style.opacity = \x270.99\x27;
|
|
4738
|
+
requestAnimationFrame(function(){ pb.style.opacity = \x271\x27; });
|
|
4739
|
+
});
|
|
4074
4740
|
}
|
|
4075
4741
|
|
|
4076
|
-
// Show initial R1 block
|
|
4742
|
+
// Show initial R1 block and scroll into view
|
|
4077
4743
|
renderParlBlock(\x27r1\x27, null, null);
|
|
4744
|
+
var pb0 = document.getElementById(\x27studioParliamentBlock\x27);
|
|
4745
|
+
if (pb0) {
|
|
4746
|
+
setTimeout(function(){
|
|
4747
|
+
pb0.scrollIntoView({behavior:\x27smooth\x27, block:\x27start\x27});
|
|
4748
|
+
}, 80);
|
|
4749
|
+
}
|
|
4078
4750
|
|
|
4079
4751
|
var deliberateBody = JSON.stringify({task: task, proposals: proposals, language: document.getElementById(\x27langSelect\x27) ? document.getElementById(\x27langSelect\x27).value : \x27it\x27});
|
|
4080
4752
|
try {
|
|
@@ -4186,6 +4858,43 @@ async function runStudio() {
|
|
|
4186
4858
|
renderStudioResult();
|
|
4187
4859
|
studioLog('Studio', '🎉', t('workflow_complete'), 'system');
|
|
4188
4860
|
|
|
4861
|
+
// If parliament was NOT active and a canvas was generated, auto-open it now
|
|
4862
|
+
// (parliament-off: old auto-open behaviour is fine since there's nothing else to watch).
|
|
4863
|
+
// If parliament WAS active, keep the canvas closed so the user sees the deliberation block.
|
|
4864
|
+
var parlWasActive = parliamentActive;
|
|
4865
|
+
if (!parlWasActive && studioState.canvas) {
|
|
4866
|
+
var cpFin = document.getElementById('canvasPanel');
|
|
4867
|
+
var cfFin = document.getElementById('canvasFrame');
|
|
4868
|
+
if (cpFin && cfFin) {
|
|
4869
|
+
cfFin.srcdoc = studioState.canvas;
|
|
4870
|
+
cpFin.classList.add('open');
|
|
4871
|
+
var ctFin = document.getElementById('canvasTitle');
|
|
4872
|
+
if (ctFin) ctFin.textContent = 'Studio Report';
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4876
|
+
// Scroll to parliament block first (user sees deliberation), then to the result.
|
|
4877
|
+
setTimeout(function() {
|
|
4878
|
+
var parlFinal = document.getElementById('studioParliamentBlock');
|
|
4879
|
+
var resEl = document.getElementById('studioResult');
|
|
4880
|
+
var scrollEl = parlFinal && parlFinal.closest ? parlFinal.closest('.content') : null;
|
|
4881
|
+
function doScroll(el) {
|
|
4882
|
+
if (!el) return;
|
|
4883
|
+
if (scrollEl) {
|
|
4884
|
+
var top = el.offsetTop - 80;
|
|
4885
|
+
scrollEl.scrollTo({top: top, behavior: 'smooth'});
|
|
4886
|
+
} else {
|
|
4887
|
+
el.scrollIntoView({behavior: 'smooth', block: 'start'});
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
if (parlFinal && parlFinal.style.display !== 'none' && parlFinal.innerHTML) {
|
|
4891
|
+
doScroll(parlFinal);
|
|
4892
|
+
setTimeout(function(){ doScroll(resEl); }, 2200);
|
|
4893
|
+
} else if (resEl) {
|
|
4894
|
+
doScroll(resEl);
|
|
4895
|
+
}
|
|
4896
|
+
}, 200);
|
|
4897
|
+
|
|
4189
4898
|
// Save session to localStorage for reuse in Chat
|
|
4190
4899
|
saveStudioSession(task, studioState.nodes, studioState.log, context);
|
|
4191
4900
|
renderStudioSessionsBar();
|
|
@@ -4317,9 +5026,15 @@ function importStudioToChat(idx) {
|
|
|
4317
5026
|
function deleteStudioSession(idx) {
|
|
4318
5027
|
try {
|
|
4319
5028
|
var sessions = JSON.parse(localStorage.getItem('nha_studio_sessions') || '[]');
|
|
5029
|
+
var s = sessions[idx]; if (!s) return;
|
|
5030
|
+
var label = s.task ? s.task.slice(0, 60) : 'questa sessione';
|
|
5031
|
+
if (!confirm('Eliminare "' + label + '"?')) return;
|
|
5032
|
+
// If the deleted session is the one currently displayed, clear the view
|
|
5033
|
+
var isCurrentlyOpen = (studioState.task === s.task && studioState.nodes.length > 0 && !studioState.running);
|
|
4320
5034
|
sessions.splice(idx, 1);
|
|
4321
5035
|
localStorage.setItem('nha_studio_sessions', JSON.stringify(sessions));
|
|
4322
5036
|
renderStudioSessionsBar();
|
|
5037
|
+
if (isCurrentlyOpen) { studioReset(); }
|
|
4323
5038
|
} catch(e) {}
|
|
4324
5039
|
}
|
|
4325
5040
|
|
|
@@ -4347,6 +5062,8 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
4347
5062
|
return new Promise(function(resolve) {
|
|
4348
5063
|
var output = '';
|
|
4349
5064
|
var canvasHtml = null;
|
|
5065
|
+
var stepTokensIn = 0;
|
|
5066
|
+
var stepTokensOut = 0;
|
|
4350
5067
|
// Inject attachment into first step only — pass PDF/image as dedicated fields,
|
|
4351
5068
|
// NOT as raw base64 in context (would cause 100k+ token overflow for any real PDF).
|
|
4352
5069
|
// Cap accumulated context to ~40KB to avoid token overflow — keep the most recent content
|
|
@@ -4384,14 +5101,14 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
4384
5101
|
var buf = '';
|
|
4385
5102
|
function pump() {
|
|
4386
5103
|
reader.read().then(function(chunk) {
|
|
4387
|
-
if (chunk.done) { resolve({output: output || '(no output)', canvas: canvasHtml}); return; }
|
|
5104
|
+
if (chunk.done) { resolve({output: output || '(no output)', canvas: canvasHtml, tokensIn: stepTokensIn, tokensOut: stepTokensOut}); return; }
|
|
4388
5105
|
buf += decoder.decode(chunk.value, {stream: true});
|
|
4389
5106
|
var lines = buf.split('\\n');
|
|
4390
5107
|
buf = lines.pop();
|
|
4391
5108
|
lines.forEach(function(line) {
|
|
4392
5109
|
if (!line.startsWith('data: ')) return;
|
|
4393
5110
|
var d = line.slice(6).trim();
|
|
4394
|
-
if (d === '[DONE]') { resolve({output: output || '(no output)', canvas: canvasHtml}); return; }
|
|
5111
|
+
if (d === '[DONE]') { resolve({output: output || '(no output)', canvas: canvasHtml, tokensIn: stepTokensIn, tokensOut: stepTokensOut}); return; }
|
|
4395
5112
|
try {
|
|
4396
5113
|
var ev = JSON.parse(d);
|
|
4397
5114
|
if (ev.token) {
|
|
@@ -4425,21 +5142,39 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
4425
5142
|
if (ev.canvas) {
|
|
4426
5143
|
canvasHtml = ev.canvas;
|
|
4427
5144
|
studioState.canvas = ev.canvas;
|
|
4428
|
-
//
|
|
4429
|
-
|
|
4430
|
-
var
|
|
4431
|
-
if (
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
5145
|
+
// Store in allCanvasData so openCanvasPanel() finds it reliably
|
|
5146
|
+
// (studioState.canvas can be stale if session is restored from localStorage)
|
|
5147
|
+
var _cid = activeConvId || \x27_default\x27;
|
|
5148
|
+
if (!allCanvasData[_cid]) allCanvasData[_cid] = {canvases:[], browsers:[]};
|
|
5149
|
+
// Replace last studio canvas if it exists, otherwise push
|
|
5150
|
+
var _cd = allCanvasData[_cid];
|
|
5151
|
+
var _existIdx = _cd.canvases.findIndex(function(c){ return c.title === \x27Studio Report\x27; });
|
|
5152
|
+
var _citem = {html: ev.canvas, title: \x27Studio Report\x27, ts: new Date().toLocaleTimeString()};
|
|
5153
|
+
if (_existIdx >= 0) { _cd.canvases[_existIdx] = _citem; canvasIdx = _existIdx; }
|
|
5154
|
+
else { _cd.canvases.push(_citem); canvasIdx = _cd.canvases.length - 1; }
|
|
5155
|
+
canvasMode = \x27canvas\x27;
|
|
5156
|
+
// Pre-load the canvas HTML into the frame tracker — do NOT open the panel
|
|
5157
|
+
// (opening mid-run would hide the parliament animation block).
|
|
5158
|
+
_canvasFrameLoadedHtml = ev.canvas;
|
|
5159
|
+
var cf2 = document.getElementById(\x27canvasFrame\x27);
|
|
5160
|
+
if (cf2) cf2.srcdoc = canvasHtml;
|
|
5161
|
+
var scb = document.getElementById(\x27studioCanvasBtn\x27);
|
|
5162
|
+
if (scb) {
|
|
5163
|
+
scb.style.display = \x27\x27;
|
|
5164
|
+
scb.style.background = \x27var(--greendim)\x27;
|
|
5165
|
+
scb.style.borderColor = \x27var(--green3)\x27;
|
|
5166
|
+
scb.style.color = \x27var(--green)\x27;
|
|
4436
5167
|
}
|
|
4437
|
-
var scb = document.getElementById('studioCanvasBtn');
|
|
4438
|
-
if (scb) scb.style.display = \x27\x27;
|
|
4439
5168
|
}
|
|
4440
|
-
if (ev.usage) {
|
|
4441
|
-
|
|
4442
|
-
|
|
5169
|
+
if (ev.usage) {
|
|
5170
|
+
var uIn = ev.usage.input||0; var uOut = ev.usage.output||0;
|
|
5171
|
+
stepTokensIn += uIn; stepTokensOut += uOut;
|
|
5172
|
+
studioAddTokens(uIn, uOut);
|
|
5173
|
+
} else if (ev.token && !isStatus) {
|
|
5174
|
+
var est = Math.ceil(ev.token.length/4);
|
|
5175
|
+
stepTokensOut += est; studioTokens.out += est; studioUpdateTokenBar();
|
|
5176
|
+
}
|
|
5177
|
+
if (ev.done) { resolve({output: output || '(no output)', canvas: canvasHtml, tokensIn: stepTokensIn, tokensOut: stepTokensOut}); return; }
|
|
4443
5178
|
if (ev.error) { resolve({error: ev.error}); return; }
|
|
4444
5179
|
} catch(e) {}
|
|
4445
5180
|
});
|
|
@@ -4593,8 +5328,8 @@ function renderStudio(el) {
|
|
|
4593
5328
|
'<button id="studioCanvasBtn" onclick="openCanvasPanel()" title="' + t('canvas_open') + '" style="font-size:12px;padding:5px 14px;background:none;border:1px solid var(--border);border-radius:6px;color:var(--dim);cursor:pointer;font-weight:700;transition:all .2s">■ Canvas</button>' +
|
|
4594
5329
|
'</div>' +
|
|
4595
5330
|
'<div class="studio-canvas" id="studioNodes"></div>' +
|
|
4596
|
-
'<div id="studioParliamentBlock" style="display:none"></div>' +
|
|
4597
5331
|
'<div class="studio-log" id="studioLog" style="display:none"></div>' +
|
|
5332
|
+
'<div id="studioParliamentBlock" style="display:none;margin-bottom:12px"></div>' +
|
|
4598
5333
|
'<div id="studioResult"></div>' +
|
|
4599
5334
|
'<div id="studioSessionsBar" style="margin-top:16px;display:none"></div>' +
|
|
4600
5335
|
'</div>' +
|
|
@@ -4777,8 +5512,7 @@ async function runManualWorkflow() {
|
|
|
4777
5512
|
studioLog(s.label, s.icon, realOut2 || (stepResult.canvas ? '[Canvas report generated]' : '(done)'), 'agent', true);
|
|
4778
5513
|
if (stepResult.canvas) {
|
|
4779
5514
|
var cf = document.getElementById('canvasFrame');
|
|
4780
|
-
|
|
4781
|
-
if (cf && cp) { cf.srcdoc = stepResult.canvas; cp.classList.add('open'); var ct3=document.getElementById('canvasTitle'); if(ct3) ct3.textContent='Studio Report'; }
|
|
5515
|
+
if (cf) { cf.srcdoc = stepResult.canvas; }
|
|
4782
5516
|
}
|
|
4783
5517
|
context = realOut2 || stepResult.canvas || context;
|
|
4784
5518
|
}
|
|
@@ -5139,24 +5873,74 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
5139
5873
|
.studio-arrow{display:flex;align-items:center;color:var(--border2);font-size:18px;padding:0 8px;flex-shrink:0;margin-bottom:30px;transition:color .4s}
|
|
5140
5874
|
.studio-arrow--active{color:var(--green3);animation:stFlow .5s ease-in-out infinite alternate}
|
|
5141
5875
|
.studio-arrow--done{color:#22c55e}
|
|
5142
|
-
|
|
5143
|
-
.studio-
|
|
5144
|
-
.studio-
|
|
5145
|
-
.studio-
|
|
5146
|
-
.studio-
|
|
5147
|
-
.studio-
|
|
5148
|
-
.studio-
|
|
5149
|
-
|
|
5150
|
-
.
|
|
5151
|
-
.
|
|
5152
|
-
|
|
5153
|
-
.
|
|
5154
|
-
.
|
|
5155
|
-
|
|
5156
|
-
.
|
|
5157
|
-
|
|
5158
|
-
.
|
|
5159
|
-
|
|
5876
|
+
/* ── Workflow node animated character ── */
|
|
5877
|
+
.studio-nodes{align-items:flex-end!important;min-height:190px!important}
|
|
5878
|
+
.studio-node--active,.studio-node--done{min-width:118px!important;max-width:140px!important;gap:4px!important}
|
|
5879
|
+
.studio-node__char{display:flex;align-items:center;justify-content:center;width:100%}
|
|
5880
|
+
.studio-node__bubble{font-size:9px;padding:2px 7px;border-radius:20px;white-space:nowrap;text-align:center;margin:0 auto}
|
|
5881
|
+
.studio-node__label--char{font-size:10px;text-align:center;max-width:130px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:700;letter-spacing:.3px;color:#6366f1}
|
|
5882
|
+
.studio-node--done .studio-node__label--char{color:#22c55e}
|
|
5883
|
+
/* ── Parliament Office Cartoon ── */
|
|
5884
|
+
.prl-wrap{background:#07070f;border:1.5px solid #6366f1;border-radius:14px;padding:14px 16px 12px;margin-bottom:16px;animation:stNodeIn .35s ease forwards;overflow:hidden}
|
|
5885
|
+
#studioParliamentBlock[style*="sticky"] .prl-wrap{animation:stNodeIn .35s ease forwards,parlPulse 2.2s ease-in-out infinite}
|
|
5886
|
+
@keyframes parlPulse{0%,100%{border-color:#6366f1;box-shadow:none}50%{border-color:#818cf8;box-shadow:0 0 20px rgba(99,102,241,.3)}}
|
|
5887
|
+
.prl-header{display:flex;align-items:center;margin-bottom:10px}
|
|
5888
|
+
.prl-phase-chip{font-size:10px;font-weight:800;font-family:var(--mono);letter-spacing:.3px;color:var(--pc,#6366f1);background:rgba(99,102,241,.12);border:1px solid rgba(99,102,241,.35);border-radius:20px;padding:3px 12px;display:inline-block}
|
|
5889
|
+
/* Office scene container */
|
|
5890
|
+
.prl-office{position:relative;min-height:130px;display:flex;align-items:flex-end;padding-bottom:8px;overflow:hidden}
|
|
5891
|
+
/* Floor — gradient wood planks effect */
|
|
5892
|
+
.prl-office-floor{position:absolute;bottom:0;left:0;right:0;height:8px;background:linear-gradient(90deg,#1c1a2e,#26243e,#1e1c32,#28263c,#1c1a2e);border-radius:4px;box-shadow:0 -1px 0 rgba(255,255,255,.05)}
|
|
5893
|
+
/* Desks row */
|
|
5894
|
+
.prl-desks-row{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;position:relative;z-index:2;padding-bottom:8px}
|
|
5895
|
+
/* Individual desk card */
|
|
5896
|
+
.prl-desk{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 6px 4px;border-radius:12px;background:#0a0a18;border:1.5px solid #252535;transition:border-color .4s,background .4s,box-shadow .4s;position:relative;min-width:80px}
|
|
5897
|
+
.prl-desk--active{background:#0c0c20;border-color:var(--dc,#6366f1);box-shadow:0 0 20px rgba(99,102,241,.2),0 0 40px rgba(99,102,241,.08)}
|
|
5898
|
+
.prl-desk--done{border-color:#1e3a1e;background:#0a150a}
|
|
5899
|
+
/* Action bubble above character */
|
|
5900
|
+
.prl-action-bubble{font-size:9px;color:#6b7280;font-family:var(--mono);padding:2px 6px;border-radius:8px;background:#111;border:1px solid #2a2a38;min-height:16px;text-align:center;transition:all .3s}
|
|
5901
|
+
.prl-action-bubble--active{color:var(--dc,#6366f1);border-color:var(--dc,#6366f1);background:rgba(99,102,241,.08);animation:parlBubblePop .4s ease}
|
|
5902
|
+
@keyframes parlBubblePop{0%{transform:scale(.85);opacity:.5}100%{transform:scale(1);opacity:1}}
|
|
5903
|
+
/* Character SVG animations */
|
|
5904
|
+
@keyframes parlArmType{0%,100%{transform:rotate(-8deg) translateY(0)}50%{transform:rotate(8deg) translateY(2px)}}
|
|
5905
|
+
@keyframes parlHeadNod{0%,100%{transform:translateY(0) rotate(0deg)}50%{transform:translateY(2px) rotate(4deg)}}
|
|
5906
|
+
@keyframes parlDocBob{0%,100%{transform:translateY(0) rotate(-5deg)}50%{transform:translateY(-3px) rotate(5deg)}}
|
|
5907
|
+
.prl-arm{transform-origin:50% 100%;animation:parlArmType .55s ease-in-out infinite}
|
|
5908
|
+
.prl-head{transform-origin:50% 100%;animation:parlHeadNod .8s ease-in-out infinite}
|
|
5909
|
+
.prl-doc-hold{transform-origin:center center;animation:parlDocBob .7s ease-in-out infinite}
|
|
5910
|
+
/* Agent name label */
|
|
5911
|
+
.prl-desk-name{font-size:9px;font-family:var(--mono);font-weight:700;letter-spacing:.3px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:68px}
|
|
5912
|
+
/* MASTER ORCHESTRATOR */
|
|
5913
|
+
.prl-master{position:absolute;bottom:8px;right:8px;display:flex;flex-direction:column;align-items:center;gap:1px;z-index:3;transition:right .8s cubic-bezier(.4,0,.2,1)}
|
|
5914
|
+
.prl-master-label{font-size:8px;font-family:var(--mono);font-weight:700;letter-spacing:.4px;text-align:center;text-shadow:0 0 8px currentColor}
|
|
5915
|
+
/* Walking animation — smooth left-right patrol */
|
|
5916
|
+
@keyframes parlMasterWalk{0%{right:8px}25%{right:calc(100% - 70px)}50%{right:calc(100% - 70px)}75%{right:8px}100%{right:8px}}
|
|
5917
|
+
/* Leg swing for natural walking gait */
|
|
5918
|
+
@keyframes parlMasterLegL{0%,100%{transform:rotate(0deg)}25%{transform:rotate(25deg)}75%{transform:rotate(-20deg)}}
|
|
5919
|
+
@keyframes parlMasterLegR{0%,100%{transform:rotate(0deg)}25%{transform:rotate(-20deg)}75%{transform:rotate(25deg)}}
|
|
5920
|
+
/* Arm swing (opposite to legs) */
|
|
5921
|
+
@keyframes parlMasterArmSwing{0%,100%{transform:rotate(0deg)}25%{transform:rotate(-18deg)}75%{transform:rotate(18deg)}}
|
|
5922
|
+
/* Head bob while walking */
|
|
5923
|
+
@keyframes parlMasterBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}
|
|
5924
|
+
.prl-master-walk{animation:parlMasterWalk 4s ease-in-out infinite}
|
|
5925
|
+
.prl-master-walk .prl-master-leg-l{transform-origin:50% 0;animation:parlMasterLegL .5s ease-in-out infinite}
|
|
5926
|
+
.prl-master-walk .prl-master-leg-r{transform-origin:50% 0;animation:parlMasterLegR .5s ease-in-out infinite}
|
|
5927
|
+
.prl-master-walk .prl-master-arm-l{transform-origin:50% 0;animation:parlMasterArmSwing .5s ease-in-out infinite}
|
|
5928
|
+
.prl-master-walk .prl-master-arm-r{transform-origin:50% 0;animation:parlMasterArmSwing .5s ease-in-out infinite reverse}
|
|
5929
|
+
/* R2 supervise: gentle idle bob at active desk */
|
|
5930
|
+
@keyframes parlMasterSupervise{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
|
|
5931
|
+
.prl-master-supervise{animation:parlMasterSupervise 2s ease-in-out infinite}
|
|
5932
|
+
/* Flying documents: smooth parabolic arc between agents */
|
|
5933
|
+
.prl-fly-container{position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;overflow:hidden;z-index:4}
|
|
5934
|
+
@keyframes parlFlyDoc{0%{transform:translate(0,70px) scale(.6) rotate(-20deg);opacity:0}15%{opacity:1;transform:translate(8%,20px) scale(.9) rotate(-5deg)}40%{transform:translate(30%,-15px) scale(1.1) rotate(3deg);opacity:1}65%{transform:translate(60%,5px) scale(.95) rotate(8deg);opacity:.9}85%{opacity:.6}100%{transform:translate(90%,60px) scale(.6) rotate(15deg);opacity:0}}
|
|
5935
|
+
.prl-fly-doc{position:absolute;top:15%;left:3%;animation:parlFlyDoc 1.6s cubic-bezier(.4,0,.2,1) infinite;opacity:0;filter:drop-shadow(0 4px 8px rgba(34,211,238,.4))}
|
|
5936
|
+
/* Progress bar (during deliberation) */
|
|
5937
|
+
.prl-progress{height:3px;background:#1a1a2e;border-radius:4px;margin-top:10px;overflow:hidden}
|
|
5938
|
+
.prl-progress__bar{height:100%;background:linear-gradient(90deg,#6366f1,#22d3ee);border-radius:4px;transition:width .5s ease}
|
|
5939
|
+
/* Convergence result */
|
|
5940
|
+
.prl-conv-wrap{margin-top:10px;padding:8px 12px;background:rgba(34,197,94,.06);border:1px solid rgba(34,197,94,.22);border-radius:8px}
|
|
5941
|
+
.prl-conv-bar-outer{height:4px;background:#1a2e1a;border-radius:4px;overflow:hidden;margin-bottom:6px}
|
|
5942
|
+
.prl-conv-bar-inner{height:100%;background:linear-gradient(90deg,#22c55e,#4ade80);border-radius:4px;transition:width .8s ease}
|
|
5943
|
+
.prl-conv-text{font-size:9px;color:#86efac;line-height:1.55}
|
|
5160
5944
|
.studio-log{background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:16px;max-height:380px;overflow-y:auto;font-size:11.5px;line-height:1.65}
|
|
5161
5945
|
.studio-log-entry{margin-bottom:12px;padding:10px 12px;border-radius:8px;background:var(--bg3);border:1px solid var(--border)}
|
|
5162
5946
|
.studio-log-entry:last-child{margin-bottom:0}
|
|
@@ -5185,6 +5969,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
5185
5969
|
.md-body .md-h1{font-size:18px;font-weight:700;color:var(--bright);margin:18px 0 10px;line-height:1.3}
|
|
5186
5970
|
.md-body .md-h2{font-size:15px;font-weight:600;color:var(--bright);margin:16px 0 8px;line-height:1.3}
|
|
5187
5971
|
.md-body .md-h3{font-size:13.5px;font-weight:600;color:var(--green);margin:12px 0 6px;line-height:1.3}
|
|
5972
|
+
.md-body .md-h4{font-size:12px;font-weight:600;color:var(--dim);margin:10px 0 4px;text-transform:uppercase;letter-spacing:.5px;line-height:1.3}
|
|
5188
5973
|
.md-body .md-ul,.md-body .md-ol{margin:6px 0 12px 0;padding-left:22px}
|
|
5189
5974
|
.md-body .md-ul li,.md-body .md-ol li{margin-bottom:5px;color:var(--text);font-size:13.5px;line-height:1.65}
|
|
5190
5975
|
.md-body .md-ul{list-style:disc}
|