Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,12 @@ Nothing interactive is square. One ladder, assigned monotonically by box height:
| Radius | Maka tier | Astryx tier | Assign to |
|---|---|---|---|
| 6px | control | inner | chips, keycaps, nested inlays, and product-drawn compact controls |
| 10px | card | element | cards, rows-as-cards, list containers, chat bubbles; Astryx `Button`, `Input`, `SegmentedControl` |
| 10px | card | element | cards, rows-as-cards, list containers; Astryx `Button`, `Input`, `SegmentedControl` |
| 12px | container | container | modals, panels, portal surfaces; Astryx `Card`, `Dialog`, `DropdownMenu` |
| 28px | chat | chat | the conversation surface as one shape: user bubble, assistant bubble, composer dock. Sourced from Astryx's `--radius-chat` by `ChatMessageBubble` and `ChatComposer` themselves; product CSS never restates it |
| full | pill (999px) | full (9999px) | badges, pills, circular controls |

- **The Two-Name Rule.** These are one ladder under two vocabularies, and the names never line up: Maka's `control` is Astryx's `inner`, Maka's `card` is Astryx's `element`, Maka's `modal` is Astryx's `container`. Resolve a tier from the box, never from the token name that sounds right. The paired values agree *today* but are independent literals, not aliases — an Astryx upgrade can move one side silently, so a mismatch is a real failure mode rather than an impossibility. Astryx's `--radius-page` (28px) has no Maka tier and no product consumer; anything reaching for a page-level radius is inventing a rung.
- **The Two-Name Rule.** These are one ladder under two vocabularies, and the names never line up: Maka's `control` is Astryx's `inner`, Maka's `card` is Astryx's `element`, Maka's `modal` is Astryx's `container`. Resolve a tier from the box, never from the token name that sounds right. The paired values agree *today* but are independent literals, not aliases — an Astryx upgrade can move one side silently, so a mismatch is a real failure mode rather than an impossibility. The chat rung is the one tier the product does not assign: `ChatMessageBubble` and `ChatComposer` both resolve `--radius-chat` on their own, which is why the bubble and the dock round together, and why setting a bubble radius or a non-default `density` in product code silently breaks the pair. Astryx's `--radius-page` carries the same 28px literal but is a different token with no Maka tier and no product consumer; reaching for it to match the chat surface is inventing a rung.
- **The Full-Bleed Rule.** `border-radius: 0` is legal only on true full-bleed rows — an element flush with its container on both sides. Radius and gap move together: if it has breathing room, it has corners.
- **Proportional marks.** Product-drawn icon plates use ratio-owned radius (~25–27% of the box edge), recorded in prose because Stitch accepts only absolute units.

