Monorepo for Aesthetic.Computer
aesthetic.computer
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <!-- CSP will be injected by extension, or use permissive one for local dev -->
7 <meta http-equiv="Content-Security-Policy" content="{{CSP}}">
8 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
9 <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
10 <style>
11 * { margin: 0; padding: 0; box-sizing: border-box; }
12
13 /* Dark theme (default) */
14 body {
15 background: #181318;
16 color: #fff;
17 font-family: monospace;
18 overflow: hidden;
19 height: 100vh;
20 }
21 body[data-theme="light"] {
22 background: #fcf7c5;
23 color: #281e5a;
24 }
25
26 #canvas { position: absolute; top: 0; left: 0; z-index: 1; }
27
28 /* ====== HEADER BAR ====== */
29 .header-bar {
30 position: fixed;
31 top: 0;
32 left: 0;
33 right: 0;
34 height: 44px;
35 display: flex;
36 align-items: center;
37 justify-content: space-between;
38 padding: 0 16px;
39 background: linear-gradient(to bottom, rgba(24, 19, 24, 0.9) 0%, rgba(24, 19, 24, 0) 100%);
40 z-index: 100;
41 pointer-events: none;
42 }
43 body[data-theme="light"] .header-bar {
44 background: linear-gradient(to bottom, rgba(252, 247, 197, 0.9) 0%, rgba(252, 247, 197, 0) 100%);
45 }
46
47 .header-left {
48 display: flex;
49 align-items: center;
50 gap: 12px;
51 }
52
53 .title {
54 font-size: 13px;
55 display: flex;
56 align-items: center;
57 gap: 8px;
58 }
59 .title .dot { color: #ff69b4; }
60 body[data-theme="light"] .title .dot { color: #006400; }
61
62 .status-dot {
63 width: 6px;
64 height: 6px;
65 border-radius: 50%;
66 background: #ff69b4;
67 flex-shrink: 0;
68 }
69 body[data-theme="light"] .status-dot { background: #387adf; }
70 .status-dot.online { background: #0f0; }
71 body[data-theme="light"] .status-dot.online { background: #006400; }
72
73 .header-center {
74 display: flex;
75 align-items: center;
76 gap: 16px;
77 font-size: 11px;
78 color: #666;
79 }
80 body[data-theme="light"] .header-center { color: #806060; }
81 .header-center .stat { display: flex; align-items: center; gap: 4px; }
82 .header-center .val { color: #fff; font-weight: bold; }
83 body[data-theme="light"] .header-center .val { color: #281e5a; }
84
85 .header-right {
86 display: flex;
87 align-items: center;
88 gap: 8px;
89 pointer-events: auto;
90 }
91
92 .header-btn {
93 background: rgba(255, 255, 255, 0.08);
94 border: 1px solid rgba(255, 255, 255, 0.15);
95 color: #fff;
96 padding: 5px 10px;
97 border-radius: 4px;
98 font-size: 11px;
99 font-family: monospace;
100 cursor: pointer;
101 transition: all 0.15s ease;
102 }
103 body[data-theme="light"] .header-btn {
104 background: rgba(0, 0, 0, 0.08);
105 border: 1px solid rgba(0, 0, 0, 0.15);
106 color: #281e5a;
107 }
108 .header-btn:hover {
109 background: rgba(255, 255, 255, 0.15);
110 border-color: #ff69b4;
111 }
112 body[data-theme="light"] .header-btn:hover {
113 background: rgba(0, 0, 0, 0.12);
114 border-color: #006400;
115 }
116
117 /* ====== CENTER PANEL (process count + actions) ====== */
118 .center-panel {
119 position: fixed;
120 bottom: 60px;
121 left: 50%;
122 transform: translateX(-50%);
123 text-align: center;
124 z-index: 100;
125 pointer-events: none;
126 padding: 12px 20px;
127 background: rgba(24, 19, 24, 0.7);
128 border-radius: 12px;
129 border: 1px solid rgba(255, 255, 255, 0.08);
130 backdrop-filter: blur(8px);
131 -webkit-backdrop-filter: blur(8px);
132 }
133 body[data-theme="light"] .center-panel {
134 background: rgba(252, 247, 197, 0.8);
135 border: 1px solid rgba(0, 0, 0, 0.1);
136 }
137
138 .process-counter {
139 display: flex;
140 align-items: baseline;
141 justify-content: center;
142 gap: 6px;
143 margin-bottom: 10px;
144 }
145 .process-counter .count {
146 font-size: 32px;
147 font-weight: bold;
148 color: #fff;
149 line-height: 1;
150 }
151 body[data-theme="light"] .process-counter .count { color: #281e5a; }
152 .process-counter .label {
153 font-size: 11px;
154 color: #666;
155 text-transform: uppercase;
156 letter-spacing: 1px;
157 }
158 body[data-theme="light"] .process-counter .label { color: #806060; }
159
160 .actions {
161 pointer-events: auto;
162 display: flex;
163 gap: 8px;
164 justify-content: center;
165 }
166 .action-btn {
167 background: rgba(255,255,255,0.1);
168 border: 1px solid rgba(255,255,255,0.2);
169 border-radius: 6px;
170 padding: 8px 14px;
171 font-size: 12px;
172 color: #fff;
173 cursor: pointer;
174 transition: all 0.15s ease;
175 font-family: inherit;
176 }
177 body[data-theme="light"] .action-btn {
178 background: rgba(0,0,0,0.08);
179 border: 1px solid rgba(0,0,0,0.15);
180 color: #281e5a;
181 }
182 .action-btn:hover {
183 background: rgba(255,255,255,0.18);
184 border-color: #ff69b4;
185 }
186 body[data-theme="light"] .action-btn:hover {
187 background: rgba(0,0,0,0.12);
188 border-color: #006400;
189 }
190 .action-btn.primary {
191 background: #ff69b4;
192 border-color: #ff69b4;
193 color: #000;
194 }
195 body[data-theme="light"] .action-btn.primary {
196 background: #006400;
197 border-color: #006400;
198 color: #fff;
199 }
200 .action-btn.primary:hover {
201 background: #ff8ab3;
202 }
203 body[data-theme="light"] .action-btn.primary:hover {
204 background: #008000;
205 }
206
207 /* ====== STATUS BAR (bottom) ====== */
208 .status-bar {
209 position: fixed;
210 bottom: 0;
211 left: 0;
212 right: 0;
213 height: 36px;
214 display: flex;
215 align-items: center;
216 justify-content: space-between;
217 padding: 0 16px;
218 background: linear-gradient(to top, rgba(24, 19, 24, 0.9) 0%, rgba(24, 19, 24, 0) 100%);
219 z-index: 100;
220 pointer-events: none;
221 font-size: 11px;
222 color: #666;
223 }
224 body[data-theme="light"] .status-bar {
225 background: linear-gradient(to top, rgba(252, 247, 197, 0.9) 0%, rgba(252, 247, 197, 0) 100%);
226 color: #806060;
227 }
228
229 .status-left {
230 display: flex;
231 align-items: center;
232 gap: 16px;
233 }
234
235 .status-right {
236 display: flex;
237 align-items: center;
238 gap: 16px;
239 }
240
241 /* ====== STATS GRAPH (bottom-right) ====== */
242 .stats-graph-container {
243 position: fixed;
244 bottom: 50px;
245 right: 16px;
246 width: 200px;
247 height: 80px;
248 z-index: 100;
249 pointer-events: none;
250 background: rgba(24, 19, 24, 0.5);
251 border-radius: 8px;
252 border: 1px solid rgba(255, 255, 255, 0.08);
253 backdrop-filter: blur(4px);
254 -webkit-backdrop-filter: blur(4px);
255 padding: 8px;
256 }
257 body[data-theme="light"] .stats-graph-container {
258 background: rgba(252, 247, 197, 0.6);
259 border: 1px solid rgba(0, 0, 0, 0.1);
260 }
261 .stats-graph-header {
262 display: flex;
263 justify-content: space-between;
264 font-size: 9px;
265 margin-bottom: 4px;
266 color: #888;
267 }
268 body[data-theme="light"] .stats-graph-header { color: #806060; }
269 .stats-graph-header .cpu-label { color: #50fa7b; }
270 .stats-graph-header .mem-label { color: #ff79c6; }
271 body[data-theme="light"] .stats-graph-header .cpu-label { color: #006400; }
272 body[data-theme="light"] .stats-graph-header .mem-label { color: #c71585; }
273 .stats-graph-canvas {
274 width: 100%;
275 height: 50px;
276 border-radius: 4px;
277 background: rgba(0, 0, 0, 0.2);
278 }
279 body[data-theme="light"] .stats-graph-canvas {
280 background: rgba(0, 0, 0, 0.05);
281 }
282
283 .dev-badge {
284 background: #ff69b4;
285 color: #000;
286 padding: 3px 8px;
287 border-radius: 3px;
288 font-size: 9px;
289 font-weight: bold;
290 text-transform: uppercase;
291 letter-spacing: 0.5px;
292 }
293 body[data-theme="light"] .dev-badge {
294 background: #006400;
295 color: #fff;
296 }
297
298 .mem-display {
299 font-family: monospace;
300 }
301
302 /* ====== LABELS CONTAINER ====== */
303 .label-container {
304 position: absolute;
305 top: 44px; /* below header */
306 left: 0;
307 width: 100%;
308 height: calc(100% - 44px - 36px); /* minus header and status bar */
309 pointer-events: none;
310 z-index: 50;
311 overflow: hidden;
312 }
313 .proc-label {
314 position: absolute;
315 text-align: center;
316 transform: translate(-50%, -100%);
317 white-space: nowrap;
318 text-shadow: 0 0 6px #000, 0 0 10px #000, 0 0 14px #000;
319 pointer-events: none;
320 padding: 2px 4px;
321 border-radius: 4px;
322 background: transparent;
323 }
324 body[data-theme="light"] .proc-label {
325 text-shadow: 0 0 6px #fcf7c5, 0 0 10px #fcf7c5, 0 0 14px #fcf7c5;
326 background: transparent;
327 }
328 .proc-label .icon { font-size: 16px; display: block; line-height: 1.2; }
329 .proc-label .name { font-size: 10px; margin-top: 2px; font-weight: bold; letter-spacing: 0.3px; }
330 .proc-label .info { font-size: 8px; color: #aaa; margin-top: 1px; }
331 body[data-theme="light"] .proc-label .info { color: #806060; }
332 </style>
333</head>
334<body data-theme="dark">
335 <canvas id="canvas"></canvas>
336
337 <!-- HEADER BAR -->
338 <div class="header-bar">
339 <div class="header-left">
340 <div class="status-dot" id="status-dot"></div>
341 <div class="title">
342 <span>Aesthetic<span class="dot">.</span>Computer</span>
343 </div>
344 </div>
345 <div class="header-center">
346 <div class="stat"><span class="val" id="uptime">—</span></div>
347 <div class="stat"><span class="val" id="cpus">—</span> cpus</div>
348 </div>
349 <div class="header-right">
350 <button class="header-btn" onclick="window.ProcessTreeViz?.toggleTheme()">🌓</button>
351 </div>
352 </div>
353
354 <!-- CENTER PANEL -->
355 <div class="center-panel">
356 <div class="process-counter">
357 <span class="count" id="process-count">0</span>
358 <span class="label">processes</span>
359 </div>
360 <div class="actions">
361 <button class="action-btn primary" id="btn-kidlisp">🎨 KidLisp</button>
362 <button class="action-btn" id="btn-pane">💻 AC Pane</button>
363 </div>
364 </div>
365
366 <!-- STATUS BAR -->
367 <div class="status-bar">
368 <div class="status-left" id="status-left"></div>
369 <div class="status-right">
370 <span class="mem-display"><span id="mem-text">— / —</span> MB</span>
371 </div>
372 </div>
373
374 <!-- STATS GRAPH (bottom-right) -->
375 <div class="stats-graph-container" id="stats-graph-container">
376 <div class="stats-graph-header">
377 <span class="cpu-label">CPU: <span id="cpu-pct">0</span>%</span>
378 <span class="mem-label">MEM: <span id="mem-pct">0</span>%</span>
379 </div>
380 <canvas class="stats-graph-canvas" id="stats-graph-canvas"></canvas>
381 </div>
382
383 <div id="labels" class="label-container"></div>
384
385 <script src="process-tree.js"></script>
386</body>
387</html>