feat: add ask notifications
This commit is contained in:
@@ -2,6 +2,10 @@ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
||||
import { Editor, type EditorTheme } from "@mariozechner/pi-tui";
|
||||
import type { AskConfig } from "../config/schema.ts";
|
||||
import { getAskConfigStore } from "../config/store.ts";
|
||||
import {
|
||||
createQuestionWaitingNotification,
|
||||
notifyQuestionWaiting,
|
||||
} from "../notifications.ts";
|
||||
import { createInitialState } from "../state/create.ts";
|
||||
import {
|
||||
getEditorDraft,
|
||||
@@ -134,6 +138,9 @@ function createAskFlowController(
|
||||
|
||||
controller.editor.onSubmit = (value) => submitEditor(controller, value);
|
||||
syncSelection(controller);
|
||||
notifyCurrentQuestion(controller).catch(() => {
|
||||
// Notification failures are best-effort and must not affect the ask flow.
|
||||
});
|
||||
|
||||
return {
|
||||
render: (width: number) => renderController(controller, width),
|
||||
@@ -457,6 +464,19 @@ function refresh(controller: AskFlowController) {
|
||||
controller.tui.requestRender();
|
||||
}
|
||||
|
||||
async function notifyCurrentQuestion(
|
||||
controller: AskFlowController
|
||||
): Promise<void> {
|
||||
const question = getCurrentQuestion(controller.state);
|
||||
if (!question) {
|
||||
return;
|
||||
}
|
||||
await notifyQuestionWaiting(
|
||||
controller.config,
|
||||
createQuestionWaitingNotification(question)
|
||||
);
|
||||
}
|
||||
|
||||
function maybeFinish(controller: AskFlowController) {
|
||||
if (controller.state.completed) {
|
||||
controller.done(toAskResult(controller.state));
|
||||
|
||||
+42
-25
@@ -29,7 +29,7 @@ interface AskSettingsListOptions {
|
||||
const DESCRIPTION_LINE_COUNT = 3;
|
||||
const COMPACT_WIDTH = 40;
|
||||
|
||||
const BEHAVIOUR_SETTINGS = [
|
||||
const SETTINGS = [
|
||||
{
|
||||
description:
|
||||
"Auto-submit completed ask flows when no question or option notes were added.",
|
||||
@@ -48,6 +48,12 @@ const BEHAVIOUR_SETTINGS = [
|
||||
key: "doublePressReviewShortcuts",
|
||||
label: "Double-press review shortcuts",
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Emit one external notification when the ask flow opens and waits for input.",
|
||||
key: "notifications.enabled",
|
||||
label: "Notifications",
|
||||
},
|
||||
{
|
||||
description: "Show footer keymap hints at the bottom of the ask flow.",
|
||||
key: "showFooterHints",
|
||||
@@ -55,7 +61,7 @@ const BEHAVIOUR_SETTINGS = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
type BehaviourKey = (typeof BEHAVIOUR_SETTINGS)[number]["key"];
|
||||
type SettingKey = (typeof SETTINGS)[number]["key"];
|
||||
|
||||
export class AskSettingsList {
|
||||
private closed = false;
|
||||
@@ -90,21 +96,19 @@ export class AskSettingsList {
|
||||
}
|
||||
if (matchesKey(data, Key.up)) {
|
||||
this.focusIndex =
|
||||
this.focusIndex === 0
|
||||
? BEHAVIOUR_SETTINGS.length - 1
|
||||
: this.focusIndex - 1;
|
||||
this.focusIndex === 0 ? SETTINGS.length - 1 : this.focusIndex - 1;
|
||||
this.tui.requestRender();
|
||||
return;
|
||||
}
|
||||
if (matchesKey(data, Key.down)) {
|
||||
this.focusIndex = (this.focusIndex + 1) % BEHAVIOUR_SETTINGS.length;
|
||||
this.focusIndex = (this.focusIndex + 1) % SETTINGS.length;
|
||||
this.tui.requestRender();
|
||||
return;
|
||||
}
|
||||
if (matchesKey(data, Key.enter) || data === " ") {
|
||||
const setting = BEHAVIOUR_SETTINGS[this.focusIndex];
|
||||
const setting = SETTINGS[this.focusIndex];
|
||||
if (setting) {
|
||||
this.saveBehaviour(setting.key, !this.config.behaviour[setting.key]);
|
||||
this.saveSetting(setting.key, !this.getSettingValue(setting.key));
|
||||
this.tui.requestRender();
|
||||
}
|
||||
}
|
||||
@@ -139,7 +143,7 @@ export class AskSettingsList {
|
||||
);
|
||||
|
||||
lines.push(this.line("", innerWidth));
|
||||
for (const [index, setting] of BEHAVIOUR_SETTINGS.entries()) {
|
||||
for (const [index, setting] of SETTINGS.entries()) {
|
||||
for (const settingLine of this.renderSetting(
|
||||
setting,
|
||||
index,
|
||||
@@ -147,7 +151,7 @@ export class AskSettingsList {
|
||||
)) {
|
||||
lines.push(this.line(settingLine, innerWidth));
|
||||
}
|
||||
if (innerWidth < COMPACT_WIDTH && index < BEHAVIOUR_SETTINGS.length - 1) {
|
||||
if (innerWidth < COMPACT_WIDTH && index < SETTINGS.length - 1) {
|
||||
lines.push(this.line("", innerWidth));
|
||||
}
|
||||
}
|
||||
@@ -185,7 +189,7 @@ export class AskSettingsList {
|
||||
}
|
||||
|
||||
private appendSelectedDescription(lines: string[], innerWidth: number): void {
|
||||
const selectedSetting = BEHAVIOUR_SETTINGS[this.focusIndex];
|
||||
const selectedSetting = SETTINGS[this.focusIndex];
|
||||
if (!selectedSetting) {
|
||||
return;
|
||||
}
|
||||
@@ -236,18 +240,16 @@ export class AskSettingsList {
|
||||
}
|
||||
|
||||
private renderSetting(
|
||||
setting: (typeof BEHAVIOUR_SETTINGS)[number],
|
||||
setting: (typeof SETTINGS)[number],
|
||||
index: number,
|
||||
innerWidth: number
|
||||
): string[] {
|
||||
const selected = index === this.focusIndex;
|
||||
const prefix = selected ? this.theme.fg("accent", "❯ ") : " ";
|
||||
const continuationPrefix = " ";
|
||||
const value = this.renderValue(
|
||||
this.config.behaviour[setting.key],
|
||||
selected
|
||||
);
|
||||
const valueWidth = this.config.behaviour[setting.key] ? 4 : 5;
|
||||
const enabled = this.getSettingValue(setting.key);
|
||||
const value = this.renderValue(enabled, selected);
|
||||
const valueWidth = enabled ? 4 : 5;
|
||||
const labelWidth = Math.max(
|
||||
1,
|
||||
innerWidth - visibleWidth(prefix) - valueWidth - 2
|
||||
@@ -272,16 +274,31 @@ export class AskSettingsList {
|
||||
return `${this.theme.fg("dim", "[")}${styledValue}${this.theme.fg("dim", "]")}`;
|
||||
}
|
||||
|
||||
private saveBehaviour(key: BehaviourKey, enabled: boolean): void {
|
||||
private getSettingValue(key: SettingKey): boolean {
|
||||
return key === "notifications.enabled"
|
||||
? this.config.notifications.enabled
|
||||
: this.config.behaviour[key];
|
||||
}
|
||||
|
||||
private saveSetting(key: SettingKey, enabled: boolean): void {
|
||||
this.error = undefined;
|
||||
const previousConfig = this.config;
|
||||
const nextConfig = {
|
||||
...this.config,
|
||||
behaviour: {
|
||||
...this.config.behaviour,
|
||||
[key]: enabled,
|
||||
},
|
||||
};
|
||||
const nextConfig =
|
||||
key === "notifications.enabled"
|
||||
? {
|
||||
...this.config,
|
||||
notifications: {
|
||||
...this.config.notifications,
|
||||
enabled,
|
||||
},
|
||||
}
|
||||
: {
|
||||
...this.config,
|
||||
behaviour: {
|
||||
...this.config.behaviour,
|
||||
[key]: enabled,
|
||||
},
|
||||
};
|
||||
this.config = nextConfig;
|
||||
this.onSave(nextConfig)
|
||||
.then((savedConfig) => {
|
||||
|
||||
Reference in New Issue
Block a user