docs/superpowers/plans/2026-03-21-logitech-cid-registry.md
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将分散在三处的 Logitech CID→名称硬编码映射统一为 LogitechCIDRegistry 单一数据源,使用 Solaar 项目的完整 CID 表 (~310 条),解决未知按键显示为 Logi(N) 的问题。
Architecture: 在 Mos/LogitechHID/ 模块中新建 LogitechCIDRegistry.swift 作为 CID 信息的唯一数据源,整合名称查询和 MosCode 映射。删除 MosInputEvent.swift 中的 LogitechCIDMap、LogitechHIDDebugPanel.swift 中的 HIDPPInfo.cidNames、KeyCode.swift 中 mouseMap 的 Logi 条目,所有消费者改为引用 Registry。
Tech Stack: Swift 4.0+, macOS 10.13+ deployment target
Spec: docs/superpowers/specs/2026-03-21-logitech-cid-registry-design.md
| File | Action | Responsibility |
|---|---|---|
Mos/LogitechHID/LogitechCIDRegistry.swift | Create | CID→名称映射表 (Solaar 数据源) + MosCode 转换 + 查询接口 |
Mos/InputEvent/MosInputEvent.swift | Modify | 删除 LogitechCIDMap (lines 51-102), 更新 3 处引用 |
Mos/LogitechHID/LogitechDeviceSession.swift | Modify | 更新 2 处 LogitechCIDMap + 5 处 HIDPPInfo.cidNames |
Mos/LogitechHID/LogitechHIDDebugPanel.swift | Modify | 删除 HIDPPInfo.cidNames, 更新 1 处引用 |
Mos/Keys/KeyCode.swift | Modify | 删除 mouseMap 中 1000~1007 条目 (8 条) |
Mos/Components/BrandTag.swift | Modify | 更新 1 处 LogitechCIDMap → Registry |
Mos/Windows/PreferencesWindow/ButtonsView/ButtonTableCellView.swift | Modify | 更新 1 处 LogitechCIDMap → Registry |
Mos/Windows/PreferencesWindow/ButtonsView/RecordedEvent.swift | Modify | ScrollHotkey.displayName 增加 Logi 码 fallback |
Mos/Windows/PreferencesWindow/ScrollingView/PreferencesScrollingViewController.swift | Modify | getBaseDisplayName 增加 Logi 码 fallback |
Files:
Create: Mos/LogitechHID/LogitechCIDRegistry.swift
Step 1: 创建文件,写入完整 CID 映射表
从 Solaar lib/logitech_receiver/special_keys.py (commit b9e0cf82) 移植完整 CONTROL 字典。
格式化规则: 下划线→空格, 双下划线→/, 保持原始大小写。
//
// LogitechCIDRegistry.swift
// Mos
// Logitech HID++ Control ID (CID) 名称注册表
//
// 数据来源: Solaar 项目
// https://github.com/pwr-Solaar/Solaar
// 文件: lib/logitech_receiver/special_keys.py
// Commit: b9e0cf823543ba1dadc8eb188083b5c8db6280b0
// 原始数据基于 Logitech 官方 controls.xml, 由 Solaar 社区维护和补充
// Solaar 项目采用 GPL-2.0 许可证
//
// Created by Mos on 2026/3/21.
// Copyright © 2026 Caldis. All rights reserved.
//
import Foundation
/// Logitech HID++ Control ID (CID) 注册表
/// 提供 CID→名称查询 和 CID↔MosCode 转换
struct LogitechCIDRegistry {
// MARK: - CID 名称表 (来自 Solaar special_keys.py CONTROL 字典)
// 名称已格式化: 下划线→空格, 双下划线→" / "
private static let cidNames: [UInt16: String] = [
// --- 多媒体 & 系统控制 (0x0001~0x004F) ---
0x0001: "Volume Up old",
0x0002: "Volume Down old",
0x0003: "Mute",
0x0004: "Play / Pause old",
0x0005: "Next",
0x0006: "Previous",
0x0007: "Stop",
0x0008: "Application Switcher",
0x0009: "Burn",
0x000A: "Calculator",
0x000B: "Calendar",
0x000C: "Close",
0x000D: "Eject",
0x000E: "Mail",
0x000F: "Help As HID",
0x0010: "Help As F1",
0x0011: "Launch Word Proc",
0x0012: "Launch Spreadsheet",
0x0013: "Launch Presentation",
0x0014: "Undo As Ctrl Z",
0x0015: "Undo As HID",
0x0016: "Redo As Ctrl Y",
0x0017: "Redo As HID",
0x0018: "Print As Ctrl P",
0x0019: "Print As HID",
0x001A: "Save As Ctrl S",
0x001B: "Save As HID",
0x001C: "Preset A",
0x001D: "Preset B",
0x001E: "Preset C",
0x001F: "Preset D",
0x0020: "Favorites",
0x0021: "Gadgets",
0x0022: "My Home",
0x0023: "Gadgets As Win G",
0x0024: "Maximize As HID",
0x0025: "Maximize As Win Shift M",
0x0026: "Minimize As HID",
0x0027: "Minimize As Win M",
0x0028: "Media Player",
0x0029: "Media Center Logi",
0x002A: "Media Center Msft",
0x002B: "Custom Menu",
0x002C: "Messenger",
0x002D: "My Documents",
0x002E: "My Music",
0x002F: "Webcam",
0x0030: "My Pictures",
0x0031: "My Videos",
0x0032: "My Computer As HID",
0x0033: "My Computer As Win E",
0x0034: "FN Key",
0x0035: "Launch Picture Viewer",
0x0036: "One Touch Search",
0x0037: "Preset 1",
0x0038: "Preset 2",
0x0039: "Preset 3",
0x003A: "Preset 4",
0x003B: "Record",
0x003C: "Internet Refresh",
0x003E: "Search",
0x003F: "Shuffle",
0x0040: "Sleep",
0x0041: "Internet Stop",
0x0042: "Synchronize",
0x0043: "Zoom",
0x0044: "Zoom In As HID",
0x0045: "Zoom In As Ctrl Wheel",
0x0046: "Zoom In As Cltr Plus", // Solaar 原文如此 (Cltr 非 Ctrl)
0x0047: "Zoom Out As HID",
0x0048: "Zoom Out As Ctrl Wheel",
0x0049: "Zoom Out As Ctrl Minus",
0x004A: "Zoom Reset",
0x004B: "Zoom Full Screen",
0x004C: "Print Screen",
0x004D: "Pause Break",
0x004E: "Scroll Lock",
0x004F: "Contextual Menu",
// --- 鼠标按键 (0x0050~0x006D) ---
0x0050: "Left Button",
0x0051: "Right Button",
0x0052: "Middle Button",
0x0053: "Back Button",
0x0054: "Back",
0x0055: "Back As Alt Win Arrow",
0x0056: "Forward Button",
0x0057: "Forward As HID",
0x0058: "Forward As Alt Win Arrow",
0x0059: "Button 6",
0x005A: "Left Scroll As Button 7",
0x005B: "Left Tilt",
0x005C: "Right Scroll As Button 8",
0x005D: "Right Tilt",
0x005E: "Button 9",
0x005F: "Button 10",
0x0060: "Button 11",
0x0061: "Button 12",
0x0062: "Button 13",
0x0063: "Button 14",
0x0064: "Button 15",
0x0065: "Button 16",
0x0066: "Button 17",
0x0067: "Button 18",
0x0068: "Button 19",
0x0069: "Button 20",
0x006A: "Button 21",
0x006B: "Button 22",
0x006C: "Button 23",
0x006D: "Button 24",
// --- 桌面 & 功能键 (0x006E~0x0082) ---
0x006E: "Show Desktop",
0x006F: "Screen Lock",
0x0070: "Fn F1",
0x0071: "Fn F2",
0x0072: "Fn F3",
0x0073: "Fn F4",
0x0074: "Fn F5",
0x0075: "Fn F6",
0x0076: "Fn F7",
0x0077: "Fn F8",
0x0078: "Fn F9",
0x0079: "Fn F10",
0x007A: "Fn F11",
0x007B: "Fn F12",
0x007C: "Fn F13",
0x007D: "Fn F14",
0x007E: "Fn F15",
0x007F: "Fn F16",
0x0080: "Fn F17",
0x0081: "Fn F18",
0x0082: "Fn F19",
// --- 移动平台 & Windows 特性 (0x0083~0x00B9) ---
0x0083: "IOS Home",
0x0084: "Android Home",
0x0085: "Android Menu",
0x0086: "Android Search",
0x0087: "Android Back",
0x0088: "Home Combo",
0x0089: "Lock Combo",
0x008A: "IOS Virtual Keyboard",
0x008B: "IOS Language Switch",
0x008C: "Mac Expose",
0x008D: "Mac Dashboard",
0x008E: "Win7 Snap Left",
0x008F: "Win7 Snap Right",
0x0090: "Minimize Window",
0x0091: "Maximize Window",
0x0092: "Win7 Stretch Up",
0x0093: "Win7 Monitor Switch As Win Shift LeftArrow",
0x0094: "Win7 Monitor Switch As Win Shift RightArrow",
0x0095: "Switch Screen",
0x0096: "Win7 Show Mobility Center",
0x0097: "Analog HScroll",
0x009F: "Metro Appswitch",
0x00A0: "Metro Appbar",
0x00A1: "Metro Charms",
0x00A2: "Calc Vkeyboard",
0x00A3: "Metro Search",
0x00A4: "Combo Sleep",
0x00A5: "Metro Share",
0x00A6: "OS Settings",
0x00A7: "Metro Devices",
0x00A9: "Metro Start Screen",
0x00AA: "Zoomin",
0x00AB: "Zoomout",
0x00AC: "Back Hscroll",
0x00AE: "Show Desktop HPP",
0x00B7: "Fn Left Click",
0x00B8: "Second Left Click",
0x00B9: "Fn Second Left Click",
// --- 跨平台 & 高级功能 (0x00BA~0x00FF) ---
0x00BA: "Multiplatform App Switch",
0x00BB: "Multiplatform Home",
0x00BC: "Multiplatform Menu",
0x00BD: "Multiplatform Back",
0x00BE: "Multiplatform Insert",
0x00BF: "Screen Capture / Print Screen",
0x00C0: "Fn Down",
0x00C1: "Fn Up",
0x00C2: "Multiplatform Lock",
0x00C3: "Mouse Gesture Button",
0x00C4: "Smart Shift",
0x00C5: "Microphone",
0x00C6: "Wifi",
0x00C7: "Brightness Down",
0x00C8: "Brightness Up",
0x00C9: "Display Out / Project Screen ", // Solaar 原文末尾有空格
0x00CA: "View Open Apps",
0x00CB: "View All Apps",
0x00CC: "Switch App",
0x00CD: "Fn Inversion Change",
0x00CE: "MultiPlatform Back",
0x00CF: "MultiPlatform Forward",
0x00D0: "MultiPlatform Gesture Button",
0x00D1: "Host Switch Channel 1",
0x00D2: "Host Switch Channel 2",
0x00D3: "Host Switch Channel 3",
0x00D4: "MultiPlatform Search",
0x00D5: "MultiPlatform Home / Mission Control",
0x00D6: "MultiPlatform Menu / Show / Hide Virtual Keyboard / Launchpad",
0x00D7: "Virtual Gesture Button",
0x00D8: "Cursor Button Long Press",
0x00D9: "Next Button Shortpress",
0x00DA: "Next Button Long Press",
0x00DB: "Back Button Short Press",
0x00DC: "Back Button Long Press",
0x00DD: "Multi Platform Language Switch",
0x00DE: "F Lock",
0x00DF: "Switch Highlight",
0x00E0: "Mission Control / Task View",
0x00E1: "Dashboard Launchpad / Action Center",
0x00E2: "Backlight Down",
0x00E3: "Backlight Up",
0x00E4: "Previous Track",
0x00E5: "Play / Pause",
0x00E6: "Next Track",
0x00E7: "Mute Sound",
0x00E8: "Volume Down",
0x00E9: "Volume Up",
0x00EA: "App Contextual Menu / Right Click",
0x00EB: "Right Arrow",
0x00EC: "Left Arrow",
0x00ED: "DPI Change",
0x00EE: "Open New Tab",
0x00EF: "F2",
0x00F0: "F3",
0x00F1: "F4",
0x00F2: "F5",
0x00F3: "F6",
0x00F4: "F7",
0x00F5: "F8",
0x00F6: "F1",
0x00F7: "Next Color Effect",
0x00F8: "Increase Color Effect Speed",
0x00F9: "Decrease Color Effect Speed",
0x00FA: "Load Lighting Custom Profile",
0x00FB: "Laser Button Short Press",
0x00FC: "Laser Button Long Press",
0x00FD: "DPI Switch",
0x00FE: "Multiplatform Home / Show Desktop",
0x00FF: "Multiplatform App Switch / Show Dashboard",
// --- 扩展功能 (0x0100~0x01B4) ---
0x0100: "Multiplatform App Switch 2",
0x0101: "Fn Inversion / Hot Key",
0x0102: "LeftAndRightClick",
0x0103: "Dictation",
0x0104: "Emoji Smiley Heart Eyes",
0x0105: "Emoji Crying Face",
0x0106: "Emoji Smiley",
0x0107: "Emoji Smilie With Tears",
0x0108: "Emoji",
0x0109: "Multiplatform App Switch / Launchpad",
0x010A: "Screen Capture",
0x010B: "Grave Accent",
0x010C: "Tab Key",
0x010D: "Caps Lock",
0x010E: "Left Shift",
0x010F: "Left Control",
0x0110: "Left Option / Start",
0x0111: "Left Command / Alt",
0x0112: "Right Command / Alt",
0x0113: "Right Option / Start",
0x0114: "Right Control",
0x0115: "Right Shift",
0x0116: "Insert",
0x0117: "Delete",
0x0118: "Home",
0x0119: "End",
0x011A: "Page Up",
0x011B: "Page Down",
0x011C: "Mute Microphone",
0x011D: "Do Not Disturb",
0x011E: "Backslash",
0x011F: "Refresh",
0x0120: "Close Tab",
0x0121: "Lang Switch",
0x0122: "Standard Key A",
0x0123: "Standard Key B",
0x0124: "Standard Key C",
0x013C: "Right Option / Start / 2",
0x0141: "Play / Pause mini",
0x01A0: "Haptic",
0x01A3: "Circle",
0x01A4: "Triangle",
0x01A5: "Diamond",
0x01A6: "Star",
0x01A9: "Cut",
0x01AA: "Copy",
0x01AB: "Paste",
0x01AC: "Video On Off",
0x01B4: "AI",
// --- G键 (0x1001~0x1020) ---
0x1001: "G1", 0x1002: "G2", 0x1003: "G3", 0x1004: "G4",
0x1005: "G5", 0x1006: "G6", 0x1007: "G7", 0x1008: "G8",
0x1009: "G9", 0x100A: "G10", 0x100B: "G11", 0x100C: "G12",
0x100D: "G13", 0x100E: "G14", 0x100F: "G15", 0x1010: "G16",
0x1011: "G17", 0x1012: "G18", 0x1013: "G19", 0x1014: "G20",
0x1015: "G21", 0x1016: "G22", 0x1017: "G23", 0x1018: "G24",
0x1019: "G25", 0x101A: "G26", 0x101B: "G27", 0x101C: "G28",
0x101D: "G29", 0x101E: "G30", 0x101F: "G31", 0x1020: "G32",
// --- M键 (0x1101~0x1108) + MR (0x1200) ---
0x1101: "M1", 0x1102: "M2", 0x1103: "M3", 0x1104: "M4",
0x1105: "M5", 0x1106: "M6", 0x1107: "M7", 0x1108: "M8",
0x1200: "MR",
]
// MARK: - CID → MosCode 特殊映射 (Mos 自有逻辑)
/// 为常见鼠标按钮保留固定 MosCode (1000~1007)
/// 其余 CID 使用公式: 2000 + CID
private static let cidToCode: [UInt16: UInt16] = [
0x0050: 1003, // Left Button (diverted)
0x0051: 1004, // Right Button (diverted)
0x0052: 1005, // Middle Button (diverted)
0x0053: 1006, // Back Button (diverted)
0x0056: 1007, // Forward Button (diverted)
0x00C3: 1000, // Mouse Gesture Button
0x00C4: 1001, // Smart Shift
0x00D7: 1002, // Virtual Gesture Button
]
// MARK: - 反向映射缓存 (预计算)
private static let codeToCID: [UInt16: UInt16] = {
var reversed: [UInt16: UInt16] = [:]
for (cid, code) in cidToCode { reversed[code] = cid }
return reversed
}()
// MARK: - 名称查询
/// 通过 CID 查询名称 (Debug 面板 + 按键面板共用)
static func name(forCID cid: UInt16) -> String {
return cidNames[cid] ?? String(format: "Unknown(0x%04X)", cid)
}
/// 通过 MosCode 查询名称 (按键面板使用)
static func name(forMosCode code: UInt16) -> String {
guard let cid = toCID(code) else { return "Logi(\(code))" }
return name(forCID: cid)
}
// MARK: - Code 转换
/// CID → MosCode
/// 已知 CID 最大值约 0x1200, 加 2000 后远小于 UInt16.max
static func toMosCode(_ cid: UInt16) -> UInt16 {
if let known = cidToCode[cid] { return known }
let mapped = UInt32(2000) + UInt32(cid)
return mapped <= UInt32(UInt16.max) ? UInt16(mapped) : UInt16(cid & 0x0FFF) + 2000
}
/// MosCode → CID (反向映射, O(1))
static func toCID(_ mosCode: UInt16) -> UInt16? {
if let cid = codeToCID[mosCode] { return cid }
if mosCode >= 2000 { return mosCode - 2000 }
return nil
}
/// 判断按钮码是否属于 Logitech HID++ 专有范围
static func isLogitechCode(_ code: UInt16) -> Bool {
return code >= 1000
}
}
在 Xcode 中将 Mos/LogitechHID/LogitechCIDRegistry.swift 添加到 Mos target。
或通过修改 Mos.xcodeproj/project.pbxproj 添加文件引用。
Run: xcodebuild -project Mos.xcodeproj -scheme Mos build 2>&1 | tail -5
Expected: BUILD SUCCEEDED (新文件不应有编译错误)
git add Mos/LogitechHID/LogitechCIDRegistry.swift
git commit -m "feat: add LogitechCIDRegistry with Solaar CID name table (~310 entries)"
Files:
Modify: Mos/InputEvent/MosInputEvent.swift
Step 1: 更新 displayComponents 中的引用 (line 158-160)
// 改前:
if LogitechCIDMap.isLogitechCode(code) {
components.append(LogitechCIDMap.displayName(forCode: code))
components.append("[Logi]")
// 改后:
if LogitechCIDRegistry.isLogitechCode(code) {
components.append(LogitechCIDRegistry.name(forMosCode: code))
components.append("[Logi]")
// 改前:
if LogitechCIDMap.isLogitechCode(code) { return true }
// 改后:
if LogitechCIDRegistry.isLogitechCode(code) { return true }
删除从 // MARK: - LogitechCIDMap 到 } 闭合的整段代码。
Run: xcodebuild -project Mos.xcodeproj -scheme Mos build 2>&1 | tail -5
Expected: 此时会有其他文件的编译错误 (BrandTag, ButtonTableCellView 等仍引用 LogitechCIDMap), 这是预期的, 后续 Task 修复。
git add Mos/InputEvent/MosInputEvent.swift
git commit -m "refactor: remove LogitechCIDMap from MosInputEvent, use LogitechCIDRegistry"
Files:
Modify: Mos/LogitechHID/LogitechDeviceSession.swift
Modify: Mos/LogitechHID/LogitechHIDDebugPanel.swift
Step 1: 更新 LogitechDeviceSession.swift - LogitechCIDMap 引用 (2 处)
Line 827:
// 改前:
let mosCode = LogitechCIDMap.toMosCode(c.cid)
// 改后:
let mosCode = LogitechCIDRegistry.toMosCode(c.cid)
Line 889:
// 改前:
code: LogitechCIDMap.toMosCode(cid),
// 改后:
code: LogitechCIDRegistry.toMosCode(cid),
注意: 各处使用的变量名不同, 需保持与当前代码一致:
Line 407 (变量名 cidName):
// 改前: let cidName = HIDPPInfo.cidNames[cid] ?? "?"
// 改后: let cidName = LogitechCIDRegistry.name(forCID: cid)
Lines 443, 453 (变量名 name):
// 改前: let name = HIDPPInfo.cidNames[cid] ?? "?"
// 改后: let name = LogitechCIDRegistry.name(forCID: cid)
Lines 872, 877 (变量名 cidName):
// 改前: let cidName = HIDPPInfo.cidNames[cid] ?? "Unknown"
// 改后: let cidName = LogitechCIDRegistry.name(forCID: cid)
删除 HIDPPInfo.cidNames 字典 (lines 55-61)。
Line 500:
// 改前:
let name = HIDPPInfo.cidNames[c.cid] ?? "Unknown"
// 改后:
let name = LogitechCIDRegistry.name(forCID: c.cid)
注意: HIDPPInfo.featureNames 和其他字典保留不动。
git add Mos/LogitechHID/LogitechDeviceSession.swift Mos/LogitechHID/LogitechHIDDebugPanel.swift
git commit -m "refactor: migrate LogitechHID module to use LogitechCIDRegistry"
Files:
Modify: Mos/Components/BrandTag.swift
Modify: Mos/Windows/PreferencesWindow/ButtonsView/ButtonTableCellView.swift
Modify: Mos/Windows/PreferencesWindow/ButtonsView/RecordedEvent.swift
Modify: Mos/Windows/PreferencesWindow/ScrollingView/PreferencesScrollingViewController.swift
Step 1: 更新 BrandTag.swift (line 34)
// 改前:
return LogitechCIDMap.isLogitechCode(code)
// 改后:
return LogitechCIDRegistry.isLogitechCode(code)
// 改前:
let isLogiTrigger = binding.triggerEvent.type == .mouse && LogitechCIDMap.isLogitechCode(binding.triggerEvent.code)
// 改后:
let isLogiTrigger = binding.triggerEvent.type == .mouse && LogitechCIDRegistry.isLogitechCode(binding.triggerEvent.code)
// 改前:
var displayName: String {
switch type {
case .keyboard:
return KeyCode.keyMap[code] ?? "Key \(code)"
case .mouse:
return KeyCode.mouseMap[code] ?? "🖱\(code)"
}
}
// 改后:
var displayName: String {
switch type {
case .keyboard:
return KeyCode.keyMap[code] ?? "Key \(code)"
case .mouse:
if LogitechCIDRegistry.isLogitechCode(code) {
return LogitechCIDRegistry.name(forMosCode: code)
}
return KeyCode.mouseMap[code] ?? "🖱\(code)"
}
}
// 改前:
case .mouse:
return KeyCode.mouseMap[hotkey.code] ?? "🖱\(hotkey.code)"
// 改后:
case .mouse:
if LogitechCIDRegistry.isLogitechCode(hotkey.code) {
return LogitechCIDRegistry.name(forMosCode: hotkey.code)
}
return KeyCode.mouseMap[hotkey.code] ?? "🖱\(hotkey.code)"
git add Mos/Components/BrandTag.swift Mos/Windows/PreferencesWindow/ButtonsView/ButtonTableCellView.swift Mos/Windows/PreferencesWindow/ButtonsView/RecordedEvent.swift Mos/Windows/PreferencesWindow/ScrollingView/PreferencesScrollingViewController.swift
git commit -m "refactor: update all consumer files to use LogitechCIDRegistry"
Files:
Modify: Mos/Keys/KeyCode.swift
Step 1: 删除 mouseMap 中 1000+ 的条目 (lines 165-168)
// 删除以下 4 行 (含注释):
// Logitech HID++ 专有按键
1000: "Gesture", 1001: "SmartShift", 1002: "DPI",
1003: "Left Click", 1004: "Right Click",
1005: "Middle Click", 1006: "Back", 1007: "Forward",
mouseMap 保留 0~20 的标准鼠标按键映射。
git add Mos/Keys/KeyCode.swift
git commit -m "refactor: remove Logi entries from KeyCode.mouseMap (now in LogitechCIDRegistry)"
CGEvent+Extensions.swift: mouseCode 范围 0~31, 不涉及 Logi 码, 无需修改tools/hidpp-full-test.swift: 独立脚本, 不属于 app target, 无法引用 app 源码, 保留本地副本MosInputProcessor.swift: 无 LogitechCIDMap/HIDPPInfo.cidNames 引用Run: xcodebuild -project Mos.xcodeproj -scheme Mos build 2>&1 | tail -10
Expected: BUILD SUCCEEDED, 0 errors
Run: grep -rn "LogitechCIDMap" Mos/ --include="*.swift"
Expected: 0 matches (仅 docs/ 目录可能有旧引用)
Run: grep -rn "HIDPPInfo.cidNames" Mos/ --include="*.swift"
Expected: 0 matches
连接 Logitech 设备后验证: