| 1 | // Isolated UI fixture. No native configuration or credentials are read or written. |
| 2 | import React, { useState } from 'react'; |
| 3 | import { createRoot } from 'react-dom/client'; |
| 4 | import { LocaleProvider } from '../src/lib/i18n'; |
| 5 | import { ProviderConnections } from '../src/components/ProviderConnections'; |
| 6 | import { providerAccessGroups, ProviderEditor, ProvidersSection, ModelsSection } from '../src/components/SettingsPanel'; |
| 7 | import { SettingsNavigation, SETTINGS_NAV_TABS } from '../src/components/SettingsNavigation'; |
| 8 | import { useT } from '../src/lib/i18n'; |
| 9 | import { ConnectionTitle } from '../src/components/ConnectionTitle'; |
| 10 | import { baseSettings } from '../src/test-support/settingsTestFixtures'; |
| 11 | import type { ProviderView } from '../src/lib/types'; |
| 12 | import '../src/styles.css'; |
| 13 | import '../src/components/SettingsPanel.css'; |
| 14 | const models = ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-v4-flash-vision-exp']; |
| 15 | const sample = {name:'deepseek', builtIn:true, added:true, kind:'anthropic', baseUrl:'https://api.deepseek.com/anthropic', models, default:models[0], apiKeyEnv:'DEEPSEEK_API_KEY', keySet:true, visionModels:[], modelsUrl:'', balanceUrl:'', supportedEfforts:[], contextWindow:1000000, webSearch:true, modelCapabilities: models.map((model,i)=>({model,state:i===2 ? 'supported' : 'unsupported'}))} as ProviderView; |
| 16 | (window as any).go = {main:{App:{FetchProviderModelCatalog:async()=>sample.modelCapabilities}}}; |
| 17 | function OnboardingPreview() { |
| 18 | const [providers,setProviders]=useState<ProviderView[]>(location.search.includes('existing') ? [{...sample,keySet:false}] : []); |
| 19 | const [done,setDone]=useState(false); |
| 20 | (window as any).go.main.App.AddProviderConnection=async (_id:string,_source:string,key:string)=>setProviders([{...sample,keySet:Boolean(key)}]); |
| 21 | (window as any).go.main.App.SetDefaultModel=async ()=>{}; |
| 22 | |
| 23 | const settings={...baseSettings(),providers,providerPresets:[],providerKinds:['anthropic','openai','responses']}; |
| 24 | return <main style={{padding:24,maxWidth:1300,margin:'auto'}}><button className="btn" onClick={()=>setDone(true)}>返回工作区</button>{done ? <p>已返回工作区(预览)</p> : <div className="model-access-page"><ProvidersSection s={settings} onboarding busy={false} apply={async fn=>{await fn();return true;}} onOnboardingComplete={()=>setDone(true)}/></div>}</main>; |
| 25 | } |
| 26 | function ScrollPreview() { |
| 27 | const [provider,setProvider]=useState(sample); |
| 28 | const [page,setPage]=useState<any>("providers"); const t=useT(); |
| 29 | const groups=providerAccessGroups(Array.from({length:12},(_,i)=>({...provider,name:i ? `connection-${i}` : provider.name,displayName:`DeepSeek ${i+1}`})), ((key:string)=>key) as any); |
| 30 | return <div className="settings-center" style={{height:"100dvh"}}><SettingsNavigation items={SETTINGS_NAV_TABS.map(id=>({id,label:id==="models"?"模型偏好":id==="providers"?"模型服务":id==="model-stats"?"用量统计":t(`settings.tab.${id}` as any),meta:""}))} activeTab={page} onSelect={setPage}/><main className="settings-center__content" style={{height:'100dvh',boxSizing:'border-box'}}><div className="settings-page-content"><div className="settings-page settings-page--models"> |
| 31 | <header className="settings-page__header"><h2>{page==="providers"?"模型服务":page==="models"?"模型偏好":"用量统计"}</h2></header>{page!=="providers" && <p>导航预览:此页面的原有内容在应用中保留。</p>}<div className="model-access-page" hidden={page!=="providers"}> |
| 32 | <section className="settings-section"><div className="settings-section__head">供应商接入</div><div className="settings-section__body"><div className="provider-access-grid"> |
| 33 | <ProviderConnections groups={groups} presets={[]} revealedProvider={null} hidden={false} busy={false} onAdd={()=>{}} renderDetail={group=><article className="provider-access-card provider-access-card--detail"><div className="provider-access-card__head"><ConnectionTitle label={group.label} busy={false} onSave={async()=>true}/></div><div className="provider-access-card__desc">Anthropic Messages</div><ProviderEditor hideConnectionName initial={provider} kinds={['anthropic','openai','responses']} busy={false} onCancel={()=>{}} onSave={async p=>setProvider(p)} /></article>}/> |
| 34 | </div></div></section></div></div></div></main></div>; |
| 35 | } |
| 36 | function PreferencesPreview() { |
| 37 | const t=useT(); |
| 38 | const [settings,setSettings]=useState({...baseSettings(),defaultModel:'deepseek/deepseek-v4-flash',visionModel:'auto',webSearchModel:'auto',webSearchModels:['deepseek/deepseek-v4-flash','deepseek/deepseek-v4-pro'],providers:[{...sample,displayName:'DeepSeek 官方1'}]}); |
| 39 | const setters: Record<string,string> = {SetDefaultModel:'defaultModel',SetPlannerModel:'plannerModel',SetVisionModel:'visionModel',SetWebSearchModel:'webSearchModel',SetSubagentModel:'subagentModel',SetSubagentEffort:'subagentEffort'}; |
| 40 | Object.entries(setters).forEach(([method,key])=>{(window as any).go.main.App[method]=async(value:string)=>setSettings(s=>({...s,[key]:value}));}); |
| 41 | const agentSetters: Record<string,string>={SetReasoningLanguage:'reasoningLanguage',SetCompactRatio:'compactRatio',SetMaxSubagentDepth:'maxSubagentDepth',SetMaxSubagentConcurrency:'maxSubagentConcurrency',SetMaxParallelWriters:'maxParallelWriters'}; |
| 42 | Object.entries(agentSetters).forEach(([method,key])=>{(window as any).go.main.App[method]=async(value:unknown)=>setSettings(s=>({...s,agent:{...s.agent,[key]:value}}));}); |
| 43 | return <div className="settings-center" style={{height:'100vh'}}><SettingsNavigation items={SETTINGS_NAV_TABS.map(id=>({id,label:id==="models"?"模型偏好":id==="model-stats"?"用量统计":t(`settings.tab.${id}` as any),meta:""}))} activeTab="models" onSelect={()=>{}}/><main className="settings-center__content"><div className="settings-page-content"><div className="settings-page settings-page--models"><ModelsSection s={settings} busy={false} subtab="usage" apply={async fn=>{await fn();return true;}} backgroundApply={async()=>{}}/></div></div></main></div>; |
| 44 | } |
| 45 | function Preview() { |
| 46 | const [provider,setProvider]=useState(sample), [label,setLabel]=useState('DeepSeek 官方'), [revision,setRevision]=useState(0); |
| 47 | return <main style={{padding:'28px 40px',maxWidth:1360,margin:'auto',background:'var(--bg-elev)'}}> |
| 48 | <h2 style={{fontSize:24,margin:'0 0 16px'}}><ConnectionTitle label={label} busy={false} onSave={async(value)=>{setLabel(value);return true;}}/></h2> |
| 49 | <ProviderEditor key={revision} hideConnectionName initial={provider} kinds={['anthropic','openai','responses']} busy={false} onCancel={()=>setRevision(r=>r+1)} onSave={async(p)=>setProvider(p)} onSaveKey={async()=>setProvider(p=>({...p,keySet:true}))} onClearKey={async()=>setProvider(p=>({...p,keySet:false}))}/> |
| 50 | </main>; |
| 51 | } |
| 52 | createRoot(document.getElementById('root')!).render(location.search.includes('narrow') ? <iframe title="Narrow provider editor" src="./provider-layout-preview.html" style={{width:390,height:850,border:0}}/> : <LocaleProvider>{location.search.includes("preferences") ? <PreferencesPreview/> : location.search.includes("onboarding") ? <OnboardingPreview/> : location.search.includes("scroll") ? <ScrollPreview/> : <Preview/>}</LocaleProvider>); |
| 53 |