Skip to content

Commit 3874b9e

Browse files
committed
Align Platform shell and conversation spacing and unify action feedback
1 parent 3fe70ac commit 3874b9e

39 files changed

Lines changed: 457 additions & 274 deletions

‎.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. 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. 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.
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,21 +22,21 @@ 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). Put the 24px resting Home inset **inside** the scroll content. The mask belongs to the actual viewport and stays at the window edge. Use the same 24px step between major dashboard/sidebar sections and on both sides of the sidebar footer. 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. 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.
2626

2727
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.
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

3131
Measure the actual scroll viewport with `ScrollArea`, or attach `useScrollEdges` directly to a native or Base UI scroll owner. Use the shared 16px mask only on edges with more content, updating after scroll, resize, input, reset or filtering. Insets establish resting layout; they cannot substitute for an edge fade. Scroll the calendar through the entire day and the message list through its full inbox independently of Home. Plain tooltips preview at most three lines; retain the complete accessible text. A structured people popover owns its own layout.
3232

33-
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. The shared borderless surface positions its title at the logical start and supplies one quiet Cancel/Back. Command search and previews use the same shadow and dismissal layer. Form controls cannot construct or style a form header, footer or Cancel button.
33+
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. The shared borderless surface positions its title at the logical start and supplies one quiet Cancel/Back. Command search and previews use the same shadow and dismissal layer. Form controls cannot construct or style a form header, footer or Cancel button. Textareas scroll or grow within their owner and never show a manual resize handle.
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 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+
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. Quiet controls gain the hover surface without changing ink. 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 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.
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. Home content starts at the same 24px inset used between major sidebar sections. Content scrolls through the viewport edge fade.
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.
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/platform-shell/platform-shell.module.css‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@
2727
inset-block-start: calc((var(--spacing-56) - var(--spacing-app-chip)) / 2);
2828
inset-inline-start: var(--ui-row-inset);
2929
}
30-
.shell[data-home] > .openControl {
31-
inset-block-start: var(--ui-page-inset-block);
30+
.homeContent {
31+
position: relative;
32+
container-type: inline-size;
33+
}
34+
.homeContent > .openControl {
35+
inset-block-start: var(--shell-home-row-start);
3236
}
3337
.desktopBar {
3438
display: flex;

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { useChatChanges } from '@/platform/app/(shell)/_components/session/chat-
1212
import { Sidebar } from '@/platform/app/(shell)/_components/sidebar/sidebar'
1313
import { useSidebarLayout } from '@/platform/app/(shell)/_components/sidebar/sidebar-layout'
1414
import { SidebarToolbar } from '@/platform/app/(shell)/_components/sidebar/sidebar-toolbar'
15-
import { IconButton, OverflowText, Tooltip } from '@/platform/design-system/components'
15+
import { IconButton, OverflowText, ScrollArea, Tooltip } from '@/platform/design-system/components'
1616
import { useCompactViewport } from '@/platform/design-system/components/layout/use-compact-viewport'
1717
import { useScrollEdges } from '@/platform/design-system/components/scroll-area/use-scroll-edges'
1818
import { platformHref } from '@/platform/design-system/navigation'
@@ -110,12 +110,14 @@ export function PlatformShell({ data, children, navigation, desktopTitle }: Plat
110110
<IconButton label='Expand sidebar' icon='I066' size='row' onClick={toggle} />
111111
</Tooltip>
112112
)
113+
const webOpener = !desktop && (compact || collapsed) && (
114+
<div className={styles.openControl}>{openControl}</div>
115+
)
113116
return (
114117
<>
115118
<div
116119
className={styles.shell}
117120
data-desktop={desktop || undefined}
118-
data-home={home || undefined}
119121
data-conversation={conversation || undefined}
120122
data-sidebar-collapsed={collapsed || undefined}
121123
>
@@ -133,11 +135,18 @@ export function PlatformShell({ data, children, navigation, desktopTitle }: Plat
133135
</header>
134136
)}
135137
{!compact && !collapsed && sidebar}
136-
{!desktop && (compact || collapsed) && (
137-
<div className={styles.openControl}>{openControl}</div>
138-
)}
138+
{!home && webOpener}
139139
<div ref={scrollEdges} className={styles.content}>
140-
{children}
140+
{home ? (
141+
<ScrollArea label='Home dashboard'>
142+
<div className={styles.homeContent}>
143+
{webOpener}
144+
{children}
145+
</div>
146+
</ScrollArea>
147+
) : (
148+
children
149+
)}
141150
</div>
142151
<dialog
143152
ref={drawerRef}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function HelpMenu({ iconOnly }: HelpMenuProps) {
2525
id: 'macos',
2626
label: 'Sim for macOS',
2727
icon: 'I041',
28-
trailingIcon: 'I037',
28+
trailingIcon: 'I030',
2929
onSelect: () => visit('https://www.sim.ai/api/desktop/update/download'),
3030
},
3131
],
@@ -42,7 +42,7 @@ export function HelpMenu({ iconOnly }: HelpMenuProps) {
4242
id: 'documentation',
4343
label: 'Documentation',
4444
icon: 'I079',
45-
trailingIcon: 'I037',
45+
trailingIcon: 'I030',
4646
onSelect: () => visit('https://docs.sim.ai'),
4747
},
4848
{

‎apps/sim/platform/app/(shell)/_components/sidebar/sidebar-collections.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export function SidebarCollections({ data, activeChat, navigate }: SidebarCollec
127127
},
128128
{
129129
id: 'archive',
130+
destructive: true,
130131
label: 'Archive',
131132
icon: 'I061',
132133
onSelect: () => setEditing({ action: 'Archive', chat: item }),

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
flex-direction: column;
44
min-block-size: 0;
55
min-inline-size: 0;
6-
padding: var(--ui-page-inset-block) var(--ui-row-inset);
6+
padding: var(--shell-edge-inset) var(--ui-row-inset);
77
border-inline-end: var(--spacing-stroke-default) solid var(--ui-divider);
88
}
99
.sidebarHeader,
@@ -19,7 +19,7 @@
1919
.sidebarFooter {
2020
gap: var(--ui-row-gap);
2121
min-block-size: var(--spacing-app-chip);
22-
margin-block-start: var(--ui-page-inset-block);
22+
margin-block-start: var(--shell-edge-inset);
2323
}
2424
.help {
2525
display: flex;
@@ -37,6 +37,8 @@
3737
flex: none;
3838
}
3939
.sidebarBody {
40+
display: grid;
41+
grid-template-rows: minmax(0, 1fr);
4042
flex: 1;
4143
min-block-size: 0;
4244
}

‎apps/sim/platform/app/(shell)/_components/sidebar/sidebar.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export function Sidebar({
9090
<AvatarImage src={preferences.picture} name={accountName} />
9191
</span>
9292
) : (
93-
<Avatar name={accountName} size='inline' singleInitial />
93+
<Avatar name={accountName} size='inline' />
9494
)
9595
}
9696
onClick={() => navigate('/settings')}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
inline-size: min(var(--ui-page-max-size), calc(100% - 2 * var(--ui-page-inline-gutter)));
3737
margin-inline: auto;
3838
}
39+
.turn:last-child {
40+
margin-block-end: var(--ui-product-section-gap);
41+
}
3942
.dock {
4043
flex: none;
4144
position: relative;

0 commit comments

Comments
 (0)