feat(ui): redesign entry surfaces + remote machine - #44
Conversation
|
Warning Review limit reached
Next review available in: 30 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughThe PR centralizes remote workspace setup through a shared store and global dialog. It restructures pairing instructions, updates onboarding and page layouts, changes menu actions to use click handlers, and removes selected UI transitions. ChangesRemote setup flow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant WorkspaceSelector
participant remoteSetup
participant AddRemoteMachineDialog
participant RemoteWorkspaceSetup
WorkspaceSelector->>remoteSetup: Open shared remote dialog
remoteSetup->>AddRemoteMachineDialog: Provide dialog state and handlers
AddRemoteMachineDialog->>RemoteWorkspaceSetup: Render setup view
RemoteWorkspaceSetup->>remoteSetup: Save workspace draft
remoteSetup->>WorkspaceSelector: Invoke registered save handler
remoteSetup->>AddRemoteMachineDialog: Close dialog
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
Confidence Score: 4/5The remote-machine dialog lifecycle defect should be fixed before merging because collapsing the sidebar can replace an active setup flow with an empty dialog. The global dialog remains open after its sidebar-owned handlers are cleared, causing its entire setup component and in-progress local state to be unmounted. Files Needing Attention: packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx, packages/ui/src/components/WorkspaceSelector.tsx, packages/ui/src/stores/ui/remoteSetup.ts
|
| Filename | Overview |
|---|---|
| packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx | Globalizes the remote-machine dialog, but conditionally removes its setup content when sidebar-owned handlers disappear. |
| packages/ui/src/components/WorkspaceSelector.tsx | Registers remote setup handlers through component lifecycle, coupling the global dialog to a conditionally mounted sidebar control. |
| packages/ui/src/stores/ui/remoteSetup.ts | Adds global dialog state and save dispatch, but clearing handlers does not reconcile the dialog's open state. |
| packages/ui/src/components/workspace/RemoteWorkspaceSetup.tsx | Restyles and reorganizes the pairing, review, and instruction views while retaining the existing pairing workflow. |
| packages/ui/src/pages/WelcomePage.tsx | Replaces the onboarding coachmark with a progress checklist and direct step navigation. |
| packages/ui/src/pages/NewThreadPage.tsx | Redesigns the new-thread surface and consolidates machine and project metadata beneath the composer. |
| packages/ui/src/pages/AgentWelcomePage.tsx | Adds theme-aware branding, a redesigned agent grid, and an actionable empty state. |
| packages/ui/src/routes/_main.tsx | Mounts the remote-machine dialog globally within MainLayout. |
Prompt To Fix All With AI
### Issue 1
packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx:51-59
**Sidebar collapse empties setup dialog**
When the sidebar is collapsed while remote-machine setup is open, `WorkspaceSelector` unmounts and clears the handlers, so this condition removes `RemoteWorkspaceSetup` while the global dialog remains open, leaving an empty dialog and discarding the active pairing or review state.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(ui): redesign entry surfaces + remo..." | Re-trigger Greptile
| {handlers && ( | ||
| <RemoteWorkspaceSetup | ||
| existingRemoteUrls={handlers.remoteUrls} | ||
| initialRemoteUrl={remoteSetup.recoveryWorkspace?.remoteUrl} | ||
| onAddWorkspace={remoteSetup.saveWorkspace} | ||
| onSaveAndSwitch={handlers.onSaveAndSwitch ? remoteSetup.saveWorkspaceAndSwitch : undefined} | ||
| onCancel={remoteSetup.closeRemoteDialog} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
Sidebar collapse empties setup dialog
When the sidebar is collapsed while remote-machine setup is open, WorkspaceSelector unmounts and clears the handlers, so this condition removes RemoteWorkspaceSetup while the global dialog remains open, leaving an empty dialog and discarding the active pairing or review state.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx
Line: 51-59
Comment:
**Sidebar collapse empties setup dialog**
When the sidebar is collapsed while remote-machine setup is open, `WorkspaceSelector` unmounts and clears the handlers, so this condition removes `RemoteWorkspaceSetup` while the global dialog remains open, leaving an empty dialog and discarding the active pairing or review state.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 785777f. WorkspaceSelector no longer calls remoteSetup.clearHandlers() on unmount, so handlers stay registered for the app lifetime — AddRemoteMachineDialog (rendered in MainLayout) keeps its body and pairing/review state when the sidebar is collapsed. WorkspaceSelectorDialogs.tsx also renders a non-empty fallback body when handlers is null.
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ui/src/components/WorkspaceSelector.tsx (1)
169-180: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winMark an accepted re-pairing as paired.
Line 180 retains
identity-changedafter the user pairs successfully with a new verified identity.handleSwitchat Lines 225-230 then always reopens the pairing dialog. The user must pair twice before the workspace becomes selectable.After the user reviews and saves the new credential, set
trustStateto"paired".Proposed fix
- trustState: identityChanged ? "identity-changed" : workspace.credentialRef ? "paired" : "pairing-required", + trustState: workspace.credentialRef ? "paired" : "pairing-required",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/WorkspaceSelector.tsx` around lines 169 - 180, Update the workspace state update in the identity-change handling around identityChanged so a successfully saved new credential sets trustState to "paired" instead of retaining "identity-changed". Preserve the existing pairing-required behavior when no credential is present and the paired behavior for normal credentialed updates.
🧹 Nitpick comments (5)
packages/ui/src/pages/NewThreadPage.tsx (2)
793-802: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant live-region attribute.
role="status"already impliesaria-live="polite". The machine name changes rarely. The duplicate attribute adds no value. Keeprole="status"only.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/pages/NewThreadPage.tsx` around lines 793 - 802, Remove the redundant aria-live="polite" attribute from the span identified by data-testid="new-thread-active-machine" in the NewThreadPage component, preserving role="status" and the existing machine-name content.
823-857: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAlign the group label with the group contents and bound the list.
Two points:
DropdownMenuLabelsays "Recent Projects", but the group also contains "No Project" and "Open Folder". Move those two items outside the group, or rename the label.- Line 834 maps every entry in
projectState.projects. The list is not truncated. A user with many projects gets a very long menu. Apply a slice for the recent list.♻️ Proposed change
- <DropdownMenuGroup> - <DropdownMenuLabel className="text-xs">Recent Projects</DropdownMenuLabel> - <DropdownMenuItem + <DropdownMenuItem data-testid="new-thread-clear-project" className="gap-2 text-xs py-1.5 px-2" disabled={!canClearProjectSelection} onClick={clearSelectedProject} > <span>No Project</span> - </DropdownMenuItem> - <DropdownMenuSeparator /> - {projectState.projects.map((project) => ( + </DropdownMenuItem> + <DropdownMenuSeparator /> + <DropdownMenuGroup> + <DropdownMenuLabel className="text-xs">Recent Projects</DropdownMenuLabel> + {projectState.projects.slice(0, RECENT_PROJECT_LIMIT).map((project) => (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/pages/NewThreadPage.tsx` around lines 823 - 857, Update the dropdown around the Recent Projects group so its label covers only project entries: move the “No Project” and “Open Folder” items outside the labeled group, or rename the label to reflect all contents. In the projectState.projects.map rendering, limit displayed entries with the established recent-list bound by slicing the collection before mapping.packages/ui/src/pages/WelcomePage.tsx (1)
47-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
entranceClassconstant in both welcome pages. Both files declare the same module-level animation class string, and both use camelCase for a constant. The shared root cause is a missing shared definition.
packages/ui/src/pages/WelcomePage.tsx#L47-L47: remove the local declaration, import the shared constant, and use SCREAMING_SNAKE_CASE at the definition site.packages/ui/src/pages/AgentWelcomePage.tsx#L13-L13: remove the local declaration and import the same shared constant.As per coding guidelines: "Use PascalCase for React components, camelCase for variables and functions, PascalCase for types and classes, and SCREAMING_SNAKE_CASE for constants."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/pages/WelcomePage.tsx` at line 47, Extract the duplicated entrance animation string into one shared SCREAMING_SNAKE_CASE constant, then remove the local declaration and import/use that shared constant in packages/ui/src/pages/WelcomePage.tsx:47-47 and packages/ui/src/pages/AgentWelcomePage.tsx:13-13. Ensure both pages reference the same definition.Source: Coding guidelines
packages/ui/src/pages/AgentWelcomePage.tsx (2)
16-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse one store hook for both subscriptions.
useSelector(agentStore, (s) => s)returns the whole state. The selector adds no narrowing and defeats the purpose ofuseSelector. Line 17 already usesuseStore. UseuseStore(agentStore)for consistency, or select onlyagents.♻️ Proposed change
- const agentState = useSelector(agentStore, (s) => s); + const agentState = useStore(agentStore); const theme = useStore(themeStore);Remove
useSelectorfrom the import at line 2 if it becomes unused.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/pages/AgentWelcomePage.tsx` around lines 16 - 17, Replace the whole-state useSelector call assigned to agentState with useStore(agentStore), matching the existing themeStore subscription; remove the unused useSelector import from AgentWelcomePage.
35-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the decorative wordmark into a shared component.
The same wordmark markup exists in
packages/ui/src/pages/NewThreadPage.tsxat lines 742-749. Only thetop-*offset differs. Extract one component that accepts the offset as a prop.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/pages/AgentWelcomePage.tsx` around lines 35 - 42, Extract the duplicated decorative “argos” wordmark markup from AgentWelcomePage and NewThreadPage into a shared component. Add a prop for the differing top offset, preserve all existing styling and accessibility attributes, and update both pages to render the shared component with their respective offsets.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx`:
- Around line 37-43: Update the remote setup dialog’s onOpenChange handler and
dismissal flow around pendingWorkspace.credentialRef to invoke
discardPendingCredential before every dialog close, including direct dismissal
during review. Keep the pending credential intact when the save operation
succeeds, and preserve the existing form Cancel and review Back behavior without
duplicating cleanup.
In `@packages/ui/src/pages/AgentWelcomePage.tsx`:
- Around line 55-56: Update AgentWelcomePage’s displayedAgents rendering so
users with more than nine enabled agents can access the remaining agents: add a
visible overflow count or a “show all” control associated with the truncated
list, and wire it to reveal or navigate to the full agent set while preserving
the existing grid behavior for up to nine agents.
In `@packages/ui/src/pages/NewThreadPage.tsx`:
- Around line 737-741: Update the root layout around guideRootRef in
NewThreadPage so short viewports do not clip the notice, chat input,
machine/project row, or ChatStatusBar. Preserve the wordmark’s horizontal
clipping by replacing the broad overflow rule with horizontal clipping and add a
dedicated vertical scroll container for the content, following the
AgentWelcomePage pattern.
- Around line 816-820: Update the warning spans in the NewThreadPage rendering
blocks, including the repeated block near the second occurrence, to provide an
accessible label describing the missing or unavailable project while marking the
⚠ glyph as decorative. Do not rely on the span title alone; ensure assistive
technology receives the label through the warning element’s accessibility
attributes.
In `@packages/ui/src/pages/WelcomePage.tsx`:
- Around line 230-238: Add the established keyboard focus classes
focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring/60 to
every interactive control in WelcomePage.tsx, including the skip control
identified by welcome-guide-expert-action, the primary action, step-list
buttons, provider tiles, and ACP card. Apply the same styling consistently
without changing their existing behavior.
- Around line 100-103: Update the completedStepCount calculation in WelcomePage
to count steps with either "completed" or "skipped" status, matching the
terminal-state handling in stepStatusIcon. Keep other progress-counting behavior
unchanged so skipped steps contribute to the total and allow the progress
display to reach completion.
- Around line 277-288: Add an accessible name to the progressbar in the
WelcomePage component, using the page’s existing localization or labeling
conventions. Ensure aria-valuemax is always a valid positive maximum when
guideSteps is empty, while preserving the current zero-width behavior and
completed-step calculations.
- Line 336: Update the entrance blocks in WelcomePage, including the div using
entranceClass and animationDelay, to disable animations and reset delays when
prefers-reduced-motion is enabled. Apply the existing motion-reduce utility or
equivalent reduced-motion CSS while preserving the current staggered behavior
for users without the preference.
---
Outside diff comments:
In `@packages/ui/src/components/WorkspaceSelector.tsx`:
- Around line 169-180: Update the workspace state update in the identity-change
handling around identityChanged so a successfully saved new credential sets
trustState to "paired" instead of retaining "identity-changed". Preserve the
existing pairing-required behavior when no credential is present and the paired
behavior for normal credentialed updates.
---
Nitpick comments:
In `@packages/ui/src/pages/AgentWelcomePage.tsx`:
- Around line 16-17: Replace the whole-state useSelector call assigned to
agentState with useStore(agentStore), matching the existing themeStore
subscription; remove the unused useSelector import from AgentWelcomePage.
- Around line 35-42: Extract the duplicated decorative “argos” wordmark markup
from AgentWelcomePage and NewThreadPage into a shared component. Add a prop for
the differing top offset, preserve all existing styling and accessibility
attributes, and update both pages to render the shared component with their
respective offsets.
In `@packages/ui/src/pages/NewThreadPage.tsx`:
- Around line 793-802: Remove the redundant aria-live="polite" attribute from
the span identified by data-testid="new-thread-active-machine" in the
NewThreadPage component, preserving role="status" and the existing machine-name
content.
- Around line 823-857: Update the dropdown around the Recent Projects group so
its label covers only project entries: move the “No Project” and “Open Folder”
items outside the labeled group, or rename the label to reflect all contents. In
the projectState.projects.map rendering, limit displayed entries with the
established recent-list bound by slicing the collection before mapping.
In `@packages/ui/src/pages/WelcomePage.tsx`:
- Line 47: Extract the duplicated entrance animation string into one shared
SCREAMING_SNAKE_CASE constant, then remove the local declaration and import/use
that shared constant in packages/ui/src/pages/WelcomePage.tsx:47-47 and
packages/ui/src/pages/AgentWelcomePage.tsx:13-13. Ensure both pages reference
the same definition.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ba733463-5552-4783-a927-30ebc6adf599
📒 Files selected for processing (14)
packages/ui/src/components/WindowSideBar.tsxpackages/ui/src/components/WorkspaceSelector.tsxpackages/ui/src/components/chat/ChatStatusBar.tsxpackages/ui/src/components/chat/ChatTopBar.tsxpackages/ui/src/components/message/ImageActionContextMenu.tsxpackages/ui/src/components/message/MessageItemAssistant.tsxpackages/ui/src/components/workspace/RemoteWorkspaceSetup.tsxpackages/ui/src/components/workspace/WorkspaceFileNode.tsxpackages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsxpackages/ui/src/pages/AgentWelcomePage.tsxpackages/ui/src/pages/NewThreadPage.tsxpackages/ui/src/pages/WelcomePage.tsxpackages/ui/src/routes/_main.tsxpackages/ui/src/stores/ui/remoteSetup.ts
There was a problem hiding this comment.
Pull request overview
This PR modernizes Argos’ entry surfaces (Welcome, Agent selection, New Thread) with a more polished onboarding/empty-state layout and introduces a globally-rendered “Connect remote machine” dialog flow wired through a new remoteSetup UI store and the workspace selector.
Changes:
- Redesign WelcomePage, AgentWelcomePage, and NewThreadPage layouts (new composition, backdrop wordmark, refined progress/cta surfaces, drag-region fixes).
- Add a global remote-machine dialog flow (
remoteSetupstore +AddRemoteMachineDialogmounted inMainLayout) and refactor workspace selector to open it. - Update menu/context-menu action handlers to use
onClickand add grouping in dropdown menus for consistency.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/src/stores/ui/remoteSetup.ts | New TanStack store to control remote-machine dialog state + handlers. |
| packages/ui/src/routes/_main.tsx | Mounts AddRemoteMachineDialog at the app layout level. |
| packages/ui/src/pages/WelcomePage.tsx | Welcome/onboarding surface redesign (progress card, provider grid, ACP CTA). |
| packages/ui/src/pages/NewThreadPage.tsx | New thread empty state redesign with brand backdrop + consolidated machine/project row. |
| packages/ui/src/pages/AgentWelcomePage.tsx | Agent selection surface redesign + empty-state primary action. |
| packages/ui/src/components/WorkspaceSelector.tsx | Refactors remote-machine actions to open the global dialog via remoteSetup. |
| packages/ui/src/components/workspace/WorkspaceSelectorDialogs.tsx | Converts AddRemoteMachineDialog to read from remoteSetup store instead of props. |
| packages/ui/src/components/workspace/WorkspaceFileNode.tsx | Context menu items switched to onClick. |
| packages/ui/src/components/workspace/RemoteWorkspaceSetup.tsx | UI restructuring (view toggle, stage labels/sr text, improved form/review flow). |
| packages/ui/src/components/WindowSideBar.tsx | Sidebar shell class adjustments (removes width-transition classes). |
| packages/ui/src/components/message/MessageItemAssistant.tsx | Context menu items switched to onClick. |
| packages/ui/src/components/message/ImageActionContextMenu.tsx | Context menu items switched to onClick. |
| packages/ui/src/components/chat/ChatTopBar.tsx | Top bar class adjustments + dropdown items switched to onClick. |
| packages/ui/src/components/chat/ChatStatusBar.tsx | Dropdown item switched to onClick. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const store = useWorkspaceStore(); | ||
| const [addDialogOpen, setAddDialogOpen] = useState(false); | ||
| const remoteSetup = useRemoteSetupStore(); | ||
| const [recoveryWorkspace, setRecoveryWorkspace] = useState<WorkspaceEntry | null>(null); |
There was a problem hiding this comment.
Fixed in 785777f. Removed the now-unused local recoveryWorkspace state from WorkspaceSelector.tsx; the store-level value is retained for the dialog's initialUrl prefill.
| useEffect(() => { | ||
| remoteSetup.registerHandlers({ | ||
| remoteUrls, | ||
| onSave: handleSave, | ||
| onSaveAndSwitch: handleSaveAndSwitch, | ||
| }); | ||
| return () => { | ||
| remoteSetup.clearHandlers(); | ||
| }; | ||
| // handleSave/handleSaveAndSwitch close over `store` and `workspaces`; re-register when workspaces change. | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [workspaces, store]); |
There was a problem hiding this comment.
Fixed in 785777f. Removed the clearHandlers() unmount cleanup — handlers stay registered (re-registered when workspaces/store change), so collapsing the sidebar no longer nulls the handlers while the dialog is open.
| {handlers && ( | ||
| <RemoteWorkspaceSetup | ||
| existingRemoteUrls={handlers.remoteUrls} | ||
| initialRemoteUrl={remoteSetup.recoveryWorkspace?.remoteUrl} | ||
| onAddWorkspace={remoteSetup.saveWorkspace} | ||
| onSaveAndSwitch={handlers.onSaveAndSwitch ? remoteSetup.saveWorkspaceAndSwitch : undefined} | ||
| onCancel={remoteSetup.closeRemoteDialog} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
Fixed in 785777f. AddRemoteMachineDialog now renders a non-empty fallback body with a Close button when handlers is null, so the user is never stuck in an empty dialog.
| const agents = useStore(agentStore, (s) => s.agents); | ||
| const isDark = useStore(themeStore, (s) => s.isDark); | ||
|
|
||
| const enabledAgents = useMemo(() => agents.filter((a) => a.enabled), [agents]); |
There was a problem hiding this comment.
Fixed in f767947. Removed the manual useMemo wrappers around the agents.filter(...) and slice in AgentWelcomePage.tsx — the project enables the React Compiler (reactCompilerPreset in the UI Vite config), so the compiler caches these pure derivations automatically. useStore is scoped to the agents slice so re-renders stay narrow.
| const isDark = useStore(themeStore, (s) => s.isDark); | ||
|
|
||
| const enabledAgents = useMemo(() => agents.filter((a) => a.enabled), [agents]); | ||
| const displayedAgents = useMemo(() => enabledAgents.slice(0, 9), [enabledAgents]); |
There was a problem hiding this comment.
Fixed in f767947. Removed the manual useMemo around displayedAgents (enabledAgents.slice(0, 9)) — React Compiler handles the memoization, and the value is pure over the stable store slice.
Summary
Redesigns the app's entry surfaces to a Linear/Vercel-grade language and adds the add-remote-machine flow.
Scope
Only staged changes were included (14 files). Skills files, skills-lock.json, and feature docs remain out of this PR.
Summary by CodeRabbit
New Features
Improvements