@danypops/pi-jittor 0.4.0 → 0.5.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.
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Confirm/select prompts rendered with malevich-tui-components' own Dialog/TabMenu, inside
3
+ * jittor's own theme and frame -- replacing Pi's host-native `ctx.ui.confirm`/`ctx.ui.select`
4
+ * (a visually separate native prompt) for every jittor-owned confirmation and selection.
5
+ *
6
+ * TUI-only: every real caller (settings-tui.ts's runSettingsAction, observability/status.ts's
7
+ * runStatusAction/chooseOverride) is itself only ever invoked from within an already-`ctx.mode
8
+ * === "tui"` code path -- the non-TUI notify fallback returns before ever reaching one of these
9
+ * calls -- so there is no non-TUI branch to fall back to here.
10
+ *
11
+ * Budget entry (settings-tui.ts's editBudget) deliberately keeps Pi's native `ctx.ui.input`:
12
+ * malevich has no plain visible text-input component today -- only `MaskedInput` (renders mask
13
+ * glyphs only, built for secrets; wrong semantics for a token count a user wants to see as they
14
+ * type) and `Form` (owns focus/validation/layout but requires a host-supplied `FormFieldInput`,
15
+ * and `MaskedInput` is the only one malevich ships). Building a new visible-text-input component
16
+ * is out of scope here; this is a deliberate, documented decision, not an oversight.
17
+ */
18
+ import type { Route } from "@danypops/jittor";
19
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
20
+ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
21
+ import { Dialog, TabMenu, type TextMeasure } from "malevich-tui-components";
22
+
23
+ interface PromptTheme {
24
+ fg(color: string, text: string): string;
25
+ bold(text: string): string;
26
+ }
27
+
28
+ const hostTextMeasure: TextMeasure = { visibleWidth, truncateToWidth };
29
+
30
+ /**
31
+ * A bordered Dialog with exactly a Yes/No choice, resolving `true` for confirm, `false` for
32
+ * cancel -- including Escape, Ctrl+C, or the host dismissing the panel outright (`custom()`
33
+ * resolving `undefined`). A drop-in replacement for `ctx.ui.confirm(title, body)`.
34
+ */
35
+ export async function showConfirmDialog(
36
+ ctx: ExtensionCommandContext,
37
+ title: string,
38
+ body: string,
39
+ options: { confirmLabel?: string; cancelLabel?: string } = {},
40
+ ): Promise<boolean> {
41
+ const confirmLabel = options.confirmLabel ?? "Yes";
42
+ const cancelLabel = options.cancelLabel ?? "No";
43
+ const confirmed = await ctx.ui.custom<boolean>((_tui, theme: PromptTheme, _keybindings, done) => {
44
+ const dialog = new Dialog({
45
+ title,
46
+ body,
47
+ actions: [
48
+ { label: confirmLabel, key: "y", action: () => done(true) },
49
+ { label: cancelLabel, key: "n", action: () => done(false) },
50
+ ],
51
+ theme: {
52
+ border: (text) => theme.fg("borderMuted", text),
53
+ title: theme.bold,
54
+ body: (text) => text,
55
+ dim: (text) => theme.fg("dim", text),
56
+ },
57
+ measure: hostTextMeasure,
58
+ });
59
+ return {
60
+ invalidate: () => dialog.invalidate(),
61
+ render: (width: number) => dialog.render(width),
62
+ handleInput(data: string): void {
63
+ if (matchesKey(data, "ctrl+c")) {
64
+ done(false);
65
+ return;
66
+ }
67
+ dialog.handleInput(data);
68
+ },
69
+ };
70
+ });
71
+ return confirmed ?? false;
72
+ }
73
+
74
+ /**
75
+ * A TabMenu over one flat level -- every route is a leaf node, no drill-down -- resolving the
76
+ * selected route, or `undefined` on cancel (Escape at the root, Ctrl+C, or the host dismissing
77
+ * the panel outright). A drop-in replacement for `ctx.ui.select("Override route", labels)`
78
+ * followed by mapping the chosen label back to its route.
79
+ */
80
+ export async function showRouteOverrideMenu(
81
+ ctx: ExtensionCommandContext,
82
+ routes: Route[],
83
+ routeLabel: (route: Route) => string,
84
+ ): Promise<Route | undefined> {
85
+ return ctx.ui.custom<Route | undefined>((_tui, theme: PromptTheme, _keybindings, done) => {
86
+ const tabMenu = new TabMenu<Route>({
87
+ nodes: routes.map((route) => ({ label: routeLabel(route), value: route })),
88
+ theme: {
89
+ tab: (text) => theme.fg("dim", text),
90
+ activeTab: (text) => theme.bold(theme.fg("accent", text)),
91
+ breadcrumb: (text) => theme.fg("dim", text),
92
+ description: (text) => theme.fg("dim", text),
93
+ help: (text) => theme.fg("dim", text),
94
+ },
95
+ onSelect: (route) => done(route),
96
+ onCancel: () => done(undefined),
97
+ });
98
+ return {
99
+ invalidate: () => tabMenu.invalidate(),
100
+ render: (width: number) => tabMenu.render(width),
101
+ handleInput(data: string): void {
102
+ if (matchesKey(data, "ctrl+c")) {
103
+ done(undefined);
104
+ return;
105
+ }
106
+ tabMenu.handleInput(data);
107
+ },
108
+ };
109
+ });
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-jittor",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Pi extension for Jittor token and context observability with optimization controls backed by the @danypops/jittor daemon",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package", "llm-router", "token-budget"],