{"owner":"graphif","repo":"project-graph","hasSkills":true,"totalSkillsCount":38,"totalTokensCount":29307,"categories":["anthropic-skill","root-instruction","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"project-graph":{"command":"npx","args":["-y","@modelcontextprotocol/server-project-graph"]}}},"found":[".agents/skills/suggest-lucide-icons/SKILL.md",".agents/skills/ui/SKILL.md",".agents/skills/type-check/SKILL.md",".agents/skills/create-setting-item/SKILL.md",".agents/skills/create-keybind/SKILL.md",".agents/skills/shadcn/SKILL.md",".trae/skills/manage-translations/SKILL.md",".trae/skills/create-setting-item/SKILL.md","packages/shapes/project.json","AGENTS.md",".trae/skills/create-keybind/SKILL.md","packages/shapes/package.json","packages/shapes/tsconfig.json","packages/serializer/package.json","packages/serializer/project.json","packages/serializer/README.md","packages/serializer/tsconfig.json","packages/extprg-types/package.json","packages/extprg-types/tsconfig.json","packages/extprg/package.json","packages/extprg/pnpm-lock.yaml","packages/extprg/tsconfig.json","packages/data-structures/package.json","packages/data-structures/project.json","packages/data-structures/tsconfig.json","packages/create-extprg/package.json","packages/create-extprg/tsconfig.json",".agents/skills/suggest-lucide-icons/changelog.md",".agents/skills/shadcn/cli.md",".agents/skills/shadcn/customization.md",".agents/skills/shadcn/mcp.md",".agents/skills/shadcn/evals/evals.json",".agents/skills/shadcn/rules/base-vs-radix.md",".agents/skills/shadcn/rules/composition.md",".agents/skills/shadcn/rules/forms.md",".agents/skills/shadcn/rules/icons.md",".agents/skills/shadcn/rules/styling.md","app/src/core/service/feedbackService/effectEngine/mathTools/README.md"],"skills":{".agents/skills/suggest-lucide-icons/SKILL.md":"---\nname: suggest-lucide-icons\ndescription: \"Suggest relevant Lucide icons for concepts or UI placements. Searches lucide.dev to find actual icons that symbolize ideas or fit design contexts. Use when needing icons for UI work, documentation, or symbolic representation.\"\nmodel: sonnet, inherit\nmetadata:\n  author: nweii\n  version: \"1.2.0\"\n---\n\n# Suggest Lucide Icons\n\nSuggest the most relevant icons from the [Lucide open source icon pack](https://lucide.dev) to symbolize a concept or fit specific UI placements. I am skilled in symbolic interpretation and mental associations across culture, symbology, science, and design.\n\n## Input\n\nProvide one or both:\n\n- **Concept**: The idea, action, or meaning to represent\n- **Screenshot**: UI context showing where icons are needed\n\n## Naming conventions\n\nIcon names follow strict rules — apply these when generating candidates:\n\n- **kebab-case**: `arrow-up` not `Arrow Up`\n- **International English**: `color` not `colour`\n- **Group variants**: `<group>-<variant>` — e.g. `badge-plus` is based on `badge`\n- **Multiple elements, different sizes**: list largest first — `circle-person` if circle is bigger\n- **Element with modifier**: `[element]-[modifier]` — `circle-dashed` not `dashed-circle`; combined: `circle-dashed-heart-broken`\n\n## Process\n\n1. **Brainstorm associations**\n   - Key ideas and visual metaphors related to the concept\n   - Context clues from screenshot if provided\n   - Generate 4–6 candidate icon names in kebab-case (e.g. `arrow-right`, `circle-check`)\n\n2. **Verify candidates**\n   - For each candidate, fetch: `https://unpkg.com/lucide-static@latest/icons/[icon-name].svg`\n   - Use WebFetch or follow redirects (`curl -L`) — unpkg issues a 302 before the final response\n   - SVG content in the response means the icon exists; a 404 means it doesn't\n   - Discard any that don't exist; try alternate names if needed to reach 3 confirmed icons\n\n3. **Present 3 confirmed candidates**\n   - Icon name\n   - Why it fits (symbolic meaning, visual clarity, context appropriateness)\n\n4. **Recommend best choice**\n   - Single strongest option\n   - Rationale for recommendation\n\n## Guidelines\n\n- Never suggest an icon without confirming it via the unpkg URL\n- Never suggest made-up icon names\n- If fewer than 3 candidates survive verification, brainstorm more before giving up\n- If no good matches exist after thorough searching, say so\n- For screenshots, tailor to specific design context\n- Provide a distinct recommendation for each icon needed\n- Ready for multiple feedback rounds to refine suggestions\n\n## Output Format\n\n**Brainstorm**: [Key associations and metaphors]\n\n**Candidate Icons**:\n\n1. **icon-name** — Explanation of fit\n2. **icon-name** — Explanation of fit\n3. **icon-name** — Explanation of fit\n\n**Recommendation**: **icon-name** — Why this is the strongest choice for [context]\n",".agents/skills/ui/SKILL.md":"---\nname: ui\ndescription: 建立于 shadcn/ui 之上的附加 UI 组件\n---\n\n# ui\n\n## `Dialog` (`app/src/components/ui/dialog.tsx`)\n\n- `Dialog.confirm`: 显示一个确认对话框，返回一个 Promise，用户点击确认时 resolve，点击取消时 reject。\n- `Dialog.input`: 显示一个输入对话框，返回一个 Promise，用户输入内容并点击确认时 resolve，点击取消时 reject。\n- `Dialog.copy`: 显示一个复制对话框，返回一个 Promise，用户关闭对话框时 resolve。\n- `Dialog.buttons`: 显示一个带有自定义按钮的对话框，返回一个 Promise，用户点击任意按钮时 resolve，参数为被点击按钮的 `id`。\n\n## `toast()` (`import { toast } from \"sonner\"`)\n\n- `toast`: 显示一个通用的、info 类型的 toast 消息，参数为消息内容，尽量不要使用此函数，推荐使用下面的 `toast.success()` 等方法。\n- `toast.success`: 显示一个成功类型的 toast 消息，参数为消息内容。\n- `toast.error`: 显示一个错误类型的 toast 消息，参数为消息内容。\n- `toast.warning`: 显示一个警告类型的 toast 消息，参数为消息内容。\n- `toast.promise`: 见下面的示例代码。\n\n```ts\nawait toast\n  .promise(\n    async () => {\n      // 异步操作\n    },\n    {\n      loading: \"promise进行中的提示\",\n      success: \"resolve后的提示\",\n      error: \"reject后的提示\",\n    },\n  )\n  // unwrap方法会直接把传入的promise返回出来\n  .unwrap();\n```\n",".agents/skills/type-check/SKILL.md":"---\nname: type-check\ndescription: 运行 TypeScript 类型检查\n---\n\n# type-check\n\n运行以下命令来执行 TypeScript 类型检查：\n\n```bash\npnpm --filter @graphif/project-graph type-check\n```\n",".agents/skills/create-setting-item/SKILL.md":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 接地气繁体中文\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n",".agents/skills/create-keybind/SKILL.md":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n- 印度尼西亚语：`app/src/locales/id.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = KeyBindsUI.getUIKeyBind(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n",".agents/skills/shadcn/SKILL.md":"---\nname: shadcn\ndescription: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for \"shadcn init\", \"create an app with --preset\", or \"switch to --preset\".\nuser-invocable: false\nallowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)\n---\n\n# shadcn/ui\n\nA framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.\n\n> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.\n\n## Current Project Context\n\n```json\n!`npx shadcn@latest info --json`\n```\n\nThe JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.\n\n## Principles\n\n1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.\n2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.\n3. **Use built-in variants before custom styles.** `variant=\"outline\"`, `size=\"sm\"`, etc.\n4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.\n\n## Critical Rules\n\nThese rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.\n\n### Styling & Tailwind → [styling.md](./rules/styling.md)\n\n- **`className` for layout, not styling.** Never override component colors or typography.\n- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.\n- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.\n- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.\n- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).\n- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.\n- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.\n\n### Forms & Inputs → [forms.md](./rules/forms.md)\n\n- **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.\n- **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.\n- **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**\n- **Option sets (2–7 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.\n- **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.\n- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.\n\n### Component Structure → [composition.md](./rules/composition.md)\n\n- **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.\n- **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)\n- **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className=\"sr-only\"` if visually hidden.\n- **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.\n- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.\n- **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.\n- **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.\n\n### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)\n\n- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.\n- **Callouts use `Alert`.** Don't build custom styled divs.\n- **Empty states use `Empty`.** Don't build custom empty state markup.\n- **Toast via `sonner`.** Use `toast()` from `sonner`.\n- **Use `Separator`** instead of `<hr>` or `<div className=\"border-t\">`.\n- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.\n- **Use `Badge`** instead of custom styled spans.\n\n### Icons → [icons.md](./rules/icons.md)\n\n- **Icons in `Button` use `data-icon`.** `data-icon=\"inline-start\"` or `data-icon=\"inline-end\"` on the icon.\n- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.\n- **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.\n\n### CLI\n\n- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest apply --preset <code>` for existing projects, or `npx shadcn@latest init --preset <code>` when initializing.\n\n## Key Patterns\n\nThese are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.\n\n```tsx\n// Form layout: FieldGroup + Field, not div + Label.\n<FieldGroup>\n  <Field>\n    <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n    <Input id=\"email\" />\n  </Field>\n</FieldGroup>\n\n// Validation: data-invalid on Field, aria-invalid on the control.\n<Field data-invalid>\n  <FieldLabel>Email</FieldLabel>\n  <Input aria-invalid />\n  <FieldDescription>Invalid email.</FieldDescription>\n</Field>\n\n// Icons in buttons: data-icon, no sizing classes.\n<Button>\n  <SearchIcon data-icon=\"inline-start\" />\n  Search\n</Button>\n\n// Spacing: gap-*, not space-y-*.\n<div className=\"flex flex-col gap-4\">  // correct\n<div className=\"space-y-4\">           // wrong\n\n// Equal dimensions: size-*, not w-* h-*.\n<Avatar className=\"size-10\">   // correct\n<Avatar className=\"w-10 h-10\"> // wrong\n\n// Status colors: Badge variants or semantic tokens, not raw colors.\n<Badge variant=\"secondary\">+20.1%</Badge>    // correct\n<span className=\"text-emerald-600\">+20.1%</span> // wrong\n```\n\n## Component Selection\n\n| Need                       | Use                                                                                                 |\n| -------------------------- | --------------------------------------------------------------------------------------------------- |\n| Button/action              | `Button` with appropriate variant                                                                   |\n| Form inputs                | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |\n| Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem`                                                                   |\n| Data display               | `Table`, `Card`, `Badge`, `Avatar`                                                                  |\n| Navigation                 | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination`                                     |\n| Overlays                   | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation)       |\n| Feedback                   | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner`                                        |\n| Command palette            | `Command` inside `Dialog`                                                                           |\n| Charts                     | `Chart` (wraps Recharts)                                                                            |\n| Layout                     | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible`                          |\n| Empty states               | `Empty`                                                                                             |\n| Menus                      | `DropdownMenu`, `ContextMenu`, `Menubar`                                                            |\n| Tooltips/info              | `Tooltip`, `HoverCard`, `Popover`                                                                   |\n\n## Key Fields\n\nThe injected project context contains these key fields:\n\n- **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.\n- **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `\"use client\"` at the top of the file. Always reference this field when advising on the directive.\n- **`tailwindVersion`** → `\"v4\"` uses `@theme inline` blocks; `\"v3\"` uses `tailwind.config.js`.\n- **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.\n- **`style`** → component visual treatment (e.g. `nova`, `vega`).\n- **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.\n- **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.\n- **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.\n- **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).\n- **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).\n\nSee [cli.md — `info` command](./cli.md) for the full field reference.\n\n## Component Docs, Examples, and Usage\n\nRun `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.\n\n```bash\nnpx shadcn@latest docs button dialog select\n```\n\n**When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.\n\n## Workflow\n\n1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.\n2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.\n3. **Find components** — `npx shadcn@latest search`.\n4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.\n5. **Install or update** — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).\n6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.\n7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.\n8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says \"add a login block\" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.\n9. **Switching presets** — Ask the user first: **overwrite**, **partial**, **merge**, or **skip**?\n   - **Overwrite**: `npx shadcn@latest apply --preset <code>`. Overwrites detected components, fonts, and CSS variables.\n   - **Partial**: `npx shadcn@latest apply --preset <code> --only theme,font`. Updates only the selected preset parts without reinstalling UI components. Supported values are `theme` and `font`; comma-separated combinations are allowed. `icon` is intentionally not supported, because icon changes may require full component reinstall and transforms.\n   - **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.\n   - **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.\n   - **Important**: Always run preset commands inside the user's project directory. `apply` only works in an existing project with a `components.json` file. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.\n\n## Updating Components\n\nWhen the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**\n\n1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.\n2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.\n3. Decide per file based on the diff:\n   - No local changes → safe to overwrite.\n   - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.\n   - User says \"just update everything\" → use `--overwrite`, but confirm first.\n4. **Never use `--overwrite` without the user's explicit approval.**\n\n## Quick Reference\n\n```bash\n# Create a new project.\nnpx shadcn@latest init --name my-app --preset base-nova\nnpx shadcn@latest init --name my-app --preset a2r6bw --template vite\n\n# Create a monorepo project.\nnpx shadcn@latest init --name my-app --preset base-nova --monorepo\nnpx shadcn@latest init --name my-app --preset base-nova --template next --monorepo\n\n# Initialize existing project.\nnpx shadcn@latest init --preset base-nova\nnpx shadcn@latest init --defaults  # shortcut: --template=next --preset=nova (base style implied)\n\n# Apply a preset to an existing project.\nnpx shadcn@latest apply --preset a2r6bw\nnpx shadcn@latest apply a2r6bw\nnpx shadcn@latest apply --preset a2r6bw --only theme\nnpx shadcn@latest apply --preset a2r6bw --only font\nnpx shadcn@latest apply --preset a2r6bw --only theme,font\n\n# Add components.\nnpx shadcn@latest add button card dialog\nnpx shadcn@latest add @magicui/shimmer-button\nnpx shadcn@latest add --all\n\n# Preview changes before adding/updating.\nnpx shadcn@latest add button --dry-run\nnpx shadcn@latest add button --diff button.tsx\nnpx shadcn@latest add @acme/form --view button.tsx\n\n# Search registries.\nnpx shadcn@latest search @shadcn -q \"sidebar\"\nnpx shadcn@latest search @tailark -q \"stats\"\n\n# Get component docs and example URLs.\nnpx shadcn@latest docs button dialog select\n\n# View registry item details (for items not yet installed).\nnpx shadcn@latest view @shadcn/button\n```\n\n**Named presets:** `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`\n**Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)\n**Preset codes:** Version-prefixed base62 strings (e.g. `a2r6bw` or `b0`), from [ui.shadcn.com](https://ui.shadcn.com).\n\n## Detailed References\n\n- [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states\n- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading\n- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects\n- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index\n- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion\n- [cli.md](./cli.md) — Commands, flags, presets, templates\n- [customization.md](./customization.md) — Theming, CSS variables, extending components\n",".trae/skills/manage-translations/SKILL.md":"---\nname: manage-translations\ndescription: \"指导如何在 Project Graph 项目中管理多语言翻译。当需要添加新翻译、更新现有翻译或了解翻译系统结构时使用此技能。\"\n---\n\n# 管理翻译\n\n本技能指导如何在 Project Graph 项目中管理多语言翻译。\n\n## 翻译文件位置\n\n所有翻译文件位于 `app/src/locales/` 目录下：\n\n```\napp/src/locales/\n├── en.yml      # 英文\n├── id.yml      # 印度尼西亚语\n├── zh_CN.yml   # 简体中文\n├── zh_TW.yml   # 繁体中文\n├── zh_TWC.yml  # 接地气繁体中文\n├── ...         # 其他语言翻译文件\n└── README.md   # 翻译系统说明\n```\n\n## 查看当前支持的语言\n\n在添加或修改翻译前，先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件：\n\n```bash\nls app/src/locales/*.yml\n```\n",".trae/skills/create-setting-item/SKILL.md":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n> **注意：** 关于翻译管理的详细说明，请参考 `manage-translations` skill。\n\n在所有语言文件中添加翻译。首先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件，然后为每个语言文件添加翻译。\n\n**设置项的翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n","packages/shapes/project.json":"{\n  \"name\": \"@graphif/shapes\",\n  \"targets\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"cache\": true,\n      \"outputs\": [\"{projectRoot}/dist/**\"]\n    },\n    \"type-check\": {\n      \"cache\": true,\n      \"inputs\": [\"{projectRoot}/src/**/*.ts\"]\n    },\n    \"publish\": {\n      \"dependsOn\": [\"build\"],\n      \"cache\": false\n    }\n  }\n}\n","AGENTS.md":"## Project Background\n\nGithub Repository: `graphif/project-graph`\n\nProject Graph 是一款桌面级节点图绘制工具，用于头脑风暴、知识图谱、项目规划等场景的可视化思考。\n\n## Tech-stack\n\n- React (TypeScript) + Tauri (Rust)\n- Vite + pnpm (monorepo) + Nx\n- Canvas 2D\n- shadcn/ui + Tailwind CSS + 自研子窗口系统\n- Jotai\n\nRust 主要负责本地文件等系统能力；绝大部分业务逻辑在前端。\n\n## Structure\n\n### Application\n\n- Frontend Vite project: `/app`\n- Rust / Tauri: `/app/src-tauri`\n- UI components: `/app/src/components`\n- i18n locales: `/app/src/locales`\n- React state (Jotai 等): `/app/src/state.tsx` 及附近\n\n### `app/src/core`（核心业务）\n\n| 路径                              | 职责                                                                            |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `Project.tsx`                     | 单个工程/项目实例的入口与生命周期                                               |\n| `Tab*.ts(x)` / `TabWorkspace.tsx` | 标签页与工作区                                                                  |\n| `loadAllServices.tsx`             | 注册/加载全部服务                                                               |\n| `algorithm/`                      | 通用算法与几何工具                                                              |\n| `stage/`                          | 舞台：`Camera`、`Canvas`、舞台对象（`stageObject`）、舞台管理（`stageManager`） |\n| `render/`                         | 渲染：`canvas2d`、`svg`、`3d`、`domElement`                                     |\n| `service/`                        | 业务服务（见下）                                                                |\n| `extension/`                      | 扩展系统运行时与 API                                                            |\n| `fileSystemProvider/`             | 文件系统抽象（草稿/本地文件等）                                                 |\n| `interfaces/`                     | 核心接口（如 `Service`）                                                        |\n| `subWindowOpen*.ts`               | 子窗口打开方式                                                                  |\n\n`service/` 主要子目录：\n\n- `controlService/` — 输入控制、快捷键、框选、自动布局等\n- `dataFileService/` — 工程文件读写\n- `dataGenerateService/` — 数据生成\n- `dataManageService/` — 内容搜索、复制、AI、节点工具等\n- `feedbackService/` — 特效、音效、舞台样式、颜色\n- `Settings.tsx` 等 — 设置、主题、菜单、教程等全局服务\n\n### Packages（`/packages`）\n\n前端复用的开源/内部库，例如：\n\n- `@graphif/serializer` — 实例序列化\n- `@graphif/shapes` — 可序列化图形\n- `@graphif/data-structures` — 可序列化数据结构\n- `extprg` / `extprg-types` / `create-extprg` — 扩展工具链与类型\n\n### Agent skills\n\n具体工作流见 `.agents/skills/`：\n\n- `type-check` — TypeScript 类型检查（**改代码后用这个，不要 build**）\n- `create-keybind` — 新增/修改快捷键\n- `create-setting-item` — 新增/修改设置项\n- `shadcn` / `ui` — UI 组件\n- `suggest-lucide-icons` — 图标建议\n\n## Locales (`app/src/locales`)\n\n| 文件         | 说明                                                               |\n| ------------ | ------------------------------------------------------------------ |\n| `en.yml`     | 英文（手写维护）                                                   |\n| `zh_CN.yml`  | 简体中文（手写维护）                                               |\n| `zh_TW.yml`  | 普通繁体中文 — **由 `zh_CN.yml` 自动生成（OpenCC）**，**不要手改** |\n| `zh_TWC.yml` | 接地气繁体中文 — **手写维护，不是自动生成**                        |\n| `id.yml`     | 印尼语（手写维护）                                                 |\n\n新增文案时：改 `en.yml` / `zh_CN.yml`（及需要时的 `zh_TWC.yml`、`id.yml`），**不要编辑 `zh_TW.yml`**。\n\n## Coding guidelines\n\n- 正确性与清晰度优先；性能除非明确要求，否则次之。\n- 不要写组织性/总结性注释；仅在「为什么这样写」不直观时解释 why。\n- 优先在已有文件中实现功能；仅在新的逻辑组件时新建文件，避免拆成大量小文件。\n- 目录内使用 `something.tsx`，不要用单独的 `index.tsx`。\n- 错误处理（**前端**）：\n  - 不要静默吞掉错误：禁止 `catch {}` 或仅 `console.error` 后忽略\n  - 能不 catch 就不 catch，让调用方处理\n  - 需要忽略时用对话框提示用户（用户看不到控制台）\n  - 错误应向上传到 DOM（如 `window`），由 `ErrorHandler` 展示友好对话框\n  - 反例：`try { something() } catch (e) { console.error(e) }` → 直接 `something()`\n- 错误处理（**Rust / Tauri 命令**）：命令在运行中不能让进程因未捕获错误闪退，须在函数内妥善处理错误并返回给前端。\n- UI：优先复用 shadcn 与 `.agents/skills/ui` 中的约定（`Dialog`、`toast` 等）。\n- 快捷键 / 设置：分别遵循 `create-keybind`、`create-setting-item` skill。\n\n## Commands\n\n包管理：`pnpm`。常用：\n\n- 开发：`pnpm dev`\n- Lint：`pnpm lint` / `pnpm lint:fix`\n- 测试：`pnpm test`（vitest）\n- 类型检查：**使用 `type-check` skill**（`pnpm --filter @graphif/project-graph type-check`）\n\n**禁止** Agent 运行 `build` / `build:ci` / `build:no-tauri` / `tauri build` 等构建命令。验证改动用 type-check（及必要的 lint/test），不要 build。\n\n## Agent constraints\n\n- **依赖 API / 用法**：禁止用 shell 在 `node_modules` 里搜索、翻源码或类型定义。需要了解第三方库时，读取该项目的官方文档（优先 `llms.txt`，例如 `https://<pkg-docs>/llms.txt`），或使用已有 skill；不要 `grep` / `find` / `cat` `node_modules/**`。\n- **实现方式**：禁止用 `python`、`python3`、`node -e`、`node --eval`、内联 shell 脚本等做文本处理或批量改文件。应直接用读/写/编辑文件的工具（Read / Write / Edit 等）完成；需要多文件改动时逐个编辑，不要写临时脚本。\n- **构建**：禁止 build（见上）；验证用 type-check skill。\n\n## Commit Message\n\n使用 [Conventional Commits](https://www.conventionalcommits.org/)，例如：`feat: ...`、`fix: ...`、`refactor: ...`。\n",".trae/skills/create-keybind/SKILL.md":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = await KeyBindsUI.get(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n","packages/shapes/package.json":"{\n  \"name\": \"@graphif/shapes\",\n  \"description\": \"Serializable shapes\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"license\": \"MIT\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\",\n    \"access\": \"public\",\n    \"module\": \"dist/index.mjs\",\n    \"types\": \"dist/index.d.mts\",\n    \"exports\": {\n      \".\": {\n        \"types\": \"./dist/index.d.mts\",\n        \"import\": \"./dist/index.mjs\"\n      }\n    }\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"module\": \"src/index.ts\",\n  \"types\": \"src/index.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./src/index.ts\",\n      \"import\": \"./src/index.ts\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"type-check\": \"tsc\",\n    \"publish\": \"pnpm publish --no-git-checks\"\n  },\n  \"devDependencies\": {\n    \"tsdown\": \"catalog:\",\n    \"typescript\": \"catalog:\"\n  },\n  \"dependencies\": {\n    \"@graphif/data-structures\": \"workspace:*\",\n    \"@graphif/serializer\": \"workspace:*\",\n    \"unplugin-original-class-name\": \"catalog:\"\n  }\n}\n","packages/shapes/tsconfig.json":"# Shapes Skill\n\nPath: `packages/shapes/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/tsconfig.json)","packages/serializer/package.json":"# Serializer Skill\n\nPath: `packages/serializer/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/package.json)","packages/serializer/project.json":"# Serializer Skill\n\nPath: `packages/serializer/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/project.json)","packages/serializer/README.md":"# serializer Documentation\n\nPath: `packages/serializer/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/README.md)","packages/serializer/tsconfig.json":"# Serializer Skill\n\nPath: `packages/serializer/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/tsconfig.json)","packages/extprg-types/package.json":"# Extprg-types Skill\n\nPath: `packages/extprg-types/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/package.json)","packages/extprg-types/tsconfig.json":"# Extprg-types Skill\n\nPath: `packages/extprg-types/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/tsconfig.json)","packages/extprg/package.json":"# Extprg Skill\n\nPath: `packages/extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/package.json)","packages/extprg/pnpm-lock.yaml":"# Extprg Skill\n\nPath: `packages/extprg/pnpm-lock.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/pnpm-lock.yaml)","packages/extprg/tsconfig.json":"# Extprg Skill\n\nPath: `packages/extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/tsconfig.json)","packages/data-structures/package.json":"# Data-structures Skill\n\nPath: `packages/data-structures/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/package.json)","packages/data-structures/project.json":"# Data-structures Skill\n\nPath: `packages/data-structures/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/project.json)","packages/data-structures/tsconfig.json":"# Data-structures Skill\n\nPath: `packages/data-structures/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/tsconfig.json)","packages/create-extprg/package.json":"# Create-extprg Skill\n\nPath: `packages/create-extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/package.json)","packages/create-extprg/tsconfig.json":"# Create-extprg Skill\n\nPath: `packages/create-extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/tsconfig.json)",".agents/skills/suggest-lucide-icons/changelog.md":"# Suggest-lucide-icons Skill\n\nPath: `.agents/skills/suggest-lucide-icons/changelog.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/changelog.md)",".agents/skills/shadcn/cli.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/cli.md)",".agents/skills/shadcn/customization.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/customization.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/customization.md)",".agents/skills/shadcn/mcp.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/mcp.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/mcp.md)",".agents/skills/shadcn/evals/evals.json":"# Evals Skill\n\nPath: `.agents/skills/shadcn/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/evals/evals.json)",".agents/skills/shadcn/rules/base-vs-radix.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/base-vs-radix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/base-vs-radix.md)",".agents/skills/shadcn/rules/composition.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/composition.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/composition.md)",".agents/skills/shadcn/rules/forms.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/forms.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/forms.md)",".agents/skills/shadcn/rules/icons.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/icons.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/icons.md)",".agents/skills/shadcn/rules/styling.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/styling.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/styling.md)","app/src/core/service/feedbackService/effectEngine/mathTools/README.md":"# mathTools Documentation\n\nPath: `app/src/core/service/feedbackService/effectEngine/mathTools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/app/src/core/service/feedbackService/effectEngine/mathTools/README.md)"},"files":{".agents/skills/suggest-lucide-icons/SKILL.md":"---\nname: suggest-lucide-icons\ndescription: \"Suggest relevant Lucide icons for concepts or UI placements. Searches lucide.dev to find actual icons that symbolize ideas or fit design contexts. Use when needing icons for UI work, documentation, or symbolic representation.\"\nmodel: sonnet, inherit\nmetadata:\n  author: nweii\n  version: \"1.2.0\"\n---\n\n# Suggest Lucide Icons\n\nSuggest the most relevant icons from the [Lucide open source icon pack](https://lucide.dev) to symbolize a concept or fit specific UI placements. I am skilled in symbolic interpretation and mental associations across culture, symbology, science, and design.\n\n## Input\n\nProvide one or both:\n\n- **Concept**: The idea, action, or meaning to represent\n- **Screenshot**: UI context showing where icons are needed\n\n## Naming conventions\n\nIcon names follow strict rules — apply these when generating candidates:\n\n- **kebab-case**: `arrow-up` not `Arrow Up`\n- **International English**: `color` not `colour`\n- **Group variants**: `<group>-<variant>` — e.g. `badge-plus` is based on `badge`\n- **Multiple elements, different sizes**: list largest first — `circle-person` if circle is bigger\n- **Element with modifier**: `[element]-[modifier]` — `circle-dashed` not `dashed-circle`; combined: `circle-dashed-heart-broken`\n\n## Process\n\n1. **Brainstorm associations**\n   - Key ideas and visual metaphors related to the concept\n   - Context clues from screenshot if provided\n   - Generate 4–6 candidate icon names in kebab-case (e.g. `arrow-right`, `circle-check`)\n\n2. **Verify candidates**\n   - For each candidate, fetch: `https://unpkg.com/lucide-static@latest/icons/[icon-name].svg`\n   - Use WebFetch or follow redirects (`curl -L`) — unpkg issues a 302 before the final response\n   - SVG content in the response means the icon exists; a 404 means it doesn't\n   - Discard any that don't exist; try alternate names if needed to reach 3 confirmed icons\n\n3. **Present 3 confirmed candidates**\n   - Icon name\n   - Why it fits (symbolic meaning, visual clarity, context appropriateness)\n\n4. **Recommend best choice**\n   - Single strongest option\n   - Rationale for recommendation\n\n## Guidelines\n\n- Never suggest an icon without confirming it via the unpkg URL\n- Never suggest made-up icon names\n- If fewer than 3 candidates survive verification, brainstorm more before giving up\n- If no good matches exist after thorough searching, say so\n- For screenshots, tailor to specific design context\n- Provide a distinct recommendation for each icon needed\n- Ready for multiple feedback rounds to refine suggestions\n\n## Output Format\n\n**Brainstorm**: [Key associations and metaphors]\n\n**Candidate Icons**:\n\n1. **icon-name** — Explanation of fit\n2. **icon-name** — Explanation of fit\n3. **icon-name** — Explanation of fit\n\n**Recommendation**: **icon-name** — Why this is the strongest choice for [context]\n",".agents/skills/ui/SKILL.md":"---\nname: ui\ndescription: 建立于 shadcn/ui 之上的附加 UI 组件\n---\n\n# ui\n\n## `Dialog` (`app/src/components/ui/dialog.tsx`)\n\n- `Dialog.confirm`: 显示一个确认对话框，返回一个 Promise，用户点击确认时 resolve，点击取消时 reject。\n- `Dialog.input`: 显示一个输入对话框，返回一个 Promise，用户输入内容并点击确认时 resolve，点击取消时 reject。\n- `Dialog.copy`: 显示一个复制对话框，返回一个 Promise，用户关闭对话框时 resolve。\n- `Dialog.buttons`: 显示一个带有自定义按钮的对话框，返回一个 Promise，用户点击任意按钮时 resolve，参数为被点击按钮的 `id`。\n\n## `toast()` (`import { toast } from \"sonner\"`)\n\n- `toast`: 显示一个通用的、info 类型的 toast 消息，参数为消息内容，尽量不要使用此函数，推荐使用下面的 `toast.success()` 等方法。\n- `toast.success`: 显示一个成功类型的 toast 消息，参数为消息内容。\n- `toast.error`: 显示一个错误类型的 toast 消息，参数为消息内容。\n- `toast.warning`: 显示一个警告类型的 toast 消息，参数为消息内容。\n- `toast.promise`: 见下面的示例代码。\n\n```ts\nawait toast\n  .promise(\n    async () => {\n      // 异步操作\n    },\n    {\n      loading: \"promise进行中的提示\",\n      success: \"resolve后的提示\",\n      error: \"reject后的提示\",\n    },\n  )\n  // unwrap方法会直接把传入的promise返回出来\n  .unwrap();\n```\n",".agents/skills/type-check/SKILL.md":"---\nname: type-check\ndescription: 运行 TypeScript 类型检查\n---\n\n# type-check\n\n运行以下命令来执行 TypeScript 类型检查：\n\n```bash\npnpm --filter @graphif/project-graph type-check\n```\n",".agents/skills/create-setting-item/SKILL.md":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 接地气繁体中文\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n",".agents/skills/create-keybind/SKILL.md":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n- 印度尼西亚语：`app/src/locales/id.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = KeyBindsUI.getUIKeyBind(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n",".agents/skills/shadcn/SKILL.md":"---\nname: shadcn\ndescription: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for \"shadcn init\", \"create an app with --preset\", or \"switch to --preset\".\nuser-invocable: false\nallowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)\n---\n\n# shadcn/ui\n\nA framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.\n\n> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.\n\n## Current Project Context\n\n```json\n!`npx shadcn@latest info --json`\n```\n\nThe JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.\n\n## Principles\n\n1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.\n2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.\n3. **Use built-in variants before custom styles.** `variant=\"outline\"`, `size=\"sm\"`, etc.\n4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.\n\n## Critical Rules\n\nThese rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.\n\n### Styling & Tailwind → [styling.md](./rules/styling.md)\n\n- **`className` for layout, not styling.** Never override component colors or typography.\n- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.\n- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.\n- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.\n- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).\n- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.\n- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.\n\n### Forms & Inputs → [forms.md](./rules/forms.md)\n\n- **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.\n- **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.\n- **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**\n- **Option sets (2–7 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.\n- **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.\n- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.\n\n### Component Structure → [composition.md](./rules/composition.md)\n\n- **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.\n- **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)\n- **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className=\"sr-only\"` if visually hidden.\n- **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.\n- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.\n- **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.\n- **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.\n\n### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)\n\n- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.\n- **Callouts use `Alert`.** Don't build custom styled divs.\n- **Empty states use `Empty`.** Don't build custom empty state markup.\n- **Toast via `sonner`.** Use `toast()` from `sonner`.\n- **Use `Separator`** instead of `<hr>` or `<div className=\"border-t\">`.\n- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.\n- **Use `Badge`** instead of custom styled spans.\n\n### Icons → [icons.md](./rules/icons.md)\n\n- **Icons in `Button` use `data-icon`.** `data-icon=\"inline-start\"` or `data-icon=\"inline-end\"` on the icon.\n- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.\n- **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.\n\n### CLI\n\n- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest apply --preset <code>` for existing projects, or `npx shadcn@latest init --preset <code>` when initializing.\n\n## Key Patterns\n\nThese are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.\n\n```tsx\n// Form layout: FieldGroup + Field, not div + Label.\n<FieldGroup>\n  <Field>\n    <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n    <Input id=\"email\" />\n  </Field>\n</FieldGroup>\n\n// Validation: data-invalid on Field, aria-invalid on the control.\n<Field data-invalid>\n  <FieldLabel>Email</FieldLabel>\n  <Input aria-invalid />\n  <FieldDescription>Invalid email.</FieldDescription>\n</Field>\n\n// Icons in buttons: data-icon, no sizing classes.\n<Button>\n  <SearchIcon data-icon=\"inline-start\" />\n  Search\n</Button>\n\n// Spacing: gap-*, not space-y-*.\n<div className=\"flex flex-col gap-4\">  // correct\n<div className=\"space-y-4\">           // wrong\n\n// Equal dimensions: size-*, not w-* h-*.\n<Avatar className=\"size-10\">   // correct\n<Avatar className=\"w-10 h-10\"> // wrong\n\n// Status colors: Badge variants or semantic tokens, not raw colors.\n<Badge variant=\"secondary\">+20.1%</Badge>    // correct\n<span className=\"text-emerald-600\">+20.1%</span> // wrong\n```\n\n## Component Selection\n\n| Need                       | Use                                                                                                 |\n| -------------------------- | --------------------------------------------------------------------------------------------------- |\n| Button/action              | `Button` with appropriate variant                                                                   |\n| Form inputs                | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |\n| Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem`                                                                   |\n| Data display               | `Table`, `Card`, `Badge`, `Avatar`                                                                  |\n| Navigation                 | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination`                                     |\n| Overlays                   | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation)       |\n| Feedback                   | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner`                                        |\n| Command palette            | `Command` inside `Dialog`                                                                           |\n| Charts                     | `Chart` (wraps Recharts)                                                                            |\n| Layout                     | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible`                          |\n| Empty states               | `Empty`                                                                                             |\n| Menus                      | `DropdownMenu`, `ContextMenu`, `Menubar`                                                            |\n| Tooltips/info              | `Tooltip`, `HoverCard`, `Popover`                                                                   |\n\n## Key Fields\n\nThe injected project context contains these key fields:\n\n- **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.\n- **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `\"use client\"` at the top of the file. Always reference this field when advising on the directive.\n- **`tailwindVersion`** → `\"v4\"` uses `@theme inline` blocks; `\"v3\"` uses `tailwind.config.js`.\n- **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.\n- **`style`** → component visual treatment (e.g. `nova`, `vega`).\n- **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.\n- **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.\n- **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.\n- **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).\n- **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).\n\nSee [cli.md — `info` command](./cli.md) for the full field reference.\n\n## Component Docs, Examples, and Usage\n\nRun `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.\n\n```bash\nnpx shadcn@latest docs button dialog select\n```\n\n**When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.\n\n## Workflow\n\n1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.\n2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.\n3. **Find components** — `npx shadcn@latest search`.\n4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.\n5. **Install or update** — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).\n6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.\n7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.\n8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says \"add a login block\" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.\n9. **Switching presets** — Ask the user first: **overwrite**, **partial**, **merge**, or **skip**?\n   - **Overwrite**: `npx shadcn@latest apply --preset <code>`. Overwrites detected components, fonts, and CSS variables.\n   - **Partial**: `npx shadcn@latest apply --preset <code> --only theme,font`. Updates only the selected preset parts without reinstalling UI components. Supported values are `theme` and `font`; comma-separated combinations are allowed. `icon` is intentionally not supported, because icon changes may require full component reinstall and transforms.\n   - **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.\n   - **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.\n   - **Important**: Always run preset commands inside the user's project directory. `apply` only works in an existing project with a `components.json` file. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.\n\n## Updating Components\n\nWhen the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**\n\n1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.\n2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.\n3. Decide per file based on the diff:\n   - No local changes → safe to overwrite.\n   - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.\n   - User says \"just update everything\" → use `--overwrite`, but confirm first.\n4. **Never use `--overwrite` without the user's explicit approval.**\n\n## Quick Reference\n\n```bash\n# Create a new project.\nnpx shadcn@latest init --name my-app --preset base-nova\nnpx shadcn@latest init --name my-app --preset a2r6bw --template vite\n\n# Create a monorepo project.\nnpx shadcn@latest init --name my-app --preset base-nova --monorepo\nnpx shadcn@latest init --name my-app --preset base-nova --template next --monorepo\n\n# Initialize existing project.\nnpx shadcn@latest init --preset base-nova\nnpx shadcn@latest init --defaults  # shortcut: --template=next --preset=nova (base style implied)\n\n# Apply a preset to an existing project.\nnpx shadcn@latest apply --preset a2r6bw\nnpx shadcn@latest apply a2r6bw\nnpx shadcn@latest apply --preset a2r6bw --only theme\nnpx shadcn@latest apply --preset a2r6bw --only font\nnpx shadcn@latest apply --preset a2r6bw --only theme,font\n\n# Add components.\nnpx shadcn@latest add button card dialog\nnpx shadcn@latest add @magicui/shimmer-button\nnpx shadcn@latest add --all\n\n# Preview changes before adding/updating.\nnpx shadcn@latest add button --dry-run\nnpx shadcn@latest add button --diff button.tsx\nnpx shadcn@latest add @acme/form --view button.tsx\n\n# Search registries.\nnpx shadcn@latest search @shadcn -q \"sidebar\"\nnpx shadcn@latest search @tailark -q \"stats\"\n\n# Get component docs and example URLs.\nnpx shadcn@latest docs button dialog select\n\n# View registry item details (for items not yet installed).\nnpx shadcn@latest view @shadcn/button\n```\n\n**Named presets:** `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`\n**Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)\n**Preset codes:** Version-prefixed base62 strings (e.g. `a2r6bw` or `b0`), from [ui.shadcn.com](https://ui.shadcn.com).\n\n## Detailed References\n\n- [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states\n- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading\n- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects\n- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index\n- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion\n- [cli.md](./cli.md) — Commands, flags, presets, templates\n- [customization.md](./customization.md) — Theming, CSS variables, extending components\n",".trae/skills/manage-translations/SKILL.md":"---\nname: manage-translations\ndescription: \"指导如何在 Project Graph 项目中管理多语言翻译。当需要添加新翻译、更新现有翻译或了解翻译系统结构时使用此技能。\"\n---\n\n# 管理翻译\n\n本技能指导如何在 Project Graph 项目中管理多语言翻译。\n\n## 翻译文件位置\n\n所有翻译文件位于 `app/src/locales/` 目录下：\n\n```\napp/src/locales/\n├── en.yml      # 英文\n├── id.yml      # 印度尼西亚语\n├── zh_CN.yml   # 简体中文\n├── zh_TW.yml   # 繁体中文\n├── zh_TWC.yml  # 接地气繁体中文\n├── ...         # 其他语言翻译文件\n└── README.md   # 翻译系统说明\n```\n\n## 查看当前支持的语言\n\n在添加或修改翻译前，先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件：\n\n```bash\nls app/src/locales/*.yml\n```\n",".trae/skills/create-setting-item/SKILL.md":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n> **注意：** 关于翻译管理的详细说明，请参考 `manage-translations` skill。\n\n在所有语言文件中添加翻译。首先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件，然后为每个语言文件添加翻译。\n\n**设置项的翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n","packages/shapes/project.json":"{\n  \"name\": \"@graphif/shapes\",\n  \"targets\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"cache\": true,\n      \"outputs\": [\"{projectRoot}/dist/**\"]\n    },\n    \"type-check\": {\n      \"cache\": true,\n      \"inputs\": [\"{projectRoot}/src/**/*.ts\"]\n    },\n    \"publish\": {\n      \"dependsOn\": [\"build\"],\n      \"cache\": false\n    }\n  }\n}\n","AGENTS.md":"## Project Background\n\nGithub Repository: `graphif/project-graph`\n\nProject Graph 是一款桌面级节点图绘制工具，用于头脑风暴、知识图谱、项目规划等场景的可视化思考。\n\n## Tech-stack\n\n- React (TypeScript) + Tauri (Rust)\n- Vite + pnpm (monorepo) + Nx\n- Canvas 2D\n- shadcn/ui + Tailwind CSS + 自研子窗口系统\n- Jotai\n\nRust 主要负责本地文件等系统能力；绝大部分业务逻辑在前端。\n\n## Structure\n\n### Application\n\n- Frontend Vite project: `/app`\n- Rust / Tauri: `/app/src-tauri`\n- UI components: `/app/src/components`\n- i18n locales: `/app/src/locales`\n- React state (Jotai 等): `/app/src/state.tsx` 及附近\n\n### `app/src/core`（核心业务）\n\n| 路径                              | 职责                                                                            |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `Project.tsx`                     | 单个工程/项目实例的入口与生命周期                                               |\n| `Tab*.ts(x)` / `TabWorkspace.tsx` | 标签页与工作区                                                                  |\n| `loadAllServices.tsx`             | 注册/加载全部服务                                                               |\n| `algorithm/`                      | 通用算法与几何工具                                                              |\n| `stage/`                          | 舞台：`Camera`、`Canvas`、舞台对象（`stageObject`）、舞台管理（`stageManager`） |\n| `render/`                         | 渲染：`canvas2d`、`svg`、`3d`、`domElement`                                     |\n| `service/`                        | 业务服务（见下）                                                                |\n| `extension/`                      | 扩展系统运行时与 API                                                            |\n| `fileSystemProvider/`             | 文件系统抽象（草稿/本地文件等）                                                 |\n| `interfaces/`                     | 核心接口（如 `Service`）                                                        |\n| `subWindowOpen*.ts`               | 子窗口打开方式                                                                  |\n\n`service/` 主要子目录：\n\n- `controlService/` — 输入控制、快捷键、框选、自动布局等\n- `dataFileService/` — 工程文件读写\n- `dataGenerateService/` — 数据生成\n- `dataManageService/` — 内容搜索、复制、AI、节点工具等\n- `feedbackService/` — 特效、音效、舞台样式、颜色\n- `Settings.tsx` 等 — 设置、主题、菜单、教程等全局服务\n\n### Packages（`/packages`）\n\n前端复用的开源/内部库，例如：\n\n- `@graphif/serializer` — 实例序列化\n- `@graphif/shapes` — 可序列化图形\n- `@graphif/data-structures` — 可序列化数据结构\n- `extprg` / `extprg-types` / `create-extprg` — 扩展工具链与类型\n\n### Agent skills\n\n具体工作流见 `.agents/skills/`：\n\n- `type-check` — TypeScript 类型检查（**改代码后用这个，不要 build**）\n- `create-keybind` — 新增/修改快捷键\n- `create-setting-item` — 新增/修改设置项\n- `shadcn` / `ui` — UI 组件\n- `suggest-lucide-icons` — 图标建议\n\n## Locales (`app/src/locales`)\n\n| 文件         | 说明                                                               |\n| ------------ | ------------------------------------------------------------------ |\n| `en.yml`     | 英文（手写维护）                                                   |\n| `zh_CN.yml`  | 简体中文（手写维护）                                               |\n| `zh_TW.yml`  | 普通繁体中文 — **由 `zh_CN.yml` 自动生成（OpenCC）**，**不要手改** |\n| `zh_TWC.yml` | 接地气繁体中文 — **手写维护，不是自动生成**                        |\n| `id.yml`     | 印尼语（手写维护）                                                 |\n\n新增文案时：改 `en.yml` / `zh_CN.yml`（及需要时的 `zh_TWC.yml`、`id.yml`），**不要编辑 `zh_TW.yml`**。\n\n## Coding guidelines\n\n- 正确性与清晰度优先；性能除非明确要求，否则次之。\n- 不要写组织性/总结性注释；仅在「为什么这样写」不直观时解释 why。\n- 优先在已有文件中实现功能；仅在新的逻辑组件时新建文件，避免拆成大量小文件。\n- 目录内使用 `something.tsx`，不要用单独的 `index.tsx`。\n- 错误处理（**前端**）：\n  - 不要静默吞掉错误：禁止 `catch {}` 或仅 `console.error` 后忽略\n  - 能不 catch 就不 catch，让调用方处理\n  - 需要忽略时用对话框提示用户（用户看不到控制台）\n  - 错误应向上传到 DOM（如 `window`），由 `ErrorHandler` 展示友好对话框\n  - 反例：`try { something() } catch (e) { console.error(e) }` → 直接 `something()`\n- 错误处理（**Rust / Tauri 命令**）：命令在运行中不能让进程因未捕获错误闪退，须在函数内妥善处理错误并返回给前端。\n- UI：优先复用 shadcn 与 `.agents/skills/ui` 中的约定（`Dialog`、`toast` 等）。\n- 快捷键 / 设置：分别遵循 `create-keybind`、`create-setting-item` skill。\n\n## Commands\n\n包管理：`pnpm`。常用：\n\n- 开发：`pnpm dev`\n- Lint：`pnpm lint` / `pnpm lint:fix`\n- 测试：`pnpm test`（vitest）\n- 类型检查：**使用 `type-check` skill**（`pnpm --filter @graphif/project-graph type-check`）\n\n**禁止** Agent 运行 `build` / `build:ci` / `build:no-tauri` / `tauri build` 等构建命令。验证改动用 type-check（及必要的 lint/test），不要 build。\n\n## Agent constraints\n\n- **依赖 API / 用法**：禁止用 shell 在 `node_modules` 里搜索、翻源码或类型定义。需要了解第三方库时，读取该项目的官方文档（优先 `llms.txt`，例如 `https://<pkg-docs>/llms.txt`），或使用已有 skill；不要 `grep` / `find` / `cat` `node_modules/**`。\n- **实现方式**：禁止用 `python`、`python3`、`node -e`、`node --eval`、内联 shell 脚本等做文本处理或批量改文件。应直接用读/写/编辑文件的工具（Read / Write / Edit 等）完成；需要多文件改动时逐个编辑，不要写临时脚本。\n- **构建**：禁止 build（见上）；验证用 type-check skill。\n\n## Commit Message\n\n使用 [Conventional Commits](https://www.conventionalcommits.org/)，例如：`feat: ...`、`fix: ...`、`refactor: ...`。\n",".trae/skills/create-keybind/SKILL.md":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = await KeyBindsUI.get(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n","packages/shapes/package.json":"{\n  \"name\": \"@graphif/shapes\",\n  \"description\": \"Serializable shapes\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"license\": \"MIT\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\",\n    \"access\": \"public\",\n    \"module\": \"dist/index.mjs\",\n    \"types\": \"dist/index.d.mts\",\n    \"exports\": {\n      \".\": {\n        \"types\": \"./dist/index.d.mts\",\n        \"import\": \"./dist/index.mjs\"\n      }\n    }\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"module\": \"src/index.ts\",\n  \"types\": \"src/index.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./src/index.ts\",\n      \"import\": \"./src/index.ts\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"type-check\": \"tsc\",\n    \"publish\": \"pnpm publish --no-git-checks\"\n  },\n  \"devDependencies\": {\n    \"tsdown\": \"catalog:\",\n    \"typescript\": \"catalog:\"\n  },\n  \"dependencies\": {\n    \"@graphif/data-structures\": \"workspace:*\",\n    \"@graphif/serializer\": \"workspace:*\",\n    \"unplugin-original-class-name\": \"catalog:\"\n  }\n}\n","packages/shapes/tsconfig.json":"# Shapes Skill\n\nPath: `packages/shapes/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/tsconfig.json)","packages/serializer/package.json":"# Serializer Skill\n\nPath: `packages/serializer/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/package.json)","packages/serializer/project.json":"# Serializer Skill\n\nPath: `packages/serializer/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/project.json)","packages/serializer/README.md":"# serializer Documentation\n\nPath: `packages/serializer/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/README.md)","packages/serializer/tsconfig.json":"# Serializer Skill\n\nPath: `packages/serializer/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/tsconfig.json)","packages/extprg-types/package.json":"# Extprg-types Skill\n\nPath: `packages/extprg-types/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/package.json)","packages/extprg-types/tsconfig.json":"# Extprg-types Skill\n\nPath: `packages/extprg-types/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/tsconfig.json)","packages/extprg/package.json":"# Extprg Skill\n\nPath: `packages/extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/package.json)","packages/extprg/pnpm-lock.yaml":"# Extprg Skill\n\nPath: `packages/extprg/pnpm-lock.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/pnpm-lock.yaml)","packages/extprg/tsconfig.json":"# Extprg Skill\n\nPath: `packages/extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/tsconfig.json)","packages/data-structures/package.json":"# Data-structures Skill\n\nPath: `packages/data-structures/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/package.json)","packages/data-structures/project.json":"# Data-structures Skill\n\nPath: `packages/data-structures/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/project.json)","packages/data-structures/tsconfig.json":"# Data-structures Skill\n\nPath: `packages/data-structures/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/tsconfig.json)","packages/create-extprg/package.json":"# Create-extprg Skill\n\nPath: `packages/create-extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/package.json)","packages/create-extprg/tsconfig.json":"# Create-extprg Skill\n\nPath: `packages/create-extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/tsconfig.json)",".agents/skills/suggest-lucide-icons/changelog.md":"# Suggest-lucide-icons Skill\n\nPath: `.agents/skills/suggest-lucide-icons/changelog.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/changelog.md)",".agents/skills/shadcn/cli.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/cli.md)",".agents/skills/shadcn/customization.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/customization.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/customization.md)",".agents/skills/shadcn/mcp.md":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/mcp.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/mcp.md)",".agents/skills/shadcn/evals/evals.json":"# Evals Skill\n\nPath: `.agents/skills/shadcn/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/evals/evals.json)",".agents/skills/shadcn/rules/base-vs-radix.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/base-vs-radix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/base-vs-radix.md)",".agents/skills/shadcn/rules/composition.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/composition.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/composition.md)",".agents/skills/shadcn/rules/forms.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/forms.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/forms.md)",".agents/skills/shadcn/rules/icons.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/icons.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/icons.md)",".agents/skills/shadcn/rules/styling.md":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/styling.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/styling.md)","app/src/core/service/feedbackService/effectEngine/mathTools/README.md":"# mathTools Documentation\n\nPath: `app/src/core/service/feedbackService/effectEngine/mathTools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/app/src/core/service/feedbackService/effectEngine/mathTools/README.md)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"## Project Background\n\nGithub Repository: `graphif/project-graph`\n\nProject Graph 是一款桌面级节点图绘制工具，用于头脑风暴、知识图谱、项目规划等场景的可视化思考。\n\n## Tech-stack\n\n- React (TypeScript) + Tauri (Rust)\n- Vite + pnpm (monorepo) + Nx\n- Canvas 2D\n- shadcn/ui + Tailwind CSS + 自研子窗口系统\n- Jotai\n\nRust 主要负责本地文件等系统能力；绝大部分业务逻辑在前端。\n\n## Structure\n\n### Application\n\n- Frontend Vite project: `/app`\n- Rust / Tauri: `/app/src-tauri`\n- UI components: `/app/src/components`\n- i18n locales: `/app/src/locales`\n- React state (Jotai 等): `/app/src/state.tsx` 及附近\n\n### `app/src/core`（核心业务）\n\n| 路径                              | 职责                                                                            |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `Project.tsx`                     | 单个工程/项目实例的入口与生命周期                                               |\n| `Tab*.ts(x)` / `TabWorkspace.tsx` | 标签页与工作区                                                                  |\n| `loadAllServices.tsx`             | 注册/加载全部服务                                                               |\n| `algorithm/`                      | 通用算法与几何工具                                                              |\n| `stage/`                          | 舞台：`Camera`、`Canvas`、舞台对象（`stageObject`）、舞台管理（`stageManager`） |\n| `render/`                         | 渲染：`canvas2d`、`svg`、`3d`、`domElement`                                     |\n| `service/`                        | 业务服务（见下）                                                                |\n| `extension/`                      | 扩展系统运行时与 API                                                            |\n| `fileSystemProvider/`             | 文件系统抽象（草稿/本地文件等）                                                 |\n| `interfaces/`                     | 核心接口（如 `Service`）                                                        |\n| `subWindowOpen*.ts`               | 子窗口打开方式                                                                  |\n\n`service/` 主要子目录：\n\n- `controlService/` — 输入控制、快捷键、框选、自动布局等\n- `dataFileService/` — 工程文件读写\n- `dataGenerateService/` — 数据生成\n- `dataManageService/` — 内容搜索、复制、AI、节点工具等\n- `feedbackService/` — 特效、音效、舞台样式、颜色\n- `Settings.tsx` 等 — 设置、主题、菜单、教程等全局服务\n\n### Packages（`/packages`）\n\n前端复用的开源/内部库，例如：\n\n- `@graphif/serializer` — 实例序列化\n- `@graphif/shapes` — 可序列化图形\n- `@graphif/data-structures` — 可序列化数据结构\n- `extprg` / `extprg-types` / `create-extprg` — 扩展工具链与类型\n\n### Agent skills\n\n具体工作流见 `.agents/skills/`：\n\n- `type-check` — TypeScript 类型检查（**改代码后用这个，不要 build**）\n- `create-keybind` — 新增/修改快捷键\n- `create-setting-item` — 新增/修改设置项\n- `shadcn` / `ui` — UI 组件\n- `suggest-lucide-icons` — 图标建议\n\n## Locales (`app/src/locales`)\n\n| 文件         | 说明                                                               |\n| ------------ | ------------------------------------------------------------------ |\n| `en.yml`     | 英文（手写维护）                                                   |\n| `zh_CN.yml`  | 简体中文（手写维护）                                               |\n| `zh_TW.yml`  | 普通繁体中文 — **由 `zh_CN.yml` 自动生成（OpenCC）**，**不要手改** |\n| `zh_TWC.yml` | 接地气繁体中文 — **手写维护，不是自动生成**                        |\n| `id.yml`     | 印尼语（手写维护）                                                 |\n\n新增文案时：改 `en.yml` / `zh_CN.yml`（及需要时的 `zh_TWC.yml`、`id.yml`），**不要编辑 `zh_TW.yml`**。\n\n## Coding guidelines\n\n- 正确性与清晰度优先；性能除非明确要求，否则次之。\n- 不要写组织性/总结性注释；仅在「为什么这样写」不直观时解释 why。\n- 优先在已有文件中实现功能；仅在新的逻辑组件时新建文件，避免拆成大量小文件。\n- 目录内使用 `something.tsx`，不要用单独的 `index.tsx`。\n- 错误处理（**前端**）：\n  - 不要静默吞掉错误：禁止 `catch {}` 或仅 `console.error` 后忽略\n  - 能不 catch 就不 catch，让调用方处理\n  - 需要忽略时用对话框提示用户（用户看不到控制台）\n  - 错误应向上传到 DOM（如 `window`），由 `ErrorHandler` 展示友好对话框\n  - 反例：`try { something() } catch (e) { console.error(e) }` → 直接 `something()`\n- 错误处理（**Rust / Tauri 命令**）：命令在运行中不能让进程因未捕获错误闪退，须在函数内妥善处理错误并返回给前端。\n- UI：优先复用 shadcn 与 `.agents/skills/ui` 中的约定（`Dialog`、`toast` 等）。\n- 快捷键 / 设置：分别遵循 `create-keybind`、`create-setting-item` skill。\n\n## Commands\n\n包管理：`pnpm`。常用：\n\n- 开发：`pnpm dev`\n- Lint：`pnpm lint` / `pnpm lint:fix`\n- 测试：`pnpm test`（vitest）\n- 类型检查：**使用 `type-check` skill**（`pnpm --filter @graphif/project-graph type-check`）\n\n**禁止** Agent 运行 `build` / `build:ci` / `build:no-tauri` / `tauri build` 等构建命令。验证改动用 type-check（及必要的 lint/test），不要 build。\n\n## Agent constraints\n\n- **依赖 API / 用法**：禁止用 shell 在 `node_modules` 里搜索、翻源码或类型定义。需要了解第三方库时，读取该项目的官方文档（优先 `llms.txt`，例如 `https://<pkg-docs>/llms.txt`），或使用已有 skill；不要 `grep` / `find` / `cat` `node_modules/**`。\n- **实现方式**：禁止用 `python`、`python3`、`node -e`、`node --eval`、内联 shell 脚本等做文本处理或批量改文件。应直接用读/写/编辑文件的工具（Read / Write / Edit 等）完成；需要多文件改动时逐个编辑，不要写临时脚本。\n- **构建**：禁止 build（见上）；验证用 type-check skill。\n\n## Commit Message\n\n使用 [Conventional Commits](https://www.conventionalcommits.org/)，例如：`feat: ...`、`fix: ...`、`refactor: ...`。\n","isInternal":false,"tokens":1899,"sizeBytes":6618},{"name":"SKILL.md","path":".agents/skills/create-keybind/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/create-keybind/SKILL.md","title":"create-keybind","category":"anthropic-skill","format":"markdown","content":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n- 印度尼西亚语：`app/src/locales/id.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = KeyBindsUI.getUIKeyBind(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n","frontmatter":{"name":"create-keybind","description":"指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。"},"isInternal":false,"tokens":3987,"sizeBytes":11850},{"name":"SKILL.md","path":".agents/skills/create-setting-item/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/create-setting-item/SKILL.md","title":"create-setting-item","category":"anthropic-skill","format":"markdown","content":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 接地气繁体中文\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n","frontmatter":{"name":"create-setting-item","description":"指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。"},"isInternal":false,"tokens":2799,"sizeBytes":8974},{"name":"cli.md","path":".agents/skills/shadcn/cli.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/cli.md","title":"Shadcn Skill","category":"anthropic-skill","format":"markdown","content":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/cli.md)","tokens":300,"sizeBytes":173},{"name":"customization.md","path":".agents/skills/shadcn/customization.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/customization.md","title":"Shadcn Skill","category":"anthropic-skill","format":"markdown","content":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/customization.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/customization.md)","tokens":300,"sizeBytes":193},{"name":"evals.json","path":".agents/skills/shadcn/evals/evals.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/evals/evals.json","title":"Evals Skill","category":"anthropic-skill","format":"json","content":"# Evals Skill\n\nPath: `.agents/skills/shadcn/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/evals/evals.json)","tokens":300,"sizeBytes":192},{"name":"mcp.md","path":".agents/skills/shadcn/mcp.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/mcp.md","title":"Shadcn Skill","category":"anthropic-skill","format":"markdown","content":"# Shadcn Skill\n\nPath: `.agents/skills/shadcn/mcp.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/mcp.md)","tokens":300,"sizeBytes":173},{"name":"base-vs-radix.md","path":".agents/skills/shadcn/rules/base-vs-radix.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/base-vs-radix.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/base-vs-radix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/base-vs-radix.md)","tokens":300,"sizeBytes":204},{"name":"composition.md","path":".agents/skills/shadcn/rules/composition.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/composition.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/composition.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/composition.md)","tokens":300,"sizeBytes":200},{"name":"forms.md","path":".agents/skills/shadcn/rules/forms.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/forms.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/forms.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/forms.md)","tokens":300,"sizeBytes":188},{"name":"icons.md","path":".agents/skills/shadcn/rules/icons.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/icons.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/icons.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/icons.md)","tokens":300,"sizeBytes":188},{"name":"styling.md","path":".agents/skills/shadcn/rules/styling.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/styling.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/styling.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/styling.md)","tokens":300,"sizeBytes":192},{"name":"SKILL.md","path":".agents/skills/shadcn/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/SKILL.md","title":"shadcn","category":"anthropic-skill","format":"markdown","content":"---\nname: shadcn\ndescription: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for \"shadcn init\", \"create an app with --preset\", or \"switch to --preset\".\nuser-invocable: false\nallowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)\n---\n\n# shadcn/ui\n\nA framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.\n\n> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.\n\n## Current Project Context\n\n```json\n!`npx shadcn@latest info --json`\n```\n\nThe JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.\n\n## Principles\n\n1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.\n2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.\n3. **Use built-in variants before custom styles.** `variant=\"outline\"`, `size=\"sm\"`, etc.\n4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.\n\n## Critical Rules\n\nThese rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.\n\n### Styling & Tailwind → [styling.md](./rules/styling.md)\n\n- **`className` for layout, not styling.** Never override component colors or typography.\n- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.\n- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.\n- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.\n- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).\n- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.\n- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.\n\n### Forms & Inputs → [forms.md](./rules/forms.md)\n\n- **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.\n- **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.\n- **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**\n- **Option sets (2–7 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.\n- **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.\n- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.\n\n### Component Structure → [composition.md](./rules/composition.md)\n\n- **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.\n- **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)\n- **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className=\"sr-only\"` if visually hidden.\n- **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.\n- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.\n- **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.\n- **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.\n\n### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)\n\n- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.\n- **Callouts use `Alert`.** Don't build custom styled divs.\n- **Empty states use `Empty`.** Don't build custom empty state markup.\n- **Toast via `sonner`.** Use `toast()` from `sonner`.\n- **Use `Separator`** instead of `<hr>` or `<div className=\"border-t\">`.\n- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.\n- **Use `Badge`** instead of custom styled spans.\n\n### Icons → [icons.md](./rules/icons.md)\n\n- **Icons in `Button` use `data-icon`.** `data-icon=\"inline-start\"` or `data-icon=\"inline-end\"` on the icon.\n- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.\n- **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.\n\n### CLI\n\n- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest apply --preset <code>` for existing projects, or `npx shadcn@latest init --preset <code>` when initializing.\n\n## Key Patterns\n\nThese are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.\n\n```tsx\n// Form layout: FieldGroup + Field, not div + Label.\n<FieldGroup>\n  <Field>\n    <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n    <Input id=\"email\" />\n  </Field>\n</FieldGroup>\n\n// Validation: data-invalid on Field, aria-invalid on the control.\n<Field data-invalid>\n  <FieldLabel>Email</FieldLabel>\n  <Input aria-invalid />\n  <FieldDescription>Invalid email.</FieldDescription>\n</Field>\n\n// Icons in buttons: data-icon, no sizing classes.\n<Button>\n  <SearchIcon data-icon=\"inline-start\" />\n  Search\n</Button>\n\n// Spacing: gap-*, not space-y-*.\n<div className=\"flex flex-col gap-4\">  // correct\n<div className=\"space-y-4\">           // wrong\n\n// Equal dimensions: size-*, not w-* h-*.\n<Avatar className=\"size-10\">   // correct\n<Avatar className=\"w-10 h-10\"> // wrong\n\n// Status colors: Badge variants or semantic tokens, not raw colors.\n<Badge variant=\"secondary\">+20.1%</Badge>    // correct\n<span className=\"text-emerald-600\">+20.1%</span> // wrong\n```\n\n## Component Selection\n\n| Need                       | Use                                                                                                 |\n| -------------------------- | --------------------------------------------------------------------------------------------------- |\n| Button/action              | `Button` with appropriate variant                                                                   |\n| Form inputs                | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |\n| Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem`                                                                   |\n| Data display               | `Table`, `Card`, `Badge`, `Avatar`                                                                  |\n| Navigation                 | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination`                                     |\n| Overlays                   | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation)       |\n| Feedback                   | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner`                                        |\n| Command palette            | `Command` inside `Dialog`                                                                           |\n| Charts                     | `Chart` (wraps Recharts)                                                                            |\n| Layout                     | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible`                          |\n| Empty states               | `Empty`                                                                                             |\n| Menus                      | `DropdownMenu`, `ContextMenu`, `Menubar`                                                            |\n| Tooltips/info              | `Tooltip`, `HoverCard`, `Popover`                                                                   |\n\n## Key Fields\n\nThe injected project context contains these key fields:\n\n- **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.\n- **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `\"use client\"` at the top of the file. Always reference this field when advising on the directive.\n- **`tailwindVersion`** → `\"v4\"` uses `@theme inline` blocks; `\"v3\"` uses `tailwind.config.js`.\n- **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.\n- **`style`** → component visual treatment (e.g. `nova`, `vega`).\n- **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.\n- **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.\n- **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.\n- **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).\n- **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).\n\nSee [cli.md — `info` command](./cli.md) for the full field reference.\n\n## Component Docs, Examples, and Usage\n\nRun `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.\n\n```bash\nnpx shadcn@latest docs button dialog select\n```\n\n**When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.\n\n## Workflow\n\n1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.\n2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.\n3. **Find components** — `npx shadcn@latest search`.\n4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.\n5. **Install or update** — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).\n6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.\n7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.\n8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says \"add a login block\" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.\n9. **Switching presets** — Ask the user first: **overwrite**, **partial**, **merge**, or **skip**?\n   - **Overwrite**: `npx shadcn@latest apply --preset <code>`. Overwrites detected components, fonts, and CSS variables.\n   - **Partial**: `npx shadcn@latest apply --preset <code> --only theme,font`. Updates only the selected preset parts without reinstalling UI components. Supported values are `theme` and `font`; comma-separated combinations are allowed. `icon` is intentionally not supported, because icon changes may require full component reinstall and transforms.\n   - **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.\n   - **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.\n   - **Important**: Always run preset commands inside the user's project directory. `apply` only works in an existing project with a `components.json` file. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.\n\n## Updating Components\n\nWhen the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**\n\n1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.\n2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.\n3. Decide per file based on the diff:\n   - No local changes → safe to overwrite.\n   - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.\n   - User says \"just update everything\" → use `--overwrite`, but confirm first.\n4. **Never use `--overwrite` without the user's explicit approval.**\n\n## Quick Reference\n\n```bash\n# Create a new project.\nnpx shadcn@latest init --name my-app --preset base-nova\nnpx shadcn@latest init --name my-app --preset a2r6bw --template vite\n\n# Create a monorepo project.\nnpx shadcn@latest init --name my-app --preset base-nova --monorepo\nnpx shadcn@latest init --name my-app --preset base-nova --template next --monorepo\n\n# Initialize existing project.\nnpx shadcn@latest init --preset base-nova\nnpx shadcn@latest init --defaults  # shortcut: --template=next --preset=nova (base style implied)\n\n# Apply a preset to an existing project.\nnpx shadcn@latest apply --preset a2r6bw\nnpx shadcn@latest apply a2r6bw\nnpx shadcn@latest apply --preset a2r6bw --only theme\nnpx shadcn@latest apply --preset a2r6bw --only font\nnpx shadcn@latest apply --preset a2r6bw --only theme,font\n\n# Add components.\nnpx shadcn@latest add button card dialog\nnpx shadcn@latest add @magicui/shimmer-button\nnpx shadcn@latest add --all\n\n# Preview changes before adding/updating.\nnpx shadcn@latest add button --dry-run\nnpx shadcn@latest add button --diff button.tsx\nnpx shadcn@latest add @acme/form --view button.tsx\n\n# Search registries.\nnpx shadcn@latest search @shadcn -q \"sidebar\"\nnpx shadcn@latest search @tailark -q \"stats\"\n\n# Get component docs and example URLs.\nnpx shadcn@latest docs button dialog select\n\n# View registry item details (for items not yet installed).\nnpx shadcn@latest view @shadcn/button\n```\n\n**Named presets:** `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`\n**Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)\n**Preset codes:** Version-prefixed base62 strings (e.g. `a2r6bw` or `b0`), from [ui.shadcn.com](https://ui.shadcn.com).\n\n## Detailed References\n\n- [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states\n- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading\n- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects\n- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index\n- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion\n- [cli.md](./cli.md) — Commands, flags, presets, templates\n- [customization.md](./customization.md) — Theming, CSS variables, extending components\n","frontmatter":{"name":"shadcn","description":"Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for \"shadcn init\", \"create an app with --preset\", or \"switch to --preset\".","user-invocable":false,"allowed-tools":"Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)","allowedTools":["Bash(npx","shadcn@latest","*),","Bash(pnpm","dlx","shadcn@latest","*),","Bash(bunx","--bun","shadcn@latest","*)"]},"allowedTools":["Bash(npx","shadcn@latest","*),","Bash(pnpm","dlx","shadcn@latest","*),","Bash(bunx","--bun","shadcn@latest","*)"],"isInternal":false,"tokens":4278,"sizeBytes":17326},{"name":"changelog.md","path":".agents/skills/suggest-lucide-icons/changelog.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/changelog.md","title":"Suggest-lucide-icons Skill","category":"anthropic-skill","format":"markdown","content":"# Suggest-lucide-icons Skill\n\nPath: `.agents/skills/suggest-lucide-icons/changelog.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/changelog.md)","tokens":300,"sizeBytes":227},{"name":"SKILL.md","path":".agents/skills/suggest-lucide-icons/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/SKILL.md","title":"suggest-lucide-icons","category":"anthropic-skill","format":"markdown","content":"---\nname: suggest-lucide-icons\ndescription: \"Suggest relevant Lucide icons for concepts or UI placements. Searches lucide.dev to find actual icons that symbolize ideas or fit design contexts. Use when needing icons for UI work, documentation, or symbolic representation.\"\nmodel: sonnet, inherit\nmetadata:\n  author: nweii\n  version: \"1.2.0\"\n---\n\n# Suggest Lucide Icons\n\nSuggest the most relevant icons from the [Lucide open source icon pack](https://lucide.dev) to symbolize a concept or fit specific UI placements. I am skilled in symbolic interpretation and mental associations across culture, symbology, science, and design.\n\n## Input\n\nProvide one or both:\n\n- **Concept**: The idea, action, or meaning to represent\n- **Screenshot**: UI context showing where icons are needed\n\n## Naming conventions\n\nIcon names follow strict rules — apply these when generating candidates:\n\n- **kebab-case**: `arrow-up` not `Arrow Up`\n- **International English**: `color` not `colour`\n- **Group variants**: `<group>-<variant>` — e.g. `badge-plus` is based on `badge`\n- **Multiple elements, different sizes**: list largest first — `circle-person` if circle is bigger\n- **Element with modifier**: `[element]-[modifier]` — `circle-dashed` not `dashed-circle`; combined: `circle-dashed-heart-broken`\n\n## Process\n\n1. **Brainstorm associations**\n   - Key ideas and visual metaphors related to the concept\n   - Context clues from screenshot if provided\n   - Generate 4–6 candidate icon names in kebab-case (e.g. `arrow-right`, `circle-check`)\n\n2. **Verify candidates**\n   - For each candidate, fetch: `https://unpkg.com/lucide-static@latest/icons/[icon-name].svg`\n   - Use WebFetch or follow redirects (`curl -L`) — unpkg issues a 302 before the final response\n   - SVG content in the response means the icon exists; a 404 means it doesn't\n   - Discard any that don't exist; try alternate names if needed to reach 3 confirmed icons\n\n3. **Present 3 confirmed candidates**\n   - Icon name\n   - Why it fits (symbolic meaning, visual clarity, context appropriateness)\n\n4. **Recommend best choice**\n   - Single strongest option\n   - Rationale for recommendation\n\n## Guidelines\n\n- Never suggest an icon without confirming it via the unpkg URL\n- Never suggest made-up icon names\n- If fewer than 3 candidates survive verification, brainstorm more before giving up\n- If no good matches exist after thorough searching, say so\n- For screenshots, tailor to specific design context\n- Provide a distinct recommendation for each icon needed\n- Ready for multiple feedback rounds to refine suggestions\n\n## Output Format\n\n**Brainstorm**: [Key associations and metaphors]\n\n**Candidate Icons**:\n\n1. **icon-name** — Explanation of fit\n2. **icon-name** — Explanation of fit\n3. **icon-name** — Explanation of fit\n\n**Recommendation**: **icon-name** — Why this is the strongest choice for [context]\n","frontmatter":{"name":"suggest-lucide-icons","description":"Suggest relevant Lucide icons for concepts or UI placements. Searches lucide.dev to find actual icons that symbolize ideas or fit design contexts. Use when needing icons for UI work, documentation, or symbolic representation.","model":"sonnet, inherit","metadata":{"author":"nweii","version":"1.2.0"}},"isInternal":false,"tokens":668,"sizeBytes":2870},{"name":"SKILL.md","path":".agents/skills/type-check/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/type-check/SKILL.md","title":"type-check","category":"anthropic-skill","format":"markdown","content":"---\nname: type-check\ndescription: 运行 TypeScript 类型检查\n---\n\n# type-check\n\n运行以下命令来执行 TypeScript 类型检查：\n\n```bash\npnpm --filter @graphif/project-graph type-check\n```\n","frontmatter":{"name":"type-check","description":"运行 TypeScript 类型检查"},"isInternal":false,"tokens":53,"sizeBytes":200},{"name":"SKILL.md","path":".agents/skills/ui/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/ui/SKILL.md","title":"ui","category":"anthropic-skill","format":"markdown","content":"---\nname: ui\ndescription: 建立于 shadcn/ui 之上的附加 UI 组件\n---\n\n# ui\n\n## `Dialog` (`app/src/components/ui/dialog.tsx`)\n\n- `Dialog.confirm`: 显示一个确认对话框，返回一个 Promise，用户点击确认时 resolve，点击取消时 reject。\n- `Dialog.input`: 显示一个输入对话框，返回一个 Promise，用户输入内容并点击确认时 resolve，点击取消时 reject。\n- `Dialog.copy`: 显示一个复制对话框，返回一个 Promise，用户关闭对话框时 resolve。\n- `Dialog.buttons`: 显示一个带有自定义按钮的对话框，返回一个 Promise，用户点击任意按钮时 resolve，参数为被点击按钮的 `id`。\n\n## `toast()` (`import { toast } from \"sonner\"`)\n\n- `toast`: 显示一个通用的、info 类型的 toast 消息，参数为消息内容，尽量不要使用此函数，推荐使用下面的 `toast.success()` 等方法。\n- `toast.success`: 显示一个成功类型的 toast 消息，参数为消息内容。\n- `toast.error`: 显示一个错误类型的 toast 消息，参数为消息内容。\n- `toast.warning`: 显示一个警告类型的 toast 消息，参数为消息内容。\n- `toast.promise`: 见下面的示例代码。\n\n```ts\nawait toast\n  .promise(\n    async () => {\n      // 异步操作\n    },\n    {\n      loading: \"promise进行中的提示\",\n      success: \"resolve后的提示\",\n      error: \"reject后的提示\",\n    },\n  )\n  // unwrap方法会直接把传入的promise返回出来\n  .unwrap();\n```\n","frontmatter":{"name":"ui","description":"建立于 shadcn/ui 之上的附加 UI 组件"},"isInternal":false,"tokens":402,"sizeBytes":1494},{"name":"SKILL.md","path":".trae/skills/create-keybind/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.trae/skills/create-keybind/SKILL.md","title":"create-keybind","category":"anthropic-skill","format":"markdown","content":"---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = await KeyBindsUI.get(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n","frontmatter":{"name":"create-keybind","description":"指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。"},"isInternal":false,"tokens":3946,"sizeBytes":11751},{"name":"SKILL.md","path":".trae/skills/create-setting-item/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.trae/skills/create-setting-item/SKILL.md","title":"create-setting-item","category":"anthropic-skill","format":"markdown","content":"---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n> **注意：** 关于翻译管理的详细说明，请参考 `manage-translations` skill。\n\n在所有语言文件中添加翻译。首先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件，然后为每个语言文件添加翻译。\n\n**设置项的翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n","frontmatter":{"name":"create-setting-item","description":"指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。"},"isInternal":false,"tokens":2773,"sizeBytes":8959},{"name":"SKILL.md","path":".trae/skills/manage-translations/SKILL.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/.trae/skills/manage-translations/SKILL.md","title":"manage-translations","category":"anthropic-skill","format":"markdown","content":"---\nname: manage-translations\ndescription: \"指导如何在 Project Graph 项目中管理多语言翻译。当需要添加新翻译、更新现有翻译或了解翻译系统结构时使用此技能。\"\n---\n\n# 管理翻译\n\n本技能指导如何在 Project Graph 项目中管理多语言翻译。\n\n## 翻译文件位置\n\n所有翻译文件位于 `app/src/locales/` 目录下：\n\n```\napp/src/locales/\n├── en.yml      # 英文\n├── id.yml      # 印度尼西亚语\n├── zh_CN.yml   # 简体中文\n├── zh_TW.yml   # 繁体中文\n├── zh_TWC.yml  # 接地气繁体中文\n├── ...         # 其他语言翻译文件\n└── README.md   # 翻译系统说明\n```\n\n## 查看当前支持的语言\n\n在添加或修改翻译前，先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件：\n\n```bash\nls app/src/locales/*.yml\n```\n","frontmatter":{"name":"manage-translations","description":"指导如何在 Project Graph 项目中管理多语言翻译。当需要添加新翻译、更新现有翻译或了解翻译系统结构时使用此技能。"},"isInternal":false,"tokens":311,"sizeBytes":855},{"name":"package.json","path":"packages/create-extprg/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/package.json","title":"Create-extprg Skill","category":"anthropic-skill","format":"json","content":"# Create-extprg Skill\n\nPath: `packages/create-extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/package.json)","tokens":300,"sizeBytes":194},{"name":"tsconfig.json","path":"packages/create-extprg/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/tsconfig.json","title":"Create-extprg Skill","category":"anthropic-skill","format":"json","content":"# Create-extprg Skill\n\nPath: `packages/create-extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/tsconfig.json)","tokens":300,"sizeBytes":196},{"name":"package.json","path":"packages/data-structures/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/package.json","title":"Data-structures Skill","category":"anthropic-skill","format":"json","content":"# Data-structures Skill\n\nPath: `packages/data-structures/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/package.json)","tokens":300,"sizeBytes":200},{"name":"project.json","path":"packages/data-structures/project.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/project.json","title":"Data-structures Skill","category":"anthropic-skill","format":"json","content":"# Data-structures Skill\n\nPath: `packages/data-structures/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/project.json)","tokens":300,"sizeBytes":200},{"name":"tsconfig.json","path":"packages/data-structures/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/tsconfig.json","title":"Data-structures Skill","category":"anthropic-skill","format":"json","content":"# Data-structures Skill\n\nPath: `packages/data-structures/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/tsconfig.json)","tokens":300,"sizeBytes":202},{"name":"package.json","path":"packages/extprg-types/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/package.json","title":"Extprg-types Skill","category":"anthropic-skill","format":"json","content":"# Extprg-types Skill\n\nPath: `packages/extprg-types/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/package.json)","tokens":300,"sizeBytes":191},{"name":"tsconfig.json","path":"packages/extprg-types/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/tsconfig.json","title":"Extprg-types Skill","category":"anthropic-skill","format":"json","content":"# Extprg-types Skill\n\nPath: `packages/extprg-types/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/tsconfig.json)","tokens":300,"sizeBytes":193},{"name":"package.json","path":"packages/extprg/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/package.json","title":"Extprg Skill","category":"anthropic-skill","format":"json","content":"# Extprg Skill\n\nPath: `packages/extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/package.json)","tokens":300,"sizeBytes":173},{"name":"pnpm-lock.yaml","path":"packages/extprg/pnpm-lock.yaml","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/pnpm-lock.yaml","title":"Extprg Skill","category":"anthropic-skill","format":"yaml","content":"# Extprg Skill\n\nPath: `packages/extprg/pnpm-lock.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/pnpm-lock.yaml)","tokens":300,"sizeBytes":177},{"name":"tsconfig.json","path":"packages/extprg/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/tsconfig.json","title":"Extprg Skill","category":"anthropic-skill","format":"json","content":"# Extprg Skill\n\nPath: `packages/extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/tsconfig.json)","tokens":300,"sizeBytes":175},{"name":"package.json","path":"packages/serializer/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/package.json","title":"Serializer Skill","category":"anthropic-skill","format":"json","content":"# Serializer Skill\n\nPath: `packages/serializer/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/package.json)","tokens":300,"sizeBytes":185},{"name":"project.json","path":"packages/serializer/project.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/project.json","title":"Serializer Skill","category":"anthropic-skill","format":"json","content":"# Serializer Skill\n\nPath: `packages/serializer/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/project.json)","tokens":300,"sizeBytes":185},{"name":"tsconfig.json","path":"packages/serializer/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/tsconfig.json","title":"Serializer Skill","category":"anthropic-skill","format":"json","content":"# Serializer Skill\n\nPath: `packages/serializer/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/tsconfig.json)","tokens":300,"sizeBytes":187},{"name":"package.json","path":"packages/shapes/package.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/package.json","title":"@graphif/shapes","category":"anthropic-skill","format":"json","content":"{\n  \"name\": \"@graphif/shapes\",\n  \"description\": \"Serializable shapes\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"license\": \"MIT\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\",\n    \"access\": \"public\",\n    \"module\": \"dist/index.mjs\",\n    \"types\": \"dist/index.d.mts\",\n    \"exports\": {\n      \".\": {\n        \"types\": \"./dist/index.d.mts\",\n        \"import\": \"./dist/index.mjs\"\n      }\n    }\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"module\": \"src/index.ts\",\n  \"types\": \"src/index.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./src/index.ts\",\n      \"import\": \"./src/index.ts\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"type-check\": \"tsc\",\n    \"publish\": \"pnpm publish --no-git-checks\"\n  },\n  \"devDependencies\": {\n    \"tsdown\": \"catalog:\",\n    \"typescript\": \"catalog:\"\n  },\n  \"dependencies\": {\n    \"@graphif/data-structures\": \"workspace:*\",\n    \"@graphif/serializer\": \"workspace:*\",\n    \"unplugin-original-class-name\": \"catalog:\"\n  }\n}\n","frontmatter":{"name":"@graphif/shapes","description":"Serializable shapes","version":"1.0.0","type":"module","license":"MIT","publishConfig":{"registry":"https://registry.npmjs.org/","access":"public","module":"dist/index.mjs","types":"dist/index.d.mts","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}}},"files":["dist"],"module":"src/index.ts","types":"src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"}},"scripts":{"build":"tsdown","type-check":"tsc","publish":"pnpm publish --no-git-checks"},"devDependencies":{"tsdown":"catalog:","typescript":"catalog:"},"dependencies":{"@graphif/data-structures":"workspace:*","@graphif/serializer":"workspace:*","unplugin-original-class-name":"catalog:"}},"isInternal":false,"tokens":290,"sizeBytes":961},{"name":"project.json","path":"packages/shapes/project.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/project.json","title":"@graphif/shapes","category":"anthropic-skill","format":"json","content":"{\n  \"name\": \"@graphif/shapes\",\n  \"targets\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"cache\": true,\n      \"outputs\": [\"{projectRoot}/dist/**\"]\n    },\n    \"type-check\": {\n      \"cache\": true,\n      \"inputs\": [\"{projectRoot}/src/**/*.ts\"]\n    },\n    \"publish\": {\n      \"dependsOn\": [\"build\"],\n      \"cache\": false\n    }\n  }\n}\n","frontmatter":{"name":"@graphif/shapes","targets":{"build":{"dependsOn":["^build"],"cache":true,"outputs":["{projectRoot}/dist/**"]},"type-check":{"cache":true,"inputs":["{projectRoot}/src/**/*.ts"]},"publish":{"dependsOn":["build"],"cache":false}}},"isInternal":false,"tokens":101,"sizeBytes":337},{"name":"tsconfig.json","path":"packages/shapes/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/tsconfig.json","title":"Shapes Skill","category":"anthropic-skill","format":"json","content":"# Shapes Skill\n\nPath: `packages/shapes/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/shapes/tsconfig.json)","tokens":300,"sizeBytes":175},{"name":"README.md","path":"app/src/core/service/feedbackService/effectEngine/mathTools/README.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/app/src/core/service/feedbackService/effectEngine/mathTools/README.md","title":"mathTools Documentation","category":"plugin-manifest","format":"markdown","content":"# mathTools Documentation\n\nPath: `app/src/core/service/feedbackService/effectEngine/mathTools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/app/src/core/service/feedbackService/effectEngine/mathTools/README.md)","tokens":300,"sizeBytes":266},{"name":"README.md","path":"packages/serializer/README.md","rawUrl":"https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/README.md","title":"serializer Documentation","category":"plugin-manifest","format":"markdown","content":"# serializer Documentation\n\nPath: `packages/serializer/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/serializer/README.md)","tokens":300,"sizeBytes":187}],"systemPromptSnippet":"<yakaai_skills repo=\"graphif/project-graph\">\n<!-- File: AGENTS.md (Tokens: ~1899 | Category: root-instruction) -->\n## Project Background\n\nGithub Repository: `graphif/project-graph`\n\nProject Graph 是一款桌面级节点图绘制工具，用于头脑风暴、知识图谱、项目规划等场景的可视化思考。\n\n## Tech-stack\n\n- React (TypeScript) + Tauri (Rust)\n- Vite + pnpm (monorepo) + Nx\n- Canvas 2D\n- shadcn/ui + Tailwind CSS + 自研子窗口系统\n- Jotai\n\nRust 主要负责本地文件等系统能力；绝大部分业务逻辑在前端。\n\n## Structure\n\n### Application\n\n- Frontend Vite project: `/app`\n- Rust / Tauri: `/app/src-tauri`\n- UI components: `/app/src/components`\n- i18n locales: `/app/src/locales`\n- React state (Jotai 等): `/app/src/state.tsx` 及附近\n\n### `app/src/core`（核心业务）\n\n| 路径                              | 职责                                                                            |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `Project.tsx`                     | 单个工程/项目实例的入口与生命周期                                               |\n| `Tab*.ts(x)` / `TabWorkspace.tsx` | 标签页与工作区                                                                  |\n| `loadAllServices.tsx`             | 注册/加载全部服务                                                               |\n| `algorithm/`                      | 通用算法与几何工具                                                              |\n| `stage/`                          | 舞台：`Camera`、`Canvas`、舞台对象（`stageObject`）、舞台管理（`stageManager`） |\n| `render/`                         | 渲染：`canvas2d`、`svg`、`3d`、`domElement`                                     |\n| `service/`                        | 业务服务（见下）                                                                |\n| `extension/`                      | 扩展系统运行时与 API                                                            |\n| `fileSystemProvider/`             | 文件系统抽象（草稿/本地文件等）                                                 |\n| `interfaces/`                     | 核心接口（如 `Service`）                                                        |\n| `subWindowOpen*.ts`               | 子窗口打开方式                                                                  |\n\n`service/` 主要子目录：\n\n- `controlService/` — 输入控制、快捷键、框选、自动布局等\n- `dataFileService/` — 工程文件读写\n- `dataGenerateService/` — 数据生成\n- `dataManageService/` — 内容搜索、复制、AI、节点工具等\n- `feedbackService/` — 特效、音效、舞台样式、颜色\n- `Settings.tsx` 等 — 设置、主题、菜单、教程等全局服务\n\n### Packages（`/packages`）\n\n前端复用的开源/内部库，例如：\n\n- `@graphif/serializer` — 实例序列化\n- `@graphif/shapes` — 可序列化图形\n- `@graphif/data-structures` — 可序列化数据结构\n- `extprg` / `extprg-types` / `create-extprg` — 扩展工具链与类型\n\n### Agent skills\n\n具体工作流见 `.agents/skills/`：\n\n- `type-check` — TypeScript 类型检查（**改代码后用这个，不要 build**）\n- `create-keybind` — 新增/修改快捷键\n- `create-setting-item` — 新增/修改设置项\n- `shadcn` / `ui` — UI 组件\n- `suggest-lucide-icons` — 图标建议\n\n## Locales (`app/src/locales`)\n\n| 文件         | 说明                                                               |\n| ------------ | ------------------------------------------------------------------ |\n| `en.yml`     | 英文（手写维护）                                                   |\n| `zh_CN.yml`  | 简体中文（手写维护）                                               |\n| `zh_TW.yml`  | 普通繁体中文 — **由 `zh_CN.yml` 自动生成（OpenCC）**，**不要手改** |\n| `zh_TWC.yml` | 接地气繁体中文 — **手写维护，不是自动生成**                        |\n| `id.yml`     | 印尼语（手写维护）                                                 |\n\n新增文案时：改 `en.yml` / `zh_CN.yml`（及需要时的 `zh_TWC.yml`、`id.yml`），**不要编辑 `zh_TW.yml`**。\n\n## Coding guidelines\n\n- 正确性与清晰度优先；性能除非明确要求，否则次之。\n- 不要写组织性/总结性注释；仅在「为什么这样写」不直观时解释 why。\n- 优先在已有文件中实现功能；仅在新的逻辑组件时新建文件，避免拆成大量小文件。\n- 目录内使用 `something.tsx`，不要用单独的 `index.tsx`。\n- 错误处理（**前端**）：\n  - 不要静默吞掉错误：禁止 `catch {}` 或仅 `console.error` 后忽略\n  - 能不 catch 就不 catch，让调用方处理\n  - 需要忽略时用对话框提示用户（用户看不到控制台）\n  - 错误应向上传到 DOM（如 `window`），由 `ErrorHandler` 展示友好对话框\n  - 反例：`try { something() } catch (e) { console.error(e) }` → 直接 `something()`\n- 错误处理（**Rust / Tauri 命令**）：命令在运行中不能让进程因未捕获错误闪退，须在函数内妥善处理错误并返回给前端。\n- UI：优先复用 shadcn 与 `.agents/skills/ui` 中的约定（`Dialog`、`toast` 等）。\n- 快捷键 / 设置：分别遵循 `create-keybind`、`create-setting-item` skill。\n\n## Commands\n\n包管理：`pnpm`。常用：\n\n- 开发：`pnpm dev`\n- Lint：`pnpm lint` / `pnpm lint:fix`\n- 测试：`pnpm test`（vitest）\n- 类型检查：**使用 `type-check` skill**（`pnpm --filter @graphif/project-graph type-check`）\n\n**禁止** Agent 运行 `build` / `build:ci` / `build:no-tauri` / `tauri build` 等构建命令。验证改动用 type-check（及必要的 lint/test），不要 build。\n\n## Agent constraints\n\n- **依赖 API / 用法**：禁止用 shell 在 `node_modules` 里搜索、翻源码或类型定义。需要了解第三方库时，读取该项目的官方文档（优先 `llms.txt`，例如 `https://<pkg-docs>/llms.txt`），或使用已有 skill；不要 `grep` / `find` / `cat` `node_modules/**`。\n- **实现方式**：禁止用 `python`、`python3`、`node -e`、`node --eval`、内联 shell 脚本等做文本处理或批量改文件。应直接用读/写/编辑文件的工具（Read / Write / Edit 等）完成；需要多文件改动时逐个编辑，不要写临时脚本。\n- **构建**：禁止 build（见上）；验证用 type-check skill。\n\n## Commit Message\n\n使用 [Conventional Commits](https://www.conventionalcommits.org/)，例如：`feat: ...`、`fix: ...`、`refactor: ...`。\n\n<!-- File: .agents/skills/create-keybind/SKILL.md (Tokens: ~3987 | Category: anthropic-skill) -->\n---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n- 印度尼西亚语：`app/src/locales/id.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = KeyBindsUI.getUIKeyBind(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n\n<!-- File: .agents/skills/create-setting-item/SKILL.md (Tokens: ~2799 | Category: anthropic-skill) -->\n---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 接地气繁体中文\n- `app/src/locales/id.yml` - 印度尼西亚语\n\n**翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n\n<!-- File: .agents/skills/shadcn/cli.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Shadcn Skill\n\nPath: `.agents/skills/shadcn/cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/cli.md)\n\n<!-- File: .agents/skills/shadcn/customization.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Shadcn Skill\n\nPath: `.agents/skills/shadcn/customization.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/customization.md)\n\n<!-- File: .agents/skills/shadcn/evals/evals.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Evals Skill\n\nPath: `.agents/skills/shadcn/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/evals/evals.json)\n\n<!-- File: .agents/skills/shadcn/mcp.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Shadcn Skill\n\nPath: `.agents/skills/shadcn/mcp.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/mcp.md)\n\n<!-- File: .agents/skills/shadcn/rules/base-vs-radix.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/base-vs-radix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/base-vs-radix.md)\n\n<!-- File: .agents/skills/shadcn/rules/composition.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/composition.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/composition.md)\n\n<!-- File: .agents/skills/shadcn/rules/forms.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/forms.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/forms.md)\n\n<!-- File: .agents/skills/shadcn/rules/icons.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/icons.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/icons.md)\n\n<!-- File: .agents/skills/shadcn/rules/styling.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.agents/skills/shadcn/rules/styling.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/shadcn/rules/styling.md)\n\n<!-- File: .agents/skills/shadcn/SKILL.md (Tokens: ~4278 | Category: anthropic-skill) -->\n---\nname: shadcn\ndescription: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for \"shadcn init\", \"create an app with --preset\", or \"switch to --preset\".\nuser-invocable: false\nallowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)\n---\n\n# shadcn/ui\n\nA framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.\n\n> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.\n\n## Current Project Context\n\n```json\n!`npx shadcn@latest info --json`\n```\n\nThe JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.\n\n## Principles\n\n1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.\n2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.\n3. **Use built-in variants before custom styles.** `variant=\"outline\"`, `size=\"sm\"`, etc.\n4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.\n\n## Critical Rules\n\nThese rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.\n\n### Styling & Tailwind → [styling.md](./rules/styling.md)\n\n- **`className` for layout, not styling.** Never override component colors or typography.\n- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.\n- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.\n- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.\n- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).\n- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.\n- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.\n\n### Forms & Inputs → [forms.md](./rules/forms.md)\n\n- **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.\n- **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.\n- **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**\n- **Option sets (2–7 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.\n- **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.\n- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.\n\n### Component Structure → [composition.md](./rules/composition.md)\n\n- **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.\n- **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)\n- **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className=\"sr-only\"` if visually hidden.\n- **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.\n- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.\n- **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.\n- **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.\n\n### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)\n\n- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.\n- **Callouts use `Alert`.** Don't build custom styled divs.\n- **Empty states use `Empty`.** Don't build custom empty state markup.\n- **Toast via `sonner`.** Use `toast()` from `sonner`.\n- **Use `Separator`** instead of `<hr>` or `<div className=\"border-t\">`.\n- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.\n- **Use `Badge`** instead of custom styled spans.\n\n### Icons → [icons.md](./rules/icons.md)\n\n- **Icons in `Button` use `data-icon`.** `data-icon=\"inline-start\"` or `data-icon=\"inline-end\"` on the icon.\n- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.\n- **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.\n\n### CLI\n\n- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest apply --preset <code>` for existing projects, or `npx shadcn@latest init --preset <code>` when initializing.\n\n## Key Patterns\n\nThese are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.\n\n```tsx\n// Form layout: FieldGroup + Field, not div + Label.\n<FieldGroup>\n  <Field>\n    <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n    <Input id=\"email\" />\n  </Field>\n</FieldGroup>\n\n// Validation: data-invalid on Field, aria-invalid on the control.\n<Field data-invalid>\n  <FieldLabel>Email</FieldLabel>\n  <Input aria-invalid />\n  <FieldDescription>Invalid email.</FieldDescription>\n</Field>\n\n// Icons in buttons: data-icon, no sizing classes.\n<Button>\n  <SearchIcon data-icon=\"inline-start\" />\n  Search\n</Button>\n\n// Spacing: gap-*, not space-y-*.\n<div className=\"flex flex-col gap-4\">  // correct\n<div className=\"space-y-4\">           // wrong\n\n// Equal dimensions: size-*, not w-* h-*.\n<Avatar className=\"size-10\">   // correct\n<Avatar className=\"w-10 h-10\"> // wrong\n\n// Status colors: Badge variants or semantic tokens, not raw colors.\n<Badge variant=\"secondary\">+20.1%</Badge>    // correct\n<span className=\"text-emerald-600\">+20.1%</span> // wrong\n```\n\n## Component Selection\n\n| Need                       | Use                                                                                                 |\n| -------------------------- | --------------------------------------------------------------------------------------------------- |\n| Button/action              | `Button` with appropriate variant                                                                   |\n| Form inputs                | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |\n| Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem`                                                                   |\n| Data display               | `Table`, `Card`, `Badge`, `Avatar`                                                                  |\n| Navigation                 | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination`                                     |\n| Overlays                   | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation)       |\n| Feedback                   | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner`                                        |\n| Command palette            | `Command` inside `Dialog`                                                                           |\n| Charts                     | `Chart` (wraps Recharts)                                                                            |\n| Layout                     | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible`                          |\n| Empty states               | `Empty`                                                                                             |\n| Menus                      | `DropdownMenu`, `ContextMenu`, `Menubar`                                                            |\n| Tooltips/info              | `Tooltip`, `HoverCard`, `Popover`                                                                   |\n\n## Key Fields\n\nThe injected project context contains these key fields:\n\n- **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.\n- **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `\"use client\"` at the top of the file. Always reference this field when advising on the directive.\n- **`tailwindVersion`** → `\"v4\"` uses `@theme inline` blocks; `\"v3\"` uses `tailwind.config.js`.\n- **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.\n- **`style`** → component visual treatment (e.g. `nova`, `vega`).\n- **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.\n- **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.\n- **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.\n- **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).\n- **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).\n\nSee [cli.md — `info` command](./cli.md) for the full field reference.\n\n## Component Docs, Examples, and Usage\n\nRun `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.\n\n```bash\nnpx shadcn@latest docs button dialog select\n```\n\n**When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.\n\n## Workflow\n\n1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.\n2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.\n3. **Find components** — `npx shadcn@latest search`.\n4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.\n5. **Install or update** — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).\n6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.\n7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.\n8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says \"add a login block\" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.\n9. **Switching presets** — Ask the user first: **overwrite**, **partial**, **merge**, or **skip**?\n   - **Overwrite**: `npx shadcn@latest apply --preset <code>`. Overwrites detected components, fonts, and CSS variables.\n   - **Partial**: `npx shadcn@latest apply --preset <code> --only theme,font`. Updates only the selected preset parts without reinstalling UI components. Supported values are `theme` and `font`; comma-separated combinations are allowed. `icon` is intentionally not supported, because icon changes may require full component reinstall and transforms.\n   - **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.\n   - **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.\n   - **Important**: Always run preset commands inside the user's project directory. `apply` only works in an existing project with a `components.json` file. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.\n\n## Updating Components\n\nWhen the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**\n\n1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.\n2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.\n3. Decide per file based on the diff:\n   - No local changes → safe to overwrite.\n   - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.\n   - User says \"just update everything\" → use `--overwrite`, but confirm first.\n4. **Never use `--overwrite` without the user's explicit approval.**\n\n## Quick Reference\n\n```bash\n# Create a new project.\nnpx shadcn@latest init --name my-app --preset base-nova\nnpx shadcn@latest init --name my-app --preset a2r6bw --template vite\n\n# Create a monorepo project.\nnpx shadcn@latest init --name my-app --preset base-nova --monorepo\nnpx shadcn@latest init --name my-app --preset base-nova --template next --monorepo\n\n# Initialize existing project.\nnpx shadcn@latest init --preset base-nova\nnpx shadcn@latest init --defaults  # shortcut: --template=next --preset=nova (base style implied)\n\n# Apply a preset to an existing project.\nnpx shadcn@latest apply --preset a2r6bw\nnpx shadcn@latest apply a2r6bw\nnpx shadcn@latest apply --preset a2r6bw --only theme\nnpx shadcn@latest apply --preset a2r6bw --only font\nnpx shadcn@latest apply --preset a2r6bw --only theme,font\n\n# Add components.\nnpx shadcn@latest add button card dialog\nnpx shadcn@latest add @magicui/shimmer-button\nnpx shadcn@latest add --all\n\n# Preview changes before adding/updating.\nnpx shadcn@latest add button --dry-run\nnpx shadcn@latest add button --diff button.tsx\nnpx shadcn@latest add @acme/form --view button.tsx\n\n# Search registries.\nnpx shadcn@latest search @shadcn -q \"sidebar\"\nnpx shadcn@latest search @tailark -q \"stats\"\n\n# Get component docs and example URLs.\nnpx shadcn@latest docs button dialog select\n\n# View registry item details (for items not yet installed).\nnpx shadcn@latest view @shadcn/button\n```\n\n**Named presets:** `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`\n**Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)\n**Preset codes:** Version-prefixed base62 strings (e.g. `a2r6bw` or `b0`), from [ui.shadcn.com](https://ui.shadcn.com).\n\n## Detailed References\n\n- [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states\n- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading\n- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects\n- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index\n- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion\n- [cli.md](./cli.md) — Commands, flags, presets, templates\n- [customization.md](./customization.md) — Theming, CSS variables, extending components\n\n<!-- File: .agents/skills/suggest-lucide-icons/changelog.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Suggest-lucide-icons Skill\n\nPath: `.agents/skills/suggest-lucide-icons/changelog.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/.agents/skills/suggest-lucide-icons/changelog.md)\n\n<!-- File: .agents/skills/suggest-lucide-icons/SKILL.md (Tokens: ~668 | Category: anthropic-skill) -->\n---\nname: suggest-lucide-icons\ndescription: \"Suggest relevant Lucide icons for concepts or UI placements. Searches lucide.dev to find actual icons that symbolize ideas or fit design contexts. Use when needing icons for UI work, documentation, or symbolic representation.\"\nmodel: sonnet, inherit\nmetadata:\n  author: nweii\n  version: \"1.2.0\"\n---\n\n# Suggest Lucide Icons\n\nSuggest the most relevant icons from the [Lucide open source icon pack](https://lucide.dev) to symbolize a concept or fit specific UI placements. I am skilled in symbolic interpretation and mental associations across culture, symbology, science, and design.\n\n## Input\n\nProvide one or both:\n\n- **Concept**: The idea, action, or meaning to represent\n- **Screenshot**: UI context showing where icons are needed\n\n## Naming conventions\n\nIcon names follow strict rules — apply these when generating candidates:\n\n- **kebab-case**: `arrow-up` not `Arrow Up`\n- **International English**: `color` not `colour`\n- **Group variants**: `<group>-<variant>` — e.g. `badge-plus` is based on `badge`\n- **Multiple elements, different sizes**: list largest first — `circle-person` if circle is bigger\n- **Element with modifier**: `[element]-[modifier]` — `circle-dashed` not `dashed-circle`; combined: `circle-dashed-heart-broken`\n\n## Process\n\n1. **Brainstorm associations**\n   - Key ideas and visual metaphors related to the concept\n   - Context clues from screenshot if provided\n   - Generate 4–6 candidate icon names in kebab-case (e.g. `arrow-right`, `circle-check`)\n\n2. **Verify candidates**\n   - For each candidate, fetch: `https://unpkg.com/lucide-static@latest/icons/[icon-name].svg`\n   - Use WebFetch or follow redirects (`curl -L`) — unpkg issues a 302 before the final response\n   - SVG content in the response means the icon exists; a 404 means it doesn't\n   - Discard any that don't exist; try alternate names if needed to reach 3 confirmed icons\n\n3. **Present 3 confirmed candidates**\n   - Icon name\n   - Why it fits (symbolic meaning, visual clarity, context appropriateness)\n\n4. **Recommend best choice**\n   - Single strongest option\n   - Rationale for recommendation\n\n## Guidelines\n\n- Never suggest an icon without confirming it via the unpkg URL\n- Never suggest made-up icon names\n- If fewer than 3 candidates survive verification, brainstorm more before giving up\n- If no good matches exist after thorough searching, say so\n- For screenshots, tailor to specific design context\n- Provide a distinct recommendation for each icon needed\n- Ready for multiple feedback rounds to refine suggestions\n\n## Output Format\n\n**Brainstorm**: [Key associations and metaphors]\n\n**Candidate Icons**:\n\n1. **icon-name** — Explanation of fit\n2. **icon-name** — Explanation of fit\n3. **icon-name** — Explanation of fit\n\n**Recommendation**: **icon-name** — Why this is the strongest choice for [context]\n\n<!-- File: .agents/skills/type-check/SKILL.md (Tokens: ~53 | Category: anthropic-skill) -->\n---\nname: type-check\ndescription: 运行 TypeScript 类型检查\n---\n\n# type-check\n\n运行以下命令来执行 TypeScript 类型检查：\n\n```bash\npnpm --filter @graphif/project-graph type-check\n```\n\n<!-- File: .agents/skills/ui/SKILL.md (Tokens: ~402 | Category: anthropic-skill) -->\n---\nname: ui\ndescription: 建立于 shadcn/ui 之上的附加 UI 组件\n---\n\n# ui\n\n## `Dialog` (`app/src/components/ui/dialog.tsx`)\n\n- `Dialog.confirm`: 显示一个确认对话框，返回一个 Promise，用户点击确认时 resolve，点击取消时 reject。\n- `Dialog.input`: 显示一个输入对话框，返回一个 Promise，用户输入内容并点击确认时 resolve，点击取消时 reject。\n- `Dialog.copy`: 显示一个复制对话框，返回一个 Promise，用户关闭对话框时 resolve。\n- `Dialog.buttons`: 显示一个带有自定义按钮的对话框，返回一个 Promise，用户点击任意按钮时 resolve，参数为被点击按钮的 `id`。\n\n## `toast()` (`import { toast } from \"sonner\"`)\n\n- `toast`: 显示一个通用的、info 类型的 toast 消息，参数为消息内容，尽量不要使用此函数，推荐使用下面的 `toast.success()` 等方法。\n- `toast.success`: 显示一个成功类型的 toast 消息，参数为消息内容。\n- `toast.error`: 显示一个错误类型的 toast 消息，参数为消息内容。\n- `toast.warning`: 显示一个警告类型的 toast 消息，参数为消息内容。\n- `toast.promise`: 见下面的示例代码。\n\n```ts\nawait toast\n  .promise(\n    async () => {\n      // 异步操作\n    },\n    {\n      loading: \"promise进行中的提示\",\n      success: \"resolve后的提示\",\n      error: \"reject后的提示\",\n    },\n  )\n  // unwrap方法会直接把传入的promise返回出来\n  .unwrap();\n```\n\n<!-- File: .trae/skills/create-keybind/SKILL.md (Tokens: ~3946 | Category: anthropic-skill) -->\n---\nname: create-keybind\ndescription: 指导如何在 Project Graph 项目中创建新的快捷键。当用户需要添加新的快捷键、修改快捷键绑定或需要了解快捷键系统的实现方式时使用此技能。\n---\n\n# 创建新的快捷键功能\n\n本技能指导如何在 Project Graph 项目中创建新的快捷键。\n\n## 创建快捷键的步骤\n\n创建新快捷键需要完成以下 4 个步骤：\n\n### 1. 在 shortcutKeysRegister.tsx 中注册快捷键\n\n在 `app/src/core/service/controlService/shortcutKeysEngine/shortcutKeysRegister.tsx` 文件的 `allKeyBinds` 数组中添加新的快捷键定义。\n\n**快捷键定义结构：**\n\n```typescript\n{\n  id: \"uniqueKeybindId\",           // 唯一标识符，使用驼峰命名\n  defaultKey: \"A-S-m\",              // 默认快捷键组合\n  onPress: (project?: Project) => void,  // 按下时的回调函数\n  onRelease?: (project?: Project) => void, // 松开时的回调函数（可选）\n  isGlobal?: boolean,               // 是否为全局快捷键（可选，默认 false）\n  defaultEnabled?: boolean,         // 默认是否启用（可选，默认 true）\n}\n```\n\n**快捷键键位格式：**\n\n- `C-` = Ctrl (Windows/Linux) 或 Command (macOS)\n- `A-` = Alt (Windows/Linux) 或 Option (macOS)\n- `S-` = Shift\n- `M-` = Meta (macOS 上等同于 Command)\n- `F11`, `F12` 等 = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape` = 特殊键\n- 普通字母直接写，如 `m`, `t`, `k` 等\n- 多个按键用空格分隔，如 `\"t t t\"` 表示连续按三次 t\n\n**注意：** Mac 系统会自动将 `C-` 和 `M-` 互换，所以不需要手动处理平台差异。\n\n**示例：**\n\n```typescript\n{\n  id: \"setWindowToMiniSize\",\n  defaultKey: \"A-S-m\",  // Alt+Shift+M\n  onPress: async () => {\n    const window = getCurrentWindow();\n    // 执行操作\n    await window.setSize(new LogicalSize(width, height));\n  },\n},\n```\n\n**快捷键类型说明：**\n\n- **项目级快捷键（默认）**：需要项目上下文，`onPress` 会接收 `project` 参数\n- **全局快捷键（`isGlobal: true`）**：使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发\n\n**使用 Tauri API 时的类型处理：**\n\n如果快捷键需要使用 Tauri 窗口 API（如 `setSize`），需要导入正确的类型：\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\"; // 或 PhysicalSize\n\n// 使用 LogicalSize（推荐，会自动处理 DPI 缩放）\nawait window.setSize(new LogicalSize(width, height));\n\n// 或使用 PhysicalSize（物理像素）\nawait window.setSize(new PhysicalSize(width, height));\n```\n\n### 2. 将快捷键添加到分组中\n\n在 `app/src/sub/SettingsWindow/keybinds.tsx` 文件的 `shortcutKeysGroups` 数组中，将新快捷键的 `id` 添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  {\n    title: \"basic\",              // 分组标识符（对应翻译文件中的 key）\n    icon: <Keyboard />,          // 分组图标\n    keys: [                      // 该分组包含的快捷键 ID 列表\n      \"saveFile\",\n      \"openFile\",\n      \"undo\",\n      \"redo\",\n      // ...\n    ],\n  },\n  {\n    title: \"ui\",                 // UI 控制分组\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",     // 添加新快捷键\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**可用分组：**\n\n- `basic` - 基础快捷键（撤销、重做、保存、打开等）\n- `camera` - 摄像机控制（移动、缩放、重置视野等）\n- `app` - 应用控制（切换项目、切换模式等）\n- `ui` - UI 控制（关闭窗口、全屏、窗口大小等）\n- `draw` - 涂鸦相关\n- `select` - 切换选择\n- `moveEntity` - 移动实体\n- `generateTextNodeInTree` - 生长节点\n- `generateTextNodeRoundedSelectedNode` - 在选中节点周围生成节点\n- `aboutTextNode` - 关于文本节点（分割、合并、创建等）\n- `section` - 分组框相关\n- `edge` - 连线相关\n- `color` - 颜色相关\n- `node` - 节点相关\n\n**分组选择指南：**\n\n- **UI 控制（`ui`）**：窗口管理、界面切换、全屏、窗口大小等\n- **基础快捷键（`basic`）**：文件操作、编辑操作（撤销、重做、复制、粘贴等）\n- **摄像机控制（`camera`）**：视野移动、缩放、重置等\n- **应用控制（`app`）**：项目切换、模式切换等\n- **文本节点相关（`aboutTextNode`）**：节点创建、分割、合并、编辑等\n- **其他**：根据功能特性选择最合适的分组\n\n**注意：** 如果快捷键不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 创建新的分组（需要同时更新翻译文件）\n\n### 3. 添加翻译文本\n\n在所有语言文件中添加翻译：\n\n- `app/src/locales/zh_CN.yml` - 简体中文\n- `app/src/locales/zh_TW.yml` - 繁体中文\n- `app/src/locales/en.yml` - 英文\n- `app/src/locales/zh_TWC.yml` - 繁体中文（台湾）\n\n**翻译结构：**\n\n在 `keyBinds` 部分添加：\n\n```yaml\nkeyBinds:\n  keybindId:\n    title: \"快捷键标题\"\n    description: |\n      快捷键的详细描述\n      可以多行\n      说明快捷键的功能和使用场景\n```\n\n**示例：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**翻译文件位置：**\n\n- 简体中文：`app/src/locales/zh_CN.yml`\n- 繁体中文：`app/src/locales/zh_TW.yml`\n- 繁体中文（台湾）：`app/src/locales/zh_TWC.yml`\n- 英文：`app/src/locales/en.yml`\n\n**注意：**\n\n- 翻译键名（`keybindId`）必须与快捷键定义中的 `id` 完全一致\n- 所有语言文件都需要添加翻译，否则会显示默认值或键名\n\n### 4. 更新分组翻译（如果需要创建新分组）\n\n如果创建了新的快捷键分组，需要在所有语言文件的 `keyBindsGroup` 部分添加分组翻译：\n\n```yaml\nkeyBindsGroup:\n  newGroupName:\n    title: \"新分组标题\"\n    description: |\n      分组的详细描述\n      说明该分组包含哪些类型的快捷键\n```\n\n**示例：**\n\n```yaml\nkeyBindsGroup:\n  ui:\n    title: UI控制\n    description: |\n      用于控制UI的一些功能\n```\n\n## 快捷键类型详解\n\n### 项目级快捷键（默认）\n\n项目级快捷键需要项目上下文，`onPress` 函数会接收 `project` 参数：\n\n```typescript\n{\n  id: \"myKeybind\",\n  defaultKey: \"C-k\",\n  onPress: (project) => {\n    if (!project) {\n      toast.warning(\"请先打开工程文件\");\n      return;\n    }\n    // 使用 project 进行操作\n    project.stageManager.doSomething();\n  },\n}\n```\n\n### UI 级别快捷键\n\nUI 级别快捷键不需要项目上下文，可以在没有打开项目时使用：\n\n```typescript\n{\n  id: \"myUIKeybind\",\n  defaultKey: \"A-S-m\",\n  onPress: async () => {\n    // 不需要 project 参数\n    const window = getCurrentWindow();\n    await window.setSize(new LogicalSize(300, 300));\n  },\n}\n```\n\n### 全局快捷键\n\n全局快捷键使用 Tauri 全局快捷键系统，即使应用不在焦点也能触发：\n\n```typescript\n{\n  id: \"myGlobalKeybind\",\n  defaultKey: \"CommandOrControl+Shift+G\",\n  onPress: () => {\n    // 全局快捷键逻辑\n  },\n  isGlobal: true,  // 标记为全局快捷键\n}\n```\n\n**注意：** 全局快捷键的键位格式与普通快捷键不同，使用 `CommandOrControl+Shift+G` 格式。\n\n## 访问快捷键配置\n\n在代码中访问快捷键配置：\n\n```typescript\nimport { KeyBindsUI } from \"@/core/service/controlService/shortcutKeysEngine/KeyBindsUI\";\n\n// 获取快捷键配置\nconst config = await KeyBindsUI.get(\"keybindId\");\n\n// 修改快捷键\nawait KeyBindsUI.changeOneUIKeyBind(\"keybindId\", \"new-key-combination\");\n\n// 重置所有快捷键\nawait KeyBindsUI.resetAllKeyBinds();\n```\n\n## 注意事项\n\n1. **快捷键 ID 命名规范：** 使用驼峰命名法（camelCase），如 `setWindowToMiniSize`\n2. **唯一性：** 快捷键 ID 必须唯一，不能与现有快捷键重复\n3. **默认键位：** 选择不冲突的默认键位组合\n4. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n5. **翻译键名：** 翻译文件中的键名必须与快捷键的 `id` 完全一致\n6. **分组必须：** 所有快捷键都必须添加到 `shortcutKeysGroups` 中的相应分组，否则不会在设置页面中显示\n7. **分组选择：** 根据快捷键的功能特性选择合适的分组，保持设置页面的逻辑清晰\n8. **Tauri API 类型：** 使用窗口 API 时，需要使用 `LogicalSize` 或 `PhysicalSize` 类型，不能直接使用普通对象\n9. **Mac 兼容性：** Mac 系统会自动将 `C-` 和 `M-` 互换，无需手动处理\n10. **UI vs 项目级：** 根据快捷键是否需要项目上下文选择合适的类型\n\n## 完整示例\n\n假设要添加一个\"设置窗口为迷你大小\"的快捷键：\n\n**1. shortcutKeysRegister.tsx - 注册快捷键：**\n\n```typescript\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { LogicalSize } from \"@tauri-apps/api/dpi\";\nimport { Settings } from \"@/core/service/Settings\";\n\nexport const allKeyBinds: KeyBindItem[] = [\n  // ... 其他快捷键\n  {\n    id: \"setWindowToMiniSize\",\n    defaultKey: \"A-S-m\",\n    onPress: async () => {\n      const window = getCurrentWindow();\n      // 如果当前是最大化状态，先取消最大化\n      if (await window.isMaximized()) {\n        await window.unmaximize();\n        store.set(isWindowMaxsizedAtom, false);\n      }\n      // 如果当前是全屏状态，先退出全屏\n      if (await window.isFullscreen()) {\n        await window.setFullscreen(false);\n      }\n      // 设置窗口大小为设置中的迷你窗口大小\n      const width = Settings.windowCollapsingWidth;\n      const height = Settings.windowCollapsingHeight;\n      await window.setSize(new LogicalSize(width, height));\n    },\n  },\n  // ... 其他快捷键\n];\n```\n\n**2. keybinds.tsx - 添加到分组：**\n\n```typescript\nexport const shortcutKeysGroups: ShortcutKeysGroup[] = [\n  // ... 其他分组\n  {\n    title: \"ui\",\n    icon: <AppWindow />,\n    keys: [\n      \"closeAllSubWindows\",\n      \"toggleFullscreen\",\n      \"setWindowToMiniSize\",  // 添加到 UI 控制分组\n      // ...\n    ],\n  },\n  // ... 其他分组\n];\n```\n\n**3. zh_CN.yml - 添加翻译：**\n\n```yaml\nkeyBinds:\n  setWindowToMiniSize:\n    title: 设置窗口为迷你大小\n    description: |\n      将窗口大小设置为设置中配置的迷你窗口宽度和高度。\n```\n\n**4. 其他语言文件也需要添加相应翻译**\n\n## 快捷键键位格式参考\n\n**修饰键：**\n\n- `C-` = Ctrl/Command\n- `A-` = Alt/Option\n- `S-` = Shift\n- `M-` = Meta (macOS)\n\n**特殊键：**\n\n- `F1` - `F12` = 功能键\n- `arrowup`, `arrowdown`, `arrowleft`, `arrowright` = 方向键\n- `home`, `end`, `pageup`, `pagedown` = 导航键\n- `space`, `enter`, `escape`, `tab`, `backspace`, `delete` = 特殊键\n\n**组合示例：**\n\n- `\"C-s\"` = Ctrl+S\n- `\"A-S-m\"` = Alt+Shift+M\n- `\"C-A-S-t\"` = Ctrl+Alt+Shift+T\n- `\"F11\"` = F11\n- `\"C-F11\"` = Ctrl+F11\n- `\"t t t\"` = 连续按三次 T\n- `\"arrowup\"` = 上方向键\n- `\"S-arrowup\"` = Shift+上方向键\n\n## 快捷键设置页面\n\n快捷键添加到分组后，会在设置页面的\"快捷键绑定\"标签页中自动显示：\n\n1. 用户可以在设置页面查看所有快捷键\n2. 用户可以自定义快捷键键位\n3. 用户可以启用/禁用快捷键\n4. 用户可以重置快捷键为默认值\n\n快捷键会自动保存到 `keybinds2.json` 文件中，并在应用重启后恢复。\n\n<!-- File: .trae/skills/create-setting-item/SKILL.md (Tokens: ~2773 | Category: anthropic-skill) -->\n---\nname: create-setting-item\ndescription: 指导如何在 Project Graph 项目中创建新的设置项。当用户需要添加新的设置项、配置选项或需要了解设置系统的实现方式时使用此技能。\n---\n\n# 创建新的设置项功能\n\n本技能指导如何在 Project Graph 项目中创建新的设置项。\n\n## 创建设置项的步骤\n\n创建新设置项需要完成以下 5 个步骤：\n\n### 1. 在 Settings.tsx 中添加 Schema 定义\n\n在 `app/src/core/service/Settings.tsx` 文件的 `settingsSchema` 对象中添加新的设置项定义。\n\n**支持的 Zod 类型：**\n\n- `z.boolean().default(false)` - 布尔值开关\n- `z.number().min(x).max(y).default(z)` - 数字（可添加 `.int()` 限制为整数）\n- `z.string().default(\"\")` - 字符串\n- `z.union([z.literal(\"option1\"), z.literal(\"option2\")]).default(\"option1\")` - 枚举选择\n- `z.tuple([z.number(), z.number(), z.number(), z.number()]).default([0,0,0,0])` - 元组（如颜色 RGBA）\n\n**示例：**\n\n```typescript\n// 布尔值设置\nenableNewFeature: z.boolean().default(false),\n\n// 数字范围设置（带滑块）\nnewSliderValue: z.number().min(0).max(100).int().default(50),\n\n// 枚举选择设置\nnewMode: z.union([z.literal(\"mode1\"), z.literal(\"mode2\")]).default(\"mode1\"),\n```\n\n### 2. 在 SettingsIcons.tsx 中添加图标\n\n在 `app/src/core/service/SettingsIcons.tsx` 文件的 `settingsIcons` 对象中添加对应的图标。\n\n**步骤：**\n\n1. 从 `lucide-react` 导入合适的图标组件\n2. 在 `settingsIcons` 对象中添加映射：`settingKey: IconComponent`\n\n**示例：**\n\n```typescript\nimport { NewIcon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableNewFeature: NewIcon,\n};\n```\n\n### 3. 添加翻译文本\n\n> **注意：** 关于翻译管理的详细说明，请参考 `manage-translations` skill。\n\n在所有语言文件中添加翻译。首先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件，然后为每个语言文件添加翻译。\n\n**设置项的翻译结构：**\n\n```yaml\nsettings:\n  settingKey:\n    title: \"设置项标题\"\n    description: |\n      设置项的详细描述\n      可以多行\n    options: # 仅枚举类型需要\n      option1: \"选项1显示文本\"\n      option2: \"选项2显示文本\"\n```\n\n**示例：**\n\n```yaml\nsettings:\n  enableNewFeature:\n    title: \"启用新功能\"\n    description: |\n      开启后将启用新功能特性。\n      此功能可能会影响性能。\n  newMode:\n    title: \"新模式\"\n    description: \"选择新的模式选项\"\n    options:\n      mode1: \"模式一\"\n      mode2: \"模式二\"\n```\n\n### 4. 将设置项添加到分组中\n\n在 `app/src/sub/SettingsWindow/settings.tsx` 文件的 `categories` 对象中，将新设置项的键名添加到相应的分组数组中。\n\n**分组结构：**\n\n```typescript\nconst categories = {\n  visual: {           // 一级分类：视觉\n    basic: [...],     // 二级分组：基础\n    background: [...], // 二级分组：背景\n    node: [...],      // 二级分组：节点样式\n    // ...\n  },\n  automation: {       // 一级分类：自动化\n    autoNamer: [...],\n    autoSave: [...],\n    // ...\n  },\n  control: {         // 一级分类：控制\n    mouse: [...],\n    cameraMove: [...],\n    // ...\n  },\n  performance: {     // 一级分类：性能\n    memory: [...],\n    cpu: [...],\n    // ...\n  },\n  ai: {              // 一级分类：AI\n    api: [...],\n  },\n};\n```\n\n**添加设置项到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableNewFeature\", // 添加新设置项\n      // ...\n    ],\n  },\n  // ...\n};\n```\n\n**分组选择指南：**\n\n- **visual（视觉）**：界面显示、主题、背景、节点样式、连线样式等\n  - `basic`: 基础视觉设置\n  - `background`: 背景相关设置\n  - `node`: 节点样式设置\n  - `edge`: 连线样式设置\n  - `section`: 分组框的样式设置\n  - `entityDetails`: 实体详情面板设置\n  - `debug`: 调试相关设置\n  - `miniWindow`: 迷你窗口设置\n  - `experimental`: 实验性视觉功能\n- **automation（自动化）**：自动保存、自动备份、自动命名等\n  - `autoNamer`: 自动命名相关\n  - `autoSave`: 自动保存相关\n  - `autoBackup`: 自动备份相关\n  - `autoImport`: 自动导入相关\n- **control（控制）**：鼠标、键盘、触摸板、相机控制等\n  - `mouse`: 鼠标相关设置\n  - `touchpad`: 触摸板设置\n  - `cameraMove`: 相机移动设置\n  - `cameraZoom`: 相机缩放设置\n  - `objectSelect`: 对象选择设置\n  - `textNode`: 文本节点编辑设置\n  - `edge`: 连线操作设置\n  - `generateNode`: 节点生成设置\n  - `gamepad`: 游戏手柄设置\n- **performance（性能）**：内存、CPU、渲染性能相关\n  - `memory`: 内存相关设置\n  - `cpu`: CPU 相关设置\n  - `render`: 渲染相关设置\n  - `experimental`: 实验性性能功能\n- **ai（AI）**：AI 相关设置\n  - `api`: AI API 配置\n\n**注意：** 如果设置项不属于任何现有分组，可以：\n\n1. 添加到最接近的现有分组\n2. 在相应分类下创建新的分组（需要同时更新翻译文件中的分类结构）\n\n### 5. 在设置页面中使用 SettingField 组件\n\n设置项添加到分组后，会在设置页面的相应分组中自动显示。如果需要手动渲染或添加额外内容，可以使用 `SettingField` 组件：\n\n**基本用法：**\n\n```tsx\nimport { SettingField } from \"@/components/ui/field\";\n\n<SettingField settingKey=\"enableNewFeature\" />;\n```\n\n**带额外内容的用法：**\n\n```tsx\n<SettingField settingKey=\"enableNewFeature\" extra={<Button>额外按钮</Button>} />\n```\n\n**注意：** 大多数情况下，只需要将设置项添加到 `categories` 中即可，设置页面会自动渲染。只有在需要特殊布局或额外功能时才需要手动使用 `SettingField` 组件。\n\n## SettingField 组件的自动类型推断\n\n`SettingField` 组件会根据 schema 定义自动渲染对应的 UI 控件：\n\n- **字符串类型** → `Input` 输入框\n- **数字类型（有 min/max）** → `Slider` 滑块 + `Input` 数字输入框\n- **数字类型（无范围）** → `Input` 数字输入框\n- **布尔类型** → `Switch` 开关\n- **枚举类型（Union）** → `Select` 下拉选择框\n\n## 访问设置值\n\n在代码中访问设置值：\n\n```typescript\nimport { Settings } from \"@/core/service/Settings\";\n\n// 读取设置值\nconst value = Settings.enableNewFeature;\n\n// 修改设置值\nSettings.enableNewFeature = true;\n\n// 监听设置变化（返回取消监听的函数）\nconst unsubscribe = Settings.watch(\"enableNewFeature\", (newValue) => {\n  console.log(\"设置已更改:\", newValue);\n});\n\n// React Hook 方式（在组件中使用）\nconst [value, setValue] = Settings.use(\"enableNewFeature\");\n```\n\n## 注意事项\n\n1. **设置项键名命名规范：** 使用驼峰命名法（camelCase），如 `enableNewFeature`\n2. **默认值：** 所有设置项都必须提供默认值（`.default()`）\n3. **类型安全：** TypeScript 会自动推断类型，确保类型一致性\n4. **持久化：** 设置值会自动保存到 `settings.json` 文件中\n5. **翻译键名：** 翻译文件中的键名必须与设置项的键名完全一致\n6. **图标可选：** 如果不需要图标，可以不在 `settingsIcons` 中添加，组件会使用 Fragment\n7. **分组必须：** 所有设置项都必须添加到 `categories` 对象中的相应分组，否则不会在设置页面中显示\n8. **分组选择：** 根据设置项的功能特性选择合适的分类和分组，保持设置页面的逻辑清晰\n\n## 完整示例\n\n假设要添加一个\"启用暗色模式\"的设置项：\n\n**1. Settings.tsx:**\n\n```typescript\nexport const settingsSchema = z.object({\n  // ... 其他设置项\n  enableDarkMode: z.boolean().default(false),\n});\n```\n\n**2. SettingsIcons.tsx:**\n\n```typescript\nimport { Moon } from \"lucide-react\";\n\nexport const settingsIcons = {\n  // ... 其他设置项\n  enableDarkMode: Moon,\n};\n```\n\n**3. zh_CN.yml:**\n\n```yaml\nsettings:\n  enableDarkMode:\n    title: \"启用暗色模式\"\n    description: \"开启后将使用暗色主题界面\"\n```\n\n**4. settings.tsx - 添加到分组：**\n\n```typescript\nconst categories = {\n  visual: {\n    basic: [\n      \"language\",\n      \"isClassroomMode\",\n      \"enableDarkMode\", // 添加到基础视觉设置分组\n      // ...\n    ],\n    // ...\n  },\n  // ...\n};\n```\n\n**5. 设置项会自动显示：**\n设置项添加到 `categories` 后，会在设置页面的\"视觉 > 基础\"分组中自动显示，无需手动使用 `SettingField` 组件。\n\n## 快捷设置栏支持\n\n如果希望设置项出现在快捷设置栏（Quick Settings Toolbar）中，需要：\n\n1. 确保设置项已正确创建（完成上述 4 步）\n2. 快捷设置栏会自动显示所有布尔类型的设置项\n3. 可以通过 `QuickSettingsManager.addQuickSetting()` 手动添加非布尔类型的设置项\n\n<!-- File: .trae/skills/manage-translations/SKILL.md (Tokens: ~311 | Category: anthropic-skill) -->\n---\nname: manage-translations\ndescription: \"指导如何在 Project Graph 项目中管理多语言翻译。当需要添加新翻译、更新现有翻译或了解翻译系统结构时使用此技能。\"\n---\n\n# 管理翻译\n\n本技能指导如何在 Project Graph 项目中管理多语言翻译。\n\n## 翻译文件位置\n\n所有翻译文件位于 `app/src/locales/` 目录下：\n\n```\napp/src/locales/\n├── en.yml      # 英文\n├── id.yml      # 印度尼西亚语\n├── zh_CN.yml   # 简体中文\n├── zh_TW.yml   # 繁体中文\n├── zh_TWC.yml  # 接地气繁体中文\n├── ...         # 其他语言翻译文件\n└── README.md   # 翻译系统说明\n```\n\n## 查看当前支持的语言\n\n在添加或修改翻译前，先查看 `app/src/locales/` 目录下有哪些 `.yml` 文件：\n\n```bash\nls app/src/locales/*.yml\n```\n\n<!-- File: packages/create-extprg/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Create-extprg Skill\n\nPath: `packages/create-extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/package.json)\n\n<!-- File: packages/create-extprg/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Create-extprg Skill\n\nPath: `packages/create-extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/create-extprg/tsconfig.json)\n\n<!-- File: packages/data-structures/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Data-structures Skill\n\nPath: `packages/data-structures/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/package.json)\n\n<!-- File: packages/data-structures/project.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Data-structures Skill\n\nPath: `packages/data-structures/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/project.json)\n\n<!-- File: packages/data-structures/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Data-structures Skill\n\nPath: `packages/data-structures/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/data-structures/tsconfig.json)\n\n<!-- File: packages/extprg-types/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extprg-types Skill\n\nPath: `packages/extprg-types/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/package.json)\n\n<!-- File: packages/extprg-types/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extprg-types Skill\n\nPath: `packages/extprg-types/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg-types/tsconfig.json)\n\n<!-- File: packages/extprg/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extprg Skill\n\nPath: `packages/extprg/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/package.json)\n\n<!-- File: packages/extprg/pnpm-lock.yaml (Tokens: ~300 | Category: anthropic-skill) -->\n# Extprg Skill\n\nPath: `packages/extprg/pnpm-lock.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/pnpm-lock.yaml)\n\n<!-- File: packages/extprg/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extprg Skill\n\nPath: `packages/extprg/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/graphif/project-graph/HEAD/packages/extprg/tsconfig.json)\n\n<!-- ... +8 more modular skills available via /api/skills/graphif/project-graph?path={path} -->\n</yakaai_skills>"}