ui: add onboarding mode for control ui

This commit is contained in:
Peter Steinberger
2026-01-22 11:40:24 +00:00
parent 019726f2d1
commit 447db67b18
10 changed files with 3122 additions and 16 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+15
View File
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clawdbot Control</title>
<meta name="color-scheme" content="dark light" />
<link rel="icon" href="./favicon.ico" sizes="any" />
<script type="module" crossorigin src="./assets/index-bYQnHP3a.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-BPDeGGxb.css">
</head>
<body>
<clawdbot-app></clawdbot-app>
</body>
</html>
+12
View File
@@ -37,6 +37,18 @@
grid-template-columns: 0px minmax(0, 1fr);
}
.shell--onboarding {
grid-template-rows: 0 1fr;
}
.shell--onboarding .topbar {
display: none;
}
.shell--onboarding .content {
padding-top: 0;
}
.shell--chat-focus .content {
padding-top: 0;
gap: 0;
+2
View File
@@ -34,6 +34,7 @@ type GatewayHost = {
connected: boolean;
hello: GatewayHelloOk | null;
lastError: string | null;
onboarding?: boolean;
eventLogBuffer: EventLogEntry[];
eventLog: EventLogEntry[];
tab: Tab;
@@ -153,6 +154,7 @@ export function handleGatewayEvent(host: GatewayHost, evt: GatewayEventFrame) {
}
if (evt.event === "agent") {
if (host.onboarding) return;
handleAgentEvent(
host as unknown as Parameters<typeof handleAgentEvent>[0],
evt.payload as AgentEventPayload | undefined,
+24 -10
View File
@@ -39,6 +39,10 @@ export function renderTab(state: AppViewState, tab: Tab) {
export function renderChatControls(state: AppViewState) {
const sessionOptions = resolveSessionOptions(state.sessionKey, state.sessionsResult);
const disableThinkingToggle = state.onboarding;
const disableFocusToggle = state.onboarding;
const showThinking = state.onboarding ? false : state.settings.chatShowThinking;
const focusActive = state.onboarding ? true : state.settings.chatFocusMode;
// Refresh icon
const refreshIcon = html`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path></svg>`;
const focusIcon = html`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h3"></path><path d="M20 7V4h-3"></path><path d="M4 17v3h3"></path><path d="M20 17v3h-3"></path><circle cx="12" cy="12" r="3"></circle></svg>`;
@@ -90,26 +94,36 @@ export function renderChatControls(state: AppViewState) {
</button>
<span class="chat-controls__separator">|</span>
<button
class="btn btn--sm btn--icon ${state.settings.chatShowThinking ? "active" : ""}"
@click=${() =>
class="btn btn--sm btn--icon ${showThinking ? "active" : ""}"
?disabled=${disableThinkingToggle}
@click=${() => {
if (disableThinkingToggle) return;
state.applySettings({
...state.settings,
chatShowThinking: !state.settings.chatShowThinking,
})}
aria-pressed=${state.settings.chatShowThinking}
title="Toggle assistant thinking/working output"
});
}}
aria-pressed=${showThinking}
title=${disableThinkingToggle
? "Disabled during onboarding"
: "Toggle assistant thinking/working output"}
>
🧠
</button>
<button
class="btn btn--sm btn--icon ${state.settings.chatFocusMode ? "active" : ""}"
@click=${() =>
class="btn btn--sm btn--icon ${focusActive ? "active" : ""}"
?disabled=${disableFocusToggle}
@click=${() => {
if (disableFocusToggle) return;
state.applySettings({
...state.settings,
chatFocusMode: !state.settings.chatFocusMode,
})}
aria-pressed=${state.settings.chatFocusMode}
title="Toggle focus mode (hide sidebar + page header)"
});
}}
aria-pressed=${focusActive}
title=${disableFocusToggle
? "Disabled during onboarding"
: "Toggle focus mode (hide sidebar + page header)"}
>
${focusIcon}
</button>
+9 -6
View File
@@ -105,12 +105,13 @@ export function renderApp(state: AppViewState) {
const cronNext = state.cronStatus?.nextWakeAtMs ?? null;
const chatDisabledReason = state.connected ? null : "Disconnected from gateway.";
const isChat = state.tab === "chat";
const chatFocus = isChat && state.settings.chatFocusMode;
const chatFocus = isChat && (state.settings.chatFocusMode || state.onboarding);
const showThinking = state.onboarding ? false : state.settings.chatShowThinking;
const assistantAvatarUrl = resolveAssistantAvatarUrl(state);
const chatAvatarUrl = state.chatAvatarUrl ?? assistantAvatarUrl ?? null;
return html`
<div class="shell ${isChat ? "shell--chat" : ""} ${chatFocus ? "shell--chat-focus" : ""} ${state.settings.navCollapsed ? "shell--nav-collapsed" : ""}">
<div class="shell ${isChat ? "shell--chat" : ""} ${chatFocus ? "shell--chat-focus" : ""} ${state.settings.navCollapsed ? "shell--nav-collapsed" : ""} ${state.onboarding ? "shell--onboarding" : ""}">
<header class="topbar">
<div class="topbar-left">
<button
@@ -440,7 +441,7 @@ export function renderApp(state: AppViewState) {
void refreshChatAvatar(state);
},
thinkingLevel: state.chatThinkingLevel,
showThinking: state.settings.chatShowThinking,
showThinking,
loading: state.chatLoading,
sending: state.chatSending,
assistantAvatarUrl: chatAvatarUrl,
@@ -455,16 +456,18 @@ export function renderApp(state: AppViewState) {
disabledReason: chatDisabledReason,
error: state.lastError,
sessions: state.sessionsResult,
focusMode: state.settings.chatFocusMode,
focusMode: chatFocus,
onRefresh: () => {
state.resetToolStream();
return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);
},
onToggleFocusMode: () =>
onToggleFocusMode: () => {
if (state.onboarding) return;
state.applySettings({
...state.settings,
chatFocusMode: !state.settings.chatFocusMode,
}),
});
},
onChatScroll: (event) => state.handleChatScroll(event),
onDraftChange: (next) => (state.chatMessage = next),
onSend: () => state.handleSendChat(),
+1
View File
@@ -34,6 +34,7 @@ export type AppViewState = {
settings: UiSettings;
password: string;
tab: Tab;
onboarding: boolean;
basePath: string;
connected: boolean;
theme: ThemeMode;
+10
View File
@@ -87,11 +87,21 @@ declare global {
const injectedAssistantIdentity = resolveInjectedAssistantIdentity();
function resolveOnboardingMode(): boolean {
if (!window.location.search) return false;
const params = new URLSearchParams(window.location.search);
const raw = params.get("onboarding");
if (!raw) return false;
const normalized = raw.trim().toLowerCase();
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
}
@customElement("clawdbot-app")
export class ClawdbotApp extends LitElement {
@state() settings: UiSettings = loadSettings();
@state() password = "";
@state() tab: Tab = "chat";
@state() onboarding = resolveOnboardingMode();
@state() connected = false;
@state() theme: ThemeMode = this.settings.theme ?? "system";
@state() themeResolved: ResolvedTheme = "dark";