Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions apps/mobile/src/components/ComposerModelControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { ModelSelection } from "@t3tools/contracts";

import type { ModelOption } from "../lib/modelOptions";
import {
composerModelOptionSummary,
resolveProviderOptionDescriptors,
} from "../lib/providerOptions";
import { AppText as Text } from "./ui/AppText";
import { SymbolView } from "./ui/AppSymbol";
import { ComposerInlineControl } from "./ComposerToolbar";
import { ProviderIcon } from "./ProviderIcon";
import { useAndroidControlSizing } from "./ui/useAndroidControlSizing";

/** Keeps option indicators fixed while the model name yields space in a narrow toolbar. */
export function ComposerModelControl(props: {
readonly modelOption: ModelOption | null | undefined;
readonly selection: ModelSelection | null;
readonly reportedSelection?: ModelSelection | null | undefined;
readonly disabled?: boolean;
readonly onPress: () => void;
}) {
const { scale } = useAndroidControlSizing();
const label = props.modelOption?.label ?? props.selection?.model ?? "Choose model";
const { reasoningLabel, speedLabel } = composerModelOptionSummary({
provider: props.modelOption?.providerDriver,
descriptors: resolveProviderOptionDescriptors({
capabilities: props.modelOption?.capabilities,
selections: props.selection?.options,
}),
selection: props.selection,
reportedSelection: props.reportedSelection,
});
return (
<ComposerInlineControl
accessibilityLabel={[
"Model and reasoning settings",
label,
reasoningLabel ? `Reasoning: ${reasoningLabel}` : null,
speedLabel ? `${speedLabel} mode on` : null,
]
.filter(Boolean)
.join(", ")}
disabled={props.disabled}
emphasized
renderIcon={(size) => (
<ProviderIcon
iconUrl={props.modelOption?.providerIconUrl}
provider={props.modelOption?.providerDriver}
size={size}
/>
)}
label={label}
suffix={
reasoningLabel || speedLabel ? (
<>
{reasoningLabel ? (
<Text className="text-2xs text-foreground-muted" numberOfLines={1}>
{reasoningLabel}
</Text>
) : null}
{speedLabel ? (
<SymbolView
name={{ ios: "bolt", android: "bolt" }}
size={Math.round(12 * scale)}
tintColorClassName="accent-icon"
type="monochrome"
/>
) : null}
</>
) : undefined
}
maxWidth="100%"
onPress={props.onPress}
/>
);
}
4 changes: 4 additions & 0 deletions apps/mobile/src/components/ComposerToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function ComposerInlineControl(props: {
readonly icon?: ComponentProps<typeof SymbolView>["name"];
readonly renderIcon?: (size: number) => ReactNode;
readonly label: string;
readonly suffix?: ReactNode;
readonly maxWidth?: ViewStyle["maxWidth"];
readonly onPress?: () => void;
readonly selected?: boolean;
Expand Down Expand Up @@ -77,6 +78,9 @@ export function ComposerInlineControl(props: {
>
{props.label}
</Text>
{props.suffix ? (
<View className="shrink-0 flex-row items-center gap-1">{props.suffix}</View>
) : null}
{props.showChevron === false ? null : (
<SymbolView
name={props.chevronDirection === "right" ? "chevron.right" : "chevron.down"}
Expand Down
21 changes: 6 additions & 15 deletions apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ import {
} from "../../state/composer-attachment-uploads";
import { FilePreviewModal, type FilePreviewSource } from "../../components/FilePreviewModal";
import { VideoPreviewModal, type VideoPreviewSource } from "../../components/VideoPreviewModal";
import { ProviderIcon } from "../../components/ProviderIcon";
import { ComposerModelControl } from "../../components/ComposerModelControl";
import { SymbolView } from "../../components/ui/AppSymbol";
import { AppText as Text } from "../../components/ui/AppText";
import { hasProviderUsageLimits, isUsageLimitsCommand } from "@t3tools/shared/usageLimits";
Expand Down Expand Up @@ -1713,21 +1713,12 @@ export function NewTaskDraftScreen(props: {
onPickMedia={handlePickMedia}
onPickFiles={handlePickFiles}
/>
<View className="min-w-0 flex-1 flex-row items-center justify-end gap-2">
<View className="min-w-0 shrink">
<ComposerInlineControl
accessibilityLabel="Model and reasoning settings"
<View className="min-w-0 flex-1 flex-row flex-wrap items-center justify-end gap-2">
<View className="min-w-0 shrink" style={{ flexBasis: 190 }}>
<ComposerModelControl
disabled={isComposerInteractionLocked}
emphasized
renderIcon={(size) => (
<ProviderIcon
iconUrl={flow.selectedModelOption?.providerIconUrl}
provider={flow.selectedModelOption?.providerDriver}
size={size}
/>
)}
label={flow.selectedModelOption?.label ?? "Choose model"}
maxWidth="100%"
modelOption={flow.selectedModelOption}
selection={flow.selectedModel}
onPress={settingsSheetPresentation.open}
/>
</View>
Expand Down
24 changes: 6 additions & 18 deletions apps/mobile/src/features/threads/ThreadComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,8 @@ import { VideoPreviewModal, type VideoPreviewSource } from "../../components/Vid
import { GlassSurface } from "../../components/ui/GlassSurface";
import { ComposerEditor, type ComposerEditorHandle } from "../../components/ComposerEditor";
import { fileRoutePathSegments } from "../files/filePath";
import {
ComposerActionButton,
ComposerInlineControl,
ComposerToolbarRow,
} from "../../components/ComposerToolbar";
import { ProviderIcon } from "../../components/ProviderIcon";
import { ComposerActionButton, ComposerToolbarRow } from "../../components/ComposerToolbar";
import { ComposerModelControl } from "../../components/ComposerModelControl";
import {
composerStripAttachments,
type DraftComposerFileAttachment,
Expand Down Expand Up @@ -1059,18 +1055,10 @@ export const ThreadComposer = function ThreadComposer(props: ThreadComposerProps
onPickFiles={props.onPickDraftFiles}
/>
<View className="min-w-0 shrink">
<ComposerInlineControl
accessibilityLabel="Model and reasoning settings"
emphasized
renderIcon={(size) => (
<ProviderIcon
iconUrl={currentModelOption?.providerIconUrl}
provider={currentModelOption?.providerDriver}
size={size}
/>
)}
label={currentModelOption?.label ?? currentModelSelection.model}
maxWidth="100%"
<ComposerModelControl
modelOption={currentModelOption}
selection={currentModelSelection}
reportedSelection={props.reportedModelSelection}
onPress={openSettings}
/>
</View>
Expand Down
Loading
Loading