Repository navigation
Build the Platform Core, Chat, and member integrations UI - #9687
emir-karabeg wants to merge 437 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
[Medium impact] The changes reach the standalone Platform’s Home, Chat, and Settings flows on web and Desktop. Fix the lost Stop control and covered desktop panel tabs before merging. Findings
SummaryAdds standalone Home, Chat and Settings routes for browsers and Sim Desktop, with shared controls, gated fixtures and browser/Electron checks.
|
| onStop={ | ||
| last?.response?.state === 'running' ? () => transition(last, 'stopped') : undefined | ||
| } | ||
| onSubmit={(draft) => { | ||
| const next = userTurn(draft) | ||
| if (id && !removed) { | ||
| changes.setTurnsFor(id, [...turns, next]) |
There was a problem hiding this comment.
In /chat/customer-onboarding?state=full, pressing Enter on a follow-up appends a user turn while the response is running. onStop then disappears because it checks only the last turn, which now has no response. The earlier response stays “Working” with no way to stop it. Track the running turn separately, or block submission while it is running.
| padding-inline: var(--spacing-12); | ||
| min-inline-size: 0; | ||
| height: var(--height-nav); | ||
| border-block-end: var(--spacing-stroke-default) solid var(--ui-divider); | ||
| } | ||
| :global([data-sim-desktop-title-bar]) .header { | ||
| height: var(--spacing-48); | ||
| -webkit-app-region: drag; |
There was a problem hiding this comment.
With the desktop sidebar collapsed, expanding the chat panel puts its tabs under the desktop toolbar and native window buttons. The shell places the content at the top-left, but the panel header keeps only 12px of left padding. Unlike the conversation header, it does not use --ui-navigation-inset. Reserve that space when the panel takes over so its first tab and close control remain clickable.
| <ChatPanels workspaces={shell?.workspaces ?? []} data={panel} key={id?.[0] ?? 'new'}> | ||
| <ChatScreen data={data?.chat ?? null} chatId={id?.[0]} /> |
There was a problem hiding this comment.
Switching chats discards unsent text and selected files. MessageComposer keeps both only in local state, while this route keys the whole panel tree by chat ID. Returning to the original chat creates a fresh composer. Keep drafts in the session, keyed by chat ID, so ordinary navigation does not erase unfinished messages.
| const [displayName, setDisplayName] = useState(name ?? '') | ||
| const [picture, setPicture] = useState<string>() |
There was a problem hiding this comment.
Saving a display name or choosing a profile picture lasts only until the user leaves Preferences. ProfileSettings owns these values locally and is removed on other Settings sections. Returning restores the fixture name and drops the picture. Keep these local edits in the session provider; no database write is needed.
| <ProductSurface preference={appearance}> | ||
| <PlatformShell data={shell} desktopTitle={desktopTitle} navigation={navigation}> | ||
| {children} | ||
| </PlatformShell> |
There was a problem hiding this comment.
Navigation resets shell changes
Each page creates its own PlatformShell, so moving between Home, Chat and Settings resets inbox read marks, dismissed notices and chat sorting. For example, reading an unread item, opening a chat and returning makes that item unread again. Move this session state above the page boundary, alongside the existing chat edits and interface preferences.
There was a problem hiding this comment.
5 issues found and verified against the latest diff
Confidence score: 3/5
- In
chat-screen.tsx, submitted attachments can reappear in the new chat when changing the chat ID remounts the composer. Clear the attachment draft when the send succeeds. - In
workspace-list.tsx, keyboard users get no visible focus indicator on workspace buttons. Add a:focus-visibletreatment. - In
product-surface.tsx, users with a dark system theme can see a light flash before hydration. Resolve the system theme before first paint. - In
notification-tray.module.css, notification controls can sit under the home indicator on notched mobile browsers. Account for the bottom safe-area inset.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/platform/design-system/components/workspace-list.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/workspace-list.tsx:43">
P2: This new keyboard-focusable workspace button has no visible focus indicator because the global stylesheet removes outlines and `.workspaceHit` adds none. Add a shared or workspace-specific `:focus-visible` treatment so keyboard users can identify the active row.</violation>
</file>
<file name="apps/sim/platform/design-system/components/product-surface.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/product-surface.tsx:10">
P2: System appearance always renders light on the server, causing dark-system users to see a light flash before hydration applies the media-query result. Resolve the system theme before first paint, such as with a CSS media-query path plus explicit-theme override or a small theme bootstrap.</violation>
</file>
<file name="apps/sim/platform/design-system/components/select.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/select.tsx:37">
P2: An arbitrary `theme` query can reach this controlled value without matching any option, so the Theme picker renders blank even though the surrounding product falls back to `system`. Ignore values outside `options` here or pass the normalized theme from the settings screen.</violation>
</file>
<file name="apps/sim/platform/design-system/features/chat/chat-screen.tsx">
<violation number="1" location="apps/sim/platform/design-system/features/chat/chat-screen.tsx:100">
P2: The first send from `/chat` clears the composer only until `router.replace` changes `id`; this prop then remounts `MessageComposer` with the unchanged draft attachments, so submitted files reappear in the new chat’s composer. Track and consume the initial draft when submitting before navigation.</violation>
</file>
<file name="apps/sim/platform/design-system/components/notification-tray.module.css">
<violation number="1" location="apps/sim/platform/design-system/components/notification-tray.module.css:10">
P2: The fixed notification stack ignores the bottom safe-area inset, so its action and dismiss controls can sit under the home indicator on notched mobile browsers. Use `max(var(--spacing-20), env(safe-area-inset-bottom))` for the bottom offset.
(Based on your team's feedback about toast safe-area minimum.) [8c7692a3-398e-4004-b266-f2a76922d36c]</violation>
</file>
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
6133099 to
8d35586
Compare
A refetch mid-turn can return the saved answer after a steering row. Only the row right after the owner was checked, so the live answer was added beside it and the turn showed two answers until it ended. It now walks past steering rows with findAssistantAfterOwner, as the reconcile path does.
A spy taken on setTimeout under fake timers was restored by the shared config after useRealTimers, which put the fake back: every later real-timer test in the file hung in sleep. Count reads of the cached chat, which every delivery pass makes, instead.
Better Auth keys it on client IP and path, so every phase's fresh sign-ins chain into one budget whoever signs in. The zone-5xx phase's first test was refused with a 429 at its fourth sign-in in 15 seconds, and the zone had no part in it.
waitForRollout reads the surface API, whose route bundle has its own AppConfig cache. The layout's bundle still held the rollback for one more read, so a single navigation right after the API agreed stayed on /o. The proxy's zone verdict was never negative in that phase.
Throwing from register() never stopped the process: Next logs 'Failed to prepare server' and keeps listening, answering 500 to everything, so the zone's guard looked alive to anything supervising it. It now logs and exits 1. Reproduced on a tiny production build of the same Next version, which stayed up before this and exits 1 after it. The live spec already asserted the exit, which is how this surfaced.
check:platform-design holds @sim/logger to lib/data, and the zone's proxy imports mode.ts, so it gets its own module.
…iss through to the chat Both spread the shared thread props, whose Retry and Dismiss are optional, so a dropped wire would still type-check.
… status fill the page
…il a heading that breaks mid-word
Every read now goes to the worker's read-only disposition endpoint: the chat-send gate, Dismiss, a steer retried under a recorded id, and the sender's reads after a turn ends or on Retry (new POST /api/copilot/chat/steer/disposition). No read can deliver a message, which closes the gap where Dismiss could. A record no run holds (`unknown`, or final `not_applied`) goes out as a turn; a steer retried under it is steered afresh, and the worker's chat-wide identity keeps it from applying twice. A worker without the read (a bare 404 or 405, or an answer outside its contract) is decided from Sim's run: held while it runs, not held once it ended. Nothing is re-sent. A message that run did apply is refused by the worker as a turn (input_applied) and settles as applied. Sim no longer stores the input it sent the worker, so the stripping in transcript reads, fork, import and data drains, and the drain size exclusion, are gone. The final disposition stays on the row. The steering contract copy is synced from the worker; the scripted agent answers the read.
…into test/chat-steering-e2e-final The org conversation moved to app/_organization-conversation; the wiring test takes the composer type from there. Two new delivery-pass tests counted disposition reads as steers; they now count reads on the read route.
A read can land between steerRun recording the message and the worker accepting it, and read 'unknown' then. Dropping the record there left a message the model would still apply with no row. 'unknown' now reads as held while Sim's run is live, as 'queued' does, and as not held once it ended, so nothing is stranded: the sender reads again after the turn ends. A message the user dismissed while a turn still held it shows as not delivered again if that turn leaves it, rather than going out as a turn on its own.
* Revert "improvement(org): move the People page onto the new design system (#9720)" This reverts commit 7625929. * Revert "fix(org): drop the removed shortcuts slot from the landing test (#9715)" This reverts commit f17f1b6. * Revert "improvement(org): move the organization view onto the new design system (#9708)" The redesigned org view ships all at once with #9687. The shared modules later staging work builds on stay: member-integrations and the accounts key factory. The legacy integration detail opens the saved-credential modal for Jama and Flow, which #9723 added only to the new view. * fix(org): keep staging's shared modules and revert only the redesigned views Hooks, contracts, key factories and lib helpers stay at staging, and the legacy view adapts to them. Jama and Flow reconnect through the saved-credential modal and disconnect through Search. Exports only the new views used are unexported or deleted, and the e2e script returns to the legacy view's selectors.
…ips the redesign
…9740 # Conflicts: # apps/sim/ee/organization-usage/components/usage-consumers.tsx
…osition reads Scenarios 10-12 cut only the worker's steer route. Reads now go to the disposition route, which said the ended run never got the message, so it went out as the next turn, correctly, and never showed Not delivered. The setup now cuts both routes, and scenario 13 proves the new path: steer unreachable, read reachable, sent once as the next turn.
…into test/chat-steering-e2e-final
…ying it; #9687 still uses the source removal
…9740 # Conflicts: # AGENTS.md # AGENTS.md~HEAD # CLAUDE.md
… Home widgets Owned credentials exclude one awaiting reconnection. Read in parallel, a concurrent read that refused a token could drop an account from the owned set after its status read 'active', and the widget reported it as not offered instead of asking to reconnect.
A re-render after a reload or reconnect can replace a message node between finding it and measuring it, so the order check reads every position in one attempt and retries until all have one.
…into test/chat-steering-e2e-final
Home, Chat, Settings and organization integrations use the shared navigation shell and real routes in the isolated Platform Next.js app. The browser and Sim Desktop share the renderer. A default-off server fixture supplies populated, empty and stress states without a database.
The shared App Router layout owns the sidebar. Selecting a chat, visiting Settings, or reopening the compact drawer preserves the reader’s position. Command K uses the shared menu rows and headings: Home, Settings and Integrations keep their icons, and workspace and chat results are text-only. Search, results and shortcut hints align to one inset, and moving from pointer to keyboard leaves only the active result highlighted.
Home and Chat share a reading column and a Recents component. Home opens recent websites in another tab; dismissal carries into Chat. The activity chart switches between Tasks run and Searches without losing its date range. Widgets and notifications share a height ceiling and scroll independently. Calendar’s Now control returns to the current day and time. Events open an external tab only when a provider supplies a URL; attendees have their own popover.
Navigation, tabs, recents and attachment previews share ControlRow. Invite recipients use compact address tags with an always-visible remove button inside each tag. Form dialogs focus the first available field and restore focus on close. Invite accepts typed and pasted addresses, supports keyboard removal and leaves sending to Phase 3. Chat keeps prose and tool activity aligned, and shares its time formatting and copy feedback with the rest of the Platform.
The member Integrations inventory uses workspace catalog descriptions for each provider. Its detail shows the provider name and description, then one connected credential with its actions menu. The inline connection prompt shares Home’s Email and Calendar styling and links to the Sim Search setup guide. Eligible tool approvals remain individual. Known OAuth permissions are grouped by purpose and always visible. Managed Search providers show their actual read-only operations, including Fireflies meeting search, transcripts, and summaries. The server fixture flag gates all sample connections. Member inventory and operation mapping.
See the component ownership map, Platform contract and Core/Chat source-state inventory. To run locally:
SIM_PLATFORM_FIXTURES=1 bun run --cwd apps/sim dev:platform; visit/home?state=full,/chat/weekly-review?state=full/settings?state=full, or/integrations?state=full.Verification exercises the production Next build, Chromium with fixture-on and fixture-off servers, and the native Electron renderer. Browser journeys measure scroll bounds, hit targets and spacing, exercise keyboard selection and focus return, and distinguish resting/hover/pressed feedback in both themes. The design audit checks ownership, token and variant use, timers, modal chrome and the fixture boundary. It recognizes JSX returned by local render helpers and active route fallbacks. The tracked Platform skill and contract route new work, cleanup and Settings reviews to the correct component library, and distinguish shared preferences from decisions owned by one feature.
Stacked on #9667, which targets staging.
Chat now steers instead of queueing. A message sent during a turn is applied to that turn at its next step, through the worker's durable v2 endpoint (simstudioai/mothership#929, which must deploy first; against an older worker a message waits for the turn's end and goes out as the next turn). If the turn ends without applying it, it goes out once as the next turn under the same id, and the worker refuses it if a run already applied it. The client queue is removed from every chat surface. Undelivered messages show a status line with Retry and Dismiss. Outgoing messages survive a reload through a per-tab journal, and anything past 10 minutes never sends itself.
apps/sim/scripts/test-chat-steering-e2e.tsproves this across a real browser, app, worker and database (bun run test:chat-steering:e2e, not wired into CI).