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} ) }