@ciphore/radiocli 0.2.4 → 0.2.6
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 +103 -2
- package/CONTRIBUTING.md +28 -0
- package/README.md +108 -253
- package/SECURITY.md +23 -1
- package/dist/agent/alarm-service.js +10 -7
- package/dist/agent/cli.js +16 -1
- package/dist/agent/command-schema.js +47 -0
- package/dist/agent/headless-host.js +226 -81
- package/dist/agent/launcher.js +2 -1
- package/dist/agent/mcp-server.js +10 -5
- package/dist/agent/service.js +75 -40
- package/dist/agent/session.js +214 -24
- package/dist/agent/tui-session.js +213 -0
- package/dist/alarms/cli.js +19 -3
- package/dist/alarms/occurrence-cache.js +25 -0
- package/dist/alarms/runner.js +87 -23
- package/dist/alarms/schedule.js +29 -1
- package/dist/alarms/scheduler.js +11 -3
- package/dist/alarms/system-volume-linux.js +160 -0
- package/dist/alarms/system-volume-ownership.js +76 -8
- package/dist/alarms/system-volume.js +44 -23
- package/dist/cli/arguments.js +212 -0
- package/dist/cli/completion.js +44 -0
- package/dist/cli/doctor.js +156 -0
- package/dist/cli/interactive.js +79 -0
- package/dist/cli/listener.js +227 -0
- package/dist/cli/output.js +121 -0
- package/dist/cli/render-tui.js +34 -0
- package/dist/cli.js +182 -199
- package/dist/platform/capabilities.js +8 -1
- package/dist/platform/desktop-media.js +61 -0
- package/dist/platform/diagnostics.js +273 -0
- package/dist/platform/json-worker.js +111 -0
- package/dist/platform/launch-command.js +9 -6
- package/dist/platform/mpris.js +356 -0
- package/dist/platform/network.js +69 -1
- package/dist/platform/omarchy.js +147 -0
- package/dist/platform/packages.js +7 -1
- package/dist/platform/runtime.js +1 -1
- package/dist/platform/terminals.js +24 -4
- package/dist/player/airplay-discovery.js +144 -61
- package/dist/player/airplay-policy.js +15 -0
- package/dist/player/airplay-process.js +63 -0
- package/dist/player/airplay-readiness.js +42 -0
- package/dist/player/airplay-sender-health.js +49 -49
- package/dist/player/airplay-sender-package.js +5 -0
- package/dist/player/airplay-sender-patch.js +102 -4
- package/dist/player/airplay-setup.js +51 -0
- package/dist/player/airplay-worker.js +97 -41
- package/dist/player/backend-install.js +9 -6
- package/dist/player/ffmpeg-input.js +37 -0
- package/dist/player/mpv-desktop-media.js +194 -0
- package/dist/player/mpv-ipc-client.js +51 -2
- package/dist/player/player-controller.js +390 -73
- package/dist/player/radio-engine.js +282 -0
- package/dist/playlists/playlist.js +18 -16
- package/dist/providers/cache.js +291 -75
- package/dist/providers/discovery-session.js +111 -0
- package/dist/providers/provider-manager.js +106 -28
- package/dist/providers/radio-browser.js +272 -41
- package/dist/providers/radio-garden.js +4 -2
- package/dist/setup-playback.js +152 -0
- package/dist/setup.js +88 -21
- package/dist/storage/file-lock.js +130 -0
- package/dist/storage/read-settings.js +33 -0
- package/dist/storage/replace-file.js +20 -0
- package/dist/storage/repository.js +247 -0
- package/dist/storage/station-identity.js +21 -0
- package/dist/storage/station-schema.js +27 -0
- package/dist/storage/store.js +314 -76
- package/dist/types.js +22 -1
- package/dist/ui/AdaptiveContent.js +21 -24
- package/dist/ui/App.js +708 -454
- package/dist/ui/AppContent.js +5 -5
- package/dist/ui/airplay-settings.js +12 -5
- package/dist/ui/bidi-ranges.js +45 -0
- package/dist/ui/components/AdaptiveMarquee.js +16 -9
- package/dist/ui/components/ReceiverVisualRow.js +39 -0
- package/dist/ui/format.js +37 -4
- package/dist/ui/help-content.js +5 -2
- package/dist/ui/rtl-output.js +137 -0
- package/dist/ui/screen-items.js +10 -4
- package/dist/ui/screens/AirPlaySettingsScreen.js +3 -3
- package/dist/ui/screens/AlarmsScreen.js +5 -2
- package/dist/ui/screens/HelpScreen.js +3 -3
- package/dist/ui/screens/NowPlayingScreen.js +2 -21
- package/dist/ui/screens/SettingsScreen.js +16 -16
- package/dist/ui/screens/StatsScreen.js +1 -1
- package/dist/ui/text-direction.js +9 -0
- package/dist/ui/theme.js +46 -0
- package/dist/ui/use-alarm-tui.js +24 -24
- package/dist/ui/use-app-input.js +42 -8
- package/dist/ui/use-command-executor.js +21 -14
- package/dist/ui/visualizers/boat-lake-kernel.js +325 -0
- package/dist/ui/visualizers/boat-lake.js +161 -0
- package/dist/ui/visualizers/gallop.js +2 -2
- package/dist/ui/visualizers/receiver-style-registry.js +3 -1
- package/dist/ui/visualizers/receiver-visualizers.js +6 -108
- package/dist/ui/visualizers/sumi-ocean.js +183 -0
- package/dist/ui/visualizers/terminal-cell.js +45 -0
- package/dist/update-check.js +2 -1
- package/docs/THIRD_PARTY_NOTICES.md +6 -4
- package/docs/boat-lake.md +88 -0
- package/docs/sumi-ocean.md +80 -0
- package/package.json +36 -12
- package/scripts/build-airplay-native.mjs +22 -0
- package/vendor/node-airtunes2/LICENSE +208 -0
- package/vendor/node-airtunes2/RADIOCLI_PATCH.md +29 -0
- package/vendor/node-airtunes2/README.md +225 -0
- package/vendor/node-airtunes2/UPSTREAM.json +50 -0
- package/vendor/node-airtunes2/binding.gyp +41 -0
- package/vendor/node-airtunes2/lib/ap2_test.js +4 -0
- package/vendor/node-airtunes2/lib/atv.js +307 -0
- package/vendor/node-airtunes2/lib/atvAuthenticator.js +136 -0
- package/vendor/node-airtunes2/lib/audio_out.js +69 -0
- package/vendor/node-airtunes2/lib/circular_buffer.js +133 -0
- package/vendor/node-airtunes2/lib/config.js +26 -0
- package/vendor/node-airtunes2/lib/device_airtunes.js +532 -0
- package/vendor/node-airtunes2/lib/devices.js +266 -0
- package/vendor/node-airtunes2/lib/ed25519.js +26 -0
- package/vendor/node-airtunes2/lib/homekit/credentials.js +86 -0
- package/vendor/node-airtunes2/lib/homekit/encryption.js +69 -0
- package/vendor/node-airtunes2/lib/homekit/number.js +44 -0
- package/vendor/node-airtunes2/lib/homekit/tlv.js +95 -0
- package/vendor/node-airtunes2/lib/http.js +189 -0
- package/vendor/node-airtunes2/lib/index.d.ts +141 -0
- package/vendor/node-airtunes2/lib/index.js +127 -0
- package/vendor/node-airtunes2/lib/ntp.js +37 -0
- package/vendor/node-airtunes2/lib/num_util.js +21 -0
- package/vendor/node-airtunes2/lib/packet_pool.js +43 -0
- package/vendor/node-airtunes2/lib/rtsp.js +1807 -0
- package/vendor/node-airtunes2/lib/srp.js +158 -0
- package/vendor/node-airtunes2/lib/udp_servers.js +214 -0
- package/vendor/node-airtunes2/lib/util.js +12 -0
- package/vendor/node-airtunes2/package.json +31 -0
- package/vendor/node-airtunes2/src/CADebugMacros.h +257 -0
- package/vendor/node-airtunes2/src/CAHostTimeBase.cpp +105 -0
- package/vendor/node-airtunes2/src/CAHostTimeBase.h +224 -0
- package/vendor/node-airtunes2/src/aes_utils.c +864 -0
- package/vendor/node-airtunes2/src/aes_utils.h +24 -0
- package/vendor/node-airtunes2/src/base64.c +136 -0
- package/vendor/node-airtunes2/src/base64.h +42 -0
- package/vendor/node-airtunes2/src/bindings.cc +29 -0
- package/vendor/node-airtunes2/src/codec.cc +170 -0
- package/vendor/node-airtunes2/src/coreaudio.cc +332 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Scene kernel extracted from https://boat.dev/ on 2026-10-28.
|
|
3
|
+
// Upstream procedural artwork; isolated from React, workers, canvas, fonts and telemetry.
|
|
4
|
+
// See docs/boat-lake.md for provenance and the local extraction.
|
|
5
|
+
// output/boat-lake/scene-0.js
|
|
6
|
+
var e = ["#FEFEFE", "#F8F9FC", "#F1F3F9", "#E9EBF6", "#E0E3F2", "#D1D6EC", "#C2C9E6", "#AAB4DD", "#929FD3", "#7F8ECC", "#6B7CC4", "#5A6DBE", "#495EB7", "#384FB0", "#263FA9", "#223895", "#1E3180", "#1A2A6C", "#162358", "#131D46", "#0F1634"], r = e.map((t2) => (-16777216 | parseInt(t2.slice(5, 7), 16) << 16 | parseInt(t2.slice(3, 5), 16) << 8 | parseInt(t2.slice(1, 3), 16)) >>> 0);
|
|
7
|
+
var m97596_z = class {
|
|
8
|
+
w;
|
|
9
|
+
h;
|
|
10
|
+
ch;
|
|
11
|
+
fg;
|
|
12
|
+
bg;
|
|
13
|
+
constructor(t2, e2, r22 = -1) {
|
|
14
|
+
this.w = t2, this.h = e2, this.ch = Array.from({ length: e2 }, () => Array(t2).fill(" ")), this.fg = Array.from({ length: e2 }, () => Array(t2).fill(-1)), this.bg = Array.from({ length: e2 }, () => Array(t2).fill(r22));
|
|
15
|
+
}
|
|
16
|
+
in(t2, e2) {
|
|
17
|
+
return t2 >= 0 && e2 >= 0 && t2 < this.w && e2 < this.h;
|
|
18
|
+
}
|
|
19
|
+
set(t2, e2, r22, n2, l3) {
|
|
20
|
+
this.in(t2, e2) && (this.ch[e2][t2] = r22, this.fg[e2][t2] = n2, l3 !== void 0 && (this.bg[e2][t2] = l3));
|
|
21
|
+
}
|
|
22
|
+
paint(t2, e2, r22) {
|
|
23
|
+
this.in(t2, e2) && (this.bg[e2][t2] = r22);
|
|
24
|
+
}
|
|
25
|
+
get(t2, e2) {
|
|
26
|
+
return this.in(t2, e2) ? this.ch[e2][t2] : "";
|
|
27
|
+
}
|
|
28
|
+
put(t2, e2, r22, n2, l3) {
|
|
29
|
+
[...r22].forEach((r3, a3) => {
|
|
30
|
+
r3 !== " " ? this.set(t2 + a3, e2, r3, n2, l3) : l3 !== void 0 && this.paint(t2 + a3, e2, l3);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
blit(t2, e2, r22) {
|
|
34
|
+
for (let n2 = 0; n2 < t2.h; n2++)
|
|
35
|
+
for (let l3 = 0; l3 < t2.w; l3++)
|
|
36
|
+
t2.ch[n2][l3] === " " && t2.bg[n2][l3] < 0 || this.set(e2 + l3, r22 + n2, t2.ch[n2][l3], t2.fg[n2][l3], t2.bg[n2][l3] >= 0 ? t2.bg[n2][l3] : void 0);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
// output/boat-lake/scene-2.js
|
|
40
|
+
var r2 = e.map((t2) => [parseInt(t2.slice(1, 3), 16), parseInt(t2.slice(3, 5), 16), parseInt(t2.slice(5, 7), 16)]), n = (t2) => (t2 /= 255) <= 0.04045 ? t2 / 12.92 : ((t2 + 0.055) / 1.055) ** 2.4, l = (t2) => 255 * (t2 <= 31308e-7 ? 12.92 * t2 : 1.055 * t2 ** (1 / 2.4) - 0.055), a = r2.map((t2) => t2.map(n)), o = [];
|
|
41
|
+
r2.forEach((t2, e2) => o.push([t2, " ", -1, e2]));
|
|
42
|
+
var h = [[0.25, "\u2591"], [0.5, "\u2592"], [0.75, "\u2593"]];
|
|
43
|
+
for (let t2 = 0; t2 < r2.length; t2++)
|
|
44
|
+
for (let e2 = 0; e2 < r2.length; e2++)
|
|
45
|
+
if (!(t2 === e2 || Math.abs(t2 - e2) > 1))
|
|
46
|
+
for (let [r22, n2] of h) {
|
|
47
|
+
let h22 = a[t2].map((t3, n3) => l(t3 * (1 - r22) + a[e2][n3] * r22));
|
|
48
|
+
o.push([h22, n2, e2, t2]);
|
|
49
|
+
}
|
|
50
|
+
var m = (t2) => r2[t2];
|
|
51
|
+
function b(t2, e2, r22) {
|
|
52
|
+
let n2 = 374761393 * t2 + 668265263 * e2 + 2147483647 * r22 | 0;
|
|
53
|
+
return (((n2 = Math.imul(n2 ^ n2 >>> 13, 1274126177)) ^ n2 >>> 16) >>> 0) / 4294967295;
|
|
54
|
+
}
|
|
55
|
+
function L(t2, e2, r22 = 0) {
|
|
56
|
+
let n2 = Math.floor(t2), l22 = Math.floor(e2), a22 = t2 - n2, o22 = e2 - l22, h22 = a22 * a22 * (3 - 2 * a22), i2 = o22 * o22 * (3 - 2 * o22), f2 = b(n2, l22, r22), u2 = b(n2 + 1, l22, r22), M2 = b(n2, l22 + 1, r22);
|
|
57
|
+
return f2 + (u2 - f2) * h22 + (M2 - f2) * i2 + (f2 - u2 - M2 + b(n2 + 1, l22 + 1, r22)) * h22 * i2;
|
|
58
|
+
}
|
|
59
|
+
var z = (t2, e2, r22 = 0) => 0.55 * L(t2, e2, r22) + 0.3 * L(2.1 * t2, 2.1 * e2, r22 + 1) + 0.15 * L(4.3 * t2, 4.3 * e2, r22 + 2);
|
|
60
|
+
var H = [[43, 338], [240, 0], [322, 240], [244, 338]], W = [[282, 338], [444, 126], [455, 132], [498, 338]], A = [[0, 361], [498, 361], [498, 412], [465, 412], [465, 462], [84, 462], [8, 412], [0, 404]];
|
|
61
|
+
function R(t2, e2, r22) {
|
|
62
|
+
let n2 = !1;
|
|
63
|
+
for (let l22 = 0, a22 = t2.length - 1; l22 < t2.length; a22 = l22++) {
|
|
64
|
+
let [o22, h22] = t2[l22], [i2, f2] = t2[a22];
|
|
65
|
+
h22 > r22 != f2 > r22 && e2 < (i2 - o22) * (r22 - h22) / (f2 - h22) + o22 && (n2 = !n2);
|
|
66
|
+
}
|
|
67
|
+
return n2;
|
|
68
|
+
}
|
|
69
|
+
function E(t2, e2, r22) {
|
|
70
|
+
let n2 = t2.h / 462, l22 = (e2 - t2.x) / n2 + 249, a22 = (r22 - t2.y) / n2 + 462;
|
|
71
|
+
return l22 < 0 || l22 > 498 || a22 < 0 || a22 > 462 ? 0 : R(A, l22, a22) ? 3 : R(H, l22, a22) ? 1 : 2 * !!R(W, l22, a22);
|
|
72
|
+
}
|
|
73
|
+
var F = [[0, 0, 0], m(0), m(5), m(3)];
|
|
74
|
+
function k(t2, e2, r22) {
|
|
75
|
+
let n2 = 8 * t2, l22 = 16 * e2, a22 = r22 != null ? l22 - 16 * r22 : Math.round(0.42 * l22);
|
|
76
|
+
return { W: n2, H: l22, hz: a22, mx: Math.round(n2 - Math.max(0.2 * n2, Math.min(340, 0.26 * n2))), my: Math.round(a22 * (a22 < 200 ? 0.6 : 0.36)) };
|
|
77
|
+
}
|
|
78
|
+
function v(t2) {
|
|
79
|
+
let e2 = 2 * t2 - 3, r22 = Math.round(0.7 * e2), n2 = e2 + 3 + r22, l22 = n2 + 3, a22 = [];
|
|
80
|
+
for (let t3 = 0; t3 < e2; t3++) {
|
|
81
|
+
let o22 = Array(l22).fill("."), h22 = Math.max(0, Math.round((e2 - 1) * ((t3 + 1) / e2) ** 0.78));
|
|
82
|
+
for (let t4 = e2 - h22; t4 <= e2; t4++)
|
|
83
|
+
o22[t4] = "L";
|
|
84
|
+
let i2 = t3 - (e2 - r22);
|
|
85
|
+
if (i2 >= 0)
|
|
86
|
+
for (let t4 = n2 - i2; t4 <= n2; t4++)
|
|
87
|
+
o22[t4] = "R";
|
|
88
|
+
a22.push(o22.join(""));
|
|
89
|
+
}
|
|
90
|
+
return a22.push("H".repeat(l22), ".." + "H".repeat(l22 - 4) + "..", ".".repeat(l22)), a22;
|
|
91
|
+
}
|
|
92
|
+
var I = ["", "\u2598", "\u259D", "\u2580", "\u2596", "\u258C", "\u259E", "\u259B", "\u2597", "\u259A", "\u2590", "\u259C", "\u2584", "\u2599", "\u259F", "\u2588"];
|
|
93
|
+
function C(t2, r22) {
|
|
94
|
+
let n2 = Math.ceil(Math.max(...t2.map((t3) => t3.length)) / 2), l22 = Math.ceil(t2.length / 2), a22 = new m97596_z(n2, l22);
|
|
95
|
+
for (let e2 = 0; e2 < l22; e2++)
|
|
96
|
+
for (let l3 = 0; l3 < n2; l3++) {
|
|
97
|
+
let n3 = [t2[2 * e2]?.[2 * l3], t2[2 * e2]?.[2 * l3 + 1], t2[2 * e2 + 1]?.[2 * l3], t2[2 * e2 + 1]?.[2 * l3 + 1]].map((t3) => t3 && t3 in r22 ? r22[t3] : -1), o22 = /* @__PURE__ */ new Map();
|
|
98
|
+
if (n3.forEach((t3) => {
|
|
99
|
+
t3 >= 0 && o22.set(t3, (o22.get(t3) ?? 0) + 1);
|
|
100
|
+
}), !o22.size)
|
|
101
|
+
continue;
|
|
102
|
+
let [h22, i2] = [...o22.entries()].sort((t3, e3) => e3[1] - t3[1]).map((t3) => t3[0]), f2 = n3.reduce((t3, e3, r3) => t3 | (e3 === h22 || e3 >= 0 && e3 !== i2 ? 1 << r3 : 0), 0);
|
|
103
|
+
a22.set(l3, e2, I[f2], h22, i2 ?? void 0);
|
|
104
|
+
}
|
|
105
|
+
return a22;
|
|
106
|
+
}
|
|
107
|
+
var D = { 2: ["...L....", "..LL.R..", ".HHHHHH.", "........"], 3: [".....L....", "....LL....", "...LLL.R..", "..LLLL.RR.", ".HHHHHHHHH", ".........."], 4: ["......L.....", ".....LL.....", "....LLL.R...", "...LLLL.RR..", "..LLLLL.RRR.", "HHHHHHHHHHHH", ".HHHHHHHHHH.", "............"] };
|
|
108
|
+
function P(t2, e2, r22, n2, l22 = 0) {
|
|
109
|
+
let a22 = D[t2] ?? v(t2), o22 = a22.filter((t3) => /[LR]/.test(t3)).length, h22 = 0, i2 = a22.map((t3) => {
|
|
110
|
+
if (!/[LR]/.test(t3))
|
|
111
|
+
return t3;
|
|
112
|
+
let e3 = h22++ / Math.max(1, o22);
|
|
113
|
+
return t3.replace(/L/g, "abc"[Math.min(2, Math.floor(3 * e3))]).replace(/R/g, "de"[Math.min(1, Math.floor(2 * e3))]);
|
|
114
|
+
}), f2 = (t3) => Math.max(0, Math.min(20, t3 + l22));
|
|
115
|
+
return C(i2, { a: f2(e2[0]), b: f2(e2[1]), c: f2(e2[2]), d: f2(r22[0]), e: f2(r22[1]), H: f2(n2) });
|
|
116
|
+
}
|
|
117
|
+
var m89369_U = function (t2, e2) {
|
|
118
|
+
let r22 = v(t2), n2 = 2 * t2 - 3, l22 = r22.filter((t3) => /[LR]/.test(t3)).length;
|
|
119
|
+
return C(r22.map((t3, e3) => t3.includes("H") || e3 >= l22 ? t3 : [...t3].map((t4, r3) => r3 === n2 ? "M" : e3 >= l22 - 2 && r3 >= n2 - Math.round(0.8 * l22) && r3 <= n2 ? "L" : ".").join("")), { M: e2[0], L: e2[1], R: Math.min(20, e2[1] + 2), H: e2[2] });
|
|
120
|
+
};
|
|
121
|
+
var m89369_V = function (t2) {
|
|
122
|
+
let e2 = [], r22 = Math.ceil(t2.hz / 16), n2 = t2.H / 16;
|
|
123
|
+
return [{ row: r22 + 4, h: 3, n: Math.max(2, Math.round(t2.W / 420)) }, { row: r22 + 9, h: 4, n: Math.max(1, Math.round(t2.W / 600)) }, { row: n2 - 1 - (t2.fade ?? 0), h: 5, n: Math.max(1, Math.round(t2.W / 1e3)) }].forEach((r3, n3) => {
|
|
124
|
+
for (let l22 = 0; l22 < r3.n; l22++) {
|
|
125
|
+
let a22 = t2.W * (0.2 + 0.62 * ((l22 + 0.5 + 0.37 * n3 + (L(l22, n3, 21) - 0.5) * 0.4) / r3.n % 1));
|
|
126
|
+
Math.abs(a22 - t2.mx) < 16 * r3.h && n3 < 2 || e2.push({ x: 8 * Math.round(a22 / 8), y: (r3.row - (n3 < 2 ? Math.round(2 * L(l22, n3, 23)) : 0)) * 16, h: 16 * r3.h });
|
|
127
|
+
}
|
|
128
|
+
}), e2;
|
|
129
|
+
};
|
|
130
|
+
// output/boat-lake/scene-1.js
|
|
131
|
+
var l2 = 2 * Math.PI, a2 = [[-1, { zen: 16.6, hor: 12.5, horFar: 14, band: 0.16, glow: 0, glowR: 0.3, cloud: 17, cloudLit: 14, star: 1, land: 17.5, landLit: 15.5, pine: 19, pineLit: 17, deep: 18.6, far: 16, fres: 0.55, crest: -2.4, trough: 1, road: 4, roadA: 0.8, sail: 6, hull: 17 }], [-0.2, { zen: 16.6, hor: 11, horFar: 14, band: 0.14, glow: 2, glowR: 0.4, cloud: 16.5, cloudLit: 13, star: 0.85, land: 17.5, landLit: 16, pine: 19, pineLit: 17.5, deep: 18.4, far: 15, fres: 0.6, crest: -2.4, trough: 1, road: 4.5, roadA: 0.8, sail: 6, hull: 17 }], [-0.06, { zen: 16.2, hor: 5, horFar: 12.5, band: 0.08, glow: 7, glowR: 0.45, cloud: 15, cloudLit: 6, star: 0.25, land: 18, landLit: 16.5, pine: 19, pineLit: 18, deep: 17.6, far: 11, fres: 0.75, crest: -2.6, trough: 1, road: 3, roadA: 0.6, sail: 5, hull: 16.5 }], [0.03, { zen: 15.4, hor: 1, horFar: 10.5, band: 0.06, glow: 9, glowR: 0.4, cloud: 14, cloudLit: 2, star: 0, land: 17.5, landLit: 13.5, pine: 18.8, pineLit: 15.5, deep: 16.6, far: 7, fres: 0.8, crest: -2.8, trough: 1.2, road: 0, roadA: 1, sail: 3, hull: 16 }], [0.16, { zen: 14.6, hor: 1.5, horFar: 8, band: 0.08, glow: 8, glowR: 0.35, cloud: 12, cloudLit: 3, star: 0, land: 15.5, landLit: 10.5, pine: 18, pineLit: 14, deep: 15.6, far: 6.5, fres: 0.75, crest: -2.8, trough: 1.2, road: 0, roadA: 1, sail: 2, hull: 15.5 }], [0.42, { zen: 13.9, hor: 4, horFar: 5.5, band: 0.12, glow: 5, glowR: 0.25, cloud: 9, cloudLit: 3, star: 0, land: 13.5, landLit: 9, pine: 16.5, pineLit: 12.5, deep: 14, far: 7, fres: 0.7, crest: -2.6, trough: 1.2, road: 1, roadA: 0.9, sail: 1, hull: 14.5 }], [1, { zen: 13.6, hor: 4.5, horFar: 5, band: 0.14, glow: 4, glowR: 0.2, cloud: 8, cloudLit: 3, star: 0, land: 13, landLit: 8.5, pine: 16, pineLit: 12, deep: 13.6, far: 7, fres: 0.7, crest: -2.6, trough: 1.2, road: 1.5, roadA: 0.9, sail: 1, hull: 14.5 }]], o2 = (t2) => t2 * t2 * (3 - 2 * t2), h2 = (t2, e2 = 0, r22 = 1) => t2 < e2 ? e2 : t2 > r22 ? r22 : t2, i = (t2, e2, r22) => o2(h2((r22 - t2) / (e2 - t2))), f = (t2, e2, r22) => t2 + (e2 - t2) * r22, u = /* @__PURE__ */ new WeakMap();
|
|
132
|
+
function M(t2, e2) {
|
|
133
|
+
let n2 = u.get(t2);
|
|
134
|
+
if (!n2) {
|
|
135
|
+
n2 = new Float32Array(t2.W + 200);
|
|
136
|
+
for (let e3 = 0; e3 < n2.length; e3++)
|
|
137
|
+
n2[e3] = Math.max(t2.hz - 48, t2.hz + 4 - (z((e3 - 100) / 300, 0.5, 5) - 0.45) * 180);
|
|
138
|
+
u.set(t2, n2);
|
|
139
|
+
}
|
|
140
|
+
let l22 = Math.round(e2) + 100;
|
|
141
|
+
return n2[l22 < 0 ? 0 : l22 >= n2.length ? n2.length - 1 : l22];
|
|
142
|
+
}
|
|
143
|
+
var s = /* @__PURE__ */ new WeakMap();
|
|
144
|
+
function c(t2) {
|
|
145
|
+
let e2 = s.get(t2);
|
|
146
|
+
if (e2)
|
|
147
|
+
return e2;
|
|
148
|
+
if (e2 = [], !t2.full)
|
|
149
|
+
return s.set(t2, e2), e2;
|
|
150
|
+
let n2 = (n3, l22, a22, o22, h22, i2) => {
|
|
151
|
+
let f2 = l22;
|
|
152
|
+
for (let l3 = 0; l3 < n3; l3++) {
|
|
153
|
+
// Receiver adaptation: keep pine tips inside short scenes instead of clipping a fixed 64px tree.
|
|
154
|
+
let u2 = Math.max(Math.min(64, t2.hz * (0.54 + 0.3 * L(l3 + 0.5, 2, h22))), o22 * (0.62 + 0.38 * L(l3 + 0.5, 2, h22)) * (1 - l3 / n3 * 0.55));
|
|
155
|
+
e2.push({ x: f2, top: t2.hz + 10 - u2, base: t2.hz + 12, w: i2 }), f2 += a22 * u2 * (0.18 + 0.22 * L(l3 + 0.5, 3, h22));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return n2(Math.max(3, Math.round(t2.W / 360)), 10, 1, Math.min(0.36 * t2.hz, 130), 7, 9), n2(Math.max(4, Math.round(t2.W / 240)), t2.W - 12, -1, 0.66 * Math.min(t2.hz, 300), 8, 11), s.set(t2, e2), e2;
|
|
159
|
+
}
|
|
160
|
+
function d(t2, e2, r22, n2) {
|
|
161
|
+
if (r22 < t2.top || r22 > t2.base)
|
|
162
|
+
return 0;
|
|
163
|
+
let l22 = r22 - t2.top, a22 = 0.72 + 0.28 * (l22 / (1.1 * t2.w) % 1), o22 = 0.24 * l22 * a22 + 0.6, h22 = e2 - t2.x;
|
|
164
|
+
return Math.abs(h22) > o22 ? +(1 > Math.abs(h22) && r22 > t2.base - 6) : (n2 > t2.x ? h22 > 0.15 * o22 : h22 < -(0.15 * o22)) && Math.abs(h22) > o22 - 3.2 ? 2 : 1;
|
|
165
|
+
}
|
|
166
|
+
var p = /* @__PURE__ */ new WeakMap();
|
|
167
|
+
function w(t2, e2, n2, l22, a22 = !1) {
|
|
168
|
+
let o22 = h2(1 - l22 / t2.hz), u2 = Math.exp(-(((n2 - e2.sx) / (0.42 * t2.W)) ** 2)), M2 = f(e2.horFar, e2.hor, u2), s2 = e2.zen - 1.4 * (1 - o22);
|
|
169
|
+
s2 -= (s2 - M2) * Math.exp(-o22 / e2.band);
|
|
170
|
+
let c2 = (n2 - e2.sx) / t2.W, d2 = (l22 - e2.sy) / t2.hz;
|
|
171
|
+
if (s2 -= e2.glow * Math.exp(-(c2 * c2 * 2.2 + d2 * d2) / (e2.glowR * e2.glowR)) * (0.35 + 0.65 * i(0.5, 0, o22)), e2.moonEl > -0.1) {
|
|
172
|
+
let r22 = (n2 - e2.mx) / t2.W, a3 = (l22 - e2.my) / t2.hz;
|
|
173
|
+
s2 -= 2.6 * e2.night * Math.exp(-(r22 * r22 * 3 + a3 * a3) / 0.03);
|
|
174
|
+
}
|
|
175
|
+
if (t2.full || t2.W > 0) {
|
|
176
|
+
let a3 = i(0.5 * t2.W, 0.62 * t2.W, n2);
|
|
177
|
+
if (a3 > 0 && o22 > 0.12 && o22 < 0.7) {
|
|
178
|
+
let h22 = i(0.6, 0.76, z((n2 - 4 * e2.t) / 230, l22 / 15, 3)) * i(0.12, 0.22, o22) * i(0.7, 0.5, o22) * a3;
|
|
179
|
+
if (h22 > 0) {
|
|
180
|
+
let r22 = Math.exp(-(((n2 - e2.lx) / t2.W) ** 2 * 6 + ((l22 - e2.ly) / t2.hz) ** 2 * 2));
|
|
181
|
+
s2 = f(s2, f(e2.cloud, e2.cloudLit, r22), 0.85 * h22);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (t2.full || (s2 = f(14, s2, i(80, 200, l22))), !a22) {
|
|
186
|
+
if (e2.star > 0 && n2 % 8 < 4 && l22 % 16 < 8 && o22 > 0.3) {
|
|
187
|
+
let a3 = Math.floor(n2 / 8), h22 = Math.floor(l22 / 16), i2 = L(1.37 * a3 + 0.5, 2.11 * h22 + 0.5, 91);
|
|
188
|
+
(a3 + h22) % 3 == 0 && i2 > 0.86 && n2 > t2.copyW && L(a3 + 0.5, h22 + 0.5, 93) < e2.star * (o22 - 0.2) && Math.hypot((n2 - e2.mx) / t2.W * 2, (l22 - e2.my) / t2.hz) > 0.22 && (s2 = Math.min(s2, 4 + 6 * L(a3 + 0.5, h22 + 0.5, 95)));
|
|
189
|
+
}
|
|
190
|
+
if (t2.full && n2 < t2.copyW + 520) {
|
|
191
|
+
let e3 = i(t2.copyW + 520, t2.copyW - 60, n2) * i(0.1, 0.22, o22);
|
|
192
|
+
s2 < 13 && (s2 = f(s2, 13, e3));
|
|
193
|
+
}
|
|
194
|
+
e2.sunEl > -0.2 && (s2 = g(s2, n2, l22, e2.sx, e2.sy, 21, 0, 1.5, 0)), e2.moonEl > -0.2 && (s2 = g(s2, n2, l22, e2.mx, e2.my, 18, 1, 3, 1));
|
|
195
|
+
}
|
|
196
|
+
return h2(s2, 0, 20);
|
|
197
|
+
}
|
|
198
|
+
function g(t2, e2, r22, n2, l22, a22, o22, u2, M2) {
|
|
199
|
+
let s2 = Math.hypot(e2 - n2, r22 - l22);
|
|
200
|
+
if (s2 > a22 + 16)
|
|
201
|
+
return t2;
|
|
202
|
+
t2 = Math.min(t2, f(t2, u2 + 2.5, 0.85 * Math.exp(-(((s2 - a22) / 7) ** 2))));
|
|
203
|
+
let c2 = h2((a22 - s2) / 8 + 0.5);
|
|
204
|
+
if (c2 <= 0)
|
|
205
|
+
return t2;
|
|
206
|
+
let d2 = o22 + (u2 - o22) * i(0.5 * a22, a22, s2);
|
|
207
|
+
return M2 && 7 > Math.hypot(e2 - n2 + 6, r22 - l22 + 2) && (d2 = 3.5), f(t2, Math.min(t2, d2), c2);
|
|
208
|
+
}
|
|
209
|
+
function y(t2, e2, n2, l22) {
|
|
210
|
+
let a22 = (function (t3, e3, n3) {
|
|
211
|
+
if (n3 >= t3.hz)
|
|
212
|
+
return 0;
|
|
213
|
+
let l3 = Math.round(e3 / 16), a3 = 16 * l3, o3 = t3.hz - M(t3, a3);
|
|
214
|
+
if (o3 < 6 || t3.hz - M(t3, e3) < 6)
|
|
215
|
+
return 0;
|
|
216
|
+
let i3 = (10 + 12 * L(l3, 1, 11)) * h2((o3 - 3) / 18), f2 = Math.min(M(t3, e3), M(t3, a3) - i3 + 1.4 * Math.abs(e3 - a3));
|
|
217
|
+
return n3 > f2 ? 1 + n3 - f2 : 0;
|
|
218
|
+
})(t2, n2, l22);
|
|
219
|
+
if (!a22)
|
|
220
|
+
return -1;
|
|
221
|
+
if (l22 > t2.hz - 5)
|
|
222
|
+
return f(e2.land, e2.landLit, 0.5);
|
|
223
|
+
let o22 = M(t2, n2 + 40) - M(t2, n2 - 40), i2 = h2((e2.lx > n2 ? o22 : -o22) / 30, -1, 1), u2 = f(e2.land, e2.landLit, 0.25 + 0.25 * i2);
|
|
224
|
+
return a22 < 5 && (u2 = f(u2, e2.landLit, 0.7)), u2;
|
|
225
|
+
}
|
|
226
|
+
var b2 = (t2, e2, r22, n2) => Math.sin(1.6 / (h2((r22 - t2.hz) / (t2.H - t2.hz)) + 0.12) + e2 / t2.W * 2.1 + 1e3 * n2 / 4200 * l2);
|
|
227
|
+
function L2(t2, e2, r22, n2) {
|
|
228
|
+
if (n2 < t2.hz) {
|
|
229
|
+
for (let l22 of c(t2)) {
|
|
230
|
+
let t3 = d(l22, r22, n2, e2.lx);
|
|
231
|
+
if (t3)
|
|
232
|
+
return t3 === 2 ? e2.pineLit : e2.pine;
|
|
233
|
+
}
|
|
234
|
+
return -1;
|
|
235
|
+
}
|
|
236
|
+
for (let [l22, a22] of (function (t3) {
|
|
237
|
+
let e3 = p.get(t3);
|
|
238
|
+
if (!e3) {
|
|
239
|
+
let r3 = c(t3);
|
|
240
|
+
e3 = [r3.filter((e4) => e4.x < t3.W / 2).map((t4) => t4.x), r3.filter((e4) => e4.x >= t3.W / 2).map((t4) => t4.x)].filter((t4) => t4.length).map((t4) => [Math.min(...t4), Math.max(...t4)]), p.set(t3, e3);
|
|
241
|
+
}
|
|
242
|
+
return e3;
|
|
243
|
+
})(t2))
|
|
244
|
+
if (r22 > l22 - 26 + (n2 - t2.hz) * 1.4 && r22 < a22 + 26 - (n2 - t2.hz) * 1.4 && n2 < t2.hz + 16)
|
|
245
|
+
return e2.pine + 0.5;
|
|
246
|
+
return -1;
|
|
247
|
+
}
|
|
248
|
+
function z2(t2, e2, l22, a22, o22) {
|
|
249
|
+
var u2;
|
|
250
|
+
let M2, s2, p2, m2, x2;
|
|
251
|
+
if (o22 < t2.hz)
|
|
252
|
+
return -1;
|
|
253
|
+
let g2 = c(t2), L22 = e2.t, z22 = h2((o22 - t2.hz) / (t2.H - t2.hz)), H22 = b2(t2, a22, o22, L22) * i(0.02, 0.3, z22), W22 = H22 * (1.5 + 6 * z22), A22 = 2 * t2.hz - o22 + H22 * (3 + 10 * z22), R22 = w(t2, e2, a22 + W22, A22, !0), E22 = y(t2, e2, a22 + W22, A22);
|
|
254
|
+
for (let t3 of (E22 >= 0 && (R22 = E22), g2))
|
|
255
|
+
if (d(t3, a22 + W22, A22, e2.lx)) {
|
|
256
|
+
R22 = f(e2.pine, R22, 0.3);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
for (let t3 of l22) {
|
|
260
|
+
if (t3.off || o22 < t3.y || o22 > t3.y + t3.h || Math.abs(a22 - t3.x) > 0.7 * t3.h)
|
|
261
|
+
continue;
|
|
262
|
+
let l3 = E(t3, a22 + W22, 2 * t3.y - o22);
|
|
263
|
+
if (l3 && Math.sin(0.55 * o22 + 3 * L(a22 / 40, o22 / 8, 5)) > -0.2) {
|
|
264
|
+
R22 = f(R22, l3 === 3 ? e2.hull : e2.sail + 2 * (l3 === 2), 0.5);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
let F22 = f(e2.far, e2.deep, Math.pow(z22, 0.7)), k22 = f(F22, R22, e2.fres * Math.pow(1 - z22, 1.3));
|
|
269
|
+
k22 -= 0.4 * H22;
|
|
270
|
+
let Q2 = (u2 = 0.78 - 0.03 * z22 - 0.05 * H22, M2 = Math.floor(o22 / 8), s2 = 16 + 60 * z22, m2 = (p2 = (t3) => L((a22 + L22 * (4 + 10 * z22) * (1 & t3 ? 1 : -0.6)) / s2, 0.05 * L22 + (7 & t3) * 3.1, 131 + 17 * t3))(M2), x2 = p2(M2 - 1), m2 > u2 && x2 <= u2 ? 1 : x2 > u2 && p2(M2 - 2) <= u2 ? -1 : 0), v22 = t2.full ? 1 : 0.6;
|
|
271
|
+
if (Q2 > 0 ? k22 += e2.crest * (0.4 + 0.3 * z22) * v22 : Q2 < 0 && (k22 += 0.6 * e2.trough * v22), e2.lEl > -0.05) {
|
|
272
|
+
let t3 = Math.max(0, e2.lEl), n2 = (7 + 70 * z22) * (0.7 + 0.9 * t3), l3 = Math.exp(-(((a22 - e2.lx) / n2) ** 2));
|
|
273
|
+
if (l3 > 0.02) {
|
|
274
|
+
let n3 = l3 * i(0, 0.35, z22 + 0.3 - 0.5 * t3) * e2.roadA * (e2.sunEl > -0.05 ? 1 : 0.85) * i(-0.05, 0.03, e2.lEl), h22 = Q2 > 0 ? 1 : 0.6 * (Q2 === 0 && L(a22 / 10, 1.3 * Math.floor(o22 / 8) + 0.3 * L22, 9) > 0.62);
|
|
275
|
+
k22 = f(k22, e2.road, n3 * h22);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return t2.fade && (k22 = f(k22, 0, i(t2.H - (t2.fade + 1) * 16, t2.H - 40, Math.min(t2.H - 40, o22 + (L(a22 / 160, 3.3, 17) - 0.5) * 22 + (L(a22 / 36, 7.1, 19) - 0.5) * 24)))), h2(k22, 0, 20);
|
|
279
|
+
}
|
|
280
|
+
function H2(t2, e2, r22 = 1) {
|
|
281
|
+
let n2 = (t3) => Math.max(0, Math.min(20, Math.floor(t3 + e2))), l22 = t2.back * i(0.5, 0.15, r22) > 0.5 ? 15 : t2.sail;
|
|
282
|
+
return [n2(l22), n2(l22 + 1.6), n2(l22 + 3.2), n2(l22 + 1), n2(l22 + 2.4), n2(t2.hull)];
|
|
283
|
+
}
|
|
284
|
+
var W2 = /* @__PURE__ */ new Map();
|
|
285
|
+
function A2(t2, e2) {
|
|
286
|
+
let r22 = `${t2.h},${+!!t2.off},${e2.join(",")}`, l22 = W2.get(r22);
|
|
287
|
+
return l22 || (l22 = t2.off ? m89369_U(t2.h / 16, [e2[1], e2[0], e2[5]]) : P(t2.h / 16, e2.slice(0, 3), e2.slice(3, 5), e2[5]), W2.size > 300 && W2.clear(), W2.set(r22, l22)), l22;
|
|
288
|
+
}
|
|
289
|
+
var R2 = e.map((t2) => {
|
|
290
|
+
let e2 = [1, 3, 5].map((e3) => parseInt(t2.slice(e3, e3 + 2), 16) / 255).map((t3) => t3 <= 0.04045 ? t3 / 12.92 : ((t3 + 0.055) / 1.055) ** 2.4);
|
|
291
|
+
return 0.2126 * e2[0] + 0.7152 * e2[1] + 0.0722 * e2[2];
|
|
292
|
+
}), E2 = e.length, F2 = [];
|
|
293
|
+
for (let t2 = 0; t2 < E2; t2++)
|
|
294
|
+
F2.push({ pos: t2, g: 0, f: -1, b: t2 }), t2 < E2 - 1 && [0.25, 0.5, 0.75].forEach((e2, r22) => F2.push({ pos: (function (t3) {
|
|
295
|
+
for (let e3 = 0; e3 < E2 - 1; e3++)
|
|
296
|
+
if (t3 >= R2[e3 + 1])
|
|
297
|
+
return e3 + (R2[e3] - t3) / (R2[e3] - R2[e3 + 1]);
|
|
298
|
+
return E2 - 1;
|
|
299
|
+
})(R2[t2] * (1 - e2) + R2[t2 + 1] * e2), g: 16 + r22, f: t2 + 1, b: t2 }));
|
|
300
|
+
var k2 = (() => {
|
|
301
|
+
let t2 = [1, 0, 3, 2], e2 = [2, 3, 0, 1], r22 = [];
|
|
302
|
+
for (let n2 = 0; n2 < 4; n2++)
|
|
303
|
+
for (let l22 = 0; l22 < 4; l22++)
|
|
304
|
+
r22.push(l22 + n2 & 1 ? l22 === t2[n2] ? 0.17 : 0.33 : l22 === e2[n2] ? 0.67 : 0.83);
|
|
305
|
+
return r22;
|
|
306
|
+
})(), Q = new Float64Array(4), v2 = new Int8Array(4);
|
|
307
|
+
var m59908_F = function (t2, e2, r22) {
|
|
308
|
+
return r22.map((r3, n2) => {
|
|
309
|
+
let l22 = A2(r3, H2(e2, (0.618 * n2 + 0.13) % 1, h2((r3.y - t2.hz) / (t2.H - t2.hz)))), a22 = r3.h >= 64 ? 4 : 2;
|
|
310
|
+
return { g: l22, cx: Math.round(r3.x / 8 - l22.w / 2), cy: (r3.y - r3.h) / 16, dy: -b2(t2, r3.x, r3.y, e2.t) * a22, y: r3.y };
|
|
311
|
+
}).sort((t3, e3) => t3.y - e3.y);
|
|
312
|
+
};
|
|
313
|
+
var m59908_z = function (t2, e2, r22) {
|
|
314
|
+
let n2, u2, M2, s2 = l2 * (n2 = (e2 % 1 + 1) % 1) - 0.25 * Math.sin(2 * l2 * n2), { cx: c2, rx: d2, ry: p2 } = (u2 = Math.min(t2.copyW + 40, 0.6 * t2.W), M2 = t2.full ? u2 + (t2.W - u2) * 0.3 : 0.5 * t2.W, { cx: M2, rx: t2.full ? (t2.W - u2) * 0.24 : 0.36 * t2.W, ry: t2.full ? 0.72 * t2.hz : Math.max(16, t2.hz - 96 - 21 - 4) }), m2 = Math.sin(s2), x2 = c2 - d2 * Math.cos(s2), w2 = t2.hz - p2 * m2, g2 = c2 + d2 * Math.cos(s2), y2 = t2.hz + p2 * m2, b22 = (function (t3) {
|
|
315
|
+
let e3 = 1;
|
|
316
|
+
for (; e3 < a2.length - 1 && t3 > a2[e3][0];)
|
|
317
|
+
e3++;
|
|
318
|
+
let [r3, n3] = a2[e3 - 1], [l22, i2] = a2[e3], u3 = o2(h2((t3 - r3) / (l22 - r3))), M3 = {};
|
|
319
|
+
for (let t4 in n3)
|
|
320
|
+
M3[t4] = f(n3[t4], i2[t4], u3);
|
|
321
|
+
return M3;
|
|
322
|
+
})(m2), L22 = m2 > -0.05;
|
|
323
|
+
return { ...b22, p: e2, t: r22, sx: x2, sy: w2, sunEl: m2, mx: g2, my: y2, moonEl: -m2, lx: L22 ? x2 : g2, ly: L22 ? w2 : y2, lEl: L22 ? m2 : -m2, night: i(-0.02, -0.25, m2), back: i(0.4, 0.12, m2) * i(-0.1, 0, m2) };
|
|
324
|
+
};
|
|
325
|
+
export { E as boatShape, m89369_V as fleet, y as land, k as layout, m59908_z as lighting, e as palette, L2 as pines, w as sky, z2 as water };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { encodeTerminalCell } from './terminal-cell.js';
|
|
2
|
+
import * as kernel from './boat-lake-kernel.js';
|
|
3
|
+
const colors = kernel.palette;
|
|
4
|
+
const mono = colors.map((_, index) => {
|
|
5
|
+
const level = Math.round(248 - index * 10.8).toString(16).padStart(2, '0');
|
|
6
|
+
return `#${level}${level}${level}`;
|
|
7
|
+
});
|
|
8
|
+
// Bounded by viewport count, independent of playback duration. No frame history.
|
|
9
|
+
const scenes = new Map();
|
|
10
|
+
function landscape(layout, light, x, y) {
|
|
11
|
+
const pine = kernel.pines(layout, light, x, y);
|
|
12
|
+
if (pine >= 0)
|
|
13
|
+
return pine;
|
|
14
|
+
const land = kernel.land(layout, light, x, y);
|
|
15
|
+
return land >= 0 ? land : kernel.sky(layout, light, x, y);
|
|
16
|
+
}
|
|
17
|
+
function sceneFor(width, height) {
|
|
18
|
+
const key = `${width}:${height}`;
|
|
19
|
+
const cached = scenes.get(key);
|
|
20
|
+
if (cached)
|
|
21
|
+
return cached;
|
|
22
|
+
// Keep native-sized artwork, rather than shrinking a 160×42 scene into
|
|
23
|
+
// every receiver. Short receivers need larger sails and less empty sky.
|
|
24
|
+
const columns = Math.max(20, width);
|
|
25
|
+
const rows = Math.max(8, height);
|
|
26
|
+
const layout = { ...kernel.layout(columns, rows, Math.round(rows * 0.6)), full: true, copyW: 0, fade: 0 };
|
|
27
|
+
layout.hz = Math.round(layout.H * (height <= 7 ? 0.36 : 0.45));
|
|
28
|
+
const light = kernel.lighting(layout, 0.44, 0);
|
|
29
|
+
const samples = new Float32Array(width * height * 8).fill(-1);
|
|
30
|
+
for (let y = 0; y < height * 4; y++) {
|
|
31
|
+
const vy = (y + 0.5) * layout.H / (height * 4);
|
|
32
|
+
for (let x = 0; x < width * 2; x++) {
|
|
33
|
+
const vx = (x + 0.5) * layout.W / (width * 2);
|
|
34
|
+
const pine = kernel.pines(layout, light, vx, vy);
|
|
35
|
+
if (pine >= 0 || vy < layout.hz)
|
|
36
|
+
samples[y * width * 2 + x] = pine >= 0 ? pine : landscape(layout, light, vx, vy);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const water = layout.H - layout.hz;
|
|
40
|
+
const size = (fraction, minimum) => Math.round(Math.max(minimum, layout.H * fraction) / 16) * 16;
|
|
41
|
+
const fleet = [{ x: layout.W * 0.41, y: layout.H - 16, h: size(0.18, width < 24 ? 48 : 64) }];
|
|
42
|
+
if (width >= 50 && height >= 7)
|
|
43
|
+
fleet.unshift({ x: layout.W * 0.30, y: layout.hz + water * 0.37, h: size(0.11, 32) });
|
|
44
|
+
if (width >= 70 && height >= 7)
|
|
45
|
+
fleet.unshift({ x: layout.W * 0.81, y: layout.hz + water * 0.18, h: 32 });
|
|
46
|
+
// The expensive reflected scene is stable at fixed lighting. Preserve its
|
|
47
|
+
// eight-sample detail, then animate small horizontal refractions at runtime.
|
|
48
|
+
const reflected = new Float32Array(samples.length);
|
|
49
|
+
for (let y = 0; y < height * 4; y++)
|
|
50
|
+
for (let x = 0; x < width * 2; x++) {
|
|
51
|
+
const index = y * width * 2 + x;
|
|
52
|
+
reflected[index] = samples[index] >= 0 ? samples[index] : kernel.water(layout, light, fleet, (x + 0.5) * layout.W / (width * 2), (y + 0.5) * layout.H / (height * 4));
|
|
53
|
+
}
|
|
54
|
+
const cells = new Array(width * height);
|
|
55
|
+
const cellSamples = new Float32Array(8);
|
|
56
|
+
for (let y = 0; y < height; y++)
|
|
57
|
+
for (let x = 0; x < width; x++) {
|
|
58
|
+
for (let q = 0; q < 8; q++)
|
|
59
|
+
cellSamples[q] = samples[(y * 4 + (q >> 1)) * width * 2 + x * 2 + (q & 1)];
|
|
60
|
+
if (cellSamples.every(value => value >= 0))
|
|
61
|
+
cells[y * width + x] = encodeTerminalCell(cellSamples, x, y, 20);
|
|
62
|
+
}
|
|
63
|
+
const scene = { layout, light, fleet, samples, cells, water: reflected };
|
|
64
|
+
if (scenes.size >= 4)
|
|
65
|
+
scenes.delete(scenes.keys().next().value);
|
|
66
|
+
scenes.set(key, scene);
|
|
67
|
+
return scene;
|
|
68
|
+
}
|
|
69
|
+
/** At four rows, subpixel averages cannot retain a sail's silhouette. Draw
|
|
70
|
+
* its two sails and hull on the terminal grid, with transparent water behind. */
|
|
71
|
+
function compactBoat(x, y, width, height) {
|
|
72
|
+
if (width < 12 || height < 3 || height > 6)
|
|
73
|
+
return undefined;
|
|
74
|
+
const left = Math.round(width * 0.41) - 3;
|
|
75
|
+
const column = x - left;
|
|
76
|
+
const hull = height - 1;
|
|
77
|
+
if (y === hull && column >= 0 && column <= 6)
|
|
78
|
+
return { glyph: column === 0 ? '◥' : column === 6 ? '◤' : '▀', fg: 18 };
|
|
79
|
+
if (y === hull - 1 && column >= 1 && column <= 5)
|
|
80
|
+
return { glyph: column === 1 ? '◢' : column === 5 ? '◣' : column === 4 ? '▌' : '█', fg: column === 5 ? 5 : 1 };
|
|
81
|
+
if (height >= 4 && y === hull - 2 && (column === 3 || column === 4))
|
|
82
|
+
return { glyph: column === 3 ? '◢' : '▌', fg: 1 };
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
function compactPine(x, y, width, height) {
|
|
86
|
+
if (width < 12 || height < 3 || height > 6 || y > 1)
|
|
87
|
+
return undefined;
|
|
88
|
+
const local = x < width / 2 ? x : x - width + 4;
|
|
89
|
+
if (y === 0 && (local === 1 || local === 2))
|
|
90
|
+
return { glyph: local === 1 ? '◢' : '◣', fg: 18, bg: 14 };
|
|
91
|
+
if (height >= 4 && y === 1 && local >= 0 && local <= 3)
|
|
92
|
+
return { glyph: local === 0 ? '◢' : local === 3 ? '◣' : '█', fg: 18, bg: 14 };
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
/** Boat's lake artwork with viewport-sized scenery and legible compact sails. */
|
|
96
|
+
export function buildBoatLake(pulse, width, height, theme) {
|
|
97
|
+
const scene = sceneFor(width, height);
|
|
98
|
+
const { layout } = scene;
|
|
99
|
+
const time = pulse * 0.08;
|
|
100
|
+
const light = scene.light;
|
|
101
|
+
const fleet = scene.fleet.map((boat, index) => ({ ...boat, y: boat.y + Math.sin(time * 0.6 + index) * 1.5 }));
|
|
102
|
+
const shifts = new Float32Array(height * 4);
|
|
103
|
+
for (let y = 0; y < shifts.length; y++) {
|
|
104
|
+
const depth = Math.max(0, ((y + 0.5) * layout.H / shifts.length - layout.hz) / (layout.H - layout.hz));
|
|
105
|
+
shifts[y] = Math.sin(time * 0.7 + y * 0.65) * (0.3 + 1.2 * depth);
|
|
106
|
+
}
|
|
107
|
+
const palette = theme === 'mono' ? mono : colors;
|
|
108
|
+
const rows = [];
|
|
109
|
+
const samples = new Float32Array(8);
|
|
110
|
+
for (let y = 0; y < height; y++) {
|
|
111
|
+
let text = '';
|
|
112
|
+
const segments = [];
|
|
113
|
+
for (let x = 0; x < width; x++) {
|
|
114
|
+
const vx0 = x * layout.W / width, vy0 = y * layout.H / height;
|
|
115
|
+
const cached = scene.cells[y * width + x];
|
|
116
|
+
const overlapsBoat = height > 6 && fleet.some(boat => vx0 < boat.x + boat.h * 0.55 && vx0 + layout.W / width > boat.x - boat.h * 0.55 && vy0 < boat.y && vy0 + layout.H / height > boat.y - boat.h);
|
|
117
|
+
let cell = cached;
|
|
118
|
+
if (!cell || overlapsBoat) {
|
|
119
|
+
for (let dy = 0; dy < 4; dy++)
|
|
120
|
+
for (let dx = 0; dx < 2; dx++) {
|
|
121
|
+
const sx = x * 2 + dx;
|
|
122
|
+
const sy = y * 4 + dy;
|
|
123
|
+
const vx = (sx + 0.5) * layout.W / (width * 2);
|
|
124
|
+
const vy = (sy + 0.5) * layout.H / (height * 4);
|
|
125
|
+
let value = scene.samples[sy * width * 2 + sx];
|
|
126
|
+
if (value < 0) {
|
|
127
|
+
const sampleX = Math.max(0, Math.min(width * 2 - 1, sx + shifts[sy]));
|
|
128
|
+
const left = Math.floor(sampleX), fraction = sampleX - left;
|
|
129
|
+
const a = scene.water[sy * width * 2 + left];
|
|
130
|
+
const b = scene.water[sy * width * 2 + Math.min(width * 2 - 1, left + 1)];
|
|
131
|
+
value = a + (b - a) * fraction;
|
|
132
|
+
}
|
|
133
|
+
// Rasterize the original sail/hull polygons at the target resolution.
|
|
134
|
+
// Sampling an already quantized sprite erased small sails a second time.
|
|
135
|
+
if (overlapsBoat)
|
|
136
|
+
for (const boat of fleet) {
|
|
137
|
+
if (vy < boat.y - boat.h || vy > boat.y || Math.abs(vx - boat.x) > boat.h * 0.55)
|
|
138
|
+
continue;
|
|
139
|
+
const shape = kernel.boatShape(boat, vx, vy);
|
|
140
|
+
if (shape)
|
|
141
|
+
value = shape === 3 ? light.hull : light.sail + (shape === 2 ? 2 : 0);
|
|
142
|
+
}
|
|
143
|
+
samples[dy * 2 + dx] = value;
|
|
144
|
+
}
|
|
145
|
+
cell = encodeTerminalCell(samples, x, y, 20);
|
|
146
|
+
}
|
|
147
|
+
const detail = compactBoat(x, y, width, height) ?? compactPine(x, y, width, height);
|
|
148
|
+
const { glyph, fg, bg } = detail ? { ...cell, ...detail } : cell;
|
|
149
|
+
const color = palette[fg];
|
|
150
|
+
const backgroundColor = palette[bg];
|
|
151
|
+
text += glyph;
|
|
152
|
+
const previous = segments.at(-1);
|
|
153
|
+
if (previous?.color === color && previous.backgroundColor === backgroundColor)
|
|
154
|
+
previous.text += glyph;
|
|
155
|
+
else
|
|
156
|
+
segments.push({ text: glyph, color, backgroundColor });
|
|
157
|
+
}
|
|
158
|
+
rows.push({ text, color: palette[14], segments });
|
|
159
|
+
}
|
|
160
|
+
return rows;
|
|
161
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { themeAccent } from '../theme.js';
|
|
2
2
|
import { horseStrideFrames, horseStrideHeight, horseStrideWidth } from './horse-stride.js';
|
|
3
|
-
const frames = horseStrideFrames.map(frame =>
|
|
3
|
+
const frames = horseStrideFrames.map(frame => Uint8Array.from(atob(frame), char => char.charCodeAt(0)));
|
|
4
4
|
const brailleBits = [[1, 8], [2, 16], [4, 32], [64, 128]];
|
|
5
5
|
/** One pose per receiver tick. The same scale and torso anchor are used for every pose. */
|
|
6
6
|
export function buildGallop(pulse, width, height, theme) {
|
|
@@ -11,7 +11,7 @@ export function buildGallop(pulse, width, height, theme) {
|
|
|
11
11
|
const frame = frames[frameIndex];
|
|
12
12
|
const usableW = w > 8 ? w - 4 : w;
|
|
13
13
|
const usableH = h > 4 ? h - 2 : h;
|
|
14
|
-
const scale = Math.min(usableW * 2 / horseStrideWidth, usableH * 4 / horseStrideHeight);
|
|
14
|
+
const scale = Math.min(usableW * 2 / horseStrideWidth, usableH * 4 / horseStrideHeight) * 0.9;
|
|
15
15
|
const dotsW = Math.max(1, Math.floor(horseStrideWidth * scale));
|
|
16
16
|
const dotsH = Math.max(1, Math.floor(horseStrideHeight * scale));
|
|
17
17
|
const left = Math.floor((w - Math.ceil(dotsW / 2)) / 2) * 2;
|
|
@@ -62,12 +62,14 @@ export const receiverStyleRegistry = [
|
|
|
62
62
|
{ id: 'golden-gate', maxRows: 14, minRows: 7 },
|
|
63
63
|
{ id: 'manhattan', maxRows: 14, minRows: 7 },
|
|
64
64
|
{ id: 'alpine', maxRows: 14, minRows: 7 },
|
|
65
|
+
{ id: 'boat-lake', maxRows: 18, minRows: 7 },
|
|
65
66
|
{ id: 'crossfade', maxRows: 14, minRows: 7 },
|
|
66
67
|
];
|
|
67
68
|
export const receiverStyleNames = receiverStyleRegistry.map(style => style.id);
|
|
68
|
-
export const defaultReceiverStyle = '
|
|
69
|
+
export const defaultReceiverStyle = 'ultracode';
|
|
69
70
|
export const receiverStyleMetadata = Object.fromEntries(receiverStyleRegistry.map(style => [style.id, style]));
|
|
70
71
|
const retiredReceiverStyleNames = [
|
|
72
|
+
'synthwave-sun', 'neon-alley', 'safari-dusk', 'hot-air-balloons', 'deep-sea-whale', 'meteor-shower', 'volcano', 'desert-highway', 'firefly-grove', 'cherry-blossom', 'event-horizon', 'lighthouse',
|
|
71
73
|
'duplex-wave', 'color-drift', 'undertow', 'cube-tide', 'parallax-slabs', 'voxel-swell',
|
|
72
74
|
'dot-current', 'flip-matrix', 'raster-harp',
|
|
73
75
|
'spin-lattice', 'packet-storm', 'spark-chamber',
|