@xynogen/pix-runtime 0.1.2 → 0.3.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/package.json +1 -1
- package/src/collapse.ts +31 -0
- package/src/pix-command.ts +55 -22
- package/src/sections/gate.ts +6 -5
package/package.json
CHANGED
package/src/collapse.ts
CHANGED
|
@@ -18,3 +18,34 @@ export function shouldCollapse(toolName: string): boolean {
|
|
|
18
18
|
export function collapseDelayMs(): number {
|
|
19
19
|
return pixRuntime().get(collapseSection).delaySec * 1000;
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
/** Per-card render state bag for the collapse timer. */
|
|
23
|
+
export interface CollapseState {
|
|
24
|
+
collapsed?: boolean;
|
|
25
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run the collapse timer for a tool card. Call this inside `renderResult`.
|
|
30
|
+
*
|
|
31
|
+
* @param toolName — the tool name (e.g. "bash", "read") for per-tool config
|
|
32
|
+
* @param state — the render context's `state` bag (mutable, per-card)
|
|
33
|
+
* @param invalidate — `context.invalidate()` to trigger re-render
|
|
34
|
+
* @param expanded — whether the host currently requests the detailed view
|
|
35
|
+
* @returns `true` if the card is currently collapsed and not expanded
|
|
36
|
+
*/
|
|
37
|
+
export function tickCollapse(
|
|
38
|
+
toolName: string,
|
|
39
|
+
state: CollapseState,
|
|
40
|
+
invalidate: () => void,
|
|
41
|
+
expanded = false,
|
|
42
|
+
): boolean {
|
|
43
|
+
if (!shouldCollapse(toolName)) return false;
|
|
44
|
+
if (!state.timer && !state.collapsed) {
|
|
45
|
+
state.timer = setTimeout(() => {
|
|
46
|
+
state.collapsed = true;
|
|
47
|
+
invalidate();
|
|
48
|
+
}, collapseDelayMs());
|
|
49
|
+
}
|
|
50
|
+
return state.collapsed === true && !expanded;
|
|
51
|
+
}
|
package/src/pix-command.ts
CHANGED
|
@@ -12,7 +12,6 @@ import type { PixRuntime } from "./runtime.ts";
|
|
|
12
12
|
import type { DeepPartial, SectionHandle } from "./schema.ts";
|
|
13
13
|
import { collapseSection } from "./sections/collapse.ts";
|
|
14
14
|
import { gateSection } from "./sections/gate.ts";
|
|
15
|
-
import { optimizerSection } from "./sections/optimizer.ts";
|
|
16
15
|
import { prettySection } from "./sections/pretty.ts";
|
|
17
16
|
|
|
18
17
|
interface SettingRow<T> {
|
|
@@ -62,29 +61,13 @@ const SETTINGS: SettingRow<unknown>[] = [
|
|
|
62
61
|
read: (v) => String(v.delaySec),
|
|
63
62
|
patch: (value) => ({ delaySec: Number(value) }),
|
|
64
63
|
}),
|
|
65
|
-
row({
|
|
66
|
-
section: "Optimizer",
|
|
67
|
-
label: "caveman",
|
|
68
|
-
handle: optimizerSection,
|
|
69
|
-
values: ["off", "lite", "full", "ultra", "micro"],
|
|
70
|
-
read: (v) => v.caveman,
|
|
71
|
-
patch: (value) => ({ caveman: value as "off" | "lite" | "full" | "ultra" | "micro" }),
|
|
72
|
-
}),
|
|
73
|
-
row({
|
|
74
|
-
section: "Optimizer",
|
|
75
|
-
label: "rtk",
|
|
76
|
-
handle: optimizerSection,
|
|
77
|
-
values: ["on", "off"],
|
|
78
|
-
read: (v) => v.rtk,
|
|
79
|
-
patch: (value) => ({ rtk: value as "on" | "off" }),
|
|
80
|
-
}),
|
|
81
64
|
row({
|
|
82
65
|
section: "Gate",
|
|
83
|
-
label: "
|
|
66
|
+
label: "Guardrails",
|
|
84
67
|
handle: gateSection,
|
|
85
|
-
values: ["
|
|
86
|
-
read: (v) =>
|
|
87
|
-
patch: (
|
|
68
|
+
values: ["on", "off"],
|
|
69
|
+
read: (v) => v.guardrails,
|
|
70
|
+
patch: (guardrails) => ({ guardrails: guardrails as "on" | "off" }),
|
|
88
71
|
}),
|
|
89
72
|
];
|
|
90
73
|
|
|
@@ -124,6 +107,7 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
|
|
|
124
107
|
tui: { requestRender(): void },
|
|
125
108
|
theme: {
|
|
126
109
|
fg(c: string, t: string): string;
|
|
110
|
+
bg(c: string, t: string): string;
|
|
127
111
|
bold(t: string): string;
|
|
128
112
|
},
|
|
129
113
|
_kb: unknown,
|
|
@@ -165,7 +149,12 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
|
|
|
165
149
|
}
|
|
166
150
|
lines.push("");
|
|
167
151
|
lines.push(theme.fg("dim", "←→ change · ↑↓ move · esc close"));
|
|
168
|
-
return
|
|
152
|
+
return frameLines({
|
|
153
|
+
width: boxW,
|
|
154
|
+
lines,
|
|
155
|
+
color: (s: string) => theme.fg("accent", s),
|
|
156
|
+
bg: (s: string) => theme.bg("customMessageBg", s),
|
|
157
|
+
});
|
|
169
158
|
},
|
|
170
159
|
invalidate: () => {},
|
|
171
160
|
handleInput: (data: string) => {
|
|
@@ -195,3 +184,47 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
|
|
|
195
184
|
},
|
|
196
185
|
});
|
|
197
186
|
}
|
|
187
|
+
|
|
188
|
+
// ── Inline frameLines (runtime must not depend on pix-pretty — pretty depends on us) ─
|
|
189
|
+
|
|
190
|
+
interface FrameOptions {
|
|
191
|
+
width: number;
|
|
192
|
+
lines: string[];
|
|
193
|
+
color: (s: string) => string;
|
|
194
|
+
bg?: (s: string) => string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function visibleWidth(s: string): number {
|
|
198
|
+
// Strip ANSI escape sequences for width calculation.
|
|
199
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Render a rounded bordered modal box (╭─╮╰─╯) with solid background fill. */
|
|
203
|
+
function frameLines(opts: FrameOptions): string[] {
|
|
204
|
+
const { width, lines, color } = opts;
|
|
205
|
+
const bg = opts.bg ?? ((s: string) => s);
|
|
206
|
+
const inner = Math.max(1, width - 4); // 2 border + 2 padding
|
|
207
|
+
const dashes = "─".repeat(width - 2);
|
|
208
|
+
|
|
209
|
+
// Re-assert the bg OPEN sequence after any full reset (\x1b[0m) or bg reset
|
|
210
|
+
// (\x1b[49m) embedded in themed content, so the fill has no transparent holes.
|
|
211
|
+
const SENTINEL = "\x00";
|
|
212
|
+
const bgOpen = bg(SENTINEL).split(SENTINEL)[0] ?? "";
|
|
213
|
+
const reassert = (s: string): string =>
|
|
214
|
+
bgOpen
|
|
215
|
+
? s.replace(/\x1b\[([0-9;]*)m/g, (seq, p: string) =>
|
|
216
|
+
p === "0" || p.split(";").includes("49") ? `${seq}${bgOpen}` : seq,
|
|
217
|
+
)
|
|
218
|
+
: s;
|
|
219
|
+
|
|
220
|
+
const row = (content: string): string => {
|
|
221
|
+
const pad = inner - visibleWidth(content);
|
|
222
|
+
const padded = pad > 0 ? content + " ".repeat(pad) : content.slice(0, inner);
|
|
223
|
+
return bg(`${color("│")} ${reassert(padded)} ${color("│")}`);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const out: string[] = [bg(color(`╭${dashes}╮`))];
|
|
227
|
+
for (const line of lines) out.push(row(line));
|
|
228
|
+
out.push(bg(color(`╰${dashes}╯`)));
|
|
229
|
+
return out;
|
|
230
|
+
}
|
package/src/sections/gate.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSection, enumOr, isObj, strArr } from "../schema.ts";
|
|
2
2
|
|
|
3
3
|
export type GateSeverity = "risky" | "dangerous" | "critical";
|
|
4
4
|
|
|
@@ -10,7 +10,8 @@ export interface GateRuleConfig {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface GateConfig {
|
|
13
|
-
|
|
13
|
+
/** Whether Pix's built-in command protections are active. */
|
|
14
|
+
guardrails: "on" | "off";
|
|
14
15
|
autoApprove: string[];
|
|
15
16
|
extraRules: GateRuleConfig[];
|
|
16
17
|
}
|
|
@@ -18,7 +19,7 @@ export interface GateConfig {
|
|
|
18
19
|
const SEVERITIES: readonly GateSeverity[] = ["risky", "dangerous", "critical"];
|
|
19
20
|
|
|
20
21
|
const DEFAULTS: Readonly<GateConfig> = {
|
|
21
|
-
|
|
22
|
+
guardrails: "on",
|
|
22
23
|
autoApprove: [],
|
|
23
24
|
extraRules: [],
|
|
24
25
|
};
|
|
@@ -37,7 +38,7 @@ export const gateSection = defineSection<"gate", GateConfig>({
|
|
|
37
38
|
key: "gate",
|
|
38
39
|
defaults: DEFAULTS,
|
|
39
40
|
parse(raw, ctx) {
|
|
40
|
-
if (!isObj(raw)) return {
|
|
41
|
+
if (!isObj(raw)) return { guardrails: "on", autoApprove: [], extraRules: [] };
|
|
41
42
|
const extraRules: GateRuleConfig[] = [];
|
|
42
43
|
if (Array.isArray(raw.extraRules)) {
|
|
43
44
|
raw.extraRules.forEach((r, i) => {
|
|
@@ -63,7 +64,7 @@ export const gateSection = defineSection<"gate", GateConfig>({
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
return {
|
|
66
|
-
|
|
67
|
+
guardrails: enumOr(raw.guardrails, ["on", "off"], DEFAULTS.guardrails),
|
|
67
68
|
autoApprove: strArr(raw.autoApprove),
|
|
68
69
|
extraRules,
|
|
69
70
|
};
|