05-03-26
Peer-to-Peer Clipboard Sync Script
This Lua script enables peer-to-peer clipboard synchronization between two machines using Rebind hardware input. It allows you to easily share text between devices on the same network.
rebindlua scriptclipboardsyncpeer-to-peernetworkhardware input
Question
Can you configure it so that two machines are both running Rebind?
Rebind AI
Here's a peer-to-peer clipboard synchronization script for use with two machines running Rebind:
lua
-- rebind: min_sdk=0.1.0
-- rebind: name=Peer Clipboard Sync
-- rebind: permission=net
local cfg = UI.Schema({
mode = UI.Select("Server", { "Server", "Client" }, {
label = "Mode",
group = "Network",
}),
listen_port = UI.Slider(8765, {
min = 1024,
max = 65535,
label = "Listen Port",
group = "Network",
showIf = "mode:Server",
}),
peer_url = UI.Text("http://localhost:8765", {
label = "Peer URL",
placeholder = "http://host:port",
maxLength = 100,
group = "Network",
showIf = "mode:Client",
}),
sync_hotkey = UI.Keybind("F9", { label = "Sync Hotkey" }),
auto_sync = UI.Toggle(false, { label = "Auto-sync every 2s" }),
timeout = UI.Slider(5000, { min = 1000, max = 30000, suffix = "ms" }),
})
local server = nil
local lastLocalClip = ""
local lastRemoteClip = ""
local function sanitizeUrl(url)
if not url:match("^https?://") then
url = "http://" .. url
end
return url:gsub("/$", "")
end
-- ============ SERVER MODE ============
local function startServer()
server = Net.Listen(cfg.listen_port, function(req)
if req.method == "GET" and req.path == "/clipboard" then
local localText = Clipboard.Get() or ""
lastLocalClip = localText
return {
status = 200,
body = JSON.Stringify({ text = localText }),
headers = { ["Content-Type"] = "application/json" },
}
elseif req.method == "POST" and req.path == "/clipboard" then
local data = JSON.Parse(req.body)
local remoteText = data.text or ""
if remoteText ~= lastRemoteClip then
lastRemoteClip = remoteText
Clipboard.Set(remoteText)
Log.Info("Server received: " .. remoteText:sub(1, 50))
end
return {
status = 200,
body = JSON.Stringify({ status = "ok" }),
headers = { ["Content-Type"] = "application/json" },
}
end
return { status = 404, body = "Not Found" }
end)
Log.Info("Clipboard server listening on port " .. cfg.listen_port)
UI.Notify("Server started on port " .. cfg.listen_port, "success")
end
-- ============ CLIENT MODE ============
local function pullFromPeer()
local url = sanitizeUrl(cfg.peer_url) .. "/clipboard"
local resp = Net.Get(url, nil, { timeout = cfg.timeout })
if resp.status == 200 then
local data = JSON.Parse(resp.body)
local remoteText = data.text or ""
if remoteText ~= lastRemoteClip and remoteText ~= "" then
lastRemoteClip = remoteText
Clipboard.Set(remoteText)
UI.Notify("Pulled from peer", "success")
Log.Info("Pulled: " .. remoteText:sub(1, 50))
return true
end
else
UI.Notify("Pull failed: " .. resp.status, "error")
end
return false
end
local function pushToPeer()
local localText = Clipboard.Get()
if not localText or localText == lastLocalClip then
UI.Notify("Local clipboard unchanged", "info")
return false
end
local url = sanitizeUrl(cfg.peer_url) .. "/clipboard"
local payload = JSON.Stringify({ text = localText })
local resp = Net.Post(url, payload, {
["Content-Type"] = "application/json",
}, { timeout = cfg.timeout })
if resp.status == 200 then
lastLocalClip = localText
UI.Notify("Pushed to peer", "success")
Log.Info("Pushed: " .. localText:sub(1, 50))
return true
else
UI.Notify("Push failed: " .. resp.status, "error")
end
return false
end
-- ============ LIFECYCLE ============
function OnStart()
if cfg.mode == "Server" then
startServer()
else
Log.Info("Client mode: targeting " .. cfg.peer_url)
UI.Notify("Client ready", "info")
end
if cfg.auto_sync then
Timer.Every(2000, function()
if cfg.mode == "Client" then
Run(function()
pullFromPeer()
end)
end
end)
end
end
function OnStop()
if server then
server:Stop()
Log.Info("Server stopped")
end
Timer.CancelAll()
end
-- ============ HOTKEY BINDING ============
if cfg.mode == "Client" then
Bind(cfg.sync_hotkey, Async(function()
UI.Notify("Syncing...", "info")
pullFromPeer()
Sleep(100)
pushToPeer()
end))
else
Bind(cfg.sync_hotkey, Async(function()
local localText = Clipboard.Get() or ""
if localText == lastLocalClip then
UI.Notify("Clipboard unchanged", "info")
else
lastLocalClip = localText
UI.Notify("Local clipboard updated", "success")
end
end))
endSetup:
- Machine A (Server): Load this script, set mode to "Server", keep default port 8765.
- Machine B (Client): Load this script, set mode to "Client", enter Machine A's IP:
http://192.168.x.x:8765
How it works:
- Server: Exposes a
/clipboardendpoint. GET returns its clipboard, POST receives updates from the client. - Client: Pulls from the server's endpoint and pushes its own clipboard.
- Hotkey (F9):
- Server: marks local clipboard as "updated" (available for client to fetch).
- Client: syncs both ways — pulls from server, then pushes local.
- Auto-sync: Client polls server every 2 seconds for new content.
- Bidirectional: Server tracks
lastRemoteClipto avoid overwriting its own clipboard with client echoes.
Network discovery: If you're on a home network, find Machine A's IP with:
- Windows:
ipconfig(look for IPv4 Address). - Mac/Linux:
ifconfigorhostname -I.
Then in Machine B's UI, set Peer URL to http://192.168.1.100:8765 (adjust IP and port as needed).