@turri-dev/cli 0.1.7-next.9aec40e → 0.1.7-next.ccadaa0
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/assets/jellyfish/dark-00.png +0 -0
- package/assets/jellyfish/dark-01.png +0 -0
- package/assets/jellyfish/dark-02.png +0 -0
- package/assets/jellyfish/dark-03.png +0 -0
- package/assets/jellyfish/dark-04.png +0 -0
- package/assets/jellyfish/dark-05.png +0 -0
- package/assets/jellyfish/dark-06.png +0 -0
- package/assets/jellyfish/dark-07.png +0 -0
- package/assets/jellyfish/dark-08.png +0 -0
- package/assets/jellyfish/dark-09.png +0 -0
- package/assets/jellyfish/dark-10.png +0 -0
- package/assets/jellyfish/dark-11.png +0 -0
- package/assets/jellyfish/dark-12.png +0 -0
- package/assets/jellyfish/dark-13.png +0 -0
- package/assets/jellyfish/dark-14.png +0 -0
- package/assets/jellyfish/dark-15.png +0 -0
- package/assets/jellyfish/dark-16.png +0 -0
- package/assets/jellyfish/dark-17.png +0 -0
- package/assets/jellyfish/dark-18.png +0 -0
- package/assets/jellyfish/dark-19.png +0 -0
- package/assets/jellyfish/dark-20.png +0 -0
- package/assets/jellyfish/dark-21.png +0 -0
- package/assets/jellyfish/dark-22.png +0 -0
- package/assets/jellyfish/dark-23.png +0 -0
- package/assets/jellyfish/dark-24.png +0 -0
- package/assets/jellyfish/dark-25.png +0 -0
- package/assets/jellyfish/dark-26.png +0 -0
- package/assets/jellyfish/dark-27.png +0 -0
- package/assets/jellyfish/dark-28.png +0 -0
- package/assets/jellyfish/dark-29.png +0 -0
- package/assets/jellyfish/dark-30.png +0 -0
- package/assets/jellyfish/dark-31.png +0 -0
- package/assets/jellyfish/dark-32.png +0 -0
- package/assets/jellyfish/dark-33.png +0 -0
- package/assets/jellyfish/dark-34.png +0 -0
- package/assets/jellyfish/dark-35.png +0 -0
- package/assets/jellyfish/dark-36.png +0 -0
- package/assets/jellyfish/dark-37.png +0 -0
- package/assets/jellyfish/dark-38.png +0 -0
- package/assets/jellyfish/dark-39.png +0 -0
- package/assets/jellyfish/dark-40.png +0 -0
- package/assets/jellyfish/light-00.png +0 -0
- package/assets/jellyfish/light-01.png +0 -0
- package/assets/jellyfish/light-02.png +0 -0
- package/assets/jellyfish/light-03.png +0 -0
- package/assets/jellyfish/light-04.png +0 -0
- package/assets/jellyfish/light-05.png +0 -0
- package/assets/jellyfish/light-06.png +0 -0
- package/assets/jellyfish/light-07.png +0 -0
- package/assets/jellyfish/light-08.png +0 -0
- package/assets/jellyfish/light-09.png +0 -0
- package/assets/jellyfish/light-10.png +0 -0
- package/assets/jellyfish/light-11.png +0 -0
- package/assets/jellyfish/light-12.png +0 -0
- package/assets/jellyfish/light-13.png +0 -0
- package/assets/jellyfish/light-14.png +0 -0
- package/assets/jellyfish/light-15.png +0 -0
- package/assets/jellyfish/light-16.png +0 -0
- package/assets/jellyfish/light-17.png +0 -0
- package/assets/jellyfish/light-18.png +0 -0
- package/assets/jellyfish/light-19.png +0 -0
- package/assets/jellyfish/light-20.png +0 -0
- package/assets/jellyfish/light-21.png +0 -0
- package/assets/jellyfish/light-22.png +0 -0
- package/assets/jellyfish/light-23.png +0 -0
- package/assets/jellyfish/light-24.png +0 -0
- package/assets/jellyfish/light-25.png +0 -0
- package/assets/jellyfish/light-26.png +0 -0
- package/assets/jellyfish/light-27.png +0 -0
- package/assets/jellyfish/light-28.png +0 -0
- package/assets/jellyfish/light-29.png +0 -0
- package/assets/jellyfish/light-30.png +0 -0
- package/assets/jellyfish/light-31.png +0 -0
- package/assets/jellyfish/light-32.png +0 -0
- package/assets/jellyfish/light-33.png +0 -0
- package/assets/jellyfish/light-34.png +0 -0
- package/assets/jellyfish/light-35.png +0 -0
- package/assets/jellyfish/light-36.png +0 -0
- package/assets/jellyfish/light-37.png +0 -0
- package/assets/jellyfish/light-38.png +0 -0
- package/assets/jellyfish/light-39.png +0 -0
- package/assets/jellyfish/light-40.png +0 -0
- package/dist/extension.js +13 -8
- package/dist/host.d.ts +1 -0
- package/dist/host.js +2 -0
- package/dist/jellyfish.d.ts +23 -13
- package/dist/jellyfish.js +230 -97
- package/docs/hosting.md +1 -0
- package/docs/images/turri-banner.png +0 -0
- package/docs/theming.md +6 -4
- package/docs/using.md +1 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/extension.js
CHANGED
|
@@ -30,7 +30,7 @@ import { checkPlainFile, checkPlainTargets, formatCheckResults } from "./plain/c
|
|
|
30
30
|
import { whyFailed } from "./regenerate/diagnose-job.js";
|
|
31
31
|
import { createWorkLoop, describeModules, isUpToDate, previewJobs } from "./regenerate/loop.js";
|
|
32
32
|
import { brandNeon, brandPink, plainPaint } from "./regenerate/plain-palette.js";
|
|
33
|
-
import { halfBlocks, JELLYFISH_FRAME_MS, JELLYFISH_TEXT_FRAMES } from "./jellyfish.js";
|
|
33
|
+
import { halfBlocks, JELLYFISH_FRAME_MS, JELLYFISH_SEQUENCE, JELLYFISH_TEXT_FRAMES } from "./jellyfish.js";
|
|
34
34
|
import { createJobPanel, didNotPass, keyPressed, PANEL_KEY, spinner } from "./regenerate/job-panel.js";
|
|
35
35
|
import { clickToFocus, dockedWidth, narrowChat, shade, shadeChat } from "./regenerate/panel-dock.js";
|
|
36
36
|
import { overPictureRow, PICTURE_AT } from "./picture-row.js";
|
|
@@ -2353,8 +2353,8 @@ function registerBranding(pi, skills) {
|
|
|
2353
2353
|
...(data.pending > 0 ? [theme.fg("warning", `${data.pending} skill${data.pending === 1 ? "" : "s"} failed to load`)] : []),
|
|
2354
2354
|
...(data.sandbox !== undefined && data.sandbox.level !== "ok" ? [theme.fg(data.sandbox.level === "limited" ? "warning" : "error", data.sandbox.text)] : []),
|
|
2355
2355
|
];
|
|
2356
|
-
// The jellyfish turri is named after, in ink for the theme's background: a picture
|
|
2357
|
-
// terminal shows images, drawn in text where it does not.
|
|
2356
|
+
// The jellyfish turri is named after, regenerating, in ink for the theme's background: a picture
|
|
2357
|
+
// where the terminal shows images, drawn in text where it does not.
|
|
2358
2358
|
return new Banner(lines, (rule) => theme.fg("borderMuted", rule), jellyfishArt(theme.appearance, (theme.getColorMode?.() ?? "truecolor") === "truecolor"));
|
|
2359
2359
|
});
|
|
2360
2360
|
pi.on("session_start", (_event, ctx) => {
|
|
@@ -2377,10 +2377,15 @@ function registerBranding(pi, skills) {
|
|
|
2377
2377
|
}
|
|
2378
2378
|
});
|
|
2379
2379
|
}
|
|
2380
|
-
/**
|
|
2381
|
-
|
|
2382
|
-
|
|
2380
|
+
/**
|
|
2381
|
+
* How many rows the banner's picture takes at most: its frame reaches down to the sea floor the polyp stands on, so it
|
|
2382
|
+
* is given more than the jellyfish drawn in text, ten.
|
|
2383
|
+
*/
|
|
2384
|
+
const JELLYFISH_ROWS = 14;
|
|
2385
|
+
/** How far along its sequence the jellyfish every banner shows is, moved on by `animateJellyfish`. */
|
|
2383
2386
|
let jellyfishFrame = 0;
|
|
2387
|
+
/** The frame the jellyfish is on: the one its sequence has at this point. */
|
|
2388
|
+
const shownFrame = () => JELLYFISH_SEQUENCE[jellyfishFrame % JELLYFISH_SEQUENCE.length];
|
|
2384
2389
|
let jellyfishTimer;
|
|
2385
2390
|
/**
|
|
2386
2391
|
* Moves the jellyfish on a frame every JELLYFISH_FRAME_MS while the banner is on the screen. An entry's
|
|
@@ -2481,7 +2486,7 @@ class Banner {
|
|
|
2481
2486
|
const text = this.lines.flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width - 1 - left)));
|
|
2482
2487
|
const height = Math.max(text.length, placed?.rows ?? 0);
|
|
2483
2488
|
const above = Math.floor((height - text.length) / 2);
|
|
2484
|
-
const drawn = placed?.kind === "text" ? placed.frames[
|
|
2489
|
+
const drawn = placed?.kind === "text" ? placed.frames[shownFrame()] : undefined;
|
|
2485
2490
|
const rows = Array.from({ length: height }, (_, row) => {
|
|
2486
2491
|
const line = text[row - above];
|
|
2487
2492
|
const jellyfish = drawn !== undefined ? (drawn[row] ?? " ".repeat(placed.columns)) : " ".repeat(placed?.columns ?? 0);
|
|
@@ -2502,7 +2507,7 @@ class Banner {
|
|
|
2502
2507
|
const kitty = getCapabilities().images === "kitty";
|
|
2503
2508
|
if (kitty && this.kittyImageId === undefined)
|
|
2504
2509
|
this.kittyImageId = allocateImageId();
|
|
2505
|
-
const result = renderImage(art.frames[
|
|
2510
|
+
const result = renderImage(art.frames[shownFrame()], art.dimensions, {
|
|
2506
2511
|
maxWidthCells: room,
|
|
2507
2512
|
maxHeightCells: rows,
|
|
2508
2513
|
imageId: this.kittyImageId,
|
package/dist/host.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type { AskUserIO } from "./ask.ts";
|
|
|
10
10
|
export * as pi from "@earendil-works/pi-coding-agent";
|
|
11
11
|
export { configForSpec, hasProjectConfig, modelSpec, projectModels, readProjectConfig, stacksOf, type ProjectConfig } from "./config.ts";
|
|
12
12
|
export { MODULES_DIR, projectDirOf, resolveTemplateDirs } from "./regenerate/paths.ts";
|
|
13
|
+
export { BEDROCK_DEFAULT, BEDROCK_REGIONS, bedrockModels, initConfig, VENDORS, type BedrockChoice, type Vendor } from "./init-config.ts";
|
|
13
14
|
export { checkPlainFile, type CheckResult } from "./plain/check.ts";
|
|
14
15
|
export { plainFileParser } from "./plain/parser.ts";
|
|
15
16
|
export { FUNCTIONAL_REQUIREMENTS, iterateFunctionalSpecs } from "./plain/spec.ts";
|
package/dist/host.js
CHANGED
|
@@ -11,6 +11,8 @@ export * as pi from "@earendil-works/pi-coding-agent";
|
|
|
11
11
|
// The project and its settings.
|
|
12
12
|
export { configForSpec, hasProjectConfig, modelSpec, projectModels, readProjectConfig, stacksOf } from "./config.js";
|
|
13
13
|
export { MODULES_DIR, projectDirOf, resolveTemplateDirs } from "./regenerate/paths.js";
|
|
14
|
+
// A project's first config.turri.yaml, as `turri init-config` writes it - the vendor whose models it takes chosen by the host.
|
|
15
|
+
export { BEDROCK_DEFAULT, BEDROCK_REGIONS, bedrockModels, initConfig, VENDORS } from "./init-config.js";
|
|
14
16
|
// A spec: parsed, checked, coloured.
|
|
15
17
|
export { checkPlainFile } from "./plain/check.js";
|
|
16
18
|
export { plainFileParser } from "./plain/parser.js";
|
package/dist/jellyfish.d.ts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The jellyfish turri is named after, in pixels: a
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The jellyfish turri is named after, in pixels, regenerating: a polyp on the sea floor grows into the jellyfish, it
|
|
3
|
+
* swims a while, then sinks back to the floor and the polyp grows again - the design system's hero and its lifecycle
|
|
4
|
+
* (its pixel kit's `sprite("hero")` and `cycleFrames()`, ported here pixel for pixel). The banner's pictures are
|
|
5
|
+
* rendered from these frames (scripts/jellyfish-frames.mjs writes assets/jellyfish/); where a terminal shows no
|
|
6
|
+
* images the same frames are drawn here in half blocks, at a third of the size.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* What a pixel is: the bell's pink, its midtone and shadow, its rim, its highlight, the gut, and a tentacle - whole,
|
|
10
|
+
* half seen, or a polyp's crown, a little more than half.
|
|
11
|
+
*/
|
|
12
|
+
type Pixel = "p" | "m" | "i" | "e" | "h" | "o" | "N" | "n" | "c";
|
|
13
|
+
/** The pixels' colours on a dark background and a light one - the tentacles blue whale on light, where neon does not show. */
|
|
11
14
|
export declare const JELLYFISH_COLOURS: Record<"dark" | "light", Record<Pixel, string>>;
|
|
15
|
+
/** How much of a pixel shows, for a picture's alpha. */
|
|
16
|
+
export declare function jellyfishAlpha(pixel: string): number;
|
|
17
|
+
/** The jellyfish's frames, each as rows of pixels: what the pictures are rendered from. */
|
|
18
|
+
export declare const JELLYFISH_FRAMES: string[][];
|
|
12
19
|
export declare const JELLYFISH_WIDTH: number;
|
|
13
20
|
export declare const JELLYFISH_HEIGHT: number;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The order the frames play in, round and round: the polyp a moment, growing into the jellyfish, swimming four
|
|
23
|
+
* seconds - a stroke every five frames - then sinking, and the polyp growing back.
|
|
24
|
+
*/
|
|
25
|
+
export declare const JELLYFISH_SEQUENCE: number[];
|
|
18
26
|
/** How long each frame shows. */
|
|
19
|
-
export declare const JELLYFISH_FRAME_MS =
|
|
27
|
+
export declare const JELLYFISH_FRAME_MS = 150;
|
|
28
|
+
/** The frames as a terminal without images draws them, in half blocks: the same order, a third of the size. */
|
|
29
|
+
export declare const JELLYFISH_TEXT_FRAMES: string[][];
|
|
20
30
|
/**
|
|
21
31
|
* A frame drawn in half blocks for a terminal that shows no images: two pixels a cell, the upper one the text's colour
|
|
22
32
|
* and the lower one the background's, in as many colours as the terminal has: a column a pixel, a row every two.
|
package/dist/jellyfish.js
CHANGED
|
@@ -1,114 +1,247 @@
|
|
|
1
1
|
import { sgr } from "./regenerate/plain-palette.js";
|
|
2
|
-
/**
|
|
2
|
+
/** How much of a pixel shows. */
|
|
3
|
+
const ALPHA = { p: 1, m: 1, i: 1, e: 1, h: 1, o: 1, N: 1, n: 0.5, c: 0.6 };
|
|
4
|
+
/** The pixels' colours on a dark background and a light one - the tentacles blue whale on light, where neon does not show. */
|
|
3
5
|
export const JELLYFISH_COLOURS = {
|
|
4
|
-
dark: { p: "#ff5c8d",
|
|
5
|
-
light: { p: "#ff5c8d",
|
|
6
|
+
dark: { p: "#ff5c8d", m: "#a9479a", i: "#5b57d9", e: "#732553", h: "#ff7fa6", o: "#ff7a3d", N: "#5ce1e6", n: "#5ce1e6", c: "#5ce1e6" },
|
|
7
|
+
light: { p: "#ff5c8d", m: "#a9479a", i: "#5b57d9", e: "#732553", h: "#ff7fa6", o: "#ff7a3d", N: "#1e3442", n: "#1e3442", c: "#1e3442" },
|
|
6
8
|
};
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{ at: -4.5, length: 13, sway: 1.3, ink: "N" },
|
|
24
|
-
{ at: -1, length: 14, sway: 0.8, ink: "n" },
|
|
25
|
-
{ at: 2, length: 13, sway: 0.8, ink: "n" },
|
|
26
|
-
{ at: 5.5, length: 13, sway: 1.3, ink: "N" },
|
|
27
|
-
{ at: 9, length: 10, sway: 1.1, ink: "N" },
|
|
28
|
-
],
|
|
29
|
-
};
|
|
30
|
-
const SMALL = {
|
|
31
|
-
width: 18,
|
|
32
|
-
height: 20,
|
|
33
|
-
open: [4, 10, 12, 14, 16, 18, 18],
|
|
34
|
-
shut: [4, 8, 10, 12, 14, 14, 14],
|
|
35
|
-
glint: [[-2, 1], [-3, 2], [-4, 3]],
|
|
36
|
-
shine: [-1, 1],
|
|
37
|
-
gut: ["oo..oo", "oooooo", "oo..oo"],
|
|
38
|
-
gutRow: 3,
|
|
39
|
-
strands: [
|
|
40
|
-
{ at: -6, length: 8, sway: 0.9, ink: "N" },
|
|
41
|
-
{ at: -3, length: 10, sway: 1, ink: "N" },
|
|
42
|
-
{ at: -0.5, length: 11, sway: 0.7, ink: "n" },
|
|
43
|
-
{ at: 1.5, length: 10, sway: 0.7, ink: "n" },
|
|
44
|
-
{ at: 4, length: 10, sway: 1, ink: "N" },
|
|
45
|
-
{ at: 7, length: 8, sway: 0.9, ink: "N" },
|
|
46
|
-
],
|
|
9
|
+
/** What a pixel short of whole is laid over, to draw it in half blocks: abyss, or white. */
|
|
10
|
+
const BACKGROUND = { dark: "#0a0f1a", light: "#f4f7f9" };
|
|
11
|
+
/** How much of a pixel shows, for a picture's alpha. */
|
|
12
|
+
export function jellyfishAlpha(pixel) {
|
|
13
|
+
return ALPHA[pixel] ?? 0;
|
|
14
|
+
}
|
|
15
|
+
/** The lifecycle's grid, and the sea floor the polyp stands on. */
|
|
16
|
+
const WIDTH = 40;
|
|
17
|
+
const HEIGHT = 60;
|
|
18
|
+
const FLOOR = 59;
|
|
19
|
+
const put = (grid, x, y, pixel, bounded = true) => {
|
|
20
|
+
x = Math.round(x);
|
|
21
|
+
y = Math.round(y);
|
|
22
|
+
if (bounded && (x < 0 || x > WIDTH - 1 || y < 0 || y > FLOOR))
|
|
23
|
+
return;
|
|
24
|
+
grid.set(`${x},${y}`, pixel);
|
|
47
25
|
};
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Shut, it has risen a pixel.
|
|
60
|
-
const top = shut > 0.6 ? 0 : 1;
|
|
61
|
-
const widths = design.open.map((open, row) => 2 * Math.round((open - (open - design.shut[row]) * shut) / 2));
|
|
62
|
-
widths.forEach((width, row) => {
|
|
63
|
-
for (let x = centre - width / 2; x < centre + width / 2; x++)
|
|
64
|
-
set(top + row, x, "p");
|
|
65
|
-
});
|
|
66
|
-
const rimWidth = widths.at(-1);
|
|
67
|
-
const rim = top + widths.length;
|
|
68
|
-
// The underside along the bottom row, the rim scalloped under it.
|
|
69
|
-
for (let x = centre - rimWidth / 2 + 2; x < centre + rimWidth / 2 - 2; x++)
|
|
70
|
-
set(rim - 1, x, "e");
|
|
71
|
-
for (let x = centre - rimWidth / 2; x < centre + rimWidth / 2; x += 2)
|
|
72
|
-
set(rim, x + 1, "p");
|
|
73
|
-
// A glint up on the left, curving with the dome, and a pixel of shine.
|
|
74
|
-
for (const [dx, dy] of design.glint)
|
|
75
|
-
set(top + dy, centre + dx, "h");
|
|
76
|
-
set(top + design.shine[1], centre + design.shine[0], "w");
|
|
77
|
-
const gutLeft = centre - Math.floor(design.gut[0].length / 2);
|
|
78
|
-
design.gut.forEach((line, dy) => [...line].forEach((pixel, dx) => pixel === "o" && set(top + design.gutRow + dy, gutLeft + dx, "o")));
|
|
79
|
-
// The tentacles: rooted under the bell, drawn in as it shuts, swaying - the further down, the later.
|
|
80
|
-
const phase = (2 * Math.PI * t) / SHUT.length;
|
|
81
|
-
design.strands.forEach((strand, i) => {
|
|
82
|
-
const root = centre - 0.5 + strand.at * (rimWidth / design.open.at(-1));
|
|
83
|
-
for (let y = 0; y < strand.length; y++) {
|
|
84
|
-
const x = Math.round(root + strand.sway * Math.sin(phase - y * 0.55 + i * 0.8) * Math.min(1, y / 3));
|
|
85
|
-
const left = strand.length - y;
|
|
86
|
-
// The tips come apart, every other pixel, fading - into code.
|
|
87
|
-
if (left <= 3 && y % 2 === 1)
|
|
26
|
+
/** The bell's colour down its height: pink, through its midtone, to its shadow - dithered where one meets the next. */
|
|
27
|
+
const band = (t, x, y) => t < 0.3 ? "p" : t < 0.42 ? ((x + y) % 2 ? "p" : "m") : t < 0.62 ? "m" : t < 0.74 ? ((x + y) % 2 ? "m" : "i") : "i";
|
|
28
|
+
/** An ellipse filled with the bell's colours, or its top half - a dome. */
|
|
29
|
+
function shape(grid, cx, cy, rx, ry, dome, bounded = true) {
|
|
30
|
+
for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {
|
|
31
|
+
if (dome && y > cy)
|
|
32
|
+
continue;
|
|
33
|
+
for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {
|
|
34
|
+
const dx = (x + 0.5 - cx) / rx;
|
|
35
|
+
const dy = (y + 0.5 - cy) / ry;
|
|
36
|
+
if (dx * dx + dy * dy > 1)
|
|
88
37
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (strand.ink === "n" && left > 5 && (y + t) % 3 === 0)
|
|
92
|
-
set(rim + 1 + y, x + (strand.at < 0 ? -1 : 1), "n", false);
|
|
38
|
+
const t = dome ? (y - (cy - ry)) / ry : (y - (cy - ry)) / (2 * ry);
|
|
39
|
+
put(grid, x, y, band(Math.max(0, Math.min(1, t)), x, y), bounded);
|
|
93
40
|
}
|
|
94
|
-
}
|
|
95
|
-
return grid.map((row) => row.join(""));
|
|
41
|
+
}
|
|
96
42
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
43
|
+
/** The bell's rim: every fourth pixel from the middle left out, the rest eggplant. */
|
|
44
|
+
function rim(grid, y) {
|
|
45
|
+
for (let x = 0; x < WIDTH + 1; x++) {
|
|
46
|
+
const key = `${x},${y}`;
|
|
47
|
+
if (!grid.has(key))
|
|
48
|
+
continue;
|
|
49
|
+
if (Math.abs(x - 20) % 4 === 2)
|
|
50
|
+
grid.delete(key);
|
|
51
|
+
else
|
|
52
|
+
grid.set(key, "e");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** The glint on the bell, up on the left. */
|
|
56
|
+
const GLINT = [[11, 6], [12, 5], [13, 5], [10, 7], [11, 7], [14, 4]];
|
|
57
|
+
/** Where each of the thirteen tentacles hangs, how long it is, and its place in the sway. */
|
|
58
|
+
const TENTACLES = [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32].map((x, i) => ({ x, length: 16 + ((i * 7) % 12), i }));
|
|
59
|
+
/** The jellyfish at a scale `k`, its rim on row `cy`, its tentacles grown `grown` of the way. */
|
|
60
|
+
function medusa(grid, k, cy, grown) {
|
|
61
|
+
shape(grid, 20.5, cy, 14.5 * k, 16 * k, true);
|
|
62
|
+
if (k > 0.45)
|
|
63
|
+
rim(grid, Math.floor(cy));
|
|
64
|
+
if (k > 0.8) {
|
|
65
|
+
for (const [gx, gy] of GLINT) {
|
|
66
|
+
const x = Math.round(20 + (gx - 20) * k);
|
|
67
|
+
const y = Math.round(cy + (gy - 18) * k);
|
|
68
|
+
if (grid.has(`${x},${y}`))
|
|
69
|
+
put(grid, x, y, "h");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const gutY = cy - 7 * k;
|
|
73
|
+
const halfWidth = Math.max(1.5, 2.5 * k);
|
|
74
|
+
const halfHeight = Math.max(0.6, 1.5 * k);
|
|
75
|
+
for (let y = Math.round(gutY - halfHeight); y < gutY + halfHeight; y++)
|
|
76
|
+
for (let x = Math.round(20 - halfWidth); x < 20 + halfWidth; x++)
|
|
77
|
+
put(grid, x, y, "o");
|
|
78
|
+
if (k > 0.7) {
|
|
79
|
+
for (let x = 19; x <= 21; x++) {
|
|
80
|
+
put(grid, x, gutY - 2, "o");
|
|
81
|
+
put(grid, x, gutY + 2, "o");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const { x, length, i } of TENTACLES) {
|
|
85
|
+
const shown = Math.round(length * Math.pow(grown, 1.2));
|
|
86
|
+
const root = 20 + (x - 20) * k;
|
|
87
|
+
for (let j = 0; j < shown; j++)
|
|
88
|
+
put(grid, root + Math.round(Math.sin(j * 0.5 + i) * 1.6 * (j / shown)), cy + 1 + j, j % 2 ? "n" : "N");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** The polyp on the floor, grown `u` of the way: a stalk, a head with its gut, a crown of tentacles. */
|
|
92
|
+
function polyp(grid, u) {
|
|
93
|
+
shape(grid, 20, FLOOR, 3, 1.4, true);
|
|
94
|
+
const height = Math.round(2 + 12 * u);
|
|
95
|
+
const radius = 1.4 + 2.2 * u;
|
|
96
|
+
for (let y = FLOOR - height; y <= FLOOR; y++) {
|
|
97
|
+
for (let x = 19; x <= 20; x++)
|
|
98
|
+
put(grid, x, y, band(0.45 + (0.55 * (y - (FLOOR - height))) / Math.max(1, height), x, y));
|
|
99
|
+
}
|
|
100
|
+
const head = FLOOR - height - radius + 1;
|
|
101
|
+
shape(grid, 20, head, radius, radius, false);
|
|
102
|
+
put(grid, 19, head, "o");
|
|
103
|
+
put(grid, 20, head, "o");
|
|
104
|
+
if (radius > 2.5) {
|
|
105
|
+
put(grid, 19, head - 1, "o");
|
|
106
|
+
put(grid, 20, head - 1, "o");
|
|
107
|
+
}
|
|
108
|
+
const crown = [[-4, -3], [3, -3], [-1, -5], [1, -5], [-5, -4], [4, -4], [-1, -6], [1, -6], [-6, -5], [5, -5]];
|
|
109
|
+
const scale = radius / 3.6;
|
|
110
|
+
for (let i = 0; i < Math.floor(u * crown.length); i++) {
|
|
111
|
+
const [dx, dy] = crown[i];
|
|
112
|
+
put(grid, 20 + dx * scale + (dx < 0 ? 0 : 0.5), head + dy * scale, i % 2 ? "c" : "N");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const ease = (t) => (t < 0 ? 0 : t > 1 ? 1 : t * t * (3 - 2 * t));
|
|
116
|
+
/** The lifecycle: twenty frames of the polyp growing into the jellyfish, nineteen of it sinking and the polyp growing back. */
|
|
117
|
+
function lifecycle() {
|
|
118
|
+
const frames = [];
|
|
119
|
+
for (let f = 0; f < 20; f++) {
|
|
120
|
+
const grid = new Map();
|
|
121
|
+
const g = f / 19;
|
|
122
|
+
const p = ease((g - 0.2) / 0.65);
|
|
123
|
+
if (1 - p > 0.06)
|
|
124
|
+
polyp(grid, 1 - p);
|
|
125
|
+
else if (p < 1)
|
|
126
|
+
shape(grid, 20, FLOOR, 2.2, 1, true);
|
|
127
|
+
const e = ease((g - 0.08) / 0.92);
|
|
128
|
+
if (e > 0)
|
|
129
|
+
medusa(grid, 0.18 + 0.82 * e, 40 - 22 * e, Math.pow(e, 1.5));
|
|
130
|
+
frames.push(grid);
|
|
131
|
+
}
|
|
132
|
+
// It sinks to the floor first, and the polyp grows up from there.
|
|
133
|
+
for (let f = 0; f <= 18; f++) {
|
|
134
|
+
const grid = new Map();
|
|
135
|
+
if (f <= 8) {
|
|
136
|
+
const s = f / 8;
|
|
137
|
+
medusa(grid, 1 - 0.72 * s, 18 + 40 * s, 1 - s);
|
|
138
|
+
}
|
|
139
|
+
else if (f <= 11) {
|
|
140
|
+
const w = (f - 9) / 2;
|
|
141
|
+
shape(grid, 20, FLOOR, 4.2 - 1.2 * w, 3.2 - 0.8 * w, true);
|
|
142
|
+
put(grid, 19, FLOOR - 1, "o");
|
|
143
|
+
put(grid, 20, FLOOR - 1, "o");
|
|
144
|
+
}
|
|
145
|
+
else
|
|
146
|
+
polyp(grid, (f - 11) / 7);
|
|
147
|
+
frames.push(grid);
|
|
148
|
+
}
|
|
149
|
+
return frames;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The jellyfish swimming, grown: its bell still, its tentacles in one of two strokes - the second drawn a pixel higher,
|
|
153
|
+
* as it bobs.
|
|
154
|
+
*/
|
|
155
|
+
function hero(stroke) {
|
|
156
|
+
const grid = new Map();
|
|
157
|
+
shape(grid, 20.5, 18, 14.5, 16, true, false);
|
|
158
|
+
rim(grid, 18);
|
|
159
|
+
for (const [x, y] of GLINT)
|
|
160
|
+
if (grid.has(`${x},${y}`))
|
|
161
|
+
put(grid, x, y, "h", false);
|
|
162
|
+
for (let y = 10; y <= 12; y++)
|
|
163
|
+
for (let x = 18; x <= 22; x++)
|
|
164
|
+
put(grid, x, y, "o", false);
|
|
165
|
+
for (let x = 19; x <= 21; x++) {
|
|
166
|
+
put(grid, x, 9, "o", false);
|
|
167
|
+
put(grid, x, 13, "o", false);
|
|
168
|
+
}
|
|
169
|
+
for (const { x, length, i } of TENTACLES) {
|
|
170
|
+
for (let k = 0; k < length; k++) {
|
|
171
|
+
const sway = Math.round(Math.sin(k * 0.5 + i + (stroke === 1 ? 2.2 : 0)) * 1.6 * (k / length));
|
|
172
|
+
put(grid, x + sway, 19 + k, k % 2 ? "n" : "N", false);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (stroke === 0)
|
|
176
|
+
return grid;
|
|
177
|
+
const raised = new Map();
|
|
178
|
+
for (const [key, pixel] of grid) {
|
|
179
|
+
const [x, y] = key.split(",").map(Number);
|
|
180
|
+
raised.set(`${x},${y - 1}`, pixel);
|
|
181
|
+
}
|
|
182
|
+
return raised;
|
|
183
|
+
}
|
|
184
|
+
/** Every frame on one grid, cut to what any of them draws on: rows of pixels, "." where there is none. */
|
|
185
|
+
function crop(grids) {
|
|
186
|
+
const cells = grids.flatMap((grid) => [...grid.keys()].map((key) => key.split(",").map(Number)));
|
|
187
|
+
const left = Math.min(...cells.map(([x]) => x));
|
|
188
|
+
const right = Math.max(...cells.map(([x]) => x));
|
|
189
|
+
const top = Math.min(...cells.map(([, y]) => y));
|
|
190
|
+
const bottom = Math.max(...cells.map(([, y]) => y));
|
|
191
|
+
return grids.map((grid) => Array.from({ length: bottom - top + 1 }, (_, y) => Array.from({ length: right - left + 1 }, (_, x) => grid.get(`${x + left},${y + top}`) ?? ".").join("")));
|
|
192
|
+
}
|
|
193
|
+
/** The lifecycle's frames, then the jellyfish's two strokes, swimming. */
|
|
194
|
+
const LIFECYCLE = lifecycle();
|
|
195
|
+
const SWIM = LIFECYCLE.length;
|
|
196
|
+
/** The jellyfish's frames, each as rows of pixels: what the pictures are rendered from. */
|
|
197
|
+
export const JELLYFISH_FRAMES = crop([...LIFECYCLE, hero(0), hero(1)]);
|
|
198
|
+
export const JELLYFISH_WIDTH = JELLYFISH_FRAMES[0][0].length;
|
|
199
|
+
export const JELLYFISH_HEIGHT = JELLYFISH_FRAMES[0].length;
|
|
200
|
+
/**
|
|
201
|
+
* The order the frames play in, round and round: the polyp a moment, growing into the jellyfish, swimming four
|
|
202
|
+
* seconds - a stroke every five frames - then sinking, and the polyp growing back.
|
|
203
|
+
*/
|
|
204
|
+
export const JELLYFISH_SEQUENCE = [
|
|
205
|
+
...[0, 0, 0],
|
|
206
|
+
...Array.from({ length: 20 }, (_, f) => f),
|
|
207
|
+
...Array.from({ length: 27 }, (_, f) => SWIM + (Math.floor(f / 5) % 2)),
|
|
208
|
+
...Array.from({ length: 19 }, (_, f) => 20 + f),
|
|
209
|
+
];
|
|
103
210
|
/** How long each frame shows. */
|
|
104
|
-
export const JELLYFISH_FRAME_MS =
|
|
211
|
+
export const JELLYFISH_FRAME_MS = 150;
|
|
212
|
+
/** How much smaller the frames are drawn in half blocks: a cell is a block of SHRINK pixels square. */
|
|
213
|
+
const SHRINK = 3;
|
|
214
|
+
/**
|
|
215
|
+
* A frame a third of the size: each block drawn as the pixel at its middle. What most of a block is would run the
|
|
216
|
+
* tentacles, two pixels apart, together into one fringe; its middle keeps strands apart.
|
|
217
|
+
*/
|
|
218
|
+
function shrink(pixels) {
|
|
219
|
+
const middle = Math.floor(SHRINK / 2);
|
|
220
|
+
const rows = [];
|
|
221
|
+
for (let y = middle; y < pixels.length; y += SHRINK) {
|
|
222
|
+
let row = "";
|
|
223
|
+
for (let x = middle; x < pixels[0].length; x += SHRINK)
|
|
224
|
+
row += pixels[y][x];
|
|
225
|
+
rows.push(row);
|
|
226
|
+
}
|
|
227
|
+
return rows;
|
|
228
|
+
}
|
|
229
|
+
/** The frames as a terminal without images draws them, in half blocks: the same order, a third of the size. */
|
|
230
|
+
export const JELLYFISH_TEXT_FRAMES = JELLYFISH_FRAMES.map(shrink);
|
|
231
|
+
/** A colour laid over another, `alpha` of it showing. */
|
|
232
|
+
function over(hex, under, alpha) {
|
|
233
|
+
if (alpha >= 1)
|
|
234
|
+
return hex;
|
|
235
|
+
const channel = (colour, at) => Number.parseInt(colour.slice(at, at + 2), 16);
|
|
236
|
+
return `#${[1, 3, 5].map((at) => Math.round(channel(hex, at) * alpha + channel(under, at) * (1 - alpha)).toString(16).padStart(2, "0")).join("")}`;
|
|
237
|
+
}
|
|
105
238
|
/**
|
|
106
239
|
* A frame drawn in half blocks for a terminal that shows no images: two pixels a cell, the upper one the text's colour
|
|
107
240
|
* and the lower one the background's, in as many colours as the terminal has: a column a pixel, a row every two.
|
|
108
241
|
*/
|
|
109
242
|
export function halfBlocks(pixels, appearance, truecolor) {
|
|
110
243
|
const colours = JELLYFISH_COLOURS[appearance];
|
|
111
|
-
const colour = (pixel) =>
|
|
244
|
+
const colour = (pixel) => pixel === undefined || pixel === "." ? undefined : over(colours[pixel], BACKGROUND[appearance], ALPHA[pixel]);
|
|
112
245
|
const rows = [];
|
|
113
246
|
for (let y = 0; y < pixels.length; y += 2) {
|
|
114
247
|
let row = "";
|
package/docs/hosting.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
| `createTurriExtension(host)` | turri's conversation as an extension of pi's: every tool, slash command, rule and skill the terminal has. The host is told of its jobs and asks its questions - see below. |
|
|
12
12
|
| `pi` | pi itself, as turri's extension is built against it. Make the session with this one - `pi.createAgentSession`, `pi.DefaultResourceLoader` - not another copy. |
|
|
13
13
|
| `readProjectConfig`, `projectModels`, `modelSpec`, `configForSpec`, `hasProjectConfig`, `stacksOf` | the project's settings, as [config.turri.yaml](config.md) has them. |
|
|
14
|
+
| `initConfig(dir, { vendor, bedrock, force })`, `VENDORS`, `BEDROCK_REGIONS`, `BEDROCK_DEFAULT`, `bedrockModels` | a project's first config.turri.yaml, as `turri init-config` writes it: the host asks which vendor's models - `VENDORS` has each one's label and its two models, `BEDROCK_REGIONS` where Bedrock serves each maker's - and `initConfig` writes it, saying what it did. One already there keeps everything but its two models. |
|
|
14
15
|
| `checkPlainFile`, `plainFileParser`, `resolveTemplateDirs`, `iterateFunctionalSpecs`, `FUNCTIONAL_REQUIREMENTS` | a spec checked and parsed, its functional specs one by one - as `turri validate` reads it. |
|
|
15
16
|
| `highlightLines`, `roleOf` | a spec's lines coloured by turri's own \*\*\*plain grammar. |
|
|
16
17
|
| `functionalityCompletion`, `createWorkLoop`, `createJobRegistry`, `promptOf`, `describeModules` | how far each functional spec is built, and - on a loop of the host's own with `dryRun` - the work a spec change would plan, as `turri glimpse` does. |
|
|
Binary file
|
package/docs/theming.md
CHANGED
|
@@ -14,7 +14,7 @@ Bad input fails quietly - an unknown `--use-theme` name and a malformed theme fi
|
|
|
14
14
|
|
|
15
15
|
## The palette
|
|
16
16
|
|
|
17
|
-
Both themes, the command line's colours, the spec diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added.
|
|
17
|
+
Both themes, the command line's colours, the spec diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added, but for the jellyfish's tentacles, every other pixel of which the design draws half seen.
|
|
18
18
|
|
|
19
19
|
| name | value | in `turri-dark` |
|
|
20
20
|
|---|---|---|
|
|
@@ -25,13 +25,15 @@ Both themes, the command line's colours, the spec diff's, the banner's jellyfish
|
|
|
25
25
|
| lines | `#22344a` | rules, dividers, frames: `borderMuted`, `mdHr` |
|
|
26
26
|
| path | `#3e5a6b` | the editor's border, what is dim: `dim`, line numbers |
|
|
27
27
|
| grauzone | `#85a3b2` | body text, and what can be skimmed: `text`, `muted` |
|
|
28
|
-
| white | `#f4f7f9` | headings, tool titles, what you typed
|
|
28
|
+
| white | `#f4f7f9` | headings, tool titles, what you typed |
|
|
29
29
|
| siesta tan | `#e9d8c8` | strings in code, \*\*\*plain's text in quotes or backticks |
|
|
30
|
-
| eggplant | `#732553` | a removed line's fill in a spec diff |
|
|
30
|
+
| eggplant | `#732553` | a removed line's fill in a spec diff, the jellyfish's rim |
|
|
31
31
|
| neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a spec diff: `accent`, `mdLink`, regen's `◆` |
|
|
32
32
|
| neon hover | `#8eebee` | inline code - siesta tan beside white text was too close to tell apart, and tiring to read - types in code, an added line's fill in a light theme's spec diff: `mdCode` |
|
|
33
33
|
| pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
|
|
34
|
-
| pink hover | `#ff7fa6` | \*\*\*plain's section headers, a removed line's `-`, keywords in code |
|
|
34
|
+
| pink hover | `#ff7fa6` | the glint on the jellyfish's bell, \*\*\*plain's section headers, a removed line's `-`, keywords in code |
|
|
35
|
+
| orchid | `#a9479a` | the jellyfish's bell, between its pink and its indigo - nowhere else |
|
|
36
|
+
| indigo | `#5b57d9` | the jellyfish's bell, at its rim - nowhere else |
|
|
35
37
|
| orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the spec map's `!`): `error`, `warning` |
|
|
36
38
|
|
|
37
39
|
The design system keeps pink for the one thing to press and orange for the `$` prompt alone. A terminal has no buttons, but it needs status colours, so pink carries what is done and orange everything that asks for you - what failed, what works with limits, what waits on you - the mark (`✗`, `!`, `■`) telling them apart. The palette has no red.
|
package/docs/using.md
CHANGED
|
@@ -7,7 +7,7 @@ cd ~/my-project
|
|
|
7
7
|
turri
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
That is pi, with turri's tools, skills and themes loaded - `turri` alone, or with pi's options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after,
|
|
10
|
+
That is pi, with turri's tools, skills and themes loaded - `turri` alone, or with pi's options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after, regenerating until the conversation fills the screen: a pixel polyp on the sea floor grows into the jellyfish - a bell from pink down to indigo, an orange gut, neon tentacles - which swims a while, sinks back to the floor, and the polyp grows again, round and round: a picture in a terminal that shows images (Kitty, Ghostty, WezTerm, Warp, iTerm2), and drawn at a third of the size in half blocks in any other, tmux included (`src/jellyfish.ts` draws it, ported from the design system's pixel kit, `scripts/jellyfish-frames.mjs` writes the pictures) - turri's version (and, run from a clone, the commit it is on, and whether the clone has changes) and where its settings are changed - and only when something is wrong, skills that failed to load or agents' commands less fenced than they should be - rather than pi's startup header and resource list; `turri --verbose` shows those too. pi's "Update Available" notice is left out - turri's pi is a pinned dependency `pi update` never touches - and the terminal's tab reads `🪼 turri - <folder>`, not pi's `π`. Talk to it: ask it to start a project, add a feature, render a module, fix a spec. It decides which skill or tool applies - but not *how* a skill runs: `cultivate` hands the work to the gated runner, which owns the steps, the gates and the judges (see [skills and gates](skills-and-gates.md)).
|
|
11
11
|
|
|
12
12
|
The skills that build from scratch:
|
|
13
13
|
|