@jxa13/pm2ui 1.17.0

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/README.md ADDED
@@ -0,0 +1,198 @@
1
+
2
+
3
+ # Node WebUI
4
+
5
+ A local web dashboard for monitoring and managing PM2 processes on macOS.
6
+
7
+ Node WebUI provides a clean browser-based interface for viewing running services, checking system usage, managing PM2 apps, and reading logs without needing to keep a Terminal window open.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - View all PM2-managed processes
14
+ - Start, stop, and restart individual processes
15
+ - Bulk start, stop, and restart actions
16
+ - View PM2 process ID, CPU usage, RAM usage, uptime, restart count, and start time
17
+ - Search and filter processes by name and status
18
+ - Sort processes by status, name, CPU, RAM, restarts, uptime, and started time
19
+ - Show or hide table columns
20
+ - Save table preferences in local storage
21
+ - Open the process folder in Finder
22
+ - View recent logs for each process
23
+ - Auto-refresh logs
24
+ - Search within logs
25
+ - Copy, clear, and download logs
26
+ - Toggle line wrapping in logs
27
+ - Differentiate stdout and stderr log lines
28
+ - View host CPU and RAM usage
29
+ - View service counts and restart totals
30
+ - React operations-console UI served locally by Express
31
+
32
+ ---
33
+
34
+ ## Requirements
35
+
36
+ - macOS
37
+ - Node.js 18+
38
+ - One or more PM2-managed processes
39
+
40
+ Install PM2 globally if you want to use the `pm2` command directly:
41
+
42
+ ```bash
43
+ npm install -g pm2
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ Install the CLI globally from npm:
51
+
52
+ ```bash
53
+ npm install -g @jxa13/pm2ui
54
+ ```
55
+
56
+ Start the dashboard:
57
+
58
+ ```bash
59
+ pm2ui
60
+ ```
61
+
62
+ By default, the dashboard runs locally on:
63
+
64
+ ```text
65
+ http://127.0.0.1:3000
66
+ ```
67
+
68
+ Install a specific version:
69
+
70
+ ```bash
71
+ npm install -g @jxa13/pm2ui@1.17.0
72
+ ```
73
+
74
+ Update to the latest version:
75
+
76
+ ```bash
77
+ npm install -g @jxa13/pm2ui@latest
78
+ ```
79
+
80
+ Roll back to a previous version:
81
+
82
+ ```bash
83
+ npm install -g @jxa13/pm2ui@1.16.4
84
+ ```
85
+
86
+ ### Install From Source
87
+
88
+ Clone the repository:
89
+
90
+ ```bash
91
+ git clone <repo-url>
92
+ cd Node-WebUI
93
+ ```
94
+
95
+ Install dependencies:
96
+
97
+ ```bash
98
+ npm install
99
+ ```
100
+
101
+ Start the app:
102
+
103
+ ```bash
104
+ npm start
105
+ ```
106
+
107
+ `npm start` builds the React frontend and starts the local Express server.
108
+
109
+ ---
110
+
111
+ ## PM2 Setup
112
+
113
+ Start a sample process:
114
+
115
+ ```bash
116
+ pm2 start app.js --name my-app
117
+ ```
118
+
119
+ View PM2 processes:
120
+
121
+ ```bash
122
+ pm2 list
123
+ ```
124
+
125
+ Save PM2 process state:
126
+
127
+ ```bash
128
+ pm2 save
129
+ ```
130
+
131
+ Enable PM2 to start automatically after reboot:
132
+
133
+ ```bash
134
+ pm2 startup
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Available Actions
140
+
141
+ ### Per Process
142
+
143
+ - Start
144
+ - Stop
145
+ - Restart
146
+ - Open logs
147
+ - Open folder in Finder
148
+
149
+ ### Bulk Actions
150
+
151
+ - Start all stopped processes
152
+ - Stop all running processes
153
+ - Restart all running processes
154
+
155
+ ---
156
+
157
+ ## Logs Viewer
158
+
159
+ The logs viewer supports:
160
+
161
+ - stdout and stderr lines
162
+ - Auto refresh
163
+ - Search within logs
164
+ - Copy logs
165
+ - Clear logs
166
+ - Download logs
167
+ - Toggle line wrapping
168
+
169
+ ---
170
+
171
+ ## Security
172
+
173
+ This app is designed for local use only.
174
+
175
+ Recommended:
176
+
177
+ - Keep it bound to localhost
178
+ - Do not expose it directly to the internet
179
+ - If remote access is needed, place it behind authentication and a reverse proxy
180
+
181
+ ---
182
+
183
+ ## Future Ideas
184
+
185
+ Potential future improvements:
186
+
187
+ - Real-time log streaming with WebSockets
188
+ - Process details modal
189
+ - Create new PM2 processes from the UI
190
+ - Historical CPU and RAM charts
191
+ - Authentication for LAN access
192
+ - Saved dashboard layouts
193
+
194
+ ---
195
+
196
+ ## License
197
+
198
+ MIT
@@ -0,0 +1,91 @@
1
+ const pm2 = require('pm2');
2
+
3
+ function connectPm2() {
4
+ return new Promise((resolve, reject) => {
5
+ pm2.connect((error) => {
6
+ if (error) {
7
+ reject(error);
8
+ return;
9
+ }
10
+
11
+ resolve();
12
+ });
13
+ });
14
+ }
15
+
16
+ function disconnectPm2() {
17
+ try {
18
+ pm2.disconnect();
19
+ } catch (error) {
20
+ console.error('PM2 disconnect error:', error);
21
+ }
22
+ }
23
+
24
+ async function withPm2(callback) {
25
+ await connectPm2();
26
+
27
+ try {
28
+ return await callback();
29
+ } finally {
30
+ disconnectPm2();
31
+ }
32
+ }
33
+
34
+ async function listProcesses() {
35
+ return withPm2(() => new Promise((resolve, reject) => {
36
+ pm2.list((error, list) => {
37
+ if (error) {
38
+ reject(error);
39
+ return;
40
+ }
41
+
42
+ resolve(list);
43
+ });
44
+ }));
45
+ }
46
+
47
+ async function startProcess(target) {
48
+ return withPm2(() => new Promise((resolve, reject) => {
49
+ pm2.start(target, (error, result) => {
50
+ if (error) {
51
+ reject(error);
52
+ return;
53
+ }
54
+
55
+ resolve(result);
56
+ });
57
+ }));
58
+ }
59
+
60
+ async function stopProcess(target) {
61
+ return withPm2(() => new Promise((resolve, reject) => {
62
+ pm2.stop(target, (error, result) => {
63
+ if (error) {
64
+ reject(error);
65
+ return;
66
+ }
67
+
68
+ resolve(result);
69
+ });
70
+ }));
71
+ }
72
+
73
+ async function restartProcess(target) {
74
+ return withPm2(() => new Promise((resolve, reject) => {
75
+ pm2.restart(target, (error, result) => {
76
+ if (error) {
77
+ reject(error);
78
+ return;
79
+ }
80
+
81
+ resolve(result);
82
+ });
83
+ }));
84
+ }
85
+
86
+ module.exports = {
87
+ listProcesses,
88
+ startProcess,
89
+ stopProcess,
90
+ restartProcess
91
+ };
@@ -0,0 +1,28 @@
1
+
2
+
3
+ const si = require('systeminformation');
4
+
5
+ async function getSystemStats() {
6
+ const [currentLoad, mem, time] = await Promise.all([
7
+ si.currentLoad(),
8
+ si.mem(),
9
+ si.time()
10
+ ]);
11
+
12
+ return {
13
+ cpu: {
14
+ currentLoad: currentLoad.currentLoad || 0
15
+ },
16
+ memory: {
17
+ total: mem.total || 0,
18
+ used: mem.used || 0,
19
+ free: mem.free || 0,
20
+ usedPercent: mem.total ? (mem.used / mem.total) * 100 : 0
21
+ },
22
+ uptime: time.uptime || 0
23
+ };
24
+ }
25
+
26
+ module.exports = {
27
+ getSystemStats
28
+ };
package/bin/pm2ui.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../server');
package/changelog.md ADDED
@@ -0,0 +1,250 @@
1
+ # Changelog
2
+
3
+ All notable changes to Node WebUI are documented here.
4
+
5
+ Version numbers should be updated with each commit that changes app behavior, user-facing UI, or project documentation. Use semantic versioning:
6
+
7
+ - Patch versions, such as `1.0.1`, for fixes and small internal changes.
8
+ - Minor versions, such as `1.1.0`, for user-visible improvements that do not break existing behavior.
9
+ - Major versions, such as `2.0.0`, for breaking changes.
10
+
11
+ ## 1.17.0 - 2026-06-07
12
+
13
+ ### Changed
14
+
15
+ - Made the React operations console the default UI served from `/`.
16
+ - Changed `npm start` to build the React frontend before starting Express.
17
+ - Kept `/react` as a compatibility route to the same React build.
18
+ - Updated the README, user manual, roadmap, and smoke checks for React as the only frontend.
19
+
20
+ ### Removed
21
+
22
+ - Removed the old vanilla `public` UI assets after React feature parity.
23
+ - Removed the `NODE_WEBUI_REACT_DEFAULT` opt-in flag because React is now the default.
24
+
25
+ ## 1.16.4 - 2026-06-07
26
+
27
+ ### Changed
28
+
29
+ - Changed protected-process badges in the React table and inspector to use a red padlock icon for stronger visual warning.
30
+
31
+ ## 1.16.3 - 2026-06-07
32
+
33
+ ### Fixed
34
+
35
+ - Aligned the React process toolbar controls so the status filter icon, status selector, density toggle, and column menu sit on the same row.
36
+
37
+ ## 1.16.2 - 2026-06-07
38
+
39
+ ### Added
40
+
41
+ - Added a Protect process checkbox to the React process inspector Config tab for quickly adding or removing the selected process from the local protected-process list.
42
+
43
+ ### Changed
44
+
45
+ - Protected processes now keep Start available when applicable, while Stop, Restart, and Delete remain disabled in the React UI.
46
+
47
+ ## 1.16.1 - 2026-06-07
48
+
49
+ ### Fixed
50
+
51
+ - Reduced React Metric History chart card and canvas heights further so the graphs stay inside the section bounds in wide dashboard layouts.
52
+
53
+ ## 1.16.0 - 2026-06-07
54
+
55
+ ### Added
56
+
57
+ - Added a Vite + React operations-console frontend beside the existing vanilla UI.
58
+ - Added React dependencies, build scripts, and smoke checks with `npm run react:build`, `npm run react:dev`, `npm run react:preview`, and `npm run react:smoke`.
59
+ - Added Express serving for the built React UI at `/react` while keeping the vanilla UI available at `/`.
60
+ - Added `NODE_WEBUI_REACT_DEFAULT=true` support to redirect `/` to `/react` when the React build exists.
61
+ - Added a three-region React layout with collapsible left sidebar, main process dashboard, and right process inspector.
62
+ - Added React dashboard summary metrics, PM2 metric history charts, process table, process inspector tabs, logs workspace, create-app modal, and settings view.
63
+ - Added a central Logs sidebar workspace that shares log target, line count, search, wrapping, auto-refresh, entries, and stream lifecycle with inspector logs.
64
+ - Added structured confirmation dialogs for stop, restart, delete, bulk stop/restart, clear logs, and local-storage reset actions.
65
+ - Added table density controls with persisted Compact and Comfortable modes.
66
+ - Added persistence for React navigation, selected process, inspector tab, log target, sidebar collapse state, inspector collapse state, and table density.
67
+ - Added a collapsible right process inspector with a persisted collapsed rail.
68
+ - Added collapsed-sidebar popover tooltips and brief native tooltips for visible React buttons.
69
+ - Added a Settings action to reset local browser preferences, including themes, filters, table columns, metric history, selected process, collapse states, protected names, and log settings.
70
+ - Added keyboard workflows for search focus, dashboard refresh, selected-process logs, row selection movement, and clearing selected rows.
71
+ - Added theme-aware chart strokes that use CSS variables.
72
+ - Added a user manual at `user_manual.md` with installation, usage, settings, troubleshooting, and security instructions.
73
+ - Added manual screenshots under `docs/images/`.
74
+
75
+ ### Changed
76
+
77
+ - Updated the roadmap to mark React migration work and the recent operations-console batch as completed or partially completed where appropriate.
78
+ - Changed the React protected-process editor to display saved process names one per line.
79
+ - Reduced metric chart card and canvas heights so charts fit inside the Metric History panel without overlapping the process table.
80
+ - Changed sidebar Logs behavior so it opens logs only in the central workspace and no longer duplicates the log viewer in the inspector.
81
+
82
+ ### Fixed
83
+
84
+ - Fixed full-app React theme application so theme choices affect the app shell, panels, controls, and charts instead of only isolated buttons.
85
+ - Fixed the sidebar Logs navigation item so it opens a real logs workspace.
86
+ - Fixed newline-separated protected process names so entries such as `node-webui` and `server` on separate lines are saved as separate protected names.
87
+ - Fixed protected process UI enforcement after saving newline-separated names, disabling protected process controls in the table and inspector.
88
+
89
+ ## 1.15.0 - 2026-06-07
90
+
91
+ ### Added
92
+
93
+ - Added a Pause Live/Resume Live dashboard control that stops automatic stream/polling updates while keeping manual refresh available.
94
+
95
+ ## 1.14.0 - 2026-06-07
96
+
97
+ ### Added
98
+
99
+ - Added a process result counter that shows total processes and filtered result counts next to the process filters.
100
+
101
+ ## 1.13.0 - 2026-06-07
102
+
103
+ ### Added
104
+
105
+ - Persisted process search, process status filter, log line count, log auto-refresh, and log wrap preferences in local browser storage.
106
+
107
+ ## 1.12.1 - 2026-06-07
108
+
109
+ ### Changed
110
+
111
+ - Updated the roadmap to mark completed vanilla dashboard work and move the app to React-ready status.
112
+
113
+ ## 1.12.0 - 2026-06-07
114
+
115
+ ### Changed
116
+
117
+ - Changed the dashboard CPU/RAM cards and metric history charts to show PM2 process usage instead of host system usage.
118
+ - Changed PM2 RAM display to bytes and reset local metric history storage so older host-memory percentage samples do not mix with PM2 RAM samples.
119
+
120
+ ## 1.11.0 - 2026-06-07
121
+
122
+ ### Added
123
+
124
+ - Added local historical charts for CPU, RAM, restart count, and process status counts with a configurable 15/30/60 minute window.
125
+
126
+ ## 1.10.0 - 2026-06-07
127
+
128
+ ### Added
129
+
130
+ - Added live PM2 log streaming for the logs modal, with the existing log reload path retained as a fallback.
131
+
132
+ ## 1.9.0 - 2026-06-07
133
+
134
+ ### Added
135
+
136
+ - Added a Server-Sent Events dashboard stream for live process and metric updates while keeping polling as a fallback.
137
+
138
+ ## 1.8.0 - 2026-06-07
139
+
140
+ ### Added
141
+
142
+ - Added optional PM2 app creation fields for interpreter, Node args, NODE_ENV, instances, watch mode, max-memory restart, and output/error log paths.
143
+
144
+ ## 1.7.0 - 2026-06-07
145
+
146
+ ### Added
147
+
148
+ - Added a PM2 Runtime dashboard panel showing PM2 version, daemon PID and uptime, managed app count, saved state, and startup persistence status.
149
+
150
+ ## 1.6.0 - 2026-06-07
151
+
152
+ ### Added
153
+
154
+ - Expanded process details with runtime summaries, PM2 namespace, version, environment, instances, watch and autorestart settings, arguments, and PM2 file paths.
155
+
156
+ ### Fixed
157
+
158
+ - Preserved zero values such as restart counts in the process details modal instead of treating them as missing.
159
+
160
+ ## 1.5.0 - 2026-06-07
161
+
162
+ ### Added
163
+
164
+ - Added Lagoon, Ruby, Amber, and Nord built-in theme families with System, Light, and Dark mode support.
165
+
166
+ ## 1.4.1 - 2026-06-07
167
+
168
+ ### Changed
169
+
170
+ - Migrated remaining scratch roadmap notes into the tracked roadmap.
171
+ - Replaced duplicate root-level system monitor scratch scripts with a single tracked example in `examples/sys-monitor.js`.
172
+
173
+ ## 1.4.0 - 2026-06-07
174
+
175
+ ### Changed
176
+
177
+ - Replaced custom color and saved-theme management with curated built-in theme families.
178
+ - Added Default, Graphite, Midnight, Forest, and High Contrast theme families, each supporting System, Light, and Dark modes.
179
+ - Clarified that protected process settings in the browser are local UI protections and that API-level protection is configured with `PROTECTED_PM2_PROCESSES`.
180
+
181
+ ## 1.3.2 - 2026-06-07
182
+
183
+ ### Changed
184
+
185
+ - Updated the roadmap to replace custom theme management with curated built-in theme families that each support System, Light, and Dark modes.
186
+
187
+ ## 1.3.1 - 2026-06-07
188
+
189
+ ### Fixed
190
+
191
+ - Improved PM2 app creation argument parsing so quoted values and escaped characters are preserved.
192
+ - Added validation for unmatched quotes in new process arguments.
193
+
194
+ ## 1.3.0 - 2026-06-07
195
+
196
+ ### Added
197
+
198
+ - Added PM2 runtime status data to the dashboard API, including PM2 home, saved dump status, dump timestamp, and macOS startup LaunchAgent detection.
199
+ - Added `/api/pm2/status` for retrieving PM2 persistence status without loading full dashboard process data.
200
+
201
+ ## 1.2.0 - 2026-06-07
202
+
203
+ ### Added
204
+
205
+ - Added richer PM2 process metadata to the dashboard API, including interpreter, exec mode, namespace, args, node args, log paths, PID path, PM2 home, watch state, unstable restarts, version, and safe environment labels.
206
+ - Populated existing process details modal fields for interpreter and exec mode when PM2 provides them.
207
+
208
+ ## 1.1.4 - 2026-06-07
209
+
210
+ ### Fixed
211
+
212
+ - Fixed service module import casing so the app can load on case-sensitive filesystems.
213
+
214
+ ## 1.1.3 - 2026-06-07
215
+
216
+ ### Fixed
217
+
218
+ - Added server-side protected process enforcement for stop, restart, and delete API routes.
219
+ - Added `PROTECTED_PM2_PROCESSES` support for configuring protected PM2 app names on the server.
220
+
221
+ ## 1.1.2 - 2026-06-07
222
+
223
+ ### Fixed
224
+
225
+ - Replaced shell-string PM2 log commands with argument-safe `execFile` calls.
226
+ - Clamped requested log line counts to a safe range before passing them to PM2.
227
+
228
+ ## 1.1.1 - 2026-06-07
229
+
230
+ ### Fixed
231
+
232
+ - Fixed dashboard CPU and RAM summary values so the UI reports host system usage instead of aggregate PM2 process usage.
233
+ - Preserved PM2 aggregate CPU and RAM usage in the dashboard API as `pm2Usage` for future UI use.
234
+
235
+ ## 1.1.0 - 2026-06-07
236
+
237
+ ### Added
238
+
239
+ - Added a project roadmap with prioritized work for safety, process data, monitoring, UX, UI refresh, React migration, and maintainability.
240
+ - Added a deferred React migration plan. React is recommended later when the frontend needs richer component structure, live logs, charts, settings tabs, and more reusable state.
241
+ - Added a more useful empty state for dashboards with no PM2 processes, including direct actions to create a PM2 app or refresh the dashboard.
242
+
243
+ ### Changed
244
+
245
+ - Refreshed the dashboard styling to feel more like a compact operations console.
246
+ - Made summary cards tighter and easier to scan.
247
+ - Improved table readability with a sticky header, clearer hover states, and right-aligned numeric columns.
248
+ - Strengthened process status badges with consistent semantic styling and status dots.
249
+ - Replaced mixed action glyphs and emoji with consistent inline SVG icons while keeping accessible labels.
250
+ - Tuned modal and control styling for smaller radii, quieter surfaces, and consistent focus states.
@@ -0,0 +1 @@
1
+ :root{color-scheme:dark;--bg: #0b0f14;--bg-2: #0f141b;--panel: #131922;--panel-2: #171f29;--panel-3: #0f151d;--line: rgba(148, 163, 184, .15);--line-strong: rgba(148, 163, 184, .26);--text: #e5edf6;--muted: #8b99a9;--muted-2: #657284;--accent: #38bdf8;--accent-2: #22d3ee;--accent-contrast: #041017;--accent-faint: color-mix(in srgb, var(--accent) 6%, transparent);--accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);--accent-selected: color-mix(in srgb, var(--accent) 9%, transparent);--accent-line: color-mix(in srgb, var(--accent) 38%, transparent);--success: #22c55e;--warning: #f59e0b;--danger: #ef4444;--shadow: 0 18px 50px rgba(0, 0, 0, .28);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}body[data-theme-mode=light]{color-scheme:light;--bg: #eef2f6;--bg-2: #f7f9fc;--panel: #ffffff;--panel-2: #f8fafc;--panel-3: #edf2f7;--line: rgba(15, 23, 42, .12);--line-strong: rgba(15, 23, 42, .22);--text: #111827;--muted: #526071;--muted-2: #7b8794;--accent-contrast: #ffffff;--shadow: 0 16px 40px rgba(15, 23, 42, .12)}body[data-theme-family=graphite][data-theme-mode=dark]{--bg: #0d0d0f;--bg-2: #131316;--panel: #19191d;--panel-2: #202026;--panel-3: #111114;--line: rgba(212, 212, 216, .14);--line-strong: rgba(212, 212, 216, .26);--text: #f4f4f5;--muted: #a1a1aa;--muted-2: #71717a;--accent: #a1a1aa;--accent-2: #d4d4d8}body[data-theme-family=graphite][data-theme-mode=light]{--bg: #f4f4f5;--bg-2: #fafafa;--panel: #ffffff;--panel-2: #f4f4f5;--panel-3: #e4e4e7;--line: rgba(39, 39, 42, .14);--line-strong: rgba(39, 39, 42, .25);--text: #18181b;--muted: #52525b;--muted-2: #71717a;--accent: #52525b;--accent-2: #27272a}body[data-theme-family=midnight][data-theme-mode=dark]{--bg: #07111f;--bg-2: #0b1627;--panel: #101d31;--panel-2: #14243a;--panel-3: #0b1828;--line: rgba(147, 197, 253, .15);--line-strong: rgba(147, 197, 253, .28);--text: #dbeafe;--muted: #93a4bb;--muted-2: #64748b;--accent: #60a5fa;--accent-2: #38bdf8}body[data-theme-family=midnight][data-theme-mode=light]{--bg: #eef2ff;--bg-2: #f8fbff;--panel: #ffffff;--panel-2: #eff6ff;--panel-3: #dbeafe;--line: rgba(37, 99, 235, .17);--line-strong: rgba(37, 99, 235, .3);--text: #172554;--muted: #475569;--muted-2: #64748b;--accent: #2563eb;--accent-2: #0891b2}body[data-theme-family=forest][data-theme-mode=dark]{--bg: #07130d;--bg-2: #0d1b14;--panel: #12241a;--panel-2: #172d20;--panel-3: #0b1811;--line: rgba(134, 239, 172, .14);--line-strong: rgba(134, 239, 172, .28);--text: #dcfce7;--muted: #9fc8aa;--muted-2: #75a681;--accent: #4ade80;--accent-2: #22c55e}body[data-theme-family=forest][data-theme-mode=light]{--bg: #f0fdf4;--bg-2: #f8fffb;--panel: #ffffff;--panel-2: #ecfdf5;--panel-3: #dcfce7;--line: rgba(21, 128, 61, .16);--line-strong: rgba(21, 128, 61, .28);--text: #14532d;--muted: #3f6212;--muted-2: #65a30d;--accent: #15803d;--accent-2: #16a34a}body[data-theme-family=lagoon][data-theme-mode=dark]{--bg: #061519;--bg-2: #0a1f25;--panel: #102a31;--panel-2: #153640;--panel-3: #0b2026;--line: rgba(103, 232, 249, .15);--line-strong: rgba(103, 232, 249, .28);--text: #cffafe;--muted: #8bcbd4;--muted-2: #5aa6b1;--accent: #22d3ee;--accent-2: #38bdf8}body[data-theme-family=lagoon][data-theme-mode=light]{--bg: #ecfeff;--bg-2: #f8ffff;--panel: #ffffff;--panel-2: #cffafe;--panel-3: #a5f3fc;--line: rgba(8, 145, 178, .18);--line-strong: rgba(8, 145, 178, .3);--text: #164e63;--muted: #155e75;--muted-2: #0e7490;--accent: #0891b2;--accent-2: #0e7490}body[data-theme-family=ruby][data-theme-mode=dark]{--bg: #19070d;--bg-2: #241018;--panel: #301522;--panel-2: #3a1827;--panel-3: #201018;--line: rgba(253, 164, 175, .15);--line-strong: rgba(253, 164, 175, .3);--text: #ffe4e6;--muted: #dda2ae;--muted-2: #b87783;--accent: #fb7185;--accent-2: #f43f5e}body[data-theme-family=ruby][data-theme-mode=light]{--bg: #fff1f2;--bg-2: #fff7f8;--panel: #ffffff;--panel-2: #ffe4e6;--panel-3: #fecdd3;--line: rgba(190, 18, 60, .17);--line-strong: rgba(190, 18, 60, .3);--text: #4c0519;--muted: #9f1239;--muted-2: #be123c;--accent: #be123c;--accent-2: #e11d48}body[data-theme-family=amber][data-theme-mode=dark]{--bg: #171006;--bg-2: #211707;--panel: #2b1d0a;--panel-2: #34240d;--panel-3: #1f1609;--line: rgba(252, 211, 77, .15);--line-strong: rgba(252, 211, 77, .3);--text: #fef3c7;--muted: #d6b979;--muted-2: #a8843b;--accent: #fbbf24;--accent-2: #f59e0b}body[data-theme-family=amber][data-theme-mode=light]{--bg: #fffbeb;--bg-2: #fffdf5;--panel: #ffffff;--panel-2: #fef3c7;--panel-3: #fde68a;--line: rgba(180, 83, 9, .18);--line-strong: rgba(180, 83, 9, .3);--text: #451a03;--muted: #92400e;--muted-2: #b45309;--accent: #b45309;--accent-2: #d97706}body[data-theme-family=nord][data-theme-mode=dark]{--bg: #111827;--bg-2: #172033;--panel: #1f2937;--panel-2: #273449;--panel-3: #162031;--line: rgba(203, 213, 225, .14);--line-strong: rgba(203, 213, 225, .27);--text: #f8fafc;--muted: #cbd5e1;--muted-2: #94a3b8;--accent: #93c5fd;--accent-2: #60a5fa}body[data-theme-family=nord][data-theme-mode=light]{--bg: #f8fafc;--bg-2: #ffffff;--panel: #ffffff;--panel-2: #f1f5f9;--panel-3: #e2e8f0;--line: rgba(71, 85, 105, .16);--line-strong: rgba(71, 85, 105, .28);--text: #1e293b;--muted: #475569;--muted-2: #64748b;--accent: #2563eb;--accent-2: #0ea5e9}body[data-theme-family=high-contrast][data-theme-mode=dark]{--bg: #000000;--bg-2: #000000;--panel: #000000;--panel-2: #111111;--panel-3: #000000;--line: #ffffff;--line-strong: #ffffff;--text: #ffffff;--muted: #d1d5db;--muted-2: #f3f4f6;--accent: #ffffff;--accent-2: #ffffff;--accent-contrast: #000000}body[data-theme-family=high-contrast][data-theme-mode=light]{--bg: #ffffff;--bg-2: #ffffff;--panel: #ffffff;--panel-2: #f3f4f6;--panel-3: #ffffff;--line: #000000;--line-strong: #000000;--text: #000000;--muted: #111827;--muted-2: #1f2937;--accent: #000000;--accent-2: #000000;--accent-contrast: #ffffff}*{box-sizing:border-box}html,body,#root{min-height:100%;margin:0}body{background:var(--bg);color:var(--text);overflow:hidden}button,input,select,textarea{font:inherit}button,select,input,textarea{color:var(--text)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.45}.app-shell{display:grid;grid-template-columns:auto minmax(620px,1fr) 340px;height:100vh;min-height:720px;background:linear-gradient(180deg,var(--accent-faint),transparent 34%),var(--bg)}.app-shell.inspector-collapsed-shell{grid-template-columns:auto minmax(620px,1fr) 44px}.sidebar{position:relative;z-index:60;width:232px;display:flex;flex-direction:column;gap:14px;padding:14px 12px;border-right:1px solid var(--line);background:var(--bg-2)}.sidebar-collapsed{width:68px;align-items:center}.brand-row{display:grid;grid-template-columns:30px 1fr 30px;align-items:center;gap:9px;min-height:38px}.sidebar-collapsed .brand-row{display:flex;flex-direction:column}.brand-mark{width:30px;height:30px;display:grid;place-items:center;border:1px solid var(--accent-line);border-radius:8px;color:var(--accent);background:var(--accent-soft)}.brand-row h1,.brand-row p,.panel-header h2,.inspector-header h2,.settings-block h3,.modal h2{margin:0;letter-spacing:0}.brand-row h1{font-size:15px;line-height:1.15}.brand-row p{color:var(--muted);font-size:11px;margin-top:2px}.connection-pill{min-height:30px;display:flex;align-items:center;gap:8px;padding:6px 8px;border:1px solid var(--line);border-radius:8px;color:var(--muted);background:var(--panel);font-size:12px}.connection-pill span{width:8px;height:8px;border-radius:999px;background:var(--muted-2)}.connection-pill.good span{background:var(--success)}.connection-pill.warn span{background:var(--warning)}.connection-pill.bad span{background:var(--danger)}.connection-pill strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.nav-stack{display:grid;gap:6px}.nav-button{position:relative;width:100%;display:flex;align-items:center;gap:9px;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--muted);padding:9px 10px;text-align:left;font-size:13px}.sidebar-collapsed .nav-button{justify-content:center;padding:9px}.sidebar-tooltip{position:absolute;left:calc(100% + 10px);top:50%;z-index:80;min-width:max-content;max-width:180px;transform:translateY(-50%) translate(-4px);border:1px solid var(--line-strong);border-radius:7px;background:var(--panel);box-shadow:var(--shadow);color:var(--text);padding:6px 8px;pointer-events:none;white-space:nowrap;font-size:12px;font-weight:700;transform:translateY(-50%) translate(0)}.nav-button.active,.nav-button:hover{border-color:var(--line);color:var(--text);background:var(--panel)}.sidebar-section{padding-top:12px;border-top:1px solid var(--line);display:grid;gap:9px}.section-title{color:var(--muted-2);font-size:10px;line-height:1;text-transform:uppercase;font-weight:800}.mini-meter{display:grid;gap:5px}.mini-meter div:first-child,.runtime-line{display:flex;justify-content:space-between;gap:10px;font-size:12px}.mini-meter span,.runtime-line span{color:var(--muted)}.mini-meter strong,.runtime-line strong{color:var(--text);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.runtime-line.good strong{color:var(--success)}.runtime-line.warn strong{color:var(--warning)}.meter-track{height:5px;overflow:hidden;border-radius:999px;background:#94a3b829}.meter-track span{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--accent),var(--success))}.quick-settings label,.inline-field,.form-field{display:grid;gap:5px;color:var(--muted);font-size:11px;font-weight:700}.quick-settings select,.inline-field select,.form-field input,.form-field select,.form-field textarea,.search-field input,.logs-toolbar select{min-height:32px;width:100%;border:1px solid var(--line);border-radius:7px;background:var(--panel-3);color:var(--text);padding:0 9px;font-size:12px}.form-field textarea{min-height:88px;padding:9px;resize:vertical}.form-field.invalid input{border-color:#ef4444cc;background:#ef444414}.form-field em{color:var(--danger);font-style:normal;margin-left:2px}.segmented{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--line);border-radius:8px;overflow:hidden}.segmented button{height:30px;border:0;border-right:1px solid var(--line);color:var(--muted);background:var(--panel-3)}.segmented button:last-child{border-right:0}.segmented button.active{color:var(--accent);background:var(--accent-soft)}.main-region{min-width:0;display:flex;flex-direction:column;gap:12px;padding:14px;overflow:auto}.top-toolbar,.panel,.inspector,.modal,.metric-tile{border:1px solid var(--line);background:color-mix(in srgb,var(--panel) 92%,transparent);box-shadow:var(--shadow)}.top-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:54px;padding:9px 10px;border-radius:8px}.toolbar-caption,.panel-header p,.chart-header span,.metric-tile span,.logs-status,.modal p{color:var(--muted);font-size:11px}.top-toolbar strong{display:block;margin-top:2px;font-size:13px}.toolbar-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.button,.icon-button{display:inline-flex;align-items:center;justify-content:center;gap:7px;border:1px solid var(--line);border-radius:7px;color:var(--text);background:var(--panel-2);min-height:32px;padding:0 10px;font-size:12px;font-weight:700}.button:hover,.icon-button:hover{border-color:var(--line-strong);background:color-mix(in srgb,var(--panel-2) 78%,var(--accent))}.button.primary{color:var(--accent-contrast);border-color:transparent;background:var(--accent)}.button.danger-button{color:#fff;border-color:#ef444473;background:var(--danger)}.button.secondary{background:var(--panel-3)}.button.ghost,.icon-button.ghost{background:transparent}.icon-button{width:30px;min-width:30px;padding:0}.icon-button.danger:hover{color:#fecaca;border-color:#ef444473;background:#ef444424}.icon-button.active{color:var(--accent);border-color:var(--accent-line);background:var(--accent-soft)}.busy-chip{display:inline-flex;align-items:center;gap:6px;min-height:30px;border:1px solid rgba(245,158,11,.35);border-radius:999px;padding:0 9px;color:var(--warning);background:#f59e0b1a;font-size:12px}.summary-row{display:grid;grid-template-columns:repeat(7,minmax(88px,1fr));gap:8px}.metric-tile{min-height:76px;display:grid;align-content:space-between;border-radius:8px;padding:10px}.metric-tile div{display:flex;align-items:center;gap:7px;color:var(--muted)}.metric-tile strong{font-size:22px;line-height:1}.metric-tile.good strong{color:var(--success)}.metric-tile.warn strong{color:var(--warning)}.metric-tile.bad strong{color:var(--danger)}.panel{border-radius:8px;padding:12px}.panel-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.panel-header h2{font-size:15px}.panel-header p{margin:3px 0 0}.metric-history{min-height:176px}.chart-grid{display:grid;grid-template-columns:repeat(4,minmax(140px,1fr));gap:8px}.chart-panel{height:96px;min-height:0;border:1px solid var(--line);border-radius:8px;background:var(--panel-3);padding:7px}.chart-header{display:flex;justify-content:space-between;gap:10px;margin-bottom:4px}.chart-header strong{font-size:12px}.spark-chart{display:block;width:100%;height:52px}.process-panel{min-height:420px;padding-bottom:0}.process-header{align-items:center}.process-tools{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.process-tools .status-filter{height:32px;min-width:144px;display:flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:7px;background:var(--panel-3);padding:0 8px;color:var(--muted)}.process-tools .status-filter select{min-height:0;border:0;background:transparent;padding:0;outline:0}.density-toggle{width:188px}.density-toggle button{padding:0 8px;font-size:11px}.search-field{min-width:220px;height:32px;display:flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:7px;background:var(--panel-3);padding:0 8px;color:var(--muted)}.search-field input{min-height:0;border:0;background:transparent;padding:0;outline:0}.search-field.mini{min-width:150px;flex:1}.bulk-bar{min-height:40px;display:flex;align-items:center;gap:7px;margin-bottom:10px;padding:7px 8px;border:1px solid var(--accent-line);border-radius:8px;background:var(--accent-selected)}.bulk-bar strong{margin-right:auto;font-size:12px}.table-scroll{height:min(47vh,540px);min-height:360px;overflow:auto;border-top:1px solid var(--line);margin:0 -12px}.process-table{width:100%;min-width:980px;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:12px}.process-table th:nth-child(2),.process-table td:nth-child(2){width:96px}.process-table th:nth-child(3),.process-table td:nth-child(3){width:52px}.process-table th:nth-child(4),.process-table td:nth-child(4){width:190px}.process-table th:nth-child(5),.process-table td:nth-child(5),.process-table th:nth-child(6),.process-table td:nth-child(6){width:76px}.process-table th:nth-child(7),.process-table td:nth-child(7){width:82px}.process-table th:nth-child(8),.process-table td:nth-child(8),.process-table th:nth-child(9),.process-table td:nth-child(9){width:118px}.process-table thead th{position:sticky;top:0;z-index:2;height:36px;border-bottom:1px solid var(--line);background:var(--panel-3);color:var(--muted);text-align:left;font-size:11px;font-weight:800;text-transform:uppercase}.process-table th,.process-table td{height:42px;padding:0 9px;border-bottom:1px solid rgba(148,163,184,.09);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.process-table-comfortable th,.process-table-comfortable td{height:52px}.process-table-comfortable{font-size:13px}.process-table tbody tr{background:transparent}.process-table tbody tr:hover,.process-table tbody tr.selected{background:var(--accent-selected)}.process-table tbody tr.busy{opacity:.7}.check-col{width:42px;text-align:center}.actions-col{width:184px}.sort-control{border:0;background:transparent;color:inherit;padding:0;font-size:inherit;font-weight:inherit;text-transform:inherit}.sort-control span{color:var(--accent);text-transform:none}.mono{font-family:SFMono-Regular,Consolas,monospace}.name-cell{display:flex;align-items:center;gap:7px}.name-cell strong{overflow:hidden;text-overflow:ellipsis}.protected-badge,.status-badge{display:inline-flex;align-items:center;gap:4px;height:22px;border:1px solid var(--line);border-radius:999px;padding:0 7px;font-size:10px;font-weight:800;text-transform:uppercase}.protected-badge{color:#f87171;border-color:#f8717180;background:#ef44441f}.protected-badge svg{color:#ef4444;stroke-width:2.7}.status-badge.online{color:#86efac;border-color:#22c55e66;background:#22c55e1f}.status-badge.stopped{color:#fcd34d;border-color:#f59e0b66;background:#f59e0b1f}.status-badge.errored{color:#fca5a5;border-color:#ef444466;background:#ef44441f}.status-badge.launching{color:var(--accent-2);border-color:var(--accent-line);background:var(--accent-soft)}.status-badge.unknown{color:var(--muted)}.row-actions{display:flex;align-items:center;gap:5px}.column-menu{position:relative}.column-popover{position:absolute;top:calc(100% + 7px);right:0;z-index:20;width:164px;display:grid;gap:6px;border:1px solid var(--line);border-radius:8px;background:var(--panel);box-shadow:var(--shadow);padding:8px}.column-popover label{display:flex;align-items:center;gap:8px;font-size:12px}.empty-cell{height:160px;text-align:center;color:var(--muted)}.empty-state{min-height:210px;display:grid;place-items:center;align-content:center;gap:8px;color:var(--muted)}.empty-state strong{color:var(--text)}.empty-state p{max-width:430px;margin:0;text-align:center}.empty-state div{display:flex;gap:8px}.logs-workspace{min-height:calc(100vh - 90px)}.logs-workspace-header{align-items:center}.logs-workspace-grid{display:grid;grid-template-columns:minmax(220px,280px) minmax(0,1fr);gap:12px;min-height:0}.logs-process-list{display:grid;align-content:start;gap:7px;min-height:0;max-height:calc(100vh - 180px);overflow:auto;padding-right:2px}.logs-process-list button{width:100%;min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:10px;border:1px solid var(--line);border-radius:8px;color:var(--text);background:var(--panel-3);padding:8px;text-align:left}.logs-process-list button:hover,.logs-process-list button.active{border-color:var(--accent-line);background:var(--accent-selected)}.logs-process-list span{min-width:0;display:grid;gap:3px}.logs-process-list strong,.logs-process-list small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.logs-process-list strong{font-size:13px}.logs-process-list small{color:var(--muted);font-size:11px}.logs-workspace-viewer{min-width:0;min-height:0;border:1px solid var(--line);border-radius:8px;background:var(--panel-3);padding:10px}.logs-workspace-viewer .log-output{height:calc(100vh - 250px);min-height:420px}.inspector{min-width:0;display:flex;flex-direction:column;border-width:0 0 0 1px;border-radius:0;box-shadow:none;padding:14px 12px;overflow:auto;background:var(--bg-2)}.inspector-collapsed{width:44px;min-width:44px;align-items:center;overflow:visible;padding:10px 6px}.inspector-rail-button{width:32px;min-height:180px;display:grid;place-items:center;align-content:center;gap:10px;border:1px solid var(--line);border-radius:8px;color:var(--muted);background:var(--panel-3);font-size:11px;font-weight:800}.inspector-rail-button:hover{color:var(--text);border-color:var(--accent-line);background:var(--accent-selected)}.inspector-rail-button span{writing-mode:vertical-rl;text-orientation:mixed}.inspector-header{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}.inspector-header h2{font-size:18px;line-height:1.15;word-break:break-word}.badge-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.inspector-actions{padding-bottom:12px;border-bottom:1px solid var(--line)}.tabs{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin:12px 0}.tabs button{height:30px;border:1px solid var(--line);border-radius:7px;color:var(--muted);background:var(--panel-3);font-size:11px;font-weight:800}.tabs button.active{color:var(--accent);border-color:var(--accent-line);background:var(--accent-soft)}.key-grid{display:grid;gap:7px}.protection-toggle{min-height:48px;display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:9px;margin-bottom:8px;padding:9px;border:1px solid var(--accent-line);border-radius:8px;background:var(--accent-selected)}.protection-toggle input{width:16px;height:16px}.protection-toggle span{min-width:0;display:grid;gap:2px}.protection-toggle strong{color:var(--text);font-size:12px}.protection-toggle small{color:var(--muted);font-size:11px;line-height:1.3}.key-row{display:grid;grid-template-columns:112px minmax(0,1fr);align-items:start;gap:8px;min-height:34px;padding:8px;border:1px solid var(--line);border-radius:8px;background:var(--panel)}.key-row span{color:var(--muted);font-size:11px}.key-row strong{min-width:0;overflow:hidden;color:var(--text);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.inspector-empty{min-height:100%;display:grid;place-items:center;align-content:center;gap:9px;color:var(--muted);text-align:center}.inspector-empty strong{color:var(--text)}.inspector-empty p{max-width:260px;margin:0;font-size:12px}.logs-viewer{display:grid;gap:8px;min-height:0}.logs-toolbar{display:flex;align-items:center;gap:5px;flex-wrap:wrap}.logs-toolbar select{width:68px}.log-output{height:360px;overflow:auto;border:1px solid var(--line);border-radius:8px;background:#06090d;padding:8px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px}body[data-theme-mode=light] .log-output{background:#111827;color:#e5edf6}.log-output.wrap .log-line p{white-space:pre-wrap}.log-output>p{margin:0;color:var(--muted)}.log-line{display:grid;grid-template-columns:48px 138px minmax(0,1fr);gap:8px;min-height:22px;align-items:start}.log-line span{color:var(--success);font-weight:800}.log-line.stderr span{color:var(--danger)}.log-line time{color:#8b99a9}.log-line p{margin:0;overflow:hidden;color:#dbeafe;text-overflow:ellipsis;white-space:pre}.settings-view{min-height:calc(100vh - 90px)}.settings-grid{display:grid;grid-template-columns:minmax(260px,360px) minmax(320px,1fr);gap:12px}.settings-block{display:grid;align-content:start;gap:12px;padding:12px;border:1px solid var(--line);border-radius:8px;background:var(--panel-3)}.settings-block h3{font-size:14px}.settings-block-wide{grid-column:1 / -1}.settings-description{max-width:760px;margin:0;color:var(--muted);font-size:12px;line-height:1.45}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:50;display:grid;place-items:center;padding:20px;background:#000000a3}.modal{width:min(860px,94vw);max-height:min(760px,92vh);display:flex;flex-direction:column;border-radius:8px;overflow:hidden}.modal-header,.modal-footer{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-bottom:1px solid var(--line)}.modal-footer{border-top:1px solid var(--line);border-bottom:0;justify-content:flex-end}.modal h2{font-size:17px}.modal p{margin:3px 0 0}.modal-body{overflow:auto;padding:12px}.confirm-modal{width:min(520px,94vw)}.confirm-modal-danger{border-color:#ef444459}.confirm-modal-warning{border-color:#f59e0b59}.confirm-details{display:grid;gap:7px;padding:12px;border-bottom:1px solid var(--line)}.confirm-details div{display:grid;grid-template-columns:96px minmax(0,1fr);gap:10px;min-height:30px;align-items:center;border:1px solid var(--line);border-radius:8px;background:var(--panel-3);padding:7px 8px}.confirm-details span{color:var(--muted);font-size:11px}.confirm-details strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.check-field{grid-template-columns:auto 1fr;align-items:center;min-height:51px;padding:18px 10px 0}.check-field input{width:auto}.toast{position:fixed;right:18px;bottom:18px;z-index:70;max-width:min(360px,calc(100vw - 36px));border:1px solid var(--line-strong);border-radius:8px;background:var(--panel);box-shadow:var(--shadow);padding:11px 12px;color:var(--text);font-size:13px;font-weight:700}.toast-success{border-color:#22c55e66}.toast-error{border-color:#ef444466}@media (max-width: 1180px){.app-shell{grid-template-columns:auto minmax(0,1fr)}.app-shell.inspector-collapsed-shell{grid-template-columns:auto minmax(0,1fr) 44px}.inspector{position:fixed;top:0;right:0;bottom:0;z-index:30;width:min(360px,94vw);transform:translate(calc(100% - 42px));transition:transform .16s ease}.inspector:hover,.inspector:focus-within{transform:translate(0)}.inspector-collapsed{width:44px;transform:none}.inspector-collapsed:hover,.inspector-collapsed:focus-within{transform:none}.summary-row,.chart-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width: 760px){body{overflow:auto}.app-shell{height:auto;min-height:100vh;grid-template-columns:1fr}.sidebar{position:sticky;top:0;z-index:40;width:100%;border-right:0;border-bottom:1px solid var(--line)}.sidebar-collapsed{width:100%}.sidebar-tooltip{display:none}.sidebar-collapsed .brand-row{flex-direction:row;width:100%}.main-region{padding:10px}.top-toolbar,.panel-header,.process-header{align-items:stretch;flex-direction:column}.toolbar-actions,.process-tools{justify-content:stretch}.button{flex:1}.summary-row,.chart-grid,.settings-grid,.form-grid,.logs-workspace-grid{grid-template-columns:1fr}.logs-process-list{max-height:230px}.logs-workspace-viewer .log-output{height:420px;min-height:360px}.table-scroll{height:60vh}.inspector{position:static;width:auto;transform:none;border-top:1px solid var(--line);border-left:0}.inspector-collapsed{width:auto;min-width:0;padding:10px}.inspector-rail-button{width:100%;min-height:40px;grid-auto-flow:column}.inspector-rail-button span{writing-mode:horizontal-tb}}