@yuriteixeira/pi-muxr 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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/dist/cli/dashboard-component.d.ts +41 -0
  4. package/dist/cli/dashboard-component.js +236 -0
  5. package/dist/cli/dashboard-component.js.map +1 -0
  6. package/dist/cli/dashboard-icons.d.ts +10 -0
  7. package/dist/cli/dashboard-icons.js +40 -0
  8. package/dist/cli/dashboard-icons.js.map +1 -0
  9. package/dist/cli/dashboard-layout.d.ts +20 -0
  10. package/dist/cli/dashboard-layout.js +50 -0
  11. package/dist/cli/dashboard-layout.js.map +1 -0
  12. package/dist/cli/dashboard-theme.d.ts +43 -0
  13. package/dist/cli/dashboard-theme.js +56 -0
  14. package/dist/cli/dashboard-theme.js.map +1 -0
  15. package/dist/cli/dashboard.d.ts +4 -0
  16. package/dist/cli/dashboard.js +186 -0
  17. package/dist/cli/dashboard.js.map +1 -0
  18. package/dist/cli/format.d.ts +4 -0
  19. package/dist/cli/format.js +35 -0
  20. package/dist/cli/format.js.map +1 -0
  21. package/dist/cli/index.d.ts +2 -0
  22. package/dist/cli/index.js +74 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cli/rows.d.ts +2 -0
  25. package/dist/cli/rows.js +27 -0
  26. package/dist/cli/rows.js.map +1 -0
  27. package/dist/config/config.d.ts +3 -0
  28. package/dist/config/config.js +37 -0
  29. package/dist/config/config.js.map +1 -0
  30. package/dist/config/paths.d.ts +5 -0
  31. package/dist/config/paths.js +22 -0
  32. package/dist/config/paths.js.map +1 -0
  33. package/dist/domain/sorting.d.ts +3 -0
  34. package/dist/domain/sorting.js +28 -0
  35. package/dist/domain/sorting.js.map +1 -0
  36. package/dist/domain/status.d.ts +60 -0
  37. package/dist/domain/status.js +17 -0
  38. package/dist/domain/status.js.map +1 -0
  39. package/dist/extension/index.d.ts +1 -0
  40. package/dist/extension/index.js +2 -0
  41. package/dist/extension/index.js.map +1 -0
  42. package/dist/extension/pi-muxr.d.ts +7 -0
  43. package/dist/extension/pi-muxr.js +262 -0
  44. package/dist/extension/pi-muxr.js.map +1 -0
  45. package/dist/index.d.ts +11 -0
  46. package/dist/index.js +12 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/notifications/desktop.d.ts +2 -0
  49. package/dist/notifications/desktop.js +29 -0
  50. package/dist/notifications/desktop.js.map +1 -0
  51. package/dist/state/dashboard-presence.d.ts +6 -0
  52. package/dist/state/dashboard-presence.js +23 -0
  53. package/dist/state/dashboard-presence.js.map +1 -0
  54. package/dist/state/database.d.ts +3 -0
  55. package/dist/state/database.js +12 -0
  56. package/dist/state/database.js.map +1 -0
  57. package/dist/state/read-statuses.d.ts +3 -0
  58. package/dist/state/read-statuses.js +5 -0
  59. package/dist/state/read-statuses.js.map +1 -0
  60. package/dist/state/rows.d.ts +23 -0
  61. package/dist/state/rows.js +23 -0
  62. package/dist/state/rows.js.map +1 -0
  63. package/dist/state/schema.d.ts +1 -0
  64. package/dist/state/schema.js +31 -0
  65. package/dist/state/schema.js.map +1 -0
  66. package/dist/state/write-status.d.ts +10 -0
  67. package/dist/state/write-status.js +59 -0
  68. package/dist/state/write-status.js.map +1 -0
  69. package/dist/tmux/focus.d.ts +3 -0
  70. package/dist/tmux/focus.js +19 -0
  71. package/dist/tmux/focus.js.map +1 -0
  72. package/dist/tmux/list-panes.d.ts +5 -0
  73. package/dist/tmux/list-panes.js +21 -0
  74. package/dist/tmux/list-panes.js.map +1 -0
  75. package/dist/tmux/sidebar.d.ts +16 -0
  76. package/dist/tmux/sidebar.js +171 -0
  77. package/dist/tmux/sidebar.js.map +1 -0
  78. package/dist/web/assets.d.ts +5 -0
  79. package/dist/web/assets.js +19 -0
  80. package/dist/web/assets.js.map +1 -0
  81. package/dist/web/base16-theme.d.ts +23 -0
  82. package/dist/web/base16-theme.js +68 -0
  83. package/dist/web/base16-theme.js.map +1 -0
  84. package/dist/web/notifications.d.ts +7 -0
  85. package/dist/web/notifications.js +47 -0
  86. package/dist/web/notifications.js.map +1 -0
  87. package/dist/web/page.d.ts +1 -0
  88. package/dist/web/page.js +155 -0
  89. package/dist/web/page.js.map +1 -0
  90. package/dist/web/protocol.d.ts +25 -0
  91. package/dist/web/protocol.js +2 -0
  92. package/dist/web/protocol.js.map +1 -0
  93. package/dist/web/server.d.ts +5 -0
  94. package/dist/web/server.js +51 -0
  95. package/dist/web/server.js.map +1 -0
  96. package/dist/web/terminal-gateway.d.ts +2 -0
  97. package/dist/web/terminal-gateway.js +110 -0
  98. package/dist/web/terminal-gateway.js.map +1 -0
  99. package/dist/web/tmux-session.d.ts +4 -0
  100. package/dist/web/tmux-session.js +50 -0
  101. package/dist/web/tmux-session.js.map +1 -0
  102. package/docs/ARCHITECTURE.md +287 -0
  103. package/docs/BUILDING.md +78 -0
  104. package/docs/CONTRIBUTING.md +32 -0
  105. package/docs/resources/fullscreen.png +0 -0
  106. package/docs/resources/sidebar.png +0 -0
  107. package/docs/resources/web.png +0 -0
  108. package/examples/config.json +13 -0
  109. package/package.json +76 -0
