Merge pull request #2154 from alipgoldberg/setup-fallback-url-in-prompt
feat(setup): move URL fallback into the open-browser prompt
This commit is contained in:
@@ -164,9 +164,8 @@ async function walkThroughBotCreation(): Promise<void> {
|
|||||||
' 2. In the "Bot" tab, click "Reset Token" and copy the token',
|
' 2. In the "Bot" tab, click "Reset Token" and copy the token',
|
||||||
' 3. On the same tab, enable "Message Content Intent"',
|
' 3. On the same tab, enable "Message Content Intent"',
|
||||||
' (under Privileged Gateway Intents)',
|
' (under Privileged Gateway Intents)',
|
||||||
'',
|
|
||||||
formatNoteLink(url),
|
formatNoteLink(url),
|
||||||
].join('\n'),
|
].filter((line): line is string => line !== null).join('\n'),
|
||||||
'Create a Discord bot',
|
'Create a Discord bot',
|
||||||
);
|
);
|
||||||
await confirmThenOpen(url, 'Press Enter to open the Developer Portal');
|
await confirmThenOpen(url, 'Press Enter to open the Developer Portal');
|
||||||
@@ -224,9 +223,8 @@ async function walkThroughServerCreation(): Promise<void> {
|
|||||||
' 1. In Discord, click the "+" at the bottom of the server list',
|
' 1. In Discord, click the "+" at the bottom of the server list',
|
||||||
' 2. Choose "Create My Own" → "For me and my friends"',
|
' 2. Choose "Create My Own" → "For me and my friends"',
|
||||||
' 3. Give it any name (e.g. "NanoClaw")',
|
' 3. Give it any name (e.g. "NanoClaw")',
|
||||||
'',
|
|
||||||
formatNoteLink(url),
|
formatNoteLink(url),
|
||||||
].join('\n'),
|
].filter((line): line is string => line !== null).join('\n'),
|
||||||
'Create a Discord server',
|
'Create a Discord server',
|
||||||
);
|
);
|
||||||
await confirmThenOpen(url, 'Press Enter to open Discord');
|
await confirmThenOpen(url, 'Press Enter to open Discord');
|
||||||
@@ -446,9 +444,8 @@ async function promptInviteBot(
|
|||||||
'',
|
'',
|
||||||
' 1. Pick any server you\'re in (a personal one is fine)',
|
' 1. Pick any server you\'re in (a personal one is fine)',
|
||||||
' 2. Click "Authorize"',
|
' 2. Click "Authorize"',
|
||||||
'',
|
|
||||||
formatNoteLink(url),
|
formatNoteLink(url),
|
||||||
].join('\n'),
|
].filter((line): line is string => line !== null).join('\n'),
|
||||||
'Add bot to a server',
|
'Add bot to a server',
|
||||||
);
|
);
|
||||||
await confirmThenOpen(url, 'Press Enter to open the invite page');
|
await confirmThenOpen(url, 'Press Enter to open the invite page');
|
||||||
|
|||||||
@@ -135,9 +135,8 @@ async function walkThroughAppCreation(): Promise<void> {
|
|||||||
' slash commands and messages from the messages tab"',
|
' slash commands and messages from the messages tab"',
|
||||||
' 4. Basic Information → copy the "Signing Secret"',
|
' 4. Basic Information → copy the "Signing Secret"',
|
||||||
' 5. Install to Workspace → copy the "Bot User OAuth Token" (xoxb-…)',
|
' 5. Install to Workspace → copy the "Bot User OAuth Token" (xoxb-…)',
|
||||||
'',
|
|
||||||
formatNoteLink(SLACK_APPS_URL),
|
formatNoteLink(SLACK_APPS_URL),
|
||||||
].join('\n'),
|
].filter((line): line is string => line !== null).join('\n'),
|
||||||
'Create a Slack app',
|
'Create a Slack app',
|
||||||
);
|
);
|
||||||
await confirmThenOpen(SLACK_APPS_URL, 'Press Enter to open Slack app settings');
|
await confirmThenOpen(SLACK_APPS_URL, 'Press Enter to open Slack app settings');
|
||||||
|
|||||||
@@ -50,9 +50,8 @@ export async function runTelegramChannel(displayName: string): Promise<void> {
|
|||||||
note(
|
note(
|
||||||
[
|
[
|
||||||
`Opening @${botUsername} in Telegram so it's ready when the pairing code shows up.`,
|
`Opening @${botUsername} in Telegram so it's ready when the pairing code shows up.`,
|
||||||
'',
|
|
||||||
formatNoteLink(botUrl),
|
formatNoteLink(botUrl),
|
||||||
].join('\n'),
|
].filter((line): line is string => line !== null).join('\n'),
|
||||||
'Open Telegram',
|
'Open Telegram',
|
||||||
);
|
);
|
||||||
await confirmThenOpen(botUrl, 'Press Enter to open Telegram');
|
await confirmThenOpen(botUrl, 'Press Enter to open Telegram');
|
||||||
|
|||||||
@@ -40,35 +40,42 @@ export function openUrl(url: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a URL for display inside a setup `note(...)` card. On
|
* Format a URL for inclusion in a setup `note(...)` card. On
|
||||||
* GUI devices the URL renders dim — it's a fallback in case the
|
* headless devices we surface the URL inside the card with a
|
||||||
* auto-open misses, and `confirmThenOpen` is doing the heavy
|
* "Get started:" label at full strength — copy-pasting onto
|
||||||
* lifting of getting the user there. On headless devices the
|
* another device is the actual action, not an incidental
|
||||||
* URL becomes the user's only path forward, so we surface it
|
* reference. The leading `\n` acts as a visual separator from
|
||||||
* with a "Get started:" label and full-strength text — copy-
|
* the body steps above; callers `.filter(line => line !== null)`
|
||||||
* pasting onto another device is the actual action, not an
|
* before joining, so on GUI we drop the line entirely (and the
|
||||||
* incidental reference.
|
* URL ends up below the next-step confirm prompt as a "if
|
||||||
|
* browser does not appear, please visit" fallback — see
|
||||||
|
* `confirmThenOpen`).
|
||||||
*/
|
*/
|
||||||
export function formatNoteLink(url: string): string {
|
export function formatNoteLink(url: string): string | null {
|
||||||
if (isHeadless()) return `Get started: ${url}`;
|
if (isHeadless()) return `\nGet started: ${url}`;
|
||||||
return k.dim(url);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gate a browser-open on a confirm so the user is ready for their browser
|
* Gate a browser-open on a confirm so the user is ready for their browser
|
||||||
* to take focus. Proceeds on cancel as well — the user can always copy the
|
* to take focus. Proceeds on cancel as well. On headless devices both the
|
||||||
* URL from the note that precedes the prompt. On headless devices both
|
* prompt and the open are skipped — the URL is already surfaced inside
|
||||||
* the prompt and the open are skipped — there's no browser to time
|
* the surrounding note (via `formatNoteLink`).
|
||||||
* focus for, and the URL is already visible in the surrounding note.
|
*
|
||||||
|
* On GUI devices the confirm message includes the fallback URL on the
|
||||||
|
* lines below the action ("If browser does not appear, please visit:
|
||||||
|
* <url>" in dim) so the user has a copy-paste path right next to the
|
||||||
|
* action button without needing to scroll back up to the card.
|
||||||
*/
|
*/
|
||||||
export async function confirmThenOpen(
|
export async function confirmThenOpen(
|
||||||
url: string,
|
url: string,
|
||||||
message = 'Press Enter to open your browser',
|
message = 'Press Enter to open your browser',
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (isHeadless()) return;
|
if (isHeadless()) return;
|
||||||
|
const fallback = `\n${k.dim(`If browser does not appear, please visit: ${url}`)}`;
|
||||||
ensureAnswer(
|
ensureAnswer(
|
||||||
await p.confirm({
|
await p.confirm({
|
||||||
message,
|
message: `${message}${fallback}`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user