mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 08:03:37 +00:00
fix(mobile): splash/icon sizing, worktree sleep, host removal persistence
- Add worktree.sleep RPC method with full notifier chain (runtime → IPC → renderer) - Optimistic UI update for sleeping worktrees in mobile client - Fix splash screen: configure expo-splash-screen plugin, hide splash only after navigator layout - Regenerate app icon and splash assets at correct density sizes - Fix host removal not persisting by disabling Android auto-backup (allowBackup=false) - Guard against invalid host credentials (base64 crash) and prevent navigating into auth-failed hosts - Extract ActionSheetContent for inline drawer content switching - Rewrite BottomDrawer as always-mounted Animated.View overlay (replaces Modal) - Add ConfirmModal component with inline confirm/cancel buttons Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -95,6 +95,7 @@ describe('fit override integration', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: (ptyId, mode, cols, rows) => {
|
||||
notifications.push({ ptyId, mode, cols, rows })
|
||||
}
|
||||
@@ -198,6 +199,7 @@ describe('fit override integration', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn()
|
||||
})
|
||||
|
||||
@@ -258,6 +260,7 @@ describe('fit override integration', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn()
|
||||
})
|
||||
|
||||
|
||||
@@ -1249,6 +1249,7 @@ describe('OrcaRuntimeService', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn()
|
||||
})
|
||||
runtime.attachWindow(TEST_WINDOW_ID)
|
||||
@@ -1274,6 +1275,7 @@ describe('OrcaRuntimeService', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn()
|
||||
})
|
||||
runtime.attachWindow(1)
|
||||
@@ -1442,6 +1444,7 @@ describe('OrcaRuntimeService', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: vi.fn()
|
||||
})
|
||||
|
||||
@@ -1575,6 +1578,7 @@ describe('OrcaRuntimeService', () => {
|
||||
renameTerminal: vi.fn(),
|
||||
focusTerminal: vi.fn(),
|
||||
closeTerminal: vi.fn(),
|
||||
sleepWorktree: vi.fn(),
|
||||
terminalFitOverrideChanged: (ptyId, mode, cols, rows) => {
|
||||
notifications.push({ ptyId, mode, cols, rows })
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ type RuntimeNotifier = {
|
||||
renameTerminal(tabId: string, title: string | null): void
|
||||
focusTerminal(tabId: string, worktreeId: string): void
|
||||
closeTerminal(tabId: string, paneRuntimeId?: number): void
|
||||
sleepWorktree(worktreeId: string): void
|
||||
terminalFitOverrideChanged(
|
||||
ptyId: string,
|
||||
mode: 'mobile-fit' | 'desktop-fit',
|
||||
@@ -1568,6 +1569,15 @@ export class OrcaRuntimeService {
|
||||
return await this.resolveWorktreeSelector(worktreeSelector)
|
||||
}
|
||||
|
||||
async sleepManagedWorktree(worktreeSelector: string): Promise<{ worktreeId: string }> {
|
||||
const worktree = await this.resolveWorktreeSelector(worktreeSelector)
|
||||
// Why: sleep is renderer-initiated on desktop (it tears down tab state
|
||||
// before killing PTYs). The notifier tells the renderer to run its own
|
||||
// sleep flow so all cleanup happens in the correct order.
|
||||
this.notifier?.sleepWorktree(worktree.id)
|
||||
return { worktreeId: worktree.id }
|
||||
}
|
||||
|
||||
async activateManagedWorktree(worktreeSelector: string): Promise<{
|
||||
repoId: string
|
||||
worktreeId: string
|
||||
|
||||
@@ -71,6 +71,11 @@ export const WORKTREE_METHODS: RpcMethod[] = [
|
||||
worktree: await runtime.showManagedWorktree(params.worktree)
|
||||
})
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'worktree.sleep',
|
||||
params: WorktreeSelector,
|
||||
handler: async (params, { runtime }) => runtime.sleepManagedWorktree(params.worktree)
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'worktree.activate',
|
||||
params: WorktreeSelector,
|
||||
|
||||
@@ -234,6 +234,7 @@ function registerRuntimeWindowLifecycle(
|
||||
renameTerminal: (tabId, title) => send('ui:renameTerminal', { tabId, title }),
|
||||
focusTerminal: (tabId, worktreeId) => send('ui:focusTerminal', { tabId, worktreeId }),
|
||||
closeTerminal: (tabId, paneRuntimeId) => send('ui:closeTerminal', { tabId, paneRuntimeId }),
|
||||
sleepWorktree: (worktreeId) => send('ui:sleepWorktree', { worktreeId }),
|
||||
terminalFitOverrideChanged: (ptyId, mode, cols, rows) =>
|
||||
send('runtime:terminalFitOverrideChanged', { ptyId, mode, cols, rows })
|
||||
})
|
||||
|
||||
@@ -828,6 +828,7 @@ export type PreloadApi = {
|
||||
onCloseTerminal: (
|
||||
callback: (data: { tabId: string; paneRuntimeId?: number }) => void
|
||||
) => () => void
|
||||
onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void
|
||||
onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void
|
||||
readClipboardText: () => Promise<string>
|
||||
saveClipboardImageAsTempFile: () => Promise<string | null>
|
||||
|
||||
@@ -1505,6 +1505,12 @@ const api = {
|
||||
ipcRenderer.on('ui:closeTerminal', listener)
|
||||
return () => ipcRenderer.removeListener('ui:closeTerminal', listener)
|
||||
},
|
||||
onSleepWorktree: (callback: (data: { worktreeId: string }) => void): (() => void) => {
|
||||
const listener = (_event: Electron.IpcRendererEvent, data: { worktreeId: string }) =>
|
||||
callback(data)
|
||||
ipcRenderer.on('ui:sleepWorktree', listener)
|
||||
return () => ipcRenderer.removeListener('ui:sleepWorktree', listener)
|
||||
},
|
||||
onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void): (() => void) => {
|
||||
const listener = (_event: Electron.IpcRendererEvent, direction: 'in' | 'out' | 'reset') =>
|
||||
callback(direction)
|
||||
|
||||
@@ -163,6 +163,7 @@ describe('useIpcEvents updater integration', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -357,6 +358,7 @@ describe('useIpcEvents updater integration', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -549,6 +551,7 @@ describe('useIpcEvents updater integration', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -744,6 +747,7 @@ describe('useIpcEvents browser tab close routing', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -933,6 +937,7 @@ describe('useIpcEvents browser tab close routing', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -1117,6 +1122,7 @@ describe('useIpcEvents browser tab close routing', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -1319,6 +1325,7 @@ describe('useIpcEvents shortcut hint clearing', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
@@ -1519,6 +1526,7 @@ describe('useIpcEvents CLI-created worktree activation', () => {
|
||||
onRenameTerminal: () => () => {},
|
||||
onFocusTerminal: () => () => {},
|
||||
onCloseTerminal: () => () => {},
|
||||
onSleepWorktree: () => () => {},
|
||||
onNewBrowserTab: () => () => {},
|
||||
onRequestTabCreate: () => () => {},
|
||||
replyTabCreate: () => {},
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect } from 'react'
|
||||
import { useAppStore } from '../store'
|
||||
import { applyUIZoom } from '@/lib/ui-zoom'
|
||||
import { activateAndRevealWorktree } from '@/lib/worktree-activation'
|
||||
import { runSleepWorktree } from '@/components/sidebar/sleep-worktree-flow'
|
||||
import { SPLIT_TERMINAL_PANE_EVENT, CLOSE_TERMINAL_PANE_EVENT } from '@/constants/terminal'
|
||||
import type { SplitTerminalPaneDetail, CloseTerminalPaneDetail } from '@/constants/terminal'
|
||||
import { getVisibleWorktreeIds } from '@/components/sidebar/visible-worktrees'
|
||||
@@ -283,6 +284,12 @@ export function useIpcEvents(): void {
|
||||
})
|
||||
)
|
||||
|
||||
unsubs.push(
|
||||
window.api.ui.onSleepWorktree(({ worktreeId }) => {
|
||||
void runSleepWorktree(worktreeId)
|
||||
})
|
||||
)
|
||||
|
||||
// Hydrate initial update status then subscribe to changes
|
||||
window.api.updater.getStatus().then((status) => {
|
||||
useAppStore.getState().setUpdateStatus(status as UpdateStatus)
|
||||
|
||||
Reference in New Issue
Block a user