nothumanallowed 13.5.10 → 13.5.11
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/constants.mjs +1 -1
- package/src/services/web-ui.mjs +231 -62
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.11",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.5.
|
|
8
|
+
export const VERSION = '13.5.11';
|
|
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
|
@@ -3798,8 +3798,11 @@ function isoCharSvg(opts) {
|
|
|
3798
3798
|
var isDone = opts.isDone;
|
|
3799
3799
|
var scale = opts.scale || 1;
|
|
3800
3800
|
var accentColor = opts.accentColor || \x27#6366f1\x27;
|
|
3801
|
-
var idx = opts.emojiIdx
|
|
3802
|
-
|
|
3801
|
+
var idx = opts.emojiIdx;
|
|
3802
|
+
// idx === 99 means orchestrator — use crown+suit combo
|
|
3803
|
+
var emoji = (idx === 99)
|
|
3804
|
+
? (String.fromCodePoint(0x1F451) + String.fromCodePoint(0x1F9D1,0x200D,0x1F4BC))
|
|
3805
|
+
: AGENT_EMOJIS[(idx || 0) % AGENT_EMOJIS.length];
|
|
3803
3806
|
var sz = Math.round(52 * scale);
|
|
3804
3807
|
var glowColor = isActive ? accentColor : (isDone ? \x27#22c55e\x27 : \x27transparent\x27);
|
|
3805
3808
|
var glowFilter = (isActive || isDone) ? (\x27filter:drop-shadow(0 0 8px \x27+glowColor+\x27aa)\x27) : \x27\x27;
|
|
@@ -3841,6 +3844,61 @@ function agentPalette(lbl) {
|
|
|
3841
3844
|
return {skin: skins[i], shirt: shirts[i], hair: hairs[i]};
|
|
3842
3845
|
}
|
|
3843
3846
|
|
|
3847
|
+
// Tool emoji map — specific icon per agent/tool type, fallback to default
|
|
3848
|
+
var TOOL_EMOJI_MAP = {
|
|
3849
|
+
websearch: String.fromCodePoint(0x1F50D),
|
|
3850
|
+
search: String.fromCodePoint(0x1F50D),
|
|
3851
|
+
browser: String.fromCodePoint(0x1F310),
|
|
3852
|
+
email: String.fromCodePoint(0x1F4E7),
|
|
3853
|
+
gmail: String.fromCodePoint(0x1F4E7),
|
|
3854
|
+
calendar: String.fromCodePoint(0x1F4C5),
|
|
3855
|
+
github: String.fromCodePoint(0x1F431),
|
|
3856
|
+
notion: String.fromCodePoint(0x1F4D3),
|
|
3857
|
+
slack: String.fromCodePoint(0x1F4AC),
|
|
3858
|
+
data: String.fromCodePoint(0x1F4CA),
|
|
3859
|
+
analyst: String.fromCodePoint(0x1F4CA),
|
|
3860
|
+
writer: String.fromCodePoint(0x270F,0xFE0F),
|
|
3861
|
+
summary: String.fromCodePoint(0x1F4CB),
|
|
3862
|
+
research: String.fromCodePoint(0x1F52C),
|
|
3863
|
+
canvas: String.fromCodePoint(0x1F3A8),
|
|
3864
|
+
security: String.fromCodePoint(0x1F6E1,0xFE0F),
|
|
3865
|
+
devops: String.fromCodePoint(0x2699,0xFE0F),
|
|
3866
|
+
code: String.fromCodePoint(0x1F4BB),
|
|
3867
|
+
file: String.fromCodePoint(0x1F4C2),
|
|
3868
|
+
drive: String.fromCodePoint(0x1F4BE),
|
|
3869
|
+
maps: String.fromCodePoint(0x1F5FA,0xFE0F),
|
|
3870
|
+
voice: String.fromCodePoint(0x1F3A4),
|
|
3871
|
+
pdf: String.fromCodePoint(0x1F4DC),
|
|
3872
|
+
document: String.fromCodePoint(0x1F4DC),
|
|
3873
|
+
task: String.fromCodePoint(0x2705),
|
|
3874
|
+
contacts: String.fromCodePoint(0x1F4F1),
|
|
3875
|
+
reminder: String.fromCodePoint(0x23F0),
|
|
3876
|
+
news: String.fromCodePoint(0x1F4F0),
|
|
3877
|
+
image: String.fromCodePoint(0x1F5BC,0xFE0F),
|
|
3878
|
+
video: String.fromCodePoint(0x1F3AC),
|
|
3879
|
+
music: String.fromCodePoint(0x1F3B5),
|
|
3880
|
+
translate: String.fromCodePoint(0x1F30D),
|
|
3881
|
+
math: String.fromCodePoint(0x1F9EE),
|
|
3882
|
+
sql: String.fromCodePoint(0x1F5C4,0xFE0F),
|
|
3883
|
+
api: String.fromCodePoint(0x1F517),
|
|
3884
|
+
test: String.fromCodePoint(0x1F9EA),
|
|
3885
|
+
monitor: String.fromCodePoint(0x1F4F6),
|
|
3886
|
+
_default: String.fromCodePoint(0x1F527) // wrench as fallback
|
|
3887
|
+
};
|
|
3888
|
+
|
|
3889
|
+
function getNodeEmoji(n) {
|
|
3890
|
+
var lbl = (n.label || n.agent || '').toLowerCase();
|
|
3891
|
+
var icon = n.icon || '';
|
|
3892
|
+
// Check by label keywords
|
|
3893
|
+
var keys = Object.keys(TOOL_EMOJI_MAP);
|
|
3894
|
+
for (var ki = 0; ki < keys.length; ki++) {
|
|
3895
|
+
if (keys[ki] !== '_default' && lbl.indexOf(keys[ki]) >= 0) return TOOL_EMOJI_MAP[keys[ki]];
|
|
3896
|
+
}
|
|
3897
|
+
// If icon looks like an emoji code point (not HTML entity), use it
|
|
3898
|
+
if (icon && icon.length > 0 && icon.charCodeAt(0) > 127) return icon;
|
|
3899
|
+
return TOOL_EMOJI_MAP._default;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3844
3902
|
function renderStudioNodes() {
|
|
3845
3903
|
var el = document.getElementById('studioNodes');
|
|
3846
3904
|
if (!el) return;
|
|
@@ -3855,78 +3913,170 @@ function renderStudioNodes() {
|
|
|
3855
3913
|
var totalCount = nodes.length;
|
|
3856
3914
|
var showMaster = hasActive || doneCount > 0;
|
|
3857
3915
|
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3916
|
+
var phaseLabel2 = hasActive
|
|
3917
|
+
? (\x27Workflow in esecuzione \u2014 \x27+doneCount+\x27/\x27+totalCount)
|
|
3918
|
+
: (doneCount===totalCount && totalCount>0 ? \x27Workflow completato\x27 : \x27Workflow pianificato\x27);
|
|
3919
|
+
var phaseColor2 = hasActive ? \x27#6366f1\x27 : (doneCount===totalCount && totalCount>0 ? \x27#22c55e\x27 : \x27#6b7280\x27);
|
|
3920
|
+
|
|
3921
|
+
// ── Layout: all nodes + orchestrator on a grid ────────────────────────────
|
|
3922
|
+
// Include orchestrator as first "station" (desk 0), then all agents
|
|
3923
|
+
// Total stations = nodes.length + 1 (orchestrator)
|
|
3924
|
+
var totalStations = totalCount + 1;
|
|
3925
|
+
// Grid: up to 3 per row, rows fill downward
|
|
3926
|
+
var cols = Math.min(totalStations, 3);
|
|
3927
|
+
var rows = Math.ceil(totalStations / cols);
|
|
3928
|
+
|
|
3929
|
+
// Scene: square, 100% width of parent, dynamic height based on grid rows
|
|
3930
|
+
// Each cell = 160px wide, 180px tall
|
|
3931
|
+
var CELL_W = 160; var CELL_H = 180;
|
|
3932
|
+
var SCENE_W = cols * CELL_W + 40; // +padding
|
|
3933
|
+
var SCENE_H = Math.max(rows * CELL_H + 80, 400);
|
|
3934
|
+
|
|
3935
|
+
// Plant emoji for decoration corners
|
|
3936
|
+
var plantEmoji = String.fromCodePoint(0x1F331); // seedling
|
|
3937
|
+
var bigPlant = String.fromCodePoint(0x1FAB4); // potted plant
|
|
3938
|
+
|
|
3939
|
+
// ── Build station HTML ───────────────────────────────────────────────────
|
|
3940
|
+
var stationsHtml = \x27\x27;
|
|
3941
|
+
|
|
3942
|
+
// Station builder — shared for orchestrator and agents
|
|
3943
|
+
function buildStation(stationIdx, label, emoji, toolEmoji, isOrch, isActive, isDone, isErr, idx) {
|
|
3944
|
+
var col = stationIdx % cols;
|
|
3945
|
+
var row2 = Math.floor(stationIdx / cols);
|
|
3946
|
+
var left = 20 + col * CELL_W + CELL_W/2;
|
|
3947
|
+
var top = 30 + row2 * CELL_H;
|
|
3948
|
+
var zIdx = 100 + row2 * 10 + col;
|
|
3949
|
+
var accentColor = isOrch ? \x27#818cf8\x27 : (isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#aaa\x27)));
|
|
3950
|
+
|
|
3951
|
+
// Desk — isometric CSS box using borders
|
|
3952
|
+
var deskHtml = \x27<div class="iso-desk" style="--dc:\x27+accentColor+\x27"></div>\x27;
|
|
3953
|
+
|
|
3954
|
+
// Monitor — small box on desk
|
|
3955
|
+
var monHtml = \x27<div class="iso-monitor" style="border-color:\x27+accentColor+\x2788">\x27+
|
|
3956
|
+
\x27<div class="iso-monitor-screen">\x27+(isOrch?\x27<span style="font-size:10px">🔍</span>\x27:(isDone?\x27<span style="color:#22c55e;font-size:10px">✓</span>\x27:(isActive?\x27<span class="iso-monitor-blink"></span>\x27:\x27<span style="font-size:9px;opacity:.4">■</span>\x27)))+\x27</div>\x27+
|
|
3957
|
+
\x27</div>\x27;
|
|
3861
3958
|
|
|
3862
|
-
|
|
3959
|
+
// Tool badge — the emoji representing what this node does
|
|
3960
|
+
var toolBadge = \x27<div class="iso-tool-badge" title="\x27+esc(label)+\x27">\x27+toolEmoji+\x27</div>\x27;
|
|
3961
|
+
|
|
3962
|
+
// Agent/tool character emoji
|
|
3963
|
+
var charEmojiHtml = isoCharSvg({emojiIdx: isOrch ? 99 : idx, isActive: isActive, isDone: isDone, scale: 1, accentColor: accentColor});
|
|
3964
|
+
|
|
3965
|
+
// Status bubble
|
|
3966
|
+
var bubbleText = isOrch
|
|
3967
|
+
? (hasActive ? (\x27Step \x27+doneCount+\x27/\x27+totalCount) : ((!hasActive && doneCount===totalCount && totalCount>0) ? \x27Completato!\x27 : \x27In attesa\x27))
|
|
3968
|
+
: (isActive ? \x27...lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27)));
|
|
3969
|
+
var bubbleColor = isOrch ? \x27#818cf8\x27 : (isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : \x27#ef4444\x27));
|
|
3970
|
+
var bubbleHtml = (bubbleText || isOrch)
|
|
3971
|
+
? \x27<div class="iso-bubble\x27+(isActive?\x27 iso-bubble--active\x27:\x27\x27)+\x27" id="isobubble_\x27+(isOrch?\x27orch\x27:idx)+\x27" style="border-color:\x27+bubbleColor+\x27;color:\x27+bubbleColor+\x27">\x27+esc(bubbleText)+\x27</div>\x27
|
|
3972
|
+
: \x27<div style="height:22px"></div>\x27;
|
|
3973
|
+
|
|
3974
|
+
// Name badge
|
|
3975
|
+
var nameColor = isDone ? \x27#16a34a\x27 : (isActive ? \x27#6366f1\x27 : (isOrch ? \x27#818cf8\x27 : (isErr ? \x27#dc2626\x27 : \x27#374151\x27)));
|
|
3976
|
+
var nameBg = isDone ? \x27#dcfce7\x27 : (isActive ? \x27#ede9fe\x27 : (isOrch ? \x27#e0e7ff\x27 : \x27#f3f4f6\x27));
|
|
3977
|
+
var nameHtml = \x27<div class="iso-name" style="color:\x27+nameColor+\x27;background:\x27+nameBg+\x27">\x27+(isOrch?\x27\u2666 \x27:\x27\x27)+esc(label.slice(0,18))+\x27</div>\x27;
|
|
3978
|
+
|
|
3979
|
+
// Orchestrator walk animation — walks between desks horizontally
|
|
3980
|
+
var orchWalkStyle = \x27\x27;
|
|
3981
|
+
if (isOrch && hasActive) {
|
|
3982
|
+
var activeIdx = nodes.findIndex(function(nd){return nd.status===\x27running\x27;});
|
|
3983
|
+
if (activeIdx >= 0) {
|
|
3984
|
+
var targetStation = activeIdx + 1; // station index of active agent (orch is 0)
|
|
3985
|
+
var targetCol = targetStation % cols;
|
|
3986
|
+
var targetRow = Math.floor(targetStation / cols);
|
|
3987
|
+
var targetLeft = 20 + targetCol * CELL_W + CELL_W/2;
|
|
3988
|
+
var targetTop = 30 + targetRow * CELL_H;
|
|
3989
|
+
orchWalkStyle = \x27--orch-tx:\x27+(targetLeft - left)+\x27px;--orch-ty:\x27+(targetTop - top)+\x27px\x27;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
var orchClass = (isOrch && hasActive) ? \x27 iso-orch-walking\x27 : (isOrch && !hasActive && doneCount===totalCount && totalCount>0 ? \x27 iso-orch-done\x27 : \x27\x27);
|
|
3993
|
+
|
|
3994
|
+
var clickHandler = isOrch ? \x27\x27 : \x27onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))"\x27;
|
|
3995
|
+
var dataAttr = isOrch ? \x27\x27 : \x27data-agent-label="\x27+esc(label)+\x27"\x27;
|
|
3996
|
+
|
|
3997
|
+
return \x27<div class="iso-station\x27+orchClass+\x27" \x27+dataAttr+\x27 style="position:absolute;left:\x27+left+\x27px;top:\x27+top+\x27px;z-index:\x27+zIdx+\x27;transform:translateX(-50%);\x27+orchWalkStyle+\x27" \x27+clickHandler+\x27>\x27+
|
|
3998
|
+
bubbleHtml+
|
|
3999
|
+
toolBadge+
|
|
4000
|
+
charEmojiHtml+
|
|
4001
|
+
deskHtml+
|
|
4002
|
+
monHtml+
|
|
4003
|
+
nameHtml+
|
|
4004
|
+
\x27</div>\x27;
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
// Orchestrator station (index 0)
|
|
4008
|
+
var orchIsActive2 = hasActive;
|
|
4009
|
+
var orchDone2 = !hasActive && doneCount === totalCount && totalCount > 0;
|
|
4010
|
+
stationsHtml += buildStation(0, \x27Orchestratore\x27, String.fromCodePoint(0x1F451), String.fromCodePoint(0x1F4CB), true, orchIsActive2, orchDone2, false, -1);
|
|
4011
|
+
|
|
4012
|
+
// Agent/tool stations (index 1..N)
|
|
3863
4013
|
nodes.forEach(function(n, idx) {
|
|
3864
4014
|
var isActive = n.status === \x27running\x27;
|
|
3865
4015
|
var isDone = n.status === \x27done\x27;
|
|
3866
4016
|
var isErr = n.status === \x27error\x27;
|
|
3867
4017
|
var lbl = n.label || n.agent;
|
|
3868
|
-
var
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
// Spread agents evenly across front rows
|
|
3872
|
-
var colStep = ISO_COLS > 1 ? 5 / (ISO_COLS - 1) : 2;
|
|
3873
|
-
var col = (ISO_COLS === 1) ? 2.5 : idx * colStep;
|
|
3874
|
-
var row = 3.5;
|
|
3875
|
-
var pos = isoProject(col, row);
|
|
3876
|
-
var px = pos.x; var py = pos.y + 80;
|
|
3877
|
-
var charScale = 1.1;
|
|
3878
|
-
var zIdx = Math.round(py + 100);
|
|
3879
|
-
|
|
3880
|
-
// Bubble: only show when active (live text) or done (checkmark)
|
|
3881
|
-
var bubbleText = isActive ? \x27...lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27));
|
|
3882
|
-
var bubbleColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : \x27#ef4444\x27);
|
|
3883
|
-
void pal; // palette no longer used for character rendering
|
|
3884
|
-
var charSvg = isoCharSvg({emojiIdx: idx, isActive: isActive, isDone: isDone, scale: charScale, accentColor: accentColor});
|
|
3885
|
-
var charW = Math.round(52 * charScale); var charH = Math.round(52 * charScale);
|
|
3886
|
-
|
|
3887
|
-
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;
|
|
3888
|
-
// Bubble — bigger, left-aligned (no mirror issue)
|
|
3889
|
-
if (bubbleText || isActive) {
|
|
3890
|
-
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;
|
|
3891
|
-
} else {
|
|
3892
|
-
agentsHtml += \x27<div class="iso-bubble" id="isobubble_\x27+idx+\x27" style="visibility:hidden;min-width:80px"></div>\x27;
|
|
3893
|
-
}
|
|
3894
|
-
agentsHtml += charSvg;
|
|
3895
|
-
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;
|
|
3896
|
-
// Desk — lighter wood color for bright office
|
|
3897
|
-
agentsHtml += isoDeskSvg(px, py - charH/2, accentColor);
|
|
3898
|
-
agentsHtml += \x27</div>\x27;
|
|
4018
|
+
var toolEmoji = getNodeEmoji(n);
|
|
4019
|
+
stationsHtml += buildStation(idx + 1, lbl, AGENT_EMOJIS[idx % AGENT_EMOJIS.length], toolEmoji, false, isActive, isDone, isErr, idx);
|
|
3899
4020
|
});
|
|
3900
4021
|
|
|
3901
|
-
// ──
|
|
3902
|
-
var
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
\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+
|
|
3912
|
-
isoOrchSvg(hasActive, doneCount / Math.max(totalCount,1))+
|
|
3913
|
-
\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+
|
|
3914
|
-
\x27</div>\x27;
|
|
3915
|
-
}
|
|
4022
|
+
// ── Decorations: plants + door ───────────────────────────────────────────
|
|
4023
|
+
var decoHtml =
|
|
4024
|
+
\x27<div style="position:absolute;bottom:8px;left:8px;font-size:32px;line-height:1;filter:drop-shadow(0 2px 4px rgba(0,0,0,.2))">\x27+bigPlant+\x27</div>\x27+
|
|
4025
|
+
\x27<div style="position:absolute;bottom:8px;right:8px;font-size:32px;line-height:1;filter:drop-shadow(0 2px 4px rgba(0,0,0,.2))">\x27+bigPlant+\x27</div>\x27+
|
|
4026
|
+
\x27<div style="position:absolute;top:8px;right:14px;font-size:22px;line-height:1">\x27+plantEmoji+\x27</div>\x27+
|
|
4027
|
+
// Door on back wall
|
|
4028
|
+
\x27<div style="position:absolute;top:0;left:50%;transform:translateX(-50%);width:38px;height:54px;background:#c8a87a;border:2px solid #a07848;border-radius:4px 4px 0 0;z-index:1">\x27+
|
|
4029
|
+
\x27<div style="position:absolute;right:5px;top:50%;width:6px;height:6px;border-radius:50%;background:#7a5a2a;transform:translateY(-50%)"></div>\x27+
|
|
4030
|
+
\x27<div style="position:absolute;top:4px;left:4px;right:4px;height:16px;background:rgba(255,255,255,.15);border-radius:2px"></div>\x27+
|
|
4031
|
+
\x27</div>\x27;
|
|
3916
4032
|
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
4033
|
+
// ── Floor SVG (fills the whole scene) ────────────────────────────────────
|
|
4034
|
+
var floorSvg = \x27<svg viewBox="0 0 \x27+SCENE_W+\x27 \x27+SCENE_H+\x27" width="\x27+SCENE_W+\x27" height="\x27+SCENE_H+\x27" xmlns="http://www.w3.org/2000/svg" style="position:absolute;top:0;left:0;z-index:0;pointer-events:none">\x27;
|
|
4035
|
+
// Back wall
|
|
4036
|
+
floorSvg += \x27<rect x="0" y="0" width="\x27+SCENE_W+\x27" height="\x27+Math.round(SCENE_H*0.38)+\x27" fill="#f5f0ea"/>\x27;
|
|
4037
|
+
// Baseboard
|
|
4038
|
+
floorSvg += \x27<rect x="0" y="\x27+Math.round(SCENE_H*0.38-4)+\x27" width="\x27+SCENE_W+\x27" height="6" fill="#d4c4a8" rx="1"/>\x27;
|
|
4039
|
+
// Floor — parquet planks
|
|
4040
|
+
var floorY = Math.round(SCENE_H*0.38);
|
|
4041
|
+
var plankColors = [\x27#c8a06a\x27,\x27#bf9860\x27,\x27#d4aa72\x27,\x27#ba9458\x27,\x27#c4a068\x27];
|
|
4042
|
+
var plankH = 28; var plankW = 80;
|
|
4043
|
+
for (var fy = floorY; fy < SCENE_H; fy += plankH) {
|
|
4044
|
+
var rowOff = (Math.floor((fy-floorY)/plankH) % 2) * (plankW/2);
|
|
4045
|
+
for (var fx = -plankW + rowOff; fx < SCENE_W + plankW; fx += plankW) {
|
|
4046
|
+
var pc = plankColors[Math.abs(Math.round(fx/plankW + fy/plankH)) % plankColors.length];
|
|
4047
|
+
floorSvg += \x27<rect x="\x27+fx+\x27" y="\x27+fy+\x27" width="\x27+(plankW-1)+\x27" height="\x27+(plankH-1)+\x27" fill="\x27+pc+\x27" rx="1"/>\x27;
|
|
4048
|
+
// Grain line
|
|
4049
|
+
floorSvg += \x27<line x1="\x27+(fx+plankW*0.35)+\x27" y1="\x27+fy+\x27" x2="\x27+(fx+plankW*0.35)+\x27" y2="\x27+(fy+plankH-1)+\x27" stroke="rgba(0,0,0,.05)" stroke-width="1"/>\x27;
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
// Window on back wall
|
|
4053
|
+
floorSvg += \x27<rect x="20" y="18" width="60" height="50" rx="3" fill="#bbd8f5" stroke="#a8c4dc" stroke-width="2"/>\x27;
|
|
4054
|
+
floorSvg += \x27<line x1="50" y1="18" x2="50" y2="68" stroke="#a8c4dc" stroke-width="1.5"/>\x27;
|
|
4055
|
+
floorSvg += \x27<line x1="20" y1="43" x2="80" y2="43" stroke="#a8c4dc" stroke-width="1.5"/>\x27;
|
|
4056
|
+
floorSvg += \x27<rect x="20" y="18" width="60" height="50" rx="3" fill="rgba(255,255,255,.15)"/>\x27;
|
|
4057
|
+
if (SCENE_W > 350) {
|
|
4058
|
+
floorSvg += \x27<rect x="\x27+(SCENE_W-100)+\x27" y="18" width="60" height="50" rx="3" fill="#bbd8f5" stroke="#a8c4dc" stroke-width="2"/>\x27;
|
|
4059
|
+
floorSvg += \x27<line x1="\x27+(SCENE_W-70)+\x27" y1="18" x2="\x27+(SCENE_W-70)+\x27" y2="68" stroke="#a8c4dc" stroke-width="1.5"/>\x27;
|
|
4060
|
+
floorSvg += \x27<line x1="\x27+(SCENE_W-100)+\x27" y1="43" x2="\x27+(SCENE_W-40)+\x27" y2="43" stroke="#a8c4dc" stroke-width="1.5"/>\x27;
|
|
4061
|
+
}
|
|
4062
|
+
// Chandelier
|
|
4063
|
+
var chandX = Math.round(SCENE_W/2);
|
|
4064
|
+
floorSvg += \x27<line x1="\x27+chandX+\x27" y1="0" x2="\x27+chandX+\x27" y2="24" stroke="#999" stroke-width="2"/>\x27;
|
|
4065
|
+
floorSvg += \x27<ellipse cx="\x27+chandX+\x27" cy="28" rx="28" ry="8" fill="#e8d980" stroke="#c8b840" stroke-width="1.5"/>\x27;
|
|
4066
|
+
floorSvg += \x27<circle cx="\x27+(chandX-18)+\x27" cy="32" r="5" fill="#fff8c0" filter="url(#bGlow)"/>\x27;
|
|
4067
|
+
floorSvg += \x27<circle cx="\x27+chandX+\x27" cy="34" r="5" fill="#fff8c0" filter="url(#bGlow)"/>\x27;
|
|
4068
|
+
floorSvg += \x27<circle cx="\x27+(chandX+18)+\x27" cy="32" r="5" fill="#fff8c0" filter="url(#bGlow)"/>\x27;
|
|
4069
|
+
floorSvg += \x27<defs><filter id="bGlow" x="-100%" y="-100%" width="300%" height="300%"><feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs>\x27;
|
|
4070
|
+
floorSvg += \x27</svg>\x27;
|
|
3921
4071
|
|
|
3922
4072
|
el.innerHTML =
|
|
3923
4073
|
\x27<div class="prl-wrap" style="border-color:\x27+phaseColor2+\x2744;padding-bottom:8px">\x27+
|
|
3924
4074
|
\x27<div class="prl-header"><span class="prl-phase-chip" style="--pc:\x27+phaseColor2+\x27">\x27+phaseLabel2+\x27</span></div>\x27+
|
|
3925
|
-
\x27<div
|
|
3926
|
-
\x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;min-width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
4075
|
+
\x27<div class="iso-scene-wrap">\x27+
|
|
4076
|
+
\x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;min-width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px">\x27+
|
|
4077
|
+
floorSvg+
|
|
4078
|
+
decoHtml+
|
|
4079
|
+
stationsHtml+
|
|
3930
4080
|
\x27</div>\x27+
|
|
3931
4081
|
\x27</div>\x27+
|
|
3932
4082
|
\x27</div>\x27;
|
|
@@ -6533,7 +6683,26 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
6533
6683
|
/* Plant right */
|
|
6534
6684
|
.prl-office-plant2{position:absolute;bottom:14px;right:8px;width:22px;height:42px;z-index:2;pointer-events:none}
|
|
6535
6685
|
/* ── ISOMETRIC JRPG SCENE ── */
|
|
6536
|
-
.iso-scene{
|
|
6686
|
+
.iso-scene-wrap{width:100%;overflow-x:auto;display:flex;justify-content:center}
|
|
6687
|
+
.iso-scene{background:#f0ede6;cursor:default;overflow:hidden;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.18)}
|
|
6688
|
+
/* ── Station layout (new grid-based office) ── */
|
|
6689
|
+
.iso-station{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;transition:filter .2s}
|
|
6690
|
+
.iso-station:hover{filter:brightness(1.08)}
|
|
6691
|
+
.iso-station.iso-orch-walking{animation:orchWalk 2.2s ease-in-out infinite alternate}
|
|
6692
|
+
@keyframes orchWalk{0%{transform:translateX(-50%)}100%{transform:translateX(calc(-50% + var(--orch-tx,0px))) translateY(var(--orch-ty,0px))}}
|
|
6693
|
+
.iso-station.iso-orch-done{animation:orchDone .6s ease forwards}
|
|
6694
|
+
@keyframes orchDone{0%{transform:translateX(-50%) scale(1)}50%{transform:translateX(-50%) scale(1.15)}100%{transform:translateX(-50%) scale(1)}}
|
|
6695
|
+
/* Desk */
|
|
6696
|
+
.iso-desk{width:90px;height:18px;background:linear-gradient(180deg,#d4a448 0%,#b8832a 100%);border-radius:4px 4px 2px 2px;box-shadow:0 4px 0 #8a5e18,0 6px 8px rgba(0,0,0,.25);border:1px solid #c49038;position:relative;margin-top:2px}
|
|
6697
|
+
.iso-desk::after{content:'';position:absolute;bottom:-4px;left:6px;right:6px;height:4px;background:#8a5e18;border-radius:0 0 3px 3px}
|
|
6698
|
+
/* Monitor on desk */
|
|
6699
|
+
.iso-monitor{width:52px;height:38px;background:#1a1a2c;border:2px solid #3a3070;border-radius:4px;position:absolute;top:-44px;left:50%;transform:translateX(-50%);display:flex;align-items:center;justify-content:center}
|
|
6700
|
+
.iso-monitor::before{content:'';position:absolute;bottom:-6px;left:50%;transform:translateX(-50%);width:8px;height:6px;background:#252436;border-radius:0 0 2px 2px}
|
|
6701
|
+
.iso-monitor-screen{width:42px;height:28px;background:rgba(80,60,180,.3);border-radius:2px;display:flex;align-items:center;justify-content:center}
|
|
6702
|
+
.iso-monitor-blink{width:6px;height:6px;border-radius:50%;background:#6366f1;animation:monBlink .8s ease-in-out infinite}
|
|
6703
|
+
@keyframes monBlink{0%,100%{opacity:1;box-shadow:0 0 6px #6366f1}50%{opacity:.3;box-shadow:none}}
|
|
6704
|
+
/* Tool badge emoji floating above desk */
|
|
6705
|
+
.iso-tool-badge{font-size:18px;line-height:1;filter:drop-shadow(0 2px 4px rgba(0,0,0,.3));margin-bottom:-2px;user-select:none}
|
|
6537
6706
|
/* Animated status chip for [bracket tokens] */
|
|
6538
6707
|
.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}
|
|
6539
6708
|
@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)}}
|