Merge pull request #1866 from deepseek-harness/feature/workspace-picker-composer
feat(web): open workspace picker from the composer
This commit is contained in:
@@ -82,10 +82,20 @@ describe('resident composer', () => {
|
||||
runtime.slots.installLocale(locale)
|
||||
await runtime.root.declare(LAYOUT_CHILDREN, AppRoot)
|
||||
await runtime.mount({ inject: [...inject], apply })
|
||||
runtime.slots.register({ name: 'conversation.hero.workspace' }, WorkspaceProbe)
|
||||
const view = runtime.renderRoot()
|
||||
const textarea = view.container.querySelector('textarea')
|
||||
expect(textarea).not.toBeNull()
|
||||
expect(textarea!.disabled).toBe(true)
|
||||
expect(textarea!.disabled).toBe(false)
|
||||
expect(textarea!.readOnly).toBe(true)
|
||||
expect(textarea!.getAttribute('aria-haspopup')).toBe('menu')
|
||||
expect(view.getByTestId('workspace-probe').textContent).toBe('false:0')
|
||||
fireEvent.click(textarea!)
|
||||
expect(view.getByTestId('workspace-probe').textContent).toBe('true:0')
|
||||
expect(textarea!.getAttribute('aria-expanded')).toBe('true')
|
||||
fireEvent.click(view.getByRole('button', { name: '选择工作区' }))
|
||||
fireEvent.keyDown(textarea!, { key: 'Enter' })
|
||||
expect(view.getByTestId('workspace-probe').textContent).toBe('true:0')
|
||||
expect(view.getByRole('button', { name: '选择工作区' })).toBeTruthy()
|
||||
await runtime.dispose()
|
||||
})
|
||||
@@ -111,7 +121,8 @@ describe('resident composer', () => {
|
||||
const textarea = view.container.querySelector('textarea')!
|
||||
const workspaceChip = view.getByRole('button', { name: '选择工作区' })
|
||||
const workspaceProbe = view.getByTestId('workspace-probe')
|
||||
expect(textarea.disabled).toBe(true)
|
||||
expect(textarea.disabled).toBe(false)
|
||||
expect(textarea.readOnly).toBe(true)
|
||||
|
||||
fireEvent.click(workspaceChip)
|
||||
fireEvent.click(workspaceProbe)
|
||||
@@ -131,6 +142,7 @@ describe('resident composer', () => {
|
||||
expect(view.getByTestId('workspace-probe')).toBe(workspaceProbe)
|
||||
expect(workspaceProbe.textContent).toBe('true:1')
|
||||
expect(textarea.disabled).toBe(false)
|
||||
expect(textarea.readOnly).toBe(false)
|
||||
await runtime.dispose()
|
||||
})
|
||||
|
||||
|
||||
@@ -60,6 +60,9 @@ interface BenchOptions {
|
||||
running?: boolean
|
||||
subagent?: Exclude<ConversationSnapshot['subagent'], null>
|
||||
disabled?: boolean
|
||||
inert?: boolean
|
||||
workspacePickerOpen?: boolean
|
||||
onRequestWorkspace?: () => void
|
||||
promptError?: ConversationSnapshot['promptError']
|
||||
/** Authoritative queue rows served to the machine overlay (empty = none). */
|
||||
queue?: ConversationSnapshot['queue']
|
||||
@@ -168,6 +171,9 @@ function bench(over?: BenchOptions) {
|
||||
t: over?.t ?? makeTranslate(zh, commonZh),
|
||||
renderSlot,
|
||||
variant: over?.variant ?? 'composer',
|
||||
...(over?.inert === true ? { disabled: true } : {}),
|
||||
...(over?.workspacePickerOpen !== undefined ? { workspacePickerOpen: over.workspacePickerOpen } : {}),
|
||||
...(over?.onRequestWorkspace !== undefined ? { onRequestWorkspace: over.onRequestWorkspace } : {}),
|
||||
...(over?.placeholder !== undefined ? { placeholder: over.placeholder } : {}),
|
||||
...(over?.accessory !== undefined ? { accessory: over.accessory } : {}),
|
||||
...(over?.overlay !== undefined ? { overlay: over.overlay } : {}),
|
||||
@@ -785,6 +791,40 @@ describe('running and lock semantics', () => {
|
||||
expect(custom.textarea.placeholder).toBe('Custom placeholder')
|
||||
})
|
||||
|
||||
it('the inert textarea opens the Workspace picker by pointer or keyboard', () => {
|
||||
const onRequestWorkspace = vi.fn()
|
||||
const { view, textarea } = bench({
|
||||
inert: true,
|
||||
workspacePickerOpen: false,
|
||||
onRequestWorkspace,
|
||||
placeholder: '选择一个工作区开始',
|
||||
})
|
||||
expect(textarea.disabled).toBe(false)
|
||||
expect(textarea.readOnly).toBe(true)
|
||||
expect(textarea.getAttribute('aria-haspopup')).toBe('menu')
|
||||
expect(textarea.getAttribute('aria-expanded')).toBe('false')
|
||||
expect((view.getByLabelText('命令') as HTMLButtonElement).disabled).toBe(true)
|
||||
|
||||
fireEvent.click(textarea)
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' })
|
||||
fireEvent.keyDown(textarea, { key: ' ' })
|
||||
expect(onRequestWorkspace).toHaveBeenCalledTimes(3)
|
||||
|
||||
// The WHOLE capsule is the pick target, and its pointerdown never reaches
|
||||
// the document — the open picker's outside-close must not race the reopen.
|
||||
const card = view.container.querySelector('[data-composer-card]') as HTMLElement
|
||||
fireEvent.click(card)
|
||||
expect(onRequestWorkspace).toHaveBeenCalledTimes(4)
|
||||
const onDocumentPointerDown = vi.fn()
|
||||
document.addEventListener('pointerdown', onDocumentPointerDown)
|
||||
try {
|
||||
fireEvent.pointerDown(card)
|
||||
} finally {
|
||||
document.removeEventListener('pointerdown', onDocumentPointerDown)
|
||||
}
|
||||
expect(onDocumentPointerDown).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
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('描述你的任务以生成计划')
|
||||
|
||||
@@ -296,8 +296,12 @@ describe('ConversationRoot resident composer', () => {
|
||||
composerBlock: { reason: 'select a model first' },
|
||||
})
|
||||
const box = b.view.getByRole('textbox') as HTMLTextAreaElement
|
||||
expect(box.disabled).toBe(true)
|
||||
expect(box.disabled).toBe(false)
|
||||
expect(box.readOnly).toBe(true)
|
||||
expect(box.getAttribute('aria-haspopup')).toBe('menu')
|
||||
expect(box.placeholder).not.toBe('select a model first')
|
||||
const modelSeat = b.seatOwners.filter(call => call.key === 'conversation.input.model').at(-1)?.owner
|
||||
expect(modelSeat).toEqual({ locked: true })
|
||||
})
|
||||
|
||||
it('keeps composer text in the machine, mirrors to the chat store, and submits through the sink', () => {
|
||||
|
||||
Reference in New Issue
Block a user