jaitus 0.3.20 → 0.3.22
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 +2 -2
- package/src/tui/orb.js +41 -0
- package/src/tui/widgets.js +14 -0
- package/src/tui-ink.js +2 -2
- package/src/ui.js +8 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jaitus",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "Jaitus — a coding agent in your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"node": ">=20"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "node tests/source-sanity.js && node tests/react-build.js && node tests/stalls.js && node tests/clipboard-write.js && node tests/rewind-images.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/service-notices.js && node tests/continuation.js && node tests/steps.js && node tests/files.js && node tests/processes.js && node tests/security.js && node tests/context-budget.js && node tests/context-trim.js && node tests/compaction.js && node tests/copy-clean.js && node tests/parallel-tools.js && node tests/plan-percent.js && node tests/project-identity.js",
|
|
19
|
+
"test": "node tests/source-sanity.js && node tests/react-build.js && node tests/stalls.js && node tests/clipboard-write.js && node tests/rewind-images.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/service-notices.js && node tests/continuation.js && node tests/steps.js && node tests/files.js && node tests/processes.js && node tests/security.js && node tests/context-budget.js && node tests/context-trim.js && node tests/compaction.js && node tests/copy-clean.js && node tests/parallel-tools.js && node tests/plan-percent.js && node tests/orb.js && node tests/project-identity.js",
|
|
20
20
|
"preview": "node scripts/tui-preview.js",
|
|
21
21
|
"prepublishOnly": "npm test"
|
|
22
22
|
},
|
package/src/tui/orb.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ─── The orb ────────────────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// The power source the website draws its energy from, in two terminal cells: a ring of
|
|
4
|
+
// braille dots spinning round a core that crackles, flaring white every couple of
|
|
5
|
+
// seconds. It replaces the one-cell spinner wherever work is running, so the terminal
|
|
6
|
+
// shows the same thing the site does — the work is being powered by the orb.
|
|
7
|
+
//
|
|
8
|
+
// Two cells are a 4×4 grid of dots. The ring is the eight dots on the edge; a two-dot
|
|
9
|
+
// gap travels round it, which is what reads as spin. The four centre dots are the core.
|
|
10
|
+
// Pure function of the tick, so it costs nothing beyond the shared animation timer, and
|
|
11
|
+
// frame 0 is a sensible still for reduced motion.
|
|
12
|
+
|
|
13
|
+
// Braille bit for column c (0..1) and row r (0..3) inside one cell.
|
|
14
|
+
const BIT = [[0x01, 0x02, 0x04, 0x40], [0x08, 0x10, 0x20, 0x80]];
|
|
15
|
+
|
|
16
|
+
// The ring, clockwise from the top: [column 0..3, row 0..3] on the 4×4 grid.
|
|
17
|
+
const RING = [[1, 0], [2, 0], [3, 1], [3, 2], [2, 3], [1, 3], [0, 2], [0, 1]];
|
|
18
|
+
const CORE = [[1, 1], [2, 1], [1, 2], [2, 2]];
|
|
19
|
+
|
|
20
|
+
function cells(dots) {
|
|
21
|
+
const bits = [0, 0];
|
|
22
|
+
for (const [x, y] of dots) bits[x >> 1] |= BIT[x & 1][y];
|
|
23
|
+
return String.fromCharCode(0x2800 + bits[0], 0x2800 + bits[1]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The orb at a given tick. `level` is 0 (steady), 1 (the core crackling) or 2 (a surge:
|
|
28
|
+
* every dot lit) so the caller can colour it — brand, lighter, near-white.
|
|
29
|
+
*/
|
|
30
|
+
export function orbFrame(tick) {
|
|
31
|
+
const n = Math.max(0, Math.floor(tick));
|
|
32
|
+
if (n % 30 < 2 && n >= 30) return { text: cells([...RING, ...CORE]), level: 2 };
|
|
33
|
+
const gap = n % RING.length;
|
|
34
|
+
const ring = RING.filter((_, i) => i !== gap && i !== (gap + 1) % RING.length);
|
|
35
|
+
// The core crackles on an irregular beat — a fixed pattern of primes rather than
|
|
36
|
+
// random, so a frame is reproducible in tests and previews.
|
|
37
|
+
const crackle = n % 11 === 3 || n % 11 === 4 || n % 7 === 0;
|
|
38
|
+
return { text: cells(crackle ? [...ring, ...CORE] : ring), level: crackle ? 1 : 0 };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const ORB_STILL = orbFrame(0).text;
|
package/src/tui/widgets.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import React from 'react';
|
|
14
14
|
import { Box, Text, useAnimation } from 'ink';
|
|
15
15
|
import { palette, mix, ramp } from './theme.js';
|
|
16
|
+
import { orbFrame } from './orb.js';
|
|
16
17
|
|
|
17
18
|
const h = React.createElement;
|
|
18
19
|
|
|
@@ -59,6 +60,19 @@ export function Spinner({ kind = 'braille', color, interval = 80 }) {
|
|
|
59
60
|
return h(Text, { color: color || p.brand }, active ? frames[frame % frames.length] : frames[0]);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The orb (tui/orb.js) as the working indicator: two cells, a spinning ring round a
|
|
65
|
+
* crackling core that surges white. Same shared timer as every other widget.
|
|
66
|
+
*/
|
|
67
|
+
export function OrbSpinner({ interval = 90 }) {
|
|
68
|
+
const p = palette();
|
|
69
|
+
const active = motionOn();
|
|
70
|
+
const { frame } = useAnimation({ interval, isActive: active });
|
|
71
|
+
const f = orbFrame(active ? frame : 0);
|
|
72
|
+
const color = f.level === 2 ? mix(p.brand, p.text, 0.75) : f.level === 1 ? p.brandSoft : p.brand;
|
|
73
|
+
return h(Text, { color }, f.text);
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
/**
|
|
63
77
|
* Text with a highlight travelling through it.
|
|
64
78
|
*
|
package/src/tui-ink.js
CHANGED
|
@@ -28,7 +28,7 @@ import { Box, Spacer, Text, render, useAnimation, useApp, useCursor, useInput, u
|
|
|
28
28
|
import { palette, resetPalette, mix, ramp } from './tui/theme.js';
|
|
29
29
|
import { makeEntry, rowsFor, invalidate, expandable } from './tui/entries.js';
|
|
30
30
|
import { plain, stringWidth, truncateRow, wrapRow, rowText, inverseRange, copyText, softJoin, isDecorationRow } from './tui/ansi.js';
|
|
31
|
-
import { Spinner, Shimmer, GradientText, FlowingRule, Meter, Elapsed, CursorRow, Rows, Row, Reveal, motionOn, publishSettings } from './tui/widgets.js';
|
|
31
|
+
import { Spinner, OrbSpinner, Shimmer, GradientText, FlowingRule, Meter, Elapsed, CursorRow, Rows, Row, Reveal, motionOn, publishSettings } from './tui/widgets.js';
|
|
32
32
|
import { wordRows, wordSize } from './tui/wordmark.js';
|
|
33
33
|
import { enableMouse, disableMouse, parseMouse, isMouse, isMouseFragment, createHitMap } from './tui/mouse.js';
|
|
34
34
|
import { openUrl } from './open.js';
|
|
@@ -590,7 +590,7 @@ function WorkBar({ state, width }) {
|
|
|
590
590
|
const label = plain(state.status) || 'Working';
|
|
591
591
|
const narrow = width < 70;
|
|
592
592
|
return h(Box, { flexDirection: 'row', paddingX: 1 },
|
|
593
|
-
h(Spinner, { kind:
|
|
593
|
+
ascii() ? h(Spinner, { kind: 'ascii' }) : h(OrbSpinner),
|
|
594
594
|
h(Text, null, ' '),
|
|
595
595
|
h(Shimmer, { from: p.brand, to: p.brandSoft }, truncatePlain(label, Math.max(12, width - (narrow ? 22 : 46)))),
|
|
596
596
|
h(Spacer),
|
package/src/ui.js
CHANGED
|
@@ -18,6 +18,7 @@ import path from 'node:path';
|
|
|
18
18
|
import os from 'node:os';
|
|
19
19
|
import { loadSettings, HOME } from './config.js';
|
|
20
20
|
import { terminalBackground } from './terminal.js';
|
|
21
|
+
import { orbFrame } from './tui/orb.js';
|
|
21
22
|
|
|
22
23
|
// Read without re-entering config's write paths; failures mean defaults.
|
|
23
24
|
function loadSettingsFile() { return loadSettings(); }
|
|
@@ -266,9 +267,8 @@ export function spinner(text) {
|
|
|
266
267
|
out(`${c.dim(glyph('…', '...'))} ${text}`);
|
|
267
268
|
return { stop: () => {}, update: () => {} };
|
|
268
269
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
: ['|', '/', '-', '\\'];
|
|
270
|
+
// The orb (tui/orb.js) where the terminal can draw braille; a plain bar where it can't.
|
|
271
|
+
const frames = unicode() ? null : ['|', '/', '-', '\\'];
|
|
272
272
|
let i = 0;
|
|
273
273
|
let label = text;
|
|
274
274
|
// stop() is called more than once per spinner: runAgent stops it when the first
|
|
@@ -285,7 +285,11 @@ export function spinner(text) {
|
|
|
285
285
|
const tick = () => {
|
|
286
286
|
if (stopped) return;
|
|
287
287
|
const n = i++;
|
|
288
|
-
|
|
288
|
+
const glyphNow = frames ? c.brand(frames[n % frames.length]) : (() => {
|
|
289
|
+
const f = orbFrame(n);
|
|
290
|
+
return f.level ? c.shimmer(f.text) : c.brand(f.text);
|
|
291
|
+
})();
|
|
292
|
+
write(`\r\x1b[2K${glyphNow} ${sweep(label, n)}`);
|
|
289
293
|
};
|
|
290
294
|
tick();
|
|
291
295
|
const timer = setInterval(tick, 80);
|