Expand Down
26 changes: 26 additions & 0 deletions apps/desktop/e2e/session-workbar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ test('narrow right workbar keeps launcher shortcuts and side-chat send button in
const composerCard = companion.locator('.maka-composer-astryx');
// Keep ChatComposer's inner elevation visible.
await expect(composerCard).toHaveCSS('overflow', 'visible');

// #3452: Side Chat is a branch of the main conversation, not a second
// conversation surface, so its dock rounds like the main dock. Both resolve
// Astryx's `--radius-chat`, the same token `ChatMessageBubble` defaults to —
// a side-only `--_chat-composer-radius` split the bubble from the dock the
// moment the bubbles moved back to that default. Compared against the main
// composer rather than a literal so an upstream token change moves both or
// fails here.
const composerRadii = await page.evaluate(() => {
const wrappers = [...document.querySelectorAll('.maka-composer-astryx')];
const inCompanion = (element: Element) => element.closest('.maka-quote-companion') !== null;
const effectiveRadius = (element: Element | undefined) => {
if (!element) return null;
const styles = getComputedStyle(element);
return (
styles.getPropertyValue('--_chat-composer-radius').trim() ||
styles.getPropertyValue('--radius-chat').trim()
);
};
return {
side: effectiveRadius(wrappers.find(inCompanion)),
main: effectiveRadius(wrappers.find((element) => !inCompanion(element))),
};
});
expect(composerRadii.side).toBeTruthy();
expect(composerRadii.side).toBe(composerRadii.main);
// Match the long model-label pressure from the reported side-chat screenshot
// without coupling the fixture's globally useful default model to this test.
await companion.locator('.maka-composer-model-chip-text').evaluate((element) => {
Expand Down
27 changes: 12 additions & 15 deletions apps/desktop/src/renderer/styles/chat-message.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,23 @@
font: var(--maka-text-body);
}

/* Padding and max-width belong to ChatMessageBubble; re-declaring those here
only forks the primitive. What stays is the product tint, the verbatim-text
rule (Maka renders user input as typed, which the primitive has no opinion
about) — and, since Visual System 2.0 (T3), the radius TIER.

The primitive resolves `--radius-container` (12px). DESIGN.md §6 assigns
radius by box height, and container is scoped to "modals, panels, portal
surfaces" — a message bubble is none of those. Measured in the chat fixture
the filled user bubble is 44px tall, which lands it in the card tier (10px,
"cards, rows-as-cards, list containers"). Astryx publishes no radius prop on
this component (only `variant`), so the tier is expressed on Maka's own
class rather than by reaching into the primitive's internals.
/* Padding, max-width and radius belong to ChatMessageBubble; re-declaring any
of them here only forks the primitive. What stays is the product tint and the
verbatim-text rule (Maka renders user input as typed, which the primitive has
no opinion about).

Radius is absent on purpose. The primitive resolves `--radius-chat` (28px),
the same token `ChatComposer` reads through `--_chat-composer-radius`, so the
bubble and the dock round together as one conversation surface — DESIGN.md §6
records that as the chat rung. Astryx publishes no radius prop on this
component (only `variant`), so a product radius could only fight the
primitive, never configure it.

Scoped to the filled bubble on purpose: the assistant bubble is `ghost` (no
fill, measured transparent), so it has no corner to round and inherits
nothing it needs to override. */
fill, measured transparent), so it carries no tint to override. */
.maka-chat-message-bubble-user {
background: var(--chat-user-bg);
color: var(--chat-user-foreground, var(--foreground));
border-radius: var(--radius-surface);
white-space: pre-wrap;
}

Expand Down
15 changes: 7 additions & 8 deletions apps/desktop/src/renderer/styles/quote-side-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,13 @@
padding-bottom: var(--space-2);
}

.maka-session-workbar-panel[data-placement="right"]
.maka-quote-companion
.maka-composer-astryx {
--_chat-composer-radius: var(--radius-surface);

/* Let ChatComposer paint its own radius and elevation. Clipping this wrapper
hides the body's hover/focus shadow against the white side panel. */
}
/* `.maka-composer-astryx` is deliberately unstyled on this surface. Side Chat is
a branch of the main conversation, not a second one, so the composer resolves
`--radius-chat` here exactly as it does at full width and pairs with the
bubble; a side-only radius token forked the pair the moment the bubbles moved
to the primitive's default. The wrapper is also left unclipped — `overflow:
hidden` here would hide the body's hover/focus shadow against the white side
panel, and `session-workbar.spec.ts` asserts `overflow: visible`. */

.maka-session-workbar-panel[data-placement="right"]
.maka-quote-companion
Expand Down
4 changes: 0 additions & 4 deletions packages/ui/src/chat-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,6 @@ export function ChatView(props: {
owns. */}
<ChatMessageList
Comment thread
Astro-Han marked this conversation as resolved.
className="maka-chat-message-list maka-chatContent"
density="compact"
gap={4}
emptyState={conversationItems.length === 0 ? emptyContent : undefined}
>
{conversationItems.length > 0 ? (
Expand Down Expand Up @@ -589,8 +587,6 @@ export function ChatView(props: {
<ChatMessageList
className="maka-chat-message-list maka-chatContent"
data-turn-source-count={turns.length}
density="compact"
gap={4}
isStreaming={streamingActive}
emptyState={showEmptyState ? emptyContent : undefined}
>
Expand Down