feat(settings): reorganize the settings dialog and the model settings

Tabs Models, General, Drawing and About, each with a fixed title row.

The models tab becomes three pages: the user's providers with one status
each and the server's models; a searchable picker of all 24 providers in
four groups; and a provider page in three steps (connection, models,
test). A new provider gets its first suggested model, and a passing test
offers to use the model in the chat. Bedrock asks for one way of signing
in, with an optional session token for temporary access keys.

The composer's model picker lists every model, marking untested and
failed ones, and can open the provider picker. A chat error that offers
model settings opens the provider the request went to.
This commit is contained in:
dayuan.jiang
2026-10-08 17:44:08 +09:00
parent c513d1a0c2
commit 69fb66804d
32 changed files with 4237 additions and 1730 deletions
+12 -3
View File
@@ -1,6 +1,8 @@
import { create } from "zustand"
export type SettingsTab = "models" | "appearance" | "advanced" | "about"
export type SettingsTab = "models" | "general" | "drawing" | "about"
/** The models tab: its provider list, the provider picker or a provider */
export type ModelsPage = "list" | "picker" | { providerId: string }
export type MobileView = "canvas" | "chat"
interface UiState {
@@ -12,6 +14,7 @@ interface UiState {
mobileView: MobileView
settingsOpen: boolean
settingsTab: SettingsTab
modelsPage: ModelsPage
/** Version shown in the compare dialog, or null when closed */
compareVersionId: string | null
saveDialogOpen: boolean
@@ -21,9 +24,12 @@ interface UiState {
setHeroDismissed: (dismissed: boolean) => void
togglePanel: () => void
setMobileView: (view: MobileView) => void
openSettings: (tab?: SettingsTab) => void
/** Opens on the given tab (else the last one); the models tab on its
* list unless a page is given */
openSettings: (tab?: SettingsTab, modelsPage?: ModelsPage) => void
setSettingsOpen: (open: boolean) => void
setSettingsTab: (tab: SettingsTab) => void
setModelsPage: (page: ModelsPage) => void
openCompare: (versionId: string) => void
closeCompare: () => void
setSaveDialogOpen: (open: boolean) => void
@@ -36,6 +42,7 @@ export const useUiStore = create<UiState>((set) => ({
mobileView: "chat",
settingsOpen: false,
settingsTab: "models",
modelsPage: "list",
compareVersionId: null,
saveDialogOpen: false,
focusComposerToken: 0,
@@ -43,13 +50,15 @@ export const useUiStore = create<UiState>((set) => ({
setHeroDismissed: (dismissed) => set({ heroDismissed: dismissed }),
togglePanel: () => set((state) => ({ panelOpen: !state.panelOpen })),
setMobileView: (view) => set({ mobileView: view }),
openSettings: (tab) =>
openSettings: (tab, modelsPage = "list") =>
set((state) => ({
settingsOpen: true,
settingsTab: tab ?? state.settingsTab,
modelsPage,
})),
setSettingsOpen: (open) => set({ settingsOpen: open }),
setSettingsTab: (tab) => set({ settingsTab: tab }),
setModelsPage: (page) => set({ modelsPage: page }),
openCompare: (versionId) => set({ compareVersionId: versionId }),
closeCompare: () => set({ compareVersionId: null }),
setSaveDialogOpen: (open) => set({ saveDialogOpen: open }),