diff --git a/docs/UI_MODERNIZATION_AUDIT.md b/docs/UI_MODERNIZATION_AUDIT.md
index dca6b8d..c62a3c9 100644
--- a/docs/UI_MODERNIZATION_AUDIT.md
+++ b/docs/UI_MODERNIZATION_AUDIT.md
@@ -1,6 +1,6 @@
# Cortex UI modernization audit
-**Status:** UI modernization follow-up complete; dedicated image-editor affordances and preset prompt cards removed; native package rebuilt and startup-verified
+**Status:** UI modernization follow-up complete; dedicated image-editor affordances, preset prompt cards, and composer clutter removed; native package rebuilt and startup-verified
**Scope:** Every user-facing frontend surface, not a cosmetic theme pass
## What was audited
@@ -19,7 +19,7 @@ primary problems were structural:
| --- | --- | --- |
| Workspace shell | The top bar conveyed almost no context and the sidebar was a loose list of text. | A clear workspace header, local-runtime status, product identity, conversation count, and a deliberate sidebar footer establish hierarchy. |
| Conversation canvas | A new chat opened into a large, empty dark region, then introduced generic preset prompts. | A quiet new-thread orientation gives the canvas context while leaving the user in control of the first message. |
-| Composer | The input was visually stranded in a full-width bottom strip. | The composer is now a centered, elevated input island with integrated local-model context, state feedback, and responsive sizing. |
+| Composer | The input was visually stranded in a full-width bottom strip, with redundant badges and competing colored controls. | The composer is a compact centered island with one model control, grouped metadata, neutral idle chrome, and an accent send action only when actionable. |
| Conversation controls | Saved chats, rename, and delete controls had weak grouping and feedback. | The list uses active/hover/focus states, reveal-on-focus actions, and purposeful confirmation dialogs. |
| Settings and submenus | Settings read as a large collection of unrelated boxes. | Text-led categories, descriptive labels, and a unified detail pane make settings read like a native control surface. |
| Supporting flows | Setup, model pulls, memory, jobs, errors, and notifications used unrelated visual patterns; the chat also advertised an image editor that is not Cortex's product surface. | Shared surfaces, status colors, progress treatment, form controls, and meaningful feedback now connect those flows. Code-driven image work stays behind the execution layer instead of appearing as a first-class editor. |
@@ -56,7 +56,8 @@ opaque background service.
not advertise image transformation, image generation, or preset prompt
suggestions.
- `frontend/src/components/MessageComposer.tsx` integrates local-model context
- into the composer island.
+ into the composer island with a compact, quiet utility row and state-based
+ send control.
- `frontend/src/components/SettingsPanel.tsx` turns settings into a structured
text-led category navigation with descriptions while retaining existing
keyboard and screen-reader names.
@@ -74,7 +75,7 @@ build, and browser-level flows for new chat, streaming, retry/regenerate/fork,
settings, memory, model progress, and compact-window composer behavior. The
native Windows package must also launch from a fresh build before release.
-This slice passed the release checks: 44 component tests in a single worker,
+This slice passed the release checks: 45 component tests in a single worker,
6 browser-level flows, typecheck, lint, production build, a fresh Windows
package build, and a packaged launch with `GET /api/v1/health/ready` returning
HTTP 200. The production bundle contains no dedicated image-transform UI
diff --git a/frontend/src/components/MessageComposer.test.tsx b/frontend/src/components/MessageComposer.test.tsx
index 35237be..47ef652 100644
--- a/frontend/src/components/MessageComposer.test.tsx
+++ b/frontend/src/components/MessageComposer.test.tsx
@@ -48,6 +48,13 @@ function ComposerHarness({
}
describe("MessageComposer", () => {
+ it("keeps the utility row quiet and model-focused", () => {
+ render();
+
+ expect(screen.queryByText("LOCAL ENGINE")).not.toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "Selected local model: local-chat:7b" })).toBeVisible();
+ });
+
it("submits once with Enter and clears only after acceptance", async () => {
const user = userEvent.setup();
const request = deferred();
diff --git a/frontend/src/components/MessageComposer.tsx b/frontend/src/components/MessageComposer.tsx
index 1a0a26d..e4f06ae 100644
--- a/frontend/src/components/MessageComposer.tsx
+++ b/frontend/src/components/MessageComposer.tsx
@@ -195,7 +195,6 @@ export function MessageComposer({
/>