下午三点,老板端着杯子从走廊那头过来。
我余光扫到,手已经先动了:表格、周报、邮件,三个窗口叠好。旁边的同事还在打字,我敲了一下桌子,他才反应过来。
后来我们四个人干脆约了个暗号:谁先看到,谁按一下「报信」。剩下的事,交给一个网页。
这个东西叫「摸了个鱼」。今天不写软文,写它背后几个不太一样的决定。
一、先承认:浏览器管不了别人的窗口
做这个需求,第一件要放弃的幻想是「远程控制」。
网页没有权限去切别人电脑上的窗口,也不该有。所以「全舱退避」不是我把你的屏幕按下去,而是:
- 每个人提前配置好自己的「工作软件」网址:表格、周报、邮件、代码仓库,各自选各自的
- 任何一个人按下报信,服务端广播一个事件
- 其他客户端收到后,把自己当前的标签页切到自己配置的地址
所以它是四次本地动作,被同一个事件触发。不是远程控制,是同步。
这也解释了它为什么不用装客户端:手机、平板、公司的旧电脑,浏览器打开就能用。代价是它只能在浏览器里切,管不了你本机的其他软件。
有人问为什么不做 Chrome 插件。插件确实能更稳地接管标签页,但安装本身就是一道坎,而且手机上用不了。先让四个人都能用,比做得更彻底重要。
二、为什么是「小舱」,不是群
一开始我们也拉了个群,后来发现不对。
群是开放的、会留记录的、可以随便拉人的。有些话不是不能说,是不想留下痕迹。所以产品做成了「小舱」:
- 一个舱最多四个人,靠邀请进去
- 不用真名,都是代号:河豚、水母、章鱼、海星
- 名单只在舱内可见,没有公开主页,也翻不到旧记录
- 舱里的消息保留 24 小时,广场内容保留 72 小时,到点自动焚毁
这些不是道德设计,是降低使用成本。你知道它不会留下来,才敢说那句「老板又来了」。
三、报信的关键不是「发出去」,是「同时」
第一版我们用的是普通消息,结果发现没用:消息发出去,别人未必在看;等看到,老板已经走到了。
第二版换成 WebSocket 广播。一条报信事件很小,服务端只负责把它扇出给舱内的其他三个人。客户端要做三件事:
- 去重:每条事件带 id,断线重连后不能重复触发
- 冷却:60 秒内只响应一次,不然一天能被吓八次
- 可取消:按下后 3 秒内可以撤回,避免误报把全舱切走
核心逻辑大概长这样,省略了重连和状态同步:
socket.on('report', (event) => {
if (seen.has(event.id)) return;
seen.add(event.id);
if (Date.now() < cooldownUntil) return;
cooldownUntil = Date.now() + 60_000;
// 每个人切到自己配置的工作软件
location.replace(workUrl);
});
真正难的不是这个回调,是「同时」:网络抖动、后台标签页被节流、有人刚锁屏。我们把目标定在 500ms 内完成绝大多数切换,剩下的交给重连和补偿。做不到 100%,但比在群里发一句「老板来了」靠谱。
四、隐私不是功能,是约束
「不制造证据」这句话听起来像文案,落到实现上是一堆约束:
- 舱内消息端到端加密,服务端只保存密文,看不到明文
- 身份在本机生成,不注册也能用;换设备时再用邮箱把身份接回来
- 到期内容由服务端清理,客户端也会校验过期,过期就打不开
- 不做公开主页,不做搜索索引,不做成员列表的对外展示
服务端能给的,只有它本来就有的东西。它没有明文,也就交不出明文。
五、那些很小的动作
真正让人愿意留下来的,往往不是「报信」这种大动作,是几个很小的东西。
八个图章:接水、蹲坑、下楼、咖啡、放空、望风、午饭、耳机。一点就发,不用组织语言。你不需要解释「我为什么不在工位」,同事也不需要追问。
还有一个待办:摸鱼时突然想起的事,顺手记下来,到点会有一只河豚叫你。换台电脑用同一个身份登录,待办还在。
六、它不适合谁
如果你要的是团队协作、项目管理、知识库,它不合适。它只解决工位上那点默契。
它也不能绕过公司的监控,更不该拿来干违规的事。它只是把「有人来了」这件事,从一句可能被截图的话,变成一个只持续几秒的动作。
七、地址
它叫「摸了个鱼」,网址是 try + moyu:
- try = 试一试
- moyu = 摸了个鱼
连起来就是:试一试,摸了个鱼。
网页版:www.trymoyu.com 不用注册,现在免费,没有广告,也没有会员墙。
如果你也有几个工位搭子,可以建个舱试试。
别在公司大群里发。