nothumanallowed 13.5.7 → 13.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/ui.mjs +5 -2
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +217 -105
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.9",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -3790,8 +3790,11 @@ ${writtenSoFar ? `## REPORT WRITTEN SO FAR (for consistency):\n${writtenSoFar.sl
|
|
|
3790
3790
|
}
|
|
3791
3791
|
|
|
3792
3792
|
// Estimate token usage (aprox: 1 token ≈ 4 chars)
|
|
3793
|
-
|
|
3794
|
-
const
|
|
3793
|
+
// Include context + toolData in input estimate since they're sent in the prompt
|
|
3794
|
+
const contextLen = (context || '').length;
|
|
3795
|
+
const toolDataLen = (toolData || '').length;
|
|
3796
|
+
const inTokens = Math.ceil((sysPrompt.length + userMsg.length + contextLen + toolDataLen) / 4);
|
|
3797
|
+
const outTokens = Math.ceil(fullOutputClean.length / 4);
|
|
3795
3798
|
clearInterval(keepalive);
|
|
3796
3799
|
sendEvent({ usage: { input: inTokens, output: outTokens } });
|
|
3797
3800
|
sendEvent({ done: true });
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.5.
|
|
8
|
+
export const VERSION = '13.5.9';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -3310,6 +3310,7 @@ var studioState = {
|
|
|
3310
3310
|
var studioAbortController = null;
|
|
3311
3311
|
var parlActiveAgent = null; // active agent label during parliament streaming
|
|
3312
3312
|
var parlDoneAgents = {}; // set of completed agent labels during parliament
|
|
3313
|
+
var _parlPersistHtml = null; // persists parliament block HTML across tab navigations
|
|
3313
3314
|
|
|
3314
3315
|
function stopStudio() {
|
|
3315
3316
|
if (!studioState.running) return;
|
|
@@ -3624,72 +3625,159 @@ function officeRoomDecor() {
|
|
|
3624
3625
|
// Projects grid positions onto an isometric plane.
|
|
3625
3626
|
// iso(col, row) → {x, y} pixel coordinates in the scene container.
|
|
3626
3627
|
// Characters are positioned with position:absolute, scale by row for depth.
|
|
3627
|
-
var ISO_TILE_W =
|
|
3628
|
-
var ISO_TILE_H =
|
|
3629
|
-
var ISO_ORIGIN_X =
|
|
3630
|
-
var ISO_ORIGIN_Y =
|
|
3628
|
+
var ISO_TILE_W = 80;
|
|
3629
|
+
var ISO_TILE_H = 40;
|
|
3630
|
+
var ISO_ORIGIN_X = 500;
|
|
3631
|
+
var ISO_ORIGIN_Y = 80;
|
|
3631
3632
|
function isoProject(col, row) {
|
|
3632
3633
|
return {
|
|
3633
3634
|
x: ISO_ORIGIN_X + (col - row) * (ISO_TILE_W / 2),
|
|
3634
3635
|
y: ISO_ORIGIN_Y + (col + row) * (ISO_TILE_H / 2)
|
|
3635
3636
|
};
|
|
3636
3637
|
}
|
|
3637
|
-
|
|
3638
|
+
|
|
3639
|
+
// Full bright office scene — wide SVG background (1000x560)
|
|
3638
3640
|
function isoFloorSvg(cols, rows) {
|
|
3639
|
-
var
|
|
3640
|
-
var out = \x27<svg viewBox="0 0 \x27+
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
out += \x27<
|
|
3644
|
-
|
|
3645
|
-
out += \x27<
|
|
3646
|
-
out += \x27<
|
|
3647
|
-
|
|
3648
|
-
out += \x27
|
|
3649
|
-
|
|
3650
|
-
//
|
|
3651
|
-
|
|
3652
|
-
out += \x27<polygon points="
|
|
3653
|
-
//
|
|
3654
|
-
out += \x27<polygon points="
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
out += \x27<
|
|
3658
|
-
out += \x27<
|
|
3659
|
-
|
|
3660
|
-
//
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3641
|
+
var W = 1000; var H = 560;
|
|
3642
|
+
var out = \x27<svg viewBox="0 0 \x27+W+\x27 \x27+H+\x27" width="\x27+W+\x27" height="\x27+H+\x27" xmlns="http://www.w3.org/2000/svg" style="position:absolute;top:0;left:0;pointer-events:none;z-index:0">\x27;
|
|
3643
|
+
|
|
3644
|
+
// ── DEFS ────────────────────────────────────────────────────────────
|
|
3645
|
+
out += \x27<defs>\x27;
|
|
3646
|
+
out += \x27<filter id="bGlow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>\x27;
|
|
3647
|
+
out += \x27<radialGradient id="lampGlow" cx="50%" cy="20%" r="70%"><stop offset="0%" stop-color="rgba(255,248,200,.28)"/><stop offset="100%" stop-color="rgba(255,248,200,0)"/></radialGradient>\x27;
|
|
3648
|
+
out += \x27<linearGradient id="wallL" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#f8f6f0"/><stop offset="100%" stop-color="#ede9e0"/></linearGradient>\x27;
|
|
3649
|
+
out += \x27<linearGradient id="wallR" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#e8e4dc"/><stop offset="100%" stop-color="#ddd8ce"/></linearGradient>\x27;
|
|
3650
|
+
out += \x27</defs>\x27;
|
|
3651
|
+
|
|
3652
|
+
// ── WALLS ────────────────────────────────────────────────────────────
|
|
3653
|
+
// Left wall (back-left)
|
|
3654
|
+
out += \x27<polygon points="0,200 500,40 500,280 0,440" fill="url(#wallL)"/>\x27;
|
|
3655
|
+
// Right wall (back-right)
|
|
3656
|
+
out += \x27<polygon points="500,40 1000,200 1000,440 500,280" fill="url(#wallR)"/>\x27;
|
|
3657
|
+
// Wall top ridge
|
|
3658
|
+
out += \x27<line x1="0" y1="200" x2="500" y2="40" stroke="#c8c0b0" stroke-width="2"/>\x27;
|
|
3659
|
+
out += \x27<line x1="500" y1="40" x2="1000" y2="200" stroke="#bbb0a0" stroke-width="2"/>\x27;
|
|
3660
|
+
out += \x27<line x1="500" y1="40" x2="500" y2="280" stroke="#c0b8a8" stroke-width="1.5"/>\x27;
|
|
3661
|
+
|
|
3662
|
+
// ── WINDOWS on left wall — 3 evenly spaced ──────────────────────────
|
|
3663
|
+
// Helper: window at left-wall position (xL,yTop)→(xR,yTop-slope)
|
|
3664
|
+
// Left wall goes from (0,200)→(500,40), slope = -160/500 per x
|
|
3665
|
+
// Window 1
|
|
3666
|
+
out += \x27<polygon points="60,232 160,206 160,272 60,300" fill="#ceeaff" stroke="#90bcd8" stroke-width="1.5"/>\x27;
|
|
3667
|
+
out += \x27<polygon points="60,232 160,206 160,213 60,239" fill="#a8d8f8" opacity=".6"/>\x27;
|
|
3668
|
+
out += \x27<line x1="60" y1="266" x2="160" y2="239" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3669
|
+
out += \x27<line x1="110" y1="219" x2="110" y2="286" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3670
|
+
out += \x27<polygon points="60,232 160,206 160,272 60,300" fill="rgba(180,220,255,.1)"/>\x27;
|
|
3671
|
+
// Window 2
|
|
3672
|
+
out += \x27<polygon points="200,178 300,152 300,218 200,246" fill="#ceeaff" stroke="#90bcd8" stroke-width="1.5"/>\x27;
|
|
3673
|
+
out += \x27<polygon points="200,178 300,152 300,159 200,185" fill="#a8d8f8" opacity=".6"/>\x27;
|
|
3674
|
+
out += \x27<line x1="200" y1="212" x2="300" y2="185" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3675
|
+
out += \x27<line x1="250" y1="165" x2="250" y2="232" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3676
|
+
out += \x27<polygon points="200,178 300,152 300,218 200,246" fill="rgba(180,220,255,.1)"/>\x27;
|
|
3677
|
+
// Window 3
|
|
3678
|
+
out += \x27<polygon points="340,126 440,100 440,166 340,192" fill="#ceeaff" stroke="#90bcd8" stroke-width="1.5"/>\x27;
|
|
3679
|
+
out += \x27<polygon points="340,126 440,100 440,107 340,133" fill="#a8d8f8" opacity=".6"/>\x27;
|
|
3680
|
+
out += \x27<line x1="340" y1="159" x2="440" y2="133" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3681
|
+
out += \x27<line x1="390" y1="113" x2="390" y2="179" stroke="#90bcd8" stroke-width="1"/>\x27;
|
|
3682
|
+
out += \x27<polygon points="340,126 440,100 440,166 340,192" fill="rgba(180,220,255,.1)"/>\x27;
|
|
3683
|
+
// Sun shafts
|
|
3684
|
+
out += \x27<polygon points="80,300 160,272 190,540 110,560" fill="rgba(255,248,200,.07)"/>\x27;
|
|
3685
|
+
out += \x27<polygon points="215,246 300,218 320,540 235,560" fill="rgba(255,248,200,.06)"/>\x27;
|
|
3686
|
+
out += \x27<polygon points="352,192 440,166 455,540 367,560" fill="rgba(255,248,200,.05)"/>\x27;
|
|
3687
|
+
|
|
3688
|
+
// ── PAINTING on left wall (between W2 and W3) ────────────────────────
|
|
3689
|
+
out += \x27<polygon points="463,112 490,104 490,132 463,140" fill="#e8e2d8" stroke="#c0b090" stroke-width="1.5"/>\x27;
|
|
3690
|
+
out += \x27<polygon points="466,114 488,107 488,130 466,137" fill="#5080b0"/>\x27;
|
|
3691
|
+
out += \x27<polygon points="469,116 488,110 488,120 469,123" fill="#7aa0c8"/>\x27;
|
|
3692
|
+
out += \x27<ellipse cx="478" cy="120" rx="5" ry="4" fill="#fff" opacity=".35"/>\x27;
|
|
3693
|
+
|
|
3694
|
+
// ── PLANT right wall corner ─────────────────────────────────────────
|
|
3695
|
+
out += \x27<ellipse cx="920" cy="360" rx="14" ry="7" fill="#2d5018" opacity=".6"/>\x27;
|
|
3696
|
+
out += \x27<rect x="912" y="348" width="14" height="14" rx="3" fill="#7a4a20"/>\x27;
|
|
3697
|
+
out += \x27<path d="M919 348 C912 328 900 312 905 300 C910 290 919 300 919 348" fill="#3a8020"/>\x27;
|
|
3698
|
+
out += \x27<path d="M919 348 C926 328 938 315 933 302 C928 292 919 303 919 348" fill="#4a9228"/>\x27;
|
|
3699
|
+
out += \x27<path d="M919 342 C908 332 900 322 902 312" stroke="#3a8020" stroke-width="2.5" fill="none" stroke-linecap="round"/>\x27;
|
|
3700
|
+
out += \x27<path d="M919 336 C930 326 938 318 936 306" stroke="#4a9228" stroke-width="2.5" fill="none" stroke-linecap="round"/>\x27;
|
|
3701
|
+
|
|
3702
|
+
// ── CHANDELIER ────────────────────────────────────────────────────────
|
|
3703
|
+
out += \x27<line x1="500" y1="0" x2="500" y2="30" stroke="#aaa" stroke-width="3"/>\x27;
|
|
3704
|
+
out += \x27<ellipse cx="500" cy="30" rx="38" ry="11" fill="#d8c870" stroke="#b8a850" stroke-width="2"/>\x27;
|
|
3705
|
+
out += \x27<ellipse cx="500" cy="36" rx="30" ry="8" fill="#ece090"/>\x27;
|
|
3706
|
+
out += \x27<line x1="478" y1="36" x2="478" y2="46" stroke="#aaa" stroke-width="1.2"/>\x27;
|
|
3707
|
+
out += \x27<line x1="500" y1="36" x2="500" y2="42" stroke="#aaa" stroke-width="1.2"/>\x27;
|
|
3708
|
+
out += \x27<line x1="522" y1="36" x2="522" y2="46" stroke="#aaa" stroke-width="1.2"/>\x27;
|
|
3709
|
+
out += \x27<circle cx="478" cy="50" r="8" fill="#fffbe0" filter="url(#bGlow)"/>\x27;
|
|
3710
|
+
out += \x27<circle cx="500" cy="46" r="10" fill="#fffbe0" filter="url(#bGlow)"/>\x27;
|
|
3711
|
+
out += \x27<circle cx="522" cy="50" r="8" fill="#fffbe0" filter="url(#bGlow)"/>\x27;
|
|
3712
|
+
// Light glow on scene
|
|
3713
|
+
out += \x27<ellipse cx="500" cy="320" rx="360" ry="220" fill="url(#lampGlow)"/>\x27;
|
|
3714
|
+
|
|
3715
|
+
// ── PARQUET FLOOR ──────────────────────────────────────────────────────
|
|
3716
|
+
var plankCols = [\x27#c8924a\x27,\x27#b8823c\x27,\x27#d4a055\x27,\x27#be9248\x27,\x27#a87838\x27];
|
|
3717
|
+
for (var r2 = 0; r2 < rows; r2++) {
|
|
3718
|
+
for (var c2 = 0; c2 < cols; c2++) {
|
|
3719
|
+
var pp = isoProject(c2, r2);
|
|
3720
|
+
var ptx = pp.x; var pty = pp.y + 120;
|
|
3721
|
+
var pFill = plankCols[(r2*3+c2) % plankCols.length];
|
|
3722
|
+
var ppts = ptx+\x27,\x27+pty+\x27 \x27+(ptx+ISO_TILE_W/2)+\x27,\x27+(pty+ISO_TILE_H/2)+\x27 \x27+ptx+\x27,\x27+(pty+ISO_TILE_H)+\x27 \x27+(ptx-ISO_TILE_W/2)+\x27,\x27+(pty+ISO_TILE_H/2);
|
|
3723
|
+
out += \x27<polygon points="\x27+ppts+\x27" fill="\x27+pFill+\x27" stroke="#906820" stroke-width=".8"/>\x27;
|
|
3724
|
+
// long grain line along the plank
|
|
3725
|
+
out += \x27<line x1="\x27+(ptx-ISO_TILE_W/2+5)+\x27" y1="\x27+(pty+ISO_TILE_H/2)+\x27" x2="\x27+(ptx+ISO_TILE_W/2-5)+\x27" y2="\x27+(pty+ISO_TILE_H/2)+\x27" stroke="rgba(0,0,0,.08)" stroke-width=".6"/>\x27;
|
|
3671
3726
|
}
|
|
3672
3727
|
}
|
|
3728
|
+
out += \x27<ellipse cx="500" cy="380" rx="120" ry="40" fill="rgba(255,248,200,.1)"/>\x27;
|
|
3729
|
+
|
|
3673
3730
|
out += \x27</svg>\x27;
|
|
3674
3731
|
return out;
|
|
3675
3732
|
}
|
|
3676
|
-
|
|
3733
|
+
|
|
3734
|
+
// Isometric desk — proper 3D look: flat top surface + two visible faces + monitor + keyboard
|
|
3677
3735
|
function isoDeskSvg(x, y, accentColor) {
|
|
3678
|
-
var ac = accentColor || \x27#
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
\x27<
|
|
3688
|
-
\x27<
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
\x27<
|
|
3692
|
-
|
|
3736
|
+
var ac = accentColor || \x27#888\x27;
|
|
3737
|
+
var isDone = ac === \x27#22c55e\x27;
|
|
3738
|
+
var isActive = ac === \x27#6366f1\x27;
|
|
3739
|
+
var screenFill = isDone ? \x27#0a2010\x27 : (isActive ? \x27#0d102a\x27 : \x27#111828\x27);
|
|
3740
|
+
var screenGlow = isDone ? \x27#22c55e\x27 : (isActive ? \x27#818cf8\x27 : \x27#4466aa\x27);
|
|
3741
|
+
// Desk is 90x56 viewBox — wide enough to look like a real desk
|
|
3742
|
+
return \x27<svg viewBox="0 0 90 56" width="90" height="56" xmlns="http://www.w3.org/2000/svg" style="position:absolute;left:\x27+(x-45)+\x27px;top:\x27+(y+4)+\x27px;z-index:\x27+(Math.round(y))+\x27;pointer-events:none">\x27+
|
|
3743
|
+
// ── DESK LEGS (4 corners, visible as small rectangles) ──
|
|
3744
|
+
\x27<rect x="14" y="42" width="5" height="10" rx="1" fill="#8a5c20"/>\x27+
|
|
3745
|
+
\x27<rect x="56" y="47" width="5" height="8" rx="1" fill="#8a5c20"/>\x27+
|
|
3746
|
+
\x27<rect x="70" y="40" width="5" height="8" rx="1" fill="#7a4e18"/>\x27+
|
|
3747
|
+
\x27<rect x="28" y="36" width="5" height="8" rx="1" fill="#7a4e18"/>\x27+
|
|
3748
|
+
// ── DESK TOP SURFACE (iso diamond) ──
|
|
3749
|
+
\x27<polygon points="45,4 78,21 45,38 12,21" fill="#d4a448" stroke="#b88830" stroke-width="1.2"/>\x27+
|
|
3750
|
+
// wood grain on top
|
|
3751
|
+
\x27<line x1="28" y1="21" x2="62" y2="12" stroke="rgba(0,0,0,.09)" stroke-width=".9"/>\x27+
|
|
3752
|
+
\x27<line x1="24" y1="26" x2="58" y2="17" stroke="rgba(0,0,0,.07)" stroke-width=".9"/>\x27+
|
|
3753
|
+
\x27<line x1="32" y1="17" x2="66" y2="8" stroke="rgba(0,0,0,.06)" stroke-width=".7"/>\x27+
|
|
3754
|
+
// shine
|
|
3755
|
+
\x27<polygon points="45,4 78,21 74,23 45,8 16,23 12,21" fill="rgba(255,255,255,.12)"/>\x27+
|
|
3756
|
+
// ── RIGHT FACE ──
|
|
3757
|
+
\x27<polygon points="78,21 78,36 45,53 45,38" fill="#a87028" stroke="#906018" stroke-width=".8"/>\x27+
|
|
3758
|
+
// ── LEFT FACE ──
|
|
3759
|
+
\x27<polygon points="12,21 45,38 45,53 12,36" fill="#c08838" stroke="#a07028" stroke-width=".8"/>\x27+
|
|
3760
|
+
// ── MONITOR — proper isometric screen ──
|
|
3761
|
+
// Stand
|
|
3762
|
+
\x27<polygon points="54,14 62,18 62,22 54,18" fill="#555" stroke="#333" stroke-width=".6"/>\x27+
|
|
3763
|
+
// Screen back
|
|
3764
|
+
\x27<polygon points="54,4 72,12 72,24 54,16" fill="#2a2a3a" stroke="#222" stroke-width=".8"/>\x27+
|
|
3765
|
+
// Screen front (bezel)
|
|
3766
|
+
\x27<polygon points="55,5 71,13 71,23 55,15" fill="#1a1a28"/>\x27+
|
|
3767
|
+
// Screen content
|
|
3768
|
+
\x27<polygon points="57,7 69,13 69,21 57,15" fill="\x27+screenFill+\x27"/>\x27+
|
|
3769
|
+
\x27<line x1="58" y1="10" x2="68" y2="14" stroke="\x27+screenGlow+\x27" stroke-width="1" opacity=".9"/>\x27+
|
|
3770
|
+
\x27<line x1="58" y1="13" x2="67" y2="17" stroke="\x27+screenGlow+\x27" stroke-width=".9" opacity=".6"/>\x27+
|
|
3771
|
+
\x27<line x1="58" y1="16" x2="66" y2="19" stroke="\x27+screenGlow+\x27" stroke-width=".8" opacity=".4"/>\x27+
|
|
3772
|
+
// ── KEYBOARD ──
|
|
3773
|
+
\x27<polygon points="24,25 44,32 40,36 20,29" fill="#ddd" stroke="#bbb" stroke-width=".6"/>\x27+
|
|
3774
|
+
\x27<line x1="24" y1="28" x2="44" y2="34" stroke="#aaa" stroke-width=".5"/>\x27+
|
|
3775
|
+
\x27<line x1="28" y1="26" x2="28" y2="30" stroke="#aaa" stroke-width=".4"/>\x27+
|
|
3776
|
+
\x27<line x1="33" y1="28" x2="33" y2="32" stroke="#aaa" stroke-width=".4"/>\x27+
|
|
3777
|
+
\x27<line x1="38" y1="30" x2="38" y2="34" stroke="#aaa" stroke-width=".4"/>\x27+
|
|
3778
|
+
// ── PAPERS on desk ──
|
|
3779
|
+
\x27<polygon points="14,24 28,29 25,34 11,29" fill="#f8f6ee" stroke="#ddd" stroke-width=".6" transform="rotate(-6 20 29)"/>\x27+
|
|
3780
|
+
\x27<polygon points="15,23 29,28 26,33 12,28" fill="#fff" stroke="#eee" stroke-width=".5" transform="rotate(-3 21 28)"/>\x27+
|
|
3693
3781
|
\x27</svg>\x27;
|
|
3694
3782
|
}
|
|
3695
3783
|
// Isometric JRPG sprite character — top-down 3/4 perspective, Pokemon-style
|
|
@@ -3835,10 +3923,8 @@ function renderStudioNodes() {
|
|
|
3835
3923
|
|
|
3836
3924
|
// ── ISO scene dimensions ───────────────────────────────────────────────────
|
|
3837
3925
|
var ISO_COLS = Math.max(nodes.length, 1);
|
|
3838
|
-
var SCENE_W =
|
|
3926
|
+
var SCENE_W = 640; var SCENE_H = 420;
|
|
3839
3927
|
|
|
3840
|
-
// Distribute agents along row=1 of the iso grid, spaced evenly
|
|
3841
|
-
// Orchestrator walks on row=0 (further back = smaller)
|
|
3842
3928
|
var agentsHtml = \x27\x27;
|
|
3843
3929
|
nodes.forEach(function(n, idx) {
|
|
3844
3930
|
var isActive = n.status === \x27running\x27;
|
|
@@ -3846,39 +3932,34 @@ function renderStudioNodes() {
|
|
|
3846
3932
|
var isErr = n.status === \x27error\x27;
|
|
3847
3933
|
var lbl = n.label || n.agent;
|
|
3848
3934
|
var pal = agentPalette(lbl);
|
|
3849
|
-
var accentColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#
|
|
3935
|
+
var accentColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#999\x27));
|
|
3850
3936
|
|
|
3851
|
-
//
|
|
3852
|
-
var
|
|
3853
|
-
col = (
|
|
3854
|
-
var row =
|
|
3937
|
+
// Spread agents evenly across front rows
|
|
3938
|
+
var colStep = ISO_COLS > 1 ? 5 / (ISO_COLS - 1) : 2;
|
|
3939
|
+
var col = (ISO_COLS === 1) ? 2.5 : idx * colStep;
|
|
3940
|
+
var row = 3.5;
|
|
3855
3941
|
var pos = isoProject(col, row);
|
|
3856
|
-
var px = pos.x; var py = pos.y +
|
|
3857
|
-
|
|
3858
|
-
var charScale = 0.9 + row * 0.08;
|
|
3942
|
+
var px = pos.x; var py = pos.y + 80;
|
|
3943
|
+
var charScale = 1.1;
|
|
3859
3944
|
var zIdx = Math.round(py + 100);
|
|
3860
3945
|
|
|
3861
|
-
//
|
|
3862
|
-
var bubbleText = \x27\x27;
|
|
3863
|
-
var bubbleColor = \x27#6366f1\x27;
|
|
3864
|
-
if (isActive) { bubbleText = \x27...lavora\x27; bubbleColor = \x27#6366f1\x27; }
|
|
3865
|
-
else if (isDone) { bubbleText = \x27\u2714 fatto\x27; bubbleColor = \x27#22c55e\x27; }
|
|
3866
|
-
else if (isErr) { bubbleText = \x27\u2715 errore\x27; bubbleColor = \x27#ef4444\x27; }
|
|
3867
|
-
else { bubbleText = \x27in attesa\x27; bubbleColor = \x27#4a4a6a\x27; }
|
|
3868
|
-
|
|
3946
|
+
// Bubble: only show when active (live text) or done (checkmark)
|
|
3947
|
+
var bubbleText = isActive ? \x27...lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27));
|
|
3948
|
+
var bubbleColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : \x27#ef4444\x27);
|
|
3869
3949
|
var charSvg = isoCharSvg({skin: pal.skin, shirt: pal.shirt, hair: pal.hair,
|
|
3870
3950
|
isActive: isActive, isDone: isDone, scale: charScale, accentColor: accentColor});
|
|
3951
|
+
var charW = Math.round(48 * charScale); var charH = Math.round(64 * charScale);
|
|
3871
3952
|
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3953
|
+
agentsHtml += \x27<div class="iso-agent" data-agent-label="\x27+esc(lbl)+\x27" style="position:absolute;left:\x27+(px-charW/2)+\x27px;top:\x27+(py-charH)+\x27px;z-index:\x27+zIdx+\x27;display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))">\x27;
|
|
3954
|
+
// Bubble — bigger, left-aligned (no mirror issue)
|
|
3955
|
+
if (bubbleText || isActive) {
|
|
3956
|
+
agentsHtml += \x27<div class="iso-bubble\x27+(isActive?\x27 iso-bubble--active\x27:\x27\x27)+\x27" id="isobubble_\x27+idx+\x27" style="border-color:\x27+bubbleColor+\x27;color:\x27+bubbleColor+\x27;min-width:80px;max-width:140px;font-size:10px;padding:4px 10px;text-align:left">\x27+esc(bubbleText)+\x27</div>\x27;
|
|
3957
|
+
} else {
|
|
3958
|
+
agentsHtml += \x27<div class="iso-bubble" id="isobubble_\x27+idx+\x27" style="visibility:hidden;min-width:80px"></div>\x27;
|
|
3959
|
+
}
|
|
3878
3960
|
agentsHtml += charSvg;
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
// Iso desk below character
|
|
3961
|
+
agentsHtml += \x27<div class="iso-name" style="font-size:10px;font-weight:700;color:\x27+(isDone?\x27#22c55e\x27:(isActive?\x27#818cf8\x27:(isErr?\x27#ef4444\x27:\x27#555\x27)))+\x27;text-shadow:0 1px 3px rgba(255,255,255,.8)">\x27+esc(lbl)+\x27</div>\x27;
|
|
3962
|
+
// Desk — lighter wood color for bright office
|
|
3882
3963
|
agentsHtml += isoDeskSvg(px, py - charH/2, accentColor);
|
|
3883
3964
|
agentsHtml += \x27</div>\x27;
|
|
3884
3965
|
});
|
|
@@ -3886,17 +3967,16 @@ function renderStudioNodes() {
|
|
|
3886
3967
|
// ── Orchestrator ──────────────────────────────────────────────────────────
|
|
3887
3968
|
var orchHtml = \x27\x27;
|
|
3888
3969
|
if (showMaster) {
|
|
3889
|
-
var oPh = isoProject(ISO_COLS / 2
|
|
3890
|
-
var orchPhrase = [\x27Analizzo il progresso...\x27, \x27Verifico gli step...\x27, \x27Coordinamento...\x27, \x27Supervisione in corso\x27][Math.floor(Date.now()/3000)%4];
|
|
3970
|
+
var oPh = isoProject(ISO_COLS / 2 + 0.5, 1.2);
|
|
3891
3971
|
var orchDone = !hasActive && doneCount === totalCount;
|
|
3892
3972
|
var orchAnim = hasActive ? \x27prl-master-walk\x27 : (orchDone ? \x27prl-master-done\x27 : \x27\x27);
|
|
3893
3973
|
var orchStatus = hasActive
|
|
3894
|
-
? (\
|
|
3895
|
-
: (orchDone ? \
|
|
3896
|
-
orchHtml = \x27<div class="prl-master \x27+orchAnim+\x27" id="wfOrch" style="position:absolute;left:\x27+(oPh.x
|
|
3897
|
-
\x27<div class="iso-bubble iso-bubble--orch" id="wfOrchBubble" style="border-color:#
|
|
3974
|
+
? (\x27Step \x27+doneCount+\x27/\x27+totalCount)
|
|
3975
|
+
: (orchDone ? \x27Completato!\x27 : \x27Pianificazione\x27);
|
|
3976
|
+
orchHtml = \x27<div class="prl-master \x27+orchAnim+\x27" id="wfOrch" style="position:absolute;left:\x27+(oPh.x-28)+\x27px;top:\x27+(oPh.y+60)+\x27px;z-index:50;display:flex;flex-direction:column;align-items:center;gap:3px">\x27+
|
|
3977
|
+
\x27<div class="iso-bubble iso-bubble--orch" id="wfOrchBubble" style="border-color:#6366f1;color:#6366f1;max-width:140px;font-size:10px;padding:4px 10px">\x27+esc(orchStatus)+\x27</div>\x27+
|
|
3898
3978
|
isoOrchSvg(hasActive, doneCount / Math.max(totalCount,1))+
|
|
3899
|
-
\x27<div class="prl-master-label" style="color:#
|
|
3979
|
+
\x27<div class="prl-master-label" style="color:#6366f1;font-size:9px;font-weight:700;text-shadow:0 1px 3px rgba(255,255,255,.8)">Orchestratore</div>\x27+
|
|
3900
3980
|
\x27</div>\x27;
|
|
3901
3981
|
}
|
|
3902
3982
|
|
|
@@ -3909,8 +3989,8 @@ function renderStudioNodes() {
|
|
|
3909
3989
|
\x27<div class="prl-wrap" style="border-color:\x27+phaseColor2+\x2744;padding-bottom:8px">\x27+
|
|
3910
3990
|
\x27<div class="prl-header"><span class="prl-phase-chip" style="--pc:\x27+phaseColor2+\x27">\x27+phaseLabel2+\x27</span></div>\x27+
|
|
3911
3991
|
\x27<div style="display:flex;justify-content:center;width:100%;overflow-x:auto">\x27+
|
|
3912
|
-
\x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;min-width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px;overflow:hidden;border-radius:
|
|
3913
|
-
isoFloorSvg(ISO_COLS + 2,
|
|
3992
|
+
\x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;min-width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px;overflow:hidden;border-radius:12px;background:#f0ede6">\x27+
|
|
3993
|
+
isoFloorSvg(ISO_COLS + 2, 5)+
|
|
3914
3994
|
agentsHtml+
|
|
3915
3995
|
orchHtml+
|
|
3916
3996
|
\x27</div>\x27+
|
|
@@ -4389,8 +4469,9 @@ async function runStudio() {
|
|
|
4389
4469
|
// Keep attachmentContext — it was loaded before hitting Run
|
|
4390
4470
|
parlActiveAgent = null;
|
|
4391
4471
|
parlDoneAgents = {};
|
|
4472
|
+
_parlPersistHtml = null; // clear persisted parliament for fresh run
|
|
4392
4473
|
var parlBlockEl = document.getElementById('studioParliamentBlock');
|
|
4393
|
-
if (parlBlockEl) parlBlockEl.style.display = 'none';
|
|
4474
|
+
if (parlBlockEl) { parlBlockEl.innerHTML = ''; parlBlockEl.style.display = 'none'; }
|
|
4394
4475
|
renderStudioNodes();
|
|
4395
4476
|
renderStudioLog();
|
|
4396
4477
|
renderStudioResult();
|
|
@@ -5199,6 +5280,9 @@ async function runStudio() {
|
|
|
5199
5280
|
flyingDocHtml += \x27</div>\x27;
|
|
5200
5281
|
}
|
|
5201
5282
|
|
|
5283
|
+
// Persist across tab navigations (snapshot — animations won\x27t replay but structure is preserved)
|
|
5284
|
+
if (pb.innerHTML && pb.innerHTML.length < 60000) { _parlPersistHtml = pb.innerHTML; }
|
|
5285
|
+
|
|
5202
5286
|
}
|
|
5203
5287
|
|
|
5204
5288
|
// Show initial R1 block and scroll into view
|
|
@@ -5365,6 +5449,7 @@ async function runStudio() {
|
|
|
5365
5449
|
}
|
|
5366
5450
|
}
|
|
5367
5451
|
if (parlFinal && parlFinal.style.display !== 'none' && parlFinal.innerHTML) {
|
|
5452
|
+
_parlPersistHtml = parlFinal.innerHTML; // persist so tab nav doesn't lose it
|
|
5368
5453
|
doScroll(parlFinal);
|
|
5369
5454
|
setTimeout(function(){ doScroll(resEl); }, 2200);
|
|
5370
5455
|
} else if (resEl) {
|
|
@@ -5611,13 +5696,16 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
5611
5696
|
if (tb) {
|
|
5612
5697
|
if (isStatus) {
|
|
5613
5698
|
var st = ev.token.replace(new RegExp(\x27[\\\\r\\\\n]+\x27,\x27g\x27), \x27 \x27);
|
|
5614
|
-
//
|
|
5615
|
-
var
|
|
5699
|
+
// Strip surrounding brackets for display
|
|
5700
|
+
var stLabel = st.replace(new RegExp(\x27^\\\\[\x27), \x27\x27).replace(new RegExp(\x27\\\\]\\\\s*$\x27), \x27\x27).trim();
|
|
5701
|
+
// Special chip for Searching
|
|
5702
|
+
var srchM = st.match(new RegExp(\x27^\\\\[Searching:\\\\s*"([^"]+)"\\\\]\\\\s*$\x27));
|
|
5616
5703
|
if (srchM) {
|
|
5617
5704
|
var qEsc = srchM[1].replace(/&/g,\x27&\x27).replace(/</g,\x27<\x27).replace(/>/g,\x27>\x27);
|
|
5618
|
-
tb.innerHTML = \x27<span
|
|
5705
|
+
tb.innerHTML = \x27<span class="iso-status-chip"><span class="iso-status-dot"></span>🔍 <span style="color:var(--dim)">Cercando</span> <strong style="color:#a5b4fc">\x27+qEsc+\x27</strong></span>\x27;
|
|
5619
5706
|
} else {
|
|
5620
|
-
|
|
5707
|
+
var stEsc = stLabel.replace(/&/g,\x27&\x27).replace(/</g,\x27<\x27).replace(/>/g,\x27>\x27);
|
|
5708
|
+
tb.innerHTML = \x27<span class="iso-status-chip"><span class="iso-status-dot"></span>\x27+stEsc+\x27</span>\x27;
|
|
5621
5709
|
}
|
|
5622
5710
|
} else {
|
|
5623
5711
|
// Word-by-word streaming: APPEND new chars, never overwrite
|
|
@@ -5704,9 +5792,14 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
5704
5792
|
}
|
|
5705
5793
|
}
|
|
5706
5794
|
if (ev.usage) {
|
|
5795
|
+
// Backend sends accurate usage at end: add input, replace out estimate with real value
|
|
5707
5796
|
var uIn = ev.usage.input||0; var uOut = ev.usage.output||0;
|
|
5708
|
-
|
|
5709
|
-
|
|
5797
|
+
// Correct output: remove per-token estimate already added, replace with real count
|
|
5798
|
+
var outDiff = uOut - stepTokensOut;
|
|
5799
|
+
studioTokens.out = Math.max(0, studioTokens.out + outDiff);
|
|
5800
|
+
stepTokensIn += uIn; stepTokensOut = uOut;
|
|
5801
|
+
studioTokens.in += uIn;
|
|
5802
|
+
studioUpdateTokenBar();
|
|
5710
5803
|
} else if (ev.token && !isStatus) {
|
|
5711
5804
|
var est = Math.ceil(ev.token.length/4);
|
|
5712
5805
|
stepTokensOut += est; studioTokens.out += est; studioUpdateTokenBar();
|
|
@@ -5728,6 +5821,12 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
|
|
|
5728
5821
|
}
|
|
5729
5822
|
|
|
5730
5823
|
function renderStudio(el) {
|
|
5824
|
+
// Persist parliament block across tab navigations
|
|
5825
|
+
var existingParl = document.getElementById('studioParliamentBlock');
|
|
5826
|
+
if (existingParl && existingParl.innerHTML.trim()) {
|
|
5827
|
+
_parlPersistHtml = existingParl.innerHTML;
|
|
5828
|
+
}
|
|
5829
|
+
|
|
5731
5830
|
var examplesHtml = STUDIO_EXAMPLES.map(function(ex) {
|
|
5732
5831
|
return '<button class="studio-example-btn" onclick="document.getElementById(\\'studioTaskInput\\').value=' + JSON.stringify(ex) + '">' + esc(ex.slice(0, 52)) + (ex.length > 52 ? '...' : '') + '</button>';
|
|
5733
5832
|
}).join('');
|
|
@@ -5870,7 +5969,6 @@ function renderStudio(el) {
|
|
|
5870
5969
|
'<div class="studio-log" id="studioLog" style="display:none"></div>' +
|
|
5871
5970
|
'<div id="studioParliamentBlock" style="display:none;margin-bottom:12px"></div>' +
|
|
5872
5971
|
'<div id="studioResult"></div>' +
|
|
5873
|
-
'<div id="studioSessionsBar" style="margin-top:16px;display:none"></div>' +
|
|
5874
5972
|
'</div>' +
|
|
5875
5973
|
|
|
5876
5974
|
// ── AGENT SIDEBAR ──
|
|
@@ -5883,6 +5981,7 @@ function renderStudio(el) {
|
|
|
5883
5981
|
'<div style="font-size:9px;color:var(--dim);margin-bottom:8px">Click to add to pipeline</div>' +
|
|
5884
5982
|
'<div id="sideToolsList">'+toolsHtml+'</div>' +
|
|
5885
5983
|
'<div id="sideAgentsList" style="display:none">'+specialistHtml+'</div>' +
|
|
5984
|
+
'<div id="studioSessionsBar" style="margin-top:16px;display:none"></div>' +
|
|
5886
5985
|
'</div>' +
|
|
5887
5986
|
'</div>';
|
|
5888
5987
|
|
|
@@ -5890,9 +5989,18 @@ function renderStudio(el) {
|
|
|
5890
5989
|
renderStudioLog();
|
|
5891
5990
|
renderStudioResult();
|
|
5892
5991
|
renderStudioSessionsBar();
|
|
5893
|
-
|
|
5992
|
+
// Restore token bar (preserve counts across re-renders)
|
|
5993
|
+
studioUpdateTokenBar();
|
|
5894
5994
|
// Restore pipeline from state
|
|
5895
5995
|
renderBuilderPipeline();
|
|
5996
|
+
// Restore parliament block if it was visible before tab navigation
|
|
5997
|
+
if (_parlPersistHtml) {
|
|
5998
|
+
var parlRestoreEl = document.getElementById('studioParliamentBlock');
|
|
5999
|
+
if (parlRestoreEl) {
|
|
6000
|
+
parlRestoreEl.innerHTML = _parlPersistHtml;
|
|
6001
|
+
parlRestoreEl.style.display = 'block';
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
5896
6004
|
}
|
|
5897
6005
|
|
|
5898
6006
|
// ---- STUDIO SIDEBAR TAB ----
|
|
@@ -6378,10 +6486,10 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
6378
6486
|
.studio-header{margin-bottom:20px}
|
|
6379
6487
|
.studio-header h2{font-size:15px;color:var(--green);margin-bottom:4px}
|
|
6380
6488
|
.studio-header p{font-size:11px;color:var(--dim);line-height:1.5}
|
|
6381
|
-
.studio-input-row{display:flex;gap:8px;margin-bottom:
|
|
6382
|
-
.studio-input-row textarea{flex:1;resize:
|
|
6489
|
+
.studio-input-row{display:flex;gap:8px;margin-bottom:16px;align-items:flex-start}
|
|
6490
|
+
.studio-input-row textarea{flex:1;resize:vertical;min-height:90px;max-height:200px;padding:10px 14px;font-size:13px;border-radius:var(--r);border:1px solid var(--border2);line-height:1.5}
|
|
6383
6491
|
.studio-input-row textarea:focus{border-color:var(--green3)}
|
|
6384
|
-
.studio-run-btn{background:var(--green3);color:var(--bg);padding:0
|
|
6492
|
+
.studio-run-btn{background:var(--green3);color:var(--bg);padding:0 16px;border-radius:var(--r);font-weight:600;font-size:12px;white-space:nowrap;align-self:stretch;min-width:80px;letter-spacing:.2px}
|
|
6385
6493
|
.studio-run-btn:disabled{opacity:.4}
|
|
6386
6494
|
.studio-canvas{position:relative;width:100%;min-height:220px;background:var(--bg2);border:1px solid var(--border);border-radius:8px;margin-bottom:20px;overflow:hidden}
|
|
6387
6495
|
.studio-canvas__empty{display:flex;align-items:center;justify-content:center;height:180px;color:var(--dim);font-size:11px;flex-direction:column;gap:8px}
|
|
@@ -6538,7 +6646,11 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
6538
6646
|
/* Plant right */
|
|
6539
6647
|
.prl-office-plant2{position:absolute;bottom:14px;right:8px;width:22px;height:42px;z-index:2;pointer-events:none}
|
|
6540
6648
|
/* ── ISOMETRIC JRPG SCENE ── */
|
|
6541
|
-
.iso-scene{background:#
|
|
6649
|
+
.iso-scene{background:#f0ede6;cursor:default;max-width:100%;overflow-x:auto;box-shadow:0 4px 24px rgba(0,0,0,.18)}
|
|
6650
|
+
/* Animated status chip for [bracket tokens] */
|
|
6651
|
+
.iso-status-chip{display:inline-flex;align-items:center;gap:6px;background:rgba(99,102,241,.1);border:1px solid rgba(99,102,241,.3);border-radius:20px;padding:3px 10px;font-size:11px;font-family:var(--mono);color:#818cf8;animation:statusPulse 2s ease-in-out infinite}
|
|
6652
|
+
@keyframes statusPulse{0%,100%{opacity:.7;border-color:rgba(99,102,241,.3)}50%{opacity:1;border-color:rgba(99,102,241,.7);box-shadow:0 0 8px rgba(99,102,241,.3)}}
|
|
6653
|
+
.iso-status-dot{width:6px;height:6px;border-radius:50%;background:#6366f1;animation:dotBounce 1s ease-in-out infinite}
|
|
6542
6654
|
.iso-agent{transition:filter .3s}
|
|
6543
6655
|
.iso-agent:hover{filter:brightness(1.15)}
|
|
6544
6656
|
/* Thought bubble / speech bubble above character */
|
|
@@ -6608,7 +6720,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
6608
6720
|
.prl-conv-bar-inner{height:100%;background:linear-gradient(90deg,#22c55e,#4ade80);border-radius:4px;transition:width .8s ease}
|
|
6609
6721
|
.prl-conv-text{font-size:9px;color:#86efac;line-height:1.55}
|
|
6610
6722
|
.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}
|
|
6611
|
-
.studio-log-entry{margin-bottom:
|
|
6723
|
+
.studio-log-entry{margin-bottom:10px;padding:14px 16px;border-radius:10px;background:var(--bg3);border:1px solid var(--border);min-height:80px}
|
|
6612
6724
|
.studio-log-entry:last-child{margin-bottom:0}
|
|
6613
6725
|
.studio-log-entry__header{display:flex;align-items:center;gap:8px;margin-bottom:6px}
|
|
6614
6726
|
.studio-log-entry__icon{font-size:15px}
|
|
@@ -6622,7 +6734,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
6622
6734
|
.studio-result__body{font-size:13px;color:var(--text);word-wrap:break-word;line-height:1.7}
|
|
6623
6735
|
.studio-example-btn{display:inline-block;padding:5px 12px;border:1px solid var(--border2);border-radius:20px;font-size:10px;color:var(--dim);cursor:pointer;background:none;margin:0 4px 6px 0;transition:all .15s}
|
|
6624
6736
|
.studio-example-btn:hover{border-color:var(--green3);color:var(--green);background:var(--greendim)}
|
|
6625
|
-
.studio-tools-panel{width:220px;flex-shrink:0;border:1px solid var(--border);border-radius:10px;padding:12px;background:var(--bg2);max-height:
|
|
6737
|
+
.studio-tools-panel{width:220px;flex-shrink:0;border:1px solid var(--border);border-radius:10px;padding:12px;background:var(--bg2);max-height:calc(100vh - 120px);overflow-y:auto;position:sticky;top:16px}
|
|
6626
6738
|
.studio-tool-item{display:flex;align-items:flex-start;gap:8px;padding:8px;border-radius:6px;cursor:pointer;transition:background .15s;margin-bottom:4px}
|
|
6627
6739
|
.studio-tool-item:hover{background:var(--bg3);border-radius:6px}
|
|
6628
6740
|
.studio-tool-icon{font-size:16px;flex-shrink:0;margin-top:1px}
|