Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 15, 2025

Summary by CodeRabbit

  • New Features
    • System clipboard support added (set/get clipboard via terminal OSC 52 sequences).
    • Paste handling enhanced to support both text and binary clipboard data with Base64 decode/encode.
    • New interactive clipboard demo application demonstrating copy/paste and clipboard requests.
    • Screen/terminal APIs extended to trigger clipboard set/get operations.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds OSC 52 clipboard support: parser Base64 decode and binary paste events, Screen API for set/get clipboard, VtScreen Base64 encode/request implementation, and a new clipboard demo demonstrating set/get and paste handling.

Changes

Cohort / File(s) Summary
Clipboard demo
demos/clipboard/dub.json, demos/clipboard/source/clipboard.d
New DUB manifest and a Demo application that drives a Dcell UI to set/get the clipboard and handle text/binary paste events.
Event model
source/dcell/event.d
PasteEvent updated: content changed to string and new binary: ubyte[] field added; comment clarifies only one holds data.
Parser / OSC 52 handling
source/dcell/parser.d
Detects OSC 52 ("52;c;") sequences, Base64-decodes payloads into binary and enqueues paste events; changed newPasteEvent(dstring)newPasteEvent(string) and added newPasteEvent(ubyte[]) overload.
Screen API
source/dcell/screen.d
Added void setClipboard(const(ubyte[])) @safe and `void getClipboard() `@safe to the Screen interface with documentation.
VT terminal implementation
source/dcell/vt.d
Added Vt.setClipboard field and VtScreen.setClipboard(const(ubyte[])) @safe and `getClipboard() `@safe; Base64-encodes outgoing clipboard data and emits OSC 52 sequences; initializes field in constructors.

Sequence Diagram(s)

sequenceDiagram
    participant App as Demo App
    participant Scr as Screen API
    participant VT as VtScreen
    participant Term as Terminal
    participant Parser as Parser

    rect rgb(240,248,255)
    Note over App,Term: Set clipboard (App → terminal)
    App->>Scr: setClipboard(data)
    Scr->>VT: setClipboard(data)
    VT->>VT: Base64 encode data
    VT->>Term: emit OSC 52 set sequence
    end

    rect rgb(240,248,255)
    Note over App,Term: Get clipboard (request → response)
    App->>Scr: getClipboard()
    Scr->>VT: getClipboard()
    VT->>Term: emit OSC 52 request ("?")
    Term-->>Parser: OSC 52 response (Base64 payload)
    Parser->>Parser: Base64 decode → binary or text
    Parser-->>App: enqueue PasteEvent(binary or string)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Files needing focused review:
    • source/dcell/parser.d — OSC 52 detection, Base64 decoding, error handling, and correct enqueueing of binary vs string events.
    • source/dcell/event.d — ensure consumers correctly interpret PasteEvent.content vs PasteEvent.binary.
    • source/dcell/vt.d — Base64 encoding, OSC 52 formatting, integration with output/flush paths, and constructor initialization.
    • demos/clipboard/source/clipboard.d — UI event handling, paste UTF‑8 detection, and binary paste handling.

Possibly related issues

  • Add clipboard access (OSC 52) #45 — Matches: implements OSC 52 clipboard access (parser handling, VtScreen set/getClipboard, Screen clipboard APIs) described by the issue.

Poem

🐰 I nibble base64 by lantern light,
I hop and paste through terminal night,
OSC fifty-two hums a secret tune,
Clipboard dreams beneath the moon,
✨📋

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: adding system clipboard access via OSC 52, which is reflected across multiple files (event, parser, screen, vt) and the new demo. It is concise, clear, and includes an issue reference.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5138122 and 77b5d73.

