@stevezhou/sisu 0.1.6 → 0.1.7
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/dist/logo.js +19 -78
- package/dist/main.js +1 -1
- package/dist/mobius.js +104 -128
- package/dist/pager/render.js +7 -10
- package/dist/tui.js +3 -7
- package/package.json +1 -1
package/dist/logo.js
CHANGED
|
@@ -18,19 +18,10 @@ Object.defineProperty(exports, "sisuTreeArt", { enumerable: true, get: function
|
|
|
18
18
|
Object.defineProperty(exports, "sisuTreeLines", { enumerable: true, get: function () { return tree_1.sisuTreeLines; } });
|
|
19
19
|
const mark_2 = require("./mark");
|
|
20
20
|
const mobius_1 = require("./mobius");
|
|
21
|
-
const tree_2 = require("./tree");
|
|
22
21
|
const RESET = '\x1b[0m';
|
|
23
|
-
const TERRACOTTA = '\x1b[38;2;184;90;58m';
|
|
24
22
|
const INK = '\x1b[38;2;220;214;204m';
|
|
25
23
|
const INK_DIM = '\x1b[38;2;140;132;120m';
|
|
26
24
|
const HEADLINE = '思有所溯';
|
|
27
|
-
const TAGLINE = '为科研而生的工作台——记录退到思考之后,不打扰任何一念。';
|
|
28
|
-
const COLOPHON = [
|
|
29
|
-
'对话、文献与数据,落笔成档',
|
|
30
|
-
'每个结论,都能沿枝回到它的根',
|
|
31
|
-
'众人的知识彼此嫁接,长成一片林',
|
|
32
|
-
];
|
|
33
|
-
const NUMERALS = ['一', '二', '三'];
|
|
34
25
|
function sisuMobiusArt(columns = 80, phase = 0, color = false) {
|
|
35
26
|
return (0, mobius_1.renderMobiusFrame)({
|
|
36
27
|
cols: (0, mobius_1.mobiusFrameWidth)(columns),
|
|
@@ -78,17 +69,6 @@ function lockup(color) {
|
|
|
78
69
|
const latin = paint('SiSu', INK_DIM, color);
|
|
79
70
|
return [`${inf} ${name}`, ` ${latin}`];
|
|
80
71
|
}
|
|
81
|
-
function colophon(columns, color) {
|
|
82
|
-
const lines = [];
|
|
83
|
-
for (let i = 0; i < COLOPHON.length; i += 1) {
|
|
84
|
-
const num = paint(NUMERALS[i], TERRACOTTA, color);
|
|
85
|
-
const body = paint(COLOPHON[i], INK_DIM, color);
|
|
86
|
-
const line = `${num} ${body}`;
|
|
87
|
-
if (displayWidth(line) + 2 <= columns)
|
|
88
|
-
lines.push(line);
|
|
89
|
-
}
|
|
90
|
-
return lines;
|
|
91
|
-
}
|
|
92
72
|
function padLeft(text, n) {
|
|
93
73
|
return `${' '.repeat(Math.max(0, n))}${text}`;
|
|
94
74
|
}
|
|
@@ -97,69 +77,30 @@ function center(text, width) {
|
|
|
97
77
|
const pad = Math.max(0, Math.floor((width - vis) / 2));
|
|
98
78
|
return padLeft(text, pad);
|
|
99
79
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
const lines = [
|
|
103
|
-
...lockup(color).map((line) => padLeft(line, indent)),
|
|
104
|
-
'',
|
|
105
|
-
padLeft(paint(HEADLINE, INK, color), indent),
|
|
106
|
-
];
|
|
107
|
-
if (!compact && displayWidth(TAGLINE) + indent <= columns) {
|
|
108
|
-
lines.push(padLeft(paint(TAGLINE, INK_DIM, color), indent));
|
|
109
|
-
}
|
|
110
|
-
if (!compact) {
|
|
111
|
-
const notes = colophon(columns, color);
|
|
112
|
-
if (notes.length) {
|
|
113
|
-
lines.push('');
|
|
114
|
-
for (const note of notes)
|
|
115
|
-
lines.push(padLeft(note, indent));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return lines;
|
|
119
|
-
}
|
|
120
|
-
function treeColumn(columns, color, phase, grow) {
|
|
121
|
-
const raw = (0, tree_2.sisuTreeLines)(columns, phase, grow);
|
|
122
|
-
const painted = (0, tree_2.sisuTreeArt)(columns, color, phase, grow).split('\n');
|
|
123
|
-
const treeWidth = raw.reduce((max, line) => Math.max(max, line.length), 0);
|
|
124
|
-
const pad = Math.max(0, Math.floor((columns - treeWidth) / 2));
|
|
125
|
-
return painted.map((line, i) => {
|
|
126
|
-
const vis = raw[i]?.length ?? displayWidth(line);
|
|
127
|
-
return padLeft(`${line}${' '.repeat(Math.max(0, treeWidth - vis))}`, pad);
|
|
128
|
-
});
|
|
80
|
+
function centerBlock(lines, width) {
|
|
81
|
+
return lines.map((line) => center(line, width));
|
|
129
82
|
}
|
|
130
|
-
const STILL_PHASE =
|
|
83
|
+
const STILL_PHASE = 0.85;
|
|
131
84
|
/**
|
|
132
|
-
*
|
|
85
|
+
* Möbius splash. `phase` slides the half-twist around the single face.
|
|
133
86
|
* Line count is stable for a given width so the intro can rewrite in place.
|
|
134
87
|
*/
|
|
135
|
-
function sisuSplashFrame(columns = 80, color = true, phase = STILL_PHASE,
|
|
88
|
+
function sisuSplashFrame(columns = 80, color = true, phase = STILL_PHASE, _grow = 1) {
|
|
136
89
|
const width = Math.max(20, Math.floor(columns));
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
merged.push(`${left}${' '.repeat(pad)}${right[i] ?? ''}`);
|
|
152
|
-
}
|
|
153
|
-
const notes = colophon(width, color);
|
|
154
|
-
if (notes.length) {
|
|
155
|
-
merged.push('');
|
|
156
|
-
for (const note of notes)
|
|
157
|
-
merged.push(padLeft(note, 2));
|
|
158
|
-
}
|
|
159
|
-
return merged.join('\n');
|
|
160
|
-
}
|
|
161
|
-
const tree = treeColumn(width, color, phase, grow);
|
|
162
|
-
return [...copy, '', ...tree].join('\n');
|
|
90
|
+
const ringW = (0, mobius_1.mobiusFrameWidth)(width);
|
|
91
|
+
const ring = (0, mobius_1.renderMobiusFrame)({
|
|
92
|
+
cols: ringW,
|
|
93
|
+
rows: (0, mobius_1.mobiusFrameHeight)(width),
|
|
94
|
+
phase,
|
|
95
|
+
color,
|
|
96
|
+
}).split('\n');
|
|
97
|
+
const pad = Math.max(0, Math.floor((width - ringW) / 2));
|
|
98
|
+
const art = ring.map((line) => padLeft(line, pad));
|
|
99
|
+
const caption = centerBlock([
|
|
100
|
+
`${infinityMark(color)} ${paint('思溯', INK, color)} ${paint('SiSu', INK_DIM, color)}`,
|
|
101
|
+
paint(HEADLINE, INK, color),
|
|
102
|
+
], width);
|
|
103
|
+
return [...art, '', ...caption].join('\n');
|
|
163
104
|
}
|
|
164
105
|
function sisuSplashHeight(columns = 80) {
|
|
165
106
|
return sisuSplashFrame(columns, false).split('\n').length;
|
package/dist/main.js
CHANGED
|
@@ -28,7 +28,7 @@ Usage:
|
|
|
28
28
|
sisu history
|
|
29
29
|
sisu thread <conversation-id>
|
|
30
30
|
sisu training --on|--off
|
|
31
|
-
sisu interactive TUI (
|
|
31
|
+
sisu interactive TUI (Möbius splash)
|
|
32
32
|
sisu help
|
|
33
33
|
|
|
34
34
|
Auth and workspaces live in $SISU_HOME (default ~/.sisu), shared with Desktop.
|
package/dist/mobius.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/** Volumetric Möbius
|
|
2
|
+
/** Volumetric ∞ Möbius: lemniscate centerline, half-twist ribbon, traveling glint. */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.mobiusRgb = mobiusRgb;
|
|
5
5
|
exports.mobiusPoint = mobiusPoint;
|
|
6
|
+
exports.logoPoint = logoPoint;
|
|
6
7
|
exports.renderMobiusFrame = renderMobiusFrame;
|
|
7
8
|
exports.mobiusFrameHeight = mobiusFrameHeight;
|
|
8
9
|
exports.mobiusFrameWidth = mobiusFrameWidth;
|
|
9
10
|
const RESET = '\x1b[0m';
|
|
10
11
|
const SHADE_RAMP = ' .:-=+*#%@';
|
|
11
|
-
const
|
|
12
|
-
const HALF_WIDTH = 0.
|
|
13
|
-
const THICKNESS = 0.
|
|
12
|
+
const SCALE = 1.72;
|
|
13
|
+
const HALF_WIDTH = 0.3;
|
|
14
|
+
const THICKNESS = 0.045;
|
|
14
15
|
const CELL_ASPECT = 0.5;
|
|
15
|
-
const CAM =
|
|
16
|
-
const FOCAL = 5.
|
|
17
|
-
const KEY = norm([-0.
|
|
18
|
-
const FILL = norm([0.
|
|
19
|
-
const VIEW = [0, 0.
|
|
16
|
+
const CAM = 6.4;
|
|
17
|
+
const FOCAL = 5.2;
|
|
18
|
+
const KEY = norm([-0.55, 0.7, 0.45]);
|
|
19
|
+
const FILL = norm([0.52, 0.1, 0.85]);
|
|
20
|
+
const VIEW = [0, 0.06, 1];
|
|
20
21
|
function clamp(value, lo, hi) {
|
|
21
22
|
return Math.max(lo, Math.min(hi, value));
|
|
22
23
|
}
|
|
@@ -37,7 +38,7 @@ function cross(a, b) {
|
|
|
37
38
|
function dot(a, b) {
|
|
38
39
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
39
40
|
}
|
|
40
|
-
/** Brand gradient along the band: blue → purple → gold
|
|
41
|
+
/** Brand gradient along the band: blue → purple → gold. */
|
|
41
42
|
function mobiusRgb(t) {
|
|
42
43
|
const u = ((t / (Math.PI * 2)) % 1 + 1) % 1;
|
|
43
44
|
if (u < 0.5) {
|
|
@@ -68,24 +69,10 @@ function rotateY(x, z, angle) {
|
|
|
68
69
|
const s = Math.sin(angle);
|
|
69
70
|
return [x * c + z * s, -x * s + z * c];
|
|
70
71
|
}
|
|
71
|
-
function rotateZ(x, y, angle) {
|
|
72
|
-
const c = Math.cos(angle);
|
|
73
|
-
const s = Math.sin(angle);
|
|
74
|
-
return [x * c - y * s, x * s + y * c];
|
|
75
|
-
}
|
|
76
|
-
function transform(x, y, z, tilt, spin) {
|
|
77
|
-
// Spin around the ring axis so the half-twist travels; then a fixed 3/4 view.
|
|
78
|
-
;
|
|
79
|
-
[x, y] = rotateZ(x, y, spin);
|
|
80
|
-
[y, z] = rotateX(y, z, tilt);
|
|
81
|
-
[x, z] = rotateY(x, z, 0.32);
|
|
82
|
-
return [x, y, z];
|
|
83
|
-
}
|
|
84
72
|
/**
|
|
85
|
-
*
|
|
86
|
-
* After one full trip around θ, the normal flips — one-sided surface.
|
|
73
|
+
* Circular Möbius (unit tests): after one full trip around θ the width flips.
|
|
87
74
|
*/
|
|
88
|
-
function mobiusPoint(theta, width, radius =
|
|
75
|
+
function mobiusPoint(theta, width, radius = 1.88, halfWidth = 0.34) {
|
|
89
76
|
const twist = theta / 2;
|
|
90
77
|
const ring = radius + width * halfWidth * Math.cos(twist);
|
|
91
78
|
return [
|
|
@@ -94,24 +81,45 @@ function mobiusPoint(theta, width, radius = RADIUS, halfWidth = HALF_WIDTH) {
|
|
|
94
81
|
width * halfWidth * Math.sin(twist),
|
|
95
82
|
];
|
|
96
83
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
84
|
+
/** Gerono lemniscate — the ∞ of the SiSu mark. */
|
|
85
|
+
function lemniscate(t) {
|
|
86
|
+
return [SCALE * Math.sin(t), SCALE * Math.sin(t) * Math.cos(t), 0];
|
|
87
|
+
}
|
|
88
|
+
function lemniscateTangent(t) {
|
|
89
|
+
return norm([Math.cos(t), Math.cos(2 * t), 0]);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Ribbon around the ∞: width offset in a frame that half-twists once per lap.
|
|
93
|
+
* phase slides the twist so the single face travels without a seam.
|
|
94
|
+
*/
|
|
95
|
+
function logoPoint(t, width, phase = 0) {
|
|
96
|
+
const [cx, cy, cz] = lemniscate(t);
|
|
97
|
+
const tangent = lemniscateTangent(t);
|
|
98
|
+
const binormal = [0, 0, 1];
|
|
99
|
+
const normal = norm(cross(binormal, tangent));
|
|
100
|
+
const twist = (t + phase) / 2;
|
|
101
|
+
const ox = normal[0] * Math.cos(twist) + binormal[0] * Math.sin(twist);
|
|
102
|
+
const oy = normal[1] * Math.cos(twist) + binormal[1] * Math.sin(twist);
|
|
103
|
+
const oz = normal[2] * Math.cos(twist) + binormal[2] * Math.sin(twist);
|
|
104
|
+
const w = width * HALF_WIDTH;
|
|
105
|
+
return [cx + ox * w, cy + oy * w, cz + oz * w];
|
|
106
|
+
}
|
|
107
|
+
function logoNormal(t, phase) {
|
|
108
|
+
const tangent = lemniscateTangent(t);
|
|
109
|
+
const binormal = [0, 0, 1];
|
|
110
|
+
const normal = norm(cross(binormal, tangent));
|
|
111
|
+
const twist = (t + phase) / 2;
|
|
112
|
+
return norm([
|
|
113
|
+
normal[0] * Math.cos(twist) + binormal[0] * Math.sin(twist),
|
|
114
|
+
normal[1] * Math.cos(twist) + binormal[1] * Math.sin(twist),
|
|
115
|
+
normal[2] * Math.cos(twist) + binormal[2] * Math.sin(twist),
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
function transform(x, y, z, tilt, yaw) {
|
|
119
|
+
;
|
|
120
|
+
[y, z] = rotateX(y, z, tilt);
|
|
121
|
+
[x, z] = rotateY(x, z, yaw);
|
|
122
|
+
return [x, y, z];
|
|
115
123
|
}
|
|
116
124
|
function shadeChar(shade) {
|
|
117
125
|
return SHADE_RAMP[Math.round(clamp(shade, 0, 1) * (SHADE_RAMP.length - 1))] || '@';
|
|
@@ -122,125 +130,93 @@ function renderMobiusFrame(options = {}) {
|
|
|
122
130
|
const phase = options.phase ?? 0;
|
|
123
131
|
const color = options.color !== false;
|
|
124
132
|
const grid = Array.from({ length: rows }, () => Array(cols).fill(null));
|
|
125
|
-
const tilt = 0.
|
|
126
|
-
const
|
|
127
|
-
const thetaSteps =
|
|
133
|
+
const tilt = 0.42;
|
|
134
|
+
const yaw = 0.18 * Math.sin(phase * 0.5);
|
|
135
|
+
const thetaSteps = 520;
|
|
136
|
+
const scale = Math.min((cols - 2) / 4.15, (rows - 2) / (2.15 * CELL_ASPECT));
|
|
128
137
|
for (let i = 0; i < thetaSteps; i += 1) {
|
|
129
138
|
const theta = (i / thetaSteps) * Math.PI * 2;
|
|
130
139
|
for (let j = -4; j <= 4; j += 1) {
|
|
131
140
|
const width = j / 4;
|
|
132
|
-
const base =
|
|
133
|
-
const n0 =
|
|
134
|
-
// Faces of the thin solid, plus the mid-surface; skip the interior fill.
|
|
141
|
+
const base = logoPoint(theta, width, phase);
|
|
142
|
+
const n0 = logoNormal(theta, phase);
|
|
135
143
|
const thicks = j === -4 || j === 4 ? [-1, 0, 1] : [-1, 1];
|
|
136
|
-
for (const
|
|
137
|
-
const thick = k;
|
|
144
|
+
for (const thick of thicks) {
|
|
138
145
|
let x = base[0] + n0[0] * thick * THICKNESS;
|
|
139
146
|
let y = base[1] + n0[1] * thick * THICKNESS;
|
|
140
147
|
let z = base[2] + n0[2] * thick * THICKNESS;
|
|
141
|
-
let nx = n0[0];
|
|
142
|
-
let ny = n0[1];
|
|
143
|
-
let nz = n0[2];
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
;
|
|
150
|
-
[x, y, z] = transform(x, y, z, tilt, phase);
|
|
151
|
-
[nx, ny, nz] = transform(nx, ny, nz, tilt, phase);
|
|
148
|
+
let nx = thick < 0 ? -n0[0] : n0[0];
|
|
149
|
+
let ny = thick < 0 ? -n0[1] : n0[1];
|
|
150
|
+
let nz = thick < 0 ? -n0[2] : n0[2];
|
|
151
|
+
[x, y, z] = transform(x, y, z, tilt, yaw);
|
|
152
|
+
[nx, ny, nz] = transform(nx, ny, nz, tilt, yaw);
|
|
153
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z))
|
|
154
|
+
continue;
|
|
152
155
|
let normal = [nx, ny, nz];
|
|
153
156
|
const facing = dot(normal, VIEW);
|
|
154
157
|
const back = facing < 0;
|
|
155
158
|
if (back)
|
|
156
159
|
normal = [-normal[0], -normal[1], -normal[2]];
|
|
157
160
|
const depth = CAM - z;
|
|
158
|
-
if (depth < 0.
|
|
161
|
+
if (depth < 0.4 || !Number.isFinite(depth))
|
|
159
162
|
continue;
|
|
160
163
|
const px = (x * FOCAL) / depth;
|
|
161
164
|
const py = (y * FOCAL) / depth;
|
|
165
|
+
if (!Number.isFinite(px) || !Number.isFinite(py))
|
|
166
|
+
continue;
|
|
162
167
|
const lambert = Math.max(0, dot(normal, KEY));
|
|
163
168
|
const fill = Math.max(0, dot(normal, FILL)) * 0.2;
|
|
164
|
-
const camFill = Math.max(0, dot(normal, VIEW)) * 0.
|
|
169
|
+
const camFill = Math.max(0, dot(normal, VIEW)) * 0.2;
|
|
165
170
|
const half = norm([KEY[0] + VIEW[0], KEY[1] + VIEW[1], KEY[2] + VIEW[2]]);
|
|
166
|
-
const spec = Math.pow(Math.max(0, dot(normal, half)),
|
|
167
|
-
const edge = Math.
|
|
168
|
-
const rim = Math.pow(1 - Math.abs(dot(normal, VIEW)), 2.2) * (0.
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
171
|
+
const spec = Math.pow(Math.max(0, dot(normal, half)), 26);
|
|
172
|
+
const edge = Math.abs(width);
|
|
173
|
+
const rim = Math.pow(1 - Math.abs(dot(normal, VIEW)), 2.2) * (0.12 + 0.45 * edge * edge);
|
|
174
|
+
const rider = Math.pow(Math.max(0, Math.cos(theta - phase)), 18);
|
|
175
|
+
const ambient = back ? 0.1 : 0.2;
|
|
176
|
+
const lit = ambient + lambert * (back ? 0.32 : 0.7) + fill + camFill + spec * 0.45 + rim + rider * 0.55;
|
|
177
|
+
const shade = clamp(Math.pow(lit, 1.12), 0.07, 1);
|
|
178
|
+
const fog = 0.5 + 0.5 * clamp(1 - (depth - 5.2) / 3.4, 0, 1);
|
|
179
|
+
const col = Math.round(px * scale + (cols - 1) / 2);
|
|
180
|
+
const row = Math.round(-py * scale * CELL_ASPECT + (rows - 1) / 2);
|
|
181
|
+
if (col < 0 || col >= cols || row < 0 || row >= rows)
|
|
182
|
+
continue;
|
|
183
|
+
const prev = grid[row][col];
|
|
184
|
+
if (!prev || depth < prev.z) {
|
|
185
|
+
grid[row][col] = {
|
|
186
|
+
z: depth,
|
|
187
|
+
shade: clamp(shade * fog, 0.06, 1),
|
|
188
|
+
t: theta + phase,
|
|
189
|
+
spec: (spec + rider) * fog,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
180
192
|
}
|
|
181
193
|
}
|
|
182
194
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let maxY = -Infinity;
|
|
187
|
-
let minD = Infinity;
|
|
188
|
-
let maxD = -Infinity;
|
|
189
|
-
for (const sample of samples) {
|
|
190
|
-
const sx = sample.px;
|
|
191
|
-
const sy = sample.py * CELL_ASPECT;
|
|
192
|
-
if (sx < minX)
|
|
193
|
-
minX = sx;
|
|
194
|
-
if (sx > maxX)
|
|
195
|
-
maxX = sx;
|
|
196
|
-
if (sy < minY)
|
|
197
|
-
minY = sy;
|
|
198
|
-
if (sy > maxY)
|
|
199
|
-
maxY = sy;
|
|
200
|
-
if (sample.depth < minD)
|
|
201
|
-
minD = sample.depth;
|
|
202
|
-
if (sample.depth > maxD)
|
|
203
|
-
maxD = sample.depth;
|
|
204
|
-
}
|
|
205
|
-
const spanX = Math.max(0.001, maxX - minX);
|
|
206
|
-
const spanY = Math.max(0.001, maxY - minY);
|
|
207
|
-
const scale = Math.min((cols - 4) / spanX, (rows - 2) / spanY);
|
|
208
|
-
const midX = (minX + maxX) / 2;
|
|
209
|
-
const midY = (minY + maxY) / 2;
|
|
210
|
-
const depthSpan = Math.max(0.001, maxD - minD);
|
|
211
|
-
for (const sample of samples) {
|
|
212
|
-
const fog = 0.4 + 0.6 * (1 - (sample.depth - minD) / depthSpan);
|
|
213
|
-
const col = Math.round((sample.px - midX) * scale + (cols - 1) / 2);
|
|
214
|
-
const row = Math.round(-((sample.py * CELL_ASPECT) - midY) * scale + (rows - 1) / 2);
|
|
215
|
-
if (col < 0 || col >= cols || row < 0 || row >= rows)
|
|
216
|
-
continue;
|
|
217
|
-
const prev = grid[row][col];
|
|
218
|
-
if (!prev || sample.depth < prev.z) {
|
|
219
|
-
grid[row][col] = {
|
|
220
|
-
z: sample.depth,
|
|
221
|
-
shade: clamp(sample.shade * fog, 0.06, 1),
|
|
222
|
-
t: sample.t,
|
|
223
|
-
spec: sample.spec * fog,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return grid.map((line) => line.map((cell) => {
|
|
195
|
+
return grid
|
|
196
|
+
.map((line) => line
|
|
197
|
+
.map((cell) => {
|
|
228
198
|
if (!cell)
|
|
229
199
|
return ' ';
|
|
230
200
|
const ch = shadeChar(cell.shade);
|
|
231
201
|
if (!color)
|
|
232
202
|
return ch;
|
|
233
203
|
let [r, g, b] = mobiusRgb(cell.t);
|
|
234
|
-
const lift = 0.
|
|
204
|
+
const lift = 0.2 + cell.shade * 0.92 + cell.spec * 0.55;
|
|
235
205
|
r = clamp(Number.isFinite(r * lift) ? Math.round(r * lift) : 180, 0, 255);
|
|
236
206
|
g = clamp(Number.isFinite(g * lift) ? Math.round(g * lift) : 180, 0, 255);
|
|
237
207
|
b = clamp(Number.isFinite(b * lift) ? Math.round(b * lift) : 180, 0, 255);
|
|
238
208
|
return `${ansiRgb(r, g, b)}${ch}${RESET}`;
|
|
239
|
-
})
|
|
209
|
+
})
|
|
210
|
+
.join(''))
|
|
211
|
+
.join('\n');
|
|
240
212
|
}
|
|
241
213
|
function mobiusFrameHeight(columns = 80) {
|
|
242
|
-
return columns <
|
|
214
|
+
return columns < 52 ? 11 : columns < 72 ? 15 : 18;
|
|
243
215
|
}
|
|
244
216
|
function mobiusFrameWidth(columns = 80) {
|
|
245
|
-
|
|
217
|
+
if (columns < 52)
|
|
218
|
+
return Math.max(32, columns);
|
|
219
|
+
if (columns < 72)
|
|
220
|
+
return Math.min(60, columns);
|
|
221
|
+
return Math.min(72, columns);
|
|
246
222
|
}
|
package/dist/pager/render.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.stripAnsi = void 0;
|
|
4
4
|
exports.renderPager = renderPager;
|
|
5
5
|
const logo_1 = require("../logo");
|
|
6
|
+
const mobius_1 = require("../mobius");
|
|
6
7
|
const model_1 = require("./model");
|
|
7
8
|
const theme_1 = require("./theme");
|
|
8
9
|
Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return theme_1.stripAnsi; } });
|
|
@@ -107,16 +108,12 @@ function center(text, width, vis = (0, logo_1.displayWidth)(text)) {
|
|
|
107
108
|
}
|
|
108
109
|
function welcomeLines(guest, width, height, _theme) {
|
|
109
110
|
const copy = (0, logo_1.sisuWelcomeCopy)(guest, true).map((line) => center(line, width));
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const painted = (0, logo_1.
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const vis = raw[i]?.length ?? (0, logo_1.displayWidth)(line);
|
|
117
|
-
return `${' '.repeat(treePad)}${line}${' '.repeat(Math.max(0, treeWidth - vis))}`;
|
|
118
|
-
});
|
|
119
|
-
return [...copy, '', ...tree];
|
|
111
|
+
const ringW = height >= 16 && width >= 48 ? (0, mobius_1.mobiusFrameWidth)(Math.min(width, 72)) : Math.min(width, 44);
|
|
112
|
+
const ringH = Math.min((0, mobius_1.mobiusFrameHeight)(ringW), Math.max(8, height - 7));
|
|
113
|
+
const painted = (0, logo_1.sisuMobiusArt)(ringW, 0.85, true).split('\n').slice(0, ringH);
|
|
114
|
+
const pad = Math.max(0, Math.floor((width - ringW) / 2));
|
|
115
|
+
const ring = painted.map((line) => `${' '.repeat(pad)}${line}`);
|
|
116
|
+
return [...copy, '', ...ring];
|
|
120
117
|
}
|
|
121
118
|
function slashMenuLines(state, theme) {
|
|
122
119
|
if (!state.slashOpen)
|
package/dist/tui.js
CHANGED
|
@@ -49,10 +49,7 @@ async function playMobiusIntro(io, options = {}) {
|
|
|
49
49
|
io.write('\x1b[?25h');
|
|
50
50
|
io.write(`\n${(0, logo_1.sisuWordmark)()}\n\n`);
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
return 1 - (1 - t) ** 3;
|
|
54
|
-
}
|
|
55
|
-
/** Grow the 溯源之树, then orbit so lighting and occlusion read as volume. */
|
|
52
|
+
/** Slide the half-twist around the ∞ so the single face loops. */
|
|
56
53
|
async function playTreeIntro(io, options = {}) {
|
|
57
54
|
const columns = options.columns ?? process.stdout.columns ?? 80;
|
|
58
55
|
const frames = Math.max(2, options.frames ?? 36);
|
|
@@ -62,9 +59,8 @@ async function playTreeIntro(io, options = {}) {
|
|
|
62
59
|
io.write('\x1b[?25l');
|
|
63
60
|
for (let i = 0; i < frames; i += 1) {
|
|
64
61
|
const u = i / (frames - 1);
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const art = (0, logo_1.sisuSplashFrame)(columns, color, phase, grow);
|
|
62
|
+
const phase = u * Math.PI * 2;
|
|
63
|
+
const art = (0, logo_1.sisuSplashFrame)(columns, color, phase);
|
|
68
64
|
if (i === 0)
|
|
69
65
|
io.write(`${art}\n`);
|
|
70
66
|
else
|