CueWeave 是一个支持多设备实时同步的网页提词器。可以用电脑显示提词画面,用手机或平板编辑文稿、控制播放和调整进度。
- Node.js + WebSocket 局域网实时同步,按房间增量持久化,无需外部数据库即可运行。
- 扫描二维码或打开邀请网址自动加入,无需手动输入房间号。
- 支持自由协作、权限协作和导播三种房间模式。
- 邀请链接直接区分查看、编辑、导播和纯显示权限,扫码后的能力与邀请文案一致。
- 文稿、排版、播放状态、速度和定位线行锚点实时同步。
- 桌面、手机和平板使用统一分行,定位线始终对应同一行文字。
- 自动滚动、手动滚动和跨设备跟随均采用平滑动画,并可翻转为文字从上方出现、向下滚动。
- 每台设备可选择跟随房间镜像,或使用仅作用于本机的镜像设置。
- 房间快照持久化,支持断线重连、房主宽限期恢复和自动移交。
- 文稿使用独立版本号防止多台编辑设备静默互相覆盖,冲突稿自动进入本机备份。
- 播放或全屏时会尝试保持屏幕常亮,并在同步中断时直接在提词画面提示。
- 支持“开始直播”锁定文稿和画面,避免现场误改导致所有显示端重排。
需要 Node.js 18 或更高版本。
npm install
npm start浏览器访问 http://localhost:17321。同一局域网内的其他设备访问:
http://运行服务的电脑局域网IP:17321
macOS 可以在“系统设置 → 网络”中查看局域网 IP。端口冲突时可以更换端口:
PORT=17322 npm start直接双击 index.html 仍然可以作为纯本地提词器使用,但无法使用多设备同步。
- 打开首页后先选择“新建房间”、“加入房间”或“仅自己使用”;也可直接进入最近房间。入口弹窗不会因误点遮罩或按下 Esc 关闭。
- 填写设备名称、设备界面偏好和房间模式。
- 进入后网址会变为
/room/房间码;让其他设备直接扫描二维码或打开邀请网址。 - 对方会自动加入,无需填写房间码或再次确认;六位房间码仅作为备用方式。
- 房主选择“查看邀请、编辑邀请、导播邀请或显示邀请”后再分享二维码;邀请中包含不可猜测的权限令牌,扫码设备直接获得对应权限。
- 开播前确认在线设备和权限,点击“开始直播并锁定文稿”;直播期间仍可播放、调速和定位,但不能修改文稿或画面。
- 结束直播后播放会暂停,文稿和画面重新开放编辑。房主仍可在设备列表中调整角色和界面用途。
当前浏览器会保存最近 20 个房间的设备恢复凭证,回到首页后可选择恢复原身份。从最近列表移除记录不会删除服务器房间。如果浏览器数据被清理,仍可通过原链接或房间码重新加入,但无账号模式下无法自动证明原房主身份。
房主可以从设备列表移除不再使用的离线设备。普通成员主动退出房间时,其恢复凭证会同时从服务器成员列表移除;长期离线成员默认保留 30 天,房主身份不会被自动清理。
当房主通过 localhost 或 127.0.0.1 打开页面时,Node 服务会读取服务器自身的局域网 IPv4并用于二维码,不会误用浏览器所在设备的地址。通过域名或公网地址访问时,邀请链接会保留当前域名。
- 自由协作(默认):除显示端外,普通加入的设备默认都可以编辑文稿和画面、控制播放和滚动;房主仍可将单个成员改为编辑者、操作者或查看者。
- 权限协作:房主为设备分配编辑者、操作者或查看者角色。
- 导播模式:严格按照角色拆分编辑和播放控制权限。
- 控制端:显示完整提词、设置和房间管理界面。
- 编辑端:优先展示文稿编辑能力;实际权限来自邀请或房主调整。
- 导播端:优先展示播放控制能力;实际权限来自邀请或房主调整。
- 显示端:只显示提词画面,隐藏房间设置、编辑和播放控件;仍可通过右上角半透明面板切换本机镜像。
手机控制端采用窄屏布局,设置栏可以独立横向滑动,不会撑宽或裁切整个页面;回到开头和专注模式收纳在播放栏右侧的更多菜单中;手机和平板横屏时会按可视高度切换紧凑布局,确保提词区和播放栏完整显示;手机显示端会铺满可视区域并适配浏览器动态工具栏。
- 房主:拥有全部权限,可以管理房间、设备和角色。
- 协作者:在自由协作房间中可以编辑文稿和画面、控制播放和滚动。
- 编辑者:可以编辑文稿和画面设置。
- 操作者:可以播放、暂停、调速和调整进度。
- 查看者:只能接收并显示同步内容。
前端会隐藏或禁用无权操作的控件,服务端还会再次校验每条修改消息,不能通过浏览器控制台绕过权限。
- 房主断线后保留身份 60 秒,刷新或网络恢复后可以自动重连。
- 超过 60 秒仍未回来时,优先把房主移交给在线编辑者,其次是操作者和普通查看者;显示端不会成为房主。
- 原房主在移交后重新上线,会以编辑者身份回来,不会自动抢回控制权。
- 所有设备离线后,房间和文稿默认永久保留,以便日后通过原房间链接恢复。
- 房主可以主动关闭并立即销毁房间。
- 每个房间分别保存在
data/rooms-v4/房间码.json,服务重启后仍可恢复;播放状态在重启后自动暂停。 - 普通文稿、画面和播放进度变化先标记为待保存,默认最多每 5 秒只写入发生变化的房间;创建/销毁房间、直播状态、暂停或跳转、权限变更、成员移除以及服务关闭会立即保存。
- 每个房间成功保存前会保留自己的
.bak备份;启动时会清理中断写入留下的临时文件,单个房间主快照损坏时自动尝试其备份。 - 如果需要定期清理,可通过
EMPTY_ROOM_TTL_MS设置空房间保留毫秒数。
- 提词文稿
- 字号、行距、字距和阅读宽度
- 背景色、文字色、定位线、房间镜像及滚动翻转设置
- 播放、暂停、速度和回到开头
- 归一化滚动进度
- 房间模式、角色、设备用途和在线状态
提词画面采用基于容器宽度的相对单位(cqw),字号和字距会随提词区域等比缩放。文稿还会经过确定性分行,同一份字号、字距和阅读宽度设置在电脑、手机和平板上会生成完全相同的每行内容,而不是由各系统自行换行。
滚动同步使用“定位线当前穿过的文稿行坐标”作为锚点,而不是只发送整篇文章的滚动百分比。因此不同高度、不同宽高比的设备会让同一行文字经过各自相同比例位置的定位线;自动滚动速度也按提词区域宽度等比换算,保持一致的行进节奏。
每台设备可独立选择是否“跟随房间镜像”。跟随时,拥有画面编辑权限的设备修改镜像会同步给全房间;关闭跟随后,左右和上下镜像只保存在当前浏览器,不广播,也不受房间镜像变化影响。纯显示端可触碰右上角的半透明本机镜像面板进行切换。
播放时各设备都以设置的可见速度连续滚动,控制设备每 400ms 左右发送一次进度锚点。显示端只在有限范围内小幅加减速来追赶误差,不会因手机和电脑的文稿高度比例而降到近乎静止,也不会在播放期间周期性硬改位置;主动跳转或暂停时才精确定位。
自动滚动使用独立的高精度浮点位置累加,再写入浏览器滚动容器,低速不会因为高刷新率屏幕或浏览器对 scrollTop 的像素取整而失效。
开启“滚动翻转”后,文稿仍按原有顺序阅读,但文字改为从画面上方出现并向下移动;回到开头、定位线锚点和多设备进度同步也会同时切换方向。
自动滚动过程中仍可使用鼠标滚轮、触控板、触摸手势或滚动条手动定位。手势期间自动滚动会暂时让出位置控制,并以约 20 帧/秒持续同步进度;其他设备通过逐帧追随动画自然移动到新位置。松手后各端平滑收敛,并从新的位置继续自动滚动。
| 变量 | 默认值 | 说明 |
|---|---|---|
HOST |
0.0.0.0 |
HTTP/WebSocket 监听地址 |
PORT |
17321 |
服务端口 |
OWNER_GRACE_MS |
60000 |
房主断线后的身份保留时间 |
EMPTY_ROOM_TTL_MS |
0 |
全员离线后的房间保留毫秒数;0 表示永久保留 |
OFFLINE_MEMBER_TTL_MS |
2592000000 |
非房主离线设备保留时间;0 表示永久保留 |
PERSIST_INTERVAL_MS |
5000 |
普通状态变化合并落盘的最大间隔,最低 1000ms |
MAX_ROOMS |
1000 |
服务端最多保留的房间数 |
MAX_MEMBERS_PER_ROOM |
50 |
单个房间最多保留的设备数 |
ATTEMPT_WINDOW_MS |
60000 |
按来源地址统计创建/加入尝试的时间窗口 |
MAX_ATTEMPTS_PER_IP |
120 |
单个时间窗口内允许的创建/加入次数 |
MAX_SOCKET_BUFFER_BYTES |
1048576 |
单个慢连接允许积压的发送字节数;临时进度会丢弃,可靠消息会要求重连 |
TRUST_PROXY |
false |
仅在可信反向代理会清洗请求头时启用 X-Forwarded-For |
ALLOWED_ORIGINS |
当前访问域名 | 额外允许建立 WebSocket 的 Origin,多个值用逗号分隔 |
ROOM_DATA_DIR |
data/rooms-v4 |
v4 房间增量快照目录 |
- 全文编辑器支持导入、导出以及最近 10 份本机稿件备份;大稿件备份存入 IndexedDB,不再挤占
localStorage。 - 停止输入 30 秒后保存一份去重备份;清空、重置、导入和恢复前也会先备份当前稿件。
- 每次文稿修改必须携带当前
scriptRevision。版本过期或缺失时,服务端拒绝覆盖并返回最新快照;被拒绝的文稿自动保存为“同步冲突”本机备份。 - v4 同步协议、邀请令牌和按房间快照不兼容旧版本。旧的
data/rooms.json不会读取;浏览器使用全新的 v4 本地存储键,不会恢复旧房间身份。
公网环境应在 Node.js 服务前配置 Nginx、Caddy 或同类反向代理:
- 启用 HTTPS,浏览器将自动使用
wss://。 - 正确转发 WebSocket 的
Upgrade和Connection请求头。 - 限制
data/rooms-v4/的文件访问权限并定期备份。 - 不要把 Node.js 服务端口直接暴露到公网。
- 静态页面默认返回 CSP、Referrer-Policy、禁止 MIME 嗅探和同源嵌入策略;WebSocket 默认只接受与访问页面同域的 Origin。
当前版本使用房间码作为加入凭证,适合可信团队和局域网使用。如果用于公开互联网或保存敏感文稿,建议继续增加账户登录、房间密码、邀请审批和操作审计。
| 文件 | 说明 |
|---|---|
server.js |
HTTP、WebSocket、房间权限、直播锁定、生命周期和增量持久化服务 |
sync-client.js |
浏览器端房间连接、重连、邀请和权限界面 |
app.js |
提词器状态、统一排版、滚动与本机镜像逻辑 |
index.html / styles.css |
页面结构和响应式界面 |
test/ |
WebSocket、权限、生命周期及同步集成测试 |
data/rooms-v4/ |
默认房间快照目录,运行时按房间自动维护 |
- 提词稿实时预览与浏览器本地自动保存
- 全文编辑和双击文字原位修改
- 自动滚动、播放/暂停、速度微调和回到开头
- 鼠标滚轮、触控板及滚动条手动定位
- 字号、行距、字距、阅读宽度和定位线调整
- 背景色、文字色、镜像、滚动翻转、专注模式和全屏
- 手机和平板进入全屏或专注模式后,可通过提词画面右上角的悬浮按钮直接退出
- 快捷键:
Space播放/暂停,↑/↓调速,R回到开头,F专注模式,Enter全屏
npm run check
npm test测试会启动隔离的临时 Node.js 服务并模拟多台 WebSocket 设备,覆盖三种房间模式、四种设备用途、权限变更、状态与播放锚点同步、房主断线重连与移交、主动关房、空房默认保留与可选过期、服务重启、异常消息、超大消息及同一设备多开页面接管。
已执行的真实浏览器回归还包括桌面/手机布局、二维码邀请自动加入、低速滚动、自动滚动期间手动接管、连续平滑跟随、跨尺寸统一分行、定位线行锚点、设备独立镜像和断线自动恢复。