* fix(app): stabilize streamed chat rendering Use deterministic Markdown AST keys and preserve unique row identities when an assistant message resumes after a tool. Keep native gesture and plan-card children correctly keyed to avoid unsupported Fabric reconciliation paths. * fix(app): stabilize retained native panels Keep retained workspace roots in a stable native order and make active panels present in the selection commit. Hidden panels now stop expensive subscriptions and animations without losing mounted state. * fix(app): harden retained panel activity
70 lines
1.8 KiB
TypeScript
70 lines
1.8 KiB
TypeScript
import { createContext, memo, type ReactNode, useContext } from "react";
|
|
import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
|
|
|
|
const RetainedPanelActiveContext = createContext(true);
|
|
|
|
export function useRetainedPanelActive(): boolean {
|
|
return useContext(RetainedPanelActiveContext);
|
|
}
|
|
|
|
interface RetainedPanelProps {
|
|
active: boolean;
|
|
children: ReactNode;
|
|
style?: StyleProp<ViewStyle>;
|
|
testID?: string;
|
|
}
|
|
|
|
interface RetainedPanelActivityProps {
|
|
active: boolean;
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function RetainedPanelActivity({ active, children }: RetainedPanelActivityProps) {
|
|
const parentActive = useRetainedPanelActive();
|
|
return (
|
|
<RetainedPanelActiveContext value={parentActive && active}>
|
|
{children}
|
|
</RetainedPanelActiveContext>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Keeps expensive panel state mounted without letting an inactive panel render
|
|
* on screen. The stable, non-collapsible native root is intentional: retained
|
|
* panels must not detach or reparent their native descendants when visibility
|
|
* changes.
|
|
*/
|
|
export const RetainedPanel = memo(function RetainedPanel({
|
|
active,
|
|
children,
|
|
style,
|
|
testID,
|
|
}: RetainedPanelProps) {
|
|
const visibleStyle = StyleSheet.compose<ViewStyle, ViewStyle, ViewStyle>(styles.root, style);
|
|
const panelStyle = active
|
|
? visibleStyle
|
|
: StyleSheet.compose<ViewStyle, ViewStyle, ViewStyle>(visibleStyle, styles.hidden);
|
|
|
|
return (
|
|
<RetainedPanelActivity active={active}>
|
|
<View
|
|
collapsable={false}
|
|
pointerEvents={active ? "auto" : "none"}
|
|
style={panelStyle}
|
|
testID={testID}
|
|
>
|
|
{children}
|
|
</View>
|
|
</RetainedPanelActivity>
|
|
);
|
|
});
|
|
|
|
const styles = StyleSheet.create({
|
|
root: {
|
|
flex: 1,
|
|
},
|
|
hidden: {
|
|
display: "none",
|
|
},
|
|
});
|