refactor: add promptEnd event

This commit is contained in:
hypercross 2026-04-04 00:59:40 +08:00
parent 697d23e932
commit 6c8d6e0790
3 changed files with 33 additions and 24 deletions

View File

@ -60,25 +60,14 @@ export class GameHost<TState extends Record<string, unknown>> {
this._activePromptSchema.value = activePrompt?.schema ?? null;
};
// Wrap _tryCommit to update schema after commit
const originalTryCommit = this.commands._tryCommit.bind(this.commands);
(this.commands as any)._tryCommit = (input: string) => {
const result = originalTryCommit(input);
updateSchema();
return result;
};
// Wrap _cancel to update schema after cancel
const originalCancel = this.commands._cancel.bind(this.commands);
(this.commands as any)._cancel = (reason?: string) => {
originalCancel(reason);
updateSchema();
};
this.commands.on('prompt', () => {
updateSchema();
});
this.commands.on('promptEnd', () => {
updateSchema();
});
updateSchema();
}

View File

@ -1,5 +1,5 @@
import type { Command, CommandSchema } from './types';
import type {CommandResult, CommandRunner, CommandRunnerContext, PromptEvent} from './command-runner';
import type {CommandResult, CommandRunner, CommandRunnerContext, CommandRunnerEvents, PromptEvent} from './command-runner';
import { parseCommand } from './command-parse';
import { applyCommandSchema } from './command-validate';
import { parseCommandSchema } from './schema-parse';
@ -39,7 +39,8 @@ export function getCommand<TContext>(
return registry.get(name);
}
type Listener = (e: PromptEvent) => void;
type PromptListener = (e: PromptEvent) => void;
type PromptEndListener = () => void;
export type CommandRunnerContextExport<TContext> = CommandRunnerContext<TContext> & {
registry: CommandRegistry<TContext>;
@ -54,14 +55,29 @@ export function createCommandRunnerContext<TContext>(
registry: CommandRegistry<TContext>,
context: TContext
): CommandRunnerContextExport<TContext> {
const listeners = new Set<Listener>();
const promptListeners = new Set<PromptListener>();
const promptEndListeners = new Set<PromptEndListener>();
const on = (_event: 'prompt', listener: Listener) => {
listeners.add(listener);
const emitPromptEnd = () => {
for (const listener of promptEndListeners) {
listener();
}
};
const off = (_event: 'prompt', listener: Listener) => {
listeners.delete(listener);
const on = <T extends keyof CommandRunnerEvents>(_event: T, listener: (e: CommandRunnerEvents[T]) => void) => {
if (_event === 'prompt') {
promptListeners.add(listener as PromptListener);
} else {
promptEndListeners.add(listener as PromptEndListener);
}
};
const off = <T extends keyof CommandRunnerEvents>(_event: T, listener: (e: CommandRunnerEvents[T]) => void) => {
if (_event === 'prompt') {
promptListeners.delete(listener as PromptListener);
} else {
promptEndListeners.delete(listener as PromptEndListener);
}
};
let activePrompt: PromptEvent | null = null;
@ -71,6 +87,7 @@ export function createCommandRunnerContext<TContext>(
const result = activePrompt.tryCommit(commandOrInput);
if (result === null) {
activePrompt = null;
emitPromptEnd();
}
return result;
}
@ -81,6 +98,7 @@ export function createCommandRunnerContext<TContext>(
if (activePrompt) {
activePrompt.cancel(reason);
activePrompt = null;
emitPromptEnd();
}
};
@ -106,7 +124,7 @@ export function createCommandRunnerContext<TContext>(
};
activePrompt = { schema: resolvedSchema, tryCommit, cancel };
const event: PromptEvent = { schema: resolvedSchema, tryCommit, cancel };
for (const listener of listeners) {
for (const listener of promptListeners) {
listener(event);
}
});
@ -136,7 +154,7 @@ export function createCommandRunnerContext<TContext>(
get(){
if (!promptQueue) {
promptQueue = new AsyncQueue();
listeners.add(async (event) => {
promptListeners.add(async (event) => {
promptQueue.push(event);
});
}

View File

@ -17,6 +17,8 @@ export type PromptEvent = {
export type CommandRunnerEvents = {
prompt: PromptEvent;
/** 当 prompt 结束tryCommit 成功或 cancel时触发 */
promptEnd: void;
};
export type CommandResult<T=unknown> = {