tmux-ide 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/bin/cli.js +1024 -519
- package/bin/cli.ts +63 -6
- package/bunfig.toml +4 -0
- package/package.json +18 -7
- package/packages/contracts/package.json +22 -0
- package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
- package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
- package/packages/contracts/src/actions-contract.ts +310 -0
- package/packages/contracts/src/actions-errors.ts +41 -0
- package/packages/contracts/src/domain.ts +36 -0
- package/packages/contracts/src/ide-config.ts +170 -0
- package/packages/contracts/src/index.ts +24 -0
- package/packages/contracts/src/lib-internal/auth.ts +13 -0
- package/packages/contracts/src/lib-internal/hq.ts +38 -0
- package/packages/contracts/src/terminals.ts +116 -0
- package/packages/contracts/src/tmux.ts +60 -0
- package/packages/contracts/src/workspace.ts +67 -0
- package/packages/daemon/dist/agent-explain.d.ts +8 -1
- package/packages/daemon/dist/agent-explain.js +19 -3
- package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
- package/packages/daemon/dist/lib/tui-binary.js +130 -0
- package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
- package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
- package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
- package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
- package/packages/daemon/src/agent-explain.ts +298 -0
- package/packages/daemon/src/attach.ts +20 -0
- package/packages/daemon/src/bin.ts +4 -0
- package/packages/daemon/src/canonical.ts +7 -0
- package/packages/daemon/src/cli.ts +499 -0
- package/packages/daemon/src/command-center/actions/contract.ts +2 -0
- package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
- package/packages/daemon/src/command-center/actions/errors.ts +105 -0
- package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
- package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
- package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
- package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
- package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
- package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
- package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
- package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
- package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
- package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
- package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
- package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
- package/packages/daemon/src/command-center/actions/registry.ts +149 -0
- package/packages/daemon/src/command-center/discovery.ts +96 -0
- package/packages/daemon/src/command-center/index.ts +31 -0
- package/packages/daemon/src/command-center/schemas.ts +85 -0
- package/packages/daemon/src/command-center/server.ts +1260 -0
- package/packages/daemon/src/command-center/ws-events.ts +316 -0
- package/packages/daemon/src/config.ts +549 -0
- package/packages/daemon/src/detect.ts +248 -0
- package/packages/daemon/src/doctor.ts +242 -0
- package/packages/daemon/src/embed.ts +5 -0
- package/packages/daemon/src/index.ts +12 -0
- package/packages/daemon/src/init.ts +211 -0
- package/packages/daemon/src/inspect.ts +178 -0
- package/packages/daemon/src/js-yaml.d.ts +10 -0
- package/packages/daemon/src/launch.ts +349 -0
- package/packages/daemon/src/lib/active-projects.ts +49 -0
- package/packages/daemon/src/lib/agent-discovery.ts +121 -0
- package/packages/daemon/src/lib/app-config.ts +427 -0
- package/packages/daemon/src/lib/app-settings.ts +53 -0
- package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
- package/packages/daemon/src/lib/auth/middleware.ts +56 -0
- package/packages/daemon/src/lib/auth/types.ts +2 -0
- package/packages/daemon/src/lib/auth-token.ts +5 -0
- package/packages/daemon/src/lib/authorship.ts +280 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
- package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
- package/packages/daemon/src/lib/daemon-embed.ts +782 -0
- package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
- package/packages/daemon/src/lib/daemon.ts +79 -0
- package/packages/daemon/src/lib/dot-path.ts +17 -0
- package/packages/daemon/src/lib/errors.ts +67 -0
- package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
- package/packages/daemon/src/lib/launch-plan.ts +90 -0
- package/packages/daemon/src/lib/log.ts +134 -0
- package/packages/daemon/src/lib/output.ts +76 -0
- package/packages/daemon/src/lib/project-init-runner.ts +150 -0
- package/packages/daemon/src/lib/project-inspect.ts +80 -0
- package/packages/daemon/src/lib/project-onboard.ts +149 -0
- package/packages/daemon/src/lib/project-probe.ts +92 -0
- package/packages/daemon/src/lib/project-registry.ts +296 -0
- package/packages/daemon/src/lib/session-monitor.ts +122 -0
- package/packages/daemon/src/lib/session-options.ts +100 -0
- package/packages/daemon/src/lib/shell.ts +8 -0
- package/packages/daemon/src/lib/sizes.ts +36 -0
- package/packages/daemon/src/lib/skill-sync.ts +155 -0
- package/packages/daemon/src/lib/slugify.ts +10 -0
- package/packages/daemon/src/lib/terminals-store.ts +125 -0
- package/packages/daemon/src/lib/tui-binary.ts +165 -0
- package/packages/daemon/src/lib/update-check.ts +298 -0
- package/packages/daemon/src/lib/update.ts +158 -0
- package/packages/daemon/src/lib/workspace-registry.ts +229 -0
- package/packages/daemon/src/lib/worktree.ts +289 -0
- package/packages/daemon/src/lib/yaml-io.ts +27 -0
- package/packages/daemon/src/ls.ts +40 -0
- package/packages/daemon/src/restart.ts +24 -0
- package/packages/daemon/src/restore.ts +514 -0
- package/packages/daemon/src/schemas/domain.ts +2 -0
- package/packages/daemon/src/schemas/filesystem.ts +34 -0
- package/packages/daemon/src/schemas/ide-config.ts +2 -0
- package/packages/daemon/src/schemas/index.ts +59 -0
- package/packages/daemon/src/schemas/inspect.ts +67 -0
- package/packages/daemon/src/schemas/registry.ts +55 -0
- package/packages/daemon/src/schemas/ws-events.ts +135 -0
- package/packages/daemon/src/send.ts +171 -0
- package/packages/daemon/src/server/README.md +15 -0
- package/packages/daemon/src/server/index.ts +74 -0
- package/packages/daemon/src/server/pty-bridge.ts +532 -0
- package/packages/daemon/src/server/standalone.ts +18 -0
- package/packages/daemon/src/server/ws-route.ts +483 -0
- package/packages/daemon/src/status.ts +59 -0
- package/packages/daemon/src/stop.ts +28 -0
- package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
- package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
- package/packages/daemon/src/terminal/README.md +92 -0
- package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
- package/packages/daemon/src/tui/chrome/chip.ts +30 -0
- package/packages/daemon/src/tui/chrome/events.ts +119 -0
- package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
- package/packages/daemon/src/tui/chrome/menu.ts +289 -0
- package/packages/daemon/src/tui/chrome/notify.ts +382 -0
- package/packages/daemon/src/tui/chrome/panels.ts +111 -0
- package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
- package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
- package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
- package/packages/daemon/src/tui/chrome/updater.ts +510 -0
- package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
- package/packages/daemon/src/tui/compiled.ts +121 -0
- package/packages/daemon/src/tui/detect/classify.ts +208 -0
- package/packages/daemon/src/tui/detect/manifest-loader.ts +193 -0
- package/packages/daemon/src/tui/detect/manifest.ts +199 -0
- package/packages/daemon/src/tui/detect/manifests.ts +354 -0
- package/packages/daemon/src/tui/detect/process-tree.ts +217 -0
- package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
- package/packages/daemon/src/tui/integrations/claude.ts +176 -0
- package/packages/daemon/src/tui/integrations/offer.ts +145 -0
- package/packages/daemon/src/tui/main.ts +82 -0
- package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
- package/packages/daemon/src/tui/mirror/agent-chip.ts +97 -0
- package/packages/daemon/src/tui/mirror/agent-rows.ts +133 -0
- package/packages/daemon/src/tui/mirror/app-state.ts +179 -0
- package/packages/daemon/src/tui/mirror/app.tsx +5265 -0
- package/packages/daemon/src/tui/mirror/blit.ts +186 -0
- package/packages/daemon/src/tui/mirror/control-client.ts +214 -0
- package/packages/daemon/src/tui/mirror/control.ts +97 -0
- package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
- package/packages/daemon/src/tui/mirror/dialog-stack.ts +354 -0
- package/packages/daemon/src/tui/mirror/diff-model.ts +112 -0
- package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
- package/packages/daemon/src/tui/mirror/file-tree.ts +97 -0
- package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
- package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
- package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
- package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
- package/packages/daemon/src/tui/mirror/menu-model.ts +187 -0
- package/packages/daemon/src/tui/mirror/palette.ts +274 -0
- package/packages/daemon/src/tui/mirror/pane-mirror.ts +561 -0
- package/packages/daemon/src/tui/mirror/pane-surface.tsx +415 -0
- package/packages/daemon/src/tui/mirror/perf-tap.ts +160 -0
- package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
- package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
- package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
- package/packages/daemon/src/tui/mirror/selection.ts +262 -0
- package/packages/daemon/src/tui/mirror/session-mirror.ts +443 -0
- package/packages/daemon/src/tui/mirror/settings-model.ts +345 -0
- package/packages/daemon/src/tui/mirror/size-truth.ts +77 -0
- package/packages/daemon/src/tui/mirror/spans.ts +46 -0
- package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
- package/packages/daemon/src/tui/team/CONTROL.md +50 -0
- package/packages/daemon/src/tui/team/entry.ts +38 -0
- package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
- package/packages/daemon/src/tui/team/home.ts +170 -0
- package/packages/daemon/src/tui/team/index.tsx +1521 -0
- package/packages/daemon/src/tui/team/input.ts +34 -0
- package/packages/daemon/src/tui/team/keymap.ts +127 -0
- package/packages/daemon/src/tui/team/mouse.ts +29 -0
- package/packages/daemon/src/tui/team/nav.ts +31 -0
- package/packages/daemon/src/tui/team/preview.ts +34 -0
- package/packages/daemon/src/tui/team/projects.ts +191 -0
- package/packages/daemon/src/tui/team/report.ts +83 -0
- package/packages/daemon/src/tui/team/sessions.ts +433 -0
- package/packages/daemon/src/tui/team/tree.ts +62 -0
- package/packages/daemon/src/types.ts +13 -0
- package/packages/daemon/src/ui/index.ts +32 -0
- package/packages/daemon/src/ui/terminal/index.ts +9 -0
- package/packages/daemon/src/ui/types.ts +91 -0
- package/packages/daemon/src/ui/web/base.css +80 -0
- package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
- package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
- package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
- package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
- package/packages/daemon/src/ui/web/hooks.ts +106 -0
- package/packages/daemon/src/ui/web/index.ts +27 -0
- package/packages/daemon/src/ui/web/render.ts +77 -0
- package/packages/daemon/src/ui/web/utils/color.ts +27 -0
- package/packages/daemon/src/validate.ts +217 -0
- package/packages/daemon/src/widgets/changes/README.md +3 -0
- package/packages/daemon/src/widgets/changes/index.tsx +691 -0
- package/packages/daemon/src/widgets/config/README.md +3 -0
- package/packages/daemon/src/widgets/config/index.tsx +481 -0
- package/packages/daemon/src/widgets/explorer/README.md +3 -0
- package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
- package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
- package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
- package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
- package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
- package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
- package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
- package/packages/daemon/src/widgets/lib/files.ts +88 -0
- package/packages/daemon/src/widgets/lib/git.ts +88 -0
- package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
- package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
- package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
- package/packages/daemon/src/widgets/lib/theme.ts +194 -0
- package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
- package/packages/daemon/src/widgets/preview/README.md +3 -0
- package/packages/daemon/src/widgets/preview/index.tsx +416 -0
- package/packages/daemon/src/widgets/resolve.ts +121 -0
- package/packages/daemon/src/widgets/setup/README.md +3 -0
- package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
- package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
- package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
- package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
- package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
- package/packages/daemon/src/widgets/setup/index.tsx +341 -0
- package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
- package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
- package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
- package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
- package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
- package/packages/tmux-bridge/package.json +22 -0
- package/packages/tmux-bridge/src/errors.ts +28 -0
- package/packages/tmux-bridge/src/index.ts +31 -0
- package/packages/tmux-bridge/src/monitor.ts +77 -0
- package/packages/tmux-bridge/src/panes.ts +136 -0
- package/packages/tmux-bridge/src/runner.test.ts +501 -0
- package/packages/tmux-bridge/src/runner.ts +91 -0
- package/packages/tmux-bridge/src/sessions.ts +126 -0
- package/packages/tmux-bridge/src/targeting.test.ts +107 -0
- package/packages/tmux-bridge/src/targeting.ts +90 -0
- package/scripts/build-tui.mjs +11 -4
- package/scripts/perf-mirror.mjs +313 -0
- package/scripts/postinstall.js +26 -2
- package/skill/SKILL.md +22 -0
- package/templates/AGENTS.md +14 -7
- package/templates/agent-team-monorepo.yml +8 -0
- package/templates/agent-team-nextjs.yml +8 -0
- package/templates/agent-team.yml +10 -0
- package/templates/convex.yml +2 -0
- package/templates/default.yml +11 -5
- package/templates/go.yml +4 -0
- package/templates/missions.yml +6 -0
- package/templates/nextjs.yml +4 -0
- package/templates/python.yml +4 -0
- package/templates/skills/backend.md +5 -12
- package/templates/skills/frontend.md +5 -12
- package/templates/skills/general-worker.md +5 -12
- package/templates/skills/researcher.md +7 -12
- package/templates/skills/reviewer.md +7 -16
- package/templates/vite.yml +4 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free fuzzy subsequence matcher for the team TUI's quick-jump
|
|
3
|
+
* filter. Pure so it can be unit-tested in isolation from `index.tsx` (which
|
|
4
|
+
* runs `render(...)` on import).
|
|
5
|
+
*
|
|
6
|
+
* The match is a case-insensitive SUBSEQUENCE test: every char of the query
|
|
7
|
+
* must appear in the target in order (not necessarily contiguously). Scoring
|
|
8
|
+
* rewards matches that feel "meaningful" to a human — contiguous runs, matches
|
|
9
|
+
* anchored at the start of the string, and matches right after a separator
|
|
10
|
+
* (so typing "web" ranks the "web" in `wavyr-website` well).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** A char that starts a new "word" — a match right after one scores a bonus. */
|
|
14
|
+
const SEPARATORS = new Set(["/", "-", "_", " ", "."]);
|
|
15
|
+
|
|
16
|
+
export interface FuzzyMatch<T> {
|
|
17
|
+
item: T;
|
|
18
|
+
score: number;
|
|
19
|
+
positions: number[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Per-char position bonuses. */
|
|
23
|
+
const START_BONUS = 10; // matched char is the first char of the target
|
|
24
|
+
const SEPARATOR_BONUS = 8; // matched char follows a separator (word start)
|
|
25
|
+
const CONTIGUOUS_BONUS = 5; // matched char is adjacent to the previous match
|
|
26
|
+
const BASE = 1; // every matched char is worth at least this
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Case-insensitive subsequence match of `query` against `target`.
|
|
30
|
+
*
|
|
31
|
+
* Returns `null` when `query` is not a subsequence of `target`. An empty query
|
|
32
|
+
* matches everything with score 0 and no positions. Otherwise returns the
|
|
33
|
+
* matched indices in `target` plus a heuristic score (higher is better).
|
|
34
|
+
*
|
|
35
|
+
* Uses a small dynamic program to find the highest-scoring alignment among all
|
|
36
|
+
* valid subsequences — a greedy leftmost scan would anchor on the first
|
|
37
|
+
* occurrence of each char and miss word-boundary matches (e.g. "web" should
|
|
38
|
+
* favour the "web" after the "-" in `wavyr-website`, not the leading "w").
|
|
39
|
+
*/
|
|
40
|
+
export function fuzzyMatch(
|
|
41
|
+
query: string,
|
|
42
|
+
target: string,
|
|
43
|
+
): { score: number; positions: number[] } | null {
|
|
44
|
+
if (query.length === 0) return { score: 0, positions: [] };
|
|
45
|
+
|
|
46
|
+
const q = query.toLowerCase();
|
|
47
|
+
const t = target.toLowerCase();
|
|
48
|
+
const m = q.length;
|
|
49
|
+
const n = t.length;
|
|
50
|
+
|
|
51
|
+
/** Position-context bonus for placing a matched char at target index j. */
|
|
52
|
+
const positionBonus = (j: number): number => {
|
|
53
|
+
if (j === 0) return START_BONUS;
|
|
54
|
+
if (SEPARATORS.has(t[j - 1]!)) return SEPARATOR_BONUS;
|
|
55
|
+
return 0;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const NEG = Number.NEGATIVE_INFINITY;
|
|
59
|
+
// f[i][j] = best score for matching q[i..] with q[i] placed at target index j
|
|
60
|
+
// (only meaningful when t[j] === q[i]); nextPos[i][j] = chosen index for q[i+1].
|
|
61
|
+
const f: number[][] = [];
|
|
62
|
+
const nextPos: number[][] = [];
|
|
63
|
+
for (let i = 0; i < m; i++) {
|
|
64
|
+
f.push(new Array<number>(n).fill(NEG));
|
|
65
|
+
nextPos.push(new Array<number>(n).fill(-1));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const last = m - 1;
|
|
69
|
+
for (let j = 0; j < n; j++) {
|
|
70
|
+
if (t[j] === q[last]) f[last]![j] = BASE + positionBonus(j);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (let i = m - 2; i >= 0; i--) {
|
|
74
|
+
const ch = q[i]!;
|
|
75
|
+
const fi = f[i]!;
|
|
76
|
+
const fnext = f[i + 1]!;
|
|
77
|
+
const ni = nextPos[i]!;
|
|
78
|
+
for (let j = 0; j < n; j++) {
|
|
79
|
+
if (t[j] !== ch) continue;
|
|
80
|
+
let best = NEG;
|
|
81
|
+
let bestNext = -1;
|
|
82
|
+
for (let j2 = j + 1; j2 < n; j2++) {
|
|
83
|
+
const sub = fnext[j2]!;
|
|
84
|
+
if (sub === NEG) continue;
|
|
85
|
+
const val = (j2 === j + 1 ? CONTIGUOUS_BONUS : 0) + sub;
|
|
86
|
+
if (val > best) {
|
|
87
|
+
best = val;
|
|
88
|
+
bestNext = j2;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (best === NEG) continue; // q[i+1..] cannot be placed after j
|
|
92
|
+
fi[j] = BASE + positionBonus(j) + best;
|
|
93
|
+
ni[j] = bestNext;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Best starting placement for q[0] (leftmost on ties, keeping it deterministic).
|
|
98
|
+
let bestScore = NEG;
|
|
99
|
+
let start = -1;
|
|
100
|
+
const f0 = f[0]!;
|
|
101
|
+
for (let j = 0; j < n; j++) {
|
|
102
|
+
if (f0[j]! > bestScore) {
|
|
103
|
+
bestScore = f0[j]!;
|
|
104
|
+
start = j;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (start === -1) return null;
|
|
108
|
+
|
|
109
|
+
const positions: number[] = [];
|
|
110
|
+
let j = start;
|
|
111
|
+
for (let i = 0; i < m && j !== -1; i++) {
|
|
112
|
+
positions.push(j);
|
|
113
|
+
j = nextPos[i]![j]!;
|
|
114
|
+
}
|
|
115
|
+
return { score: bestScore, positions };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Filter `items` by `query`, keeping only fuzzy matches, sorted by score
|
|
120
|
+
* descending. The sort is STABLE — items with equal scores keep their input
|
|
121
|
+
* order. An empty query returns every item (score 0) in original order.
|
|
122
|
+
*/
|
|
123
|
+
export function fuzzyFilter<T>(query: string, items: T[], key: (t: T) => string): FuzzyMatch<T>[] {
|
|
124
|
+
const matches: FuzzyMatch<T>[] = [];
|
|
125
|
+
for (const item of items) {
|
|
126
|
+
const m = fuzzyMatch(query, key(item));
|
|
127
|
+
if (m) matches.push({ item, score: m.score, positions: m.positions });
|
|
128
|
+
}
|
|
129
|
+
// Stable sort by score desc: Array.prototype.sort is stable in modern JS, so
|
|
130
|
+
// equal scores preserve the push (input) order.
|
|
131
|
+
matches.sort((a, b) => b.score - a.score);
|
|
132
|
+
return matches;
|
|
133
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure model helpers for the cockpit's HOME screen — the surface bare
|
|
3
|
+
* `tmux-ide` opens when there's no single-project `ide.yml` to launch.
|
|
4
|
+
*
|
|
5
|
+
* The `.tsx` renders these plain-data shapes: the header's fleet rollup counts,
|
|
6
|
+
* the empty-fleet hero actions, the in-app panel keys (e/g/,), and the footer
|
|
7
|
+
* hint line. Keeping them here (io-free, opentui-free) means the header/footer/
|
|
8
|
+
* empty-state logic unit-tests as tables, and the panel keys + footer stay
|
|
9
|
+
* sourced from the grammar and the panel registry so they can't drift from the
|
|
10
|
+
* chrome binds or the widget set.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentStatus } from "../detect/classify.ts";
|
|
13
|
+
import type { TeamProject } from "./projects.ts";
|
|
14
|
+
import { GRAMMAR_KEYS } from "../../widgets/lib/grammar.ts";
|
|
15
|
+
import { PANEL_POPUPS, type PanelPopup } from "../chrome/panels.ts";
|
|
16
|
+
|
|
17
|
+
/** Live-session status tallies across the whole fleet, for the home header. */
|
|
18
|
+
export interface FleetRollup {
|
|
19
|
+
blocked: number;
|
|
20
|
+
working: number;
|
|
21
|
+
done: number;
|
|
22
|
+
idle: number;
|
|
23
|
+
unknown: number;
|
|
24
|
+
/** Total live sessions counted. */
|
|
25
|
+
sessions: number;
|
|
26
|
+
/** Total projects (registered + ad-hoc) in the fleet. */
|
|
27
|
+
projects: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* PURE — tally every live session's status across the fleet. Counts SESSIONS
|
|
32
|
+
* (the unit a person acts on), not panes: a project with three sessions
|
|
33
|
+
* contributes three to the rollup.
|
|
34
|
+
*/
|
|
35
|
+
export function fleetRollup(projects: TeamProject[]): FleetRollup {
|
|
36
|
+
const r: FleetRollup = {
|
|
37
|
+
blocked: 0,
|
|
38
|
+
working: 0,
|
|
39
|
+
done: 0,
|
|
40
|
+
idle: 0,
|
|
41
|
+
unknown: 0,
|
|
42
|
+
sessions: 0,
|
|
43
|
+
projects: projects.length,
|
|
44
|
+
};
|
|
45
|
+
for (const p of projects) {
|
|
46
|
+
for (const s of p.sessions) {
|
|
47
|
+
r[s.status] += 1;
|
|
48
|
+
r.sessions += 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return r;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A header rollup chip: a status and its count, colored by the status token. */
|
|
55
|
+
export interface RollupChip {
|
|
56
|
+
status: AgentStatus;
|
|
57
|
+
count: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The rollup chips shown in the header, in severity order — the meaningful
|
|
61
|
+
* agent states (blocked/working/done) plus idle to round out the total. */
|
|
62
|
+
export const ROLLUP_ORDER: AgentStatus[] = ["blocked", "working", "done", "idle"];
|
|
63
|
+
|
|
64
|
+
/** PURE — the ordered header chips for a rollup. */
|
|
65
|
+
export function rollupChips(r: FleetRollup): RollupChip[] {
|
|
66
|
+
return ROLLUP_ORDER.map((status) => ({ status, count: r[status] }));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* PURE — true when the fleet has nothing to show: no registered projects and no
|
|
71
|
+
* live sessions (ad-hoc projects only exist for live sessions, and registered
|
|
72
|
+
* projects always appear, so an empty list means a truly empty fleet). The home
|
|
73
|
+
* screen swaps to the hero box in this state.
|
|
74
|
+
*/
|
|
75
|
+
export function isFleetEmpty(projects: TeamProject[]): boolean {
|
|
76
|
+
return projects.length === 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** An empty-fleet hero call-to-action: the key and what it does. */
|
|
80
|
+
export interface HeroAction {
|
|
81
|
+
key: string;
|
|
82
|
+
label: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** PURE — the empty-fleet hero's action rows. `l` opens the add-a-project-dir
|
|
86
|
+
* prompt (the entry point to get a launchable project into the fleet). */
|
|
87
|
+
export function emptyFleetActions(): HeroAction[] {
|
|
88
|
+
return [
|
|
89
|
+
{ key: "n", label: "new session" },
|
|
90
|
+
{ key: "l", label: "launch a project dir" },
|
|
91
|
+
{ key: "q", label: "quit" },
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The cockpit's IN-APP panel keys — plain letters matched against a bare
|
|
97
|
+
* keypress in the home handler, distinct from the tmux root-table `M-e`/`M-g`/
|
|
98
|
+
* `M-,` chrome binds. `,` opens config to echo its `M-,` bind.
|
|
99
|
+
*/
|
|
100
|
+
export const HOME_PANEL_KEYS: Record<string, PanelPopup["widget"]> = {
|
|
101
|
+
e: "explorer",
|
|
102
|
+
g: "changes",
|
|
103
|
+
",": "config",
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** PURE — the panel a home keypress opens, or null when it isn't a panel key. */
|
|
107
|
+
export function panelForKey(name: string): PanelPopup["widget"] | null {
|
|
108
|
+
return HOME_PANEL_KEYS[name] ?? null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** PURE — the in-app key that opens `widget`, or "" when unbound. */
|
|
112
|
+
export function keyForPanel(widget: PanelPopup["widget"]): string {
|
|
113
|
+
const found = Object.entries(HOME_PANEL_KEYS).find(([, w]) => w === widget);
|
|
114
|
+
return found ? found[0] : "";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** One footer / help hint: the key glyph(s) and the verb. */
|
|
118
|
+
export interface FooterHint {
|
|
119
|
+
keys: string;
|
|
120
|
+
label: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* PURE — the panel hints, DERIVED from the panel registry + the in-app keys so
|
|
125
|
+
* adding a panel or re-keying it updates the footer and help automatically.
|
|
126
|
+
* `use` picks the label source: the terse `widget` name for the footer, the
|
|
127
|
+
* registry `label` (e.g. "⊞ Files") for the roomier help overlay.
|
|
128
|
+
*/
|
|
129
|
+
export function panelHints(use: "widget" | "label" = "widget"): FooterHint[] {
|
|
130
|
+
return PANEL_POPUPS.map((p) => ({
|
|
131
|
+
keys: keyForPanel(p.widget),
|
|
132
|
+
label: use === "label" ? p.label : p.widget,
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* PURE — the home footer hint line. The universal verbs' KEY glyphs come from
|
|
138
|
+
* {@link GRAMMAR_KEYS} (so the footer can't advertise a key the grammar doesn't
|
|
139
|
+
* bind), interleaved with the cockpit's own session + panel keys. Rendered as a
|
|
140
|
+
* single flex row by the `.tsx`.
|
|
141
|
+
*/
|
|
142
|
+
export function homeFooterHints(): FooterHint[] {
|
|
143
|
+
return [
|
|
144
|
+
{ keys: "↑↓", label: "nav" },
|
|
145
|
+
{ keys: "↵", label: "attach/launch" },
|
|
146
|
+
{ keys: "n", label: "new" },
|
|
147
|
+
{ keys: "l", label: "launch" },
|
|
148
|
+
...panelHints("widget"),
|
|
149
|
+
{ keys: GRAMMAR_KEYS.filter[0]!, label: "filter" },
|
|
150
|
+
{ keys: GRAMMAR_KEYS.help[0]!, label: "help" },
|
|
151
|
+
{ keys: GRAMMAR_KEYS.quit[0]!, label: "quit" },
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* PURE — the compact PICKER popup's footer hints. The picker ends in a
|
|
157
|
+
* switch-client + close, so it advertises that. The discoverable universal verbs
|
|
158
|
+
* (filter / help / dismiss) come from {@link GRAMMAR_KEYS} so the footer can't
|
|
159
|
+
* advertise a key the grammar doesn't bind — in particular `? help`, which
|
|
160
|
+
* surfaces the full keybindings overlay from inside the popup.
|
|
161
|
+
*/
|
|
162
|
+
export function pickerFooterHints(): FooterHint[] {
|
|
163
|
+
return [
|
|
164
|
+
{ keys: "↵", label: "switch" },
|
|
165
|
+
{ keys: "l", label: "launch" },
|
|
166
|
+
{ keys: GRAMMAR_KEYS.filter[0]!, label: "find" },
|
|
167
|
+
{ keys: GRAMMAR_KEYS.help[0]!, label: "help" },
|
|
168
|
+
{ keys: "esc", label: "close" },
|
|
169
|
+
];
|
|
170
|
+
}
|