feat: add review shortcut confirmation setting
This commit is contained in:
@@ -35,7 +35,7 @@ test("dirty flow state is true for saved answers or editor drafts", () => {
|
||||
assert.equal(hasDirtyFlowState(noteEditing, "draft note"), true);
|
||||
});
|
||||
|
||||
test("confirm dismiss when dirty is opt-in and respects dirty state", () => {
|
||||
test("confirm dismiss when dirty respects dirty state and config", () => {
|
||||
const state = applyNumberShortcut(
|
||||
createInitialState({
|
||||
questions: [
|
||||
@@ -48,20 +48,20 @@ test("confirm dismiss when dirty is opt-in and respects dirty state", () => {
|
||||
}),
|
||||
1
|
||||
);
|
||||
const enabledConfig = {
|
||||
const disabledConfig = {
|
||||
...DEFAULT_ASK_CONFIG,
|
||||
behaviour: {
|
||||
...DEFAULT_ASK_CONFIG.behaviour,
|
||||
confirmDismissWhenDirty: true,
|
||||
confirmDismissWhenDirty: false,
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
shouldConfirmDirtyDismiss({ config: DEFAULT_ASK_CONFIG, state }),
|
||||
shouldConfirmDirtyDismiss({ config: disabledConfig, state }),
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
shouldConfirmDirtyDismiss({ config: enabledConfig, state }),
|
||||
shouldConfirmDirtyDismiss({ config: DEFAULT_ASK_CONFIG, state }),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ test("wide header keeps all tabs and framing arrows on the tab row", () => {
|
||||
editor: mockEditor(),
|
||||
});
|
||||
|
||||
assert.equal(lines[3], " ← ☐ One ☐ Two ✔ Submit →");
|
||||
assert.equal(lines[3], " ← ☐ One ☐ Two ☰ Review →");
|
||||
});
|
||||
|
||||
test("narrow tab strip keeps active middle tab visible", () => {
|
||||
@@ -143,7 +143,7 @@ test("narrow tab strip keeps submit tab visible when active", () => {
|
||||
editor: mockEditor(),
|
||||
});
|
||||
|
||||
assert.equal(lines[3], " ← ✔ Submit →");
|
||||
assert.equal(lines[3], " ← ☰ Review →");
|
||||
});
|
||||
|
||||
test("tab strip avoids truncation at narrow boundary widths", () => {
|
||||
|
||||
@@ -111,6 +111,34 @@ test("submit screen stacks review above actions on narrow screens", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("submit screen can show a review shortcut hint below the actions", () => {
|
||||
const state = createInitialState({
|
||||
questions: [
|
||||
{
|
||||
id: "q1",
|
||||
label: "Color",
|
||||
prompt: "Pick one color.",
|
||||
options: [{ value: "blue", label: "Blue" }],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const lines: string[] = [];
|
||||
renderSubmitScreen(
|
||||
lines,
|
||||
state,
|
||||
plainTheme(),
|
||||
UI_DIMENSIONS.submitWideMinWidth - 14,
|
||||
"Press 1, 2, or 3 twice to confirm a review action."
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
lines.includes(" Press 1, 2, or 3 twice to confirm a review"),
|
||||
true
|
||||
);
|
||||
assert.equal(lines.includes(" action."), true);
|
||||
});
|
||||
|
||||
test("submit screen shows notes only for answered questions in submit mode", () => {
|
||||
let state = createInitialState({
|
||||
questions: [
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
getReviewShortcutHint,
|
||||
resolveReviewShortcutDoublePress,
|
||||
} from "../src/ui/review-shortcuts.ts";
|
||||
|
||||
test("review shortcut double press requires the same digit twice", () => {
|
||||
const first = resolveReviewShortcutDoublePress(2);
|
||||
assert.equal(first.confirmed, false);
|
||||
assert.equal(first.actionIndex, 1);
|
||||
assert.equal(first.pendingActionIndex, 1);
|
||||
|
||||
const second = resolveReviewShortcutDoublePress(2, first.pendingActionIndex);
|
||||
assert.equal(second.confirmed, true);
|
||||
assert.equal(second.actionIndex, 1);
|
||||
assert.equal(second.pendingActionIndex, undefined);
|
||||
});
|
||||
|
||||
test("review shortcut double press switches pending action when digit changes", () => {
|
||||
const first = resolveReviewShortcutDoublePress(1);
|
||||
const second = resolveReviewShortcutDoublePress(3, first.pendingActionIndex);
|
||||
|
||||
assert.equal(second.confirmed, false);
|
||||
assert.equal(second.actionIndex, 2);
|
||||
assert.equal(second.pendingActionIndex, 2);
|
||||
});
|
||||
|
||||
test("review shortcut hint reflects pending confirmation state", () => {
|
||||
assert.equal(
|
||||
getReviewShortcutHint(),
|
||||
"Press 1, 2, or 3 twice to confirm a review action."
|
||||
);
|
||||
assert.equal(getReviewShortcutHint(1), "Press 2 again to Elaborate.");
|
||||
});
|
||||
@@ -7,7 +7,8 @@ import { AskSettingsModal } from "../src/ui/settings-modal.ts";
|
||||
const savedConfig: AskConfig = {
|
||||
behaviour: {
|
||||
autoSubmitWhenAnsweredWithoutNotes: false,
|
||||
confirmDismissWhenDirty: false,
|
||||
confirmDismissWhenDirty: true,
|
||||
doublePressReviewShortcuts: true,
|
||||
showFooterHints: true,
|
||||
},
|
||||
keymaps: {
|
||||
@@ -59,12 +60,15 @@ function createModal(
|
||||
test("settings modal renders compactly on phone-width screens", () => {
|
||||
const modal = createModal();
|
||||
const lines = modal.render(28);
|
||||
const text = lines.join("\n");
|
||||
|
||||
assert(lines.every((line) => visibleWidth(line) <= 28));
|
||||
assert(lines.join("\n").includes("@eko24ive/pi-ask"));
|
||||
assert(lines.length <= 24);
|
||||
assert(text.includes("@eko24ive/pi-ask"));
|
||||
assert(lines[1]?.includes("Keymaps"));
|
||||
assert(lines.join("\n").includes("/reload after editing."));
|
||||
assert(lines.join("\n").includes("Open ask settings modal"));
|
||||
assert(text.includes("Edit keymaps there, then"));
|
||||
assert(text.includes("? settings"));
|
||||
assert(text.includes("Esc cancel"));
|
||||
assert.equal(lines[0]?.includes("├"), false);
|
||||
});
|
||||
|
||||
@@ -77,12 +81,23 @@ test("settings modal renders behaviour tab with checkbox and dirty notice", () =
|
||||
|
||||
assert(text.includes("Behaviour"));
|
||||
assert(text.includes("[x] Auto-submit when answered without notes"));
|
||||
assert(text.includes("[ ] Confirm dismiss when dirty"));
|
||||
assert(text.includes("[x] Confirm dismiss when dirty"));
|
||||
assert(text.includes("[x] Double-press review shortcuts"));
|
||||
assert(text.includes("[x] Show footer hints"));
|
||||
assert(text.includes("Unsaved changes. Press Ctrl+S to save."));
|
||||
assert(text.includes("Ctrl+S save · Esc/Ctrl+C/? twice discard"));
|
||||
});
|
||||
|
||||
test("settings modal keeps behaviour tab compact on narrow screens", () => {
|
||||
const modal = createModal();
|
||||
modal.handleInput("\t");
|
||||
const lines = modal.render(36);
|
||||
|
||||
assert(lines.every((line) => visibleWidth(line) <= 36));
|
||||
assert(lines.length <= 21);
|
||||
assert(lines.join("\n").includes("Show footer hints"));
|
||||
});
|
||||
|
||||
test("settings modal keymaps tab shows invalid-keymaps session warning", () => {
|
||||
const modal = new AskSettingsModal(plainTheme(), {
|
||||
configPath: "/tmp/eko24ive-pi-ask.json",
|
||||
@@ -135,7 +150,8 @@ test("settings modal saves and clears dirty state", async () => {
|
||||
|
||||
const text = modal.render(72).join("\n");
|
||||
assert.equal(saved?.behaviour.autoSubmitWhenAnsweredWithoutNotes, true);
|
||||
assert.equal(saved?.behaviour.confirmDismissWhenDirty, false);
|
||||
assert.equal(saved?.behaviour.confirmDismissWhenDirty, true);
|
||||
assert.equal(saved?.behaviour.doublePressReviewShortcuts, true);
|
||||
assert.equal(saved?.behaviour.showFooterHints, true);
|
||||
assert(text.includes("Saved"));
|
||||
assert.equal(text.includes("Unsaved changes. Press Ctrl+S to save."), false);
|
||||
|
||||
Reference in New Issue
Block a user