OrbDrop 这次更新了。

局域网传文件,大多数工具做的是「发送」:选文件、传过去、对面落一份副本。OrbDrop 把通路拆成两条,共享是引用,推送才是复制:共享让对端直接读写你磁盘上的目录,推送才在接收端落副本。

说「中转站」不是把它当公网中继。它只是常驻在局域网里:共享、会话、跨网段中转都从这颗球走,不经过云。

这次没动这个模型,主要补了中英双语和后台更新检查,另外修了几个更新后才会撞见的问题。

共享是引用,推送是复制

打开 OrbDrop,主体是一颗始终置顶的悬浮球。Windows、macOS、Android 都能装,开源,不用账号,数据不经过云。文件夹拖进去,就会登记成一个共享项,对端当场可见。

收起后能选圆球或圆角矩形卡片。圆球更省屏幕,卡片能把条目直接拖进资源管理器或别的应用;共享和推送的逻辑一样,只是收起后的操作方式不同。

圆球与圆角矩形悬浮框

对端看到的不是上传后的快照,而是你磁盘上那棵真实的目录树。它能浏览、能下载、能丢进 Beyond Compare 比较,也能改、能新建、能删。这些写操作会直接落回源文件。整个过程不复制文件,数据始终留在拥有它的那台机器上;对端一掉线,这条访问也就结束。

推送是另一条路。在会话里点「+」选文件或文件夹,清单只建一次,可以分发给多台设备。传输按块续传,每块做 SHA-256 校验,先写 <名字>.orbdrop-part 再原子改名,接收端最后拿到一份独立副本。之后源端怎么样,都和这份副本无关。

推送不是一次性发送,它挂在会话里:有历史、有离线队列、能群发,也能多台设备各看各的进度。

拖入共享 会话推送
文件在哪 留在源机器原地 接收端多一份独立副本
对端能做什么 浏览、下载、比较、直接改 打开、另存、继续转发
改动落到哪 源文件 只影响副本
适合什么 大目录、临时协作 交付、留档

共享是引用,推送是复制

这里的关键不是「不用复制」——LocalSend、PairDrop 这类工具传文件时也不需要你手动复制。差别在终点:它们最后仍然在接收端落一份副本;OrbDrop 的共享是链路存在期间的引用,对端看到的是你磁盘上的目录树,改的就是源文件。

所以这两个动作别混。让别人看你的目录、改你的文件,用共享;真要交付一份东西,用推送。硬塞进同一个「发送」按钮里,最后一定是什么都能干,但都不顺手。

Windows 上还有个挺实用的小把戏:对端的文件明明不在本机磁盘上,也能从 OrbDrop 的树里直接拖进资源管理器,或者拖进微信这类只认 CF_HDROP 的程序。拖出时只在本地建一个 CFAPI 占位符,实测每个 0.22 ms、占用 0 字节;目标程序真的去读,才从对端拉字节。macOS 没有公开的 CFAPI 等价物,只能在起拖前把对端项整份暂存,大目录要等一会儿。这不是偷懒,是平台没给等价机制。

Windows 占位符拖出,macOS 起拖前暂存

先补界面,中英文能切了

之前只有中文。这次把桌面端、安卓端和原生层都补上了 English。

首次启动直接采用系统语言,支持 English 和简体中文;系统语言不在列表里,比如繁体中文,就回退到英语。设置页只列这两项,没有「跟随系统」这一档。选中即刻生效并落盘,下一次启动第一帧就是该语言。旧版本配置里的 auto 会在启动时解析成系统语言,再写回具体值。

