diff --git a/apps/mobile/src/features/threads/ThreadSettingsRows.android.tsx b/apps/mobile/src/features/threads/ThreadSettingsRows.android.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..98b414ef55d3c2c74b4684a105a24d15229887a3
--- /dev/null
+++ b/apps/mobile/src/features/threads/ThreadSettingsRows.android.tsx
@@ -0,0 +1,29 @@
+import { MaterialRadioIndicator } from "../../components/MaterialRadioIndicator";
+import {
+ ModelRowContent,
+ ChoiceRowContent,
+ type ModelRowProps,
+ type ChoiceRowProps,
+} from "./ThreadSettingsRows.shared";
+
+export function ModelRow(props: ModelRowProps) {
+ return (
+ }
+ />
+ );
+}
+
+export function ChoiceRow(props: ChoiceRowProps) {
+ return (
+ }
+ />
+ );
+}
diff --git a/apps/mobile/src/features/threads/ThreadSettingsRows.shared.tsx b/apps/mobile/src/features/threads/ThreadSettingsRows.shared.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..70a66704cdd37c998c12b5e062ca7abe7d2b8b27
--- /dev/null
+++ b/apps/mobile/src/features/threads/ThreadSettingsRows.shared.tsx
@@ -0,0 +1,140 @@
+import type { ReactNode } from "react";
+import { Pressable, View } from "react-native";
+import { AppText as Text } from "../../components/AppText";
+import { SymbolView } from "../../components/AppSymbol";
+import { cn } from "../../lib/cn";
+import type { ModelOption } from "../../lib/modelOptions";
+
+export type ModelRowProps = {
+ readonly option: ModelOption;
+ readonly selected: boolean;
+ readonly onPress: () => void;
+ readonly isFavorite: boolean;
+ readonly favoritesLoaded: boolean;
+ readonly onToggleFavorite: () => void;
+ readonly isFirst: boolean;
+ readonly isLast: boolean;
+};
+
+export type ChoiceRowProps = {
+ readonly label: string;
+ readonly description?: string;
+ readonly selected: boolean;
+ readonly onPress: () => void;
+ readonly isLast: boolean;
+};
+
+type RowSelectionProps = {
+ readonly leadingSelection?: ReactNode;
+ readonly trailingSelection?: ReactNode;
+ readonly minimumHeight?: number;
+};
+
+export function ModelRowContent(
+ props: ModelRowProps &
+ RowSelectionProps & {
+ readonly labelNumberOfLines: number;
+ readonly selectedClassName?: string;
+ },
+) {
+ return (
+
+
+ {props.leadingSelection}
+
+
+
+ {props.option.label}
+
+ {props.option.isDefault ? (
+
+ Default
+
+ ) : null}
+ {props.option.isLegacy ? (
+
+ Legacy
+
+ ) : null}
+ {props.option.isUnavailable ? (
+ Unavailable
+ ) : null}
+
+ {props.option.subtitle ? (
+
+ {props.option.subtitle}
+
+ ) : null}
+
+ {props.trailingSelection}
+
+
+
+
+
+ );
+}
+
+/** Single option inside a submenu panel. */
+export function ChoiceRowContent(props: ChoiceRowProps & RowSelectionProps) {
+ return (
+
+ {props.leadingSelection}
+
+ {props.label}
+ {props.description ? (
+ {props.description}
+ ) : null}
+
+ {props.trailingSelection}
+
+ );
+}
diff --git a/apps/mobile/src/features/threads/ThreadSettingsRows.tsx b/apps/mobile/src/features/threads/ThreadSettingsRows.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..016be4cb9c16de07b0d898adfe549612b46ad81f
--- /dev/null
+++ b/apps/mobile/src/features/threads/ThreadSettingsRows.tsx
@@ -0,0 +1,38 @@
+import { SymbolView } from "../../components/AppSymbol";
+import {
+ ModelRowContent,
+ ChoiceRowContent,
+ type ModelRowProps,
+ type ChoiceRowProps,
+} from "./ThreadSettingsRows.shared";
+
+function SelectedCheckmark(props: { readonly selected: boolean }) {
+ return props.selected ? (
+
+ ) : null;
+}
+
+export function ModelRow(props: ModelRowProps) {
+ return (
+ }
+ />
+ );
+}
+
+export function ChoiceRow(props: ChoiceRowProps) {
+ return (
+ }
+ />
+ );
+}
diff --git a/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx b/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx
index 6c0218d453f5125c3863b5adab06621950ec871c..9dbefe6018b7683cc3f0d1a5a19a29be07a99f12 100644
--- a/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx
+++ b/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx
@@ -40,7 +40,6 @@ import { AppText as Text } from "../../components/AppText";
import { AndroidScreenHeader } from "../../components/AndroidScreenHeader";
import { MaterialButton } from "../../components/MaterialButton";
import { MaterialIconButton } from "../../components/MaterialIconButton";
-import { MaterialRadioIndicator } from "../../components/MaterialRadioIndicator";
import { ProviderIcon } from "../../components/ProviderIcon";
import { ThemedSwitch } from "../../components/ThemedSwitch";
import { cn } from "../../lib/cn";
@@ -69,6 +68,7 @@ import {
NATIVE_MAIL_SEARCH_TOOLBAR_CONTENT_INSET,
NATIVE_MAIL_SEARCH_TOOLBAR_SUPPORTED,
} from "../layout/native-mail-search-toolbar";
+import { ModelRow, ChoiceRow } from "./ThreadSettingsRows";
import { RUNTIME_MODE_CHOICES, selectableChoices } from "./thread-settings-options";
import {
canCommitPendingModel,
@@ -113,101 +113,6 @@ const EMPTY_MODEL_FAVORITES: ReadonlyArray<{
readonly model: string;
}> = [];
const FAVORITES_PROVIDER_FILTER = "@favorites";
-function ModelRow(props: {
- readonly option: ModelOption;
- readonly selected: boolean;
- readonly onPress: () => void;
- readonly isFavorite: boolean;
- readonly favoritesLoaded: boolean;
- readonly onToggleFavorite: () => void;
- readonly isFirst: boolean;
- readonly isLast: boolean;
-}) {
- const selectedMaterialRow = Platform.OS === "android" && props.selected;
- return (
-
-
- {Platform.OS === "android" ? : null}
-
-
-
- {props.option.label}
-
- {props.option.isDefault ? (
-
- Default
-
- ) : null}
- {props.option.isLegacy ? (
-
- Legacy
-
- ) : null}
- {props.option.isUnavailable ? (
- Unavailable
- ) : null}
-
- {props.option.subtitle ? (
-
- {props.option.subtitle}
-
- ) : null}
-
- {props.selected && Platform.OS !== "android" ? (
-
- ) : null}
-
-
-
-
-
- );
-}
-
/** Provider catalog header with its harness logo and disclosure state. */
function ProviderHeader(props: {
readonly driver: string | undefined;
@@ -296,46 +201,6 @@ function DisclosureRow(props: {
);
}
-/** Single option inside a submenu panel. */
-function ChoiceRow(props: {
- readonly label: string;
- readonly description?: string;
- readonly selected: boolean;
- readonly onPress: () => void;
- readonly isLast: boolean;
-}) {
- return (
-
- {Platform.OS === "android" ? : null}
-
- {props.label}
- {props.description ? (
- {props.description}
- ) : null}
-
- {props.selected && Platform.OS !== "android" ? (
-
- ) : null}
-
- );
-}
-
function SwitchRow(props: {
readonly label: string;
readonly value: boolean;