diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1e3be4a3fd..a3c04f3ae887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai - **TUI fuzzy selectors:** delegate list matching to pi-tui, adding slash-token and alpha-number matching while removing the local matcher fork. - **macOS paired-node terminals:** advertise duplex Codex and Claude terminal resume commands from the embedded node host and forward interactive input and cancellation through the native app bridge. (#107335) - **Control UI catalog terminals:** open eligible Codex and Claude Code sessions in the native CLI on their Gateway or paired-node host, with viewer-versus-terminal preferences, validated resume commands, and an interactive PTY relay. (#107086) +- **Control UI coding catalogs:** show provider brand icons beside Claude Code and Codex session catalog headings in the sidebar. - **Skill Workshop history review:** add a manual, newest-first session scan that progressively searches older substantial work for conservative skill ideas, stores only SQLite cursor metadata, and leaves up to three results as pending proposals even when autonomous self-learning is disabled. (#106182) - **SQLite snapshots:** add `openclaw backup sqlite create|list|verify|restore` for compact, verified global and per-agent database artifacts with fresh-target-only restore. (#94805) Thanks @giodl73-repo. - **GPT-5.6 Ultra and runtime switching:** support Sol, Terra, and Luna across OpenClaw and Codex engines; keep model, runtime, and thinking selection atomic through `/model` and fallback; and add live matrix coverage for both harnesses. (#98021) Thanks @anyech. diff --git a/ui/src/components/app-sidebar-session-catalog-render.ts b/ui/src/components/app-sidebar-session-catalog-render.ts index b908e1f73073..78ab8e5d3318 100644 --- a/ui/src/components/app-sidebar-session-catalog-render.ts +++ b/ui/src/components/app-sidebar-session-catalog-render.ts @@ -25,6 +25,7 @@ import { } from "./app-sidebar-session-catalogs.ts"; import { renderSidebarSessionSectionHeader } from "./app-sidebar-session-section-header.ts"; import { icons } from "./icons.ts"; +import { hasProviderBrandIcon, renderProviderBrandIcon } from "./provider-icon.ts"; import { renderSessionRowBadges } from "./session-row-badges.ts"; type SessionCatalogGroupsParams = { @@ -180,6 +181,11 @@ export function renderSessionCatalogGroups(params: SessionCatalogGroupsParams) { title=${hasError ? errorHelp : nothing} @click=${() => params.onToggleSection(sectionId)} > + ${hasProviderBrandIcon(catalog.id) + ? renderProviderBrandIcon(catalog.id, { + className: "sidebar-session-catalog-provider-icon", + }) + : nothing} ${catalog.label} { }, ], }, + { + id: "claude", + label: "Claude Code", + capabilities: { continueSession: true, archive: false }, + hosts: [ + { + hostId: "gateway:claude", + label: "Local Claude", + kind: "gateway", + connected: true, + sessions: [ + { + threadId: "thread-claude", + name: "Review the provider catalog UI", + cwd: "/workspace/openclaw", + status: "idle", + archived: false, + canContinue: true, + canArchive: false, + }, + ], + }, + ], + }, ], }, }, @@ -179,7 +203,25 @@ suite("Codex native session catalog", () => { const workSection = sessionGroups.locator(':scope > [data-session-section="work"]'); const liveRows = workSection.locator(":scope > .sidebar-recent-sessions__list"); const catalog = sessionGroups.locator(':scope > [data-session-section="catalog:codex"]'); + const claudeCatalog = sessionGroups.locator( + ':scope > [data-session-section="catalog:claude"]', + ); await catalog.waitFor({ state: "visible" }); + await claudeCatalog.waitFor({ state: "visible" }); + await expect + .poll(() => + catalog + .locator(".sidebar-session-catalog-provider-icon") + .getAttribute("data-provider-icon"), + ) + .toBe("codex"); + await expect + .poll(() => + claudeCatalog + .locator(".sidebar-session-catalog-provider-icon") + .getAttribute("data-provider-icon"), + ) + .toBe("claude"); const [liveRowsBox, catalogBox] = await Promise.all([ liveRows.boundingBox(), catalog.boundingBox(), diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css index 70853a9bf2f3..0d9a541fa486 100644 --- a/ui/src/styles/layout.css +++ b/ui/src/styles/layout.css @@ -1414,6 +1414,12 @@ html.openclaw-native-macos stroke-width: 1.8px; } +.sidebar-session-catalog-provider-icon { + width: 12px; + height: 12px; + flex: 0 0 12px; +} + .sidebar-session-group-count { flex: 0 0 auto; color: var(--muted); diff --git a/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts b/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts index feae719b5f9e..0a5dfcccdf16 100644 --- a/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts +++ b/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts @@ -225,6 +225,11 @@ describe("AppSidebar session catalog pagination", () => { await sidebar.updateComplete; const section = sidebar.querySelector(`[data-session-section="catalog:${id}"]`); + expect( + section + ?.querySelector(".sidebar-session-catalog-provider-icon") + ?.getAttribute("data-provider-icon"), + ).toBe(id); const hostGroups = section?.querySelectorAll("[data-session-catalog-host]"); expect(Array.from(hostGroups ?? []).map((host) => host.dataset.sessionCatalogHost)).toEqual([ "gateway:local",