feat(events): a searchable source's chosen note is found when the page lacks it
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 1m47s
PR Checks / client-build (pull_request) Successful in 1m58s
PR Checks / server-tests (pull_request) Successful in 14m58s

A searchable source answers the editor a bounded first page, so the value an
author picked (or a template filled in) may not be on it, and its note with it.
useChosenNote asks the source for that value as a search term, once per value,
and shows the exact match's note. Found building the Rust kit warning
(PLAN_REDESIGNS §2.6), whose source - rust.options.grouppermissions - is
searchable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-12 02:23:18 -05:00
parent 3e243ce772
commit e58857c133

View File

@@ -220,6 +220,46 @@ function SearchableOptions({ sourceId, label, disabled, onPick }) {
)
}
/**
* The chosen option's note (MODULE_API 1.13.0), found even when the list the
* form holds does not reach it.
*
* A searchable source answers the form a bounded first page, so the value an
* author picked — or a template filled in — may not be in it, and its note with
* it. Then the source is asked for that value as a search term, once per value,
* and the row whose value matches exactly is the one whose note is shown. A
* failure is no note: advice never breaks the field it advises.
*/
function useChosenNote(sourceId, value, entry) {
const [asked, setAsked] = useState({ key: null, note: null })
const text = value === undefined || value === null ? '' : String(value)
const known = noteFor(entry, value)
const needAsk = Boolean(sourceId && entry?.state === 'ok' && entry.searchable && text && !known &&
!entry.options.some((o) => o.value === text))
const key = needAsk ? `${sourceId}\u0000${text}` : null
useEffect(() => {
if (!key || asked.key === key) return undefined
let alive = true
const timer = setTimeout(async () => {
try {
const answer = await api.admin.eventOptions(sourceId, text)
const row = answer?.ok ? (answer.options || []).find((o) => o.value === text) : null
if (alive) setAsked({ key, note: row?.note || null })
} catch {
if (alive) setAsked({ key, note: null })
}
}, 300)
return () => {
alive = false
clearTimeout(timer)
}
}, [key, asked.key, sourceId, text])
if (known) return known
return key && asked.key === key ? asked.note : null
}
/**
* A row of buttons for a choice of two or three (Rust PLAN_REDESIGNS §2.4).
*
@@ -366,7 +406,7 @@ function ParamField({ param, value, sourceEntry, disabled, onChange, missing, la
// editable rather than look unset.
const segmented = param.source && param.type !== 'boolean' && !values && isSegmentable(sourceEntry)
const offList = segmented && asText !== '' && !sourceEntry.options.some((o) => o.value === asText)
const note = param.source ? noteFor(sourceEntry, value) : null
const note = useChosenNote(param.source, value, sourceEntry)
return (
<div style={{ marginBottom: 12 }}>