@oh-my-pi/pi-coding-agent 15.6.0 → 15.7.1
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 +35 -0
- package/dist/types/capability/rule-buckets.d.ts +30 -0
- package/dist/types/capability/rule.d.ts +7 -0
- package/dist/types/cli/completion-gen.d.ts +80 -0
- package/dist/types/commands/complete.d.ts +6 -0
- package/dist/types/commands/completions.d.ts +13 -0
- package/dist/types/commands/setup.d.ts +10 -1
- package/dist/types/config/settings-schema.d.ts +170 -10
- package/dist/types/discovery/builtin-defaults.d.ts +1 -0
- package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
- package/dist/types/edit/hashline/index.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +11 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/segment-track.d.ts +22 -0
- package/dist/types/modes/components/welcome.d.ts +21 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/setup-wizard/index.d.ts +16 -0
- package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
- package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
- package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
- package/dist/types/modes/theme/shimmer.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +11 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/tiny/device.d.ts +78 -0
- package/dist/types/tiny/dtype.d.ts +85 -0
- package/dist/types/tiny/models.d.ts +6 -6
- package/dist/types/tiny/text.d.ts +15 -0
- package/dist/types/tiny/title-client.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/index.d.ts +0 -1
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +17 -0
- package/package.json +9 -9
- package/src/capability/rule-buckets.ts +64 -0
- package/src/capability/rule.ts +8 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/setup-cli.ts +5 -3
- package/src/cli-commands.ts +2 -0
- package/src/cli.ts +1 -7
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/setup.ts +29 -4
- package/src/config/settings-schema.ts +70 -11
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/index.ts +48 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
- package/src/discovery/builtin-rules/rs-result-type.md +19 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
- package/src/discovery/builtin-rules/ts-import-type.md +42 -0
- package/src/discovery/builtin-rules/ts-no-any.md +56 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/index.ts +1 -0
- package/src/edit/hashline/block-resolver.ts +14 -0
- package/src/edit/hashline/diff.ts +4 -1
- package/src/edit/hashline/execute.ts +2 -1
- package/src/edit/hashline/index.ts +1 -0
- package/src/eval/py/kernel.ts +37 -15
- package/src/eval/py/runtime.ts +57 -28
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -12
- package/src/export/ttsr.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +7 -8
- package/src/main.ts +18 -1
- package/src/modes/components/hook-selector.ts +15 -17
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +5 -1
- package/src/modes/components/welcome.ts +47 -42
- package/src/modes/controllers/input-controller.ts +12 -21
- package/src/modes/interactive-mode.ts +17 -5
- package/src/modes/setup-wizard/index.ts +88 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +69 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +299 -0
- package/src/modes/setup-wizard/scenes/types.ts +48 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
- package/src/modes/theme/shimmer.ts +5 -0
- package/src/modes/theme/theme.ts +44 -20
- package/src/modes/types.ts +6 -1
- package/src/prompts/system/orchestrate-notice.md +1 -1
- package/src/prompts/tools/read.md +4 -0
- package/src/sdk.ts +5 -15
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/tiny/device.ts +117 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +7 -6
- package/src/tiny/text.ts +36 -1
- package/src/tiny/title-client.ts +58 -3
- package/src/tiny/worker.ts +93 -29
- package/src/tools/bash.ts +16 -13
- package/src/tools/eval.ts +9 -4
- package/src/tools/index.ts +0 -11
- package/src/tools/read.ts +1 -0
- package/src/tools/renderers.ts +0 -2
- package/src/tui/code-cell.ts +6 -1
- package/src/tui/output-block.ts +199 -38
- package/dist/types/tools/recipe/index.d.ts +0 -46
- package/dist/types/tools/recipe/render.d.ts +0 -36
- package/dist/types/tools/recipe/runner.d.ts +0 -60
- package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
- package/dist/types/tools/recipe/runners/index.d.ts +0 -2
- package/dist/types/tools/recipe/runners/just.d.ts +0 -2
- package/dist/types/tools/recipe/runners/make.d.ts +0 -2
- package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
- package/dist/types/tools/recipe/runners/task.d.ts +0 -2
- package/src/prompts/tools/recipe.md +0 -16
- package/src/tools/recipe/index.ts +0 -81
- package/src/tools/recipe/render.ts +0 -19
- package/src/tools/recipe/runner.ts +0 -219
- package/src/tools/recipe/runners/cargo.ts +0 -131
- package/src/tools/recipe/runners/index.ts +0 -8
- package/src/tools/recipe/runners/just.ts +0 -73
- package/src/tools/recipe/runners/make.ts +0 -101
- package/src/tools/recipe/runners/pkg.ts +0 -167
- package/src/tools/recipe/runners/task.ts +0 -72
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { type Component, matchesKey, padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import { gradientLogo, PI_LOGO } from "../components/welcome";
|
|
4
|
+
import { theme } from "../theme/theme";
|
|
5
|
+
import type { InteractiveModeContext } from "../types";
|
|
6
|
+
import { renderSetupOutro, SETUP_OUTRO_MS } from "./scenes/outro";
|
|
7
|
+
import { renderSetupSplash, SETUP_SPLASH_MS, SETUP_TICK_MS } from "./scenes/splash";
|
|
8
|
+
import type { SetupScene, SetupSceneController, SetupSceneHost, SetupSceneResult } from "./scenes/types";
|
|
9
|
+
|
|
10
|
+
type WizardPhase = "splash" | "transition" | "scene" | "outro" | "done";
|
|
11
|
+
|
|
12
|
+
const SCENE_MARGIN_X = 4;
|
|
13
|
+
const MIN_CONTENT_WIDTH = 20;
|
|
14
|
+
/** Cross-dissolve duration from the splash into the first scene. */
|
|
15
|
+
const SCENE_TRANSITION_MS = 420;
|
|
16
|
+
|
|
17
|
+
function centerLine(line: string, width: number): string {
|
|
18
|
+
const lineWidth = visibleWidth(line);
|
|
19
|
+
if (lineWidth >= width) return truncateToWidth(line, width);
|
|
20
|
+
const left = Math.floor((width - lineWidth) / 2);
|
|
21
|
+
return padding(left) + line + padding(width - left - lineWidth);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function clampLine(line: string, width: number): string {
|
|
25
|
+
const truncated = truncateToWidth(line, width);
|
|
26
|
+
return truncated + padding(Math.max(0, width - visibleWidth(truncated)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function indentLine(line: string, width: number, indent: number): string {
|
|
30
|
+
const prefix = padding(Math.min(indent, Math.max(0, width - 1)));
|
|
31
|
+
return clampLine(prefix + line, width);
|
|
32
|
+
}
|
|
33
|
+
/** Stable per-row jitter in [0,1) for the dissolve reveal order. */
|
|
34
|
+
function rowNoise(y: number): number {
|
|
35
|
+
const h = Math.imul(y ^ 0x9e3779b9, 2654435761);
|
|
36
|
+
return ((h ^ (h >>> 15)) >>> 0) / 4294967296;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Top-biased cross-dissolve between two equal-height frames. As `progress`
|
|
41
|
+
* (0..1) advances, each row flips from `from` to `to` once it crosses a per-row
|
|
42
|
+
* threshold — top rows reveal first (so the scene's mark/header materializes
|
|
43
|
+
* before the splash water below it), with a little jitter for an organic edge.
|
|
44
|
+
*/
|
|
45
|
+
function dissolveFrames(from: string[], to: string[], progress: number, height: number): string[] {
|
|
46
|
+
const eased = progress * progress * (3 - 2 * progress);
|
|
47
|
+
const denom = Math.max(1, height - 1);
|
|
48
|
+
const out: string[] = [];
|
|
49
|
+
for (let y = 0; y < height; y++) {
|
|
50
|
+
const threshold = 0.78 * (y / denom) + 0.22 * rowNoise(y);
|
|
51
|
+
out.push((eased >= threshold ? to[y] : from[y]) ?? "");
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class SetupWizardComponent implements Component {
|
|
57
|
+
#phase: WizardPhase = "splash";
|
|
58
|
+
#phaseStartedAt = performance.now();
|
|
59
|
+
#sceneIndex = 0;
|
|
60
|
+
#activeScene: SetupSceneController | undefined;
|
|
61
|
+
#timer: NodeJS.Timeout | undefined;
|
|
62
|
+
#done = Promise.withResolvers<void>();
|
|
63
|
+
#disposed = false;
|
|
64
|
+
|
|
65
|
+
constructor(
|
|
66
|
+
readonly ctx: InteractiveModeContext,
|
|
67
|
+
readonly scenes: readonly SetupScene[],
|
|
68
|
+
) {}
|
|
69
|
+
|
|
70
|
+
run(): Promise<void> {
|
|
71
|
+
this.#phase = this.scenes.length === 0 ? "outro" : "splash";
|
|
72
|
+
this.#phaseStartedAt = performance.now();
|
|
73
|
+
this.#startTimer();
|
|
74
|
+
this.ctx.ui.requestRender();
|
|
75
|
+
return this.#done.promise;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
dispose(): void {
|
|
79
|
+
this.#disposed = true;
|
|
80
|
+
this.#stopTimer();
|
|
81
|
+
this.#unmountActiveScene();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
invalidate(): void {
|
|
85
|
+
this.#activeScene?.invalidate();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
handleInput(data: string): void {
|
|
89
|
+
if (this.#phase === "done") return;
|
|
90
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
91
|
+
this.#beginOutro();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (this.#phase === "splash") {
|
|
95
|
+
if (
|
|
96
|
+
matchesKey(data, "enter") ||
|
|
97
|
+
matchesKey(data, "return") ||
|
|
98
|
+
matchesKey(data, "space") ||
|
|
99
|
+
matchesKey(data, "escape")
|
|
100
|
+
) {
|
|
101
|
+
this.#beginScene();
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (this.#phase === "outro") {
|
|
106
|
+
if (
|
|
107
|
+
matchesKey(data, "enter") ||
|
|
108
|
+
matchesKey(data, "return") ||
|
|
109
|
+
matchesKey(data, "space") ||
|
|
110
|
+
matchesKey(data, "escape")
|
|
111
|
+
) {
|
|
112
|
+
this.#complete();
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.#activeScene?.handleInput?.(data);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
render(width: number): string[] {
|
|
120
|
+
const safeWidth = Math.max(1, width);
|
|
121
|
+
const height = Math.max(1, this.ctx.ui.terminal.rows);
|
|
122
|
+
let lines: string[];
|
|
123
|
+
switch (this.#phase) {
|
|
124
|
+
case "splash":
|
|
125
|
+
lines = renderSetupSplash(safeWidth, height, performance.now() - this.#phaseStartedAt);
|
|
126
|
+
break;
|
|
127
|
+
case "transition": {
|
|
128
|
+
const elapsed = performance.now() - this.#phaseStartedAt;
|
|
129
|
+
const progress = Math.min(1, elapsed / SCENE_TRANSITION_MS);
|
|
130
|
+
const splash = renderSetupSplash(safeWidth, height, SETUP_SPLASH_MS + elapsed);
|
|
131
|
+
const scene = this.#renderScene(safeWidth, height);
|
|
132
|
+
lines = dissolveFrames(splash, scene, progress, height);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "outro":
|
|
136
|
+
lines = renderSetupOutro(safeWidth, height, performance.now() - this.#phaseStartedAt);
|
|
137
|
+
break;
|
|
138
|
+
case "scene":
|
|
139
|
+
lines = this.#renderScene(safeWidth, height);
|
|
140
|
+
break;
|
|
141
|
+
case "done":
|
|
142
|
+
lines = [];
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
return this.#fitToScreen(lines, safeWidth, height);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#renderScene(width: number, height: number): string[] {
|
|
149
|
+
const scene = this.scenes[this.#sceneIndex];
|
|
150
|
+
const title = this.#activeScene?.title ?? scene?.title ?? "Setup";
|
|
151
|
+
const subtitle = this.#activeScene?.subtitle;
|
|
152
|
+
const contentWidth = Math.max(MIN_CONTENT_WIDTH, width - SCENE_MARGIN_X * 2);
|
|
153
|
+
const logo = gradientLogo(PI_LOGO, 0);
|
|
154
|
+
const header = [
|
|
155
|
+
"",
|
|
156
|
+
...logo.map(line => centerLine(line, width)),
|
|
157
|
+
centerLine(theme.bold(theme.fg("accent", APP_NAME)), width),
|
|
158
|
+
centerLine(theme.fg("muted", `Setup step ${this.#sceneIndex + 1} of ${this.scenes.length}`), width),
|
|
159
|
+
"",
|
|
160
|
+
indentLine(theme.bold(title), width, SCENE_MARGIN_X),
|
|
161
|
+
];
|
|
162
|
+
if (subtitle) {
|
|
163
|
+
header.push(indentLine(theme.fg("muted", subtitle), width, SCENE_MARGIN_X));
|
|
164
|
+
}
|
|
165
|
+
header.push("");
|
|
166
|
+
|
|
167
|
+
const footer = [
|
|
168
|
+
"",
|
|
169
|
+
centerLine(theme.fg("dim", "↑/↓ select · enter confirm · esc skip · ctrl+c exit setup"), width),
|
|
170
|
+
];
|
|
171
|
+
const maxBodyLines = Math.max(0, height - header.length - footer.length);
|
|
172
|
+
const body = this.#activeScene?.render(contentWidth).slice(0, maxBodyLines) ?? [];
|
|
173
|
+
const lines = [...header, ...body.map(line => indentLine(line, width, SCENE_MARGIN_X))];
|
|
174
|
+
while (lines.length + footer.length < height) {
|
|
175
|
+
lines.push("");
|
|
176
|
+
}
|
|
177
|
+
lines.push(...footer);
|
|
178
|
+
return lines;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#fitToScreen(lines: string[], width: number, height: number): string[] {
|
|
182
|
+
const fitted = lines.slice(0, height).map(line => clampLine(line, width));
|
|
183
|
+
while (fitted.length < height) {
|
|
184
|
+
fitted.push(padding(width));
|
|
185
|
+
}
|
|
186
|
+
return fitted;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#startTimer(): void {
|
|
190
|
+
if (this.#timer) return;
|
|
191
|
+
this.#timer = setInterval(() => {
|
|
192
|
+
if (this.#disposed) return;
|
|
193
|
+
const elapsed = performance.now() - this.#phaseStartedAt;
|
|
194
|
+
if (this.#phase === "splash" && elapsed >= SETUP_SPLASH_MS) {
|
|
195
|
+
this.#beginScene();
|
|
196
|
+
} else if (this.#phase === "transition" && elapsed >= SCENE_TRANSITION_MS) {
|
|
197
|
+
this.#phase = "scene";
|
|
198
|
+
this.#phaseStartedAt = performance.now();
|
|
199
|
+
this.ctx.ui.requestRender();
|
|
200
|
+
} else if (this.#phase === "outro" && elapsed >= SETUP_OUTRO_MS) {
|
|
201
|
+
this.#complete();
|
|
202
|
+
} else {
|
|
203
|
+
this.ctx.ui.requestRender();
|
|
204
|
+
}
|
|
205
|
+
}, SETUP_TICK_MS);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#stopTimer(): void {
|
|
209
|
+
if (!this.#timer) return;
|
|
210
|
+
clearInterval(this.#timer);
|
|
211
|
+
this.#timer = undefined;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#mountSceneController(targetPhase: "scene" | "transition"): void {
|
|
215
|
+
if (this.#disposed) return;
|
|
216
|
+
this.#unmountActiveScene();
|
|
217
|
+
if (this.#sceneIndex >= this.scenes.length) {
|
|
218
|
+
this.#beginOutro();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const scene = this.scenes[this.#sceneIndex];
|
|
222
|
+
const host: SetupSceneHost = {
|
|
223
|
+
ctx: this.ctx,
|
|
224
|
+
requestRender: () => this.ctx.ui.requestRender(),
|
|
225
|
+
finish: (_result: SetupSceneResult) => this.#finishScene(),
|
|
226
|
+
setFocus: component => this.ctx.ui.setFocus(component),
|
|
227
|
+
restoreFocus: () => this.ctx.ui.setFocus(this),
|
|
228
|
+
};
|
|
229
|
+
this.#activeScene = scene.mount(host);
|
|
230
|
+
this.#phase = targetPhase;
|
|
231
|
+
this.#phaseStartedAt = performance.now();
|
|
232
|
+
this.ctx.ui.setFocus(this);
|
|
233
|
+
void this.#activeScene.onMount?.();
|
|
234
|
+
this.ctx.ui.requestRender();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Enter the first scene through a dissolve from the splash. */
|
|
238
|
+
#beginScene(): void {
|
|
239
|
+
this.#mountSceneController("transition");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#mountCurrentScene(): void {
|
|
243
|
+
this.#mountSceneController("scene");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#finishScene(): void {
|
|
247
|
+
if (this.#phase !== "scene" && this.#phase !== "transition") return;
|
|
248
|
+
this.#unmountActiveScene();
|
|
249
|
+
this.#sceneIndex += 1;
|
|
250
|
+
this.#mountCurrentScene();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
#unmountActiveScene(): void {
|
|
254
|
+
this.#activeScene?.onUnmount?.();
|
|
255
|
+
this.#activeScene?.dispose?.();
|
|
256
|
+
this.#activeScene = undefined;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#beginOutro(): void {
|
|
260
|
+
if (this.#phase === "done") return;
|
|
261
|
+
this.#unmountActiveScene();
|
|
262
|
+
this.#phase = "outro";
|
|
263
|
+
this.#phaseStartedAt = performance.now();
|
|
264
|
+
this.ctx.ui.setFocus(this);
|
|
265
|
+
this.#startTimer();
|
|
266
|
+
this.ctx.ui.requestRender();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#complete(): void {
|
|
270
|
+
if (this.#phase === "done") return;
|
|
271
|
+
this.#phase = "done";
|
|
272
|
+
this.#stopTimer();
|
|
273
|
+
this.#done.resolve();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -147,6 +147,11 @@ function resolveMode(): ShimmerMode {
|
|
|
147
147
|
return settings.get("display.shimmer");
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/** Whether shimmer animations are active (any mode other than `disabled`). */
|
|
151
|
+
export function shimmerEnabled(): boolean {
|
|
152
|
+
return resolveMode() !== "disabled";
|
|
153
|
+
}
|
|
154
|
+
|
|
150
155
|
/**
|
|
151
156
|
* Apply a shimmer sweep across one or more segments, treating them as a
|
|
152
157
|
* single continuous string for band positioning. Each segment can supply
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -1310,6 +1310,24 @@ export class Theme {
|
|
|
1310
1310
|
return ansi;
|
|
1311
1311
|
}
|
|
1312
1312
|
|
|
1313
|
+
/**
|
|
1314
|
+
* Foreground ANSI for text drawn **on top of** `fillColor` used as a solid
|
|
1315
|
+
* background (e.g. a powerline chip). Picks near-black or near-white by the
|
|
1316
|
+
* fill's perceived luminance (Rec. 601 luma) so the label stays legible on
|
|
1317
|
+
* both bright and dark fills, across light and dark themes.
|
|
1318
|
+
*
|
|
1319
|
+
* Reads the RGB out of the already-resolved truecolor escape; when the fill
|
|
1320
|
+
* is encoded as a 256-palette index (limited terminals) the RGB is
|
|
1321
|
+
* unavailable, so it falls back to the theme `text` color.
|
|
1322
|
+
*/
|
|
1323
|
+
getContrastFgAnsi(fillColor: ThemeColor): string {
|
|
1324
|
+
const ansi = this.#fgColors[fillColor];
|
|
1325
|
+
const match = ansi ? /38;2;(\d+);(\d+);(\d+)/.exec(ansi) : null;
|
|
1326
|
+
if (!match) return this.#fgColors.text;
|
|
1327
|
+
const luma = 0.299 * Number(match[1]) + 0.587 * Number(match[2]) + 0.114 * Number(match[3]);
|
|
1328
|
+
return luma > 140 ? "\x1b[38;2;0;0;0m" : "\x1b[38;2;255;255;255m";
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1313
1331
|
getColorMode(): ColorMode {
|
|
1314
1332
|
return this.mode;
|
|
1315
1333
|
}
|
|
@@ -1929,17 +1947,20 @@ export function setThemeInstance(themeInstance: Theme): void {
|
|
|
1929
1947
|
*/
|
|
1930
1948
|
export async function setSymbolPreset(preset: SymbolPreset): Promise<void> {
|
|
1931
1949
|
currentSymbolPresetOverride = preset;
|
|
1932
|
-
if (currentThemeName)
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1950
|
+
if (!currentThemeName) return;
|
|
1951
|
+
|
|
1952
|
+
const requestId = ++themeLoadRequestId;
|
|
1953
|
+
try {
|
|
1954
|
+
const loadedTheme = await loadTheme(currentThemeName, getCurrentThemeOptions());
|
|
1955
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1956
|
+
theme = loadedTheme;
|
|
1957
|
+
} catch {
|
|
1958
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1959
|
+
// Fall back to dark theme with new preset
|
|
1960
|
+
theme = await loadTheme("dark", getCurrentThemeOptions());
|
|
1961
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1942
1962
|
}
|
|
1963
|
+
onThemeChangeCallback?.();
|
|
1943
1964
|
}
|
|
1944
1965
|
|
|
1945
1966
|
/**
|
|
@@ -1955,17 +1976,20 @@ export function getSymbolPresetOverride(): SymbolPreset | undefined {
|
|
|
1955
1976
|
*/
|
|
1956
1977
|
export async function setColorBlindMode(enabled: boolean): Promise<void> {
|
|
1957
1978
|
currentColorBlindMode = enabled;
|
|
1958
|
-
if (currentThemeName)
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1979
|
+
if (!currentThemeName) return;
|
|
1980
|
+
|
|
1981
|
+
const requestId = ++themeLoadRequestId;
|
|
1982
|
+
try {
|
|
1983
|
+
const loadedTheme = await loadTheme(currentThemeName, getCurrentThemeOptions());
|
|
1984
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1985
|
+
theme = loadedTheme;
|
|
1986
|
+
} catch {
|
|
1987
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1988
|
+
// Fall back to dark theme
|
|
1989
|
+
theme = await loadTheme("dark", getCurrentThemeOptions());
|
|
1990
|
+
if (requestId !== themeLoadRequestId) return;
|
|
1968
1991
|
}
|
|
1992
|
+
onThemeChangeCallback?.();
|
|
1969
1993
|
}
|
|
1970
1994
|
|
|
1971
1995
|
/**
|
package/src/modes/types.ts
CHANGED
|
@@ -58,6 +58,10 @@ export type TodoPhase = {
|
|
|
58
58
|
tasks: TodoItem[];
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
export interface InteractiveModeInitOptions {
|
|
62
|
+
suppressWelcomeIntro?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
61
65
|
export interface InteractiveModeContext {
|
|
62
66
|
// UI access
|
|
63
67
|
ui: TUI;
|
|
@@ -129,7 +133,8 @@ export interface InteractiveModeContext {
|
|
|
129
133
|
todoPhases: TodoPhase[];
|
|
130
134
|
|
|
131
135
|
// Lifecycle
|
|
132
|
-
init(): Promise<void>;
|
|
136
|
+
init(options?: InteractiveModeInitOptions): Promise<void>;
|
|
137
|
+
playWelcomeIntro(): void;
|
|
133
138
|
shutdown(): Promise<void>;
|
|
134
139
|
checkShutdownRequested(): Promise<void>;
|
|
135
140
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
The user's message above is an **orchestration request**. Execute it as the orchestrator under the contract below. This contract overrides any default tendency to yield early, narrate, or do the work yourself.
|
|
3
3
|
|
|
4
4
|
<role>
|
|
5
|
-
You decompose, dispatch, verify, and iterate. You do **not** edit code. Every file mutation goes through a `task` subagent. Your tool budget is: reading for planning, `task` for dispatch, verification (`bun check`, `bun test`, `
|
|
5
|
+
You decompose, dispatch, verify, and iterate. You do **not** edit code. Every file mutation goes through a `task` subagent. Your tool budget is: reading for planning, `task` for dispatch, verification (`bun check`, `bun test`, `lsp diagnostics`), git via `bash`, and `todo_write` for tracking.
|
|
6
6
|
</role>
|
|
7
7
|
|
|
8
8
|
<rules>
|
|
@@ -46,7 +46,11 @@ Extracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipy
|
|
|
46
46
|
|
|
47
47
|
# Images
|
|
48
48
|
|
|
49
|
+
{{#if INSPECT_IMAGE_ENABLED}}
|
|
49
50
|
Reading an image path returns metadata (mime, bytes, dimensions, channels, alpha). For actual visual analysis, call `inspect_image` with the path and a question describing what to inspect.
|
|
51
|
+
{{else}}
|
|
52
|
+
Reading an image path returns the decoded image inline (PNG, JPEG, GIF, WEBP) for direct visual analysis.
|
|
53
|
+
{{/if}}
|
|
50
54
|
|
|
51
55
|
# Archives
|
|
52
56
|
|
package/src/sdk.ts
CHANGED
|
@@ -38,6 +38,7 @@ import { type AsyncJob, AsyncJobManager, isBackgroundJobSupportEnabled } from ".
|
|
|
38
38
|
import { createAutoresearchExtension } from "./autoresearch";
|
|
39
39
|
import { loadCapability } from "./capability";
|
|
40
40
|
import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
|
|
41
|
+
import { bucketRules } from "./capability/rule-buckets";
|
|
41
42
|
import { ModelRegistry } from "./config/model-registry";
|
|
42
43
|
import {
|
|
43
44
|
formatModelString,
|
|
@@ -1045,21 +1046,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1045
1046
|
options.rules !== undefined
|
|
1046
1047
|
? { items: options.rules, warnings: undefined }
|
|
1047
1048
|
: await loadCapability<Rule>(ruleCapability.id, { cwd });
|
|
1048
|
-
const rulebookRules
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (isTtsrRule) {
|
|
1053
|
-
continue;
|
|
1054
|
-
}
|
|
1055
|
-
if (rule.alwaysApply === true) {
|
|
1056
|
-
alwaysApplyRules.push(rule);
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
if (rule.description) {
|
|
1060
|
-
rulebookRules.push(rule);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1049
|
+
const { rulebookRules, alwaysApplyRules } = bucketRules(rulesResult.items, ttsrManager, {
|
|
1050
|
+
builtinRules: ttsrSettings.builtinRules,
|
|
1051
|
+
disabledRules: ttsrSettings.disabledRules,
|
|
1052
|
+
});
|
|
1063
1053
|
if (existingSession.injectedTtsrRules.length > 0) {
|
|
1064
1054
|
ttsrManager.restoreInjected(existingSession.injectedTtsrRules);
|
|
1065
1055
|
}
|
|
@@ -160,6 +160,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
160
160
|
runtime.ctx.editor.setText("");
|
|
161
161
|
},
|
|
162
162
|
},
|
|
163
|
+
{
|
|
164
|
+
name: "switch",
|
|
165
|
+
description: "Switch model for this session (same as alt+p)",
|
|
166
|
+
handleTui: (_command, runtime) => {
|
|
167
|
+
runtime.ctx.showModelSelector({ temporaryOnly: true });
|
|
168
|
+
runtime.ctx.editor.setText("");
|
|
169
|
+
},
|
|
170
|
+
},
|
|
163
171
|
{
|
|
164
172
|
name: "fast",
|
|
165
173
|
description: "Toggle priority service tier (OpenAI service_tier=priority, Anthropic speed=fast)",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { DeviceType } from "@huggingface/transformers";
|
|
2
|
+
import { $env } from "@oh-my-pi/pi-utils";
|
|
3
|
+
|
|
4
|
+
export type TinyModelDevice = DeviceType;
|
|
5
|
+
|
|
6
|
+
export interface TinyModelDevicePreference {
|
|
7
|
+
device: TinyModelDevice;
|
|
8
|
+
raw: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const CPU_DEVICE: TinyModelDevice = "cpu";
|
|
12
|
+
const CPU_ONLY_ORDER: readonly TinyModelDevice[] = [CPU_DEVICE];
|
|
13
|
+
const DARWIN_WEBGPU_UNSAFE_ORDER: readonly TinyModelDevice[] = [CPU_DEVICE];
|
|
14
|
+
|
|
15
|
+
const DEVICE_VALUES: Record<TinyModelDevice, true> = {
|
|
16
|
+
auto: true,
|
|
17
|
+
gpu: true,
|
|
18
|
+
cpu: true,
|
|
19
|
+
wasm: true,
|
|
20
|
+
webgpu: true,
|
|
21
|
+
cuda: true,
|
|
22
|
+
dml: true,
|
|
23
|
+
coreml: true,
|
|
24
|
+
webnn: true,
|
|
25
|
+
"webnn-npu": true,
|
|
26
|
+
"webnn-gpu": true,
|
|
27
|
+
"webnn-cpu": true,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function defaultTinyModelDevice(): TinyModelDevice {
|
|
31
|
+
if (process.platform === "win32") return "dml";
|
|
32
|
+
if (process.platform === "linux" && process.arch === "x64") return "cuda";
|
|
33
|
+
return CPU_DEVICE;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function usesDarwinWorkerWebGpu(device: TinyModelDevice): boolean {
|
|
37
|
+
return process.platform === "darwin" && (device === "gpu" || device === "webgpu" || device === "auto");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function normalizeTinyModelDevice(value: string | undefined): TinyModelDevice | undefined {
|
|
41
|
+
const raw = value?.trim().toLowerCase();
|
|
42
|
+
if (!raw) return undefined;
|
|
43
|
+
if (raw === "metal") return "webgpu";
|
|
44
|
+
if (raw in DEVICE_VALUES) return raw as TinyModelDevice;
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Unsupported PI_TINY_DEVICE=${JSON.stringify(value)}. Use cpu, gpu, metal, webgpu, auto, cuda, dml, coreml, wasm, webnn, webnn-gpu, webnn-cpu, or webnn-npu.`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resolveTinyModelDevicePreference(
|
|
51
|
+
value: string | undefined = $env.PI_TINY_DEVICE,
|
|
52
|
+
): TinyModelDevicePreference {
|
|
53
|
+
return {
|
|
54
|
+
device: normalizeTinyModelDevice(value) ?? defaultTinyModelDevice(),
|
|
55
|
+
raw: value,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function tinyModelDeviceLoadOrder(preference: TinyModelDevicePreference): readonly TinyModelDevice[] {
|
|
60
|
+
if (preference.device === CPU_DEVICE) return CPU_ONLY_ORDER;
|
|
61
|
+
if (usesDarwinWorkerWebGpu(preference.device)) return DARWIN_WEBGPU_UNSAFE_ORDER;
|
|
62
|
+
return [preference.device, CPU_DEVICE];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Sentinel `providers.tinyModelDevice` value meaning "use the built-in platform default". */
|
|
66
|
+
export const TINY_MODEL_DEVICE_DEFAULT = "default";
|
|
67
|
+
|
|
68
|
+
/** Accepted values for the `providers.tinyModelDevice` setting (validation + UI). */
|
|
69
|
+
export const TINY_MODEL_DEVICE_SETTING_VALUES = [
|
|
70
|
+
TINY_MODEL_DEVICE_DEFAULT,
|
|
71
|
+
"gpu",
|
|
72
|
+
"cpu",
|
|
73
|
+
"metal",
|
|
74
|
+
"webgpu",
|
|
75
|
+
"cuda",
|
|
76
|
+
"dml",
|
|
77
|
+
"coreml",
|
|
78
|
+
"auto",
|
|
79
|
+
"wasm",
|
|
80
|
+
"webnn",
|
|
81
|
+
"webnn-gpu",
|
|
82
|
+
"webnn-cpu",
|
|
83
|
+
"webnn-npu",
|
|
84
|
+
] as const;
|
|
85
|
+
|
|
86
|
+
/** Submenu metadata for the `providers.tinyModelDevice` setting. */
|
|
87
|
+
export const TINY_MODEL_DEVICE_SETTING_OPTIONS = [
|
|
88
|
+
{ value: "default", label: "Default", description: "DirectML on Windows, CUDA on Linux x64, CPU elsewhere" },
|
|
89
|
+
{ value: "gpu", label: "GPU", description: "Accelerated provider (WebGPU/Metal, CUDA, or DirectML)" },
|
|
90
|
+
{ value: "cpu", label: "CPU", description: "CPU-only inference" },
|
|
91
|
+
{ value: "metal", label: "Metal", description: "WebGPU alias for Apple GPUs" },
|
|
92
|
+
{ value: "webgpu", label: "WebGPU", description: "WebGPU/Metal backend" },
|
|
93
|
+
{ value: "cuda", label: "CUDA", description: "NVIDIA CUDA (Linux x64)" },
|
|
94
|
+
{ value: "dml", label: "DirectML", description: "DirectML backend (Windows)" },
|
|
95
|
+
{ value: "coreml", label: "CoreML", description: "Apple CoreML (opt-in; can fail to load)" },
|
|
96
|
+
{ value: "auto", label: "Auto", description: "Let ONNX Runtime choose a provider" },
|
|
97
|
+
{ value: "wasm", label: "WASM", description: "WebAssembly backend" },
|
|
98
|
+
{ value: "webnn", label: "WebNN", description: "WebNN backend" },
|
|
99
|
+
{ value: "webnn-gpu", label: "WebNN GPU", description: "WebNN GPU device" },
|
|
100
|
+
{ value: "webnn-cpu", label: "WebNN CPU", description: "WebNN CPU device" },
|
|
101
|
+
{ value: "webnn-npu", label: "WebNN NPU", description: "WebNN NPU device" },
|
|
102
|
+
] as const satisfies ReadonlyArray<{
|
|
103
|
+
value: (typeof TINY_MODEL_DEVICE_SETTING_VALUES)[number];
|
|
104
|
+
label: string;
|
|
105
|
+
description: string;
|
|
106
|
+
}>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Map a `providers.tinyModelDevice` setting value onto a `PI_TINY_DEVICE` env
|
|
110
|
+
* value for the worker. Returns `undefined` for the default sentinel so the
|
|
111
|
+
* worker keeps its built-in platform default; the worker still validates the
|
|
112
|
+
* forwarded value via {@link normalizeTinyModelDevice}.
|
|
113
|
+
*/
|
|
114
|
+
export function tinyModelDeviceSettingToEnv(value: string | undefined): string | undefined {
|
|
115
|
+
if (!value || value === TINY_MODEL_DEVICE_DEFAULT) return undefined;
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { DataType } from "@huggingface/transformers";
|
|
2
|
+
import { $env } from "@oh-my-pi/pi-utils";
|
|
3
|
+
|
|
4
|
+
/** ONNX quantization / precision for local tiny models (transformers.js `dtype`). */
|
|
5
|
+
export type TinyModelDtype = DataType;
|
|
6
|
+
|
|
7
|
+
const DTYPE_VALUES: Record<TinyModelDtype, true> = {
|
|
8
|
+
auto: true,
|
|
9
|
+
fp32: true,
|
|
10
|
+
fp16: true,
|
|
11
|
+
q8: true,
|
|
12
|
+
int8: true,
|
|
13
|
+
uint8: true,
|
|
14
|
+
q4: true,
|
|
15
|
+
bnb4: true,
|
|
16
|
+
q4f16: true,
|
|
17
|
+
q2: true,
|
|
18
|
+
q2f16: true,
|
|
19
|
+
q1: true,
|
|
20
|
+
q1f16: true,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate and canonicalize a `PI_TINY_DTYPE` value. Returns `undefined` when
|
|
25
|
+
* unset/blank so callers fall back to the per-model spec dtype, and throws on an
|
|
26
|
+
* unrecognized value so a misconfiguration fails loudly instead of silently
|
|
27
|
+
* loading a different precision than requested.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeTinyModelDtype(value: string | undefined): TinyModelDtype | undefined {
|
|
30
|
+
const raw = value?.trim().toLowerCase();
|
|
31
|
+
if (!raw) return undefined;
|
|
32
|
+
if (raw in DTYPE_VALUES) return raw as TinyModelDtype;
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Unsupported PI_TINY_DTYPE=${JSON.stringify(value)}. Use auto, fp32, fp16, q8, int8, uint8, q4, bnb4, q4f16, q2, q2f16, q1, or q1f16.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the `PI_TINY_DTYPE` override. `undefined` means "use the per-model spec
|
|
40
|
+
* dtype" (currently `q4` for every shipped model); a concrete value overrides the
|
|
41
|
+
* precision for whichever local tiny model loads.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveTinyModelDtypeOverride(
|
|
44
|
+
value: string | undefined = $env.PI_TINY_DTYPE,
|
|
45
|
+
): TinyModelDtype | undefined {
|
|
46
|
+
return normalizeTinyModelDtype(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Sentinel `providers.tinyModelDtype` value meaning "use each model's shipped dtype". */
|
|
50
|
+
export const TINY_MODEL_DTYPE_DEFAULT = "default";
|
|
51
|
+
|
|
52
|
+
/** Accepted values for the `providers.tinyModelDtype` setting (validation + UI). */
|
|
53
|
+
export const TINY_MODEL_DTYPE_SETTING_VALUES = [
|
|
54
|
+
TINY_MODEL_DTYPE_DEFAULT,
|
|
55
|
+
"q4",
|
|
56
|
+
"q4f16",
|
|
57
|
+
"q8",
|
|
58
|
+
"fp16",
|
|
59
|
+
"fp32",
|
|
60
|
+
"int8",
|
|
61
|
+
"uint8",
|
|
62
|
+
"bnb4",
|
|
63
|
+
"q2",
|
|
64
|
+
"q2f16",
|
|
65
|
+
"q1",
|
|
66
|
+
"q1f16",
|
|
67
|
+
"auto",
|
|
68
|
+
] as const;
|
|
69
|
+
|
|
70
|
+
/** Submenu metadata for the `providers.tinyModelDtype` setting. */
|
|
71
|
+
export const TINY_MODEL_DTYPE_SETTING_OPTIONS = [
|
|
72
|
+
{ value: "default", label: "Default", description: "Each model's shipped dtype (currently q4)" },
|
|
73
|
+
{ value: "q4", label: "q4", description: "4-bit weights; smallest and fastest" },
|
|
74
|
+
{ value: "q4f16", label: "q4f16", description: "4-bit weights with fp16 activations" },
|
|
75
|
+
{ value: "q8", label: "q8", description: "8-bit quantization" },
|
|
76
|
+
{ value: "fp16", label: "fp16", description: "16-bit float; higher fidelity, larger" },
|
|
77
|
+
{ value: "fp32", label: "fp32", description: "Full precision; largest and slowest" },
|
|
78
|
+
{ value: "int8", label: "int8", description: "Signed 8-bit integer" },
|
|
79
|
+
{ value: "uint8", label: "uint8", description: "Unsigned 8-bit integer" },
|
|
80
|
+
{ value: "bnb4", label: "bnb4", description: "bitsandbytes 4-bit" },
|
|
81
|
+
{ value: "q2", label: "q2", description: "2-bit weights" },
|
|
82
|
+
{ value: "q2f16", label: "q2f16", description: "2-bit weights with fp16 activations" },
|
|
83
|
+
{ value: "q1", label: "q1", description: "1-bit weights" },
|
|
84
|
+
{ value: "q1f16", label: "q1f16", description: "1-bit weights with fp16 activations" },
|
|
85
|
+
{ value: "auto", label: "Auto", description: "Let transformers.js choose per device" },
|
|
86
|
+
] as const satisfies ReadonlyArray<{
|
|
87
|
+
value: (typeof TINY_MODEL_DTYPE_SETTING_VALUES)[number];
|
|
88
|
+
label: string;
|
|
89
|
+
description: string;
|
|
90
|
+
}>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Map a `providers.tinyModelDtype` setting value onto a `PI_TINY_DTYPE` env value
|
|
94
|
+
* for the worker. Returns `undefined` for the default sentinel so the worker keeps
|
|
95
|
+
* each model's shipped dtype; the worker still validates the forwarded value via
|
|
96
|
+
* {@link normalizeTinyModelDtype}.
|
|
97
|
+
*/
|
|
98
|
+
export function tinyModelDtypeSettingToEnv(value: string | undefined): string | undefined {
|
|
99
|
+
if (!value || value === TINY_MODEL_DTYPE_DEFAULT) return undefined;
|
|
100
|
+
return value;
|
|
101
|
+
}
|