mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge updated DeepSeek onboarding base
# Conflicts: # .agents/notes/implemented/feature/2026-07-30-deepseek-onboarding-credential-setup.i18n.yaml # .agents/notes/implemented/feature/2026-07-30-deepseek-onboarding-credential-setup.md # .agents/notes/implemented/feature/2026-07-30-deepseek-onboarding-credential-setup.zh.md # packages/client/ui-models/README.i18n.yaml # packages/client/ui-models/README.md # packages/client/ui-models/README.zh.md # packages/client/ui-models/src/client/DeepSeekOnboardingDialog.module.css # packages/client/ui-models/src/client/DeepSeekOnboardingDialog.tsx # packages/client/ui-models/tests/onboarding-dialog.spec.tsx
This commit is contained in:
@@ -26,8 +26,7 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.description,
|
||||
.diagnostic {
|
||||
.description {
|
||||
max-width: 600px;
|
||||
margin: 22px 0 0;
|
||||
font-size: 17px;
|
||||
@@ -73,15 +72,13 @@
|
||||
.brand,
|
||||
.title,
|
||||
.description,
|
||||
.diagnostic,
|
||||
.provider,
|
||||
.actions {
|
||||
animation: credential-enter 280ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||||
}
|
||||
|
||||
.title { animation-delay: 40ms; }
|
||||
.description,
|
||||
.diagnostic { animation-delay: 80ms; }
|
||||
.description { animation-delay: 80ms; }
|
||||
.provider { animation-delay: 120ms; }
|
||||
.actions { animation-delay: 160ms; }
|
||||
|
||||
@@ -101,7 +98,6 @@
|
||||
.brand,
|
||||
.title,
|
||||
.description,
|
||||
.diagnostic,
|
||||
.provider,
|
||||
.actions {
|
||||
animation: none;
|
||||
@@ -118,8 +114,7 @@
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.description,
|
||||
.diagnostic {
|
||||
.description {
|
||||
font-size: 16px;
|
||||
line-height: 27px;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { ReactNode } from 'react'
|
||||
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { BrandWordmark, Button } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { DeepSeekReadiness, ModelsSettingsState, ModelsSettingsStore } from './store.ts'
|
||||
import type { ModelsSettingsState, ModelsSettingsStore } from './store.ts'
|
||||
import { deepSeekReadiness } from './store.ts'
|
||||
import type { en } from './locales.ts'
|
||||
import styles from './DeepSeekOnboardingDialog.module.css'
|
||||
@@ -28,35 +28,11 @@ export interface DeepSeekOnboardingInjected {
|
||||
export type DeepSeekOnboardingDialogProps =
|
||||
PropsRuntime<'settings.onboarding'> & DeepSeekOnboardingInjected
|
||||
|
||||
type UnavailableReason = Extract<DeepSeekReadiness, { kind: 'unavailable' }>['reason']
|
||||
|
||||
/* v8 ignore next 3 -- closed-union defaults only defend future source widening */
|
||||
function assertNever(_value: never): never {
|
||||
throw new Error('unexpected DeepSeek onboarding state')
|
||||
}
|
||||
|
||||
function unavailableDiagnostic(
|
||||
reason: UnavailableReason,
|
||||
t: DeepSeekOnboardingInjected['t'],
|
||||
): string {
|
||||
switch (reason) {
|
||||
case 'load-failed':
|
||||
return t('onboardingLoadFailed')
|
||||
case 'credentials-unavailable':
|
||||
return t('onboardingCredentialsUnavailable')
|
||||
case 'settings-read-only':
|
||||
case 'credential-read-only':
|
||||
return t('onboardingReadOnly')
|
||||
case 'provider-inactive':
|
||||
case 'settings-unavailable':
|
||||
case 'credential-ref-unavailable':
|
||||
return t('onboardingConfigurationUnavailable')
|
||||
/* v8 ignore next -- every current unavailable reason is handled above */
|
||||
default:
|
||||
return assertNever(reason)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt a first-run user to open Models while the official adapter exists
|
||||
* and its effective credential is not configured.
|
||||
@@ -74,42 +50,34 @@ export function DeepSeekOnboardingDialog(props: DeepSeekOnboardingDialogProps):
|
||||
}, [controller, state.status])
|
||||
|
||||
useEffect(() => {
|
||||
if (readiness.kind === 'adapter-absent' || readiness.kind === 'configured') complete()
|
||||
if (
|
||||
readiness.kind === 'adapter-absent'
|
||||
|| readiness.kind === 'configured'
|
||||
|| readiness.kind === 'unavailable'
|
||||
) complete()
|
||||
}, [complete, readiness.kind])
|
||||
|
||||
useEffect(() => {
|
||||
if (readiness.kind === 'credential-missing') titleRef.current?.focus()
|
||||
}, [readiness.kind])
|
||||
|
||||
const openModels = (): void => {
|
||||
complete()
|
||||
openSection('models')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (readiness.kind === 'credential-missing' || readiness.kind === 'unavailable') {
|
||||
titleRef.current?.focus()
|
||||
}
|
||||
}, [readiness.kind])
|
||||
|
||||
let unavailableReason: UnavailableReason | undefined
|
||||
switch (readiness.kind) {
|
||||
case 'loading':
|
||||
case 'adapter-absent':
|
||||
case 'configured':
|
||||
case 'unavailable':
|
||||
return null
|
||||
case 'credential-missing':
|
||||
unavailableReason = undefined
|
||||
break
|
||||
case 'unavailable':
|
||||
unavailableReason = readiness.reason
|
||||
break
|
||||
/* v8 ignore next -- every current readiness variant is handled above */
|
||||
default:
|
||||
return assertNever(readiness)
|
||||
}
|
||||
const unavailable = unavailableReason !== undefined
|
||||
const diagnostic = unavailableReason === undefined
|
||||
? undefined
|
||||
: unavailableDiagnostic(unavailableReason, t)
|
||||
|
||||
const title = unavailable ? t('onboardingUnavailableTitle') : t('onboardingTitle')
|
||||
|
||||
return (
|
||||
<section className={styles['page']} role="region" aria-labelledby="deepseek-onboarding-title">
|
||||
@@ -120,11 +88,9 @@ export function DeepSeekOnboardingDialog(props: DeepSeekOnboardingDialogProps):
|
||||
className={styles['title']}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{title}
|
||||
{t('onboardingTitle')}
|
||||
</h2>
|
||||
{unavailable
|
||||
? <p className={styles['diagnostic']}>{diagnostic}</p>
|
||||
: <p className={styles['description']}>{t('onboardingDescription')}</p>}
|
||||
<p className={styles['description']}>{t('onboardingDescription')}</p>
|
||||
<div className={styles['provider']}>
|
||||
<span className={styles['providerName']}>DeepSeek</span>
|
||||
<span className={styles['providerRoute']}>deepseek-official</span>
|
||||
@@ -133,11 +99,7 @@ export function DeepSeekOnboardingDialog(props: DeepSeekOnboardingDialogProps):
|
||||
<Button variant="ghost" className={styles['later']} onClick={complete}>
|
||||
{t('onboardingLater')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className={styles['primary']}
|
||||
onClick={openModels}
|
||||
>
|
||||
<Button variant="primary" className={styles['primary']} onClick={openModels}>
|
||||
{t('onboardingGoToSettings')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -32,11 +32,6 @@ export const en = {
|
||||
onboardingDescription: 'Configure the official DeepSeek provider to start building.',
|
||||
onboardingGoToSettings: 'Go to settings',
|
||||
onboardingLater: 'Configure later',
|
||||
onboardingUnavailableTitle: 'DeepSeek setup is unavailable',
|
||||
onboardingLoadFailed: 'DeepSeek configuration could not be loaded. Check the connection and try again in Models.',
|
||||
onboardingCredentialsUnavailable: 'Credential storage is unavailable in this deployment. Check the deployment configuration.',
|
||||
onboardingReadOnly: 'This deployment does not allow the DeepSeek API key to be changed here. Ask an administrator to provide the credential.',
|
||||
onboardingConfigurationUnavailable: 'DeepSeek configuration is unavailable in this deployment. Check the deployment composition.',
|
||||
}
|
||||
|
||||
/** Chinese strings (same keys as {@link en}). */
|
||||
@@ -71,9 +66,4 @@ export const zh: typeof en = {
|
||||
onboardingDescription: '配置 DeepSeek 官方模型,即可开始使用。',
|
||||
onboardingGoToSettings: '前往配置',
|
||||
onboardingLater: '稍后配置',
|
||||
onboardingUnavailableTitle: '无法在此配置 DeepSeek',
|
||||
onboardingLoadFailed: '无法加载 DeepSeek 配置。请检查连接,然后在模型设置中重试。',
|
||||
onboardingCredentialsUnavailable: '当前部署无法使用凭据存储。请检查部署配置。',
|
||||
onboardingReadOnly: '当前部署不允许在此修改 DeepSeek API 密钥。请联系管理员提供凭据。',
|
||||
onboardingConfigurationUnavailable: '当前部署无法使用 DeepSeek 配置。请检查部署组合。',
|
||||
}
|
||||
|
||||
@@ -215,8 +215,8 @@ export type DeepSeekReadiness =
|
||||
|
||||
/**
|
||||
* Project official-DeepSeek readiness from the provider/settings/credential
|
||||
* join used by the Models page. A missing directory entry means the adapter
|
||||
* is not mounted and therefore cannot be repaired by navigating to Models.
|
||||
* join used by the Models page. A missing official configurable-provider
|
||||
* declaration means the adapter is not repairable by navigating to Models.
|
||||
* @param state - current shared Models join snapshot.
|
||||
* @returns the onboarding state without reading a parallel fact source.
|
||||
*/
|
||||
@@ -230,7 +230,10 @@ export function deepSeekReadiness(state: ModelsSettingsState): DeepSeekReadiness
|
||||
reason: 'load-failed',
|
||||
}
|
||||
}
|
||||
const row = state.rows.find(candidate => candidate.entry.provider === 'deepseek-official')
|
||||
const row = state.rows.find(candidate =>
|
||||
candidate.entry.provider === 'deepseek-official'
|
||||
&& candidate.entry.settingsNs === 'llm-deepseek'
|
||||
&& candidate.entry.settingsPath.length === 0)
|
||||
if (row === undefined) return { kind: 'adapter-absent' }
|
||||
if (!row.entry.active) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user