Skip to content

Fix #58: mapValidated on MultipleChoice prompts and move error display to bottom#59

Closed
kevin-lee wants to merge 1 commit into
neandertech:mainfrom
kevin-lee:fix-validated-multiple-choice
Closed

Fix #58: mapValidated on MultipleChoice prompts and move error display to bottom#59
kevin-lee wants to merge 1 commit into
neandertech:mainfrom
kevin-lee:fix-validated-multiple-choice

Conversation

@kevin-lee

@kevin-lee kevin-lee commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Fix #58: mapValidated on MultipleChoice prompts and move error display to bottom

Bug:

cue4s-multiple-choice-bug.mp4

Why was the error moved to the bottom?

After the first fix, the list rendering was fine, but it had another issue. Please watch this video.

cue4s-multiple-choice-bug-fixed-but-another-bug.mp4

The error message at the bottom (SOLUTION IN THIS PR)

This is a demo of the fix that this PR offers.

cue4s-multiple-choice-bug-fixed-all.mp4

Two bugs existed in PromptFramework.mapValidated when used with MultipleChoice prompts:

  1. After a validation rejection showed an error message, subsequent non-ENTER
    actions (TAB toggle, arrow keys) did not clear the error. The inner
    framework's status was permanently stuck at Running(Left(err)) because
    non-ENTER handlers use identity for status, causing stale error rendering
    and visual artifacts (duplicate/shifted items on terminal).

  2. After a first rejection, selecting then deselecting and pressing ENTER again
    produced no visible feedback because the rendering was already identical to
    the displayed error state.

Fix in PromptFramework.mapValidated (two coordinated changes):

  • renderState: translate the outer wrapper's Running(Left(err)) status
    directly into an inner self.Status.Running(Left(err)) for rendering,
    instead of always reading self.currentStatus() which could be stale.

  • handleEvent: backward-propagate self.Status.Init instead of
    self.Status.Running(Left(err)), guarded by innerWasFinished to only
    apply when the inner was actually Finished (not when it was Running
    with its own validation error, e.g. InteractiveTextInput).

Fix in InteractiveMultipleChoice.renderState:

  • Move the error message rendering from between instructions and options to
    after the options list. When no error is present, a blank line is rendered
    in its place. This keeps the line count constant across error/non-error
    states, preventing terminal scroll-induced cursor offset issues that caused
    duplicate rendering when the prompt was near the terminal bottom.

Also adds a validated-multi interactive example for manual testing.

…ve error display to bottom

Two bugs existed in `PromptFramework.mapValidated` when used with `MultipleChoice` prompts:

1. After a validation rejection showed an error message, subsequent non-ENTER
   actions (TAB toggle, arrow keys) did not clear the error. The inner
   framework's status was permanently stuck at `Running(Left(err))` because
   non-ENTER handlers use `identity` for status, causing stale error rendering
   and visual artifacts (duplicate/shifted items on terminal).

2. After a first rejection, selecting then deselecting and pressing ENTER again
   produced no visible feedback because the rendering was already identical to
   the displayed error state.

Fix in `PromptFramework.mapValidated` (two coordinated changes):

- `renderState`: translate the outer wrapper's `Running(Left(err))` status
  directly into an inner `self.Status.Running(Left(err))` for rendering,
  instead of always reading `self.currentStatus()` which could be stale.

- `handleEvent`: backward-propagate `self.Status.Init` instead of
  `self.Status.Running(Left(err))`, guarded by `innerWasFinished` to only
  apply when the inner was actually `Finished` (not when it was `Running`
  with its own validation error, e.g. `InteractiveTextInput`).

Fix in `InteractiveMultipleChoice.renderState`:

- Move the error message rendering from between instructions and options to
  after the options list. When no error is present, a blank line is rendered
  in its place. This keeps the line count constant across error/non-error
  states, preventing terminal scroll-induced cursor offset issues that caused
  duplicate rendering when the prompt was near the terminal bottom.

Also adds a `validated-multi` interactive example for manual testing.
@keynmol

keynmol commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

@kevin-lee thanks for the PR and issue.

Derived prompts are the bane of my existence and I have already been working on a rewrite of the internals in #53, this PR fixes one symptom, but it's building on my original bad design.

I converted your example to a test and finally got it to behave as expected: https://github.com/neandertech/cue4s/pull/53/changes#diff-732c817c2758f69e8e28a40ff53de8321a77e2bd7fc5a6032f819447bbadc6f4

You can publish that PR locally (it should be source compatible) and see how it behaves in your project.

I'm close to finishing it and will release a new version

@kevin-lee

Copy link
Copy Markdown
Contributor Author

@keynmol Oh, that's great! Thank you, and I will test it.
I also found the same issue in TextInput as well. I guess your PR will fix it too.

@keynmol

keynmol commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Superseded by #60

@keynmol keynmol closed this Apr 24, 2026
@kevin-lee

Copy link
Copy Markdown
Contributor Author

@keynmol Sorry, I didn’t have time to test it due to trip preparations. I’ve just tested mine with cue4s 0.0.12, and it works well. Thank you so much!

@keynmol

keynmol commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Excellent, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MultipleChoice with mapValidated: Error rendering breaks list when terminal scroll happens

2 participants