feat(web): replace the plan select with a status chip and placeholder swap
Plan mode is entered through /plan only — the select control is retired. The conversation.input.plan seat (now right of the access-mode control) renders a read-only Plan chip while the projection's effective target is plan mode; its hover x executes /plan off, and the chip follows the folded target (appears on /plan immediately, disappears on /plan off) with frames correcting either way. While plan mode is targeted the composer textarea's placeholder switches to the plan-task wording — InputBar reads the same projection through the standard-kit useProjection (the TodoDock posture: a type-only key merge, no domain service edge), and owner placeholders still win.
This commit is contained in:
@@ -30,6 +30,8 @@ function snapshotOf(overrides: Partial<ConversationSnapshot> = {}): Conversation
|
||||
|
||||
interface BenchOptions {
|
||||
planEntry?: React.ReactNode
|
||||
/** The `plan` projection value the standard-kit useProjection serves. */
|
||||
plan?: { active: boolean; pending: boolean }
|
||||
modelEntry?: React.ReactNode
|
||||
/** Hot text-ref lexicon (injects a minimal slash stub exposing only lexicon()). */
|
||||
lexicon?: ReadonlyMap<'/' | '@', readonly string[]>
|
||||
@@ -88,7 +90,8 @@ function bench(over?: BenchOptions) {
|
||||
items: [], state: 'idle', phase: 'ready', error: null,
|
||||
baselinesReady: true, recentWorkspaceId: undefined,
|
||||
})),
|
||||
useProjection: (() => undefined),
|
||||
useProjection: ((_key: string, selector?: (v: unknown) => unknown) =>
|
||||
(selector ?? (v => v))(over?.plan)),
|
||||
useInput: bindSnapshotSelector(shell.state),
|
||||
inputActions: shell.actions,
|
||||
keyboard: shell,
|
||||
@@ -230,6 +233,20 @@ describe('running and lock semantics (queue cut 1)', () => {
|
||||
const custom = bench({ placeholder: 'Custom placeholder' })
|
||||
expect(custom.textarea.placeholder).toBe('Custom placeholder')
|
||||
})
|
||||
|
||||
it('the plan projection swaps the placeholder while its effective target is plan mode', () => {
|
||||
const active = bench({ plan: { active: true, pending: false } })
|
||||
expect(active.textarea.placeholder).toBe('describe your task to generate plan')
|
||||
// /plan just ran: pending entry already reads as the plan target.
|
||||
const entering = bench({ plan: { active: false, pending: true } })
|
||||
expect(entering.textarea.placeholder).toBe('describe your task to generate plan')
|
||||
// Pending exit: target is default again.
|
||||
const leaving = bench({ plan: { active: true, pending: true } })
|
||||
expect(leaving.textarea.placeholder).toBe('Message the agent')
|
||||
// Owner placeholder outranks the plan swap.
|
||||
const custom = bench({ plan: { active: true, pending: false }, placeholder: 'Custom placeholder' })
|
||||
expect(custom.textarea.placeholder).toBe('Custom placeholder')
|
||||
})
|
||||
})
|
||||
|
||||
describe('machine pending lock', () => {
|
||||
|
||||
Reference in New Issue
Block a user