Skip to content

Mouse selection copy drops blank lines (double newlines collapsed to single) #16521

Closed

Description

@michalrus

Description

When selecting text with the mouse that spans multiple paragraphs (i.e., text separated by blank lines / \n\n), the copied text has all blank lines collapsed to single newlines. For example, if an assistant response contains:

First paragraph.

Second paragraph.

Third paragraph.

Mouse-selecting and pasting produces:

First paragraph.
Second paragraph.
Third paragraph.

The <leader>y keybinding (which copies raw part.text) works correctly and preserves blank lines. Only the mouse selection path is affected.

Probable root cause

The bug is probably in opentui's Zig native code, in packages/core/src/zig/text-buffer-iterators.zig, function extractTextBetweenOffsets. The line_end_callback at line 472 only emits a \n when line_had_content is true:

https://github.com/anomalyco/opentui/blob/v0.1.86/packages/core/src/zig/text-buffer-iterators.zig#L471-L475

Working patch

I'm not sure if it's entirely correct, but it works on my system:

--- a/node_modules/.bun/@[email protected]+8e67d58793ed4a15/node_modules/@opentui/core/index.js
+++ b/node_modules/.bun/@[email protected]+8e67d58793ed4a15/node_modules/@opentui/core/index.js
@@ -237,13 +237,32 @@
   }
   getSelectedText() {
     this.guard();
-    const byteSize = this.textBuffer.byteSize;
-    if (byteSize === 0)
-      return "";
-    const selectedBytes = this.lib.textBufferViewGetSelectedTextBytes(this.viewPtr, byteSize);
-    if (!selectedBytes)
-      return "";
-    return this.lib.decoder.decode(selectedBytes);
+    const sel = this.getSelection();
+    if (!sel) return "";
+    const plain = this.getPlainText();
+    if (!plain) return "";
+    let offset = 0;
+    let startIdx = -1;
+    let endIdx = plain.length;
+    for (let i = 0; i < plain.length; i++) {
+      if (offset >= sel.end) { endIdx = i; break; }
+      if (offset >= sel.start && startIdx < 0) startIdx = i;
+      const ch = plain.charCodeAt(i);
+      if (ch === 10) { offset += 1; }
+      else if (ch === 9) { offset += 4; }
+      else if (ch >= 0x1100 && (
+        (ch >= 0x1100 && ch <= 0x115F) ||
+        (ch >= 0x2E80 && ch <= 0xA4CF && ch !== 0x303F) ||
+        (ch >= 0xAC00 && ch <= 0xD7A3) ||
+        (ch >= 0xF900 && ch <= 0xFAFF) ||
+        (ch >= 0xFE10 && ch <= 0xFE6F) ||
+        (ch >= 0xFF01 && ch <= 0xFF60) ||
+        (ch >= 0xFFE0 && ch <= 0xFFE6)
+      )) { offset += 2; }
+      else { offset += 1; }
+    }
+    if (startIdx < 0) return "";
+    return plain.slice(startIdx, endIdx);
   }
   getPlainText() {
     this.guard();

Plugins

none

OpenCode version

1.2.17

Steps to reproduce

  1. Start opencode TUI and send a prompt that will produce a response with multiple paragraphs separated by blank lines (e.g., "Write three short paragraphs separated by blank lines")
  2. Use the mouse to click-and-drag to select text spanning across one or more blank lines
  3. Paste the selection into any editor or terminal
  4. Observe that all blank lines (\n\n) have been collapsed to single newlines (\n)

Screenshot and/or share link

N/A — the bug is in the clipboard content, not visible on screen.

Operating System

NixOS 25.11 (Linux x86_64), but the bug is platform-independent (it's in the Zig/JS layer of opentui).

Terminal

Alacritty

Activity

  1. added
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on Mar 7, 2026
  2. github-actions commented on Mar 7, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  3. removed
    bugSomething isn't working
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on May 3, 2026
  4. benkoppe commented on Jun 2, 2026

    @benkoppe
  5. michalrus commented on Jun 14, 2026

    @michalrus
    Author

    Yup, I confirm that – thanks!

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions