Skip to content

Using Home/End to move cursor to start or end of line breaks after submitting prompt #56890

Closed as not planned

Description

@theodorejb

Description

When I first launch OpenCode and am entering an initial prompt, the Home/End keys work to move the cursor to the start or end of the prompt. But after submitting the initial prompt, Home and End stop working when the prompt editor is focused, and instead unexpectedly scroll the output history.

Plugins

None

OpenCode version

2.0.32

Steps to reproduce

  1. Launch OpenCode and type a short prompt.
  2. Press Home key and notice that it moves the cursor to the start of the prompt, and End moves the cursor to the end of the prompt.
  3. Press Enter to submit the prompt, then start typing a new prompt within the same context.
  4. Notice that Home/End no longer move the cursor as expected, and instead scroll to the top/bottom of the output history.

This very much feels like a bug, since Home/End work initially before submitting the first prompt, but stop working after submitting it.

Screenshot and/or share link

No response

Operating System

Windows 11

Terminal

Windows Terminal

Activity

  1. opencode-agent commented on Oct 18, 2026

    @opencode-agent
    Contributor

    Thanks for the clear report, @theodorejb. I reproduced this on 2.0.32, and it still happens on the latest dev (501b15a).

    Steps:

    1. On the start screen, type hello world, press Home, type X, press End, type Y. The prompt reads Xhello worldY, as expected.
    2. Submit a prompt and get a reply long enough to scroll.
    3. Repeat step 1 in the session prompt. Home jumps the transcript to the first message and End jumps to the last, but the cursor doesn't move, so the prompt ends up as hello worldXY.

    The recording and screenshots below show this, and the script below reproduces it. It looks like inside a session, the default bindings for jumping to the first and last message include home and end, and they take priority over the prompt.

    Until this is addressed, ctrl+a / ctrl+e move the cursor to the start and end of the line. The older #54519 reported the same thing. It was filed against v1 and closed in the v1 cleanup.

    recording.mp4

    Screenshot

    Screenshot

    Screenshot

    Reproduction script (drive.ts)
    // Repro for #56890: Home/End move the prompt cursor on the start screen,
    // but inside a session they scroll the transcript instead.
    // Expected in both places: typing "hello world", Home, "X", End, "Y" gives "Xhello worldY".
    import { Effect } from "effect"
    import { Llm, OpenCodeDriver } from "opencode-drive"
    
    const probe = (ui: any, label: string) =>
      Effect.gen(function* () {
        yield* ui.type("hello world")
        yield* Effect.sleep(300)
        yield* ui.press("home")
        yield* Effect.sleep(400)
        yield* ui.screenshot(`${label}-after-home`)
        yield* ui.type("X")
        yield* ui.press("end")
        yield* Effect.sleep(400)
        yield* ui.type("Y")
        yield* Effect.sleep(500)
        yield* ui.screenshot(`${label}-after-end`)
        const ok = yield* ui.waitFor("Xhello worldY", { timeout: 2000 }).pipe(
          Effect.as(true),
          Effect.catch(() => Effect.succeed(false)),
        )
        const bad = yield* ui.waitFor("hello worldXY", { timeout: 1000 }).pipe(
          Effect.as(true),
          Effect.catch(() => Effect.succeed(false)),
        )
        yield* Effect.log(`${label}: Xhello worldY=${ok} hello worldXY=${bad}`)
      }) as Effect.Effect<void, unknown, never>
    
    export default OpenCodeDriver.use(
      {
        ...(process.env.OC_DEV ? { opencode: { dev: process.env.OC_DEV } } : {}),
        tui: { recording: true, keypressOverlay: true },
      },
      ({ ui, llm }) =>
        Effect.gen(function* () {
          // 1) Start screen: works
          yield* probe(ui, "start-screen")
          yield* ui.press("u", { ctrl: true })
          yield* ui.press("u", { ctrl: true })
          yield* Effect.sleep(300)
    
          // 2) Submit a prompt, get a reply long enough to scroll
          yield* llm.queue(
            Llm.text("First reply.\n\n" + Array.from({ length: 60 }, (_, i) => `line ${i + 1}`).join("\n")),
          )
          yield* ui.submit("first message")
          yield* ui.waitFor("line 60")
          yield* Effect.sleep(800)
    
          // 3) Same keys inside the session: transcript scrolls, cursor does not move
          yield* probe(ui, "session")
          yield* Effect.sleep(500)
        }),
    )

    Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.

  2. argszero commented on Oct 18, 2026

    @argszero
    Contributor

    I'd like to work on this.

    Tracing the cause to what the triage note already points at: session.first and session.last are bound to bare home / end in packages/tui/src/config/keybind.ts, and they are also listed in the ungated layer in packages/tui/src/routes/session/index.tsx. The layer that is gated on renderer.currentFocusedEditor === null is the one that should carry them; the second layer repeats them without that gate, so while the prompt editor is focused the transcript jump still claims the key before the editor can move the caret.

    So the fix is to keep those two commands in the unfocused layer only, and cover it with a keymap test for both states: with the editor focused home/end must reach the input, and with it unfocused they must still jump to the first and last message.

  3. argszero commented on Oct 19, 2026

    @argszero
    Contributor

    Opened #56910 for this: the transcript jumps keep ctrl+g, alt+home and ctrl+alt+g, and Home / End now reach the focused prompt again.

  4. Tarquinen commented on Nov 6, 2026

    @Tarquinen
    Member

    Thanks for the report! Inside a session, Home and End are intentionally bound to jumping to the first and last message (#40123, #42066), so they don't move the prompt cursor there. To move within the prompt, use Ctrl+A / Ctrl+E for the start and end of the line, Alt+A / Alt+E for the visual line, or Shift+Home / Shift+End to select.

    If you'd rather have Home/End edit the prompt, you can rebind them in tui.json:

    { "keybinds": { "session_first": "ctrl+g,alt+home", "session_last": "ctrl+alt+g", "input_buffer_home": "home", "input_buffer_end": "end" } }

    Closing this as working as intended.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  5. theodorejb commented on Nov 6, 2026

    @theodorejb
    Author

    It's confusing that the behavior when pressing Home and End changes when editing the initial prompt to start a session, vs. subsequent prompts in the session. IMO it should consistently do the same thing when editing any prompt and the focus is there. That's the expected behavior.

    On a webpage, Home/End jump to the top or bottom of the page when no input is focused, but when a text input or textarea is focused they move the cursor to the beginning or end of the line. Couldn't OpenCode work like that?

  6. flamewave000 commented on Nov 6, 2026

    @flamewave000

    Thanks for the report! Inside a session, Home and End are intentionally bound to jumping to the first and last message (#40123, #42066), so they don't move the prompt cursor there. To move within the prompt, use Ctrl+A / Ctrl+E for the start and end of the line, Alt+A / Alt+E for the visual line, or Shift+Home / Shift+End to select.

    If you'd rather have Home/End edit the prompt, you can rebind them in tui.json:

    { "keybinds": { "session_first": "ctrl+g,alt+home", "session_last": "ctrl+alt+g", "input_buffer_home": "home", "input_buffer_end": "end" } }

    Closing this as working as intended.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

    This still doesn't make much sense to me. These keybindings appear to come from the Emacs editing model, but that's a very niche convention.

    For decades, the de facto standard in text editors and IDEs has been:

    • Home / End: jump to the beginning/end of the current line
    • Ctrl+Home / Ctrl+End: jump to the beginning/end of the document
    • Ctrl+A: Select All

    This behavior is consistent across editors such as nano, micro, vim (when not using vim specific binding), VS Code, Visual Studio, IntelliJ IDEA, Notepad++, and Sublime Text. It's what most users reasonably expect when editing text.

    I agree with @theodorejb that Home and End should behave differently when focus is on the chat history rather than the input field. In that context, I'd expect behavior similar to other terminal applications like less: Home/End or Page Up/Page Down should navigate the history.

    My main concern is using Emacs-style bindings as the default for everyone. While those bindings make sense for users who prefer them, they are not what the majority of users will expect. Defaults should generally follow the most common platform conventions and established UI standards, with alternative keymaps available as an opt-in preference.

    As an analogy, I personally prefer tabs over spaces, but I recognize that many projects standardize on spaces. I don't expect the default to match my preference; I simply change the setting. Keyboard bindings should work the same way: use the convention most users are familiar with by default, and allow alternative schemes for those who prefer them.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions