wraith/frontend/components/rdp/RdpCanvas.vue
Vantz Stockwell e2e03be2dd feat: RDP frontend — Guacamole client with custom JSON WebSocket tunnel
- useRdp.ts: JsonWsTunnel class extends Guacamole.Tunnel to bridge
  guacamole-common-js (expects raw protocol) with our JSON gateway
  (consistent with SSH/SFTP message envelope pattern). Parses
  length-prefixed Guacamole instructions, dispatches to Guacamole.Client.
  Handles mouse/keyboard input, clipboard send, and session lifecycle.
- RdpCanvas.vue: full-size container that mounts the Guacamole display
  canvas. Calls useRdp().connectRdp() on mount, cleans up on unmount.
  Exposes sendClipboard() and disconnect() for toolbar integration.
- RdpToolbar.vue: auto-hiding floating toolbar (3s idle timeout) with
  clipboard paste dialog, fullscreen toggle (HTML5 Fullscreen API),
  settings panel stub, and disconnect button.
- SessionContainer.vue: renders RdpCanvas + RdpToolbar when
  session.protocol === 'rdp', replacing the Phase 3 placeholder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:27:19 -04:00

67 lines
1.5 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useRdp } from '~/composables/useRdp'
const props = defineProps<{
hostId: number
hostName: string
color?: string | null
}>()
const container = ref<HTMLDivElement | null>(null)
const { connectRdp } = useRdp()
let rdpSession: ReturnType<ReturnType<typeof useRdp>['connectRdp']> | null = null
// Expose to parent (RdpToolbar uses these)
const sendClipboard = (text: string) => rdpSession?.sendClipboardText(text)
const disconnect = () => rdpSession?.disconnect()
defineExpose({ sendClipboard, disconnect })
onMounted(() => {
if (!container.value) return
rdpSession = connectRdp(
container.value,
props.hostId,
props.hostName,
props.color ?? null,
{
width: container.value.clientWidth,
height: container.value.clientHeight,
},
)
})
onBeforeUnmount(() => {
rdpSession?.disconnect()
rdpSession = null
})
</script>
<template>
<!-- Full-size container for the Guacamole display canvas.
The Guacamole client appends its own <canvas> element here. -->
<div
ref="container"
class="rdp-canvas-container"
/>
</template>
<style scoped>
.rdp-canvas-container {
@apply absolute inset-0 bg-gray-950 overflow-hidden cursor-default;
}
/* Guacamole appends a display div; make it fill the container */
.rdp-canvas-container :deep(> div) {
width: 100% !important;
height: 100% !important;
}
.rdp-canvas-container :deep(canvas) {
display: block;
}
</style>