diff --git a/README.md b/README.md index f412994..7f26bec 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ guacd (C, from guacamole-server) - **Connections** with folder-based organisation and OIDC group access control - **Active Sessions** section with live thumbnail previews - **Session ended overlay** with Reconnect/Close buttons +- **Clipboard panel controls** (Home + Fullscreen) - **8 built-in themes** with CSS gradient backgrounds, or configure your own - **Reports page** with session analytics, history, and CSV export diff --git a/docs/web-sessions.md b/docs/web-sessions.md index d47c33e..caa3236 100644 --- a/docs/web-sessions.md +++ b/docs/web-sessions.md @@ -431,6 +431,22 @@ Clipboard copy and paste can be independently disabled per connections entry. Th These work for all session types (SSH, RDP, VNC, Web), not just web sessions. See [Security: Clipboard control](security.md#clipboard-control) for details. +## In-session keyboard shortcuts + +The session page supports a small set of browser-side shortcuts: + +| Shortcut | Action | Notes | +|----------|--------|-------| +| `Ctrl+Alt+Shift` | Toggle the clipboard side panel | Works globally on the session page (capture phase), including when the remote display is focused. | +| `Ctrl+V` (Windows/Linux) or `Cmd+V` (macOS) | Sync browser clipboard text to the remote session, then send paste | If clipboard API access is available, rustguac reads local clipboard text and sends it to the remote before forwarding the paste key event. | +| `Esc` (browser fullscreen) | Exit fullscreen | Browser-native fullscreen key. rustguac may request keyboard lock while in fullscreen; if lock is unavailable, an on-screen notice reminds users to press Esc. | + +Additional behavior: + +- No dedicated keyboard combo is currently assigned for entering fullscreen. Users can use entry-level fullscreen-on-connect, or open the `Ctrl+Alt+Shift` clipboard panel and click its **Fullscreen** button (next to **Home**). +- All other key presses are passed through to the remote host by Guacamole keyboard handling. +- Clipboard policy flags still apply: `disable_copy` and `disable_paste` can block corresponding clipboard flows regardless of local shortcuts. + ## URL placeholders The entry URL supports credential placeholders that are URL-encoded and substituted before Chromium navigates: diff --git a/static/client.html b/static/client.html index d9a88d2..790d7a1 100644 --- a/static/client.html +++ b/static/client.html @@ -118,24 +118,6 @@ border: 1px solid var(--border) !important; } #btn-reconnect:hover { background: var(--border); } - #fs-toggle { - display: none; - position: fixed; - top: 10px; - right: 10px; - z-index: 1500; - background: rgba(0,0,0,0.55); - color: var(--text-muted); - border: 1px solid var(--border); - border-radius: 4px; - padding: 4px 10px; - font-family: monospace; - font-size: 14px; - cursor: pointer; - opacity: 0.45; - transition: opacity 0.2s; - } - #fs-toggle:hover { opacity: 1; color: var(--accent); } #fs-bar { display: none; position: fixed; @@ -247,7 +229,6 @@

Session Ended

Loading...
-
@@ -528,6 +509,7 @@

Session Ended

'
' + '

Session

' + '' + + '' + '' + '
' + '
' + @@ -557,6 +539,10 @@

Session Ended

// Connections' active-session card. window.location.href = '/connections.html'; }); + document.getElementById('cp-fullscreen').addEventListener('click', function() { + rgEnterFullscreen(); + if (panelOpen) toggleClipboardPanel(); + }); document.getElementById('cp-send').addEventListener('click', function() { if (clipboardTextarea.value) sendClipboardToRemote(clipboardTextarea.value); }); @@ -1043,9 +1029,9 @@

Session Ended

// the prior gesture. So when the entry is marked // fullscreen-on-connect we don't try to fire on page load. // Instead we arm a one-shot listener for the next mousedown - // or keydown after CONNECTED. The manual gear-toggle button - // in the corner uses its own click gesture and works at any - // time once shown. + // or keydown after CONNECTED. The Ctrl+Alt+Shift menu's + // fullscreen button uses its own click gesture and works at + // any time once shown. function rgFullscreenEl() { return document.fullscreenElement || document.webkitFullscreenElement @@ -1138,9 +1124,7 @@

Session Ended

} function rgOnFullscreenChange() { var inFs = !!rgFullscreenEl(); - var tg = document.getElementById('fs-toggle'); var bar = document.getElementById('fs-bar'); - if (tg) tg.style.display = inFs ? 'none' : 'block'; if (bar) { if (inFs) { bar.classList.add('visible'); @@ -1159,7 +1143,6 @@

Session Ended

} ['fullscreenchange','webkitfullscreenchange','mozfullscreenchange','MSFullscreenChange'] .forEach(function(ev){ document.addEventListener(ev, rgOnFullscreenChange); }); - document.getElementById('fs-toggle').addEventListener('click', rgEnterFullscreen); document.getElementById('fs-exit').addEventListener('click', rgExitFullscreen); document.getElementById('fs-disconnect').addEventListener('click', function() { rgExitFullscreen(); @@ -1179,7 +1162,6 @@

Session Ended

// First capture after 3s (let display render) setTimeout(captureAndUploadThumbnail, 3000); } - document.getElementById('fs-toggle').style.display = 'block'; document.getElementById('fs-bar-title').textContent = entryDisplayName || ('Session ' + sessionId.substring(0,8)); if (wantFullscreen) rgArmFullscreenGesture(); @@ -1197,7 +1179,6 @@

Session Ended

statusEl.textContent = 'Disconnected'; statusEl.className = ''; if (_thumbInterval) { clearInterval(_thumbInterval); _thumbInterval = null; } document.getElementById('disconnected-overlay').className = 'visible'; - document.getElementById('fs-toggle').style.display = 'none'; if (rgFullscreenEl()) rgExitFullscreen(); break; } @@ -1212,7 +1193,6 @@

Session Ended

document.getElementById('disconnected-title').textContent = 'Connection Error'; document.getElementById('disconnected-message').textContent = status.message || 'An error occurred.'; document.getElementById('disconnected-overlay').className = 'visible'; - document.getElementById('fs-toggle').style.display = 'none'; if (rgFullscreenEl()) rgExitFullscreen(); };