opencroc 1.8.1 → 1.8.3

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.
Files changed (37) hide show
  1. package/dist/cli/index.js +755 -8
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.d.ts +128 -1
  4. package/dist/index.js +548 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/web/dist/assets/main-Ccg3eDNK.js +1 -0
  7. package/dist/web/dist/assets/office-runtime-B3iNctxE.css +1 -0
  8. package/dist/web/dist/assets/office-runtime-BsCh82Pj.js +183 -0
  9. package/dist/web/dist/assets/pixel-page-3BYGm7dH.js +470 -0
  10. package/dist/web/dist/assets/react-vendor-C8RhVn0h.js +49 -0
  11. package/dist/web/dist/assets/studio-page-BInoyoV2.css +1 -0
  12. package/dist/web/dist/assets/studio-page-o3SCvE_v.js +351 -0
  13. package/dist/web/dist/assets/three-addons-BdrPp04O.js +470 -0
  14. package/dist/web/dist/assets/three-core-CsxM1PCY.js +4057 -0
  15. package/dist/web/dist/index.html +15 -0
  16. package/package.json +11 -2
  17. package/dist/web/index-studio.html +0 -1644
  18. package/dist/web/index-v2-pixel.html +0 -1571
  19. package/dist/web/index.html +0 -573
  20. package/dist/web/js/agents.js +0 -465
  21. package/dist/web/js/camera.js +0 -125
  22. package/dist/web/js/dataviz.js +0 -288
  23. package/dist/web/js/effects.js +0 -345
  24. package/dist/web/js/engine.js +0 -489
  25. package/dist/web/js/office.js +0 -816
  26. package/dist/web/js/state.js +0 -37
  27. package/dist/web/js/ui.js +0 -384
  28. /package/dist/web/{assets → dist}/botreview/char_0.png +0 -0
  29. /package/dist/web/{assets → dist}/botreview/char_1.png +0 -0
  30. /package/dist/web/{assets → dist}/botreview/char_2.png +0 -0
  31. /package/dist/web/{assets → dist}/botreview/coffee-machine.gif +0 -0
  32. /package/dist/web/{assets → dist}/botreview/server.gif +0 -0
  33. /package/dist/web/{assets → dist}/botreview/walls.png +0 -0
  34. /package/dist/web/{assets → dist}/star/desk-v3.webp +0 -0
  35. /package/dist/web/{assets → dist}/star/office_bg_small.webp +0 -0
  36. /package/dist/web/{assets → dist}/star/star-idle-v5.png +0 -0
  37. /package/dist/web/{assets → dist}/star/star-working-spritesheet-grid.webp +0 -0
