Move mobile settings to beta section (#1741)
* Move mobile settings to beta section Co-authored-by: Orca <help@stably.ai> * Add mobile setup guidance Co-authored-by: Orca <help@stably.ai> * Use global App Store URL for mobile Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
954fb9f316
commit
fa769edb8d
|
|
@ -9,7 +9,6 @@ import { useAppStore } from '../../store'
|
|||
import { SearchableSetting } from './SearchableSetting'
|
||||
import { matchesSettingsSearch } from './settings-search'
|
||||
import { EXPERIMENTAL_PANE_SEARCH_ENTRIES, EXPERIMENTAL_SEARCH_ENTRY } from './experimental-search'
|
||||
import { MobilePane } from './MobilePane'
|
||||
import { HiddenExperimentalGroup } from './HiddenExperimentalGroup'
|
||||
|
||||
export { EXPERIMENTAL_PANE_SEARCH_ENTRIES }
|
||||
|
|
@ -31,7 +30,6 @@ export function ExperimentalPane({
|
|||
hiddenExperimentalUnlocked = false
|
||||
}: ExperimentalPaneProps): React.JSX.Element {
|
||||
const searchQuery = useAppStore((s) => s.settingsSearchQuery)
|
||||
const showMobile = matchesSettingsSearch(searchQuery, [EXPERIMENTAL_SEARCH_ENTRY.mobile])
|
||||
const showPet = matchesSettingsSearch(searchQuery, [EXPERIMENTAL_SEARCH_ENTRY.pet])
|
||||
const showOrchestration = matchesSettingsSearch(searchQuery, [
|
||||
EXPERIMENTAL_SEARCH_ENTRY.orchestration
|
||||
|
|
@ -70,66 +68,6 @@ export function ExperimentalPane({
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{showMobile ? (
|
||||
<SearchableSetting
|
||||
title="Mobile Pairing"
|
||||
description="Pair a mobile device to control Orca remotely."
|
||||
keywords={EXPERIMENTAL_SEARCH_ENTRY.mobile.keywords}
|
||||
className="space-y-3 px-1 py-2"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="min-w-0 shrink space-y-1.5">
|
||||
<Label>Mobile Pairing</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Control Orca from your phone by scanning a QR code. Beta / early preview —
|
||||
expect bugs and breaking changes. Get started from the{' '}
|
||||
<button
|
||||
type="button"
|
||||
// Why: points at the current mobile release tag rather than
|
||||
// the generic /releases page, which is dominated by desktop
|
||||
// releases and forces the user to scroll. Update this URL
|
||||
// when cutting a new mobile-v* tag.
|
||||
onClick={() =>
|
||||
void window.api.shell.openUrl(
|
||||
'https://github.com/stablyai/orca/releases/tag/mobile-v0.0.2'
|
||||
)
|
||||
}
|
||||
className="cursor-pointer underline underline-offset-2 hover:text-foreground"
|
||||
>
|
||||
GitHub Releases page
|
||||
</button>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={settings.experimentalMobile}
|
||||
onClick={() =>
|
||||
updateSettings({
|
||||
experimentalMobile: !settings.experimentalMobile
|
||||
})
|
||||
}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors ${
|
||||
settings.experimentalMobile ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-3.5 w-3.5 transform rounded-full bg-background shadow-sm transition-transform ${
|
||||
settings.experimentalMobile ? 'translate-x-4' : 'translate-x-0.5'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{settings.experimentalMobile ? (
|
||||
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
|
||||
<MobilePane />
|
||||
</div>
|
||||
) : null}
|
||||
</SearchableSetting>
|
||||
) : null}
|
||||
|
||||
{showPet ? (
|
||||
<SearchableSetting
|
||||
title="Pet"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Check, Copy, Maximize2, RefreshCw, Smartphone, Trash2, Wifi } from 'lucide-react'
|
||||
import {
|
||||
Check,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
Maximize2,
|
||||
RefreshCw,
|
||||
Smartphone,
|
||||
Trash2,
|
||||
Wifi
|
||||
} from 'lucide-react'
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../ui/accordion'
|
||||
import { Button } from '../ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
|
||||
|
|
@ -18,6 +28,8 @@ const AUTO_RESTORE_FIT_OPTIONS: { value: string; label: string; ms: number | nul
|
|||
{ value: '30m', label: 'After 30 minutes', ms: 30 * 60_000 }
|
||||
]
|
||||
|
||||
const TAILSCALE_DOWNLOAD_URL = 'https://tailscale.com/download'
|
||||
|
||||
function autoRestoreValueFromMs(ms: number | null | undefined): string {
|
||||
if (ms == null) {
|
||||
return 'indefinite'
|
||||
|
|
@ -40,7 +52,19 @@ export const MOBILE_PANE_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
|||
{
|
||||
title: 'Network Interface',
|
||||
description: 'Choose which network address to use for mobile pairing.',
|
||||
keywords: ['network', 'interface', 'tailscale', 'vpn', 'overlay', 'ip', 'address', 'wifi']
|
||||
keywords: [
|
||||
'network',
|
||||
'interface',
|
||||
'tailscale',
|
||||
'tailnet',
|
||||
'vpn',
|
||||
'overlay',
|
||||
'ip',
|
||||
'address',
|
||||
'wifi',
|
||||
'lan',
|
||||
'remote'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'When you leave the mobile app',
|
||||
|
|
@ -227,6 +251,40 @@ export function MobilePane(): React.JSX.Element {
|
|||
{qrDataUrl ? 'Regenerate' : 'Generate QR Code'}
|
||||
</Button>
|
||||
</div>
|
||||
<Accordion type="single" collapsible className="mt-4 border-t border-border/60 pt-2">
|
||||
<AccordionItem value="remote-pairing-guide">
|
||||
<AccordionTrigger className="py-2 text-xs">
|
||||
Connect outside your Wi-Fi with a tailnet
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="space-y-3 text-xs text-muted-foreground">
|
||||
<p>
|
||||
Orca Mobile connects directly to this computer. To use it away from the same local
|
||||
network, put your computer and phone on the same private overlay network, then
|
||||
generate the QR code with that network address selected.
|
||||
</p>
|
||||
<ol className="list-decimal space-y-1 pl-4">
|
||||
<li>
|
||||
Install{' '}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void window.api.shell.openUrl(TAILSCALE_DOWNLOAD_URL)}
|
||||
className="inline-flex items-center gap-1 font-medium text-foreground underline-offset-2 hover:underline"
|
||||
>
|
||||
Tailscale
|
||||
<ExternalLink className="size-3" />
|
||||
</button>{' '}
|
||||
on your computer and phone.
|
||||
</li>
|
||||
<li>Sign in to the same tailnet on both devices.</li>
|
||||
<li>
|
||||
In this Network Interface menu, choose the Tailscale address, usually a 100.x.y.z
|
||||
IP.
|
||||
</li>
|
||||
<li>Regenerate the QR code and scan it from the Orca mobile app.</li>
|
||||
</ol>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
|
||||
{/* QR code display */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
import type { GlobalSettings } from '../../../../shared/types'
|
||||
import { Label } from '../ui/label'
|
||||
import { SearchableSetting } from './SearchableSetting'
|
||||
import { matchesSettingsSearch, type SettingsSearchEntry } from './settings-search'
|
||||
import { useAppStore } from '../../store'
|
||||
import { MobilePane, MOBILE_PANE_SEARCH_ENTRIES } from './MobilePane'
|
||||
|
||||
const ORCA_IOS_APP_STORE_URL = 'https://apps.apple.com/app/orca-ide/id6766130217'
|
||||
const ORCA_ANDROID_RELEASE_URL = 'https://github.com/stablyai/orca/releases/tag/mobile-v0.0.2'
|
||||
|
||||
const MOBILE_ENABLE_SEARCH_ENTRY: SettingsSearchEntry = {
|
||||
title: 'Mobile',
|
||||
description: 'Pair a mobile device to control Orca remotely.',
|
||||
keywords: [
|
||||
'mobile',
|
||||
'phone',
|
||||
'pair',
|
||||
'qr',
|
||||
'code',
|
||||
'scan',
|
||||
'remote',
|
||||
'android',
|
||||
'apk',
|
||||
'beta',
|
||||
'experimental'
|
||||
]
|
||||
}
|
||||
|
||||
export const MOBILE_SETTINGS_PANE_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
||||
MOBILE_ENABLE_SEARCH_ENTRY,
|
||||
...MOBILE_PANE_SEARCH_ENTRIES
|
||||
]
|
||||
|
||||
type MobileSettingsPaneProps = {
|
||||
settings: GlobalSettings
|
||||
updateSettings: (updates: Partial<GlobalSettings>) => void
|
||||
}
|
||||
|
||||
export function MobileSettingsPane({
|
||||
settings,
|
||||
updateSettings
|
||||
}: MobileSettingsPaneProps): React.JSX.Element {
|
||||
const searchQuery = useAppStore((s) => s.settingsSearchQuery)
|
||||
const showEnableSetting =
|
||||
!settings.experimentalMobile || matchesSettingsSearch(searchQuery, [MOBILE_ENABLE_SEARCH_ENTRY])
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{showEnableSetting ? (
|
||||
<SearchableSetting
|
||||
title="Mobile"
|
||||
description="Pair a mobile device to control Orca remotely."
|
||||
keywords={MOBILE_ENABLE_SEARCH_ENTRY.keywords}
|
||||
className="space-y-3 px-1 py-2"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="min-w-0 shrink space-y-1.5">
|
||||
<Label>Mobile</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Control Orca from your phone by scanning a QR code. Beta / early preview —
|
||||
expect bugs and breaking changes. Get the iOS app from the{' '}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void window.api.shell.openUrl(ORCA_IOS_APP_STORE_URL)}
|
||||
className="cursor-pointer underline underline-offset-2 hover:text-foreground"
|
||||
>
|
||||
App Store
|
||||
</button>{' '}
|
||||
or the Android APK from{' '}
|
||||
<button
|
||||
type="button"
|
||||
// Why: Android still ships from the current mobile release tag rather than
|
||||
// the generic /releases page, which is dominated by desktop
|
||||
// releases and forces the user to scroll. Update this URL
|
||||
// when cutting a new mobile-v* tag.
|
||||
onClick={() => void window.api.shell.openUrl(ORCA_ANDROID_RELEASE_URL)}
|
||||
className="cursor-pointer underline underline-offset-2 hover:text-foreground"
|
||||
>
|
||||
GitHub Releases page
|
||||
</button>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={settings.experimentalMobile}
|
||||
onClick={() =>
|
||||
updateSettings({
|
||||
experimentalMobile: !settings.experimentalMobile
|
||||
})
|
||||
}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors ${
|
||||
settings.experimentalMobile ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-3.5 w-3.5 transform rounded-full bg-background shadow-sm transition-transform ${
|
||||
settings.experimentalMobile ? 'translate-x-4' : 'translate-x-0.5'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</SearchableSetting>
|
||||
) : null}
|
||||
|
||||
{settings.experimentalMobile ? (
|
||||
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
|
||||
<MobilePane />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
Palette,
|
||||
Server,
|
||||
SlidersHorizontal,
|
||||
Smartphone,
|
||||
Blocks,
|
||||
SquareTerminal,
|
||||
UserCog
|
||||
|
|
@ -51,6 +52,7 @@ import {
|
|||
DEVELOPER_PERMISSIONS_PANE_SEARCH_ENTRIES
|
||||
} from './DeveloperPermissionsPane'
|
||||
import { ComputerUsePane, COMPUTER_USE_PANE_SEARCH_ENTRIES } from './ComputerUsePane'
|
||||
import { MobileSettingsPane, MOBILE_SETTINGS_PANE_SEARCH_ENTRIES } from './MobileSettingsPane'
|
||||
import { PrivacyPane } from './PrivacyPane'
|
||||
import { PRIVACY_PANE_SEARCH_ENTRIES } from './privacy-search'
|
||||
import { SettingsSidebar } from './SettingsSidebar'
|
||||
|
|
@ -431,6 +433,14 @@ function Settings(): React.JSX.Element {
|
|||
icon: Bell,
|
||||
searchEntries: NOTIFICATIONS_PANE_SEARCH_ENTRIES
|
||||
},
|
||||
{
|
||||
id: 'mobile',
|
||||
title: 'Mobile',
|
||||
description: 'Pair and manage mobile devices.',
|
||||
icon: Smartphone,
|
||||
searchEntries: MOBILE_SETTINGS_PANE_SEARCH_ENTRIES,
|
||||
badge: 'Beta'
|
||||
},
|
||||
{
|
||||
id: 'computer-use',
|
||||
title: 'Computer Use',
|
||||
|
|
@ -781,6 +791,16 @@ function Settings(): React.JSX.Element {
|
|||
<NotificationsPane settings={settings} updateSettings={updateSettings} />
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="mobile"
|
||||
title="Mobile"
|
||||
badge="Beta"
|
||||
description="Pair and manage mobile devices."
|
||||
searchEntries={MOBILE_SETTINGS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
<MobileSettingsPane settings={settings} updateSettings={updateSettings} />
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="computer-use"
|
||||
title="Computer Use"
|
||||
|
|
|
|||
|
|
@ -1,23 +1,6 @@
|
|||
import type { SettingsSearchEntry } from './settings-search'
|
||||
|
||||
export const EXPERIMENTAL_PANE_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
||||
{
|
||||
title: 'Mobile Pairing',
|
||||
description:
|
||||
'Pair a mobile device to control Orca remotely. Experimental — requires the Orca mobile APK from GitHub Releases.',
|
||||
keywords: [
|
||||
'experimental',
|
||||
'mobile',
|
||||
'phone',
|
||||
'pair',
|
||||
'qr',
|
||||
'code',
|
||||
'scan',
|
||||
'remote',
|
||||
'android',
|
||||
'apk'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Pet',
|
||||
description: 'Floating animated pet in the bottom-right corner.',
|
||||
|
|
@ -95,7 +78,6 @@ function findEntry(title: string): SettingsSearchEntry {
|
|||
}
|
||||
|
||||
export const EXPERIMENTAL_SEARCH_ENTRY = {
|
||||
mobile: findEntry('Mobile Pairing'),
|
||||
pet: findEntry('Pet'),
|
||||
orchestration: findEntry('Agent Orchestration'),
|
||||
activity: findEntry('Activity Page'),
|
||||
|
|
|
|||
Loading…
Reference in New Issue