Skip to content

Commit 02a65c6

Browse files
committed
Align sidebar toggles, message hover and workspace controls
1 parent 3874b9e commit 02a65c6

24 files changed

Lines changed: 205 additions & 135 deletions

File tree

‎.agents/skills/implement-platform/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Read `apps/sim/platform/AGENTS.md` first. It is the canonical ownership and code
1313
Consult the tracked component ownership map before adding markup. Choose the existing action button, navigable row, two-line activity row, picker, or disclosure by behavior. Render Base UI triggers through their controls. Trace copy/status lifetimes to the shared expiry owner; verify pause, reset, reduced motion and long tooltip limits. Compare Home/Chat reading edges and stable controls across responsive layouts, including structural rules and the actual scroll boundary.
1414
4. **Translate anatomy, not artboard coordinates.** Consult the installed Next docs in `node_modules/next/dist/docs` (especially project structure, CSS, Server/Client Components). Use semantic DOM, native or accessible behaviors, intrinsic size and responsive relationships. A 560px Paper modal is a preferred maximum, not a fixed width. The same modal should fit 647px and 390px viewports without new variants. Use centralized Tailwind roles, the type and material contracts, shared icon geometry and slots. Confirm the rendered DOM has the intended behavior. A searchable field accepts text, a disclosure opens by keyboard, a modal part composes directly, and a table renders the caller’s records. Repeated data takes an array, never a fixed list of numbered specimen props. Register a named native component for these cases. A new size requires a distinct, evidenced use; it isn't a device enum. Never pass styles through a composition. Trace repeated rows to their registered item and preserve the surrounding layout. Accept a typed collection, then render empty, short and longer-than-specimen data to prove that any example count can change. Keep different semantic roles distinct. Wrap Base UI primitives in EMCN when behavior involves floating layers, list navigation, cascading actions or focus management. Main-view and modal dropdowns share the same surface and rows. Preserve the caller’s value, accessible label and form submission through the owner. When editing generated files, change the source contract or compiler and regenerate the affected set.
1515
5. **Exercise the right states.** Build a matrix of actual themes, relevant content sizes, resting, hover, selected, error and disabled states, keyboard navigation, long data, zoom and widths between Paper examples. The workbench is tooling, not a Phase 2 product screen. Entities, including organizations, workspaces, members and chats, come only through the server fixture gate. Render empty or absent data distinctly. Focusing a control or typing in it preserves its resting chrome; verify there is no focus paint on the control or its wrappers. Test keyboard focus and restoration separately. For cascading menus, test hover travel, arrow keys, Escape one level at a time, and bounds at either edge of the viewport and inside a modal. Client interaction may open, dismiss or change steps; no authentication, persistence or network effects.
16-
6. **Prove it.** Run the structural audit against real code, regenerate/check relevant files, render the app in a real browser, and compare rendered geometry, type, icon ink/weight and interactions with the product contract and relevant Paper states. Exercise empty, full, stress and flag-off. Retire unused variants and resolve every affected use. Measure the shell inset against both sides of the footer, the Home greeting against its navigation row, and adjacent chat line boxes across thinking, tools and prose. Check destructive controls with pointer, keyboard menus and both themes. Verify isolation and the repository gates. Report covered keys/states separately from visual comparisons; record unresolved differences and durable verification paths. Stop at the authorized phase.
16+
6. **Prove it.** Run the structural audit against real code, regenerate/check relevant files, render the app in a real browser, and compare rendered geometry, type, icon ink/weight and interactions with the product contract and relevant Paper states. Exercise empty, full, stress and flag-off. Retire unused variants and resolve every affected use. Measure the shell inset against both sides of the footer, the Home greeting against its navigation row, and adjacent chat line boxes across thinking, tools and prose. Compare primary and destructive confirmation lightness changes in both themes. Check that dismissing a recent stays neutral, expand/collapse controls share a height, and message metadata remains visible as the pointer crosses into its toolbar. Verify isolation and the repository gates. Report covered keys/states separately from visual comparisons; record unresolved differences and durable verification paths. Stop at the authorized phase.
1717

1818
**Product and desktop.** Map the feature to its existing route, feature owner, shared controls and both host compositions. Calendar events or workspace summaries can be unique, while their type, left/stacked text alignment, insets and action menus remain system-owned. Keep one renderer for browsers and Electron: see the Platform contract and `apps/desktop/README.md`. Use the typed preload adapter to detect desktop, leave OS window controls native, and exercise the actual desktop shell with a disposable profile. Cover fullscreen, split/expanded panels, compact takeover and returning to the previous tab. Keep real native execution and persisted resources for Phase 3.
1919

‎apps/sim/platform/AGENTS.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Keep routes thin. Colocate each feature component, its CSS Module and private he
2222

