fix: park cursor in composer; glitch stream frontier (0.9.26)

leaveok on chrome panes and always redraw input after side/log motion
so the caret no longer sticks on the Seat Deck version line. Live
assistant text briefly scrambles at the tip then settles to plaintext.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-10-29 19:02:05 +08:00
co-authored by Cursor
parent 6e09f79666
commit 7d183d5bba
2 changed files with 123 additions and 4 deletions
+122 -3
View File
@@ -961,6 +961,102 @@ 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
static unsigned glitch_mix(unsigned x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return x;
}
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 */
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;
size_t i = 0;
while (i < n) {
if (src[i] == '\033' && i + 1 < n && src[i + 1] == '[') {
size_t j = i + 2;
while (j < n && src[j] != 'm')
j++;
i = j < n ? j + 1 : n;
continue;
}
wchar_t wc;
size_t eaten;
utf8_decode(src + i, n - i, &wc, &eaten);
if (eaten == 0)
break;
if (wc != L'\n' && wc != L'\r') {
if (nv < GLITCH_FRONT) {
starts[nv] = i;
lens[nv] = eaten;
nv++;
} else {
memmove(starts, starts + 1, (GLITCH_FRONT - 1) * sizeof(starts[0]));
memmove(lens, lens + 1, (GLITCH_FRONT - 1) * sizeof(lens[0]));
starts[GLITCH_FRONT - 1] = i;
lens[GLITCH_FRONT - 1] = eaten;
}
}
i += eaten;
}
if (nv < 4) {
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]));
size_t o = 0;
size_t cursor = 0;
for (int v = 0; v < nv; v++) {
size_t st = starts[v];
if (st > cursor) {
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 */
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;
} else {
memcpy(out + o, src + st, lens[v]);
o += lens[v];
}
cursor = st + lens[v];
}
if (cursor < n) {
memcpy(out + o, src + cursor, n - cursor);
o += n - cursor;
}
out[o] = 0;
return out;
}
static void live_begin(tui_t *t) {
live_end_cleanup(t);
t->md_cap = MD_INIT;
@@ -1835,8 +1931,19 @@ static void build_display(tui_t *t, dlines_t *dl, int width) {
block_to_lines_indented(&t->hist.items[i], width, t->details_all, dl);
}
g_dl_blk = -1;
if (t->as_live)
block_to_lines_indented(&t->live, width, t->details_all, dl);
if (t->as_live) {
block_t live = t->live;
char *glitch = NULL;
if (t->motion_on && t->busy) {
const char *src =
t->live.render ? t->live.render : (t->live.body ? t->live.body : "");
glitch = glitch_stream_frontier(src, t->spin);
if (glitch)
live.render = glitch;
}
block_to_lines_indented(&live, width, t->details_all, dl);
free(glitch);
}
}
/* Draws s at (y, x0) within maxw columns; returns the columns used. */
@@ -3279,6 +3386,15 @@ static void layout(tui_t *t) {
if (t->side)
wbkgdset(t->side, COLOR_PAIR(CP_SIDE));
}
/* Side/status refreshes must not steal the terminal cursor from input. */
if (t->side)
leaveok(t->side, TRUE);
if (t->status)
leaveok(t->status, TRUE);
if (t->header)
leaveok(t->header, TRUE);
if (t->todow)
leaveok(t->todow, TRUE);
scrollok(t->logw, FALSE);
keypad(t->input, TRUE);
wtimeout(t->input, 80);
@@ -7436,9 +7552,11 @@ int cst_tui_run(cst_agent_t *agent) {
if (busy) {
t.spin++;
draw_status(&t);
draw_input(&t);
if (t.motion_on && t.as_live)
draw_log(&t); /* animate stream frontier glitch */
if (t.motion_on && t.side)
draw_side(&t);
draw_input(&t); /* always last — park cursor in the composer */
}
if (turn_done) {
@@ -7472,6 +7590,7 @@ int cst_tui_run(cst_agent_t *agent) {
if (!busy && t.motion_on && t.side && !t.home) {
t.spin++;
draw_side(&t);
draw_input(&t); /* restore cursor after side refresh */
}
continue;
}