@wql00/xpra-vue-client
v0.1.0
Published
Xpra HTML5 client refactored for Vue / modern bundlers (Vite, Webpack 5).
Readme
@wql00/xpra-vue-client
Xpra HTML5 client refactored for Vue / modern bundlers (Vite, Webpack 5).
基于官方 xpra-html5 重构,适配 Vue 3 / Vite 工程化,将 worker、协议、窗口管理等模块以 ESM 形式导出,可直接 import 使用。
状态
0.1.0 — 早期版本。API 可能调整,已知存在遗留 DOM/全局耦合(见下方 已知限制)。
安装
npm install @wql00/xpra-vue-client interactjs
# 或
pnpm add @wql00/xpra-vue-client interactjsinteractjs 是 peer dependency,需自行安装(用于窗口拖拽/缩放)。
要求
- 打包器:Vite ≥ 4 或 Webpack 5(必须支持
new Worker(new URL('./xxx.worker.ts', import.meta.url), { type: 'module' })语法) - 浏览器:现代浏览器(Chrome/Edge/Firefox/Safari 最新版)
- TypeScript:≥ 5.0(可选,包内置
.d.ts类型声明)
快速开始
1. 准备容器元素
xpra 客户端会通过 document.getElementById(containerId) 查找容器,并将远程桌面渲染到其中。
<!-- index.html 或 Vue 模板 -->
<div id="screen" style="width: 100vw; height: 100vh;"></div>2. 创建客户端并连接
import { XpraClient } from '@wql00/xpra-vue-client';
const client = new XpraClient('screen'); // 容器元素 id
// 连接参数
client.host = '1.2.3.4';
client.port = 10000;
client.path = '/';
client.username = 'foo';
client.passwords = ['bar'];
client.ssl = true; // wss://,false 则用 ws://
// 可选:加密
// client.encryption = 'AES'
// 可选:编码
// client.encoding = 'jpeg'
// 回调
client.on_connection_progress = (msg, details, progress) => {
console.log(`[${progress}%] ${msg}`);
};
client.on_connect = () => console.log('connected');
client.callback_close = (reason) => console.log('disconnected:', reason);
// 启动
client.init();
client.connect();3. 断开连接
client.reconnect = false;
client.close();API
XpraClient
主类,从 '@wql00/xpra-vue-client' 导出。
构造函数
new XpraClient(containerId: string)containerId:HTML 容器元素的id(不是元素本身)
主要属性
| 属性 | 类型 | 说明 |
| ------------ | ---------- | ----------------------------------------- |
| host | string | 服务器主机 |
| port | number | 服务器端口 |
| path | string | WebSocket 路径,默认 '/' |
| username | string | 用户名 |
| passwords | string[] | 密码(数组,支持多密码轮询) |
| ssl | boolean | 是否使用 wss:// |
| encryption | string | 加密方式(如 'AES') |
| encoding | string | 图像编码(如 'jpeg'、'png'、'rgb') |
| reconnect | boolean | 是否自动重连 |
主要方法
| 方法 | 说明 |
| ----------- | --------------------------------- |
| init() | 初始化(在 connect() 之前调用) |
| connect() | 建立连接 |
| close() | 关闭连接 |
回调
| 回调 | 说明 |
| ------------------------------------------------ | ---------------- |
| on_connect() | 连接成功 |
| on_open() | WebSocket 已打开 |
| callback_close(reason: string) | 连接关闭 |
| on_connection_progress(msg, details, progress) | 连接进度 |
类型
import type { XpraOptions, ClientOptions, ClientCallbacks } from '@wql00/xpra-vue-client';XpraOptions 是简化的连接参数接口:
interface XpraOptions {
host: string;
port: number;
path: string;
username: string;
password: string;
ssl?: boolean;
encryption?: string;
encoding?: string;
}Vue 3 集成示例
虽然本包不包含 Vue 组件(保持框架无关),但可以很轻松地封装成 composable:
// useXpra.ts
import { ref, onUnmounted } from 'vue';
import { XpraClient, type XpraOptions } from '@wql00/xpra-vue-client';
export function useXpra(containerId: string) {
const client = ref<XpraClient | null>(null);
const status = ref<'idle' | 'connecting' | 'connected' | 'disconnected'>('idle');
function connect(options: XpraOptions) {
if (client.value) client.value.close();
const c = new XpraClient(containerId);
c.host = options.host;
c.port = options.port;
c.path = options.path;
c.username = options.username;
c.passwords = [options.password];
c.ssl = options.ssl ?? false;
if (options.encryption) c.encryption = options.encryption;
if (options.encoding) c.encoding = options.encoding;
c.on_connect = () => {
status.value = 'connected';
};
c.callback_close = () => {
status.value = 'disconnected';
};
c.on_connection_progress = () => {
status.value = 'connecting';
};
client.value = c;
c.init();
c.connect();
}
function disconnect() {
client.value?.close();
client.value = null;
status.value = 'disconnected';
}
onUnmounted(disconnect);
return { client, status, connect, disconnect };
}<!-- XpraViewer.vue -->
<template>
<div :id="containerId" style="width: 100%; height: 100%;"></div>
</template>
<script setup lang="ts">
import { useXpra } from './useXpra';
const containerId = 'screen';
const { status, connect } = useXpra(containerId);
connect({
host: '1.2.3.4',
port: 10000,
path: '/',
username: 'foo',
password: 'bar',
ssl: true,
});
</script>工作原理
Worker 加载
本包内部使用 3 个 Web Worker:
protocol.worker.ts— 协议层(rencode/lz4/brotli 解码、加密)decode.worker.ts— 图像解码offscreen.worker.ts— 离屏渲染
通过 Vite/Webpack 5 的 new Worker(new URL('./workers/*.worker.ts', import.meta.url), { type: 'module' }) 语法加载。这意味着运行时入口指向 src/index.ts 源码(而非预编译的 dist/index.mjs),由消费方的打包器编译 worker。dist/ 下的 JS 产物仅供 CJS 环境或不使用 worker 的场景使用。
Brotli 解码器
包入口(src/index.ts)会自动将 vendored 的 BrotliDecode 注册到 self 全局,消费方无需手动处理。
已知限制(TODO)
以下为官方 xpra-html5 遗留的耦合,尚未完全解耦,计划在后续版本逐步清理:
- DOM ID 耦合:
Client.ts/Window.ts直接通过 ID 访问 DOM 元素(#screen、#float_menu、#pasteboard、#window_preview、#dpi、#favicon等)。若宿主页面没有这些元素,相关功能会静默失败。 - 全局耦合:
MenuCustom.ts和Client.ts部分代码依赖window.client、window.toggle_float_menu、window.doNotification等全局变量。 - 动态生成的 DOM ID:
Window.ts内部会创建head${wid}、title${wid}、windowicon${wid}等元素,与宿主页面可能冲突。
临时方案:在宿主页面预留必要的容器元素(至少 #screen)。
开发
# 安装依赖
pnpm install
# 构建(生成 dist/)
pnpm --filter @wql00/xpra-vue-client build
# 类型检查
npx tsc --noEmit --project packages/xpra-html5/tsconfig.jsonLicense
This package is based on the xpra-html5 project (MPL-2.0).
