Manages system clipboard interaction with transient feedback.
Apps need to copy data to the clipboard. Users need feedback: “Did it work?” Manual clipboard handling and feedback timers scattered through app logic is messy.
This object handles clipboard writes to all platforms (pbcopy, xclip, xsel). It manages a feedback message and countdown timer.
Use it to provide copy-to-clipboard functionality with user feedback.
Example
clipboard = Clipboard.new clipboard.copy("#FF0000") puts clipboard.message # => "Copied!" # In render loop: clipboard.tick # Decrement timer puts clipboard.message # => "" (after 60 frames)
Source Code
# frozen_string_literal: true #-- # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com> # SPDX-License-Identifier: AGPL-3.0-or-later #++ # Manages system clipboard interaction with transient feedback. # # Apps need to copy data to the clipboard. Users need feedback: "Did it work?" # Manual clipboard handling and feedback timers scattered through app logic is # messy. # # This object handles clipboard writes to all platforms (pbcopy, xclip, xsel). # It manages a feedback message and countdown timer. # # Use it to provide copy-to-clipboard functionality with user feedback. # # === Example # # clipboard = Clipboard.new # clipboard.copy("#FF0000") # puts clipboard.message # => "Copied!" # # # In render loop: # clipboard.tick # Decrement timer # puts clipboard.message # => "" (after 60 frames) class Clipboard def initialize @message = "" @timer = 0 end # Writes text to the system clipboard. # # Tries pbcopy (macOS), xclip (Linux), then xsel (Linux fallback). Sets the # feedback message to <tt>"Copied!"</tt> and starts a 60-frame timer. # # [text] String to copy # # === Example # # clipboard = Clipboard.new # clipboard.copy("#FF0000") # clipboard.message # => "Copied!" def copy(text) if `which pbcopy 2>/dev/null`.strip.length > 0 IO.popen("pbcopy", "w") { |io| io.write(text) } elsif `which xclip 2>/dev/null`.strip.length > 0 IO.popen("xclip -selection clipboard", "w") { |io| io.write(text) } elsif `which xsel 2>/dev/null`.strip.length > 0 IO.popen("xsel --clipboard --input", "w") { |io| io.write(text) } end @message = "Copied!" @timer = 60 end # Decrements the feedback timer by one frame. # # Call this once per render cycle. The message disappears when the timer # reaches zero. # # === Example # # clipboard.copy("text") # timer = 60 # clipboard.tick # timer = 59 # 60.times { clipboard.tick } # message becomes "" def tick @timer -= 1 if @timer > 0 @message = "" if @timer <= 0 end # Current feedback message. # # Empty string when no active message. <tt>"Copied!"</tt> after a successful # copy, fading after 60 frames. # # === Example # # clipboard.message # => "" # clipboard.copy("x") # clipboard.message # => "Copied!" def message @message end end