2323
Use `design-system/styles/rhythm.css` for title/detail, list and action gaps (2px), row insets and icon sizing. Separate sibling rows with the sidebar list gap, without divider rules. Borders delineating surfaces and actual data rules, such as chart axes, serve different purposes. Align a leading avatar or glyph with the first text line; center trailing metadata, badges and actions against the whole row. Align action ink with adjacent text by extending the hit area into the margin. Chat thinking, tool labels and prose share that text edge. Composer and message action groups use the same compact gap. Custom calendar, chart and summary rows use the same type, spacing and tooltips.
2424

25-
Home and Chat share a centered, border-box reading column (maximum width and inline gutter). Align the resting Home greeting with the Home navigation row using the shell’s shared row origin **inside** the moving content. The unpadded viewport keeps the fade at the window edge. Keep one 12px shell inset above the organization and immediately above and below the footer. Major dashboard/sidebar sections are independently spaced at 24px. Responsive layouts can wrap, collapse or split panels; button height, padding and type stay stable.
25+
Home and Chat share a centered, border-box reading column (maximum width and inline gutter). Align the resting Home greeting with the Home navigation row using the shell’s shared row origin **inside** the moving content. The unpadded viewport keeps the fade at the window edge. The web opener sits at the expanded sidebar toolbar’s 12px top inset on every route. On Home it scrolls with the page. Electron keeps both controls in its native titlebar. Keep the same shell inset immediately above and below the footer. Major dashboard/sidebar sections are independently spaced at 24px. Responsive layouts can wrap, collapse or split panels; button height, padding and type stay stable.
2626

27-
Use `ControlRow` for the sidebar, identity controls, panel tabs, recent links, wrapping workspace choices and horizontal destination links. The row owns its 30px height, 8px inset and the placement and hover of a 24px action, including a leading remove button that replaces the glyph. Keep the link, tab or button semantics on the rendered element, with sibling actions outside it. Whole section headers reveal their chevron on hover; identity controls need none. Ordinary text and icon triggers use `Button` / `IconButton`, including Base UI menu triggers. Pass icons and labels through their slots. Binary switches stay pill-shaped; clicking the selected half of a two-choice segment switches to the other half. Native keyboard choice navigation still selects its destination.
27+
Use `ControlRow` for the sidebar, identity controls, panel tabs, recent links, wrapping workspace choices and horizontal destination links. The row owns its 30px height, 8px inset and the placement and hover of a 24px action, including a leading remove button that replaces an existing glyph. Named workspace rows are text-only; a recent without a glyph keeps its remove action at the end. The Workspaces destination may have an icon. Keep the link, tab or button semantics on the rendered element, with sibling actions outside it. Whole section headers reveal their chevron on hover; identity controls need none. Ordinary text and icon triggers use `Button` / `IconButton`, including Base UI menu triggers. Pass icons and labels through their slots. Binary switches stay pill-shaped; clicking the selected half of a two-choice segment switches to the other half. Native keyboard choice navigation still selects its destination.
2828

2929
Use `ActivityRow` for a first-line glyph or avatar, title/detail and centered trailing metadata. Use the chart, calendar, composer or attachment owner when those parts have a different interaction. See the [component ownership map](docs/component-ownership.md) before adding another family.
3030

@@ -34,9 +34,9 @@ Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labe
3434

3535
Use `useClipboard` / `CopyButton` for successful copy feedback and `useFeedbackExpiry` for temporary status. Copied indicators and transient notices dwell for four seconds; repeated events restart the timer. Pause an actionable notice during hover or focus and keep its action available in the stack afterward. Icon color changes use `--ui-motion-color` (120ms, zero under reduced motion). Do not put literal timers or CSS durations in product components.
3636

37-
Chat thinking, tool summaries and prose paragraphs share one 12px block gap. Expanded payloads have equal block padding; the final message keeps a section of clearance from the composer. Render pasted text, files and images with ControlRow and a sibling remove action. Tool summaries use one body-size label with quieter ink. A run divider, expanded tool rail and split-panel edge are structural rules with one shared hairline role. Ordinary list items remain separated by gap alone. Panel tabs, action controls and key values have their own semantics; the same CopyButton copies message text and either tool payload. Composer and jump-to-latest share their border, background and shadow; leave space between them.
37+
Each chat message owns a continuous hover area from its content through its action row. Its timestamp and actions stay available when navigating by keyboard and on touch. Chat thinking, tool summaries and prose paragraphs share one 12px block gap. Expanded payloads have equal block padding; the final message keeps a section of clearance from the composer. Render pasted text, files and images with ControlRow and a sibling remove action. Tool summaries use one body-size label with quieter ink. A run divider, expanded tool rail and split-panel edge are structural rules with one shared hairline role. Ordinary list items remain separated by gap alone. Panel tabs, action controls and key values have their own semantics; the same CopyButton copies message text and either tool payload. Composer and jump-to-latest share their border, background and shadow; leave space between them.
3838

