// 命令面板(⌘K)纯逻辑:命令清单组装 + 模糊过滤 + 键盘选择状态机。 // 对齐 UX §7(快速导航/动作)。纯逻辑,便于 node 环境单测(不依赖 DOM)。 export type CommandKind = "navigate" | "action"; export interface Command { id: string; // 展示标题(如「写本章」「跳转伏笔」)。 title: string; // 用于匹配的关键词(拼音/别名),与 title 一起参与过滤。 keywords: string[]; kind: CommandKind; // navigate:跳转路径;action:交由调用方按 id 分派。 href?: string; group: string; } // 项目内可用命令(projectId 已知)。导航项给 href;动作项(生成角色等)给 id。 export function projectCommands(projectId: string): Command[] { const p = `/projects/${projectId}`; return [ { id: "nav-write", title: "写本章", keywords: ["write", "写作", "工作台", "起草"], kind: "navigate", href: `${p}/write`, group: "导航", }, { id: "nav-review", title: "审稿", keywords: ["review", "质检", "冲突", "裁决"], kind: "navigate", href: `${p}/review`, group: "导航", }, { id: "nav-outline", title: "大纲", keywords: ["outline", "节拍", "beats"], kind: "navigate", href: `${p}/outline`, group: "导航", }, { id: "nav-foreshadow", title: "跳转伏笔", keywords: ["foreshadow", "伏笔", "看板", "线索"], kind: "navigate", href: `${p}/foreshadow`, group: "导航", }, { id: "nav-style", title: "文风", keywords: ["style", "文风", "指纹", "漂移"], kind: "navigate", href: `${p}/style`, group: "导航", }, { id: "nav-codex", title: "搜设定", keywords: ["codex", "设定库", "人物", "世界观", "角色"], kind: "navigate", href: `${p}/codex`, group: "导航", }, { id: "nav-rules", title: "规则", keywords: ["rules", "规则", "硬规则"], kind: "navigate", href: `${p}/rules`, group: "导航", }, { id: "nav-skills", title: "技能库", keywords: ["skills", "技能", "注册表"], kind: "navigate", href: `${p}/skills`, group: "导航", }, { id: "action-gen-character", title: "生成角色", keywords: ["character", "角色", "生成器", "群像"], kind: "action", href: `${p}/codex?gen=character`, group: "动作", }, { id: "action-gen-world", title: "生成世界观", keywords: ["world", "世界观", "设定", "设计器"], kind: "action", href: `${p}/codex?gen=world`, group: "动作", }, ]; } // 全局命令(无 projectId 时;如作品库/设置)。 export function globalCommands(): Command[] { return [ { id: "nav-home", title: "作品库", keywords: ["home", "作品", "library", "projects"], kind: "navigate", href: "/", group: "导航", }, { id: "nav-settings", title: "设置", keywords: ["settings", "设置", "凭据", "provider", "档位"], kind: "navigate", href: "/settings/providers", group: "导航", }, ]; } // 模糊过滤:空查询返回全部;否则在 title + keywords 上做大小写无关子串匹配(保持原顺序)。 export function filterCommands( commands: readonly Command[], query: string, ): Command[] { const q = query.trim().toLowerCase(); if (!q) return [...commands]; return commands.filter((cmd) => { if (cmd.title.toLowerCase().includes(q)) return true; return cmd.keywords.some((kw) => kw.toLowerCase().includes(q)); }); } // 键盘高亮索引在 [0, len) 内循环(空列表归 0)。 export function clampHighlight(index: number, length: number): number { if (length <= 0) return 0; const wrapped = index % length; return wrapped < 0 ? wrapped + length : wrapped; } // 上/下方向移动高亮(循环)。 export function moveHighlight( index: number, delta: number, length: number, ): number { return clampHighlight(index + delta, length); }