commit 00df5eedf8e92b78e7db6d28f85cce5988525b62 from: Isaac Meerleo date: Tue Jul 21 14:40:38 2026 UTC Fix welcome title and align help block commit - c227bd7f6c0f7d41565ed3a1fd4a788ff9476e76 commit + 00df5eedf8e92b78e7db6d28f85cce5988525b62 blob - f61986f71509bbd3138c9bcdb0c69c885e25cd03 blob + 63d38c4b7f71a7c9c93672e2e0b57dd4c8497a52 --- handoff/verification.md +++ handoff/verification.md @@ -218,7 +218,8 @@ states the property, not just examples: and matching input-cursor width, narrow-pane fallback, drawer clipping, and resize reflow. The curses pty path exercises the styled input and transcript through wide curses while retaining clean terminal-mode startup and teardown. -- **Empty-transcript welcome:** a pure suite pins the exact copy, centering in +- **Empty-transcript welcome:** a pure suite pins the exact copy, centered + copy rows and shared help-column alignment in full and drawer-narrowed viewports, ASCII punctuation, whole-row omission on short/narrow terminals, nonoverlapping status captions, and one-way dismissal. Pure geometry checks keep tiny picker overlays above the status blob - 4aa6cbfc990bfd2484d1c0005273f56e13d04b82 blob + 6ce6fbd248b4ec24efb550d290158c225e01152a --- regress/turn/run.sh +++ regress/turn/run.sh @@ -1959,7 +1959,7 @@ if [ -x "$obj/ptydrive" ]; then ok "curses: the typed prompt reached the provider" $? grep -a -Fq "OWNER-BLOCK-MARKER" "$dir/cursesout" ok "curses: Owner marker renders over a monochrome pty" $? - grep -a -Fq "I like Fugu" "$dir/welcome-stage.0" && + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.0" && grep -a -Fq "by Isaac Meerleo" "$dir/welcome-stage.0" && grep -a -Fq "git ssh://anonymous@got.fugu.farm" \ "$dir/welcome-stage.0" @@ -1969,9 +1969,9 @@ if [ -x "$obj/ptydrive" ]; then grep -a -Fq "context used / limit" "$dir/welcome-stage.0" ok "curses: welcome captions label the live status fields" $? grep -a -Fq "conversations" "$dir/welcome-stage.1" && - grep -a -Fq "I like Fugu" "$dir/welcome-stage.1" + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.1" ok "curses: drawer overlays the undismissed welcome" $? - grep -a -Fq "I like Fugu" "$dir/welcome-stage.2" && + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.2" && grep -a -Fq "agent state" "$dir/welcome-stage.2" ok "curses: closing the drawer restores the welcome" $? picker_stage='' @@ -1982,7 +1982,7 @@ if [ -x "$obj/ptydrive" ]; then done test -n "$picker_stage" ok "curses: model picker overlays the undismissed welcome" $? - grep -a -Fq "I like Fugu" "$dir/welcome-stage.5" && + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.5" && grep -a -Fq "context used / limit" "$dir/welcome-stage.5" && ! grep -a -Fq "model: stub-model-a" "$dir/welcome-stage.5" ok "curses: selecting from m restores welcome without scrollback" $? @@ -1994,12 +1994,12 @@ if [ -x "$obj/ptydrive" ]; then done test -n "$picker_stage" ok "curses: model picker can reopen before Owner input" $? - grep -a -Fq "I like Fugu" "$dir/welcome-stage.8" && + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.8" && grep -a -Fq "context used / limit" "$dir/welcome-stage.8" ok "curses: cancelling the model picker restores the welcome" $? test -s "$dir/welcome-stage.9" && grep -a -Fq "OWNER-BLOCK-MARKER" "$dir/welcome-stage.9" && - ! grep -a -Fq "I like Fugu" "$dir/welcome-stage.9" && + ! grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/welcome-stage.9" && ! grep -a -Fq "agent state" "$dir/welcome-stage.9" && ! grep -a -Fq "context used / limit" "$dir/welcome-stage.9" ok "curses: forced repaint after first edit omits welcome and captions" $? @@ -2007,10 +2007,10 @@ if [ -x "$obj/ptydrive" ]; then ok "curses: pre-input picker selection drives the request" $? grep -a -Fq "Context 46k/111k 41%" "$dir/cursesout" ok "curses: status shows Lead cache plus session context" $? - grep -q "I like Fugu" "$dir/reqout" 2>/dev/null + grep -q "Fugu - Japanese for pufferfish" "$dir/reqout" 2>/dev/null ok "curses: welcome is absent from the Provider request" \ $([ $? -ne 0 ]; echo $?) - grep -q "I like Fugu" "$sessions"/*.ndjson 2>/dev/null + grep -q "Fugu - Japanese for pufferfish" "$sessions"/*.ndjson 2>/dev/null ok "curses: welcome is absent from the Journal" \ $([ $? -ne 0 ]; echo $?) n=$(grep -c '"outcome":"ok"' "$sessions"/*.ndjson 2>/dev/null || echo 0) @@ -2060,7 +2060,7 @@ EOF "$dir/profileout" ok "curses: $profile subscription warning is not transcript" \ $([ $? -ne 0 ]; echo $?) - grep -a -Fq "I like Fugu" "$dir/profileout" + grep -a -Fq "Fugu - Japanese for pufferfish" "$dir/profileout" ok "curses: $profile subscription profile retains welcome" $? done else blob - faff7f7a2086a2eaa36bae5ff1a68a662739a4db blob + 4eacb92d89e483fcaa0099d7acf235e78fd4b52a --- regress/welcome/welcome_test.c +++ regress/welcome/welcome_test.c @@ -30,6 +30,14 @@ find_row(const struct welcome_view *view, const char * return (NULL); } +static int +text_col(const struct welcome_row *row, const char *text) +{ + const char *at = strstr(row->text, text); + + return (at == NULL ? -1 : row->col + (int)(at - row->text)); +} + int main(void) { @@ -54,7 +62,7 @@ main(void) welcome_build(&view, 0, 100, 30, 0); CHECK(view.nrows == 8); CHECK(strcmp(view.rows[0].text, - "I like Fugu \xe2\x80\x94 Japanese for pufferfish.") == 0); + "Fugu - Japanese for pufferfish") == 0); CHECK(view.rows[0].kind == WELCOME_ROW_TITLE); CHECK(strcmp(view.rows[1].text, "by Isaac Meerleo") == 0); CHECK(view.rows[1].kind == WELCOME_ROW_DIM); @@ -70,15 +78,18 @@ main(void) "press Esc, then v to compose in your editor") == 0); CHECK(strcmp(view.rows[7].text, "type /quit to exit") == 0); + for (i = 5; i <= 7; i++) + CHECK(text_col(&view.rows[i], "to ") == + text_col(&view.rows[4], "to ")); CHECK(view.rows[4].row == view.rows[3].row + 2); for (i = 0; i < view.nrows; i++) CHECK(view.rows[i].col >= 0 && view.rows[i].col + view.rows[i].width <= 100); - /* ASCII mode folds only the em dash, consistently with markdown. */ + /* The title is already ASCII and remains byte-identical in ASCII mode. */ welcome_build(&view, 0, 100, 30, 1); CHECK(strcmp(view.rows[0].text, - "I like Fugu -- Japanese for pufferfish.") == 0); + "Fugu - Japanese for pufferfish") == 0); /* Drawer offset and resize recenter without changing the copy. */ welcome_build(&view, 31, 69, 20, 0); @@ -86,6 +97,9 @@ main(void) for (i = 0; i < view.nrows; i++) CHECK(view.rows[i].col >= 31 && view.rows[i].col + view.rows[i].width <= 100); + for (i = 5; i <= 7; i++) + CHECK(text_col(&view.rows[i], "to ") == + text_col(&view.rows[4], "to ")); row = find_row(&view, "by Isaac Meerleo"); CHECK(row != NULL && row->col == 31 + (69 - row->width) / 2); @@ -93,7 +107,7 @@ main(void) welcome_build(&view, 0, 100, 4, 0); CHECK(view.nrows == 4); CHECK(strcmp(view.rows[0].text, - "I like Fugu \xe2\x80\x94 Japanese for pufferfish.") == 0); + "Fugu - Japanese for pufferfish") == 0); CHECK(strcmp(view.rows[1].text, "by Isaac Meerleo") == 0); CHECK(strcmp(view.rows[2].text, "git ssh://anonymous@got.fugu.farm") == 0); blob - 08bf7571eb0a391928f20d47fc251059e0f3d4f0 blob + bd8d8b8a52b9c15aa981574aa6a339e26038115e --- src/fugu-tty/welcome.c +++ src/fugu-tty/welcome.c @@ -31,8 +31,7 @@ struct welcome_candidate { }; static const struct welcome_candidate candidates[WELCOME_ROWS_MAX] = { - { "I like Fugu \xe2\x80\x94 Japanese for pufferfish.", - "I like Fugu -- Japanese for pufferfish.", WELCOME_ROW_TITLE, 0, 0 }, + { "Fugu - Japanese for pufferfish", NULL, WELCOME_ROW_TITLE, 0, 0 }, { "by Isaac Meerleo", NULL, WELCOME_ROW_DIM, 0, 1 }, { "git ssh://anonymous@got.fugu.farm", NULL, WELCOME_ROW_DIM, 0, 2 }, { "Fugu is open source and freely available!", NULL, @@ -99,7 +98,8 @@ welcome_build(struct welcome_view *view, int xoff, int { const char *text[WELCOME_ROWS_MAX]; int cells[WELCOME_ROWS_MAX], keep[WELCOME_ROWS_MAX]; - int count, gap, groups, i, last_group, row, total, victim; + int count, gap, groups, help_width = 0, i, last_group; + int row, total, victim; memset(view, 0, sizeof(*view)); if (xoff < 0 || width <= 0 || height <= 0) @@ -129,6 +129,12 @@ welcome_build(struct welcome_view *view, int xoff, int } if (count == 0) return; + /* The padded help text is one two-column block. Center its widest + * retained row once so every row's second column remains aligned. */ + for (i = 0; i < WELCOME_ROWS_MAX; i++) + if (keep[i] && candidates[i].group == 1 && + cells[i] > help_width) + help_width = cells[i]; row = (height - total) / 2; last_group = -1; @@ -145,7 +151,8 @@ welcome_build(struct welcome_view *view, int xoff, int out->kind = candidates[i].kind; out->row = row++; out->width = cells[i]; - out->col = xoff + (width - cells[i]) / 2; + out->col = xoff + (width - (candidates[i].group == 1 ? + help_width : cells[i])) / 2; last_group = candidates[i].group; } }