39-
Interface strokes have one non-scaling optical weight in CSS pixels. Size glyphs relative to the control’s type, capped at the shared icon size. Paint interface strokes with opaque semantic ink so overlapping segments do not darken; branded artwork and chart marks retain their own geometry. Neutral quiet controls gain the hover surface without changing ink. Destructive confirmations use the same filled-state color steps as primary buttons. Destructive quiet actions and menu items turn red on hover or active menu navigation; dismissing a surface stays neutral. Callers choose intent, never paint. A selectable menu row gains the same surface and promotes its glyph from secondary to primary ink on hover or active keyboard navigation. Selection, disabled and inverse controls have distinct semantics. Never change stroke thickness, spacing or radius on hover. Render interface glyphs through `Icon`. A unique data graphic may own accessible, labeled SVG and paint its own marks. DOM focus alone adds no paint.
39+
Interface strokes have one non-scaling optical weight in CSS pixels. Size glyphs relative to the control’s type, capped at the shared icon size. Paint interface strokes with opaque semantic ink so overlapping segments do not darken; branded artwork and chart marks retain their own geometry. Neutral quiet controls gain the hover surface without changing ink. Destructive and primary confirmations change perceived lightness by the same amount within each theme; check rendered colors, not blend percentages. Destructive menu commands turn red on hover or keyboard highlight. A compact × dismisses a tab, recent, draft attachment or recipient with the ordinary quiet IconButton. Callers choose intent, never paint. A selectable menu row gains the same surface and promotes its glyph from secondary to primary ink on hover or active keyboard navigation. Selection, disabled and inverse controls have distinct semantics. Never change stroke thickness, spacing or radius on hover. Render interface glyphs through `Icon`. A unique data graphic may own accessible, labeled SVG and paint its own marks. DOM focus alone adds no paint.
4040

4141
## Public API
4242

‎apps/sim/platform/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Shared and Platform still own different recipes; `catalog/evidence.json` preserv
3939

4040
Workspace lists, sidebar items, recents and attachment batches accept typed arrays with stable item IDs. Use the named components for these collection masters: a Paper specimen's item count is sample data. An empty array renders zero items, and adding items preserves their registered row/card appearance. Each scroll viewport determines fades and controls from its actual contents. Chart labels, marks, inspected values and legends come from the same series. Supply raw numeric values and their units to data tables. The column schema determines alignment and which columns remain visible as the table narrows.
4141

42-
The full fixture includes twenty sidebar workspaces, sixteen conversations, a twelve-message inbox and a calendar that scrolls from midnight to midnight even on empty days. Home and Chat share a reading width. The resting Home greeting aligns with the sidebar Home row. The shell uses one 12px inset above the organization and on both sides of the profile row. The Home inset scrolls away through the viewport edge fade. Calendar events open local details; attendees have a separate control. Provider navigation awaits real event URLs.
42+
The full fixture includes twenty sidebar workspaces, sixteen conversations, a twelve-message inbox and a calendar that scrolls from midnight to midnight even on empty days. Home and Chat share a reading width. The resting Home greeting aligns with the sidebar Home row. The shell uses one 12px inset above the organization and on both sides of the profile row. The Home inset scrolls away through the viewport edge fade. The collapsed sidebar control aligns with the expanded toolbar. Calendar events share the muted button’s solid, borderless rest, hover and pressed fills; the event opens local details and attendees have a separate control. Provider navigation awaits real event URLs.
4343

4444
Start with the [component ownership map](docs/component-ownership.md) when choosing a primitive. Sidebar, recents and panel tabs use one row; form and command dialogs use one borderless surface. Copy confirmations and transient notifications last four seconds. Notifications pause under the pointer or keyboard focus; icon color transitions take 120ms and respect reduced motion. The [design contract](AGENTS.md) defines these rules.
4545

‎apps/sim/platform/app/(shell)/_components/command-menu/command-menu.tsx‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export function CommandMenu({ open, onClose, navigate, chats, workspaces }: Comm
3838
items: workspaces.map((item) => ({
3939
id: item.id,
4040
label: item.name,
41-
icon: 'I025',
4241
value: '/home',
4342
})),
4443
},