实现没走「先给几百条文案起一套 key,再各写两份译文」的路。消息键就是简体中文原文,gettext 风格,英文词条按域放在 src/lib/i18n/locales/en/*.json。这样改中文文案不用同步 key,代价是同一条中文在不同上下文要译成不同英文时,得改中文原文来区分。npm run check:i18n 会查缺译、占位符对不上和词典冲突。

原生层也跟着切:托盘菜单和 tooltip 就地换字,不重建图标;消息通知正文也走同一份语言偏好。安卓的通知渠道名、常驻通知标题和插件错误走 values / values-zh-rCN 资源,这部分按系统语言选择,不跟应用内切换走。英文覆盖了目录、移动端、所有对话框、气泡、空态和无障碍标签。

边界也说清楚:后端日志还是中文,命令返回的底层错误文本也可能还是中文,只是界面提示前缀会本地化。不影响主路径,但得知道。

再补更新检查,终于像个后台服务

旧版只有启动后 8 秒查一次,30 分钟内复用上次结论。托盘常驻和安卓前台服务可能几周不重启,更新就要等到下次启动才被发现;启动时正好断网,这次运行也不再重试。

这次在后端加了一个调度循环,管四档:

  • 启动 8 秒后,静默查一次
  • 运行期间每 6 小时 ± 30 分钟查一次
  • 安卓回前台、桌面单实例、macOS Reopen 时,距上次成功 ≥ 1 小时才查
  • 手动点「检查更新」,每次都查

静默检查失败按 5 分钟 → 30 分钟 → 2 小时 → 6 小时退避,成功一次归零。调度用墙钟判定,记在 <数据目录>/update.json 的 lastSuccessMs 里,不是 Instant。系统休眠或挂起期间单调时钟不前进,睡一晚上醒来,不能被当成「才过了一小时」。

查到的新版本和按掉的版本都会落盘。重启后第一帧就有气泡或角标;按过「稍后」只压住当前这一版,换新版照弹,手动「检查更新」会清掉按掉的记录。后台那趟失败,也不会把已经查到的「有新版本」抹掉。

更新日志现在默认折叠成两行,点「查看完整更新日志」再展开。这点在手机底板上尤其明显,不然十几行变更能把一格撑得比面板还高。

更新检查的触发与退避

还是不发系统通知。更新不是急事,应用自己那组通知位够了,用户没看着应用时,多一条系统级弹窗只是干扰。

几个更新后才会撞见的问题

设备列表里冒出自己

macOS 上最容易看到,Windows 重启后同样可能。根因不在 mDNS,在中转记忆:已配对对端推回来的反向路由记录里点名了收件人自己,本机把它并进设备表和池;第一拍探活够不着某台设备时,选路又把「自己」当中转候选,向自己的监听端点发了一次 /relay/open,服务端核实请求方就是本机后,反而把自己登记成一台在线设备。现在推送前剔掉收件人,接收、池合并和选路都排除自己的 id,两个中转接口也拒绝对自称本机的请求;启动时还会清掉旧版本写进盘里的自己。

macOS 上 Beyond Compare 找不到配置

BC5 的机器如果存在 ~/Library/Application Support/Beyond Compare 5,它只读这个目录;OrbDrop 之前一直写不带版本号的 Beyond Compare,于是「用 BeyondCompare 比较」对端共享项时报「配置文件找不到」。现在从 .app 的 CFBundleShortVersionString 取大版本号,优先匹配 Beyond Compare 5 / Beyond Compare 4 目录。真机 BC 5.2.6 的 profile 加载和读取已经实测通了,保存时的写回还没在真机上验收。BC 只在启动时登记 BCProfiles.xml,更新后第一次要重启一次 BC。

安卓界面能像网页一样双指缩放

根因是 WebView 的 supportZoom 默认 true,而页面 viewport 只写了 width=device-width, initial-scale=1。整页连同底栏、固定层一起被放大,布局不重排,边缘被裁还退不回去。现在显式关掉 supportZoom、builtInZoomControls 和 displayZoomControls,viewport 也补上 maximum-scale=1.0, user-scalable=no。系统字体、显示大小和无障碍放大不受影响。

输入过内容再清空,光标跑到占位文案右边

清空后 Chromium 会在框里留下一个 <br>,光标锚在它前面,而占位文案的 ::before 仍在行内流里,是那一行的第一个盒子。现在占位文案改成绝对定位,桌面和安卓两份样式同步,光标回到内容左缘。

都是小问题,但不修就一直在。

网络不给力的时候

办公室、宿舍、酒店的 Wi-Fi 经常开 AP 隔离:同一网段,mDNS 互相看得见,单播却被掐死。OrbDrop 会向一台自报允许中转的设备借一条单跳链路,中转方只盲搬字节,端到端 TLS 不变。中转默认开启,不想出这笔上行带宽,在设置里关掉就行。

跨网段是另一件事,mDNS 跨不过路由。每个网段手动加一台已配对的锚点,它会把各自完整的 mDNS 视野单播换过来;发现本身仍是单跳,不会扩成一张多跳网络。

AP 隔离中转与跨网段锚点

它还是不做什么

  • 不做公网穿透,也没有中继服务器。局域网内的中转和跨网段锚点,见上一节。
  • 不做虚拟盘符挂载,也不做目录镜像同步。共享是「链路存在期间的访问」,对端离线,访问就结束。
  • 文件推给离线设备会立即失败,只有消息会排队。
  • 目前只有 Windows / macOS 桌面端和 Android。Linux 桌面端、iOS 都还没有。
  • macOS 没有 CFAPI 等价物,对端共享项拖出前要整份暂存,大目录不是零等待。
  • macOS 包未签名、未公证,首次运行要右键「打开」;自更新要求应用放在可写位置,通常是 /Applications。
  • 它还是早期版本。前几个版本主要补安全和 macOS(SPAKE2 配对、8 位 SAS、群聊签名、资源上限),这次补双语和更新调度。核心路径能用,但请把它当「正在打磨的工具」,不是成熟商业软件。

下载和升级

  • Windows:windows_x64 单文件绿色版,双击即用
  • macOS 11+:macos_universal 的 dmg,首次安装用这个;未签名包首次运行右键「打开」
  • Android:android_arm64,绝大多数手机够用;拿不准就选 android_universal

具体文件名以 Release 页为准。macOS 的 .app.zip 和 update.json 是应用内自动更新用的,不用手动下。老版本用户可以直接在应用里检查更新,之后后台也会按上面的节奏自己查。

想自己核对下载:Windows 用 Get-FileHash .\<下载的 exe> -Algorithm SHA256,macOS 用 shasum -a 256 <下载的 app.zip>,和更新清单里的 sha256 对一下。


共享归共享,推送归推送。