dev-cockpit 0.1.0 → 0.2.2

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.
Files changed (153) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +86 -29
  3. package/bin/dev-cockpit.mjs +26 -4
  4. package/dist/actions/builtin.d.ts +25 -0
  5. package/dist/actions/builtin.d.ts.map +1 -0
  6. package/dist/actions/dispatch.d.ts +21 -0
  7. package/dist/actions/dispatch.d.ts.map +1 -0
  8. package/dist/actions/registry.d.ts +11 -0
  9. package/dist/actions/registry.d.ts.map +1 -0
  10. package/dist/actions/types.d.ts +76 -0
  11. package/dist/actions/types.d.ts.map +1 -0
  12. package/dist/buildCli.d.ts.map +1 -1
  13. package/dist/chunk-6XGHLLYT.js +46 -0
  14. package/dist/chunk-6XGHLLYT.js.map +7 -0
  15. package/dist/chunk-C4GFJDMG.js +79 -0
  16. package/dist/chunk-C4GFJDMG.js.map +7 -0
  17. package/dist/chunk-Q6677JQF.js +32609 -0
  18. package/dist/chunk-Q6677JQF.js.map +7 -0
  19. package/dist/chunk-VN6UILQW.js +1460 -0
  20. package/dist/chunk-VN6UILQW.js.map +7 -0
  21. package/dist/cockpit/Cockpit.d.ts +6 -0
  22. package/dist/cockpit/Cockpit.d.ts.map +1 -1
  23. package/dist/cockpit/Footer.d.ts +6 -4
  24. package/dist/cockpit/Footer.d.ts.map +1 -1
  25. package/dist/cockpit/TabBar.d.ts.map +1 -1
  26. package/dist/cockpit/hooks/useGlobalKeys.d.ts +15 -15
  27. package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -1
  28. package/dist/cockpit/hooks/useTerminalWidth.d.ts +12 -0
  29. package/dist/cockpit/hooks/useTerminalWidth.d.ts.map +1 -0
  30. package/dist/cockpit/panes/CommandModal.d.ts +18 -0
  31. package/dist/cockpit/panes/CommandModal.d.ts.map +1 -0
  32. package/dist/cockpit/panes/Help.d.ts.map +1 -1
  33. package/dist/cockpit/panes/Output.d.ts +7 -0
  34. package/dist/cockpit/panes/Output.d.ts.map +1 -1
  35. package/dist/cockpit/panes/Repos.d.ts.map +1 -1
  36. package/dist/cockpit/state/store.d.ts +14 -11
  37. package/dist/cockpit/state/store.d.ts.map +1 -1
  38. package/dist/cockpit/tab-state.d.ts +12 -0
  39. package/dist/cockpit/tab-state.d.ts.map +1 -1
  40. package/dist/commands/dev.d.ts.map +1 -1
  41. package/dist/commands/doctor.d.ts.map +1 -1
  42. package/dist/commands/init-config-wizard.d.ts +103 -2
  43. package/dist/commands/init-config-wizard.d.ts.map +1 -1
  44. package/dist/commands/init-config.d.ts +2 -0
  45. package/dist/commands/init-config.d.ts.map +1 -1
  46. package/dist/commands/link.d.ts +20 -0
  47. package/dist/commands/link.d.ts.map +1 -0
  48. package/dist/commands/migrate-config.d.ts +18 -0
  49. package/dist/commands/migrate-config.d.ts.map +1 -0
  50. package/dist/commands/mount.d.ts +17 -32
  51. package/dist/commands/mount.d.ts.map +1 -1
  52. package/dist/core/config-discovery.d.ts +39 -0
  53. package/dist/core/config-discovery.d.ts.map +1 -0
  54. package/dist/core/config.d.ts +73 -5
  55. package/dist/core/config.d.ts.map +1 -1
  56. package/dist/core/manifest.d.ts +47 -0
  57. package/dist/core/manifest.d.ts.map +1 -0
  58. package/dist/core/migrations.d.ts +33 -0
  59. package/dist/core/migrations.d.ts.map +1 -0
  60. package/dist/core/subprocess.d.ts +20 -0
  61. package/dist/core/subprocess.d.ts.map +1 -1
  62. package/dist/core/types.d.ts +36 -12
  63. package/dist/core/types.d.ts.map +1 -1
  64. package/dist/devtools-YXMW6JJ6.js +3720 -0
  65. package/dist/devtools-YXMW6JJ6.js.map +7 -0
  66. package/dist/docker/highlights.d.ts +14 -4
  67. package/dist/docker/highlights.d.ts.map +1 -1
  68. package/dist/docker/logs.d.ts +3 -2
  69. package/dist/docker/logs.d.ts.map +1 -1
  70. package/dist/health/builtin.d.ts.map +1 -1
  71. package/dist/index.d.ts +14 -3
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +92944 -53
  74. package/dist/index.js.map +7 -0
  75. package/dist/ink.js +38 -1
  76. package/dist/ink.js.map +7 -0
  77. package/dist/link-HXNII7EU.js +65 -0
  78. package/dist/link-HXNII7EU.js.map +7 -0
  79. package/dist/mount/compose.d.ts +21 -0
  80. package/dist/mount/compose.d.ts.map +1 -0
  81. package/dist/mount/discovery.d.ts +35 -0
  82. package/dist/mount/discovery.d.ts.map +1 -0
  83. package/dist/mount/git-status.d.ts +12 -0
  84. package/dist/mount/git-status.d.ts.map +1 -0
  85. package/dist/mount/manifest.d.ts +16 -0
  86. package/dist/mount/manifest.d.ts.map +1 -0
  87. package/dist/mount/symlinks.d.ts +30 -0
  88. package/dist/mount/symlinks.d.ts.map +1 -0
  89. package/dist/mount/types.d.ts +60 -0
  90. package/dist/mount/types.d.ts.map +1 -0
  91. package/dist/react.js +35 -1
  92. package/dist/react.js.map +7 -0
  93. package/dist/runCockpit.d.ts +3 -0
  94. package/dist/runCockpit.d.ts.map +1 -1
  95. package/docs/commands.md +29 -16
  96. package/docs/config-reference.md +115 -11
  97. package/docs/getting-started.md +9 -6
  98. package/docs/index.md +5 -1
  99. package/docs/init-config.md +34 -8
  100. package/docs/mount.md +198 -25
  101. package/docs/notifications.md +14 -13
  102. package/docs/panes.md +36 -15
  103. package/docs/processes.md +42 -0
  104. package/package.json +93 -90
  105. package/dist/buildCli.js +0 -107
  106. package/dist/cli.js +0 -2
  107. package/dist/cockpit/Cockpit.js +0 -73
  108. package/dist/cockpit/Footer.js +0 -33
  109. package/dist/cockpit/TabBar.js +0 -12
  110. package/dist/cockpit/help/content.js +0 -22
  111. package/dist/cockpit/help/loader.js +0 -118
  112. package/dist/cockpit/help/renderer.js +0 -35
  113. package/dist/cockpit/help/types.js +0 -1
  114. package/dist/cockpit/hooks/useCockpitStore.js +0 -5
  115. package/dist/cockpit/hooks/useGlobalKeys.js +0 -173
  116. package/dist/cockpit/panes/FilterModal.js +0 -22
  117. package/dist/cockpit/panes/Health.js +0 -30
  118. package/dist/cockpit/panes/Help.js +0 -81
  119. package/dist/cockpit/panes/Output.js +0 -108
  120. package/dist/cockpit/panes/Repos.js +0 -48
  121. package/dist/cockpit/panes/SearchModal.js +0 -31
  122. package/dist/cockpit/state/store.js +0 -111
  123. package/dist/cockpit/tab-state.js +0 -7
  124. package/dist/commands/dev.js +0 -158
  125. package/dist/commands/doctor.js +0 -66
  126. package/dist/commands/init-config-wizard.js +0 -818
  127. package/dist/commands/init-config.js +0 -131
  128. package/dist/commands/mount.js +0 -150
  129. package/dist/core/config.js +0 -152
  130. package/dist/core/logger.js +0 -38
  131. package/dist/core/notifier.js +0 -100
  132. package/dist/core/paths.js +0 -18
  133. package/dist/core/subprocess.js +0 -82
  134. package/dist/core/types.js +0 -1
  135. package/dist/docker/highlights.js +0 -79
  136. package/dist/docker/logs.js +0 -172
  137. package/dist/docker/restart.js +0 -45
  138. package/dist/docker/stack-trace.js +0 -44
  139. package/dist/health/builtin.js +0 -144
  140. package/dist/health/context.js +0 -31
  141. package/dist/health/notify-resolver.js +0 -28
  142. package/dist/health/registry.js +0 -64
  143. package/dist/health/remediations.js +0 -41
  144. package/dist/health/runner.js +0 -22
  145. package/dist/health/scheduler.js +0 -107
  146. package/dist/health/types.js +0 -1
  147. package/dist/health/useHealth.js +0 -122
  148. package/dist/lint/reactive.js +0 -131
  149. package/dist/runCockpit.js +0 -75
  150. package/dist/watchers/manager.js +0 -239
  151. package/dist/watchers/path-mapper.js +0 -29
  152. package/dist/watchers/types.js +0 -9
  153. package/docs/watchers.md +0 -27
