@timurproko/a1 0.2.7-dev.820 → 0.2.7-dev.828
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 +7 -1
- package/bin/automatic-update.js +7 -0
- package/bin/tab-launch-context.d.ts +1 -0
- package/bin/tab-launch-context.js +3 -3
- package/bin/ui.js +96 -66
- package/dist/app/session-shell/session-shell-root.d.ts +13 -6
- package/dist/app/session-shell/session-shell-root.js +9 -5
- package/dist/app/session-shell/session-shell.js +68 -18
- package/dist/app/session-shell/shell-host.js +6 -3
- package/dist/app/session-shell/transcript-document.js +1 -1
- package/dist/cli/tabs.d.ts +1 -1
- package/dist/cli/tabs.js +10 -14
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +5 -14
- package/dist/composition/owned-ui.js +93 -63
- package/dist/composition/resident-tab-bridge.d.ts +10 -1
- package/dist/composition/resident-tab-bridge.js +21 -0
- package/dist/composition/settings-route-host.d.ts +12 -2
- package/dist/composition/settings-route-host.js +27 -9
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +242 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +6 -8
- package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
- package/dist/features/owned-ui/project-trust-prompt.js +7 -3
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/settings-app.d.ts +10 -3
- package/dist/features/owned-ui/settings-app.js +98 -45
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +2 -0
- package/dist/foundation/launch-context/index.d.ts +2 -0
- package/dist/foundation/launch-context/index.js +1 -0
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +1 -9
- package/dist/foundation/release/bootstrap.js +7 -3
- package/dist/foundation/release/early-accent.d.ts +12 -0
- package/dist/foundation/release/early-accent.js +59 -0
- package/dist/foundation/release/index.d.ts +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -2
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
- package/dist/foundation/release/progress-palette.generated.js +137 -3
- package/dist/foundation/release/release-gc.js +19 -4
- package/dist/foundation/release/update-execution-lease.d.ts +36 -0
- package/dist/foundation/release/update-execution-lease.js +145 -0
- package/dist/foundation/release/update.d.ts +9 -7
- package/dist/foundation/release/update.js +244 -175
- package/dist/foundation/resident-tabs/bridge-client.d.ts +9 -2
- package/dist/foundation/resident-tabs/bridge-client.js +19 -7
- package/dist/foundation/resident-tabs/maintenance.d.ts +2 -6
- package/dist/foundation/resident-tabs/maintenance.js +5 -23
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +3 -6
- package/dist/foundation/resident-tabs/progress-observation.js +43 -12
- package/dist/foundation/resident-tabs/release-holds.js +3 -14
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
- package/dist/integrations/pi/components/shell-footer-status.js +19 -3
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
- package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +5 -1
- package/dist/integrations/pi/engine/adapter.js +7 -2
- package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
- package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
- package/dist/integrations/pi/engine/ports.d.ts +1 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +13 -0
- package/dist/integrations/pi/engine/settings-port.js +1 -14
- package/dist/integrations/pi/engine/workflow-runner.js +9 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +2 -2
- package/dist/product-identity.json +2 -0
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.js +35 -30
- package/dist/ui/components/line-input.d.ts +3 -0
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +3 -2
- package/dist/ui/components/prompt-input.js +6 -4
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/settings/declarations.d.ts +56 -10
- package/dist/ui/settings/declarations.js +38 -9
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +15 -0
- package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
- package/dist/ui/settings/progress-palette.generated.js +139 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/resident-tabs.md +48 -17
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/docs/validation.md +25 -5
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ import { OwnedEditorUxInterception, createPromptSelectionInterceptor, editorVisu
|
|
|
5
5
|
import { PINNED_PI_LAYOUT, getPiSelectListTheme, piTheme, renderPiListSelection, } from "./theme.js";
|
|
6
6
|
import { createPiKeybindingsHost, createTuiFacade, ensureTheme, isAutocompleteProvider, } from "./shell-shared-facade.js";
|
|
7
7
|
import { SKILLS_COMMAND_NAME, collapseSkillCommands, createSkillsTunnelProvider, } from "./skills-command.js";
|
|
8
|
+
/** The built-in commands resident A1 serves from its tab bar instead: utility tabs, and the tab rename for `/name`. */
|
|
9
|
+
const RESIDENT_UTILITY_TAB_COMMANDS = new Set(["settings", "changelog", "name"]);
|
|
8
10
|
/** A selected autocomplete row: its arrow/primary column, then the aligned description. */
|
|
9
11
|
const SELECTED_DESCRIBED_ROW = /^(→ .*?\S)(\s{2,}.*)$/u;
|
|
10
12
|
const INPUT_COUNTER_INSET = 3;
|
|
@@ -62,6 +64,8 @@ export function createPiShellEditor(options) {
|
|
|
62
64
|
ensureTheme();
|
|
63
65
|
const tui = createTuiFacade(options);
|
|
64
66
|
const inputPresentation = options.keybindingProfile === "a1" ? options.promptPresentation?.input : undefined;
|
|
67
|
+
const inputCapabilities = options.inputCapabilities ?? {};
|
|
68
|
+
const autocompleteEnabled = inputCapabilities.autocomplete !== false;
|
|
65
69
|
// Invariant: the process shares one manager; a standalone editor gets its own host for its profile.
|
|
66
70
|
const host = options.keybindings ?? createPiKeybindingsHost({
|
|
67
71
|
profile: options.keybindingProfile ?? "pi",
|
|
@@ -74,7 +78,7 @@ export function createPiShellEditor(options) {
|
|
|
74
78
|
}
|
|
75
79
|
const scrollInfo = { emitted: false, counter: undefined };
|
|
76
80
|
const selectListTheme = getPiSelectListTheme();
|
|
77
|
-
|
|
81
|
+
const autocomplete = { provider: emptyAutocompleteProvider(), skills: [] };
|
|
78
82
|
const EditorClass = options.keybindingProfile === "a1" && options.persistentHistory === true ? options.historyEditor : OwnedEditor;
|
|
79
83
|
const editor = new EditorClass(tui, {
|
|
80
84
|
borderColor: (value) => inputPresentation === undefined ? piTheme().fg("borderMuted", value) : inputPresentation.styleRule(value),
|
|
@@ -100,13 +104,14 @@ export function createPiShellEditor(options) {
|
|
|
100
104
|
...(options.keybindingProfile === "a1" ? {
|
|
101
105
|
terminalRows: options.getRows,
|
|
102
106
|
getVisualLineCount: (width) => editorVisualLineCount(editor, width),
|
|
103
|
-
clearCommandSearchOnEscape:
|
|
104
|
-
commandTunnels: () =>
|
|
107
|
+
clearCommandSearchOnEscape: autocompleteEnabled,
|
|
108
|
+
commandTunnels: () => !autocompleteEnabled || autocomplete.skills.length === 0 ? [] : [SKILLS_COMMAND_NAME],
|
|
105
109
|
} : {}),
|
|
106
110
|
...(options.keybindingProfile === "a1" && options.promptPresentation !== undefined ? {
|
|
107
111
|
...(inputPresentation === undefined ? {} : { inputPresentation }),
|
|
108
112
|
styleSuggestion: options.promptPresentation.styleSuggestion,
|
|
109
113
|
styleSuggestionCaret: options.promptPresentation.styleSuggestionCaret,
|
|
114
|
+
...(inputCapabilities.placeholder === undefined ? {} : { placeholder: inputCapabilities.placeholder }),
|
|
110
115
|
} : {}),
|
|
111
116
|
});
|
|
112
117
|
const editorUx = options.keybindingProfile === "a1"
|
|
@@ -117,6 +122,7 @@ export function createPiShellEditor(options) {
|
|
|
117
122
|
...(options.beginClipboardPaste === undefined ? {} : { beginClipboardPaste: options.beginClipboardPaste }),
|
|
118
123
|
...(options.beginTextPaste === undefined ? {} : { beginTextPaste: options.beginTextPaste }),
|
|
119
124
|
...(options.deferTextPaste === undefined ? {} : { deferTextPaste: options.deferTextPaste }),
|
|
125
|
+
pasteEnabled: inputCapabilities.paste !== false,
|
|
120
126
|
...(options.onPasteRejected === undefined ? {} : { onPasteRejected: options.onPasteRejected }),
|
|
121
127
|
...(options.onPasteInput === undefined ? {} : { onPasteInput: options.onPasteInput }),
|
|
122
128
|
transformPastedContent: options.transformPastedContent ?? (content => content.kind === "text" ? content.text : ""),
|
|
@@ -142,37 +148,7 @@ export function createPiShellEditor(options) {
|
|
|
142
148
|
: isBashMode ? piTheme().getBashModeBorderColor() : piTheme().getThinkingBorderColor(thinkingLevel);
|
|
143
149
|
tui.requestRender();
|
|
144
150
|
};
|
|
145
|
-
|
|
146
|
-
const builtInCatalog = (options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS)
|
|
147
|
-
.filter(command => !options.hideSettingsCommand || command.name !== "settings");
|
|
148
|
-
const setAutocompleteCommands = (installed) => {
|
|
149
|
-
// Invariant: collapse is a bare-A1 presentation; the comparison profile installs the pinned list.
|
|
150
|
-
const collapsed = options.keybindingProfile === "a1" && options.skillsPresentation?.() === "collapse"
|
|
151
|
-
? collapseSkillCommands(installed)
|
|
152
|
-
: { commands: installed, skills: [] };
|
|
153
|
-
tunnelSkills = collapsed.skills;
|
|
154
|
-
const commands = collapsed.commands;
|
|
155
|
-
const additions = new Map(commands.map(command => [command.name, command]));
|
|
156
|
-
const builtInNames = new Set(builtInCatalog.map(command => command.name));
|
|
157
|
-
const builtIns = builtInCatalog.flatMap(command => [
|
|
158
|
-
autocompleteCommand(command, additions.get(command.name)),
|
|
159
|
-
// Rationale: the collapsed browser is a primary bare-A1 command, not a resource after quit.
|
|
160
|
-
...(command.name === "settings" && collapsed.collapsedCommand !== undefined
|
|
161
|
-
? [autocompleteCommand(collapsed.collapsedCommand)]
|
|
162
|
-
: []),
|
|
163
|
-
]);
|
|
164
|
-
// Invariant: built-in additions and the promoted collapsed browser never reappear as resources.
|
|
165
|
-
const resources = commands
|
|
166
|
-
.filter(command => command.name !== collapsed.collapsedCommand?.name && command.source !== "builtin" && !builtInNames.has(command.name))
|
|
167
|
-
.map(command => autocompleteCommand(command));
|
|
168
|
-
const catalog = [...builtIns, ...resources];
|
|
169
|
-
const combined = new CombinedAutocompleteProvider(catalog, options.cwd ?? process.cwd());
|
|
170
|
-
const tunnelAware = tunnelSkills.length === 0 ? combined : createSkillsTunnelProvider(combined, tunnelSkills);
|
|
171
|
-
autocompleteProvider = options.keybindingProfile === "a1"
|
|
172
|
-
? createDelimiterReadyCommandProvider(tunnelAware, catalog)
|
|
173
|
-
: tunnelAware;
|
|
174
|
-
editor.setAutocompleteProvider(autocompleteProvider);
|
|
175
|
-
};
|
|
151
|
+
const setAutocompleteCommands = autocompleteCommandSetter(options, editor, autocomplete, autocompleteEnabled);
|
|
176
152
|
setAutocompleteCommands(options.autocompleteCommands ?? []);
|
|
177
153
|
let submitHandler = options.onSubmit;
|
|
178
154
|
let interruptHandler = options.onInterrupt ?? (() => { });
|
|
@@ -259,6 +235,9 @@ export function createPiShellEditor(options) {
|
|
|
259
235
|
reloadKeybindings: () => host.reload(),
|
|
260
236
|
matchesTerminalKey: (data, key) => matchesKey(data, key),
|
|
261
237
|
handleInput: data => {
|
|
238
|
+
if (inputCapabilities.singleLine === true
|
|
239
|
+
&& (keybindings.matches(data, "tui.input.newLine") || keybindings.matches(data, "tui.input.submit")))
|
|
240
|
+
return;
|
|
262
241
|
// Compatibility: an unassigned reverse Tab must not clear selection or reach autocomplete fallback.
|
|
263
242
|
if (editorUx !== undefined && matchesKey(data, "shift+tab")
|
|
264
243
|
&& !Object.values(keybindings.getEffectiveConfig()).some(keys => (Array.isArray(keys) ? keys : [keys]).some(key => key !== undefined && matchesKey(data, key))))
|
|
@@ -316,13 +295,15 @@ export function createPiShellEditor(options) {
|
|
|
316
295
|
tui.requestRender();
|
|
317
296
|
},
|
|
318
297
|
addAutocompleteProvider(factory) {
|
|
298
|
+
if (!autocompleteEnabled)
|
|
299
|
+
return;
|
|
319
300
|
if (typeof factory !== "function")
|
|
320
301
|
throw new TypeError("extension autocomplete factory must be a function");
|
|
321
|
-
const candidate = Reflect.apply(factory, undefined, [
|
|
302
|
+
const candidate = Reflect.apply(factory, undefined, [autocomplete.provider]);
|
|
322
303
|
if (!isAutocompleteProvider(candidate))
|
|
323
304
|
throw new TypeError("extension autocomplete factory returned a malformed provider");
|
|
324
|
-
|
|
325
|
-
editor.setAutocompleteProvider(
|
|
305
|
+
autocomplete.provider = candidate;
|
|
306
|
+
editor.setAutocompleteProvider(autocomplete.provider);
|
|
326
307
|
},
|
|
327
308
|
setThinkingLevel(level) {
|
|
328
309
|
if (thinkingLevel === level)
|
|
@@ -354,6 +335,45 @@ export function createPiShellEditor(options) {
|
|
|
354
335
|
cancelPendingPastes: () => editorUx?.cancelPendingPastes(),
|
|
355
336
|
};
|
|
356
337
|
}
|
|
338
|
+
function autocompleteCommandSetter(options, editor, state, enabled) {
|
|
339
|
+
const builtInCatalog = (options.keybindingProfile === "a1" ? OWNED_BUILTIN_SLASH_COMMANDS : PINNED_PI_BUILTIN_SLASH_COMMANDS)
|
|
340
|
+
.filter(command => !options.hideUtilityTabCommands || !RESIDENT_UTILITY_TAB_COMMANDS.has(command.name));
|
|
341
|
+
return installed => {
|
|
342
|
+
if (!enabled) {
|
|
343
|
+
state.provider = emptyAutocompleteProvider();
|
|
344
|
+
editor.setAutocompleteProvider(state.provider);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
// Invariant: collapse is a bare-A1 presentation; the comparison profile installs the pinned list.
|
|
348
|
+
const collapsed = options.keybindingProfile === "a1" && options.skillsPresentation?.() === "collapse"
|
|
349
|
+
? collapseSkillCommands(installed)
|
|
350
|
+
: { commands: installed, skills: [] };
|
|
351
|
+
state.skills = collapsed.skills;
|
|
352
|
+
const additions = new Map(collapsed.commands.map(command => [command.name, command]));
|
|
353
|
+
const builtInNames = new Set(builtInCatalog.map(command => command.name));
|
|
354
|
+
const builtIns = builtInCatalog.flatMap(command => [
|
|
355
|
+
autocompleteCommand(command, additions.get(command.name)),
|
|
356
|
+
// Rationale: the collapsed browser is a primary bare-A1 command, not a resource after quit.
|
|
357
|
+
...(command.name === "settings" && collapsed.collapsedCommand !== undefined
|
|
358
|
+
? [autocompleteCommand(collapsed.collapsedCommand)] : []),
|
|
359
|
+
]);
|
|
360
|
+
const resources = collapsed.commands
|
|
361
|
+
.filter(command => command.name !== collapsed.collapsedCommand?.name && command.source !== "builtin" && !builtInNames.has(command.name))
|
|
362
|
+
.map(command => autocompleteCommand(command));
|
|
363
|
+
const catalog = [...builtIns, ...resources];
|
|
364
|
+
const combined = new CombinedAutocompleteProvider(catalog, options.cwd ?? process.cwd());
|
|
365
|
+
const tunnelAware = state.skills.length === 0 ? combined : createSkillsTunnelProvider(combined, state.skills);
|
|
366
|
+
state.provider = options.keybindingProfile === "a1" ? createDelimiterReadyCommandProvider(tunnelAware, catalog) : tunnelAware;
|
|
367
|
+
editor.setAutocompleteProvider(state.provider);
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function emptyAutocompleteProvider() {
|
|
371
|
+
return {
|
|
372
|
+
getSuggestions: async () => null,
|
|
373
|
+
applyCompletion: (lines, cursorLine, cursorCol) => ({ lines, cursorLine, cursorCol }),
|
|
374
|
+
shouldTriggerFileCompletion: () => false,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
357
377
|
/** Bare A1 leaves an applied catalog command ready for `:`, a user-entered space, or submission. */
|
|
358
378
|
function createDelimiterReadyCommandProvider(base, commands) {
|
|
359
379
|
const commandNames = new Set(commands.map(command => command.name));
|
|
@@ -7,7 +7,7 @@ import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
|
|
|
7
7
|
import { StatusIndicator, WorkingStatusIndicator } from "./upstream/components/status-indicator.js";
|
|
8
8
|
import { protectPiPromptChipPresentation } from "./prompt-chip-presentation.js";
|
|
9
9
|
import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
|
|
10
|
-
import { createTuiFacade, ensureTheme, } from "./shell-shared-facade.js";
|
|
10
|
+
import { createTuiFacade, ensureTheme, piShellTruncateToWidth, piShellVisibleWidth, } from "./shell-shared-facade.js";
|
|
11
11
|
export function createPiShellHeader(options = {}) {
|
|
12
12
|
ensureTheme();
|
|
13
13
|
let expanded = options.expanded ?? false;
|
|
@@ -107,9 +107,9 @@ export function createPiShellStatus(view, progressStatus, runtime) {
|
|
|
107
107
|
component = statusComponent(view, statusUi, outputPad, liveStatusText, placement, progressPresentation, progressStatus);
|
|
108
108
|
};
|
|
109
109
|
return {
|
|
110
|
-
render: width => component?.render(width) ?? [],
|
|
110
|
+
render: width => appendInstalledUpdateStatus(component?.render(width) ?? [], width, view),
|
|
111
111
|
renderDock: width => placement === "dock" ? component?.render(width) ?? [] : [],
|
|
112
|
-
renderLive: width => placement === "live" ? component?.render(width) ?? [] : [],
|
|
112
|
+
renderLive: width => appendInstalledUpdateStatus(placement === "live" ? component?.render(width) ?? [] : [], width, view),
|
|
113
113
|
placement: () => placement,
|
|
114
114
|
invalidate: () => component?.invalidate(),
|
|
115
115
|
update(next) {
|
|
@@ -228,6 +228,22 @@ function statusSignature(view, workingOverride, outputPad, placement, progressPr
|
|
|
228
228
|
const message = withMessage ? `${workingOverride ?? ""}\u0000${view.status.workingMessage ?? ""}\u0000${view.status.workingProgress ?? ""}` : "";
|
|
229
229
|
return `${placement}\u0000${outputPad}\u0000${view.lifecycle}\u0000${progressPresentation}\u0000${message}\u0000${view.status.diagnostics.at(-1) ?? ""}`;
|
|
230
230
|
}
|
|
231
|
+
function appendInstalledUpdateStatus(rows, width, view) {
|
|
232
|
+
if (width <= 0 || !view.diagnostics.some(diagnostic => diagnostic.code === "release-update-installed"))
|
|
233
|
+
return rows;
|
|
234
|
+
const theme = piTheme();
|
|
235
|
+
const full = `${theme.fg("success", "Update installed")} ${theme.fg("muted", "·")} ${theme.fg("accent", "Restart to update")}`;
|
|
236
|
+
const status = piShellTruncateToWidth(full, width);
|
|
237
|
+
const statusWidth = piShellVisibleWidth(status);
|
|
238
|
+
const target = rows.findLastIndex(row => piShellVisibleWidth(row.trimEnd()) > 0);
|
|
239
|
+
if (target < 0)
|
|
240
|
+
return [`${" ".repeat(Math.max(0, width - statusWidth))}${status}`];
|
|
241
|
+
const base = (rows[target] ?? "").trimEnd();
|
|
242
|
+
const gap = width - piShellVisibleWidth(base) - statusWidth;
|
|
243
|
+
if (gap >= 2)
|
|
244
|
+
return rows.map((row, index) => index === target ? `${base}${" ".repeat(gap)}${status}` : row);
|
|
245
|
+
return [...rows.slice(0, target + 1), `${" ".repeat(Math.max(0, width - statusWidth))}${status}`, ...rows.slice(target + 1)];
|
|
246
|
+
}
|
|
231
247
|
function queuedInputChipWrapping(text, presentation, width) {
|
|
232
248
|
return presentation === "custom-viewport"
|
|
233
249
|
? protectPiPromptChipPresentation(text, width)
|
|
@@ -4,7 +4,7 @@ import { SkillInvocationMessageComponent } from "./upstream/components/skill-inv
|
|
|
4
4
|
import { piToolArguments as toolArguments, updatePiToolResult as updateToolResult } from "./tool-result-adapter.js";
|
|
5
5
|
import { createTranscriptImageResolver } from "./transcript-image-resolver.js";
|
|
6
6
|
import { createMermaidMarkdownTransformer } from "./upstream/components/mermaid.js";
|
|
7
|
-
import { Container, Image, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
7
|
+
import { Container, getCapabilities, Image, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
8
8
|
import { createPiSubmittedPromptComponent, isPiPromptStyleCompaction, } from "./submitted-prompt-adapter.js";
|
|
9
9
|
export { isPiPromptStyleCompaction, paintPiSubmittedPromptTimestamp } from "./submitted-prompt-adapter.js";
|
|
10
10
|
import { getPiMarkdownTheme, PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
|
|
@@ -127,7 +127,10 @@ function withTranscriptImages(component, block, assets, showImages, imageWidthCe
|
|
|
127
127
|
return component;
|
|
128
128
|
const container = new Container();
|
|
129
129
|
container.addChild(component);
|
|
130
|
+
const imageProtocolUnavailable = getCapabilities().images === null;
|
|
130
131
|
for (const reference of references) {
|
|
132
|
+
if (showImages && imageProtocolUnavailable)
|
|
133
|
+
continue;
|
|
131
134
|
container.addChild(new Spacer(1));
|
|
132
135
|
const asset = assets?.resolve(reference.assetId) ?? null;
|
|
133
136
|
if (!showImages) {
|
|
@@ -3,7 +3,7 @@ import { EarendilAnnouncementComponent } from "./upstream/components/earendil-an
|
|
|
3
3
|
import { ExtensionSelectorComponent } from "./upstream/components/extension-selector.js";
|
|
4
4
|
import { Box, Container, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
5
5
|
import { ScopedModelsSelectorComponent } from "./upstream/components/scoped-models-selector.js";
|
|
6
|
-
import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader } from "./modal-frame.js";
|
|
6
|
+
import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader, adoptPiModalSearchInput } from "./modal-frame.js";
|
|
7
7
|
import { ModelsDialogComponent } from "./models-dialog.js";
|
|
8
8
|
import { TrustSelectorComponent } from "./upstream/components/trust-selector.js";
|
|
9
9
|
import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, getPiSelectListTheme, paintPiBorder, piTheme, renderPiModalShortcutHints, } from "./theme.js";
|
|
@@ -96,6 +96,7 @@ export function createPiShellSettingsSelector(options) {
|
|
|
96
96
|
export function createPiShellModelSelector(options) {
|
|
97
97
|
ensureTheme();
|
|
98
98
|
const selector = new ModelSelectorComponent(createTuiFacade(options.runtime), options.currentModel, options.modelRuntime, options.scopedModels, options.onSelect, options.onCancel, options.initialSearchInput, options.onSelectAsDefault, options.defaultModel);
|
|
99
|
+
adoptPiModalSearchInput(selector, selector.getSearchInput());
|
|
99
100
|
adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
|
|
100
101
|
return componentPort(selector);
|
|
101
102
|
}
|
|
@@ -235,6 +236,8 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
|
|
|
235
236
|
if (selected)
|
|
236
237
|
onSelect(selected.id);
|
|
237
238
|
}, onCancel, initialSearchInput);
|
|
239
|
+
// Provenance: OAuth selector slot is top rule, spacer, title, spacer, then its search Input.
|
|
240
|
+
adoptPiModalSearchInput(selector, selector.children[4]);
|
|
238
241
|
addPiModalSelectFooter(selector);
|
|
239
242
|
const preInsetContent = [selector.children[2], selector.children[6]];
|
|
240
243
|
const header = adoptPiModalHeader(selector, 0, 2);
|
|
@@ -161,11 +161,18 @@ export interface PiShellPasteReservation {
|
|
|
161
161
|
}
|
|
162
162
|
export interface PiShellEditorOptions {
|
|
163
163
|
readonly persistentHistory?: boolean;
|
|
164
|
+
/** Configures the shared interactive input for a caller narrower than the agent prompt. */
|
|
165
|
+
readonly inputCapabilities?: {
|
|
166
|
+
readonly singleLine?: boolean;
|
|
167
|
+
readonly paste?: boolean;
|
|
168
|
+
readonly autocomplete?: boolean;
|
|
169
|
+
readonly placeholder?: string;
|
|
170
|
+
};
|
|
164
171
|
readonly historyEditor?: HistoryEditorConstructor;
|
|
165
172
|
/** Bare A1 adds ergonomic aliases while comparison profiles retain Pi defaults. */
|
|
166
173
|
readonly keybindingProfile?: "pi" | "a1";
|
|
167
|
-
/** Resident Agents expose Settings
|
|
168
|
-
readonly
|
|
174
|
+
/** Resident Agents expose Settings and the changelog as utility tabs instead of slash commands. */
|
|
175
|
+
readonly hideUtilityTabCommands?: boolean;
|
|
169
176
|
/** The process's shared keybindings; its profile must match `keybindingProfile`. */
|
|
170
177
|
readonly keybindings?: PiKeybindingsHost;
|
|
171
178
|
readonly getColumns: () => number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DynamicBorder } from "../startup-public.js";
|
|
2
2
|
import { Container, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
|
|
3
|
-
import { addPiModalHeader, adoptPiModalFrame } from "./modal-frame.js";
|
|
3
|
+
import { addPiModalHeader, addPiModalSearchInput, adoptPiModalFrame } from "./modal-frame.js";
|
|
4
4
|
import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, PINNED_PI_LAYOUT, piTheme, renderPiModalListRow, renderPiModalShortcutHints, } from "./theme.js";
|
|
5
5
|
import { componentPort, ensureTheme, piShellTruncateToWidth, piShellVisibleWidth } from "./shell-shared-facade.js";
|
|
6
6
|
import { skillMatchesQuery } from "./skills-command.js";
|
|
@@ -50,7 +50,7 @@ class SkillsSelectorComponent extends Container {
|
|
|
50
50
|
this.#searchInput.onSubmit = () => this.#selectCurrent();
|
|
51
51
|
const header = addPiModalHeader(this, new DynamicBorder(paintPiBorder), new Text(piTheme().fg("accent", piTheme().bold("Skills")), 0, 0));
|
|
52
52
|
this.addChild(new Spacer(1));
|
|
53
|
-
|
|
53
|
+
addPiModalSearchInput(this, this.#searchInput);
|
|
54
54
|
this.addChild(new Spacer(1));
|
|
55
55
|
this.addChild(this.#listContainer);
|
|
56
56
|
this.addChild(new Spacer(1));
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
|
|
4
4
|
* Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
|
|
5
5
|
* private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
|
|
6
|
-
* frame, transient contextual-suggestion branch, and
|
|
7
|
-
* above-prompt autocomplete with a one-cell outer gutter.
|
|
8
|
-
* slash-command search on Escape and completes a selected or
|
|
9
|
-
* and reopens the menu on its tunnel rows, and synchronously
|
|
10
|
-
* command menu after Tab applies its row.
|
|
6
|
+
* frame, configurable empty-input placeholder branch, transient contextual-suggestion branch, and
|
|
7
|
+
* explicit body geometry for selection and above-prompt autocomplete with a one-cell outer gutter.
|
|
8
|
+
* Bare A1 also clears a sole top-level slash-command search on Escape and completes a selected or
|
|
9
|
+
* exact completed tunnel command with `:` and reopens the menu on its tunnel rows, and synchronously
|
|
10
|
+
* retains then refreshes an exact top-level command menu after Tab applies its row.
|
|
11
11
|
* Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
|
|
12
12
|
* clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
|
|
13
13
|
*/
|
|
@@ -21,6 +21,8 @@ export interface OwnedEditorOptions extends EditorOptions {
|
|
|
21
21
|
readonly inputPresentation?: PiShellPromptInputPresentation;
|
|
22
22
|
readonly styleSuggestion?: (text: string) => string;
|
|
23
23
|
readonly styleSuggestionCaret?: (text: string) => string;
|
|
24
|
+
/** Non-accepting empty-input guidance rendered through the shared suggestion presentation. */
|
|
25
|
+
readonly placeholder?: string;
|
|
24
26
|
readonly terminalRows?: () => number;
|
|
25
27
|
/** Reuses the editor's established atomic-aware visual layout when available. */
|
|
26
28
|
readonly getVisualLineCount?: (width: number) => number | undefined;
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
|
|
4
4
|
* Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
|
|
5
5
|
* private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
|
|
6
|
-
* frame, transient contextual-suggestion branch, and
|
|
7
|
-
* above-prompt autocomplete with a one-cell outer gutter.
|
|
8
|
-
* slash-command search on Escape and completes a selected or
|
|
9
|
-
* and reopens the menu on its tunnel rows, and synchronously
|
|
10
|
-
* command menu after Tab applies its row.
|
|
6
|
+
* frame, configurable empty-input placeholder branch, transient contextual-suggestion branch, and
|
|
7
|
+
* explicit body geometry for selection and above-prompt autocomplete with a one-cell outer gutter.
|
|
8
|
+
* Bare A1 also clears a sole top-level slash-command search on Escape and completes a selected or
|
|
9
|
+
* exact completed tunnel command with `:` and reopens the menu on its tunnel rows, and synchronously
|
|
10
|
+
* retains then refreshes an exact top-level command menu after Tab applies its row.
|
|
11
11
|
* Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
|
|
12
12
|
* clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
|
|
13
13
|
*/
|
|
@@ -25,6 +25,7 @@ export function createOwnedEditorClass(Base) {
|
|
|
25
25
|
#inputPresentation;
|
|
26
26
|
#styleSuggestion;
|
|
27
27
|
#styleSuggestionCaret;
|
|
28
|
+
#placeholder;
|
|
28
29
|
#terminalRows;
|
|
29
30
|
#getVisualLineCount;
|
|
30
31
|
#clearCommandSearchOnEscape;
|
|
@@ -37,6 +38,7 @@ export function createOwnedEditorClass(Base) {
|
|
|
37
38
|
this.#inputPresentation = options.inputPresentation;
|
|
38
39
|
this.#styleSuggestion = options.styleSuggestion ?? (text => text);
|
|
39
40
|
this.#styleSuggestionCaret = options.styleSuggestionCaret ?? (text => `\u001b[7m${text}\u001b[27m`);
|
|
41
|
+
this.#placeholder = options.placeholder ?? null;
|
|
40
42
|
this.#terminalRows = options.terminalRows ?? (() => 24);
|
|
41
43
|
this.#getVisualLineCount = options.getVisualLineCount;
|
|
42
44
|
this.#clearCommandSearchOnEscape = options.clearCommandSearchOnEscape === true;
|
|
@@ -70,7 +72,12 @@ export function createOwnedEditorClass(Base) {
|
|
|
70
72
|
return rows;
|
|
71
73
|
}
|
|
72
74
|
if (this.#promptSuggestion !== null && this.canPresentPromptSuggestion()) {
|
|
73
|
-
const rows = this.#renderSuggestion(width);
|
|
75
|
+
const rows = this.#renderSuggestion(width, this.#promptSuggestion);
|
|
76
|
+
this.#renderedBodyRowCount = rows.length;
|
|
77
|
+
return rows;
|
|
78
|
+
}
|
|
79
|
+
if (this.#placeholder !== null && this.focused && this.getText().length === 0 && !this.isShowingAutocomplete()) {
|
|
80
|
+
const rows = this.#renderSuggestion(width, this.#placeholder);
|
|
74
81
|
this.#renderedBodyRowCount = rows.length;
|
|
75
82
|
return rows;
|
|
76
83
|
}
|
|
@@ -176,10 +183,10 @@ export function createOwnedEditorClass(Base) {
|
|
|
176
183
|
triggerAutocomplete(this);
|
|
177
184
|
return true;
|
|
178
185
|
}
|
|
179
|
-
#renderSuggestion(width) {
|
|
186
|
+
#renderSuggestion(width, suggestion) {
|
|
180
187
|
const presentation = this.#inputPresentation;
|
|
181
188
|
const { paddingX, contentWidth, layoutWidth } = presentation.geometry(width, this.getPaddingX());
|
|
182
|
-
const chunks = wrapTextWithAnsi(
|
|
189
|
+
const chunks = wrapTextWithAnsi(suggestion, layoutWidth).map(text => ({ text }));
|
|
183
190
|
const maxVisible = Math.max(5, Math.floor(this.#terminalRows() * 0.3));
|
|
184
191
|
const visible = chunks.slice(0, maxVisible);
|
|
185
192
|
const leftPadding = " ".repeat(paddingX);
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* search, bulk/provider/reorder actions, dirty state, Ctrl+S persistence, refresh status,
|
|
6
6
|
* cancellation, and focus while remapping theme and public helper imports; local key labels preserve
|
|
7
7
|
* pinned platform formatting before layout without changing binding identities, and bare A1 uses the
|
|
8
|
-
* shared semantic modal shortcut row and compact padded modal frame while
|
|
9
|
-
* implicit Ctrl+C selection-cancel alias without advertising it.
|
|
10
|
-
* Deviations: owned-modal-shortcut-hints, owned-dialog-ctrl-c-cancel.
|
|
8
|
+
* shared semantic modal shortcut row, accent search marker, and compact padded modal frame while
|
|
9
|
+
* immediately closing on the implicit Ctrl+C selection-cancel alias without advertising it.
|
|
10
|
+
* Deviations: owned-modal-shortcut-hints, owned-dialog-ctrl-c-cancel, owned-dialog-search-marker.
|
|
11
11
|
*/
|
|
12
12
|
interface ScopedModel {
|
|
13
13
|
readonly provider: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
3
3
|
import { getModelSearchText } from "../model-search.js";
|
|
4
|
-
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
4
|
+
import { addPiModalHeader, addPiModalSearchInput, adoptPiModalFrame } from "../../modal-frame.js";
|
|
5
5
|
import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
6
6
|
// Rationale: upstream's capitalized hint formatter is private to the package; the same mapping lives here.
|
|
7
7
|
function keyDisplayText(keybinding) {
|
|
@@ -118,7 +118,7 @@ export class ScopedModelsSelectorComponent extends Container {
|
|
|
118
118
|
this.addChild(new Spacer(1));
|
|
119
119
|
// Search input
|
|
120
120
|
this.searchInput = new Input();
|
|
121
|
-
|
|
121
|
+
addPiModalSearchInput(this, this.searchInput);
|
|
122
122
|
this.addChild(new Spacer(1));
|
|
123
123
|
// List container
|
|
124
124
|
this.listContainer = new Container();
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provenance: @earendil-works/pi-coding-agent 1.4.0 (MIT), commit 4a080252f06caf63a525d7873c5d94d3c23887e0,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/footer.ts.
|
|
4
|
-
* Modifications:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* surfaces may suppress a thinking level they already present; a resident tab whose chip already shows
|
|
9
|
-
* the session name may suppress its path-row suffix.
|
|
10
|
-
* Deviations: owned-status-level-color, owned-pull-request-badge, owned-resident-session-name.
|
|
4
|
+
* Modifications: Owned UI/routing; A1 level color, linked PRs, resident names, dim ellipsis and model
|
|
5
|
+
* priority; Pi unchanged.
|
|
6
|
+
* Deviations: owned-status-level-color, owned-pull-request-badge, owned-resident-session-name,
|
|
7
|
+
* owned-footer-ellipsis, owned-footer-model-priority.
|
|
11
8
|
*/
|
|
12
9
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
13
10
|
import { hyperlink, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
@@ -31,6 +28,7 @@ export class SessionFooter {
|
|
|
31
28
|
const view = this.getView();
|
|
32
29
|
const usage = view.status.usage;
|
|
33
30
|
const theme = piTheme();
|
|
31
|
+
const dimEllipsis = theme.fg("dim", this.profile === "a1" ? "…" : "...");
|
|
34
32
|
const input = usage?.input ?? 0;
|
|
35
33
|
const output = usage?.output ?? 0;
|
|
36
34
|
const cacheRead = usage?.cacheRead ?? 0;
|
|
@@ -38,8 +36,8 @@ export class SessionFooter {
|
|
|
38
36
|
const cost = usage?.cost ?? 0;
|
|
39
37
|
const contextWindow = usage?.contextWindow ?? 0;
|
|
40
38
|
const contextPercentValue = usage?.contextPercent ?? 0;
|
|
41
|
-
const
|
|
42
|
-
|
|
39
|
+
const contextPercent = usage?.contextAvailable !== false && usage?.contextPercent === null
|
|
40
|
+
? "?" : contextPercentValue.toFixed(1);
|
|
43
41
|
const repositoryPath = this.profile === "a1" ? view.status.footer?.repositoryPath ?? this.cwd : this.cwd;
|
|
44
42
|
let pwd = formatCwd(repositoryPath, process.env.HOME || process.env.USERPROFILE);
|
|
45
43
|
const branch = view.status.footer?.branch;
|
|
@@ -48,7 +46,7 @@ export class SessionFooter {
|
|
|
48
46
|
const sessionName = this.showSessionName ? view.status.footer?.sessionName : null;
|
|
49
47
|
const pullRequest = this.profile === "a1" ? view.status.footer?.pullRequest : null;
|
|
50
48
|
let pathRow;
|
|
51
|
-
if (pullRequest
|
|
49
|
+
if (pullRequest == null) {
|
|
52
50
|
pathRow = theme.fg("dim", sessionName ? `${pwd} • ${sessionName}` : pwd);
|
|
53
51
|
}
|
|
54
52
|
else {
|
|
@@ -59,12 +57,10 @@ export class SessionFooter {
|
|
|
59
57
|
if (visibleWidth(full) <= width)
|
|
60
58
|
pathRow = full;
|
|
61
59
|
else if (visibleWidth(badge) <= width) {
|
|
62
|
-
|
|
63
|
-
const path = pathWidth === 0 ? "" : truncateToWidth(theme.fg("dim", pwd), pathWidth, theme.fg("dim", "..."));
|
|
64
|
-
pathRow = path + badge;
|
|
60
|
+
pathRow = truncateToWidth(theme.fg("dim", pwd), width - visibleWidth(badge), dimEllipsis) + badge;
|
|
65
61
|
}
|
|
66
62
|
else {
|
|
67
|
-
pathRow = truncateToWidth(badge, width,
|
|
63
|
+
pathRow = truncateToWidth(badge, width, dimEllipsis);
|
|
68
64
|
}
|
|
69
65
|
}
|
|
70
66
|
const parts = [];
|
|
@@ -88,7 +84,7 @@ export class SessionFooter {
|
|
|
88
84
|
let left = parts.join(" ");
|
|
89
85
|
let leftWidth = visibleWidth(left);
|
|
90
86
|
if (leftWidth > width) {
|
|
91
|
-
left = truncateToWidth(left, width, "...");
|
|
87
|
+
left = truncateToWidth(left, width, this.profile === "a1" ? dimEllipsis : "...");
|
|
92
88
|
leftWidth = visibleWidth(left);
|
|
93
89
|
}
|
|
94
90
|
const modelName = view.activeModel?.modelId ?? "no-model";
|
|
@@ -96,6 +92,7 @@ export class SessionFooter {
|
|
|
96
92
|
? view.activeModel === null || !this.show() ? theme.fg("dim", modelName)
|
|
97
93
|
: theme.fg("dim", `${modelName} • `) + theme.getThinkingBorderColor(view.thinkingLevel)(view.thinkingLevel)
|
|
98
94
|
: view.activeModel === null || view.thinkingLevel === "off" || !this.show() ? modelName : `${modelName} • ${view.thinkingLevel}`;
|
|
95
|
+
const modelLevel = rightWithoutProvider;
|
|
99
96
|
const routed = view.routedModel;
|
|
100
97
|
if (routed) {
|
|
101
98
|
const level = routed.thinkingLevel === undefined ? "" : ` • ${routed.thinkingLevel}`;
|
|
@@ -109,9 +106,18 @@ export class SessionFooter {
|
|
|
109
106
|
if (leftWidth + 2 + visibleWidth(right) > width)
|
|
110
107
|
right = rightWithoutProvider;
|
|
111
108
|
}
|
|
109
|
+
if (this.profile === "a1" && leftWidth + 2 + visibleWidth(right) > width) {
|
|
110
|
+
right = [rightWithoutProvider, modelLevel].find(value => visibleWidth(value) + 2 <= width)
|
|
111
|
+
?? truncateToWidth(theme.fg("dim", modelName), width, dimEllipsis);
|
|
112
|
+
left = truncateToWidth(left, Math.max(0, width - visibleWidth(right) - 2), dimEllipsis);
|
|
113
|
+
leftWidth = visibleWidth(left);
|
|
114
|
+
}
|
|
112
115
|
const rightWidth = visibleWidth(right);
|
|
113
116
|
let line;
|
|
114
|
-
if (
|
|
117
|
+
if (this.profile === "a1" && leftWidth === 0) {
|
|
118
|
+
line = " ".repeat(Math.max(0, width - rightWidth)) + right;
|
|
119
|
+
}
|
|
120
|
+
else if (leftWidth + 2 + rightWidth <= width) {
|
|
115
121
|
line = left + " ".repeat(width - leftWidth - rightWidth) + right;
|
|
116
122
|
}
|
|
117
123
|
else {
|
|
@@ -124,13 +130,13 @@ export class SessionFooter {
|
|
|
124
130
|
line = left;
|
|
125
131
|
}
|
|
126
132
|
const lines = [
|
|
127
|
-
truncateToWidth(pathRow, width,
|
|
133
|
+
truncateToWidth(pathRow, width, dimEllipsis),
|
|
128
134
|
theme.fg("dim", left) + (this.profile === "a1" ? line.slice(left.length) : theme.fg("dim", line.slice(left.length))),
|
|
129
135
|
];
|
|
130
136
|
const statuses = view.status.footer?.extensionStatuses ?? [];
|
|
131
137
|
if (statuses.length > 0) {
|
|
132
138
|
const status = [...statuses].sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([, text]) => sanitize(text)).join(" ");
|
|
133
|
-
lines.push(truncateToWidth(status, width,
|
|
139
|
+
lines.push(truncateToWidth(status, width, dimEllipsis));
|
|
134
140
|
}
|
|
135
141
|
return lines;
|
|
136
142
|
}
|
|
@@ -150,11 +156,8 @@ function formatTokens(count) {
|
|
|
150
156
|
function formatCwd(cwd, home) {
|
|
151
157
|
if (!home)
|
|
152
158
|
return cwd;
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
const relativeToHome = relative(resolvedHome, resolvedCwd);
|
|
156
|
-
const inside = relativeToHome === "" || (relativeToHome !== ".." && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));
|
|
157
|
-
if (!inside)
|
|
159
|
+
const relativeToHome = relative(resolve(home), resolve(cwd));
|
|
160
|
+
if (isAbsolute(relativeToHome) || relativeToHome === ".." || relativeToHome.startsWith(`..${sep}`))
|
|
158
161
|
return cwd;
|
|
159
162
|
return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`;
|
|
160
163
|
}
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* keybindings/theme, canonical path handling, reverse-Tab scope cycling, and the shared bare-A1 modal
|
|
8
8
|
* frame with standalone title, standard outer rules, stable filter/status row, progressive result
|
|
9
9
|
* paging, aligned result columns, Session Tree selection roles, quiet in-field query syntax guidance,
|
|
10
|
-
* and one-line bottom dynamic feedback and searchable-dialog shortcut
|
|
11
|
-
* use the blue selection surface while preserving title and metadata
|
|
12
|
-
* title uses success green independently of keyboard selection, with
|
|
10
|
+
* an accent dialog-search marker, and one-line bottom dynamic feedback and searchable-dialog shortcut
|
|
11
|
+
* footer. Selected session rows use the blue selection surface while preserving title and metadata
|
|
12
|
+
* foregrounds; the active session title uses success green independently of keyboard selection, with
|
|
13
|
+
* ordinary text weight.
|
|
13
14
|
* Deviations: owned-modal-shortcut-hints, owned-resume-session-dialog,
|
|
14
|
-
* owned-standard-dialog-selection.
|
|
15
|
+
* owned-standard-dialog-selection, owned-dialog-search-marker.
|
|
15
16
|
*/
|
|
16
17
|
import { spawnSync } from "node:child_process";
|
|
17
18
|
import { existsSync, realpathSync } from "node:fs";
|
|
@@ -20,7 +21,7 @@ import * as os from "node:os";
|
|
|
20
21
|
import { Container, getKeybindings, Input, matchesKey, sliceByColumn, Spacer, Text, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
21
22
|
import { KeybindingsManager } from "../adjacent/core/keybindings.js";
|
|
22
23
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
23
|
-
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
24
|
+
import { addPiModalHeader, adoptPiModalFrame, renderPiModalSearchInput } from "../../modal-frame.js";
|
|
24
25
|
import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
25
26
|
const theme = new Proxy({}, {
|
|
26
27
|
get(_target, property) {
|
|
@@ -408,7 +409,7 @@ class SessionList {
|
|
|
408
409
|
render(width) {
|
|
409
410
|
const lines = [];
|
|
410
411
|
// Render search input
|
|
411
|
-
lines.push(...this.searchInput
|
|
412
|
+
lines.push(...renderPiModalSearchInput(this.searchInput, width));
|
|
412
413
|
lines.push(""); // Blank line after search
|
|
413
414
|
if (this.filteredSessions.length === 0) {
|
|
414
415
|
let emptyMessage;
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
* levels, and rendering exclusive radio-style default markers before level names, an item-adjacent
|
|
9
9
|
* active checkmark, and aligned muted descriptions. Bare A1 persists defaults immediately on Space,
|
|
10
10
|
* closes through the implicit Ctrl+C-capable selection-cancel action, and uses the shared compact
|
|
11
|
-
* semantic shortcut row with Type/search guidance
|
|
12
|
-
* colors use the owned theme and its explicit color mode. Selected rows use
|
|
13
|
-
* selection surface only around the rendered item span, with the existing
|
|
14
|
-
* level, and preserved current/default/description roles. The comparison
|
|
15
|
-
* pinned component.
|
|
11
|
+
* semantic shortcut row with Type/search guidance and an accent search marker without advertising that
|
|
12
|
+
* alias. All list and border colors use the owned theme and its explicit color mode. Selected rows use
|
|
13
|
+
* the shared bare-A1 blue selection surface only around the rendered item span, with the existing
|
|
14
|
+
* accent arrow, normal-text level, and preserved current/default/description roles. The comparison
|
|
15
|
+
* profile retains the public pinned component.
|
|
16
16
|
* Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading,
|
|
17
|
-
* owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection
|
|
17
|
+
* owned-thinking-selector-controls, owned-dialog-ctrl-c-cancel, owned-standard-dialog-selection,
|
|
18
|
+
* owned-dialog-search-marker.
|
|
18
19
|
*/
|
|
19
20
|
import { Container, type Focusable, SelectList } from "@earendil-works/pi-tui";
|
|
20
21
|
export type ThinkingSelectorLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|