diff --git a/apps/desktop/src/renderer/styles/workbar/inspector.css b/apps/desktop/src/renderer/styles/workbar/inspector.css
index 6e5ad01d2b..d8d378d734 100644
--- a/apps/desktop/src/renderer/styles/workbar/inspector.css
+++ b/apps/desktop/src/renderer/styles/workbar/inspector.css
@@ -28,6 +28,7 @@
height: 100%;
min-height: 0;
overflow-y: auto;
+ container: maka-inspector / inline-size;
}
/* The live region is a wrapper only; its children carry their own spacing. */
@@ -125,7 +126,7 @@
font: var(--maka-text-label);
color: var(--destructive-text);
min-width: 0;
- overflow-wrap: anywhere;
+ white-space: nowrap;
}
.maka-inspector-turn-meta {
@@ -141,6 +142,37 @@
color: var(--foreground-secondary);
}
+/* The default 480px workbar does not leave enough room for the timestamp,
+ failure and measurement to remain one useful line in every locale. Reflow
+ from the inspector's own width rather than the window's: the workbar can be
+ resized independently, and the same panel can also live in the bottom
+ placement. The identity gets the first row; status and measurement become
+ a compact comparison row below it. Keeping the failure phrase atomic avoids
+ the character-by-character column that `overflow-wrap: anywhere` produced. */
+@container maka-inspector (max-width: 32rem) {
+ .maka-inspector-turn-head {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ row-gap: var(--space-1);
+ }
+
+ .maka-inspector-turn-label {
+ grid-column: 1 / -1;
+ min-width: 0;
+ white-space: normal;
+ }
+
+ .maka-inspector-turn-failure {
+ grid-column: 1;
+ }
+
+ .maka-inspector-turn-meta {
+ grid-column: 2;
+ justify-self: end;
+ margin-left: 0;
+ }
+}
+
.maka-inspector-steps {
gap: var(--space-1);
}
diff --git a/apps/desktop/stories/session-workbar.stories.tsx b/apps/desktop/stories/session-workbar.stories.tsx
index 35f3fb080a..3dce3fcf81 100644
--- a/apps/desktop/stories/session-workbar.stories.tsx
+++ b/apps/desktop/stories/session-workbar.stories.tsx
@@ -19,6 +19,7 @@
import type { CSSProperties } from 'react';
import type { Decorator, Meta, StoryObj } from '@storybook/react-vite';
+import { expect, waitFor } from 'storybook/test';
import type { ArtifactRecord } from '@maka/core/artifacts';
import type { BrowserState } from '@maka/core/browser';
import type { GitReviewSnapshot } from '@maka/core/git-review';
@@ -433,6 +434,18 @@ const populatedTrace: SessionTrace = {
},
};
+const narrowTrace: SessionTrace = {
+ ...populatedTrace,
+ turns: populatedTrace.turns.map((turn) =>
+ turn.turnId === 'turn-2'
+ ? {
+ ...turn,
+ failure: { code: 'turn_aborted', message: 'turn was aborted' },
+ }
+ : turn,
+ ),
+};
+
const populatedContext: ContextDiagnosticsResult = {
status: 'available',
providerId: 'zai',
@@ -933,6 +946,39 @@ export const Trace: Story = {
render: () => ,
};
+// Real path: resize the right workbar to its 320px floor while a failed turn
+// is visible. The timestamp owns the first row; the failure and measurement
+// share the second without squeezing the failure into a vertical word.
+export const TraceMinimumWidth: Story = {
+ decorators: [bridge({ trace: narrowTrace, context: populatedContext })],
+ render: () => ,
+ play: async ({ canvasElement }) => {
+ await waitFor(() => {
+ const failure = canvasElement.querySelector(
+ '[data-maka-contract="session-inspector-turn-failed"]',
+ );
+ const turn = failure?.closest(
+ '[data-maka-contract="session-inspector-turn"]',
+ );
+ const label = turn?.querySelector('.maka-inspector-turn-label');
+ const meta = turn?.querySelector('.maka-inspector-turn-meta');
+ expect(failure).not.toBeNull();
+ expect(turn).not.toBeNull();
+ expect(label).not.toBeNull();
+ expect(meta).not.toBeNull();
+ if (!failure || !turn || !label || !meta) return;
+
+ const failureRect = failure.getBoundingClientRect();
+ const labelRect = label.getBoundingClientRect();
+ const metaRect = meta.getBoundingClientRect();
+ expect(failureRect.top).toBeGreaterThan(labelRect.top);
+ expect(Math.abs(failureRect.top - metaRect.top)).toBeLessThanOrEqual(1);
+ expect(failureRect.height).toBeLessThanOrEqual(metaRect.height + 1);
+ expect(turn.scrollWidth).toBeLessThanOrEqual(turn.clientWidth);
+ });
+ },
+};
+
// Real path: the first bounded page of a longer trace. The continuation control
// sits before the ascending timeline because earlier records are inserted at
// that edge, not after the newest turn.