feat: add answer extraction replay commands
This commit is contained in:
+23
-6
@@ -52,8 +52,16 @@ type Tui = CustomCallbackArgs[0];
|
||||
type Theme = CustomCallbackArgs[1];
|
||||
type Keybindings = CustomCallbackArgs[2];
|
||||
type Done = (result: AskResult) => void;
|
||||
interface AskFlowOptions {
|
||||
allowFreeform?: boolean;
|
||||
}
|
||||
|
||||
type AskFlowParams = AskParams &
|
||||
Pick<ExtensionContext, "cwd"> & { config: AskConfig; ctx: ExtensionContext };
|
||||
Pick<ExtensionContext, "cwd"> & {
|
||||
config: AskConfig;
|
||||
ctx: ExtensionContext;
|
||||
flowOptions: AskFlowOptions;
|
||||
};
|
||||
|
||||
interface AskFlowController {
|
||||
config: AskConfig;
|
||||
@@ -72,12 +80,19 @@ interface AskFlowController {
|
||||
|
||||
export async function runAskFlow(
|
||||
ctx: ExtensionContext,
|
||||
params: AskParams
|
||||
params: AskParams,
|
||||
options: AskFlowOptions = {}
|
||||
): Promise<AskResult> {
|
||||
const store = getAskConfigStore();
|
||||
const config = await store.getConfig();
|
||||
return ctx.ui.custom<AskResult>((...args) =>
|
||||
createAskFlowController(args, { ...params, config, cwd: ctx.cwd, ctx })
|
||||
createAskFlowController(args, {
|
||||
...params,
|
||||
config,
|
||||
cwd: ctx.cwd,
|
||||
ctx,
|
||||
flowOptions: options,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,7 +112,7 @@ function createAskFlowController(
|
||||
done,
|
||||
editor: createEditor(tui, theme, params.cwd),
|
||||
settingsOpen: false,
|
||||
state: createInitialState(params),
|
||||
state: createInitialState(params, params.flowOptions),
|
||||
suppressAutoInputForSelection: false,
|
||||
pendingReviewShortcutActionIndex: undefined,
|
||||
theme,
|
||||
@@ -321,8 +336,10 @@ function submitEditor(controller: AskFlowController, value: string) {
|
||||
if (nextState.activeTabIndex !== controller.state.activeTabIndex) {
|
||||
clearDismissNotice(controller);
|
||||
}
|
||||
controller.state = maybeAutoSubmitState(nextState, controller.config);
|
||||
controller.editor.setText("");
|
||||
controller.state = nextState;
|
||||
syncSelection(controller);
|
||||
controller.state = maybeAutoSubmitState(controller.state, controller.config);
|
||||
hydrateEditor(controller);
|
||||
refresh(controller);
|
||||
maybeFinish(controller);
|
||||
}
|
||||
|
||||
+24
-19
@@ -76,7 +76,7 @@ function renderStandardOption(
|
||||
|
||||
pushWrappedText(
|
||||
lines,
|
||||
`${row.index + 1}. ${row.prefix}${row.label}`,
|
||||
formatOptionLabel(row),
|
||||
context.width,
|
||||
context.theme,
|
||||
row.color,
|
||||
@@ -179,11 +179,16 @@ function renderOptionDetail(
|
||||
lines: string[],
|
||||
detail: OptionDetailModel | undefined,
|
||||
context: QuestionRenderContext,
|
||||
options: { suppressLeadingGap?: boolean } = {}
|
||||
options: { indent?: string; suppressLeadingGap?: boolean } = {}
|
||||
) {
|
||||
if (!detail) {
|
||||
return;
|
||||
}
|
||||
const indent = options.indent ?? " ";
|
||||
const padding =
|
||||
indent === " "
|
||||
? UI_DIMENSIONS.editorContentPadding
|
||||
: UI_DIMENSIONS.editorIndentedPadding;
|
||||
if (detail.withGap && !options.suppressLeadingGap) {
|
||||
lines.push("");
|
||||
}
|
||||
@@ -193,8 +198,8 @@ function renderOptionDetail(
|
||||
editor: context.editor,
|
||||
width: context.width,
|
||||
theme: context.theme,
|
||||
indent: " ",
|
||||
padding: UI_DIMENSIONS.editorIndentedPadding,
|
||||
indent,
|
||||
padding,
|
||||
placeholder: detail.placeholder,
|
||||
});
|
||||
return;
|
||||
@@ -205,7 +210,7 @@ function renderOptionDetail(
|
||||
note: detail.text,
|
||||
width: context.width,
|
||||
theme: context.theme,
|
||||
indent: " ",
|
||||
indent,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -215,8 +220,8 @@ function renderOptionDetail(
|
||||
context.width,
|
||||
context.theme,
|
||||
"muted",
|
||||
" ",
|
||||
" "
|
||||
indent,
|
||||
indent
|
||||
);
|
||||
}
|
||||
|
||||
@@ -244,29 +249,29 @@ function renderEditorWithIndent(args: {
|
||||
});
|
||||
}
|
||||
|
||||
function formatOptionLabel(row: OptionRowModel): string {
|
||||
return row.isFreeformOnly
|
||||
? row.label
|
||||
: `${row.index + 1}. ${row.prefix}${row.label}`;
|
||||
}
|
||||
|
||||
function renderInteractiveCustomOption(
|
||||
lines: string[],
|
||||
row: OptionRowModel,
|
||||
context: QuestionRenderContext
|
||||
) {
|
||||
const indent = row.isFreeformOnly ? " " : row.pointer;
|
||||
pushWrappedText(
|
||||
lines,
|
||||
`${row.index + 1}. ${row.prefix}${row.label}`,
|
||||
formatOptionLabel(row),
|
||||
context.width,
|
||||
context.theme,
|
||||
row.color,
|
||||
row.pointer,
|
||||
" ".repeat(visibleWidth(row.pointer))
|
||||
indent,
|
||||
" ".repeat(visibleWidth(indent))
|
||||
);
|
||||
renderEditorWithIndent({
|
||||
lines,
|
||||
editor: context.editor,
|
||||
width: context.width,
|
||||
theme: context.theme,
|
||||
indent: " ",
|
||||
padding: UI_DIMENSIONS.editorIndentedPadding,
|
||||
placeholder:
|
||||
row.detail?.kind === "editor" ? row.detail.placeholder : "Type answer...",
|
||||
renderOptionDetail(lines, row.detail, context, {
|
||||
indent: row.isFreeformOnly ? " " : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface OptionRowModel {
|
||||
detail?: OptionDetailModel;
|
||||
index: number;
|
||||
isCustom: boolean;
|
||||
isFreeformOnly: boolean;
|
||||
label: string;
|
||||
pointer: string;
|
||||
prefix: string;
|
||||
@@ -89,6 +90,7 @@ function buildOptionRowModel(
|
||||
const answered = option.isCustomOption
|
||||
? !!answer?.customText
|
||||
: isOptionSelected(answer, option.value);
|
||||
const pointer = getOptionPointer(option, selected);
|
||||
return {
|
||||
color: getOptionColor(answered, selected),
|
||||
description: option.description,
|
||||
@@ -98,13 +100,21 @@ function buildOptionRowModel(
|
||||
: buildOptionDetailModel(context, option, selected),
|
||||
index,
|
||||
isCustom: !!option.isCustomOption,
|
||||
isFreeformOnly: !!option.isFreeformOnlyOption,
|
||||
label: option.label,
|
||||
pointer: selected ? "❯ " : " ",
|
||||
pointer,
|
||||
prefix: getOptionPrefix(question.type, option, answered),
|
||||
selected,
|
||||
};
|
||||
}
|
||||
|
||||
function getOptionPointer(option: AskDisplayOption, selected: boolean): string {
|
||||
if (option.isFreeformOnlyOption) {
|
||||
return "";
|
||||
}
|
||||
return selected ? "❯ " : " ";
|
||||
}
|
||||
|
||||
function buildOptionDetailModel(
|
||||
context: QuestionRenderContext,
|
||||
option: AskDisplayOption | undefined,
|
||||
@@ -139,11 +149,15 @@ function buildOptionDetailModel(
|
||||
return {
|
||||
kind: "editor",
|
||||
placeholder: UI_TEXT.editorPlaceholderInput,
|
||||
withGap: false,
|
||||
withGap: !!option.isFreeformOnlyOption,
|
||||
};
|
||||
}
|
||||
if (customText) {
|
||||
return { kind: "custom-text", text: customText, withGap: false };
|
||||
return {
|
||||
kind: "custom-text",
|
||||
text: customText,
|
||||
withGap: !!option.isFreeformOnlyOption,
|
||||
};
|
||||
}
|
||||
if (note) {
|
||||
return { kind: "saved-note", text: note, withGap: false };
|
||||
|
||||
Reference in New Issue
Block a user