cli-jaw 2.17.67 → 2.17.68
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/dist/src/browser/web-ai/context-pack/file-selector.js +20 -0
- package/dist/src/browser/web-ai/context-pack/file-selector.js.map +1 -1
- package/package.json +1 -1
- package/public/dist/.vite/manifest.json +2 -2
- package/public/dist/assets/manager-B9imHVqw.css +1 -0
- package/public/dist/assets/{manager-BXf6oL68.js → manager-Dhz8mX5E.js} +2 -2
- package/public/dist/manager/index.html +2 -2
- package/public/manager/src/components/InstanceDetailPanel.tsx +64 -11
- package/public/manager/src/components/ProcessControlPanel.tsx +92 -59
- package/public/manager/src/components/instance-overview.css +97 -0
- package/public/manager/src/components/process-control.css +128 -0
- package/public/manager/src/manager-components.css +1 -76
- package/public/manager/src/manager-layout.css +0 -2
- package/scripts/audit-allowlist.json +48 -2
- package/public/dist/assets/manager-CEtdsk3Q.css +0 -1
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/dist/assets/icon-192-CADbZUeU.png">
|
|
7
7
|
<title>Jaw Manager</title>
|
|
8
8
|
<script src="/manager/theme-boot.js"></script>
|
|
9
|
-
<script type="module" crossorigin src="/dist/assets/manager-
|
|
9
|
+
<script type="module" crossorigin src="/dist/assets/manager-Dhz8mX5E.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-DSSkXCir.css">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/dist/assets/settings-embedding-CUQ8Is3y.css">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/dist/assets/manager-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/dist/assets/manager-B9imHVqw.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="manager-root"></div>
|
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
} from '../types';
|
|
7
7
|
import { ProcessControlPanel } from './ProcessControlPanel';
|
|
8
8
|
import { InstanceLogsPanel } from './InstanceLogsPanel';
|
|
9
|
+
import { formatUptime } from '../instance-label';
|
|
10
|
+
import './instance-overview.css';
|
|
9
11
|
|
|
10
12
|
type InstanceDetailPanelProps = {
|
|
11
13
|
instance: DashboardInstance | null;
|
|
@@ -16,22 +18,73 @@ type InstanceDetailPanelProps = {
|
|
|
16
18
|
|
|
17
19
|
export function InstanceDetailPanel(props: InstanceDetailPanelProps) {
|
|
18
20
|
const instance = props.instance;
|
|
21
|
+
const checkedAt = instance ? new Date(instance.lastCheckedAt) : null;
|
|
22
|
+
const hasCheckedAt = checkedAt != null && Number.isFinite(checkedAt.getTime());
|
|
19
23
|
|
|
20
24
|
return (
|
|
21
25
|
<section className="detail-panel" aria-label="Selected instance detail">
|
|
22
26
|
{props.activeTab === 'overview' && (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
<div className="instance-overview">
|
|
28
|
+
{instance ? (
|
|
29
|
+
<section className="overview-instance" aria-label="Instance overview">
|
|
30
|
+
<header className="overview-status-line">
|
|
31
|
+
<div className="overview-status-heading">
|
|
32
|
+
<span className={`overview-status is-${instance.status}`}>
|
|
33
|
+
<span aria-hidden="true" />
|
|
34
|
+
{instance.status}
|
|
35
|
+
</span>
|
|
36
|
+
<span className="overview-port">:{instance.port}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<span className="overview-checked">
|
|
39
|
+
{hasCheckedAt ? <>Checked <time dateTime={checkedAt.toISOString()} title={checkedAt.toLocaleString()}>{checkedAt.toLocaleTimeString()}</time></> : 'Check time unavailable'}
|
|
40
|
+
</span>
|
|
41
|
+
</header>
|
|
42
|
+
{!instance.ok && <p className="overview-unavailable">This instance is not responding. Check its logs or use the instance menu to manage it.</p>}
|
|
43
|
+
<div className="overview-sections">
|
|
44
|
+
<section aria-label="Runtime">
|
|
45
|
+
<h3>Runtime</h3>
|
|
46
|
+
<dl className="overview-properties">
|
|
47
|
+
<div><dt>CLI</dt><dd>{instance.currentCli || 'Not reported'}</dd></div>
|
|
48
|
+
<div><dt>Model</dt><dd className="overview-mono">{instance.currentModel || 'Not reported'}</dd></div>
|
|
49
|
+
<div><dt>Version</dt><dd>{instance.version || 'Not reported'}</dd></div>
|
|
50
|
+
<div><dt>Uptime</dt><dd>{instance.ok && instance.uptime != null ? formatUptime(instance.uptime) : 'Not available'}</dd></div>
|
|
51
|
+
</dl>
|
|
52
|
+
</section>
|
|
53
|
+
<section aria-label="Connection">
|
|
54
|
+
<h3>Connection</h3>
|
|
55
|
+
<dl className="overview-properties">
|
|
56
|
+
<div><dt>Address</dt><dd className="overview-mono">{instance.url}</dd></div>
|
|
57
|
+
<div><dt>Managed by</dt><dd>{instance.lifecycle?.owner || 'Unknown'}</dd></div>
|
|
58
|
+
<div><dt>Process ID</dt><dd className="overview-mono">{instance.lifecycle?.pid ?? 'Not reported'}</dd></div>
|
|
59
|
+
<div><dt>Group</dt><dd>{instance.group || 'Ungrouped'}</dd></div>
|
|
60
|
+
</dl>
|
|
61
|
+
</section>
|
|
62
|
+
</div>
|
|
63
|
+
<section className="overview-project" aria-label="Project directories">
|
|
64
|
+
<h3>Project</h3>
|
|
65
|
+
{instance.projectDirs?.length ? (
|
|
66
|
+
<ul>{instance.projectDirs.map(dir => <li key={dir} className="overview-mono">{dir}</li>)}</ul>
|
|
67
|
+
) : <p>{instance.ok && instance.projectDirs != null
|
|
68
|
+
? 'No project selected. Choose a folder from the instance header.'
|
|
69
|
+
: 'Project information unavailable.'}</p>}
|
|
70
|
+
</section>
|
|
71
|
+
<details className="overview-diagnostics">
|
|
72
|
+
<summary>Health & ownership details</summary>
|
|
73
|
+
<dl className="overview-properties">
|
|
74
|
+
<div><dt>Health reason</dt><dd>{instance.healthReason || 'Not reported'}</dd></div>
|
|
75
|
+
<div><dt>Ownership reason</dt><dd>{instance.lifecycle?.reason || 'Not reported'}</dd></div>
|
|
76
|
+
<div><dt>Service mode</dt><dd>{instance.serviceMode}</dd></div>
|
|
77
|
+
</dl>
|
|
78
|
+
</details>
|
|
79
|
+
</section>
|
|
80
|
+
) : (
|
|
81
|
+
<div className="overview-no-selection">
|
|
82
|
+
<h3>No instance selected</h3>
|
|
83
|
+
<p>Select an instance in the sidebar to see its runtime and connection details.</p>
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
33
86
|
<ProcessControlPanel />
|
|
34
|
-
|
|
87
|
+
</div>
|
|
35
88
|
)}
|
|
36
89
|
|
|
37
90
|
{props.activeTab === 'logs' && instance && (
|
|
@@ -1,87 +1,120 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
adoptManagedProcesses,
|
|
4
4
|
fetchProcessControlState,
|
|
5
5
|
stopManagedProcesses,
|
|
6
6
|
} from '../api';
|
|
7
7
|
import type { DashboardProcessControlState } from '../types';
|
|
8
|
+
import './process-control.css';
|
|
9
|
+
|
|
10
|
+
type ProcessOperation = 'refresh' | 'recover' | 'stop';
|
|
8
11
|
|
|
9
12
|
export function ProcessControlPanel() {
|
|
10
13
|
const [state, setState] = useState<DashboardProcessControlState | null>(null);
|
|
11
|
-
const [busy, setBusy] = useState<
|
|
14
|
+
const [busy, setBusy] = useState<ProcessOperation | null>('refresh');
|
|
12
15
|
const [message, setMessage] = useState<string | null>(null);
|
|
13
|
-
const
|
|
16
|
+
const [error, setError] = useState<string | null>(null);
|
|
17
|
+
const mounted = useRef(false);
|
|
18
|
+
const activeRequest = useRef<object | null>(null);
|
|
19
|
+
const managedCount = state?.managed.length ?? 0;
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
setMessage((error as Error).message);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function run(label: string, fn: () => Promise<DashboardProcessControlState>): Promise<void> {
|
|
28
|
-
setBusy(label);
|
|
21
|
+
const run = useCallback(async (
|
|
22
|
+
operation: ProcessOperation,
|
|
23
|
+
request: () => Promise<DashboardProcessControlState>,
|
|
24
|
+
): Promise<void> => {
|
|
25
|
+
if (!mounted.current || activeRequest.current) return;
|
|
26
|
+
const token = {};
|
|
27
|
+
activeRequest.current = token;
|
|
28
|
+
setBusy(operation);
|
|
29
29
|
setMessage(null);
|
|
30
|
+
setError(null);
|
|
30
31
|
try {
|
|
31
|
-
const next = await
|
|
32
|
+
const next = await request();
|
|
33
|
+
if (!mounted.current || activeRequest.current !== token) return;
|
|
32
34
|
setState(next);
|
|
33
|
-
setMessage(
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
setMessage(operation === 'stop'
|
|
36
|
+
? 'Stop request complete. The list shows remaining managed servers.'
|
|
37
|
+
: operation === 'recover' ? 'Recovery complete. Managed process records refreshed.' : null);
|
|
38
|
+
} catch (cause) {
|
|
39
|
+
if (!mounted.current || activeRequest.current !== token) return;
|
|
40
|
+
setError(cause instanceof Error ? cause.message : 'Unable to update managed processes. Try refreshing.');
|
|
36
41
|
} finally {
|
|
37
|
-
|
|
42
|
+
if (mounted.current && activeRequest.current === token) {
|
|
43
|
+
activeRequest.current = null;
|
|
44
|
+
setBusy(null);
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
|
-
}
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
mounted.current = true;
|
|
51
|
+
void run('refresh', fetchProcessControlState);
|
|
52
|
+
return () => {
|
|
53
|
+
mounted.current = false;
|
|
54
|
+
activeRequest.current = null;
|
|
55
|
+
};
|
|
56
|
+
}, [run]);
|
|
40
57
|
|
|
41
58
|
function handleStopAll(): void {
|
|
42
|
-
if (managedCount === 0) return;
|
|
43
|
-
if (!window.confirm(`Stop ${managedCount} dashboard-managed server${managedCount === 1 ? '' : 's'}?`)) return;
|
|
44
|
-
void run('
|
|
59
|
+
if (activeRequest.current || managedCount === 0 || error) return;
|
|
60
|
+
if (!window.confirm(`Stop ${managedCount} dashboard-managed server${managedCount === 1 ? '' : 's'} across all instances?`)) return;
|
|
61
|
+
void run('stop', stopManagedProcesses);
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
return (
|
|
48
|
-
<section className="process-control-panel" aria-label="
|
|
49
|
-
<
|
|
50
|
-
<div>
|
|
51
|
-
<
|
|
52
|
-
<
|
|
65
|
+
<section className="process-control-panel" aria-label="Managed processes — all instances">
|
|
66
|
+
<header className="process-control-header">
|
|
67
|
+
<div className="process-control-heading">
|
|
68
|
+
<h3>Managed processes</h3>
|
|
69
|
+
<p>All instances in this manager</p>
|
|
53
70
|
</div>
|
|
54
|
-
<button type="button" onClick={refresh} disabled={busy
|
|
55
|
-
|
|
56
|
-
<div className="process-control-actions">
|
|
57
|
-
<button type="button" onClick={handleStopAll} disabled={busy != null || managedCount === 0}>
|
|
58
|
-
Stop all managed
|
|
59
|
-
</button>
|
|
60
|
-
<button
|
|
61
|
-
type="button"
|
|
62
|
-
onClick={() => void run('Adopt/recover managed', adoptManagedProcesses)}
|
|
63
|
-
disabled={busy != null}
|
|
64
|
-
>
|
|
65
|
-
Adopt/recover
|
|
66
|
-
</button>
|
|
67
|
-
<button type="button" disabled title={state?.unsupported.reason || 'Planned'}>
|
|
68
|
-
Force release port
|
|
71
|
+
<button type="button" onClick={() => void run('refresh', fetchProcessControlState)} disabled={busy !== null}>
|
|
72
|
+
{busy === 'refresh' ? 'Refreshing…' : 'Refresh'}
|
|
69
73
|
</button>
|
|
74
|
+
</header>
|
|
75
|
+
<div className="process-control-content" aria-busy={busy !== null}>
|
|
76
|
+
<p className="process-control-count">
|
|
77
|
+
{state ? `${managedCount} managed server${managedCount === 1 ? '' : 's'}` : 'Process count unavailable'}
|
|
78
|
+
{error && state && <span> · Last loaded list</span>}
|
|
79
|
+
</p>
|
|
80
|
+
{state && managedCount > 0 && (
|
|
81
|
+
<table className="process-control-table">
|
|
82
|
+
<caption className="process-control-sr-only">Dashboard-managed servers across all instances</caption>
|
|
83
|
+
<thead><tr><th scope="col">Port</th><th scope="col">PID</th><th scope="col">Ownership proof</th></tr></thead>
|
|
84
|
+
<tbody>
|
|
85
|
+
{state.managed.map(entry => (
|
|
86
|
+
<tr key={entry.port}>
|
|
87
|
+
<th scope="row">:{entry.port}</th>
|
|
88
|
+
<td>{entry.pid ?? 'Unavailable'}</td>
|
|
89
|
+
<td>{entry.proof === 'child' ? 'Child process' : 'Registry record'}</td>
|
|
90
|
+
</tr>
|
|
91
|
+
))}
|
|
92
|
+
</tbody>
|
|
93
|
+
</table>
|
|
94
|
+
)}
|
|
95
|
+
{state && managedCount === 0 && (
|
|
96
|
+
<p className="process-control-empty">No managed servers. Start an instance or recover existing records below.</p>
|
|
97
|
+
)}
|
|
70
98
|
</div>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
</p>
|
|
74
|
-
<div className="process-control-list">
|
|
75
|
-
{state?.managed.map(entry => (
|
|
76
|
-
<div key={entry.port} className="process-control-row">
|
|
77
|
-
<span>:{entry.port}</span>
|
|
78
|
-
<strong>{entry.pid ? `pid ${entry.pid}` : 'pid n/a'}</strong>
|
|
79
|
-
<em>{entry.proof}</em>
|
|
80
|
-
</div>
|
|
81
|
-
))}
|
|
82
|
-
{managedCount === 0 && <p>No dashboard-managed servers are registered.</p>}
|
|
99
|
+
<div className="process-control-feedback" role="status" aria-live="polite">
|
|
100
|
+
{busy ? (busy === 'refresh' ? 'Loading managed processes…' : busy === 'recover' ? 'Recovering managed processes…' : 'Stopping managed servers…') : message}
|
|
83
101
|
</div>
|
|
84
|
-
{
|
|
102
|
+
{error && <p className="process-control-error" role="alert">{error} Use Refresh to check the current state.</p>}
|
|
103
|
+
<details className="process-control-maintenance">
|
|
104
|
+
<summary>Recovery & stop all</summary>
|
|
105
|
+
<div className="process-control-maintenance-body">
|
|
106
|
+
<p>These actions apply to all dashboard-managed servers. External instances are not stopped.</p>
|
|
107
|
+
<div className="process-control-actions">
|
|
108
|
+
<button type="button" onClick={() => void run('recover', adoptManagedProcesses)} disabled={busy !== null}>
|
|
109
|
+
{busy === 'recover' ? 'Recovering…' : 'Adopt/recover'}
|
|
110
|
+
</button>
|
|
111
|
+
<button type="button" className="process-control-stop" onClick={handleStopAll} disabled={busy !== null || managedCount === 0 || error !== null}>
|
|
112
|
+
{busy === 'stop' ? 'Stopping…' : 'Stop all managed'}
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
<p className="process-control-hint">Adopt/recover reconnects known managed process records.</p>
|
|
116
|
+
</div>
|
|
117
|
+
</details>
|
|
85
118
|
</section>
|
|
86
119
|
);
|
|
87
120
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
.workbench-overview > .workbench-header {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
height: auto;
|
|
5
|
+
min-height: var(--workspace-topbar-height);
|
|
6
|
+
padding: 12px;
|
|
7
|
+
}
|
|
8
|
+
.workbench-overview .workbench-header .detail-header {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex: 1 1 520px;
|
|
11
|
+
min-width: 0;
|
|
12
|
+
align-items: center;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
border: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
}
|
|
17
|
+
.workbench-overview .workbench-header .detail-header > div:first-child { flex: 1 1 180px; }
|
|
18
|
+
.workbench-overview .workbench-header .detail-header-actions { flex-wrap: wrap; }
|
|
19
|
+
.workbench-overview .workbench-mode-bar { margin-left: auto; }
|
|
20
|
+
.instance-overview {
|
|
21
|
+
width: 100%;
|
|
22
|
+
min-height: 0;
|
|
23
|
+
overflow: auto;
|
|
24
|
+
padding: clamp(16px, 3vw, 32px);
|
|
25
|
+
container-type: inline-size;
|
|
26
|
+
}
|
|
27
|
+
.instance-overview > * {
|
|
28
|
+
max-width: 1040px;
|
|
29
|
+
margin-inline: auto;
|
|
30
|
+
}
|
|
31
|
+
.overview-instance { margin-bottom: 28px; }
|
|
32
|
+
.overview-status-line,
|
|
33
|
+
.overview-status-heading { display: flex; align-items: center; gap: 14px; }
|
|
34
|
+
.overview-status-line {
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
flex-wrap: wrap;
|
|
37
|
+
padding-bottom: 20px;
|
|
38
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
39
|
+
}
|
|
40
|
+
.overview-status {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 8px;
|
|
44
|
+
font-size: 16px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
text-transform: capitalize;
|
|
47
|
+
}
|
|
48
|
+
.overview-status > span {
|
|
49
|
+
width: 8px;
|
|
50
|
+
height: 8px;
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
background: var(--status-idle);
|
|
53
|
+
}
|
|
54
|
+
.overview-status.is-online > span { background: var(--status-success); }
|
|
55
|
+
.overview-status.is-error > span { background: var(--status-error); }
|
|
56
|
+
.overview-status.is-timeout > span { background: var(--status-warn); }
|
|
57
|
+
.overview-port { color: var(--text-secondary); font-family: var(--font-mono); font-size: 13px; }
|
|
58
|
+
.overview-checked { color: var(--text-secondary); font-size: 12px; }
|
|
59
|
+
.overview-unavailable { color: var(--text-secondary); font-size: 13px; line-height: 1.6; }
|
|
60
|
+
.overview-sections { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; }
|
|
61
|
+
.instance-overview h3 { margin: 0 0 14px; font-size: 13px; font-weight: 600; }
|
|
62
|
+
.overview-sections > section { min-width: 0; padding-block: 22px; }
|
|
63
|
+
.overview-properties { margin: 0; display: grid; gap: 13px; }
|
|
64
|
+
.overview-properties > div { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px; font-size: 13px; line-height: 1.5; }
|
|
65
|
+
.overview-properties dt { color: var(--text-secondary); }
|
|
66
|
+
.overview-properties dd { margin: 0; overflow-wrap: anywhere; }
|
|
67
|
+
.overview-mono { font-family: var(--font-mono); font-size: 12px; }
|
|
68
|
+
.overview-project { padding-block: 18px; border-top: 1px solid var(--border-subtle); }
|
|
69
|
+
.overview-project ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
|
|
70
|
+
.overview-project li { overflow-wrap: anywhere; line-height: 1.6; }
|
|
71
|
+
.overview-project p,
|
|
72
|
+
.overview-no-selection p { margin: 0; color: var(--text-secondary); font-size: 13px; line-height: 1.6; }
|
|
73
|
+
.overview-diagnostics { border-top: 1px solid var(--border-subtle); }
|
|
74
|
+
.overview-diagnostics summary { padding-block: 14px; color: var(--text-secondary); cursor: pointer; font-size: 12px; }
|
|
75
|
+
.overview-diagnostics summary:hover { color: var(--text-primary); }
|
|
76
|
+
.overview-diagnostics summary:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
|
|
77
|
+
.overview-diagnostics .overview-properties { padding-block: 4px 16px; }
|
|
78
|
+
.overview-diagnostics .overview-properties > div { grid-template-columns: 128px minmax(0, 1fr); }
|
|
79
|
+
.overview-no-selection { padding-block: 20px 32px; }
|
|
80
|
+
@container (max-width: 640px) {
|
|
81
|
+
.overview-sections { grid-template-columns: minmax(0, 1fr); gap: 0; }
|
|
82
|
+
.overview-sections > section + section { border-top: 1px solid var(--border-subtle); }
|
|
83
|
+
}
|
|
84
|
+
@container (max-width: 360px) {
|
|
85
|
+
.overview-properties > div { grid-template-columns: 80px minmax(0, 1fr); gap: 8px; }
|
|
86
|
+
.overview-diagnostics .overview-properties > div { grid-template-columns: minmax(0, 1fr); gap: 4px; }
|
|
87
|
+
}
|
|
88
|
+
@media (max-width: 480px) {
|
|
89
|
+
.workbench-overview .workbench-header .detail-header { align-items: stretch; flex-direction: column; }
|
|
90
|
+
.workbench-overview .workbench-header .detail-header > div:first-child { flex-basis: auto; }
|
|
91
|
+
.workbench-overview .workbench-mode-bar { margin-left: 0; }
|
|
92
|
+
.workbench-overview .workbench-header .detail-header-actions { justify-content: flex-start; }
|
|
93
|
+
.workbench-overview .workbench-header .detail-header button,
|
|
94
|
+
.workbench-overview .workbench-header .detail-header .open-link,
|
|
95
|
+
.workbench-overview .workbench-mode-tabs button { min-height: 44px; height: auto; }
|
|
96
|
+
.overview-diagnostics summary { min-height: 44px; }
|
|
97
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
.process-control-panel {
|
|
2
|
+
min-width: 0;
|
|
3
|
+
border: 1px solid var(--border-subtle);
|
|
4
|
+
border-radius: 8px;
|
|
5
|
+
background: var(--canvas-mid);
|
|
6
|
+
color: var(--text-primary);
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.process-control-header {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
gap: 16px;
|
|
15
|
+
padding: 16px 18px 12px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.process-control-panel .process-control-heading h3 {
|
|
19
|
+
margin: 0;
|
|
20
|
+
font-size: 14px;
|
|
21
|
+
font-weight: 600;
|
|
22
|
+
line-height: 1.4;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.process-control-heading p,
|
|
26
|
+
.process-control-count,
|
|
27
|
+
.process-control-empty,
|
|
28
|
+
.process-control-maintenance-body p {
|
|
29
|
+
margin: 5px 0 0;
|
|
30
|
+
color: var(--text-secondary);
|
|
31
|
+
font-size: 12px;
|
|
32
|
+
line-height: 1.5;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.process-control-header button,
|
|
36
|
+
.process-control-actions button {
|
|
37
|
+
min-height: 32px;
|
|
38
|
+
padding: 5px 10px;
|
|
39
|
+
font: inherit;
|
|
40
|
+
font-size: 12px;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.process-control-content { padding: 0 18px 14px; }
|
|
45
|
+
.process-control-count { margin: 0 0 10px; }
|
|
46
|
+
.process-control-count span { font-size: 11px; }
|
|
47
|
+
|
|
48
|
+
.process-control-table {
|
|
49
|
+
width: 100%;
|
|
50
|
+
border-collapse: collapse;
|
|
51
|
+
font-size: 12px;
|
|
52
|
+
text-align: left;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.process-control-table th,
|
|
56
|
+
.process-control-table td {
|
|
57
|
+
padding: 9px 10px;
|
|
58
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
59
|
+
overflow-wrap: anywhere;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.process-control-table thead th {
|
|
63
|
+
padding-top: 0;
|
|
64
|
+
color: var(--text-secondary);
|
|
65
|
+
font-size: 11px;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.process-control-table tbody th,
|
|
70
|
+
.process-control-table tbody td:nth-child(2) {
|
|
71
|
+
font-variant-numeric: tabular-nums;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.process-control-table tbody th { font-weight: 600; }
|
|
75
|
+
.process-control-table tbody td { color: var(--text-secondary); }
|
|
76
|
+
.process-control-table tr:last-child > * { border-bottom: 0; }
|
|
77
|
+
.process-control-table thead tr > * { border-bottom: 1px solid var(--border-subtle); }
|
|
78
|
+
.process-control-table tr > :first-child { padding-left: 0; }
|
|
79
|
+
.process-control-table tr > :last-child { padding-right: 0; }
|
|
80
|
+
|
|
81
|
+
.process-control-feedback:not(:empty),
|
|
82
|
+
.process-control-error {
|
|
83
|
+
margin: 0;
|
|
84
|
+
padding: 0 18px 14px;
|
|
85
|
+
color: var(--text-secondary);
|
|
86
|
+
font-size: 12px;
|
|
87
|
+
line-height: 1.5;
|
|
88
|
+
overflow-wrap: anywhere;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.process-control-error { color: var(--status-error); }
|
|
92
|
+
.process-control-maintenance { border-top: 1px solid var(--border-subtle); }
|
|
93
|
+
.process-control-maintenance summary {
|
|
94
|
+
padding: 12px 18px;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
color: var(--text-secondary);
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
}
|
|
100
|
+
.process-control-maintenance summary:hover { color: var(--text-primary); }
|
|
101
|
+
.process-control-maintenance-body { padding: 0 18px 16px; }
|
|
102
|
+
.process-control-maintenance-body p { margin-top: 0; }
|
|
103
|
+
.process-control-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 8px; }
|
|
104
|
+
.process-control-actions .process-control-stop { color: var(--status-error); }
|
|
105
|
+
.process-control-panel button:disabled { cursor: not-allowed; opacity: 0.5; }
|
|
106
|
+
.process-control-panel button:focus-visible,
|
|
107
|
+
.process-control-maintenance summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
108
|
+
|
|
109
|
+
.process-control-sr-only {
|
|
110
|
+
position: absolute;
|
|
111
|
+
width: 1px;
|
|
112
|
+
height: 1px;
|
|
113
|
+
padding: 0;
|
|
114
|
+
margin: -1px;
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
clip-path: inset(50%);
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@media (max-width: 480px) {
|
|
121
|
+
.process-control-header button, .process-control-actions button, .process-control-maintenance summary { min-height: 44px; }
|
|
122
|
+
.process-control-header { gap: 10px; padding: 14px 12px 10px; }
|
|
123
|
+
.process-control-content { padding: 0 12px 12px; }
|
|
124
|
+
.process-control-feedback:not(:empty), .process-control-error { padding-inline: 12px; }
|
|
125
|
+
.process-control-maintenance summary { padding: 12px; }
|
|
126
|
+
.process-control-maintenance-body { padding: 0 12px 14px; }
|
|
127
|
+
.process-control-table th, .process-control-table td { padding-inline: 6px; }
|
|
128
|
+
}
|
|
@@ -885,29 +885,12 @@ button.instance-group-header[aria-expanded="false"] .instance-group-chevron {
|
|
|
885
885
|
overflow: auto;
|
|
886
886
|
padding: 12px;
|
|
887
887
|
}
|
|
888
|
-
.
|
|
889
|
-
display: grid;
|
|
890
|
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
891
|
-
gap: 10px;
|
|
892
|
-
}
|
|
893
|
-
.overview-grid > div, .metric, .detail-empty {
|
|
888
|
+
.metric, .detail-empty {
|
|
894
889
|
border: 1px solid var(--border-subtle);
|
|
895
890
|
background: var(--canvas-mid);
|
|
896
891
|
border-radius: 8px;
|
|
897
892
|
padding: 11px;
|
|
898
893
|
}
|
|
899
|
-
.overview-grid span {
|
|
900
|
-
display: block;
|
|
901
|
-
color: var(--text-secondary);
|
|
902
|
-
font-size: 11px;
|
|
903
|
-
}
|
|
904
|
-
.overview-grid strong {
|
|
905
|
-
display: block;
|
|
906
|
-
margin-top: 6px;
|
|
907
|
-
overflow-wrap: anywhere;
|
|
908
|
-
font-size: 14px;
|
|
909
|
-
font-weight: 500;
|
|
910
|
-
}
|
|
911
894
|
/* ─── Logs Panel ──────────────────────────────── */
|
|
912
895
|
.logs-panel {
|
|
913
896
|
display: flex;
|
|
@@ -984,64 +967,6 @@ button.instance-group-header[aria-expanded="false"] .instance-group-chevron {
|
|
|
984
967
|
min-width: 0;
|
|
985
968
|
}
|
|
986
969
|
|
|
987
|
-
.process-control-panel {
|
|
988
|
-
margin-top: 12px;
|
|
989
|
-
border: 1px solid var(--border-subtle);
|
|
990
|
-
background: var(--canvas-mid);
|
|
991
|
-
border-radius: 8px;
|
|
992
|
-
padding: 11px;
|
|
993
|
-
}
|
|
994
|
-
.process-control-header,
|
|
995
|
-
.process-control-actions,
|
|
996
|
-
.process-control-row {
|
|
997
|
-
display: flex;
|
|
998
|
-
align-items: center;
|
|
999
|
-
gap: 8px;
|
|
1000
|
-
}
|
|
1001
|
-
.process-control-header {
|
|
1002
|
-
justify-content: space-between;
|
|
1003
|
-
}
|
|
1004
|
-
.process-control-header span,
|
|
1005
|
-
.process-control-list p,
|
|
1006
|
-
.process-control-message {
|
|
1007
|
-
color: var(--text-secondary);
|
|
1008
|
-
font-size: 11px;
|
|
1009
|
-
}
|
|
1010
|
-
.process-control-header strong {
|
|
1011
|
-
display: block;
|
|
1012
|
-
margin-top: 3px;
|
|
1013
|
-
font-size: 14px;
|
|
1014
|
-
}
|
|
1015
|
-
.process-control-actions {
|
|
1016
|
-
flex-wrap: wrap;
|
|
1017
|
-
margin-top: 10px;
|
|
1018
|
-
}
|
|
1019
|
-
.process-control-actions button,
|
|
1020
|
-
.process-control-header button {
|
|
1021
|
-
min-height: 28px;
|
|
1022
|
-
padding: 5px 9px;
|
|
1023
|
-
font-size: 12px;
|
|
1024
|
-
}
|
|
1025
|
-
.process-control-list {
|
|
1026
|
-
display: grid;
|
|
1027
|
-
gap: 6px;
|
|
1028
|
-
margin-top: 10px;
|
|
1029
|
-
}
|
|
1030
|
-
.process-control-row {
|
|
1031
|
-
min-height: 28px;
|
|
1032
|
-
border: 1px solid var(--border-subtle);
|
|
1033
|
-
border-radius: 6px;
|
|
1034
|
-
padding: 5px 7px;
|
|
1035
|
-
}
|
|
1036
|
-
.process-control-row span {
|
|
1037
|
-
font-weight: 700;
|
|
1038
|
-
}
|
|
1039
|
-
.process-control-row strong,
|
|
1040
|
-
.process-control-row em {
|
|
1041
|
-
color: var(--text-secondary);
|
|
1042
|
-
font-size: 11px;
|
|
1043
|
-
font-style: normal;
|
|
1044
|
-
}
|
|
1045
970
|
.preview-panel {
|
|
1046
971
|
position: relative;
|
|
1047
972
|
min-height: 100%;
|
|
@@ -150,7 +150,6 @@
|
|
|
150
150
|
.dashboard-shell.manager-shell:not(.is-sidebar-collapsed) .manager-workspace.is-side-panel-open {
|
|
151
151
|
--right-panel-width: 50vw;
|
|
152
152
|
}
|
|
153
|
-
.overview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
154
153
|
}
|
|
155
154
|
@media (max-width: 1023px) {
|
|
156
155
|
.dashboard-shell.manager-shell .manager-workspace,
|
|
@@ -285,7 +284,6 @@
|
|
|
285
284
|
overflow-x: auto;
|
|
286
285
|
}
|
|
287
286
|
.workbench-header .workbench-mode-tabs button { flex: 1 0 auto; }
|
|
288
|
-
.overview-grid { grid-template-columns: 1fr; }
|
|
289
287
|
.workbench-preview .preview-panel {
|
|
290
288
|
gap: 8px;
|
|
291
289
|
padding: 8px 10px;
|