Improve dashboard setup command copy UX (#56551)

This commit is contained in:
Tak Hoffman 2026-03-28 13:09:22 -05:00 committed by GitHub
parent 31112d5985
commit f32f7d0809
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 85 additions and 6 deletions

View File

@ -126,6 +126,36 @@
user-select: all;
}
.login-gate__command {
display: flex;
align-items: center;
gap: 8px;
margin: 4px 0 2px;
padding: 5px 8px 5px 10px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--fg);
cursor: copy;
}
.login-gate__command:hover {
border-color: var(--accent);
}
.login-gate__command:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.login-gate__command code {
flex: 1;
margin: 0;
padding: 0;
background: transparent;
border: 0;
}
.login-gate__docs {
margin-top: 10px;
font-size: 11px;

View File

@ -92,6 +92,10 @@ function createCopyButton(options: CopyButtonOptions): TemplateResult {
`;
}
export function renderCopyAsMarkdownButton(markdown: string): TemplateResult {
return createCopyButton({ text: () => markdown, label: COPY_LABEL });
export function renderCopyButton(text: string, label = COPY_LABEL): TemplateResult {
return createCopyButton({ text: () => text, label });
}
export function renderCopyAsMarkdownButton(markdown: string): TemplateResult {
return renderCopyButton(markdown, COPY_LABEL);
}

View File

@ -0,0 +1,38 @@
import { html } from "lit";
import { renderCopyButton } from "../chat/copy-as-markdown.ts";
async function copyCommand(command: string) {
try {
await navigator.clipboard.writeText(command);
} catch {
// Best effort only; the explicit copy button provides visible feedback.
}
}
export function renderConnectCommand(command: string) {
return html`
<div
class="login-gate__command"
role="button"
tabindex="0"
title="Copy command"
aria-label=${`Copy command: ${command}`}
@click=${async (e: Event) => {
if ((e.target as HTMLElement | null)?.closest(".chat-copy-btn")) {
return;
}
await copyCommand(command);
}}
@keydown=${async (e: KeyboardEvent) => {
if (e.key !== "Enter" && e.key !== " ") {
return;
}
e.preventDefault();
await copyCommand(command);
}}
>
<code>${command}</code>
${renderCopyButton(command, "Copy command")}
</div>
`;
}

View File

@ -4,6 +4,7 @@ import type { AppViewState } from "../app-view-state.ts";
import { icons } from "../icons.ts";
import { normalizeBasePath } from "../navigation.ts";
import { agentLogoUrl } from "./agents-utils.ts";
import { renderConnectCommand } from "./connect-command.ts";
export function renderLoginGate(state: AppViewState) {
const basePath = normalizeBasePath(state.basePath ?? "");
@ -107,8 +108,10 @@ export function renderLoginGate(state: AppViewState) {
<div class="login-gate__help">
<div class="login-gate__help-title">${t("overview.connection.title")}</div>
<ol class="login-gate__steps">
<li>${t("overview.connection.step1")}<code>openclaw gateway run</code></li>
<li>${t("overview.connection.step2")}<code>openclaw dashboard --no-open</code></li>
<li>
${t("overview.connection.step1")}${renderConnectCommand("openclaw gateway run")}
</li>
<li>${t("overview.connection.step2")} ${renderConnectCommand("openclaw dashboard")}</li>
<li>${t("overview.connection.step3")}</li>
</ol>
<div class="login-gate__docs">

View File

@ -14,6 +14,7 @@ import type {
SessionsUsageResult,
SkillStatusReport,
} from "../types.ts";
import { renderConnectCommand } from "./connect-command.ts";
import { renderOverviewAttention } from "./overview-attention.ts";
import { renderOverviewCards } from "./overview-cards.ts";
import { renderOverviewEventLog } from "./overview-event-log.ts";
@ -316,9 +317,12 @@ export function renderOverview(props: OverviewProps) {
<div class="login-gate__help" style="margin-top: 16px;">
<div class="login-gate__help-title">${t("overview.connection.title")}</div>
<ol class="login-gate__steps">
<li>${t("overview.connection.step1")}<code>openclaw gateway run</code></li>
<li>
${t("overview.connection.step2")}<code>openclaw dashboard --no-open</code>
${t("overview.connection.step1")}
${renderConnectCommand("openclaw gateway run")}
</li>
<li>
${t("overview.connection.step2")} ${renderConnectCommand("openclaw dashboard")}
</li>
<li>${t("overview.connection.step3")}</li>
<li>