Skip to content

Conversation

@xerial
Copy link
Member

@xerial xerial commented Feb 5, 2026

Summary

  • Add Clipboard object for reading and writing to the system clipboard
  • Provides both async (Future-based) and reactive (Rx-based) APIs
  • Includes event handlers for copy/cut/paste interception
  • Falls back to execCommand for older browsers

Features

import wvlet.uni.dom.all.*

// Write text to clipboard
Clipboard.writeText("Hello, World!").foreach(_ => println("Copied!"))

// Read text from clipboard  
Clipboard.readText().foreach(text => println(s"Pasted: ${text}"))

// One-click copy button
button(
  Clipboard.copyOnClick("Copy this text", onSuccess = () => showToast("Copied!")),
  "Copy"
)

// Dynamic copy (evaluates at click time)
button(
  Clipboard.copyOnClickDynamic(() => inputRef.current.map(_.value).getOrElse("")),
  "Copy Input"
)

// Intercept paste events
input(
  Clipboard.onPaste { text =>
    println(s"User pasted: ${text}")
  }
)

// Customize what gets copied
div(
  Clipboard.copyAs(() => "Custom formatted text"),
  "Select and copy me"
)

// Check browser support
if Clipboard.isSupported then
  // Use modern API
else
  // Fallback behavior

API

Method Description
writeText(text) Write text to clipboard (Future)
readText() Read text from clipboard (Future)
writeTextRx(text) Write text, returns Rx[Boolean]
onCopy(handler) Handle copy events
onCut(handler) Handle cut events
onPaste(handler) Handle paste events
copyAs(getText) Customize copied content
cutAs(getText) Customize cut content
copyOnClick(text) Copy on click
copyOnClickDynamic(getText) Copy dynamic text on click
isSupported Check if Clipboard API is available

Test plan

  • All 257 tests pass (17 new clipboard tests)
  • Compilation successful
  • Code formatted with scalafmtAll

🤖 Generated with Claude Code

Add Clipboard object for reading and writing to the system clipboard:
- writeText/readText for async clipboard operations
- writeTextRx for reactive clipboard writing
- onCopy/onCut/onPaste event handlers
- copyAs/cutAs for customizing clipboard content
- copyOnClick/copyOnClickDynamic for one-click copy buttons
- Fallback to execCommand for older browsers
- isSupported check for feature detection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions github-actions bot added the feature New feature label Feb 5, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @xerial, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances uni-dom by integrating a robust Clipboard API. This new feature empowers developers to programmatically interact with the user's system clipboard, offering capabilities to read and write text, handle copy/cut/paste events, and provide custom content for these operations. The implementation prioritizes modern browser APIs while ensuring backward compatibility through a fallback mechanism for writing operations, thereby broadening the utility and reach of uni-dom in web applications.

Highlights

  • New Clipboard API: Introduced a comprehensive Clipboard API to uni-dom for interacting with the system clipboard, allowing for reading and writing text.
  • Asynchronous and Reactive APIs: The API provides both Future-based (asynchronous) methods like writeText and readText, and Rx-based (reactive) methods such as writeTextRx.
  • Clipboard Event Handlers: Added event handlers (onCopy, onCut, onPaste) to intercept and customize clipboard operations, including copyAs and cutAs for custom content.
  • Click-to-Copy Functionality: Included convenient copyOnClick and copyOnClickDynamic methods to easily attach copy functionality to UI elements.
  • Browser Compatibility: The writeText functionality includes a fallback mechanism using document.execCommand for older browsers that do not support the modern Clipboard API.
  • New Test Suite: A new test file, ClipboardTest.scala, has been added to thoroughly test the functionality of the new Clipboard API.
Changelog
  • uni-dom-test/src/test/scala/wvlet/uni/dom/ClipboardTest.scala
    • Added a new test file to cover the functionality of the new Clipboard API.
    • Includes tests for isSupported, writeText, readText, writeTextRx, and various event handlers like onCopy, onCut, onPaste, copyAs, cutAs, copyOnClick, and copyOnClickDynamic.
  • uni/.js/src/main/scala/wvlet/uni/dom/Clipboard.scala
    • Introduced a new Clipboard object providing methods for clipboard interaction.
    • Implemented isSupported to check for modern Clipboard API availability.
    • Provided writeText and readText methods for asynchronous clipboard operations.
    • Added writeTextRx for reactive clipboard writing.
    • Included onCopy, onCut, onPaste, and onPasteEvent for handling clipboard events.
    • Developed copyAs and cutAs to allow customization of copied/cut content.
    • Implemented copyOnClick and copyOnClickDynamic for easy integration of copy functionality with UI elements.
    • Incorporated a fallbackCopy method using document.execCommand for older browser compatibility when writing to the clipboard.
  • uni/.js/src/main/scala/wvlet/uni/dom/all.scala
    • Modified to export the newly added wvlet.uni.dom.Clipboard object, making it accessible through wvlet.uni.dom.all.*.
