From a5e806031c5c4df130bc152c58bb6da04b60ba5b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:07:22 -0700 Subject: [PATCH] perf(renderer): load the project-location and feedback dialogs on click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both are reachable only from an explicit click, but their chunks sat on the renderer boot graph and were fetched and parsed on every launch. Route them through the existing `lazy-with-retry` helper, keeping each trigger eager so the click target still exists, and keep the mount sticky once opened so the dialog's own close animation and repeat opens are unaffected. Renderer boot graph 4,473,242 -> 4,424,142 bytes (-49,100 B / -47.9 KiB). Trade-off: the first open per session now waits on a local chunk fetch — measured at ~0.53 ms (project location) and ~0.26 ms (feedback) of read plus V8 parse/compile, warm page cache. --- ...orkspaceComposerCard.set-location.test.tsx | 4 +- .../components/NewWorkspaceComposerCard.tsx | 38 ++++++++++++++----- .../sidebar/SidebarSettingsHelpMenu.tsx | 23 ++++++++++- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx index 9cdea1d7d54..87ef9de0631 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx @@ -225,7 +225,8 @@ describe('NewWorkspaceComposerCard set location', () => { container = null }) - it('opens set-location over the composer without leaving the create dialog', () => { + // Async because the dialog is a lazy chunk: the click mounts Suspense, the chunk resolves next tick. + it('opens set-location over the composer without leaving the create dialog', async () => { const nestedOpenChanges: boolean[] = [] container = renderCard({ onNestedDialogOpenChange: (open) => nestedOpenChanges.push(open) @@ -239,6 +240,7 @@ describe('NewWorkspaceComposerCard set location', () => { ) expect(setLocation).toBeTruthy() act(() => setLocation?.click()) + await act(async () => {}) const dialog = document.body.querySelector('[data-testid="set-project-location-dialog"]') expect(dialog?.getAttribute('data-host')).toBe('Devbox') diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.tsx index c6cca5192ea..a074e272130 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.tsx @@ -11,7 +11,7 @@ import { AddRemoteHostDialog, type AddRemoteHostMode } from '@/components/sidebar/AddRemoteHostDialog' -import { SetProjectLocationDialog } from '@/components/new-workspace/SetProjectLocationDialog' +import { lazyWithRetry } from '@/lib/lazy-with-retry' import { unwrapRuntimeRpcResult } from '@/runtime/runtime-rpc-client' import { withUiConnectTimeout } from '@/ssh/ssh-connect-ui-timeout' import { isSshConnectInFlight, trackSshConnect } from '@/ssh/ssh-connect-in-flight' @@ -37,6 +37,16 @@ import { import { getSshStatusLabel } from './new-workspace/new-workspace-composer-ssh-status' import { useComposerFileDragOver } from './new-workspace/use-composer-file-drag-over' +// Why lazy: this pulls the ~41 KB project-location browser onto the boot graph, and nothing +// reaches it without an explicit "Set location" click. +const SetProjectLocationDialog = lazyWithRetry( + () => + import('@/components/new-workspace/SetProjectLocationDialog').then((module) => ({ + default: module.SetProjectLocationDialog + })), + { reloadKey: 'set-project-location-dialog' } +) + export default function NewWorkspaceComposerCard( props: NewWorkspaceComposerCardProps ): React.JSX.Element { @@ -83,6 +93,12 @@ export default function NewWorkspaceComposerCard( const [setLocationOption, setSetLocationOption] = React.useState( null ) + // Why sticky: the dialog animates itself closed off its own `option` prop, so unmounting it + // when the option clears would cut that animation short. + const setLocationDialogRequested = React.useRef(false) + if (setLocationOption !== null) { + setLocationDialogRequested.current = true + } const selectedRepo = eligibleRepos.find((candidate) => candidate.id === repoId) const selectedRepoName = selectedRepo?.displayName ?? selectedRepo?.path ?? 'This project' @@ -319,14 +335,18 @@ export default function NewWorkspaceComposerCard( submitShortcutModifierLabel={getScreenSubmitModifierLabel()} /> - + {setLocationDialogRequested.current ? ( + + + + ) : null} ) } diff --git a/src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.tsx b/src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.tsx index fc4975eb474..ac4c40fb9a8 100644 --- a/src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.tsx +++ b/src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.tsx @@ -31,10 +31,20 @@ import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' import { showOnboardingFromRenderer } from '../onboarding/show-onboarding-event' import { SetupGuideProgressRing } from '../setup-guide/SetupGuideProgressRing' import { useSetupGuideProgress } from '../setup-guide/use-setup-guide-progress' -import { SidebarFeedbackDialog } from './SidebarFeedbackDialog' +import { lazyWithRetry } from '@/lib/lazy-with-retry' import { translate } from '@/i18n/i18n' import { getUpdateCheckClickOptions, getUpdateCheckHint } from '@/lib/update-check-click-options' +// Why lazy: the feedback form is only reachable from this menu's own item, so it does not +// belong on the renderer boot graph. +const SidebarFeedbackDialog = lazyWithRetry( + () => + import('./SidebarFeedbackDialog').then((module) => ({ + default: module.SidebarFeedbackDialog + })), + { reloadKey: 'sidebar-feedback-dialog' } +) + const DOCS_URL = 'https://www.onorca.dev/docs' const CHANGELOG_URL = 'https://onorca.dev/changelog' const GITHUB_URL = 'https://github.com/stablyai/orca' @@ -95,6 +105,11 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element { const settingsShortcut = useShortcutKeyDetails('app.settings') const [menuOpen, setMenuOpen] = useState(false) const [feedbackOpen, setFeedbackOpen] = useState(false) + // Why sticky: the dialog animates itself closed off `open`, so unmounting on close cuts that short. + const feedbackDialogRequested = React.useRef(false) + if (feedbackOpen) { + feedbackDialogRequested.current = true + } const [isRestartingOrca, setIsRestartingOrca] = useState(false) const lastShowOnboardingAtRef = React.useRef(0) const updateCheckModifiersRef = React.useRef(NO_UPDATE_CHECK_MODIFIERS) @@ -330,7 +345,11 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element { - + {feedbackDialogRequested.current ? ( + + + + ) : null} ) }