mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
feat(remote-control): 优化 Web 展示、状态同步与桥接控制流程 (#288)
Co-authored-by: chengzifeng <chengzifeng@meituan.com>
This commit is contained in:
17
src/__tests__/commandsBridgeSafety.test.ts
Normal file
17
src/__tests__/commandsBridgeSafety.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import { isBridgeSafeCommand } from '../commands.js'
|
||||
import clear from '../commands/clear/index.js'
|
||||
import plan from '../commands/plan/index.js'
|
||||
import proactive from '../commands/proactive.js'
|
||||
|
||||
describe('isBridgeSafeCommand', () => {
|
||||
test('allows bridge-safe local-jsx commands', () => {
|
||||
expect(isBridgeSafeCommand(plan)).toBe(true)
|
||||
expect(isBridgeSafeCommand(proactive)).toBe(true)
|
||||
})
|
||||
|
||||
test('continues allowing explicit local bridge-safe commands', () => {
|
||||
expect(isBridgeSafeCommand(clear)).toBe(true)
|
||||
})
|
||||
})
|
||||
121
src/__tests__/handlePromptSubmit.test.ts
Normal file
121
src/__tests__/handlePromptSubmit.test.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { beforeEach, describe, expect, mock, test } from 'bun:test'
|
||||
import { createAbortController } from '../utils/abortController'
|
||||
import { QueryGuard } from '../utils/QueryGuard'
|
||||
import { handlePromptSubmit } from '../utils/handlePromptSubmit'
|
||||
import { getCommandQueue, resetCommandQueue } from '../utils/messageQueueManager'
|
||||
|
||||
function createBaseParams() {
|
||||
const queryGuard = new QueryGuard()
|
||||
queryGuard.reserve()
|
||||
|
||||
return {
|
||||
queryGuard,
|
||||
helpers: {
|
||||
setCursorOffset: mock((_offset: number) => {}),
|
||||
clearBuffer: mock(() => {}),
|
||||
resetHistory: mock(() => {}),
|
||||
},
|
||||
onInputChange: mock((_value: string) => {}),
|
||||
setPastedContents: mock((_value: unknown) => {}),
|
||||
setToolJSX: mock((_value: unknown) => {}),
|
||||
getToolUseContext: mock(() => {
|
||||
throw new Error('getToolUseContext should not be called in queued path')
|
||||
}),
|
||||
messages: [],
|
||||
mainLoopModel: 'claude-sonnet-4-6',
|
||||
ideSelection: undefined,
|
||||
querySource: 'repl_main_thread' as any,
|
||||
commands: [],
|
||||
setUserInputOnProcessing: mock((_prompt?: string) => {}),
|
||||
setAbortController: mock((_abortController: AbortController | null) => {}),
|
||||
onQuery: mock(
|
||||
async () => undefined,
|
||||
) as unknown as (
|
||||
...args: unknown[]
|
||||
) => Promise<void>,
|
||||
setAppState: mock((_updater: unknown) => {}),
|
||||
}
|
||||
}
|
||||
|
||||
describe('handlePromptSubmit', () => {
|
||||
beforeEach(() => {
|
||||
resetCommandQueue()
|
||||
})
|
||||
|
||||
test('aborts the current turn when only cancel-interrupt tools are running', async () => {
|
||||
const params = createBaseParams()
|
||||
const abortController = createAbortController()
|
||||
|
||||
await handlePromptSubmit({
|
||||
...params,
|
||||
input: 'hello',
|
||||
mode: 'prompt',
|
||||
pastedContents: {},
|
||||
abortController,
|
||||
streamMode: 'normal' as any,
|
||||
hasInterruptibleToolInProgress: true,
|
||||
isExternalLoading: false,
|
||||
})
|
||||
|
||||
expect(abortController.signal.aborted).toBe(true)
|
||||
expect(abortController.signal.reason).toBe('interrupt')
|
||||
expect(getCommandQueue()).toHaveLength(1)
|
||||
expect(getCommandQueue()[0]).toMatchObject({
|
||||
value: 'hello',
|
||||
preExpansionValue: 'hello',
|
||||
mode: 'prompt',
|
||||
})
|
||||
expect(params.onInputChange).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
test('queues the input without aborting when a blocking tool is running', async () => {
|
||||
const params = createBaseParams()
|
||||
const abortController = createAbortController()
|
||||
|
||||
await handlePromptSubmit({
|
||||
...params,
|
||||
input: 'hello',
|
||||
mode: 'prompt',
|
||||
pastedContents: {},
|
||||
abortController,
|
||||
streamMode: 'normal' as any,
|
||||
hasInterruptibleToolInProgress: false,
|
||||
isExternalLoading: false,
|
||||
})
|
||||
|
||||
expect(abortController.signal.aborted).toBe(false)
|
||||
expect(getCommandQueue()).toHaveLength(1)
|
||||
expect(getCommandQueue()[0]).toMatchObject({
|
||||
value: 'hello',
|
||||
preExpansionValue: 'hello',
|
||||
mode: 'prompt',
|
||||
})
|
||||
})
|
||||
|
||||
test('preserves bridgeOrigin when a remote slash command is queued during external loading', async () => {
|
||||
const params = createBaseParams()
|
||||
const abortController = createAbortController()
|
||||
|
||||
await handlePromptSubmit({
|
||||
...params,
|
||||
input: '/proactive',
|
||||
mode: 'prompt',
|
||||
pastedContents: {},
|
||||
abortController,
|
||||
streamMode: 'normal' as any,
|
||||
hasInterruptibleToolInProgress: true,
|
||||
isExternalLoading: true,
|
||||
skipSlashCommands: true,
|
||||
bridgeOrigin: true,
|
||||
})
|
||||
|
||||
expect(getCommandQueue()).toHaveLength(1)
|
||||
expect(getCommandQueue()[0]).toMatchObject({
|
||||
value: '/proactive',
|
||||
preExpansionValue: '/proactive',
|
||||
mode: 'prompt',
|
||||
skipSlashCommands: true,
|
||||
bridgeOrigin: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user