产品设计

Raycast:找到文件之后,把下一步也放在手边

Raycast 是面向高频电脑用户的桌面启动器和命令工具,把查找应用、文件与执行相关动作连在一起,让日常操作可以逐渐形成稳定的键盘路径。

先尝试拆解问题、设计和实现,再展开思路,检查状态、边界与取舍,并设计验证用例。思路提供分析路径,不给出完整实现。

01
界面实现进阶

异步命令面板怎样只执行当前真正选中的动作?

练习场景

为自拟网页文件工具设计一个命令面板的状态模型。选中文件后,要异步加载它可用的动作。用户可能在加载期间换文件、关闭面板,或者正在用中文输入法搜索动作。只讨论客户端状态与事件路由,不写完整组件。

输入与术语

  1. 文件有稳定唯一 itemId。打开面板时创建递增的 sessionId;这是本次展开的身份,不是文件 ID。同一文件关掉再打开,也属于新会话;在面板内换选另一文件,也创建新会话并重新加载动作。动作有唯一 actionId 与 enabled,加载成功返回有序动作列表;失败返回错误。
  2. 网络层会把 {sessionId, itemId, requestId} 原样带回成功或失败事件。requestId 每次请求都不同,包括重试;返回顺序任意,取消请求只是尽力而为。题目不要求实现网络或服务端写入。
  3. 按键事件已由适配层规范为 {key, composing, repeat}。composing=true 表示本次按键仍在输入法组合输入中,repeat=true 表示按住按键产生的自动重复;本题只处理 Enter 与 Escape,不实现浏览器输入法兼容层。
  4. execute(itemId, actionId) 是同步交给外部执行器的命令请求,本题只验证派发目标与次数,不判断服务端是否完成。面板会在一次有效派发后关闭;用户重新打开后可再次执行同一动作,不做跨会话业务去重。

行为要求

  1. 仅接受当前打开会话、当前对象和最新请求三者都匹配的响应;过期成功和过期失败都不能改变状态。加载或失败时不允许执行。
  2. 动作过滤后通过 actionId 保持选择;原选择不可见或不可用时,选第一个可见且可用动作,没有则为空。重排不能按旧数组下标替换目标。
  3. 输入法组合中的 Enter/Escape 交回适配层,不执行也不关闭面板。非组合输入时,重复 Enter 不派发;普通 Enter 只有在就绪且当前选择仍可见可用时,才派发一次并关闭;Escape 关闭,迟到响应不得重新打开它。
  4. 不必实现 DOM、样式或完整代码。你需要说明状态、接受响应的条件、过滤后的选择修复与命令派发边界。

用这组事件推演

打开 A:会话1请求r1;改选 B:会话2请求r2。r2先成功,动作顺序为 [open, copy],用户选中copy;r1随后失败。把列表重排为 [copy, open] 后,先输入组合中的Enter,再输入普通Enter,最后收到一次重复Enter。写出每步状态与派发记录。

再推演:同一会话重试r3后,旧r2回调迟到;关闭B并重新打开B,旧会话成功迟到;过滤后所有动作不可用。

需要交付什么

提交状态转换表、上述序列的预期结果、至少三条始终成立的不变量,以及仅客户端能保证的边界。说明动作数为n时,一次筛选和选择修复的时间及空间成本;不要提供完整实现或逐行伪代码。

查看思路
  1. 把三个身份各自保存。 文件身份回答“操作谁”,会话身份回答“哪次展开”,请求身份回答“哪次加载或重试”。响应与当前三元组比较,关闭后的回调即使内容正确也不能把界面恢复出来。
  2. 选择绑定ID,派发前再检查。 排序改变位置不改变copy的身份。过滤或可用性变化后,以当前可见且可用列表修复选择;派发时仍检查,不能使用旧闭包里的下标或旧enabled值。
  3. 先处理输入上下文,再处理动作。 组合中的Enter不是执行指令,组合中的Escape也不属于本面板的关闭逻辑。正常Enter需排除repeat,并在一次同步事件处理中记录关闭状态再交出命令;后续重复事件看见关闭状态,不能再派发。
  4. 手工核对事件序列。 r2成功后copy保持选中;r1失败被忽略;重排后仍是copy。组合Enter不改变面板,普通Enter只派发一次execute(B, copy)并关闭,重复Enter无新命令。重试后的旧r2、重新打开B后的旧会话响应均忽略;没有可用动作则选择为空、不能执行。
  5. 限定保证范围。 一次会话最多派发一次不等于网络写入幂等;外部执行器的重试、未知结果不在本题内。若筛选需逐个检查n项,时间O(n);创建可见数组用O(n)额外空间,选择ID本身O(1)。输入法适配、渲染和网络成本单独计算。