/** * Deployment settings view shared between the panel Settings tab and the DSH Plugin Manager config page. * Edits host-level plugin configuration limits through DSH `ConfigForm` when writable. */ import { useCallback, useMemo, useSyncExternalStore } from 'react' import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client' import type { ConfigPageForm } from '@deepseek-ai/dsh-client-ui-plugin-manager/client' import { LIMITS } from '../limits.ts' import type { SetupView } from './api.ts' import { InfoTip, Input } from './controls.tsx' import { SaveBar, useDraft } from './draft.tsx' import { PanelProvider, usePanel, usePanelData } from './provider.tsx' import { Field, Fields } from './settings-kit.tsx' import { setupText, type T } from './text.ts' import { CheckSetupButton, Hint, KeyValues, Section, SetupMessage } from './widgets.tsx' export type DeploymentForm = ConfigPageForm | undefined type Op = Parameters[0][number] const MACHINE_LIMITS = ['promptMaxChars', 'admissionContextMaxPages', 'admissionContextTimeoutMs'] as const type Limits = Record const text = (value: unknown): string => typeof value === 'string' || typeof value === 'number' ? String(value) : '' const limitsOf = (values: object | undefined): Limits => Object.fromEntries(MACHINE_LIMITS.map(key => [key, text((values as Record | undefined)?.[key])])) as Limits export function usePageForm(form: ConfigForm> | undefined): DeploymentForm { const subscribe = useCallback((listener: () => void) => form ? form.subscribe(listener) : () => {}, [form]) const state = useSyncExternalStore(subscribe, () => form?.getSnapshot()) return useMemo(() => form && state ? { state, mutate: form.mutate.bind(form) } : undefined, [form, state]) } /** Deployment configuration form; falls back to read-only display when DSH config forms are unavailable or read-only. */ export function DeploymentPage({ setup, form, accounts: showAccounts }: { setup: SetupView; form: DeploymentForm; accounts?: boolean }) { const { t, notify } = usePanel() const snapshot = form?.state const editable = snapshot?.status === 'ready' && snapshot.writable // Pass `revision` only while DSH serves writable values so a draft started before `ready` rebases once writable. const draft = useDraft({ saved: limitsOf(editable ? snapshot.value : setup.machine), revision: editable ? snapshot.revision : undefined, reload: async () => {}, save: async (value, revision, base) => { const ops: Op[] = MACHINE_LIMITS.filter(key => value[key].trim() !== base[key].trim()) .map(key => value[key].trim() ? { op: 'set', path: [key], value: Number(value[key]) } : { op: 'unset', path: [key] }) if (!form || !await form.mutate(ops, revision)) throw new Error(t('deploymentRefused')) notify(t('saved')) return { revision: revision + 1, restarting: false } } }) const accounts = setup.accounts // Remote browsers (`memory` mode) are read-only; `unavailable` indicates `dsh-superagent` is still starting. const readOnlyHint = !form ? t('deploymentNoForms') : snapshot?.mode === 'memory' ? t('deploymentReadOnly') : snapshot?.status === 'loading' ? t('loading') : snapshot?.status === 'unavailable' ? t('deploymentUnavailable') : !editable ? t('deploymentReadOnly') : undefined const limit = (key: typeof MACHINE_LIMITS[number], label: string) => draft.edit({ ...draft.value, [key]: event.target.value })} /> const feishu = accounts?.feishu ? accounts.feishu.name || accounts.feishu.botId : setup.feishuProblem ? setupText(t, setup.feishuProblem) : t('deploymentFeishuNone') return
{ void draft.submit(event) }}> {t('deploymentIntro')} {setup.problem &&

} {showAccounts &&
{t('deploymentAccountsWhere')}
}
{readOnlyHint && {readOnlyHint}}
{t('deploymentStateDir')} {setup.machine.stateDir}
{limit('promptMaxChars', t('deploymentPromptMaxChars'))} {limit('admissionContextMaxPages', t('deploymentContextPages'))} {limit('admissionContextTimeoutMs', t('deploymentContextTimeout'))}
{editable && } } /** Plugin Manager config page; includes derived repository and accounts because Source settings live in the main panel. */ function PluginPage({ form }: { form: DeploymentForm }) { const { t } = usePanel() const { state, error } = usePanelData() return
{state ? : error ?

{error.message}

:

{t('loading')}

}
} /** DSH Plugin Manager config view entry; renders nothing in `summary` mode because DSH displays the package description. */ export function PluginConfig({ t, view, form }: { t: T; view: 'page' | 'summary'; form: DeploymentForm }) { return view === 'page' ? : null }