Skip to content
Merged
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
27 changes: 27 additions & 0 deletions apps/desktop/src/renderer/styles/chat-header.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@
-webkit-app-region: no-drag;
}

/* A live Goal can keep creating Turns after the user stops typing, so its
identity and kill switches stay reachable while the transcript scrolls.
Ordinary lineage/revision context remains in-flow when no Goal is present. */
.maka-session-context[data-has-goal="true"] {
position: sticky;
top: 0;
z-index: var(--z-sticky);
box-shadow: 0 var(--border-width-hairline) 0 var(--border-soft);
}

.maka-session-context__inner {
min-width: 0;
/* Control rhythm 36 (lg) — band holds sm/md controls without off-ladder 40. */
Expand Down Expand Up @@ -124,6 +134,23 @@
white-space: nowrap;
}

.maka-session-context__goal-description {
display: block;
min-width: 0;
overflow: hidden;
padding-left: var(--space-1);
text-overflow: ellipsis;
white-space: nowrap;
}

/* Goal rows are intentionally single-line, but Astryx tooltip layers inherit
typography from their trigger subtree even after entering the top layer.
Restore the Tooltip contract here so long Goal copy wraps inside its 300px
content measure instead of being clipped as one unbreakable line. */
.maka-session-context [role="tooltip"] {
white-space: normal;
}

.maka-session-context__cluster {
width: min(100%, 560px);
justify-self: end;
Expand Down
2 changes: 1 addition & 1 deletion docs/astryx-surface-file-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Wiki bar: Design Conventions · API Use-the-System · Theming · Container Paddi
| `packages/ui/src/scheduled-task-inspector.tsx` | module-hub | Button, Divider, HStack, Heading, List, ListItem, MetadataList, MetadataListItem, Switch, Text, VStack | aligned — uses Astryx (Button, Divider, HStack, Heading, List, ListItem, MetadataList, MetadataListItem) | aligned |
| `packages/ui/src/scheduled-task-panel.tsx` | module-hub | Button, Divider, EmptyState, List, ListItem, SegmentedControl, SegmentedControlItem, Selector, Text, TextInput, Toolbar | aligned — uses Astryx (Button, Divider, EmptyState, List, ListItem, SegmentedControl, SegmentedControlItem, Selector) | aligned |
| `packages/ui/src/search-modal.tsx` | dialog-overlay | none | aligned — no raw controls; no Astryx JSX usage | aligned |
| `packages/ui/src/session-context-layer.tsx` | shell-chrome-or-panel | BreadcrumbItem, Breadcrumbs, IconButton, LayoutHeader, Text, Token | aligned — uses Astryx (BreadcrumbItem, Breadcrumbs, IconButton, LayoutHeader, Text, Token) | aligned |
| `packages/ui/src/session-context-layer.tsx` | shell-chrome-or-panel | BreadcrumbItem, Breadcrumbs, IconButton, LayoutHeader, Text, Token, Tooltip | aligned — uses Astryx (BreadcrumbItem, Breadcrumbs, IconButton, LayoutHeader, Text, Token, Tooltip) | aligned |
| `packages/ui/src/session-history-list.tsx` | shell-chrome-or-panel | Badge, VStack | aligned — uses Astryx (Badge, VStack) | aligned |
| `packages/ui/src/session-list-panel.tsx` | shell-chrome-or-panel | SegmentedControl, SegmentedControlItem, SideNav | aligned — uses Astryx (SegmentedControl, SegmentedControlItem, SideNav) | aligned |
| `packages/ui/src/session-rename-dialog.tsx` | dialog-overlay | Button, Dialog, DialogHeader, HStack, Layout, LayoutContent, TextInput | aligned — uses Astryx (Button, Dialog, DialogHeader, HStack, Layout, LayoutContent, TextInput) | aligned |
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/__tests__/session-context-layer-goal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ test('a running goal reads as running and offers pause, with elapsed and tokens'
onClear: () => undefined,
});
assert.ok(markup.includes('Goal 3 of 50'));
assert.ok(markup.includes('Ship the feature'));
assert.ok(markup.includes('role="tooltip"'));
assert.ok(markup.includes('12m'));
assert.ok(markup.includes('12k / 100k'));
assert.ok(markup.includes('Autonomous goal running'));
Expand All @@ -58,6 +60,7 @@ test('a running goal reads as running and offers pause, with elapsed and tokens'
assert.ok(!markup.includes('Resume autonomous goal'));
// The clear kill switch stays.
assert.ok(markup.includes('Clear autonomous goal after 3/50 iterations'));
assert.ok(markup.includes('data-has-goal="true"'));
});

test('a paused goal reads as paused and offers resume, not pause', () => {
Expand Down
12 changes: 11 additions & 1 deletion packages/ui/src/session-context-layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
StatusDot,
Text,
Token,
Tooltip,
type DropdownMenuOption,
} from '@astryxdesign/core';
import { getConversationCopy } from './conversation-copy.js';
Expand Down Expand Up @@ -348,6 +349,7 @@ export function SessionContextLayer(props: {
label={copy.sessionContextAriaLabel}
className="maka-session-context"
data-session-context-layer="true"
data-has-goal={props.goal ? 'true' : undefined}
>
<div className="maka-session-context__inner">
{/* Lineage only. The session's own name moved to the window titlebar
Expand All @@ -356,7 +358,15 @@ export function SessionContextLayer(props: {
twice. What stays is what the titlebar cannot answer: which session
this one branched FROM. */}
<div className="maka-session-context__lineage">
{props.branch ? (
{props.goal ? (
<Tooltip content={props.goal.condition}>
<div className="maka-session-context__goal-description">
<Text type="supporting" maxLines={1}>
{props.goal.condition}
</Text>
</div>
</Tooltip>
) : props.branch ? (
<Breadcrumbs
label={copy.sessionLineageAriaLabel}
variant="supporting"
Expand Down