Merge remote-tracking branch 'origin/master' into worktree-guifork

This commit is contained in:
imccyu
2026-07-28 16:12:12 +08:00
237 changed files with 3140 additions and 1659 deletions
@@ -129,6 +129,8 @@ async function scopedBench(register?: (slash: SlashService) => void) {
useInput: bindSnapshotSelector(shell.state),
inputActions: shell.actions,
keyboard: shell,
useNotices: bindSnapshotSelector(shell.notices),
useLexicon: bindSnapshotSelector(shell.lexicon),
renderSlot: (() => null) as InputBarProps['renderSlot'],
stop: vi.fn(),
variant: 'composer',
@@ -235,6 +237,35 @@ describe('scenario H: backspace breaks the token', () => {
})
})
describe('scenario: reference decoration lights up when the lexicon settles', () => {
it('a typed /name token gains the text-ref mark without further input once the roll goes hot', async () => {
let roll: readonly string[] | undefined
let notify: (() => void) | undefined
const b = await scopedBench((slash) => {
slash.registerSource({
trigger: '/', name: 'skill',
candidates: () => Promise.resolve([]),
onPick: () => undefined,
lexicon: () => roll,
subscribeLexicon: (_session: ClientSessionContext, listener: () => void) => {
notify = listener
return () => { notify = undefined }
},
} as never)
})
// Typed before the catalog settled: a plain token, no decoration.
b.type('/deploy now')
expect(b.view.container.querySelector('[data-decoration="text-ref"]')).toBeNull()
// The catalog settles (ui-skill's settle path fires the same notification).
act(() => {
roll = ['deploy']
notify?.()
})
const mark = b.view.container.querySelector('[data-decoration="text-ref"]')
expect(mark?.textContent).toBe('/deploy')
})
})
describe('scenario I: unknown /xyz + enter', () => {
it('adjudication misses in one hop and the whole line rides the default sink', async () => {
const b = await bench()