pi-bro 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +29 -15
- package/docs/README.md +32 -14
- package/docs/elevate.md +125 -0
- package/docs/examples.md +268 -0
- package/docs/features/advisor.md +70 -0
- package/docs/features/btw.md +51 -0
- package/docs/features/explain.md +92 -0
- package/docs/features/show.md +154 -0
- package/docs/getting-started.md +47 -0
- package/docs/pig-compatibility.md +1 -0
- package/docs/reference/commands.md +50 -0
- package/docs/reference/data-and-permissions.md +130 -0
- package/docs/reference/settings.md +108 -0
- package/docs/reference/troubleshooting.md +55 -0
- package/docs/user-guide.md +50 -900
- package/package.json +12 -3
- package/src/bro.ts +25 -0
- package/src/elevate-engine.ts +214 -0
- package/src/elevate.ts +597 -0
- package/src/explain-modal.ts +12 -6
- package/src/modal-kit.ts +35 -2
package/src/modal-kit.ts
CHANGED
|
@@ -71,6 +71,9 @@ export type FrameParts = {
|
|
|
71
71
|
notice?: string;
|
|
72
72
|
/** Omit when the body prints its own hints (config's SettingsList). */
|
|
73
73
|
hints?: string;
|
|
74
|
+
/** Lines rendered under the hints, e.g. a focusable action row. Wrapped by the caller; a line
|
|
75
|
+
* wider than the frame is truncated, so keep a single label narrower than the modal. */
|
|
76
|
+
footerAfterHints?: string;
|
|
74
77
|
/** false: size to the content instead of padding the body to the full height. */
|
|
75
78
|
fill?: boolean;
|
|
76
79
|
/** Body rows the notice must leave free; it shrinks to one line before taking them. */
|
|
@@ -94,10 +97,12 @@ export function renderFrame(theme: Theme, width: number, height: number, parts:
|
|
|
94
97
|
const footer = parts.footer ?? [];
|
|
95
98
|
// Wrapped, not truncated: Esc comes last in every hint line and must not be cut off.
|
|
96
99
|
const hints = parts.hints ? wrapTextWithAnsi(theme.fg("dim", parts.hints), inner) : [];
|
|
100
|
+
// The action row is already wrapped by the caller, so keep its own line breaks.
|
|
101
|
+
const belowHints = parts.footerAfterHints ? parts.footerAfterHints.split("\n").map((line) => truncateToWidth(line, inner, "")) : [];
|
|
97
102
|
const notice = parts.notice ? wrapTextWithAnsi(parts.notice, inner).slice(0, 3) : [];
|
|
98
|
-
while (notice.length > 1 && height - 5 - footer.length - hints.length - notice.length < (parts.minBody ?? 0)) notice.pop();
|
|
103
|
+
while (notice.length > 1 && height - 5 - footer.length - hints.length - belowHints.length - notice.length < (parts.minBody ?? 0)) notice.pop();
|
|
99
104
|
// The notice sits above the footer so it is dropped before a composer when space runs out.
|
|
100
|
-
const below = [...notice, ...footer, ...hints];
|
|
105
|
+
const below = [...notice, ...footer, ...hints, ...belowHints];
|
|
101
106
|
const bodyRows = Math.max(0, height - 5 - below.length);
|
|
102
107
|
const body = bodyRows ? parts.body(bodyRows, inner).slice(0, bodyRows) : [];
|
|
103
108
|
if (parts.fill !== false) while (body.length < bodyRows) body.push("");
|
|
@@ -174,6 +179,34 @@ export class Scroller {
|
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Render a focusable action row: the focused button carries a leading marker and accent colour, so a
|
|
184
|
+
* keypress always has exactly one visible target. Callers own focus, keys and activation; this only
|
|
185
|
+
* draws, which is why Guided Review can keep its own navigation and layout.
|
|
186
|
+
*/
|
|
187
|
+
export function buttonLabels(theme: Theme, labels: readonly string[], focused?: number): string[] {
|
|
188
|
+
return labels.map((label, index) => {
|
|
189
|
+
const text = `${index === focused ? "›" : ""}[${label}]`;
|
|
190
|
+
return index === focused ? theme.bold(theme.fg("accent", text)) : text;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Pack whole buttons onto lines with `visibleWidth`, never splitting one: a label broken mid-word
|
|
196
|
+
* makes the focused target ambiguous, which is the misclick this row exists to prevent.
|
|
197
|
+
*/
|
|
198
|
+
export function wrapButtons(buttons: readonly string[], width: number): string[] {
|
|
199
|
+
const lines: string[] = [];
|
|
200
|
+
let line = "";
|
|
201
|
+
for (const button of buttons) {
|
|
202
|
+
if (!line) { line = button; continue; }
|
|
203
|
+
if (visibleWidth(`${line} ${button}`) <= width) line += ` ${button}`;
|
|
204
|
+
else { lines.push(line); line = button; }
|
|
205
|
+
}
|
|
206
|
+
if (line) lines.push(line);
|
|
207
|
+
return lines;
|
|
208
|
+
}
|
|
209
|
+
|
|
177
210
|
/**
|
|
178
211
|
* Forward a pointer event to a pi-tui child drawn at `area` inside the overlay, in the child's own
|
|
179
212
|
* coordinates (without `area`, the event already is). Pi 0.84 components have no handleMouse, hence
|