From 6d2e106e26114d3f36785ad8c0626561ac65b557 Mon Sep 17 00:00:00 2001 From: WHC Date: Thu, 29 Oct 2026 19:08:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20decode-wave=20stream=20glitch=20?= =?UTF-8?q?=E2=80=94=20plasma=20tip,=20braille=20wake,=20caret=20(0.9.28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace noisy glyph spam with a layered frontier: bright cyan plasma at the tip, a traveling braille wake that ghosts the real text, a lock flash as glyphs settle, and a scanning write-head caret. Co-authored-by: Cursor --- include/cst_version.h | 2 +- src/tui.c | 114 ++++++++++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 27 deletions(-) diff --git a/include/cst_version.h b/include/cst_version.h index 55b281a..a52ac89 100644 --- a/include/cst_version.h +++ b/include/cst_version.h @@ -3,7 +3,7 @@ #define CST_VERSION_MAJOR 0 #define CST_VERSION_MINOR 9 -#define CST_VERSION_PATCH 27 +#define CST_VERSION_PATCH 28 #define CST_VERSION_STR_(x) #x #define CST_VERSION_STR(x) CST_VERSION_STR_(x) diff --git a/src/tui.c b/src/tui.c index 259d04c..da5305d 100644 --- a/src/tui.c +++ b/src/tui.c @@ -967,9 +967,9 @@ static long ms_since(const struct timespec *a) { (long)(now.tv_nsec - a->tv_nsec) / 1000000; } -/* Stream frontier: trailing glyphs briefly scramble then settle to plaintext. - * Returns a malloc'd ANSI copy, or NULL to use the source as-is. */ -#define GLITCH_FRONT 28 +/* Stream frontier as a decode wave: plasma tip → braille wake → lock flash → + * plaintext. Returns a malloc'd ANSI copy, or NULL to use the source as-is. */ +#define GLITCH_FRONT 42 static unsigned glitch_mix(unsigned x) { x ^= x << 13; @@ -978,14 +978,20 @@ static unsigned glitch_mix(unsigned x) { return x; } +static size_t glitch_put(char *out, size_t o, const char *s) { + size_t n = strlen(s); + memcpy(out + o, s, n); + return o + n; +} + static char *glitch_stream_frontier(const char *src, int spin) { if (!src || !src[0]) return NULL; size_t n = strlen(src); - char *out = malloc(n * 3 + 64); /* room if we swap ASCII for UTF-8 glyphs */ + /* SGR wraps + glyph swaps need headroom. */ + char *out = malloc(n + (size_t)GLITCH_FRONT * 24 + 64); if (!out) return NULL; - /* Collect starts of the last GLITCH_FRONT visible codepoints (skip SGR). */ size_t starts[GLITCH_FRONT]; size_t lens[GLITCH_FRONT]; int nv = 0; @@ -1017,19 +1023,36 @@ static char *glitch_stream_frontier(const char *src, int spin) { } i += eaten; } - if (nv < 4) { + if (nv < 3) { free(out); return NULL; } - static const char *const uni_g[] = { - "░", "▒", "▓", "█", "▄", "▀", "▌", "▐", "╳", "╬", "═", "║", - "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "#", "*", "+", "/"}; - static const char *const asc_g[] = { - "#", "$", "%", "&", "*", "+", "=", "?", "/", "\\", "|", ":", - ".", ",", ";", "~", "^", "x", "o", "0", "1", "Z", "X", "/"}; - const char *const *glyphs = g_ascii ? asc_g : uni_g; - const int ng = g_ascii ? (int)(sizeof(asc_g) / sizeof(asc_g[0])) - : (int)(sizeof(uni_g) / sizeof(uni_g[0])); + + /* Braille / phosphor — quieter than katakana soup, still cyber. */ + static const char *const uni_plasma[] = { + "⣿", "⣾", "⣷", "⣯", "⣟", "⡿", "⢿", "⣻", "⣽", "⣾", "⠁", "⠂", + "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", "░", "▒", "▓"}; + static const char *const uni_wake[] = { + "·", "˙", "˚", "˳", "⠁", "⠉", "⠋", "⠛", "⠟", "⠿", "∙", "•"}; + static const char *const uni_caret[] = {"▍", "▋", "▌", "▎", "▏", "▎", "▌", "▋"}; + static const char *const asc_plasma[] = { + "#", "@", "%", "&", "*", "+", "=", "?", "/", "\\", "|", ":", + "0", "1", "X", "Z", "~", "^", ".", ":", ";"}; + static const char *const asc_wake[] = { + ".", ":", "-", "~", "*", "+", "o", "x", "=", ";", "`", "'"}; + static const char *const asc_caret[] = {"#", "=", "#", "-", "#", "=", "#", "-"}; + const char *const *plasma = g_ascii ? asc_plasma : uni_plasma; + const char *const *wake = g_ascii ? asc_wake : uni_wake; + const char *const *caret = g_ascii ? asc_caret : uni_caret; + const int nplasma = g_ascii ? (int)(sizeof(asc_plasma) / sizeof(asc_plasma[0])) + : (int)(sizeof(uni_plasma) / sizeof(uni_plasma[0])); + const int nwake = g_ascii ? (int)(sizeof(asc_wake) / sizeof(asc_wake[0])) + : (int)(sizeof(uni_wake) / sizeof(uni_wake[0])); + const int ncaret = g_ascii ? (int)(sizeof(asc_caret) / sizeof(asc_caret[0])) + : (int)(sizeof(uni_caret) / sizeof(uni_caret[0])); + + /* Traveling decode head — a soft sine along the wake. */ + int wave = spin % (nv + 8); size_t o = 0; size_t cursor = 0; @@ -1039,16 +1062,51 @@ static char *glitch_stream_frontier(const char *src, int spin) { memcpy(out + o, src + cursor, st - cursor); o += st - cursor; } - /* Intensity: tip of stream most scrambled; older glyphs settle. */ - int age = nv - 1 - v; /* 0 = newest */ + int age = nv - 1 - v; /* 0 = newest (tip) */ unsigned h = glitch_mix((unsigned)spin * 131u + (unsigned)v * 17u + - (unsigned)st); - int scramble = age < 10 && (int)(h % 10) < (10 - age); - if (scramble && age < 6) { - const char *g = glyphs[h % (unsigned)ng]; - size_t gl = strlen(g); - memcpy(out + o, g, gl); - o += gl; + (unsigned)st * 3u); + int on_wave = (age >= wave - 1 && age <= wave + 2); + + if (age <= 2) { + /* Plasma tip: bright cyan decode glyphs. */ + const char *g = plasma[(h + (unsigned)spin) % (unsigned)nplasma]; + o = glitch_put(out, o, "\033[1;96m"); + o = glitch_put(out, o, g); + o = glitch_put(out, o, "\033[22;39m"); + } else if (age <= 16) { + /* Wake: mostly ghosting the real glyph; braid with phosphor on the + * wave crest so it reads as a decode sweep, not TV static. */ + int dens = 18 - age; /* 15..2 */ + int hit = on_wave || (int)(h % 18) < dens / 2; + if (hit && (spin + v) % 3 != 0) { + const char *g = wake[h % (unsigned)nwake]; + o = glitch_put(out, o, on_wave ? "\033[96m" : "\033[2;36m"); + o = glitch_put(out, o, g); + o = glitch_put(out, o, "\033[22;39m"); + } else if (age <= 6 && (spin + age) % 4 == 0) { + /* Brief bright reveal of the true char — locking in. */ + o = glitch_put(out, o, "\033[1;97m"); + memcpy(out + o, src + st, lens[v]); + o += lens[v]; + o = glitch_put(out, o, "\033[22;39m"); + } else { + /* Dim true glyph — readable under the wake. */ + o = glitch_put(out, o, "\033[2m"); + memcpy(out + o, src + st, lens[v]); + o += lens[v]; + o = glitch_put(out, o, "\033[22m"); + } + } else if (age <= 20) { + /* Lock flash: settle with a one-beat bright pulse. */ + if ((spin + age) % 5 == 0) { + o = glitch_put(out, o, "\033[1m"); + memcpy(out + o, src + st, lens[v]); + o += lens[v]; + o = glitch_put(out, o, "\033[22m"); + } else { + memcpy(out + o, src + st, lens[v]); + o += lens[v]; + } } else { memcpy(out + o, src + st, lens[v]); o += lens[v]; @@ -1059,6 +1117,10 @@ static char *glitch_stream_frontier(const char *src, int spin) { memcpy(out + o, src + cursor, n - cursor); o += n - cursor; } + /* Scanning caret past the tip — the "write head". */ + o = glitch_put(out, o, "\033[1;96m"); + o = glitch_put(out, o, caret[(unsigned)spin % (unsigned)ncaret]); + o = glitch_put(out, o, "\033[22;39m"); out[o] = 0; return out; } @@ -1108,9 +1170,9 @@ static double drip_cps(size_t backlog_chars) { if (backlog_chars == 0) return 0; if (backlog_chars <= 6) - return 14.0; /* ~one glyph / tick — glitch is visible */ + return 10.0; /* hold the tip so the decode wave is readable */ if (backlog_chars <= 40) - return 18.0 + (double)backlog_chars * 0.9; + return 16.0 + (double)backlog_chars * 0.85; /* ~ 22 + 14*sqrt(backlog) */ double cps = 22.0 + 14.0 * (double)isqrt_sz(backlog_chars); if (backlog_chars > 600)