docs(agent-notes): record the web plugin configuration decision
Also folds the duplication the three controls had grown: the draft-and-commit input both editable fields render is now one component, and the two sibling executors' identical import surface is marked as the deliberate mirror their READMEs already describe.
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
* @module @deepseek-ai/dsh-pwsh-local
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start -- this executor mirrors dsh-bash-local call-for-call by
|
||||
design (see this package's README), so the two import the same seam surface */
|
||||
import { Context } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import { BASH_SETTINGS_NAMESPACE, BashExecutor } from '@deepseek-ai/dsh-bash'
|
||||
@@ -20,6 +22,7 @@ import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashR
|
||||
import type { SubprocessCollect, SubprocessHandle, SubprocessOutputReader, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'
|
||||
import { installSettingsSection } from '@deepseek-ai/dsh-settings'
|
||||
import { clampTimeout, deadline, MAX_TIMER_DELAY_MS, timeoutOf } from '@deepseek-ai/dsh-timeout'
|
||||
/* jscpd:ignore-end */
|
||||
import { resolvePwshPath } from './resolve.ts'
|
||||
|
||||
/* jscpd:ignore-start -- deliberate call-for-call mirror of dsh-bash-local (Agent Note: pwsh-tool-and-executor). */
|
||||
|
||||
@@ -73,6 +73,46 @@ function useDraft(value: string): [string, (next: string) => void] {
|
||||
return [draft, setDraft]
|
||||
}
|
||||
|
||||
/** Blur the input so its own blur handler is the single commit path. */
|
||||
function commitOnEnter(event: KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.key === 'Enter') event.currentTarget.blur()
|
||||
}
|
||||
|
||||
/**
|
||||
* The text input both editable fields render: a draft seeded from the
|
||||
* authoritative text, committed on blur and on Enter.
|
||||
*/
|
||||
function DraftInput(props: {
|
||||
/** Stable id associating the label with this control. */
|
||||
id: string
|
||||
/** Authoritative text the draft re-seeds from. */
|
||||
value: string
|
||||
/** Disables editing. */
|
||||
disabled: boolean
|
||||
/** Placeholder shown while the draft is empty. */
|
||||
placeholder?: string | undefined
|
||||
/** Hints a numeric keypad without narrowing the value type. */
|
||||
numeric?: boolean | undefined
|
||||
/** Settle the draft; the returned text replaces it (a rejected draft restores the value). */
|
||||
onSettle: (draft: string, restore: (text: string) => void) => void
|
||||
}) {
|
||||
const [draft, setDraft] = useDraft(props.value)
|
||||
return (
|
||||
<input
|
||||
id={props.id}
|
||||
className={css.input}
|
||||
type="text"
|
||||
{...props.numeric === true ? { inputMode: 'numeric' as const } : {}}
|
||||
value={draft}
|
||||
placeholder={props.placeholder ?? ''}
|
||||
disabled={props.disabled}
|
||||
onChange={(event) => { setDraft(event.target.value) }}
|
||||
onBlur={() => { props.onSettle(draft, setDraft) }}
|
||||
onKeyDown={commitOnEnter}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/** A whole-number field committed on blur or Enter. */
|
||||
export function NumberField(props: FieldProps & {
|
||||
/** Current effective value. */
|
||||
@@ -80,31 +120,22 @@ export function NumberField(props: FieldProps & {
|
||||
/** Commit a parsed value; a draft that is not a finite number is discarded. */
|
||||
onCommit: (next: number) => void
|
||||
}) {
|
||||
const [draft, setDraft] = useDraft(String(props.value))
|
||||
const commit = () => {
|
||||
const parsed = Number(draft)
|
||||
if (draft.trim() === '' || !Number.isFinite(parsed)) {
|
||||
setDraft(String(props.value))
|
||||
return
|
||||
}
|
||||
if (parsed === props.value) return
|
||||
props.onCommit(parsed)
|
||||
}
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') event.currentTarget.blur()
|
||||
}
|
||||
return (
|
||||
<FieldFrame {...props}>
|
||||
<input
|
||||
<DraftInput
|
||||
id={props.id}
|
||||
className={css.input}
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={draft}
|
||||
value={String(props.value)}
|
||||
disabled={props.disabled}
|
||||
onChange={(event) => { setDraft(event.target.value) }}
|
||||
onBlur={commit}
|
||||
onKeyDown={onKeyDown}
|
||||
numeric
|
||||
onSettle={(draft, restore) => {
|
||||
const parsed = Number(draft)
|
||||
if (draft.trim() === '' || !Number.isFinite(parsed)) {
|
||||
restore(String(props.value))
|
||||
return
|
||||
}
|
||||
if (parsed === props.value) return
|
||||
props.onCommit(parsed)
|
||||
}}
|
||||
/>
|
||||
</FieldFrame>
|
||||
)
|
||||
@@ -119,27 +150,18 @@ export function TextField(props: FieldProps & {
|
||||
/** Commit the trimmed draft. */
|
||||
onCommit: (next: string) => void
|
||||
}) {
|
||||
const [draft, setDraft] = useDraft(props.value)
|
||||
const commit = () => {
|
||||
const next = draft.trim()
|
||||
if (next === props.value) return
|
||||
props.onCommit(next)
|
||||
}
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') event.currentTarget.blur()
|
||||
}
|
||||
return (
|
||||
<FieldFrame {...props}>
|
||||
<input
|
||||
<DraftInput
|
||||
id={props.id}
|
||||
className={css.input}
|
||||
type="text"
|
||||
value={draft}
|
||||
placeholder={props.placeholder ?? ''}
|
||||
value={props.value}
|
||||
disabled={props.disabled}
|
||||
onChange={(event) => { setDraft(event.target.value) }}
|
||||
onBlur={commit}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder={props.placeholder}
|
||||
onSettle={(draft) => {
|
||||
const next = draft.trim()
|
||||
if (next === props.value) return
|
||||
props.onCommit(next)
|
||||
}}
|
||||
/>
|
||||
</FieldFrame>
|
||||
)
|
||||
@@ -159,15 +181,6 @@ export function SecretField(props: Omit<FieldProps, 'overridden' | 'onReset'> &
|
||||
onCommit: (next: string) => void
|
||||
}) {
|
||||
const [draft, setDraft] = useState('')
|
||||
const commit = () => {
|
||||
const next = draft.trim()
|
||||
if (next === '') return
|
||||
setDraft('')
|
||||
props.onCommit(next)
|
||||
}
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') event.currentTarget.blur()
|
||||
}
|
||||
return (
|
||||
<div className={css.field}>
|
||||
<div className={css.head}>
|
||||
@@ -184,8 +197,13 @@ export function SecretField(props: Omit<FieldProps, 'overridden' | 'onReset'> &
|
||||
value={draft}
|
||||
disabled={props.disabled}
|
||||
onChange={(event) => { setDraft(event.target.value) }}
|
||||
onBlur={commit}
|
||||
onKeyDown={onKeyDown}
|
||||
onBlur={() => {
|
||||
const next = draft.trim()
|
||||
if (next === '') return
|
||||
setDraft('')
|
||||
props.onCommit(next)
|
||||
}}
|
||||
onKeyDown={commitOnEnter}
|
||||
/>
|
||||
<p className={css.hint}>{props.hint}</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user