@gavin-hu/my-pi-agent 0.18.0 → 0.20.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 +66 -52
- package/README.zh-CN.md +56 -46
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +507 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +774 -574
- package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
- package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
- package/docs/extensions/file-browse.md +561 -0
- package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
- package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
- package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
- package/{extensions/job/README.md → docs/extensions/job.md} +62 -52
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +440 -322
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
- package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
- package/docs/extensions/session-bus.md +298 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +254 -0
- package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
- package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +260 -163
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +137 -79
- package/extensions/board/commands.ts +105 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +170 -0
- package/extensions/board/index.ts +40 -0
- package/extensions/board/input.ts +54 -0
- package/extensions/board/join.ts +42 -0
- package/extensions/board/page.ts +388 -0
- package/extensions/board/result.ts +108 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +90 -0
- package/extensions/board/seat.ts +28 -0
- package/extensions/board/snapshot.ts +42 -0
- package/extensions/board/tools.ts +444 -0
- package/extensions/board/transport/router.ts +91 -0
- package/extensions/board/transport/server.ts +138 -0
- package/extensions/board/types.ts +121 -0
- package/extensions/board/view/client.ts +51 -0
- package/extensions/board/view/dialog.ts +43 -0
- package/extensions/board/view/keys.ts +125 -0
- package/extensions/board/view/live.ts +231 -0
- package/extensions/board/view/names.ts +91 -0
- package/extensions/board/view/page.ts +118 -0
- package/extensions/board/view/render.ts +519 -0
- package/extensions/board/view/styles.ts +228 -0
- package/extensions/board/view/team.ts +327 -0
- package/extensions/bytefly/footer/budget.ts +6 -5
- package/extensions/bytefly/footer/collect/entries.ts +12 -4
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
- package/extensions/bytefly/footer/config.ts +19 -8
- package/extensions/bytefly/footer/price/price.ts +30 -8
- package/extensions/bytefly/footer/register.ts +5 -5
- package/extensions/bytefly/footer/render/format.ts +18 -0
- package/extensions/bytefly/footer/render/lines.ts +23 -7
- package/extensions/bytefly/footer/render/segments.ts +117 -31
- package/extensions/bytefly/footer/types.ts +29 -5
- package/extensions/bytefly/footer/unpriced.ts +42 -17
- package/extensions/bytefly/panel/commands.ts +67 -7
- package/extensions/bytefly/panel/filter.ts +68 -0
- package/extensions/bytefly/panel/format.ts +16 -15
- package/extensions/bytefly/panel/join.ts +56 -13
- package/extensions/bytefly/panel/prices.ts +43 -0
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +75 -43
- package/extensions/bytefly/panel/tui/entries.ts +26 -8
- package/extensions/bytefly/panel/tui/frame.ts +130 -93
- package/extensions/bytefly/panel/tui/keys.ts +352 -0
- package/extensions/bytefly/panel/tui/list-view.ts +25 -46
- package/extensions/bytefly/panel/tui/panes.ts +76 -0
- package/extensions/bytefly/panel/tui/section-view.ts +126 -0
- package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
- package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
- package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
- package/extensions/bytefly/panel/tui.ts +306 -259
- package/extensions/bytefly/picker/discovery.ts +0 -2
- package/extensions/bytefly/picker/types.ts +0 -2
- package/extensions/bytefly/update/check.ts +39 -0
- package/extensions/bytefly/update/format.ts +15 -0
- package/extensions/bytefly/update/register.ts +73 -34
- package/extensions/bytefly/update/state.ts +6 -3
- package/extensions/check/commands.ts +2 -1
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/files/classify.ts +136 -0
- package/extensions/file-browse/files/files.ts +12 -0
- package/extensions/file-browse/files/index.ts +90 -0
- package/extensions/file-browse/files/list.ts +261 -0
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/files/tree.ts +101 -0
- package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
- package/extensions/file-browse/index.ts +4 -4
- package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
- package/extensions/file-browse/{router.ts → transport/router.ts} +21 -17
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
- package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
- package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
- package/extensions/file-browse/view/emit.ts +51 -0
- package/extensions/file-browse/{html.ts → view/html.ts} +19 -18
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +18 -20
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +19 -14
- package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
- package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
- package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
- package/extensions/file-read/schema.ts +2 -1
- package/extensions/job/runtime.ts +4 -4
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +21 -16
- package/extensions/plan/feedback.ts +185 -52
- package/extensions/plan/format.ts +10 -3
- package/extensions/plan/index.ts +23 -5
- package/extensions/plan/plan-files.ts +88 -0
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +27 -34
- package/extensions/plan/policy.ts +30 -17
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +52 -11
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/todos.ts +51 -3
- package/extensions/plan/tools/exit.ts +20 -95
- package/extensions/plan/tools/todo-seed.ts +4 -2
- package/extensions/plan/tools/write.ts +12 -1
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +15 -8
- package/extensions/plan/web-blocks.ts +89 -0
- package/extensions/plan/web-chrome.ts +3 -5
- package/extensions/plan/web-client.ts +179 -330
- package/extensions/plan/web-collab.ts +3 -3
- package/extensions/plan/web-comments.ts +18 -8
- package/extensions/plan/web-composer.ts +80 -0
- package/extensions/plan/web-draft.ts +40 -0
- package/extensions/plan/web-keys.ts +134 -0
- package/extensions/plan/web-outline.ts +192 -0
- package/extensions/plan/web-page.ts +4 -8
- package/extensions/plan/web-plan.ts +150 -0
- package/extensions/plan/web-router.ts +3 -58
- package/extensions/plan/web-settle.ts +194 -0
- package/extensions/plan/web-share-session.ts +24 -30
- package/extensions/plan/web-streams.ts +14 -14
- package/extensions/plan/web-tokens.ts +2 -2
- package/extensions/plan/web.ts +66 -120
- package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
- package/extensions/rewind/{review.ts → commands/review.ts} +15 -14
- package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
- package/extensions/rewind/{config.ts → config/config.ts} +1 -1
- package/extensions/rewind/index.ts +3 -3
- package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
- package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
- package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
- package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
- package/extensions/rewind/{format.ts → text/format.ts} +3 -3
- package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
- package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
- package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
- package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
- package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
- package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
- package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
- package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
- package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
- package/extensions/rewind/{status.ts → view/status.ts} +4 -4
- package/extensions/session-bus/commands.ts +17 -69
- package/extensions/session-bus/format.ts +22 -45
- package/extensions/session-bus/index.ts +13 -7
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +32 -4
- package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
- package/extensions/session-bus/protocol.ts +19 -49
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +16 -51
- package/extensions/session-bus/rows.ts +11 -5
- package/extensions/session-bus/runtime.ts +220 -267
- package/extensions/session-bus/store.ts +55 -7
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +5 -5
- package/extensions/session-bus/types.ts +23 -14
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/commands.ts +2 -1
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +145 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +119 -0
- package/extensions/team/index.ts +34 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +267 -0
- package/extensions/team/types.ts +65 -0
- package/extensions/todo/index.ts +4 -1
- package/extensions/todo/nudge.ts +8 -2
- package/extensions/web-access/fetch/page.ts +4 -1
- package/extensions/web-access/fetch/structured.ts +447 -0
- package/extensions/web-access/fetch/tool.ts +2 -1
- package/extensions/web-access/search/tool.ts +2 -1
- package/extensions/wechat/bridge.ts +106 -251
- package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
- package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
- package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
- package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
- package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
- package/extensions/wechat/dialog/channels.ts +136 -0
- package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
- package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
- package/extensions/wechat/index.ts +10 -10
- package/extensions/wechat/intake.ts +245 -0
- package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
- package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
- package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
- package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
- package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
- package/extensions/wechat/{state.ts → state/state.ts} +14 -2
- package/extensions/wechat/{format.ts → text/format.ts} +3 -3
- package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
- package/extensions/wechat/tools.ts +29 -12
- package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
- package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
- package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
- package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
- package/extensions/wechat/transport/outbound.ts +142 -0
- package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
- package/extensions/wechat/types.ts +10 -1
- package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
- package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
- package/extensions/worktree/commands.ts +24 -17
- package/extensions/worktree/exit.ts +12 -6
- package/extensions/worktree/git.ts +22 -14
- package/extensions/worktree/guard.ts +18 -290
- package/extensions/worktree/handoff.ts +10 -2
- package/extensions/worktree/index.ts +2 -1
- package/extensions/worktree/lifecycle.ts +2 -0
- package/extensions/worktree/registry.ts +20 -11
- package/extensions/worktree/root-tools.ts +13 -3
- package/extensions/worktree/runtime.ts +8 -2
- package/extensions/worktree/screen.ts +15 -8
- package/extensions/worktree/shell.ts +303 -0
- package/extensions/worktree/state.ts +1 -1
- package/extensions/worktree/status.ts +42 -9
- package/extensions/worktree/tools.ts +19 -13
- package/lib/README.md +11 -8
- package/lib/board/card.ts +479 -0
- package/lib/board/links.ts +141 -0
- package/lib/board/page-pointer.ts +310 -0
- package/lib/board/round-text.ts +81 -0
- package/lib/board/round.ts +94 -0
- package/lib/board/rules.ts +437 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +497 -0
- package/lib/board/view.ts +234 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +35 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +237 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +135 -0
- package/lib/http/token.ts +38 -0
- package/lib/plan-entry.ts +11 -0
- package/lib/policy.ts +25 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +201 -0
- package/lib/session-tool.ts +184 -0
- package/lib/spawn.ts +37 -3
- package/lib/stray-fields.ts +45 -0
- package/lib/tool-names.ts +38 -2
- package/lib/tui/glyphs.ts +3 -2
- package/lib/web/README.md +57 -4
- package/lib/web/client-runtime.ts +123 -0
- package/lib/web/components.ts +5 -0
- package/lib/web/index.ts +4 -2
- package/lib/web/markup.ts +88 -1
- package/lib/web/names.ts +42 -0
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +26 -14
- package/skills/next/SKILL.md +4 -3
- package/skills/project-docs/SKILL.md +133 -0
- package/skills/project-docs/references/audit.md +131 -0
- package/skills/project-docs/references/changelog.md +105 -0
- package/skills/project-docs/references/discovery.md +127 -0
- package/skills/project-docs/references/readme.md +80 -0
- package/skills/project-docs/references/release.md +84 -0
- package/skills/project-docs/references/roadmap.md +93 -0
- package/skills/project-docs/scripts/state.ts +316 -0
- package/skills/refactor/SKILL.md +100 -125
- package/skills/refactor/references/handoff.md +35 -0
- package/skills/refactor/references/signals.md +54 -0
- package/skills/refactor/scripts/inventory.ts +587 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
- package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/files.ts +0 -564
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/job/process.ts +0 -35
- package/extensions/plan/split.ts +0 -87
- package/extensions/session-bus/README.md +0 -247
- package/extensions/session-bus/team.ts +0 -102
- package/extensions/wechat/outbound.ts +0 -100
- package/lib/plans/sections.ts +0 -172
- package/lib/plans/setting.ts +0 -52
- package/lib/plans/split.ts +0 -215
- package/lib/plans/store.ts +0 -19
- package/lib/plans/team.ts +0 -151
- package/skills/changelog/SKILL.md +0 -148
- package/skills/parallel-dev/SKILL.md +0 -150
- /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
- /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
- /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
- /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
- /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
- /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
- /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
- /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
- /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
- /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
- /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Settings section
|
|
3
|
-
*
|
|
2
|
+
* The Settings section's own state: the text editor, the JSON pane, and the
|
|
3
|
+
* writes. Everything it draws comes from `tui.ts` and `side-pane.ts`, because all
|
|
4
|
+
* three sections draw one frame.
|
|
4
5
|
*
|
|
5
|
-
* The component owns the section switch, the write scope,
|
|
6
|
-
*
|
|
7
|
-
* list keys, and the section reads and writes through the same surface.
|
|
6
|
+
* The component owns the section switch, the write scope, the cursor, the filter
|
|
7
|
+
* and the keys the three sections share; this class owns what only Settings has.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
11
|
-
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
12
|
-
import type { ViewportRowsSource } from "../../../../lib/tui/chrome.ts";
|
|
13
10
|
import type { ListCursor } from "../../../../lib/tui/cursor.ts";
|
|
14
11
|
import type { WriteScope } from "../../picker/types.ts";
|
|
12
|
+
import { settingMatches, type PanelFilter } from "../filter.ts";
|
|
15
13
|
import type { SettingsSurface } from "../settings-runtime.ts";
|
|
16
|
-
import { SETTING_ROWS } from "../settings.ts";
|
|
14
|
+
import { SETTING_ROWS, type SettingView } from "../settings.ts";
|
|
17
15
|
import { applyEditKey, type EditingState } from "./editing.ts";
|
|
18
16
|
import { settingEntries, type PanelEntry } from "./entries.ts";
|
|
19
|
-
import { detailRows, groupLine, panelFrame, sectionBar } from "./frame.ts";
|
|
20
17
|
import { clampSelection } from "./nav.ts";
|
|
21
|
-
import { projectNote, sectionJsonPane, settingDetail, settingRow, settingsSummary } from "./settings-view.ts";
|
|
22
18
|
|
|
23
19
|
/** What the Settings section needs from the component that hosts it. */
|
|
24
20
|
export interface SettingsSectionDeps {
|
|
25
21
|
/** The rows, values, and writes the section reads and calls. */
|
|
26
22
|
surface: SettingsSurface;
|
|
27
|
-
|
|
23
|
+
/** The write scope the component owns. */
|
|
28
24
|
scope(): WriteScope;
|
|
25
|
+
/** The filter the component owns. */
|
|
26
|
+
filter(): PanelFilter;
|
|
29
27
|
cursor: ListCursor;
|
|
30
|
-
viewportRows: ViewportRowsSource | undefined;
|
|
31
|
-
cwd: string;
|
|
32
|
-
agentDir: string;
|
|
33
28
|
/** Keep the focused row across a rebuild and return it. */
|
|
34
29
|
retain(entries: PanelEntry[]): PanelEntry | undefined;
|
|
35
30
|
/** Panel navigation, for the keys this section does not own. */
|
|
@@ -38,13 +33,9 @@ export interface SettingsSectionDeps {
|
|
|
38
33
|
switchScope(): void;
|
|
39
34
|
notify(message: string): void;
|
|
40
35
|
requestRender(): void;
|
|
41
|
-
/** Keep the frame's project-note marker in step for the mouse mapping. */
|
|
42
|
-
setNoteShown(shown: boolean): void;
|
|
43
|
-
/** The hint row, already assembled (or the notice that replaces it). */
|
|
44
|
-
hint(width: number): string;
|
|
45
36
|
}
|
|
46
37
|
|
|
47
|
-
/** The Settings section's state
|
|
38
|
+
/** The Settings section's state. */
|
|
48
39
|
export class SettingsSection {
|
|
49
40
|
private chain: Promise<void> = Promise.resolve();
|
|
50
41
|
private editing: EditingState | undefined;
|
|
@@ -52,16 +43,21 @@ export class SettingsSection {
|
|
|
52
43
|
|
|
53
44
|
constructor(private readonly deps: SettingsSectionDeps) {}
|
|
54
45
|
|
|
55
|
-
/** Whether a text row is open in the editor, so the hint row and
|
|
46
|
+
/** Whether a text row is open in the editor, so the hint row and keys defer. */
|
|
56
47
|
get isEditing(): boolean {
|
|
57
48
|
return this.editing !== undefined;
|
|
58
49
|
}
|
|
59
50
|
|
|
60
|
-
/** Whether the
|
|
51
|
+
/** Whether the focused row's section is shown as JSON. */
|
|
61
52
|
get jsonOpen(): boolean {
|
|
62
53
|
return this.json;
|
|
63
54
|
}
|
|
64
55
|
|
|
56
|
+
/** The editor's buffer and caret, for the row that is being typed into. */
|
|
57
|
+
get editingState(): EditingState | undefined {
|
|
58
|
+
return this.editing;
|
|
59
|
+
}
|
|
60
|
+
|
|
65
61
|
/** Run `task` after the previous write, then repaint once. */
|
|
66
62
|
private write(task: () => Promise<unknown>, onSettled?: (result: unknown) => void): void {
|
|
67
63
|
this.chain = this.chain
|
|
@@ -71,16 +67,30 @@ export class SettingsSection {
|
|
|
71
67
|
.then(() => this.deps.requestRender());
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
/** The rows
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
/** The rows the active scope shows, after the filter. */
|
|
71
|
+
views(): SettingView[] {
|
|
72
|
+
const filter = this.deps.filter();
|
|
73
|
+
return this.deps.surface.views(this.deps.scope()).filter((view) => settingMatches(view, filter));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Those rows as display entries, grouped under their headings. */
|
|
77
|
+
entries(): PanelEntry[] {
|
|
78
|
+
return settingEntries(this.views());
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
/** The focused
|
|
80
|
-
|
|
81
|
+
/** The focused setting, after clamping the cursor onto a row. */
|
|
82
|
+
focused(): SettingView | undefined {
|
|
81
83
|
const entries = this.entries();
|
|
82
84
|
clampSelection(entries, this.deps.cursor);
|
|
83
|
-
|
|
85
|
+
const entry = entries[this.deps.cursor.selected];
|
|
86
|
+
return entry?.kind === "setting" ? entry.setting : undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The stored document behind the focused row, when the JSON pane is open. */
|
|
90
|
+
jsonFor(view: SettingView | undefined): { section: string; document: unknown } | undefined {
|
|
91
|
+
if (!this.json || view?.config === undefined) return undefined;
|
|
92
|
+
const { section } = view.config;
|
|
93
|
+
return { section, document: this.deps.surface.sectionDocument(this.deps.scope(), section) };
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
/** Drop the editor and the JSON pane when another section takes over. */
|
|
@@ -96,10 +106,9 @@ export class SettingsSection {
|
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
/** Change the focused setting: cycle it, or open the editor for a text row. */
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
const view = entry.setting;
|
|
109
|
+
changeSetting(): void {
|
|
110
|
+
const view = this.focused();
|
|
111
|
+
if (view === undefined) return;
|
|
103
112
|
if (view.kind === "text") {
|
|
104
113
|
this.editing = { id: view.id, buffer: view.value, caret: view.value.length };
|
|
105
114
|
this.deps.requestRender();
|
|
@@ -110,22 +119,22 @@ export class SettingsSection {
|
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
/** Delete the focused setting's leaf, so its default applies again. */
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
122
|
+
resetSetting(): void {
|
|
123
|
+
const view = this.focused();
|
|
124
|
+
if (view === undefined) return;
|
|
116
125
|
const scope = this.deps.scope();
|
|
117
|
-
this.write(() => this.deps.surface.reset(scope,
|
|
126
|
+
this.write(() => this.deps.surface.reset(scope, view.id));
|
|
118
127
|
}
|
|
119
128
|
|
|
120
|
-
/** Toggle the
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
if (!
|
|
129
|
+
/** Toggle the focused row between its value and the whole section as stored. */
|
|
130
|
+
toggleJson(): void {
|
|
131
|
+
const view = this.focused();
|
|
132
|
+
if (view === undefined) return;
|
|
133
|
+
if (!view.config) {
|
|
125
134
|
// There is no section to show, so leave the mode off rather than keeping a
|
|
126
135
|
// hidden toggle that would reopen the pane on another row.
|
|
127
136
|
this.json = false;
|
|
128
|
-
this.deps.notify(`${
|
|
137
|
+
this.deps.notify(`${view.label} is a Pi setting; bytefly.json has no section for it.`);
|
|
129
138
|
return;
|
|
130
139
|
}
|
|
131
140
|
this.json = !this.json;
|
|
@@ -133,7 +142,7 @@ export class SettingsSection {
|
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
/** Store the editor's buffer; a refusal keeps it open with the reason shown. */
|
|
136
|
-
|
|
145
|
+
commitEdit(): void {
|
|
137
146
|
const editing = this.editing;
|
|
138
147
|
if (!editing) return;
|
|
139
148
|
const scope = this.deps.scope();
|
|
@@ -149,13 +158,13 @@ export class SettingsSection {
|
|
|
149
158
|
* Editor keys. `Esc`/`Ctrl+C` cancel the edit rather than closing the screen,
|
|
150
159
|
* and everything else the section binds is inert until the editor closes.
|
|
151
160
|
*/
|
|
152
|
-
|
|
161
|
+
handleEditingInput(data: string): void {
|
|
153
162
|
const editing = this.editing;
|
|
154
163
|
if (!editing) return;
|
|
155
164
|
const max = SETTING_ROWS.find((row) => row.id === editing.id)?.text?.maxLength;
|
|
156
165
|
const action = applyEditKey(data, editing, max);
|
|
157
166
|
if (action.kind === "commit") {
|
|
158
|
-
|
|
167
|
+
this.commitEdit();
|
|
159
168
|
return;
|
|
160
169
|
}
|
|
161
170
|
if (action.kind === "cancel") {
|
|
@@ -167,84 +176,4 @@ export class SettingsSection {
|
|
|
167
176
|
this.editing = action.state;
|
|
168
177
|
this.deps.requestRender();
|
|
169
178
|
}
|
|
170
|
-
|
|
171
|
-
/** One key: editor keys first, then change, scope, reset, JSON, navigation. */
|
|
172
|
-
handleInput(data: string): void {
|
|
173
|
-
if (this.editing) {
|
|
174
|
-
this.handleEditingInput(data);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
this.focused();
|
|
178
|
-
if (matchesKey(data, Key.space) || matchesKey(data, Key.enter)) {
|
|
179
|
-
this.changeSetting();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (matchesKey(data, Key.tab)) {
|
|
183
|
-
this.deps.switchScope();
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (data === "u") {
|
|
187
|
-
this.resetSetting();
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (data === "J") {
|
|
191
|
-
this.toggleJson();
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
this.deps.navigate(data);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** The Settings body: scope counts, the project note, rows, and detail. */
|
|
198
|
-
render(width: number, header: string): string[] {
|
|
199
|
-
const { theme, surface, viewportRows, cwd, agentDir } = this.deps;
|
|
200
|
-
const scope = this.deps.scope();
|
|
201
|
-
const views = surface.views(scope);
|
|
202
|
-
const entries = settingEntries(views);
|
|
203
|
-
const focusedEntry = this.deps.retain(entries);
|
|
204
|
-
const focused = focusedEntry?.kind === "setting" ? focusedEntry.setting : undefined;
|
|
205
|
-
// A row with no section leaves the mode off, so moving away and back cannot
|
|
206
|
-
// reopen a pane the user closed.
|
|
207
|
-
if (focused && !focused.config) this.json = false;
|
|
208
|
-
const jsonSection = this.json ? focused?.config : undefined;
|
|
209
|
-
const paths = { scope, cwd, agentDir };
|
|
210
|
-
const detail = jsonSection
|
|
211
|
-
? sectionJsonPane({
|
|
212
|
-
theme,
|
|
213
|
-
width,
|
|
214
|
-
section: jsonSection.section,
|
|
215
|
-
document: surface.sectionDocument(scope, jsonSection.section),
|
|
216
|
-
budget: detailRows(true, viewportRows),
|
|
217
|
-
dirty: surface.isDirty(),
|
|
218
|
-
paths,
|
|
219
|
-
})
|
|
220
|
-
: settingDetail({
|
|
221
|
-
theme,
|
|
222
|
-
width,
|
|
223
|
-
view: focused,
|
|
224
|
-
budget: detailRows(false, viewportRows),
|
|
225
|
-
dirty: surface.isDirty(),
|
|
226
|
-
paths,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const { lines, noteShown } = panelFrame({
|
|
230
|
-
theme,
|
|
231
|
-
viewportRows,
|
|
232
|
-
cursor: this.deps.cursor,
|
|
233
|
-
width,
|
|
234
|
-
header,
|
|
235
|
-
summary: sectionBar(theme, width, "settings", settingsSummary(theme, width, scope, views.length)),
|
|
236
|
-
entries,
|
|
237
|
-
note: scope === "project" ? projectNote(theme, width) : undefined,
|
|
238
|
-
detail,
|
|
239
|
-
hint: this.deps.hint(width),
|
|
240
|
-
draw: (entry, index) => {
|
|
241
|
-
if (entry.kind === "group") return groupLine(theme, width, entry);
|
|
242
|
-
if (entry.kind !== "setting") return undefined;
|
|
243
|
-
const editing = this.editing?.id === entry.setting.id ? this.editing : undefined;
|
|
244
|
-
return settingRow(theme, width, entry.setting, index === this.deps.cursor.selected, editing);
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
this.deps.setNoteShown(noteShown);
|
|
248
|
-
return lines;
|
|
249
|
-
}
|
|
250
179
|
}
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Settings section
|
|
3
|
-
* detail pane that names the value's source.
|
|
2
|
+
* The Settings section's rows and the two words only it says.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* A row is a checkbox for a boolean, the label, and the value in a column of its
|
|
5
|
+
* own — the column is what makes twelve values scannable, and it is measured from
|
|
6
|
+
* the labels actually on screen rather than from the longest label in the
|
|
7
|
+
* catalogue, so a narrow terminal keeps the value instead of the padding.
|
|
8
|
+
*
|
|
9
|
+
* Pure: pure functions over the catalogue's views, theme passed in.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
import { join } from "node:path";
|
|
10
12
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
11
|
-
import { sliceByColumn, truncateToWidth, visibleWidth
|
|
13
|
+
import { sliceByColumn, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
12
14
|
import { sanitizeLine } from "../../../../lib/format.ts";
|
|
13
|
-
import { clip } from "../../../../lib/tui/columns.ts";
|
|
14
|
-
import { BODY_INDENT } from "../../../../lib/tui/indent.ts";
|
|
15
|
+
import { clip, padTo } from "../../../../lib/tui/columns.ts";
|
|
15
16
|
import { checkbox, listRow } from "../../../../lib/tui/rows.ts";
|
|
16
17
|
import { paint } from "../../../../lib/tui/tokens.ts";
|
|
17
|
-
import { settingsSummarySegments, type PanelScope } from "../format.ts";
|
|
18
18
|
import type { SettingView } from "../settings.ts";
|
|
19
|
-
import {
|
|
20
|
-
import type { WriteScope } from "../../picker/types.ts";
|
|
21
|
-
import { countsLine, INDENT, savedLine } from "./frame.ts";
|
|
22
|
-
|
|
23
|
-
/** Pi's personal settings file, named in the detail pane. */
|
|
24
|
-
const PI_SETTINGS_FILE = "settings.json";
|
|
19
|
+
import { INDENT } from "./frame.ts";
|
|
25
20
|
|
|
26
21
|
/** Columns the marker and the leading cell take before the label. */
|
|
27
22
|
const ROW_PREFIX_WIDTH = 4;
|
|
28
23
|
/** Columns a value keeps when the row has to choose between the label and the value. */
|
|
29
24
|
const MIN_VALUE_WIDTH = 8;
|
|
30
|
-
/** Columns between the label and its value. */
|
|
25
|
+
/** Columns between the label column and its value. */
|
|
31
26
|
const VALUE_GAP = 2;
|
|
32
27
|
|
|
33
28
|
/**
|
|
@@ -53,141 +48,45 @@ function editingCell(buffer: string, caret: number, budget: number): string {
|
|
|
53
48
|
return `${before}▏${after}`;
|
|
54
49
|
}
|
|
55
50
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The label column: the widest label on screen, capped so a value always keeps
|
|
53
|
+
* `MIN_VALUE_WIDTH` columns and a long label is clipped rather than the value
|
|
54
|
+
* pushed off the row.
|
|
55
|
+
*/
|
|
56
|
+
export function settingLabelColumn(views: readonly SettingView[], width: number): number {
|
|
57
|
+
const widest = views.reduce((columns, view) => Math.max(columns, visibleWidth(sanitizeLine(view.label))), 0);
|
|
58
|
+
const room = Math.max(0, width - ROW_PREFIX_WIDTH - VALUE_GAP - MIN_VALUE_WIDTH);
|
|
59
|
+
return Math.max(0, Math.min(widest, room));
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
/** One setting row: a checkbox for
|
|
62
|
+
/** One setting row: a checkbox for a boolean, the label, and the value in its column. */
|
|
62
63
|
export function settingRow(
|
|
63
64
|
theme: Theme,
|
|
64
65
|
width: number,
|
|
65
66
|
view: SettingView,
|
|
66
67
|
selected: boolean,
|
|
67
|
-
editing
|
|
68
|
+
editing: { buffer: string; caret: number } | undefined,
|
|
69
|
+
labelColumn: number,
|
|
68
70
|
): string {
|
|
69
71
|
const label = sanitizeLine(view.label);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const available = Math.max(1, width - ROW_PREFIX_WIDTH - VALUE_GAP);
|
|
73
|
-
const labelReserve = Math.min(visibleWidth(label), Math.max(0, available - MIN_VALUE_WIDTH));
|
|
74
|
-
const valueBudget = Math.max(1, available - labelReserve);
|
|
75
|
-
const rawValue = editing ? editingCell(editing.buffer, editing.caret, valueBudget) : view.valueLabel;
|
|
72
|
+
const available = Math.max(1, width - ROW_PREFIX_WIDTH - VALUE_GAP - labelColumn);
|
|
73
|
+
const rawValue = editing ? editingCell(editing.buffer, editing.caret, available) : view.valueLabel;
|
|
76
74
|
return listRow(theme, width, {
|
|
77
75
|
selected,
|
|
78
76
|
leading: view.kind === "boolean" ? checkbox(theme, view.value === "true") : " ",
|
|
79
|
-
label,
|
|
77
|
+
label: padTo(label, labelColumn),
|
|
80
78
|
labelRole: selected ? "accent" : "label",
|
|
81
|
-
trailing: clip(sanitizeLine(rawValue),
|
|
79
|
+
trailing: clip(sanitizeLine(rawValue), available),
|
|
82
80
|
trailingRole: editing ? "accent" : "meta",
|
|
83
|
-
// The value sits
|
|
84
|
-
//
|
|
81
|
+
// The value sits in its own column rather than at the far edge, so a wide
|
|
82
|
+
// terminal does not put the label and its value at opposite ends of the row.
|
|
85
83
|
align: "inline",
|
|
86
84
|
});
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
/** The project note: app rows are absent because Pi has no project setter. */
|
|
90
88
|
export function projectNote(theme: Theme, width: number): string {
|
|
91
|
-
return clip(`${INDENT}${paint(theme, "faint", "App settings are Global only.")}`, width);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Where the focused row's value is written, and which scope supplied it. */
|
|
95
|
-
function originLine(view: SettingView, paths: { scope: WriteScope; cwd: string; agentDir: string }): string {
|
|
96
|
-
const scope = view.origin === "inherited" ? "global" : paths.scope;
|
|
97
|
-
const path =
|
|
98
|
-
view.source === "app" ? join(paths.agentDir, PI_SETTINGS_FILE) : configPath(scope, paths.cwd, paths.agentDir);
|
|
99
|
-
const file = sanitizeLine(path);
|
|
100
|
-
switch (view.origin) {
|
|
101
|
-
case "inherited":
|
|
102
|
-
return `Inherited from Global · ${file}`;
|
|
103
|
-
case "project":
|
|
104
|
-
return `Project · ${file}`;
|
|
105
|
-
case "global":
|
|
106
|
-
return `Global · ${file}`;
|
|
107
|
-
default:
|
|
108
|
-
return `Default · ${file}`;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** The focused setting's source, description, origin, and pending changes. */
|
|
113
|
-
export function settingDetail(options: {
|
|
114
|
-
theme: Theme;
|
|
115
|
-
width: number;
|
|
116
|
-
view: SettingView | undefined;
|
|
117
|
-
budget: number;
|
|
118
|
-
dirty: boolean;
|
|
119
|
-
paths: { scope: WriteScope; cwd: string; agentDir: string };
|
|
120
|
-
}): string[] {
|
|
121
|
-
const { theme, width, view, budget, dirty, paths } = options;
|
|
122
|
-
if (!view) return savedLine(theme, width, dirty);
|
|
123
|
-
const source = view.source === "app" ? "Pi setting" : "bytefly.json";
|
|
124
|
-
const head = `${paint(theme, "meta", sanitizeLine(view.label))} ${paint(theme, "faint", "—")} ${paint(theme, "faint", source)}`;
|
|
125
|
-
const headLine = clip(`${INDENT}${head}`, width);
|
|
126
|
-
// The pending-changes marker survives the collapsed body: on a short terminal
|
|
127
|
-
// the value still changed, and the screen has to say so.
|
|
128
|
-
const saved = savedLine(theme, width, dirty);
|
|
129
|
-
if (budget === 0) return saved;
|
|
130
|
-
|
|
131
|
-
const lines = [headLine];
|
|
132
|
-
const descriptionWidth = Math.max(1, width - BODY_INDENT * 2);
|
|
133
|
-
const description = wrapTextWithAnsi(sanitizeLine(view.description), descriptionWidth).slice(
|
|
134
|
-
0,
|
|
135
|
-
Math.max(0, Math.min(2, budget - 2 - saved.length)),
|
|
136
|
-
);
|
|
137
|
-
for (const line of description) lines.push(clip(`${INDENT}${paint(theme, "meta", line)}`, width));
|
|
138
|
-
lines.push(clip(`${INDENT}${paint(theme, "faint", originLine(view, paths))}`, width));
|
|
139
|
-
lines.push(...saved);
|
|
140
|
-
return lines;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** The section's key hints, in the order they drop as the width runs out. */
|
|
144
|
-
export function settingsHints(json = false): string[] {
|
|
145
|
-
return ["Esc close", "←/→", "Space change", "Tab scope", "u reset", json ? "J hide" : "J JSON", "↑/↓ select"];
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* The focused row's section as the active scope stores it.
|
|
150
|
-
*
|
|
151
|
-
* This is the only view of the keys the catalogue leaves to JSON, so it names the
|
|
152
|
-
* section and its file rather than pretending to be complete: the pane is clipped
|
|
153
|
-
* to the detail budget and says how many lines it left out. `section` is the row's
|
|
154
|
-
* `bytefly.json` section, which the caller has already established.
|
|
155
|
-
*/
|
|
156
|
-
export function sectionJsonPane(options: {
|
|
157
|
-
theme: Theme;
|
|
158
|
-
width: number;
|
|
159
|
-
section: string;
|
|
160
|
-
document: unknown;
|
|
161
|
-
budget: number;
|
|
162
|
-
dirty: boolean;
|
|
163
|
-
paths: { scope: WriteScope; cwd: string; agentDir: string };
|
|
164
|
-
}): string[] {
|
|
165
|
-
const { theme, width, section, document, budget, dirty, paths } = options;
|
|
166
|
-
const saved = savedLine(theme, width, dirty);
|
|
167
|
-
if (budget === 0) return saved;
|
|
168
|
-
const scope = paths.scope === "global" ? "Global" : "Project";
|
|
169
|
-
const file = sanitizeLine(configPath(paths.scope, paths.cwd, paths.agentDir));
|
|
170
|
-
const source = paint(theme, "faint", `${scope} · ${file}`);
|
|
171
|
-
const head = `${paint(theme, "meta", sanitizeLine(section))} ${paint(theme, "faint", "—")} ${source}`;
|
|
172
|
-
const lines = [clip(`${INDENT}${head}`, width)];
|
|
173
|
-
const room = Math.max(0, budget - lines.length - saved.length);
|
|
174
|
-
const body = document === undefined ? [] : JSON.stringify(document, null, 2).split("\n");
|
|
175
|
-
|
|
176
|
-
if (body.length === 0) {
|
|
177
|
-
if (room > 0) lines.push(clip(`${INDENT}${paint(theme, "faint", "Not set in this scope.")}`, width));
|
|
178
|
-
lines.push(...saved);
|
|
179
|
-
return lines;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const shown = body.slice(0, room);
|
|
183
|
-
if (body.length > room && shown.length > 0) {
|
|
184
|
-
// The marker replaces one already-shown line, so the hidden count is one
|
|
185
|
-
// larger than the number of lines the slice left out.
|
|
186
|
-
shown[shown.length - 1] = `… ${body.length - room + 1} more lines`;
|
|
187
|
-
}
|
|
188
|
-
for (const line of shown) lines.push(clip(`${INDENT}${paint(theme, "faint", sanitizeLine(line))}`, width));
|
|
189
|
-
lines.push(...saved);
|
|
190
|
-
return lines;
|
|
89
|
+
return clip(`${INDENT}${paint(theme, "faint", "App settings are Global only.")}`, Math.max(1, width));
|
|
191
90
|
}
|
|
192
91
|
|
|
193
92
|
/** The editor's key hints: `Esc` cancels the edit, it does not close the screen. */
|