@xingular/harness 1.1.1 → 1.3.1-unstable.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 +48 -0
- package/README.md +36 -28
- package/dist/cli.js +10837 -6159
- package/dist/sdk/accounts/accounts.d.ts +27 -3
- package/dist/sdk/accounts/accounts.js +78 -24
- package/dist/sdk/accounts/secrets.d.ts +12 -0
- package/dist/sdk/accounts/secrets.js +114 -7
- package/dist/sdk/accounts/store.d.ts +26 -1
- package/dist/sdk/accounts/store.js +1 -1
- package/dist/sdk/agents/builtin.js +18 -4
- package/dist/sdk/agents/planResearch.d.ts +27 -0
- package/dist/sdk/agents/planResearch.js +82 -0
- package/dist/sdk/agents/profiles.d.ts +5 -1
- package/dist/sdk/agents/profiles.js +21 -5
- package/dist/sdk/annotations/pageScript.js +349 -48
- package/dist/sdk/annotations/part.js +34 -23
- package/dist/sdk/annotations/store.d.ts +7 -0
- package/dist/sdk/annotations/store.js +13 -5
- package/dist/sdk/browser/backend.d.ts +16 -0
- package/dist/sdk/browser/backend.js +17 -0
- package/dist/sdk/browser/contracts.d.ts +19 -0
- package/dist/sdk/browser/contracts.js +116 -0
- package/dist/sdk/browser/tools.d.ts +1 -4
- package/dist/sdk/browser/tools.js +14 -91
- package/dist/sdk/capabilities/runSnapshot.js +10 -4
- package/dist/sdk/cli.js +5 -4
- package/dist/sdk/commands/list.d.ts +3 -0
- package/dist/sdk/commands/list.js +75 -0
- package/dist/sdk/commands/registry.d.ts +258 -0
- package/dist/sdk/commands/registry.js +48 -0
- package/dist/sdk/commands/routes.d.ts +16 -0
- package/dist/sdk/commands/routes.js +33 -0
- package/dist/sdk/core/commandBudget.d.ts +23 -0
- package/dist/sdk/core/commandBudget.js +57 -0
- package/dist/sdk/core/index.js +17 -1
- package/dist/sdk/core/interactiveProfile.d.ts +46 -0
- package/dist/sdk/core/interactiveProfile.js +143 -0
- package/dist/sdk/core/shell.d.ts +20 -0
- package/dist/sdk/core/shell.js +73 -0
- package/dist/sdk/core/workspace.d.ts +13 -0
- package/dist/sdk/core/workspace.js +88 -0
- package/dist/sdk/core/workspaceBashTool.d.ts +20 -0
- package/dist/sdk/core/workspaceBashTool.js +50 -0
- package/dist/sdk/core/workspaceSandbox.d.ts +41 -0
- package/dist/sdk/core/workspaceSandbox.js +360 -0
- package/dist/sdk/core/workspaceWriteOperations.d.ts +33 -0
- package/dist/sdk/core/workspaceWriteOperations.js +224 -0
- package/dist/sdk/doctor/checks.js +71 -12
- package/dist/sdk/doctor/types.d.ts +2 -2
- package/dist/sdk/harness.d.ts +7 -0
- package/dist/sdk/harness.js +42 -2
- package/dist/sdk/hub/hub.js +5 -3
- package/dist/sdk/index.d.ts +8 -3
- package/dist/sdk/index.js +4 -1
- package/dist/sdk/pi/runtime.d.ts +2 -1
- package/dist/sdk/pi/runtime.js +2 -2
- package/dist/sdk/plugins/builtin.js +32 -15
- package/dist/sdk/plugins/builtinRegistry.d.ts +10 -3
- package/dist/sdk/plugins/builtinRegistry.js +30 -8
- package/dist/sdk/plugins/catalogue.js +2 -1
- package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
- package/dist/sdk/plugins/commandPrefix.js +21 -0
- package/dist/sdk/plugins/firstParty.d.ts +1 -1
- package/dist/sdk/plugins/firstParty.js +8 -2
- package/dist/sdk/plugins/install.d.ts +6 -2
- package/dist/sdk/plugins/install.js +29 -5
- package/dist/sdk/plugins/inventory.js +6 -5
- package/dist/sdk/plugins/manifest.d.ts +2 -0
- package/dist/sdk/plugins/manifest.js +2 -1
- package/dist/sdk/plugins/surfaces.d.ts +5 -0
- package/dist/sdk/plugins/surfaces.js +11 -0
- package/dist/sdk/plugins/types.d.ts +15 -0
- package/dist/sdk/prompt/compile.d.ts +14 -0
- package/dist/sdk/prompt/compile.js +65 -31
- package/dist/sdk/prompt/standard.d.ts +17 -0
- package/dist/sdk/prompt/standard.js +21 -0
- package/dist/sdk/server/methods.js +22 -2
- package/dist/sdk/server/remote.js +36 -11
- package/dist/sdk/sessions/compactionCheckpoint.d.ts +11 -0
- package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
- package/dist/sdk/sessions/continuation.d.ts +7 -2
- package/dist/sdk/sessions/continuation.js +11 -6
- package/dist/sdk/sessions/contracts.d.ts +190 -4
- package/dist/sdk/sessions/contracts.js +71 -8
- package/dist/sdk/sessions/control.d.ts +72 -0
- package/dist/sdk/sessions/control.js +158 -0
- package/dist/sdk/sessions/initialBash.d.ts +7 -0
- package/dist/sdk/sessions/initialBash.js +7 -0
- package/dist/sdk/sessions/planMode.d.ts +14 -3
- package/dist/sdk/sessions/planMode.js +74 -17
- package/dist/sdk/sessions/registry.d.ts +1 -1
- package/dist/sdk/sessions/registry.js +2 -2
- package/dist/sdk/sessions/runStore.d.ts +14 -0
- package/dist/sdk/sessions/runStore.js +38 -1
- package/dist/sdk/sessions/runner.d.ts +40 -2
- package/dist/sdk/sessions/runner.js +161 -13
- package/dist/sdk/sessions/sessions.d.ts +23 -3
- package/dist/sdk/sessions/sessions.js +225 -22
- package/dist/sdk/sessions/subagentService.d.ts +24 -0
- package/dist/sdk/sessions/subagentService.js +225 -26
- package/dist/sdk/sessions/subagents.d.ts +10 -1
- package/dist/sdk/sessions/subagents.js +4 -0
- package/dist/sdk/sessions/workSlice.d.ts +106 -0
- package/dist/sdk/sessions/workSlice.js +329 -0
- package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
- package/dist/sdk/sessions/workspaceBoundary.js +46 -0
- package/dist/sdk/terminal/browserControl.js +2 -1
- package/dist/sdk/terminal/controller.d.ts +31 -3
- package/dist/sdk/terminal/controller.js +81 -29
- package/dist/sdk/terminal/opentui/ansi.d.ts +25 -0
- package/dist/sdk/terminal/opentui/ansi.js +192 -0
- package/dist/sdk/terminal/opentui/model.d.ts +163 -0
- package/dist/sdk/terminal/opentui/model.js +482 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +3 -3
- package/dist/sdk/terminal/opentui/subagentsWidget.js +7 -5
- package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
- package/dist/sdk/terminal/opentui/theme.js +88 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -1
- package/dist/sdk/terminal/piExtension.js +177 -63
- package/dist/sdk/terminal/standalone.d.ts +7 -0
- package/dist/sdk/terminal/standalone.js +72 -13
- package/dist/sdk/terminal/subagentMonitor.d.ts +28 -6
- package/dist/sdk/terminal/subagentMonitor.js +61 -9
- package/dist/sdk/terminal/terminals.d.ts +26 -2
- package/dist/sdk/terminal/terminals.js +325 -43
- package/dist/sdk/terminal/trajectory.d.ts +6 -1
- package/dist/sdk/terminal/trajectory.js +8 -0
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +1 -1
- package/dist/sdk/terminal/tui/annotateCommand.js +3 -3
- package/dist/sdk/terminal/tui/chrome.d.ts +4 -0
- package/dist/sdk/terminal/tui/chrome.js +13 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +3 -1
- package/dist/sdk/terminal/tui/commandPages.js +17 -4
- package/dist/sdk/terminal/tui/commands.d.ts +6 -0
- package/dist/sdk/terminal/tui/commands.js +213 -124
- package/dist/sdk/terminal/tui/list.d.ts +14 -1
- package/dist/sdk/terminal/tui/list.js +96 -14
- package/dist/sdk/terminal/tui/pageOverlay.js +21 -5
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +1 -1
- package/dist/sdk/terminal/tui/pages/browserPage.js +5 -4
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +14 -1
- package/dist/sdk/terminal/tui/pages/hubPage.js +192 -28
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +5 -5
- package/dist/sdk/terminal/tui/pages/notesPage.js +18 -18
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +8 -6
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +206 -28
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +1 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +104 -19
- package/dist/sdk/terminal/tui/pages/usagePage.d.ts +43 -0
- package/dist/sdk/terminal/tui/pages/usagePage.js +370 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +1 -2
- package/dist/sdk/terminal/tui/piAdapter.js +3 -5
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +13 -9
- package/dist/sdk/terminal/tui/runtime.js +23 -3
- package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
- package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
- package/dist/sdk/tools/checkpoint.d.ts +27 -0
- package/dist/sdk/tools/checkpoint.js +154 -0
- package/dist/sdk/tools/getCapabilities.d.ts +31 -0
- package/dist/sdk/tools/getCapabilities.js +34 -0
- package/dist/sdk/tools/sessionName.d.ts +2 -0
- package/dist/sdk/tools/sessionName.js +5 -3
- package/dist/sdk/tools/spawnSubagent.js +11 -8
- package/dist/sdk/tools/todo.d.ts +15 -0
- package/dist/sdk/tools/todo.js +64 -0
- package/dist/sdk/usage/analytics.d.ts +20 -0
- package/dist/sdk/usage/analytics.js +151 -0
- package/dist/sdk/usage/ledger.d.ts +38 -0
- package/dist/sdk/usage/ledger.js +142 -0
- package/dist/sdk/usage/limits/adapters.d.ts +11 -0
- package/dist/sdk/usage/limits/adapters.js +349 -0
- package/dist/sdk/usage/limits/capture.d.ts +8 -0
- package/dist/sdk/usage/limits/capture.js +47 -0
- package/dist/sdk/usage/limits/http.d.ts +12 -0
- package/dist/sdk/usage/limits/http.js +51 -0
- package/dist/sdk/usage/limits/normalize.d.ts +15 -0
- package/dist/sdk/usage/limits/normalize.js +211 -0
- package/dist/sdk/usage/limits/service.d.ts +30 -0
- package/dist/sdk/usage/limits/service.js +176 -0
- package/dist/sdk/usage/limits/types.d.ts +65 -0
- package/dist/sdk/usage/limits/types.js +1 -0
- package/dist/sdk/usage/serverMethods.d.ts +3 -0
- package/dist/sdk/usage/serverMethods.js +22 -0
- package/dist/sdk/usage/service.d.ts +32 -0
- package/dist/sdk/usage/service.js +66 -0
- package/dist/sdk/usage/settings.d.ts +7 -0
- package/dist/sdk/usage/settings.js +28 -0
- package/dist/sdk/usage/types.d.ts +60 -0
- package/dist/sdk/usage/types.js +4 -0
- package/dist/terminal-opentui.js +9204 -4812
- package/dist/terminal-pi.js +1848 -272
- package/docs/architecture/00-overview.md +1 -1
- package/docs/architecture/01-extension-points.md +1 -1
- package/docs/architecture/02-sessions-and-runs.md +169 -36
- package/docs/architecture/04-prompt.md +55 -15
- package/docs/architecture/06-plugins-and-capabilities.md +36 -15
- package/docs/architecture/07-tools-and-gate.md +131 -25
- package/docs/architecture/08-memory-and-history.md +4 -2
- package/docs/architecture/09-accounts-and-connectors.md +39 -3
- package/docs/architecture/10-server-protocol.md +71 -9
- package/docs/architecture/11-terminal.md +148 -60
- package/docs/architecture/12-storage.md +30 -2
- package/docs/architecture/13-world-tools.md +22 -1
- package/docs/architecture/14-surfaces-and-parity.md +58 -8
- package/docs/architecture/README.md +20 -17
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +2 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +6 -2
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +1 -1
- package/docs/architecture/proposals/PROP-0031-plugin-command-owner-namespaces.md +54 -0
- package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
- package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
- package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
- package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
- package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
- package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
- package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
- package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
- package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
- package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +258 -0
- package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
- package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
- package/docs/architecture/proposals/PROP-0044-work-slice-defaults.md +93 -0
- package/docs/architecture/proposals/PROP-0045-rename-checklist-to-todo.md +132 -0
- package/docs/architecture/proposals/PROP-0046-progress-aware-stall-guards.md +82 -0
- package/docs/architecture/proposals/PROP-0047-plugins-hub-separation.md +122 -0
- package/docs/architecture/proposals/README.md +24 -6
- package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
- package/docs/decisions/ADR-0021-required-command-namespaces.md +1 -1
- package/docs/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
- package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +33 -0
- package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
- package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
- package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
- package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
- package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
- package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
- package/docs/decisions/ADR-0039-progress-aware-stall-guards.md +39 -0
- package/docs/decisions/ADR-0040-architecture-change-workflow.md +31 -0
- package/docs/decisions/ADR-0041-plugins-hub-separation.md +41 -0
- package/docs/decisions/ADR-0042-windows-shell-without-sandbox.md +40 -0
- package/docs/decisions/ADR-0043-parents-continue-stopped-subagents.md +49 -0
- package/docs/doctor.md +14 -1
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
- package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
- package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
- package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
- package/docs/parity.md +83 -27
- package/docs/releases/1.1.1.md +28 -0
- package/docs/releases/1.2.0.md +49 -0
- package/docs/releases/1.3.0.md +21 -0
- package/docs/releases/4.0.0.md +10 -9
- package/docs/releases/README.md +35 -30
- package/docs/study/00-index.md +3 -0
- package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
- package/docs/terminal-tui.md +162 -82
- package/package.json +4 -3
- package/dist/sdk/tools/checklist.d.ts +0 -9
- package/dist/sdk/tools/checklist.js +0 -54
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { truncateToWidth } from '@earendil-works/pi-tui';
|
|
2
2
|
import { FilterList } from "../list.js";
|
|
3
|
-
const TABS = [{ id: 'notes', title: '
|
|
3
|
+
const TABS = [{ id: 'notes', title: 'Web annotations' }];
|
|
4
4
|
export const NOTE_KINDS = { page: 'The whole page', text: 'Text I select in the window', element: 'An element I click in the window' };
|
|
5
5
|
export const NOTE_ACTIONS = {
|
|
6
6
|
reveal: 'Show it in the browser window',
|
|
7
|
-
edit: 'Edit the
|
|
7
|
+
edit: 'Edit the annotation',
|
|
8
8
|
resolve: 'Mark resolved',
|
|
9
9
|
reopen: 'Reopen',
|
|
10
10
|
share: 'Let this session\'s agent read it',
|
|
@@ -31,7 +31,7 @@ export class NotesPage {
|
|
|
31
31
|
constructor(options) {
|
|
32
32
|
this.options = options;
|
|
33
33
|
this.list = new FilterList({
|
|
34
|
-
theme: options.theme, search: 'always', noun: ['
|
|
34
|
+
theme: options.theme, search: 'always', noun: ['annotation', 'annotations'], empty: 'No web annotations yet',
|
|
35
35
|
onActivate: row => { if (row.data)
|
|
36
36
|
void this.activateRow(row.data); }
|
|
37
37
|
});
|
|
@@ -67,10 +67,10 @@ export class NotesPage {
|
|
|
67
67
|
return note.grants.some(grant => grant.principal.sessionId && grant.principal.sessionId === this.sessionId);
|
|
68
68
|
}
|
|
69
69
|
rows() {
|
|
70
|
-
const rows = [{ id: 'new', label: 'New
|
|
70
|
+
const rows = [{ id: 'new', label: 'New web annotation…', group: 'Actions', detail: 'opens a browser window', tone: 'accent', data: { kind: 'new' } }];
|
|
71
71
|
for (const note of this.notes ?? []) {
|
|
72
72
|
rows.push({
|
|
73
|
-
id: `note:${note.id}`, label: clip(note.body.replace(/\s+/g, ' '), 60), group: '
|
|
73
|
+
id: `note:${note.id}`, label: clip(note.body.replace(/\s+/g, ' '), 60), group: 'Annotations',
|
|
74
74
|
detail: `${describeTarget(note.target)} · ${note.status}${this.shared(note) ? ' · shared here' : ''}`,
|
|
75
75
|
tone: note.status === 'resolved' ? 'dim' : this.shared(note) ? 'success' : 'muted',
|
|
76
76
|
searchText: `${note.body} ${note.source.url} ${note.source.title} ${note.tags.join(' ')}`,
|
|
@@ -101,7 +101,7 @@ export class NotesPage {
|
|
|
101
101
|
return this.newNote();
|
|
102
102
|
return this.noteMenu(row.note);
|
|
103
103
|
}
|
|
104
|
-
/** Opens the capture window, takes what the person points at, and saves their
|
|
104
|
+
/** Opens the capture window, takes what the person points at, and saves their annotation. */
|
|
105
105
|
newNote(startUrl) {
|
|
106
106
|
const { call, ui } = this.options;
|
|
107
107
|
return this.attempt(async () => {
|
|
@@ -109,7 +109,7 @@ export class NotesPage {
|
|
|
109
109
|
if (!url)
|
|
110
110
|
return;
|
|
111
111
|
await call('terminal.annotations.open', { url });
|
|
112
|
-
const kind = await ui.select('What does the
|
|
112
|
+
const kind = await ui.select('What does the annotation point at?', Object.values(NOTE_KINDS));
|
|
113
113
|
if (!kind)
|
|
114
114
|
return;
|
|
115
115
|
let envelope;
|
|
@@ -127,18 +127,18 @@ export class NotesPage {
|
|
|
127
127
|
}
|
|
128
128
|
envelope = await call('terminal.annotations.capture', { captureId });
|
|
129
129
|
if (!envelope)
|
|
130
|
-
return 'Nothing was selected in the browser window, so no
|
|
130
|
+
return 'Nothing was selected in the browser window, so no annotation was made.';
|
|
131
131
|
}
|
|
132
|
-
const body = await ui.input('
|
|
132
|
+
const body = await ui.input('Comment', 'What should change or be remembered?');
|
|
133
133
|
if (!body?.trim())
|
|
134
|
-
return '
|
|
134
|
+
return 'An annotation needs a comment; nothing was saved.';
|
|
135
135
|
const tags = (await ui.input('Tags (optional, comma separated)', '') ?? '').split(',').map(tag => tag.trim()).filter(Boolean);
|
|
136
|
-
const share = await ui.confirm('Share it with this session?', 'This session\'s agent can then read the
|
|
136
|
+
const share = await ui.confirm('Share it with this session?', 'This session\'s agent can then read the annotation with page_notes_read. You can stop sharing it later.');
|
|
137
137
|
await call('terminal.annotations.create', {
|
|
138
138
|
source: { url: envelope.url, title: envelope.title, capturedAt: new Date().toISOString(), surface: 'terminal', representationHint: envelope.documentId },
|
|
139
139
|
target: envelope.target, body, tags, share
|
|
140
140
|
});
|
|
141
|
-
return share ? '
|
|
141
|
+
return share ? 'Annotation saved and shared with this session.' : 'Annotation saved. It stays private until you share it.';
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
async noteMenu(note) {
|
|
@@ -163,7 +163,7 @@ export class NotesPage {
|
|
|
163
163
|
if (!body?.trim() || body === note.body)
|
|
164
164
|
return;
|
|
165
165
|
await call('terminal.annotations.update', { id, expectedVersion: note.version, patch: { body } });
|
|
166
|
-
return '
|
|
166
|
+
return 'Annotation saved.';
|
|
167
167
|
});
|
|
168
168
|
if (choice === NOTE_ACTIONS.resolve || choice === NOTE_ACTIONS.reopen)
|
|
169
169
|
return this.attempt(async () => {
|
|
@@ -172,14 +172,14 @@ export class NotesPage {
|
|
|
172
172
|
if (choice === NOTE_ACTIONS.share || choice === NOTE_ACTIONS.unshare)
|
|
173
173
|
return this.attempt(async () => {
|
|
174
174
|
await call(choice === NOTE_ACTIONS.share ? 'terminal.annotations.share' : 'terminal.annotations.unshare', { id });
|
|
175
|
-
return choice === NOTE_ACTIONS.share ? 'This session\'s agent can read the
|
|
175
|
+
return choice === NOTE_ACTIONS.share ? 'This session\'s agent can read the annotation.' : 'The annotation is no longer shared with this session.';
|
|
176
176
|
});
|
|
177
177
|
if (choice === NOTE_ACTIONS.delete)
|
|
178
178
|
return this.attempt(async () => {
|
|
179
|
-
if (!await ui.confirm('Delete this
|
|
179
|
+
if (!await ui.confirm('Delete this annotation?', 'It cannot be restored.'))
|
|
180
180
|
return;
|
|
181
181
|
await call('terminal.annotations.delete', { id, expectedVersion: note.version });
|
|
182
|
-
return '
|
|
182
|
+
return 'Annotation deleted.';
|
|
183
183
|
});
|
|
184
184
|
}
|
|
185
185
|
handleMouse(event) {
|
|
@@ -193,9 +193,9 @@ export class NotesPage {
|
|
|
193
193
|
if (width < 1 || rows < 1)
|
|
194
194
|
return [];
|
|
195
195
|
if (this.error)
|
|
196
|
-
return [theme.fg('error', `Could not read
|
|
196
|
+
return [theme.fg('error', `Could not read web annotations: ${this.error}`)];
|
|
197
197
|
if (!this.notes)
|
|
198
|
-
return [theme.fg('dim', 'Reading
|
|
198
|
+
return [theme.fg('dim', 'Reading web annotations…')];
|
|
199
199
|
return this.list.render(width, rows).slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
200
200
|
}
|
|
201
201
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Plugins page: what this session loads, switched on or off, with trust for plugin code.
|
|
3
|
-
* Enter opens what can be done with a row; nothing changes without a choice made here. A plugin
|
|
4
|
-
* with settings has a Settings row that opens them as a subpage; Esc goes back to the list.
|
|
5
|
-
*/
|
|
6
1
|
import type { Theme } from '@earendil-works/pi-coding-agent';
|
|
7
2
|
import { type MenuCall, type MenuUi } from "../../pluginMenu.js";
|
|
8
3
|
import type { TabInfo } from "../chrome.js";
|
|
@@ -18,14 +13,17 @@ export interface PluginsPageOptions {
|
|
|
18
13
|
}
|
|
19
14
|
export declare class PluginsPage implements TerminalPage {
|
|
20
15
|
private readonly options;
|
|
16
|
+
readonly presentation = "fullscreen";
|
|
21
17
|
private plugins;
|
|
22
18
|
private section;
|
|
23
19
|
private projectId;
|
|
20
|
+
private hubManagedIds;
|
|
24
21
|
private error;
|
|
25
22
|
private restart;
|
|
26
23
|
private busy;
|
|
27
24
|
private disposed;
|
|
28
25
|
private readonly list;
|
|
26
|
+
private pointerLayout?;
|
|
29
27
|
/** The plugin whose settings are open, or null on the list. */
|
|
30
28
|
private settingsFor;
|
|
31
29
|
private saved;
|
|
@@ -41,17 +39,21 @@ export declare class PluginsPage implements TerminalPage {
|
|
|
41
39
|
private settingsPlugin;
|
|
42
40
|
private reload;
|
|
43
41
|
private fill;
|
|
44
|
-
/**
|
|
42
|
+
/** Switch, trust, settings and package actions stay grouped under their owning plugin. */
|
|
45
43
|
private rows;
|
|
46
44
|
private menu;
|
|
47
45
|
private key;
|
|
48
46
|
private toRow;
|
|
49
47
|
private open;
|
|
48
|
+
private installPackage;
|
|
49
|
+
private managePackage;
|
|
50
50
|
private back;
|
|
51
51
|
/** Flips a switch, or asks for a number inside the setting's range, and saves it. */
|
|
52
52
|
private change;
|
|
53
53
|
handleMouse(event: TerminalMouseInput): import("@earendil-works/pi-tui").TuiMouseEventResult | undefined;
|
|
54
54
|
handleInput(data: string): boolean;
|
|
55
|
+
private selectedPlugin;
|
|
56
|
+
private pluginInspector;
|
|
55
57
|
render(width: number, rows: number): string[];
|
|
56
58
|
private renderSettings;
|
|
57
59
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Plugins page: what this session loads, switched on or off, with trust for plugin code.
|
|
3
|
+
* Enter opens what can be done with a row; nothing changes without a choice made here. A plugin
|
|
4
|
+
* with settings has a Settings row that opens them as a subpage; Esc goes back to the list.
|
|
5
|
+
*/
|
|
6
|
+
import { describePlugin, surfaceBadge } from "../../../plugins/surfaces.js";
|
|
1
7
|
import { matchesKey, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
2
8
|
import { BACK, applyPluginAction, menuRows, rowActions } from "../../pluginMenu.js";
|
|
3
|
-
import { safeText } from "../chrome.js";
|
|
9
|
+
import { inspectorListWidth, safeText, splitColumns } from "../chrome.js";
|
|
4
10
|
import { FilterList } from "../list.js";
|
|
5
11
|
const SECTIONS = [
|
|
6
12
|
{ id: 'plugins', title: 'Plugins' },
|
|
7
|
-
{ id: 'trust', title: 'Trust' }
|
|
13
|
+
{ id: 'trust', title: 'Trust' },
|
|
14
|
+
{ id: 'packages', title: 'Packages' }
|
|
8
15
|
];
|
|
9
16
|
const SETTINGS_ROW = 'settings:';
|
|
17
|
+
const PACKAGE_ROW = 'package:';
|
|
18
|
+
const INSTALL_PACKAGE = 'action:install-package';
|
|
10
19
|
const isSwitch = (setting) => setting.min === 0 && setting.max === 1;
|
|
11
20
|
const settingValueText = (setting) => isSwitch(setting)
|
|
12
21
|
? setting.value === 1 ? 'on' : 'off'
|
|
@@ -14,14 +23,17 @@ const settingValueText = (setting) => isSwitch(setting)
|
|
|
14
23
|
const settingsOf = (plugin) => plugin.settings ?? [];
|
|
15
24
|
export class PluginsPage {
|
|
16
25
|
options;
|
|
26
|
+
presentation = 'fullscreen';
|
|
17
27
|
plugins;
|
|
18
28
|
section = 'plugins';
|
|
19
29
|
projectId = null;
|
|
30
|
+
hubManagedIds = new Set();
|
|
20
31
|
error;
|
|
21
32
|
restart = false;
|
|
22
33
|
busy = false;
|
|
23
34
|
disposed = false;
|
|
24
35
|
list;
|
|
36
|
+
pointerLayout;
|
|
25
37
|
/** The plugin whose settings are open, or null on the list. */
|
|
26
38
|
settingsFor = null;
|
|
27
39
|
saved = false;
|
|
@@ -64,6 +76,12 @@ export class PluginsPage {
|
|
|
64
76
|
this.projectId = (await call('plugins.session').catch(() => ({ projectId: null }))).projectId;
|
|
65
77
|
this.plugins = await call('plugins.list');
|
|
66
78
|
this.error = undefined;
|
|
79
|
+
this.hubManagedIds = new Set();
|
|
80
|
+
try {
|
|
81
|
+
const overview = await call('hub.list');
|
|
82
|
+
this.hubManagedIds = new Set(overview.installed.filter(receipt => receipt.entry.delivery.kind === 'pi-package').flatMap(receipt => receipt.resourceIds));
|
|
83
|
+
}
|
|
84
|
+
catch { /* Package actions still go through the receipt-aware Harness service. */ }
|
|
67
85
|
}
|
|
68
86
|
catch (error) {
|
|
69
87
|
this.error = error instanceof Error ? error.message : String(error);
|
|
@@ -85,8 +103,17 @@ export class PluginsPage {
|
|
|
85
103
|
searchText: `${setting.label} ${setting.description}`
|
|
86
104
|
})));
|
|
87
105
|
}
|
|
88
|
-
/**
|
|
106
|
+
/** Switch, trust, settings and package actions stay grouped under their owning plugin. */
|
|
89
107
|
rows() {
|
|
108
|
+
if (this.section === 'packages')
|
|
109
|
+
return [
|
|
110
|
+
...(this.plugins ?? []).filter(plugin => plugin.install).map(plugin => {
|
|
111
|
+
const source = plugin.install.kind === 'folder' ? 'local folder' : 'package';
|
|
112
|
+
const hub = this.hubManagedIds.has(plugin.id);
|
|
113
|
+
return { id: `${PACKAGE_ROW}${plugin.id}`, group: safeText(plugin.title), label: 'Manage package', detail: `${source} · ${hub ? 'Hub' : 'local'} · ${plugin.version ?? plugin.install.source}`, tone: 'muted', searchText: `${plugin.title} ${plugin.install.source} ${plugin.version ?? ''} package ${hub ? 'Hub' : 'local'}` };
|
|
114
|
+
}),
|
|
115
|
+
{ id: INSTALL_PACKAGE, group: 'Packages', label: 'Install package…', detail: 'Folder, npm or Git', tone: 'accent', searchText: 'install add package plugin npm Git folder' }
|
|
116
|
+
];
|
|
90
117
|
const menu = this.menu();
|
|
91
118
|
const out = [];
|
|
92
119
|
const placed = new Set();
|
|
@@ -101,25 +128,26 @@ export class PluginsPage {
|
|
|
101
128
|
if (this.section === 'plugins' && last && settingsOf(row.plugin).length)
|
|
102
129
|
out.push(settingsRow(row.plugin));
|
|
103
130
|
});
|
|
104
|
-
if (this.section === 'plugins')
|
|
131
|
+
if (this.section === 'plugins')
|
|
105
132
|
for (const plugin of this.plugins ?? [])
|
|
106
133
|
if (settingsOf(plugin).length && !placed.has(plugin.id))
|
|
107
134
|
out.push(settingsRow(plugin));
|
|
108
|
-
}
|
|
109
135
|
return out;
|
|
110
136
|
}
|
|
111
137
|
menu() {
|
|
112
138
|
const rows = menuRows(this.plugins ?? [], this.projectId);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
if (this.section === 'trust')
|
|
140
|
+
return rows.filter(row => row.whole && Boolean(row.plugin.code));
|
|
141
|
+
if (this.section === 'packages')
|
|
142
|
+
return [];
|
|
143
|
+
return rows.filter(row => rowActions(row, this.projectId).some(action => action.startsWith('Switch ')));
|
|
116
144
|
}
|
|
117
145
|
key(row) { return `${row.whole ? 'plugin' : 'item'}:${row.id}`; }
|
|
118
146
|
toRow(row) {
|
|
119
147
|
const kind = row.whole ? row.note : row.note.replace(/ in .*$/, '');
|
|
120
148
|
return {
|
|
121
149
|
id: this.key(row), group: safeText(row.plugin.title), label: row.whole ? 'Whole plugin' : safeText(row.name),
|
|
122
|
-
detail: `${row.on ? 'on' : 'off'}${kind ? ` · ${kind}` : ''}`, tone: row.on ? 'success' : 'dim',
|
|
150
|
+
detail: `${row.on ? 'on' : 'off'}${kind ? ` · ${kind}` : ''}${row.whole && surfaceBadge(row.plugin.surfaces) ? ` · ${surfaceBadge(row.plugin.surfaces)}` : ''}`, tone: row.on ? 'success' : 'dim',
|
|
123
151
|
searchText: `${row.name} ${row.plugin.title} ${row.note}`
|
|
124
152
|
};
|
|
125
153
|
}
|
|
@@ -131,12 +159,22 @@ export class PluginsPage {
|
|
|
131
159
|
this.options.changed();
|
|
132
160
|
return;
|
|
133
161
|
}
|
|
162
|
+
if (id === INSTALL_PACKAGE) {
|
|
163
|
+
await this.installPackage();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (id.startsWith(PACKAGE_ROW)) {
|
|
167
|
+
const plugin = this.plugins?.find(candidate => candidate.id === id.slice(PACKAGE_ROW.length));
|
|
168
|
+
if (plugin?.install)
|
|
169
|
+
await this.managePackage(plugin);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
134
172
|
const row = this.menu().find(candidate => this.key(candidate) === id);
|
|
135
173
|
if (!row || this.busy)
|
|
136
174
|
return;
|
|
137
175
|
this.busy = true;
|
|
138
176
|
try {
|
|
139
|
-
const action = await this.options.ui.select(`${row.plugin.title}: ${row.plugin
|
|
177
|
+
const action = await this.options.ui.select(`${row.plugin.title}: ${describePlugin(row.plugin)}`, rowActions(row, this.projectId));
|
|
140
178
|
if (action && action !== BACK && await applyPluginAction(this.options.ui, this.options.call, row, action))
|
|
141
179
|
this.restart = true;
|
|
142
180
|
}
|
|
@@ -145,6 +183,74 @@ export class PluginsPage {
|
|
|
145
183
|
}
|
|
146
184
|
await this.reload();
|
|
147
185
|
}
|
|
186
|
+
async installPackage() {
|
|
187
|
+
if (this.busy)
|
|
188
|
+
return;
|
|
189
|
+
this.busy = true;
|
|
190
|
+
try {
|
|
191
|
+
const source = await this.options.ui.input('Install local plugin package', 'npm:package@version, Git address, or absolute folder path');
|
|
192
|
+
if (!source?.trim())
|
|
193
|
+
return;
|
|
194
|
+
const confirmed = await this.options.ui.confirm('Install package?', `${source.trim()} will be installed without running package code. It arrives switched off and code remains untrusted until reviewed.`);
|
|
195
|
+
if (!confirmed)
|
|
196
|
+
return;
|
|
197
|
+
await this.options.call('plugins.install', { source: source.trim() });
|
|
198
|
+
this.restart = true;
|
|
199
|
+
this.options.ui.notify('Installed switched off. Changes apply when this session restarts.', 'info');
|
|
200
|
+
await this.reload();
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
this.options.ui.notify(error instanceof Error ? error.message : String(error), 'error');
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
this.busy = false;
|
|
207
|
+
if (!this.disposed)
|
|
208
|
+
this.options.changed();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async managePackage(plugin) {
|
|
212
|
+
if (this.busy || !plugin.install)
|
|
213
|
+
return;
|
|
214
|
+
const hubManaged = this.hubManagedIds.has(plugin.id);
|
|
215
|
+
const actions = [
|
|
216
|
+
...(hubManaged || plugin.install.kind === 'folder' ? [] : ['Update package']),
|
|
217
|
+
hubManaged ? 'Uninstall Hub version' : 'Remove package',
|
|
218
|
+
BACK
|
|
219
|
+
];
|
|
220
|
+
this.busy = true;
|
|
221
|
+
try {
|
|
222
|
+
const action = await this.options.ui.select(`${plugin.title}: ${plugin.install.source}`, actions);
|
|
223
|
+
if (!action || action === BACK)
|
|
224
|
+
return;
|
|
225
|
+
if (action === 'Update package') {
|
|
226
|
+
const confirmed = await this.options.ui.confirm(`Update ${plugin.title}?`, 'Fetches a newer package version without running its scripts. Changed code remains subject to trust review.');
|
|
227
|
+
if (!confirmed)
|
|
228
|
+
return;
|
|
229
|
+
await this.options.call('plugins.update', { pluginId: plugin.id });
|
|
230
|
+
this.options.ui.notify('Updated. Changes apply when this session restarts.', 'info');
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const title = hubManaged ? `Uninstall ${plugin.title} from Hub?` : `Remove ${plugin.title}?`;
|
|
234
|
+
const message = hubManaged
|
|
235
|
+
? 'This removes the exact Hub receipt and only its recorded package resources.'
|
|
236
|
+
: 'This removes the installed package. A local folder is moved to the Harness trash.';
|
|
237
|
+
if (!await this.options.ui.confirm(title, message))
|
|
238
|
+
return;
|
|
239
|
+
await this.options.call('plugins.remove', { pluginId: plugin.id });
|
|
240
|
+
this.options.ui.notify(hubManaged ? 'Hub package uninstalled.' : 'Package removed.', 'info');
|
|
241
|
+
}
|
|
242
|
+
this.restart = true;
|
|
243
|
+
await this.reload();
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
this.options.ui.notify(error instanceof Error ? error.message : String(error), 'error');
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
this.busy = false;
|
|
250
|
+
if (!this.disposed)
|
|
251
|
+
this.options.changed();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
148
254
|
back() {
|
|
149
255
|
this.settingsFor = null;
|
|
150
256
|
this.saved = false;
|
|
@@ -190,7 +296,12 @@ export class PluginsPage {
|
|
|
190
296
|
}
|
|
191
297
|
}
|
|
192
298
|
handleMouse(event) {
|
|
193
|
-
|
|
299
|
+
const layout = this.pointerLayout;
|
|
300
|
+
if (!layout)
|
|
301
|
+
return undefined;
|
|
302
|
+
if (event.x < 0 || event.x >= layout.width || event.y < layout.top || event.y >= layout.top + layout.rows)
|
|
303
|
+
return { handled: true, render: false };
|
|
304
|
+
return layout.list.handleMouse({ ...event, y: event.y - layout.top });
|
|
194
305
|
}
|
|
195
306
|
handleInput(data) {
|
|
196
307
|
if (this.settingsPlugin()) {
|
|
@@ -200,44 +311,111 @@ export class PluginsPage {
|
|
|
200
311
|
}
|
|
201
312
|
if (matchesKey(data, 'tab') || matchesKey(data, 'shift+tab')) {
|
|
202
313
|
if (!this.list.query) {
|
|
203
|
-
|
|
314
|
+
const direction = matchesKey(data, 'shift+tab') ? -1 : 1;
|
|
315
|
+
const current = SECTIONS.findIndex(section => section.id === this.section);
|
|
316
|
+
this.section = SECTIONS[(current + direction + SECTIONS.length) % SECTIONS.length].id;
|
|
204
317
|
this.fill();
|
|
205
318
|
}
|
|
206
319
|
return true;
|
|
207
320
|
}
|
|
208
321
|
return this.list.handleInput(data);
|
|
209
322
|
}
|
|
323
|
+
selectedPlugin() {
|
|
324
|
+
const selectedId = this.list.selected?.id;
|
|
325
|
+
return selectedId?.startsWith(SETTINGS_ROW)
|
|
326
|
+
? this.plugins?.find(candidate => `${SETTINGS_ROW}${candidate.id}` === selectedId)
|
|
327
|
+
: selectedId?.startsWith(PACKAGE_ROW)
|
|
328
|
+
? this.plugins?.find(candidate => `${PACKAGE_ROW}${candidate.id}` === selectedId)
|
|
329
|
+
: this.menu().find(candidate => this.key(candidate) === selectedId)?.plugin;
|
|
330
|
+
}
|
|
331
|
+
pluginInspector(plugin, width, rows) {
|
|
332
|
+
const theme = this.options.theme();
|
|
333
|
+
if (width < 1 || rows < 1)
|
|
334
|
+
return [];
|
|
335
|
+
if (!plugin) {
|
|
336
|
+
const action = this.list.selected?.id === INSTALL_PACKAGE;
|
|
337
|
+
return [theme.fg('muted', action ? 'Add a package from npm, Git, or a local folder.' : 'Select a plugin to inspect it.')].slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
338
|
+
}
|
|
339
|
+
const selectedId = this.list.selected?.id;
|
|
340
|
+
const row = this.menu().find(candidate => this.key(candidate) === selectedId);
|
|
341
|
+
const title = row ? row.whole ? row.plugin.title : row.name : plugin.title;
|
|
342
|
+
const lines = [theme.bold(safeText(title))];
|
|
343
|
+
if (title !== plugin.title)
|
|
344
|
+
lines.push(theme.fg('dim', `Part of ${safeText(plugin.title)}`));
|
|
345
|
+
lines.push('', ...wrapTextWithAnsi(safeText(describePlugin(plugin)), width));
|
|
346
|
+
if (row) {
|
|
347
|
+
const status = this.section === 'trust' && row.whole
|
|
348
|
+
? plugin.trust === 'trusted' ? 'Code trusted' : 'Code not trusted'
|
|
349
|
+
: row.on ? 'Enabled' : 'Disabled';
|
|
350
|
+
lines.push('', theme.fg(row.on ? 'success' : 'dim', status));
|
|
351
|
+
}
|
|
352
|
+
if (plugin.code) {
|
|
353
|
+
lines.push(theme.fg('muted', `Code trust: ${plugin.trust === 'trusted' ? 'trusted' : 'not trusted'}`));
|
|
354
|
+
lines.push(theme.fg('muted', `Fingerprint: ${plugin.code.hash.slice(0, 12)}`));
|
|
355
|
+
}
|
|
356
|
+
if (plugin.install) {
|
|
357
|
+
lines.push('', theme.fg('muted', `Package: ${this.hubManagedIds.has(plugin.id) ? 'Hub receipt' : 'local'}`));
|
|
358
|
+
lines.push(...wrapTextWithAnsi(theme.fg('dim', safeText(plugin.install.source)), width));
|
|
359
|
+
}
|
|
360
|
+
if (settingsOf(plugin).length)
|
|
361
|
+
lines.push('', theme.fg('muted', `${settingsOf(plugin).length} settings`));
|
|
362
|
+
return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
363
|
+
}
|
|
210
364
|
render(width, rows) {
|
|
211
365
|
const theme = this.options.theme();
|
|
212
366
|
if (width < 1 || rows < 1)
|
|
213
367
|
return [];
|
|
214
|
-
if (!this.plugins)
|
|
368
|
+
if (!this.plugins) {
|
|
369
|
+
this.pointerLayout = undefined;
|
|
215
370
|
return [theme.fg(this.error ? 'error' : 'dim', this.error ? `Could not read plugins: ${this.error}` : 'Reading plugins…')];
|
|
371
|
+
}
|
|
216
372
|
const plugin = this.settingsPlugin();
|
|
217
373
|
if (plugin)
|
|
218
374
|
return this.renderSettings(plugin, width, rows);
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
375
|
+
const note = this.restart ? [theme.fg('warning', 'Saved. Changes apply when this session restarts.')] : [];
|
|
376
|
+
const paneWidth = inspectorListWidth(width);
|
|
377
|
+
if (paneWidth !== undefined) {
|
|
378
|
+
const bodyRows = Math.max(0, rows - note.length);
|
|
379
|
+
const rightWidth = Math.max(0, width - paneWidth - 3);
|
|
380
|
+
const listLines = this.list.render(paneWidth, bodyRows);
|
|
381
|
+
this.pointerLayout = { list: this.list, width: paneWidth, top: note.length, rows: listLines.length };
|
|
382
|
+
return [...note, ...splitColumns(listLines, this.pluginInspector(this.selectedPlugin(), rightWidth, bodyRows), paneWidth, width, bodyRows, theme)]
|
|
383
|
+
.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
384
|
+
}
|
|
385
|
+
const selected = this.selectedPlugin();
|
|
386
|
+
const detail = selected && rows >= 8 ? wrapTextWithAnsi(safeText(describePlugin(selected)), width).slice(0, 3) : [];
|
|
387
|
+
const listRows = Math.max(2, rows - note.length - (detail.length ? detail.length + 1 : 0));
|
|
388
|
+
const listLines = this.list.render(width, listRows);
|
|
389
|
+
this.pointerLayout = { list: this.list, width, top: note.length, rows: Math.min(listLines.length, Math.max(0, rows - note.length)) };
|
|
390
|
+
const lines = [...note, ...listLines];
|
|
391
|
+
if (detail.length)
|
|
392
|
+
lines.push('', ...detail.map(line => theme.fg('muted', line)));
|
|
229
393
|
return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
230
394
|
}
|
|
231
395
|
renderSettings(plugin, width, rows) {
|
|
232
396
|
const theme = this.options.theme();
|
|
233
397
|
const setting = settingsOf(plugin).find(candidate => candidate.key === this.settingsList.selected?.id);
|
|
234
398
|
const range = setting && !isSwitch(setting) ? [`From ${setting.min.toLocaleString('en-US')} to ${setting.max.toLocaleString('en-US')} · default ${setting.default.toLocaleString('en-US')}`] : [];
|
|
399
|
+
const note = this.saved ? [theme.fg('success', 'Saved.')] : [];
|
|
400
|
+
const paneWidth = inspectorListWidth(width);
|
|
401
|
+
if (paneWidth !== undefined) {
|
|
402
|
+
const bodyRows = Math.max(0, rows - note.length);
|
|
403
|
+
const rightWidth = Math.max(0, width - paneWidth - 3);
|
|
404
|
+
const listLines = this.settingsList.render(paneWidth, bodyRows);
|
|
405
|
+
const details = setting
|
|
406
|
+
? [theme.bold(safeText(setting.label)), '', ...wrapTextWithAnsi(safeText(setting.description), rightWidth), '', ...range.map(line => theme.fg('muted', line)), theme.fg('dim', `Current value: ${settingValueText(setting)}`)]
|
|
407
|
+
: [theme.fg('muted', 'Select a setting to review its value and description.')];
|
|
408
|
+
this.pointerLayout = { list: this.settingsList, width: paneWidth, top: note.length, rows: listLines.length };
|
|
409
|
+
return [...note, ...splitColumns(listLines, details, paneWidth, width, bodyRows, theme)]
|
|
410
|
+
.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
411
|
+
}
|
|
235
412
|
const detail = setting && rows >= 8 ? [...wrapTextWithAnsi(safeText(setting.description), width).slice(0, 3), ...range] : [];
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
413
|
+
const listRows = Math.max(2, rows - note.length - (detail.length ? detail.length + 1 : 0));
|
|
414
|
+
const listLines = this.settingsList.render(width, listRows);
|
|
415
|
+
this.pointerLayout = { list: this.settingsList, width, top: note.length, rows: Math.min(listLines.length, Math.max(0, rows - note.length)) };
|
|
416
|
+
const lines = [...note, ...listLines];
|
|
417
|
+
if (detail.length)
|
|
418
|
+
lines.push('', ...detail.map(line => theme.fg('muted', line)));
|
|
241
419
|
return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
242
420
|
}
|
|
243
421
|
}
|