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
4 changes: 3 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,18 @@ Default surfaces are flat. Depth comes first from the surface ladder, then a lin

## 6. Radius

Nothing interactive is square. One ladder, assigned monotonically by box height:
Nothing interactive is square. One ladder, assigned monotonically by box height — with one register exception, the conversation tier:

| 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` |
| 12px | container | container | modals, panels, portal surfaces; Astryx `Card`, `Dialog`, `DropdownMenu` |
| 16px | conversation | — | the filled user speech bubble, and nothing else |
| 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 Conversation Exception.** The filled user bubble takes 16px regardless of its box height: it is speech, and at the card tier's 10px a one-line message read as a settings row. The tier has exactly one consumer; anything else claiming it is reaching for a softer look, which is not a role. The assistant side stays a ghost bubble — no fill, no corner to round.
- **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
5 changes: 5 additions & 0 deletions apps/desktop/src/renderer/maka-tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,11 @@
--radius-control: 6px;
--radius-surface: 10px;
--radius-modal: 12px;
/* Conversation tier (DESIGN.md §6): the filled user speech bubble only.
The box-height ladder put the 44px bubble on the card tier, where it
read as a settings row; speech rounds by register, not by height. Not
a general rung — chrome and cards stay on the ladder above. */
--radius-conversation: 16px;
--radius-pill: 999px;
/* Square icon plates (provider logo, about logo, feature status) are
governed by RATIO, not by tier: their roundness must read the same
Expand Down
28 changes: 18 additions & 10 deletions apps/desktop/src/renderer/styles/chat-message.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,20 @@
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.
The primitive resolves `--radius-container` (12px). DESIGN.md §6 gives the
filled user bubble the conversation tier (16px): the bubble is speech, not
chrome, and at the card tier's 10px it read as a settings row instead of a
message. 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.

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. */
.maka-chat-message-bubble-user {
background: var(--chat-user-bg);
color: var(--chat-user-foreground, var(--foreground));
border-radius: var(--radius-surface);
border-radius: var(--radius-conversation);
white-space: pre-wrap;
}

Expand All @@ -91,19 +90,28 @@
padding: var(--space-0-5) 0 0;
}

/* Reading rhythm between a turn's evidence blocks (reasoning row, tool group,
prose). At --space-1 the blocks measured 8-12px apart and read as one dense
log; --space-3 lands them at ~16-20px, the reading tier DESIGN.md asks for
("spacious around reading"). Turn-to-turn spacing is ChatMessageList's own
balanced gap and is not declared here. */
.maka-processing-sequence {
display: flex;
min-width: 0;
flex-direction: column;
gap: var(--space-1);
gap: var(--space-3);
}

/* Expanded activity headers stay reachable while their own detail is being
read. Native sticky positioning keeps the header in the transcript flow,
so it leaves naturally at the card boundary and does not disturb ChatLayout
scroll anchoring while reasoning or tool output continues to stream. */
/* Sticky offset 0, not a breathing gap: any positive offset opens a strip
above the stuck header through which the previous row scrolls half-visible
(measured 8px of bleed-through at --space-2). Flush with the scrollport is
what sticky headers look like everywhere else in the product. */
.maka-turn :is(.maka-deep-thinking, .maka-tool-activity-card) {
--maka-activity-sticky-top: var(--space-2);
--maka-activity-sticky-top: 0px;
--maka-activity-header-height: var(--h-control-sm);
}

Expand Down
12 changes: 8 additions & 4 deletions packages/ui/src/chat-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,13 @@ export function ChatView(props: {
header used to be rendered here anyway, holding a lone spacer, to
occupy the window titlebar line — which the shell's titlebar row now
owns. */}
{/* `balanced`, Astryx's own reading density. The list used to force
`compact` with a `gap={4}` override that already restored the
balanced gap — so compact bought nothing but the tighter padding,
against DESIGN.md's "spacious around reading". */}
<ChatMessageList
className="maka-chat-message-list maka-chatContent"
density="compact"
gap={4}
density="balanced"
emptyState={conversationItems.length === 0 ? emptyContent : undefined}
>
{conversationItems.length > 0 ? (
Expand Down Expand Up @@ -578,11 +581,12 @@ export function ChatView(props: {
onNavigateFallback={navigatePromptRailFallback}
onNavigateStart={chatLayout.unlockAutoFollow}
/>
{/* `balanced` for the same reason as the empty-session list above:
compact + `gap={4}` was balanced's gap with compact's padding. */}
<ChatMessageList
className="maka-chat-message-list maka-chatContent"
data-turn-source-count={turns.length}
density="compact"
gap={4}
density="balanced"
isStreaming={streamingActive}
emptyState={showEmptyState ? emptyContent : undefined}
>
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@
opacity: 1;
}
.maka-user-quotes { display: flex; max-width: 100%; flex-wrap: wrap; align-items: flex-start; gap: 4px; }
.maka-assistant-answer-content { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: 8px; }
/* 16px, the same reading rhythm as .maka-processing-sequence's --space-3 +
the tool group's own 8px margin: evidence and answer are separate thoughts,
and at 8px the prose sat glued to the last tool row. */
.maka-assistant-answer-content { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: 16px; }
.maka-subagent-session-label,
.maka-subagent-session-end {
display: flex;
Expand Down