mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 16:03:53 +00:00
Remove browser settings draft sync effects
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import type { GlobalSettings } from '../../../../shared/types'
|
||||
@@ -24,6 +24,29 @@ import { BrowserUseSetup } from './BrowserUsePane'
|
||||
import { KagiSessionLinkForm } from './KagiSessionLinkForm'
|
||||
export { BROWSER_PANE_SEARCH_ENTRIES }
|
||||
|
||||
type HomePageDraftState = {
|
||||
sourceUrl: string
|
||||
draft: string
|
||||
}
|
||||
|
||||
function createHomePageDraftState(
|
||||
browserDefaultUrl: string | null | undefined
|
||||
): HomePageDraftState {
|
||||
const sourceUrl = browserDefaultUrl ?? ''
|
||||
return {
|
||||
sourceUrl,
|
||||
draft: sourceUrl
|
||||
}
|
||||
}
|
||||
|
||||
function resolveHomePageDraftState(
|
||||
state: HomePageDraftState,
|
||||
browserDefaultUrl: string | null | undefined
|
||||
): HomePageDraftState {
|
||||
const sourceUrl = browserDefaultUrl ?? ''
|
||||
return state.sourceUrl === sourceUrl ? state : { sourceUrl, draft: sourceUrl }
|
||||
}
|
||||
|
||||
type BrowserPaneProps = {
|
||||
settings: GlobalSettings
|
||||
updateSettings: (updates: Partial<GlobalSettings>) => void
|
||||
@@ -47,17 +70,29 @@ export function BrowserPane({
|
||||
const setDefaultBrowserSessionProfileId = useAppStore((s) => s.setDefaultBrowserSessionProfileId)
|
||||
const defaultProfile = browserSessionProfiles.find((p) => p.id === 'default')
|
||||
const nonDefaultProfiles = browserSessionProfiles.filter((p) => p.scope !== 'default')
|
||||
const [homePageDraft, setHomePageDraft] = useState(browserDefaultUrl ?? '')
|
||||
const [homePageDraftState, setHomePageDraftState] = useState(() =>
|
||||
createHomePageDraftState(browserDefaultUrl)
|
||||
)
|
||||
const [newProfileDialogOpen, setNewProfileDialogOpen] = useState(false)
|
||||
const [newProfileName, setNewProfileName] = useState('')
|
||||
const [isCreatingProfile, setIsCreatingProfile] = useState(false)
|
||||
|
||||
// Why: sync draft with store value whenever it changes externally (e.g. the
|
||||
// in-app browser tab's address bar saves a home page). Without this, the
|
||||
// settings field would show stale text after another surface wrote the value.
|
||||
useEffect(() => {
|
||||
setHomePageDraft(browserDefaultUrl ?? '')
|
||||
}, [browserDefaultUrl])
|
||||
const resolvedHomePageDraftState = resolveHomePageDraftState(
|
||||
homePageDraftState,
|
||||
browserDefaultUrl
|
||||
)
|
||||
if (resolvedHomePageDraftState !== homePageDraftState) {
|
||||
// Why: the address bar can save a new home page outside Settings; reconcile
|
||||
// the draft before paint so the field never flashes stale text.
|
||||
setHomePageDraftState(resolvedHomePageDraftState)
|
||||
}
|
||||
const homePageDraft = resolvedHomePageDraftState.draft
|
||||
const updateHomePageDraft = (draft: string): void => {
|
||||
setHomePageDraftState((current) => ({
|
||||
...resolveHomePageDraftState(current, browserDefaultUrl),
|
||||
draft
|
||||
}))
|
||||
}
|
||||
|
||||
const selectedSearchEngine = browserDefaultSearchEngine ?? 'google'
|
||||
|
||||
@@ -116,19 +151,20 @@ export function BrowserPane({
|
||||
const trimmed = homePageDraft.trim()
|
||||
if (!trimmed) {
|
||||
setBrowserDefaultUrl(null)
|
||||
setHomePageDraftState(createHomePageDraftState(null))
|
||||
return
|
||||
}
|
||||
const normalized = normalizeBrowserNavigationUrl(trimmed)
|
||||
if (normalized && normalized !== ORCA_BROWSER_BLANK_URL) {
|
||||
setBrowserDefaultUrl(normalized)
|
||||
setHomePageDraft(normalized)
|
||||
setHomePageDraftState(createHomePageDraftState(normalized))
|
||||
toast.success('Home page saved.')
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={homePageDraft}
|
||||
onChange={(e) => setHomePageDraft(e.target.value)}
|
||||
onChange={(e) => updateHomePageDraft(e.target.value)}
|
||||
placeholder="https://google.com"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
|
||||
@@ -1,26 +1,59 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { normalizeKagiSessionLink } from '../../../../shared/browser-url'
|
||||
import { useAppStore } from '../../store'
|
||||
import { Button } from '../ui/button'
|
||||
import { Input } from '../ui/input'
|
||||
|
||||
type KagiSessionLinkDraftState = {
|
||||
sourceLink: string
|
||||
draft: string
|
||||
}
|
||||
|
||||
function createKagiSessionLinkDraftState(
|
||||
browserKagiSessionLink: string | null | undefined
|
||||
): KagiSessionLinkDraftState {
|
||||
const sourceLink = browserKagiSessionLink ?? ''
|
||||
return {
|
||||
sourceLink,
|
||||
draft: sourceLink
|
||||
}
|
||||
}
|
||||
|
||||
function resolveKagiSessionLinkDraftState(
|
||||
state: KagiSessionLinkDraftState,
|
||||
browserKagiSessionLink: string | null | undefined
|
||||
): KagiSessionLinkDraftState {
|
||||
const sourceLink = browserKagiSessionLink ?? ''
|
||||
return state.sourceLink === sourceLink ? state : { sourceLink, draft: sourceLink }
|
||||
}
|
||||
|
||||
export function KagiSessionLinkForm(): React.JSX.Element {
|
||||
const browserKagiSessionLink = useAppStore((s) => s.browserKagiSessionLink)
|
||||
const setBrowserKagiSessionLink = useAppStore((s) => s.setBrowserKagiSessionLink)
|
||||
const [draft, setDraft] = useState(browserKagiSessionLink ?? '')
|
||||
const [draftState, setDraftState] = useState(() =>
|
||||
createKagiSessionLinkDraftState(browserKagiSessionLink)
|
||||
)
|
||||
|
||||
// Why: the Kagi token is edited as a masked draft so accidental typing or
|
||||
// external settings updates do not immediately overwrite the persisted secret.
|
||||
useEffect(() => {
|
||||
setDraft(browserKagiSessionLink ?? '')
|
||||
}, [browserKagiSessionLink])
|
||||
const resolvedDraftState = resolveKagiSessionLinkDraftState(draftState, browserKagiSessionLink)
|
||||
if (resolvedDraftState !== draftState) {
|
||||
// Why: the masked draft tracks persisted secret updates without committing
|
||||
// accidental typing back to settings or waiting for an after-paint Effect.
|
||||
setDraftState(resolvedDraftState)
|
||||
}
|
||||
const draft = resolvedDraftState.draft
|
||||
const updateDraft = (nextDraft: string): void => {
|
||||
setDraftState((current) => ({
|
||||
...resolveKagiSessionLinkDraftState(current, browserKagiSessionLink),
|
||||
draft: nextDraft
|
||||
}))
|
||||
}
|
||||
|
||||
const save = (): void => {
|
||||
const trimmed = draft.trim()
|
||||
if (!trimmed) {
|
||||
setBrowserKagiSessionLink(null)
|
||||
setDraft('')
|
||||
setDraftState(createKagiSessionLinkDraftState(null))
|
||||
toast.success('Kagi session link cleared.')
|
||||
return
|
||||
}
|
||||
@@ -30,7 +63,7 @@ export function KagiSessionLinkForm(): React.JSX.Element {
|
||||
return
|
||||
}
|
||||
setBrowserKagiSessionLink(normalized)
|
||||
setDraft(normalized)
|
||||
setDraftState(createKagiSessionLinkDraftState(normalized))
|
||||
toast.success('Kagi session link saved.')
|
||||
}
|
||||
|
||||
@@ -49,7 +82,7 @@ export function KagiSessionLinkForm(): React.JSX.Element {
|
||||
<Input
|
||||
type="password"
|
||||
value={draft}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
onChange={(e) => updateDraft(e.target.value)}
|
||||
placeholder="https://kagi.com/search?token=..."
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
@@ -69,7 +102,7 @@ export function KagiSessionLinkForm(): React.JSX.Element {
|
||||
className="h-7 text-xs"
|
||||
onClick={() => {
|
||||
setBrowserKagiSessionLink(null)
|
||||
setDraft('')
|
||||
setDraftState(createKagiSessionLinkDraftState(null))
|
||||
toast.success('Kagi session link cleared.')
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user