Add copy and paste shortcuts to trackpad toolbar#102
Add copy and paste shortcuts to trackpad toolbar#102Nakshatra480 wants to merge 1 commit 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:
📝 WalkthroughWalkthroughAdds clipboard copy/paste support across UI, client connection hook, WebSocket handling, and server input handling; ControlBar gets onCopy/onPaste props used to trigger clipboard actions via the remote connection. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ControlBar
participant TrackpadPage
participant RemoteConn
participant WebSocket
participant Server
User->>ControlBar: pointer down "Copy"/"Paste"
ControlBar->>TrackpadPage: onCopy() / onPaste()
TrackpadPage->>RemoteConn: sendClipboard('copy') / sendClipboard('paste', clipboardText)
RemoteConn->>WebSocket: WS send { type: "input", ... , clipboard/action }
WebSocket->>Server: deliver message
Server->>Server: handleMessage -> (simulate Ctrl/Cmd+C or write paste text)
alt copy returns clipboard text
Server-->>WebSocket: send { type: "clipboard-content", text }
WebSocket-->>RemoteConn: WS message
RemoteConn-->>TrackpadPage: update clipboardText
TrackpadPage-->>ControlBar: (future paste uses clipboardText)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
🧹 Nitpick comments (1)
src/routes/trackpad.tsx (1)
156-162: Consider replacing helper functions with constants.
getCopyComboandgetPasteComboreturn the same static arrays every time. Plain constants (e.g. at module or component scope) would be simpler and signal immutability more clearly.♻️ Suggested simplification
- const getCopyCombo = () => { - return ["control", "c"]; - }; - - const getPasteCombo = () => { - return ["control", "v"]; - }; + const COPY_COMBO = ["control", "c"] as const; + const PASTE_COMBO = ["control", "v"] as const;Then update the call sites:
- onCopy={() => sendCombo(getCopyCombo())} - onPaste={() => sendCombo(getPasteCombo())} + onCopy={() => sendCombo([...COPY_COMBO])} + onPaste={() => sendCombo([...PASTE_COMBO])}(Or pass directly if
sendCombodoesn't mutate its argument.)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 156 - 162, Replace the two trivial helper functions getCopyCombo and getPasteCombo with constant arrays (e.g. COPY_COMBO and PASTE_COMBO) at module or component scope and update their call sites (places that call getCopyCombo/getPasteCombo or pass them into sendCombo) to use the constants directly; ensure you use frozen/readonly arrays if desired to signal immutability and only replace calls where sendCombo or other code does not mutate the array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/routes/trackpad.tsx`:
- Around line 156-162: Replace the two trivial helper functions getCopyCombo and
getPasteCombo with constant arrays (e.g. COPY_COMBO and PASTE_COMBO) at module
or component scope and update their call sites (places that call
getCopyCombo/getPasteCombo or pass them into sendCombo) to use the constants
directly; ensure you use frozen/readonly arrays if desired to signal
immutability and only replace calls where sendCombo or other code does not
mutate the array.
5f5978d to
fc24303
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/routes/trackpad.tsx (1)
156-157: HoistCOPY_COMBOandPASTE_COMBOoutside the component.These are static constants and don't depend on props, state, or closures. Defining them inside the component body means they're re-created on every render. Move them to module scope.
Proposed fix
+const COPY_COMBO = ["control", "c"] as const; +const PASTE_COMBO = ["control", "v"] as const; + function TrackpadPage() { ... - const COPY_COMBO = ["control", "c"] as const; - const PASTE_COMBO = ["control", "v"] as const; - return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 156 - 157, COPY_COMBO and PASTE_COMBO are defined inside the component and are recreated every render; hoist them to module scope by moving the const declarations for COPY_COMBO and PASTE_COMBO out of the Trackpad component (or whatever component function contains them) to the top of the file (near imports) so they become true static constants and are not redefined on each render.src/components/Trackpad/ControlBar.tsx (1)
95-102: Remove commented-out L-Click button.Commented-out code adds noise. If this removal is intentional, delete it entirely; if it's temporary, track it with an issue instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Trackpad/ControlBar.tsx` around lines 95 - 102, Remove the commented-out L-Click button markup from the ControlBar component to eliminate dead code noise: delete the entire commented block that contains the button using className "btn btn-sm btn-outline" and the onPointerDown handler that calls handleInteraction with onLeftClick so only active UI remains; if this was meant to be tracked, create an issue instead of leaving the commented code.
🤖 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/components/Trackpad/ControlBar.tsx`:
- Line 8: The ControlBar component's latency prop is currently typed as number |
null but upstream may pass undefined; update the prop type for latency in
ControlBar (the latency prop/interface) to number | null | undefined or add a
default (e.g., normalize undefined to null inside the ControlBar props handling)
so the component accepts the hook's initial undefined value; adjust any
consumers or destructuring in ControlBar to handle undefined safely (use
null-coalescing or guards) and keep types consistent with the upstream hook that
provides latency.
- Around line 77-120: Buttons in ControlBar.tsx are missing explicit type
attributes which defaults them to "submit" and can trigger form submissions;
update every <button> element that uses handleInteraction with handlers
onToggleScroll, onCopy, onPaste, onRightClick, onModifierToggle, and
onKeyboardToggle to include type="button" (also for the modifier button that
uses getModifierButtonClass/getModifierLabel) so none of these control buttons
act as form submitters.
In `@src/routes/trackpad.tsx`:
- Line 36: The trackpad.tsx runtime bug comes from destructuring a non-existent
latency from useRemoteConnection; remove latency from the destructure (keep
const { status, send, sendCombo } = useRemoteConnection()) and pass a safe
fallback to ControlBar (e.g., latency={null}) until you implement latency
tracking, or alternatively implement latency measurement inside
useRemoteConnection (add a latency state, measure ping in useRemoteConnection,
and return latency alongside status/send/sendCombo) and update the hook
signature accordingly.
---
Nitpick comments:
In `@src/components/Trackpad/ControlBar.tsx`:
- Around line 95-102: Remove the commented-out L-Click button markup from the
ControlBar component to eliminate dead code noise: delete the entire commented
block that contains the button using className "btn btn-sm btn-outline" and the
onPointerDown handler that calls handleInteraction with onLeftClick so only
active UI remains; if this was meant to be tracked, create an issue instead of
leaving the commented code.
In `@src/routes/trackpad.tsx`:
- Around line 156-157: COPY_COMBO and PASTE_COMBO are defined inside the
component and are recreated every render; hoist them to module scope by moving
the const declarations for COPY_COMBO and PASTE_COMBO out of the Trackpad
component (or whatever component function contains them) to the top of the file
(near imports) so they become true static constants and are not redefined on
each render.
fc24303 to
c5c9959
Compare
c5c9959 to
22263a0
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/routes/trackpad.tsx (1)
15-16: Consider marking combo constantsas constfor immutability.These module-level arrays are intended to be fixed values. Adding
as constprevents accidental mutation and narrows the type.Suggested change
-const COPY_COMBO = ["control", "c"]; -const PASTE_COMBO = ["control", "v"]; +const COPY_COMBO = ["control", "c"] as const; +const PASTE_COMBO = ["control", "v"] as const;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 15 - 16, COPY_COMBO and PASTE_COMBO are declared as mutable arrays; change them to readonly tuple literal types by appending "as const" to each declaration so they become immutable and their types are narrowed (e.g., COPY_COMBO and PASTE_COMBO should be declared as ["control","c"] as const and ["control","v"] as const), update any code that expects a mutable array if necessary to use readonly accessors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/routes/trackpad.tsx`:
- Around line 15-16: COPY_COMBO and PASTE_COMBO are declared as mutable arrays;
change them to readonly tuple literal types by appending "as const" to each
declaration so they become immutable and their types are narrowed (e.g.,
COPY_COMBO and PASTE_COMBO should be declared as ["control","c"] as const and
["control","v"] as const), update any code that expects a mutable array if
necessary to use readonly accessors.
22263a0 to
30878cc
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/routes/trackpad.tsx (1)
15-16: Clean constant definitions; note macOS caveat.Using
as consthere is good — it producesreadonlytuples that align with the updatedsendCombosignature. As acknowledged in the PR description,"control"won't trigger copy/paste on macOS (needs"meta"). A small helper likegetPlatformModifier()checkingnavigator.platformornavigator.userAgentDatawould be a natural follow-up.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/trackpad.tsx` around lines 15 - 16, COPY_COMBO and PASTE_COMBO are defined with a hardcoded "control" modifier which won't trigger copy/paste on macOS; add a small helper (e.g., getPlatformModifier) that detects platform via navigator.platform or navigator.userAgentData and returns "meta" for macOS and "control" otherwise, then replace uses of the hardcoded tuples (COPY_COMBO, PASTE_COMBO) or compute them using that helper so the tuples passed to sendCombo match the platform-specific modifier; ensure the tuples remain readonly to match sendCombo's signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/routes/trackpad.tsx`:
- Around line 15-16: COPY_COMBO and PASTE_COMBO are defined with a hardcoded
"control" modifier which won't trigger copy/paste on macOS; add a small helper
(e.g., getPlatformModifier) that detects platform via navigator.platform or
navigator.userAgentData and returns "meta" for macOS and "control" otherwise,
then replace uses of the hardcoded tuples (COPY_COMBO, PASTE_COMBO) or compute
them using that helper so the tuples passed to sendCombo match the
platform-specific modifier; ensure the tuples remain readonly to match
sendCombo's signature.
30878cc to
b6fe2b2
Compare
|
Hi @imxade the copy and paste shortcuts are properly working now, can you pls review this PR. |
|
@Nakshatra480 Sending key combinations might trigger unintended scenarios. For example, pressing copy in a terminal (Ctrl + C) could terminate a running process. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/hooks/useRemoteConnection.ts (1)
48-55: Consider guardingsetClipboardTextwithisMounted.The
onopenhandler (line 33) checksisMountedbefore updating state, butonmessagedoes not. If a message arrives in the narrow window between cleanup settingisMounted = falseand the socket being closed, this could trigger a state update after unmount. Additionally, cleanup nullifiesonopen/onclose/onerrorbut notonmessage.Not a practical issue in React 18 (no warning), but worth aligning for consistency.
Suggested fix
socket.onmessage = (event) => { try { const data = JSON.parse(event.data); - if (data.type === 'clipboard-content' && typeof data.text === 'string') { + if (isMounted && data.type === 'clipboard-content' && typeof data.text === 'string') { setClipboardText(data.text); } } catch { /* ignore non-JSON or irrelevant messages */ } };And in the cleanup (around line 68), add
wsRef.current.onmessage = null;alongside the other handler nullifications.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useRemoteConnection.ts` around lines 48 - 55, The onmessage handler may call setClipboardText after unmount; update socket.onmessage to check the existing isMounted flag before calling setClipboardText (same pattern used in the onopen handler) and ensure the cleanup also nulls the handler by setting wsRef.current.onmessage = null alongside onopen/onclose/onerror so no late messages can trigger state updates; reference socket.onmessage, setClipboardText, isMounted and wsRef.current.onmessage to locate and modify the code.src/server/websocket.ts (1)
80-80: No validation on theactionfield before passing toInputHandler.The
msgis cast toInputMessagewithout verifying thatactionis actually'copy'or'paste'for clipboard-type messages. A malformed message like{ type: 'clipboard', action: 'drop-tables' }reacheshandleMessageand falls through harmlessly (no-op), but more dangerous is a message like{ type: 'clipboard', action: 'paste', text: '<huge string>' }— theclipboard.setContent()call has no size bound. Consider adding basic validation at the WebSocket layer, especially for theclipboardtype.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/websocket.ts` at line 80, The WebSocket message is cast to InputMessage without validating the clipboard-specific fields; update the message handling in websocket.ts (before calling inputHandler.handleMessage) to validate that for messages with type === 'clipboard' the action is one of 'copy' or 'paste' and that for 'paste' the text length is bounded (introduce a MAX_CLIPBOARD_LENGTH constant and reject/trim messages exceeding it); return an error or ignore invalid clipboard messages instead of passing malformed input to inputHandler.handleMessage (reference InputMessage, the clipboard type/action values, inputHandler.handleMessage, and clipboard.setContent when applying the size check).
🤖 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 182-183: The paste handler currently re-sends the server-provided
clipboardText by calling sendClipboard('paste', clipboardText), which prevents
pasting content copied on the mobile device; change the onPaste flow in the
Trackpad component to read the device clipboard via
navigator.clipboard.readText() (fallback to clipboardText if unavailable or
permission denied) and then call sendClipboard('paste', <readText>); update any
related logic around sendClipboard and clipboardText (e.g., the onCopy handler
and state usage) to treat clipboardText as the server-origin copy cache only,
not the primary source for paste operations.
In `@src/server/InputHandler.ts`:
- Around line 160-161: Replace the fragile fixed await new Promise(resolve =>
setTimeout(resolve, 100)) delay with a short polling loop that reads
clipboard.getContent repeatedly until it changes or a timeout/maximum attempts
is reached: before triggering the simulated Ctrl+C, capture initialContent =
await clipboard.getContent(); after triggering the copy, poll in a loop (e.g.,
sleep 30–100ms between attempts) calling clipboard.getContent() and compare to
initialContent, returning as soon as it differs or when maxAttempts/timeout
elapses (return the last value); update the code in InputHandler around the
clipboard.getContent usage to implement this retry/polling strategy and add a
brief comment documenting the known limitation if the clipboard never updates.
- Around line 155-170: In the 'clipboard' branch of InputHandler.ts (case
'clipboard'), wrap the key-press operations in try/finally so modifier and
letter keys are always released even if clipboard.getContent() or
clipboard.setContent() throws: call keyboard.pressKey(modifier, Key.C/V) then in
a try block perform clipboard.getContent() (for copy) or clipboard.setContent()
and any paste logic, capture the copy result before finally, and in the finally
block call keyboard.releaseKey(modifier, Key.C/V) to release both keys; do the
same pattern for the paste path (press then try the clipboard operation, finally
release), mirroring the try/finally protection used in the existing combo
handling.
---
Duplicate comments:
In `@src/components/Trackpad/ControlBar.tsx`:
- Around line 13-14: The new onCopy and onPaste props are wired correctly but
the toolbar's <button> elements (including those that call onCopy, onPaste and
other handlers via handleInteraction in ControlBar) are missing explicit
type="button", which can cause unexpected form submissions; update every
<button> in the ControlBar component (the elements that invoke onCopy, onPaste
and those using handleInteraction) to include type="button". Ensure you add
type="button" to the buttons around the copy/paste handlers and the other
toolbar buttons referenced in the same component so all buttons behave as
non-submit controls.
---
Nitpick comments:
In `@src/hooks/useRemoteConnection.ts`:
- Around line 48-55: The onmessage handler may call setClipboardText after
unmount; update socket.onmessage to check the existing isMounted flag before
calling setClipboardText (same pattern used in the onopen handler) and ensure
the cleanup also nulls the handler by setting wsRef.current.onmessage = null
alongside onopen/onclose/onerror so no late messages can trigger state updates;
reference socket.onmessage, setClipboardText, isMounted and
wsRef.current.onmessage to locate and modify the code.
In `@src/server/websocket.ts`:
- Line 80: The WebSocket message is cast to InputMessage without validating the
clipboard-specific fields; update the message handling in websocket.ts (before
calling inputHandler.handleMessage) to validate that for messages with type ===
'clipboard' the action is one of 'copy' or 'paste' and that for 'paste' the text
length is bounded (introduce a MAX_CLIPBOARD_LENGTH constant and reject/trim
messages exceeding it); return an error or ignore invalid clipboard messages
instead of passing malformed input to inputHandler.handleMessage (reference
InputMessage, the clipboard type/action values, inputHandler.handleMessage, and
clipboard.setContent when applying the size check).
src/routes/trackpad.tsx
Outdated
| onCopy={() => sendClipboard('copy')} | ||
| onPaste={() => sendClipboard('paste', clipboardText)} |
There was a problem hiding this comment.
Paste sends server-side clipboard content, not the mobile device's clipboard.
The current flow round-trips clipboard through the server:
- Copy: server simulates Ctrl+C → reads host clipboard → sends content to client → stored in
clipboardText - Paste: client sends
clipboardTextback to server → writes to host clipboard → simulates Ctrl+V
This means "paste" can only paste what was previously "copied" through this app — not content from the mobile device's own clipboard. If the user copies text on their phone (outside the app) and taps Paste, the host clipboard gets overwritten with stale clipboardText (or empty string on first use).
Consider using the Clipboard API (navigator.clipboard.readText()) to read from the mobile device's clipboard on paste, which would make the feature more intuitive. This aligns with the reviewer comment in issue #97 about using the nut-js clipboard component more directly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/routes/trackpad.tsx` around lines 182 - 183, The paste handler currently
re-sends the server-provided clipboardText by calling sendClipboard('paste',
clipboardText), which prevents pasting content copied on the mobile device;
change the onPaste flow in the Trackpad component to read the device clipboard
via navigator.clipboard.readText() (fallback to clipboardText if unavailable or
permission denied) and then call sendClipboard('paste', <readText>); update any
related logic around sendClipboard and clipboardText (e.g., the onCopy handler
and state usage) to treat clipboardText as the server-origin copy cache only,
not the primary source for paste operations.
| case 'clipboard': { | ||
| const modifier = process.platform === 'darwin' ? Key.LeftSuper : Key.LeftControl; | ||
| if (msg.action === 'copy') { | ||
| await keyboard.pressKey(modifier, Key.C); | ||
| await keyboard.releaseKey(modifier, Key.C); | ||
| await new Promise(resolve => setTimeout(resolve, 100)); | ||
| return await clipboard.getContent(); | ||
| } else if (msg.action === 'paste') { | ||
| if (msg.text) { | ||
| await clipboard.setContent(msg.text); | ||
| } | ||
| await keyboard.pressKey(modifier, Key.V); | ||
| await keyboard.releaseKey(modifier, Key.V); | ||
| } | ||
| break; | ||
| } |
There was a problem hiding this comment.
Missing try/finally for key release — modifier keys can get stuck.
If keyboard.releaseKey or clipboard.getContent()/clipboard.setContent() throws, the modifier key (and C/V) remain pressed on the host machine. The existing combo case (lines 134-149) already guards against this with try/finally. The clipboard case should do the same.
Proposed fix
case 'clipboard': {
const modifier = process.platform === 'darwin' ? Key.LeftSuper : Key.LeftControl;
if (msg.action === 'copy') {
- await keyboard.pressKey(modifier, Key.C);
- await keyboard.releaseKey(modifier, Key.C);
- await new Promise(resolve => setTimeout(resolve, 100));
- return await clipboard.getContent();
+ try {
+ await keyboard.pressKey(modifier, Key.C);
+ } finally {
+ await keyboard.releaseKey(modifier, Key.C);
+ }
+ await new Promise(resolve => setTimeout(resolve, 100));
+ return await clipboard.getContent();
} else if (msg.action === 'paste') {
if (msg.text) {
await clipboard.setContent(msg.text);
}
- await keyboard.pressKey(modifier, Key.V);
- await keyboard.releaseKey(modifier, Key.V);
+ try {
+ await keyboard.pressKey(modifier, Key.V);
+ } finally {
+ await keyboard.releaseKey(modifier, Key.V);
+ }
}
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 155 - 170, In the 'clipboard' branch
of InputHandler.ts (case 'clipboard'), wrap the key-press operations in
try/finally so modifier and letter keys are always released even if
clipboard.getContent() or clipboard.setContent() throws: call
keyboard.pressKey(modifier, Key.C/V) then in a try block perform
clipboard.getContent() (for copy) or clipboard.setContent() and any paste logic,
capture the copy result before finally, and in the finally block call
keyboard.releaseKey(modifier, Key.C/V) to release both keys; do the same pattern
for the paste path (press then try the clipboard operation, finally release),
mirroring the try/finally protection used in the existing combo handling.
src/server/InputHandler.ts
Outdated
| await new Promise(resolve => setTimeout(resolve, 100)); | ||
| return await clipboard.getContent(); |
There was a problem hiding this comment.
The 100ms delay before reading clipboard is a fragile heuristic.
After simulating Ctrl+C, the target application writes to the clipboard asynchronously. 100ms may not be sufficient for large selections or slow applications, leading to stale/empty clipboard reads. There's no reliable way to "wait until clipboard changes," but consider:
- Polling the clipboard a few times with short intervals until content changes (or a timeout is reached).
- Documenting this known limitation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/InputHandler.ts` around lines 160 - 161, Replace the fragile fixed
await new Promise(resolve => setTimeout(resolve, 100)) delay with a short
polling loop that reads clipboard.getContent repeatedly until it changes or a
timeout/maximum attempts is reached: before triggering the simulated Ctrl+C,
capture initialContent = await clipboard.getContent(); after triggering the
copy, poll in a loop (e.g., sleep 30–100ms between attempts) calling
clipboard.getContent() and compare to initialContent, returning as soon as it
differs or when maxAttempts/timeout elapses (return the last value); update the
code in InputHandler around the clipboard.getContent usage to implement this
retry/polling strategy and add a brief comment documenting the known limitation
if the clipboard never updates.
aba46b3 to
db8292f
Compare
|
@Nakshatra480 I’ve reviewed your PR and the functionality looks good 👍 |
db8292f to
8863ae0
Compare
- Add clipboard case to InputHandler with platform-aware modifier keys - Use try/finally to prevent stuck keys on copy and paste operations - Poll clipboard for changes after copy (up to 500ms) - Validate clipboard messages and enforce 1MB text limit in websocket - Forward clipboard content from server to client via WebSocket - Add clipboardText state and sendClipboard to useRemoteConnection hook - Wire onCopy/onPaste handlers in ControlBar and trackpad page - Add type='button' to all ControlBar buttons - Read device clipboard on paste with server-cache fallback Closes AOSSIE-Org#97
8863ae0 to
8cb10d4
Compare
I have resolved the merge conflicts, now review the PR. |
Closes #97
Summary
This PR wires up the existing Copy and Paste buttons in the trackpad toolbar so they send real keyboard shortcuts to the host machine. Previously these buttons were just placeholders with no functionality.
What’s changed
ControlBarprops to accept two new callbacks:onCopyandonPaste.handleInteractionhelper inControlBar, keeping behavior consistent with other buttons.TrackpadPage:getCopyCombo()andgetPasteCombo()that return["control", "c"]and["control", "v"].onCopyandonPastetosendCombo(getCopyCombo())andsendCombo(getPasteCombo()).All keyboard events still go through the existing
useRemoteConnection+sendCombopath and are handled on the server via the existingInputHandlerandKEY_MAP.Implementation details
type: "combo") and combo handling logic on the server.Testing
npm run buildCtrl + C.Ctrl + V.Notes
controlfor compatibility across platforms. If needed, a follow-up can make the modifier platform-aware (e.g.metaon macOS) without changing this UI wiring.Summary by CodeRabbit
New Features
Enhancements