@huseynsnmz/pi-board 0.15.3 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +55 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +277 -0
- package/package.json +5 -11
- package/src/commands/agent-board.ts +26 -2
- package/src/commands/attach-flow.ts +4 -0
- package/src/core/double-press.mjs +6 -2
- package/src/core/slash-commands.mjs +52 -0
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +59 -25
- package/src/ui/pty-attach.ts +8 -3
- package/IMPLEMENTATION_PLAN.md +0 -920
- package/PRD.md +0 -484
- package/PROGRESS.md +0 -142
- package/VERIFY.md +0 -115
- package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
- package/docs/EXPLORATION.md +0 -187
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
- package/docs/RELEASE.md +0 -148
- package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
- package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
- package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
- package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
- package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
- package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
- package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
- package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
- package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
- package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
- package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
- package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
- package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
- package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
- package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
- package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
- package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
- package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
- package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
- package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
- package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
- package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
- package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
- package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
- package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
- package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
- package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
- package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
- package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
- package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
- package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
- package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
- package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
- package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
- package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
- package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
- package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
- package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
- package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
- package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
- package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
- package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
- package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
- package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
- package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
- package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
- package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
- package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
- package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
- package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
- package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
- package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
- package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
- package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
- package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
- package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
- package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
- package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
- package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
- package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
- package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
- package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
- package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
- package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
- package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
- package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
- package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
- package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
- package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
- package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
- package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
- package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
- package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
- package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
- package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
- package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
- package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
- package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
- package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
- package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
- package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
- package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
- package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
- package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
- package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
- package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
- package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
- package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
- package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
- package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
- package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
- package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
- package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
- package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
- package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
- package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
- package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
- package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
- package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
- package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
- package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
- package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
- package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
- package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
- package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
- package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
- package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
- package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
- package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
- package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
- package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
- package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
- package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
- package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
- package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
- package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
- package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
- package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
- package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
- package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
- package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
- package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
- package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
- package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
- package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
- package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
- package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
- package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
- package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
- package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
- package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
- package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
- package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
- package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
- package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
- package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
- package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
- package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
- package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
- package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
- package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
- package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
- package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
- package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
- package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
- package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
- package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
- package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
- package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
- package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
- package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
- package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
- package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
- package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
- package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
- package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
- package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
- package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
- package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
- package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
- package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
- package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
- package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
- package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
- package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
- package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
- package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
- package/scripts/budget-audit.mjs +0 -135
- package/scripts/release.mjs +0 -446
- package/scripts/release_helper.mjs +0 -309
- package/scripts/run-perf-gate.mjs +0 -40
- package/scripts/run-tests.mjs +0 -375
package/src/ui/dashboard.ts
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
import { readFileSync } from "node:fs";
|
|
10
10
|
import { CustomEditor } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import type { Component, EditorTheme, KeybindingsManager, TUI } from "@earendil-works/pi-tui";
|
|
12
|
-
import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
12
|
+
import { CombinedAutocompleteProvider, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
13
|
+
import { createDoublePress, QUIT_PRESS_MS } from "../core/double-press.mjs";
|
|
14
|
+
import { runsInPi } from "../core/slash-commands.mjs";
|
|
13
15
|
import { readConfig, writeConfig } from "../core/config.mjs";
|
|
14
16
|
import { editExternally } from "./external-editor.ts";
|
|
15
17
|
import { requestDashboardRender } from "../core/dashboard-render.mjs";
|
|
@@ -57,7 +59,11 @@ interface ThemeLike {
|
|
|
57
59
|
strikethrough(text: string): string;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
export type DashboardResult =
|
|
62
|
+
export type DashboardResult =
|
|
63
|
+
| { action: "exit" }
|
|
64
|
+
| { action: "attach"; viewId: string; stopFirst: boolean }
|
|
65
|
+
| { action: "quit" }
|
|
66
|
+
| { action: "command"; text: string };
|
|
61
67
|
|
|
62
68
|
type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
63
69
|
type LaunchModel = {
|
|
@@ -125,6 +131,7 @@ export interface DashboardDeps {
|
|
|
125
131
|
currentThinkingLevel: ThinkingLevel;
|
|
126
132
|
initialSelectedId?: string | null;
|
|
127
133
|
initialNotice?: string;
|
|
134
|
+
commands?: { name: string; description?: string; source: string }[];
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
export class DashboardComponent implements Component {
|
|
@@ -137,10 +144,11 @@ export class DashboardComponent implements Component {
|
|
|
137
144
|
private filterQuery = "";
|
|
138
145
|
private pending: PendingConfirm | null = null;
|
|
139
146
|
private deleteArm: { id: string; at: number } | null = null;
|
|
140
|
-
private
|
|
147
|
+
private readonly quitPresses = createDoublePress(Date.now, QUIT_PRESS_MS);
|
|
141
148
|
private groupBy: "state" | "directory" = readConfig().groupBy === "directory" ? "directory" : "state";
|
|
142
149
|
private readonly groupOf = new Map<string, string>();
|
|
143
150
|
private readonly neighborsOf = new Map<string, string>();
|
|
151
|
+
private readonly folderOf = new Map<string, string>();
|
|
144
152
|
private helpReturnMode: ReturnableMode = "list";
|
|
145
153
|
private ptyHelpReturnMode: ReturnableMode = "list";
|
|
146
154
|
private peekId: string | null = null;
|
|
@@ -181,6 +189,7 @@ export class DashboardComponent implements Component {
|
|
|
181
189
|
}
|
|
182
190
|
};
|
|
183
191
|
this.editor.onSubmit = (text) => this.submitEditor(text);
|
|
192
|
+
this.editor.setAutocompleteProvider(this.commandMenu());
|
|
184
193
|
this.selectedId = deps.initialSelectedId ?? null;
|
|
185
194
|
// Issue #147: the post-delete reopen carries the Deleted notice here —
|
|
186
195
|
// the component is new each open, so an inline notice cannot travel.
|
|
@@ -202,15 +211,17 @@ export class DashboardComponent implements Component {
|
|
|
202
211
|
this.rows = this.filterQuery ? filterRows(all, this.filterQuery) : all;
|
|
203
212
|
this.groupOf.clear();
|
|
204
213
|
this.neighborsOf.clear();
|
|
205
|
-
|
|
214
|
+
this.folderOf.clear();
|
|
215
|
+
const place = (group: string, folder: string, ids: string[]) => {
|
|
206
216
|
for (const id of ids) {
|
|
207
217
|
this.groupOf.set(id, group);
|
|
208
|
-
this.
|
|
218
|
+
this.folderOf.set(id, folder);
|
|
219
|
+
this.neighborsOf.set(id, `${group}\0${folder}`);
|
|
209
220
|
}
|
|
210
221
|
return ids;
|
|
211
222
|
};
|
|
212
223
|
this.orderedIds = this.groupBy === "directory"
|
|
213
|
-
? groupRowsByDirectory(this.rows, Date.now()).flatMap((f) => place(f.key,
|
|
224
|
+
? groupRowsByDirectory(this.rows, Date.now()).flatMap((f) => place(f.key, f.key, f.rows.map((r) => r.id)))
|
|
214
225
|
: groupRowsByFolder(this.rows, Date.now()).flatMap((g) => g.folders.flatMap((f) => place(g.state, f.key, f.rows.map((r) => r.id))));
|
|
215
226
|
const visibleIds = new Set(this.orderedIds);
|
|
216
227
|
this.selectedIds = new Set([...this.selectedIds].filter((id) => visibleIds.has(id)));
|
|
@@ -332,6 +343,8 @@ export class DashboardComponent implements Component {
|
|
|
332
343
|
// ---- input --------------------------------------------------------------
|
|
333
344
|
|
|
334
345
|
handleInput(data: string): void {
|
|
346
|
+
if (!matchesKey(data, Key.ctrl("c"))) this.quitPresses.reset();
|
|
347
|
+
else if (this.quitPresses.press()) return this.done({ action: "quit" });
|
|
335
348
|
switch (this.mode) {
|
|
336
349
|
case "list":
|
|
337
350
|
this.handleListKey(data);
|
|
@@ -378,11 +391,10 @@ export class DashboardComponent implements Component {
|
|
|
378
391
|
|
|
379
392
|
private handleListKey(data: string): void {
|
|
380
393
|
if (!matchesKey(data, Key.ctrl("x"))) this.deleteArm = null;
|
|
381
|
-
if (!matchesKey(data, Key.ctrl("c"))) this.exitArmedAt = null;
|
|
382
394
|
if (this.handleRowKey(data)) return;
|
|
383
395
|
if (matchesKey(data, Key.right) || matchesKey(data, Key.left) || matchesKey(data, Key.enter)) return this.attachSelected();
|
|
384
396
|
if (matchesKey(data, Key.space)) return this.openPeek();
|
|
385
|
-
if (
|
|
397
|
+
if (matchesKey(data, Key.ctrl("f"))) return this.startFilter();
|
|
386
398
|
if (data === "?") return this.openHelp("list");
|
|
387
399
|
if (data === "!") return this.openPtyHelp("list");
|
|
388
400
|
if (matchesKey(data, Key.ctrl("r"))) return this.startRename();
|
|
@@ -399,16 +411,10 @@ export class DashboardComponent implements Component {
|
|
|
399
411
|
if (matchesKey(data, Key.ctrl("b"))) return this.toggleHold();
|
|
400
412
|
if (matchesKey(data, Key.ctrl("k"))) return this.confirmDeleteState();
|
|
401
413
|
for (let n = 1; n <= 9; n++) {
|
|
402
|
-
if (matchesKey(data, Key.alt(String(n) as never))) return this.
|
|
414
|
+
if (matchesKey(data, Key.alt(String(n) as never))) return this.attachInFolder(n - 1);
|
|
403
415
|
}
|
|
404
416
|
if (matchesKey(data, Key.escape)) return this.done({ action: "exit" });
|
|
405
|
-
if (matchesKey(data, Key.ctrl("c")))
|
|
406
|
-
const now = Date.now();
|
|
407
|
-
if (this.exitArmedAt !== null && now - this.exitArmedAt <= DELETE_DOUBLE_PRESS_MS) return this.done({ action: "exit" });
|
|
408
|
-
this.exitArmedAt = now;
|
|
409
|
-
this.notifyInputState("Press ctrl+c again to exit", "accent");
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
417
|
+
if (matchesKey(data, Key.ctrl("c"))) return this.notifyInputState("Press ctrl+c again to quit Pi", "accent");
|
|
412
418
|
if (isPrintable(data) || isBracketedPaste(data)) {
|
|
413
419
|
this.mode = "dispatch";
|
|
414
420
|
this.handleEditorInput(data);
|
|
@@ -496,13 +502,22 @@ export class DashboardComponent implements Component {
|
|
|
496
502
|
this.selectIndex(index);
|
|
497
503
|
}
|
|
498
504
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
this.
|
|
505
|
+
/** Counts the rows in the selected session's folder. */
|
|
506
|
+
private attachInFolder(index: number): void {
|
|
507
|
+
const folder = this.folderOf.get(this.selectedId ?? "");
|
|
508
|
+
const id = this.orderedIds.filter((other) => this.folderOf.get(other) === folder)[index];
|
|
509
|
+
if (!id) return;
|
|
510
|
+
this.selectIndex(this.orderedIds.indexOf(id));
|
|
502
511
|
this.attachSelected();
|
|
503
512
|
}
|
|
504
513
|
|
|
505
514
|
private handleDispatchKey(data: string): void {
|
|
515
|
+
// The menu has its own use for Enter, Tab, Esc and the arrows.
|
|
516
|
+
if (this.editor.isShowingAutocomplete() && !matchesKey(data, Key.ctrl("c"))) {
|
|
517
|
+
this.handleEditorInput(data);
|
|
518
|
+
if (!this.input && this.mode === "dispatch") this.mode = "list";
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
506
521
|
if (matchesKey(data, Key.ctrl("n"))) return this.openLaunchDialog();
|
|
507
522
|
if (matchesKey(data, Key.ctrl("g"))) return void this.openExternalEditor();
|
|
508
523
|
if (matchesKey(data, Key.ctrl("enter"))) return this.startNow(true);
|
|
@@ -795,6 +810,11 @@ export class DashboardComponent implements Component {
|
|
|
795
810
|
|
|
796
811
|
/** In this folder, with this session's model. Ctrl+N is there for anything else. */
|
|
797
812
|
private startNow(attach = false): void {
|
|
813
|
+
const text = this.input.trim();
|
|
814
|
+
if (runsInPi(text, this.deps.commands ?? [])) {
|
|
815
|
+
this.setInput("");
|
|
816
|
+
return this.done({ action: "command", text });
|
|
817
|
+
}
|
|
798
818
|
const cwd = this.deps.defaultCwd;
|
|
799
819
|
const { model, thinking } = this.buildLaunchState(cwd, this.deps.currentModel, this.deps.currentThinkingLevel);
|
|
800
820
|
this.submitDispatch({ cwd, model: model ? canonicalModelRef(model) : null, thinkingLevel: thinking, attach });
|
|
@@ -930,6 +950,17 @@ export class DashboardComponent implements Component {
|
|
|
930
950
|
this.handleEditorInput(data);
|
|
931
951
|
}
|
|
932
952
|
|
|
953
|
+
/** Only for a task that starts with `/`: the same editor takes filters, names and replies. */
|
|
954
|
+
private commandMenu(): CombinedAutocompleteProvider {
|
|
955
|
+
const menu = new CombinedAutocompleteProvider(this.deps.commands ?? [], this.deps.defaultCwd);
|
|
956
|
+
const suggest = menu.getSuggestions.bind(menu);
|
|
957
|
+
menu.getSuggestions = async (lines, line, col, options) => {
|
|
958
|
+
if (this.mode !== "dispatch" || line !== 0 || !lines[0]?.startsWith("/")) return null;
|
|
959
|
+
return suggest(lines, line, col, options);
|
|
960
|
+
};
|
|
961
|
+
return menu;
|
|
962
|
+
}
|
|
963
|
+
|
|
933
964
|
private handleEditorInput(data: string): void {
|
|
934
965
|
this.editor.handleInput(data);
|
|
935
966
|
this.input = this.editor.getExpandedText();
|
|
@@ -1435,7 +1466,7 @@ export class DashboardComponent implements Component {
|
|
|
1435
1466
|
if (this.mode === "select") {
|
|
1436
1467
|
return [`${this.selectionCount()} selected`, ...dots, "space toggle", "a all", "u clear", "d done", "ctrl+x delete", "esc cancel"];
|
|
1437
1468
|
}
|
|
1438
|
-
return [selected ? (live ? "enter attach live" : "enter resume") : "type to start", "space peek", "
|
|
1469
|
+
return [selected ? (live ? "enter attach live" : "enter resume") : "type to start", "space peek", "ctrl+f filter", "ctrl+x stop", "? keys", ...dots];
|
|
1439
1470
|
}
|
|
1440
1471
|
|
|
1441
1472
|
private hintLine(label: string, color: InputNoticeColor, hints: string[]): string {
|
|
@@ -1479,8 +1510,10 @@ export class DashboardComponent implements Component {
|
|
|
1479
1510
|
const contentWidth = Math.max(1, width - visibleWidth(opts.prompt));
|
|
1480
1511
|
return [clip(`${opts.prompt}${clip(this.theme.fg("muted", opts.placeholder ?? ""), contentWidth)}`, width)];
|
|
1481
1512
|
}
|
|
1482
|
-
// Its first and
|
|
1483
|
-
const
|
|
1513
|
+
// Its first line and the one above any command menu are its own rules, and the frame already draws those.
|
|
1514
|
+
const all = this.editor.render(width);
|
|
1515
|
+
const menu = (this.editor as unknown as { renderedAutocompleteHeight: number }).renderedAutocompleteHeight;
|
|
1516
|
+
const rendered = [...all.slice(1, all.length - menu - 1), ...all.slice(all.length - menu)];
|
|
1484
1517
|
return opts.editing ? rendered : rendered.map((line) => stripInactiveEditorCursor(line));
|
|
1485
1518
|
}
|
|
1486
1519
|
|
|
@@ -1798,9 +1831,10 @@ export class DashboardComponent implements Component {
|
|
|
1798
1831
|
["pgup / pgdn", "Move by a screenful"],
|
|
1799
1832
|
["home / end", "First or last row"],
|
|
1800
1833
|
["alt+↑ / alt+↓", "Previous or next group"],
|
|
1801
|
-
["alt+1..9", "Attach to that row"],
|
|
1834
|
+
["alt+1..9", "Attach to that row in this folder"],
|
|
1802
1835
|
["space", "Open or close the peek panel"],
|
|
1803
|
-
["/
|
|
1836
|
+
["/", "Pi's commands. They run here; a template or skill starts a session"],
|
|
1837
|
+
["ctrl+f", "Filter the list"],
|
|
1804
1838
|
["ctrl+s", "Group by state or by directory"],
|
|
1805
1839
|
["shift+↑ / ↓", "Move the row up or down its group"],
|
|
1806
1840
|
["ctrl+g", "Write the task in $VISUAL or $EDITOR"],
|
|
@@ -1814,7 +1848,7 @@ export class DashboardComponent implements Component {
|
|
|
1814
1848
|
["ctrl+e", "Evidence and diagnostics"],
|
|
1815
1849
|
["ctrl+k", "Delete every inactive session in this state"],
|
|
1816
1850
|
["esc", "Close, clear the input, or leave the board"],
|
|
1817
|
-
["ctrl+c", "Clear the input; press twice to
|
|
1851
|
+
["ctrl+c", "Clear the input; press twice to quit Pi, here or in a session"],
|
|
1818
1852
|
["!", "node-pty diagnostics"],
|
|
1819
1853
|
["?", "This help"],
|
|
1820
1854
|
["In select", "↑↓ move · space toggle · a all · u clear · d done · ctrl+x delete · esc cancel"],
|
package/src/ui/pty-attach.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { createConnection, type Socket } from "node:net";
|
|
|
6
6
|
import type { Component, KeybindingsManager, RgbColor, TUI, TerminalColorScheme, TuiMouseEvent, TuiMouseEventResult } from "@earendil-works/pi-tui";
|
|
7
7
|
import { CURSOR_MARKER, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
8
8
|
import { resolveAttachDeleteKey } from "../core/attach-delete.mjs";
|
|
9
|
+
import { createDoublePress, QUIT_PRESS_MS } from "../core/double-press.mjs";
|
|
9
10
|
import { findHttpUrlAtCells, findWordRangeAtCells } from "../core/pty-links.mjs";
|
|
10
11
|
import { canPublishAttachView, createAttachOutputRenderScheduler, decideAttachPresentation, detectCursorDesync, isPtyCursorHidden, nextAttachRender, projectPtyCursor, shouldScheduleAttachRenderForMessage } from "../core/pty-attach-render.mjs";
|
|
11
12
|
import { evaluateAttachReconnect, shouldEscapeAttach } from "../core/pty-attach-reconnect.mjs";
|
|
@@ -18,7 +19,7 @@ import { backgroundRgbFromTerminalColors, buildSettleSchemePatch, colorSchemeFor
|
|
|
18
19
|
import { REPLY_GUARD_WINDOW_MS, createReplyGuard, isReplyGuardEnabled } from "../core/terminal-reply-guard.mjs";
|
|
19
20
|
import { clampInt, encodeWheelReport, fullscreenMouseEnableSequence, historyFloorAfterHydrate, isFullscreenSelectionEnd, resolveAttachMouseAction, resolveAttachWheelRoute, resolvePasteSelectionSource, resolveWheelLines, sameMousePoint, scrollableHistoryLines, scrollViewportTop, selectionDragScrollLines, selectionReleaseIsDrag, shouldOwnOuterMouseMode, splitAttachInput, wheelScrollLinesUp } from "../core/pty-scroll.mjs";
|
|
20
21
|
|
|
21
|
-
export type PtyAttachResult = { action: "detached" } | { action: "closed"; exitCode?: number | null } | { action: "delete" };
|
|
22
|
+
export type PtyAttachResult = { action: "detached" } | { action: "closed"; exitCode?: number | null } | { action: "delete" } | { action: "quit" };
|
|
22
23
|
|
|
23
24
|
type ThemeLike = {
|
|
24
25
|
fg(color: string, text: string): string;
|
|
@@ -400,6 +401,7 @@ export class PtyAttachComponent implements Component {
|
|
|
400
401
|
private selectionAutoScrollLines = 0;
|
|
401
402
|
private selectionAutoScrollMouse: { row: number; col: number } | null = null;
|
|
402
403
|
private pendingClickTimer: ReturnType<typeof setTimeout> | null = null;
|
|
404
|
+
private readonly quitPresses = createDoublePress(Date.now, QUIT_PRESS_MS);
|
|
403
405
|
private lastClickPoint: MousePoint | null = null;
|
|
404
406
|
private lastClickAt = 0;
|
|
405
407
|
/** Authoritative editor emptiness pushed by the child Pi extension via the
|
|
@@ -577,6 +579,9 @@ export class PtyAttachComponent implements Component {
|
|
|
577
579
|
this.send({ type: "input", data });
|
|
578
580
|
return;
|
|
579
581
|
}
|
|
582
|
+
// The first goes to the session as usual. The second never does, so the session lives on.
|
|
583
|
+
if (!matchesKey(data, Key.ctrl("c"))) this.quitPresses.reset();
|
|
584
|
+
else if (this.quitPresses.press()) return this.detach({ action: "quit" });
|
|
580
585
|
if (matchesKey(data, "ctrl+\\")) {
|
|
581
586
|
// Unconditional, terminal-independent escape (issue #126): 0x1c is a
|
|
582
587
|
// single raw ASCII byte every terminal forwards unchanged in raw
|
|
@@ -761,7 +766,7 @@ export class PtyAttachComponent implements Component {
|
|
|
761
766
|
this.scheduleRender();
|
|
762
767
|
}
|
|
763
768
|
|
|
764
|
-
private detach(): void {
|
|
769
|
+
private detach(result: PtyAttachResult = { action: "detached" }): void {
|
|
765
770
|
// Restore a held PTY before ending the control socket. The runner closes
|
|
766
771
|
// the socket immediately after receiving detach, so sending detach first
|
|
767
772
|
// can drop the G3 restore resize (issue #42).
|
|
@@ -772,7 +777,7 @@ export class PtyAttachComponent implements Component {
|
|
|
772
777
|
this.send({ type: "detach" });
|
|
773
778
|
}
|
|
774
779
|
this.close(true);
|
|
775
|
-
this.done(
|
|
780
|
+
this.done(result);
|
|
776
781
|
}
|
|
777
782
|
|
|
778
783
|
/** Install the component's public ?2026 observers; false return keeps xterm defaults. */
|