@@ -1,573 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN" data-theme="light">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
6
- <title>OpenCroc Studio · 3D</title>
7
- <style>
8
- /* ═══════════════════════════════════════════════════════════════════════════════
9
- OpenCroc Studio 3D — Glass-morphism Design System
10
- ═══════════════════════════════════════════════════════════════════════════════ */
11
-
12
- /* ─── CSS Reset ─────────────────────────────────────────────────────────────── */
13
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
14
- html,body{width:100%;height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans SC","Helvetica Neue",Arial,sans-serif}
15
- body{background:#000;color:var(--text)}
16
- ::-webkit-scrollbar{width:6px;height:6px}
17
- ::-webkit-scrollbar-track{background:transparent}
18
- ::-webkit-scrollbar-thumb{background:var(--text-subtle);border-radius:3px}
19
- ::-webkit-scrollbar-thumb:hover{background:var(--text-dim)}
20
- ::selection{background:var(--accent);color:#000}
21
-
22
- /* ─── Design Tokens: Dark (Default) ────────────────────────────────────────── */
23
- :root{
24
- --bg-void:#050510;--bg-deep:#0a0f1e;
25
- --bg-panel:rgba(12,18,36,0.75);--bg-panel-solid:#0c1224;
26
- --bg-card:rgba(18,26,50,0.6);--bg-hover:rgba(30,42,72,0.5);
27
- --bg-glass:rgba(15,22,42,0.55);--bg-glass-strong:rgba(15,22,42,0.8);
28
- --bg-frosted:rgba(20,28,52,0.45);--bg-input:rgba(15,22,44,0.6);
29
- --accent:#34d399;--accent-rgb:52,211,153;--accent-dim:#059669;
30
- --accent-glow:rgba(52,211,153,0.4);--accent-bg:rgba(52,211,153,0.1);
31
- --red:#f87171;--red-rgb:248,113,113;--red-glow:rgba(248,113,113,0.35);--red-bg:rgba(248,113,113,0.1);
32
- --orange:#fbbf24;--orange-rgb:251,191,36;--orange-glow:rgba(251,191,36,0.35);--orange-bg:rgba(251,191,36,0.1);
33
- --blue:#60a5fa;--blue-rgb:96,165,250;--blue-glow:rgba(96,165,250,0.35);--blue-bg:rgba(96,165,250,0.1);
34
- --purple:#a78bfa;--purple-rgb:167,139,250;--purple-glow:rgba(167,139,250,0.35);--purple-bg:rgba(167,139,250,0.1);
35
- --cyan:#22d3ee;--cyan-rgb:34,211,238;--cyan-glow:rgba(34,211,238,0.35);--cyan-bg:rgba(34,211,238,0.1);
36
- --pink:#f472b6;--pink-rgb:244,114,182;
37
- --text:#f1f5f9;--text-dim:#94a3b8;--text-subtle:#4a5568;--text-muted:#2d3748;
38
- --border:rgba(148,163,184,0.12);--border-accent:rgba(52,211,153,0.25);--border-glass:rgba(255,255,255,0.06);
39
- --shadow-xl:0 25px 50px -12px rgba(0,0,0,0.6);--shadow-lg:0 20px 40px rgba(0,0,0,0.4);
40
- --shadow-md:0 8px 24px rgba(0,0,0,0.3);--shadow-sm:0 2px 8px rgba(0,0,0,0.2);
41
- --shadow-glow:0 0 30px rgba(52,211,153,0.15);--shadow-inset:inset 0 1px 0 rgba(255,255,255,0.05);
42
- --blur-lg:blur(24px);--blur-md:blur(16px);--blur-sm:blur(8px);--blur-xs:blur(4px);
43
- --radius-xl:20px;--radius-lg:14px;--radius-md:10px;--radius-sm:6px;--radius-xs:4px;--radius-full:9999px;
44
- --ease-out-expo:cubic-bezier(0.16,1,0.3,1);--ease-spring:cubic-bezier(0.175,0.885,0.32,1.275);
45
- --transition-fast:0.15s var(--ease-out-expo);--transition:0.25s var(--ease-out-expo);
46
- --transition-slow:0.4s var(--ease-out-expo);--transition-spring:0.5s var(--ease-spring);
47
- --z-canvas:0;--z-overlay:10;--z-panel:20;--z-header:30;--z-modal:40;--z-tooltip:50;--z-notify:60;
48
- }
49
-
50
- /* ─── Design Tokens: Light ─────────────────────────────────────────────────── */
51
- [data-theme="light"]{
52
- --bg-void:#e8ecf4;--bg-deep:#f0f4fa;
53
- --bg-panel:rgba(255,255,255,0.78);--bg-panel-solid:#ffffff;
54
- --bg-card:rgba(248,250,255,0.7);--bg-hover:rgba(226,232,240,0.6);
55
- --bg-glass:rgba(255,255,255,0.6);--bg-glass-strong:rgba(255,255,255,0.85);
56
- --bg-frosted:rgba(255,255,255,0.5);--bg-input:rgba(241,245,249,0.8);
57
- --accent:#059669;--accent-rgb:5,150,105;--accent-dim:#047857;
58
- --accent-glow:rgba(5,150,105,0.25);--accent-bg:rgba(5,150,105,0.08);
59
- --red:#dc2626;--red-rgb:220,38,38;--red-glow:rgba(220,38,38,0.2);--red-bg:rgba(220,38,38,0.06);
60
- --orange:#d97706;--orange-rgb:217,119,6;--orange-glow:rgba(217,119,6,0.2);--orange-bg:rgba(217,119,6,0.06);
61
- --blue:#2563eb;--blue-rgb:37,99,235;--blue-glow:rgba(37,99,235,0.2);--blue-bg:rgba(37,99,235,0.06);
62
- --purple:#7c3aed;--purple-rgb:124,58,237;--purple-glow:rgba(124,58,237,0.2);--purple-bg:rgba(124,58,237,0.06);
63
- --cyan:#0891b2;--cyan-rgb:8,145,178;--cyan-glow:rgba(8,145,178,0.2);--cyan-bg:rgba(8,145,178,0.06);
64
- --pink:#db2777;--pink-rgb:219,39,119;
65
- --text:#0f172a;--text-dim:#475569;--text-subtle:#94a3b8;--text-muted:#cbd5e1;
66
- --border:rgba(100,116,139,0.15);--border-accent:rgba(5,150,105,0.3);--border-glass:rgba(0,0,0,0.04);
67
- --shadow-xl:0 25px 50px -12px rgba(0,0,0,0.12);--shadow-lg:0 20px 40px rgba(0,0,0,0.06);
68
- --shadow-md:0 8px 24px rgba(0,0,0,0.05);--shadow-sm:0 2px 8px rgba(0,0,0,0.03);
69
- --shadow-glow:0 0 30px rgba(5,150,105,0.08);--shadow-inset:inset 0 1px 0 rgba(255,255,255,0.8);
70
- }
71
-
72
- /* ─── 3D Canvas ────────────────────────────────────────────────────────────── */
73
- #three-canvas{position:fixed;inset:0;width:100%;height:100%;z-index:var(--z-canvas);display:block}
74
-
75
- /* ─── Glass Panel Base ─────────────────────────────────────────────────────── */
76
- .glass{background:var(--bg-glass);backdrop-filter:var(--blur-md);-webkit-backdrop-filter:var(--blur-md);border:1px solid var(--border-glass);box-shadow:var(--shadow-md),var(--shadow-inset);border-radius:var(--radius-lg);transition:background var(--transition),border-color var(--transition),box-shadow var(--transition)}
77
- .glass:hover{background:var(--bg-glass-strong);border-color:var(--border);box-shadow:var(--shadow-lg),var(--shadow-inset)}
78
- .glass-strong{background:var(--bg-glass-strong);backdrop-filter:var(--blur-lg);-webkit-backdrop-filter:var(--blur-lg);border:1px solid var(--border);box-shadow:var(--shadow-lg),var(--shadow-inset);border-radius:var(--radius-lg)}
79
-
80
- /* ─── Header ───────────────────────────────────────────────────────────────── */
81
- .header{position:fixed;top:12px;left:12px;right:12px;height:52px;z-index:var(--z-header);display:flex;align-items:center;gap:8px;padding:0 16px;background:var(--bg-glass-strong);backdrop-filter:var(--blur-lg);-webkit-backdrop-filter:var(--blur-lg);border:1px solid var(--border-glass);border-radius:var(--radius-xl);box-shadow:var(--shadow-lg),var(--shadow-inset);transition:all var(--transition)}
82
- .header::before{content:'';position:absolute;inset:0;border-radius:var(--radius-xl);background:linear-gradient(135deg,rgba(var(--accent-rgb),0.05),transparent 60%);pointer-events:none}
83
- .logo{width:32px;height:32px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--accent-bg);border:1px solid var(--border-accent);border-radius:var(--radius-md);transition:all var(--transition)}
84
- .logo:hover{transform:scale(1.08) rotate(-3deg);box-shadow:var(--shadow-glow)}
85
- .logo svg{width:20px;height:20px}
86
- .title-wrap{display:flex;flex-direction:column;gap:0;margin-right:16px;min-width:0}
87
- .title-wrap h1{font-size:14px;font-weight:700;letter-spacing:-0.02em;color:var(--text);line-height:1.2;white-space:nowrap}
88
- .title-wrap .subtitle{font-size:10px;color:var(--text-subtle);letter-spacing:0.05em;text-transform:uppercase;line-height:1}
89
- .h-divider{width:1px;height:28px;flex-shrink:0;background:var(--border);margin:0 4px}
90
-
91
- /* View Switch */
92
- .view-switch{display:flex;gap:2px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-md);padding:2px}
93
- .view-switch button{display:flex;align-items:center;gap:5px;padding:5px 12px;border:none;border-radius:var(--radius-sm);font-size:12px;font-weight:500;color:var(--text-dim);cursor:pointer;background:transparent;transition:all var(--transition);white-space:nowrap}
94
- .view-switch button:hover{color:var(--text);background:var(--bg-hover)}
95
- .view-switch button.active{color:var(--text);background:var(--bg-panel);box-shadow:var(--shadow-sm)}
96
- .view-switch button svg{width:14px;height:14px;flex-shrink:0}
97
-
98
- /* Actions */
99
- .actions{display:flex;gap:4px;align-items:center;flex-shrink:0}
100
- .btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:12px;font-weight:600;transition:all var(--transition);white-space:nowrap;position:relative;overflow:hidden}
101
- .btn::before{content:'';position:absolute;inset:0;background:linear-gradient(180deg,rgba(255,255,255,0.06),transparent);pointer-events:none}
102
- .btn-primary{background:var(--accent);color:#000;border-color:var(--accent-dim)}
103
- .btn-primary:hover{background:var(--accent-dim);transform:translateY(-1px);box-shadow:0 4px 16px var(--accent-glow)}
104
- .btn-danger{background:var(--red-bg);color:var(--red);border-color:rgba(var(--red-rgb),0.25)}
105
- .btn-danger:hover{background:rgba(var(--red-rgb),0.18);transform:translateY(-1px);box-shadow:0 4px 16px var(--red-glow)}
106
- .btn-secondary{background:var(--bg-card);color:var(--text-dim)}
107
- .btn-secondary:hover{background:var(--bg-hover);color:var(--text);transform:translateY(-1px)}
108
- .btn:disabled{opacity:0.4;cursor:not-allowed;transform:none!important;box-shadow:none!important}
109
- .btn svg{width:14px;height:14px;flex-shrink:0}
110
- .btn:active{transform:translateY(0)}
111
-
112
- /* Run Mode */
113
- .run-mode-wrap{position:relative}
114
- .run-mode-wrap select{appearance:none;background:var(--bg-card);color:var(--text-dim);border:1px solid var(--border);border-radius:var(--radius-sm);padding:5px 28px 5px 10px;font-size:12px;font-weight:500;cursor:pointer;transition:all var(--transition);outline:none}
115
- .run-mode-wrap select:hover{border-color:var(--border-accent);color:var(--text)}
116
- .run-mode-wrap::after{content:'▾';position:absolute;right:8px;top:50%;transform:translateY(-50%);color:var(--text-subtle);font-size:10px;pointer-events:none}
117
-
118
- /* Stats */
119
- .stats{display:flex;gap:2px;margin-left:auto;flex-shrink:0}
120
- .stat-box{display:flex;flex-direction:column;align-items:center;padding:4px 12px;min-width:56px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-sm);transition:all var(--transition)}
121
- .stat-box:first-child{border-radius:var(--radius-sm) var(--radius-xs) var(--radius-xs) var(--radius-sm)}
122
- .stat-box:last-child{border-radius:var(--radius-xs) var(--radius-sm) var(--radius-sm) var(--radius-xs)}
123
- .stat-box:hover{background:var(--bg-hover);border-color:var(--border-accent)}
124
- .stat-label{font-size:9px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-subtle);line-height:1}
125
- .stat-value{font-size:16px;font-weight:700;color:var(--accent);line-height:1.3;font-variant-numeric:tabular-nums}
126
-
127
- /* Header end */
128
- .header-end{display:flex;align-items:center;gap:6px;margin-left:8px}
129
- #theme-toggle{width:32px;height:32px;border:none;background:var(--bg-card);border-radius:var(--radius-sm);cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-dim);transition:all var(--transition);border:1px solid var(--border)}
130
- #theme-toggle:hover{background:var(--bg-hover);color:var(--text);transform:scale(1.05)}
131
- #theme-toggle svg{width:16px;height:16px}
132
- #conn-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--text-subtle);transition:background 0.3s}
133
- #conn-dot.connected{background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:conn-pulse 2s infinite}
134
-
135
- /* ─── Sidebar ──────────────────────────────────────────────────────────────── */
136
- .sidebar{position:fixed;top:76px;left:12px;bottom:240px;width:220px;z-index:var(--z-panel);display:flex;flex-direction:column;overflow:hidden;background:var(--bg-glass);backdrop-filter:var(--blur-md);-webkit-backdrop-filter:var(--blur-md);border:1px solid var(--border-glass);border-radius:var(--radius-lg);box-shadow:var(--shadow-md),var(--shadow-inset);transition:all var(--transition-slow)}
137
- .sidebar.collapsed{width:48px}
138
- .sidebar.collapsed .sidebar-content{opacity:0;pointer-events:none}
139
- .sidebar.collapsed .sidebar-toggle svg{transform:rotate(180deg)}
140
- .sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 8px;flex-shrink:0}
141
- .sidebar-header h3{font-size:11px;text-transform:uppercase;letter-spacing:0.08em;color:var(--text-subtle);font-weight:600}
142
- .sidebar-toggle{width:24px;height:24px;border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-subtle);border-radius:var(--radius-xs);transition:all var(--transition)}
143
- .sidebar-toggle:hover{background:var(--bg-hover);color:var(--text)}
144
- .sidebar-toggle svg{width:14px;height:14px;transition:transform var(--transition)}
145
- .sidebar-content{flex:1;overflow-y:auto;padding:0 8px 8px;transition:opacity var(--transition)}
146
- .mod-item{display:flex;align-items:center;gap:8px;padding:7px 10px;margin-bottom:2px;border-radius:var(--radius-sm);cursor:pointer;transition:all var(--transition)}
147
- .mod-item:hover{background:var(--bg-hover)}
148
- .mod-item.active{background:var(--accent-bg);color:var(--accent)}
149
- .mod-item .dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;background:var(--accent);opacity:0.6;transition:all var(--transition)}
150
- .mod-item:hover .dot{opacity:1;transform:scale(1.3)}
151
- .mod-item .name{flex:1;font-size:12px;font-weight:500;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color var(--transition)}
152
- .mod-item:hover .name{color:var(--text)}
153
- .mod-item .count{font-size:11px;color:var(--text-subtle);font-variant-numeric:tabular-nums;padding:1px 6px;background:var(--bg-card);border-radius:var(--radius-xs);font-weight:500;transition:all var(--transition)}
154
- .mod-item:hover .count{background:var(--bg-hover);color:var(--text-dim)}
155
- .agent-item{display:flex;align-items:center;gap:8px;padding:8px 10px;margin-bottom:2px;border-radius:var(--radius-sm);transition:all var(--transition)}
156
- .agent-item .status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;transition:all 0.3s}
157
- .agent-item .status-dot.working{background:var(--accent);box-shadow:0 0 6px var(--accent-glow);animation:dot-pulse 1s infinite}
158
- .agent-item .status-dot.testing{background:var(--blue);box-shadow:0 0 6px var(--blue-glow);animation:dot-pulse 1s infinite}
159
- .agent-item .status-dot.thinking{background:var(--purple);box-shadow:0 0 6px var(--purple-glow);animation:dot-pulse 1.2s infinite}
160
- .agent-item .status-dot.error,.agent-item .status-dot.failed{background:var(--red);box-shadow:0 0 6px var(--red-glow);animation:dot-shake 0.3s infinite}
161
- .agent-item .status-dot.done,.agent-item .status-dot.passed{background:var(--accent);box-shadow:0 0 6px var(--accent-glow)}
162
- .agent-item .status-dot.idle{background:var(--text-subtle)}
163
- .agent-item .agent-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
164
- .agent-item .agent-name{font-size:12px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
165
- .agent-item .agent-role{font-size:10px;color:var(--text-subtle);text-transform:uppercase;letter-spacing:0.04em}
166
-
167
- /* ─── Right Panel ──────────────────────────────────────────────────────────── */
168
- .right-panel{position:fixed;top:76px;right:12px;bottom:240px;width:320px;z-index:var(--z-panel);display:flex;flex-direction:column;background:var(--bg-glass);backdrop-filter:var(--blur-md);-webkit-backdrop-filter:var(--blur-md);border:1px solid var(--border-glass);border-radius:var(--radius-lg);box-shadow:var(--shadow-md),var(--shadow-inset);overflow:hidden;transition:all var(--transition-slow)}
169
- .panel-tabs{display:flex;gap:0;padding:6px 6px 0;flex-shrink:0;border-bottom:1px solid var(--border)}
170
- .tab{flex:1;display:flex;align-items:center;justify-content:center;gap:5px;padding:8px 4px;border:none;background:transparent;font-size:12px;font-weight:500;color:var(--text-subtle);cursor:pointer;position:relative;border-radius:var(--radius-sm) var(--radius-sm) 0 0;transition:all var(--transition);white-space:nowrap}
171
- .tab:hover{color:var(--text-dim);background:var(--bg-hover)}
172
- .tab.active{color:var(--accent);background:var(--bg-card)}
173
- .tab.active::after{content:'';position:absolute;bottom:0;left:20%;right:20%;height:2px;background:var(--accent);border-radius:1px}
174
- .tab svg{width:13px;height:13px;flex-shrink:0}
175
- .tab-badge{font-size:9px;font-weight:700;min-width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;background:var(--accent-bg);color:var(--accent);border-radius:var(--radius-full);padding:0 4px;font-variant-numeric:tabular-nums}
176
- .tab-badge.alert{background:var(--red-bg);color:var(--red)}
177
- .panel-content{flex:1;overflow-y:auto;padding:8px}
178
- .panel-content.hidden{display:none}
179
-
180
- /* Log */
181
- .log-entry{padding:5px 10px;margin-bottom:2px;border-radius:var(--radius-xs);font-size:12px;line-height:1.5;font-family:"SF Mono","Fira Code","Cascadia Code",Consolas,monospace;transition:background var(--transition);display:flex;gap:8px;align-items:flex-start}
182
- .log-entry:hover{background:var(--bg-hover)}
183
- .log-entry .timestamp{color:var(--text-subtle);font-size:10px;flex-shrink:0;font-variant-numeric:tabular-nums;padding-top:1px}
184
- .log-entry .message{color:var(--text-dim);word-break:break-word;flex:1}
185
- .log-entry.warn .message{color:var(--orange)}
186
- .log-entry.error .message{color:var(--red)}
187
- .log-entry.success .message{color:var(--accent)}
188
- .tw-cursor{display:inline-block;width:2px;height:1em;background:var(--accent);margin-left:2px;vertical-align:text-bottom;animation:tw-blink 0.6s step-end infinite}
189
- @keyframes tw-blink{0%,100%{opacity:1}50%{opacity:0}}
190
-
191
- /* Files */
192
- .file-item{display:flex;align-items:center;gap:8px;padding:8px 10px;margin-bottom:2px;border-radius:var(--radius-sm);cursor:pointer;transition:all var(--transition)}
193
- .file-item:hover{background:var(--bg-hover)}
194
- .file-item svg{width:14px;height:14px;flex-shrink:0;color:var(--text-subtle)}
195
- .file-item .file-name{flex:1;font-size:12px;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
196
- .file-item:hover .file-name{color:var(--text)}
197
-
198
- /* Results */
199
- .results-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:4px}
200
- .result-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-md);padding:12px;display:flex;flex-direction:column;gap:4px;transition:all var(--transition)}
201
- .result-card:hover{border-color:var(--border-accent);background:var(--bg-hover)}
202
- .result-card .label{font-size:10px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-subtle)}
203
- .result-card .value{font-size:24px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1}
204
- .result-card.passed .value{color:var(--accent)}
205
- .result-card.failed .value{color:var(--red)}
206
- .result-card.skipped .value{color:var(--orange)}
207
- .result-card.timeout .value{color:var(--purple)}
208
- .pass-rate-bar{height:6px;background:var(--bg-card);border-radius:3px;overflow:hidden;margin:8px 4px;border:1px solid var(--border)}
209
- .pass-rate-bar .fill{height:100%;border-radius:3px;background:linear-gradient(90deg,var(--accent),var(--cyan));transition:width 0.8s var(--ease-out-expo)}
210
- .quality-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;padding:4px}
211
- .quality-item{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px;text-align:center;transition:all var(--transition)}
212
- .quality-item:hover{border-color:var(--border-accent)}
213
- .quality-item .q-label{font-size:9px;text-transform:uppercase;letter-spacing:0.04em;color:var(--text-subtle)}
214
- .quality-item .q-value{font-size:14px;font-weight:700;color:var(--blue);margin-top:2px}
215
-
216
- /* ─── Bottom Bar ───────────────────────────────────────────────────────────── */
217
- .bottom-bar{position:fixed;bottom:12px;left:12px;right:12px;height:216px;z-index:var(--z-panel);display:flex;flex-direction:column;background:var(--bg-glass);backdrop-filter:var(--blur-md);-webkit-backdrop-filter:var(--blur-md);border:1px solid var(--border-glass);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg),var(--shadow-inset);overflow:hidden;transition:all var(--transition-slow)}
218
- .bottom-bar.collapsed{height:40px}
219
- .bottom-bar.collapsed .desk-row{opacity:0;pointer-events:none}
220
- .bottom-bar-header{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;flex-shrink:0;border-bottom:1px solid var(--border);cursor:pointer}
221
- .bottom-bar-header h3{font-size:11px;text-transform:uppercase;letter-spacing:0.08em;color:var(--text-subtle);font-weight:600}
222
- .bottom-bar-header .toggle-icon{color:var(--text-subtle);transition:transform var(--transition)}
223
- .bottom-bar.collapsed .toggle-icon{transform:rotate(180deg)}
224
- .desk-row{flex:1;display:flex;gap:8px;padding:8px 12px;overflow-x:auto;overflow-y:hidden;transition:opacity var(--transition)}
225
-
226
- /* Desk Cards */
227
- .desk-card{flex:0 0 200px;display:flex;flex-direction:column;gap:6px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-md);padding:12px;position:relative;overflow:hidden;transition:all var(--transition)}
228
- .desk-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:var(--accent);opacity:0;transition:opacity var(--transition)}
229
- .desk-card:hover{border-color:var(--border-accent);transform:translateY(-2px);box-shadow:var(--shadow-md)}
230
- .desk-card:hover::before{opacity:1}
231
- .desk-card.working::before{background:var(--accent);opacity:1}
232
- .desk-card.error::before,.desk-card.failed::before{background:var(--red);opacity:1}
233
- .desk-card.thinking::before{background:var(--purple);opacity:1}
234
- .desk-card.testing::before{background:var(--blue);opacity:1}
235
- .desk-card .card-top{display:flex;align-items:center;gap:8px}
236
- .desk-card .card-avatar{width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--bg-hover);border:1px solid var(--border);border-radius:var(--radius-sm)}
237
- .desk-card .card-avatar svg{width:20px;height:20px}
238
- .desk-card .card-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
239
- .desk-card .card-name{font-size:13px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
240
- .desk-card .card-role{font-size:10px;color:var(--text-subtle);text-transform:uppercase;letter-spacing:0.04em}
241
- .desk-card .status-badge{position:absolute;top:8px;right:8px;font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:0.04em;padding:2px 6px;border-radius:var(--radius-xs)}
242
- .desk-card .status-badge.working{background:var(--accent-bg);color:var(--accent)}
243
- .desk-card .status-badge.testing{background:var(--blue-bg);color:var(--blue)}
244
- .desk-card .status-badge.thinking{background:var(--purple-bg);color:var(--purple)}
245
- .desk-card .status-badge.error,.desk-card .status-badge.failed{background:var(--red-bg);color:var(--red)}
246
- .desk-card .status-badge.done,.desk-card .status-badge.passed{background:var(--accent-bg);color:var(--accent)}
247
- .desk-card .status-badge.idle{background:var(--bg-hover);color:var(--text-subtle)}
248
- .desk-card .card-task{font-size:11px;color:var(--text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:16px}
249
- .desk-card .progress-bar{height:3px;background:var(--bg-hover);border-radius:2px;overflow:hidden}
250
- .desk-card .progress-bar .fill{height:100%;border-radius:2px;background:linear-gradient(90deg,var(--accent),var(--cyan));transition:width 0.4s var(--ease-out-expo)}
251
-
252
- /* ─── File Preview Modal ───────────────────────────────────────────────────── */
253
- .file-preview{position:fixed;inset:0;z-index:var(--z-modal);display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity var(--transition)}
254
- .file-preview.visible{opacity:1;pointer-events:auto}
255
- .file-preview .backdrop{position:absolute;inset:0;background:rgba(0,0,0,0.5);backdrop-filter:var(--blur-sm)}
256
- .file-preview .fp-dialog{position:relative;width:min(800px,90vw);height:min(600px,80vh);background:var(--bg-glass-strong);backdrop-filter:var(--blur-lg);border:1px solid var(--border);border-radius:var(--radius-xl);box-shadow:var(--shadow-xl);display:flex;flex-direction:column;overflow:hidden;transform:scale(0.95) translateY(10px);transition:transform var(--transition-spring)}
257
- .file-preview.visible .fp-dialog{transform:scale(1) translateY(0)}
258
- .fp-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);flex-shrink:0}
259
- .fp-header .fp-title{font-size:13px;font-weight:600;color:var(--text)}
260
- .fp-header .fp-close{width:28px;height:28px;border:none;background:var(--bg-card);border-radius:var(--radius-sm);cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-dim);transition:all var(--transition);border:1px solid var(--border)}
261
- .fp-header .fp-close:hover{background:var(--red-bg);color:var(--red);border-color:rgba(var(--red-rgb),0.25)}
262
- .fp-code{flex:1;overflow:auto;padding:16px;font-family:"SF Mono","Fira Code","Cascadia Code",Consolas,monospace;font-size:12px;line-height:1.6;color:var(--text-dim);white-space:pre-wrap;word-break:break-all;tab-size:2}
263
-
264
- /* ─── Tooltip ──────────────────────────────────────────────────────────────── */
265
- .tooltip{position:fixed;z-index:var(--z-tooltip);padding:6px 10px;background:var(--bg-glass-strong);backdrop-filter:var(--blur-md);border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);font-size:11px;color:var(--text);pointer-events:none;opacity:0;transition:opacity 0.15s;max-width:280px}
266
- .tooltip.visible{opacity:1}
267
-
268
- /* ─── Shortcut Legend ──────────────────────────────────────────────────────── */
269
- .shortcut-legend{position:fixed;bottom:240px;right:12px;z-index:var(--z-notify);display:grid;grid-template-columns:auto 1fr;gap:4px 10px;padding:12px 16px;background:var(--bg-glass-strong);backdrop-filter:var(--blur-lg);border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);opacity:0;transform:translateY(8px) scale(0.96);pointer-events:none;transition:all var(--transition-spring)}
270
- .shortcut-legend.visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}
271
- .shortcut-legend kbd{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 6px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-xs);font-size:11px;font-weight:600;color:var(--text);font-family:inherit;box-shadow:0 1px 2px rgba(0,0,0,0.15)}
272
- .shortcut-legend span{font-size:12px;color:var(--text-dim);display:flex;align-items:center}
273
-
274
- /* ─── Loading ──────────────────────────────────────────────────────────────── */
275
- .loading-overlay{position:fixed;inset:0;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;background:var(--bg-void);transition:opacity 0.8s var(--ease-out-expo)}
276
- .loading-overlay.hidden{opacity:0;pointer-events:none}
277
- .loading-bar{width:200px;height:3px;background:var(--bg-card);border-radius:2px;overflow:hidden}
278
- .loading-bar .fill{width:0%;height:100%;background:linear-gradient(90deg,var(--accent),var(--cyan));border-radius:2px;transition:width 0.3s var(--ease-out-expo)}
279
- .loading-text{font-size:12px;color:var(--text-subtle);letter-spacing:0.04em}
280
-
281
- /* ─── Toast ────────────────────────────────────────────────────────────────── */
282
- .toast-container{position:fixed;top:76px;right:12px;z-index:var(--z-notify);display:flex;flex-direction:column;gap:8px;pointer-events:none}
283
- .toast{display:flex;align-items:center;gap:8px;padding:10px 16px;background:var(--bg-glass-strong);backdrop-filter:var(--blur-md);border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);font-size:12px;color:var(--text);pointer-events:auto;animation:toast-in 0.4s var(--ease-spring),toast-out 0.3s ease-in 3.7s forwards;max-width:320px}
284
- .toast.success{border-left:3px solid var(--accent)}
285
- .toast.error{border-left:3px solid var(--red)}
286
- .toast.warning{border-left:3px solid var(--orange)}
287
- .toast.info{border-left:3px solid var(--blue)}
288
-
289
- /* ─── 3D HUD ───────────────────────────────────────────────────────────────── */
290
- .bubble-3d{padding:6px 12px;background:var(--bg-glass-strong);backdrop-filter:var(--blur-sm);border:1px solid var(--border-accent);border-radius:12px 12px 12px 2px;font-size:12px;color:var(--text);box-shadow:var(--shadow-sm);animation:bubble-3d-in 0.3s var(--ease-spring);pointer-events:none;white-space:nowrap}
291
- .agent-label-3d{padding:3px 8px;background:var(--bg-glass);backdrop-filter:var(--blur-xs);border:1px solid var(--border-glass);border-radius:var(--radius-sm);font-size:10px;font-weight:600;color:var(--text);text-align:center;pointer-events:none;box-shadow:var(--shadow-sm);white-space:nowrap}
292
- .agent-label-3d .role{display:block;font-size:8px;font-weight:400;color:var(--text-subtle);text-transform:uppercase;letter-spacing:0.04em}
293
-
294
- /* ─── Animations ───────────────────────────────────────────────────────────── */
295
- @keyframes conn-pulse{0%{box-shadow:0 0 0 0 var(--accent-glow)}70%{box-shadow:0 0 0 6px transparent}100%{box-shadow:0 0 0 0 transparent}}
296
- @keyframes dot-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:0.5;transform:scale(0.85)}}
297
- @keyframes dot-shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-1px)}75%{transform:translateX(1px)}}
298
- @keyframes toast-in{from{opacity:0;transform:translateX(40px) scale(0.9)}to{opacity:1;transform:translateX(0) scale(1)}}
299
- @keyframes toast-out{from{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(40px)}}
300
- @keyframes bubble-3d-in{from{opacity:0;transform:translateY(6px) scale(0.9)}to{opacity:1;transform:translateY(0) scale(1)}}
301
- @keyframes skeleton-pulse{0%{background-position:200% 0}100%{background-position:-200% 0}}
302
- @keyframes float-gentle{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
303
- @keyframes rotate-slow{from{transform:rotate(0)}to{transform:rotate(360deg)}}
304
- @keyframes glow-pulse{0%,100%{opacity:0.6}50%{opacity:1}}
305
- .skeleton{background:linear-gradient(90deg,var(--bg-card) 25%,var(--bg-hover) 50%,var(--bg-card) 75%);background-size:200% 100%;animation:skeleton-pulse 1.5s ease infinite;border-radius:var(--radius-sm)}
306
-
307
- /* ─── Responsive ───────────────────────────────────────────────────────────── */
308
- @media(max-width:1200px){.sidebar{width:180px}.right-panel{width:280px}.stats{display:none}}
309
- @media(max-width:960px){.sidebar{display:none}.right-panel{width:260px}}
310
- @media(max-width:768px){.header{padding:0 10px;gap:4px}.right-panel{display:none}.bottom-bar{height:180px}}
311
- @media print{.header,.sidebar,.bottom-bar,.right-panel,.shortcut-legend,.tooltip,.file-preview,.loading-overlay,.toast-container{display:none!important}#three-canvas{position:static;width:100%;height:auto}}
312
- </style>
313
- </head>
314
- <body>
315
-
316
- <!-- Loading Overlay -->
317
- <div class="loading-overlay" id="loading-overlay">
318
- <svg width="48" height="48" viewBox="0 0 16 16" fill="none" style="opacity:0.8"><rect x="2" y="4" width="12" height="10" rx="1" fill="none" stroke="var(--accent)" stroke-width="1.5"/><circle cx="8" cy="9" r="2.5" fill="var(--accent)"/><rect x="5" y="2" width="6" height="2" rx="0.5" fill="var(--accent)" opacity="0.6"/></svg>
319
- <div class="loading-bar"><div class="fill" id="loading-fill"></div></div>
320
- <div class="loading-text" id="loading-text">Initializing 3D Engine…</div>
321
- </div>
322
-
323
- <!-- 3D Canvas -->
324
- <canvas id="three-canvas"></canvas>
325
-
326
- <!-- Header -->
327
- <div class="header" id="header">
328
- <div class="logo"><svg viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="10" rx="1" fill="none" stroke="var(--accent)" stroke-width="1.5"/><circle cx="8" cy="9" r="2.5" fill="var(--accent)"/><rect x="5" y="2" width="6" height="2" rx="0.5" fill="var(--accent)" opacity="0.6"/></svg></div>
329
- <div class="title-wrap"><h1>OpenCroc Studio</h1><span class="subtitle">3D Ops Dashboard · Real-time Multi-Agent Runtime</span></div>
330
- <div class="h-divider"></div>
331
- <div class="view-switch">
332
- <button id="view-3d" class="active"><svg viewBox="0 0 16 16" fill="none"><path d="M8 1L14.5 5v6L8 15 1.5 11V5z" stroke="currentColor" stroke-width="1.2"/><path d="M8 1v14M1.5 5L8 9l6.5-4" stroke="currentColor" stroke-width="1" opacity="0.5"/></svg>3D Office</button>
333
- <button id="view-graph"><svg viewBox="0 0 16 16" fill="none"><circle cx="4" cy="4" r="2" fill="currentColor" opacity="0.6"/><circle cx="12" cy="4" r="2" fill="currentColor" opacity="0.6"/><circle cx="8" cy="12" r="2" fill="currentColor" opacity="0.6"/><line x1="4" y1="6" x2="8" y2="10" stroke="currentColor" stroke-width="1"/><line x1="12" y1="6" x2="8" y2="10" stroke="currentColor" stroke-width="1"/></svg>Graph</button>
334
- </div>
335
- <div class="h-divider"></div>
336
- <div class="actions">
337
- <button class="btn btn-secondary" id="btn-scan"><svg viewBox="0 0 16 16" fill="none"><path d="M2 8h12M4 4h8M6 12h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>Scan</button>
338
- <button class="btn btn-primary" id="btn-pipeline"><svg viewBox="0 0 16 16" fill="none"><path d="M2 8h3l2-4 2 8 2-4h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>Pipeline</button>
339
- <div class="run-mode-wrap"><select id="run-mode"><option value="auto">Auto</option><option value="reuse">Reuse</option><option value="managed">Managed</option></select></div>
340
- <button class="btn btn-secondary" id="btn-run-tests"><svg viewBox="0 0 16 16" fill="none"><path d="M4 2v12l8-6z" fill="currentColor" opacity="0.7"/></svg>Tests</button>
341
- <button class="btn btn-secondary" id="btn-reports"><svg viewBox="0 0 16 16" fill="none"><rect x="3" y="1" width="10" height="14" rx="1" stroke="currentColor" stroke-width="1.2"/><line x1="5" y1="5" x2="11" y2="5" stroke="currentColor" stroke-width="1" opacity="0.5"/><line x1="5" y1="8" x2="11" y2="8" stroke="currentColor" stroke-width="1" opacity="0.5"/><line x1="5" y1="11" x2="9" y2="11" stroke="currentColor" stroke-width="1" opacity="0.5"/></svg>Reports</button>
342
- <button class="btn btn-danger" id="btn-reset"><svg viewBox="0 0 16 16" fill="none"><path d="M2 8a6 6 0 0111.3-2.8M14 8a6 6 0 01-11.3 2.8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M14 2v4h-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>Reset</button>
343
- </div>
344
- <div class="stats">
345
- <div class="stat-box"><span class="stat-label">MODULES</span><span class="stat-value" id="s-mod">0</span></div>
346
- <div class="stat-box"><span class="stat-label">MODELS</span><span class="stat-value" id="s-mdl">0</span></div>
347
- <div class="stat-box"><span class="stat-label">APIs</span><span class="stat-value" id="s-api">0</span></div>
348
- <div class="stat-box"><span class="stat-label">TESTS</span><span class="stat-value" id="s-files">-</span></div>
349
- </div>
350
- <div class="header-end">
351
- <button id="theme-toggle" title="Toggle theme"><svg id="theme-icon-dark" viewBox="0 0 16 16" fill="none"><path d="M8 1a7 7 0 100 14 5 5 0 010-14z" fill="currentColor"/></svg><svg id="theme-icon-light" viewBox="0 0 16 16" fill="none" style="display:none"><circle cx="8" cy="8" r="3" fill="currentColor"/><g stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="8" y1="1" x2="8" y2="3"/><line x1="8" y1="13" x2="8" y2="15"/><line x1="1" y1="8" x2="3" y2="8"/><line x1="13" y1="8" x2="15" y2="8"/><line x1="3.05" y1="3.05" x2="4.46" y2="4.46"/><line x1="11.54" y1="11.54" x2="12.95" y2="12.95"/><line x1="3.05" y1="12.95" x2="4.46" y2="11.54"/><line x1="11.54" y1="4.46" x2="12.95" y2="3.05"/></g></svg></button>
352
- <div id="conn-dot" title="WebSocket"></div>
353
- </div>
354
- </div>
355
-
356
- <!-- Sidebar -->
357
- <div class="sidebar" id="sidebar">
358
- <div class="sidebar-header"><h3>Modules</h3><button class="sidebar-toggle" id="sidebar-toggle"><svg viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></button></div>
359
- <div class="sidebar-content" id="sidebar-content"><div id="mod-list"></div><div id="agent-sidebar" style="margin-top:12px"></div></div>
360
- </div>
361
-
362
- <!-- Right Panel -->
363
- <div class="right-panel" id="right-panel">
364
- <div class="panel-tabs">
365
- <button class="tab active" data-tab="log"><svg viewBox="0 0 16 16" fill="none"><path d="M2 3h12M2 7h8M2 11h10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>Log</button>
366
- <button class="tab" data-tab="files"><svg viewBox="0 0 16 16" fill="none"><path d="M3 1h6l4 4v10H3z" stroke="currentColor" stroke-width="1.2"/><path d="M9 1v4h4" stroke="currentColor" stroke-width="1" opacity="0.5"/></svg>Tests</button>
367
- <button class="tab" data-tab="results"><svg viewBox="0 0 16 16" fill="none"><polyline points="2,12 5,6 9,10 14,3" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>Results</button>
368
- <button class="tab" data-tab="reports"><svg viewBox="0 0 16 16" fill="none"><rect x="2" y="2" width="12" height="12" rx="1" stroke="currentColor" stroke-width="1.2"/><path d="M5 10V7M8 10V5M11 10V8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>Reports</button>
369
- </div>
370
- <div class="panel-content" id="log-list"></div>
371
- <div class="panel-content hidden" id="file-list"></div>
372
- <div class="panel-content hidden" id="results-panel"></div>
373
- <div class="panel-content hidden" id="reports-panel"></div>
374
- </div>
375
-
376
- <!-- Bottom Bar -->
377
- <div class="bottom-bar" id="bottom-bar">
378
- <div class="bottom-bar-header" id="bottom-bar-toggle"><h3>Agent Desks</h3><svg class="toggle-icon" width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M4 10l4-4 4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
379
- <div class="desk-row" id="desk-row"></div>
380
- </div>
381
-
382
- <!-- File Preview -->
383
- <div class="file-preview" id="file-preview">
384
- <div class="backdrop" id="fp-backdrop"></div>
385
- <div class="fp-dialog"><div class="fp-header"><span class="fp-title" id="fp-title">File Preview</span><button class="fp-close" id="fp-close"><svg viewBox="0 0 16 16" width="14" height="14" fill="none"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button></div><pre class="fp-code" id="fp-code"></pre></div>
386
- </div>
387
-
388
- <!-- Tooltip -->
389
- <div class="tooltip" id="tooltip"><div class="tt-name"></div><div class="tt-module"></div><div class="tt-type"></div></div>
390
-
391
- <!-- Shortcut Legend -->
392
- <div class="shortcut-legend" id="shortcut-legend">
393
- <kbd>1</kbd><span>3D Office</span><kbd>2</kbd><span>Graph View</span>
394
- <kbd>S</kbd><span>Scan</span><kbd>P</kbd><span>Pipeline</span>
395
- <kbd>T</kbd><span>Run Tests</span><kbd>R</kbd><span>Reports</span>
396
- <kbd>X</kbd><span>Reset</span><kbd>D</kbd><span>Dark/Light</span>
397
- <kbd>?</kbd><span>Shortcuts</span><kbd>Esc</kbd><span>Close</span>
398
- </div>
399
-
400
- <!-- Toast Container -->
401
- <div class="toast-container" id="toast-container"></div>
402
-
403
- <!-- Import Map -->
404
- <script type="importmap">{"imports":{"three":"https://unpkg.com/three@0.170.0/build/three.module.js","three/addons/":"https://unpkg.com/three@0.170.0/examples/jsm/"}}</script>
405
-
406
- <!-- Main App -->
407
- <script type="module">
408
- import { createEngine, resizeEngine, getRenderer, getScene, getCamera, getComposer, getClock } from './js/engine.js';
409
- import { createOffice, updateOfficeLighting, getFloorY } from './js/office.js';
410
- import { AgentManager } from './js/agents.js';
411
- import { ParticleManager } from './js/effects.js';
412
- import { CameraController } from './js/camera.js';
413
- import { GraphViz, HologramDisplay } from './js/dataviz.js';
414
- import { StateManager } from './js/state.js';
415
- import { UIManager } from './js/ui.js';
416
-
417
- /* ─── Constants ────────────────────────────────────────────────────────────── */
418
- const ICONS={
419
- croc:'<svg viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="10" rx="1" stroke="currentColor" stroke-width="1.5"/><circle cx="8" cy="9" r="2.5" fill="currentColor"/><rect x="5" y="2" width="6" height="2" rx="0.5" fill="currentColor" opacity="0.6"/></svg>',
420
- parser:'<svg viewBox="0 0 16 16" fill="none"><rect x="2" y="9" width="3" height="5" rx="0.5" fill="currentColor" opacity="0.7"/><rect x="6.5" y="5" width="3" height="9" rx="0.5" fill="currentColor" opacity="0.8"/><rect x="11" y="2" width="3" height="12" rx="0.5" fill="currentColor"/></svg>',
421
- analyzer:'<svg viewBox="0 0 16 16" fill="none"><rect x="2" y="8" width="2.5" height="6" rx="0.5" fill="currentColor" opacity="0.5"/><rect x="5.5" y="5" width="2.5" height="9" rx="0.5" fill="currentColor" opacity="0.7"/><rect x="9" y="3" width="2.5" height="11" rx="0.5" fill="currentColor" opacity="0.85"/><rect x="12.5" y="6" width="2.5" height="8" rx="0.5" fill="currentColor"/></svg>',
422
- tester:'<svg viewBox="0 0 16 16" fill="none"><path d="M6 2h4v4l3 7a1 1 0 01-1 1H4a1 1 0 01-1-1l3-7V2z" stroke="currentColor" stroke-width="1.2"/><line x1="5" y1="2" x2="11" y2="2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',
423
- healer:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 2L3 9h4l-1 5 6-7H8l1-5z" fill="currentColor" opacity="0.8"/></svg>',
424
- planner:'<svg viewBox="0 0 16 16" fill="none"><rect x="3" y="1" width="10" height="14" rx="1" stroke="currentColor" stroke-width="1.2"/><path d="M6 1V3M10 1V3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><line x1="5" y1="6" x2="11" y2="6" stroke="currentColor" stroke-width="1" opacity="0.4"/><line x1="5" y1="9" x2="11" y2="9" stroke="currentColor" stroke-width="1" opacity="0.4"/><line x1="5" y1="12" x2="9" y2="12" stroke="currentColor" stroke-width="1" opacity="0.4"/></svg>',
425
- reporter:'<svg viewBox="0 0 16 16" fill="none"><polyline points="2,12 5,6 8,9 11,4 14,7" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',
426
- };
427
- const ROLE_ICONS={parser:ICONS.parser,analyzer:ICONS.analyzer,tester:ICONS.tester,healer:ICONS.healer,planner:ICONS.planner,reporter:ICONS.reporter};
428
- const BUBBLE_TEXTS={
429
- working:['正在执行...','快了快了','处理中...','加油 💪'],testing:['跑测试中...','验证 API...','等结果...'],
430
- thinking:['让我想想...','分析中...','推理...','🤔'],error:['出错了!','修复中...','糟糕...','排查问题...'],
431
- idle:['摸鱼中~','等任务...','☕ 喝咖啡','zzZ'],done:['搞定!','完成 ✓','下一个!'],
432
- passed:['全绿 ✓','测试通过!'],failed:['有失败...','需要修复'],
433
- };
434
- function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')}
435
-
436
- /* ─── Managers ─────────────────────────────────────────────────────────────── */
437
- const state=new StateManager();
438
- const ui=new UIManager(state,{ICONS,ROLE_ICONS,BUBBLE_TEXTS,esc});
439
- let agentMgr, particleMgr, camCtrl, graphViz, hologram;
440
-
441
- /* ─── Boot ─────────────────────────────────────────────────────────────────── */
442
- async function boot(){
443
- ui.setLoading(5,'Creating state…');
444
- state.set({project:null,graph:{nodes:[],edges:[]},agents:[],ws:null,running:false,
445
- generatedFiles:[],testMetrics:null,testQuality:null,reports:[],runMode:'auto',
446
- currentView:'3d',theme:localStorage.getItem('opencroc-theme')||'light',modMeta:new Map(),nodePos:new Map()});
447
-
448
- const theme=state.get('theme');
449
- document.documentElement.setAttribute('data-theme',theme);
450
- if(theme==='light'){document.getElementById('theme-icon-dark').style.display='none';document.getElementById('theme-icon-light').style.display=''}
451
-
452
- ui.setLoading(10,'Initializing 3D engine…');
453
- const canvas=document.getElementById('three-canvas');
454
- await createEngine(canvas,theme);
455
-
456
- ui.setLoading(25,'Building office…');
457
- await createOffice(theme);
458
-
459
- ui.setLoading(40,'Setting up camera…');
460
- camCtrl=new CameraController(canvas,getCamera(),getScene());
461
-
462
- ui.setLoading(50,'Creating agents…');
463
- agentMgr=new AgentManager(getScene());
464
-
465
- ui.setLoading(60,'Creating effects…');
466
- particleMgr=new ParticleManager(getScene());
467
-
468
- ui.setLoading(70,'Creating data visualization…');
469
- graphViz=new GraphViz(getScene());
470
- hologram=new HologramDisplay(getScene());
471
-
472
- ui.setLoading(80,'Connecting…');
473
- ui.init({doScan,doPipeline,doReset,doRunTests,doReports,toggleTheme,setView,openFilePreview,openReportPreview});
474
- await fetchProject();
475
-
476
- ui.setLoading(90,'Starting WebSocket…');
477
- connectWS();
478
-
479
- ui.setLoading(100,'Ready!');
480
- setTimeout(()=>document.getElementById('loading-overlay').classList.add('hidden'),400);
481
-
482
- requestAnimationFrame(renderLoop);
483
- ui.addLog('OpenCroc Studio 3D ready — press ? for shortcuts','info',true);
484
- }
485
-
486
- /* ─── Render Loop ──────────────────────────────────────────────────────────── */
487
- function renderLoop(){
488
- requestAnimationFrame(renderLoop);
489
- const dt=getClock().getDelta();
490
- if(dt>0.1) return;
491
- if(camCtrl) camCtrl.update(dt);
492
- if(particleMgr) particleMgr.update(dt);
493
- if(agentMgr) agentMgr.update(dt);
494
- if(hologram) hologram.update(dt,state.get('graph'));
495
- const composer=getComposer();
496
- if(composer) composer.render(dt); else getRenderer().render(getScene(),getCamera());
497
- }
498
-
499
- /* ─── API ──────────────────────────────────────────────────────────────────── */
500
- async function fetchProject(){
501
- try{const r=await fetch('/api/project');const d=await r.json();
502
- state.set({project:d,graph:d.graph||state.get('graph'),agents:d.agents||state.get('agents')});
503
- updateAll();
504
- }catch(e){ui.addLog('Failed to fetch project: '+e.message,'error')}
505
- }
506
- async function doScan(){if(state.get('running'))return;state.set({running:true});updateBtns();ui.addLog('🔍 Starting codebase scan…','info',true);try{await fetch('/api/scan',{method:'POST'})}catch(e){ui.addLog('Scan error: '+e.message,'error');state.set({running:false});updateBtns()}}
507
- async function doPipeline(){if(state.get('running'))return;state.set({running:true});updateBtns();ui.addLog('⚡ Pipeline started…','info',true);try{await fetch('/api/pipeline',{method:'POST'})}catch(e){ui.addLog('Pipeline error: '+e.message,'error');state.set({running:false});updateBtns()}}
508
- async function doReset(){try{await fetch('/api/reset',{method:'POST'})}catch(e){ui.addLog('Reset error: '+e.message,'error')}state.set({running:false});updateBtns();ui.addLog('♻️ Agents reset','info',true)}
509
- async function doRunTests(){if(state.get('running'))return;state.set({running:true});updateBtns();ui.addLog('🧪 Running tests (mode: '+state.get('runMode')+')…','info',true);try{const r=await fetch('/api/run-tests',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({mode:state.get('runMode')})});const d=await r.json();if(d.error){ui.addLog('Test error: '+d.error,'error');state.set({running:false});updateBtns()}}catch(e){ui.addLog('Test error: '+e.message,'error');state.set({running:false});updateBtns()}}
510
- async function doReports(){if(state.get('running'))return;state.set({running:true});updateBtns();ui.addLog('📊 Generating reports…','info',true);try{await fetch('/api/reports/generate',{method:'POST'})}catch(e){ui.addLog('Report error: '+e.message,'error');state.set({running:false});updateBtns()}}
511
-
512
- /* ─── WebSocket ────────────────────────────────────────────────────────────── */
513
- function connectWS(){
514
- const prot=location.protocol==='https:'?'wss:':'ws:';
515
- const ws=new WebSocket(prot+'//'+location.host);
516
- ws.onopen=()=>{document.getElementById('conn-dot').classList.add('connected');state.set({ws})};
517
- ws.onclose=()=>{document.getElementById('conn-dot').classList.remove('connected');setTimeout(connectWS,3000)};
518
- ws.onmessage=ev=>{try{handleWS(JSON.parse(ev.data))}catch(e){}};
519
- }
520
- function handleWS(msg){
521
- switch(msg.type){
522
- case'agent:update':state.set({agents:msg.data});agentMgr.sync(msg.data);ui.updateDeskCards(msg.data);ui.updateSidebar(null,msg.data);break;
523
- case'graph:update':state.set({graph:msg.data});graphViz.update(msg.data);ui.updateSidebar(msg.data,null);ui.updateStats(msg.data);break;
524
- case'log':ui.addLog(msg.data.message,msg.data.level);break;
525
- case'files:generated':state.set({generatedFiles:msg.data});ui.updateFileList(msg.data);break;
526
- case'pipeline:complete':state.set({running:false});updateBtns();
527
- if(msg.data.error)ui.addLog('Pipeline failed: '+msg.data.error,'error');
528
- else{ui.addLog('✅ Pipeline complete','success',true);ui.showToast('Pipeline completed','success');particleMgr.triggerCelebration()}
529
- fetchProject();break;
530
- case'test:complete':state.set({running:false,testMetrics:msg.data.metrics,testQuality:msg.data.quality});updateBtns();
531
- ui.updateResults(msg.data);ui.addLog(`Tests: ${msg.data.metrics?.passed||0}✓ ${msg.data.metrics?.failed||0}✗`,'info',true);
532
- ui.showToast(`Tests: ${msg.data.metrics?.passed||0}✓ ${msg.data.metrics?.failed||0}✗`,msg.data.metrics?.failed?'warning':'success');break;
533
- case'reports:generated':state.set({running:false,reports:msg.data});updateBtns();
534
- ui.updateReports(msg.data);ui.addLog('📊 '+msg.data.length+' reports generated','success',true);break;
535
- }
536
- }
537
-
538
- /* ─── Helpers ──────────────────────────────────────────────────────────────── */
539
- function updateAll(){
540
- const g=state.get('graph'),a=state.get('agents');
541
- ui.updateSidebar(g,a);ui.updateStats(g);ui.updateDeskCards(a);
542
- if(a) agentMgr.sync(a);
543
- if(g) graphViz.update(g);
544
- }
545
- function updateBtns(){const r=state.get('running');['btn-scan','btn-pipeline','btn-reset','btn-run-tests','btn-reports'].forEach(id=>document.getElementById(id).disabled=r)}
546
- async function openFilePreview(idx){try{const r=await fetch('/api/files/'+idx);const d=await r.json();document.getElementById('fp-title').textContent=d.filePath||'File';document.getElementById('fp-code').textContent=d.content||'';document.getElementById('file-preview').classList.add('visible')}catch(e){ui.showToast('Failed to load file','error')}}
547
- async function openReportPreview(fmt){try{const r=await fetch('/api/reports/'+fmt);const t=await r.text();if(fmt==='html'){const w=window.open('','_blank');if(w){w.document.write(t);w.document.close()}return}document.getElementById('fp-title').textContent='Report: '+fmt;document.getElementById('fp-code').textContent=t;document.getElementById('file-preview').classList.add('visible')}catch(e){ui.showToast('Failed to load report','error')}}
548
- function toggleTheme(){const cur=state.get('theme');const nxt=cur==='dark'?'light':'dark';state.set({theme:nxt});document.documentElement.setAttribute('data-theme',nxt);localStorage.setItem('opencroc-theme',nxt);document.getElementById('theme-icon-dark').style.display=nxt==='dark'?'':'none';document.getElementById('theme-icon-light').style.display=nxt==='light'?'':'none';updateOfficeLighting(nxt)}
549
- function setView(v){state.set({currentView:v});document.getElementById('view-3d').classList.toggle('active',v==='3d');document.getElementById('view-graph').classList.toggle('active',v==='graph');if(v==='3d'){camCtrl.flyTo('office')}else{window.location.href='/index-studio.html'}}
550
-
551
- /* ─── Events ───────────────────────────────────────────────────────────────── */
552
- document.getElementById('btn-scan').addEventListener('click',doScan);
553
- document.getElementById('btn-pipeline').addEventListener('click',doPipeline);
554
- document.getElementById('btn-reset').addEventListener('click',doReset);
555
- document.getElementById('btn-run-tests').addEventListener('click',doRunTests);
556
- document.getElementById('btn-reports').addEventListener('click',doReports);
557
- document.getElementById('run-mode').addEventListener('change',e=>state.set({runMode:e.target.value}));
558
- document.getElementById('view-3d').addEventListener('click',()=>setView('3d'));
559
- document.getElementById('view-graph').addEventListener('click',()=>setView('graph'));
560
- document.getElementById('theme-toggle').addEventListener('click',toggleTheme);
561
- document.getElementById('sidebar-toggle').addEventListener('click',()=>document.getElementById('sidebar').classList.toggle('collapsed'));
562
- document.getElementById('bottom-bar-toggle').addEventListener('click',()=>document.getElementById('bottom-bar').classList.toggle('collapsed'));
563
- document.getElementById('fp-close').addEventListener('click',()=>document.getElementById('file-preview').classList.remove('visible'));
564
- document.getElementById('fp-backdrop').addEventListener('click',()=>document.getElementById('file-preview').classList.remove('visible'));
565
- document.querySelectorAll('.panel-tabs .tab').forEach(tab=>{tab.addEventListener('click',()=>{document.querySelectorAll('.panel-tabs .tab').forEach(t=>t.classList.remove('active'));tab.classList.add('active');const tgt=tab.dataset.tab;document.getElementById('log-list').classList.toggle('hidden',tgt!=='log');document.getElementById('file-list').classList.toggle('hidden',tgt!=='files');document.getElementById('results-panel').classList.toggle('hidden',tgt!=='results');document.getElementById('reports-panel').classList.toggle('hidden',tgt!=='reports')})});
566
- window.addEventListener('resize',()=>resizeEngine());
567
- let slt=null;
568
- document.addEventListener('keydown',e=>{const tag=e.target.tagName.toLowerCase();if(tag==='input'||tag==='textarea'||tag==='select')return;const k=e.key.toLowerCase();if(e.key==='Escape'){document.getElementById('file-preview').classList.remove('visible');document.getElementById('shortcut-legend').classList.remove('visible');return}if(k==='?'||(e.key==='/'&&e.shiftKey)){e.preventDefault();const el=document.getElementById('shortcut-legend');el.classList.add('visible');if(slt)clearTimeout(slt);slt=setTimeout(()=>el.classList.remove('visible'),4000);return}if(k==='1'){e.preventDefault();setView('3d');return}if(k==='2'){e.preventDefault();setView('graph');return}if(k==='s'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();doScan();return}if(k==='p'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();doPipeline();return}if(k==='t'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();doRunTests();return}if(k==='r'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();doReports();return}if(k==='x'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();doReset();return}if(k==='d'&&!e.ctrlKey&&!e.metaKey){e.preventDefault();toggleTheme();return}});
569
-
570
- boot().catch(e=>console.error('Boot failed:',e));
571
- </script>
572
- </body>
573
- </html>