feat: add client-based clipboard sync (HTTP-safe fallback without HTTPS)#318
feat: add client-based clipboard sync (HTTP-safe fallback without HTTPS)#318AnshuPriya-1 wants to merge 6 commits intoAOSSIE-Org:mainfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds bidirectional clipboard sync: client can push clipboard text to server or request server clipboard; server handles "clipboard-push" by typing text and "clipboard-pull" by simulating Ctrl+C then returns "clipboard-text" to client; client subscribes and writes received text to its clipboard. Changes
Sequence DiagramsequenceDiagram
participant C as Client (trackpad.tsx)
participant Net as Network / WebSocket
participant S as Server (InputHandler)
participant OS as OS Clipboard
C->>C: User triggers Copy (UI)
C->>C: navigator.clipboard.readText() or selection fallback
C->>Net: "clipboard-push" {text}
Net->>S: deliver "clipboard-push"
S->>OS: type text (fallback typing)
C->>C: User triggers Paste (UI)
C->>Net: "clipboard-pull"
Net->>S: deliver "clipboard-pull"
S->>OS: simulate Ctrl+C
S->>S: short delay for clipboard update
S->>Net: "clipboard-text" {content}
Net->>C: deliver "clipboard-text"
C->>C: write to navigator.clipboard or textarea fallback
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/routes/trackpad.tsx`:
- Line 71: Run the project's code formatter over the updated async function
block for handleCopy in src/routes/trackpad.tsx to fix CI formatting drift;
locate the handleCopy declaration and reformat its entire function body
(including indentation, spacing, and trailing commas) using the project's
configured formatter (e.g., Prettier/ESLint) so the file matches repository
style and CI passes.
- Around line 71-93: The handlers are inverted: currently handleCopy reads the
local clipboard and sends "clipboard-push" while handlePaste sends
"clipboard-pull"; flip their behaviors so they match the feature contract and
ControlBar intent. Update handleCopy to request the remote clipboard (send({
type: "clipboard-pull" })) and update handlePaste to read navigator.clipboard
(with the existing secure-context fallback using window.getSelection()) and then
send({ type: "clipboard-push", text }) to the server; keep existing try/catch
and console.error behavior and reuse the same function names handleCopy and
handlePaste.
- Around line 91-93: handlePaste currently sends the "clipboard-pull" message
but never handles the server's "clipboard-text" response; use the subscribe
function returned from useRemoteConnection to register a listener for
"clipboard-text" (e.g., in an effect) that casts the incoming msg to { text?:
string } and calls navigator.clipboard.writeText(clipboardMsg.text) when text
exists, logging errors on failure, and ensure you return the unsubscribe
function to clean up the subscription; keep handlePaste to only send
"clipboard-pull" while the subscribe handler performs the clipboard write.
In `@src/server/InputHandler.ts`:
- Around line 11-12: The two new union members "clipboard-push" and
"clipboard-pull" in InputHandler.ts are misindented causing CI formatting
failures; locate the union/type declaration (the block containing these string
members) and align the indentation of "clipboard-pull" so it matches the
surrounding union entries (same column/indent as "clipboard-push" and other
members), then run the formatter to ensure the file now passes CI; reference the
string literals "clipboard-push" and "clipboard-pull" to find the exact lines to
fix.
- Around line 226-230: The handler is sending the hardcoded string
"TEMP_CLIPBOARD_DATA" instead of the real clipboard contents; update the
clipboard-pull handling code in InputHandler (the part that calls
this.sendToClient) to retrieve the actual text (e.g., await getClipboardText()
or call the existing method that reads system clipboard like
readClipboard()/clipboardReadFromSystem()/this.getClipboardText()) and pass that
value as the text field to this.sendToClient({ type: "clipboard-text", text:
actualText }); ensure the retrieval is awaited if async and fallback to an empty
string or error logging if reading the clipboard fails.
- Around line 204-210: In the "clipboard-push" branch of InputHandler (the
switch handling messages, e.g., case "clipboard-push"), wrap the awaited
keyboard.type(msg.text) call in a try-catch so exceptions don't propagate out of
handleMessage; on error catch and log or warn (using the existing logger or
console) with context (include msg.text or message id) and then continue without
rethrowing so subsequent input processing is not disrupted.
- Around line 39-42: The constructor signature of InputHandler is wrong for
existing instantiation: change the constructor to accept throttleMs first and a
sendToClient callback second (e.g., constructor(throttleMs = 8, sendToClient?:
(msg: InputMessage) => void)), set this.sendToClient to the provided function or
a safe noop to avoid runtime TypeError, and replace the msg parameter type from
any to the explicit InputMessage interface everywhere (e.g., in the
clipboard-pull handler and other handlers) so types align and websocket.ts can
continue to call new InputHandler(inputThrottleMs) without breaking.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 46fa1d58-0b7e-4b78-945d-c33800214926
📒 Files selected for processing (2)
src/routes/trackpad.tsxsrc/server/InputHandler.ts
src/server/InputHandler.ts
Outdated
| constructor( | ||
| private sendToClient: (msg: any) => void, | ||
| throttleMs = 8 | ||
| ) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify all InputHandler constructor call sites
rg -nP --type=ts 'new\s+InputHandler\s*\(' src/server
# Verify explicit-any usage in constructor callback typing
rg -nP --type=ts '\(\s*msg\s*:\s*any\s*\)\s*=>\s*void' src/server/InputHandler.tsRepository: AOSSIE-Org/Rein
Length of output: 187
🏁 Script executed:
# Find all InputHandler instantiation sites
rg -nP --type=ts 'new\s+InputHandler' src/server
# Find clipboard-pull references to understand potential failures
rg -nP --type=ts 'clipboard-pull' src/server
# Check the full InputHandler class to understand the constructor and usage
head -60 src/server/InputHandler.tsRepository: AOSSIE-Org/Rein
Length of output: 1709
🏁 Script executed:
# Get full context around the InputHandler instantiation in websocket.ts
sed -n '40,70p' src/server/websocket.ts
# Check if the clipboard-pull handler uses sendToClient
sed -n '212,230p' src/server/InputHandler.ts
# Verify the type signature of sendToClient and how it's used
rg -nP --type=ts 'this\.sendToClient' src/server/InputHandler.ts | head -20Repository: AOSSIE-Org/Rein
Length of output: 1717
🏁 Script executed:
# Check if there are any other InputHandler instantiations we might have missed
rg -nP --type=ts 'InputHandler' src/server --type-list | grep -v node_modules
# Verify the exact error that would occur by checking how clipboard-pull calls sendToClient
sed -n '212,240p' src/server/InputHandler.tsRepository: AOSSIE-Org/Rein
Length of output: 1449
Fix constructor signature mismatch—InputHandler instantiation in src/server/websocket.ts will fail at runtime.
The constructor at lines 39-42 now requires a callback function as the first parameter (sendToClient), but src/server/websocket.ts:50 instantiates it with only new InputHandler(inputThrottleMs), passing a number as the first argument. This causes this.sendToClient to be a number instead of a function. When the clipboard-pull handler at line 227 calls this.sendToClient(...), it will throw a runtime error (TypeError: this.sendToClient is not a function).
Additionally, replace the any type for the msg parameter with an explicit type based on the InputMessage interface to comply with coding guidelines.
🧰 Tools
🪛 GitHub Actions: CI
[error] 39-41: no-explicit-any: Unexpected any. Specify a different type.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/InputHandler.ts` around lines 39 - 42, The constructor signature
of InputHandler is wrong for existing instantiation: change the constructor to
accept throttleMs first and a sendToClient callback second (e.g.,
constructor(throttleMs = 8, sendToClient?: (msg: InputMessage) => void)), set
this.sendToClient to the provided function or a safe noop to avoid runtime
TypeError, and replace the msg parameter type from any to the explicit
InputMessage interface everywhere (e.g., in the clipboard-pull handler and other
handlers) so types align and websocket.ts can continue to call new
InputHandler(inputThrottleMs) without breaking.
src/server/InputHandler.ts
Outdated
| // ❗ send back to client (IMPORTANT) | ||
| this.sendToClient({ | ||
| type: "clipboard-text", | ||
| text: "TEMP_CLIPBOARD_DATA", // ⚠️ temporary (we’ll improve later) | ||
| }) |
There was a problem hiding this comment.
clipboard-pull currently returns fake data.
This always sends "TEMP_CLIPBOARD_DATA" instead of actual copied content, so copy-to-client is functionally broken.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/InputHandler.ts` around lines 226 - 230, The handler is sending
the hardcoded string "TEMP_CLIPBOARD_DATA" instead of the real clipboard
contents; update the clipboard-pull handling code in InputHandler (the part that
calls this.sendToClient) to retrieve the actual text (e.g., await
getClipboardText() or call the existing method that reads system clipboard like
readClipboard()/clipboardReadFromSystem()/this.getClipboardText()) and pass that
value as the text field to this.sendToClient({ type: "clipboard-text", text:
actualText }); ensure the retrieval is awaited if async and fallback to an empty
string or error logging if reading the clipboard fails.
There was a problem hiding this comment.
♻️ Duplicate comments (5)
src/routes/trackpad.tsx (2)
71-93:⚠️ Potential issue | 🟠 MajorCopy/Paste handlers are inverted against the intended flow.
handleCopycurrently pushes client text (Lines 71-85), whilehandlePastepulls server text (Line 92). This reverses the contract described for issue#97and control-bar intent.Suggested swap
- const handleCopy = async () => { - try { - let text = "" - - if (navigator.clipboard && window.isSecureContext) { - text = await navigator.clipboard.readText() - } else { - text = window.getSelection()?.toString() || "" - } - - send({ - type: "clipboard-push", - text, - }) - } catch (err) { - console.error("Copy failed", err) - } - } + const handleCopy = async () => { + send({ type: "clipboard-pull" }) + } const handlePaste = async () => { - send({ type: "clipboard-pull" }) + try { + let text = "" + if (navigator.clipboard && window.isSecureContext) { + text = await navigator.clipboard.readText() + } else { + text = window.getSelection()?.toString() || "" + } + send({ type: "clipboard-push", text }) + } catch (err) { + console.error("Paste failed", err) + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 71 - 93, The handlers are reversed: handleCopy should request server clipboard data and handlePaste should push the client clipboard content to the server. Update handleCopy to call send({ type: "clipboard-pull" }) (remove the client clipboard reading logic there) and update handlePaste to read from navigator.clipboard (with window.isSecureContext fallback to window.getSelection()) and then send({ type: "clipboard-push", text }) — keep references to the existing send function, navigator.clipboard and window.getSelection when moving the logic.
91-93:⚠️ Potential issue | 🔴 Critical
clipboard-pullis sent, butclipboard-textis not handled here.This file requests server clipboard data but does not subscribe to/process the response message, so pulled content is dropped on arrival.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 91 - 93, The handlePaste function sends the "clipboard-pull" request but never consumes the server's reply; add handling for the incoming "clipboard-text" message and apply the returned text (e.g., update component state or write to the clipboard). Concretely, register a message handler (or extend the existing WS/message dispatcher) to listen for type "clipboard-text" and then call the proper updater (the same state/clipboard logic used elsewhere in this component) so pulled content isn’t dropped; ensure the handler is registered/unregistered appropriately (or integrate into the existing useEffect that manages incoming messages) and reference handlePaste and the "clipboard-text" message type when making the change.src/server/InputHandler.ts (3)
209-215:⚠️ Potential issue | 🟠 Major
clipboard-pushcan fail hard and interrupt input handling.Line 212 is unguarded. If
keyboard.typerejects,handleMessagerejects and subsequent input events can be impacted.Suggested guard
case "clipboard-push": { if (msg.text) { - // TEMP: fallback using typing instead of real clipboard - await keyboard.type(msg.text) + try { + // TEMP: fallback using typing instead of real clipboard + await keyboard.type(msg.text) + } catch (err) { + console.warn("Clipboard push failed:", err) + } } break }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 209 - 215, The unguarded await in the "clipboard-push" case (inside handleMessage in InputHandler.ts) can cause handleMessage to reject if keyboard.type throws; wrap the await keyboard.type(msg.text) in a try/catch, catch any error, log it (use the module/logger available in this file or console.error) and do not rethrow so input handling continues; ensure the catch only runs when msg.text is present and preserve existing control flow (break) after handling the error.
217-237:⚠️ Potential issue | 🔴 Critical
clipboard-pullresponse is still placeholder data.Line 234 always sends
"TEMP_CLIPBOARD_DATA", so server-to-client copy is functionally broken.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 217 - 237, The handler for the "clipboard-pull" case is sending the placeholder string "TEMP_CLIPBOARD_DATA" instead of the actual clipboard contents; replace that placeholder by reading the system clipboard after the simulated Ctrl+C and sending the real text. Use your environment's clipboard API (e.g., clipboard.readText() or clipboardy.read() depending on runtime) to get the clipboard string, await it, and pass that value into this.sendToClient({ type: "clipboard-text", text: /* actual clipboard text */ }); also handle errors (fallback to an empty string or send an error message) so sendToClient is always called even if clipboard read fails; keep existing keyboard.pressKey / releaseKey flow and the small delay intact.
44-47:⚠️ Potential issue | 🔴 CriticalConstructor signature now breaks the existing server instantiation path.
sendToClientis mandatory at Line 45, butsrc/server/websocket.tsLine 50 still constructs with onlyinputThrottleMs. That makesthis.sendToClientinvalid andclipboard-pullcan crash when it calls Line 232.Suggested compatibility fix in this file
- constructor( - private sendToClient: (msg: ServerToClientMessage) => void, - throttleMs = 8, - ) { + constructor( + throttleMs = 8, + private sendToClient: (msg: ServerToClientMessage) => void = () => {}, + ) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 44 - 47, The constructor on InputHandler now requires sendToClient causing existing callers that pass only throttleMs to leave this.sendToClient undefined; make sendToClient optional and default it to a no-op to preserve backwards compatibility: update the InputHandler constructor signature (constructor(sendToClient?: (msg: ServerToClientMessage) => void, throttleMs = 8)) and set this.sendToClient = sendToClient ?? (() => {}), so callers from websocket.ts that only pass inputThrottleMs continue to work and clipboard-pull calls to sendToClient remain safe; ensure any internal invocations use the stored this.sendToClient (no other callsites need changes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/routes/trackpad.tsx`:
- Around line 71-93: The handlers are reversed: handleCopy should request server
clipboard data and handlePaste should push the client clipboard content to the
server. Update handleCopy to call send({ type: "clipboard-pull" }) (remove the
client clipboard reading logic there) and update handlePaste to read from
navigator.clipboard (with window.isSecureContext fallback to
window.getSelection()) and then send({ type: "clipboard-push", text }) — keep
references to the existing send function, navigator.clipboard and
window.getSelection when moving the logic.
- Around line 91-93: The handlePaste function sends the "clipboard-pull" request
but never consumes the server's reply; add handling for the incoming
"clipboard-text" message and apply the returned text (e.g., update component
state or write to the clipboard). Concretely, register a message handler (or
extend the existing WS/message dispatcher) to listen for type "clipboard-text"
and then call the proper updater (the same state/clipboard logic used elsewhere
in this component) so pulled content isn’t dropped; ensure the handler is
registered/unregistered appropriately (or integrate into the existing useEffect
that manages incoming messages) and reference handlePaste and the
"clipboard-text" message type when making the change.
In `@src/server/InputHandler.ts`:
- Around line 209-215: The unguarded await in the "clipboard-push" case (inside
handleMessage in InputHandler.ts) can cause handleMessage to reject if
keyboard.type throws; wrap the await keyboard.type(msg.text) in a try/catch,
catch any error, log it (use the module/logger available in this file or
console.error) and do not rethrow so input handling continues; ensure the catch
only runs when msg.text is present and preserve existing control flow (break)
after handling the error.
- Around line 217-237: The handler for the "clipboard-pull" case is sending the
placeholder string "TEMP_CLIPBOARD_DATA" instead of the actual clipboard
contents; replace that placeholder by reading the system clipboard after the
simulated Ctrl+C and sending the real text. Use your environment's clipboard API
(e.g., clipboard.readText() or clipboardy.read() depending on runtime) to get
the clipboard string, await it, and pass that value into this.sendToClient({
type: "clipboard-text", text: /* actual clipboard text */ }); also handle errors
(fallback to an empty string or send an error message) so sendToClient is always
called even if clipboard read fails; keep existing keyboard.pressKey /
releaseKey flow and the small delay intact.
- Around line 44-47: The constructor on InputHandler now requires sendToClient
causing existing callers that pass only throttleMs to leave this.sendToClient
undefined; make sendToClient optional and default it to a no-op to preserve
backwards compatibility: update the InputHandler constructor signature
(constructor(sendToClient?: (msg: ServerToClientMessage) => void, throttleMs =
8)) and set this.sendToClient = sendToClient ?? (() => {}), so callers from
websocket.ts that only pass inputThrottleMs continue to work and clipboard-pull
calls to sendToClient remain safe; ensure any internal invocations use the
stored this.sendToClient (no other callsites need changes).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e7486b87-38f8-4ae8-80f7-9ca22d8e2892
📒 Files selected for processing (2)
src/routes/trackpad.tsxsrc/server/InputHandler.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/server/websocket.ts (1)
43-47:⚠️ Potential issue | 🟠 Major
inputThrottleMsupdates won’t apply to new connections.
inputThrottleMsis read once at startup, so afterupdate-config, newly connected clients still get the old throttle value. Only the current connection is updated.Proposed fix
- const inputThrottleMs = + let inputThrottleMs = typeof serverConfig.inputThrottleMs === "number" && serverConfig.inputThrottleMs > 0 ? serverConfig.inputThrottleMs : 8 ... if (typeof filtered.inputThrottleMs === "number") { - inputHandler.setThrottleMs(filtered.inputThrottleMs) + inputThrottleMs = filtered.inputThrottleMs + inputHandler.setThrottleMs(inputThrottleMs) }Also applies to: 120-127, 326-328
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/websocket.ts` around lines 43 - 47, The code caches inputThrottleMs at module startup so new connections keep the old value; change consumers to read a computed value per-connection by replacing the module-level inputThrottleMs with a getter (e.g., getInputThrottleMs()) that evaluates the same logic against serverConfig (typeof serverConfig.inputThrottleMs === "number" && serverConfig.inputThrottleMs > 0 ? serverConfig.inputThrottleMs : 8) at the time a socket is created or when initializing per-connection throttling; update all places that reference inputThrottleMs (including the other occurrences noted) to call this getter or inline the same evaluation so updated serverConfig values apply to newly connected clients.
♻️ Duplicate comments (4)
src/routes/trackpad.tsx (1)
63-87:⚠️ Potential issue | 🟡 MinorRun formatter on the updated clipboard blocks.
CI still reports formatting drift in this region; please run the project formatter before merge.
Also applies to: 103-125
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 63 - 87, The clipboard handler block inside the useEffect (the subscribe("clipboard-text", async (msg) => { ... }) callback that uses ClipboardMessage, navigator.clipboard, textarea creation, and document.execCommand) has formatting drift; run the project's formatter (e.g., prettier/format script) across the file to normalize spacing/indentation and fix trailing/consistent braces and indentation in that callback and the similar block at the second occurrence (lines around the other clipboard block). Ensure the file is saved with the formatter's changes before committing.src/server/websocket.ts (1)
120-127:⚠️ Potential issue | 🟡 MinorRun formatter on this updated block.
The CI formatter is still failing for this region.
Also applies to: 355-356
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/websocket.ts` around lines 120 - 127, This block is misformatted and causing CI formatter failures; re-run or apply the project's code formatter to the InputHandler instantiation so spacing/indentation and trailing commas match project style (ensure the arrow callback, the if-check using WebSocket.OPEN, the ws.send(JSON.stringify(msg)) call, and the inputThrottleMs argument are indented and punctuated consistently), and apply the same fix to the similar region around the InputHandler usage at lines 355-356 (look for the same InputHandler constructor call to reformat).src/server/InputHandler.ts (2)
210-216:⚠️ Potential issue | 🟡 MinorRun formatter on this new clipboard-push block.
This region still has a CI formatting mismatch.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 210 - 216, Run the project formatter on the new clipboard-push block in InputHandler.ts to resolve the CI formatting error: reformat the if (msg.text) { ... } block so indentation, spacing and brace placement match the repository style (e.g., align the try { await keyboard.type(msg.text); } catch (err) { console.error("Clipboard push failed:", err); } ), ensure consistent semicolons and spacing around catch and await, and commit the formatted result referencing the keyboard.type call and the msg.text check.
221-239:⚠️ Potential issue | 🔴 Critical
clipboard-pullstill returns placeholder text, so server copy is functionally broken.The handler simulates Ctrl+C but sends a constant string instead of actual clipboard contents.
Proposed fix
- this.sendToClient({ - type: "clipboard-text", - text: "CLIPBOARD_DATA_UNAVAILABLE", - }) + const text = await this.getClipboardText().catch((err) => { + console.error("Clipboard pull failed:", err) + return "" + }) + this.sendToClient({ type: "clipboard-text", text })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 221 - 239, The "clipboard-pull" case currently simulates Ctrl+C with keyboard.pressKey/keyboard.releaseKey but then sends the constant "CLIPBOARD_DATA_UNAVAILABLE" text; replace that placeholder by actually reading the system clipboard after the simulated copy and sending that value via this.sendToClient. Concretely: after the small delay following the key presses, call your clipboard read utility (e.g., navigator.clipboard.readText() or the project's clipboard helper) to obtain the copied text and pass that string into this.sendToClient({ type: "clipboard-text", text: copiedText }), keeping the existing keyboard.pressKey/keyboard.releaseKey and timeout logic intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/server/InputHandler.ts`:
- Around line 209-218: The current "clipboard-push" case uses
keyboard.type(msg.text) which types characters one-by-one; replace it with a
real one-shot paste: write msg.text to the system/renderer clipboard (e.g.,
navigator.clipboard.writeText or a Node clipboard helper like clipboardy) and
then perform a single paste action (one keyboard.press of the platform modifier
+ "V" or use the renderer's paste API) instead of keyboard.type; update the
"clipboard-push" handler to call clipboard write with msg.text and then trigger
a single paste keystroke (taking into account Ctrl vs Meta for macOS) so paste
is atomic and IME/shortcut-safe.
---
Outside diff comments:
In `@src/server/websocket.ts`:
- Around line 43-47: The code caches inputThrottleMs at module startup so new
connections keep the old value; change consumers to read a computed value
per-connection by replacing the module-level inputThrottleMs with a getter
(e.g., getInputThrottleMs()) that evaluates the same logic against serverConfig
(typeof serverConfig.inputThrottleMs === "number" &&
serverConfig.inputThrottleMs > 0 ? serverConfig.inputThrottleMs : 8) at the time
a socket is created or when initializing per-connection throttling; update all
places that reference inputThrottleMs (including the other occurrences noted) to
call this getter or inline the same evaluation so updated serverConfig values
apply to newly connected clients.
---
Duplicate comments:
In `@src/routes/trackpad.tsx`:
- Around line 63-87: The clipboard handler block inside the useEffect (the
subscribe("clipboard-text", async (msg) => { ... }) callback that uses
ClipboardMessage, navigator.clipboard, textarea creation, and
document.execCommand) has formatting drift; run the project's formatter (e.g.,
prettier/format script) across the file to normalize spacing/indentation and fix
trailing/consistent braces and indentation in that callback and the similar
block at the second occurrence (lines around the other clipboard block). Ensure
the file is saved with the formatter's changes before committing.
In `@src/server/InputHandler.ts`:
- Around line 210-216: Run the project formatter on the new clipboard-push block
in InputHandler.ts to resolve the CI formatting error: reformat the if
(msg.text) { ... } block so indentation, spacing and brace placement match the
repository style (e.g., align the try { await keyboard.type(msg.text); } catch
(err) { console.error("Clipboard push failed:", err); } ), ensure consistent
semicolons and spacing around catch and await, and commit the formatted result
referencing the keyboard.type call and the msg.text check.
- Around line 221-239: The "clipboard-pull" case currently simulates Ctrl+C with
keyboard.pressKey/keyboard.releaseKey but then sends the constant
"CLIPBOARD_DATA_UNAVAILABLE" text; replace that placeholder by actually reading
the system clipboard after the simulated copy and sending that value via
this.sendToClient. Concretely: after the small delay following the key presses,
call your clipboard read utility (e.g., navigator.clipboard.readText() or the
project's clipboard helper) to obtain the copied text and pass that string into
this.sendToClient({ type: "clipboard-text", text: copiedText }), keeping the
existing keyboard.pressKey/keyboard.releaseKey and timeout logic intact.
In `@src/server/websocket.ts`:
- Around line 120-127: This block is misformatted and causing CI formatter
failures; re-run or apply the project's code formatter to the InputHandler
instantiation so spacing/indentation and trailing commas match project style
(ensure the arrow callback, the if-check using WebSocket.OPEN, the
ws.send(JSON.stringify(msg)) call, and the inputThrottleMs argument are indented
and punctuated consistently), and apply the same fix to the similar region
around the InputHandler usage at lines 355-356 (look for the same InputHandler
constructor call to reformat).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 451ce14d-7a6f-44d5-9d66-b69183c38639
📒 Files selected for processing (3)
src/routes/trackpad.tsxsrc/server/InputHandler.tssrc/server/websocket.ts
| case "clipboard-push": { | ||
| if (msg.text) { | ||
| // TEMP: fallback using typing instead of real clipboard | ||
| try{ | ||
| await keyboard.type(msg.text) | ||
| }catch(err) { | ||
| console.error("Clipboard push failed:", err) | ||
| } | ||
| } | ||
| break |
There was a problem hiding this comment.
clipboard-push is not a one-operation paste path.
keyboard.type(msg.text) replays keystrokes character-by-character, which does not satisfy the one-shot paste requirement and can alter behavior (shortcuts/IME/focus-sensitive fields).
🧰 Tools
🪛 GitHub Actions: CI
[error] 210-216: Biome formatting detected mismatch: File content differs from formatting output. Run the formatter and re-run the CI checks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/InputHandler.ts` around lines 209 - 218, The current
"clipboard-push" case uses keyboard.type(msg.text) which types characters
one-by-one; replace it with a real one-shot paste: write msg.text to the
system/renderer clipboard (e.g., navigator.clipboard.writeText or a Node
clipboard helper like clipboardy) and then perform a single paste action (one
keyboard.press of the platform modifier + "V" or use the renderer's paste API)
instead of keyboard.type; update the "clipboard-push" handler to call clipboard
write with msg.text and then trigger a single paste keystroke (taking into
account Ctrl vs Meta for macOS) so paste is atomic and IME/shortcut-safe.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/server/InputHandler.ts (2)
209-219:⚠️ Potential issue | 🟠 Major
clipboard-pushis still character typing, not one-shot paste.Line 213 uses
keyboard.type(msg.text), which is not an atomic paste operation and can change behavior in shortcut/IME-sensitive contexts.#!/bin/bash # Verify clipboard-push implementation path and check for one-shot paste primitives. rg -nP --type=ts 'case "clipboard-push"|keyboard\.type\(|clipboardy|writeText|pressKey\(.*Key\.V' src/server/InputHandler.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/InputHandler.ts` around lines 209 - 219, The "clipboard-push" case is using keyboard.type(msg.text) which types characters instead of doing an atomic paste; change the implementation in the InputHandler's "clipboard-push" case to write msg.text to the system clipboard (e.g., using a clipboard utility like clipboardy or the platform clipboard API) and then trigger a single paste gesture (e.g., send the platform-appropriate modifier+V paste keypress) instead of keyboard.type; preserve the existing try/catch, ensure you reference the same msg.text and replace calls to keyboard.type with clipboard write + one-shot paste logic, and handle platform modifier differences (Ctrl on Windows/Linux, Meta/Cmd on macOS).
221-241:⚠️ Potential issue | 🔴 Critical
clipboard-pullreturns placeholder text instead of real clipboard content.On Line 238, sending
"CLIPBOARD_DATA_UNAVAILABLE"makes copy-to-client functionally incorrect. Return actual system clipboard text (or a safe empty fallback when retrieval fails).Suggested direction
- this.sendToClient({ - type: "clipboard-text", - text: "CLIPBOARD_DATA_UNAVAILABLE", - }) + const text = await this.readSystemClipboardText().catch(() => "") + this.sendToClient({ + type: "clipboard-text", + text, + })
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/routes/trackpad.tsx`:
- Around line 107-123: The current paste handler swallows clipboard read errors
and does nothing; update the catch block in src/routes/trackpad.tsx so that when
navigator.clipboard.readText() or selection access throws, you fallback by
sending a server-paste request (send({ type: "paste" })) instead of only
logging; specifically modify the catch around the clipboard read where send
currently sends { type: "clipboard-push", text } to call send({ type: "paste" })
on error (keep or include the console.error call if desired) so the paste still
works via the server clipboard.
---
Duplicate comments:
In `@src/server/InputHandler.ts`:
- Around line 209-219: The "clipboard-push" case is using
keyboard.type(msg.text) which types characters instead of doing an atomic paste;
change the implementation in the InputHandler's "clipboard-push" case to write
msg.text to the system clipboard (e.g., using a clipboard utility like
clipboardy or the platform clipboard API) and then trigger a single paste
gesture (e.g., send the platform-appropriate modifier+V paste keypress) instead
of keyboard.type; preserve the existing try/catch, ensure you reference the same
msg.text and replace calls to keyboard.type with clipboard write + one-shot
paste logic, and handle platform modifier differences (Ctrl on Windows/Linux,
Meta/Cmd on macOS).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d766f1ff-76b3-4bf2-acf8-a7a56681b3d0
📒 Files selected for processing (3)
src/routes/trackpad.tsxsrc/server/InputHandler.tssrc/server/websocket.ts
|
Good point! I've added a fallback so that if client-side clipboard read fails, the app falls back to sending a standard "paste" event to the server. This ensures paste functionality still works even when clipboard access is unavailable. |
|
Good point! Currently, I agree this is not a true one-shot paste and may behave differently in some contexts (e.g., IME or shortcuts). In a future improvement, this can be replaced with a proper system clipboard integration for more accurate paste behavior. |
|
All review comments have been addressed:
All CI checks are passing. Let me know if any further changes are needed! |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/routes/trackpad.tsx (1)
117-126:⚠️ Potential issue | 🟠 MajorEmpty-text path skips fallback and can make Paste a no-op in HTTP mode.
handlePastefalls back to{ type: "paste" }only on exceptions. When clipboard-read fallback returns an empty string without throwing, the code sends{ type: "clipboard-push", text: "" }and does nothing server-side.Proposed patch
try { let text = "" if (navigator.clipboard && window.isSecureContext) { text = await navigator.clipboard.readText() } else { text = window.getSelection()?.toString() || "" } - send({ - type: "clipboard-push", - text, - }) + if (text) { + send({ + type: "clipboard-push", + text, + }) + } else { + send({ type: "paste" }) + } } catch (err) { console.error("Paste failed", err) // fallback to server-side paste send({ type: "paste" }) }#!/bin/bash set -euo pipefail # Inspect current paste handler block sed -n '106,128p' src/routes/trackpad.tsx # Verify whether a non-empty guard exists before clipboard-push rg -nP 'if\s*\(\s*text\s*\)' src/routes/trackpad.tsx🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 117 - 126, handlePaste currently sends a clipboard-push with whatever text was read and only falls back to send({ type: "paste" }) on exceptions, so an empty string becomes a no-op; update handlePaste to check the clipboard text after await (the variable used) and only call send({ type: "clipboard-push", text }) when text is non-empty, otherwise call send({ type: "paste" }) to trigger server-side paste; reference the handlePaste function and the send calls that use types "clipboard-push" and "paste" when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/routes/trackpad.tsx`:
- Around line 117-126: handlePaste currently sends a clipboard-push with
whatever text was read and only falls back to send({ type: "paste" }) on
exceptions, so an empty string becomes a no-op; update handlePaste to check the
clipboard text after await (the variable used) and only call send({ type:
"clipboard-push", text }) when text is non-empty, otherwise call send({ type:
"paste" }) to trigger server-side paste; reference the handlePaste function and
the send calls that use types "clipboard-push" and "paste" when making this
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1ae8954f-d14c-4bc5-ad48-88b30bf4b19c
📒 Files selected for processing (3)
src/components/Trackpad/TouchArea.tsxsrc/hooks/useTrackpadGesture.tssrc/routes/trackpad.tsx
Addressed Issues:
Fixes #97
Description
This PR implements a basic clipboard synchronization mechanism based on the client's clipboard without requiring HTTPS.
Since navigator.clipboard requires a secure context (HTTPS), this solution introduces an HTTP-safe fallback approach to enable clipboard functionality in the current setup.
Key Changes:
Added new message types:
clipboard-push → sends client clipboard data to server
clipboard-pull → requests clipboard data from server
Client-side:
Uses navigator.clipboard.readText() when available
Falls back to window.getSelection() in non-secure environments
Handles server response and writes to client clipboard
Server-side:
Added support for clipboard message handling in InputHandler
Uses keyboard simulation (Ctrl+C) to trigger copy
Sends clipboard response back to client
Temporary fallback: uses keyboard.type() instead of direct clipboard write
Screenshots/Recordings:
Functional Verification
Screen Mirror
Authentication
Basic Gestures
One-finger tap: Verified as Left Click.
Two-finger tap: Verified as Right Click.
Click and drag: Verified selection behavior.
Pinch to zoom: Verified zoom functionality (if applicable).
Modes & Settings
Cursor mode: Cursor moves smoothly and accurately.
Scroll mode: Page scrolls as expected.
Sensitivity: Verified changes in cursor speed/sensitivity settings.
Copy and Paste: Verified both Copy and Paste functionality.
Invert Scrolling: Verified scroll direction toggles correctly.
Advanced Input
Key combinations: Verified "hold" behavior for modifiers (e.g., Ctrl+C) and held keys are shown in buffer.
Keyboard input: Verified Space, Backspace, and Enter keys work correctly.
Glide typing: Verified path drawing and text output.
Voice input: Verified speech-to-text functionality for full sentences.
Backspace doesn't send the previous input.
Any other gesture or input behavior introduced:
Additional Notes:
Checklist
My PR addresses a single issue, fixes a single bug or makes a single improvement.
My code follows the project's code style and conventions
I have performed a self-review of my own code
I have commented my code, particularly in hard-to-understand areas
If applicable, I have made corresponding changes or additions to the documentation
If applicable, I have made corresponding changes or additions to tests
My changes generate no new warnings or errors
I have joined the and I will share a link to this PR with the project maintainers there
I have read the
Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
Incase of UI change I've added a demo video.
We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.
Summary by CodeRabbit
New Features
Bug Fixes