@timurproko/a1 0.2.7-dev.1275 → 0.2.7-dev.1276
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/dist/app/session-shell/apps-dialog.d.ts +1 -1
- package/dist/app/session-shell/apps-dialog.js +2 -1
- package/dist/app/session-shell/dialog-coordinator.d.ts +4 -5
- package/dist/app/session-shell/dialog-coordinator.js +41 -12
- package/dist/app/session-shell/input-route.d.ts +23 -0
- package/dist/app/session-shell/input-route.js +59 -0
- package/dist/app/session-shell/session-selectors.d.ts +3 -3
- package/dist/app/session-shell/session-selectors.js +7 -2
- package/dist/app/session-shell/session-shell-root.js +6 -2
- package/dist/app/session-shell/session-shell.js +4 -2
- package/dist/app/session-shell/viewport-pointer.js +5 -1
- package/dist/app/session-shell/workflow-router.d.ts +1 -1
- package/dist/composition/settings-route-host.js +1 -0
- package/dist/contracts/owned-ui/shell-components.d.ts +10 -0
- package/dist/contracts/presentation/component.d.ts +7 -0
- package/dist/contracts/presentation/index.d.ts +2 -0
- package/dist/contracts/presentation/index.js +4 -0
- package/dist/contracts/presentation/progress-palette.generated.d.ts +24 -0
- package/dist/contracts/presentation/progress-palette.generated.js +1 -134
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +5 -5
- package/dist/features/owned-ui/project-trust-prompt.js +2 -2
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +4 -1
- package/dist/features/owned-ui/settings-app.js +6 -6
- package/dist/features/owned-ui/settings-model-dialog.js +3 -3
- package/dist/integrations/pi/components/deferred-selector.d.ts +6 -0
- package/dist/integrations/pi/components/deferred-selector.js +40 -0
- package/dist/integrations/pi/components/login-dialog-loader.d.ts +3 -0
- package/dist/integrations/pi/components/login-dialog-loader.js +57 -0
- package/dist/integrations/pi/components/login-links.d.ts +6 -0
- package/dist/integrations/pi/components/login-links.js +31 -0
- package/dist/integrations/pi/components/login-text.d.ts +7 -0
- package/dist/integrations/pi/components/login-text.js +17 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +5 -0
- package/dist/integrations/pi/components/modal-frame.js +30 -2
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +11 -8
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +36 -35
- package/dist/integrations/pi/components/shell-session-info-reference.d.ts +1 -1
- package/dist/integrations/pi/components/shell-session-info-reference.js +49 -46
- package/dist/integrations/pi/components/theme.d.ts +2 -0
- package/dist/integrations/pi/components/theme.js +4 -0
- package/dist/integrations/pi/components/upstream/components/auth-url.d.ts +16 -0
- package/dist/integrations/pi/components/upstream/components/auth-url.js +49 -0
- package/dist/integrations/pi/components/upstream/components/extension-selector.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +21 -10
- package/dist/integrations/pi/components/upstream/components/login-dialog.d.ts +3 -0
- package/dist/integrations/pi/components/upstream/components/login-dialog.js +216 -0
- package/dist/integrations/pi/components/upstream/components/oauth-selector.d.ts +19 -0
- package/dist/integrations/pi/components/upstream/components/oauth-selector.js +123 -0
- package/dist/integrations/pi/components/upstream/components/session-footer.js +10 -7
- package/dist/integrations/pi/components/upstream/components/trust-selector.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +29 -12
- package/dist/integrations/pi/session-presenters/components.d.ts +1 -1
- package/dist/integrations/pi/session-presenters/components.js +8 -8
- package/dist/integrations/pi/session-presenters/presenters.js +2 -2
- package/dist/ui/components/list-block.d.ts +2 -2
- package/dist/ui/components/list-block.js +4 -3
- package/dist/ui/settings/early-accent.d.ts +24 -0
- package/package.json +3 -3
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
|
|
3
|
+
* packages/coding-agent/src/modes/interactive/components/login-dialog.ts.
|
|
4
|
+
* Modifications: Adapt all provider login states to shared live-themed framing: title, immediate muted
|
|
5
|
+
* description, blank row before accent-chevron input and one-cell inset. Preserve prompt, callback,
|
|
6
|
+
* device/copy, browser, cancellation and nested lifecycle contracts through neutral runtime ports;
|
|
7
|
+
* retain the public component for comparison.
|
|
8
|
+
* Deviations: shared-login-template.
|
|
9
|
+
*/
|
|
10
|
+
import { Container, getKeybindings, Input, Spacer } from "@earendil-works/pi-tui";
|
|
11
|
+
import { copyToClipboard, DynamicBorder } from "../../../startup-public.js";
|
|
12
|
+
import { addPiModalHeader, adoptPiModalFrame, renderPiModalSearchInput } from "../../modal-frame.js";
|
|
13
|
+
import { componentPort } from "../../shell-shared-facade.js";
|
|
14
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, internalDialogTitle, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
15
|
+
import { loginLink, loginWebUrl, openLoginBrowser } from "../../login-links.js";
|
|
16
|
+
import { loginPlainText, loginText } from "../../login-text.js";
|
|
17
|
+
import { AuthUrlComponent } from "./auth-url.js";
|
|
18
|
+
/** Provider-independent login state presentation with the pinned interaction/effect contract. */
|
|
19
|
+
class LoginDialog extends Container {
|
|
20
|
+
#input = new Input();
|
|
21
|
+
#content = new Container();
|
|
22
|
+
#runtime;
|
|
23
|
+
#complete;
|
|
24
|
+
#abort = new AbortController();
|
|
25
|
+
#pending;
|
|
26
|
+
#auth;
|
|
27
|
+
#device;
|
|
28
|
+
#deviceHint;
|
|
29
|
+
#focused = false;
|
|
30
|
+
#closed = false;
|
|
31
|
+
constructor(runtime, providerId, complete, providerName, title) {
|
|
32
|
+
super();
|
|
33
|
+
this.#runtime = runtime;
|
|
34
|
+
this.#complete = complete;
|
|
35
|
+
const heading = internalDialogTitle(title ?? `Login to ${providerName ?? providerId}`);
|
|
36
|
+
const header = addPiModalHeader(this, new DynamicBorder(paintPiBorder), loginText(heading, "accent", true));
|
|
37
|
+
this.addChild(this.#content);
|
|
38
|
+
this.addChild(new Spacer(1));
|
|
39
|
+
this.addChild({
|
|
40
|
+
render: width => loginText(() => renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT])).render(width),
|
|
41
|
+
invalidate() { },
|
|
42
|
+
});
|
|
43
|
+
this.addChild(new DynamicBorder(paintPiBorder));
|
|
44
|
+
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
45
|
+
this.#input.onSubmit = value => {
|
|
46
|
+
const pending = this.#pending;
|
|
47
|
+
if (pending === undefined || this.#closed)
|
|
48
|
+
return;
|
|
49
|
+
this.#pending = undefined;
|
|
50
|
+
this.#content.children = this.#content.children.map(child => child === this.#inputView
|
|
51
|
+
? { render: width => loginText(() => `${piTheme().fg("accent", ">")} ${piTheme().fg("text", loginPlainText(value))}`).render(width), invalidate() { } }
|
|
52
|
+
: child);
|
|
53
|
+
pending.resolve(value);
|
|
54
|
+
this.#runtime.requestRender();
|
|
55
|
+
};
|
|
56
|
+
this.#input.onEscape = () => this.#cancel();
|
|
57
|
+
}
|
|
58
|
+
#inputView = {
|
|
59
|
+
render: width => renderPiModalSearchInput(this.#input, width),
|
|
60
|
+
invalidate: () => this.#input.invalidate(),
|
|
61
|
+
};
|
|
62
|
+
get focused() { return this.#focused; }
|
|
63
|
+
set focused(value) { this.#focused = value; this.#input.focused = value; }
|
|
64
|
+
showAuth(url, instructions) {
|
|
65
|
+
if (this.#closed)
|
|
66
|
+
return;
|
|
67
|
+
this.#clearContent();
|
|
68
|
+
if (instructions)
|
|
69
|
+
this.#content.addChild(loginText(instructions));
|
|
70
|
+
else
|
|
71
|
+
this.#content.addChild(loginText("Continue in your browser"));
|
|
72
|
+
this.#content.addChild(new Spacer(1));
|
|
73
|
+
this.#auth = new AuthUrlComponent(url, () => { if (!this.#closed)
|
|
74
|
+
this.#runtime.requestRender(); });
|
|
75
|
+
this.#content.addChild(this.#auth);
|
|
76
|
+
openLoginBrowser(url);
|
|
77
|
+
this.#runtime.requestRender();
|
|
78
|
+
}
|
|
79
|
+
showDeviceCode(info) {
|
|
80
|
+
if (this.#closed)
|
|
81
|
+
return;
|
|
82
|
+
this.#clearContent();
|
|
83
|
+
this.#content.addChild(loginText("Continue in your browser"));
|
|
84
|
+
this.#content.addChild(new Spacer(1));
|
|
85
|
+
this.#content.addChild(loginText(() => loginLink(info.verificationUri), "accent"));
|
|
86
|
+
const click = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
|
|
87
|
+
this.#content.addChild(loginText(() => loginLink(info.verificationUri, click), "dim"));
|
|
88
|
+
this.#content.addChild(new Spacer(1));
|
|
89
|
+
this.#content.addChild(loginText(`Enter code: ${info.userCode}`, "warning"));
|
|
90
|
+
const hint = new Container();
|
|
91
|
+
hint.addChild({ render: width => loginText(() => renderPiModalShortcutHints([{ key: getKeybindings().getKeys("tui.select.confirm").join("/"), action: "open browser and copy code" }])).render(width), invalidate() { } });
|
|
92
|
+
this.#content.addChild(hint);
|
|
93
|
+
this.#device = { info, hint };
|
|
94
|
+
this.#deviceHint = hint;
|
|
95
|
+
this.#runtime.requestRender();
|
|
96
|
+
}
|
|
97
|
+
showManualInput(message) { return this.showPrompt(message); }
|
|
98
|
+
showPrompt(message, placeholder) {
|
|
99
|
+
if (this.#closed)
|
|
100
|
+
return Promise.reject(new Error("Login cancelled"));
|
|
101
|
+
this.#clearDevice();
|
|
102
|
+
const context = [...this.#content.children];
|
|
103
|
+
// Invariant: the current description is directly under the title; retained auth context follows input.
|
|
104
|
+
this.#content.clear();
|
|
105
|
+
this.#content.addChild(loginText(message));
|
|
106
|
+
if (placeholder)
|
|
107
|
+
this.#content.addChild(loginText(`e.g., ${placeholder}`, "dim"));
|
|
108
|
+
this.#content.addChild(new Spacer(1));
|
|
109
|
+
this.#input.setValue("");
|
|
110
|
+
this.#content.addChild(this.#inputView);
|
|
111
|
+
if (context.length > 0) {
|
|
112
|
+
this.#content.addChild(new Spacer(1));
|
|
113
|
+
for (const child of context)
|
|
114
|
+
this.#content.addChild(child);
|
|
115
|
+
}
|
|
116
|
+
this.#runtime.requestRender();
|
|
117
|
+
return new Promise((resolve, reject) => { this.#pending = { resolve, reject }; });
|
|
118
|
+
}
|
|
119
|
+
showDetails(lines) {
|
|
120
|
+
if (this.#closed)
|
|
121
|
+
return;
|
|
122
|
+
this.#clearContent();
|
|
123
|
+
for (const line of lines)
|
|
124
|
+
this.#content.addChild(loginText(line));
|
|
125
|
+
this.#runtime.requestRender();
|
|
126
|
+
}
|
|
127
|
+
showInfo(message, links = []) {
|
|
128
|
+
if (this.#closed)
|
|
129
|
+
return;
|
|
130
|
+
this.#appendDescription(message);
|
|
131
|
+
for (const link of links)
|
|
132
|
+
this.#content.addChild(loginText(() => loginLink(link.url, link.label ? `${link.label}: ${loginWebUrl(link.url) ?? link.url}` : undefined), "accent"));
|
|
133
|
+
this.#runtime.requestRender();
|
|
134
|
+
}
|
|
135
|
+
showWaiting(message) { if (!this.#closed) {
|
|
136
|
+
this.#appendDescription(message);
|
|
137
|
+
this.#runtime.requestRender();
|
|
138
|
+
} }
|
|
139
|
+
showProgress(message) { if (!this.#closed) {
|
|
140
|
+
this.#content.addChild(loginText(message));
|
|
141
|
+
this.#runtime.requestRender();
|
|
142
|
+
} }
|
|
143
|
+
handleInput(data) {
|
|
144
|
+
if (this.#closed)
|
|
145
|
+
return;
|
|
146
|
+
const keys = getKeybindings();
|
|
147
|
+
if (keys.matches(data, "tui.select.cancel")) {
|
|
148
|
+
this.#cancel();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (this.#auth !== undefined && keys.matches(data, "app.message.copy")) {
|
|
152
|
+
void this.#auth.copy();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (this.#device !== undefined && this.#pending === undefined && this.#focused && keys.matches(data, "tui.select.confirm")) {
|
|
156
|
+
const { info, hint } = this.#device;
|
|
157
|
+
this.#device = undefined;
|
|
158
|
+
hint.clear();
|
|
159
|
+
hint.addChild(loginText("Copying code...", "dim"));
|
|
160
|
+
openLoginBrowser(info.verificationUri);
|
|
161
|
+
void copyToClipboard(info.userCode).then(() => this.#deviceStatus(hint, "Code copied to clipboard"), () => this.#deviceStatus(hint, "Copy code manually"));
|
|
162
|
+
this.#runtime.requestRender();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.#input.handleInput(data);
|
|
166
|
+
}
|
|
167
|
+
dispose() {
|
|
168
|
+
if (this.#closed)
|
|
169
|
+
return;
|
|
170
|
+
this.#closed = true;
|
|
171
|
+
this.#abort.abort();
|
|
172
|
+
this.#auth?.dispose();
|
|
173
|
+
this.#pending?.reject(new Error("Login cancelled"));
|
|
174
|
+
this.#pending = undefined;
|
|
175
|
+
}
|
|
176
|
+
#cancel() { this.dispose(); this.#complete(false, "Login cancelled"); }
|
|
177
|
+
#appendDescription(message) {
|
|
178
|
+
if (this.#content.children.length > 0)
|
|
179
|
+
this.#content.addChild(new Spacer(1));
|
|
180
|
+
this.#content.addChild(loginText(message));
|
|
181
|
+
}
|
|
182
|
+
#clearDevice() {
|
|
183
|
+
if (this.#deviceHint !== undefined)
|
|
184
|
+
this.#content.removeChild(this.#deviceHint);
|
|
185
|
+
this.#deviceHint = undefined;
|
|
186
|
+
this.#device = undefined;
|
|
187
|
+
}
|
|
188
|
+
#clearContent() {
|
|
189
|
+
this.#clearDevice();
|
|
190
|
+
this.#auth?.dispose();
|
|
191
|
+
this.#auth = undefined;
|
|
192
|
+
this.#content.clear();
|
|
193
|
+
}
|
|
194
|
+
#deviceStatus(hint, message) {
|
|
195
|
+
if (this.#closed || !this.#focused || !this.#content.children.includes(hint))
|
|
196
|
+
return;
|
|
197
|
+
hint.clear();
|
|
198
|
+
hint.addChild(loginText(message, "dim"));
|
|
199
|
+
this.#runtime.requestRender();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/** One owned login port for every provider, keeping all state methods on the shared template. */
|
|
203
|
+
export function createThemedLoginDialog(runtime, providerId, complete, providerName, title) {
|
|
204
|
+
const dialog = new LoginDialog(runtime, providerId, complete, providerName, title);
|
|
205
|
+
return {
|
|
206
|
+
...componentPort(dialog),
|
|
207
|
+
showAuth: (url, instructions) => dialog.showAuth(url, instructions),
|
|
208
|
+
showDeviceCode: info => dialog.showDeviceCode(info),
|
|
209
|
+
showManualInput: message => dialog.showManualInput(message),
|
|
210
|
+
showPrompt: (message, placeholder) => dialog.showPrompt(message, placeholder),
|
|
211
|
+
showDetails: lines => dialog.showDetails(lines),
|
|
212
|
+
showInfo: (message, links) => dialog.showInfo(message, links),
|
|
213
|
+
showWaiting: message => dialog.showWaiting(message),
|
|
214
|
+
showProgress: message => dialog.showProgress(message),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
|
|
3
|
+
* packages/coding-agent/src/modes/interactive/components/oauth-selector.ts.
|
|
4
|
+
* Modifications: Adapt the provider controller to injected neutral options, shared framing, live
|
|
5
|
+
* roles, Title Case and circular navigation. Configured status uses a muted dot, success-green
|
|
6
|
+
* configured text and trailing checkmark without method/source qualifiers. Omit decorative
|
|
7
|
+
* preferred-partner badges while retaining filtering, paging, actions and comparison presentation.
|
|
8
|
+
* Deviations: standard-sign-in-dialog-styling.
|
|
9
|
+
*/
|
|
10
|
+
import { Container } from "@earendil-works/pi-tui";
|
|
11
|
+
import type { OwnedUiShellAuthProviderOption as Provider } from "../../../../../contracts/owned-ui/index.js";
|
|
12
|
+
/** Owned provider controller with pinned content/status semantics and circular choice navigation. */
|
|
13
|
+
export declare class AuthProviderSelector extends Container {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(mode: "login" | "logout", providers: readonly Provider[], select: (id: string) => void, cancel: () => void, query?: string);
|
|
16
|
+
get focused(): boolean;
|
|
17
|
+
set focused(value: boolean);
|
|
18
|
+
handleInput(data: string): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
|
|
3
|
+
* packages/coding-agent/src/modes/interactive/components/oauth-selector.ts.
|
|
4
|
+
* Modifications: Adapt the provider controller to injected neutral options, shared framing, live
|
|
5
|
+
* roles, Title Case and circular navigation. Configured status uses a muted dot, success-green
|
|
6
|
+
* configured text and trailing checkmark without method/source qualifiers. Omit decorative
|
|
7
|
+
* preferred-partner badges while retaining filtering, paging, actions and comparison presentation.
|
|
8
|
+
* Deviations: standard-sign-in-dialog-styling.
|
|
9
|
+
*/
|
|
10
|
+
import { Container, fuzzyFilter, getKeybindings, getSelectPageIndex, Input, Spacer, Text, TruncatedText } from "@earendil-works/pi-tui";
|
|
11
|
+
import { wrapChoiceIndex } from "../../../../../contracts/presentation/index.js";
|
|
12
|
+
import { DynamicBorder } from "../../../startup-public.js";
|
|
13
|
+
import { addPiModalHeader, addPiModalSearchInput, adoptPiModalFrame } from "../../modal-frame.js";
|
|
14
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalListRow, renderPiModalShortcutHints } from "../../theme.js";
|
|
15
|
+
const MAX_VISIBLE = 8;
|
|
16
|
+
/** Owned provider controller with pinned content/status semantics and circular choice navigation. */
|
|
17
|
+
export class AuthProviderSelector extends Container {
|
|
18
|
+
#search = new Input();
|
|
19
|
+
#list = new Container();
|
|
20
|
+
#providers;
|
|
21
|
+
#select;
|
|
22
|
+
#cancel;
|
|
23
|
+
#showTypes;
|
|
24
|
+
#mode;
|
|
25
|
+
#filtered;
|
|
26
|
+
#index = 0;
|
|
27
|
+
constructor(mode, providers, select, cancel, query = "") {
|
|
28
|
+
super();
|
|
29
|
+
this.#providers = providers;
|
|
30
|
+
this.#filtered = providers;
|
|
31
|
+
this.#select = select;
|
|
32
|
+
this.#cancel = cancel;
|
|
33
|
+
this.#mode = mode;
|
|
34
|
+
this.#showTypes = new Set(providers.map(provider => provider.authType)).size > 1;
|
|
35
|
+
this.#search.setValue(query);
|
|
36
|
+
this.#search.onSubmit = () => this.#selectCurrent();
|
|
37
|
+
const title = mode === "login" ? "Select Provider to Configure" : "Select Provider to Log Out";
|
|
38
|
+
const header = addPiModalHeader(this, new DynamicBorder(paintPiBorder), {
|
|
39
|
+
render: width => new TruncatedText(piTheme().fg("accent", piTheme().bold(title)), 0, 0).render(width),
|
|
40
|
+
invalidate: () => { },
|
|
41
|
+
});
|
|
42
|
+
this.addChild(new Spacer(1));
|
|
43
|
+
addPiModalSearchInput(this, this.#search);
|
|
44
|
+
this.addChild(new Spacer(1));
|
|
45
|
+
this.addChild(this.#list);
|
|
46
|
+
this.addChild(new Spacer(1));
|
|
47
|
+
this.addChild(new Text(renderPiModalShortcutHints([
|
|
48
|
+
{ key: "↑↓", action: "navigate" },
|
|
49
|
+
{ key: getKeybindings().getKeys("tui.select.confirm").join("/"), action: "select" },
|
|
50
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
51
|
+
]), 0, 0));
|
|
52
|
+
this.addChild(new DynamicBorder(paintPiBorder));
|
|
53
|
+
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
54
|
+
this.#filter();
|
|
55
|
+
}
|
|
56
|
+
get focused() { return this.#search.focused; }
|
|
57
|
+
set focused(value) { this.#search.focused = value; }
|
|
58
|
+
handleInput(data) {
|
|
59
|
+
const keys = getKeybindings();
|
|
60
|
+
const page = getSelectPageIndex(keys, data, this.#index, this.#filtered.length, MAX_VISIBLE);
|
|
61
|
+
if (keys.matches(data, "tui.select.up") || keys.matches(data, "tui.select.down")) {
|
|
62
|
+
this.#index = wrapChoiceIndex(this.#index, keys.matches(data, "tui.select.down") ? 1 : -1, this.#filtered.length);
|
|
63
|
+
}
|
|
64
|
+
else if (page !== undefined)
|
|
65
|
+
this.#index = page;
|
|
66
|
+
else if (keys.matches(data, "tui.select.confirm")) {
|
|
67
|
+
this.#selectCurrent();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
else if (keys.matches(data, "tui.select.cancel")) {
|
|
71
|
+
this.#cancel();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.#search.handleInput(data);
|
|
76
|
+
this.#filter();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.#update();
|
|
80
|
+
}
|
|
81
|
+
#selectCurrent() {
|
|
82
|
+
const provider = this.#filtered[this.#index];
|
|
83
|
+
if (provider !== undefined)
|
|
84
|
+
this.#select(provider.id);
|
|
85
|
+
}
|
|
86
|
+
#filter() {
|
|
87
|
+
const query = this.#search.getValue();
|
|
88
|
+
this.#filtered = query ? fuzzyFilter([...this.#providers], query, provider => `${provider.label} ${provider.providerId} ${provider.authType} `) : this.#providers;
|
|
89
|
+
this.#index = Math.min(this.#index, Math.max(0, this.#filtered.length - 1));
|
|
90
|
+
this.#update();
|
|
91
|
+
}
|
|
92
|
+
#update() {
|
|
93
|
+
this.#list.clear();
|
|
94
|
+
const start = Math.max(0, Math.min(this.#index - Math.floor(MAX_VISIBLE / 2), this.#filtered.length - MAX_VISIBLE));
|
|
95
|
+
const end = Math.min(start + MAX_VISIBLE, this.#filtered.length);
|
|
96
|
+
for (let index = start; index < end; index++) {
|
|
97
|
+
const provider = this.#filtered[index];
|
|
98
|
+
const selected = index === this.#index;
|
|
99
|
+
this.#list.addChild({
|
|
100
|
+
render: width => {
|
|
101
|
+
const theme = piTheme();
|
|
102
|
+
const prefix = selected ? theme.fg("accent", "→ ") : " ";
|
|
103
|
+
const type = this.#showTypes ? theme.fg("muted", ` [${provider.authType === "api_key" ? "API key" : "subscription"}]`) : "";
|
|
104
|
+
return [renderPiModalListRow(`${prefix}${theme.fg("text", provider.label)}${type}${providerStatus(provider)}`, width, selected)];
|
|
105
|
+
},
|
|
106
|
+
invalidate: () => { },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (start > 0 || end < this.#filtered.length)
|
|
110
|
+
this.#list.addChild(new TruncatedText(piTheme().fg("muted", ` (${this.#index + 1}/${this.#filtered.length})`), 0, 0));
|
|
111
|
+
if (this.#filtered.length === 0) {
|
|
112
|
+
const empty = this.#mode === "login" ? "No providers available" : "No providers logged in. Use /login first.";
|
|
113
|
+
this.#list.addChild(new TruncatedText(piTheme().fg("muted", ` ${this.#providers.length === 0 ? empty : "No matching providers"}`), 0, 0));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function providerStatus(provider) {
|
|
118
|
+
const theme = piTheme();
|
|
119
|
+
const separator = theme.fg("muted", " • ");
|
|
120
|
+
if (provider.status === undefined)
|
|
121
|
+
return separator + theme.fg("muted", "not configured");
|
|
122
|
+
return separator + theme.fg("success", "configured ✓");
|
|
123
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/footer.ts.
|
|
4
|
-
* Modifications:
|
|
5
|
-
*
|
|
4
|
+
* Modifications: A1 PR links, model priority, level colors, secondary-accent name reveals, dim
|
|
5
|
+
* name/row clipping; Pi intact.
|
|
6
6
|
* Deviations: owned-status-level-color, owned-pull-request-badge, owned-footer-ellipsis,
|
|
7
7
|
* owned-footer-model-priority, owned-session-name-reveal.
|
|
8
8
|
*/
|
|
9
9
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
10
|
-
import { hyperlink, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
10
|
+
import { hyperlink, stripTerminalSequences, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
11
11
|
import { piTheme } from "../../theme.js";
|
|
12
12
|
import { sessionNameFrame } from "../../../../../ui/components/session-name-frame.js";
|
|
13
13
|
export class SessionFooter {
|
|
14
|
+
#reveal = null;
|
|
15
|
+
#nameBudget = Number.MAX_SAFE_INTEGER;
|
|
14
16
|
getView;
|
|
15
17
|
cwd;
|
|
16
18
|
profile;
|
|
17
19
|
show;
|
|
18
|
-
#reveal = null;
|
|
19
|
-
#nameBudget = Number.MAX_SAFE_INTEGER;
|
|
20
20
|
constructor(getView, cwd, profile = "pi", show = () => true) {
|
|
21
21
|
this.getView = getView;
|
|
22
22
|
this.cwd = cwd;
|
|
@@ -35,14 +35,17 @@ export class SessionFooter {
|
|
|
35
35
|
const theme = piTheme();
|
|
36
36
|
const secondary = rgb(theme.getFgAnsi("mdHeading"));
|
|
37
37
|
const settled = rgb(theme.getFgAnsi("dim"));
|
|
38
|
+
const clip = (text, columns) => stripTerminalSequences(truncateToWidth(text, columns, "…"));
|
|
38
39
|
if (!this.#reveal)
|
|
39
|
-
return { text: theme.fg("dim",
|
|
40
|
+
return { text: theme.fg("dim", clip(name, width)), done: true };
|
|
40
41
|
return sessionNameFrame({
|
|
41
42
|
...this.#reveal, to: name, width,
|
|
42
43
|
truecolor: theme.getColorMode() === "truecolor" && secondary !== null && settled !== null,
|
|
43
|
-
measure: visibleWidth, clip
|
|
44
|
+
measure: visibleWidth, clip,
|
|
44
45
|
dim: text => theme.fg("dim", text),
|
|
45
46
|
paint: (text, brightness, progress) => {
|
|
47
|
+
if (text === "…")
|
|
48
|
+
return theme.fg("dim", text);
|
|
46
49
|
const channels = secondary.map((value, index) => {
|
|
47
50
|
const bright = value + (235 - value) * brightness;
|
|
48
51
|
return Math.round(bright + (settled[index] - bright) * progress);
|
|
@@ -17,9 +17,11 @@ export declare class TrustSelectorComponent extends Container {
|
|
|
17
17
|
private selectedIndex;
|
|
18
18
|
private readonly listContainer;
|
|
19
19
|
private readonly trustOptions;
|
|
20
|
+
private readonly standardStyle;
|
|
20
21
|
readonly handleInput: (data: string) => void;
|
|
21
22
|
constructor(options: {
|
|
22
23
|
readonly cwd: string;
|
|
24
|
+
readonly standardStyle?: boolean;
|
|
23
25
|
readonly savedDecision: TrustDecision | null;
|
|
24
26
|
readonly projectTrusted: boolean;
|
|
25
27
|
readonly trustOptions: readonly TrustOption[];
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/trust-selector.ts.
|
|
4
|
-
* Modifications:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Deviations: owned-modal-shortcut-hints.
|
|
4
|
+
* Modifications: Source-synchronized trust port with injected public options and shared framing; bare
|
|
5
|
+
* A1 opts into Title Case, selected backgrounds, circular navigation, aligned decisions and no
|
|
6
|
+
* saved-path suffix while comparison styling and trust effects remain unchanged.
|
|
7
|
+
* Deviations: owned-modal-shortcut-hints, standard-trust-dialog-styling.
|
|
9
8
|
*/
|
|
9
|
+
import { wrapChoiceIndex } from "../../../../../contracts/presentation/index.js";
|
|
10
10
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
11
11
|
import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
|
|
12
12
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
13
|
-
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
13
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints, renderPiModalListRow } from "../../theme.js";
|
|
14
14
|
function formatDecision(trustPath, decision) {
|
|
15
15
|
if (decision === null)
|
|
16
16
|
return "none";
|
|
@@ -23,18 +23,23 @@ export class TrustSelectorComponent extends Container {
|
|
|
23
23
|
selectedIndex;
|
|
24
24
|
listContainer;
|
|
25
25
|
trustOptions;
|
|
26
|
+
standardStyle;
|
|
26
27
|
handleInput;
|
|
27
28
|
constructor(options) {
|
|
28
29
|
super();
|
|
29
30
|
this.trustOptions = options.trustOptions;
|
|
31
|
+
this.standardStyle = options.standardStyle === true;
|
|
30
32
|
const isSaved = (option) => option.savedPath !== undefined
|
|
31
33
|
&& options.savedDecision?.decision === option.trusted
|
|
32
34
|
&& options.savedDecision.path === option.savedPath;
|
|
33
35
|
this.selectedIndex = Math.max(0, this.trustOptions.findIndex(isSaved));
|
|
34
|
-
const header = addPiModalHeader(this, new DynamicBorder(text => piTheme().fg("border", text)), new Text(piTheme().fg("accent", piTheme().bold("Project trust")), 0, 0));
|
|
36
|
+
const header = addPiModalHeader(this, new DynamicBorder(text => piTheme().fg("border", text)), new Text(piTheme().fg("accent", piTheme().bold(this.standardStyle ? "Project Trust" : "Project trust")), 0, 0));
|
|
35
37
|
this.addChild(new Text(piTheme().fg("muted", options.cwd), 0, 0));
|
|
36
38
|
this.addChild(new Spacer(1));
|
|
37
|
-
|
|
39
|
+
const saved = this.standardStyle
|
|
40
|
+
? options.savedDecision === null ? "none" : options.savedDecision.decision ? "trusted" : "untrusted"
|
|
41
|
+
: formatDecision(this.trustOptions[0]?.savedPath, options.savedDecision);
|
|
42
|
+
this.addChild(new Text(`${piTheme().fg("muted", "Saved decision:")}${this.standardStyle ? " " : " "}${piTheme().fg("text", saved)}`, 0, 0));
|
|
38
43
|
this.addChild(new Text(`${piTheme().fg("muted", "Current session:")} ${piTheme().fg("text", options.projectTrusted ? "trusted" : "untrusted")}`, 0, 0));
|
|
39
44
|
this.addChild(new Spacer(1));
|
|
40
45
|
this.listContainer = new Container();
|
|
@@ -52,9 +57,9 @@ export class TrustSelectorComponent extends Container {
|
|
|
52
57
|
this.handleInput = data => {
|
|
53
58
|
const kb = getKeybindings();
|
|
54
59
|
if (kb.matches(data, "tui.select.up") || data === "k")
|
|
55
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
60
|
+
this.selectedIndex = this.standardStyle ? wrapChoiceIndex(this.selectedIndex, -1, this.trustOptions.length) : Math.max(0, this.selectedIndex - 1);
|
|
56
61
|
else if (kb.matches(data, "tui.select.down") || data === "j")
|
|
57
|
-
this.selectedIndex = Math.min(this.trustOptions.length - 1, this.selectedIndex + 1);
|
|
62
|
+
this.selectedIndex = this.standardStyle ? wrapChoiceIndex(this.selectedIndex, 1, this.trustOptions.length) : Math.min(this.trustOptions.length - 1, this.selectedIndex + 1);
|
|
58
63
|
else if (kb.matches(data, "tui.select.confirm") || data === "\n") {
|
|
59
64
|
const selected = this.trustOptions[this.selectedIndex];
|
|
60
65
|
if (selected)
|
|
@@ -78,8 +83,20 @@ export class TrustSelectorComponent extends Container {
|
|
|
78
83
|
continue;
|
|
79
84
|
const selected = index === this.selectedIndex;
|
|
80
85
|
const prefix = selected ? piTheme().fg("accent", "→ ") : " ";
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
if (this.standardStyle) {
|
|
87
|
+
this.listContainer.addChild({
|
|
88
|
+
render: width => {
|
|
89
|
+
const theme = piTheme();
|
|
90
|
+
const arrow = selected ? theme.fg("accent", "→ ") : " ";
|
|
91
|
+
return [renderPiModalListRow(`${arrow}${theme.fg("text", option.label)}`, width, selected)];
|
|
92
|
+
},
|
|
93
|
+
invalidate: () => { },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const label = selected ? piTheme().fg("accent", option.label) : piTheme().fg("text", option.label);
|
|
98
|
+
this.listContainer.addChild(new Text(`${prefix}${label}`, 0, 0));
|
|
99
|
+
}
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { OwnedUiShellComponents, OwnedUiShellLazySelectorLoader } from "../../../contracts/owned-ui/index.js";
|
|
2
2
|
/** The Pi components and helpers behind the shell's neutral component record. */
|
|
3
|
-
export declare function createPiShellComponents(loadLazySelectors: () => Promise<OwnedUiShellLazySelectorLoader
|
|
3
|
+
export declare function createPiShellComponents(loadLazySelectors: () => Promise<OwnedUiShellLazySelectorLoader>, bare?: boolean): OwnedUiShellComponents;
|
|
@@ -9,7 +9,7 @@ import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission
|
|
|
9
9
|
import { ProgramStatusReporter } from "../components/upstream/program-status-reporter.js";
|
|
10
10
|
import { workflowCommandNames } from "../engine/workflows.js";
|
|
11
11
|
/** The Pi components and helpers behind the shell's neutral component record. */
|
|
12
|
-
export function createPiShellComponents(loadLazySelectors) {
|
|
12
|
+
export function createPiShellComponents(loadLazySelectors, bare = false) {
|
|
13
13
|
return {
|
|
14
14
|
editor: { create: createPiShellEditor },
|
|
15
15
|
chrome: {
|
|
@@ -42,14 +42,14 @@ export function createPiShellComponents(loadLazySelectors) {
|
|
|
42
42
|
reloadBox: createPiShellReloadBox,
|
|
43
43
|
},
|
|
44
44
|
dialogs: {
|
|
45
|
-
dialog: createPiShellDialog,
|
|
46
|
-
selector: createPiShellSelector,
|
|
47
|
-
extensionSelector: createPiShellExtensionSelector,
|
|
48
|
-
authProviderSelector: createPiShellAuthProviderSelector,
|
|
49
|
-
loginDialog: createPiShellLoginDialog,
|
|
45
|
+
dialog: (dialog, handlers) => createPiShellDialog(dialog, handlers, bare),
|
|
46
|
+
selector: options => createPiShellSelector(options, bare),
|
|
47
|
+
extensionSelector: (title, values, select, cancel) => createPiShellExtensionSelector(title, values, select, cancel, bare),
|
|
48
|
+
authProviderSelector: (mode, providers, select, cancel, query) => createPiShellAuthProviderSelector(mode, providers, select, cancel, query, bare),
|
|
49
|
+
loginDialog: (runtime, id, complete, name, title) => createPiShellLoginDialog(runtime, id, complete, name, title, bare),
|
|
50
50
|
skillsSelector: createPiShellSkillsSelector,
|
|
51
|
-
trustSelector: createPiShellTrustSelector,
|
|
52
|
-
userMessageSelector: createPiShellUserMessageSelector,
|
|
51
|
+
trustSelector: options => createPiShellTrustSelector({ ...options, standardStyle: bare }),
|
|
52
|
+
userMessageSelector: (messages, select, cancel, initial) => createPiShellUserMessageSelector(messages, select, cancel, initial, bare),
|
|
53
53
|
async shareProgress(runtime, message, style) {
|
|
54
54
|
// Rationale: sharing is rare, so its progress surfaces stay off the startup graph.
|
|
55
55
|
const surfaces = await import("../components/share-operation-dialog.js");
|
|
@@ -32,13 +32,13 @@ export function createPiSessionPresenters(adapter, options = {}) {
|
|
|
32
32
|
// Compatibility: the component adapter passes Pi's own keybinding config through the neutral port.
|
|
33
33
|
getShortcuts: bindings => source().pinnedShortcutDescriptions(bindings),
|
|
34
34
|
};
|
|
35
|
-
return { ...PI_SHELL_PRESENTATION, components: createPiShellComponents(loadLazySelectors),
|
|
35
|
+
return { ...PI_SHELL_PRESENTATION, components: createPiShellComponents(loadLazySelectors, adapter.settings.productMode === "bare"),
|
|
36
36
|
transcriptRenderers: () => renderers,
|
|
37
37
|
openModelSelector(host, initialSearchInput) {
|
|
38
38
|
const context = source().pinnedModelSelectorContext();
|
|
39
39
|
const close = closeSurface(host);
|
|
40
40
|
const component = createPiShellModelSelector({
|
|
41
|
-
...context,
|
|
41
|
+
...context, standardStyle: adapter.settings.productMode === "bare",
|
|
42
42
|
runtime: {
|
|
43
43
|
getColumns: () => host.viewport().columns,
|
|
44
44
|
getRows: () => host.viewport().rows,
|
|
@@ -27,8 +27,8 @@ export interface ListRowSpan {
|
|
|
27
27
|
export declare function rowKey<T>(row: ListRow<T> | undefined): string | undefined;
|
|
28
28
|
export declare function selectableIndexes<T>(rows: readonly ListRow<T>[]): readonly number[];
|
|
29
29
|
export declare function indexOfKey<T>(rows: readonly ListRow<T>[], key: string | undefined): number;
|
|
30
|
-
/**
|
|
31
|
-
export declare function moveSelection<T>(rows: readonly ListRow<T>[], current: number, delta: number): number;
|
|
30
|
+
/** Moves between selectable rows; choice lists can opt into circular navigation. */
|
|
31
|
+
export declare function moveSelection<T>(rows: readonly ListRow<T>[], current: number, delta: number, wrap?: boolean): number;
|
|
32
32
|
/**
|
|
33
33
|
* Forward lands on the first element of the next group. Backward lands on the
|
|
34
34
|
* first element of the current group, or the previous group's when already
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* the top visible row belongs to a group, that group's header stays pinned as
|
|
4
4
|
* the first rendered row, so the reader always knows which group is on screen.
|
|
5
5
|
*/
|
|
6
|
+
import { wrapChoiceIndex } from "../../contracts/presentation/index.js";
|
|
6
7
|
export function rowKey(row) {
|
|
7
8
|
return row !== undefined && row.kind === "element" && row.selectable ? row.key : undefined;
|
|
8
9
|
}
|
|
@@ -22,15 +23,15 @@ export function indexOfKey(rows, key) {
|
|
|
22
23
|
}
|
|
23
24
|
return selectableIndexes(rows)[0] ?? -1;
|
|
24
25
|
}
|
|
25
|
-
/**
|
|
26
|
-
export function moveSelection(rows, current, delta) {
|
|
26
|
+
/** Moves between selectable rows; choice lists can opt into circular navigation. */
|
|
27
|
+
export function moveSelection(rows, current, delta, wrap = false) {
|
|
27
28
|
const selectable = selectableIndexes(rows);
|
|
28
29
|
if (selectable.length === 0)
|
|
29
30
|
return -1;
|
|
30
31
|
const position = selectable.indexOf(current);
|
|
31
32
|
if (position < 0)
|
|
32
33
|
return selectable[0] ?? -1;
|
|
33
|
-
const next = Math.min(Math.max(position + delta, 0), selectable.length - 1);
|
|
34
|
+
const next = wrap ? wrapChoiceIndex(position, delta, selectable.length) : Math.min(Math.max(position + delta, 0), selectable.length - 1);
|
|
34
35
|
return selectable[next] ?? current;
|
|
35
36
|
}
|
|
36
37
|
function firstElementOfBlock(rows, headerIndex) {
|