‎apps/sim/platform/app/(shell)/_components/platform-shell/platform-shell.module.css‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,13 @@
2424
.openControl {
2525
position: absolute;
2626
z-index: 2;
27-
inset-block-start: calc((var(--spacing-56) - var(--spacing-app-chip)) / 2);
27+
inset-block-start: var(--shell-edge-inset);
2828
inset-inline-start: var(--ui-row-inset);
2929
}
3030
.homeContent {
3131
position: relative;
3232
container-type: inline-size;
3333
}
34-
.homeContent > .openControl {
35-
inset-block-start: var(--shell-home-row-start);
36-
}
3734
.desktopBar {
3835
display: flex;
3936
align-items: center;
@@ -110,15 +107,15 @@
110107
}
111108
.shell[data-desktop][data-conversation][data-sidebar-collapsed] .desktopBar {
112109
justify-self: start;
113-
border: 0;
110+
border-color: transparent;
114111
}
115112
.shell[data-desktop][data-conversation][data-sidebar-collapsed] > .content {
116113
grid-column: 1;
117114
}
118115
@media (max-width: 48rem) {
119116
.shell[data-desktop][data-conversation] .desktopBar {
120117
justify-self: start;
121-
border: 0;
118+
border-color: transparent;
122119
}
123120
.shell[data-desktop][data-conversation] > .content {
124121
grid-column: 1;

‎apps/sim/platform/app/(shell)/chat/_components/message/message.module.css‎

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
font-weight: var(--font-weight-medium);
1717
}
1818
.user {
19-
position: relative;
19+
justify-self: end;
2020
justify-items: end;
21-
}
22-
.user > * {
2321
max-inline-size: min(100%, var(--container-chat-message));
2422
}
2523
.bubble {
@@ -51,38 +49,12 @@
5149
margin-inline-start: calc(-1 * var(--ui-action-overhang));
5250
}
5351
.userActions {
54-
position: absolute;
55-
inset-block-start: 100%;
56-
padding-block-start: var(--ui-section-gap);
57-
inset-inline-end: 0;
5852
margin-inline-end: calc(var(--spacing-16) - var(--ui-action-overhang));
5953
}
60-
.userActions,
61-
.responseActions time {
62-
opacity: 0;
63-
}
64-
.user:hover .userActions,
65-
.responseActions:hover time {
66-
opacity: 1;
67-
}
68-
@container conversation (width < 24rem) {
69-
.userActions {
70-
position: static;
71-
padding-block-start: 0;
72-
}
73-
}
74-
.user:only-child .userActions {
75-
position: static;
76-
padding-block-start: 0;
77-
}
78-
@media (hover: none) {
79-
.userActions {
80-
position: static;
81-
padding-block-start: 0;
82-
}
83-
.userActions,
84-
.responseActions time {
85-
opacity: 1;
54+
@media (hover: hover) {
55+
.user:not(:hover) .userActions:not(:has([aria-describedby])),
56+
.answer:not(:hover) .responseActions:not(:has([aria-describedby], [aria-expanded="true"])) time {
57+
opacity: 0;
8658
}
8759
}
8860
.status,

‎apps/sim/platform/app/(shell)/chat/_components/panel/model.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { IconName } from '@/platform/design-system/catalog/icon-geometry'
22

3-
export interface PanelRecent {
3+
export type PanelRecent = {
44
id: string
55
label: string
6-
icon: IconName
7-
destination: 'file' | 'workspace' | 'website'
8-
address?: string
9-
}
6+
} & (
7+
| { destination: 'workspace'; icon?: never; address?: never }
8+
| { destination: 'file' | 'website'; icon: IconName; address?: string }
9+
)
1010

1111
export interface PanelData {
1212
recents: PanelRecent[]

‎apps/sim/platform/app/(shell)/home/_components/calendar/calendar-day.module.css‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@
5353
grid-column: 2;
5454
min-inline-size: 0;
5555
padding: var(--ui-row-block) var(--ui-row-inset);
56-
border: var(--spacing-stroke-default) solid var(--ui-divider);
5756
border-radius: var(--ui-control-radius);
58-
background: linear-gradient(var(--control-avatar) 0 0), var(--ui-canvas);
57+
background: var(--control-muted-fill);
5958
}
6059
.event:has(.eventName:hover) {
61-
background: linear-gradient(var(--ui-hover) 0 0), var(--ui-canvas);
62-
border-color: var(--control-hover-border);
60+
background: var(--control-muted-hover);
61+
}
62+
.event:has(.eventName:active) {
63+
background: var(--control-muted-pressed);
6364
}
6465
.event > :last-child {
6566
position: relative;

‎apps/sim/platform/app/(shell)/home/_components/home-dashboard/home-dashboard.module.css‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
align-items: start;
1111
justify-content: space-between;
1212
gap: var(--ui-row-gap);
13-
margin-inline-start: max(0px, calc(var(--ui-navigation-inset) - (100cqi - 100%) / 2));
1413
margin-inline-end: calc(-1 * var(--ui-action-overhang));
1514
}
1615
.greeting > div {

0 commit comments

Comments
 (0)