@linxiraos/pi-tui 1.2.1 → 1.3.1
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 +245 -224
- package/README.md +36 -0
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/app-keybindings.d.ts +18 -8
- package/dist/types/apps/git/diff-pane.d.ts +5 -0
- package/dist/types/apps/git/git-tui.d.ts +16 -2
- package/dist/types/apps/git/state.d.ts +11 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/charts/chart-svg.d.ts +7 -13
- package/dist/types/chat/advisor-message.d.ts +4 -1
- package/dist/types/chat/assistant-message.d.ts +9 -4
- package/dist/types/chat/figure-markdown.d.ts +7 -2
- package/dist/types/chat/messages.d.ts +3 -1
- package/dist/types/chat/read-tool-group.d.ts +2 -2
- package/dist/types/chat/table-chart.d.ts +11 -15
- package/dist/types/chat/tool-execution.d.ts +32 -20
- package/dist/types/chat/user-message.d.ts +3 -2
- package/dist/types/chrome/countdown-timer.d.ts +3 -2
- package/dist/types/chrome/diff.d.ts +2 -3
- package/dist/types/chrome/status-notice.d.ts +2 -0
- package/dist/types/chrome/transcript-container.d.ts +2 -0
- package/dist/types/components/image.d.ts +2 -0
- package/dist/types/components/markdown.d.ts +49 -3
- package/dist/types/components/text.d.ts +2 -0
- package/dist/types/debug-server.d.ts +2 -0
- package/dist/types/keybinding-matchers.d.ts +6 -0
- package/dist/types/multiplexers/tmux.d.ts +1 -0
- package/dist/types/multiplexers/types.d.ts +6 -0
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +24 -12
- package/dist/types/overlays/agent-hub-projection.d.ts +10 -5
- package/dist/types/overlays/annotation-overlay.d.ts +19 -0
- package/dist/types/overlays/approval-dialog.d.ts +79 -0
- package/dist/types/overlays/ask-dialog.d.ts +11 -6
- package/dist/types/overlays/btw-history-panel.d.ts +2 -6
- package/dist/types/overlays/hook-editor.d.ts +7 -2
- package/dist/types/overlays/hook-input.d.ts +12 -4
- package/dist/types/overlays/job-tree.d.ts +50 -0
- package/dist/types/overlays/jobs-panel.d.ts +25 -11
- package/dist/types/overlays/login-dialog.d.ts +8 -3
- package/dist/types/overlays/mcp-add-wizard.d.ts +9 -1
- package/dist/types/overlays/model-browser.d.ts +6 -1
- package/dist/types/overlays/model-picker.d.ts +2 -0
- package/dist/types/overlays/pause-screen.d.ts +5 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +4 -0
- package/dist/types/overlays/report-panel.d.ts +5 -1
- package/dist/types/overlays/reset-usage-selector.d.ts +7 -5
- package/dist/types/overlays/session-account-selector.d.ts +4 -0
- package/dist/types/overlays/session-info-overlay.d.ts +7 -3
- package/dist/types/overlays/session-selector.d.ts +3 -1
- package/dist/types/overlays/usage-dashboard.d.ts +6 -6
- package/dist/types/prompt/composer.d.ts +23 -20
- package/dist/types/prompt/custom-editor.d.ts +20 -10
- package/dist/types/prompt/image-references.d.ts +22 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
- package/dist/types/prompt/macos-spelling.d.ts +2 -1
- package/dist/types/prompt/prose-gate.d.ts +3 -0
- package/dist/types/prompt/queued-messages.d.ts +1 -1
- package/dist/types/prompt/typing-word.d.ts +25 -0
- package/dist/types/prompt/welcome.d.ts +26 -6
- package/dist/types/render/hanging-wrap.d.ts +7 -0
- package/dist/types/render/hyperlink.d.ts +2 -0
- package/dist/types/render/render-utils.d.ts +9 -1
- package/dist/types/render/utils.d.ts +1 -1
- package/dist/types/setup/scenes/splash.d.ts +6 -5
- package/dist/types/status-line/context-usage.d.ts +4 -1
- package/dist/types/status-line/host.d.ts +6 -4
- package/dist/types/status-line/startup.d.ts +6 -6
- package/dist/types/status-line/types.d.ts +3 -0
- package/dist/types/stdin-buffer.d.ts +7 -0
- package/dist/types/terminal-capabilities.d.ts +16 -1
- package/dist/types/terminal-multiplexer.d.ts +7 -0
- package/dist/types/terminal.d.ts +8 -4
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme.d.ts +11 -0
- package/dist/types/thinking.d.ts +5 -0
- package/dist/types/tmux.d.ts +39 -4
- package/dist/types/tools/ast-edit.d.ts +2 -2
- package/dist/types/tools/ast-grep.d.ts +2 -2
- package/dist/types/tools/bash.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +53 -4
- package/dist/types/tools/find.d.ts +14 -4
- package/dist/types/tools/glob.d.ts +3 -3
- package/dist/types/tools/grep.d.ts +2 -2
- package/dist/types/tools/grouped-file-output.d.ts +9 -4
- package/dist/types/tools/native-view.d.ts +1 -1
- package/dist/types/tools/read.d.ts +32 -10
- package/dist/types/tools/renderer.d.ts +2 -0
- package/dist/types/tools/task.d.ts +31 -0
- package/dist/types/tools/web-search-types.d.ts +1 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/tui.d.ts +20 -3
- package/package.json +10 -10
- package/src/app-keybindings.d.ts +18 -8
- package/src/app-keybindings.ts +20 -8
- package/src/apps/autoresearch-dashboard.ts +444 -27
- package/src/apps/debug/raw-sse.ts +2 -2
- package/src/apps/git/diff-pane.ts +39 -41
- package/src/apps/git/git-tui.ts +80 -14
- package/src/apps/git/sidebar.ts +10 -1
- package/src/apps/git/state.ts +12 -0
- package/src/autocomplete.d.ts +2 -0
- package/src/autocomplete.ts +21 -19
- package/src/bracketed-paste.ts +9 -2
- package/src/charts/chart-svg.d.ts +7 -13
- package/src/charts/chart-svg.ts +19 -22
- package/src/charts/table-data.ts +17 -6
- package/src/chat/advisor-message.d.ts +4 -1
- package/src/chat/advisor-message.ts +77 -24
- package/src/chat/assistant-message.d.ts +9 -4
- package/src/chat/assistant-message.ts +127 -31
- package/src/chat/bash-execution.ts +7 -2
- package/src/chat/figure-markdown.d.ts +7 -2
- package/src/chat/figure-markdown.ts +10 -3
- package/src/chat/image-loading.ts +9 -6
- package/src/chat/messages.d.ts +3 -1
- package/src/chat/messages.ts +4 -1
- package/src/chat/read-tool-group.d.ts +2 -2
- package/src/chat/read-tool-group.ts +97 -42
- package/src/chat/svg-figure.ts +3 -1
- package/src/chat/svg-source.ts +6 -3
- package/src/chat/table-chart.d.ts +11 -15
- package/src/chat/table-chart.ts +24 -57
- package/src/chat/tool-execution.d.ts +32 -20
- package/src/chat/tool-execution.ts +103 -36
- package/src/chat/user-message.d.ts +3 -2
- package/src/chat/user-message.ts +54 -34
- package/src/chrome/countdown-timer.ts +3 -3
- package/src/chrome/diff.d.ts +2 -3
- package/src/chrome/diff.ts +4 -9
- package/src/chrome/status-notice.ts +5 -0
- package/src/chrome/transcript-container.d.ts +2 -0
- package/src/chrome/transcript-container.ts +212 -28
- package/src/components/editor.ts +12 -12
- package/src/components/image.d.ts +2 -0
- package/src/components/image.ts +54 -20
- package/src/components/markdown.d.ts +49 -3
- package/src/components/markdown.ts +482 -12
- package/src/components/settings-list.ts +2 -4
- package/src/components/text.d.ts +2 -0
- package/src/components/text.ts +11 -1
- package/src/debug-server.d.ts +2 -0
- package/src/debug-server.ts +51 -69
- package/src/hotkeys-markdown.ts +2 -0
- package/src/keybinding-matchers.d.ts +6 -0
- package/src/keybinding-matchers.ts +19 -0
- package/src/multiplexers/tmux.d.ts +1 -0
- package/src/multiplexers/tmux.ts +3 -0
- package/src/multiplexers/types.d.ts +6 -0
- package/src/multiplexers/types.ts +6 -0
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +13 -5
- package/src/native/blobs.d.ts +24 -12
- package/src/native/blobs.ts +56 -7
- package/src/native/reconcile.ts +43 -21
- package/src/overlays/advisor-config.ts +259 -8
- package/src/overlays/agent-hub-projection.d.ts +10 -5
- package/src/overlays/agent-hub-projection.ts +63 -12
- package/src/overlays/annotation-overlay.ts +422 -168
- package/src/overlays/approval-dialog.ts +389 -0
- package/src/overlays/ask-dialog.ts +244 -105
- package/src/overlays/btw-history-panel.ts +208 -147
- package/src/overlays/extensions/extension-list.ts +4 -6
- package/src/overlays/hook-editor.ts +90 -32
- package/src/overlays/hook-input.ts +105 -19
- package/src/overlays/job-tree.ts +177 -0
- package/src/overlays/jobs-panel.ts +222 -142
- package/src/overlays/login-dialog.ts +201 -91
- package/src/overlays/mcp-add-wizard.ts +562 -279
- package/src/overlays/model-browser.ts +32 -7
- package/src/overlays/model-hub.ts +4 -10
- package/src/overlays/model-picker.ts +58 -21
- package/src/overlays/pause-screen.ts +81 -23
- package/src/overlays/plan-review-overlay.ts +16 -0
- package/src/overlays/plugin-selector.ts +17 -1
- package/src/overlays/queue-mode-selector.ts +10 -0
- package/src/overlays/report-panel.ts +6 -2
- package/src/overlays/reset-usage-selector.ts +130 -21
- package/src/overlays/session-account-selector.ts +36 -0
- package/src/overlays/session-info-overlay.ts +199 -76
- package/src/overlays/session-selector.ts +15 -10
- package/src/overlays/settings-selector.ts +3 -0
- package/src/overlays/show-images-selector.ts +1 -0
- package/src/overlays/snapcompact-shape-preview.ts +13 -6
- package/src/overlays/theme-selector.ts +6 -1
- package/src/overlays/thinking-selector.ts +8 -4
- package/src/overlays/tree-selector.ts +117 -2
- package/src/overlays/usage-dashboard.ts +498 -144
- package/src/prompt/composer-cache.ts +4 -1
- package/src/prompt/composer.ts +200 -74
- package/src/prompt/custom-editor.ts +168 -65
- package/src/prompt/image-references.d.ts +22 -0
- package/src/prompt/image-references.ts +79 -2
- package/src/prompt/internal-url-autocomplete.ts +1 -0
- package/src/prompt/macos-spelling.ts +28 -5
- package/src/prompt/prose-gate.ts +3 -0
- package/src/prompt/queued-messages.ts +9 -2
- package/src/prompt/tips.txt +1 -2
- package/src/prompt/typing-word.ts +59 -0
- package/src/prompt/welcome.ts +97 -45
- package/src/render/code-cell.ts +18 -5
- package/src/render/hanging-wrap.d.ts +7 -0
- package/src/render/hanging-wrap.ts +58 -0
- package/src/render/hyperlink.d.ts +2 -0
- package/src/render/hyperlink.ts +5 -0
- package/src/render/render-utils.d.ts +9 -1
- package/src/render/render-utils.ts +16 -2
- package/src/render/utils.d.ts +1 -1
- package/src/render/utils.ts +1 -1
- package/src/render/width-aware-text.ts +6 -5
- package/src/setup/scenes/splash.ts +22 -8
- package/src/status-line/component.ts +79 -11
- package/src/status-line/context-usage.ts +176 -73
- package/src/status-line/footer.ts +18 -35
- package/src/status-line/host.ts +6 -1
- package/src/status-line/segments.ts +16 -5
- package/src/status-line/startup.ts +6 -6
- package/src/status-line/types.ts +1 -0
- package/src/stdin-buffer.d.ts +7 -0
- package/src/stdin-buffer.ts +300 -62
- package/src/terminal-capabilities.d.ts +16 -1
- package/src/terminal-capabilities.ts +66 -28
- package/src/terminal-multiplexer.d.ts +7 -0
- package/src/terminal-multiplexer.ts +14 -0
- package/src/terminal.d.ts +8 -4
- package/src/terminal.ts +190 -63
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +1 -0
- package/src/theme/theme-class.ts +1 -0
- package/src/theme/theme.d.ts +11 -0
- package/src/theme/theme.ts +46 -14
- package/src/thinking.d.ts +5 -0
- package/src/thinking.ts +9 -1
- package/src/tmux.d.ts +39 -4
- package/src/tmux.ts +110 -16
- package/src/tools/ask.ts +20 -9
- package/src/tools/ast-edit.d.ts +2 -2
- package/src/tools/ast-edit.ts +30 -7
- package/src/tools/ast-grep.d.ts +2 -2
- package/src/tools/ast-grep.ts +39 -35
- package/src/tools/bash.d.ts +6 -1
- package/src/tools/bash.ts +15 -9
- package/src/tools/debug.ts +2 -1
- package/src/tools/default-renderer.ts +3 -2
- package/src/tools/edit.d.ts +2 -0
- package/src/tools/edit.ts +100 -21
- package/src/tools/eval.d.ts +53 -4
- package/src/tools/eval.ts +173 -137
- package/src/tools/find.d.ts +14 -4
- package/src/tools/find.ts +71 -18
- package/src/tools/glob.d.ts +3 -3
- package/src/tools/glob.ts +51 -17
- package/src/tools/grep.d.ts +2 -2
- package/src/tools/grep.ts +99 -22
- package/src/tools/grouped-file-output.d.ts +9 -4
- package/src/tools/grouped-file-output.ts +21 -6
- package/src/tools/lsp.ts +30 -18
- package/src/tools/mcp.ts +2 -2
- package/src/tools/native-view.d.ts +1 -1
- package/src/tools/native-view.ts +2 -2
- package/src/tools/read.d.ts +32 -10
- package/src/tools/read.ts +89 -43
- package/src/tools/renderer.d.ts +2 -0
- package/src/tools/renderer.ts +2 -0
- package/src/tools/task.d.ts +31 -0
- package/src/tools/task.ts +40 -7
- package/src/tools/todo.ts +3 -2
- package/src/tools/wait.ts +16 -30
- package/src/tools/web-search-types.d.ts +1 -0
- package/src/tools/web-search-types.ts +1 -0
- package/src/tools/web-search.ts +2 -1
- package/src/tools/write.d.ts +2 -0
- package/src/tools/write.ts +36 -12
- package/src/tools/yield.ts +2 -2
- package/src/tui.d.ts +20 -3
- package/src/tui.ts +160 -43
- package/src/utils.ts +5 -3
- package/dist/types/tools/eval-format/code-mode-declarations.d.ts +0 -5
- package/src/tools/eval-format/code-mode-declarations.ts +0 -61
|
@@ -16,11 +16,48 @@ import { plainText } from "../native/spans";
|
|
|
16
16
|
/** `Enter code: ABCD-1234` style device-flow instructions: the lead-in and the code. */
|
|
17
17
|
const DEVICE_CODE_INSTRUCTIONS = /^(.*\bcode:?\s+)([A-Za-z0-9][A-Za-z0-9-]{3,})\s*$/s;
|
|
18
18
|
|
|
19
|
+
/** A prompt's trailing `, then press Enter:` — the sheet's Continue button says it. */
|
|
20
|
+
const PRESS_KEY_SUFFIX = /,?\s*then press\s+\S+\s*:?\s*$/i;
|
|
21
|
+
|
|
22
|
+
/** Progress that reports a problem rather than a finished stage. */
|
|
23
|
+
const PROGRESS_PROBLEM = /\b(?:fail(?:ed|ure)?|error|unavailable|in use|could ?n[o']t|unable)\b/i;
|
|
24
|
+
|
|
25
|
+
/** Shown on the field when the flow asks for the manual code again: the last paste had no usable code. */
|
|
26
|
+
const RETRY_MESSAGE = "Couldn't read a code from that. Paste the authorization code or the full redirect URL.";
|
|
27
|
+
|
|
28
|
+
/** Shown while the browser half of the flow runs and the provider reports nothing. */
|
|
29
|
+
const BROWSER_WAIT = "Waiting for you to finish in the browser…";
|
|
30
|
+
|
|
31
|
+
/** The sign-in link from `showAuth`: the provider's instructions, or the device code split out of them. */
|
|
32
|
+
interface LoginAuth {
|
|
33
|
+
url: string;
|
|
34
|
+
launchUrl?: string;
|
|
35
|
+
instructions?: string;
|
|
36
|
+
device?: { code: string };
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
/** One native step after the sign-in link: a text answer (live or given) or a progress message. */
|
|
20
40
|
type LoginStep =
|
|
21
|
-
| { kind: "input"; message: string;
|
|
41
|
+
| { kind: "input"; message: string; field: TextFormField; answer?: string }
|
|
22
42
|
| { kind: "progress"; message: string };
|
|
23
43
|
|
|
44
|
+
/** Sheet copy: plain text, a trailing ASCII ellipsis as `…`. */
|
|
45
|
+
function sheetText(message: string): string {
|
|
46
|
+
return plainText(message)
|
|
47
|
+
.trim()
|
|
48
|
+
.replace(/\.\.\.$/, "…");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `github.com/login/device` for a tooltip; the input when it isn't a URL. */
|
|
52
|
+
function urlPlace(url: string): string {
|
|
53
|
+
try {
|
|
54
|
+
const parsed = new URL(url);
|
|
55
|
+
return `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
|
|
56
|
+
} catch {
|
|
57
|
+
return url;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
24
61
|
/**
|
|
25
62
|
* Login dialog component - replaces editor during OAuth login flow
|
|
26
63
|
*/
|
|
@@ -35,8 +72,7 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
35
72
|
#inputRejecter?: (error: Error) => void;
|
|
36
73
|
#inputAbortCleanup?: () => void;
|
|
37
74
|
readonly #providerName: string;
|
|
38
|
-
|
|
39
|
-
#nativeAuth: { url: string; launchUrl?: string; instructions?: string } | undefined;
|
|
75
|
+
#nativeAuth: LoginAuth | undefined;
|
|
40
76
|
/** Native steps after the link, in arrival order; the live input field is embedded by identity. */
|
|
41
77
|
#nativeSteps: LoginStep[] = [];
|
|
42
78
|
#nativeRoot: NativeNode | undefined;
|
|
@@ -134,10 +170,13 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
134
170
|
this.#contentContainer.addChild(new Text(theme.fg("warning", instructions), 0, 0));
|
|
135
171
|
}
|
|
136
172
|
|
|
173
|
+
const plain = instructions ? plainText(instructions) : undefined;
|
|
174
|
+
const device = plain ? DEVICE_CODE_INSTRUCTIONS.exec(plain) : null;
|
|
137
175
|
this.#nativeAuth = {
|
|
138
176
|
url,
|
|
139
177
|
launchUrl: launchUrl && launchUrl !== url ? launchUrl : undefined,
|
|
140
|
-
instructions:
|
|
178
|
+
instructions: plain,
|
|
179
|
+
device: device ? { code: device[2]! } : undefined,
|
|
141
180
|
};
|
|
142
181
|
this.#nativeSteps = [];
|
|
143
182
|
this.#nativeRoot = undefined;
|
|
@@ -158,10 +197,12 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
158
197
|
this.#input = this.#createInput();
|
|
159
198
|
const nativeMounted = this.#nativeSteps.find(step => step.kind === "input" && step.field === previousInput);
|
|
160
199
|
if (mounted !== -1) {
|
|
200
|
+
// The flow asks again only when the last paste carried no usable code.
|
|
201
|
+
this.#input.setError(RETRY_MESSAGE);
|
|
161
202
|
this.#contentContainer.children.splice(mounted, 1, this.#input);
|
|
162
203
|
if (nativeMounted?.kind === "input") nativeMounted.field = this.#input;
|
|
163
204
|
} else {
|
|
164
|
-
this.#nativeSteps.push({ kind: "input", message: plainText(prompt),
|
|
205
|
+
this.#nativeSteps.push({ kind: "input", message: plainText(prompt), field: this.#input });
|
|
165
206
|
this.#contentContainer.addChild(new Spacer(1));
|
|
166
207
|
this.#contentContainer.addChild(new Text(theme.fg("dim", prompt), 0, 0));
|
|
167
208
|
this.#contentContainer.addChild(this.#input);
|
|
@@ -203,10 +244,11 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
203
244
|
this.#contentContainer.removeChild(this.#input);
|
|
204
245
|
this.#contentContainer.children.splice(mounted, 0, answer);
|
|
205
246
|
const answered = this.#nativeSteps.find(step => step.kind === "input" && step.field === this.#input);
|
|
206
|
-
if (answered?.kind === "input") answered.answer =
|
|
247
|
+
if (answered?.kind === "input") answered.answer = value;
|
|
207
248
|
}
|
|
208
249
|
// A new prompt must not recover a previous secret through undo or yank.
|
|
209
250
|
this.#input = this.#createInput(prompt.secret === true);
|
|
251
|
+
if (prompt.placeholder) this.#input.input.placeholder = plainText(prompt.placeholder);
|
|
210
252
|
this.#contentContainer.addChild(new Spacer(1));
|
|
211
253
|
this.#contentContainer.addChild(new Text(theme.fg("text", prompt.message), 0, 0));
|
|
212
254
|
if (prompt.placeholder) {
|
|
@@ -223,13 +265,7 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
223
265
|
0,
|
|
224
266
|
),
|
|
225
267
|
);
|
|
226
|
-
this.#nativeSteps.push({
|
|
227
|
-
kind: "input",
|
|
228
|
-
message: plainText(prompt.message),
|
|
229
|
-
placeholder: prompt.placeholder ? plainText(prompt.placeholder) : undefined,
|
|
230
|
-
paste: false,
|
|
231
|
-
field: this.#input,
|
|
232
|
-
});
|
|
268
|
+
this.#nativeSteps.push({ kind: "input", message: plainText(prompt.message), field: this.#input });
|
|
233
269
|
this.#nativeRoot = undefined;
|
|
234
270
|
|
|
235
271
|
this.#tui.requestRender();
|
|
@@ -273,56 +309,91 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
273
309
|
|
|
274
310
|
/**
|
|
275
311
|
* A glass sheet over the composer (the dialog replaces the editor in the
|
|
276
|
-
* dock; the `overlay` hoists into the terminal's layer)
|
|
277
|
-
*
|
|
278
|
-
* field
|
|
312
|
+
* dock; the `overlay` hoists into the terminal's layer). Top to bottom: one
|
|
313
|
+
* sentence saying what to do, the device code as a large click-to-copy
|
|
314
|
+
* well, the sign-in link as a read-only field with Copy, the questions
|
|
315
|
+
* (answered ones as label/value lines, the live one as a field), and the
|
|
316
|
+
* flow's status — finished stages checked, the current one spinning. The
|
|
317
|
+
* footer holds Cancel and one primary action: Continue while a field waits
|
|
318
|
+
* for an answer (Open sign-in page then leads the footer as a plain
|
|
319
|
+
* button), else Open sign-in page.
|
|
279
320
|
*/
|
|
280
321
|
override describe(): NativeNode {
|
|
281
322
|
if (this.#nativeRoot) return this.#nativeRoot;
|
|
282
|
-
const steps: NativeChild[] = [];
|
|
283
323
|
const auth = this.#nativeAuth;
|
|
324
|
+
const field = this.#activeField();
|
|
325
|
+
const body: NativeChild[] = [];
|
|
284
326
|
if (auth) {
|
|
285
|
-
|
|
286
|
-
if (auth.
|
|
327
|
+
body.push(keyed(text(this.#lead(auth), { role: "zeta.login.lead", wrap: "word" }), "lead"));
|
|
328
|
+
if (auth.device) body.push(this.#describeCode(auth.device.code));
|
|
329
|
+
body.push(this.#describeLink(auth.url));
|
|
330
|
+
if (auth.launchUrl) {
|
|
331
|
+
body.push(
|
|
332
|
+
keyed(
|
|
333
|
+
text([span("On this machine ", "dim"), span(auth.launchUrl, "mono", { href: auth.launchUrl })], {
|
|
334
|
+
role: "zeta.login.shortcut",
|
|
335
|
+
truncate: "middle",
|
|
336
|
+
lines: 1,
|
|
337
|
+
title: auth.launchUrl,
|
|
338
|
+
}),
|
|
339
|
+
"shortcut",
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
287
343
|
}
|
|
344
|
+
const status: NativeNode[] = [];
|
|
288
345
|
const live = this.#nativeSteps.findLastIndex(step => step.kind === "progress");
|
|
289
|
-
// Progress messages share one step: settled ones dim, the latest one the live spinner.
|
|
290
|
-
let progress: NativeChild[] | undefined;
|
|
291
346
|
for (const [index, step] of this.#nativeSteps.entries()) {
|
|
292
347
|
if (step.kind === "progress") {
|
|
293
|
-
|
|
348
|
+
status.push(
|
|
294
349
|
index === live
|
|
295
|
-
?
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
: text([span(step.message, "dim")], { wrap: "word" });
|
|
299
|
-
if (progress) {
|
|
300
|
-
progress.push(line);
|
|
301
|
-
} else {
|
|
302
|
-
progress = [line];
|
|
303
|
-
steps.push(node("col", { role: "zeta.login.step", gap: "xs" }, progress, `p${index}`));
|
|
304
|
-
}
|
|
350
|
+
? this.#describeLive(step.message, `p${index}`)
|
|
351
|
+
: this.#describeDone(step.message, `p${index}`),
|
|
352
|
+
);
|
|
305
353
|
continue;
|
|
306
354
|
}
|
|
355
|
+
// A field label: the footer's Continue says the prompt's `then press Enter`.
|
|
356
|
+
const label = step.message.trim().replace(PRESS_KEY_SUFFIX, "").replace(/:$/, "");
|
|
307
357
|
if (step.answer !== undefined) {
|
|
308
|
-
|
|
309
|
-
|
|
358
|
+
body.push(
|
|
359
|
+
node(
|
|
360
|
+
"row",
|
|
361
|
+
{ role: "zeta.login.answer", gap: "sm", align: "baseline" },
|
|
362
|
+
[
|
|
363
|
+
keyed(text(label, { truncate: "end", lines: 1 }), "label"),
|
|
364
|
+
keyed(text(step.answer || "—", { truncate: "end", lines: 1 }), "value"),
|
|
365
|
+
],
|
|
366
|
+
`a${index}`,
|
|
367
|
+
),
|
|
368
|
+
);
|
|
369
|
+
} else {
|
|
370
|
+
body.push(
|
|
371
|
+
node(
|
|
372
|
+
"col",
|
|
373
|
+
{ role: "zeta.login.prompt", gap: "sm" },
|
|
374
|
+
[keyed(text(label, { role: "zeta.login.label", wrap: "word" }), "label"), step.field],
|
|
375
|
+
`i${index}`,
|
|
376
|
+
),
|
|
377
|
+
);
|
|
310
378
|
}
|
|
311
|
-
const children: NativeChild[] = [text(step.message, { wrap: "word" })];
|
|
312
|
-
if (step.placeholder) children.push(text([span(`e.g., ${step.placeholder}`, "dim")]));
|
|
313
|
-
children.push(node("col", { role: "zeta.login.paste" }, [step.field], "field"));
|
|
314
|
-
steps.push(node("col", { role: "zeta.login.step", gap: "xs" }, children, `i${index}`));
|
|
315
379
|
}
|
|
380
|
+
// The provider reports nothing while the browser half runs (GitHub's device poll): say so.
|
|
381
|
+
if (auth && live === -1 && !field) status.push(this.#describeLive(BROWSER_WAIT, "browser"));
|
|
382
|
+
if (status.length > 0) body.push(node("col", { role: "zeta.login.status", gap: "xs" }, status, "status"));
|
|
383
|
+
|
|
316
384
|
const cancelKey = getKeybindings().getKeys("tui.select.cancel")[0];
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (this.#activeField()) {
|
|
385
|
+
const cancel = actionButton("Cancel", "cancel", cancelKey ? { keys: cancelKey } : {});
|
|
386
|
+
const open = auth ? this.#describeOpen(auth.url, !field) : null;
|
|
387
|
+
let buttons: (NativeNode | null)[];
|
|
388
|
+
if (field) {
|
|
322
389
|
const submitKey = getKeybindings().getKeys("tui.input.submit")[0];
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
390
|
+
const submit = actionButton("Continue", "submit", {
|
|
391
|
+
tone: "accent",
|
|
392
|
+
...(submitKey ? { keys: submitKey } : {}),
|
|
393
|
+
});
|
|
394
|
+
buttons = open ? [open, null, cancel, submit] : [null, cancel, submit];
|
|
395
|
+
} else {
|
|
396
|
+
buttons = open ? [null, cancel, open] : [null, cancel];
|
|
326
397
|
}
|
|
327
398
|
const sheet = node(
|
|
328
399
|
"overlay",
|
|
@@ -333,62 +404,101 @@ export class LoginDialogComponent extends OverlayPanel {
|
|
|
333
404
|
size: "md",
|
|
334
405
|
modal: true,
|
|
335
406
|
},
|
|
336
|
-
[
|
|
337
|
-
col([node("col", { role: "zeta.login.steps", gap: "md" }, steps, "steps"), actionBar(buttons)], {
|
|
338
|
-
gap: "lg",
|
|
339
|
-
}),
|
|
340
|
-
],
|
|
407
|
+
[col([node("col", { role: "zeta.login.body", gap: "md" }, body, "body"), actionBar(buttons)], { gap: "lg" })],
|
|
341
408
|
"sheet",
|
|
342
409
|
);
|
|
343
410
|
this.#nativeRoot = col([sheet]);
|
|
344
411
|
return this.#nativeRoot;
|
|
345
412
|
}
|
|
346
413
|
|
|
347
|
-
/**
|
|
348
|
-
#
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
414
|
+
/** What to do, in one sentence: enter the code, follow the provider's instructions, or finish in the browser. */
|
|
415
|
+
#lead(auth: LoginAuth): string {
|
|
416
|
+
if (auth.device) return "Enter this code on the sign-in page to connect your account.";
|
|
417
|
+
if (auth.instructions) return sheetText(auth.instructions);
|
|
418
|
+
return "Continue in your browser. If it didn't open, open or copy the link below.";
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** The device code, large in a well; a click copies it. */
|
|
422
|
+
#describeCode(code: string): NativeNode {
|
|
423
|
+
return node(
|
|
424
|
+
"row",
|
|
425
|
+
{ role: "zeta.login.code", align: "center", actions: { click: "copy" }, href: code, title: "Copy code" },
|
|
426
|
+
[keyed(text([span(code, "mono")]), "value"), node("icon", { name: "copy" }, undefined, "icon")],
|
|
427
|
+
"code",
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** The sign-in link as a read-only field: the full URL (middle-truncated; a click opens it) and Copy. */
|
|
432
|
+
#describeLink(url: string): NativeNode {
|
|
433
|
+
const shown = text([span(url, "mono", { href: url })], {
|
|
434
|
+
role: "zeta.login.url",
|
|
435
|
+
truncate: "middle",
|
|
436
|
+
lines: 1,
|
|
437
|
+
title: url,
|
|
438
|
+
});
|
|
439
|
+
return node(
|
|
440
|
+
"row",
|
|
441
|
+
{ role: "zeta.login.link", gap: "sm", align: "center" },
|
|
442
|
+
[
|
|
443
|
+
node("icon", { name: "link" }, undefined, "icon"),
|
|
444
|
+
keyed(shown, "url"),
|
|
445
|
+
node(
|
|
446
|
+
"row",
|
|
368
447
|
{
|
|
369
|
-
|
|
370
|
-
|
|
448
|
+
role: "zeta.btn",
|
|
449
|
+
align: "center",
|
|
450
|
+
actions: { click: "copy" },
|
|
451
|
+
href: url,
|
|
452
|
+
title: "Copy the sign-in link",
|
|
371
453
|
},
|
|
454
|
+
[text("Copy")],
|
|
455
|
+
"copyLink",
|
|
372
456
|
),
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
457
|
+
],
|
|
458
|
+
"link",
|
|
459
|
+
);
|
|
376
460
|
}
|
|
377
461
|
|
|
378
|
-
/**
|
|
379
|
-
#
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
462
|
+
/** Open sign-in page (Tern opens the link itself): the primary action while nothing waits for an answer. */
|
|
463
|
+
#describeOpen(url: string, primary: boolean): NativeNode {
|
|
464
|
+
return node(
|
|
465
|
+
"row",
|
|
466
|
+
{
|
|
467
|
+
role: "zeta.btn",
|
|
468
|
+
gap: "xs",
|
|
469
|
+
align: "center",
|
|
470
|
+
actions: { click: "open" },
|
|
471
|
+
href: url,
|
|
472
|
+
title: `Open ${urlPlace(url)} in your browser`,
|
|
473
|
+
...(primary ? { tone: "accent" as const } : {}),
|
|
474
|
+
},
|
|
475
|
+
[text("Open sign-in page"), node("icon", { name: "open" }, undefined, "icon")],
|
|
476
|
+
"openLink",
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** The stage running now: a spinner and what it waits for. */
|
|
481
|
+
#describeLive(message: string, key: string): NativeNode {
|
|
482
|
+
return node(
|
|
483
|
+
"row",
|
|
484
|
+
{ role: "zeta.login.waiting", gap: "sm", align: "center" },
|
|
485
|
+
[node("spinner", { style: "orbit", label: sheetText(message), tone: "muted" })],
|
|
486
|
+
key,
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** A finished stage, checked; one that reported a problem keeps a warning mark. */
|
|
491
|
+
#describeDone(message: string, key: string): NativeNode {
|
|
492
|
+
const problem = PROGRESS_PROBLEM.test(message);
|
|
493
|
+
return node(
|
|
494
|
+
"row",
|
|
495
|
+
{ role: "zeta.login.done", gap: "sm", align: "baseline", ...(problem ? { tone: "warning" as const } : {}) },
|
|
496
|
+
[
|
|
497
|
+
node("icon", { name: problem ? "warn" : "check" }, undefined, "icon"),
|
|
498
|
+
keyed(text(sheetText(message).replace(/…$/, ""), { wrap: "word" }), "text"),
|
|
499
|
+
],
|
|
500
|
+
key,
|
|
501
|
+
);
|
|
392
502
|
}
|
|
393
503
|
|
|
394
504
|
/** The input field still waiting for an answer, if any. */
|