Skip to content

Commit 17d3c2e

Browse files
feat(ui): redesign chat composer controls (#100461)
* feat(ui): redesign chat composer controls * fix(ui): preserve scoped chat model catalogs * fix(ui): honor configured default agent in chat commands * fix(ui): align composer translations and tests * test(ui): expect cached chat commands * fix(ui): inset composer attachment previews * fix(ui): add attachment preview top padding * fix(ui): preserve composer layout after main merge * fix(ui): keep abort available during voice input * fix(ui): merge duplicate model provider groups * feat(ui): finalize chat composer redesign * fix(ui): refine composer layout and scroll indicators * fix(ui): align composer with chat column * fix(ui): restore composer max width * fix(ui): match composer Codex width * fix(ui): stabilize chat composer controls * docs: refresh generated map * fix(ui): discard rejected model drafts * fix(ui): clear chat composer CI gates * test(ui): await rejected model rollback * fix(ui): address chat composer review findings * fix(ui): preserve default provider ordering * fix(ui): stage model picker changes until save * fix(ui): scope model picker drafts to chat context * fix(ui): align composer drafts and message actions * fix(ui): preserve reasoning rendering * feat(ui): refine chat composer interactions * fix(ui): repair chat composer CI checks * fix(ui): repair chat composer integration checks * fix(ui): translate chat composer labels * fix(ui): restore mobile camera action * test(ui): target visible attachment control * test(ui): locate attachment disclosure directly * fix(ui): preserve composer control ordering * test(ui): track current composer controls * fix(ui): honor terminal active-run state * fix(ui): guard command metadata ownership * fix(ui): stage agent model defaults * fix(ui): preserve chat settings and scroll signals * fix(ui): reconcile merged chat controls * fix(ui): localize Talk composer options * test(ui): infer chat control props * fix(ui): refresh agent model metadata on session switch * style(ui): format merged chat controls * test(ui): avoid shadowed composer geometry names
1 parent 5a7c676 commit 17d3c2e

165 files changed

Lines changed: 9065 additions & 1657 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/control-ui-mock-dev.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function sessionRow(
7474
options: { model?: string; modelProvider?: string } = {},
7575
) {
7676
return {
77-
contextTokens: null,
77+
contextTokens: 200_000,
7878
displayName: label,
7979
hasActiveRun: false,
8080
key,
@@ -92,7 +92,7 @@ function sessionsListResponse(sessions: unknown[], options: SessionListOptions)
9292
return {
9393
count: sessions.length,
9494
defaults: {
95-
contextTokens: null,
95+
contextTokens: 200_000,
9696
model: "gpt-5.5",
9797
modelProvider: "openai",
9898
},

src/gateway/server-methods/models-list-result.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
resolveVisibleModelCatalog,
2525
} from "../../agents/model-catalog-visibility.js";
2626
import type { ModelCatalogEntry } from "../../agents/model-catalog.types.js";
27+
import { resolveCliRuntimeExecutionProvider } from "../../agents/model-runtime-aliases.js";
2728
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
2829
import type { OpenClawConfig } from "../../config/types.openclaw.js";
2930
import { isSecretRef } from "../../config/types.secrets.js";
@@ -195,17 +196,43 @@ function createModelsListProviderAuthChecker(params: {
195196
async function resolveModelsListEntryAvailability(
196197
providerAuthChecker: ModelsListProviderAuthChecker,
197198
entry: ModelCatalogEntry,
199+
cfg: OpenClawConfig,
200+
agentId: string,
198201
): Promise<ModelsListAvailability> {
199202
const primary = await providerAuthChecker(entry.provider, entry.api);
200-
if (primary === true || !isCodexRoutableOpenAIPlatformCatalogEntry(entry)) {
203+
if (primary === true) {
201204
return primary;
202205
}
206+
let available = primary;
207+
const runtimeProvider = resolveCliRuntimeExecutionProvider({
208+
provider: entry.provider,
209+
cfg,
210+
agentId,
211+
modelId: entry.id,
212+
});
213+
if (
214+
runtimeProvider &&
215+
normalizeProviderId(runtimeProvider) !== normalizeProviderId(entry.provider)
216+
) {
217+
const runtimeAvailable = await providerAuthChecker(runtimeProvider);
218+
if (runtimeAvailable === true) {
219+
return true;
220+
}
221+
if (available === false && runtimeAvailable === undefined) {
222+
available = undefined;
223+
}
224+
}
225+
if (!isCodexRoutableOpenAIPlatformCatalogEntry(entry)) {
226+
return available;
227+
}
203228
const codexResponses = await providerAuthChecker(entry.provider, OPENAI_CODEX_RESPONSES_API);
204-
return codexResponses ?? primary;
229+
return codexResponses ?? available;
205230
}
206231

207232
async function buildPublicModelsListEntry(params: {
208233
entry: ModelCatalogEntry;
234+
cfg: OpenClawConfig;
235+
agentId: string;
209236
providerAuthChecker?: ModelsListProviderAuthChecker;
210237
}): Promise<ModelsListEntry> {
211238
const publicEntry = omitRuntimeModelParams(params.entry);
@@ -215,6 +242,8 @@ async function buildPublicModelsListEntry(params: {
215242
const available = await resolveModelsListEntryAvailability(
216243
params.providerAuthChecker,
217244
params.entry,
245+
params.cfg,
246+
params.agentId,
218247
);
219248
return {
220249
...publicEntry,
@@ -233,6 +262,8 @@ async function buildPublicModelsListEntries(params: {
233262
params.catalog.map((entry) =>
234263
buildPublicModelsListEntry({
235264
entry,
265+
cfg: params.cfg,
266+
agentId: params.agentId,
236267
providerAuthChecker,
237268
}),
238269
),

src/gateway/server-methods/models.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,74 @@ describe("models.list", () => {
334334
);
335335
});
336336

337+
it("marks catalog models available through their configured CLI runtime", async () => {
338+
await withEnvAsync({ ANTHROPIC_API_KEY: undefined }, async () => {
339+
await withOpenClawTestState(
340+
{
341+
layout: "state-only",
342+
prefix: "openclaw-models-list-cli-runtime-",
343+
agentEnv: "main",
344+
},
345+
async (state) => {
346+
await state.writeAuthProfiles({
347+
version: 1,
348+
profiles: {
349+
"anthropic:claude-cli": {
350+
type: "oauth",
351+
provider: "claude-cli",
352+
access: "claude-cli-access",
353+
refresh: "claude-cli-refresh",
354+
expires: Date.now() + 30 * 60_000,
355+
},
356+
},
357+
});
358+
359+
const runtimeConfig = {
360+
agents: {
361+
defaults: {
362+
models: {
363+
"anthropic/claude-opus-4-8": {
364+
agentRuntime: { id: "claude-cli" },
365+
},
366+
},
367+
},
368+
},
369+
} as unknown as OpenClawConfig;
370+
const { request, respond } = requestModelsList({
371+
view: "all",
372+
runtimeConfig,
373+
loadGatewayModelCatalog: vi.fn(() =>
374+
Promise.resolve([
375+
{
376+
id: "claude-opus-4-8",
377+
name: "Claude Opus 4.8",
378+
provider: "anthropic",
379+
},
380+
]),
381+
),
382+
reqId: "req-models-list-cli-runtime",
383+
});
384+
await request;
385+
386+
expect(respond).toHaveBeenCalledWith(
387+
true,
388+
{
389+
models: [
390+
{
391+
id: "claude-opus-4-8",
392+
name: "Claude Opus 4.8",
393+
provider: "anthropic",
394+
available: true,
395+
},
396+
],
397+
},
398+
undefined,
399+
);
400+
},
401+
);
402+
});
403+
});
404+
337405
it("marks file SecretRef provider unavailable when read-only auth cannot prove availability", async () => {
338406
const catalog = [{ id: "llama-secure", name: "Llama Secure", provider: "vllm" }];
339407
const cfg = {

ui/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
5+
<meta
6+
name="viewport"
7+
content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content"
8+
/>
69
<title>OpenClaw Control</title>
710
<meta name="color-scheme" content="dark light" />
811
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Provider icon attribution
2+
3+
The 52 `ProviderIcon-*.svg` files in this directory were copied one-for-one
4+
from `Sources/CodexBar/Resources/ProviderIcon-*.svg` in CodexBar commit
5+
`215efbd39ad58acb4b0cd637c407d63e6183bc8d`:
6+
7+
- Source commit:
8+
https://github.com/steipete/CodexBar/commit/215efbd39ad58acb4b0cd637c407d63e6183bc8d
9+
- Source directory:
10+
https://github.com/steipete/CodexBar/tree/215efbd39ad58acb4b0cd637c407d63e6183bc8d/Sources/CodexBar/Resources
11+
- Upstream license:
12+
https://github.com/steipete/CodexBar/blob/215efbd39ad58acb4b0cd637c407d63e6183bc8d/LICENSE
13+
14+
OpenClaw removes XML declarations, external DTD declarations, and generator
15+
metadata before serving the files as public assets. The SVG geometry and visual
16+
content otherwise match the cited upstream files.
17+
18+
Provider names and marks remain the property of their respective owners.
19+
20+
## CodexBar license
21+
22+
MIT License
23+
24+
Copyright (c) 2026 Peter Steinberger
25+
26+
Permission is hereby granted, free of charge, to any person obtaining a copy
27+
of this software and associated documentation files (the "Software"), to deal
28+
in the Software without restriction, including without limitation the rights
29+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30+
copies of the Software, and to permit persons to whom the Software is
31+
furnished to do so, subject to the following conditions:
32+
33+
The above copyright notice and this permission notice shall be included in all
34+
copies or substantial portions of the Software.
35+
36+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42+
SOFTWARE.
Lines changed: 17 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 15 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)