authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-05 22:13:35-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 23:00:41-08:00
log116c8c5e8e0db2fe7fad070c2f67738a62f93546
treeec6b3428610593479a94c4d56c0cd4d354e63e62
parentf8886e874d43fe591fa50d28e9a8983632ba7e45

rm some aliases


6 files changed, 40 insertions(+), 70 deletions(-)

apps/web/src/components/ChatView.tsx+12-17
......@@ -1,6 +1,6 @@
11import {
22 type ApprovalRequestId,
3 DEFAULT_MODEL,
3 DEFAULT_MODEL_BY_PROVIDER,
44 EDITORS,
55 type EditorId,
66 type KeybindingCommand,
......@@ -575,7 +575,9 @@ export default function ChatView({ threadId }: ChatViewProps) {
575575 const setComposerDraftProvider = useComposerDraftStore((store) => store.setProvider);
576576 const setComposerDraftModel = useComposerDraftStore((store) => store.setModel);
577577 const setComposerDraftRuntimeMode = useComposerDraftStore((store) => store.setRuntimeMode);
578 const setComposerDraftInteractionMode = useComposerDraftStore((store) => store.setInteractionMode);
578 const setComposerDraftInteractionMode = useComposerDraftStore(
579 (store) => store.setInteractionMode,
580 );
579581 const setComposerDraftEffort = useComposerDraftStore((store) => store.setEffort);
580582 const setComposerDraftCodexFastMode = useComposerDraftStore((store) => store.setCodexFastMode);
581583 const addComposerDraftImage = useComposerDraftStore((store) => store.addImage);
......@@ -703,14 +705,15 @@ export default function ChatView({ threadId }: ChatViewProps) {
703705 ? buildLocalDraftThread(
704706 threadId,
705707 draftThread,
706 fallbackDraftProject?.model ?? DEFAULT_MODEL,
708 fallbackDraftProject?.model ?? DEFAULT_MODEL_BY_PROVIDER.codex,
707709 localDraftError,
708710 )
709711 : undefined,
710712 [draftThread, fallbackDraftProject?.model, localDraftError, threadId],
711713 );
712714 const activeThread = serverThread ?? localDraftThread;
713 const runtimeMode = composerDraft.runtimeMode ?? activeThread?.runtimeMode ?? DEFAULT_RUNTIME_MODE;
715 const runtimeMode =
716 composerDraft.runtimeMode ?? activeThread?.runtimeMode ?? DEFAULT_RUNTIME_MODE;
714717 const interactionMode =
715718 composerDraft.interactionMode ?? activeThread?.interactionMode ?? DEFAULT_INTERACTION_MODE;
716719 const isServerThread = serverThread !== undefined;
......@@ -757,10 +760,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
757760 const selectedProvider: ProviderKind = lockedProvider ?? selectedProviderByThreadId ?? "codex";
758761 const baseThreadModel = resolveModelSlugForProvider(
759762 selectedProvider,
760 activeThread?.model ??
761 activeProject?.model ??
762 getDefaultModel(selectedProvider) ??
763 DEFAULT_MODEL,
763 activeThread?.model ?? activeProject?.model ?? getDefaultModel(selectedProvider),
764764 );
765765 const selectedModel = useMemo(() => {
766766 const draftModel = composerDraft.model;
......@@ -2470,7 +2470,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
24702470 }
24712471 const title = truncateTitle(titleSeed);
24722472 let threadCreateModel: ModelSlug =
2473 selectedModel || (activeProject.model as ModelSlug) || DEFAULT_MODEL;
2473 selectedModel || (activeProject.model as ModelSlug) || DEFAULT_MODEL_BY_PROVIDER.codex;
24742474
24752475 if (isLocalDraftThread) {
24762476 await api.orchestration.dispatchCommand({
......@@ -2881,7 +2881,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
28812881 selectedModel ||
28822882 (activeThread.model as ModelSlug) ||
28832883 (activeProject.model as ModelSlug) ||
2884 DEFAULT_MODEL;
2884 DEFAULT_MODEL_BY_PROVIDER.codex;
28852885
28862886 sendInFlightRef.current = true;
28872887 setSendPhase("sending-turn");
......@@ -5127,9 +5127,7 @@ const MessagesTimeline = memo(function MessagesTimeline({
51275127 );
51285128});
51295129
5130function isAvailableProviderOption(
5131 option: (typeof PROVIDER_OPTIONS)[number],
5132): option is {
5130function isAvailableProviderOption(option: (typeof PROVIDER_OPTIONS)[number]): option is {
51335131 value: ProviderKind;
51345132 label: string;
51355133 available: true;
......@@ -5227,10 +5225,7 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {
52275225 }
52285226 >
52295227 <span className="flex min-w-0 items-center gap-2">
5230 <ProviderIcon
5231 aria-hidden="true"
5232 className="size-4 shrink-0 text-muted-foreground/70"
5233 />
5228 <ProviderIcon aria-hidden="true" className="size-4 shrink-0 text-muted-foreground/70" />
52345229 <span className="truncate">{selectedModelLabel}</span>
52355230 <ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />
52365231 </span>
apps/web/src/components/Sidebar.tsx+3-3
......@@ -9,7 +9,7 @@ import {
99import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1010import {
1111 DEFAULT_RUNTIME_MODE,
12 DEFAULT_MODEL,
12 DEFAULT_MODEL_BY_PROVIDER,
1313 type DesktopUpdateState,
1414 ProjectId,
1515 ThreadId,
......@@ -506,7 +506,7 @@ export default function Sidebar() {
506506 projectId,
507507 title,
508508 workspaceRoot: cwd,
509 defaultModel: DEFAULT_MODEL,
509 defaultModel: DEFAULT_MODEL_BY_PROVIDER.codex,
510510 createdAt,
511511 })
512512 .then(() => true)
......@@ -885,7 +885,7 @@ export default function Sidebar() {
885885 ? "text-sky-400"
886886 : shouldHighlightDesktopUpdateError(desktopUpdateState)
887887 ? "text-rose-500 animate-pulse"
888 : "text-amber-500 animate-pulse";
888 : "text-amber-500 animate-pulse";
889889 const newThreadShortcutLabel = useMemo(
890890 () =>
891891 shortcutLabelForCommand(keybindings, "chat.newLocal") ??
apps/web/src/store.ts+14-35
......@@ -1,6 +1,6 @@
11import { Fragment, type ReactNode, createElement, useEffect } from "react";
22import {
3 DEFAULT_MODEL,
3 DEFAULT_MODEL_BY_PROVIDER,
44 type ProviderKind,
55 ThreadId,
66 type OrchestrationReadModel,
......@@ -13,11 +13,7 @@ import {
1313 resolveModelSlugForProvider,
1414} from "@t3tools/shared/model";
1515import { create } from "zustand";
16import {
17 type ChatMessage,
18 type Project,
19 type Thread,
20} from "./types";
16import { type ChatMessage, type Project, type Thread } from "./types";
2117
2218// ── State ────────────────────────────────────────────────────────────
2319
......@@ -114,7 +110,9 @@ function mapProjectsFromReadModel(
114110 id: project.id,
115111 name: project.title,
116112 cwd: project.workspaceRoot,
117 model: existing?.model ?? resolveModelSlug(project.defaultModel ?? DEFAULT_MODEL),
113 model:
114 existing?.model ??
115 resolveModelSlug(project.defaultModel ?? DEFAULT_MODEL_BY_PROVIDER.codex),
118116 expanded:
119117 existing?.expanded ??
120118 (persistedExpandedProjectCwds.size > 0
......@@ -181,11 +179,7 @@ function resolveWsHttpOrigin(): string {
181179 try {
182180 const wsUrl = new URL(wsCandidate);
183181 const protocol =
184 wsUrl.protocol === "wss:"
185 ? "https:"
186 : wsUrl.protocol === "ws:"
187 ? "http:"
188 : wsUrl.protocol;
182 wsUrl.protocol === "wss:" ? "https:" : wsUrl.protocol === "ws:" ? "http:" : wsUrl.protocol;
189183 return `${protocol}//${wsUrl.host}`;
190184 } catch {
191185 return window.location.origin;
......@@ -205,17 +199,12 @@ function attachmentPreviewRoutePath(attachmentId: string): string {
205199
206200// ── Pure state transition functions ────────────────────────────────────
207201
208export function syncServerReadModel(
209 state: AppState,
210 readModel: OrchestrationReadModel,
211): AppState {
202export function syncServerReadModel(state: AppState, readModel: OrchestrationReadModel): AppState {
212203 const projects = mapProjectsFromReadModel(
213204 readModel.projects.filter((project) => project.deletedAt === null),
214205 state.projects,
215206 );
216 const existingThreadById = new Map(
217 state.threads.map((thread) => [thread.id, thread] as const),
218 );
207 const existingThreadById = new Map(state.threads.map((thread) => [thread.id, thread] as const));
219208 const threads = readModel.threads
220209 .filter((thread) => thread.deletedAt === null)
221210 .map((thread) => {
......@@ -334,9 +323,7 @@ export function markThreadUnread(state: AppState, threadId: ThreadId): AppState
334323export function toggleProject(state: AppState, projectId: Project["id"]): AppState {
335324 return {
336325 ...state,
337 projects: state.projects.map((p) =>
338 p.id === projectId ? { ...p, expanded: !p.expanded } : p,
339 ),
326 projects: state.projects.map((p) => (p.id === projectId ? { ...p, expanded: !p.expanded } : p)),
340327 };
341328}
342329
......@@ -390,27 +377,19 @@ interface AppStore extends AppState {
390377 toggleProject: (projectId: Project["id"]) => void;
391378 setProjectExpanded: (projectId: Project["id"], expanded: boolean) => void;
392379 setError: (threadId: ThreadId, error: string | null) => void;
393 setThreadBranch: (
394 threadId: ThreadId,
395 branch: string | null,
396 worktreePath: string | null,
397 ) => void;
380 setThreadBranch: (threadId: ThreadId, branch: string | null, worktreePath: string | null) => void;
398381}
399382
400383export const useStore = create<AppStore>((set) => ({
401384 ...readPersistedState(),
402 syncServerReadModel: (readModel) =>
403 set((state) => syncServerReadModel(state, readModel)),
385 syncServerReadModel: (readModel) => set((state) => syncServerReadModel(state, readModel)),
404386 markThreadVisited: (threadId, visitedAt) =>
405387 set((state) => markThreadVisited(state, threadId, visitedAt)),
406 markThreadUnread: (threadId) =>
407 set((state) => markThreadUnread(state, threadId)),
408 toggleProject: (projectId) =>
409 set((state) => toggleProject(state, projectId)),
388 markThreadUnread: (threadId) => set((state) => markThreadUnread(state, threadId)),
389 toggleProject: (projectId) => set((state) => toggleProject(state, projectId)),
410390 setProjectExpanded: (projectId, expanded) =>
411391 set((state) => setProjectExpanded(state, projectId, expanded)),
412 setError: (threadId, error) =>
413 set((state) => setError(state, threadId, error)),
392 setError: (threadId, error) => set((state) => setError(state, threadId, error)),
414393 setThreadBranch: (threadId, branch, worktreePath) =>
415394 set((state) => setThreadBranch(state, threadId, branch, worktreePath)),
416395}));
packages/contracts/src/model.ts+2-5
......@@ -29,18 +29,15 @@ export const MODEL_OPTIONS_BY_PROVIDER = {
2929 { slug: "gpt-5.2", name: "GPT-5.2" },
3030 ],
3131} as const satisfies Record<ProviderKind, readonly ModelOption[]>;
32export type ModelOptionsByProvider = typeof MODEL_OPTIONS_BY_PROVIDER;
3233
33type BuiltInModelSlug = (typeof MODEL_OPTIONS_BY_PROVIDER)[ProviderKind][number]["slug"];
34type BuiltInModelSlug = ModelOptionsByProvider[ProviderKind][number]["slug"];
3435export type ModelSlug = BuiltInModelSlug | (string & {});
3536
3637export const DEFAULT_MODEL_BY_PROVIDER = {
3738 codex: "gpt-5.4",
3839} as const satisfies Record<ProviderKind, ModelSlug>;
3940
40// Backward compatibility for existing Codex-only call sites.
41export const MODEL_OPTIONS = MODEL_OPTIONS_BY_PROVIDER.codex;
42export const DEFAULT_MODEL = DEFAULT_MODEL_BY_PROVIDER.codex;
43
4441export const MODEL_SLUG_ALIASES_BY_PROVIDER = {
4542 codex: {
4643 "5.4": "gpt-5.4",
packages/shared/src/model.test.ts+8-8
......@@ -1,5 +1,5 @@
11import { describe, expect, it } from "vitest";
2import { DEFAULT_MODEL, MODEL_OPTIONS } from "@t3tools/contracts";
2import { DEFAULT_MODEL_BY_PROVIDER, MODEL_OPTIONS_BY_PROVIDER } from "@t3tools/contracts";
33
44import {
55 getDefaultModel,
......@@ -31,23 +31,23 @@ describe("normalizeModelSlug", () => {
3131
3232describe("resolveModelSlug", () => {
3333 it("returns default only when the model is missing", () => {
34 expect(resolveModelSlug(undefined)).toBe(DEFAULT_MODEL);
35 expect(resolveModelSlug(null)).toBe(DEFAULT_MODEL);
34 expect(resolveModelSlug(undefined)).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
35 expect(resolveModelSlug(null)).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
3636 });
3737
3838 it("preserves unknown custom models", () => {
39 expect(resolveModelSlug("gpt-4.1")).toBe(DEFAULT_MODEL);
40 expect(resolveModelSlug("custom/internal-model")).toBe(DEFAULT_MODEL);
39 expect(resolveModelSlug("gpt-4.1")).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
40 expect(resolveModelSlug("custom/internal-model")).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
4141 });
4242
4343 it("resolves only supported model options", () => {
44 for (const model of MODEL_OPTIONS) {
44 for (const model of MODEL_OPTIONS_BY_PROVIDER.codex) {
4545 expect(resolveModelSlug(model.slug)).toBe(model.slug);
4646 }
4747 });
4848 it("keeps codex defaults for backward compatibility", () => {
49 expect(getDefaultModel()).toBe(DEFAULT_MODEL);
50 expect(getModelOptions()).toEqual(MODEL_OPTIONS);
49 expect(getDefaultModel()).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
50 expect(getModelOptions()).toEqual(MODEL_OPTIONS_BY_PROVIDER.codex);
5151 });
5252});
5353
packages/shared/src/model.ts+1-2
......@@ -1,6 +1,5 @@
11import {
22 CODEX_REASONING_EFFORT_OPTIONS,
3 DEFAULT_MODEL,
43 DEFAULT_MODEL_BY_PROVIDER,
54 MODEL_OPTIONS_BY_PROVIDER,
65 MODEL_SLUG_ALIASES_BY_PROVIDER,
......@@ -20,7 +19,7 @@ export function getModelOptions(provider: ProviderKind = "codex") {
2019}
2120
2221export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug {
23 return DEFAULT_MODEL_BY_PROVIDER[provider] ?? DEFAULT_MODEL;
22 return DEFAULT_MODEL_BY_PROVIDER[provider];
2423}
2524
2625export function normalizeModelSlug(