mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-26 11:12:53 +00:00
fix(app): use popout scrolling on iPad desktop (#3992)
Fix native wide-layout model selectors mounting bottom-sheet scrolling outside a bottom sheet. Keep the scrolling policy with the model browser and cover native compact, native wide, and web surfaces.
This commit is contained in:
@@ -8,6 +8,8 @@ import { ComboboxTrigger } from "@/components/ui/combobox-trigger";
|
||||
import { LoadingSpinner } from "@/components/ui/loading-spinner";
|
||||
import { Combobox, type ComboboxOption, type ComboboxProps } from "@/components/ui/combobox";
|
||||
import { ModelBrowser, ModelProviderGlyph, useModelBrowser } from "@/components/model-browser";
|
||||
import { resolveModelBrowserScrolling } from "@/components/model-browser-view";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { isNative, isWeb } from "@/constants/platform";
|
||||
import type { ProviderSelectorProvider } from "@/provider-selection/provider-selection";
|
||||
import { ICON_SIZE, type Theme } from "@/styles/theme";
|
||||
@@ -87,6 +89,8 @@ export function CombinedModelSelector({
|
||||
toolbar,
|
||||
}: CombinedModelSelectorProps) {
|
||||
const { t } = useTranslation();
|
||||
const isCompact = useIsCompactFormFactor();
|
||||
const modelBrowserScrolling = resolveModelBrowserScrolling({ isNative, isCompact });
|
||||
const anchorRef = useRef<View>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isContentReady, setIsContentReady] = useState(isWeb);
|
||||
@@ -198,7 +202,7 @@ export function CombinedModelSelector({
|
||||
onEditProfile={onEditProfile ? handleEditProfile : undefined}
|
||||
onRetryProvider={onRetryProvider}
|
||||
isRetryingProvider={isRetryingProvider}
|
||||
scrolling={isWeb ? "independent" : "sheet"}
|
||||
scrolling={modelBrowserScrolling}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.sheetLoadingState}>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
resolveInitialModelBrowserView,
|
||||
resolveModelBrowserAllView,
|
||||
groupProfilesByProviderModel,
|
||||
resolveModelBrowserScrolling,
|
||||
} from "./model-browser-view";
|
||||
|
||||
function provider(
|
||||
@@ -37,6 +38,20 @@ function modelRow(
|
||||
};
|
||||
}
|
||||
|
||||
describe("model browser scrolling", () => {
|
||||
it("participates in native compact bottom-sheet scrolling", () => {
|
||||
expect(resolveModelBrowserScrolling({ isNative: true, isCompact: true })).toBe("sheet");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ platform: "native wide", isNative: true, isCompact: false },
|
||||
{ platform: "compact web", isNative: false, isCompact: true },
|
||||
{ platform: "wide web", isNative: false, isCompact: false },
|
||||
])("owns scrolling on $platform surfaces", ({ isNative, isCompact }) => {
|
||||
expect(resolveModelBrowserScrolling({ isNative, isCompact })).toBe("independent");
|
||||
});
|
||||
});
|
||||
|
||||
describe("model browser initial view", () => {
|
||||
const codex = provider("codex", "Codex");
|
||||
const pi = provider("pi", "Pi");
|
||||
|
||||
@@ -9,6 +9,16 @@ export type ModelBrowserView =
|
||||
| { kind: "all" }
|
||||
| { kind: "provider"; providerId: string; providerLabel: string };
|
||||
|
||||
export function resolveModelBrowserScrolling({
|
||||
isNative,
|
||||
isCompact,
|
||||
}: {
|
||||
isNative: boolean;
|
||||
isCompact: boolean;
|
||||
}): "sheet" | "independent" {
|
||||
return isNative && isCompact ? "sheet" : "independent";
|
||||
}
|
||||
|
||||
/** A profile's model reference; used to match profiles back to model rows. */
|
||||
export interface ModelProfileRef {
|
||||
provider: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ProviderSelectorProvider } from "@/provider-selection/provider-selection";
|
||||
import { resolveModelBrowserScrolling, resolveModelSheetOpening } from "./model-sheet-flow";
|
||||
import { resolveModelSheetOpening } from "./model-sheet-flow";
|
||||
|
||||
function provider(id: string, label: string): ProviderSelectorProvider {
|
||||
return { id, label, modelSelection: { kind: "models", rows: [] } };
|
||||
@@ -40,13 +40,3 @@ describe("model sheet opening", () => {
|
||||
).toEqual({ kind: "provider", providerId: "codex", providerLabel: "Codex" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("model sheet gesture ownership", () => {
|
||||
it("uses the sheet-aware model browser inside compact bottom sheets", () => {
|
||||
expect(resolveModelBrowserScrolling(true)).toBe("sheet");
|
||||
});
|
||||
|
||||
it("keeps independent scrolling in the desktop split viewport", () => {
|
||||
expect(resolveModelBrowserScrolling(false)).toBe("independent");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { ModelBrowserView } from "@/components/model-browser-view";
|
||||
import type { ProviderSelectorProvider } from "@/provider-selection/provider-selection";
|
||||
|
||||
export function resolveModelBrowserScrolling(usesBottomSheet: boolean): "sheet" | "independent" {
|
||||
return usesBottomSheet ? "sheet" : "independent";
|
||||
}
|
||||
|
||||
export function resolveModelSheetOpening({
|
||||
canSwitchProvider,
|
||||
providers,
|
||||
|
||||
@@ -9,15 +9,13 @@ import { AdaptiveModalSheet } from "@/components/adaptive-modal-sheet";
|
||||
import { ComboboxTrigger } from "@/components/ui/combobox-trigger";
|
||||
import { getProviderIcon } from "@/components/provider-icons";
|
||||
import { ModelBrowser, useModelBrowser } from "@/components/model-browser";
|
||||
import { resolveModelBrowserScrolling } from "@/components/model-browser-view";
|
||||
import { AgentControlTrigger } from "@/composer/agent-controls/control";
|
||||
import { ComposerToolbarGlyph } from "@/composer/agent-controls/glyph";
|
||||
import {
|
||||
resolveModelBrowserScrolling,
|
||||
resolveModelSheetOpening,
|
||||
} from "@/composer/agent-controls/model-sheet-flow";
|
||||
import { resolveModelSheetOpening } from "@/composer/agent-controls/model-sheet-flow";
|
||||
import type { ProviderSelectorProvider } from "@/provider-selection/provider-selection";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { isWeb } from "@/constants/platform";
|
||||
import { isNative, isWeb } from "@/constants/platform";
|
||||
|
||||
const SNAP_POINTS = ["80%", "90%"];
|
||||
const MODEL_LIST_TOP_INSET = 4;
|
||||
@@ -102,7 +100,10 @@ export function CompactModelSheet({
|
||||
}: CompactModelSheetProps) {
|
||||
const { t } = useTranslation();
|
||||
const usesBottomSheet = useIsCompactFormFactor();
|
||||
const modelBrowserScrolling = resolveModelBrowserScrolling(usesBottomSheet);
|
||||
const modelBrowserScrolling = resolveModelBrowserScrolling({
|
||||
isNative,
|
||||
isCompact: usesBottomSheet,
|
||||
});
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isModelBrowserOpen, setIsModelBrowserOpen] = useState(false);
|
||||
const availableProviders = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user