dev-cockpit 0.1.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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/bin/dev-cockpit.mjs +20 -0
- package/dist/buildCli.d.ts +17 -0
- package/dist/buildCli.d.ts.map +1 -0
- package/dist/buildCli.js +107 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/cockpit/Cockpit.d.ts +33 -0
- package/dist/cockpit/Cockpit.d.ts.map +1 -0
- package/dist/cockpit/Cockpit.js +73 -0
- package/dist/cockpit/Footer.d.ts +22 -0
- package/dist/cockpit/Footer.d.ts.map +1 -0
- package/dist/cockpit/Footer.js +33 -0
- package/dist/cockpit/TabBar.d.ts +3 -0
- package/dist/cockpit/TabBar.d.ts.map +1 -0
- package/dist/cockpit/TabBar.js +12 -0
- package/dist/cockpit/help/content.d.ts +12 -0
- package/dist/cockpit/help/content.d.ts.map +1 -0
- package/dist/cockpit/help/content.js +22 -0
- package/dist/cockpit/help/loader.d.ts +65 -0
- package/dist/cockpit/help/loader.d.ts.map +1 -0
- package/dist/cockpit/help/loader.js +118 -0
- package/dist/cockpit/help/renderer.d.ts +16 -0
- package/dist/cockpit/help/renderer.d.ts.map +1 -0
- package/dist/cockpit/help/renderer.js +35 -0
- package/dist/cockpit/help/types.d.ts +12 -0
- package/dist/cockpit/help/types.d.ts.map +1 -0
- package/dist/cockpit/help/types.js +1 -0
- package/dist/cockpit/hooks/useCockpitStore.d.ts +3 -0
- package/dist/cockpit/hooks/useCockpitStore.d.ts.map +1 -0
- package/dist/cockpit/hooks/useCockpitStore.js +5 -0
- package/dist/cockpit/hooks/useGlobalKeys.d.ts +56 -0
- package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -0
- package/dist/cockpit/hooks/useGlobalKeys.js +173 -0
- package/dist/cockpit/panes/FilterModal.d.ts +3 -0
- package/dist/cockpit/panes/FilterModal.d.ts.map +1 -0
- package/dist/cockpit/panes/FilterModal.js +22 -0
- package/dist/cockpit/panes/Health.d.ts +13 -0
- package/dist/cockpit/panes/Health.d.ts.map +1 -0
- package/dist/cockpit/panes/Health.js +30 -0
- package/dist/cockpit/panes/Help.d.ts +14 -0
- package/dist/cockpit/panes/Help.d.ts.map +1 -0
- package/dist/cockpit/panes/Help.js +81 -0
- package/dist/cockpit/panes/Output.d.ts +14 -0
- package/dist/cockpit/panes/Output.d.ts.map +1 -0
- package/dist/cockpit/panes/Output.js +108 -0
- package/dist/cockpit/panes/Repos.d.ts +3 -0
- package/dist/cockpit/panes/Repos.d.ts.map +1 -0
- package/dist/cockpit/panes/Repos.js +48 -0
- package/dist/cockpit/panes/SearchModal.d.ts +3 -0
- package/dist/cockpit/panes/SearchModal.d.ts.map +1 -0
- package/dist/cockpit/panes/SearchModal.js +31 -0
- package/dist/cockpit/state/store.d.ts +93 -0
- package/dist/cockpit/state/store.d.ts.map +1 -0
- package/dist/cockpit/state/store.js +111 -0
- package/dist/cockpit/tab-state.d.ts +4 -0
- package/dist/cockpit/tab-state.d.ts.map +1 -0
- package/dist/cockpit/tab-state.js +7 -0
- package/dist/commands/dev.d.ts +20 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +158 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +66 -0
- package/dist/commands/init-config-wizard.d.ts +84 -0
- package/dist/commands/init-config-wizard.d.ts.map +1 -0
- package/dist/commands/init-config-wizard.js +818 -0
- package/dist/commands/init-config.d.ts +35 -0
- package/dist/commands/init-config.d.ts.map +1 -0
- package/dist/commands/init-config.js +131 -0
- package/dist/commands/mount.d.ts +48 -0
- package/dist/commands/mount.d.ts.map +1 -0
- package/dist/commands/mount.js +150 -0
- package/dist/core/config.d.ts +391 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +152 -0
- package/dist/core/logger.d.ts +6 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +38 -0
- package/dist/core/notifier.d.ts +23 -0
- package/dist/core/notifier.d.ts.map +1 -0
- package/dist/core/notifier.js +100 -0
- package/dist/core/paths.d.ts +15 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +18 -0
- package/dist/core/subprocess.d.ts +20 -0
- package/dist/core/subprocess.d.ts.map +1 -0
- package/dist/core/subprocess.js +82 -0
- package/dist/core/types.d.ts +125 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/docker/highlights.d.ts +48 -0
- package/dist/docker/highlights.d.ts.map +1 -0
- package/dist/docker/highlights.js +79 -0
- package/dist/docker/logs.d.ts +84 -0
- package/dist/docker/logs.d.ts.map +1 -0
- package/dist/docker/logs.js +172 -0
- package/dist/docker/restart.d.ts +26 -0
- package/dist/docker/restart.d.ts.map +1 -0
- package/dist/docker/restart.js +45 -0
- package/dist/docker/stack-trace.d.ts +25 -0
- package/dist/docker/stack-trace.d.ts.map +1 -0
- package/dist/docker/stack-trace.js +44 -0
- package/dist/health/builtin.d.ts +8 -0
- package/dist/health/builtin.d.ts.map +1 -0
- package/dist/health/builtin.js +144 -0
- package/dist/health/context.d.ts +3 -0
- package/dist/health/context.d.ts.map +1 -0
- package/dist/health/context.js +31 -0
- package/dist/health/notify-resolver.d.ts +18 -0
- package/dist/health/notify-resolver.d.ts.map +1 -0
- package/dist/health/notify-resolver.js +28 -0
- package/dist/health/registry.d.ts +20 -0
- package/dist/health/registry.d.ts.map +1 -0
- package/dist/health/registry.js +64 -0
- package/dist/health/remediations.d.ts +6 -0
- package/dist/health/remediations.d.ts.map +1 -0
- package/dist/health/remediations.js +41 -0
- package/dist/health/runner.d.ts +4 -0
- package/dist/health/runner.d.ts.map +1 -0
- package/dist/health/runner.js +22 -0
- package/dist/health/scheduler.d.ts +41 -0
- package/dist/health/scheduler.d.ts.map +1 -0
- package/dist/health/scheduler.js +107 -0
- package/dist/health/types.d.ts +73 -0
- package/dist/health/types.d.ts.map +1 -0
- package/dist/health/types.js +1 -0
- package/dist/health/useHealth.d.ts +40 -0
- package/dist/health/useHealth.d.ts.map +1 -0
- package/dist/health/useHealth.js +122 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/ink.d.ts +3 -0
- package/dist/ink.d.ts.map +1 -0
- package/dist/ink.js +1 -0
- package/dist/lint/reactive.d.ts +38 -0
- package/dist/lint/reactive.d.ts.map +1 -0
- package/dist/lint/reactive.js +131 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +1 -0
- package/dist/runCockpit.d.ts +34 -0
- package/dist/runCockpit.d.ts.map +1 -0
- package/dist/runCockpit.js +75 -0
- package/dist/watchers/manager.d.ts +63 -0
- package/dist/watchers/manager.d.ts.map +1 -0
- package/dist/watchers/manager.js +239 -0
- package/dist/watchers/path-mapper.d.ts +23 -0
- package/dist/watchers/path-mapper.d.ts.map +1 -0
- package/dist/watchers/path-mapper.js +29 -0
- package/dist/watchers/types.d.ts +22 -0
- package/dist/watchers/types.d.ts.map +1 -0
- package/dist/watchers/types.js +9 -0
- package/docs/commands.md +71 -0
- package/docs/config-reference.md +20 -0
- package/docs/getting-started.md +39 -0
- package/docs/health.md +120 -0
- package/docs/index.md +13 -0
- package/docs/init-config.md +46 -0
- package/docs/mount.md +55 -0
- package/docs/notifications.md +39 -0
- package/docs/panes.md +45 -0
- package/docs/watchers.md +27 -0
- package/examples/cockpit.yaml +116 -0
- package/package.json +91 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Help pane — reads pages merged via loader.ts (core docs + profile sources)
|
|
4
|
+
* and renders the active page through marked-terminal.
|
|
5
|
+
*
|
|
6
|
+
* Per-tab keys:
|
|
7
|
+
* ↑/↓ scroll body up/down
|
|
8
|
+
* j / k previous / next page
|
|
9
|
+
* shift+tab / tab same as j / k (alias for non-vim users; the global
|
|
10
|
+
* tab-cycle handler suspends on Help)
|
|
11
|
+
* g / G jump to top / bottom of body
|
|
12
|
+
*/
|
|
13
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
14
|
+
import { Box, Text, useInput, useStdout } from 'ink';
|
|
15
|
+
import { useCockpitStore } from '../hooks/useCockpitStore.js';
|
|
16
|
+
import { loadHelpPages } from '../help/loader.js';
|
|
17
|
+
import { renderMarkdown } from '../help/renderer.js';
|
|
18
|
+
import { FALLBACK_PAGE } from '../help/content.js';
|
|
19
|
+
export function Help() {
|
|
20
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
21
|
+
const helpConfig = useCockpitStore((s) => s.helpConfig);
|
|
22
|
+
const { stdout } = useStdout();
|
|
23
|
+
const isActive = focus === 'help';
|
|
24
|
+
const initial = useMemo(() => {
|
|
25
|
+
const result = loadHelpPages({
|
|
26
|
+
sources: helpConfig.sources,
|
|
27
|
+
defaultPage: helpConfig.defaultPage,
|
|
28
|
+
});
|
|
29
|
+
if (result)
|
|
30
|
+
return result;
|
|
31
|
+
return { pages: [FALLBACK_PAGE], defaultIndex: 0 };
|
|
32
|
+
}, [helpConfig.sources, helpConfig.defaultPage]);
|
|
33
|
+
const pages = initial.pages;
|
|
34
|
+
const [pageIndex, setPageIndex] = useState(initial.defaultIndex);
|
|
35
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
36
|
+
const lastPageIndexRef = useRef(pageIndex);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (lastPageIndexRef.current !== pageIndex) {
|
|
39
|
+
lastPageIndexRef.current = pageIndex;
|
|
40
|
+
setScrollOffset(0);
|
|
41
|
+
}
|
|
42
|
+
}, [pageIndex]);
|
|
43
|
+
const activePage = pages[pageIndex] ?? pages[0];
|
|
44
|
+
const renderedLines = useMemo(() => renderMarkdown(activePage.body).split('\n'), [activePage.body]);
|
|
45
|
+
const bodyHeight = Math.max(5, (stdout.rows ?? 24) - 8);
|
|
46
|
+
const maxOffset = Math.max(0, renderedLines.length - bodyHeight);
|
|
47
|
+
useInput((input, key) => {
|
|
48
|
+
if (key.upArrow) {
|
|
49
|
+
setScrollOffset((o) => Math.max(0, o - 1));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (key.downArrow) {
|
|
53
|
+
setScrollOffset((o) => Math.min(maxOffset, o + 1));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (input === 'j' || (key.tab && key.shift)) {
|
|
57
|
+
setPageIndex((i) => Math.max(0, i - 1));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (input === 'k' || (key.tab && !key.shift)) {
|
|
61
|
+
setPageIndex((i) => Math.min(pages.length - 1, i + 1));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (input === 'g') {
|
|
65
|
+
setScrollOffset(0);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (input === 'G') {
|
|
69
|
+
setScrollOffset(maxOffset);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}, { isActive });
|
|
73
|
+
const visibleLines = renderedLines.slice(scrollOffset, scrollOffset + bodyHeight);
|
|
74
|
+
const scrollHint = renderedLines.length > bodyHeight
|
|
75
|
+
? `${scrollOffset + 1}-${Math.min(scrollOffset + bodyHeight, renderedLines.length)} / ${renderedLines.length}`
|
|
76
|
+
: `${renderedLines.length} lines`;
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingTop: 1, children: [_jsxs(Box, { flexDirection: "row", paddingX: 1, marginBottom: 1, children: [pages.map((p, idx) => {
|
|
78
|
+
const isCurrent = idx === pageIndex;
|
|
79
|
+
return (_jsx(Box, { marginRight: 2, children: _jsxs(Text, { inverse: isCurrent, bold: isCurrent, color: isCurrent ? 'cyan' : undefined, children: [' ', p.title, ' '] }) }, p.slug));
|
|
80
|
+
}), _jsx(Box, { flexGrow: 1, justifyContent: "flex-end", children: _jsxs(Text, { dimColor: true, children: [scrollHint, " \u00B7 [j/k or tab] page [\u2191\u2193] scroll [g/G] top/bot [\u2190\u2192] tabs"] }) })] }), _jsx(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: _jsx(Text, { children: visibleLines.join('\n') }) })] }));
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output pane — streams source-tagged log lines.
|
|
3
|
+
*
|
|
4
|
+
* - Virtualized scrollback (hand-rolled slice against visible terminal height).
|
|
5
|
+
* - Per-source color prefixes (deterministic source → color hash).
|
|
6
|
+
* - outputFilter applied (sources, severity, search text).
|
|
7
|
+
* - Auto-scroll to bottom unless the user has scrolled up.
|
|
8
|
+
*
|
|
9
|
+
* Domain handlers (e.g. "open error in editor") are NOT imported here. They
|
|
10
|
+
* live in the global keybindings layer; this pane is purely presentational.
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
export declare function Output(): React.ReactElement;
|
|
14
|
+
//# sourceMappingURL=Output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Output.d.ts","sourceRoot":"","sources":["../../../src/cockpit/panes/Output.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AA4ExC,wBAAgB,MAAM,IAAI,KAAK,CAAC,YAAY,CA0E3C"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Output pane — streams source-tagged log lines.
|
|
4
|
+
*
|
|
5
|
+
* - Virtualized scrollback (hand-rolled slice against visible terminal height).
|
|
6
|
+
* - Per-source color prefixes (deterministic source → color hash).
|
|
7
|
+
* - outputFilter applied (sources, severity, search text).
|
|
8
|
+
* - Auto-scroll to bottom unless the user has scrolled up.
|
|
9
|
+
*
|
|
10
|
+
* Domain handlers (e.g. "open error in editor") are NOT imported here. They
|
|
11
|
+
* live in the global keybindings layer; this pane is purely presentational.
|
|
12
|
+
*/
|
|
13
|
+
import { useState } from 'react';
|
|
14
|
+
import { Box, Text, useStdout, useInput } from 'ink';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { useCockpitStore } from '../hooks/useCockpitStore.js';
|
|
17
|
+
const SOURCE_COLORS = ['cyan', 'yellow', 'magenta', 'blue', 'green', 'red', 'white'];
|
|
18
|
+
const colorCache = new Map();
|
|
19
|
+
function sourceColor(source) {
|
|
20
|
+
const cached = colorCache.get(source);
|
|
21
|
+
if (cached)
|
|
22
|
+
return cached;
|
|
23
|
+
let hash = 0;
|
|
24
|
+
for (let i = 0; i < source.length; i++) {
|
|
25
|
+
hash = (hash * 31 + source.charCodeAt(i)) | 0;
|
|
26
|
+
}
|
|
27
|
+
const color = SOURCE_COLORS[Math.abs(hash) % SOURCE_COLORS.length] ?? 'white';
|
|
28
|
+
colorCache.set(source, color);
|
|
29
|
+
return color;
|
|
30
|
+
}
|
|
31
|
+
function applyFilter(lines, filter) {
|
|
32
|
+
let result = lines;
|
|
33
|
+
if (filter.sources && filter.sources.length > 0) {
|
|
34
|
+
const srcs = new Set(filter.sources);
|
|
35
|
+
result = result.filter((l) => srcs.has(l.source));
|
|
36
|
+
}
|
|
37
|
+
if (filter.severity) {
|
|
38
|
+
result = result.filter((l) => l.severity === filter.severity);
|
|
39
|
+
}
|
|
40
|
+
if (filter.search && filter.search.length > 0) {
|
|
41
|
+
const needle = filter.search.toLowerCase();
|
|
42
|
+
result = result.filter((l) => l.text.toLowerCase().includes(needle));
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function severityColor(severity) {
|
|
47
|
+
switch (severity) {
|
|
48
|
+
case 'error':
|
|
49
|
+
return 'red';
|
|
50
|
+
case 'warn':
|
|
51
|
+
return 'yellow';
|
|
52
|
+
default:
|
|
53
|
+
return 'white';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function OutputLineRow({ line }) {
|
|
57
|
+
const color = sourceColor(line.source);
|
|
58
|
+
const prefix = chalk[color](`[${line.source}]`);
|
|
59
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { children: [prefix, " "] }), _jsx(Text, { color: severityColor(line.severity), children: line.text })] }));
|
|
60
|
+
}
|
|
61
|
+
const CHROME_LINES = 3;
|
|
62
|
+
const MIN_VISIBLE = 3;
|
|
63
|
+
export function Output() {
|
|
64
|
+
const { stdout } = useStdout();
|
|
65
|
+
const terminalHeight = stdout?.rows ?? 24;
|
|
66
|
+
const visibleLines = Math.max(MIN_VISIBLE, terminalHeight - CHROME_LINES * 3);
|
|
67
|
+
const output = useCockpitStore((s) => s.output);
|
|
68
|
+
const outputFilter = useCockpitStore((s) => s.outputFilter);
|
|
69
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
70
|
+
const recentErrors = useCockpitStore((s) => s.recentErrors);
|
|
71
|
+
const isFocused = focus === 'output';
|
|
72
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
73
|
+
const filtered = applyFilter(output, outputFilter);
|
|
74
|
+
const total = filtered.length;
|
|
75
|
+
const effectiveOffset = Math.min(scrollOffset, Math.max(0, total - visibleLines));
|
|
76
|
+
const startIdx = Math.max(0, total - visibleLines - effectiveOffset);
|
|
77
|
+
const visible = filtered.slice(startIdx, startIdx + visibleLines);
|
|
78
|
+
const atBottom = effectiveOffset === 0 || total <= visibleLines;
|
|
79
|
+
useInput((_input, key) => {
|
|
80
|
+
if (!isFocused)
|
|
81
|
+
return;
|
|
82
|
+
if (key.upArrow) {
|
|
83
|
+
setScrollOffset((prev) => Math.min(prev + 1, Math.max(0, total - visibleLines)));
|
|
84
|
+
}
|
|
85
|
+
else if (key.downArrow) {
|
|
86
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
87
|
+
}
|
|
88
|
+
else if (key.pageUp) {
|
|
89
|
+
setScrollOffset((prev) => Math.min(prev + visibleLines, Math.max(0, total - visibleLines)));
|
|
90
|
+
}
|
|
91
|
+
else if (key.pageDown) {
|
|
92
|
+
setScrollOffset((prev) => Math.max(0, prev - visibleLines));
|
|
93
|
+
}
|
|
94
|
+
else if (key.return) {
|
|
95
|
+
setScrollOffset(0);
|
|
96
|
+
}
|
|
97
|
+
}, { isActive: isFocused });
|
|
98
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: isFocused, children: " Output " }), outputFilter.sources && outputFilter.sources.length > 0 && (_jsxs(Text, { dimColor: true, children: ["[src: ", outputFilter.sources.join(','), "] "] })), outputFilter.severity && _jsxs(Text, { dimColor: true, children: ["[", outputFilter.severity, "] "] }), outputFilter.search && _jsxs(Text, { dimColor: true, children: ["[/", outputFilter.search, "/] "] }), !atBottom && _jsxs(Text, { dimColor: true, children: [" \u2191 scrolled (", effectiveOffset, " lines up)"] })] }), recentErrors.length > 0 && _jsx(RecentErrorsStrip, { errors: recentErrors.slice(0, 3) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: " no output yet" })) : (visible.map((line, idx) => _jsx(OutputLineRow, { line: line }, `${line.ts}-${idx}`))) }), isFocused && (_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [' ↑↓ scroll · PgUp/PgDn · Enter=bottom · c=clear · /=search · f=filter', recentErrors.length > 0 ? ' · e=open error' : ''] }) }))] }));
|
|
99
|
+
}
|
|
100
|
+
function RecentErrorsStrip({ errors }) {
|
|
101
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, children: [_jsx(Text, { bold: true, color: "red", children: "Recent errors" }), errors.map((err, idx) => (_jsx(RecentErrorRow, { err: err }, `${err.ts}-${idx}`)))] }));
|
|
102
|
+
}
|
|
103
|
+
function RecentErrorRow({ err }) {
|
|
104
|
+
const sevColor = err.severity === 'error' ? 'red' : 'yellow';
|
|
105
|
+
const glyph = err.severity === 'error' ? '✗' : '⚠';
|
|
106
|
+
const trailer = err.file && typeof err.line === 'number' ? ` ↳ ${err.file}:${err.line}` : '';
|
|
107
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: sevColor, children: [glyph, " "] }), _jsxs(Text, { dimColor: true, children: ["[", err.service, "] "] }), _jsx(Text, { children: err.text }), trailer && _jsx(Text, { dimColor: true, children: trailer })] }));
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Repos.d.ts","sourceRoot":"","sources":["../../../src/cockpit/panes/Repos.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AA0D1B,wBAAgB,KAAK,IAAI,KAAK,CAAC,YAAY,CA0C1C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useCockpitStore } from '../hooks/useCockpitStore.js';
|
|
4
|
+
function statusGlyph(status) {
|
|
5
|
+
switch (status) {
|
|
6
|
+
case 'running':
|
|
7
|
+
return { glyph: '●', color: 'green' };
|
|
8
|
+
case 'failing':
|
|
9
|
+
return { glyph: '✗', color: 'red' };
|
|
10
|
+
case 'idle':
|
|
11
|
+
default:
|
|
12
|
+
return { glyph: '○', color: 'gray' };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function lintGlyph(lint) {
|
|
16
|
+
switch (lint) {
|
|
17
|
+
case 'pass':
|
|
18
|
+
return { glyph: '✓', color: 'green' };
|
|
19
|
+
case 'fail':
|
|
20
|
+
return { glyph: '!', color: 'red' };
|
|
21
|
+
case 'unknown':
|
|
22
|
+
default:
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function RepoRow({ entry, lint, isSelected, isRunningAction }) {
|
|
27
|
+
const { glyph, color } = isRunningAction
|
|
28
|
+
? { glyph: '…', color: 'yellow' }
|
|
29
|
+
: statusGlyph(entry.status);
|
|
30
|
+
const lg = lintGlyph(lint);
|
|
31
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: isSelected ? '>' : ' ' }), _jsxs(Text, { color: color, children: [glyph, " "] }), _jsx(Text, { bold: isSelected, children: entry.name }), entry.kind === 'docker' && _jsx(Text, { dimColor: true, children: " [docker]" }), lg && (_jsxs(Text, { dimColor: true, color: lg.color, children: [' ', lg.glyph] })), isRunningAction && _jsx(Text, { color: "yellow", children: ' (running…)' })] }));
|
|
32
|
+
}
|
|
33
|
+
export function Repos() {
|
|
34
|
+
const repos = useCockpitStore((s) => s.repos);
|
|
35
|
+
const repoLintStatus = useCockpitStore((s) => s.repoLintStatus);
|
|
36
|
+
const repoOrder = useCockpitStore((s) => s.repoOrder);
|
|
37
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
38
|
+
const selectedRepoIndex = useCockpitStore((s) => s.selectedRepoIndex);
|
|
39
|
+
const activeRepoAction = useCockpitStore((s) => s.activeRepoAction);
|
|
40
|
+
const isFocused = focus === 'repos';
|
|
41
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "single", borderColor: isFocused ? 'cyan' : undefined, children: [_jsx(Text, { bold: isFocused, children: " Repos " }), activeRepoAction && (_jsx(Box, { marginBottom: 1, paddingX: 1, children: _jsxs(Text, { color: "yellow", children: ['… ', activeRepoAction.label, ' — switch to Output (→) to watch'] }) })), repoOrder.map((key, idx) => {
|
|
42
|
+
const entry = repos[key];
|
|
43
|
+
if (!entry)
|
|
44
|
+
return null;
|
|
45
|
+
const lint = repoLintStatus[key] ?? 'unknown';
|
|
46
|
+
return (_jsx(RepoRow, { entry: entry, lint: lint, isSelected: isFocused && idx === selectedRepoIndex, isRunningAction: activeRepoAction?.repoKey === key }, key));
|
|
47
|
+
})] }));
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchModal.d.ts","sourceRoot":"","sources":["../../../src/cockpit/panes/SearchModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAKxC,wBAAgB,WAAW,IAAI,KAAK,CAAC,YAAY,CAoDhD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { cockpitStore } from '../state/store.js';
|
|
5
|
+
import { useCockpitStore } from '../hooks/useCockpitStore.js';
|
|
6
|
+
export function SearchModal() {
|
|
7
|
+
const outputFilter = useCockpitStore((s) => s.outputFilter);
|
|
8
|
+
const [query, setQuery] = useState(outputFilter.search ?? '');
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (key.escape) {
|
|
11
|
+
cockpitStore.getState().setActiveModal(null);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (key.return) {
|
|
15
|
+
cockpitStore.getState().setFilter({
|
|
16
|
+
...outputFilter,
|
|
17
|
+
search: query.length > 0 ? query : undefined,
|
|
18
|
+
});
|
|
19
|
+
cockpitStore.getState().setActiveModal(null);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (key.backspace || key.delete) {
|
|
23
|
+
setQuery((prev) => prev.slice(0, -1));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (input && !key.ctrl && !key.meta) {
|
|
27
|
+
setQuery((prev) => prev + input);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, children: " Search Output " }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { children: "Query: " }), _jsxs(Text, { color: "cyan", children: [query, _jsx(Text, { color: "gray", children: "\u2588" })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Enter = apply \u00B7 Esc = cancel \u00B7 Backspace = delete" })] }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store — single source of truth for cockpit state.
|
|
3
|
+
*
|
|
4
|
+
* Slices: repos, repoLintStatus, repoOrder, focus, selectedRepoIndex, output,
|
|
5
|
+
* outputFilter, activeModal, health, previousHealth, selectedHealthIndex,
|
|
6
|
+
* notificationsEnabledSession, expandedHealthId, recentErrors, activeRemediation,
|
|
7
|
+
* activeRepoAction, helpConfig.
|
|
8
|
+
*
|
|
9
|
+
* No per-pane local stores. All cockpit state lives here. Domain-specific
|
|
10
|
+
* extensions (e.g. profile-specific repo metadata) flow in through the
|
|
11
|
+
* pluggable repo `kind` and the helpConfig sources mechanism.
|
|
12
|
+
*/
|
|
13
|
+
import type { HealthStatus, RepoState, RepoStatus, LintStatus } from '../../core/types.js';
|
|
14
|
+
import type { HelpSourceInput } from '../help/loader.js';
|
|
15
|
+
export type { RepoStatus, LintStatus, RepoKind, RepoState } from '../../core/types.js';
|
|
16
|
+
export type FocusTarget = 'repos' | 'output' | 'health' | 'help';
|
|
17
|
+
export interface RecentError {
|
|
18
|
+
ts: number;
|
|
19
|
+
service: string;
|
|
20
|
+
severity: 'warn' | 'error';
|
|
21
|
+
text: string;
|
|
22
|
+
file?: string;
|
|
23
|
+
line?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface OutputLine {
|
|
26
|
+
ts: number;
|
|
27
|
+
source: string;
|
|
28
|
+
severity: 'info' | 'warn' | 'error';
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
export interface OutputFilter {
|
|
32
|
+
sources?: string[];
|
|
33
|
+
severity?: 'info' | 'warn' | 'error';
|
|
34
|
+
search?: string;
|
|
35
|
+
}
|
|
36
|
+
export type OnTransitionCallback = (prev: HealthStatus[], next: HealthStatus[]) => void;
|
|
37
|
+
/** Help configuration consumed by the Help pane. Set during boot via runCockpit / profile. */
|
|
38
|
+
export interface HelpConfig {
|
|
39
|
+
sources?: HelpSourceInput[];
|
|
40
|
+
defaultPage?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface CockpitState {
|
|
43
|
+
repos: Record<string, RepoState>;
|
|
44
|
+
repoLintStatus: Record<string, LintStatus>;
|
|
45
|
+
repoOrder: string[];
|
|
46
|
+
focus: FocusTarget;
|
|
47
|
+
selectedRepoIndex: number;
|
|
48
|
+
output: OutputLine[];
|
|
49
|
+
outputFilter: OutputFilter;
|
|
50
|
+
activeModal: 'filter' | 'search' | null;
|
|
51
|
+
health: HealthStatus[];
|
|
52
|
+
previousHealth: HealthStatus[];
|
|
53
|
+
selectedHealthIndex: number;
|
|
54
|
+
notificationsEnabledSession: boolean;
|
|
55
|
+
expandedHealthId: string | null;
|
|
56
|
+
recentErrors: RecentError[];
|
|
57
|
+
selectedRecentErrorIndex: number;
|
|
58
|
+
activeRemediation: {
|
|
59
|
+
healthId: string;
|
|
60
|
+
healthLabel: string;
|
|
61
|
+
} | null;
|
|
62
|
+
activeRepoAction: {
|
|
63
|
+
repoKey: string;
|
|
64
|
+
label: string;
|
|
65
|
+
} | null;
|
|
66
|
+
helpConfig: HelpConfig;
|
|
67
|
+
setRepoStatus: (key: string, status: RepoStatus) => void;
|
|
68
|
+
setLintStatus: (key: string, lintStatus: LintStatus) => void;
|
|
69
|
+
cycleFocus: (direction: 'forward' | 'backward') => void;
|
|
70
|
+
initRepos: (repos: Record<string, RepoState>, repoOrder: string[]) => void;
|
|
71
|
+
moveSelection: (direction: 'up' | 'down') => void;
|
|
72
|
+
appendOutput: (line: OutputLine) => void;
|
|
73
|
+
clearOutput: () => void;
|
|
74
|
+
setFilter: (filter: OutputFilter) => void;
|
|
75
|
+
setActiveModal: (modal: 'filter' | 'search' | null) => void;
|
|
76
|
+
setHealth: (next: HealthStatus[], onTransition?: OnTransitionCallback) => void;
|
|
77
|
+
moveHealthSelection: (direction: 'up' | 'down') => void;
|
|
78
|
+
toggleSessionNotifications: () => void;
|
|
79
|
+
toggleHealthExpand: (id: string) => void;
|
|
80
|
+
pushRecentError: (err: RecentError) => void;
|
|
81
|
+
moveRecentErrorSelection: (direction: 'up' | 'down') => void;
|
|
82
|
+
setActiveRemediation: (remediation: {
|
|
83
|
+
healthId: string;
|
|
84
|
+
healthLabel: string;
|
|
85
|
+
} | null) => void;
|
|
86
|
+
setActiveRepoAction: (action: {
|
|
87
|
+
repoKey: string;
|
|
88
|
+
label: string;
|
|
89
|
+
} | null) => void;
|
|
90
|
+
setHelpConfig: (config: HelpConfig) => void;
|
|
91
|
+
}
|
|
92
|
+
export declare const cockpitStore: import("zustand/vanilla").StoreApi<CockpitState>;
|
|
93
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/cockpit/state/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store — single source of truth for cockpit state.
|
|
3
|
+
*
|
|
4
|
+
* Slices: repos, repoLintStatus, repoOrder, focus, selectedRepoIndex, output,
|
|
5
|
+
* outputFilter, activeModal, health, previousHealth, selectedHealthIndex,
|
|
6
|
+
* notificationsEnabledSession, expandedHealthId, recentErrors, activeRemediation,
|
|
7
|
+
* activeRepoAction, helpConfig.
|
|
8
|
+
*
|
|
9
|
+
* No per-pane local stores. All cockpit state lives here. Domain-specific
|
|
10
|
+
* extensions (e.g. profile-specific repo metadata) flow in through the
|
|
11
|
+
* pluggable repo `kind` and the helpConfig sources mechanism.
|
|
12
|
+
*/
|
|
13
|
+
import { createStore } from 'zustand/vanilla';
|
|
14
|
+
const MAX_RECENT_ERRORS = 20;
|
|
15
|
+
const MAX_OUTPUT_LINES = 5000;
|
|
16
|
+
const FOCUS_ORDER = ['repos', 'output', 'health', 'help'];
|
|
17
|
+
export const cockpitStore = createStore()((set, get) => ({
|
|
18
|
+
repos: {},
|
|
19
|
+
repoLintStatus: {},
|
|
20
|
+
repoOrder: [],
|
|
21
|
+
focus: 'repos',
|
|
22
|
+
selectedRepoIndex: 0,
|
|
23
|
+
output: [],
|
|
24
|
+
outputFilter: {},
|
|
25
|
+
activeModal: null,
|
|
26
|
+
health: [],
|
|
27
|
+
previousHealth: [],
|
|
28
|
+
selectedHealthIndex: 0,
|
|
29
|
+
notificationsEnabledSession: true,
|
|
30
|
+
expandedHealthId: null,
|
|
31
|
+
recentErrors: [],
|
|
32
|
+
selectedRecentErrorIndex: 0,
|
|
33
|
+
activeRemediation: null,
|
|
34
|
+
activeRepoAction: null,
|
|
35
|
+
helpConfig: {},
|
|
36
|
+
setRepoStatus: (key, status) => set((state) => {
|
|
37
|
+
const existing = state.repos[key];
|
|
38
|
+
if (!existing)
|
|
39
|
+
return state;
|
|
40
|
+
return { repos: { ...state.repos, [key]: { ...existing, status } } };
|
|
41
|
+
}),
|
|
42
|
+
setLintStatus: (key, lintStatus) => set((state) => ({
|
|
43
|
+
repoLintStatus: { ...state.repoLintStatus, [key]: lintStatus },
|
|
44
|
+
})),
|
|
45
|
+
cycleFocus: (direction) => set((state) => {
|
|
46
|
+
const idx = FOCUS_ORDER.indexOf(state.focus);
|
|
47
|
+
const len = FOCUS_ORDER.length;
|
|
48
|
+
const nextIdx = direction === 'forward' ? (idx + 1) % len : (idx - 1 + len) % len;
|
|
49
|
+
const next = FOCUS_ORDER[nextIdx];
|
|
50
|
+
if (!next)
|
|
51
|
+
return state;
|
|
52
|
+
return { focus: next };
|
|
53
|
+
}),
|
|
54
|
+
initRepos: (repos, repoOrder) => {
|
|
55
|
+
const repoLintStatus = {};
|
|
56
|
+
for (const key of repoOrder) {
|
|
57
|
+
repoLintStatus[key] = 'unknown';
|
|
58
|
+
}
|
|
59
|
+
return set({ repos, repoOrder, repoLintStatus, selectedRepoIndex: 0 });
|
|
60
|
+
},
|
|
61
|
+
moveSelection: (direction) => set((state) => {
|
|
62
|
+
const len = state.repoOrder.length;
|
|
63
|
+
if (len === 0)
|
|
64
|
+
return state;
|
|
65
|
+
const current = state.selectedRepoIndex;
|
|
66
|
+
const next = direction === 'up' ? Math.max(0, current - 1) : Math.min(len - 1, current + 1);
|
|
67
|
+
return { selectedRepoIndex: next };
|
|
68
|
+
}),
|
|
69
|
+
appendOutput: (line) => set((state) => {
|
|
70
|
+
const next = [...state.output, line];
|
|
71
|
+
const capped = next.length > MAX_OUTPUT_LINES ? next.slice(next.length - MAX_OUTPUT_LINES) : next;
|
|
72
|
+
return { output: capped };
|
|
73
|
+
}),
|
|
74
|
+
clearOutput: () => set({ output: [] }),
|
|
75
|
+
setFilter: (filter) => set({ outputFilter: filter }),
|
|
76
|
+
setActiveModal: (modal) => set({ activeModal: modal }),
|
|
77
|
+
setHealth: (next, onTransition) => {
|
|
78
|
+
const prev = get().health;
|
|
79
|
+
if (onTransition)
|
|
80
|
+
onTransition(prev, next);
|
|
81
|
+
set({ health: next, previousHealth: prev });
|
|
82
|
+
},
|
|
83
|
+
moveHealthSelection: (direction) => set((state) => {
|
|
84
|
+
const len = state.health.length;
|
|
85
|
+
if (len === 0)
|
|
86
|
+
return state;
|
|
87
|
+
const current = state.selectedHealthIndex;
|
|
88
|
+
const next = direction === 'up' ? Math.max(0, current - 1) : Math.min(len - 1, current + 1);
|
|
89
|
+
return { selectedHealthIndex: next };
|
|
90
|
+
}),
|
|
91
|
+
toggleSessionNotifications: () => set((state) => ({ notificationsEnabledSession: !state.notificationsEnabledSession })),
|
|
92
|
+
toggleHealthExpand: (id) => set((state) => ({
|
|
93
|
+
expandedHealthId: state.expandedHealthId === id ? null : id,
|
|
94
|
+
})),
|
|
95
|
+
pushRecentError: (err) => set((state) => {
|
|
96
|
+
const next = [err, ...state.recentErrors].slice(0, MAX_RECENT_ERRORS);
|
|
97
|
+
const selectedRecentErrorIndex = Math.min(state.selectedRecentErrorIndex, Math.max(0, next.length - 1));
|
|
98
|
+
return { recentErrors: next, selectedRecentErrorIndex };
|
|
99
|
+
}),
|
|
100
|
+
moveRecentErrorSelection: (direction) => set((state) => {
|
|
101
|
+
const len = state.recentErrors.length;
|
|
102
|
+
if (len === 0)
|
|
103
|
+
return state;
|
|
104
|
+
const current = state.selectedRecentErrorIndex;
|
|
105
|
+
const next = direction === 'up' ? Math.max(0, current - 1) : Math.min(len - 1, current + 1);
|
|
106
|
+
return { selectedRecentErrorIndex: next };
|
|
107
|
+
}),
|
|
108
|
+
setActiveRemediation: (remediation) => set({ activeRemediation: remediation }),
|
|
109
|
+
setActiveRepoAction: (action) => set({ activeRepoAction: action }),
|
|
110
|
+
setHelpConfig: (config) => set({ helpConfig: config }),
|
|
111
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab-state.d.ts","sourceRoot":"","sources":["../../src/cockpit/tab-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,EAAE,SAAS,WAAW,EAAmD,CAAC;AAEhG,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAKlD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dev-cockpit dev` — generic boot of the three-pane TUI.
|
|
3
|
+
*
|
|
4
|
+
* Loads `cockpit.yaml`, runs the active profile's boot lifecycle hook to
|
|
5
|
+
* collect cockpit handlers + fs-event source + cleanup, wires up docker
|
|
6
|
+
* log tailers and the health framework, then renders the cockpit shell.
|
|
7
|
+
*
|
|
8
|
+
* Workspace root resolution order:
|
|
9
|
+
* 1. `profile.discoverer()` if present
|
|
10
|
+
* 2. parent directory of the resolved config file
|
|
11
|
+
*/
|
|
12
|
+
import type { Profile } from '../core/types.js';
|
|
13
|
+
export interface DevCommandOptions {
|
|
14
|
+
/** Path to the cockpit config file. Default: ./cockpit.yaml */
|
|
15
|
+
config?: string;
|
|
16
|
+
/** Active profile bundle (passed by buildCli). */
|
|
17
|
+
profile?: Profile;
|
|
18
|
+
}
|
|
19
|
+
export declare function devCommand(opts?: DevCommandOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EACV,OAAO,EAGR,MAAM,kBAAkB,CAAC;AAU1B,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqJ5E"}
|