From 58de00f75a62c818311d935bda33e549296c0961 Mon Sep 17 00:00:00 2001 From: Byntham <135178921+Byntham@users.noreply.github.com> Date: Thu, 5 Nov 2026 01:42:29 -0800 Subject: [PATCH] Keep words in place while typing near the end of a line The caret was an inline-block, which gives the browser a line-break point between typed and ghost text. A word at the line end split around the caret. Draw the caret as a plain inline span with an absolutely positioned bar. Co-Authored-By: Claude Opus 5.5 --- src/index.css | 11 +++++++---- tests/play.mjs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/index.css b/src/index.css index 42bc61f..29a8250 100644 --- a/src/index.css +++ b/src/index.css @@ -534,11 +534,14 @@ code { } .t3type-caret { - display: inline-block; + position: relative; +} +.t3type-caret::after { + content: ""; + position: absolute; + inset-block: 0; + left: -0.75px; width: 1.5px; - height: 1.15em; - margin-inline: -0.75px; - vertical-align: -0.2em; background: var(--primary); animation: t3type-blink 1.1s steps(1) infinite; } diff --git a/tests/play.mjs b/tests/play.mjs index 7601510..23ccd99 100644 --- a/tests/play.mjs +++ b/tests/play.mjs @@ -225,8 +225,48 @@ async function checkComposerFontSize(browser) { await page.close(); } +async function checkWordsStayPutWhileTyping(browser) { + const { page, errors } = await openPage(browser, "dark", "speed=4&seed=1"); + const lineOfEachChar = () => + page.locator('[data-testid="composer-editor"]').evaluate((editor) => { + const walker = document.createTreeWalker(editor, NodeFilter.SHOW_TEXT); + const tops = []; + const range = document.createRange(); + for (let node = walker.nextNode(); node; node = walker.nextNode()) { + for (let i = 0; i < node.length; i += 1) { + range.setStart(node, i); + range.setEnd(node, i + 1); + tops.push(Math.round(range.getClientRects()[0]?.top ?? -1)); + } + } + return tops; + }); + const moved = []; + let wrapped = 0; + for (let width = 800; width <= 1200; width += 40) { + await page.setViewportSize({ width, height: 900 }); + await page.reload(); + await page.waitForFunction(() => window.__game !== undefined); + await page.evaluate(() => document.fonts.ready); + const text = activeOf(await state(page)).def.prompts[0].text; + const before = await lineOfEachChar(); + if (new Set(before).size > 1) wrapped += 1; + for (let n = 1; n <= text.length; n += 1) { + await page.keyboard.type(text[n - 1]); + const now = await lineOfEachChar(); + const shifted = now.findIndex((top, i) => top !== before[i]); + if (shifted >= 0) moved.push(`${width}px "${text.slice(0, n).slice(-12)}|" moved "${text.slice(shifted, shifted + 8)}"`); + } + } + check(wrapped >= 5, `prompt wraps at ${wrapped} of 11 widths`); + check(moved.length === 0, `no character changes line while typing${moved.length ? ` (${moved.length} keys): ${moved.slice(0, 3).join("; ")}` : ""}`); + check(errors.length === 0, `no console errors in wrap run${errors.length ? `: ${errors.join(" | ")}` : ""}`); + await page.close(); +} + const browser = await chromium.launch(); try { + await checkWordsStayPutWhileTyping(browser); await playFullSessionInDark(browser); await checkAutoSettle(browser); await checkPerPromptWpmInLight(browser);