You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 6b3a320
Browse filesBrowse the repository at this point in the historyBrowse files
refine(platform): load without skeletons and align integration icons
- Credential rows on an app's page wear that app's mark, so a shared
Google credential no longer shows Gmail under Google BigQuery.
- The MCP glyph is an interface icon and takes the shared icon ink, like
the skill and key glyphs. Integration marks keep their own paint.
- The integration center renders its heading, and app details their Back
link, from the same nuqs state that picks the view. Going back no longer
flashes the previous view's chrome for a few frames.
- Sidebar, Home and Integrations paint nothing while loading: no route
loading.tsx, no widget skeletons, no loading copy. The shell's unkeyed
Suspense keeps the current page through a navigation, and a page load
paints the sidebar first. The calendar keeps its hour grid while a day
loads. The rule is recorded in apps/sim/platform/AGENTS.md.
Copy file name to clipboardExpand all lines: apps/sim/platform/AGENTS.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,7 +26,7 @@ Keep routes thin. Colocate each feature component, its CSS Module and private he
26
26
27
27
Use the shared semantic gaps from `@sim/emcn/styles/global.css` and Platform layout roles from `@sim/emcn-platform/styles/platform.css`. Title/detail stacks, sibling list items and adjacent actions each have a 2px role. The 8px row gap separates leading art from copy. Separate sibling rows with the list gap. Reserve lines for surface boundaries and data rules such as chart axes. 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 and spacing. An icon that identifies a two-line notification aligns to the title; its trailing action group centers against the full row. Email thread counts and the notification trigger use the same centered inline label/count spacing. The notification trigger takes the compact secondary (Invite) button surface, and has no chevron. The tray applies control elevation once around its scroll viewport; stacked notices do not cast shadows on one another. An active popup stays above the tray, including at narrow widths. Labelled range pickers and inline ellipsis menus need accessible names, not redundant tooltips; keep tooltips on top-level icon actions.
28
28
29
-
Every Platform reading page uses `PageColumn` for the same centered, border-box column, with Home’s 1296px content maximum and fluid 16–48px gutters. `PageOverflow` extends surfaces 8px around the text edge. Home, Chat, Search, Settings, integration details, resource directories and people pages share these edges. Embedded editors and split preview canvases retain their available working space. PageColumn owns geometry only and preserves Home’s legacy theme isolation. Home and Integrations use the shared page-heading inset inside the moving content. The shell separately reserves clearance for its sidebar opener or native titlebar; responsive layout does not change either page’s own heading inset. The unpadded viewport keeps the fade at the window edge. The web sidebar opener belongs to the shell, outside the scrolling page. It stays at the expanded toolbar’s 12px top inset as the center content scrolls, including on Home and in the drawer layout. Keep the button reachable without covering page actions. 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. Home uses the compact 3px heading gap from greeting to description, Recents to links and activity title to total. Use the 12px content gap from the Sim heading row to its input. Home widget content uses the 16px gap measured from the activity total to the chart, and from the attention, Email and Calendar labels to their first content. Header and calendar controls extend around their label line without adding height to these gaps. The ordinary 12px section gap still serves other compositions. Home has two inline alignment edges: headings and leading content share the reading-column text edge; the composer, row hover surfaces and outer control bounds extend by the shared 8px row inset. Keep Sim and the Build/Search toggle on the text edges, and align the composer and its loading placeholder with the leftmost recent and Email row on the left and the attention row and menu controls on the right. Measure the 24px dashboard gap between component boxes, including their inline hover overhang. Chart and widget boxes own that inset; the column layout allows for both insets so neighboring surfaces remain 24px apart. Vertical widget gaps use the same 24px as the description-to-Sim gap. Responsive layouts can wrap, collapse or split panels; button height, padding and type stay stable. Composer draft and placeholder keep the same type role across widths and themes; browser zoom and user text scaling still apply.
29
+
Every Platform reading page uses `PageColumn` for the same centered, border-box column, with Home’s 1296px content maximum and fluid 16–48px gutters. `PageOverflow` extends surfaces 8px around the text edge. Home, Chat, Search, Settings, integration details, resource directories and people pages share these edges. Embedded editors and split preview canvases retain their available working space. PageColumn owns geometry only and preserves Home’s legacy theme isolation. Home and Integrations use the shared page-heading inset inside the moving content. The shell separately reserves clearance for its sidebar opener or native titlebar; responsive layout does not change either page’s own heading inset. The unpadded viewport keeps the fade at the window edge. The web sidebar opener belongs to the shell, outside the scrolling page. It stays at the expanded toolbar’s 12px top inset as the center content scrolls, including on Home and in the drawer layout. Keep the button reachable without covering page actions. 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. Home uses the compact 3px heading gap from greeting to description, Recents to links and activity title to total. Use the 12px content gap from the Sim heading row to its input. Home widget content uses the 16px gap measured from the activity total to the chart, and from the attention, Email and Calendar labels to their first content. Header and calendar controls extend around their label line without adding height to these gaps. The ordinary 12px section gap still serves other compositions. Home has two inline alignment edges: headings and leading content share the reading-column text edge; the composer, row hover surfaces and outer control bounds extend by the shared 8px row inset. Keep Sim and the Build/Search toggle on the text edges, and align the composer with the leftmost recent and Email row on the left and the attention row and menu controls on the right. Measure the 24px dashboard gap between component boxes, including their inline hover overhang. Chart and widget boxes own that inset; the column layout allows for both insets so neighboring surfaces remain 24px apart. Vertical widget gaps use the same 24px as the description-to-Sim gap. Responsive layouts can wrap, collapse or split panels; button height, padding and type stay stable. Composer draft and placeholder keep the same type role across widths and themes; browser zoom and user text scaling still apply.
30
30
31
31
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. Workspace names, the Workspaces launcher and its tab title are text-only. A recent without a glyph keeps its remove action at the end. A pinned workspace or chat shows its pin in the row action slot. Hover or opening the menu reveals the ellipsis in place. On touch, tapping the resting pin opens the same menu. Sort pinned rows first, applying the section’s chosen order within each group. 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 buttons, icon triggers and rows share the sidebar Home radius (`--ui-control-radius`). Use `Button` / `IconButton` for actions and Base UI triggers, passing icons and labels through their slots. Buttons that open destructive confirmation dialogs use a quiet Button with `destructive`, including page actions, Settings rows, bulk actions and modal footer actions. It shares the menu row’s neutral resting paint and red hover recipe. Final confirmation actions retain the solid destructive variant. Close, clear and dismiss × controls keep their neutral hover; their glyph alone never makes an action destructive. The composer toolbar owns its round actions. Switches and segmented controls keep their pill shape. Clicking the selected half of a two-choice segment switches to the other half. Native keyboard choice navigation still selects its destination.
32
32
@@ -79,6 +79,8 @@ Each `lib/data/<domain>.ts` wraps the existing hook and maps its contract shape
79
79
80
80
Views that fixtures also render stay prop-driven. A route composes `data` and a named `actions` interface, filled by session state on fixtures and by mutations on live data. A surface that exists only on live data, such as a Settings section, People or Search, reads its domain's hooks from `lib/data` directly. No fixture branch inside a view. Hide what has no backend and record it in the [parity ledger](docs/org-parity.md); never fake it. Distinguish loading, empty and failure. A list reads `status` rather than treating empty as loading.
81
81
82
+
Loading paints nothing. Static chrome (headings, labels, menus, the composer, a calendar's hour grid) renders at once; data-backed content appears when its read settles. No skeletons, shimmer, placeholder blocks or loading copy in the shell, Home or Integrations. A Home or Integrations route has no `loading.tsx`: the shell's unkeyed `<Suspense fallback={null}>` around the page keeps the current page on screen through a client navigation, and on a page load paints the sidebar before the page. One component decides which view a URL shows, and that same component renders the view's heading and Back link from the same `nuqs` state. A second reader, such as Next's `useSearchParams`, settles a few frames later and flashes the previous view's chrome.
83
+
82
84
A server read goes through `serverRequestJson`, which gives every read a deadline. A first-paint read lives in `lib/data/prefetch-<domain>.ts` under the hook's own key and `staleTime`, through `prefetchRequestJson`; it may never block or fail the page, so the client fetches what it gave up. `resolvePlatformMode` decides each server entry once: `fixture` with `SIM_PLATFORM_FIXTURES=1`, `live` with `SIM_PLATFORM_API_ORIGIN`, otherwise `none`, today's empty rendering. Load the seam with a dynamic import behind the `live` check, so fixture servers never evaluate it. The viewer's organization comes from membership on the server, read once per request by `readOrganizationSurface`. Platform URLs carry no organization.
83
85
84
86
The legacy organization layout redirects flagged organizations here through `lib/platform/routes.ts`. Link out to a legacy route only with `legacyRoutes`, which builds an absolute URL, and only to a route that mapper leaves unmapped. Linking to a mapped route makes the two surfaces redirect to each other.
0 commit comments