package/package.json CHANGED
@@ -1,91 +1,94 @@
1
1
  {
2
- "name": "dev-cockpit",
3
- "version": "0.1.0",
4
- "description": "A reusable, domain-neutral terminal UI dev cockpit — tabbed pane shell, watcher streaming, optional Docker log tail with highlights, health framework with one-keystroke remediations, transition-only OS notifications, live-markdown Help. Profiles extend it with project-specific commands, repos, and health checks.",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/frankfava/package--dev-cockpit.git"
9
- },
10
- "homepage": "https://github.com/frankfava/package--dev-cockpit#readme",
11
- "bugs": {
12
- "url": "https://github.com/frankfava/package--dev-cockpit/issues"
13
- },
14
- "keywords": [
15
- "tui",
16
- "terminal",
17
- "dashboard",
18
- "cockpit",
19
- "watcher",
20
- "docker",
21
- "dev",
22
- "dx"
23
- ],
24
- "type": "module",
25
- "engines": {
26
- "node": ">=20.12.0"
27
- },
28
- "main": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
- "exports": {
31
- ".": {
32
- "types": "./dist/index.d.ts",
33
- "import": "./dist/index.js"
34
- },
35
- "./ink": {
36
- "types": "./dist/ink.d.ts",
37
- "import": "./dist/ink.js"
38
- },
39
- "./react": {
40
- "types": "./dist/react.d.ts",
41
- "import": "./dist/react.js"
42
- }
43
- },
44
- "bin": {
45
- "dev-cockpit": "bin/dev-cockpit.mjs"
46
- },
47
- "files": [
48
- "dist/",
49
- "bin/",
50
- "docs/",
51
- "examples/",
52
- "README.md",
53
- "CHANGELOG.md",
54
- "LICENSE"
55
- ],
56
- "scripts": {
57
- "build": "tsc -p tsconfig.build.json",
58
- "typecheck": "tsc --noEmit",
59
- "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}'",
60
- "test": "vitest run"
61
- },
62
- "dependencies": {
63
- "@inquirer/prompts": "^8.4.2",
64
- "chalk": "^5.3.0",
65
- "chokidar": "^5.0.0",
66
- "commander": "^12.1.0",
67
- "execa": "^9.3.0",
68
- "ink": "^7.0.2",
69
- "marked": "^15.0.12",
70
- "marked-terminal": "^7.3.0",
71
- "node-notifier": "^10.0.1",
72
- "pino": "^9.3.2",
73
- "react": "^19.2.6",
74
- "yaml": "^2.5.0",
75
- "zod": "^3.23.8",
76
- "zustand": "^5.0.13"
77
- },
78
- "devDependencies": {
79
- "@types/node": "^22.5.0",
80
- "@types/node-notifier": "^8.0.5",
81
- "@types/react": "^19.2.14",
82
- "@typescript-eslint/eslint-plugin": "^8.3.0",
83
- "@typescript-eslint/parser": "^8.3.0",
84
- "eslint": "^9.9.1",
85
- "eslint-config-prettier": "^9.1.0",
86
- "ink-testing-library": "^4.0.0",
87
- "prettier": "^3.3.3",
88
- "typescript": "^5.5.4",
89
- "vitest": "^2.0.5"
90
- }
91
- }
2
+ "name": "dev-cockpit",
3
+ "version": "0.2.2",
4
+ "description": "A reusable, domain-neutral terminal UI dev cockpit — tabbed pane shell, watcher streaming, optional Docker log tail with highlights, health framework with one-keystroke remediations, transition-only OS notifications, live-markdown Help. Profiles extend it with project-specific commands, repos, and health checks.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/frankfava/package--dev-cockpit.git"
9
+ },
10
+ "homepage": "https://github.com/frankfava/package--dev-cockpit#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/frankfava/package--dev-cockpit/issues"
13
+ },
14
+ "keywords": [
15
+ "tui",
16
+ "terminal",
17
+ "dashboard",
18
+ "cockpit",
19
+ "watcher",
20
+ "docker",
21
+ "dev",
22
+ "dx"
23
+ ],
24
+ "type": "module",
25
+ "engines": {
26
+ "node": ">=20.12.0"
27
+ },
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./ink": {
36
+ "types": "./dist/ink.d.ts",
37
+ "import": "./dist/ink.js"
38
+ },
39
+ "./react": {
40
+ "types": "./dist/react.d.ts",
41
+ "import": "./dist/react.js"
42
+ }
43
+ },
44
+ "bin": {
45
+ "dev-cockpit": "bin/dev-cockpit.mjs"
46
+ },
47
+ "files": [
48
+ "dist/",
49
+ "bin/",
50
+ "docs/",
51
+ "examples/",
52
+ "README.md",
53
+ "CHANGELOG.md",
54
+ "LICENSE"
55
+ ],
56
+ "scripts": {
57
+ "build": "rm -rf dist && node esbuild.config.mjs && tsc -p tsconfig.build.json --emitDeclarationOnly --declaration",
58
+ "typecheck": "tsc --noEmit",
59
+ "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}'",
60
+ "test": "vitest run"
61
+ },
62
+ "peerDependencies": {
63
+ "@inquirer/prompts": "^8.4.2",
64
+ "node-notifier": "^10.0.1"
65
+ },
66
+ "devDependencies": {
67
+ "@inquirer/prompts": "^8.4.2",
68
+ "@types/node": "^22.5.0",
69
+ "@types/node-notifier": "^8.0.5",
70
+ "@types/react": "^19.2.14",
71
+ "@typescript-eslint/eslint-plugin": "^8.3.0",
72
+ "@typescript-eslint/parser": "^8.3.0",
73
+ "chalk": "^5.3.0",
74
+ "chokidar": "^5.0.0",
75
+ "commander": "^12.1.0",
76
+ "esbuild": "^0.23.1",
77
+ "eslint": "^9.9.1",
78
+ "eslint-config-prettier": "^9.1.0",
79
+ "execa": "^9.3.0",
80
+ "ink": "^7.0.2",
81
+ "ink-testing-library": "^4.0.0",
82
+ "marked": "^15.0.12",
83
+ "marked-terminal": "^7.3.0",
84
+ "node-notifier": "^10.0.1",
85
+ "pino": "^9.3.2",
86
+ "prettier": "^3.3.3",
87
+ "react": "^19.2.6",
88
+ "typescript": "^5.5.4",
89
+ "vitest": "^2.0.5",
90
+ "yaml": "^2.5.0",
91
+ "zod": "^3.23.8",
92
+ "zustand": "^5.0.13"
93
+ }
94
+ }
package/dist/buildCli.js DELETED
@@ -1,107 +0,0 @@
1
- /**
2
- * `buildCli({ profile? })` — assemble the dev-cockpit commander program.
3
- *
4
- * Registration order: **profile first, core last.** The profile's
5
- * `setupCli(program)` runs before any core command is registered. When core
6
- * goes to register a command (`dev`, `doctor`, `init-config`, `mount`),
7
- * it skips any name the profile already claimed. Profiles override by
8
- * registering first, not by mutating commander's internal arrays — one
9
- * mechanism, no escape hatch.
10
- */
11
- import { Command } from 'commander';
12
- import { readFileSync } from 'node:fs';
13
- import { fileURLToPath } from 'node:url';
14
- import { dirname, join } from 'node:path';
15
- import { devCommand } from './commands/dev.js';
16
- import { doctorCommand } from './commands/doctor.js';
17
- import { initConfigCommand } from './commands/init-config.js';
18
- import { mountCommand, mountStatusCommand, mountClearCommand, } from './commands/mount.js';
19
- function readPackageVersion() {
20
- try {
21
- const here = dirname(fileURLToPath(import.meta.url));
22
- const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8'));
23
- return pkg.version ?? '0.0.0';
24
- }
25
- catch {
26
- return '0.0.0';
27
- }
28
- }
29
- function hasCommand(program, name) {
30
- return program.commands.some((c) => c.name() === name);
31
- }
32
- export function buildCli(opts = {}) {
33
- const program = new Command();
34
- const profile = opts.profile;
35
- const appName = profile?.appName ?? 'dev-cockpit';
36
- program
37
- .name(appName)
38
- .description(profile
39
- ? `${appName} — built on dev-cockpit`
40
- : 'Generic terminal UI dev cockpit')
41
- .version(readPackageVersion(), '-V, --version', 'Output the version number');
42
- // Profile registers first; its commands win by precedence.
43
- if (profile?.setupCli) {
44
- profile.setupCli(program);
45
- }
46
- // Core commands fill in any names the profile didn't claim.
47
- if (!hasCommand(program, 'dev')) {
48
- program
49
- .command('dev')
50
- .description('Boot the three-pane TUI (Repos / Output / Health / Help)')
51
- .option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
52
- .action(async (cmdOpts) => {
53
- await devCommand({ config: cmdOpts.config, profile });
54
- });
55
- }
56
- if (!hasCommand(program, 'doctor')) {
57
- program
58
- .command('doctor')
59
- .description('Run all health checks once and print a status table')
60
- .option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
61
- .action(async (cmdOpts) => {
62
- await doctorCommand({ config: cmdOpts.config, profile });
63
- });
64
- }
65
- if (!hasCommand(program, 'init-config')) {
66
- program
67
- .command('init-config')
68
- .description('Write a starter cockpit.yaml in the current directory')
69
- .option('-f, --force', 'Overwrite an existing cockpit.yaml without prompting', false)
70
- .option('--with-docker', 'Include an uncommented docker block (static template only)', false)
71
- .option('-i, --interactive', 'Walk through prompts to populate the file (skips static template)', false)
72
- .action(async (cmdOpts) => {
73
- await initConfigCommand({
74
- force: cmdOpts.force ?? false,
75
- withDocker: cmdOpts.withDocker ?? false,
76
- interactive: cmdOpts.interactive ?? false,
77
- appName,
78
- profile,
79
- });
80
- });
81
- }
82
- if (!hasCommand(program, 'mount')) {
83
- const mountCmd = program
84
- .command('mount')
85
- .description('Generate a Docker compose overlay for bind-mount candidates')
86
- .option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
87
- .option('-s, --service <name>', 'Target compose service (default: first in docker.services)')
88
- .action(async (cmdOpts) => {
89
- await mountCommand({ config: cmdOpts.config, service: cmdOpts.service, profile });
90
- });
91
- mountCmd
92
- .command('status')
93
- .description('Show the active overlay manifest')
94
- .option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
95
- .action(async (cmdOpts) => {
96
- await mountStatusCommand({ config: cmdOpts.config, profile });
97
- });
98
- mountCmd
99
- .command('clear')
100
- .description('Remove the overlay file and manifest')
101
- .option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
102
- .action(async (cmdOpts) => {
103
- await mountClearCommand({ config: cmdOpts.config, profile });
104
- });
105
- }
106
- return program;
107
- }
package/dist/cli.js DELETED
@@ -1,2 +0,0 @@
1
- import { buildCli } from './index.js';
2
- buildCli().parse();
@@ -1,73 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * Top-level Ink component for the cockpit TUI.
4
- *
5
- * Layout:
6
- * ┌──────────────────────────────────────────────┐
7
- * │ TabBar (tabs + global key legend) │
8
- * ├──────────────────────────────────────────────┤
9
- * │ Active pane OR active modal (full area) │
10
- * ├──────────────────────────────────────────────┤
11
- * │ Footer (per-tab contextual hints) │
12
- * └──────────────────────────────────────────────┘
13
- *
14
- * Modals render IN PLACE of the active pane (opaque, fills available height).
15
- *
16
- * Domain handlers (repo action, lint, open-error) flow in via props and are
17
- * routed through useGlobalKeys. Health remediation is owned by the `health`
18
- * option — useHealth produces the runRemediation dispatcher (one mechanism).
19
- */
20
- import { useEffect, useState } from 'react';
21
- import { Box, useApp, useStdout } from 'ink';
22
- import { Repos } from './panes/Repos.js';
23
- import { Output } from './panes/Output.js';
24
- import { Health } from './panes/Health.js';
25
- import { Help } from './panes/Help.js';
26
- import { TabBar } from './TabBar.js';
27
- import { Footer } from './Footer.js';
28
- import { FilterModal } from './panes/FilterModal.js';
29
- import { SearchModal } from './panes/SearchModal.js';
30
- import { useGlobalKeys } from './hooks/useGlobalKeys.js';
31
- import { useCockpitStore } from './hooks/useCockpitStore.js';
32
- import { useHealth } from '../health/useHealth.js';
33
- export function Cockpit(props) {
34
- const { health, footerLegends, ...rest } = props;
35
- const { exit } = useApp();
36
- const { stdout } = useStdout();
37
- const focus = useCockpitStore((s) => s.focus);
38
- const activeModal = useCockpitStore((s) => s.activeModal);
39
- const [rows, setRows] = useState(stdout.rows ?? 24);
40
- useEffect(() => {
41
- const onResize = () => setRows(stdout.rows ?? 24);
42
- stdout.on('resize', onResize);
43
- return () => {
44
- stdout.off('resize', onResize);
45
- };
46
- }, [stdout]);
47
- const healthHandle = useHealth(health);
48
- useGlobalKeys({
49
- onQuit: exit,
50
- ...rest,
51
- runRemediation: healthHandle?.runRemediation,
52
- });
53
- let mainContent;
54
- if (activeModal === 'filter') {
55
- mainContent = _jsx(FilterModal, {});
56
- }
57
- else if (activeModal === 'search') {
58
- mainContent = _jsx(SearchModal, {});
59
- }
60
- else if (focus === 'repos') {
61
- mainContent = _jsx(Repos, {});
62
- }
63
- else if (focus === 'output') {
64
- mainContent = _jsx(Output, {});
65
- }
66
- else if (focus === 'health') {
67
- mainContent = _jsx(Health, {});
68
- }
69
- else {
70
- mainContent = _jsx(Help, {});
71
- }
72
- return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(TabBar, {}), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: mainContent }), _jsx(Footer, { legends: footerLegends })] }));
73
- }
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { useCockpitStore } from './hooks/useCockpitStore.js';
4
- const DEFAULT_REPOS_LEGEND_REPO = '[↑↓] select [r] action [w] watch [l] lint';
5
- const DEFAULT_REPOS_LEGEND_DOCKER = '[↑↓] select [r] restart';
6
- const DEFAULT_OUTPUT_LEGEND = '[c] clear [/] search [f] filter';
7
- const DEFAULT_HEALTH_LEGEND = '[↑↓] select [enter] details [letter] fix';
8
- const DEFAULT_HELP_LEGEND = 'read-only — see TabBar for navigation';
9
- export function Footer({ legends } = {}) {
10
- const focus = useCockpitStore((s) => s.focus);
11
- const reposLegend = useCockpitStore((s) => {
12
- const key = s.repoOrder[s.selectedRepoIndex];
13
- const entry = key ? s.repos[key] : undefined;
14
- if (entry?.kind === 'docker') {
15
- return legends?.reposDocker ?? DEFAULT_REPOS_LEGEND_DOCKER;
16
- }
17
- return legends?.reposRepo ?? DEFAULT_REPOS_LEGEND_REPO;
18
- });
19
- let legend;
20
- if (focus === 'repos') {
21
- legend = reposLegend;
22
- }
23
- else if (focus === 'output') {
24
- legend = legends?.output ?? DEFAULT_OUTPUT_LEGEND;
25
- }
26
- else if (focus === 'health') {
27
- legend = legends?.health ?? DEFAULT_HEALTH_LEGEND;
28
- }
29
- else {
30
- legend = legends?.help ?? DEFAULT_HELP_LEGEND;
31
- }
32
- return (_jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: legend }) }));
33
- }
@@ -1,12 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { useCockpitStore } from './hooks/useCockpitStore.js';
4
- import { TAB_ORDER, TAB_LABELS } from './tab-state.js';
5
- export function TabBar() {
6
- const focus = useCockpitStore((s) => s.focus);
7
- return (_jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "row", children: [_jsx(Box, { flexDirection: "row", children: TAB_ORDER.map((tab) => {
8
- const isActive = focus === tab;
9
- const label = TAB_LABELS[tab];
10
- return (_jsx(Box, { marginRight: 2, children: _jsxs(Text, { inverse: isActive, bold: isActive, color: isActive ? 'cyan' : undefined, children: [' ', label, ' '] }) }, tab));
11
- }) }), _jsx(Box, { flexGrow: 1, justifyContent: "flex-end", children: _jsx(Text, { dimColor: true, children: "[\u2190\u2192 / tab] cycle tabs" }) })] }));
12
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Compiled-in fallback for the Help tab.
3
- *
4
- * Source of truth is `docs/` (loaded at runtime by `loader.ts`). This
5
- * fallback only renders when the loader can't locate any docs/ — typically
6
- * a published tarball that forgot to whitelist the folder, or a dev build
7
- * run from an unusual working directory.
8
- */
9
- /** A pre-rendered fallback page. The pane shows this when no docs/ can be located. */
10
- export const FALLBACK_PAGE = {
11
- slug: 'fallback',
12
- title: 'Help (fallback)',
13
- path: '',
14
- body: [
15
- '# Help is unavailable',
16
- '',
17
- 'The Help tab usually loads markdown from the `docs/` folder bundled with the package.',
18
- 'You are seeing this fallback because the loader could not find `docs/index.md`.',
19
- 'Check that `docs/` ships alongside `dist/` in the install, or that the consumer profile',
20
- 'configured `helpSources` correctly.',
21
- ].join('\n'),
22
- };
@@ -1,118 +0,0 @@
1
- /**
2
- * Help-tab content loader.
3
- *
4
- * Resolves a docs/ folder by walking up from a given module URL, looking for
5
- * a directory containing both `package.json` and `docs/index.md`. This works
6
- * across three contexts:
7
- * - tsx-from-source → src/cockpit/help/loader.ts → ../../../docs
8
- * - dist/-from-build → dist/cockpit/help/loader.js → ../../../docs
9
- * - npm i installed → node_modules/<pkg>/dist/.../loader.js → <pkg>/docs
10
- *
11
- * The loader supports merging multiple sources: core docs first, then any
12
- * profile-supplied sources (paths or inline pages). Sources later in the list
13
- * override earlier ones by slug. A source with `omit: true` removes that slug.
14
- *
15
- * `docs/index.md` defines the ordered page list for that source. Each entry
16
- * is a markdown bullet of the form:
17
- * - [Title](./slug.md) — optional summary
18
- *
19
- * The index page is itself returned as the first HelpPage (slug = 'index').
20
- */
21
- import fs from 'node:fs';
22
- import path from 'node:path';
23
- import { fileURLToPath } from 'node:url';
24
- /** Walk up from `startUrl` (or this module by default) for package.json + docs/index.md. */
25
- export function resolveDocsRoot(startUrl = import.meta.url) {
26
- let dir = path.dirname(fileURLToPath(startUrl));
27
- while (dir !== path.dirname(dir)) {
28
- const pkgJson = path.join(dir, 'package.json');
29
- const docsIndex = path.join(dir, 'docs', 'index.md');
30
- if (fs.existsSync(pkgJson) && fs.existsSync(docsIndex)) {
31
- return path.join(dir, 'docs');
32
- }
33
- dir = path.dirname(dir);
34
- }
35
- return null;
36
- }
37
- /** Parse the markdown index into an ordered list of page slugs. */
38
- export function parseIndex(indexBody) {
39
- const out = [];
40
- const linkRe = /^\s*[-*]\s+\[([^\]]+)\]\((?:\.\/)?([^)]+?)\.md\)/;
41
- for (const line of indexBody.split(/\r?\n/)) {
42
- const match = linkRe.exec(line);
43
- if (match && match[1] && match[2]) {
44
- out.push({ title: match[1], slug: match[2] });
45
- }
46
- }
47
- return out;
48
- }
49
- /** Load all pages (in index order) from a single docs/ directory. */
50
- export function loadPagesFromDir(docsRoot) {
51
- const indexPath = path.join(docsRoot, 'index.md');
52
- let indexBody;
53
- try {
54
- indexBody = fs.readFileSync(indexPath, 'utf8');
55
- }
56
- catch {
57
- return [];
58
- }
59
- const pages = [{ slug: 'index', title: 'Index', path: indexPath, body: indexBody }];
60
- for (const entry of parseIndex(indexBody)) {
61
- const pagePath = path.join(docsRoot, `${entry.slug}.md`);
62
- try {
63
- const body = fs.readFileSync(pagePath, 'utf8');
64
- pages.push({ slug: entry.slug, title: entry.title, path: pagePath, body });
65
- }
66
- catch {
67
- // Missing page file — skip silently.
68
- }
69
- }
70
- return pages;
71
- }
72
- /**
73
- * Load and merge help pages from core + any contributed sources.
74
- *
75
- * Returns `null` only if the core docs root cannot be found AND no sources
76
- * provide pages. Otherwise returns a non-empty merged list with `defaultIndex`
77
- * pointing at `defaultPage` (or 0 when `defaultPage` is absent / unknown).
78
- */
79
- export function loadHelpPages(opts = {}) {
80
- const coreUrl = opts.coreUrl ?? import.meta.url;
81
- const corePages = [];
82
- const coreRoot = resolveDocsRoot(coreUrl);
83
- if (coreRoot) {
84
- corePages.push(...loadPagesFromDir(coreRoot));
85
- }
86
- // Merge sources in order. Map keyed by slug preserves first-write insertion
87
- // order; later sources REPLACE by re-assigning the same key.
88
- const merged = new Map();
89
- for (const page of corePages) {
90
- merged.set(page.slug, page);
91
- }
92
- for (const src of opts.sources ?? []) {
93
- if (src.omit && src.id) {
94
- merged.delete(src.id);
95
- continue;
96
- }
97
- if (src.page) {
98
- merged.set(src.page.slug, src.page);
99
- continue;
100
- }
101
- if (src.path) {
102
- const pages = loadPagesFromDir(src.path);
103
- for (const p of pages) {
104
- merged.set(p.slug, p);
105
- }
106
- }
107
- }
108
- const pages = Array.from(merged.values());
109
- if (pages.length === 0)
110
- return null;
111
- let defaultIndex = 0;
112
- if (opts.defaultPage) {
113
- const idx = pages.findIndex((p) => p.slug === opts.defaultPage);
114
- if (idx >= 0)
115
- defaultIndex = idx;
116
- }
117
- return { pages, defaultIndex };
118
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Markdown → terminal ANSI renderer.
3
- *
4
- * marked + marked-terminal — the latter emits an ANSI string handed to Ink's
5
- * <Text>. Ink passes ANSI through to stdout. Tradeoff accepted: long lines
6
- * wrap at the terminal column boundary, which is fine for prose docs.
7
- *
8
- * Renderer is configured once at module load (marked-terminal mutates the
9
- * shared marked instance).
10
- */
11
- import { marked } from 'marked';
12
- import { markedTerminal } from 'marked-terminal';
13
- let configured = false;
14
- function ensureConfigured() {
15
- if (configured)
16
- return;
17
- marked.use(markedTerminal());
18
- configured = true;
19
- }
20
- /**
21
- * Render a markdown string to ANSI-colored terminal output.
22
- * Always returns a string; never throws on well-formed markdown.
23
- */
24
- export function renderMarkdown(markdown) {
25
- ensureConfigured();
26
- if (!markdown)
27
- return '';
28
- try {
29
- const out = marked.parse(markdown);
30
- return typeof out === 'string' ? out : markdown;
31
- }
32
- catch {
33
- return markdown;
34
- }
35
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,5 +0,0 @@
1
- import { useSyncExternalStore } from 'react';
2
- import { cockpitStore } from '../state/store.js';
3
- export function useCockpitStore(selector) {
4
- return useSyncExternalStore(cockpitStore.subscribe, () => selector(cockpitStore.getState()));
5
- }