Skip to content

Feature: Domain-centric Command Palette (Cmd+K cross-profile lookup) #124

Description

@flyhigher139

背景

mHost 是 profile-centric 组织(每个 profile 一组 rules,用户手动切换)。但开发者的真实心智模型往往是「以域名为中心」—— 我关心的是"现在 api.local 解析到什么",而不是"我启用了哪个 profile"。

这种 mismatch 体现在几个常见痛点上:

  1. 多个 profile 覆盖同一个域名:5 个 enabled profiles 里有两个都管 api.local,到底哪个 wins?
  2. profile 之间互相覆盖:启用了 profile A 然后 profile B,发现 B 把 A 的某条规则挤掉了 —— 用户要肉眼比对两个 profile 的内容才能发现
  3. DNS mode 混合 hosts mode:hosts file 和 DNS proxy 各自有规则,最终生效的优先级不直观
  4. 系统 hosts 没单独编辑入口:发现 system hosts 里有问题得新建 profile override,没有"看一眼现在的全景"的能力

目标

加一个 Cmd+K command palette,作为产品的 command center:

核心查询:域名查找

输入 api.local → 立即显示所有匹配规则,按来源优先级排序:

api.local  →  192.168.1.100       WIN      [profile: dev-local, line 3, enabled]
api.local  →  10.0.0.5            shadowed [profile: staging, line 12, enabled]

每个匹配项展示:

  • 域名 → IP(高亮域名)
  • 来源:profile name + line number,或者 system hosts,或者 DNS proxy rule
  • 状态:enabled / disabled(disabled 的灰显)
  • WIN 标记:最终生效的那个,加 accent 颜色

跳转

点击匹配项(或 Enter 选中)→ 跳到对应 profile 视图,滚动到该行,行号 + 当前行高亮已经在 RuleEditor 里有了。

额外 commands

Cmd+K 不只是查域名,还能做:

  • Enable profile X —— 不切到 Profiles 页面直接启用
  • Disable profile X
  • Create new profile(带模板选择)
  • Apply current profile
  • Open settings
  • Switch to DNS / Hosts mode
  • Import from URL

模糊匹配 + 历史

  • 子串匹配 + 模糊(fzf-like)
  • 最近 5 次查询历史(回车直接复用)

实现

Backend(Rust)

新 IPC command:

#[tauri::command]
async fn query_domain(
    domain: String,
    state: tauri::State<'_, AppState>,
) -> Result<QueryResult, String>

struct QueryResult {
    matches: Vec<RuleMatch>,
    winner: Option<RuleMatch>,
    source_priority: Vec<Source>,
}

enum Source {
    SystemHosts { line: usize },
    Profile { id: String, name: String, line: usize, enabled: bool },
    DnsRule { profile_id: String, line: usize },
}

后端聚合:

  • mhost-hosts/etc/hosts 当前内容
  • mhost-storage 读所有 enabled profiles 的 rules
  • mhost-dns 读 DNS proxy 当前规则集

优先级规则:

  1. mhost-dns DNS proxy 规则(如果 DNS mode 启用)> 一切
  2. profile rules 按 profile 顺序 first-wins(已有逻辑)
  3. system hosts 作为兜底

Frontend(React)

新组件 src/components/CommandPalette.tsx

  • Portal 渲染到 body 顶层
  • 全局 modal overlay + 输入框 + 滚动列表
  • 键盘导航:↑↓ 选,Enter 执行,Esc 关闭
  • debounce 输入 100ms 触发 IPC

新 hook src/hooks/useGlobalKeybindings.ts

  • 注册 Cmd+K / Ctrl+K 全局监听
  • 后续可扩展到其他快捷键

需要在 src/components/Layout.tsx 把 CommandPalette 挂在最外层,确保任何路由下都能唤起。

验收

  • Cmd+K(或 Ctrl+K on Linux/Windows)从任何路由任何页面唤起 palette
  • 输入域名后 ≤ 200ms 显示匹配结果
  • 每个匹配显示来源 + line + enabled 状态
  • WIN 标记用 accent 颜色清晰区分
  • Enter 选中跳转对应 profile,滚到对应行(RuleEditor line-highlight 自动触发)
  • Esc 关闭
  • 模糊匹配:输入 apiloc 能匹配 api.local
  • commands 列表:profile enable / disable / create / apply / settings 都能用 palette 触发
  • 最近 5 次查询历史(localStorage 持久化)
  • 648 测试继续通过,加新测试:command match ranking + jump navigation

优先级

长期(4-8 周)。涉及:

  • 新 Rust IPC + 后端聚合层
  • 新 frontend command palette 组件 + 全局快捷键
  • 测试覆盖(rank 算法 + 跳转逻辑 + 历史持久化)

单一最重要的功能:域名查询。一旦有,用户会天天用。是把 mHost 从「工具」升级到「专业工具」的关键动作。

衍生 issue(实施时拆)

  • 后端 query_domain 实现 + 优先级规则文档化
  • frontend CommandPalette 组件 + 快捷键
  • commands(enable / disable / apply)通过 palette 的执行通道
  • 历史持久化

不在范围内(明确边界)

  • 不重做 hosts mode / DNS mode 优先级(保持现有逻辑)
  • 不引入 fuzzy search 库如 fzf —— 自己写简单实现(输入框小,匹配逻辑 ≤ 50 行)
  • 不做 voice / natural language 输入
  • 不动 profile-centric 的数据模型(profile 仍然是 source of truth,command palette 只是查询视图)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions