// dsh-think-any-lang — Browser half (hand-written factory bundle). // Loaded as a classic script by the web shell's module table; the factory is // materialized on first require and receives the platform seed words (react, // ui-primitives) through the injected require. Registers the General-settings // thinking-language selector row. // // Values are read/written through the host half's private `/think-any-lang` // RPC channel instead of the settings RPC surface: dsh-host-apiproxy only // exposes an allowlist of settings namespaces to configuration clients, so a // third-party namespace would otherwise answer `settings-not-exposed` and the // selection would never persist (or even render from the stored value). window.__ModuleLoader__.load({ id: 'dsh-think-any-lang', factory: (require) => { const module = { exports: {} } const React = require('react') const primitives = require('@deepseek-ai/dsh-client-ui-primitives') const { IconChevronDownOutline14, Menu } = primitives const RPC_CHANNEL = '/think-any-lang' const FIELD = 'language' // Display labels mirror the `LANGUAGES` keys in index.js; `off` means "no // directive" and is listed first. const OPTIONS = [ { id: 'off', label: '跟随默认' }, { id: 'zh', label: '中文' }, { id: 'en', label: 'English' }, { id: 'ja', label: '日本語' }, { id: 'ko', label: '한국어' }, { id: 'de', label: 'Deutsch' }, { id: 'fr', label: 'Français' }, { id: 'es', label: 'Español' }, { id: 'pt', label: 'Português' }, { id: 'ru', label: 'Русский' }, { id: 'it', label: 'Italiano' }, { id: 'ar', label: 'العربية' }, { id: 'hi', label: 'हिन्दी' }, ] const rowStyle = { display: 'flex', alignItems: 'center', gap: '8px', padding: '16px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)', } const textStyle = { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '4px', paddingRight: '48px', } const titleStyle = { fontSize: '14px', fontWeight: '400', lineHeight: '22px', color: 'var(--dsw-alias-label-primary)', } const descStyle = { fontSize: '12px', fontWeight: '400', lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', } const selectorStyle = { display: 'inline-flex', alignItems: 'center', gap: '12px', height: '36px', padding: '0 14px', border: 'none', borderRadius: '18px', background: 'var(--dsw-alias-bg-module-platform)', font: 'inherit', fontSize: '14px', lineHeight: '22px', color: 'var(--dsw-alias-label-primary)', cursor: 'pointer', } module.exports = { name: 'dsh-think-any-lang', inject: ['slots', 'connection'], apply(ctx) { const rpc = ctx.connection.rpc function ThinkLanguageRow() { const [language, setLanguage] = React.useState('zh') const [ready, setReady] = React.useState(false) const [open, setOpen] = React.useState(false) React.useEffect(() => { let alive = true const load = async () => { try { const result = await rpc.call(RPC_CHANNEL, 'get', {}) if (!alive) return const value = result.ok ? result.value : undefined if (value && typeof value[FIELD] === 'string') setLanguage(value[FIELD]) } catch (error) { // Channel unavailable (host half not loaded, transport down): // keep the default value and render the row read-only. } finally { if (alive) setReady(true) } } load() return () => { alive = false } }, []) const activeLabel = OPTIONS.find(option => option.id === language)?.label ?? language const handleSelect = (id) => { setOpen(false) rpc.call(RPC_CHANNEL, 'set', { [FIELD]: id }).then((result) => { const value = result.ok ? result.value : undefined if (value && typeof value[FIELD] === 'string') setLanguage(value[FIELD]) }).catch(() => { // Write failed: keep the previously stored value on display. }) } return React.createElement('div', { style: rowStyle }, React.createElement('div', { style: textStyle }, React.createElement('div', { style: titleStyle }, '思考语言'), React.createElement('div', { style: descStyle }, '选择模型进行推理思考时使用的语言'), ), React.createElement(Menu, { open, onClose: () => { setOpen(false) }, items: OPTIONS, selectedId: language, onSelect: handleSelect, align: 'end', portal: true, anchor: React.createElement('button', { type: 'button', className: undefined, 'aria-haspopup': 'menu', 'aria-expanded': open, disabled: !ready, style: selectorStyle, onClick: () => { setOpen(value => !value) }, }, activeLabel, React.createElement(IconChevronDownOutline14, { size: 14 }), ), }), ) } ctx.slots.inject('settings.general.item', () => ctx.slots.register({ name: 'settings.general.item', id: 'thinking-language', order: 15, }, ThinkLanguageRow)) }, } return module.exports }, })