@@ -0,0 +1,56 @@
1
+ export const DEFAULT_BASE16_PALETTE = {
2
+ base00: 0,
3
+ base01: 8,
4
+ base02: 8,
5
+ base03: 8,
6
+ base04: 8,
7
+ base05: 7,
8
+ base06: 15,
9
+ base07: 15,
10
+ base08: 9,
11
+ base09: 3,
12
+ base0A: 11,
13
+ base0B: 10,
14
+ base0C: 14,
15
+ base0D: 12,
16
+ base0E: 13,
17
+ base0F: 3,
18
+ };
19
+ export const DEFAULT_DASHBOARD_THEME = createDashboardTheme(DEFAULT_BASE16_PALETTE);
20
+ export function createDashboardTheme(palette) {
21
+ return {
22
+ palette,
23
+ background: ansiBackground(palette.base00),
24
+ surface: identity,
25
+ selectedSurface: identity,
26
+ border: ansiForeground(palette.base03),
27
+ muted: ansiForeground(palette.base04),
28
+ text: ansiForeground(palette.base05),
29
+ bright: compose(ansiForeground(palette.base06), bold),
30
+ accent: ansiForeground(palette.base0D),
31
+ success: ansiForeground(palette.base0B),
32
+ warning: ansiForeground(palette.base0A),
33
+ error: ansiForeground(palette.base08),
34
+ info: ansiForeground(palette.base0C),
35
+ purple: ansiForeground(palette.base0E),
36
+ stale: ansiForeground(palette.base04),
37
+ };
38
+ }
39
+ export function ansiForeground(color) {
40
+ const code = color < 8 ? 30 + color : 82 + color;
41
+ return (text) => `\x1b[${code}m${text}\x1b[39m`;
42
+ }
43
+ export function ansiBackground(color) {
44
+ const code = color < 8 ? 40 + color : 92 + color;
45
+ return (text) => `\x1b[${code}m${text}\x1b[49m`;
46
+ }
47
+ export function bold(text) {
48
+ return `\x1b[1m${text}\x1b[22m`;
49
+ }
50
+ function compose(...styles) {
51
+ return (text) => styles.reduceRight((value, style) => style(value), text);
52
+ }
53
+ function identity(text) {
54
+ return text;
55
+ }
56
+ //# sourceMappingURL=dashboard-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-theme.js","sourceRoot":"","sources":["../../src/cli/dashboard-theme.ts"],"names":[],"mappings":"AAyCA,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;AAEpF,MAAM,UAAU,oBAAoB,CAAC,OAAsB;IACzD,OAAO;QACL,OAAO;QACP,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,OAAO,EAAE,QAAQ;QACjB,eAAe,EAAE,QAAQ;QACzB,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACtC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACrC,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACpC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;QACrD,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACtC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACvC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACvC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACrC,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACpC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACtC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;IACjD,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,IAAI,IAAI,IAAI,UAAU,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;IACjD,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,IAAI,IAAI,IAAI,UAAU,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,UAAU,IAAI,UAAU,CAAC;AAClC,CAAC;AAED,SAAS,OAAO,CAAC,GAAG,MAAiB;IACnC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,4 @@
1
+ export interface DashboardOptions {
2
+ quitOnSelect?: boolean;
3
+ }
4
+ export declare function runDashboard(options?: DashboardOptions): void;
@@ -0,0 +1,186 @@
1
+ import { ProcessTerminal, TuiAltScreen } from "@earendil-works/pi-tui";
2
+ import { loadConfig } from "../config/config.js";
3
+ import { createPresenceId, removeDashboardPresence, writeDashboardPresence } from "../state/dashboard-presence.js";
4
+ import { openDatabase } from "../state/database.js";
5
+ import { readStatuses } from "../state/read-statuses.js";
6
+ import { dismissAllRead, dismissStatus, markRead } from "../state/write-status.js";
7
+ import { focusPane } from "../tmux/focus.js";
8
+ import { listTmuxPanes } from "../tmux/list-panes.js";
9
+ import { DashboardComponent } from "./dashboard-component.js";
10
+ import { RUN_SPINNER_INTERVAL_MS } from "./dashboard-icons.js";
11
+ import { buildDashboardRows } from "./rows.js";
12
+ export function runDashboard(options = {}) {
13
+ const config = loadConfig();
14
+ const db = openDatabase(config.databasePath);
15
+ const presenceId = createPresenceId();
16
+ const state = { rows: [], selected: 0, seenEvents: new Set(), message: null };
17
+ const terminal = new ProcessTerminal();
18
+ const tui = new TuiAltScreen(terminal);
19
+ let runtime;
20
+ const actions = createDashboardActions(() => runtime);
21
+ const component = new DashboardComponent(actions, () => terminal.rows);
22
+ runtime = {
23
+ config,
24
+ db,
25
+ presenceId,
26
+ state,
27
+ terminal,
28
+ tui,
29
+ component,
30
+ quitOnSelect: options.quitOnSelect ?? false,
31
+ refreshTimer: setInterval(() => refresh(runtime, true), 1_000),
32
+ presenceTimer: setInterval(() => writeDashboardPresence(db, presenceId, process.env.TMUX_PANE ?? null), config.dashboardPresenceIntervalMs),
33
+ animationTimer: setInterval(() => renderAnimationFrame(runtime), RUN_SPINNER_INTERVAL_MS),
34
+ cleaned: false,
35
+ };
36
+ tui.addChild(component);
37
+ tui.setFocus(component);
38
+ writeDashboardPresence(db, presenceId, process.env.TMUX_PANE ?? null);
39
+ refresh(runtime, false);
40
+ tui.start();
41
+ const cleanupHandler = () => cleanup(runtime);
42
+ const sigintHandler = () => { cleanup(runtime); process.exit(0); };
43
+ process.on("exit", cleanupHandler);
44
+ process.on("SIGINT", sigintHandler);
45
+ }
46
+ function createDashboardActions(getRuntime) {
47
+ return {
48
+ moveSelection: (delta) => moveSelection(getRuntime(), delta),
49
+ selectFirst: () => selectFirst(getRuntime()),
50
+ selectLast: () => selectLast(getRuntime()),
51
+ refresh: () => refreshFromInput(getRuntime()),
52
+ focusSelected: () => focusSelectedFromInput(getRuntime()),
53
+ dismissSelected: () => dismissSelectedFromInput(getRuntime()),
54
+ dismissAllRead: () => dismissAllReadFromInput(getRuntime()),
55
+ quit: () => quit(getRuntime()),
56
+ };
57
+ }
58
+ function cleanup(runtime) {
59
+ if (runtime.cleaned)
60
+ return;
61
+ runtime.cleaned = true;
62
+ clearInterval(runtime.refreshTimer);
63
+ clearInterval(runtime.presenceTimer);
64
+ clearInterval(runtime.animationTimer);
65
+ removeDashboardPresence(runtime.db, runtime.presenceId);
66
+ runtime.tui.stop();
67
+ runtime.db.close();
68
+ }
69
+ function refresh(runtime, beep) {
70
+ runtime.state.rows = buildDashboardRows(readStatuses(runtime.db), listTmuxPanes(), runtime.config);
71
+ clampSelection(runtime.state);
72
+ if (beep && runtime.config.dashboardBell)
73
+ beepForNewEvents(runtime);
74
+ else
75
+ rememberCurrentActionableEvents(runtime.state);
76
+ render(runtime);
77
+ }
78
+ function render(runtime) {
79
+ runtime.component.setSnapshot({ rows: runtime.state.rows, selected: runtime.state.selected, message: runtime.state.message });
80
+ runtime.tui.requestRender();
81
+ }
82
+ function renderAnimationFrame(runtime) {
83
+ if (!runtime.state.rows.some((row) => row.displayState === "RUN"))
84
+ return;
85
+ runtime.component.invalidate();
86
+ runtime.tui.requestRender();
87
+ }
88
+ function moveSelection(runtime, delta) {
89
+ runtime.state.message = null;
90
+ const lastIndex = Math.max(0, runtime.state.rows.length - 1);
91
+ runtime.state.selected = Math.min(lastIndex, Math.max(0, runtime.state.selected + delta));
92
+ render(runtime);
93
+ }
94
+ function selectFirst(runtime) {
95
+ runtime.state.message = null;
96
+ runtime.state.selected = 0;
97
+ render(runtime);
98
+ }
99
+ function selectLast(runtime) {
100
+ runtime.state.message = null;
101
+ runtime.state.selected = Math.max(0, runtime.state.rows.length - 1);
102
+ render(runtime);
103
+ }
104
+ function refreshFromInput(runtime) {
105
+ runtime.state.message = null;
106
+ refresh(runtime, false);
107
+ }
108
+ function dismissSelectedFromInput(runtime) {
109
+ runtime.state.message = null;
110
+ dismissSelected(runtime.db, runtime.state);
111
+ refresh(runtime, false);
112
+ }
113
+ function dismissAllReadFromInput(runtime) {
114
+ runtime.state.message = null;
115
+ dismissAllRead(runtime.db, runtime.config.actionableStates);
116
+ refresh(runtime, false);
117
+ }
118
+ function focusSelectedFromInput(runtime) {
119
+ runtime.state.message = null;
120
+ const rowSelected = focusSelected(runtime.db, runtime.state);
121
+ if (rowSelected && runtime.quitOnSelect) {
122
+ quit(runtime);
123
+ return;
124
+ }
125
+ refresh(runtime, false);
126
+ }
127
+ function quit(runtime) {
128
+ cleanup(runtime);
129
+ process.exit(0);
130
+ }
131
+ function clampSelection(state) {
132
+ state.selected = Math.min(state.selected, Math.max(0, state.rows.length - 1));
133
+ }
134
+ function beepForNewEvents(runtime) {
135
+ for (const row of runtime.state.rows) {
136
+ const key = eventKey(row);
137
+ if (row.actionable && row.unread && !row.dismissed && !runtime.state.seenEvents.has(key)) {
138
+ runtime.terminal.write("\x07");
139
+ runtime.state.seenEvents.add(key);
140
+ }
141
+ }
142
+ }
143
+ function rememberCurrentActionableEvents(state) {
144
+ for (const row of state.rows) {
145
+ if (row.actionable && !row.dismissed)
146
+ state.seenEvents.add(eventKey(row));
147
+ }
148
+ }
149
+ function eventKey(row) {
150
+ return `${row.id}:${row.lastEventAt}`;
151
+ }
152
+ function dismissSelected(db, state) {
153
+ const row = state.rows[state.selected];
154
+ if (row)
155
+ dismissStatus(db, row.id, row.lastEventAt);
156
+ }
157
+ function focusSelected(db, state) {
158
+ const row = state.rows[state.selected];
159
+ if (!row)
160
+ return false;
161
+ markRead(db, row.id, row.lastEventAt);
162
+ const pane = findPaneForFocus(row);
163
+ if (!pane) {
164
+ state.message = `Pane ${row.paneId ?? "?"} is no longer available. Refresh marks this row as STALE.`;
165
+ return true;
166
+ }
167
+ try {
168
+ const commands = focusPane(pane);
169
+ if (!process.env.TMUX)
170
+ state.message = `Not inside tmux. Run:\n${commands.join("\n")}`;
171
+ }
172
+ catch (error) {
173
+ state.message = `Failed to focus ${pane.paneId}: ${formatError(error)}`;
174
+ }
175
+ return true;
176
+ }
177
+ function findPaneForFocus(row) {
178
+ const paneId = row.pane?.paneId ?? row.paneId;
179
+ if (!paneId)
180
+ return null;
181
+ return listTmuxPanes().find((pane) => pane.paneId === paneId) ?? null;
182
+ }
183
+ function formatError(error) {
184
+ return error instanceof Error ? error.message : String(error);
185
+ }
186
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/cli/dashboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { DashboardRow } from "../domain/status.js";
2
+ export declare function renderRows(rows: DashboardRow[], selected?: number, now?: number): string;
3
+ export declare function formatRow(row: DashboardRow, selected: boolean, now?: number): string;
4
+ export declare function formatAge(ms: number): string;
@@ -0,0 +1,35 @@
1
+ import { relative } from "node:path";
2
+ export function renderRows(rows, selected = 0, now = Date.now()) {
3
+ const lines = ["STATE AGE SESSION PROJECT LAST EVENT"];
4
+ rows.forEach((row, index) => lines.push(formatRow(row, index === selected, now)));
5
+ if (rows.length === 0)
6
+ lines.push("No pi sessions found.");
7
+ return lines.join("\n");
8
+ }
9
+ export function formatRow(row, selected, now = Date.now()) {
10
+ const marker = selected ? ">" : " ";
11
+ const state = row.displayState.padEnd(6);
12
+ const age = formatAge(now - row.lastEventAt).padEnd(5);
13
+ const session = (row.pane?.sessionName ?? row.tmuxSession ?? "?").padEnd(16).slice(0, 16);
14
+ const project = shortenPath(row.cwd).padEnd(22).slice(0, 22);
15
+ const unread = row.unread ? "*" : " ";
16
+ return `${marker}${unread}${state} ${age} ${session} ${project} ${row.summary}`;
17
+ }
18
+ export function formatAge(ms) {
19
+ const seconds = Math.max(0, Math.floor(ms / 1000));
20
+ if (seconds < 60)
21
+ return `${seconds}s`;
22
+ const minutes = Math.floor(seconds / 60);
23
+ if (minutes < 60)
24
+ return `${minutes}m`;
25
+ const hours = Math.floor(minutes / 60);
26
+ if (hours < 24)
27
+ return `${hours}h`;
28
+ return `${Math.floor(hours / 24)}d`;
29
+ }
30
+ function shortenPath(cwd) {
31
+ const home = process.env.HOME;
32
+ const value = home && cwd.startsWith(home) ? `~${cwd.slice(home.length)}` : cwd;
33
+ return value.startsWith(process.cwd()) ? relative(process.cwd(), value) || "." : value;
34
+ }
35
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig } from "../config/config.js";
3
+ import { openDatabase } from "../state/database.js";
4
+ import { readStatuses } from "../state/read-statuses.js";
5
+ import { listTmuxPanes } from "../tmux/list-panes.js";
6
+ import { ensurePinnedSidebar, parseSidebarSide, parseSidebarWindowTarget, toggleSidebar } from "../tmux/sidebar.js";
7
+ import { renderRows } from "./format.js";
8
+ import { buildDashboardRows } from "./rows.js";
9
+ import { runWebServer } from "../web/server.js";
10
+ import { runDashboard } from "./dashboard.js";
11
+ const HELP = `pi-muxr
12
+
13
+ Usage:
14
+ pi-muxr Open interactive dashboard
15
+ pi-muxr --sidebar [SIDE] Toggle pinned panes in every tmux window (default: right)
16
+ pi-muxr --list Print current rows and exit
17
+ pi-muxr --config Print resolved config
18
+ pi-muxr --web Serve browser terminal for a pi-muxr tmux session
19
+ pi-muxr --quit-on-select Exit after Enter focuses the selected row
20
+ pi-muxr --help Show this help
21
+
22
+ Keys: ↑/↓ or j/k select, Enter focus/read, d dismiss, D dismiss all read, r refresh, q quit.
23
+
24
+ Web: set HOST/PORT to change the bind address (defaults to 127.0.0.1:3042).
25
+ `;
26
+ function main(argv) {
27
+ if (argv.includes("--help") || argv.includes("-h")) {
28
+ console.log(HELP);
29
+ return;
30
+ }
31
+ try {
32
+ const sidebarWindowTarget = parseSidebarWindowTarget(argv);
33
+ if (sidebarWindowTarget) {
34
+ ensurePinnedSidebar(sidebarWindowTarget);
35
+ return;
36
+ }
37
+ const sidebarSide = parseSidebarSide(argv);
38
+ if (sidebarSide) {
39
+ const result = toggleSidebar(sidebarSide);
40
+ console.log(result === "created" ? `Opened and pinned pi-muxr sidebars on the ${sidebarSide}.` : "Closed and unpinned pi-muxr sidebars.");
41
+ return;
42
+ }
43
+ }
44
+ catch (error) {
45
+ console.error(formatError(error));
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const config = loadConfig();
50
+ if (argv.includes("--config")) {
51
+ console.log(JSON.stringify(config, null, 2));
52
+ return;
53
+ }
54
+ if (argv.includes("--web")) {
55
+ runWebServer();
56
+ return;
57
+ }
58
+ if (argv.includes("--list")) {
59
+ const db = openDatabase(config.databasePath);
60
+ try {
61
+ console.log(renderRows(buildDashboardRows(readStatuses(db), listTmuxPanes(), config)));
62
+ }
63
+ finally {
64
+ db.close();
65
+ }
66
+ return;
67
+ }
68
+ runDashboard({ quitOnSelect: argv.includes("--quit-on-select") });
69
+ }
70
+ function formatError(error) {
71
+ return error instanceof Error ? error.message : String(error);
72
+ }
73
+ main(process.argv.slice(2));
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { DashboardConfig, DashboardRow, PiMuxrStatus, TmuxPane } from "../domain/status.js";
2
+ export declare function buildDashboardRows(statuses: PiMuxrStatus[], panes: TmuxPane[], config: DashboardConfig, now?: number): DashboardRow[];
@@ -0,0 +1,27 @@
1
+ import { isActionableStatus, isDismissedForCurrentEvent, isUnreadStatus } from "../domain/status.js";
2
+ import { sortDashboardRows } from "../domain/sorting.js";
3
+ export function buildDashboardRows(statuses, panes, config, now = Date.now()) {
4
+ const panesById = new Map(panes.map((pane) => [pane.paneId, pane]));
5
+ const rows = statuses.map((status) => toDashboardRow(status, panesById, config, now));
6
+ return sortDashboardRows(rows)
7
+ .filter((row) => row.displayState !== "STALE")
8
+ .filter((row) => config.showDismissedRows || !row.dismissed);
9
+ }
10
+ function toDashboardRow(status, panesById, config, now) {
11
+ const pane = status.paneId ? panesById.get(status.paneId) ?? null : null;
12
+ const missingPane = Boolean(status.paneId) && !pane;
13
+ const oldHeartbeat = now - status.heartbeatAt > config.staleAfterMs;
14
+ const displayState = oldHeartbeat || missingPane ? "STALE" : status.state;
15
+ const staleReason = oldHeartbeat ? "heartbeat expired" : missingPane ? "pane missing" : undefined;
16
+ const currentStatus = { ...status, state: displayState };
17
+ return {
18
+ ...status,
19
+ displayState,
20
+ pane,
21
+ staleReason,
22
+ dismissed: isDismissedForCurrentEvent(status),
23
+ actionable: displayState === status.state && isActionableStatus(currentStatus, config.actionableStates),
24
+ unread: displayState === status.state && isUnreadStatus(currentStatus),
25
+ };
26
+ }
27
+ //# sourceMappingURL=rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../src/cli/rows.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,UAAU,kBAAkB,CAAC,QAAwB,EAAE,KAAiB,EAAE,MAAuB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACvH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACtF,OAAO,iBAAiB,CAAC,IAAI,CAAC;SAC3B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,KAAK,OAAO,CAAC;SAC7C,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,cAAc,CAAC,MAAoB,EAAE,SAAgC,EAAE,MAAuB,EAAE,GAAW;IAClH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACzE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IACpD,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;IACpE,MAAM,YAAY,GAAG,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1E,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,MAAM,aAAa,GAAG,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IACzD,OAAO;QACL,GAAG,MAAM;QACT,YAAY;QACZ,IAAI;QACJ,WAAW;QACX,SAAS,EAAE,0BAA0B,CAAC,MAAM,CAAC;QAC7C,UAAU,EAAE,YAAY,KAAK,MAAM,CAAC,KAAK,IAAI,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,gBAAgB,CAAC;QACvG,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,KAAK,IAAI,cAAc,CAAC,aAAa,CAAC;KACvE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DashboardConfig } from "../domain/status.js";
2
+ export declare const DEFAULT_CONFIG: DashboardConfig;
3
+ export declare function loadConfig(configPath?: string): DashboardConfig;
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { DEFAULT_ACTIONABLE_STATES, PI_MUXR_STATES } from "../domain/status.js";
3
+ import { defaultConfigPath, defaultDatabasePath, defaultStateDir, expandHome } from "./paths.js";
4
+ export const DEFAULT_CONFIG = {
5
+ stateDir: defaultStateDir(),
6
+ databasePath: defaultDatabasePath(),
7
+ actionableStates: DEFAULT_ACTIONABLE_STATES,
8
+ desktopNotifications: true,
9
+ suppressDesktopNotificationsWhenDashboardOpen: true,
10
+ dashboardBell: true,
11
+ showDismissedRows: false,
12
+ staleAfterMs: 30_000,
13
+ heartbeatIntervalMs: 5_000,
14
+ dashboardPresenceIntervalMs: 2_000,
15
+ dashboardPresenceStaleAfterMs: 5_000,
16
+ };
17
+ export function loadConfig(configPath = defaultConfigPath()) {
18
+ let raw = {};
19
+ try {
20
+ raw = JSON.parse(readFileSync(expandHome(configPath), "utf8"));
21
+ }
22
+ catch (error) {
23
+ if (error.code !== "ENOENT")
24
+ throw error;
25
+ }
26
+ const stateDir = expandHome(raw.stateDir ?? DEFAULT_CONFIG.stateDir);
27
+ const databasePath = expandHome(raw.databasePath ?? (raw.stateDir ? `${stateDir}/pi-muxr.sqlite` : DEFAULT_CONFIG.databasePath));
28
+ const actionableStates = (raw.actionableStates ?? DEFAULT_CONFIG.actionableStates).filter((state) => PI_MUXR_STATES.includes(state));
29
+ return {
30
+ ...DEFAULT_CONFIG,
31
+ ...raw,
32
+ stateDir,
33
+ databasePath,
34
+ actionableStates: actionableStates.length > 0 ? actionableStates : DEFAULT_CONFIG.actionableStates,
35
+ };
36
+ }
37
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEjG,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,QAAQ,EAAE,eAAe,EAAE;IAC3B,YAAY,EAAE,mBAAmB,EAAE;IACnC,gBAAgB,EAAE,yBAAyB;IAC3C,oBAAoB,EAAE,IAAI;IAC1B,6CAA6C,EAAE,IAAI;IACnD,aAAa,EAAE,IAAI;IACnB,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,MAAM;IACpB,mBAAmB,EAAE,KAAK;IAC1B,2BAA2B,EAAE,KAAK;IAClC,6BAA6B,EAAE,KAAK;CACrC,CAAC;AAIF,MAAM,UAAU,UAAU,CAAC,UAAU,GAAG,iBAAiB,EAAE;IACzD,IAAI,GAAG,GAAc,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAc,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACtE,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,iBAAiB,CAAC,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IACjI,MAAM,gBAAgB,GAAG,CAAC,GAAG,CAAC,gBAAgB,IAAI,cAAc,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAwD,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAc,CAAC,CAAC,CAAC;IAEpM,OAAO;QACL,GAAG,cAAc;QACjB,GAAG,GAAG;QACN,QAAQ;QACR,YAAY;QACZ,gBAAgB,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC,gBAAgB;KACnG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function expandHome(input: string): string;
2
+ export declare function defaultStateDir(): string;
3
+ export declare function defaultDatabasePath(): string;
4
+ export declare function defaultConfigPath(): string;
5
+ export declare function parentDir(path: string): string;
@@ -0,0 +1,22 @@
1
+ import { homedir } from "node:os";
2
+ import { dirname, join } from "node:path";
3
+ export function expandHome(input) {
4
+ if (input === "~")
5
+ return homedir();
6
+ if (input.startsWith("~/"))
7
+ return join(homedir(), input.slice(2));
8
+ return input;
9
+ }
10
+ export function defaultStateDir() {
11
+ return join(homedir(), ".pi-muxr");
12
+ }
13
+ export function defaultDatabasePath() {
14
+ return join(defaultStateDir(), "pi-muxr.sqlite");
15
+ }
16
+ export function defaultConfigPath() {
17
+ return join(defaultStateDir(), "config.json");
18
+ }
19
+ export function parentDir(path) {
20
+ return dirname(path);
21
+ }
22
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,gBAAgB,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DashboardRow } from "./status.js";
2
+ export declare function compareDashboardRows(a: DashboardRow, b: DashboardRow): number;
3
+ export declare function sortDashboardRows(rows: DashboardRow[]): DashboardRow[];
@@ -0,0 +1,28 @@
1
+ import { projectName } from "./status.js";
2
+ const STATE_PRIORITY = {
3
+ ASK: 0,
4
+ ERROR: 1,
5
+ DONE: 2,
6
+ QUEUED: 3,
7
+ RUN: 4,
8
+ IDLE: 5,
9
+ STALE: 6,
10
+ };
11
+ function actionBucket(row) {
12
+ if (row.actionable && row.unread)
13
+ return 0;
14
+ if (row.actionable)
15
+ return 1;
16
+ return 2;
17
+ }
18
+ export function compareDashboardRows(a, b) {
19
+ return actionBucket(a) - actionBucket(b)
20
+ || STATE_PRIORITY[a.displayState] - STATE_PRIORITY[b.displayState]
21
+ || b.lastEventAt - a.lastEventAt
22
+ || projectName(a.cwd).localeCompare(projectName(b.cwd))
23
+ || a.id.localeCompare(b.id);
24
+ }
25
+ export function sortDashboardRows(rows) {
26
+ return [...rows].sort(compareDashboardRows);
27
+ }
28
+ //# sourceMappingURL=sorting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sorting.js","sourceRoot":"","sources":["../../src/domain/sorting.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,cAAc,GAAgC;IAClD,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,SAAS,YAAY,CAAC,GAAiB;IACrC,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,GAAG,CAAC,UAAU;QAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAAe,EAAE,CAAe;IACnE,OAAO,YAAY,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;WACnC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC;WAC/D,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;WAC7B,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;WACpD,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAoB;IACpD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,60 @@
1
+ export declare const PI_MUXR_STATES: readonly ["ASK", "ERROR", "DONE", "QUEUED", "RUN", "IDLE", "STALE"];
2
+ export type PiMuxrState = (typeof PI_MUXR_STATES)[number];
3
+ export declare const SEVERITIES: readonly ["low", "medium", "high"];
4
+ export type PiMuxrSeverity = (typeof SEVERITIES)[number];
5
+ export interface PiMuxrStatus {
6
+ id: string;
7
+ paneId: string | null;
8
+ tmuxSession: string | null;
9
+ tmuxWindow: string | null;
10
+ tmuxWindowIndex: string | null;
11
+ pid: number;
12
+ cwd: string;
13
+ piSessionFile: string | null;
14
+ model: string | null;
15
+ state: PiMuxrState;
16
+ severity: PiMuxrSeverity;
17
+ summary: string;
18
+ lastEventAt: number;
19
+ heartbeatAt: number;
20
+ readUntilEventAt?: number | null;
21
+ acknowledgedAt?: number | null;
22
+ dismissedUntilEventAt?: number | null;
23
+ lastNotifiedEventAt?: number | null;
24
+ }
25
+ export interface DashboardConfig {
26
+ stateDir: string;
27
+ databasePath: string;
28
+ actionableStates: PiMuxrState[];
29
+ desktopNotifications: boolean;
30
+ suppressDesktopNotificationsWhenDashboardOpen: boolean;
31
+ dashboardBell: boolean;
32
+ showDismissedRows: boolean;
33
+ staleAfterMs: number;
34
+ heartbeatIntervalMs: number;
35
+ dashboardPresenceIntervalMs: number;
36
+ dashboardPresenceStaleAfterMs: number;
37
+ }
38
+ export interface TmuxPane {
39
+ sessionName: string;
40
+ windowIndex: string;
41
+ windowName: string;
42
+ paneId: string;
43
+ paneActive: boolean;
44
+ currentCommand: string;
45
+ paneTitle: string;
46
+ panePid: number;
47
+ }
48
+ export interface DashboardRow extends PiMuxrStatus {
49
+ displayState: PiMuxrState;
50
+ pane: TmuxPane | null;
51
+ actionable: boolean;
52
+ unread: boolean;
53
+ dismissed: boolean;
54
+ staleReason?: string;
55
+ }
56
+ export declare const DEFAULT_ACTIONABLE_STATES: PiMuxrState[];
57
+ export declare function isActionableStatus(status: Pick<PiMuxrStatus, "state" | "lastEventAt" | "dismissedUntilEventAt">, actionableStates?: ("ASK" | "DONE" | "ERROR" | "IDLE" | "QUEUED" | "RUN" | "STALE")[]): boolean;
58
+ export declare function isUnreadStatus(status: Pick<PiMuxrStatus, "lastEventAt" | "readUntilEventAt" | "dismissedUntilEventAt">): boolean;
59
+ export declare function isDismissedForCurrentEvent(status: Pick<PiMuxrStatus, "lastEventAt" | "dismissedUntilEventAt">): boolean;
60
+ export declare function projectName(cwd: string): string;
@@ -0,0 +1,17 @@
1
+ export const PI_MUXR_STATES = ["ASK", "ERROR", "DONE", "QUEUED", "RUN", "IDLE", "STALE"];
2
+ export const SEVERITIES = ["low", "medium", "high"];
3
+ export const DEFAULT_ACTIONABLE_STATES = ["ASK", "ERROR", "DONE"];
4
+ export function isActionableStatus(status, actionableStates = DEFAULT_ACTIONABLE_STATES) {
5
+ return actionableStates.includes(status.state) && !isDismissedForCurrentEvent(status);
6
+ }
7
+ export function isUnreadStatus(status) {
8
+ return !isDismissedForCurrentEvent(status) && (status.readUntilEventAt ?? 0) < status.lastEventAt;
9
+ }
10
+ export function isDismissedForCurrentEvent(status) {
11
+ return (status.dismissedUntilEventAt ?? 0) >= status.lastEventAt;
12
+ }
13
+ export function projectName(cwd) {
14
+ const normalized = cwd.replace(/\/$/, "");
15
+ return normalized.split("/").filter(Boolean).at(-1) ?? cwd;
16
+ }
17
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/domain/status.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AA0D7D,MAAM,CAAC,MAAM,yBAAyB,GAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEjF,MAAM,UAAU,kBAAkB,CAAC,MAA6E,EAAE,gBAAgB,GAAG,yBAAyB;IAC5J,OAAO,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAwF;IACrH,OAAO,CAAC,0BAA0B,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAmE;IAC5G,OAAO,CAAC,MAAM,CAAC,qBAAqB,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AAC7D,CAAC"}