📒 Files selected for processing (6)
  • demos/clipboard/dub.json (1 hunks)
  • demos/clipboard/source/clipboard.d (1 hunks)
  • source/dcell/event.d (1 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/vt.d (4 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
source/dcell/vt.d (1)

559-575: Consider adding a size limit for clipboard data.

Large clipboard data could result in extremely long escape sequences. Some terminals have limits on OSC sequence length (commonly 100KB-1MB). Consider documenting this limitation or adding a size guard.

That said, the implementation is correct and functional as-is. The guard on vt.setClipboard.empty properly handles legacy terminals.

demos/clipboard/source/clipboard.d (2)

11-11: Use a more appropriate module name for the clipboard demo

module hello; is misleading for a clipboard demo and may also clash with other simple examples. Consider renaming it to better reflect the file path / purpose, e.g.:

-module hello;
+module clipboard;

(or a more fully‑qualified name consistent with how demos are organized in this repo).


50-59: Avoid mutating stored clipboard content when truncating for display

display() truncates content in place:

if (content.length >= 40)
{
    content = content[0 .. 36] ~ " ...";
}

This permanently shortens the stored clipboard text after the first render. Prefer keeping the full value and only truncating a temporary copy for display:

-        auto msg = "No clipboard data";
-        if (!content.empty)
-        {
-            auto len = content.length;
-            if (content.length >= 40)
-            {
-                content = content[0 .. 36] ~ " ...";
-            }
-            msg = format("Clipboard (length %d): %s", len, content);
-        }
+        auto msg = "No clipboard data";
+        if (!content.empty)
+        {
+            auto len = content.length;
+            auto shown = content;
+            if (shown.length >= 40)
+            {
+                shown = shown[0 .. 36] ~ " ...";
+            }
+            msg = format("Clipboard (length %d): %s", len, shown);
+        }

This preserves the full clipboard text while still rendering a shortened version.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7e44d7 and 6c9ec97.

📒 Files selected for processing (6)
  • demos/clipboard/dub.json (1 hunks)
  • demos/clipboard/source/clipboard.d (1 hunks)
  • source/dcell/event.d (1 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/vt.d (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-15T05:12:35.344Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 56
File: source/dcell/vt.d:2-2
Timestamp: 2025-12-15T05:12:35.344Z
Learning: In D (as used in gdamore/dcell), an enum-type variable like Attr attr can be referred to in case statements using either attr.EnumMember or Attr.EnumMember due to aliasing. For consistency and readability, prefer using the type name (Attr.EnumMember) in case statements instead of the lowercase variable name (attr.EnumMember). This guideline applies to D files across the repository (source/**/*.d or any .d files).

Applied to files:

  • source/dcell/event.d
  • demos/clipboard/source/clipboard.d
  • source/dcell/screen.d
  • source/dcell/parser.d
  • source/dcell/vt.d
📚 Learning: 2025-12-08T19:17:33.678Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:448-494
Timestamp: 2025-12-08T19:17:33.678Z
Learning: In source/dcell/ttyscreen.d, Color.reset is defined as a value above the range of valid colors. In the sendColors() function, after emitting the reset sequence (vt.resetFgBg), Color.reset values naturally skip all subsequent color-setting logic (isRGB checks and palette range checks < 8, < 256) because they are out of range, which is the correct behavior by design.

Applied to files:

  • source/dcell/vt.d
🔇 Additional comments (5)
source/dcell/event.d (1)

61-67: LGTM! Clear struct design for dual-mode paste data.

The mutual exclusivity of content and binary is well-documented. The change from dstring to string for content aligns with the OSC 52 binary pathway where UTF-8 string data is more common.

demos/clipboard/dub.json (1)

1-12: LGTM! Standard demo manifest configuration.

The DUB configuration follows the expected pattern for demo executables within the repository.

source/dcell/screen.d (1)

271-288: LGTM! Well-designed clipboard API with appropriate security notes.

The asynchronous design for getClipboard() returning data via PasteEvent is appropriate for terminal I/O. The documentation appropriately warns about security considerations and terminal restrictions.

source/dcell/parser.d (1)

1209-1227: LGTM! Clean overloaded event constructors.

The two newPasteEvent overloads correctly handle the string and binary pathways, following the established pattern for event creation in this module.

source/dcell/vt.d (1)

123-128: LGTM! Correct OSC 52 sequence format.

The OSC 52 format string "\x1b]52;c;%s\x1b\\" follows the standard clipboard sequence specification correctly, with c indicating the clipboard selection.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
demos/clipboard/source/clipboard.d (1)

67-70: Remove unused import.

core.stdc.stdlib : exit is imported but never used.

     void handleEvent(Event ev)
     {
-        import core.stdc.stdlib : exit;
-
         switch (ev.type)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c9ec97 and 5138122.

📒 Files selected for processing (6)
  • demos/clipboard/dub.json (1 hunks)
  • demos/clipboard/source/clipboard.d (1 hunks)
  • source/dcell/event.d (1 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/vt.d (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • demos/clipboard/dub.json
  • source/dcell/screen.d
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-15T05:12:35.344Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 56
File: source/dcell/vt.d:2-2
Timestamp: 2025-12-15T05:12:35.344Z
Learning: In D (as used in gdamore/dcell), an enum-type variable like Attr attr can be referred to in case statements using either attr.EnumMember or Attr.EnumMember due to aliasing. For consistency and readability, prefer using the type name (Attr.EnumMember) in case statements instead of the lowercase variable name (attr.EnumMember). This guideline applies to D files across the repository (source/**/*.d or any .d files).

Applied to files:

  • source/dcell/parser.d
  • demos/clipboard/source/clipboard.d
  • source/dcell/event.d
  • source/dcell/vt.d
📚 Learning: 2025-12-08T19:17:33.678Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:448-494
Timestamp: 2025-12-08T19:17:33.678Z
Learning: In source/dcell/ttyscreen.d, Color.reset is defined as a value above the range of valid colors. In the sendColors() function, after emitting the reset sequence (vt.resetFgBg), Color.reset values naturally skip all subsequent color-setting logic (isRGB checks and palette range checks < 8, < 256) because they are out of range, which is the correct behavior by design.

Applied to files:

  • source/dcell/vt.d
🔇 Additional comments (7)
source/dcell/parser.d (2)

665-676: LGTM! Exception handling for Base64 decoding is now in place.

The OSC 52 clipboard response handling correctly:

  1. Detects the 52;c; prefix
  2. Strips the prefix before decoding
  3. Wraps Base64.decode in a try-catch to gracefully handle malformed clipboard data

1215-1233: LGTM! Clean overloads for string vs binary paste data.

The two newPasteEvent overloads correctly separate:

  • String content path (from bracketed paste mode)
  • Binary data path (from OSC 52 clipboard response)

This aligns well with the updated PasteEvent struct.

source/dcell/vt.d (3)

123-128: LGTM! OSC 52 clipboard format string is correct.

The OSC 52 sequence format \x1b]52;c;%s\x1b\\ follows the standard:

  • \x1b] - OSC introducer
  • 52 - clipboard operation
  • c - system clipboard selection
  • %s - Base64 payload (or ? for query)
  • \x1b\\ - ST (String Terminator)

559-575: LGTM! Clipboard methods correctly implement OSC 52 set/get.

setClipboard encodes data as Base64 and sends it via OSC 52. getClipboard sends a query (?) and the response will arrive asynchronously as a PasteEvent with binary data.


257-257: LGTM! Correctly disables clipboard for legacy terminals.

demos/clipboard/source/clipboard.d (1)

93-110: LGTM! Binary paste handling correctly uses idup for safe string conversion.

The past review concern about buffer aliasing has been addressed. Using chars.idup creates an owned immutable copy, ensuring content doesn't alias the event buffer.

source/dcell/event.d (1)

61-67: LGTM! Clean struct extension for binary clipboard data.

The PasteEvent update correctly:

  1. Maintains content as string (compatible with all current usages)
  2. Adds binary field for raw clipboard data from OSC 52
  3. Documents the mutual exclusivity in the comment

All downstream usages of paste.content in the codebase are compatible with the string type, including in test assertions, clipboard demo, and mouse demo code.

Comment on lines 50 to 62
auto msg = "No clipboard data";
if (!content.empty)
{
auto len = content.length;
if (content.length >= 40)
{
content = content[0 .. 36] ~ " ...";
}
msg = format("Clipboard (length %d): %s", len, content);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: Content truncation mutates the class field permanently.

The truncation logic modifies content in place. On each display() call, if content.length >= 40, it gets truncated again. This corrupts the stored clipboard data.

Use a local variable for display:

         auto msg = "No clipboard data";
         if (!content.empty)
         {
             auto len = content.length;
-            if (content.length >= 40)
-            {
-                content = content[0 .. 36] ~ " ...";
-            }
-            msg = format("Clipboard (length %d): %s", len, content);
+            auto displayContent = content;
+            if (displayContent.length >= 40)
+            {
+                displayContent = displayContent[0 .. 36] ~ " ...";
+            }
+            msg = format("Clipboard (length %d): %s", len, displayContent);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto msg = "No clipboard data";
if (!content.empty)
{
auto len = content.length;
if (content.length >= 40)
{
content = content[0 .. 36] ~ " ...";
}
msg = format("Clipboard (length %d): %s", len, content);
}
auto msg = "No clipboard data";
if (!content.empty)
{
auto len = content.length;
auto displayContent = content;
if (displayContent.length >= 40)
{
displayContent = displayContent[0 .. 36] ~ " ...";
}
msg = format("Clipboard (length %d): %s", len, displayContent);
}
🤖 Prompt for AI Agents
In demos/clipboard/source/clipboard.d around lines 50 to 59, the display() code
truncates the class field content in place which mutates stored clipboard data
on each call; fix by using a local variable (e.g., auto displayContent =
content) for any truncation/formatting and leave the original content untouched,
then build msg from that local variable; ensure you do not assign back to the
field and preserve its length/value.

@gdamore gdamore merged commit 77b5d73 into main Dec 15, 2025
4 of 5 checks passed
@gdamore gdamore deleted the gdamore/clipboard branch December 15, 2025 06:36
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.

2 participants