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 9b4e3a8
Browse filesBrowse the repository at this point in the historyBrowse files
Format dates that can render on the server in one zone and record the new owners
Dates from server-prefetched reads no longer depend on the server's zone. Route
entries share one name, Discard keeps legacy's Keep editing, a failed workspace
list no longer reads as a dead deep link, and the contract, ownership map and
parity ledger describe what now exists.
Copy file name to clipboardExpand all lines: apps/sim/platform/AGENTS.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,7 +31,7 @@ Use `ActivityRow` for a first-line glyph or avatar, title/detail and centered tr
31
31
32
32
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. Platform and Settings navigation hide the native scrollbar and keep their edge fades, keyboard, wheel and touch scrolling. Other scroll areas keep their existing scrollbar behavior. Insets establish resting layout; they cannot substitute for an edge fade. Home widgets and notification panels use `--ui-widget-max-size` as a ceiling, not a forced height. Keep the dashboard free to scroll and let each overflowing list or schedule scroll independently. Plain tooltips preview at most three lines; retain the complete accessible text. A structured people popover owns its content and uses the dropdown’s popup padding and row insets.
33
33
34
-
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. A confirm handler that returns a promise holds the dialog open as pending, closes it on success, and keeps the draft with an inline message on failure; nothing dismisses it while pending. 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. Opening a dialog or advancing its step focuses the first available field. Confirmation dialogs focus Cancel, and read-only previews focus Close. A nested dialog handles Escape one layer at a time. While a form is open, the global search shortcut leaves its draft and keyboard focus in place. Closing returns focus to the invoking control. Textareas scroll or grow within their owner and never show a manual resize handle. Recipient fields use the textarea’s height, draft typography and inset. Committed addresses use compact caption text and an always-visible neutral ×, like a panel tab. The remove button sits inside the tag beside its label; long addresses yield space to the button. Comma or Enter commits a draft, pasting a list commits its addresses, and Backspace from an empty draft removes the last tag.
34
+
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. A confirm handler that returns a promise holds the dialog open as pending, closes it on success, and keeps the draft with an inline message on failure; nothing dismisses it while pending. Returning `false` keeps it open at the dialog's next stage, and `cancelLabel`/`onCancel` turn the quiet footer action into a step back. 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. Opening a dialog or advancing its step focuses the first available field. Confirmation dialogs focus Cancel, and read-only previews focus Close. A nested dialog handles Escape one layer at a time. While a form is open, the global search shortcut leaves its draft and keyboard focus in place. Closing returns focus to the invoking control. Textareas scroll or grow within their owner and never show a manual resize handle. Recipient fields use the textarea’s height, draft typography and inset. Committed addresses use compact caption text and an always-visible neutral ×, like a panel tab. The remove button sits inside the tag beside its label; long addresses yield space to the button. Comma or Enter commits a draft, pasting a list commits its addresses, and Backspace from an empty draft removes the last tag.
35
35
36
36
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.
37
37
@@ -58,13 +58,13 @@ Use [the shared Platform time formatters](lib/time.ts) for displayed ages, clock
58
58
59
59
Only `lib/data/**` may import from outside this project: query hooks under `@/hooks/queries/<domain>`, contracts and clients under `@/lib/api/<module>`, and the named helpers listed in `scripts/check-platform-design.ts`. `@/app/<route>`, `@/components/<name>` and the legacy `@sim/emcn` barrel stay banned there too, apart from named modules on that list, such as the chat hook. Add a helper to that list only after reading its runtime import closure.
60
60
61
-
The chat hook runs here unchanged. Views read a `ChatRuntime` (`(shell)/_components/chat/model.ts`), filled by `lib/data/chat.ts` on live data and by the session on fixtures. What the hook reaches for a canvas this app does not have, `canvasShims` in `next.config.ts` replaces with a named module under `lib/data/shims/` saying what it drops.
61
+
The chat hook runs here unchanged. Views read a `ChatRuntime` (`(shell)/_components/chat/model.ts`), filled by `lib/data/chat.ts` on live data and by the session on fixtures. What the hook reaches for a canvas this app does not have, `canvasShims` in `next.config.ts` replaces with a named module under `lib/data/shims/` saying what it drops. A shimmed store exposes only the fields the hook reads: subscribing to it or reading anything else throws. `check:platform-design` walks the seam's runtime closure and fails on any legacy view it bundles.
62
62
63
-
Each `lib/data/<domain>.ts` wraps the existing hook and maps its contract shape to a Platform view model with a pure adapter. Never fork a hook, key factory, invalidation or contract; a hook that lacks a needed option gains it additively. A module's first import of a query hook that toasts resolves through `lib/data/emcn-shim.ts`, which webpack substitutes for `@sim/emcn` and which exports only `toast`, bridged to the notification tray. Any other name from that package fails the build, which is what proves no legacy UI entered.
63
+
Each `lib/data/<domain>.ts` wraps the existing hook and maps its contract shape to a Platform view model with a pure adapter. A domain whose views take the contract shape as it is has nothing to adapt: its `lib/data/<domain>/index.ts` is a barrel naming what crosses, with any hook of its own in a sibling file. A non-barrel module never re-exports. Never fork a hook, key factory, invalidation or contract; a hook that lacks a needed option gains it additively. A module's first import of a query hook that toasts resolves through `lib/data/emcn-shim.ts`, which webpack substitutes for `@sim/emcn` and which exports only `toast`, bridged to the notification tray. Any other name from that package fails the build, which is what proves no legacy UI entered.
64
64
65
-
Views stay prop-driven. A route composes `data` and a named `actions` interface, filled by session state on fixtures and by mutations on live data. 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.
65
+
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.
66
66
67
-
`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.
67
+
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`, with `prefetchSignal()`; 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.
68
68
69
69
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