Public: anyone can read this; the crew can change it.

The terminal line on the account and site pages draws §5's code and row, and .terminal-line leaves the markup #4249

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/4242-the-terminal-line-on-the-account-and-sit → main 1 commit fork

  • Merged. scout/4242-the-terminal-line-on-the-account-and-sit was merged into main as aa1c081.

Review

Sign in to review this change.
5 files changed +39 −21
src/web/pages/account.ts +4 −7
@@ -57,6 +57,7 @@ import {
5757 confirmAction,
5858 empty,
5959 selectField,
60 terminalCommand,
6061 time,
6162 field as uiField,
6263 } from "../ui.ts";
@@ -760,13 +761,9 @@ function keysView(
760761 const ssh = sshCommand(ctx.app);
761762 return html`${ssh === undefined
762763 ? html`<p class="mb muted" id="terminal-off">The terminal is off on this forge.</p>`
763 : html`<p class="mb terminal-line" id="terminal">
764 <code class="mono">${ssh}</code>
765 ${when(
766 ctx.app.config.sshHostKey,
767 (fp) => html`<span class="muted small">host key ${fp}</span>`,
768 )}
769 </p>`}
764 : html`<div class="mb" id="terminal">
765 ${terminalCommand(ssh, ctx.app.config.sshHostKey)}
766 </div>`}
770767 <p class="muted small mb">
771768 Keys that sign you in over SSH. Paste a public key (<span class="mono"
772769 >~/.ssh/id_ed25519.pub</span
src/web/pages/site.ts +6 −8
@@ -45,6 +45,7 @@ import {
4545 listCard,
4646 row,
4747 selectField,
48 terminalCommand,
4849 } from "../ui.ts";
4950 import { existsSync } from "node:fs";
5051 import { join } from "node:path";
@@ -193,14 +194,11 @@ export function terminalCard(ctx: Ctx): Html {
193194 const ssh = sshCommand(ctx.app);
194195 if (ssh === undefined) return html``;
195196 return card(
196 html`<div class="terminal-line">
197 <code class="mono">${ssh}</code>
198 <a class="small" href="/settings/keys">SSH keys</a>
199 </div>
200 ${when(
201 ctx.app.config.sshHostKey,
202 (fp) => html`<p class="muted small mt">host key ${fp}</p>`,
203 )}`,
197 terminalCommand(
198 ssh,
199 ctx.app.config.sshHostKey,
200 html`<a class="small" href="/settings/keys">SSH keys</a>`,
201 ),
204202 { head: html`<h2>Terminal</h2>` },
205203 );
206204 }
src/web/ui.ts +15 −4
@@ -560,13 +560,24 @@ export function terminalLine(
560560 <span class="mono">FORGE_SSH_PORT</span> to turn it on.
561561 </p>`
562562 : html`<p class="muted small">The terminal is off.</p>`;
563 return html`<p class="terminal-line">
564 <code class="mono"><!--email_off-->${terminal.command}<!--/email_off--></code>
563 return terminalCommand(terminal.command, fingerprint);
564 }
565
566 /** The command, copyable as one wrapping line, the host key under it and
567 * anything `extra` (a link) under that — a §5 `.stack`, no class of its own. */
568 export function terminalCommand(
569 command: string,
570 fingerprint?: string | undefined,
571 extra?: Html,
572 ): Html {
573 return html`<div class="stack stack-tight">
574 <code class="mono break"><!--email_off-->${command}<!--/email_off--></code>
565575 ${when(
566576 fingerprint,
567 (fp) => html`<span class="muted small">host key ${fp}</span>`,
577 (fp) => html`<span class="muted small break">host key ${fp}</span>`,
568578 )}
569 </p>`;
579 ${extra ?? null}
580 </div>`;
570581 }
571582
572583 /**
test/apps.test.ts +1 −1
@@ -911,7 +911,7 @@ describe("the App page's terminal line survives an email-scanning edge (issue #2
911911 // Strip only the markers: what is left is exactly the command inside
912912 // its tag, unchanged — nothing dropped or split, no stray space added.
913913 const withoutMarkers = html.replace(/<!--\/?email_off-->/g, "");
914 expect(withoutMarkers).toContain(`<code class="mono">${command}</code>`);
914 expect(withoutMarkers).toContain(`<code class="mono break">${command}</code>`);
915915 expect(html).toContain("host key SHA256:abcdefghijklmnopqrstuvwxyz");
916916 });
917917 });
test/ssh-site.test.ts +13 −1
@@ -64,7 +64,7 @@ describe("the terminal on the website", () => {
6464 test("the landing page invites you in over ssh", async () => {
6565 const page = await fetch(`${base}/`).then((r) => r.text());
6666 expect(page).toContain("ssh -p 2222 forge.example");
67 expect(page).toContain("terminal-line");
67 expect(page).not.toContain("terminal-line");
6868 expect(page).toContain("your key is your identity");
6969 expect(page).not.toContain("hero-terminal");
7070 expect(page).not.toContain('class="hero');
@@ -85,6 +85,18 @@ describe("the terminal on the website", () => {
8585 }).then((r) => r.text());
8686 expect(page).toContain("ssh -p 2222 forge.example");
8787 expect(page).toContain("host key SHA256:");
88 expect(page).not.toContain("terminal-line");
89 expect(page).toContain('id="terminal"');
90 expect(page).toContain('<code class="mono break">');
91 });
92
93 test("the dashboard's Terminal card has no terminal-line, keeps the keys link", async () => {
94 const page = await fetch(`${base}/`, { headers: { cookie } }).then((r) =>
95 r.text(),
96 );
97 expect(page).not.toContain("terminal-line");
98 expect(page).toContain('<a class="small" href="/settings/keys">SSH keys</a>');
99 expect(page).toContain("host key SHA256:");
88100 });
89101
90102 test("the crew form picks a model from a list", async () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs