jsgui3-server 0.0.148 → 0.0.150
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/.github/agents/Mobile Developer.agent.md +89 -0
- package/.github/workflows/control-scan-manifest-check.yml +31 -0
- package/AGENTS.md +4 -0
- package/README.md +215 -3
- package/admin-ui/client.js +81 -51
- package/admin-ui/v1/admin_auth_service.js +197 -0
- package/admin-ui/v1/admin_user_store.js +71 -0
- package/admin-ui/v1/client.js +17 -0
- package/admin-ui/v1/controls/admin_shell.js +1399 -0
- package/admin-ui/v1/controls/group_box.js +84 -0
- package/admin-ui/v1/controls/stat_card.js +125 -0
- package/admin-ui/v1/server.js +658 -0
- package/admin-ui/v1/utils/formatters.js +68 -0
- package/dev-status.svg +139 -0
- package/docs/admin-extension-guide.md +345 -0
- package/docs/api-reference.md +301 -43
- package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
- package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
- package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
- package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
- package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
- package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
- package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
- package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
- package/docs/books/adaptive-control-improvements/README.md +66 -0
- package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
- package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
- package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
- package/docs/books/admin-ui-authentication/README.md +25 -0
- package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
- package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
- package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
- package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
- package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
- package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
- package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
- package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
- package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
- package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
- package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
- package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
- package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
- package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
- package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
- package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
- package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
- package/docs/books/creating-a-new-admin-ui/README.md +68 -0
- package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
- package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
- package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
- package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
- package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
- package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
- package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
- package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
- package/docs/books/device-adaptive-composition/README.md +47 -0
- package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
- package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
- package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
- package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
- package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
- package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
- package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
- package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
- package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
- package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
- package/docs/bundling-system-deep-dive.md +9 -4
- package/docs/comparison-report-express-plex-cpanel.md +549 -0
- package/docs/comprehensive-documentation.md +49 -18
- package/docs/configuration-reference.md +152 -27
- package/docs/core/README.md +19 -0
- package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
- package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
- package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
- package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
- package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
- package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
- package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
- package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
- package/docs/designs/server-admin-interface-aero.svg +611 -0
- package/docs/publishers-guide.md +59 -4
- package/docs/resources-guide.md +184 -35
- package/docs/simple-server-api-design.md +72 -17
- package/docs/system-architecture.md +18 -14
- package/docs/troubleshooting.md +84 -53
- package/examples/controls/15) window, observable SSE/server.js +6 -1
- package/examples/controls/19) window, auto observable ui/server.js +9 -0
- package/examples/controls/20) window, task manager app/README.md +133 -0
- package/examples/controls/20) window, task manager app/client.js +797 -0
- package/examples/controls/20) window, task manager app/server.js +178 -0
- package/examples/controls/6) window, color_palette/client.js +165 -68
- package/examples/controls/9) window, date picker/client.js +362 -76
- package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
- package/examples/jsgui3-html/06) theming/client.js +22 -1
- package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
- package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
- package/lab/experiments/capture-color-controls.js +196 -0
- package/lab/results/screenshots/color-controls/full_page.png +0 -0
- package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
- package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
- package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
- package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
- package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
- package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
- package/lab/screenshot-utils.js +248 -0
- package/module.js +12 -0
- package/package.json +12 -2
- package/publishers/Publishers.js +4 -3
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
- package/publishers/http-sse-publisher.js +341 -0
- package/resources/process-resource.js +950 -0
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
- package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
- package/resources/remote-process-resource.js +355 -0
- package/resources/server-resource-pool.js +354 -41
- package/serve-factory.js +442 -259
- package/server.js +288 -13
- package/tests/README.md +71 -4
- package/tests/admin-ui-jsgui-controls.test.js +581 -0
- package/tests/admin-ui-render.test.js +24 -0
- package/tests/assigners.test.js +56 -40
- package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
- package/tests/configuration-validation.test.js +21 -18
- package/tests/content-analysis.test.js +7 -6
- package/tests/control-optimizer-cache-behavior.test.js +52 -0
- package/tests/control-scan-manifest-regression.test.js +144 -0
- package/tests/end-to-end.test.js +15 -14
- package/tests/error-handling.test.js +222 -179
- package/tests/fixtures/bundling-default-button-client.js +37 -0
- package/tests/fixtures/bundling-default-window-client.js +34 -0
- package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
- package/tests/fixtures/resource-monitor-client.js +319 -0
- package/tests/helpers/puppeteer-e2e-harness.js +317 -0
- package/tests/http-sse-publisher.test.js +136 -0
- package/tests/performance.test.js +69 -65
- package/tests/process-resource.test.js +138 -0
- package/tests/publishers.test.js +7 -7
- package/tests/remote-process-resource.test.js +160 -0
- package/tests/sass-controls.e2e.test.js +7 -1
- package/tests/serve-resources.test.js +270 -0
- package/tests/serve.test.js +120 -50
- package/tests/server-resource-pool.test.js +106 -0
- package/tests/small-controls-bundle-size.test.js +252 -0
- package/tests/test-runner.js +14 -1
- package/tests/window-examples.puppeteer.test.js +204 -1
- package/tests/window-resource-integration.puppeteer.test.js +585 -0
- package/tests/temp_invalid.js +0 -7
- package/tests/temp_invalid_utf8.js +0 -1
- package/tests/temp_malformed.js +0 -10
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# Chapter 6: Domain Controls — Process Manager Panel
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Process Manager Panel is a group-box control that visualizes the server's process tree: the main Node.js process at the top, with fork arrows pointing down to child processes. Each process displays its name, state, PID, and memory usage.
|
|
6
|
+
|
|
7
|
+
In the design reference, this appears as the "Processes" group box on the left side of the dashboard, showing:
|
|
8
|
+
- A large main server process bar with a blue accent strip
|
|
9
|
+
- Fork arrows (`fork()`) connecting to child process cards
|
|
10
|
+
- Each child card has a distinctive color (purple for Bundle Builder, amber for Compiler, cyan for File Watcher)
|
|
11
|
+
- Health indicators (green dots) on each process
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Process_Panel Control
|
|
16
|
+
|
|
17
|
+
### Spec
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
{
|
|
21
|
+
__type_name: 'process_panel',
|
|
22
|
+
title: 'Processes', // Group box label
|
|
23
|
+
processes: { // Data from /api/admin/processes
|
|
24
|
+
main: { pid, state, uptime_seconds, memory },
|
|
25
|
+
children: [
|
|
26
|
+
{ name, pid, state, memory, restart_count, type }
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Visual Anatomy
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌─ Processes ────────────────────────────────────────────┐
|
|
36
|
+
│ │
|
|
37
|
+
│ ┌════════════════════════════════════════════════════┐ │
|
|
38
|
+
│ │▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ (blue bar) │ │
|
|
39
|
+
│ │ ● Main Server Process [RUNNING] │ │
|
|
40
|
+
│ │ HTTP Server · Router · SSR Engine PID 7824 │ │
|
|
41
|
+
│ │ 128 MB │ │
|
|
42
|
+
│ └═══════════════════╤═══════════╤══════════╤════════┘ │
|
|
43
|
+
│ │ │ │ │
|
|
44
|
+
│ fork() fork() fork() │
|
|
45
|
+
│ │ │ │ │
|
|
46
|
+
│ ┌──────────────┐ ┌──────────┐ ┌──────────┐ │
|
|
47
|
+
│ │ ▔▔ (purple) │ │ ▔▔(amber)│ │ ▔▔(cyan) │ │
|
|
48
|
+
│ │ ● Bundle │ │ ●Compiler│ │ ● File │ │
|
|
49
|
+
│ │ Builder │ │ │ │ Watcher │ │
|
|
50
|
+
│ │ PID 7830 │ │ PID 7836 │ │ PID 7842 │ │
|
|
51
|
+
│ │ 86 MB │ │ 64 MB │ │ 24 MB │ │
|
|
52
|
+
│ └──────────────┘ └──────────┘ └──────────┘ │
|
|
53
|
+
│ │
|
|
54
|
+
│ Total memory: 302 MB across 4 processes │
|
|
55
|
+
└────────────────────────────────────────────────────────┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Constructor
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
class Process_Panel extends jsgui.Control {
|
|
62
|
+
constructor(spec = {}) {
|
|
63
|
+
spec.__type_name = spec.__type_name || 'process_panel';
|
|
64
|
+
super(spec);
|
|
65
|
+
const { context } = this;
|
|
66
|
+
this._processes = spec.processes || { main: null, children: [] };
|
|
67
|
+
|
|
68
|
+
const compose = () => {
|
|
69
|
+
// Group box wrapper
|
|
70
|
+
const group = new Group_Box({
|
|
71
|
+
context,
|
|
72
|
+
title: spec.title || 'Processes'
|
|
73
|
+
});
|
|
74
|
+
this.add(group);
|
|
75
|
+
this._group = group;
|
|
76
|
+
|
|
77
|
+
this._render_processes();
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (!spec.el) { compose(); }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_render_processes() {
|
|
84
|
+
const { context } = this;
|
|
85
|
+
const data = this._processes;
|
|
86
|
+
|
|
87
|
+
// Main process card
|
|
88
|
+
if (data.main) {
|
|
89
|
+
const main_card = new Process_Card({
|
|
90
|
+
context,
|
|
91
|
+
name: 'Main Server Process',
|
|
92
|
+
description: 'HTTP Server · Router · SSR Engine · API Publisher',
|
|
93
|
+
pid: data.main.pid,
|
|
94
|
+
state: data.main.state,
|
|
95
|
+
memory_bytes: data.main.memory?.rss,
|
|
96
|
+
accent: 'blue',
|
|
97
|
+
is_main: true
|
|
98
|
+
});
|
|
99
|
+
this._group.add(main_card);
|
|
100
|
+
this._main_card = main_card;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fork arrows + children
|
|
104
|
+
if (data.children && data.children.length > 0) {
|
|
105
|
+
const fork_row = new controls.div({ context, class: 'fork-row' });
|
|
106
|
+
this._group.add(fork_row);
|
|
107
|
+
|
|
108
|
+
const children_row = new controls.div({ context, class: 'children-row' });
|
|
109
|
+
this._group.add(children_row);
|
|
110
|
+
|
|
111
|
+
const accent_colors = ['purple', 'amber', 'cyan', 'green', 'pink'];
|
|
112
|
+
|
|
113
|
+
data.children.forEach((child, i) => {
|
|
114
|
+
// Fork arrow
|
|
115
|
+
const arrow = new controls.div({ context, class: 'fork-arrow' });
|
|
116
|
+
const arrow_label = new controls.span({ context, class: 'fork-label' });
|
|
117
|
+
arrow_label.add('fork()');
|
|
118
|
+
arrow.add(arrow_label);
|
|
119
|
+
fork_row.add(arrow);
|
|
120
|
+
|
|
121
|
+
// Child card
|
|
122
|
+
const child_card = new Process_Card({
|
|
123
|
+
context,
|
|
124
|
+
name: child.name,
|
|
125
|
+
description: child.description || '',
|
|
126
|
+
pid: child.pid,
|
|
127
|
+
state: child.state,
|
|
128
|
+
memory_bytes: child.memory?.rss_bytes,
|
|
129
|
+
accent: accent_colors[i % accent_colors.length],
|
|
130
|
+
restart_count: child.restart_count
|
|
131
|
+
});
|
|
132
|
+
children_row.add(child_card);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Total memory line
|
|
137
|
+
const total_memory = this._calculate_total_memory(data);
|
|
138
|
+
const total_line = new controls.div({ context, class: 'process-total' });
|
|
139
|
+
total_line.add(
|
|
140
|
+
`Total memory: ${format_bytes(total_memory)} across ${1 + (data.children?.length || 0)} processes`
|
|
141
|
+
);
|
|
142
|
+
this._group.add(total_line);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_calculate_total_memory(data) {
|
|
146
|
+
let total = data.main?.memory?.rss || 0;
|
|
147
|
+
if (data.children) {
|
|
148
|
+
data.children.forEach(c => {
|
|
149
|
+
total += c.memory?.rss_bytes || 0;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return total;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
update(processes_data) {
|
|
156
|
+
this._processes = processes_data;
|
|
157
|
+
// Client-side: clear and re-render
|
|
158
|
+
if (this._group && this._group.el) {
|
|
159
|
+
// Clear children
|
|
160
|
+
while (this._group.el.firstChild) {
|
|
161
|
+
this._group.el.removeChild(this._group.el.firstChild);
|
|
162
|
+
}
|
|
163
|
+
this._render_processes();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Process_Card Sub-Control
|
|
172
|
+
|
|
173
|
+
An individual process card within the panel.
|
|
174
|
+
|
|
175
|
+
### Spec
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
{
|
|
179
|
+
__type_name: 'process_card',
|
|
180
|
+
name: 'Bundle Builder',
|
|
181
|
+
description: 'JS bundling · CSS extraction',
|
|
182
|
+
pid: 7830,
|
|
183
|
+
state: 'running',
|
|
184
|
+
memory_bytes: 90177536,
|
|
185
|
+
accent: 'purple', // 'blue', 'purple', 'amber', 'cyan', 'green'
|
|
186
|
+
is_main: false,
|
|
187
|
+
restart_count: 0
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Constructor
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
class Process_Card extends jsgui.Control {
|
|
195
|
+
constructor(spec = {}) {
|
|
196
|
+
spec.__type_name = spec.__type_name || 'process_card';
|
|
197
|
+
super(spec);
|
|
198
|
+
const { context } = this;
|
|
199
|
+
|
|
200
|
+
const compose = () => {
|
|
201
|
+
// Accent bar at top
|
|
202
|
+
const accent = new controls.div({
|
|
203
|
+
context,
|
|
204
|
+
class: `process-accent accent-${spec.accent || 'blue'}`
|
|
205
|
+
});
|
|
206
|
+
this.add(accent);
|
|
207
|
+
|
|
208
|
+
// Health dot + name
|
|
209
|
+
const header = new controls.div({ context, class: 'process-header' });
|
|
210
|
+
this.add(header);
|
|
211
|
+
|
|
212
|
+
const health = new Health_Badge({
|
|
213
|
+
context,
|
|
214
|
+
state: spec.state || 'unknown'
|
|
215
|
+
});
|
|
216
|
+
header.add(health);
|
|
217
|
+
this._health = health;
|
|
218
|
+
|
|
219
|
+
const name_el = new controls.span({ context, class: 'process-name' });
|
|
220
|
+
name_el.add(spec.name || 'Unknown');
|
|
221
|
+
header.add(name_el);
|
|
222
|
+
|
|
223
|
+
// Status badge (if main)
|
|
224
|
+
if (spec.is_main) {
|
|
225
|
+
const status_badge = new controls.div({
|
|
226
|
+
context,
|
|
227
|
+
class: `process-status-badge badge-${spec.state || 'unknown'}`
|
|
228
|
+
});
|
|
229
|
+
status_badge.add(spec.state?.toUpperCase() || 'UNKNOWN');
|
|
230
|
+
header.add(status_badge);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Description
|
|
234
|
+
if (spec.description) {
|
|
235
|
+
const desc = new controls.div({ context, class: 'process-desc' });
|
|
236
|
+
desc.add(spec.description);
|
|
237
|
+
this.add(desc);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Footer: PID + memory
|
|
241
|
+
const footer = new controls.div({ context, class: 'process-footer' });
|
|
242
|
+
this.add(footer);
|
|
243
|
+
|
|
244
|
+
const pid_el = new controls.span({ context, class: 'process-pid' });
|
|
245
|
+
pid_el.add(spec.pid ? `PID ${spec.pid}` : '—');
|
|
246
|
+
footer.add(pid_el);
|
|
247
|
+
|
|
248
|
+
const mem_el = new controls.span({ context, class: 'process-mem' });
|
|
249
|
+
mem_el.add(spec.memory_bytes ? format_bytes(spec.memory_bytes) : '—');
|
|
250
|
+
footer.add(mem_el);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (!spec.el) { compose(); }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### CSS
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
.process_card {
|
|
262
|
+
border-radius: 4px;
|
|
263
|
+
border: 1px solid #B0A898;
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
background: linear-gradient(to bottom, #F4F8FD, #D8E4F0 45%, #C0D4E8 55%, #CCDDEE);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.process_card.process-main {
|
|
269
|
+
width: 100%;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.process-accent {
|
|
273
|
+
height: 4px;
|
|
274
|
+
border-radius: 2px 2px 0 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.accent-blue { background: linear-gradient(to right, #7AACE0, #5090D0); }
|
|
278
|
+
.accent-purple { background: #9070C0; }
|
|
279
|
+
.accent-amber { background: #D8A020; }
|
|
280
|
+
.accent-cyan { background: #4098B8; }
|
|
281
|
+
.accent-green { background: #48B848; }
|
|
282
|
+
|
|
283
|
+
.process-header {
|
|
284
|
+
display: flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 8px;
|
|
287
|
+
padding: 8px 12px 4px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.process-name {
|
|
291
|
+
font-size: 11px;
|
|
292
|
+
font-weight: 600;
|
|
293
|
+
color: #1A3060;
|
|
294
|
+
flex: 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.process-status-badge {
|
|
298
|
+
font-size: 8px;
|
|
299
|
+
font-weight: 500;
|
|
300
|
+
padding: 2px 10px;
|
|
301
|
+
border-radius: 3px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.badge-running {
|
|
305
|
+
background: rgba(72, 184, 72, 0.15);
|
|
306
|
+
border: 0.5px solid #48B848;
|
|
307
|
+
color: #2A6A2A;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.badge-stopped {
|
|
311
|
+
background: rgba(128, 128, 128, 0.15);
|
|
312
|
+
border: 0.5px solid #808080;
|
|
313
|
+
color: #606060;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.badge-crashed {
|
|
317
|
+
background: rgba(204, 68, 68, 0.15);
|
|
318
|
+
border: 0.5px solid #CC4444;
|
|
319
|
+
color: #992222;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.process-desc {
|
|
323
|
+
font-size: 8px;
|
|
324
|
+
color: #808080;
|
|
325
|
+
padding: 0 12px 4px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.process-footer {
|
|
329
|
+
display: flex;
|
|
330
|
+
justify-content: space-between;
|
|
331
|
+
padding: 4px 12px 8px;
|
|
332
|
+
font-size: 8px;
|
|
333
|
+
color: #908888;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Fork visualization */
|
|
337
|
+
.fork-row {
|
|
338
|
+
display: flex;
|
|
339
|
+
justify-content: space-around;
|
|
340
|
+
padding: 4px 60px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.fork-arrow {
|
|
344
|
+
display: flex;
|
|
345
|
+
flex-direction: column;
|
|
346
|
+
align-items: center;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.fork-arrow::before {
|
|
350
|
+
content: '';
|
|
351
|
+
width: 1px;
|
|
352
|
+
height: 16px;
|
|
353
|
+
background: #7090B0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.fork-arrow::after {
|
|
357
|
+
content: '';
|
|
358
|
+
width: 0;
|
|
359
|
+
height: 0;
|
|
360
|
+
border-left: 4px solid transparent;
|
|
361
|
+
border-right: 4px solid transparent;
|
|
362
|
+
border-top: 6px solid #7090B0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.fork-label {
|
|
366
|
+
font-size: 8px;
|
|
367
|
+
color: #7090B0;
|
|
368
|
+
margin: 2px 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.children-row {
|
|
372
|
+
display: flex;
|
|
373
|
+
gap: 12px;
|
|
374
|
+
padding: 0 16px;
|
|
375
|
+
flex-wrap: wrap;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.children-row .process_card {
|
|
379
|
+
flex: 1 1 120px;
|
|
380
|
+
min-width: 110px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.process-total {
|
|
384
|
+
font-size: 8px;
|
|
385
|
+
color: #908888;
|
|
386
|
+
padding: 12px 4px 4px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Child card accent colors */
|
|
390
|
+
.process_card[data-accent="purple"] {
|
|
391
|
+
background: linear-gradient(to bottom, #E8E0F0, #D8D0E8);
|
|
392
|
+
border-color: #A090C0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.process_card[data-accent="amber"] {
|
|
396
|
+
background: linear-gradient(to bottom, #FFF0D0, #F0E4C0);
|
|
397
|
+
border-color: #C0A860;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.process_card[data-accent="cyan"] {
|
|
401
|
+
background: linear-gradient(to bottom, #D8F0F8, #C8E4F0);
|
|
402
|
+
border-color: #70A8C0;
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Group_Box Control
|
|
409
|
+
|
|
410
|
+
A classic Windows-style group box with an inset label. Used by Process_Panel, Resource_Table, and other group-level controls.
|
|
411
|
+
|
|
412
|
+
### Spec
|
|
413
|
+
|
|
414
|
+
```javascript
|
|
415
|
+
{
|
|
416
|
+
__type_name: 'group_box',
|
|
417
|
+
title: 'Processes'
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Constructor
|
|
422
|
+
|
|
423
|
+
```javascript
|
|
424
|
+
class Group_Box extends jsgui.Control {
|
|
425
|
+
constructor(spec = {}) {
|
|
426
|
+
spec.__type_name = spec.__type_name || 'group_box';
|
|
427
|
+
super(spec);
|
|
428
|
+
const { context } = this;
|
|
429
|
+
|
|
430
|
+
const compose = () => {
|
|
431
|
+
// Title overlay (positioned to interrupt the border)
|
|
432
|
+
if (spec.title) {
|
|
433
|
+
const title = new controls.div({ context, class: 'group-box-title' });
|
|
434
|
+
title.add(spec.title);
|
|
435
|
+
this.add(title);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Content container
|
|
439
|
+
const content = new controls.div({ context, class: 'group-box-content' });
|
|
440
|
+
this.add(content);
|
|
441
|
+
this._content = content;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
if (!spec.el) { compose(); }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Override add to route children into content area
|
|
448
|
+
add_child(child) {
|
|
449
|
+
if (this._content) {
|
|
450
|
+
return this._content.add(child);
|
|
451
|
+
}
|
|
452
|
+
return super.add(child);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
Group_Box.css = `
|
|
457
|
+
.group_box {
|
|
458
|
+
position: relative;
|
|
459
|
+
border: 1px solid #B0A898;
|
|
460
|
+
border-radius: 4px;
|
|
461
|
+
background: linear-gradient(to bottom, #FFFFFF, #F4F2EC);
|
|
462
|
+
padding: 20px 16px 12px;
|
|
463
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.group-box-title {
|
|
467
|
+
position: absolute;
|
|
468
|
+
top: -8px;
|
|
469
|
+
left: 10px;
|
|
470
|
+
background: linear-gradient(to bottom, #F0EDE6, #E8E4DC);
|
|
471
|
+
padding: 0 6px;
|
|
472
|
+
font-size: 11px;
|
|
473
|
+
font-weight: 600;
|
|
474
|
+
color: #2A4060;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.group-box-content {
|
|
478
|
+
display: flex;
|
|
479
|
+
flex-direction: column;
|
|
480
|
+
gap: 8px;
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Process Data Mapping
|
|
488
|
+
|
|
489
|
+
The Process_Panel maps data from the `/api/admin/processes` endpoint:
|
|
490
|
+
|
|
491
|
+
| API Field | Process_Card Spec | Notes |
|
|
492
|
+
|-----------|-------------------|-------|
|
|
493
|
+
| `main.pid` | `pid: data.main.pid` | Always available |
|
|
494
|
+
| `main.state` | `state: 'running'` | Derived from server._started |
|
|
495
|
+
| `main.memory.rss` | `memory_bytes: data.main.memory.rss` | From `process.memoryUsage()` |
|
|
496
|
+
| `main.uptime_seconds` | Not shown in card | Shown in status bar |
|
|
497
|
+
| `children[].name` | `name: child.name` | Resource name |
|
|
498
|
+
| `children[].pid` | `pid: child.pid` | From Process_Resource.pid |
|
|
499
|
+
| `children[].state` | `state: child.state` | Lifecycle state |
|
|
500
|
+
| `children[].memory.rss_bytes` | `memory_bytes: child.memory.rss_bytes` | From Process_Resource.memory_usage |
|
|
501
|
+
| `children[].restart_count` | `restart_count: child.restart_count` | Auto-restart counter |
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Actions
|
|
506
|
+
|
|
507
|
+
The Process_Panel will eventually support interactive actions:
|
|
508
|
+
|
|
509
|
+
| Action | UI Element | API Call |
|
|
510
|
+
|--------|-----------|----------|
|
|
511
|
+
| Restart child | Button on card (hover) | `POST /api/admin/action { "action":"restart_resource", "target": name }` |
|
|
512
|
+
| Stop child | Button on card (hover) | `POST /api/admin/action { "action":"stop_resource", "target": name }` |
|
|
513
|
+
| View stdout | Click on card | Opens Log_Viewer filtered to process |
|
|
514
|
+
|
|
515
|
+
These are Phase 2 features — the initial implementation is display-only.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Real-Time Updates
|
|
520
|
+
|
|
521
|
+
The Process_Panel subscribes to SSE events for live state changes:
|
|
522
|
+
|
|
523
|
+
```javascript
|
|
524
|
+
// In activate()
|
|
525
|
+
const event_source = new EventSource('/api/admin/events');
|
|
526
|
+
|
|
527
|
+
event_source.addEventListener('resource_state_change', (e) => {
|
|
528
|
+
const data = JSON.parse(e.data);
|
|
529
|
+
// Find matching child process card and update health badge
|
|
530
|
+
this._update_child_state(data.resourceName, data.to);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
event_source.addEventListener('crashed', (e) => {
|
|
534
|
+
const data = JSON.parse(e.data);
|
|
535
|
+
this._update_child_state(data.resourceName, 'crashed');
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
event_source.addEventListener('recovered', (e) => {
|
|
539
|
+
const data = JSON.parse(e.data);
|
|
540
|
+
this._update_child_state(data.resourceName, 'running');
|
|
541
|
+
});
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
This means the process panel never needs to poll — state changes are pushed instantly.
|