mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 08:03:12 +00:00
fix(mobile): derive browser navigability in render and keep offset-keyed syntax segments
react-doctor flagged the tab-prop resync effect as derived state and the index key in MobileSyntaxSegments. Navigability is now resolved in render from the tab props plus the host refinement it superseded, so a tab update never lags a commit. appendSegment already drops zero-length segments, so the source-offset key is unique without an index. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -22,6 +22,11 @@ import {
|
||||
} from './mobile-browser-frame-state'
|
||||
import { displayBrowserUrl, normalizeBrowserUrl } from './browser-url'
|
||||
import { resolveMobileBrowserAddressSync } from './mobile-browser-address-sync'
|
||||
import {
|
||||
resolveMobileBrowserNavigationState,
|
||||
type MobileBrowserNavigationRefinement,
|
||||
type MobileBrowserNavigationState
|
||||
} from './mobile-browser-navigation-state'
|
||||
import { MobileBrowserPaneView } from './MobileBrowserPaneView'
|
||||
import { useMobileBrowserInteractions } from './use-mobile-browser-interactions'
|
||||
import { useMobileBrowserPaneLayers } from './use-mobile-browser-pane-layers'
|
||||
@@ -84,10 +89,21 @@ export function MobileBrowserPane({
|
||||
const cachedInitialFrame = peekCachedBrowserFrame(cacheKey)
|
||||
const [addressValue, setAddressValue] = useState(displayBrowserUrl(tab.url))
|
||||
const [addressFocused, setAddressFocused] = useState(false)
|
||||
const [navigationState, setNavigationState] = useState({
|
||||
canGoBack: tab.canGoBack,
|
||||
canGoForward: tab.canGoForward
|
||||
})
|
||||
// A host `navigation` event refines the tab's navigability until the tab itself republishes;
|
||||
// an older host that never emits it leaves the tab props in charge. Derived in render so a tab
|
||||
// update never lags one commit behind.
|
||||
const [navigationRefinement, setNavigationRefinement] =
|
||||
useState<MobileBrowserNavigationRefinement | null>(null)
|
||||
const navigationState = resolveMobileBrowserNavigationState(tab, navigationRefinement)
|
||||
const setNavigationState = useCallback(
|
||||
(next: MobileBrowserNavigationState) => {
|
||||
setNavigationRefinement({
|
||||
...next,
|
||||
base: { canGoBack: tab.canGoBack, canGoForward: tab.canGoForward }
|
||||
})
|
||||
},
|
||||
[tab.canGoBack, tab.canGoForward]
|
||||
)
|
||||
const [addressSyncState, setAddressSyncState] = useState({
|
||||
focused: false,
|
||||
url: tab.url
|
||||
@@ -177,12 +193,6 @@ export function MobileBrowserPane({
|
||||
focused: addressFocused,
|
||||
url: tab.url
|
||||
})
|
||||
// The screencast `navigation` event is a newer-host refinement; the tab props stay
|
||||
// the baseline so an older host that never emits it does not freeze Back/Forward.
|
||||
useEffect(() => {
|
||||
setNavigationState({ canGoBack: tab.canGoBack, canGoForward: tab.canGoForward })
|
||||
}, [tab.canGoBack, tab.canGoForward])
|
||||
|
||||
useEffect(() => {
|
||||
if (addressSync.nextState === addressSyncState) {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export type MobileBrowserNavigationState = { canGoBack: boolean; canGoForward: boolean }
|
||||
|
||||
/** A host `navigation` refinement plus the tab props it refined, so a later tab update wins. */
|
||||
export type MobileBrowserNavigationRefinement = MobileBrowserNavigationState & {
|
||||
base: MobileBrowserNavigationState
|
||||
}
|
||||
|
||||
// Why: the tab props are the baseline every host publishes; the screencast `navigation` event is
|
||||
// a newer-host refinement that only stands until the tab itself republishes navigability.
|
||||
export function resolveMobileBrowserNavigationState(
|
||||
tab: MobileBrowserNavigationState,
|
||||
refinement: MobileBrowserNavigationRefinement | null
|
||||
): MobileBrowserNavigationState {
|
||||
if (
|
||||
refinement &&
|
||||
refinement.base.canGoBack === tab.canGoBack &&
|
||||
refinement.base.canGoForward === tab.canGoForward
|
||||
) {
|
||||
return { canGoBack: refinement.canGoBack, canGoForward: refinement.canGoForward }
|
||||
}
|
||||
return { canGoBack: tab.canGoBack, canGoForward: tab.canGoForward }
|
||||
}
|
||||
@@ -60,7 +60,7 @@ type MobileBrowserStreamArgs = {
|
||||
setError: Dispatch<SetStateAction<string | null>>
|
||||
setFrameMetadata: Dispatch<SetStateAction<BrowserScreencastFrameMetadata | null>>
|
||||
setFrameUri: Dispatch<SetStateAction<string | null>>
|
||||
setNavigationState?: Dispatch<SetStateAction<{ canGoBack: boolean; canGoForward: boolean }>>
|
||||
setNavigationState?: (next: { canGoBack: boolean; canGoForward: boolean }) => void
|
||||
setZoom: Dispatch<SetStateAction<BrowserZoomState>>
|
||||
streamGenerationRef: { current: number }
|
||||
tab: MobileBrowserTab
|
||||
|
||||
@@ -6,9 +6,8 @@ export function MobileSyntaxSegments({ segments }: { segments: MobileSyntaxSegme
|
||||
let sourceOffset = 0
|
||||
return (
|
||||
<>
|
||||
{segments.map((segment, index) => {
|
||||
// The index keeps two adjacent zero-length segments of the same kind distinct.
|
||||
const key = `${index}:${sourceOffset}:${segment.kind}`
|
||||
{segments.map((segment) => {
|
||||
const key = `${sourceOffset}:${segment.kind}`
|
||||
sourceOffset += segment.text.length
|
||||
return (
|
||||
<Text key={key} style={[webSyntaxTextStyle, syntaxTokenStyles[segment.kind]]}>
|
||||
|
||||
Reference in New Issue
Block a user