Merge branch 'main' into feat/mattermost-channel
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -34,6 +34,7 @@ export type AppViewState = {
|
||||
settings: UiSettings;
|
||||
password: string;
|
||||
tab: Tab;
|
||||
onboarding: boolean;
|
||||
basePath: string;
|
||||
connected: boolean;
|
||||
theme: ThemeMode;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -158,7 +158,8 @@ function renderAvatar(
|
||||
function isAvatarUrl(value: string): boolean {
|
||||
return (
|
||||
/^https?:\/\//i.test(value) ||
|
||||
/^data:image\//i.test(value)
|
||||
/^data:image\//i.test(value) ||
|
||||
/^\//.test(value) // Relative paths from avatar endpoint
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user