diff --git a/CHANGELOG.md b/CHANGELOG.md index f3c541684ea..1c1eab9ba8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Control UI/chat: add a per-session thinking-level picker in the chat header and mobile chat settings, and keep the browser bundle on UI-local thinking/session-key helpers so Safari no longer crashes on Node-only imports before rendering chat controls. - Synology Chat/security: route webhook token comparison through the shared constant-time secret helper for consistency with other bundled plugins. - Gateway/security: scope loopback browser-origin auth throttling by normalized origin so one localhost Control UI tab cannot lock out a different localhost browser origin after repeated auth failures. - Node exec approvals: keep node-host `system.run` approvals bound to the prepared execution plan, so script-drift revalidation still runs after agent-side approval forwarding. diff --git a/ui/src/styles/chat/layout.css b/ui/src/styles/chat/layout.css index 99bf65644bb..9455ee13495 100644 --- a/ui/src/styles/chat/layout.css +++ b/ui/src/styles/chat/layout.css @@ -743,8 +743,8 @@ } .chat-controls__session { - min-width: 140px; - max-width: 300px; + min-width: 98px; + max-width: 190px; } .chat-controls__session-row { @@ -755,8 +755,13 @@ } .chat-controls__model { - min-width: 170px; - max-width: 320px; + min-width: 124px; + max-width: 206px; +} + +.chat-controls__thinking-select { + min-width: 88px; + max-width: 118px; } .chat-controls__thinking { @@ -781,13 +786,17 @@ .chat-controls__session select { padding: 6px 10px; font-size: 13px; - max-width: 300px; + max-width: 190px; overflow: hidden; text-overflow: ellipsis; } .chat-controls__model select { - max-width: 320px; + max-width: 206px; +} + +.chat-controls__thinking-select select { + max-width: 118px; } .chat-controls__thinking { @@ -818,6 +827,11 @@ max-width: none; } + .chat-controls__thinking-select { + min-width: 130px; + max-width: none; + } + .chat-controls { gap: 8px; } @@ -863,6 +877,10 @@ .chat-controls__model { min-width: 150px; } + + .chat-controls__thinking-select { + min-width: 140px; + } } /* Chat loading skeleton */ diff --git a/ui/src/ui/app-chat.ts b/ui/src/ui/app-chat.ts index 637ba709236..9ace7542120 100644 --- a/ui/src/ui/app-chat.ts +++ b/ui/src/ui/app-chat.ts @@ -1,4 +1,3 @@ -import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js"; import { scheduleChatScroll, resetChatScroll } from "./app-scroll.ts"; import { setLastActiveSessionKey } from "./app-settings.ts"; import { resetToolStream } from "./app-tool-stream.ts"; @@ -10,6 +9,7 @@ import { loadModels } from "./controllers/models.ts"; import { loadSessions } from "./controllers/sessions.ts"; import type { GatewayBrowserClient, GatewayHelloOk } from "./gateway.ts"; import { normalizeBasePath } from "./navigation.ts"; +import { parseAgentSessionKey } from "./session-key.ts"; import type { ChatModelOverride, ModelCatalogEntry } from "./types.ts"; import type { SessionsListResult } from "./types.ts"; import type { ChatAttachment, ChatQueueItem } from "./ui-types.ts"; diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts index e85913dd46d..8a66aa4c2a1 100644 --- a/ui/src/ui/app-render.helpers.ts +++ b/ui/src/ui/app-render.helpers.ts @@ -1,6 +1,5 @@ import { html, nothing } from "lit"; import { repeat } from "lit/directives/repeat.js"; -import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js"; import { t } from "../i18n/index.ts"; import { refreshChat } from "./app-chat.ts"; import { syncUrlWithSessionKey } from "./app-settings.ts"; @@ -16,8 +15,14 @@ import { ChatState, loadChatHistory } from "./controllers/chat.ts"; import { loadSessions } from "./controllers/sessions.ts"; import { icons } from "./icons.ts"; import { iconForTab, pathForTab, titleForTab, type Tab } from "./navigation.ts"; +import { parseAgentSessionKey } from "./session-key.ts"; import type { ThemeTransitionContext } from "./theme-transition.ts"; import type { ThemeMode, ThemeName } from "./theme.ts"; +import { + listThinkingLevelLabels, + normalizeThinkLevel, + resolveThinkingDefaultForModel, +} from "./thinking.ts"; import type { SessionsListResult } from "./types.ts"; type SessionDefaultsSnapshot = { @@ -133,11 +138,16 @@ function renderCronFilterIcon(hiddenCount: number) { export function renderChatSessionSelect(state: AppViewState) { const sessionGroups = resolveSessionOptionGroups(state, state.sessionKey, state.sessionsResult); const modelSelect = renderChatModelSelect(state); + const thinkingSelect = renderChatThinkingSelect(state); + const selectedSessionLabel = + sessionGroups.flatMap((group) => group.options).find((entry) => entry.key === state.sessionKey) + ?.label ?? state.sessionKey; return html`
- ${modelSelect} + ${modelSelect} ${thinkingSelect}
`; } @@ -436,6 +446,7 @@ export function renderChatMobileToggle(state: AppViewState) { )} + ${renderChatThinkingSelect(state)}