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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ __screenshots__/
squashfs-root/
.vercel
.gstack/
.worktrees/
dist-electron/
.electron-runtime/
.showcase/
Expand Down
8 changes: 8 additions & 0 deletions apps/mobile/src/components/ProviderIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ export function ProviderIcon(props: ProviderIconProps) {
);
}

if (props.provider === "kimi") {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<Path fill={mono} d="M20.5 14.3A8.5 8.5 0 0 1 9.7 3.5 9 9 0 1 0 20.5 14.3Z" />
</Svg>
);
}

if (props.provider === "opencode") {
return (
<Svg width={size} height={size} viewBox="0 0 32 40" fill="none">
Expand Down
9 changes: 9 additions & 0 deletions apps/mobile/src/features/threads/ThreadSettingsSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ function ModelRow(props: {
function ProviderHeader(props: {
readonly driver: string | undefined;
readonly label: string;
readonly badgeLabel: string | undefined;
readonly collapsible: boolean;
readonly collapsed: boolean;
readonly modelCount: number;
Expand All @@ -148,6 +149,11 @@ function ProviderHeader(props: {
<>
<ProviderIcon provider={props.driver} size={15} />
<Text className="text-sm font-t3-medium text-foreground-muted">{props.label}</Text>
{props.badgeLabel ? (
<Text className="rounded-full bg-surface-raised px-2 py-0.5 text-3xs font-t3-medium uppercase text-foreground-muted">
{props.badgeLabel}
</Text>
) : null}
{props.collapsible ? (
<>
<View className="flex-1" />
Expand Down Expand Up @@ -512,6 +518,7 @@ type ThreadSettingsProviderCatalog = {
readonly key: string;
readonly driver: string | undefined;
readonly label: string;
readonly badgeLabel: string | undefined;
readonly collapsible: boolean;
readonly collapsed: boolean;
readonly modelCount: number;
Expand Down Expand Up @@ -577,6 +584,7 @@ function ThreadSettingsProviderListHeader(props: {
collapsed={props.provider.collapsed}
driver={props.provider.driver}
label={props.provider.label}
badgeLabel={props.provider.badgeLabel}
modelCount={props.provider.modelCount}
onToggle={onToggle}
/>
Expand Down Expand Up @@ -622,6 +630,7 @@ function useThreadSettingsCatalogItems(
key: group.providerKey,
driver,
label: group.providerLabel,
badgeLabel: group.providerBadgeLabel,
collapsible,
collapsed,
modelCount: visibleModels.length,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function modelOption(
providerKey: "codex",
providerLabel: "Codex",
providerDriver: "codex",
providerBadgeLabel: undefined,
isDefault: false,
isLegacy: false,
capabilities: null,
Expand Down
29 changes: 29 additions & 0 deletions apps/mobile/src/lib/modelOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,35 @@ import {
} from "./modelOptions";

describe("mobile model options", () => {
it("uses the Kimi product name when a snapshot has no display name", () => {
const config = {
providers: [
{
instanceId: "kimi",
driver: "kimi",
badgeLabel: "Early Access",
enabled: true,
installed: true,
auth: { status: "authenticated" },
models: [
{
slug: "kimi-dynamic-model",
name: "Kimi Dynamic Model",
isCustom: false,
capabilities: null,
},
],
},
],
} as unknown as ServerConfig;

expect(groupByProvider(buildModelOptions(config, null))[0]).toMatchObject({
providerLabel: "Kimi",
providerBadgeLabel: "Early Access",
models: [{ providerBadgeLabel: "Early Access" }],
});
});

it("groups models by provider and flags legacy entries", () => {
const config = {
providers: [
Expand Down
16 changes: 15 additions & 1 deletion apps/mobile/src/lib/modelOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ModelOption = {
readonly providerKey: string;
readonly providerLabel: string;
readonly providerDriver: string;
readonly providerBadgeLabel: string | undefined;
readonly isDefault: boolean;
readonly isLegacy: boolean;
readonly capabilities: ModelCapabilities | null;
Expand All @@ -24,6 +25,7 @@ export type ModelOption = {
export type ProviderGroup = {
readonly providerKey: string;
readonly providerLabel: string;
readonly providerBadgeLabel: string | undefined;
readonly models: ReadonlyArray<ModelOption>;
};

Expand All @@ -35,6 +37,7 @@ function providerDisplayLabel(provider: {
if (provider.displayName) return provider.displayName;
if (provider.driver === "codex") return "Codex";
if (provider.driver === "claudeAgent") return "Claude";
if (provider.driver === "kimi") return "Kimi";
return provider.instanceId;
}

Expand Down Expand Up @@ -125,6 +128,7 @@ export function buildModelOptions(
providerKey: provider.instanceId,
providerLabel,
providerDriver: provider.driver,
providerBadgeLabel: provider.badgeLabel,
isDefault: model.isDefault === true,
isLegacy: model.isLegacy === true,
capabilities: model.capabilities,
Expand Down Expand Up @@ -156,6 +160,7 @@ export function buildModelOptions(
providerKey: fallbackModelSelection.instanceId,
providerLabel,
providerDriver: fallbackModelSelection.instanceId,
providerBadgeLabel: undefined,
isDefault: false,
isLegacy: false,
capabilities: null,
Expand All @@ -168,14 +173,22 @@ export function buildModelOptions(
}

export function groupByProvider(options: ReadonlyArray<ModelOption>): ReadonlyArray<ProviderGroup> {
const groups = new Map<string, { providerLabel: string; models: ModelOption[] }>();
const groups = new Map<
string,
{
providerLabel: string;
providerBadgeLabel: string | undefined;
models: ModelOption[];
}
>();
for (const option of options) {
const existing = groups.get(option.providerKey);
if (existing) {
existing.models.push(option);
} else {
groups.set(option.providerKey, {
providerLabel: option.providerLabel,
providerBadgeLabel: option.providerBadgeLabel,
models: [option],
});
}
Expand All @@ -184,6 +197,7 @@ export function groupByProvider(options: ReadonlyArray<ModelOption>): ReadonlyAr
return [...groups.entries()].map(([providerKey, group]) => ({
providerKey,
providerLabel: group.providerLabel,
providerBadgeLabel: group.providerBadgeLabel,
models: group.models,
}));
}
Loading
Loading