Problem
When launching AceClaw via ./dev.sh, the text input cursor does not appear immediately after the aceclaw> prompt marker. Typed text appears at a wrong position, making the console unusable.
Analysis
Likely Cause 1: SCP/RCP cursor save/restore conflicts with JLine
In renderStatusFrame() (line 1592-1596):
writer.print("\u001B[s"); // SCP — save cursor position
writer.flush();
promptStatus.update(rendered); // JLine does its own cursor mgmt internally
writer.print("\u001B[u"); // RCP — restore cursor position
writer.flush();
JLine's Status.update() internally moves the cursor to the status area, renders lines, then repositions the cursor back to the prompt. Wrapping it with SCP/RCP (\u001B[s/\u001B[u) can corrupt the cursor position because:
- The
\u001B[s saves a stale position (before JLine moves the cursor)
Status.update() changes cursor position internally
\u001B[u] restores to the stale position, overriding JLine's correct repositioning
Likely Cause 2: Background thread updates during readLine()
renderStatusFrame() is called from the aceclaw-ui-renderer virtual thread (line 560), which runs every 100ms. The main thread is concurrently in reader.readLine(PROMPT_STR) (line 408). Even though renderStatusFrame() uses synchronized (uiRenderLock), the main readLine() doesn't participate in that lock, so JLine's internal cursor state can become inconsistent.
Likely Cause 3: ensureCursorVisible() doesn't fix position
After renderStatusFrame(), ensureCursorVisible() sends \u001B[?25h (DECTCEM — make cursor visible). This only toggles cursor visibility, not its position. If the cursor was misplaced by the status update, ensureCursorVisible() doesn't help.
Suggested Fix
-
Remove SCP/RCP wrapping: JLine's Status.update() manages cursor position internally. The \u001B[s/\u001B[u wrapper is both unnecessary and harmful.
-
Coordinate with JLine's threading model: Either:
- Use JLine's
Display.update() from the reader thread (triggered by a signal)
- Or use
reader.callWidget() to trigger status updates within JLine's event loop
-
After status update, explicitly reposition cursor: If background updates must continue, send \u001B[H + row/col positioning to force the cursor back to the prompt line, rather than relying on JLine's internal state.
How to Reproduce
./dev.sh
- Wait for the status panel to render
- Start typing — cursor is not at the correct position after
aceclaw>
Related
May share root cause with #219 (status panel width issues)
Problem
When launching AceClaw via
./dev.sh, the text input cursor does not appear immediately after theaceclaw>prompt marker. Typed text appears at a wrong position, making the console unusable.Analysis
Likely Cause 1: SCP/RCP cursor save/restore conflicts with JLine
In
renderStatusFrame()(line 1592-1596):JLine's
Status.update()internally moves the cursor to the status area, renders lines, then repositions the cursor back to the prompt. Wrapping it with SCP/RCP (\u001B[s/\u001B[u) can corrupt the cursor position because:\u001B[ssaves a stale position (before JLine moves the cursor)Status.update()changes cursor position internally\u001B[u]restores to the stale position, overriding JLine's correct repositioningLikely Cause 2: Background thread updates during
readLine()renderStatusFrame()is called from theaceclaw-ui-renderervirtual thread (line 560), which runs every 100ms. The main thread is concurrently inreader.readLine(PROMPT_STR)(line 408). Even thoughrenderStatusFrame()usessynchronized (uiRenderLock), the mainreadLine()doesn't participate in that lock, so JLine's internal cursor state can become inconsistent.Likely Cause 3:
ensureCursorVisible()doesn't fix positionAfter
renderStatusFrame(),ensureCursorVisible()sends\u001B[?25h(DECTCEM — make cursor visible). This only toggles cursor visibility, not its position. If the cursor was misplaced by the status update,ensureCursorVisible()doesn't help.Suggested Fix
Remove SCP/RCP wrapping: JLine's
Status.update()manages cursor position internally. The\u001B[s/\u001B[uwrapper is both unnecessary and harmful.Coordinate with JLine's threading model: Either:
Display.update()from the reader thread (triggered by a signal)reader.callWidget()to trigger status updates within JLine's event loopAfter status update, explicitly reposition cursor: If background updates must continue, send
\u001B[H+ row/col positioning to force the cursor back to the prompt line, rather than relying on JLine's internal state.How to Reproduce
./dev.shaceclaw>Related
May share root cause with #219 (status panel width issues)