Apply the repo's comments guideline to src/client/**: delete comments that
restate JSX, Tailwind utilities or standard React behaviour, narrate history,
duplicate docs/, or describe what the next line does; keep only container-query
and containment traps, portal requirements, reference-stability rules with a
real re-render consequence, CSS<->TS constant parities, harness seat and payload
contracts. Align the page's name with the shipped UI label ("Context Insights",
not "Context Dashboard"), unify drifted phrasings, drop the last non-English
fragments, and rewrap survivors onto fewer, fuller lines within the 140-col limit.
Comment lines 5,183 -> 1,822. No code change: comment-stripped content is
identical per file; the client bundle differs only by dead Tailwind utilities
that existed because deleted comments contained those words.
165 lines
5.5 KiB
TypeScript
165 lines
5.5 KiB
TypeScript
/** The dsh-context preference card, keyed on the Host-served `dsh-context` namespace: while that namespace is
|
|
* unavailable (a deployment without the Host half, or a remote browser) the card renders nothing. */
|
|
|
|
import { useState, type ReactElement } from 'react'
|
|
import { Menu } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
import { IconChevronDown } from '../primitives'
|
|
import type { SettingsField, SettingsState } from '../settings'
|
|
import type { ViewKit } from '../viewkit'
|
|
|
|
export interface SettingsCardProps {
|
|
useContextSettings?: <T>(selector: (state: SettingsState) => T) => T
|
|
set?: (field: SettingsField, value: string) => void
|
|
}
|
|
|
|
interface PrefRowProps {
|
|
label: string
|
|
value: string
|
|
options: ReadonlyArray<{ id: string; label: string }>
|
|
disabled: boolean
|
|
onPick: (id: string) => void
|
|
}
|
|
|
|
function PrefRow(props: PrefRowProps): ReactElement {
|
|
const [open, setOpen] = useState(false)
|
|
const active = props.options.find(o => o.id === props.value)?.label ?? props.value
|
|
return (
|
|
<div className="lc-settings-row">
|
|
<span className="lc-settings-label">{props.label}</span>
|
|
<Menu
|
|
open={open}
|
|
onClose={() => { setOpen(false) }}
|
|
items={props.options}
|
|
selectedId={props.value}
|
|
onSelect={(id) => { setOpen(false); props.onPick(id) }}
|
|
align="end"
|
|
portal
|
|
anchor={(
|
|
<button
|
|
type="button"
|
|
className="lc-settings-select hover:enabled:bg-(--dsw-alias-interactive-bg-hover) disabled:opacity-50 disabled:cursor-default"
|
|
disabled={props.disabled}
|
|
aria-haspopup="menu"
|
|
aria-expanded={open}
|
|
onClick={() => { setOpen(v => !v) }}
|
|
>
|
|
{active}
|
|
<IconChevronDown />
|
|
</button>
|
|
)}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
type Translate = ViewKit['t']
|
|
|
|
function PreferenceRows(props: { t: Translate; state: SettingsState; set?: SettingsCardProps['set'] }): ReactElement {
|
|
const { t, state, set } = props
|
|
const disabled = state.status !== 'ready' || !state.writable
|
|
return (
|
|
<>
|
|
<PrefRow
|
|
label={t('settings.placement')}
|
|
value={state.placement}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'all', label: t('placement.all') },
|
|
{ id: 'tab', label: t('placement.tab') },
|
|
{ id: 'sidebar', label: t('placement.sidebar') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultPlacement', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.insightsEntry')}
|
|
value={state.insightsEntry}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'show', label: t('insightsEntry.show') },
|
|
{ id: 'hide', label: t('insightsEntry.hide') },
|
|
]}
|
|
onPick={(id) => { set?.('insightsEntry', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.gran')}
|
|
value={state.granularity}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'step', label: t('gran.step') },
|
|
{ id: 'turn', label: t('gran.turn') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultGranularity', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.mode')}
|
|
value={state.mode}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'total', label: t('gran.total') },
|
|
{ id: 'delta', label: t('gran.delta') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultTrendMode', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.durationCurve')}
|
|
value={state.durationCurve}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'show', label: t('insightsEntry.show') },
|
|
{ id: 'hide', label: t('insightsEntry.hide') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultDurationCurve', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.deltaBase')}
|
|
value={state.deltaBase}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'step', label: t('browser.base.step') },
|
|
{ id: 'turn', label: t('browser.base.turn') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultDeltaBase', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.toolSort')}
|
|
value={state.toolSort}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'size', label: t('tool.sort.size') },
|
|
{ id: 'count', label: t('tool.sort.count') },
|
|
{ id: 'name', label: t('tool.sort.name') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultToolSort', id) }}
|
|
/>
|
|
<PrefRow
|
|
label={t('settings.fileSort')}
|
|
value={state.fileSort}
|
|
disabled={disabled}
|
|
options={[
|
|
{ id: 'count', label: t('files.sort.count') },
|
|
{ id: 'latest', label: t('files.sort.latest') },
|
|
{ id: 'path', label: t('files.sort.path') },
|
|
]}
|
|
onPick={(id) => { set?.('defaultFileSort', id) }}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
/** The Plugins page owns the bundle's page and section chrome, so the rows render flat; unserved or absent degrades to nothing. */
|
|
export function makePluginConfigCard(kit: ViewKit): (props: SettingsCardProps) => ReactElement | null {
|
|
const { t } = kit
|
|
return function PluginConfigCard(props: SettingsCardProps): ReactElement | null {
|
|
const state = typeof props.useContextSettings === 'function' ? props.useContextSettings(s => s) : undefined
|
|
if (state === undefined || state.status === 'unavailable') return null
|
|
return (
|
|
<div className="lc-settings-prefs">
|
|
{!state.writable && state.status === 'ready'
|
|
? <p className="lc-settings-note" role="status">{t('settings.readOnly')}</p>
|
|
: null}
|
|
<PreferenceRows t={t} state={state} set={props.set} />
|
|
</div>
|
|
)
|
|
}
|
|
}
|