Activity
  • The pull request introduces a new feature: Clipboard API support.
  • 17 new tests specifically for the clipboard functionality have been added.
  • All 257 existing and new tests are passing.
  • The project compiles successfully.
  • The code has been formatted using scalafmtAll.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive Clipboard API to uni-dom, providing both asynchronous Future-based and reactive Rx-based methods for interacting with the system clipboard. It includes support for reading and writing text, intercepting clipboard events (copy, cut, paste), and provides a fallback mechanism using execCommand for older browsers. The changes are well-structured and include a new test suite. My review includes a couple of suggestions to improve the implementation of the reactive API and reduce code duplication for better maintainability.

Comment on lines 117 to 125
def writeTextRx(text: String): Rx[Boolean] =
val result = Rx.variable(false)
writeText(text).onComplete {
case Success(_) =>
result := true
case Failure(_) =>
result := false
}
result
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation of writeTextRx can be ambiguous. It returns an RxVar that is initialized to false, and then updated to true on success or false again on failure. This makes it difficult for a subscriber to distinguish between the initial "in-progress" state and a "failure" state.

A more idiomatic and clearer approach would be to return an Rx that only emits a value when the write operation is complete. Using Rx.future would achieve this and make the stream's behavior more predictable.

  def writeTextRx(text: String): Rx[Boolean] =
    Rx.future {
      writeText(text)
        .map(_ => true)
        .recover { case _ => false }
    }

Comment on lines +232 to +272
def copyOnClick(
text: String,
onSuccess: () => Unit = () => (),
onFailure: Throwable => Unit = _ => ()
): DomNode =
val onclick = HtmlTags.handler[dom.MouseEvent]("onclick")
onclick { (_: dom.MouseEvent) =>
writeText(text).onComplete {
case Success(_) =>
onSuccess()
case Failure(e) =>
onFailure(e)
}
}

/**
* Create a click handler that copies dynamic text to clipboard.
*
* @param getText
* Function that returns the text to copy
* @param onSuccess
* Optional callback when copy succeeds
* @param onFailure
* Optional callback when copy fails
* @return
* DomNode modifier
*/
def copyOnClickDynamic(
getText: () => String,
onSuccess: () => Unit = () => (),
onFailure: Throwable => Unit = _ => ()
): DomNode =
val onclick = HtmlTags.handler[dom.MouseEvent]("onclick")
onclick { (_: dom.MouseEvent) =>
writeText(getText()).onComplete {
case Success(_) =>
onSuccess()
case Failure(e) =>
onFailure(e)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The methods copyOnClick and copyOnClickDynamic have very similar implementations. This code duplication can be reduced by extracting the common logic into a private helper method. This will improve maintainability and reduce the chance of bugs if this logic needs to be updated in the future.

  private def createCopyHandler(
      getText: () => String,
      onSuccess: () => Unit,
      onFailure: Throwable => Unit
  ): DomNode = {
    val onclick = HtmlTags.handler[dom.MouseEvent]("onclick")
    onclick { (_: dom.MouseEvent) =>
      writeText(getText()).onComplete {
        case Success(_) => onSuccess()
        case Failure(e) => onFailure(e)
      }
    }
  }

  def copyOnClick(
      text: String,
      onSuccess: () => Unit = () => (),
      onFailure: Throwable => Unit = _ => ()
  ): DomNode =
    createCopyHandler(() => text, onSuccess, onFailure)

  /**
    * Create a click handler that copies dynamic text to clipboard.
    *
    * @param getText
    *   Function that returns the text to copy
    * @param onSuccess
    *   Optional callback when copy succeeds
    * @param onFailure
    *   Optional callback when copy fails
    * @return
    *   DomNode modifier
    */
  def copyOnClickDynamic(
      getText: () => String,
      onSuccess: () => Unit = () => (),
      onFailure: Throwable => Unit = _ => ()
  ): DomNode =
    createCopyHandler(getText, onSuccess, onFailure)

- Change writeTextRx to return Rx[Option[Boolean]] (None initially, Some on complete)
- Fix onCopy/onCut to handle input/textarea selections via selectionStart/selectionEnd

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@xerial xerial merged commit 6d375ea into main Feb 5, 2026
13 checks passed
@xerial xerial deleted the feature/clipboard branch February 5, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant