Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/aft-sakee01_desktopcodexassistant.json

1 line
1.2 MiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "aft-sakee01/desktopcodexassistant", "files": {"Settings/Win11SettingsForm.cs": {"5d73239e59112a3448573ea87f915bdf2dbe1bc9": {"t": 1785745804.2224734, "n": 216186, "h": "44d4d3cc7da57241", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.IO;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int WmSettingChange = 0x001A;\n private const int WmDisplayChange = 0x007E;\n private const int EmSetCueBanner = 0x1501;\n private const int ReferenceWorkAreaWidth = 2880;\n private const int ReferenceWorkAreaHeight = 1740;\n private const int PreferredClientWidth = 1888;\n private const int PreferredClientHeight = 1312;\n private const int MinimumClientWidth = 760;\n private const int MinimumClientHeight = 560;\n private const int ScreenMargin = 80;\n private const int DefaultNavWidth = 409;\n private const int MediumNavWidth = 300;\n private const int CompactNavWidth = 220;\n private const int NavItemHeight = 60;\n private const int ClaudeRadarModelGridColumns = 5;\n private const int ClaudeRadarModelButtonWidth = 144;\n private const int ClaudeRadarModelButtonMinimumWidth = 88;\n private const int ClaudeRadarModelButtonHeight = 38;\n private const int ClaudeRadarModelButtonGap = 8;\n private const int ClaudeRadarModelButtonMaxTextChars = 14;\n private const string ClaudeRadarModelGridName = \"ClaudeRadarModelGrid\";\n private const string GlobalLayoutEditCommandName = \"GlobalLayoutEditCommand\";\n private const string ClaudeSetupTokenCommandName = \"ClaudeSetupTokenCommand\";\n\n private static readonly Color MicaBase = DesignTokens.SettingsWarmTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.SettingsWarmTheme.CardRest;\n private static readonly Color CardHover = DesignTokens.SettingsWarmTheme.CardHover;\n private static readonly Color StrokeColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.SettingsWarmTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.SettingsWarmTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.SettingsWarmTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.SettingsWarmTheme.Accent;\n private static readonly Color AccentHover = DesignTokens.SettingsWarmTheme.AccentHover;\n private static readonly Color AccentPressed = DesignTokens.SettingsWarmTheme.AccentPressed;\n private static readonly Color ErrorClr = DesignTokens.SettingsWarmTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private TableLayoutPanel bodyLayout;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool dirty;\n private bool messageFilterRegistered;\n private bool unsavedPreviewConsumed;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n public bool TryConsumeUnsavedPreview(out WidgetSettings settings)\n {\n settings = null;\n if (this.saved || this.OwnerFormClosing || this.unsavedPreviewConsumed)\n {\n return false;\n }\n\n this.unsavedPreviewConsumed = true;\n settings = this.baseline.Clone();\n settings.Normalize();\n return true;\n }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n ApplicationIcon.ApplyTo(this);\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = true;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n ApplyDynamicResolutionSizing(true);\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n ApplyDynamicResolutionSizing(false);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n ApplyResponsiveShellLayout();\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n if (m.Msg == WmDisplayChange || m.Msg == WmSettingChange)\n {\n ScheduleDynamicResolutionSizing();\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n if (this.dirty && !this.saved && this.owner != null && !this.OwnerFormClosing)\n {\n DialogResult result = MessageBox.Show(\n this,\n \"设置还没有保存。要先保存再关闭吗?\",\n \"未保存的更改\",\n MessageBoxButtons.YesNoCancel,\n MessageBoxIcon.Warning,\n MessageBoxDefaultButton.Button1);\n if (result == DialogResult.Cancel)\n {\n e.Cancel = true;\n return;\n }\n\n if (result == DialogResult.Yes && !TrySaveSettings())\n {\n e.Cancel = true;\n return;\n }\n }\n\n this.previewTimer.Stop();\n WidgetSettings revertSettings;\n if (this.owner != null && TryConsumeUnsavedPreview(out revertSettings))\n {\n try\n {\n this.owner.RevertSettings(revertSettings);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.SettingsWarmTheme.ErrorText; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n this.bodyLayout = body;\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetNavigationWidthForClientWidth(this.ClientSize.Width)));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false, DesignTokens.Colors.Warning);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n SetDirtyState(true);\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n if (TrySaveSettings())\n {\n ShowStatus(\"保存完成\", SettingsStatusSeverity.Success);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold));\n }\n\n private Button BuildCommandButton(string text, bool primary, Color outlineAccent)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold), outlineAccent);\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n // 分组名以 '!' 开头 = 收进该页底部的「复杂选项」折叠区。\n AddPageGrouped(\"\\uE115\", \"系统\", \"开机启动、性能和窗口基础行为。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"VisibilityOverlapIgnoresOperationPanelEnabled\", \"ClickThroughMode\" },\n new string[] { \"AI 请求阻断\", \"AiRequestProtectionAutoEnabled\", \"AiRequestProtectionManualBlockEnabled\" },\n new string[] { \"!Codex 额度计划\", \"CodexQuotaPlanEnabled\", \"CodexQuotaPlanWeeklyComparison\", \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\", \"CodexQuotaPlanFiveHourThresholdPercent\", \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\", \"CodexQuotaPlanPauseGoalIds\", \"CodexQuotaPlanResumeGoalIds\" },\n new string[] { \"!恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" },\n new string[] { \"!调试\", \"ForceShowForegroundFpsEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7C2\", \"布局与位置\", \"所有浮窗的位置、大小和所在显示器;推荐用可视化编辑直接拖拽。\", new string[][]\n {\n new string[] { \"可视化编辑\", GlobalLayoutEditCommandName },\n new string[] { \"分辨率兼容\", \"ResolutionCompatibilityModeEnabled\", \"ResolutionCompatibilityScalePercent\" },\n new string[] { \"显示器分配\", \"FallbackDisconnectedDisplaysEnabled\", \"MainDisplayDeviceName\", \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\", \"PowerThermalDisplayDeviceName\", \"NetworkMonitorDisplayDeviceName\", \"ConnectionCheckDisplayDeviceName\", \"OperationDisplayDeviceName\" },\n new string[] { \"!主窗口位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"!Codex Radar 位置\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\" },\n new string[] { \"!Claude Radar 位置\", \"ClaudeRadarWidth\", \"ClaudeRadarHeight\", \"ClaudeRadarLeftX\", \"ClaudeRadarBottomY\" },\n new string[] { \"!功耗温度位置\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\" },\n new string[] { \"!网络监控位置\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\" },\n new string[] { \"!连接检测位置\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\" },\n new string[] { \"!操作面板位置\", \"OperationLeftOffset\", \"OperationBottomOffset\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与防烧屏\", \"鼠标靠近时隐藏、空闲自动隐藏和 OLED 防烧屏。\", new string[][]\n {\n new string[] { \"鼠标靠近时隐藏\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"OperationRadialCoreAutoHideKeepAliveEnabled\", \"OperationRadialIdleCollapseSeconds\", \"OperationRadialIdleResetOnInteractionEnabled\", \"OperationRadialKeepOpenAfterLeafClickEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" },\n new string[] { \"!延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"!覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" }\n });\n\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口显示哪些指标、透明度和外观。\", new string[][]\n {\n new string[] { \"显示哪些指标\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" }\n });\n\n AddPageGrouped(\"\\uE121\", \"Radar 通用\", \"Codex / Claude Radar 共用的时钟显示和时区。\", new string[][]\n {\n new string[] { \"时钟显示\", \"RadarClockTimeDisplayMode\", \"RadarClockAutoSwitchModelEnabled\" },\n new string[] { \"显示时区\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" }\n });\n\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"共享 Radar 小窗:可按前台自动显示 Codex 或 Claude,也可固定检测对象。\", new string[][]\n {\n // 手动布局与元素偏移分组已从设置界面隐藏(改用渲染变体切换布局)。\n // 对应 WidgetSettings 属性仍保留,Classic 布局若通过 settings.ini 设置仍生效;\n // 均布变体本就忽略这些设置。恢复入口时把下方分组加回,并同步 VerifySelfTest 必需绑定。\n new string[] { \"共享小窗\", \"CodexRadarEnabled\", \"CodexRadarSoftwareMode\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"CODEX 模式数据\", \"CodexRadarModelKey\" },\n new string[] { \"!CodexRadar.com 读取链路\", \"CodexRadarPublicJsonEnabled\", \"CodexRadarHtmlFallbackEnabled\", \"CodexRadarRssFallbackEnabled\", \"CodexRadarServiceProbeToken\" },\n new string[] { \"!额度保护\", \"CodexQuotaDueResetProtectionEnabled\", \"CodexQuotaRssResetProtectionEnabled\",\n \"CodexQuotaProviderZeroDropProtectionEnabled\", \"CodexQuotaDuplicateSameBalanceRingProtectionEnabled\",\n \"CodexQuotaProviderFiveHourEarlyResetSpikeProtectionEnabled\", \"CodexQuotaProviderWeeklySpikeProtectionEnabled\",\n \"CodexQuotaStrictFiveHourResetBoundaryEnabled\", \"CodexQuotaWeeklyBaselineAutoRepairEnabled\" },\n new string[] { \"!兼容模型设置\", \"CodexRadarModelVersion\" },\n new string[] { \"!IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineAutoEnabled\", \"CodexModelIqBaselinePassed\", \"CodexModelIqBaselineValidTasks\" },\n new string[] { \"!效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"!随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uE8D4\", \"Claude Radar\", \"Claude 相关数据:独立 Claude 小窗,以及共享 Radar 小窗的 CLAUDE 模式辅助状态。\", new string[][]\n {\n new string[] { \"独立小窗\", \"ClaudeRadarEnabled\", \"ClaudeRadarTransparencyPercent\" },\n new string[] { \"Claude 模型\", \"ClaudeRadarModelKey\" },\n new string[] { \"Claude 数据链路\", \"ClaudeRadarJsonEnabled\", \"ClaudeRadarCommunityRatingsEnabled\", \"ClaudeRadarLocalQuotaFallbackEnabled\" },\n new string[] { \"Claude Code 用量令牌\", ClaudeSetupTokenCommandName },\n new string[] { \"DeepSeek 余额\", \"DeepSeekApiKeyRevision\" },\n new string[] { \"!元数据与诊断\", \"ClaudeRadarHomepageFallbackEnabled\", \"ClaudeRadarServiceProbeToken\" },\n new string[] { \"!随机测试\", \"ClaudeRadarRandomTestEnabled\", \"ClaudeRadarRandomTestAutoRefresh\", \"ClaudeRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"电池与节能\", \"PowerThermalManualEnergySaverThresholdPercent\" },\n new string[] { \"透明度\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"!测试\", \"ThermalTestMode\" }\n });\n\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW 检测、云服务和出口身份。\", new string[][]\n {\n new string[] { \"网络监控\", \"NetworkMonitorAdapterId\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\" },\n new string[] { \"连接检测\", \"ConnectionCheckIntervalSeconds\", \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\" },\n new string[] { \"!手动刷新\", \"GfwProbeManualRefreshToken\", \"ConnectionCheckManualRefreshToken\" },\n new string[] { \"!云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"!测试\", \"CleanIpBadgeTestMode\", \"NetworkStatusTestMode\" }\n });\n\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板的按钮、透明度和外观。\", new string[][]\n {\n new string[] { \"按钮与面板\", \"OperationButtonSize\", \"OperationPrimaryPanelMode\", \"OperationSettingsLogicExtensionEnabled\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"外观风格\", \"OperationRenderVariant\" },\n new string[] { \"!测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n // A leading '!' marks the group as advanced: it renders collapsed under the\n // per-page \"复杂选项\" header until the user expands it (or searches).\n bool advanced = groupTitle.Length > 0 && groupTitle[0] == '!';\n if (advanced)\n {\n groupTitle = groupTitle.Substring(1);\n }\n\n if (advanced && page.AdvancedHeader == null)\n {\n AdvancedSectionHeader advancedHeader = new AdvancedSectionHeader(GetUiFont(10.0f, FontStyle.Bold), GetUiFont(8.5f));\n advancedHeader.Width = 1152;\n advancedHeader.Height = 64;\n advancedHeader.Margin = new Padding(0, 32, 0, 6);\n advancedHeader.Click += delegate { ToggleAdvancedSection(page); };\n page.AdvancedHeader = advancedHeader;\n stack.Controls.Add(advancedHeader);\n }\n\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n group.Advanced = advanced;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n titleLabel.Visible = !advanced;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n card.Visible = !advanced;\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.AdvancedRowCount = CountAdvancedRows(page);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = Math.Max(160, GetNavigationWidthForClientWidth(this.ClientSize.Width) - 24);\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n private static int CountAdvancedRows(CategoryPage page)\n {\n int count = 0;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n if (page.Groups[g].Advanced)\n {\n count += page.Groups[g].Editors.Count;\n }\n }\n\n return count;\n }\n\n private void ToggleAdvancedSection(CategoryPage page)\n {\n page.AdvancedExpanded = !page.AdvancedExpanded;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Expanded = page.AdvancedExpanded;\n }\n\n ApplyAdvancedVisibility(page);\n LayoutPage(page);\n }\n\n private void ApplyAdvancedVisibility(CategoryPage page)\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n if (query.Length > 0)\n {\n // Search visibility is authoritative while a query is active.\n ApplySearchFilter();\n return;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (!group.Advanced)\n {\n continue;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = page.AdvancedExpanded;\n }\n\n if (group.Card != null)\n {\n group.Card.Visible = page.AdvancedExpanded;\n if (page.AdvancedExpanded)\n {\n group.Card.LayoutRows();\n }\n }\n }\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n if (string.Equals(propertyName, GlobalLayoutEditCommandName, StringComparison.Ordinal))\n {\n return BuildGlobalLayoutEditEditor();\n }\n\n if (string.Equals(propertyName, ClaudeSetupTokenCommandName, StringComparison.Ordinal))\n {\n return BuildClaudeSetupTokenEditor();\n }\n\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n // The model button grid wraps to multiple rows on its own and doesn't fit alongside the\n // title/hint text in the normal side-by-side column - stack it below instead of squeezing.\n if (string.Equals(propertyName, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n card.ForceCompactLayout = true;\n }\n\n return new SettingEditor(property, card, control);\n }\n\n private SettingEditor BuildGlobalLayoutEditEditor()\n {\n Button button = BuildCommandButton(\"全局编辑\", false);\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenGlobalLayoutEditorFromSettings(); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(GlobalLayoutEditCommandName);\n card.HintLabel.Text = GetSettingHint(GlobalLayoutEditCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(GlobalLayoutEditCommandName, card, button);\n }\n\n private SettingEditor BuildClaudeSetupTokenEditor()\n {\n Button button = BuildCommandButton(GetClaudeSetupTokenButtonText(), false, GetClaudeSetupTokenAccentColor());\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenClaudeSetupTokenDialog(button); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(ClaudeSetupTokenCommandName);\n card.HintLabel.Text = GetSettingHint(ClaudeSetupTokenCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(ClaudeSetupTokenCommandName, card, button);\n }\n\n private void OpenClaudeSetupTokenDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"Claude Code 用量令牌\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.AutoScaleMode = AutoScaleMode.None;\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n // Every control's Y is accumulated from the previous control's *measured* height\n // (via GetSingleLineHeight/GetWrappedTextHeight) rather than guessed constants -\n // guessed row heights overlapped once the actual rendered font metrics differed\n // from what was assumed at a different DPI/text-scale setting.\n const int marginLeft = 24;\n const int contentWidth = 572;\n int y = 20;\n\n Font titleFont = GetUiFont(12.0f, FontStyle.Bold);\n Label title = new Label();\n title.Text = \"生成并粘贴 setup-token\";\n title.Font = titleFont;\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(marginLeft, y);\n title.Size = new Size(contentWidth, GetSingleLineHeight(titleFont, 8));\n title.TextAlign = ContentAlignment.MiddleLeft;\n y += title.Height + 8;\n\n Font hintFont = GetUiFont(8.8f);\n string hintText = \"Claude 桌面版不会主动上报用量数据。先在下面复制命令,在任意 PowerShell 窗口运行并登录授权,\" +\n \"把打印出的一次性长效令牌粘贴到下方输入框保存。未配置时两个额度环会显示满环红色。\";\n Label hint = new Label();\n hint.Text = hintText;\n hint.Font = hintFont;\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(marginLeft, y);\n hint.Size = new Size(contentWidth, GetWrappedTextHeight(hintText, hintFont, contentWidth, 8));\n hint.TextAlign = ContentAlignment.TopLeft;\n y += hint.Height + 18;\n\n Font sectionFont = GetUiFont(9.0f, FontStyle.Bold);\n Label commandLabel = new Label();\n commandLabel.Text = \"第 1 步:复制并在 PowerShell 中运行\";\n commandLabel.Font = sectionFont;\n commandLabel.ForeColor = TextPrimary;\n commandLabel.BackColor = MicaBase;\n commandLabel.Location = new Point(marginLeft, y);\n commandLabel.Size = new Size(contentWidth, GetSingleLineHeight(sectionFont, 6));\n commandLabel.TextAlign = ContentAlignment.MiddleLeft;\n y += commandLabel.Height + 6;\n\n int commandRowHeight = 34;\n int copyButtonWidth = 112;\n int gapBeforeCopy = 10;\n int commandBoxWidth = contentWidth - copyButtonWidth - gapBeforeCopy;\n\n TextBox commandBox = new TextBox();\n commandBox.Location = new Point(marginLeft, y);\n commandBox.Size = new Size(commandBoxWidth, commandRowHeight);\n commandBox.BackColor = ControlBg;\n commandBox.ForeColor = TextSecondary;\n commandBox.BorderStyle = BorderStyle.FixedSingle;\n commandBox.Font = GetUiFont(8.8f);\n commandBox.ReadOnly = true;\n commandBox.Text = BuildClaudeSetupTokenCommandText();\n\n Button copyCommand = BuildCommandButton(\"复制命令\", false);\n copyCommand.Location = new Point(marginLeft + commandBoxWidth + gapBeforeCopy, y);\n copyCommand.Width = copyButtonWidth;\n copyCommand.Height = commandRowHeight;\n copyCommand.Click += delegate\n {\n try\n {\n Clipboard.SetText(commandBox.Text);\n ShowStatus(\"命令已复制\", SettingsStatusSeverity.Success);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"复制失败\", SettingsStatusSeverity.Error);\n }\n };\n y += commandRowHeight + 18;\n\n Label tokenLabel = new Label();\n tokenLabel.Text = \"第 2 步:粘贴命令打印出的令牌\";\n tokenLabel.Font = sectionFont;\n tokenLabel.ForeColor = TextPrimary;\n tokenLabel.BackColor = MicaBase;\n tokenLabel.Location = new Point(marginLeft, y);\n tokenLabel.Size = new Size(contentWidth, GetSingleLineHeight(sectionFont, 6));\n tokenLabel.TextAlign = ContentAlignment.MiddleLeft;\n y += tokenLabel.Height + 6;\n\n TextBox tokenBox = new TextBox();\n tokenBox.Location = new Point(marginLeft, y);\n tokenBox.Size = new Size(contentWidth, commandRowHeight);\n tokenBox.BackColor = ControlBg;\n tokenBox.ForeColor = TextSecondary;\n tokenBox.BorderStyle = BorderStyle.FixedSingle;\n tokenBox.Font = GetUiFont(9.5f);\n tokenBox.UseSystemPasswordChar = true;\n y += commandRowHeight + 14;\n\n Font statusFont = GetUiFont(8.8f, FontStyle.Bold);\n Label statusHint = new Label();\n statusHint.AutoSize = false;\n statusHint.Font = statusFont;\n statusHint.Location = new Point(marginLeft, y);\n statusHint.Size = new Size(contentWidth, GetSingleLineHeight(statusFont, 4));\n statusHint.BackColor = MicaBase;\n statusHint.TextAlign = ContentAlignment.MiddleLeft;\n ApplyClaudeSetupTokenStatusHint(statusHint);\n y += statusHint.Height + 20;\n\n int buttonHeight = 44;\n int buttonWidth = 112;\n int buttonGap = 12;\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(marginLeft, y);\n clear.Width = buttonWidth;\n clear.Height = buttonHeight;\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Width = buttonWidth;\n save.Height = buttonHeight;\n save.Location = new Point(marginLeft + contentWidth - buttonWidth, y);\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Width = buttonWidth;\n cancel.Height = buttonHeight;\n cancel.Location = new Point(save.Left - buttonGap - buttonWidth, y);\n cancel.Click += delegate { dialog.Close(); };\n\n y += buttonHeight + 20;\n dialog.ClientSize = new Size(marginLeft * 2 + contentWidth, y);\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveClaudeSetupTokenFile(string.Empty, out errorCode))\n {\n tokenBox.Text = string.Empty;\n ApplyClaudeSetupTokenStatusHint(statusHint);\n RefreshClaudeSetupTokenButton(sourceButton);\n ShowStatus(\"Claude 用量令牌已清除\", SettingsStatusSeverity.Warning);\n return;\n }\n\n ShowStatus(\"Claude 用量令牌清除失败 \" + errorCode, SettingsStatusSeverity.Error);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveClaudeSetupTokenFile(tokenBox.Text, out errorCode))\n {\n ApplyClaudeSetupTokenStatusHint(statusHint);\n RefreshClaudeSetupTokenButton(sourceButton);\n ShowStatus(\"Claude 用量令牌已保存\", SettingsStatusSeverity.Success);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"Claude 用量令牌保存失败 \" + errorCode, SettingsStatusSeverity.Error);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(commandLabel);\n dialog.Controls.Add(commandBox);\n dialog.Controls.Add(copyCommand);\n dialog.Controls.Add(tokenLabel);\n dialog.Controls.Add(tokenBox);\n dialog.Controls.Add(statusHint);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void RefreshClaudeSetupTokenButton(Button sourceButton)\n {\n if (sourceButton != null)\n {\n sourceButton.Text = GetClaudeSetupTokenButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static void ApplyClaudeSetupTokenStatusHint(Label statusHint)\n {\n if (statusHint == null)\n {\n return;\n }\n\n bool configured = IsClaudeSetupTokenConfiguredForUi();\n statusHint.Text = configured ? \"状态:已配置\" : \"状态:未配置(额度环将显示满环红色)\";\n statusHint.ForeColor = configured ? DesignTokens.Colors.Success : DesignTokens.Colors.Danger;\n }\n\n private static string GetClaudeSetupTokenButtonText()\n {\n return IsClaudeSetupTokenConfiguredForUi() ? \"已配置 · 修改\" : \"未配置 · 立即设置\";\n }\n\n private static Color GetClaudeSetupTokenAccentColor()\n {\n return IsClaudeSetupTokenConfiguredForUi() ? DesignTokens.Colors.Success : DesignTokens.Colors.Danger;\n }\n\n private static bool IsClaudeSetupTokenConfiguredForUi()\n {\n try\n {\n return ClaudeCodeUsageReader.ReadConfiguredSetupToken().Length > 0;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n return false;\n }\n }\n\n // Dynamically resolves the installed claude-code CLI version directory instead of hardcoding\n // it - the Claude desktop app updates this bundled CLI independently of our own releases.\n private static string BuildClaudeSetupTokenCommandText()\n {\n return \"$dir = Get-ChildItem \\\"$env:APPDATA\\\\Claude\\\\claude-code\\\" -Directory | \" +\n \"Sort-Object Name -Descending | Select-Object -First 1; \" +\n \"& \\\"$($dir.FullName)\\\\claude.exe\\\" setup-token\";\n }\n\n private static bool TrySaveClaudeSetupTokenFile(string token, out string errorCode)\n {\n errorCode = string.Empty;\n try\n {\n string trimmed = (token ?? string.Empty).Trim().Trim('\"', '\\'');\n string path = ClaudeCodeUsageReader.SetupTokenFilePath;\n if (trimmed.Length == 0)\n {\n if (File.Exists(path))\n {\n File.Delete(path);\n }\n\n return true;\n }\n\n string directory = Path.GetDirectoryName(path);\n if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))\n {\n Directory.CreateDirectory(directory);\n }\n\n File.WriteAllText(path, trimmed, new UTF8Encoding(false));\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n return false;\n }\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n if (property.Name.EndsWith(\"RenderVariant\", StringComparison.Ordinal) &&\n !property.Name.StartsWith(\"ClaudeRadar\", StringComparison.Ordinal))\n {\n return BuildVariantPicker(property);\n }\n\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildCodexRadarModelCombo();\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildClaudeRadarModelGridSelector();\n }\n\n if (type == typeof(string) && IsDisplayDeviceSetting(property.Name))\n {\n return BuildDisplayDeviceCombo();\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n if (string.Equals(property.Name, \"ResolutionCompatibilityScalePercent\", StringComparison.Ordinal))\n {\n PercentSliderControl slider = new PercentSliderControl(GetUiFont(9.0f, FontStyle.Bold));\n slider.Width = 400;\n slider.Height = 54;\n slider.Minimum = WidgetSettings.MinResolutionCompatibilityScalePercent;\n slider.Maximum = WidgetSettings.MaxResolutionCompatibilityScalePercent;\n slider.ValueChanged += delegate { OnSettingChanged(); };\n return slider;\n }\n\n if (string.Equals(property.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(GetDeepSeekApiKeyButtonText(), false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate { OpenDeepSeekApiKeyDialog(button); };\n return button;\n }\n\n if (string.Equals(property.Name, \"CodexRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Codex Radar 服务检测已启动,结果写入本地诊断文件。\", SettingsStatusSeverity.Warning);\n OnSettingChanged();\n };\n return button;\n }\n\n if (string.Equals(property.Name, \"ClaudeRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Claude Radar 服务检测已启动,结果写入本地诊断文件。\", SettingsStatusSeverity.Warning);\n OnSettingChanged();\n };\n return button;\n }\n\n if (type == typeof(int) && property.Name.EndsWith(\"RefreshToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即刷新\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"刷新请求已发送。\", SettingsStatusSeverity.Warning);\n OnSettingChanged();\n };\n return button;\n }\n\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n private Control BuildVariantPicker(PropertyInfo property)\n {\n VariantPicker picker = new VariantPicker(property.Name, property.PropertyType, GetUiFont(8.8f, FontStyle.Bold), GetUiFont(8.0f));\n picker.Width = 560;\n picker.ValueChanged += delegate { OnSettingChanged(); };\n picker.PreferredHeightChanged += delegate { RelayoutSettingGroup(picker); };\n return picker;\n }\n\n private Control BuildDisplayDeviceCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n combo.Items.Add(new DisplayOption(string.Empty, \"主显示器/自动\"));\n Screen[] screens = Screen.AllScreens;\n for (int i = 0; i < screens.Length; i++)\n {\n Screen screen = screens[i];\n Rectangle bounds = screen.Bounds;\n string label = string.Format(\n CultureInfo.InvariantCulture,\n \"{0} {1}x{2} @ {3},{4}{5}\",\n screen.DeviceName,\n bounds.Width,\n bounds.Height,\n bounds.Left,\n bounds.Top,\n screen.Primary ? \" 主屏\" : string.Empty);\n combo.Items.Add(new DisplayOption(screen.DeviceName, label));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildCodexRadarModelCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n\n List<CodexRadarModelInfo> models = CodexRadarModelCatalog.LoadModels();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n CodexRadarModelInfo model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = CodexRadarModelCatalog.NormalizeModelKey(model.Key);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n combo.Items.Add(new ModelOption(\n key,\n CodexRadarModelCatalog.GetDisplayLabel(model.Label, key),\n model.Available,\n false));\n keys.Add(key);\n }\n\n string defaultKey = CodexRadarModelCatalog.DefaultModelKey;\n if (!keys.Contains(defaultKey))\n {\n combo.Items.Insert(0, new ModelOption(\n defaultKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, defaultKey),\n true,\n false));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildClaudeRadarModelGridSelector()\n {\n Panel panel = new Panel();\n panel.Width = GetClaudeRadarModelGridPreferredWidth();\n panel.BackColor = Color.Transparent;\n panel.Resize += delegate { LayoutClaudeRadarModelPanel(panel); };\n\n FlowLayoutPanel grid = new FlowLayoutPanel();\n grid.Name = ClaudeRadarModelGridName;\n grid.Width = panel.Width;\n grid.Location = new Point(0, 0);\n grid.Margin = new Padding(0);\n grid.Padding = new Padding(0);\n grid.FlowDirection = FlowDirection.LeftToRight;\n grid.WrapContents = true;\n grid.AutoSize = false;\n grid.BackColor = Color.Transparent;\n panel.Controls.Add(grid);\n\n Button edit = BuildCommandButton(\"编辑映射\", false);\n edit.Width = panel.Width;\n edit.Height = 54;\n edit.Click += delegate\n {\n using (ClaudeRadarModelMapEditorForm dialog = new ClaudeRadarModelMapEditorForm())\n {\n if (dialog.ShowDialog(this) == DialogResult.OK)\n {\n string selected = GetClaudeRadarModelGridValue(grid);\n PopulateClaudeRadarModelGrid(grid, selected);\n LayoutClaudeRadarModelPanel(panel);\n RelayoutSettingGroup(panel);\n ShowStatus(\"Claude Radar 模型映射已保存。\", SettingsStatusSeverity.Success);\n OnSettingChanged();\n }\n }\n };\n panel.Controls.Add(edit);\n PopulateClaudeRadarModelGrid(grid, string.Empty);\n LayoutClaudeRadarModelPanel(panel);\n return panel;\n }\n\n private void PopulateClaudeRadarModelGrid(FlowLayoutPanel grid, string selectedKey)\n {\n if (grid == null)\n {\n return;\n }\n\n string normalizedSelected = WidgetSettings.NormalizeClaudeRadarModelKey(selectedKey);\n List<ClaudeModelOption> options = BuildClaudeRadarModelOptions(normalizedSelected);\n bool selectedExists = false;\n for (int i = 0; i < options.Count; i++)\n {\n if (string.Equals(options[i].Key, normalizedSelected, StringComparison.OrdinalIgnoreCase))\n {\n selectedExists = true;\n break;\n }\n }\n\n if (!selectedExists)\n {\n normalizedSelected = string.Empty;\n }\n\n grid.Tag = normalizedSelected;\n grid.SuspendLayout();\n try\n {\n while (grid.Controls.Count > 0)\n {\n Control old = grid.Controls[0];\n grid.Controls.RemoveAt(0);\n old.Dispose();\n }\n\n int columns = GetClaudeRadarModelColumnCountForGrid(grid.Width);\n int slots = Math.Max(\n ClaudeRadarModelGridColumns,\n ((options.Count + ClaudeRadarModelGridColumns - 1) / ClaudeRadarModelGridColumns) * ClaudeRadarModelGridColumns);\n for (int i = 0; i < slots; i++)\n {\n ClaudeModelOption option = i < options.Count ? options[i] : null;\n Button button = BuildClaudeRadarModelButton(option);\n button.Margin = new Padding(0, 0, i % columns == columns - 1 ? 0 : ClaudeRadarModelButtonGap, ClaudeRadarModelButtonGap);\n grid.Controls.Add(button);\n }\n\n ApplyClaudeRadarModelGridSelection(grid);\n int rows = Math.Max(1, (slots + columns - 1) / columns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n }\n finally\n {\n grid.ResumeLayout();\n }\n }\n\n private List<ClaudeModelOption> BuildClaudeRadarModelOptions(string selectedKey)\n {\n List<ClaudeModelOption> options = new List<ClaudeModelOption>();\n options.Add(new ClaudeModelOption(string.Empty, \"自动\", true, false));\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n keys.Add(string.Empty);\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n string status = (model.Status ?? string.Empty).Trim();\n bool deleted = string.Equals(status, \"deleted\", StringComparison.OrdinalIgnoreCase);\n if (deleted && !string.Equals(key, selectedKey, StringComparison.OrdinalIgnoreCase))\n {\n continue;\n }\n\n bool pending = string.Equals(status, \"pending\", StringComparison.OrdinalIgnoreCase);\n bool temporarilyMissing = string.Equals(status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase);\n bool available = model.Enabled &&\n !deleted &&\n !pending &&\n !temporarilyMissing &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n options.Add(new ClaudeModelOption(\n key,\n label,\n available,\n pending || temporarilyMissing));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (!string.IsNullOrEmpty(selectedKey) && !keys.Contains(selectedKey))\n {\n options.Add(new ClaudeModelOption(selectedKey, selectedKey, false, true));\n }\n\n return options;\n }\n\n private Button BuildClaudeRadarModelButton(ClaudeModelOption option)\n {\n Button button = new Button();\n button.Width = ClaudeRadarModelButtonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Tag = option;\n button.Text = option == null ? \"--\" : GetClaudeRadarModelButtonText(option);\n button.AutoEllipsis = true;\n button.TextAlign = ContentAlignment.MiddleCenter;\n button.FlatStyle = FlatStyle.Flat;\n button.UseVisualStyleBackColor = false;\n button.FlatAppearance.BorderSize = 1;\n button.Font = GetUiFont(8.8f, FontStyle.Bold);\n button.Cursor = option != null && option.Available ? Cursors.Hand : Cursors.Default;\n button.Enabled = option != null && option.Available;\n button.Click += delegate\n {\n ClaudeModelOption clicked = button.Tag as ClaudeModelOption;\n if (clicked == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(button.Parent);\n if (grid == null)\n {\n return;\n }\n\n string current = GetClaudeRadarModelGridValue(grid);\n if (string.Equals(current, clicked.Key, StringComparison.OrdinalIgnoreCase))\n {\n return;\n }\n\n grid.Tag = clicked.Key;\n ApplyClaudeRadarModelGridSelection(grid);\n OnSettingChanged();\n };\n return button;\n }\n\n private static string GetClaudeRadarModelButtonText(ClaudeModelOption option)\n {\n if (option == null)\n {\n return \"--\";\n }\n\n if (option.Key.Length == 0)\n {\n return \"自动\";\n }\n\n string label = option.Label ?? option.Key;\n if (label.Length <= ClaudeRadarModelButtonMaxTextChars)\n {\n return label;\n }\n\n return label.Substring(0, ClaudeRadarModelButtonMaxTextChars);\n }\n\n private void ApplyClaudeRadarModelGridSelection(FlowLayoutPanel grid)\n {\n if (grid == null)\n {\n return;\n }\n\n string selectedKey = WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n bool selected = option != null &&\n string.Equals(option.Key, selectedKey, StringComparison.OrdinalIgnoreCase);\n ApplyClaudeRadarModelButtonStyle(button, option, selected);\n }\n }\n\n private static void ApplyClaudeRadarModelButtonStyle(Button button, ClaudeModelOption option, bool selected)\n {\n if (button == null)\n {\n return;\n }\n\n if (option == null)\n {\n button.BackColor = ControlBg;\n button.ForeColor = TextTertiary;\n button.FlatAppearance.BorderColor = ControlBorder;\n return;\n }\n\n if (selected)\n {\n button.BackColor = DesignTokens.SettingsWarmTheme.Accent;\n button.ForeColor = Color.Black;\n button.FlatAppearance.BorderColor = DesignTokens.SettingsWarmTheme.AccentHover;\n return;\n }\n\n button.BackColor = option.Available ? DesignTokens.SettingsWarmTheme.ButtonRest : MicaLayer;\n button.ForeColor = option.Available ? TextSecondary : TextTertiary;\n button.FlatAppearance.BorderColor = option.Pending ? DesignTokens.Colors.Warning : ControlBorder;\n }\n\n private static string GetClaudeRadarModelGridValue(FlowLayoutPanel grid)\n {\n return grid == null ? string.Empty : WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n }\n\n private void SetClaudeRadarModelGridValue(FlowLayoutPanel grid, string value)\n {\n PopulateClaudeRadarModelGrid(grid, value);\n if (grid != null && grid.Parent != null)\n {\n LayoutClaudeRadarModelPanel(grid.Parent);\n }\n }\n\n private static FlowLayoutPanel FindClaudeRadarModelGrid(Control control)\n {\n FlowLayoutPanel grid = control as FlowLayoutPanel;\n if (grid != null && string.Equals(grid.Name, ClaudeRadarModelGridName, StringComparison.Ordinal))\n {\n return grid;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n grid = FindClaudeRadarModelGrid(control.Controls[i]);\n if (grid != null)\n {\n return grid;\n }\n }\n }\n\n return null;\n }\n\n private static void LayoutClaudeRadarModelPanel(Control panel)\n {\n if (panel == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(panel);\n Button edit = null;\n for (int i = 0; i < panel.Controls.Count; i++)\n {\n Button candidate = panel.Controls[i] as Button;\n if (candidate != null && string.Equals(candidate.Text, \"编辑映射\", StringComparison.Ordinal))\n {\n edit = candidate;\n break;\n }\n }\n\n int y = 0;\n if (grid != null)\n {\n grid.Location = new Point(0, 0);\n grid.Width = panel.Width;\n int columns = GetClaudeRadarModelColumnCountForGrid(grid.Width);\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n button.Width = buttonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Margin = new Padding(\n 0,\n 0,\n i % columns == columns - 1 ? 0 : ClaudeRadarModelButtonGap,\n ClaudeRadarModelButtonGap);\n }\n\n int rows = Math.Max(1, (grid.Controls.Count + columns - 1) / columns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n y = grid.Bottom + 10;\n }\n\n if (edit != null)\n {\n edit.Location = new Point(0, y);\n edit.Width = panel.Width;\n y = edit.Bottom;\n }\n\n panel.Height = Math.Max(54, y);\n }\n\n private static int GetClaudeRadarModelButtonWidthForGrid(int gridWidth)\n {\n int columns = GetClaudeRadarModelColumnCountForGrid(gridWidth);\n int gaps = Math.Max(0, columns - 1) * ClaudeRadarModelButtonGap;\n int usable = Math.Max(columns, gridWidth - gaps);\n return Math.Max(32, Math.Min(ClaudeRadarModelButtonWidth, usable / columns));\n }\n\n private static int GetClaudeRadarModelColumnCountForGrid(int gridWidth)\n {\n for (int columns = ClaudeRadarModelGridColumns; columns > 1; columns--)\n {\n int gaps = Math.Max(0, columns - 1) * ClaudeRadarModelButtonGap;\n if (gridWidth >= columns * ClaudeRadarModelButtonMinimumWidth + gaps)\n {\n return columns;\n }\n }\n\n return 1;\n }\n\n private static int GetClaudeRadarModelGridPreferredWidth()\n {\n return ClaudeRadarModelGridColumns * ClaudeRadarModelButtonWidth +\n Math.Max(0, ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n }\n\n private static void RelayoutSettingGroup(Control control)\n {\n Control parent = control;\n while (parent != null)\n {\n SettingGroupCard card = parent as SettingGroupCard;\n if (card != null)\n {\n card.LayoutRows();\n return;\n }\n\n parent = parent.Parent;\n }\n }\n\n private static ComboBox FindComboBox(Control control)\n {\n ComboBox combo = control as ComboBox;\n if (combo != null)\n {\n return combo;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n combo = FindComboBox(control.Controls[i]);\n if (combo != null)\n {\n return combo;\n }\n }\n }\n\n return null;\n }\n\n private void PopulateClaudeRadarModelCombo(ComboBox combo)\n {\n if (combo == null)\n {\n return;\n }\n\n combo.Items.Clear();\n combo.Items.Add(new ClaudeModelOption(string.Empty, \"自动选择\", true, false));\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n bool available = model.Enabled &&\n !string.Equals(model.Status, \"deleted\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase) &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n combo.Items.Add(new ClaudeModelOption(\n key,\n label,\n available,\n string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase)));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (combo.Items.Count > 0 && combo.SelectedIndex < 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static object GetClaudeRadarModelComboValue(ComboBox combo)\n {\n ClaudeModelOption option = combo == null ? null : combo.SelectedItem as ClaudeModelOption;\n return option == null ? string.Empty : option.Key;\n }\n\n private static void SetClaudeRadarModelComboValue(ComboBox combo, string value)\n {\n if (combo == null)\n {\n return;\n }\n\n string modelKey = WidgetSettings.NormalizeClaudeRadarModelKey(value);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ClaudeModelOption option = combo.Items[i] as ClaudeModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(modelKey))\n {\n combo.Items.Add(new ClaudeModelOption(modelKey, modelKey, false, true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static bool IsDisplayDeviceSetting(string propertyName)\n {\n return propertyName != null &&\n propertyName.EndsWith(\"DisplayDeviceName\", StringComparison.Ordinal);\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n EnsureVariantSamplesForPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void EnsureVariantSamplesForPage(CategoryPage page)\n {\n if (this.owner == null || page == null)\n {\n return;\n }\n\n string directory = GetVariantSampleDirectory();\n for (int i = 0; i < page.Editors.Count; i++)\n {\n SettingEditor editor = page.Editors[i];\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker == null)\n {\n continue;\n }\n\n string prefix = GetVariantSamplePrefix(editor.Name);\n if (prefix.Length == 0)\n {\n continue;\n }\n\n try\n {\n if (picker.HasMissingSamples(directory, prefix))\n {\n System.IO.Directory.CreateDirectory(directory);\n RenderVariantSamplesForProperty(editor.Name, directory);\n }\n\n picker.LoadSamples(directory, prefix);\n RelayoutSettingGroup(picker);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n picker.MarkSamplesUnavailable();\n }\n }\n }\n\n private static string GetVariantSampleDirectory()\n {\n return System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName,\n \"variant-samples\",\n \"v\" + ProductIdentity.Version);\n }\n\n private static string GetVariantSamplePrefix(string propertyName)\n {\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal)) return \"operation\";\n return string.Empty;\n }\n\n private static void RenderVariantSamplesForProperty(string propertyName, string directory)\n {\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal))\n {\n OperationForm.RenderVariantSamples(directory);\n }\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Width = width;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n\n SetDirtyState(false);\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private bool TrySaveSettings()\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n SetDirtyState(false);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), SettingsStatusSeverity.Error);\n return false;\n }\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n SetClaudeRadarModelGridValue(grid, value as string);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n picker.SetSelectedValue(value);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n\n PercentSliderControl slider = editor.Control as PercentSliderControl;\n if (slider != null)\n {\n slider.SetValueSilent(Convert.ToInt32(value, CultureInfo.InvariantCulture));\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n string displayDeviceName = value as string;\n if (string.Equals(editor.Property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n string modelKey = CodexRadarModelCatalog.NormalizeModelKey(value as string);\n if (modelKey.Length == 0)\n {\n modelKey = CodexRadarModelCatalog.DefaultModelKey;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ModelOption option = combo.Items[i] as ModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n combo.Items.Add(new ModelOption(\n modelKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, modelKey),\n false,\n true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n SetClaudeRadarModelComboValue(combo, value as string);\n return;\n }\n\n if (IsDisplayDeviceSetting(editor.Property.Name))\n {\n displayDeviceName = WidgetSettings.NormalizeDisplayDeviceName(displayDeviceName);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n DisplayOption option = combo.Items[i] as DisplayOption;\n if (option != null && string.Equals(option.Value, displayDeviceName, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(displayDeviceName))\n {\n combo.Items.Add(new DisplayOption(displayDeviceName, \"未连接 \" + displayDeviceName));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n button.Tag = value is int ? (int)value : 0;\n if (string.Equals(editor.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n button.Text = GetDeepSeekApiKeyButtonText();\n }\n }\n }\n\n private void ApplyResponsiveShellLayout()\n {\n int navWidth = GetNavigationWidthForClientWidth(this.ClientSize.Width);\n if (this.bodyLayout != null && this.bodyLayout.ColumnStyles.Count > 0)\n {\n this.bodyLayout.ColumnStyles[0].Width = navWidth;\n }\n\n if (this.navigationPanel != null)\n {\n int itemWidth = Math.Max(160, navWidth - 24);\n for (int i = 0; i < this.navigationPanel.Controls.Count; i++)\n {\n this.navigationPanel.Controls[i].Width = itemWidth;\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n if (editor.Property == null)\n {\n return null;\n }\n\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n return GetClaudeRadarModelGridValue(grid);\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n return picker.GetSelectedValue();\n }\n\n PercentSliderControl slider = editor.Control as PercentSliderControl;\n if (slider != null)\n {\n return slider.Value;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n ClaudeModelOption claudeModelOption = combo.SelectedItem as ClaudeModelOption;\n if (claudeModelOption != null)\n {\n return claudeModelOption.Key;\n }\n\n ModelOption modelOption = combo.SelectedItem as ModelOption;\n if (modelOption != null)\n {\n return modelOption.Key;\n }\n\n DisplayOption displayOption = combo.SelectedItem as DisplayOption;\n if (displayOption != null)\n {\n return displayOption.Value;\n }\n\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n return button.Tag is int ? (int)button.Tag : 0;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n SetDirtyState(true);\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n private void OpenGlobalLayoutEditorFromSettings()\n {\n if (this.owner == null)\n {\n return;\n }\n\n this.previewTimer.Stop();\n WidgetSettings settings = ReadSettings();\n WidgetSettings editedSettings;\n if (this.owner.TryEditGlobalLayout(settings, out editedSettings))\n {\n this.baseline = editedSettings.Clone();\n this.baseline.Normalize();\n LoadSettings(this.baseline);\n this.saved = true;\n ShowStatus(\"全局布局已保存\", SettingsStatusSeverity.Success);\n return;\n }\n\n this.owner.PreviewSettings(ReadSettings());\n ShowStatus(\"全局编辑已取消\", SettingsStatusSeverity.Warning);\n }\n\n private void OpenDeepSeekApiKeyDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"DeepSeek 配置\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.ClientSize = new Size(560, 238);\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n Label title = new Label();\n title.Text = \"DeepSeek API Key\";\n title.Font = GetUiFont(12.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(24, 20);\n title.Size = new Size(500, 30);\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label hint = new Label();\n hint.Text = HasDeepSeekEnvironmentApiKeyForUi()\n ? \"检测到 DEEPSEEK_API_KEY 环境变量;环境变量会优先于本地文件。\"\n : \"密钥保存到本地应用数据目录,不写入 settings.ini 或日志。\";\n hint.Font = GetUiFont(8.8f);\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(24, 56);\n hint.Size = new Size(500, 42);\n hint.TextAlign = ContentAlignment.MiddleLeft;\n\n TextBox keyBox = new TextBox();\n keyBox.Location = new Point(24, 104);\n keyBox.Size = new Size(512, 34);\n keyBox.BackColor = ControlBg;\n keyBox.ForeColor = TextSecondary;\n keyBox.BorderStyle = BorderStyle.FixedSingle;\n keyBox.Font = GetUiFont(9.5f);\n keyBox.UseSystemPasswordChar = true;\n keyBox.Text = ReadDeepSeekApiKeyFileForUi();\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(24, 166);\n clear.Width = 112;\n clear.Height = 44;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Location = new Point(302, 166);\n cancel.Width = 112;\n cancel.Height = 44;\n cancel.Click += delegate { dialog.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Location = new Point(424, 166);\n save.Width = 112;\n save.Height = 44;\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(string.Empty, out errorCode))\n {\n keyBox.Text = string.Empty;\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已清除\", SettingsStatusSeverity.Success);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置清除失败 \" + errorCode, SettingsStatusSeverity.Error);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(keyBox.Text, out errorCode))\n {\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已保存\", SettingsStatusSeverity.Success);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置保存失败 \" + errorCode, SettingsStatusSeverity.Error);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(keyBox);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void IncrementDeepSeekApiKeyRevision(Button sourceButton)\n {\n if (sourceButton != null)\n {\n int token = sourceButton.Tag is int ? (int)sourceButton.Tag : 0;\n sourceButton.Tag = token == int.MaxValue ? 1 : token + 1;\n sourceButton.Text = GetDeepSeekApiKeyButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static string GetDeepSeekApiKeyButtonText()\n {\n return IsDeepSeekApiKeyConfiguredForUi() ? \"修改配置\" : \"配置\";\n }\n\n private static bool IsDeepSeekApiKeyConfiguredForUi()\n {\n if (HasDeepSeekEnvironmentApiKeyForUi())\n {\n return true;\n }\n\n return ReadDeepSeekApiKeyFileForUi().Length > 0;\n }\n\n private static bool HasDeepSeekEnvironmentApiKeyForUi()\n {\n try\n {\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(DeepSeekBalanceMonitor.ApiKeyEnvironmentVariable)))\n {\n return true;\n }\n\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(DeepSeekBalanceMonitor.ApiKeyEnvironmentVariable, EnvironmentVariableTarget.User)))\n {\n return true;\n }\n\n return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(DeepSeekBalanceMonitor.ApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine));\n }\n catch\n {\n return false;\n }\n }\n\n private static string ReadDeepSeekApiKeyFileForUi()\n {\n try\n {\n string secret;\n bool migrated;\n string errorCode;\n return SecretStore.TryReadOrMigrateSecret(\n DeepSeekBalanceMonitor.ApiKeyPath,\n DeepSeekBalanceMonitor.LegacyApiKeyPath,\n SecretStore.TrimSecret,\n out secret,\n out migrated,\n out errorCode)\n ? secret\n : string.Empty;\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static bool TrySaveDeepSeekApiKeyFile(string apiKey, out string errorCode)\n {\n errorCode = string.Empty;\n try\n {\n string trimmed = (apiKey ?? string.Empty).Trim();\n if (trimmed.Length == 0)\n {\n SecretStore.DeleteSecretFiles(DeepSeekBalanceMonitor.ApiKeyPath, DeepSeekBalanceMonitor.LegacyApiKeyPath);\n return true;\n }\n\n SecretStore.WriteSecret(DeepSeekBalanceMonitor.ApiKeyPath, trimmed);\n SecretStore.DeleteLegacySecretFiles(DeepSeekBalanceMonitor.LegacyApiKeyPath);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n return false;\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n // Three-state severity instead of a plain success/error bool: Warning covers \"action\n // fired but the outcome is not known yet\" (detection/refresh requests, cancellations),\n // which used to be lumped in with Success and always rendered green.\n private enum SettingsStatusSeverity\n {\n Success,\n Warning,\n Error\n }\n\n private void ShowStatus(string text, SettingsStatusSeverity severity)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = GetStatusSeverityColor(severity);\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private static Color GetStatusSeverityColor(SettingsStatusSeverity severity)\n {\n if (severity == SettingsStatusSeverity.Error)\n {\n return ErrorClr;\n }\n\n if (severity == SettingsStatusSeverity.Warning)\n {\n return DesignTokens.Colors.Warning;\n }\n\n return AccentClr;\n }\n\n private void SetDirtyState(bool hasChanges)\n {\n this.dirty = hasChanges;\n if (this.statusLabel == null)\n {\n return;\n }\n\n if (hasChanges)\n {\n this.statusTimer.Stop();\n this.statusLabel.Text = \"有未保存的更改\";\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.Visible = true;\n }\n else if (!this.statusTimer.Enabled)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n if (this.dirty)\n {\n SetDirtyState(true);\n }\n else\n {\n this.statusLabel.Visible = false;\n }\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n bool searching = query.Length > 0;\n int firstMatchingPageIndex = -1;\n bool selectedPageHasVisibleRows = false;\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n if (page.AdvancedHeader != null)\n {\n // While searching, advanced rows surface directly, so the collapse header hides.\n page.AdvancedHeader.Visible = !searching;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool allowed = !group.Advanced || page.AdvancedExpanded || searching;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = allowed && (pageMatch || groupTitleMatch ||\n editor.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n\n if (searching && anyVisible)\n {\n if (firstMatchingPageIndex < 0)\n {\n firstMatchingPageIndex = i;\n }\n\n if (i == this.selectedPageIndex)\n {\n selectedPageHasVisibleRows = true;\n }\n }\n }\n\n if (searching &&\n !selectedPageHasVisibleRows &&\n firstMatchingPageIndex >= 0 &&\n firstMatchingPageIndex != this.selectedPageIndex)\n {\n SelectPage(firstMatchingPageIndex);\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : string.Empty;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return 1;\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return new NumericRange(\n WidgetSettings.MinCodexRadarManualElementOffsetPixels,\n WidgetSettings.MaxCodexRadarManualElementOffsetPixels);\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static bool IsCodexRadarManualElementOffsetSetting(string name)\n {\n return name != null &&\n name.StartsWith(\"CodexRadar\", StringComparison.OrdinalIgnoreCase) &&\n (name.EndsWith(\"OffsetX\", StringComparison.OrdinalIgnoreCase) ||\n name.EndsWith(\"OffsetY\", StringComparison.OrdinalIgnoreCase));\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private void ScheduleDynamicResolutionSizing()\n {\n if (!this.IsHandleCreated)\n {\n ApplyDynamicResolutionSizing(false);\n return;\n }\n\n try\n {\n this.BeginInvoke((MethodInvoker)delegate\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n });\n }\n catch\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n }\n }\n\n private void ApplyDynamicResolutionSizing(bool usePreferredSize)\n {\n Rectangle workArea = GetTargetWorkArea();\n Size minimum = GetMinimumWindowSizeForWorkArea(workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n\n this.MinimumSize = minimum;\n this.MaximumSize = new Size(\n Math.Max(minimum.Width, Math.Max(1, workArea.Width)),\n Math.Max(minimum.Height, Math.Max(1, workArea.Height)));\n\n Size target = usePreferredSize || this.ClientSize.Width <= 0 || this.ClientSize.Height <= 0\n ? preferred\n : this.ClientSize;\n target = new Size(\n Clamp(target.Width, minimum.Width, preferred.Width),\n Clamp(target.Height, minimum.Height, preferred.Height));\n\n if (this.ClientSize != target)\n {\n this.ClientSize = target;\n }\n\n if (this.Visible || this.IsHandleCreated)\n {\n ClampWindowToWorkArea(workArea);\n }\n\n ApplyResponsiveShellLayout();\n }\n\n private Rectangle GetTargetWorkArea()\n {\n Rectangle workArea;\n if (this.owner != null && !this.owner.IsDisposed)\n {\n workArea = Screen.FromControl(this.owner).WorkingArea;\n }\n else if (this.IsHandleCreated)\n {\n workArea = Screen.FromHandle(this.Handle).WorkingArea;\n }\n else\n {\n workArea = Screen.PrimaryScreen.WorkingArea;\n }\n\n EnsureUsableWorkArea(ref workArea);\n return workArea;\n }\n\n private void ClampWindowToWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int left = Clamp(this.Left, workArea.Left, Math.Max(workArea.Left, workArea.Right - this.Width));\n int top = Clamp(this.Top, workArea.Top, Math.Max(workArea.Top, workArea.Bottom - this.Height));\n if (left != this.Left || top != this.Top)\n {\n this.Location = new Point(left, top);\n }\n }\n\n private static Size GetPreferredClientSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Clamp(\n (int)Math.Round(workArea.Width * (PreferredClientWidth / (double)ReferenceWorkAreaWidth), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientWidth, Math.Max(1, workArea.Width)),\n PreferredClientWidth);\n int height = Clamp(\n (int)Math.Round(workArea.Height * (PreferredClientHeight / (double)ReferenceWorkAreaHeight), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientHeight, Math.Max(1, workArea.Height)),\n PreferredClientHeight);\n return FitClientSizeToWorkArea(new Size(width, height), workArea);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n return FitClientSizeToWorkArea(desiredSize, Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size FitClientSizeToWorkArea(Size desiredSize, Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Math.Min(desiredSize.Width, Math.Max(MinimumClientWidth, workArea.Width - ScreenMargin));\n int height = Math.Min(desiredSize.Height, Math.Max(MinimumClientHeight, workArea.Height - ScreenMargin));\n width = Math.Min(width, Math.Max(1, workArea.Width));\n height = Math.Min(height, Math.Max(1, workArea.Height));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n return GetMinimumWindowSizeForWorkArea(Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size GetMinimumWindowSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n return new Size(\n Math.Min(preferred.Width, Math.Max(Math.Min(MinimumClientWidth, workArea.Width), workArea.Width - 128)),\n Math.Min(preferred.Height, Math.Max(Math.Min(MinimumClientHeight, workArea.Height), workArea.Height - 128)));\n }\n\n private static int GetNavigationWidthForClientWidth(int clientWidth)\n {\n if (clientWidth <= 960)\n {\n return CompactNavWidth;\n }\n\n if (clientWidth <= 1280)\n {\n return MediumNavWidth;\n }\n\n return DefaultNavWidth;\n }\n\n private static void EnsureUsableWorkArea(ref Rectangle workArea)\n {\n if (workArea.Width > 0 && workArea.Height > 0)\n {\n return;\n }\n\n workArea = Screen.PrimaryScreen.Bounds;\n if (workArea.Width <= 0 || workArea.Height <= 0)\n {\n workArea = new Rectangle(0, 0, ReferenceWorkAreaWidth, ReferenceWorkAreaHeight);\n }\n }\n\n private static int Clamp(int value, int minimum, int maximum)\n {\n if (maximum < minimum)\n {\n maximum = minimum;\n }\n\n if (value < minimum)\n {\n return minimum;\n }\n\n if (value > maximum)\n {\n return maximum;\n }\n\n return value;\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static string RunOpenCloseStressSelfTest(int iterations)\n {\n int loopCount = Math.Max(1, Math.Min(500, iterations));\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n baseline.CodexRadarEnabled = true;\n baseline.ClaudeRadarEnabled = true;\n baseline.Normalize();\n\n using (Win11SettingsForm warmup = new Win11SettingsForm(null, baseline))\n {\n warmup.StartPosition = FormStartPosition.Manual;\n warmup.Location = new Point(-30000, -30000);\n warmup.Show();\n Application.DoEvents();\n warmup.Close();\n Application.DoEvents();\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters before = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n for (int i = 0; i < loopCount; i++)\n {\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-30000, -30000);\n form.Show();\n Application.DoEvents();\n form.Close();\n Application.DoEvents();\n }\n\n if ((i + 1) % 25 == 0)\n {\n ForceResourceCleanup();\n }\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters after = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n int handleDelta = after.HandleCount - before.HandleCount;\n int gdiDelta = after.GdiObjects - before.GdiObjects;\n int userDelta = after.UserObjects - before.UserObjects;\n if (handleDelta > 80 || gdiDelta > 8 || userDelta > 16)\n {\n throw new InvalidOperationException(\n \"WinUI settings open/close resource growth exceeded threshold. Iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \", HandlesDelta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \", GdiDelta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \", UserDelta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture));\n }\n\n return \"Settings open/close policy: PASS iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \" handles_delta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \" gdi_delta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \" user_delta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture);\n }\n\n internal static void RunSettingsBindingSelfTest()\n {\n VerifySettingsWindowActivationPolicy();\n VerifyUnsavedPreviewConsumePolicy();\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.ShowInTaskbar = false;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n private static void VerifySettingsWindowActivationPolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n if (!form.ShowInTaskbar)\n {\n throw new InvalidOperationException(\"WinUI settings window must be visible in taskbar and Alt+Tab.\");\n }\n }\n }\n\n private static void VerifyUnsavedPreviewConsumePolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n WidgetSettings revertSettings;\n if (!form.TryConsumeUnsavedPreview(out revertSettings) ||\n revertSettings == null ||\n !object.Equals(revertSettings.OperationPrimaryPanelMode, baseline.OperationPrimaryPanelMode))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consume failed.\");\n }\n\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consumed twice.\");\n }\n }\n\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n WidgetSettings revertSettings;\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings owner-closing preview should not be consumed.\");\n }\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private static void ForceResourceCleanup()\n {\n GC.Collect();\n GC.WaitForPendingFinalizers();\n GC.Collect();\n Application.DoEvents();\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"OperationRadialCoreAutoHideKeepAliveEnabled\",\n \"OperationRadialIdleCollapseSeconds\",\n \"OperationRadialIdleResetOnInteractionEnabled\",\n \"OperationRadialKeepOpenAfterLeafClickEnabled\",\n \"FallbackDisconnectedDisplaysEnabled\",\n \"ResolutionCompatibilityModeEnabled\",\n \"ResolutionCompatibilityScalePercent\",\n \"MainDisplayDeviceName\",\n \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\",\n GlobalLayoutEditCommandName,\n \"Width\",\n \"CodexRadarEnabled\",\n \"CodexRadarSoftwareMode\",\n \"CodexRadarModelKey\",\n \"RadarClockAutoSwitchModelEnabled\",\n \"RadarClockTimeDisplayMode\",\n \"DisplayTimeZoneMode\",\n \"DisplayTimeZoneId\",\n \"CodexRadarPublicJsonEnabled\",\n \"CodexRadarHtmlFallbackEnabled\",\n \"CodexRadarRssFallbackEnabled\",\n \"CodexRadarServiceProbeToken\",\n \"CodexQuotaDueResetProtectionEnabled\",\n \"CodexQuotaRssResetProtectionEnabled\",\n \"CodexQuotaProviderZeroDropProtectionEnabled\",\n \"CodexQuotaDuplicateSameBalanceRingProtectionEnabled\",\n \"CodexQuotaProviderFiveHourEarlyResetSpikeProtectionEnabled\",\n \"CodexQuotaProviderWeeklySpikeProtectionEnabled\",\n \"CodexQuotaStrictFiveHourResetBoundaryEnabled\",\n \"CodexQuotaWeeklyBaselineAutoRepairEnabled\",\n \"ClaudeRadarEnabled\",\n \"ClaudeRadarModelKey\",\n \"ClaudeRadarJsonEnabled\",\n \"ClaudeRadarCommunityRatingsEnabled\",\n \"ClaudeRadarLocalQuotaFallbackEnabled\",\n \"ClaudeRadarServiceProbeToken\",\n ClaudeSetupTokenCommandName,\n \"DeepSeekApiKeyRevision\",\n \"AiRequestProtectionAutoEnabled\",\n \"AiRequestProtectionManualBlockEnabled\",\n \"CodexQuotaPlanEnabled\",\n \"CodexQuotaPlanWeeklyComparison\",\n \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\",\n \"CodexQuotaPlanFiveHourThresholdPercent\",\n \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\",\n \"CodexQuotaPlanPauseGoalIds\",\n \"CodexQuotaPlanResumeGoalIds\",\n \"OperationRenderVariant\",\n \"PowerThermalAutoSizeEnabled\",\n \"PowerThermalManualEnergySaverThresholdPercent\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\",\n \"OperationPrimaryPanelMode\",\n \"OperationSettingsLogicExtensionEnabled\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyClaudeRadarModelGridPolicy();\n VerifyVariantPickerPolicy();\n VerifyDynamicResolutionSizingPolicy();\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyClaudeRadarModelGridPolicy()\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(\"ClaudeRadarModelKey\", out editor))\n {\n throw new InvalidOperationException(\"Claude Radar model grid editor missing.\");\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid == null)\n {\n throw new InvalidOperationException(\"Claude Radar model selector must use the responsive button grid.\");\n }\n\n if (grid.Controls.Count < ClaudeRadarModelGridColumns)\n {\n throw new InvalidOperationException(\"Claude Radar model grid slot count is below the maximum row size.\");\n }\n\n int columns = GetClaudeRadarModelColumnCountForGrid(grid.Width);\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n int rowWidth = columns * buttonWidth +\n Math.Max(0, columns - 1) * ClaudeRadarModelButtonGap;\n if (rowWidth > grid.Width)\n {\n throw new InvalidOperationException(\"Claude Radar model grid buttons exceed selector width.\");\n }\n\n int preferredGridWidth = GetClaudeRadarModelGridPreferredWidth();\n if (grid.Width >= preferredGridWidth && buttonWidth < ClaudeRadarModelButtonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid preferred button width was reduced.\");\n }\n\n int compactWidth = 280;\n int compactColumns = GetClaudeRadarModelColumnCountForGrid(compactWidth);\n int compactButtonWidth = GetClaudeRadarModelButtonWidthForGrid(compactWidth);\n if (compactColumns != 3 || compactButtonWidth < ClaudeRadarModelButtonMinimumWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid does not expand cells in narrow settings rows.\");\n }\n\n string commonLongLabel = GetClaudeRadarModelButtonText(\n new ClaudeModelOption(\"m1\", \"Opus 4.8 high\", true, false));\n if (!string.Equals(commonLongLabel, \"Opus 4.8 high\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar model grid truncates common model labels.\");\n }\n\n bool sawAuto = false;\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n throw new InvalidOperationException(\"Claude Radar model grid contains a non-button slot.\");\n }\n\n if (button.Width != buttonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid button width is not responsive.\");\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n if (option == null)\n {\n if (button.Enabled || !string.Equals(button.Text, \"--\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar empty model slot must be disabled --.\");\n }\n continue;\n }\n\n if (option.Key.Length == 0)\n {\n sawAuto = true;\n if (!button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar automatic model slot must be selectable.\");\n }\n }\n else if (!option.Available && button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar unavailable model slot must be disabled.\");\n }\n }\n\n if (!sawAuto)\n {\n throw new InvalidOperationException(\"Claude Radar model grid missing automatic slot.\");\n }\n }\n\n private void VerifyVariantPickerPolicy()\n {\n string[] pickerNames = new string[]\n {\n \"OperationRenderVariant\"\n };\n\n for (int i = 0; i < pickerNames.Length; i++)\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(pickerNames[i], out editor))\n {\n throw new InvalidOperationException(\"Render variant picker binding missing: \" + pickerNames[i]);\n }\n\n if (!(editor.Control is VariantPicker))\n {\n throw new InvalidOperationException(\"Render variant must use VariantPicker: \" + pickerNames[i]);\n }\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n Size originalMinimum = this.MinimumSize;\n int originalPageIndex = this.selectedPageIndex;\n try\n {\n Size[] testSizes = new Size[]\n {\n this.MinimumSize,\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1280, 680)),\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1024, 728))\n };\n\n for (int sizeIndex = 0; sizeIndex < testSizes.Length; sizeIndex++)\n {\n this.MinimumSize = testSizes[sizeIndex];\n this.ClientSize = testSizes[sizeIndex];\n Application.DoEvents();\n VerifyNoVisibleControlClippingAtCurrentSize();\n }\n }\n finally\n {\n this.MinimumSize = originalMinimum;\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n private void VerifyNoVisibleControlClippingAtCurrentSize()\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n card.RefreshLayoutForWidth(card.ClientSize.Width);\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\n \"WinUI settings layout clipped: \" + editor.Name +\n \" card=\" + card.ClientSize.ToString() +\n \" padding=\" + card.Padding.ToString() +\n \" control=\" + control.Bounds.ToString() +\n \" title=\" + card.TitleLabel.Bounds.ToString() +\n \" hint=\" + card.HintLabel.Bounds.ToString() +\n \" rightLimit=\" + rightLimit.ToString(CultureInfo.InvariantCulture) +\n \" bottomLimit=\" + bottomLimit.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n }\n\n private static void VerifyDynamicResolutionSizingPolicy()\n {\n Rectangle[] workAreas = new Rectangle[]\n {\n new Rectangle(0, 60, 2880, 1740),\n new Rectangle(0, 0, 1920, 1040),\n new Rectangle(0, 0, 1600, 860),\n new Rectangle(0, 0, 1366, 728),\n new Rectangle(0, 0, 1280, 680),\n new Rectangle(0, 0, 1024, 728)\n };\n\n for (int i = 0; i < workAreas.Length; i++)\n {\n Size preferred = GetPreferredClientSizeForWorkArea(workAreas[i]);\n Size minimum = GetMinimumWindowSizeForWorkArea(workAreas[i]);\n if (preferred.Width > workAreas[i].Width ||\n preferred.Height > workAreas[i].Height ||\n minimum.Width > workAreas[i].Width ||\n minimum.Height > workAreas[i].Height ||\n minimum.Width > preferred.Width ||\n minimum.Height > preferred.Height)\n {\n throw new InvalidOperationException(\n \"WinUI settings dynamic resolution sizing failed: \" +\n workAreas[i].Width.ToString(CultureInfo.InvariantCulture) +\n \"x\" +\n workAreas[i].Height.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"ClaudeRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"ClaudeRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"CodexRadarManualLeftPercent\", new NumericRange(WidgetSettings.MinCodexRadarManualLeftPercent, WidgetSettings.MaxCodexRadarManualLeftPercent) },\n { \"CodexRadarManualGapPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualGapPixels, WidgetSettings.MaxCodexRadarManualGapPixels) },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualEfficiencyTextWidthPixels, WidgetSettings.MaxCodexRadarManualEfficiencyTextWidthPixels) },\n { \"CodexRadarManualQuotaRowsWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualQuotaRowsWidthPixels, WidgetSettings.MaxCodexRadarManualQuotaRowsWidthPixels) },\n { \"CodexRadarManualIqStatusWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualIqStatusWidthPixels, WidgetSettings.MaxCodexRadarManualIqStatusWidthPixels) },\n { \"CodexRadarManualTextScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualTextScalePercent, WidgetSettings.MaxCodexRadarManualTextScalePercent) },\n { \"CodexRadarManualRingScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualRingScalePercent, WidgetSettings.MaxCodexRadarManualRingScalePercent) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"PowerThermalManualEnergySaverThresholdPercent\", new NumericRange(WidgetSettings.MinPowerThermalManualEnergySaverThresholdPercent, WidgetSettings.MaxPowerThermalManualEnergySaverThresholdPercent) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"ResolutionCompatibilityScalePercent\", new NumericRange(WidgetSettings.MinResolutionCompatibilityScalePercent, WidgetSettings.MaxResolutionCompatibilityScalePercent) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"OperationRadialIdleCollapseSeconds\", new NumericRange(WidgetSettings.NeverOperationRadialIdleCollapseSeconds, WidgetSettings.MaxOperationRadialIdleCollapseSeconds) },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselineValidTasks\", new NumericRange(WidgetSettings.MinCodexModelIqValidTasks, WidgetSettings.MaxCodexModelIqValidTasks) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"VisibilityOverlapIgnoresOperationPanelEnabled\", \"遮挡忽略操作面板\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"OperationPrimaryPanelMode\", \"左侧区域模式\" },\n { \"OperationWindowsButtonEnabled\", \"显示 Windows 按钮\" },\n { \"OperationMemoryPieEnabled\", \"显示内存饼图\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"AiRequestProtectionAutoEnabled\", \"AI 自动阻断\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"AI 手动阻断\" },\n { \"CodexQuotaPlanEnabled\", \"启用额度计划\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度条件\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度阈值\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时条件\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时阈值\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"恢复额度类型\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"恢复上次暂停\" },\n { \"CodexQuotaPlanPauseGoalIds\", \"暂停 goal 列表\" },\n { \"CodexQuotaPlanResumeGoalIds\", \"恢复 goal 列表\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"断开后回退显示器\" },\n { \"MainDisplayDeviceName\", \"主窗口显示器\" },\n { \"CodexRadarDisplayDeviceName\", \"Codex Radar 显示器\" },\n { \"ClaudeRadarDisplayDeviceName\", \"Claude Radar 显示器\" },\n { \"PowerThermalDisplayDeviceName\", \"功耗温度显示器\" },\n { \"NetworkMonitorDisplayDeviceName\", \"网络监控显示器\" },\n { \"ConnectionCheckDisplayDeviceName\", \"连接检测显示器\" },\n { \"OperationDisplayDeviceName\", \"操作面板显示器\" },\n { \"ResolutionCompatibilityModeEnabled\", \"分辨率兼容模式\" },\n { \"ResolutionCompatibilityScalePercent\", \"兼容缩放比例\" },\n { GlobalLayoutEditCommandName, \"全局编辑\" },\n { \"Width\", \"主窗口宽度\" },\n { \"Height\", \"主窗口高度\" },\n { \"LeftX\", \"主窗口距左边\" },\n { \"BottomY\", \"主窗口距下边\" },\n { \"CodexRadarWidth\", \"Codex Radar 宽度\" },\n { \"CodexRadarHeight\", \"Codex Radar 高度\" },\n { \"CodexRadarLeftX\", \"Codex Radar 距左边\" },\n { \"CodexRadarBottomY\", \"Codex Radar 距下边\" },\n { \"PowerThermalWidth\", \"功耗温度宽度\" },\n { \"PowerThermalHeight\", \"功耗温度高度\" },\n { \"PowerThermalLeftX\", \"功耗温度距左边\" },\n { \"PowerThermalBottomY\", \"功耗温度距下边\" },\n { \"NetworkMonitorWidth\", \"网络监控宽度\" },\n { \"NetworkMonitorHeight\", \"网络监控高度\" },\n { \"NetworkMonitorLeftX\", \"网络监控距左边\" },\n { \"NetworkMonitorBottomY\", \"网络监控距下边\" },\n { \"ConnectionCheckWidth\", \"连接检测宽度\" },\n { \"ConnectionCheckHeight\", \"连接检测高度\" },\n { \"ConnectionCheckLeftX\", \"连接检测距左边\" },\n { \"ConnectionCheckBottomY\", \"连接检测距下边\" },\n { \"OperationLeftOffset\", \"操作面板距左边\" },\n { \"OperationBottomOffset\", \"操作面板距下边\" },\n { \"BackgroundTransparencyPercent\", \"主窗口背景透明度\" },\n { \"ApplicationTransparencyPercent\", \"主窗口整体透明度\" },\n { \"ShowCpu\", \"显示 CPU\" },\n { \"ShowMemory\", \"显示内存\" },\n { \"ShowDisk\", \"显示磁盘\" },\n { \"ShowNetwork\", \"显示网络\" },\n { \"ShowGpu\", \"显示 GPU\" },\n { \"ShowNpu\", \"显示 NPU\" },\n { \"HoverOpacityEnabled\", \"鼠标靠近时隐藏\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"触发距离(像素)\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"OperationRadialCoreAutoHideKeepAliveEnabled\", \"圆圈悬停保持显示\" },\n { \"OperationRadialIdleCollapseSeconds\", \"扇形盘自动收回秒数\" },\n { \"OperationRadialIdleResetOnInteractionEnabled\", \"操作后重置收回计时\" },\n { \"OperationRadialKeepOpenAfterLeafClickEnabled\", \"末端按钮后保持展开\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarSoftwareMode\", \"共享窗检测对象\" },\n { \"CodexRadarModelKey\", \"CODEX 模型\" },\n { \"CodexRadarModelVersion\", \"旧模型版本\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"过期自动切换模型\" },\n { \"RadarClockTimeDisplayMode\", \"时钟时间显示\" },\n { \"CodexRadarEnabled\", \"启用共享 Radar 小窗\" },\n { \"CodexRadarTransparencyPercent\", \"Codex Radar 透明度\" },\n { \"ClaudeRadarEnabled\", \"启用独立 Claude Radar\" },\n { \"ClaudeRadarWidth\", \"Claude Radar 宽度\" },\n { \"ClaudeRadarHeight\", \"Claude Radar 高度\" },\n { \"ClaudeRadarLeftX\", \"Claude Radar 左侧 X\" },\n { \"ClaudeRadarBottomY\", \"Claude Radar 底部 Y\" },\n { \"ClaudeRadarTransparencyPercent\", \"Claude Radar 背景透明\" },\n { \"ClaudeRadarModelKey\", \"Claude 模型映射\" },\n { \"ClaudeRadarJsonEnabled\", \"Claude 站点 JSON\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"首页模型元数据回退\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"Claude 社区体感分\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"本地 7 天额度线回退\" },\n { \"ClaudeRadarServiceProbeToken\", \"检查 Claude 数据链路\" },\n { ClaudeSetupTokenCommandName, \"Claude Code 用量令牌\" },\n { \"ClaudeRadarRandomTestEnabled\", \"Claude 随机测试\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"Claude 随机测试自动刷新\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"OperationRenderVariant\", \"外观风格\" },\n { \"OperationSettingsLogicExtensionEnabled\", \"设置扩展到操作逻辑\" },\n { \"CodexRadarManualLayoutEnabled\", \"启用手动布局\" },\n { \"CodexRadarManualLeftPercent\", \"左侧区域占比\" },\n { \"CodexRadarManualGapPixels\", \"模块间距\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"效率文字列宽\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"余额列宽\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"IQ 状态列宽\" },\n { \"CodexRadarManualTextScalePercent\", \"文字比例\" },\n { \"CodexRadarManualRingScalePercent\", \"圆环比例\" },\n { \"CodexRadarTimeEfficiencyRingOffsetX\", \"时间环 X\" },\n { \"CodexRadarTimeEfficiencyRingOffsetY\", \"时间环 Y\" },\n { \"CodexRadarTimeEfficiencyTextOffsetX\", \"时间字 X\" },\n { \"CodexRadarTimeEfficiencyTextOffsetY\", \"时间字 Y\" },\n { \"CodexRadarTokenEfficiencyRingOffsetX\", \"Token 环 X\" },\n { \"CodexRadarTokenEfficiencyRingOffsetY\", \"Token 环 Y\" },\n { \"CodexRadarTokenEfficiencyTextOffsetX\", \"Token 字 X\" },\n { \"CodexRadarTokenEfficiencyTextOffsetY\", \"Token 字 Y\" },\n { \"CodexRadarConnectionTopTextOffsetX\", \"连接上字 X\" },\n { \"CodexRadarConnectionTopTextOffsetY\", \"连接上字 Y\" },\n { \"CodexRadarConnectionLineOffsetX\", \"连接线 X\" },\n { \"CodexRadarConnectionLineOffsetY\", \"连接线 Y\" },\n { \"CodexRadarConnectionBottomTextOffsetX\", \"连接下字 X\" },\n { \"CodexRadarConnectionBottomTextOffsetY\", \"连接下字 Y\" },\n { \"CodexRadarFiveHourQuotaRingOffsetX\", \"5h 环 X\" },\n { \"CodexRadarFiveHourQuotaRingOffsetY\", \"5h 环 Y\" },\n { \"CodexRadarFiveHourQuotaTextOffsetX\", \"5h 字 X\" },\n { \"CodexRadarFiveHourQuotaTextOffsetY\", \"5h 字 Y\" },\n { \"CodexRadarWeeklyQuotaRingOffsetX\", \"周环 X\" },\n { \"CodexRadarWeeklyQuotaRingOffsetY\", \"周环 Y\" },\n { \"CodexRadarWeeklyQuotaTextOffsetX\", \"周字 X\" },\n { \"CodexRadarWeeklyQuotaTextOffsetY\", \"周字 Y\" },\n { \"CodexRadarQuotaRadarLineOffsetX\", \"额度线 X\" },\n { \"CodexRadarQuotaRadarLineOffsetY\", \"额度线 Y\" },\n { \"CodexRadarIqRingOffsetX\", \"IQ 环 X\" },\n { \"CodexRadarIqRingOffsetY\", \"IQ 环 Y\" },\n { \"CodexRadarIqTextOffsetX\", \"IQ 字 X\" },\n { \"CodexRadarIqTextOffsetY\", \"IQ 字 Y\" },\n { \"CodexRadarPublicJsonEnabled\", \"Codex 公开 JSON\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"Codex 首页 HTML 回退\" },\n { \"CodexRadarRssFallbackEnabled\", \"Codex RSS 重置提醒\" },\n { \"CodexRadarServiceProbeToken\", \"检查 Codex 数据链路\" },\n { \"CodexQuotaDueResetProtectionEnabled\", \"到期重置保护\" },\n { \"CodexQuotaRssResetProtectionEnabled\", \"RSS 重置保护\" },\n { \"CodexQuotaProviderZeroDropProtectionEnabled\", \"Provider 零值保护\" },\n { \"CodexQuotaDuplicateSameBalanceRingProtectionEnabled\", \"相同余额保留消耗环\" },\n { \"CodexQuotaProviderFiveHourEarlyResetSpikeProtectionEnabled\", \"5h 提前满额保护\" },\n { \"CodexQuotaProviderWeeklySpikeProtectionEnabled\", \"周额度突增保护\" },\n { \"CodexQuotaStrictFiveHourResetBoundaryEnabled\", \"严格 5h 边界\" },\n { \"CodexQuotaWeeklyBaselineAutoRepairEnabled\", \"周基线自动修复\" },\n { \"DeepSeekApiKeyRevision\", \"DeepSeek 余额配置\" },\n { \"CodexModelIqTestEnabled\", \"用测试值代替实时 IQ(调试用)\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过数\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"IQ 基准自动跟随网站\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过数\" },\n { \"CodexModelIqBaselineValidTasks\", \"IQ 基准总题数\" },\n { \"CodexModelEfficiencyTestEnabled\", \"用测试值代替实时效率(调试用)\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"通用显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"通用显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"PowerThermalManualEnergySaverThresholdPercent\", \"手动节能阈值\" },\n { \"PowerThermalTransparencyPercent\", \"功耗温度透明度\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkMonitorTransparencyPercent\", \"网络监控透明度\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"立即刷新 GFW 检测\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测间隔\" },\n { \"ConnectionCheckTransparencyPercent\", \"连接检测透明度\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"连接检测边框透明度\" },\n { \"ConnectionCheckManualRefreshToken\", \"立即刷新连接检测\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"OperationButtonSize\", \"按钮大小\" },\n { \"OperationBackgroundTransparencyPercent\", \"操作面板透明度\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过数\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过数\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"五档:总是可见、全屏时不可见、最大化时不可见、遮挡时不可见、仅桌面可见;默认全屏时不可见。最大化档也包含全屏。\" },\n { \"VisibilityOverlapIgnoresOperationPanelEnabled\", \"仅在“遮挡时不可见”生效;开启后左下角操作面板及其展开区域不会因为被其他应用窗口覆盖而隐藏。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"ForceShowForegroundFpsEnabled\", \"调试用,强制显示前台 FPS 信息。\" },\n { \"AiRequestProtectionAutoEnabled\", \"网络监控判定为 GFW 明确阻断时,阻断本程序发往 OpenAI、ChatGPT、Claude 和 Anthropic 的请求。\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"手动启用后立即阻断本程序相关 AI 请求;也可在左下角程序设置按钮单击打开的特殊设置中切换。\" },\n { \"CodexQuotaPlanEnabled\", \"按本地 Codex 剩余额度缓存自动暂停或恢复选中的 Codex goal。\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度百分比的比较方向;默认小于 3%。\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时额度百分比的比较方向;默认小于 90%。\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"额度计划触发后,选择额度恢复时看周额度、5 小时额度,还是两者都恢复。\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"额度恢复时优先恢复本程序上次因额度计划暂停的 goal;关闭后使用恢复列表。\" },\n { \"CodexQuotaPlanPauseGoalIds\", \"达到额度计划触发条件时暂停的 Codex goal ID,用 | 分隔;可从本地 goal 管理记录复制。\" },\n { \"CodexQuotaPlanResumeGoalIds\", \"关闭“恢复上次暂停”时使用,额度恢复后启用这些 Codex goal ID,用 | 分隔。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityRevealDelaySeconds\", \"鼠标离开后继续隐藏的秒数。\" },\n { \"HoverOpacityRevealResetSeconds\", \"短时间重新靠近时,保持隐藏的重置秒数。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"反向隐藏临时恢复后,鼠标移开多久回到隐藏。\" },\n { \"AutoHoverOpacityIdleEnabled\", \"鼠标一段时间不动时自动进入隐藏透明度。\" },\n { \"AutoHoverOpacityIdleSeconds\", \"范围 1-300 秒,达到后自动隐藏。\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"窗口状态缓存检测到非本程序、非 SeelenUI 应用最大化或全屏时自动进入隐藏透明度。\" },\n { \"OperationRadialCoreAutoHideKeepAliveEnabled\", \"鼠标停在左下角扇形速控盘核心圆圈上时,暂停并重置自动隐藏计时器,让所有隐藏透明度窗口保持显示。\" },\n { \"OperationRadialIdleCollapseSeconds\", \"范围 1-60 秒;设为 0 表示永不自动收回扇形速控盘。\" },\n { \"OperationRadialIdleResetOnInteractionEnabled\", \"开启后鼠标移动、按下或展开新分支会重新开始扇形速控盘自动收回计时。\" },\n { \"OperationRadialKeepOpenAfterLeafClickEnabled\", \"开启后点击扇形速控盘末端按钮不会自动收起菜单;关闭后恢复点击末端按钮即收起。\" },\n { \"OperationSettingsLogicExtensionEnabled\", \"开启后在扇形速控盘“设置”分支中增加常用逻辑和全部开关目录;关闭时保持原来的 3 项设置菜单。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarSoftwareMode\", \"只影响 Codex Radar 这个共享小窗:自动按前台和运行态选择,或固定显示 CODEX/CLAUDE 数据。独立 Claude Radar 不受这里影响。\" },\n { \"CodexRadarModelKey\", \"共享小窗处于 CODEX 模式时使用的 CodexRadar 模型;CLAUDE 模式使用 Claude 模型映射。\" },\n { \"CodexRadarModelVersion\", \"兼容旧配置的模型版本枚举;日常使用上方 CODEX 模型选择,不需要改这里。\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"Codex/Claude Radar 时钟跨过完整周期仍没有当前模型 IQ 更新时,自动切到同站点当天最近刷新 IQ 的模型。\" },\n { \"RadarClockTimeDisplayMode\", \"控制两个 Radar 时钟中心下方时间:UTC、当前本机时间、上次尝试刷新,或上次实际 IQ 刷新。\" },\n { \"CodexRadarPublicJsonEnabled\", \"读取 codexradar.com/current.json 公开摘要层,包含窗口、预测和 API 可用性说明。\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"公开 JSON 缺少展示字段时,从 codexradar.com 首页补齐 IQ、效率、额度线和模型目录展示数据。\" },\n { \"CodexRadarRssFallbackEnabled\", \"读取 CodexRadar feed.xml 的重置提醒;关闭后不会用 RSS 触发额度重置保护。\" },\n { \"CodexRadarServiceProbeToken\", \"点击后探测 Codex 公开摘要、授权 API、首页 HTML 和 RSS,并写入本地诊断结果。\" },\n { \"CodexQuotaDueResetProtectionEnabled\", \"本地 resets_at 到期后临时把对应余额显示为 100,直到新样本证明进入下一窗口;推荐保持开启。\" },\n { \"CodexQuotaRssResetProtectionEnabled\", \"CodexRadar RSS 出现新的重置记录时触发额外重置保护;推荐保持开启。\" },\n { \"CodexQuotaProviderZeroDropProtectionEnabled\", \"Provider 突然把高余额报成 0 且 reset 边界未推进时拒绝该样本;推荐保持开启。\" },\n { \"CodexQuotaDuplicateSameBalanceRingProtectionEnabled\", \"连续读取到相同余额时保留已有消耗环,不因重复日志清空尾段;推荐保持开启。\" },\n { \"CodexQuotaProviderFiveHourEarlyResetSpikeProtectionEnabled\", \"Provider 在旧 5 小时窗口未到期时突然报接近满额并后移 reset 时拒绝;默认关闭,避免拦截手动重置卡。\" },\n { \"CodexQuotaProviderWeeklySpikeProtectionEnabled\", \"Provider 在周窗口未到期时把低周余额跳到近满时拒绝;默认关闭,避免拦截手动重置卡。\" },\n { \"CodexQuotaStrictFiveHourResetBoundaryEnabled\", \"只有旧 5 小时 reset 已到期且边界推进时才重建周消耗基线;默认关闭,允许手动重置卡被余额上涨识别。\" },\n { \"CodexQuotaWeeklyBaselineAutoRepairEnabled\", \"检测到疑似被 100 污染的周消耗基线时自动修回当前周余额;默认关闭,避免隐藏真实重置卡效果。\" },\n { \"CodexRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"CodexModelIqTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelIqTestPassed\", \"手动指定 IQ 测试通过数。\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"开启时自动读取网站有效题数和常态区推导 n/N;关闭后使用下方手动 n/N。\" },\n { \"CodexModelIqBaselineMode\", \"选择 IQ 对比基准的来源。\" },\n { \"CodexModelIqBaselinePassed\", \"手动指定 IQ 基准通过数 n。\" },\n { \"CodexModelIqBaselineValidTasks\", \"手动指定 IQ 基准总题数 N;仅在关闭自动基准时用于 IQ 环。\" },\n { \"CodexModelEfficiencyTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"手动指定 Token 效率测试百分比。\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"手动指定时间效率测试百分比。\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"选择 Token 效率对比基准的来源。\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"手动指定 Token 效率基准通过数。\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"手动指定 Token 效率基准消耗。\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"选择时间效率对比基准的来源。\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"手动指定时间效率基准通过数。\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"手动指定时间效率基准秒数。\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexRadarEnabled\", \"关闭后会释放共享 Codex Radar 分层窗口;该窗口仍可在 CLAUDE 模式显示 Claude 数据。\" },\n { \"DisplayTimeZoneMode\", \"控制 Radar 和其他窗口里的时间文字使用本机、北京时间还是指定时区。\" },\n { \"DisplayTimeZoneId\", \"Windows 时区 ID;仅在通用显示时区模式选择“指定时区”时生效。\" },\n { \"ClaudeRadarEnabled\", \"开启独立 Claude Radar 分层窗口;它固定显示 Claude 数据,不读取 Codex Radar 缓存。\" },\n { \"ClaudeRadarModelKey\", \"五列按钮选择 Claude Radar 站点 m* 模型;留空为自动,编辑映射用于维护站点模型和社区评分 key 的对应关系。\" },\n { \"ClaudeRadarJsonEnabled\", \"读取 claudecoderadar.com/data/claude-code-radar.json 作为主数据源。\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"主 JSON 缺少模型元数据时,读取首页 MODEL_NAMES 作为弱回退;不会伪造 IQ、效率或额度。\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"读取 Claude Radar 社区体感分接口,并通过模型映射表关联当前模型。\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"站点额度趋势不完整时使用本地 7 天 JSONL 历史绘制额度线。\" },\n { \"ClaudeRadarServiceProbeToken\", \"点击后触发 Claude Radar 数据链路检查;服务状态显示在窗口右侧 R/C/U 和 API 摘要中。\" },\n { ClaudeSetupTokenCommandName, \"Claude 桌面版不会主动上报用量,需要生成一次性长效令牌并粘贴进来;未配置时两个额度环会显示满环红色。\" },\n { \"ClaudeRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"OperationRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"DeepSeekApiKeyRevision\", \"配置共享 Radar 小窗 CLAUDE 模式和独立 Claude Radar 底部 DS 余额使用的 DeepSeek API Key;密钥只写入本地 DPAPI 文件,修订号只用于触发即时刷新。\" },\n { \"CodexRadarManualLayoutEnabled\", \"开启后下方布局参数实时影响 Codex Radar 内部模块,不需要重启。\" },\n { \"CodexRadarManualLeftPercent\", \"调整效率/IQ/连接流程区与余额区的左右分配。\" },\n { \"CodexRadarManualGapPixels\", \"调整左侧区域和余额区之间的像素间距。\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"调整左侧效率/IQ 圆环右侧文字列宽。\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"调整余额两行圆环和重置时间的总宽度。\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"调整余额区右侧 IQ 圆环与降智文字列宽。\" },\n { \"CodexRadarManualTextScalePercent\", \"只影响 Codex Radar 内部状态文字和余额时间字号。\" },\n { \"CodexRadarManualRingScalePercent\", \"只影响 Codex Radar 内部效率、余额和 IQ 圆环大小。\" },\n { \"PowerThermalAutoSizeEnabled\", \"根据告警数量自动调整功耗温度窗口高度。\" },\n { \"PowerThermalAutoDirection\", \"自动调整时从哪个方向展开。\" },\n { \"PowerThermalVisibleAlertCount\", \"功耗温度窗口中最多显示的告警数量。\" },\n { \"PowerThermalManualEnergySaverThresholdPercent\", \"电量低于该百分比时,在功耗电池模块显示节能叶子和“(节能)”;0 表示关闭。默认 30。\" },\n { \"ThermalTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"NetworkStatusTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测的自动刷新间隔。\" },\n { \"GfwProbeManualRefreshToken\", \"点击后请求立刻刷新 GFW 检测。\" },\n { \"ConnectionCheckManualRefreshToken\", \"点击后请求立刻刷新连接检测。\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测的自动刷新间隔。\" },\n { \"CloudEndpointTestSeed\", \"仅用于测试显示效果,日常保持默认。\" },\n { \"CloudStatusRegionMask\", \"云服务状态的地区过滤位掩码,日常保持默认。\" },\n { \"CleanIpBadgeTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"AlertTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"SeelenDockForegroundPulseEnabled\", \"需要时短暂拉前 Seelen Dock,避免被系统窗口压住。\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"指定显示器未连接时,将对应模块回退到当前主显示器。\" },\n { \"MainDisplayDeviceName\", \"留空使用当前主显示器;选择 DISPLAY 后按该显示器工作区定位。\" },\n { \"CodexRadarDisplayDeviceName\", \"留空使用当前主显示器;未连接且不回退时保留原显示器工作区。\" },\n { \"PowerThermalDisplayDeviceName\", \"留空使用当前主显示器;适合把功耗模块固定到副屏。\" },\n { \"NetworkMonitorDisplayDeviceName\", \"留空使用当前主显示器;适合把网络监控固定到副屏。\" },\n { \"ConnectionCheckDisplayDeviceName\", \"留空使用当前主显示器;适合把连接检测固定到副屏。\" },\n { \"OperationDisplayDeviceName\", \"留空使用当前主显示器;操作面板偏移量按目标显示器左下角计算。\" },\n { \"ResolutionCompatibilityModeEnabled\", \"默认关闭。开启后按 2880x1800 参考布局在运行时投影所有浮窗,用于在当前设备预览其他分辨率占比。\" },\n { \"ResolutionCompatibilityScalePercent\", \"运行时输出比例,低于 100% 压缩,高于 100% 放大;不会改写保存的真实布局坐标。\" },\n { GlobalLayoutEditCommandName, \"打开全屏布局编辑遮罩,拖拽模块位置;Enter 保存,Esc 放弃。\" },\n { \"Width\", \"逻辑像素,主窗口宽度。\" },\n { \"Height\", \"逻辑像素,主窗口高度。\" },\n { \"LeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"BottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"CodexRadarWidth\", \"逻辑像素,Codex Radar 宽度。\" },\n { \"CodexRadarHeight\", \"逻辑像素,Codex Radar 高度。\" },\n { \"CodexRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"CodexRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ClaudeRadarWidth\", \"逻辑像素,Claude Radar 宽度。\" },\n { \"ClaudeRadarHeight\", \"逻辑像素,Claude Radar 高度。\" },\n { \"ClaudeRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ClaudeRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"PowerThermalWidth\", \"逻辑像素,功耗温度窗口宽度。\" },\n { \"PowerThermalHeight\", \"逻辑像素,功耗温度窗口高度。\" },\n { \"PowerThermalLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"PowerThermalBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"NetworkMonitorWidth\", \"逻辑像素,网络监控窗口宽度。\" },\n { \"NetworkMonitorHeight\", \"逻辑像素,网络监控窗口高度。\" },\n { \"NetworkMonitorLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"NetworkMonitorBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ConnectionCheckWidth\", \"逻辑像素,连接检测窗口宽度。\" },\n { \"ConnectionCheckHeight\", \"逻辑像素,连接检测窗口高度。\" },\n { \"ConnectionCheckLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ConnectionCheckBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"OperationLeftOffset\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"OperationBottomOffset\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"BackgroundTransparencyPercent\", \"只影响主窗口背景底色,数值越高越透明。\" },\n { \"ApplicationTransparencyPercent\", \"影响主窗口整体透明度,数值越高越透明。\" },\n { \"CodexRadarTransparencyPercent\", \"影响 Codex Radar 背景透明度。\" },\n { \"ClaudeRadarTransparencyPercent\", \"影响 Claude Radar 背景透明度。\" },\n { \"PowerThermalTransparencyPercent\", \"影响功耗温度窗口背景透明度。\" },\n { \"NetworkMonitorTransparencyPercent\", \"影响网络监控窗口背景透明度。\" },\n { \"ConnectionCheckTransparencyPercent\", \"影响连接检测窗口背景透明度。\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"影响连接检测三框边框透明度。\" },\n { \"OperationBackgroundTransparencyPercent\", \"影响左下角操作面板背景透明度。\" },\n { \"ShowCpu\", \"在主窗口显示 CPU 指标。\" },\n { \"ShowMemory\", \"在主窗口显示内存指标。\" },\n { \"ShowDisk\", \"在主窗口显示磁盘指标。\" },\n { \"ShowNetwork\", \"在主窗口显示网络指标。\" },\n { \"ShowGpu\", \"在主窗口显示 GPU 指标。\" },\n { \"ShowNpu\", \"在主窗口显示 NPU 指标。\" },\n { \"OperationPrimaryPanelMode\", \"自动模式会按 SeelenUI 运行态在 Windows 按钮和内存饼图之间切换;隐藏会让小按钮移到最左侧。\" },\n { \"OperationButtonSize\", \"左下角操作面板按钮的逻辑像素大小。\" },\n { \"OperationWindowsButtonEnabled\", \"SeelenUI 未运行时会自动隐藏;关闭后始终不显示左侧 Windows 按钮。\" },\n { \"OperationMemoryPieEnabled\", \"左侧 Windows 按钮隐藏时显示物理、虚拟和前台程序内存占用饼图。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── PercentSliderControl ─────────────────────────────────────────────\n private sealed class PercentSliderControl : Panel\n {\n private readonly TrackBar trackBar;\n private readonly Label valueLabel;\n private bool suppressChanged;\n\n public event EventHandler ValueChanged;\n\n public PercentSliderControl(Font labelFont)\n {\n this.BackColor = Color.Transparent;\n this.trackBar = new TrackBar();\n this.trackBar.AutoSize = false;\n this.trackBar.TickStyle = TickStyle.None;\n this.trackBar.SmallChange = 1;\n this.trackBar.LargeChange = 5;\n this.trackBar.Height = 34;\n this.trackBar.Minimum = 0;\n this.trackBar.Maximum = 100;\n this.trackBar.ValueChanged += OnTrackBarValueChanged;\n\n this.valueLabel = new Label();\n this.valueLabel.AutoSize = false;\n this.valueLabel.TextAlign = ContentAlignment.MiddleRight;\n this.valueLabel.Font = labelFont;\n this.valueLabel.ForeColor = TextSecondary;\n this.valueLabel.BackColor = Color.Transparent;\n\n this.Controls.Add(this.trackBar);\n this.Controls.Add(this.valueLabel);\n this.Height = 54;\n UpdateValueLabel();\n }\n\n public int Minimum\n {\n get { return this.trackBar.Minimum; }\n set\n {\n this.trackBar.Minimum = value;\n if (this.trackBar.Value < value)\n {\n this.trackBar.Value = value;\n }\n }\n }\n\n public int Maximum\n {\n get { return this.trackBar.Maximum; }\n set\n {\n this.trackBar.Maximum = value;\n if (this.trackBar.Value > value)\n {\n this.trackBar.Value = value;\n }\n }\n }\n\n public int Value\n {\n get { return this.trackBar.Value; }\n }\n\n public void SetValueSilent(int value)\n {\n int next = Math.Max(this.trackBar.Minimum, Math.Min(this.trackBar.Maximum, value));\n this.suppressChanged = true;\n try\n {\n this.trackBar.Value = next;\n UpdateValueLabel();\n }\n finally\n {\n this.suppressChanged = false;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n int labelWidth = 70;\n int gap = 8;\n int trackWidth = Math.Max(80, this.Width - labelWidth - gap);\n int top = Math.Max(0, (this.Height - this.trackBar.Height) / 2);\n this.trackBar.SetBounds(0, top, trackWidth, this.trackBar.Height);\n this.valueLabel.SetBounds(trackWidth + gap, 0, labelWidth, this.Height);\n }\n\n private void OnTrackBarValueChanged(object sender, EventArgs e)\n {\n UpdateValueLabel();\n if (!this.suppressChanged && this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n private void UpdateValueLabel()\n {\n this.valueLabel.Text = this.trackBar.Value.ToString(CultureInfo.InvariantCulture) + \"%\";\n }\n }\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.SettingsWarmTheme.ToggleTrackHover : DesignTokens.SettingsWarmTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsWarmTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsWarmTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsWarmTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int layoutWidth = Math.Max(1, this.ClientSize.Width > 0 ? this.ClientSize.Width : this.Width);\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(layoutWidth);\n row.SetBounds(0, y, layoutWidth, h);\n row.RefreshLayoutForWidth(layoutWidth);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private readonly int preferredValueControlWidth;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.preferredValueControlWidth = Math.Max(44, valueControl.Width);\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = false;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = false;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n // Some value controls (e.g. the Claude model button grid) are inherently wide and wrap\n // to multiple rows on their own; forcing them to share row width with the title/hint text\n // squeezes them into unreadable single-character buttons. This skips the width-threshold\n // heuristic and always stacks the control below the text for that row.\n public bool ForceCompactLayout;\n\n public int ComputeDesiredHeight(int width)\n {\n int controlWidth = Math.Min(this.preferredValueControlWidth, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(1, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + controlHeight + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(1, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n public void RefreshLayout()\n {\n RefreshLayoutForWidth(this.ClientSize.Width);\n }\n\n public void RefreshLayoutForWidth(int clientWidth)\n {\n LayoutChildren(Math.Max(1, clientWidth));\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren(Math.Max(1, this.ClientSize.Width));\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n LayoutChildren(Math.Max(1, this.ClientSize.Width));\n }\n\n private void LayoutChildren(int clientWidth)\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = clientWidth - this.Padding.Right;\n int controlWidth = Math.Min(this.preferredValueControlWidth, Math.Max(44, clientWidth - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(clientWidth, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(1, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, controlHeight);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(1, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n int controlTop = top + Math.Max(0, (contentHeight - controlHeight) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, controlHeight);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private int GetValueControlHeight(int controlWidth)\n {\n VariantPicker picker = this.valueControl as VariantPicker;\n if (picker != null)\n {\n return picker.GetPreferredHeightForWidth(controlWidth);\n }\n\n if (FindClaudeRadarModelGrid(this.valueControl) != null)\n {\n if (this.valueControl.Width != controlWidth)\n {\n this.valueControl.Width = controlWidth;\n }\n\n LayoutClaudeRadarModelPanel(this.valueControl);\n return this.valueControl.Height;\n }\n\n return this.valueControl.Height;\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n if (this.ForceCompactLayout)\n {\n return true;\n }\n\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── AdvancedSectionHeader ────────────────────────────────────────────\n // Clickable \"复杂选项\" divider: everything below it stays collapsed until\n // the user expands it, keeping everyday pages short. Searching bypasses it.\n private sealed class AdvancedSectionHeader : Panel\n {\n private readonly Font titleFont;\n private readonly Font hintFont;\n private bool expanded;\n private bool hover;\n\n public AdvancedSectionHeader(Font titleFont, Font hintFont)\n {\n this.titleFont = titleFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = MicaBase;\n this.Cursor = Cursors.Hand;\n }\n\n public int AdvancedRowCount { get; set; }\n\n public bool Expanded\n {\n get { return this.expanded; }\n set { this.expanded = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(this.hover ? CardHover : CardRest))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(StrokeColor))\n {\n g.DrawPath(pen, path);\n }\n\n // Chevron glyph: E70D = down, E76C = right (Segoe MDL2 / Fluent Icons). Tinted warning\n // yellow rather than the default green accent - this section holds advanced/power-user\n // settings, so it gets the \"proceed with caution\" color instead of \"all good\".\n string chevron = this.expanded ? \"\\uE70D\" : \"\\uE76C\";\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(DesignTokens.Colors.Warning))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(chevron, icoFont, brush, 20, iconY);\n }\n\n using (SolidBrush brush = new SolidBrush(TextPrimary))\n {\n g.DrawString(\"复杂选项\", this.titleFont, brush, 58, (this.Height - this.titleFont.Height) / 2f - 8);\n }\n\n string hint = this.expanded\n ? \"点击收起\"\n : \"不常用的精细调整与测试项\" + (this.AdvancedRowCount > 0 ? \"(\" + this.AdvancedRowCount.ToString(CultureInfo.InvariantCulture) + \" 项)\" : string.Empty);\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n g.DrawString(hint, this.hintFont, brush, 58, this.Height / 2f + 2);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── VariantPicker ───────────────────────────────────────────────────\n // Renders per-window visual variants as cards. Sample PNGs are loaded lazily by the outer form\n // only for real owner-backed settings windows, so binding self-tests never construct render forms.\n private sealed class VariantPicker : Panel\n {\n private const int CardHeight = 124;\n private const int CardGap = 10;\n private const int ThumbnailHeight = 78;\n\n private readonly Type enumType;\n private readonly Font labelFont;\n private readonly Font hintFont;\n private readonly List<VariantChoice> choices = new List<VariantChoice>();\n private object selectedValue;\n private int hoverIndex = -1;\n private bool samplesAttempted;\n\n public event EventHandler ValueChanged;\n public event EventHandler PreferredHeightChanged;\n\n public VariantPicker(string propertyName, Type enumType, Font labelFont, Font hintFont)\n {\n this.enumType = enumType;\n this.labelFont = labelFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n\n Array values = Enum.GetValues(enumType);\n for (int i = 0; i < values.Length; i++)\n {\n object value = values.GetValue(i);\n this.choices.Add(new VariantChoice(value, new EnumOption(value).ToString()));\n if (i == 0)\n {\n this.selectedValue = value;\n }\n }\n\n UpdatePreferredHeight();\n }\n\n public void SetSelectedValue(object value)\n {\n if (value == null || !this.enumType.IsInstanceOfType(value))\n {\n if (this.choices.Count > 0)\n {\n this.selectedValue = this.choices[0].Value;\n }\n }\n else\n {\n this.selectedValue = value;\n }\n\n this.Invalidate();\n }\n\n public object GetSelectedValue()\n {\n return this.selectedValue;\n }\n\n public int GetPreferredHeightForWidth(int width)\n {\n int columns = GetColumnCount(width);\n int rows = Math.Max(1, (this.choices.Count + columns - 1) / columns);\n return rows * CardHeight + Math.Max(0, rows - 1) * CardGap;\n }\n\n public bool HasMissingSamples(string directory, string prefix)\n {\n if (this.samplesAttempted)\n {\n return false;\n }\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n string path = GetSamplePath(directory, prefix, this.choices[i].Value);\n if (!System.IO.File.Exists(path))\n {\n return true;\n }\n }\n\n return false;\n }\n\n public void LoadSamples(string directory, string prefix)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n VariantChoice choice = this.choices[i];\n string path = GetSamplePath(directory, prefix, choice.Value);\n if (!System.IO.File.Exists(path))\n {\n continue;\n }\n\n try\n {\n using (Image original = Image.FromFile(path))\n {\n Image old = choice.Image;\n choice.Image = new Bitmap(original);\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n catch\n {\n Image old = choice.Image;\n choice.Image = null;\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n public void MarkSamplesUnavailable()\n {\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n UpdatePreferredHeight();\n }\n\n protected override void OnMouseMove(MouseEventArgs e)\n {\n int nextHover = GetChoiceIndexAt(e.Location);\n if (nextHover != this.hoverIndex)\n {\n this.hoverIndex = nextHover;\n this.Invalidate();\n }\n\n base.OnMouseMove(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hoverIndex = -1;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n protected override void OnMouseClick(MouseEventArgs e)\n {\n base.OnMouseClick(e);\n int index = GetChoiceIndexAt(e.Location);\n if (index < 0 || index >= this.choices.Count)\n {\n return;\n }\n\n object nextValue = this.choices[index].Value;\n if (object.Equals(this.selectedValue, nextValue))\n {\n return;\n }\n\n this.selectedValue = nextValue;\n this.Invalidate();\n if (this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.InterpolationMode = InterpolationMode.HighQualityBicubic;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n Rectangle rect = GetCardBounds(i);\n if (rect.Width <= 0 || rect.Height <= 0)\n {\n continue;\n }\n\n VariantChoice choice = this.choices[i];\n bool selected = object.Equals(choice.Value, this.selectedValue);\n bool hover = i == this.hoverIndex;\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(hover ? CardHover : ControlBg))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(selected ? AccentClr : ControlBorder, selected ? 2.0f : 1.0f))\n {\n g.DrawPath(pen, path);\n }\n\n Rectangle thumb = new Rectangle(rect.Left + 8, rect.Top + 8, rect.Width - 16, ThumbnailHeight);\n DrawChoiceThumbnail(g, choice, thumb);\n\n Rectangle labelRect = new Rectangle(rect.Left + 10, thumb.Bottom + 7, rect.Width - 20, 24);\n using (StringFormat format = new StringFormat())\n using (SolidBrush textBrush = new SolidBrush(selected ? TextPrimary : TextSecondary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(choice.Label, this.labelFont, textBrush, labelRect, format);\n }\n }\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (this.choices[i].Image != null)\n {\n this.choices[i].Image.Dispose();\n this.choices[i].Image = null;\n }\n }\n }\n\n base.Dispose(disposing);\n }\n\n private void DrawChoiceThumbnail(Graphics g, VariantChoice choice, Rectangle thumb)\n {\n using (GraphicsPath thumbPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n using (SolidBrush thumbBg = new SolidBrush(MicaBase))\n {\n g.FillPath(thumbBg, thumbPath);\n }\n\n if (choice.Image == null)\n {\n using (StringFormat format = new StringFormat())\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(\"纯文字模式\", this.hintFont, brush, thumb, format);\n }\n return;\n }\n\n Rectangle target = FitImage(choice.Image.Size, thumb);\n GraphicsState state = g.Save();\n using (GraphicsPath clipPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n {\n g.SetClip(clipPath);\n g.DrawImage(choice.Image, target);\n }\n g.Restore(state);\n }\n\n private void UpdatePreferredHeight()\n {\n int oldHeight = this.Height;\n int nextHeight = GetPreferredHeightForWidth(this.Width);\n if (nextHeight != oldHeight)\n {\n this.Height = nextHeight;\n if (this.PreferredHeightChanged != null)\n {\n this.PreferredHeightChanged(this, EventArgs.Empty);\n }\n }\n }\n\n private int GetColumnCount()\n {\n return GetColumnCount(this.Width);\n }\n\n private static int GetColumnCount(int width)\n {\n width = Math.Max(1, width);\n if (width >= 520)\n {\n return 3;\n }\n\n if (width >= 340)\n {\n return 2;\n }\n\n return 1;\n }\n\n private Rectangle GetCardBounds(int index)\n {\n int columns = GetColumnCount();\n int col = index % columns;\n int row = index / columns;\n int cardWidth = Math.Max(86, (Math.Max(1, this.Width) - (columns - 1) * CardGap) / columns);\n return new Rectangle(col * (cardWidth + CardGap), row * (CardHeight + CardGap), cardWidth, CardHeight);\n }\n\n private int GetChoiceIndexAt(Point point)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (GetCardBounds(i).Contains(point))\n {\n return i;\n }\n }\n\n return -1;\n }\n\n private static string GetSamplePath(string directory, string prefix, object value)\n {\n string variant = Convert.ToString(value, CultureInfo.InvariantCulture);\n return System.IO.Path.Combine(directory, prefix + \"-\" + variant.ToLowerInvariant() + \".png\");\n }\n\n private static Rectangle FitImage(Size imageSize, Rectangle bounds)\n {\n if (imageSize.Width <= 0 || imageSize.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0)\n {\n return bounds;\n }\n\n float sx = bounds.Width / (float)imageSize.Width;\n float sy = bounds.Height / (float)imageSize.Height;\n float scale = Math.Min(sx, sy);\n int width = Math.Max(1, (int)Math.Round(imageSize.Width * scale));\n int height = Math.Max(1, (int)Math.Round(imageSize.Height * scale));\n return new Rectangle(\n bounds.Left + (bounds.Width - width) / 2,\n bounds.Top + (bounds.Height - height) / 2,\n width,\n height);\n }\n\n private sealed class VariantChoice\n {\n public VariantChoice(object value, string label)\n {\n this.Value = value;\n this.Label = label ?? string.Empty;\n }\n\n public object Value;\n public string Label;\n public Image Image;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public AdvancedSectionHeader AdvancedHeader;\n public bool AdvancedExpanded;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public bool Advanced;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Name = property == null ? string.Empty : property.Name;\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public SettingEditor(string name, SettingRow card, Control control)\n {\n this.Name = name ?? string.Empty;\n this.Property = null;\n this.Card = card;\n this.Control = control;\n }\n\n public string Name { get; private set; }\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class ModelOption\n {\n public ModelOption(string key, string label, bool available, bool missingFromCatalog)\n {\n this.Key = CodexRadarModelCatalog.NormalizeModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.MissingFromCatalog = missingFromCatalog;\n }\n\n public string Key { get; private set; }\n private string Label { get; set; }\n private bool Available { get; set; }\n private bool MissingFromCatalog { get; set; }\n\n public override string ToString()\n {\n if (this.MissingFromCatalog)\n {\n return this.Label + \"(未在目录)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class ClaudeModelOption\n {\n public ClaudeModelOption(string key, string label, bool available, bool pending)\n {\n this.Key = WidgetSettings.NormalizeClaudeRadarModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.Pending = pending;\n }\n\n public string Key { get; private set; }\n public string Label { get; private set; }\n public bool Available { get; private set; }\n public bool Pending { get; private set; }\n\n public override string ToString()\n {\n if (this.Key.Length == 0)\n {\n return this.Label;\n }\n\n if (this.Pending)\n {\n return this.Label + \"(待映射)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class DisplayOption\n {\n public DisplayOption(string value, string label)\n {\n this.Value = WidgetSettings.NormalizeDisplayDeviceName(value);\n this.Label = label ?? string.Empty;\n }\n\n public string Value { get; private set; }\n private string Label { get; set; }\n\n public override string ToString()\n {\n return this.Label;\n }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n if (this.Value is OperationPrimaryPanelMode)\n {\n OperationPrimaryPanelMode mode = (OperationPrimaryPanelMode)this.Value;\n if (mode == OperationPrimaryPanelMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == OperationPrimaryPanelMode.WindowsButton)\n {\n return \"Windows 按钮\";\n }\n\n if (mode == OperationPrimaryPanelMode.MemoryPie)\n {\n return \"内存饼图\";\n }\n\n if (mode == OperationPrimaryPanelMode.Hidden)\n {\n return \"隐藏\";\n }\n }\n\n if (this.Value is CodexRadarSoftwareMode)\n {\n CodexRadarSoftwareMode mode = (CodexRadarSoftwareMode)this.Value;\n if (mode == CodexRadarSoftwareMode.Auto)\n {\n return \"自动(按前台)\";\n }\n\n if (mode == CodexRadarSoftwareMode.Codex)\n {\n return \"固定 CODEX\";\n }\n\n if (mode == CodexRadarSoftwareMode.Claude)\n {\n return \"固定 CLAUDE\";\n }\n }\n\n if (this.Value is WidgetVisibilityMode)\n {\n WidgetVisibilityMode mode = (WidgetVisibilityMode)this.Value;\n if (mode == WidgetVisibilityMode.AlwaysVisible)\n {\n return \"总是可见\";\n }\n\n if (mode == WidgetVisibilityMode.HideWhenFullscreen)\n {\n return \"全屏时不可见\";\n }\n\n if (mode == WidgetVisibilityMode.HideWhenMaximized)\n {\n return \"最大化时不可见\";\n }\n\n if (mode == WidgetVisibilityMode.HideWhenOverlapped)\n {\n return \"遮挡时不可见\";\n }\n\n if (mode == WidgetVisibilityMode.DesktopOnly)\n {\n return \"仅桌面可见\";\n }\n }\n\n if (this.Value is RadarClockTimeDisplayMode)\n {\n RadarClockTimeDisplayMode mode = (RadarClockTimeDisplayMode)this.Value;\n if (mode == RadarClockTimeDisplayMode.Utc)\n {\n return \"UTC\";\n }\n\n if (mode == RadarClockTimeDisplayMode.CurrentLocal)\n {\n return \"当前时间\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastAttemptRefresh)\n {\n return \"上次尝试刷新\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastActualRefresh)\n {\n return \"上次实际刷新\";\n }\n }\n\n if (this.Value is CodexQuotaPlanComparison)\n {\n CodexQuotaPlanComparison comparison = (CodexQuotaPlanComparison)this.Value;\n if (comparison == CodexQuotaPlanComparison.GreaterThan)\n {\n return \"大于\";\n }\n\n return \"小于\";\n }\n\n if (this.Value is CodexQuotaPlanResumeConditionMode)\n {\n CodexQuotaPlanResumeConditionMode mode = (CodexQuotaPlanResumeConditionMode)this.Value;\n if (mode == CodexQuotaPlanResumeConditionMode.WeeklyOnly)\n {\n return \"仅周额度\";\n }\n\n if (mode == CodexQuotaPlanResumeConditionMode.FiveHourOnly)\n {\n return \"仅 5 小时额度\";\n }\n\n return \"周额度与 5 小时额度\";\n }\n\n // All six per-window render-variant enums share member names for the common variants\n // (Classic plus the four OLED-safe restyle schemes); CodexRadarRenderVariant additionally\n // carries EvenGrid/EvenRow. Label by member name once instead of duplicating per enum type.\n if (this.Value is NetworkMonitorRenderVariant)\n {\n string networkVariantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (networkVariantName == \"Classic\") return \"扁平信息条\";\n if (networkVariantName == \"GroupedCards\") return \"分组卡片\";\n if (networkVariantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (networkVariantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (networkVariantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (networkVariantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n }\n\n if (this.Value is CodexRadarRenderVariant ||\n this.Value is MainWidgetRenderVariant ||\n this.Value is PowerThermalRenderVariant ||\n this.Value is ConnectionCheckRenderVariant ||\n this.Value is OperationRenderVariant)\n {\n string variantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (variantName == \"Classic\") return \"经典布局\";\n if (variantName == \"EvenGrid\") return \"均布六格\";\n if (variantName == \"EvenRow\") return \"均布单行\";\n if (variantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (variantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (variantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (variantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n if (variantName == \"RadialDial\") return \"扇形速控盘(新)\";\n }\n\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "69df34f5c16e7174343b8c5cac7c4286320f1386": {"t": 1785756672.7165017, "n": 185434, "h": "71716f4264d52a77", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int WmSettingChange = 0x001A;\n private const int WmDisplayChange = 0x007E;\n private const int EmSetCueBanner = 0x1501;\n private const int ReferenceWorkAreaWidth = 2880;\n private const int ReferenceWorkAreaHeight = 1740;\n private const int PreferredClientWidth = 1888;\n private const int PreferredClientHeight = 1312;\n private const int MinimumClientWidth = 760;\n private const int MinimumClientHeight = 560;\n private const int ScreenMargin = 80;\n private const int DefaultNavWidth = 409;\n private const int MediumNavWidth = 300;\n private const int CompactNavWidth = 220;\n private const int NavItemHeight = 60;\n private const int ClaudeRadarModelGridColumns = 5;\n private const int ClaudeRadarModelButtonWidth = 72;\n private const int ClaudeRadarModelButtonHeight = 38;\n private const int ClaudeRadarModelButtonGap = 8;\n private const string ClaudeRadarModelGridName = \"ClaudeRadarModelGrid\";\n private const string GlobalLayoutEditCommandName = \"GlobalLayoutEditCommand\";\n\n private static readonly Color MicaBase = DesignTokens.SettingsWarmTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.SettingsWarmTheme.CardRest;\n private static readonly Color CardHover = DesignTokens.SettingsWarmTheme.CardHover;\n private static readonly Color StrokeColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.SettingsWarmTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.SettingsWarmTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.SettingsWarmTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.SettingsWarmTheme.Accent;\n private static readonly Color AccentHover = DesignTokens.SettingsWarmTheme.AccentHover;\n private static readonly Color AccentPressed = DesignTokens.SettingsWarmTheme.AccentPressed;\n private static readonly Color ErrorClr = DesignTokens.SettingsWarmTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private TableLayoutPanel bodyLayout;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool dirty;\n private bool messageFilterRegistered;\n private bool unsavedPreviewConsumed;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n public bool TryConsumeUnsavedPreview(out WidgetSettings settings)\n {\n settings = null;\n if (this.saved || this.OwnerFormClosing || this.unsavedPreviewConsumed)\n {\n return false;\n }\n\n this.unsavedPreviewConsumed = true;\n settings = this.baseline.Clone();\n settings.Normalize();\n return true;\n }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n ApplicationIcon.ApplyTo(this);\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = true;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n ApplyDynamicResolutionSizing(true);\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n ApplyDynamicResolutionSizing(false);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n ApplyResponsiveShellLayout();\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n if (m.Msg == WmDisplayChange || m.Msg == WmSettingChange)\n {\n ScheduleDynamicResolutionSizing();\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n if (this.dirty && !this.saved && this.owner != null && !this.OwnerFormClosing)\n {\n DialogResult result = MessageBox.Show(\n this,\n \"设置还没有保存。要先保存再关闭吗?\",\n \"未保存的更改\",\n MessageBoxButtons.YesNoCancel,\n MessageBoxIcon.Warning,\n MessageBoxDefaultButton.Button1);\n if (result == DialogResult.Cancel)\n {\n e.Cancel = true;\n return;\n }\n\n if (result == DialogResult.Yes && !TrySaveSettings())\n {\n e.Cancel = true;\n return;\n }\n }\n\n this.previewTimer.Stop();\n WidgetSettings revertSettings;\n if (this.owner != null && TryConsumeUnsavedPreview(out revertSettings))\n {\n try\n {\n this.owner.RevertSettings(revertSettings);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.SettingsWarmTheme.ErrorText; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n this.bodyLayout = body;\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetNavigationWidthForClientWidth(this.ClientSize.Width)));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n SetDirtyState(true);\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n if (TrySaveSettings())\n {\n ShowStatus(\"保存完成\", false);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold));\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n // 分组名以 '!' 开头 = 收进该页底部的「复杂选项」折叠区。\n AddPageGrouped(\"\\uE115\", \"系统\", \"开机启动、性能和窗口基础行为。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\" },\n new string[] { \"AI 请求阻断\", \"AiRequestProtectionAutoEnabled\", \"AiRequestProtectionManualBlockEnabled\" },\n new string[] { \"!Codex 额度计划\", \"CodexQuotaPlanEnabled\", \"CodexQuotaPlanWeeklyComparison\", \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\", \"CodexQuotaPlanFiveHourThresholdPercent\", \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\", \"CodexQuotaPlanPauseGoalIds\", \"CodexQuotaPlanResumeGoalIds\" },\n new string[] { \"!恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" },\n new string[] { \"!调试\", \"ForceShowForegroundFpsEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7C2\", \"布局与位置\", \"所有浮窗的位置、大小和所在显示器;推荐用可视化编辑直接拖拽。\", new string[][]\n {\n new string[] { \"可视化编辑\", GlobalLayoutEditCommandName },\n new string[] { \"显示器分配\", \"FallbackDisconnectedDisplaysEnabled\", \"MainDisplayDeviceName\", \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\", \"PowerThermalDisplayDeviceName\", \"NetworkMonitorDisplayDeviceName\", \"ConnectionCheckDisplayDeviceName\", \"OperationDisplayDeviceName\" },\n new string[] { \"!主窗口位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"!Codex Radar 位置\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\" },\n new string[] { \"!Claude Radar 位置\", \"ClaudeRadarWidth\", \"ClaudeRadarHeight\", \"ClaudeRadarLeftX\", \"ClaudeRadarBottomY\" },\n new string[] { \"!功耗温度位置\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\" },\n new string[] { \"!网络监控位置\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\" },\n new string[] { \"!连接检测位置\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\" },\n new string[] { \"!操作面板位置\", \"OperationLeftOffset\", \"OperationBottomOffset\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与防烧屏\", \"鼠标靠近时隐藏、空闲自动隐藏和 OLED 防烧屏。\", new string[][]\n {\n new string[] { \"鼠标靠近时隐藏\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" },\n new string[] { \"!延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"!覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" }\n });\n\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口显示哪些指标、透明度和外观。\", new string[][]\n {\n new string[] { \"显示哪些指标\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" }\n });\n\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"Codex 用量监控:模型、时区和外观。\", new string[][]\n {\n // 手动布局与元素偏移分组已从设置界面隐藏(改用渲染变体切换布局)。\n // 对应 WidgetSettings 属性仍保留,Classic 布局若通过 settings.ini 设置仍生效;\n // 均布变体本就忽略这些设置。恢复入口时把下方分组加回,并同步 VerifySelfTest 必需绑定。\n new string[] { \"窗口\", \"CodexRadarEnabled\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarSoftwareMode\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"RadarClockAutoSwitchModelEnabled\", \"RadarClockTimeDisplayMode\", \"DeepSeekApiKeyRevision\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"!网站数据源\", \"CodexRadarPublicJsonEnabled\", \"CodexRadarHtmlFallbackEnabled\", \"CodexRadarRssFallbackEnabled\", \"CodexRadarServiceProbeToken\" },\n new string[] { \"!IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineAutoEnabled\", \"CodexModelIqBaselinePassed\", \"CodexModelIqBaselineValidTasks\" },\n new string[] { \"!效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"!随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uE8D4\", \"Claude Radar\", \"Claude 用量监控独立窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"ClaudeRadarEnabled\", \"ClaudeRadarTransparencyPercent\" },\n new string[] { \"模型\", \"ClaudeRadarModelKey\" },\n new string[] { \"!网站数据源\", \"ClaudeRadarJsonEnabled\", \"ClaudeRadarHomepageFallbackEnabled\", \"ClaudeRadarCommunityRatingsEnabled\", \"ClaudeRadarLocalQuotaFallbackEnabled\", \"ClaudeRadarServiceProbeToken\" },\n new string[] { \"!随机测试\", \"ClaudeRadarRandomTestEnabled\", \"ClaudeRadarRandomTestAutoRefresh\", \"ClaudeRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"透明度\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"!测试\", \"ThermalTestMode\" }\n });\n\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW 检测、云服务和出口身份。\", new string[][]\n {\n new string[] { \"网络监控\", \"NetworkMonitorAdapterId\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\" },\n new string[] { \"连接检测\", \"ConnectionCheckIntervalSeconds\", \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\" },\n new string[] { \"!手动刷新\", \"GfwProbeManualRefreshToken\", \"ConnectionCheckManualRefreshToken\" },\n new string[] { \"!云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"!测试\", \"CleanIpBadgeTestMode\", \"NetworkStatusTestMode\" }\n });\n\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板的按钮、透明度和外观。\", new string[][]\n {\n new string[] { \"按钮与面板\", \"OperationButtonSize\", \"OperationPrimaryPanelMode\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"外观风格\", \"OperationRenderVariant\" },\n new string[] { \"!测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n // A leading '!' marks the group as advanced: it renders collapsed under the\n // per-page \"复杂选项\" header until the user expands it (or searches).\n bool advanced = groupTitle.Length > 0 && groupTitle[0] == '!';\n if (advanced)\n {\n groupTitle = groupTitle.Substring(1);\n }\n\n if (advanced && page.AdvancedHeader == null)\n {\n AdvancedSectionHeader advancedHeader = new AdvancedSectionHeader(GetUiFont(10.0f, FontStyle.Bold), GetUiFont(8.5f));\n advancedHeader.Width = 1152;\n advancedHeader.Height = 64;\n advancedHeader.Margin = new Padding(0, 32, 0, 6);\n advancedHeader.Click += delegate { ToggleAdvancedSection(page); };\n page.AdvancedHeader = advancedHeader;\n stack.Controls.Add(advancedHeader);\n }\n\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n group.Advanced = advanced;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n titleLabel.Visible = !advanced;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n card.Visible = !advanced;\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.AdvancedRowCount = CountAdvancedRows(page);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = Math.Max(160, GetNavigationWidthForClientWidth(this.ClientSize.Width) - 24);\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n private static int CountAdvancedRows(CategoryPage page)\n {\n int count = 0;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n if (page.Groups[g].Advanced)\n {\n count += page.Groups[g].Editors.Count;\n }\n }\n\n return count;\n }\n\n private void ToggleAdvancedSection(CategoryPage page)\n {\n page.AdvancedExpanded = !page.AdvancedExpanded;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Expanded = page.AdvancedExpanded;\n }\n\n ApplyAdvancedVisibility(page);\n LayoutPage(page);\n }\n\n private void ApplyAdvancedVisibility(CategoryPage page)\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n if (query.Length > 0)\n {\n // Search visibility is authoritative while a query is active.\n ApplySearchFilter();\n return;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (!group.Advanced)\n {\n continue;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = page.AdvancedExpanded;\n }\n\n if (group.Card != null)\n {\n group.Card.Visible = page.AdvancedExpanded;\n if (page.AdvancedExpanded)\n {\n group.Card.LayoutRows();\n }\n }\n }\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n if (string.Equals(propertyName, GlobalLayoutEditCommandName, StringComparison.Ordinal))\n {\n return BuildGlobalLayoutEditEditor();\n }\n\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private SettingEditor BuildGlobalLayoutEditEditor()\n {\n Button button = BuildCommandButton(\"全局编辑\", false);\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenGlobalLayoutEditorFromSettings(); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(GlobalLayoutEditCommandName);\n card.HintLabel.Text = GetSettingHint(GlobalLayoutEditCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(GlobalLayoutEditCommandName, card, button);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n if (property.Name.EndsWith(\"RenderVariant\", StringComparison.Ordinal) &&\n !property.Name.StartsWith(\"ClaudeRadar\", StringComparison.Ordinal))\n {\n return BuildVariantPicker(property);\n }\n\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildCodexRadarModelCombo();\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildClaudeRadarModelGridSelector();\n }\n\n if (type == typeof(string) && IsDisplayDeviceSetting(property.Name))\n {\n return BuildDisplayDeviceCombo();\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n if (string.Equals(property.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(GetDeepSeekApiKeyButtonText(), false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate { OpenDeepSeekApiKeyDialog(button); };\n return button;\n }\n\n if (string.Equals(property.Name, \"CodexRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Codex Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (string.Equals(property.Name, \"ClaudeRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Claude Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (type == typeof(int) && property.Name.EndsWith(\"RefreshToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即刷新\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"刷新请求已发送。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n private Control BuildVariantPicker(PropertyInfo property)\n {\n VariantPicker picker = new VariantPicker(property.Name, property.PropertyType, GetUiFont(8.8f, FontStyle.Bold), GetUiFont(8.0f));\n picker.Width = 560;\n picker.ValueChanged += delegate { OnSettingChanged(); };\n picker.PreferredHeightChanged += delegate { RelayoutSettingGroup(picker); };\n return picker;\n }\n\n private Control BuildDisplayDeviceCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n combo.Items.Add(new DisplayOption(string.Empty, \"主显示器/自动\"));\n Screen[] screens = Screen.AllScreens;\n for (int i = 0; i < screens.Length; i++)\n {\n Screen screen = screens[i];\n Rectangle bounds = screen.Bounds;\n string label = string.Format(\n CultureInfo.InvariantCulture,\n \"{0} {1}x{2} @ {3},{4}{5}\",\n screen.DeviceName,\n bounds.Width,\n bounds.Height,\n bounds.Left,\n bounds.Top,\n screen.Primary ? \" 主屏\" : string.Empty);\n combo.Items.Add(new DisplayOption(screen.DeviceName, label));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildCodexRadarModelCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n\n List<CodexRadarModelInfo> models = CodexRadarModelCatalog.LoadModels();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n CodexRadarModelInfo model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = CodexRadarModelCatalog.NormalizeModelKey(model.Key);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n combo.Items.Add(new ModelOption(\n key,\n CodexRadarModelCatalog.GetDisplayLabel(model.Label, key),\n model.Available,\n false));\n keys.Add(key);\n }\n\n string defaultKey = CodexRadarModelCatalog.DefaultModelKey;\n if (!keys.Contains(defaultKey))\n {\n combo.Items.Insert(0, new ModelOption(\n defaultKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, defaultKey),\n true,\n false));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildClaudeRadarModelGridSelector()\n {\n Panel panel = new Panel();\n panel.Width = 400;\n panel.BackColor = Color.Transparent;\n panel.Resize += delegate { LayoutClaudeRadarModelPanel(panel); };\n\n FlowLayoutPanel grid = new FlowLayoutPanel();\n grid.Name = ClaudeRadarModelGridName;\n grid.Width = panel.Width;\n grid.Location = new Point(0, 0);\n grid.Margin = new Padding(0);\n grid.Padding = new Padding(0);\n grid.FlowDirection = FlowDirection.LeftToRight;\n grid.WrapContents = true;\n grid.AutoSize = false;\n grid.BackColor = Color.Transparent;\n panel.Controls.Add(grid);\n\n Button edit = BuildCommandButton(\"编辑映射\", false);\n edit.Width = panel.Width;\n edit.Height = 54;\n edit.Click += delegate\n {\n using (ClaudeRadarModelMapEditorForm dialog = new ClaudeRadarModelMapEditorForm())\n {\n if (dialog.ShowDialog(this) == DialogResult.OK)\n {\n string selected = GetClaudeRadarModelGridValue(grid);\n PopulateClaudeRadarModelGrid(grid, selected);\n LayoutClaudeRadarModelPanel(panel);\n RelayoutSettingGroup(panel);\n ShowStatus(\"Claude Radar 模型映射已保存。\", false);\n OnSettingChanged();\n }\n }\n };\n panel.Controls.Add(edit);\n PopulateClaudeRadarModelGrid(grid, string.Empty);\n LayoutClaudeRadarModelPanel(panel);\n return panel;\n }\n\n private void PopulateClaudeRadarModelGrid(FlowLayoutPanel grid, string selectedKey)\n {\n if (grid == null)\n {\n return;\n }\n\n string normalizedSelected = WidgetSettings.NormalizeClaudeRadarModelKey(selectedKey);\n List<ClaudeModelOption> options = BuildClaudeRadarModelOptions(normalizedSelected);\n bool selectedExists = false;\n for (int i = 0; i < options.Count; i++)\n {\n if (string.Equals(options[i].Key, normalizedSelected, StringComparison.OrdinalIgnoreCase))\n {\n selectedExists = true;\n break;\n }\n }\n\n if (!selectedExists)\n {\n normalizedSelected = string.Empty;\n }\n\n grid.Tag = normalizedSelected;\n grid.SuspendLayout();\n try\n {\n while (grid.Controls.Count > 0)\n {\n Control old = grid.Controls[0];\n grid.Controls.RemoveAt(0);\n old.Dispose();\n }\n\n int slots = Math.Max(\n ClaudeRadarModelGridColumns,\n ((options.Count + ClaudeRadarModelGridColumns - 1) / ClaudeRadarModelGridColumns) * ClaudeRadarModelGridColumns);\n for (int i = 0; i < slots; i++)\n {\n ClaudeModelOption option = i < options.Count ? options[i] : null;\n Button button = BuildClaudeRadarModelButton(option);\n button.Margin = new Padding(0, 0, i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap, ClaudeRadarModelButtonGap);\n grid.Controls.Add(button);\n }\n\n ApplyClaudeRadarModelGridSelection(grid);\n int rows = Math.Max(1, slots / ClaudeRadarModelGridColumns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n }\n finally\n {\n grid.ResumeLayout();\n }\n }\n\n private List<ClaudeModelOption> BuildClaudeRadarModelOptions(string selectedKey)\n {\n List<ClaudeModelOption> options = new List<ClaudeModelOption>();\n options.Add(new ClaudeModelOption(string.Empty, \"自动\", true, false));\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n keys.Add(string.Empty);\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n string status = (model.Status ?? string.Empty).Trim();\n bool deleted = string.Equals(status, \"deleted\", StringComparison.OrdinalIgnoreCase);\n if (deleted && !string.Equals(key, selectedKey, StringComparison.OrdinalIgnoreCase))\n {\n continue;\n }\n\n bool pending = string.Equals(status, \"pending\", StringComparison.OrdinalIgnoreCase);\n bool temporarilyMissing = string.Equals(status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase);\n bool available = model.Enabled &&\n !deleted &&\n !pending &&\n !temporarilyMissing &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n options.Add(new ClaudeModelOption(\n key,\n label,\n available,\n pending || temporarilyMissing));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (!string.IsNullOrEmpty(selectedKey) && !keys.Contains(selectedKey))\n {\n options.Add(new ClaudeModelOption(selectedKey, selectedKey, false, true));\n }\n\n return options;\n }\n\n private Button BuildClaudeRadarModelButton(ClaudeModelOption option)\n {\n Button button = new Button();\n button.Width = ClaudeRadarModelButtonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Tag = option;\n button.Text = option == null ? \"--\" : GetClaudeRadarModelButtonText(option);\n button.AutoEllipsis = true;\n button.TextAlign = ContentAlignment.MiddleCenter;\n button.FlatStyle = FlatStyle.Flat;\n button.UseVisualStyleBackColor = false;\n button.FlatAppearance.BorderSize = 1;\n button.Font = GetUiFont(8.8f, FontStyle.Bold);\n button.Cursor = option != null && option.Available ? Cursors.Hand : Cursors.Default;\n button.Enabled = option != null && option.Available;\n button.Click += delegate\n {\n ClaudeModelOption clicked = button.Tag as ClaudeModelOption;\n if (clicked == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(button.Parent);\n if (grid == null)\n {\n return;\n }\n\n string current = GetClaudeRadarModelGridValue(grid);\n if (string.Equals(current, clicked.Key, StringComparison.OrdinalIgnoreCase))\n {\n return;\n }\n\n grid.Tag = clicked.Key;\n ApplyClaudeRadarModelGridSelection(grid);\n OnSettingChanged();\n };\n return button;\n }\n\n private static string GetClaudeRadarModelButtonText(ClaudeModelOption option)\n {\n if (option == null)\n {\n return \"--\";\n }\n\n if (option.Key.Length == 0)\n {\n return \"自动\";\n }\n\n string label = option.Label ?? option.Key;\n if (label.Length <= 7)\n {\n return label;\n }\n\n return label.Substring(0, 7);\n }\n\n private void ApplyClaudeRadarModelGridSelection(FlowLayoutPanel grid)\n {\n if (grid == null)\n {\n return;\n }\n\n string selectedKey = WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n bool selected = option != null &&\n string.Equals(option.Key, selectedKey, StringComparison.OrdinalIgnoreCase);\n ApplyClaudeRadarModelButtonStyle(button, option, selected);\n }\n }\n\n private static void ApplyClaudeRadarModelButtonStyle(Button button, ClaudeModelOption option, bool selected)\n {\n if (button == null)\n {\n return;\n }\n\n if (option == null)\n {\n button.BackColor = ControlBg;\n button.ForeColor = TextTertiary;\n button.FlatAppearance.BorderColor = ControlBorder;\n return;\n }\n\n if (selected)\n {\n button.BackColor = DesignTokens.SettingsWarmTheme.Accent;\n button.ForeColor = Color.Black;\n button.FlatAppearance.BorderColor = DesignTokens.SettingsWarmTheme.AccentHover;\n return;\n }\n\n button.BackColor = option.Available ? DesignTokens.SettingsWarmTheme.ButtonRest : MicaLayer;\n button.ForeColor = option.Available ? TextSecondary : TextTertiary;\n button.FlatAppearance.BorderColor = option.Pending ? DesignTokens.Colors.Warning : ControlBorder;\n }\n\n private static string GetClaudeRadarModelGridValue(FlowLayoutPanel grid)\n {\n return grid == null ? string.Empty : WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n }\n\n private void SetClaudeRadarModelGridValue(FlowLayoutPanel grid, string value)\n {\n PopulateClaudeRadarModelGrid(grid, value);\n if (grid != null && grid.Parent != null)\n {\n LayoutClaudeRadarModelPanel(grid.Parent);\n }\n }\n\n private static FlowLayoutPanel FindClaudeRadarModelGrid(Control control)\n {\n FlowLayoutPanel grid = control as FlowLayoutPanel;\n if (grid != null && string.Equals(grid.Name, ClaudeRadarModelGridName, StringComparison.Ordinal))\n {\n return grid;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n grid = FindClaudeRadarModelGrid(control.Controls[i]);\n if (grid != null)\n {\n return grid;\n }\n }\n }\n\n return null;\n }\n\n private static void LayoutClaudeRadarModelPanel(Control panel)\n {\n if (panel == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(panel);\n Button edit = null;\n for (int i = 0; i < panel.Controls.Count; i++)\n {\n Button candidate = panel.Controls[i] as Button;\n if (candidate != null && string.Equals(candidate.Text, \"编辑映射\", StringComparison.Ordinal))\n {\n edit = candidate;\n break;\n }\n }\n\n int y = 0;\n if (grid != null)\n {\n grid.Location = new Point(0, 0);\n grid.Width = panel.Width;\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n button.Width = buttonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Margin = new Padding(\n 0,\n 0,\n i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap,\n ClaudeRadarModelButtonGap);\n }\n\n y = grid.Bottom + 10;\n }\n\n if (edit != null)\n {\n edit.Location = new Point(0, y);\n edit.Width = panel.Width;\n y = edit.Bottom;\n }\n\n panel.Height = Math.Max(54, y);\n }\n\n private static int GetClaudeRadarModelButtonWidthForGrid(int gridWidth)\n {\n int gaps = (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n int usable = Math.Max(ClaudeRadarModelGridColumns, gridWidth - gaps);\n return Math.Max(32, usable / ClaudeRadarModelGridColumns);\n }\n\n private static void RelayoutSettingGroup(Control control)\n {\n Control parent = control;\n while (parent != null)\n {\n SettingGroupCard card = parent as SettingGroupCard;\n if (card != null)\n {\n card.LayoutRows();\n return;\n }\n\n parent = parent.Parent;\n }\n }\n\n private static ComboBox FindComboBox(Control control)\n {\n ComboBox combo = control as ComboBox;\n if (combo != null)\n {\n return combo;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n combo = FindComboBox(control.Controls[i]);\n if (combo != null)\n {\n return combo;\n }\n }\n }\n\n return null;\n }\n\n private void PopulateClaudeRadarModelCombo(ComboBox combo)\n {\n if (combo == null)\n {\n return;\n }\n\n combo.Items.Clear();\n combo.Items.Add(new ClaudeModelOption(string.Empty, \"自动选择\", true, false));\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n bool available = model.Enabled &&\n !string.Equals(model.Status, \"deleted\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase) &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n combo.Items.Add(new ClaudeModelOption(\n key,\n label,\n available,\n string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase)));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (combo.Items.Count > 0 && combo.SelectedIndex < 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static object GetClaudeRadarModelComboValue(ComboBox combo)\n {\n ClaudeModelOption option = combo == null ? null : combo.SelectedItem as ClaudeModelOption;\n return option == null ? string.Empty : option.Key;\n }\n\n private static void SetClaudeRadarModelComboValue(ComboBox combo, string value)\n {\n if (combo == null)\n {\n return;\n }\n\n string modelKey = WidgetSettings.NormalizeClaudeRadarModelKey(value);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ClaudeModelOption option = combo.Items[i] as ClaudeModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(modelKey))\n {\n combo.Items.Add(new ClaudeModelOption(modelKey, modelKey, false, true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static bool IsDisplayDeviceSetting(string propertyName)\n {\n return propertyName != null &&\n propertyName.EndsWith(\"DisplayDeviceName\", StringComparison.Ordinal);\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n EnsureVariantSamplesForPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void EnsureVariantSamplesForPage(CategoryPage page)\n {\n if (this.owner == null || page == null)\n {\n return;\n }\n\n string directory = GetVariantSampleDirectory();\n for (int i = 0; i < page.Editors.Count; i++)\n {\n SettingEditor editor = page.Editors[i];\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker == null)\n {\n continue;\n }\n\n string prefix = GetVariantSamplePrefix(editor.Name);\n if (prefix.Length == 0)\n {\n continue;\n }\n\n try\n {\n if (picker.HasMissingSamples(directory, prefix))\n {\n System.IO.Directory.CreateDirectory(directory);\n RenderVariantSamplesForProperty(editor.Name, directory);\n }\n\n picker.LoadSamples(directory, prefix);\n RelayoutSettingGroup(picker);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n picker.MarkSamplesUnavailable();\n }\n }\n }\n\n private static string GetVariantSampleDirectory()\n {\n return System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName,\n \"variant-samples\",\n \"v\" + ProductIdentity.Version);\n }\n\n private static string GetVariantSamplePrefix(string propertyName)\n {\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal)) return \"operation\";\n return string.Empty;\n }\n\n private static void RenderVariantSamplesForProperty(string propertyName, string directory)\n {\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal))\n {\n OperationForm.RenderVariantSamples(directory);\n }\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Width = width;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n\n SetDirtyState(false);\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private bool TrySaveSettings()\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n SetDirtyState(false);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n return false;\n }\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n SetClaudeRadarModelGridValue(grid, value as string);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n picker.SetSelectedValue(value);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n string displayDeviceName = value as string;\n if (string.Equals(editor.Property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n string modelKey = CodexRadarModelCatalog.NormalizeModelKey(value as string);\n if (modelKey.Length == 0)\n {\n modelKey = CodexRadarModelCatalog.DefaultModelKey;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ModelOption option = combo.Items[i] as ModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n combo.Items.Add(new ModelOption(\n modelKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, modelKey),\n false,\n true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n SetClaudeRadarModelComboValue(combo, value as string);\n return;\n }\n\n if (IsDisplayDeviceSetting(editor.Property.Name))\n {\n displayDeviceName = WidgetSettings.NormalizeDisplayDeviceName(displayDeviceName);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n DisplayOption option = combo.Items[i] as DisplayOption;\n if (option != null && string.Equals(option.Value, displayDeviceName, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(displayDeviceName))\n {\n combo.Items.Add(new DisplayOption(displayDeviceName, \"未连接 \" + displayDeviceName));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n button.Tag = value is int ? (int)value : 0;\n if (string.Equals(editor.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n button.Text = GetDeepSeekApiKeyButtonText();\n }\n }\n }\n\n private void ApplyResponsiveShellLayout()\n {\n int navWidth = GetNavigationWidthForClientWidth(this.ClientSize.Width);\n if (this.bodyLayout != null && this.bodyLayout.ColumnStyles.Count > 0)\n {\n this.bodyLayout.ColumnStyles[0].Width = navWidth;\n }\n\n if (this.navigationPanel != null)\n {\n int itemWidth = Math.Max(160, navWidth - 24);\n for (int i = 0; i < this.navigationPanel.Controls.Count; i++)\n {\n this.navigationPanel.Controls[i].Width = itemWidth;\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n if (editor.Property == null)\n {\n return null;\n }\n\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n return GetClaudeRadarModelGridValue(grid);\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n return picker.GetSelectedValue();\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n ClaudeModelOption claudeModelOption = combo.SelectedItem as ClaudeModelOption;\n if (claudeModelOption != null)\n {\n return claudeModelOption.Key;\n }\n\n ModelOption modelOption = combo.SelectedItem as ModelOption;\n if (modelOption != null)\n {\n return modelOption.Key;\n }\n\n DisplayOption displayOption = combo.SelectedItem as DisplayOption;\n if (displayOption != null)\n {\n return displayOption.Value;\n }\n\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n return button.Tag is int ? (int)button.Tag : 0;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n SetDirtyState(true);\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n private void OpenGlobalLayoutEditorFromSettings()\n {\n if (this.owner == null)\n {\n return;\n }\n\n this.previewTimer.Stop();\n WidgetSettings settings = ReadSettings();\n WidgetSettings editedSettings;\n if (this.owner.TryEditGlobalLayout(settings, out editedSettings))\n {\n this.baseline = editedSettings.Clone();\n this.baseline.Normalize();\n LoadSettings(this.baseline);\n this.saved = true;\n ShowStatus(\"全局布局已保存\", false);\n return;\n }\n\n this.owner.PreviewSettings(ReadSettings());\n ShowStatus(\"全局编辑已取消\", false);\n }\n\n private void OpenDeepSeekApiKeyDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"DeepSeek 配置\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.ClientSize = new Size(560, 238);\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n Label title = new Label();\n title.Text = \"DeepSeek API Key\";\n title.Font = GetUiFont(12.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(24, 20);\n title.Size = new Size(500, 30);\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label hint = new Label();\n hint.Text = HasDeepSeekEnvironmentApiKeyForUi()\n ? \"检测到 DEEPSEEK_API_KEY 环境变量;环境变量会优先于本地文件。\"\n : \"密钥保存到本地应用数据目录,不写入 settings.ini 或日志。\";\n hint.Font = GetUiFont(8.8f);\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(24, 56);\n hint.Size = new Size(500, 42);\n hint.TextAlign = ContentAlignment.MiddleLeft;\n\n TextBox keyBox = new TextBox();\n keyBox.Location = new Point(24, 104);\n keyBox.Size = new Size(512, 34);\n keyBox.BackColor = ControlBg;\n keyBox.ForeColor = TextSecondary;\n keyBox.BorderStyle = BorderStyle.FixedSingle;\n keyBox.Font = GetUiFont(9.5f);\n keyBox.UseSystemPasswordChar = true;\n keyBox.Text = ReadDeepSeekApiKeyFileForUi();\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(24, 166);\n clear.Width = 112;\n clear.Height = 44;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Location = new Point(302, 166);\n cancel.Width = 112;\n cancel.Height = 44;\n cancel.Click += delegate { dialog.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Location = new Point(424, 166);\n save.Width = 112;\n save.Height = 44;\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(string.Empty, out errorCode))\n {\n keyBox.Text = string.Empty;\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已清除\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置清除失败 \" + errorCode, true);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(keyBox.Text, out errorCode))\n {\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已保存\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置保存失败 \" + errorCode, true);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(keyBox);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void IncrementDeepSeekApiKeyRevision(Button sourceButton)\n {\n if (sourceButton != null)\n {\n int token = sourceButton.Tag is int ? (int)sourceButton.Tag : 0;\n sourceButton.Tag = token == int.MaxValue ? 1 : token + 1;\n sourceButton.Text = GetDeepSeekApiKeyButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static string GetDeepSeekApiKeyButtonText()\n {\n return IsDeepSeekApiKeyConfiguredForUi() ? \"修改配置\" : \"配置\";\n }\n\n private static bool IsDeepSeekApiKeyConfiguredForUi()\n {\n if (HasDeepSeekEnvironmentApiKeyForUi())\n {\n return true;\n }\n\n return ReadDeepSeekApiKeyFileForUi().Length > 0;\n }\n\n private static bool HasDeepSeekEnvironmentApiKeyForUi()\n {\n try\n {\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable)))\n {\n return true;\n }\n\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.User)))\n {\n return true;\n }\n\n return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine));\n }\n catch\n {\n return false;\n }\n }\n\n private static string ReadDeepSeekApiKeyFileForUi()\n {\n try\n {\n string secret;\n bool migrated;\n string errorCode;\n return SecretStore.TryReadOrMigrateSecret(\n CodexRadarForm.DeepSeekApiKeyPath,\n CodexRadarForm.LegacyDeepSeekApiKeyPath,\n SecretStore.TrimSecret,\n out secret,\n out migrated,\n out errorCode)\n ? secret\n : string.Empty;\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static bool TrySaveDeepSeekApiKeyFile(string apiKey, out string errorCode)\n {\n errorCode = string.Empty;\n try\n {\n string trimmed = (apiKey ?? string.Empty).Trim();\n if (trimmed.Length == 0)\n {\n SecretStore.DeleteSecretFiles(CodexRadarForm.DeepSeekApiKeyPath, CodexRadarForm.LegacyDeepSeekApiKeyPath);\n return true;\n }\n\n SecretStore.WriteSecret(CodexRadarForm.DeepSeekApiKeyPath, trimmed);\n SecretStore.DeleteLegacySecretFiles(CodexRadarForm.LegacyDeepSeekApiKeyPath);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n return false;\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void SetDirtyState(bool hasChanges)\n {\n this.dirty = hasChanges;\n if (this.statusLabel == null)\n {\n return;\n }\n\n if (hasChanges)\n {\n this.statusTimer.Stop();\n this.statusLabel.Text = \"有未保存的更改\";\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.Visible = true;\n }\n else if (!this.statusTimer.Enabled)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n if (this.dirty)\n {\n SetDirtyState(true);\n }\n else\n {\n this.statusLabel.Visible = false;\n }\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n bool searching = query.Length > 0;\n int firstMatchingPageIndex = -1;\n bool selectedPageHasVisibleRows = false;\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n if (page.AdvancedHeader != null)\n {\n // While searching, advanced rows surface directly, so the collapse header hides.\n page.AdvancedHeader.Visible = !searching;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool allowed = !group.Advanced || page.AdvancedExpanded || searching;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = allowed && (pageMatch || groupTitleMatch ||\n editor.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n\n if (searching && anyVisible)\n {\n if (firstMatchingPageIndex < 0)\n {\n firstMatchingPageIndex = i;\n }\n\n if (i == this.selectedPageIndex)\n {\n selectedPageHasVisibleRows = true;\n }\n }\n }\n\n if (searching &&\n !selectedPageHasVisibleRows &&\n firstMatchingPageIndex >= 0 &&\n firstMatchingPageIndex != this.selectedPageIndex)\n {\n SelectPage(firstMatchingPageIndex);\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : string.Empty;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return 1;\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return new NumericRange(\n WidgetSettings.MinCodexRadarManualElementOffsetPixels,\n WidgetSettings.MaxCodexRadarManualElementOffsetPixels);\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static bool IsCodexRadarManualElementOffsetSetting(string name)\n {\n return name != null &&\n name.StartsWith(\"CodexRadar\", StringComparison.OrdinalIgnoreCase) &&\n (name.EndsWith(\"OffsetX\", StringComparison.OrdinalIgnoreCase) ||\n name.EndsWith(\"OffsetY\", StringComparison.OrdinalIgnoreCase));\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private void ScheduleDynamicResolutionSizing()\n {\n if (!this.IsHandleCreated)\n {\n ApplyDynamicResolutionSizing(false);\n return;\n }\n\n try\n {\n this.BeginInvoke((MethodInvoker)delegate\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n });\n }\n catch\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n }\n }\n\n private void ApplyDynamicResolutionSizing(bool usePreferredSize)\n {\n Rectangle workArea = GetTargetWorkArea();\n Size minimum = GetMinimumWindowSizeForWorkArea(workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n\n this.MinimumSize = minimum;\n this.MaximumSize = new Size(\n Math.Max(minimum.Width, Math.Max(1, workArea.Width)),\n Math.Max(minimum.Height, Math.Max(1, workArea.Height)));\n\n Size target = usePreferredSize || this.ClientSize.Width <= 0 || this.ClientSize.Height <= 0\n ? preferred\n : this.ClientSize;\n target = new Size(\n Clamp(target.Width, minimum.Width, preferred.Width),\n Clamp(target.Height, minimum.Height, preferred.Height));\n\n if (this.ClientSize != target)\n {\n this.ClientSize = target;\n }\n\n if (this.Visible || this.IsHandleCreated)\n {\n ClampWindowToWorkArea(workArea);\n }\n\n ApplyResponsiveShellLayout();\n }\n\n private Rectangle GetTargetWorkArea()\n {\n Rectangle workArea;\n if (this.owner != null && !this.owner.IsDisposed)\n {\n workArea = Screen.FromControl(this.owner).WorkingArea;\n }\n else if (this.IsHandleCreated)\n {\n workArea = Screen.FromHandle(this.Handle).WorkingArea;\n }\n else\n {\n workArea = Screen.PrimaryScreen.WorkingArea;\n }\n\n EnsureUsableWorkArea(ref workArea);\n return workArea;\n }\n\n private void ClampWindowToWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int left = Clamp(this.Left, workArea.Left, Math.Max(workArea.Left, workArea.Right - this.Width));\n int top = Clamp(this.Top, workArea.Top, Math.Max(workArea.Top, workArea.Bottom - this.Height));\n if (left != this.Left || top != this.Top)\n {\n this.Location = new Point(left, top);\n }\n }\n\n private static Size GetPreferredClientSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Clamp(\n (int)Math.Round(workArea.Width * (PreferredClientWidth / (double)ReferenceWorkAreaWidth), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientWidth, Math.Max(1, workArea.Width)),\n PreferredClientWidth);\n int height = Clamp(\n (int)Math.Round(workArea.Height * (PreferredClientHeight / (double)ReferenceWorkAreaHeight), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientHeight, Math.Max(1, workArea.Height)),\n PreferredClientHeight);\n return FitClientSizeToWorkArea(new Size(width, height), workArea);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n return FitClientSizeToWorkArea(desiredSize, Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size FitClientSizeToWorkArea(Size desiredSize, Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Math.Min(desiredSize.Width, Math.Max(MinimumClientWidth, workArea.Width - ScreenMargin));\n int height = Math.Min(desiredSize.Height, Math.Max(MinimumClientHeight, workArea.Height - ScreenMargin));\n width = Math.Min(width, Math.Max(1, workArea.Width));\n height = Math.Min(height, Math.Max(1, workArea.Height));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n return GetMinimumWindowSizeForWorkArea(Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size GetMinimumWindowSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n return new Size(\n Math.Min(preferred.Width, Math.Max(Math.Min(MinimumClientWidth, workArea.Width), workArea.Width - 128)),\n Math.Min(preferred.Height, Math.Max(Math.Min(MinimumClientHeight, workArea.Height), workArea.Height - 128)));\n }\n\n private static int GetNavigationWidthForClientWidth(int clientWidth)\n {\n if (clientWidth <= 960)\n {\n return CompactNavWidth;\n }\n\n if (clientWidth <= 1280)\n {\n return MediumNavWidth;\n }\n\n return DefaultNavWidth;\n }\n\n private static void EnsureUsableWorkArea(ref Rectangle workArea)\n {\n if (workArea.Width > 0 && workArea.Height > 0)\n {\n return;\n }\n\n workArea = Screen.PrimaryScreen.Bounds;\n if (workArea.Width <= 0 || workArea.Height <= 0)\n {\n workArea = new Rectangle(0, 0, ReferenceWorkAreaWidth, ReferenceWorkAreaHeight);\n }\n }\n\n private static int Clamp(int value, int minimum, int maximum)\n {\n if (maximum < minimum)\n {\n maximum = minimum;\n }\n\n if (value < minimum)\n {\n return minimum;\n }\n\n if (value > maximum)\n {\n return maximum;\n }\n\n return value;\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static string RunOpenCloseStressSelfTest(int iterations)\n {\n int loopCount = Math.Max(1, Math.Min(500, iterations));\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n baseline.CodexRadarEnabled = true;\n baseline.ClaudeRadarEnabled = true;\n baseline.Normalize();\n\n using (Win11SettingsForm warmup = new Win11SettingsForm(null, baseline))\n {\n warmup.StartPosition = FormStartPosition.Manual;\n warmup.Location = new Point(-30000, -30000);\n warmup.Show();\n Application.DoEvents();\n warmup.Close();\n Application.DoEvents();\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters before = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n for (int i = 0; i < loopCount; i++)\n {\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-30000, -30000);\n form.Show();\n Application.DoEvents();\n form.Close();\n Application.DoEvents();\n }\n\n if ((i + 1) % 25 == 0)\n {\n ForceResourceCleanup();\n }\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters after = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n int handleDelta = after.HandleCount - before.HandleCount;\n int gdiDelta = after.GdiObjects - before.GdiObjects;\n int userDelta = after.UserObjects - before.UserObjects;\n if (handleDelta > 80 || gdiDelta > 8 || userDelta > 16)\n {\n throw new InvalidOperationException(\n \"WinUI settings open/close resource growth exceeded threshold. Iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \", HandlesDelta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \", GdiDelta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \", UserDelta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture));\n }\n\n return \"Settings open/close policy: PASS iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \" handles_delta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \" gdi_delta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \" user_delta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture);\n }\n\n internal static void RunSettingsBindingSelfTest()\n {\n VerifySettingsWindowActivationPolicy();\n VerifyUnsavedPreviewConsumePolicy();\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.ShowInTaskbar = false;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n private static void VerifySettingsWindowActivationPolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n if (!form.ShowInTaskbar)\n {\n throw new InvalidOperationException(\"WinUI settings window must be visible in taskbar and Alt+Tab.\");\n }\n }\n }\n\n private static void VerifyUnsavedPreviewConsumePolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n WidgetSettings revertSettings;\n if (!form.TryConsumeUnsavedPreview(out revertSettings) ||\n revertSettings == null ||\n !object.Equals(revertSettings.OperationPrimaryPanelMode, baseline.OperationPrimaryPanelMode))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consume failed.\");\n }\n\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consumed twice.\");\n }\n }\n\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n WidgetSettings revertSettings;\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings owner-closing preview should not be consumed.\");\n }\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private static void ForceResourceCleanup()\n {\n GC.Collect();\n GC.WaitForPendingFinalizers();\n GC.Collect();\n Application.DoEvents();\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"FallbackDisconnectedDisplaysEnabled\",\n \"MainDisplayDeviceName\",\n \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\",\n GlobalLayoutEditCommandName,\n \"Width\",\n \"CodexRadarEnabled\",\n \"CodexRadarSoftwareMode\",\n \"CodexRadarModelKey\",\n \"RadarClockAutoSwitchModelEnabled\",\n \"RadarClockTimeDisplayMode\",\n \"CodexRadarHtmlFallbackEnabled\",\n \"CodexRadarServiceProbeToken\",\n \"ClaudeRadarEnabled\",\n \"ClaudeRadarModelKey\",\n \"ClaudeRadarJsonEnabled\",\n \"ClaudeRadarServiceProbeToken\",\n \"DeepSeekApiKeyRevision\",\n \"AiRequestProtectionAutoEnabled\",\n \"AiRequestProtectionManualBlockEnabled\",\n \"CodexQuotaPlanEnabled\",\n \"CodexQuotaPlanWeeklyComparison\",\n \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\",\n \"CodexQuotaPlanFiveHourThresholdPercent\",\n \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\",\n \"CodexQuotaPlanPauseGoalIds\",\n \"CodexQuotaPlanResumeGoalIds\",\n \"OperationRenderVariant\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\",\n \"OperationPrimaryPanelMode\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyClaudeRadarModelGridPolicy();\n VerifyVariantPickerPolicy();\n VerifyDynamicResolutionSizingPolicy();\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyClaudeRadarModelGridPolicy()\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(\"ClaudeRadarModelKey\", out editor))\n {\n throw new InvalidOperationException(\"Claude Radar model grid editor missing.\");\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid == null)\n {\n throw new InvalidOperationException(\"Claude Radar model selector must use the five-column button grid.\");\n }\n\n if (grid.Controls.Count < ClaudeRadarModelGridColumns ||\n grid.Controls.Count % ClaudeRadarModelGridColumns != 0)\n {\n throw new InvalidOperationException(\"Claude Radar model grid slot count is not a multiple of five.\");\n }\n\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n int rowWidth = ClaudeRadarModelGridColumns * buttonWidth +\n (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n if (rowWidth > grid.Width)\n {\n throw new InvalidOperationException(\"Claude Radar model grid buttons exceed selector width.\");\n }\n\n bool sawAuto = false;\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n throw new InvalidOperationException(\"Claude Radar model grid contains a non-button slot.\");\n }\n\n if (button.Width != buttonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid button width is not responsive.\");\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n if (option == null)\n {\n if (button.Enabled || !string.Equals(button.Text, \"--\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar empty model slot must be disabled --.\");\n }\n continue;\n }\n\n if (option.Key.Length == 0)\n {\n sawAuto = true;\n if (!button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar automatic model slot must be selectable.\");\n }\n }\n else if (!option.Available && button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar unavailable model slot must be disabled.\");\n }\n }\n\n if (!sawAuto)\n {\n throw new InvalidOperationException(\"Claude Radar model grid missing automatic slot.\");\n }\n }\n\n private void VerifyVariantPickerPolicy()\n {\n string[] pickerNames = new string[]\n {\n \"OperationRenderVariant\"\n };\n\n for (int i = 0; i < pickerNames.Length; i++)\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(pickerNames[i], out editor))\n {\n throw new InvalidOperationException(\"Render variant picker binding missing: \" + pickerNames[i]);\n }\n\n if (!(editor.Control is VariantPicker))\n {\n throw new InvalidOperationException(\"Render variant must use VariantPicker: \" + pickerNames[i]);\n }\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n Size originalMinimum = this.MinimumSize;\n int originalPageIndex = this.selectedPageIndex;\n try\n {\n Size[] testSizes = new Size[]\n {\n this.MinimumSize,\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1280, 680)),\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1024, 728))\n };\n\n for (int sizeIndex = 0; sizeIndex < testSizes.Length; sizeIndex++)\n {\n this.MinimumSize = testSizes[sizeIndex];\n this.ClientSize = testSizes[sizeIndex];\n Application.DoEvents();\n VerifyNoVisibleControlClippingAtCurrentSize();\n }\n }\n finally\n {\n this.MinimumSize = originalMinimum;\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n private void VerifyNoVisibleControlClippingAtCurrentSize()\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Name);\n }\n }\n }\n }\n\n private static void VerifyDynamicResolutionSizingPolicy()\n {\n Rectangle[] workAreas = new Rectangle[]\n {\n new Rectangle(0, 60, 2880, 1740),\n new Rectangle(0, 0, 1920, 1040),\n new Rectangle(0, 0, 1600, 860),\n new Rectangle(0, 0, 1366, 728),\n new Rectangle(0, 0, 1280, 680),\n new Rectangle(0, 0, 1024, 728)\n };\n\n for (int i = 0; i < workAreas.Length; i++)\n {\n Size preferred = GetPreferredClientSizeForWorkArea(workAreas[i]);\n Size minimum = GetMinimumWindowSizeForWorkArea(workAreas[i]);\n if (preferred.Width > workAreas[i].Width ||\n preferred.Height > workAreas[i].Height ||\n minimum.Width > workAreas[i].Width ||\n minimum.Height > workAreas[i].Height ||\n minimum.Width > preferred.Width ||\n minimum.Height > preferred.Height)\n {\n throw new InvalidOperationException(\n \"WinUI settings dynamic resolution sizing failed: \" +\n workAreas[i].Width.ToString(CultureInfo.InvariantCulture) +\n \"x\" +\n workAreas[i].Height.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"ClaudeRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"ClaudeRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"CodexRadarManualLeftPercent\", new NumericRange(WidgetSettings.MinCodexRadarManualLeftPercent, WidgetSettings.MaxCodexRadarManualLeftPercent) },\n { \"CodexRadarManualGapPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualGapPixels, WidgetSettings.MaxCodexRadarManualGapPixels) },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualEfficiencyTextWidthPixels, WidgetSettings.MaxCodexRadarManualEfficiencyTextWidthPixels) },\n { \"CodexRadarManualQuotaRowsWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualQuotaRowsWidthPixels, WidgetSettings.MaxCodexRadarManualQuotaRowsWidthPixels) },\n { \"CodexRadarManualIqStatusWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualIqStatusWidthPixels, WidgetSettings.MaxCodexRadarManualIqStatusWidthPixels) },\n { \"CodexRadarManualTextScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualTextScalePercent, WidgetSettings.MaxCodexRadarManualTextScalePercent) },\n { \"CodexRadarManualRingScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualRingScalePercent, WidgetSettings.MaxCodexRadarManualRingScalePercent) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselineValidTasks\", new NumericRange(WidgetSettings.MinCodexModelIqValidTasks, WidgetSettings.MaxCodexModelIqValidTasks) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"OperationPrimaryPanelMode\", \"左侧区域模式\" },\n { \"OperationWindowsButtonEnabled\", \"显示 Windows 按钮\" },\n { \"OperationMemoryPieEnabled\", \"显示内存饼图\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"AiRequestProtectionAutoEnabled\", \"AI 自动阻断\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"AI 手动阻断\" },\n { \"CodexQuotaPlanEnabled\", \"启用额度计划\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度条件\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度阈值\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时条件\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时阈值\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"恢复额度类型\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"恢复上次暂停\" },\n { \"CodexQuotaPlanPauseGoalIds\", \"暂停 goal 列表\" },\n { \"CodexQuotaPlanResumeGoalIds\", \"恢复 goal 列表\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"断开后回退显示器\" },\n { \"MainDisplayDeviceName\", \"主窗口显示器\" },\n { \"CodexRadarDisplayDeviceName\", \"Codex Radar 显示器\" },\n { \"ClaudeRadarDisplayDeviceName\", \"Claude Radar 显示器\" },\n { \"PowerThermalDisplayDeviceName\", \"功耗温度显示器\" },\n { \"NetworkMonitorDisplayDeviceName\", \"网络监控显示器\" },\n { \"ConnectionCheckDisplayDeviceName\", \"连接检测显示器\" },\n { \"OperationDisplayDeviceName\", \"操作面板显示器\" },\n { GlobalLayoutEditCommandName, \"全局编辑\" },\n { \"Width\", \"主窗口宽度\" },\n { \"Height\", \"主窗口高度\" },\n { \"LeftX\", \"主窗口距左边\" },\n { \"BottomY\", \"主窗口距下边\" },\n { \"CodexRadarWidth\", \"Codex Radar 宽度\" },\n { \"CodexRadarHeight\", \"Codex Radar 高度\" },\n { \"CodexRadarLeftX\", \"Codex Radar 距左边\" },\n { \"CodexRadarBottomY\", \"Codex Radar 距下边\" },\n { \"PowerThermalWidth\", \"功耗温度宽度\" },\n { \"PowerThermalHeight\", \"功耗温度高度\" },\n { \"PowerThermalLeftX\", \"功耗温度距左边\" },\n { \"PowerThermalBottomY\", \"功耗温度距下边\" },\n { \"NetworkMonitorWidth\", \"网络监控宽度\" },\n { \"NetworkMonitorHeight\", \"网络监控高度\" },\n { \"NetworkMonitorLeftX\", \"网络监控距左边\" },\n { \"NetworkMonitorBottomY\", \"网络监控距下边\" },\n { \"ConnectionCheckWidth\", \"连接检测宽度\" },\n { \"ConnectionCheckHeight\", \"连接检测高度\" },\n { \"ConnectionCheckLeftX\", \"连接检测距左边\" },\n { \"ConnectionCheckBottomY\", \"连接检测距下边\" },\n { \"OperationLeftOffset\", \"操作面板距左边\" },\n { \"OperationBottomOffset\", \"操作面板距下边\" },\n { \"BackgroundTransparencyPercent\", \"主窗口背景透明度\" },\n { \"ApplicationTransparencyPercent\", \"主窗口整体透明度\" },\n { \"ShowCpu\", \"显示 CPU\" },\n { \"ShowMemory\", \"显示内存\" },\n { \"ShowDisk\", \"显示磁盘\" },\n { \"ShowNetwork\", \"显示网络\" },\n { \"ShowGpu\", \"显示 GPU\" },\n { \"ShowNpu\", \"显示 NPU\" },\n { \"HoverOpacityEnabled\", \"鼠标靠近时隐藏\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"触发距离(像素)\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarSoftwareMode\", \"监控哪个软件\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"时钟过期自动切模型\" },\n { \"RadarClockTimeDisplayMode\", \"时钟时间来源\" },\n { \"CodexRadarEnabled\", \"启用 Codex Radar\" },\n { \"CodexRadarTransparencyPercent\", \"Codex Radar 透明度\" },\n { \"ClaudeRadarEnabled\", \"启用 Claude Radar\" },\n { \"ClaudeRadarWidth\", \"Claude Radar 宽度\" },\n { \"ClaudeRadarHeight\", \"Claude Radar 高度\" },\n { \"ClaudeRadarLeftX\", \"Claude Radar 左侧 X\" },\n { \"ClaudeRadarBottomY\", \"Claude Radar 底部 Y\" },\n { \"ClaudeRadarTransparencyPercent\", \"Claude Radar 背景透明\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 模型\" },\n { \"ClaudeRadarJsonEnabled\", \"Claude Radar JSON\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"首页元数据回退\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"社区体感分\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"本地 7 天额度线回退\" },\n { \"ClaudeRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"ClaudeRadarRandomTestEnabled\", \"Claude 随机测试\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"Claude 随机测试自动刷新\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"OperationRenderVariant\", \"外观风格\" },\n { \"CodexRadarManualLayoutEnabled\", \"启用手动布局\" },\n { \"CodexRadarManualLeftPercent\", \"左侧区域占比\" },\n { \"CodexRadarManualGapPixels\", \"模块间距\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"效率文字列宽\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"余额列宽\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"IQ 状态列宽\" },\n { \"CodexRadarManualTextScalePercent\", \"文字比例\" },\n { \"CodexRadarManualRingScalePercent\", \"圆环比例\" },\n { \"CodexRadarTimeEfficiencyRingOffsetX\", \"时间环 X\" },\n { \"CodexRadarTimeEfficiencyRingOffsetY\", \"时间环 Y\" },\n { \"CodexRadarTimeEfficiencyTextOffsetX\", \"时间字 X\" },\n { \"CodexRadarTimeEfficiencyTextOffsetY\", \"时间字 Y\" },\n { \"CodexRadarTokenEfficiencyRingOffsetX\", \"Token 环 X\" },\n { \"CodexRadarTokenEfficiencyRingOffsetY\", \"Token 环 Y\" },\n { \"CodexRadarTokenEfficiencyTextOffsetX\", \"Token 字 X\" },\n { \"CodexRadarTokenEfficiencyTextOffsetY\", \"Token 字 Y\" },\n { \"CodexRadarConnectionTopTextOffsetX\", \"连接上字 X\" },\n { \"CodexRadarConnectionTopTextOffsetY\", \"连接上字 Y\" },\n { \"CodexRadarConnectionLineOffsetX\", \"连接线 X\" },\n { \"CodexRadarConnectionLineOffsetY\", \"连接线 Y\" },\n { \"CodexRadarConnectionBottomTextOffsetX\", \"连接下字 X\" },\n { \"CodexRadarConnectionBottomTextOffsetY\", \"连接下字 Y\" },\n { \"CodexRadarFiveHourQuotaRingOffsetX\", \"5h 环 X\" },\n { \"CodexRadarFiveHourQuotaRingOffsetY\", \"5h 环 Y\" },\n { \"CodexRadarFiveHourQuotaTextOffsetX\", \"5h 字 X\" },\n { \"CodexRadarFiveHourQuotaTextOffsetY\", \"5h 字 Y\" },\n { \"CodexRadarWeeklyQuotaRingOffsetX\", \"周环 X\" },\n { \"CodexRadarWeeklyQuotaRingOffsetY\", \"周环 Y\" },\n { \"CodexRadarWeeklyQuotaTextOffsetX\", \"周字 X\" },\n { \"CodexRadarWeeklyQuotaTextOffsetY\", \"周字 Y\" },\n { \"CodexRadarQuotaRadarLineOffsetX\", \"额度线 X\" },\n { \"CodexRadarQuotaRadarLineOffsetY\", \"额度线 Y\" },\n { \"CodexRadarIqRingOffsetX\", \"IQ 环 X\" },\n { \"CodexRadarIqRingOffsetY\", \"IQ 环 Y\" },\n { \"CodexRadarIqTextOffsetX\", \"IQ 字 X\" },\n { \"CodexRadarIqTextOffsetY\", \"IQ 字 Y\" },\n { \"CodexRadarPublicJsonEnabled\", \"公开 JSON 摘要\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"首页 HTML 回退\" },\n { \"CodexRadarRssFallbackEnabled\", \"RSS 重置提醒\" },\n { \"CodexRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"DeepSeekApiKeyRevision\", \"DeepSeek 配置\" },\n { \"CodexModelIqTestEnabled\", \"用测试值代替实时 IQ(调试用)\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过数\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"IQ 基准自动跟随网站\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过数\" },\n { \"CodexModelIqBaselineValidTasks\", \"IQ 基准总题数\" },\n { \"CodexModelEfficiencyTestEnabled\", \"用测试值代替实时效率(调试用)\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"PowerThermalTransparencyPercent\", \"功耗温度透明度\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkMonitorTransparencyPercent\", \"网络监控透明度\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"立即刷新 GFW 检测\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测间隔\" },\n { \"ConnectionCheckTransparencyPercent\", \"连接检测透明度\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"连接检测边框透明度\" },\n { \"ConnectionCheckManualRefreshToken\", \"立即刷新连接检测\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"OperationButtonSize\", \"按钮大小\" },\n { \"OperationBackgroundTransparencyPercent\", \"操作面板透明度\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过数\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过数\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"ForceShowForegroundFpsEnabled\", \"调试用,强制显示前台 FPS 信息。\" },\n { \"AiRequestProtectionAutoEnabled\", \"网络监控判定为 GFW 明确阻断时,阻断本程序发往 OpenAI、ChatGPT、Claude 和 Anthropic 的请求。\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"手动启用后立即阻断本程序相关 AI 请求;也可在左下角程序设置按钮单击打开的特殊设置中切换。\" },\n { \"CodexQuotaPlanEnabled\", \"按本地 Codex 剩余额度缓存自动暂停或恢复选中的 Codex goal。\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度百分比的比较方向;默认小于 3%。\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时额度百分比的比较方向;默认小于 90%。\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"额度计划触发后,选择额度恢复时看周额度、5 小时额度,还是两者都恢复。\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"额度恢复时优先恢复本程序上次因额度计划暂停的 goal;关闭后使用恢复列表。\" },\n { \"CodexQuotaPlanPauseGoalIds\", \"达到额度计划触发条件时暂停的 Codex goal ID,用 | 分隔;可从本地 goal 管理记录复制。\" },\n { \"CodexQuotaPlanResumeGoalIds\", \"关闭“恢复上次暂停”时使用,额度恢复后启用这些 Codex goal ID,用 | 分隔。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityRevealDelaySeconds\", \"鼠标离开后继续隐藏的秒数。\" },\n { \"HoverOpacityRevealResetSeconds\", \"短时间重新靠近时,保持隐藏的重置秒数。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"反向隐藏临时恢复后,鼠标移开多久回到隐藏。\" },\n { \"AutoHoverOpacityIdleEnabled\", \"鼠标一段时间不动时自动进入隐藏透明度。\" },\n { \"AutoHoverOpacityIdleSeconds\", \"范围 1-300 秒,达到后自动隐藏。\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"有非 SeelenUI 应用最大化时自动进入隐藏透明度。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarSoftwareMode\", \"自动按前台窗口选择 CODEX 或 CLAUDE;也可强制固定检测软件。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"CodexRadarModelVersion\", \"模型目录版本,日常保持默认。\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"当 Codex/Claude 时钟跨过完整周期仍未获取当前模型 IQ 更新时,自动切到同站点当天最近刷新 IQ 的模型。\" },\n { \"RadarClockTimeDisplayMode\", \"控制 Codex/Claude Radar 时钟中心下方时间:UTC、当前本机时间、上次尝试刷新,或上次实际 IQ 刷新。\" },\n { \"CodexRadarPublicJsonEnabled\", \"读取 current.json 的公开摘要层,包含窗口、预测和 API 可用性说明。\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"当公开 JSON 不含 model_iq 时,从首页明文模块补齐 IQ、效率和模型目录。\" },\n { \"CodexRadarRssFallbackEnabled\", \"读取 feed.xml 的重置提醒;关闭后不会用 RSS 触发额度重置保护。\" },\n { \"CodexRadarServiceProbeToken\", \"点击后探测公开摘要、授权 API、首页 HTML 和 RSS,并写入本地诊断结果。\" },\n { \"CodexRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"CodexModelIqTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelIqTestPassed\", \"手动指定 IQ 测试通过数。\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"开启时自动读取网站有效题数和常态区推导 n/N;关闭后使用下方手动 n/N。\" },\n { \"CodexModelIqBaselineMode\", \"选择 IQ 对比基准的来源。\" },\n { \"CodexModelIqBaselinePassed\", \"手动指定 IQ 基准通过数 n。\" },\n { \"CodexModelIqBaselineValidTasks\", \"手动指定 IQ 基准总题数 N;仅在关闭自动基准时用于 IQ 环。\" },\n { \"CodexModelEfficiencyTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"手动指定 Token 效率测试百分比。\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"手动指定时间效率测试百分比。\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"选择 Token 效率对比基准的来源。\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"手动指定 Token 效率基准通过数。\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"手动指定 Token 效率基准消耗。\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"选择时间效率对比基准的来源。\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"手动指定时间效率基准通过数。\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"手动指定时间效率基准秒数。\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexRadarEnabled\", \"关闭后会释放 Codex Radar 分层窗口;默认开启以保持现有桌面不变。\" },\n { \"DisplayTimeZoneMode\", \"控制窗口里时间文字使用本机、北京时间还是指定时区。\" },\n { \"DisplayTimeZoneId\", \"Windows 时区 ID,指定时区模式下生效。\" },\n { \"ClaudeRadarEnabled\", \"开启独立 Claude Radar 分层窗口;默认关闭,避免升级后改变现有桌面。\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 站点内部 m* 模型键;留空时按站点当前目录自动选择。\" },\n { \"ClaudeRadarJsonEnabled\", \"读取 claudecoderadar.com/data/claude-code-radar.json 作为主数据源。\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"主 JSON 失败或缺少模型元数据时,顺序读取首页 MODEL_NAMES 作为弱回退;不会伪造 IQ、效率或额度。\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"读取 Claude Radar 社区体感分接口,并通过独立映射表关联模型。\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"站点额度趋势不完整时使用本地 7 天 JSONL 历史绘制额度线。\" },\n { \"ClaudeRadarServiceProbeToken\", \"点击后触发 Claude Radar 下次刷新,服务状态显示在窗口右侧。\" },\n { \"ClaudeRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"OperationRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"DeepSeekApiKeyRevision\", \"配置 DeepSeek API Key;密钥只写入本地文件,修订号用于触发即时刷新。\" },\n { \"CodexRadarManualLayoutEnabled\", \"开启后下方布局参数实时影响 Codex Radar 内部模块,不需要重启。\" },\n { \"CodexRadarManualLeftPercent\", \"调整效率/IQ/连接流程区与余额区的左右分配。\" },\n { \"CodexRadarManualGapPixels\", \"调整左侧区域和余额区之间的像素间距。\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"调整左侧效率/IQ 圆环右侧文字列宽。\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"调整余额两行圆环和重置时间的总宽度。\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"调整余额区右侧 IQ 圆环与降智文字列宽。\" },\n { \"CodexRadarManualTextScalePercent\", \"只影响 Codex Radar 内部状态文字和余额时间字号。\" },\n { \"CodexRadarManualRingScalePercent\", \"只影响 Codex Radar 内部效率、余额和 IQ 圆环大小。\" },\n { \"PowerThermalAutoSizeEnabled\", \"根据告警数量自动调整功耗温度窗口高度。\" },\n { \"PowerThermalAutoDirection\", \"自动调整时从哪个方向展开。\" },\n { \"PowerThermalVisibleAlertCount\", \"功耗温度窗口中最多显示的告警数量。\" },\n { \"ThermalTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"NetworkStatusTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测的自动刷新间隔。\" },\n { \"GfwProbeManualRefreshToken\", \"点击后请求立刻刷新 GFW 检测。\" },\n { \"ConnectionCheckManualRefreshToken\", \"点击后请求立刻刷新连接检测。\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测的自动刷新间隔。\" },\n { \"CloudEndpointTestSeed\", \"仅用于测试显示效果,日常保持默认。\" },\n { \"CloudStatusRegionMask\", \"云服务状态的地区过滤位掩码,日常保持默认。\" },\n { \"CleanIpBadgeTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"AlertTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"SeelenDockForegroundPulseEnabled\", \"需要时短暂拉前 Seelen Dock,避免被系统窗口压住。\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"指定显示器未连接时,将对应模块回退到当前主显示器。\" },\n { \"MainDisplayDeviceName\", \"留空使用当前主显示器;选择 DISPLAY 后按该显示器工作区定位。\" },\n { \"CodexRadarDisplayDeviceName\", \"留空使用当前主显示器;未连接且不回退时保留原显示器工作区。\" },\n { \"PowerThermalDisplayDeviceName\", \"留空使用当前主显示器;适合把功耗模块固定到副屏。\" },\n { \"NetworkMonitorDisplayDeviceName\", \"留空使用当前主显示器;适合把网络监控固定到副屏。\" },\n { \"ConnectionCheckDisplayDeviceName\", \"留空使用当前主显示器;适合把连接检测固定到副屏。\" },\n { \"OperationDisplayDeviceName\", \"留空使用当前主显示器;操作面板偏移量按目标显示器左下角计算。\" },\n { GlobalLayoutEditCommandName, \"打开全屏布局编辑遮罩,拖拽模块位置;Enter 保存,Esc 放弃。\" },\n { \"Width\", \"逻辑像素,主窗口宽度。\" },\n { \"Height\", \"逻辑像素,主窗口高度。\" },\n { \"LeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"BottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"CodexRadarWidth\", \"逻辑像素,Codex Radar 宽度。\" },\n { \"CodexRadarHeight\", \"逻辑像素,Codex Radar 高度。\" },\n { \"CodexRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"CodexRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ClaudeRadarWidth\", \"逻辑像素,Claude Radar 宽度。\" },\n { \"ClaudeRadarHeight\", \"逻辑像素,Claude Radar 高度。\" },\n { \"ClaudeRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ClaudeRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"PowerThermalWidth\", \"逻辑像素,功耗温度窗口宽度。\" },\n { \"PowerThermalHeight\", \"逻辑像素,功耗温度窗口高度。\" },\n { \"PowerThermalLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"PowerThermalBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"NetworkMonitorWidth\", \"逻辑像素,网络监控窗口宽度。\" },\n { \"NetworkMonitorHeight\", \"逻辑像素,网络监控窗口高度。\" },\n { \"NetworkMonitorLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"NetworkMonitorBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ConnectionCheckWidth\", \"逻辑像素,连接检测窗口宽度。\" },\n { \"ConnectionCheckHeight\", \"逻辑像素,连接检测窗口高度。\" },\n { \"ConnectionCheckLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ConnectionCheckBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"OperationLeftOffset\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"OperationBottomOffset\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"BackgroundTransparencyPercent\", \"只影响主窗口背景底色,数值越高越透明。\" },\n { \"ApplicationTransparencyPercent\", \"影响主窗口整体透明度,数值越高越透明。\" },\n { \"CodexRadarTransparencyPercent\", \"影响 Codex Radar 背景透明度。\" },\n { \"ClaudeRadarTransparencyPercent\", \"影响 Claude Radar 背景透明度。\" },\n { \"PowerThermalTransparencyPercent\", \"影响功耗温度窗口背景透明度。\" },\n { \"NetworkMonitorTransparencyPercent\", \"影响网络监控窗口背景透明度。\" },\n { \"ConnectionCheckTransparencyPercent\", \"影响连接检测窗口背景透明度。\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"影响连接检测三框边框透明度。\" },\n { \"OperationBackgroundTransparencyPercent\", \"影响左下角操作面板背景透明度。\" },\n { \"ShowCpu\", \"在主窗口显示 CPU 指标。\" },\n { \"ShowMemory\", \"在主窗口显示内存指标。\" },\n { \"ShowDisk\", \"在主窗口显示磁盘指标。\" },\n { \"ShowNetwork\", \"在主窗口显示网络指标。\" },\n { \"ShowGpu\", \"在主窗口显示 GPU 指标。\" },\n { \"ShowNpu\", \"在主窗口显示 NPU 指标。\" },\n { \"OperationPrimaryPanelMode\", \"自动模式会按 SeelenUI 运行态在 Windows 按钮和内存饼图之间切换;隐藏会让小按钮移到最左侧。\" },\n { \"OperationButtonSize\", \"左下角操作面板按钮的逻辑像素大小。\" },\n { \"OperationWindowsButtonEnabled\", \"SeelenUI 未运行时会自动隐藏;关闭后始终不显示左侧 Windows 按钮。\" },\n { \"OperationMemoryPieEnabled\", \"左侧 Windows 按钮隐藏时显示物理、虚拟和前台程序内存占用饼图。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.SettingsWarmTheme.ToggleTrackHover : DesignTokens.SettingsWarmTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsWarmTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsWarmTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsWarmTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = false;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = false;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + controlHeight + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(this.Width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, controlHeight);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n int controlTop = top + Math.Max(0, (contentHeight - controlHeight) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, controlHeight);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private int GetValueControlHeight(int controlWidth)\n {\n VariantPicker picker = this.valueControl as VariantPicker;\n if (picker != null)\n {\n return picker.GetPreferredHeightForWidth(controlWidth);\n }\n\n return this.valueControl.Height;\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── AdvancedSectionHeader ────────────────────────────────────────────\n // Clickable \"复杂选项\" divider: everything below it stays collapsed until\n // the user expands it, keeping everyday pages short. Searching bypasses it.\n private sealed class AdvancedSectionHeader : Panel\n {\n private readonly Font titleFont;\n private readonly Font hintFont;\n private bool expanded;\n private bool hover;\n\n public AdvancedSectionHeader(Font titleFont, Font hintFont)\n {\n this.titleFont = titleFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = MicaBase;\n this.Cursor = Cursors.Hand;\n }\n\n public int AdvancedRowCount { get; set; }\n\n public bool Expanded\n {\n get { return this.expanded; }\n set { this.expanded = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(this.hover ? CardHover : CardRest))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(StrokeColor))\n {\n g.DrawPath(pen, path);\n }\n\n // Chevron glyph: E70D = down, E76C = right (Segoe MDL2 / Fluent Icons).\n string chevron = this.expanded ? \"\\uE70D\" : \"\\uE76C\";\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(chevron, icoFont, brush, 20, iconY);\n }\n\n using (SolidBrush brush = new SolidBrush(TextPrimary))\n {\n g.DrawString(\"复杂选项\", this.titleFont, brush, 58, (this.Height - this.titleFont.Height) / 2f - 8);\n }\n\n string hint = this.expanded\n ? \"点击收起\"\n : \"不常用的精细调整与测试项\" + (this.AdvancedRowCount > 0 ? \"(\" + this.AdvancedRowCount.ToString(CultureInfo.InvariantCulture) + \" 项)\" : string.Empty);\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n g.DrawString(hint, this.hintFont, brush, 58, this.Height / 2f + 2);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── VariantPicker ───────────────────────────────────────────────────\n // Renders per-window visual variants as cards. Sample PNGs are loaded lazily by the outer form\n // only for real owner-backed settings windows, so binding self-tests never construct render forms.\n private sealed class VariantPicker : Panel\n {\n private const int CardHeight = 124;\n private const int CardGap = 10;\n private const int ThumbnailHeight = 78;\n\n private readonly Type enumType;\n private readonly Font labelFont;\n private readonly Font hintFont;\n private readonly List<VariantChoice> choices = new List<VariantChoice>();\n private object selectedValue;\n private int hoverIndex = -1;\n private bool samplesAttempted;\n\n public event EventHandler ValueChanged;\n public event EventHandler PreferredHeightChanged;\n\n public VariantPicker(string propertyName, Type enumType, Font labelFont, Font hintFont)\n {\n this.enumType = enumType;\n this.labelFont = labelFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n\n Array values = Enum.GetValues(enumType);\n for (int i = 0; i < values.Length; i++)\n {\n object value = values.GetValue(i);\n this.choices.Add(new VariantChoice(value, new EnumOption(value).ToString()));\n if (i == 0)\n {\n this.selectedValue = value;\n }\n }\n\n UpdatePreferredHeight();\n }\n\n public void SetSelectedValue(object value)\n {\n if (value == null || !this.enumType.IsInstanceOfType(value))\n {\n if (this.choices.Count > 0)\n {\n this.selectedValue = this.choices[0].Value;\n }\n }\n else\n {\n this.selectedValue = value;\n }\n\n this.Invalidate();\n }\n\n public object GetSelectedValue()\n {\n return this.selectedValue;\n }\n\n public int GetPreferredHeightForWidth(int width)\n {\n int columns = GetColumnCount(width);\n int rows = Math.Max(1, (this.choices.Count + columns - 1) / columns);\n return rows * CardHeight + Math.Max(0, rows - 1) * CardGap;\n }\n\n public bool HasMissingSamples(string directory, string prefix)\n {\n if (this.samplesAttempted)\n {\n return false;\n }\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n string path = GetSamplePath(directory, prefix, this.choices[i].Value);\n if (!System.IO.File.Exists(path))\n {\n return true;\n }\n }\n\n return false;\n }\n\n public void LoadSamples(string directory, string prefix)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n VariantChoice choice = this.choices[i];\n string path = GetSamplePath(directory, prefix, choice.Value);\n if (!System.IO.File.Exists(path))\n {\n continue;\n }\n\n try\n {\n using (Image original = Image.FromFile(path))\n {\n Image old = choice.Image;\n choice.Image = new Bitmap(original);\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n catch\n {\n Image old = choice.Image;\n choice.Image = null;\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n public void MarkSamplesUnavailable()\n {\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n UpdatePreferredHeight();\n }\n\n protected override void OnMouseMove(MouseEventArgs e)\n {\n int nextHover = GetChoiceIndexAt(e.Location);\n if (nextHover != this.hoverIndex)\n {\n this.hoverIndex = nextHover;\n this.Invalidate();\n }\n\n base.OnMouseMove(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hoverIndex = -1;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n protected override void OnMouseClick(MouseEventArgs e)\n {\n base.OnMouseClick(e);\n int index = GetChoiceIndexAt(e.Location);\n if (index < 0 || index >= this.choices.Count)\n {\n return;\n }\n\n object nextValue = this.choices[index].Value;\n if (object.Equals(this.selectedValue, nextValue))\n {\n return;\n }\n\n this.selectedValue = nextValue;\n this.Invalidate();\n if (this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.InterpolationMode = InterpolationMode.HighQualityBicubic;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n Rectangle rect = GetCardBounds(i);\n if (rect.Width <= 0 || rect.Height <= 0)\n {\n continue;\n }\n\n VariantChoice choice = this.choices[i];\n bool selected = object.Equals(choice.Value, this.selectedValue);\n bool hover = i == this.hoverIndex;\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(hover ? CardHover : ControlBg))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(selected ? AccentClr : ControlBorder, selected ? 2.0f : 1.0f))\n {\n g.DrawPath(pen, path);\n }\n\n Rectangle thumb = new Rectangle(rect.Left + 8, rect.Top + 8, rect.Width - 16, ThumbnailHeight);\n DrawChoiceThumbnail(g, choice, thumb);\n\n Rectangle labelRect = new Rectangle(rect.Left + 10, thumb.Bottom + 7, rect.Width - 20, 24);\n using (StringFormat format = new StringFormat())\n using (SolidBrush textBrush = new SolidBrush(selected ? TextPrimary : TextSecondary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(choice.Label, this.labelFont, textBrush, labelRect, format);\n }\n }\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (this.choices[i].Image != null)\n {\n this.choices[i].Image.Dispose();\n this.choices[i].Image = null;\n }\n }\n }\n\n base.Dispose(disposing);\n }\n\n private void DrawChoiceThumbnail(Graphics g, VariantChoice choice, Rectangle thumb)\n {\n using (GraphicsPath thumbPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n using (SolidBrush thumbBg = new SolidBrush(MicaBase))\n {\n g.FillPath(thumbBg, thumbPath);\n }\n\n if (choice.Image == null)\n {\n using (StringFormat format = new StringFormat())\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(\"纯文字模式\", this.hintFont, brush, thumb, format);\n }\n return;\n }\n\n Rectangle target = FitImage(choice.Image.Size, thumb);\n GraphicsState state = g.Save();\n using (GraphicsPath clipPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n {\n g.SetClip(clipPath);\n g.DrawImage(choice.Image, target);\n }\n g.Restore(state);\n }\n\n private void UpdatePreferredHeight()\n {\n int oldHeight = this.Height;\n int nextHeight = GetPreferredHeightForWidth(this.Width);\n if (nextHeight != oldHeight)\n {\n this.Height = nextHeight;\n if (this.PreferredHeightChanged != null)\n {\n this.PreferredHeightChanged(this, EventArgs.Empty);\n }\n }\n }\n\n private int GetColumnCount()\n {\n return GetColumnCount(this.Width);\n }\n\n private static int GetColumnCount(int width)\n {\n width = Math.Max(1, width);\n if (width >= 520)\n {\n return 3;\n }\n\n if (width >= 340)\n {\n return 2;\n }\n\n return 1;\n }\n\n private Rectangle GetCardBounds(int index)\n {\n int columns = GetColumnCount();\n int col = index % columns;\n int row = index / columns;\n int cardWidth = Math.Max(86, (Math.Max(1, this.Width) - (columns - 1) * CardGap) / columns);\n return new Rectangle(col * (cardWidth + CardGap), row * (CardHeight + CardGap), cardWidth, CardHeight);\n }\n\n private int GetChoiceIndexAt(Point point)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (GetCardBounds(i).Contains(point))\n {\n return i;\n }\n }\n\n return -1;\n }\n\n private static string GetSamplePath(string directory, string prefix, object value)\n {\n string variant = Convert.ToString(value, CultureInfo.InvariantCulture);\n return System.IO.Path.Combine(directory, prefix + \"-\" + variant.ToLowerInvariant() + \".png\");\n }\n\n private static Rectangle FitImage(Size imageSize, Rectangle bounds)\n {\n if (imageSize.Width <= 0 || imageSize.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0)\n {\n return bounds;\n }\n\n float sx = bounds.Width / (float)imageSize.Width;\n float sy = bounds.Height / (float)imageSize.Height;\n float scale = Math.Min(sx, sy);\n int width = Math.Max(1, (int)Math.Round(imageSize.Width * scale));\n int height = Math.Max(1, (int)Math.Round(imageSize.Height * scale));\n return new Rectangle(\n bounds.Left + (bounds.Width - width) / 2,\n bounds.Top + (bounds.Height - height) / 2,\n width,\n height);\n }\n\n private sealed class VariantChoice\n {\n public VariantChoice(object value, string label)\n {\n this.Value = value;\n this.Label = label ?? string.Empty;\n }\n\n public object Value;\n public string Label;\n public Image Image;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public AdvancedSectionHeader AdvancedHeader;\n public bool AdvancedExpanded;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public bool Advanced;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Name = property == null ? string.Empty : property.Name;\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public SettingEditor(string name, SettingRow card, Control control)\n {\n this.Name = name ?? string.Empty;\n this.Property = null;\n this.Card = card;\n this.Control = control;\n }\n\n public string Name { get; private set; }\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class ModelOption\n {\n public ModelOption(string key, string label, bool available, bool missingFromCatalog)\n {\n this.Key = CodexRadarModelCatalog.NormalizeModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.MissingFromCatalog = missingFromCatalog;\n }\n\n public string Key { get; private set; }\n private string Label { get; set; }\n private bool Available { get; set; }\n private bool MissingFromCatalog { get; set; }\n\n public override string ToString()\n {\n if (this.MissingFromCatalog)\n {\n return this.Label + \"(未在目录)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class ClaudeModelOption\n {\n public ClaudeModelOption(string key, string label, bool available, bool pending)\n {\n this.Key = WidgetSettings.NormalizeClaudeRadarModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.Pending = pending;\n }\n\n public string Key { get; private set; }\n public string Label { get; private set; }\n public bool Available { get; private set; }\n public bool Pending { get; private set; }\n\n public override string ToString()\n {\n if (this.Key.Length == 0)\n {\n return this.Label;\n }\n\n if (this.Pending)\n {\n return this.Label + \"(待映射)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class DisplayOption\n {\n public DisplayOption(string value, string label)\n {\n this.Value = WidgetSettings.NormalizeDisplayDeviceName(value);\n this.Label = label ?? string.Empty;\n }\n\n public string Value { get; private set; }\n private string Label { get; set; }\n\n public override string ToString()\n {\n return this.Label;\n }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n if (this.Value is OperationPrimaryPanelMode)\n {\n OperationPrimaryPanelMode mode = (OperationPrimaryPanelMode)this.Value;\n if (mode == OperationPrimaryPanelMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == OperationPrimaryPanelMode.WindowsButton)\n {\n return \"Windows 按钮\";\n }\n\n if (mode == OperationPrimaryPanelMode.MemoryPie)\n {\n return \"内存饼图\";\n }\n\n if (mode == OperationPrimaryPanelMode.Hidden)\n {\n return \"隐藏\";\n }\n }\n\n if (this.Value is CodexRadarSoftwareMode)\n {\n CodexRadarSoftwareMode mode = (CodexRadarSoftwareMode)this.Value;\n if (mode == CodexRadarSoftwareMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == CodexRadarSoftwareMode.Codex)\n {\n return \"CODEX\";\n }\n\n if (mode == CodexRadarSoftwareMode.Claude)\n {\n return \"CLAUDE\";\n }\n }\n\n if (this.Value is RadarClockTimeDisplayMode)\n {\n RadarClockTimeDisplayMode mode = (RadarClockTimeDisplayMode)this.Value;\n if (mode == RadarClockTimeDisplayMode.Utc)\n {\n return \"UTC\";\n }\n\n if (mode == RadarClockTimeDisplayMode.CurrentLocal)\n {\n return \"当前时间\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastAttemptRefresh)\n {\n return \"上次尝试刷新\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastActualRefresh)\n {\n return \"上次实际刷新\";\n }\n }\n\n if (this.Value is CodexQuotaPlanComparison)\n {\n CodexQuotaPlanComparison comparison = (CodexQuotaPlanComparison)this.Value;\n if (comparison == CodexQuotaPlanComparison.GreaterThan)\n {\n return \"大于\";\n }\n\n return \"小于\";\n }\n\n if (this.Value is CodexQuotaPlanResumeConditionMode)\n {\n CodexQuotaPlanResumeConditionMode mode = (CodexQuotaPlanResumeConditionMode)this.Value;\n if (mode == CodexQuotaPlanResumeConditionMode.WeeklyOnly)\n {\n return \"仅周额度\";\n }\n\n if (mode == CodexQuotaPlanResumeConditionMode.FiveHourOnly)\n {\n return \"仅 5 小时额度\";\n }\n\n return \"周额度与 5 小时额度\";\n }\n\n // All six per-window render-variant enums share member names for the common variants\n // (Classic plus the four OLED-safe restyle schemes); CodexRadarRenderVariant additionally\n // carries EvenGrid/EvenRow. Label by member name once instead of duplicating per enum type.\n if (this.Value is NetworkMonitorRenderVariant)\n {\n string networkVariantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (networkVariantName == \"Classic\") return \"扁平信息条\";\n if (networkVariantName == \"GroupedCards\") return \"分组卡片\";\n if (networkVariantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (networkVariantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (networkVariantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (networkVariantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n }\n\n if (this.Value is CodexRadarRenderVariant ||\n this.Value is MainWidgetRenderVariant ||\n this.Value is PowerThermalRenderVariant ||\n this.Value is ConnectionCheckRenderVariant ||\n this.Value is OperationRenderVariant)\n {\n string variantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (variantName == \"Classic\") return \"经典布局\";\n if (variantName == \"EvenGrid\") return \"均布六格\";\n if (variantName == \"EvenRow\") return \"均布单行\";\n if (variantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (variantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (variantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (variantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n if (variantName == \"RadialDial\") return \"扇形速控盘(新)\";\n }\n\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "7ba4413ad1a3b25f8613d553149be9535c812279": {"t": 1785756674.5234048, "n": 187710, "h": "a2d4e53afae16d00", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int WmSettingChange = 0x001A;\n private const int WmDisplayChange = 0x007E;\n private const int EmSetCueBanner = 0x1501;\n private const int ReferenceWorkAreaWidth = 2880;\n private const int ReferenceWorkAreaHeight = 1740;\n private const int PreferredClientWidth = 1888;\n private const int PreferredClientHeight = 1312;\n private const int MinimumClientWidth = 760;\n private const int MinimumClientHeight = 560;\n private const int ScreenMargin = 80;\n private const int DefaultNavWidth = 409;\n private const int MediumNavWidth = 300;\n private const int CompactNavWidth = 220;\n private const int NavItemHeight = 60;\n private const int ClaudeRadarModelGridColumns = 5;\n private const int ClaudeRadarModelButtonWidth = 72;\n private const int ClaudeRadarModelButtonHeight = 38;\n private const int ClaudeRadarModelButtonGap = 8;\n private const string ClaudeRadarModelGridName = \"ClaudeRadarModelGrid\";\n private const string GlobalLayoutEditCommandName = \"GlobalLayoutEditCommand\";\n\n private static readonly Color MicaBase = DesignTokens.SettingsWarmTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.SettingsWarmTheme.CardRest;\n private static readonly Color CardHover = DesignTokens.SettingsWarmTheme.CardHover;\n private static readonly Color StrokeColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.SettingsWarmTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.SettingsWarmTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.SettingsWarmTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.SettingsWarmTheme.Accent;\n private static readonly Color AccentHover = DesignTokens.SettingsWarmTheme.AccentHover;\n private static readonly Color AccentPressed = DesignTokens.SettingsWarmTheme.AccentPressed;\n private static readonly Color ErrorClr = DesignTokens.SettingsWarmTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private TableLayoutPanel bodyLayout;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool dirty;\n private bool messageFilterRegistered;\n private bool unsavedPreviewConsumed;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n public bool TryConsumeUnsavedPreview(out WidgetSettings settings)\n {\n settings = null;\n if (this.saved || this.OwnerFormClosing || this.unsavedPreviewConsumed)\n {\n return false;\n }\n\n this.unsavedPreviewConsumed = true;\n settings = this.baseline.Clone();\n settings.Normalize();\n return true;\n }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n ApplicationIcon.ApplyTo(this);\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = true;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n ApplyDynamicResolutionSizing(true);\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n ApplyDynamicResolutionSizing(false);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n ApplyResponsiveShellLayout();\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n if (m.Msg == WmDisplayChange || m.Msg == WmSettingChange)\n {\n ScheduleDynamicResolutionSizing();\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n if (this.dirty && !this.saved && this.owner != null && !this.OwnerFormClosing)\n {\n DialogResult result = MessageBox.Show(\n this,\n \"设置还没有保存。要先保存再关闭吗?\",\n \"未保存的更改\",\n MessageBoxButtons.YesNoCancel,\n MessageBoxIcon.Warning,\n MessageBoxDefaultButton.Button1);\n if (result == DialogResult.Cancel)\n {\n e.Cancel = true;\n return;\n }\n\n if (result == DialogResult.Yes && !TrySaveSettings())\n {\n e.Cancel = true;\n return;\n }\n }\n\n this.previewTimer.Stop();\n WidgetSettings revertSettings;\n if (this.owner != null && TryConsumeUnsavedPreview(out revertSettings))\n {\n try\n {\n this.owner.RevertSettings(revertSettings);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.SettingsWarmTheme.ErrorText; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n this.bodyLayout = body;\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetNavigationWidthForClientWidth(this.ClientSize.Width)));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n SetDirtyState(true);\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n if (TrySaveSettings())\n {\n ShowStatus(\"保存完成\", false);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold));\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n // 分组名以 '!' 开头 = 收进该页底部的「复杂选项」折叠区。\n AddPageGrouped(\"\\uE115\", \"系统\", \"开机启动、性能和窗口基础行为。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\" },\n new string[] { \"AI 请求阻断\", \"AiRequestProtectionAutoEnabled\", \"AiRequestProtectionManualBlockEnabled\" },\n new string[] { \"!Codex 额度计划\", \"CodexQuotaPlanEnabled\", \"CodexQuotaPlanWeeklyComparison\", \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\", \"CodexQuotaPlanFiveHourThresholdPercent\", \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\" },\n new string[] { \"!恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" },\n new string[] { \"!调试\", \"ForceShowForegroundFpsEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7C2\", \"布局与位置\", \"所有浮窗的位置、大小和所在显示器;推荐用可视化编辑直接拖拽。\", new string[][]\n {\n new string[] { \"可视化编辑\", GlobalLayoutEditCommandName },\n new string[] { \"显示器分配\", \"FallbackDisconnectedDisplaysEnabled\", \"MainDisplayDeviceName\", \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\", \"PowerThermalDisplayDeviceName\", \"NetworkMonitorDisplayDeviceName\", \"ConnectionCheckDisplayDeviceName\", \"OperationDisplayDeviceName\" },\n new string[] { \"!主窗口位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"!Codex Radar 位置\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\" },\n new string[] { \"!Claude Radar 位置\", \"ClaudeRadarWidth\", \"ClaudeRadarHeight\", \"ClaudeRadarLeftX\", \"ClaudeRadarBottomY\" },\n new string[] { \"!功耗温度位置\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\" },\n new string[] { \"!网络监控位置\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\" },\n new string[] { \"!连接检测位置\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\" },\n new string[] { \"!操作面板位置\", \"OperationLeftOffset\", \"OperationBottomOffset\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与防烧屏\", \"鼠标靠近时隐藏、空闲自动隐藏和 OLED 防烧屏。\", new string[][]\n {\n new string[] { \"鼠标靠近时隐藏\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" },\n new string[] { \"!延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"!覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" }\n });\n\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口显示哪些指标、透明度和外观。\", new string[][]\n {\n new string[] { \"显示哪些指标\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" },\n new string[] { \"外观风格\", \"MainWidgetRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"Codex 用量监控:模型、时区和外观。\", new string[][]\n {\n // 手动布局与元素偏移分组已从设置界面隐藏(改用渲染变体切换布局)。\n // 对应 WidgetSettings 属性仍保留,Classic 布局若通过 settings.ini 设置仍生效;\n // 均布变体本就忽略这些设置。恢复入口时把下方分组加回,并同步 VerifySelfTest 必需绑定。\n new string[] { \"窗口\", \"CodexRadarEnabled\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarSoftwareMode\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"RadarClockAutoSwitchModelEnabled\", \"RadarClockTimeDisplayMode\", \"DeepSeekApiKeyRevision\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"外观风格\", \"CodexRadarRenderVariant\" },\n new string[] { \"!网站数据源\", \"CodexRadarPublicJsonEnabled\", \"CodexRadarHtmlFallbackEnabled\", \"CodexRadarRssFallbackEnabled\", \"CodexRadarServiceProbeToken\" },\n new string[] { \"!IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineAutoEnabled\", \"CodexModelIqBaselinePassed\", \"CodexModelIqBaselineValidTasks\" },\n new string[] { \"!效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"!随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uE8D4\", \"Claude Radar\", \"Claude 用量监控独立窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"ClaudeRadarEnabled\", \"ClaudeRadarTransparencyPercent\" },\n new string[] { \"模型\", \"ClaudeRadarModelKey\" },\n new string[] { \"!网站数据源\", \"ClaudeRadarJsonEnabled\", \"ClaudeRadarHomepageFallbackEnabled\", \"ClaudeRadarCommunityRatingsEnabled\", \"ClaudeRadarLocalQuotaFallbackEnabled\", \"ClaudeRadarServiceProbeToken\" },\n new string[] { \"!随机测试\", \"ClaudeRadarRandomTestEnabled\", \"ClaudeRadarRandomTestAutoRefresh\", \"ClaudeRadarRandomTestRefreshToken\" },\n new string[] { \"!外观风格(预留)\", \"ClaudeRadarRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"透明度\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"外观风格\", \"PowerThermalRenderVariant\" },\n new string[] { \"!测试\", \"ThermalTestMode\" }\n });\n\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW 检测、云服务和出口身份。\", new string[][]\n {\n new string[] { \"网络监控\", \"NetworkMonitorAdapterId\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\" },\n new string[] { \"连接检测\", \"ConnectionCheckIntervalSeconds\", \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\" },\n new string[] { \"网络监控外观\", \"NetworkMonitorRenderVariant\" },\n new string[] { \"连接检测外观\", \"ConnectionCheckRenderVariant\" },\n new string[] { \"!手动刷新\", \"GfwProbeManualRefreshToken\", \"ConnectionCheckManualRefreshToken\" },\n new string[] { \"!云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"!测试\", \"CleanIpBadgeTestMode\", \"NetworkStatusTestMode\" }\n });\n\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板的按钮、透明度和外观。\", new string[][]\n {\n new string[] { \"按钮与面板\", \"OperationButtonSize\", \"OperationPrimaryPanelMode\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"外观风格\", \"OperationRenderVariant\" },\n new string[] { \"!测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n // A leading '!' marks the group as advanced: it renders collapsed under the\n // per-page \"复杂选项\" header until the user expands it (or searches).\n bool advanced = groupTitle.Length > 0 && groupTitle[0] == '!';\n if (advanced)\n {\n groupTitle = groupTitle.Substring(1);\n }\n\n if (advanced && page.AdvancedHeader == null)\n {\n AdvancedSectionHeader advancedHeader = new AdvancedSectionHeader(GetUiFont(10.0f, FontStyle.Bold), GetUiFont(8.5f));\n advancedHeader.Width = 1152;\n advancedHeader.Height = 64;\n advancedHeader.Margin = new Padding(0, 32, 0, 6);\n advancedHeader.Click += delegate { ToggleAdvancedSection(page); };\n page.AdvancedHeader = advancedHeader;\n stack.Controls.Add(advancedHeader);\n }\n\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n group.Advanced = advanced;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n titleLabel.Visible = !advanced;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n card.Visible = !advanced;\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.AdvancedRowCount = CountAdvancedRows(page);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = Math.Max(160, GetNavigationWidthForClientWidth(this.ClientSize.Width) - 24);\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n private static int CountAdvancedRows(CategoryPage page)\n {\n int count = 0;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n if (page.Groups[g].Advanced)\n {\n count += page.Groups[g].Editors.Count;\n }\n }\n\n return count;\n }\n\n private void ToggleAdvancedSection(CategoryPage page)\n {\n page.AdvancedExpanded = !page.AdvancedExpanded;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Expanded = page.AdvancedExpanded;\n }\n\n ApplyAdvancedVisibility(page);\n LayoutPage(page);\n }\n\n private void ApplyAdvancedVisibility(CategoryPage page)\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n if (query.Length > 0)\n {\n // Search visibility is authoritative while a query is active.\n ApplySearchFilter();\n return;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (!group.Advanced)\n {\n continue;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = page.AdvancedExpanded;\n }\n\n if (group.Card != null)\n {\n group.Card.Visible = page.AdvancedExpanded;\n if (page.AdvancedExpanded)\n {\n group.Card.LayoutRows();\n }\n }\n }\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n if (string.Equals(propertyName, GlobalLayoutEditCommandName, StringComparison.Ordinal))\n {\n return BuildGlobalLayoutEditEditor();\n }\n\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private SettingEditor BuildGlobalLayoutEditEditor()\n {\n Button button = BuildCommandButton(\"全局编辑\", false);\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenGlobalLayoutEditorFromSettings(); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(GlobalLayoutEditCommandName);\n card.HintLabel.Text = GetSettingHint(GlobalLayoutEditCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(GlobalLayoutEditCommandName, card, button);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n if (property.Name.EndsWith(\"RenderVariant\", StringComparison.Ordinal) &&\n !property.Name.StartsWith(\"ClaudeRadar\", StringComparison.Ordinal))\n {\n return BuildVariantPicker(property);\n }\n\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildCodexRadarModelCombo();\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildClaudeRadarModelGridSelector();\n }\n\n if (type == typeof(string) && IsDisplayDeviceSetting(property.Name))\n {\n return BuildDisplayDeviceCombo();\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n if (string.Equals(property.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(GetDeepSeekApiKeyButtonText(), false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate { OpenDeepSeekApiKeyDialog(button); };\n return button;\n }\n\n if (string.Equals(property.Name, \"CodexRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Codex Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (string.Equals(property.Name, \"ClaudeRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Claude Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (type == typeof(int) && property.Name.EndsWith(\"RefreshToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即刷新\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"刷新请求已发送。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n private Control BuildVariantPicker(PropertyInfo property)\n {\n VariantPicker picker = new VariantPicker(property.Name, property.PropertyType, GetUiFont(8.8f, FontStyle.Bold), GetUiFont(8.0f));\n picker.Width = 560;\n picker.ValueChanged += delegate { OnSettingChanged(); };\n picker.PreferredHeightChanged += delegate { RelayoutSettingGroup(picker); };\n return picker;\n }\n\n private Control BuildDisplayDeviceCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n combo.Items.Add(new DisplayOption(string.Empty, \"主显示器/自动\"));\n Screen[] screens = Screen.AllScreens;\n for (int i = 0; i < screens.Length; i++)\n {\n Screen screen = screens[i];\n Rectangle bounds = screen.Bounds;\n string label = string.Format(\n CultureInfo.InvariantCulture,\n \"{0} {1}x{2} @ {3},{4}{5}\",\n screen.DeviceName,\n bounds.Width,\n bounds.Height,\n bounds.Left,\n bounds.Top,\n screen.Primary ? \" 主屏\" : string.Empty);\n combo.Items.Add(new DisplayOption(screen.DeviceName, label));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildCodexRadarModelCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n\n List<CodexRadarModelInfo> models = CodexRadarModelCatalog.LoadModels();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n CodexRadarModelInfo model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = CodexRadarModelCatalog.NormalizeModelKey(model.Key);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n combo.Items.Add(new ModelOption(\n key,\n CodexRadarModelCatalog.GetDisplayLabel(model.Label, key),\n model.Available,\n false));\n keys.Add(key);\n }\n\n string defaultKey = CodexRadarModelCatalog.DefaultModelKey;\n if (!keys.Contains(defaultKey))\n {\n combo.Items.Insert(0, new ModelOption(\n defaultKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, defaultKey),\n true,\n false));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildClaudeRadarModelGridSelector()\n {\n Panel panel = new Panel();\n panel.Width = 400;\n panel.BackColor = Color.Transparent;\n panel.Resize += delegate { LayoutClaudeRadarModelPanel(panel); };\n\n FlowLayoutPanel grid = new FlowLayoutPanel();\n grid.Name = ClaudeRadarModelGridName;\n grid.Width = panel.Width;\n grid.Location = new Point(0, 0);\n grid.Margin = new Padding(0);\n grid.Padding = new Padding(0);\n grid.FlowDirection = FlowDirection.LeftToRight;\n grid.WrapContents = true;\n grid.AutoSize = false;\n grid.BackColor = Color.Transparent;\n panel.Controls.Add(grid);\n\n Button edit = BuildCommandButton(\"编辑映射\", false);\n edit.Width = panel.Width;\n edit.Height = 54;\n edit.Click += delegate\n {\n using (ClaudeRadarModelMapEditorForm dialog = new ClaudeRadarModelMapEditorForm())\n {\n if (dialog.ShowDialog(this) == DialogResult.OK)\n {\n string selected = GetClaudeRadarModelGridValue(grid);\n PopulateClaudeRadarModelGrid(grid, selected);\n LayoutClaudeRadarModelPanel(panel);\n RelayoutSettingGroup(panel);\n ShowStatus(\"Claude Radar 模型映射已保存。\", false);\n OnSettingChanged();\n }\n }\n };\n panel.Controls.Add(edit);\n PopulateClaudeRadarModelGrid(grid, string.Empty);\n LayoutClaudeRadarModelPanel(panel);\n return panel;\n }\n\n private void PopulateClaudeRadarModelGrid(FlowLayoutPanel grid, string selectedKey)\n {\n if (grid == null)\n {\n return;\n }\n\n string normalizedSelected = WidgetSettings.NormalizeClaudeRadarModelKey(selectedKey);\n List<ClaudeModelOption> options = BuildClaudeRadarModelOptions(normalizedSelected);\n bool selectedExists = false;\n for (int i = 0; i < options.Count; i++)\n {\n if (string.Equals(options[i].Key, normalizedSelected, StringComparison.OrdinalIgnoreCase))\n {\n selectedExists = true;\n break;\n }\n }\n\n if (!selectedExists)\n {\n normalizedSelected = string.Empty;\n }\n\n grid.Tag = normalizedSelected;\n grid.SuspendLayout();\n try\n {\n while (grid.Controls.Count > 0)\n {\n Control old = grid.Controls[0];\n grid.Controls.RemoveAt(0);\n old.Dispose();\n }\n\n int slots = Math.Max(\n ClaudeRadarModelGridColumns,\n ((options.Count + ClaudeRadarModelGridColumns - 1) / ClaudeRadarModelGridColumns) * ClaudeRadarModelGridColumns);\n for (int i = 0; i < slots; i++)\n {\n ClaudeModelOption option = i < options.Count ? options[i] : null;\n Button button = BuildClaudeRadarModelButton(option);\n button.Margin = new Padding(0, 0, i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap, ClaudeRadarModelButtonGap);\n grid.Controls.Add(button);\n }\n\n ApplyClaudeRadarModelGridSelection(grid);\n int rows = Math.Max(1, slots / ClaudeRadarModelGridColumns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n }\n finally\n {\n grid.ResumeLayout();\n }\n }\n\n private List<ClaudeModelOption> BuildClaudeRadarModelOptions(string selectedKey)\n {\n List<ClaudeModelOption> options = new List<ClaudeModelOption>();\n options.Add(new ClaudeModelOption(string.Empty, \"自动\", true, false));\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n keys.Add(string.Empty);\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n string status = (model.Status ?? string.Empty).Trim();\n bool deleted = string.Equals(status, \"deleted\", StringComparison.OrdinalIgnoreCase);\n if (deleted && !string.Equals(key, selectedKey, StringComparison.OrdinalIgnoreCase))\n {\n continue;\n }\n\n bool pending = string.Equals(status, \"pending\", StringComparison.OrdinalIgnoreCase);\n bool temporarilyMissing = string.Equals(status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase);\n bool available = model.Enabled &&\n !deleted &&\n !pending &&\n !temporarilyMissing &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n options.Add(new ClaudeModelOption(\n key,\n label,\n available,\n pending || temporarilyMissing));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (!string.IsNullOrEmpty(selectedKey) && !keys.Contains(selectedKey))\n {\n options.Add(new ClaudeModelOption(selectedKey, selectedKey, false, true));\n }\n\n return options;\n }\n\n private Button BuildClaudeRadarModelButton(ClaudeModelOption option)\n {\n Button button = new Button();\n button.Width = ClaudeRadarModelButtonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Tag = option;\n button.Text = option == null ? \"--\" : GetClaudeRadarModelButtonText(option);\n button.AutoEllipsis = true;\n button.TextAlign = ContentAlignment.MiddleCenter;\n button.FlatStyle = FlatStyle.Flat;\n button.UseVisualStyleBackColor = false;\n button.FlatAppearance.BorderSize = 1;\n button.Font = GetUiFont(8.8f, FontStyle.Bold);\n button.Cursor = option != null && option.Available ? Cursors.Hand : Cursors.Default;\n button.Enabled = option != null && option.Available;\n button.Click += delegate\n {\n ClaudeModelOption clicked = button.Tag as ClaudeModelOption;\n if (clicked == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(button.Parent);\n if (grid == null)\n {\n return;\n }\n\n string current = GetClaudeRadarModelGridValue(grid);\n if (string.Equals(current, clicked.Key, StringComparison.OrdinalIgnoreCase))\n {\n return;\n }\n\n grid.Tag = clicked.Key;\n ApplyClaudeRadarModelGridSelection(grid);\n OnSettingChanged();\n };\n return button;\n }\n\n private static string GetClaudeRadarModelButtonText(ClaudeModelOption option)\n {\n if (option == null)\n {\n return \"--\";\n }\n\n if (option.Key.Length == 0)\n {\n return \"自动\";\n }\n\n string label = option.Label ?? option.Key;\n if (label.Length <= 7)\n {\n return label;\n }\n\n return label.Substring(0, 7);\n }\n\n private void ApplyClaudeRadarModelGridSelection(FlowLayoutPanel grid)\n {\n if (grid == null)\n {\n return;\n }\n\n string selectedKey = WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n bool selected = option != null &&\n string.Equals(option.Key, selectedKey, StringComparison.OrdinalIgnoreCase);\n ApplyClaudeRadarModelButtonStyle(button, option, selected);\n }\n }\n\n private static void ApplyClaudeRadarModelButtonStyle(Button button, ClaudeModelOption option, bool selected)\n {\n if (button == null)\n {\n return;\n }\n\n if (option == null)\n {\n button.BackColor = ControlBg;\n button.ForeColor = TextTertiary;\n button.FlatAppearance.BorderColor = ControlBorder;\n return;\n }\n\n if (selected)\n {\n button.BackColor = DesignTokens.SettingsWarmTheme.Accent;\n button.ForeColor = Color.Black;\n button.FlatAppearance.BorderColor = DesignTokens.SettingsWarmTheme.AccentHover;\n return;\n }\n\n button.BackColor = option.Available ? DesignTokens.SettingsWarmTheme.ButtonRest : MicaLayer;\n button.ForeColor = option.Available ? TextSecondary : TextTertiary;\n button.FlatAppearance.BorderColor = option.Pending ? DesignTokens.Colors.Warning : ControlBorder;\n }\n\n private static string GetClaudeRadarModelGridValue(FlowLayoutPanel grid)\n {\n return grid == null ? string.Empty : WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n }\n\n private void SetClaudeRadarModelGridValue(FlowLayoutPanel grid, string value)\n {\n PopulateClaudeRadarModelGrid(grid, value);\n if (grid != null && grid.Parent != null)\n {\n LayoutClaudeRadarModelPanel(grid.Parent);\n }\n }\n\n private static FlowLayoutPanel FindClaudeRadarModelGrid(Control control)\n {\n FlowLayoutPanel grid = control as FlowLayoutPanel;\n if (grid != null && string.Equals(grid.Name, ClaudeRadarModelGridName, StringComparison.Ordinal))\n {\n return grid;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n grid = FindClaudeRadarModelGrid(control.Controls[i]);\n if (grid != null)\n {\n return grid;\n }\n }\n }\n\n return null;\n }\n\n private static void LayoutClaudeRadarModelPanel(Control panel)\n {\n if (panel == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(panel);\n Button edit = null;\n for (int i = 0; i < panel.Controls.Count; i++)\n {\n Button candidate = panel.Controls[i] as Button;\n if (candidate != null && string.Equals(candidate.Text, \"编辑映射\", StringComparison.Ordinal))\n {\n edit = candidate;\n break;\n }\n }\n\n int y = 0;\n if (grid != null)\n {\n grid.Location = new Point(0, 0);\n grid.Width = panel.Width;\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n button.Width = buttonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Margin = new Padding(\n 0,\n 0,\n i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap,\n ClaudeRadarModelButtonGap);\n }\n\n y = grid.Bottom + 10;\n }\n\n if (edit != null)\n {\n edit.Location = new Point(0, y);\n edit.Width = panel.Width;\n y = edit.Bottom;\n }\n\n panel.Height = Math.Max(54, y);\n }\n\n private static int GetClaudeRadarModelButtonWidthForGrid(int gridWidth)\n {\n int gaps = (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n int usable = Math.Max(ClaudeRadarModelGridColumns, gridWidth - gaps);\n return Math.Max(32, usable / ClaudeRadarModelGridColumns);\n }\n\n private static void RelayoutSettingGroup(Control control)\n {\n Control parent = control;\n while (parent != null)\n {\n SettingGroupCard card = parent as SettingGroupCard;\n if (card != null)\n {\n card.LayoutRows();\n return;\n }\n\n parent = parent.Parent;\n }\n }\n\n private static ComboBox FindComboBox(Control control)\n {\n ComboBox combo = control as ComboBox;\n if (combo != null)\n {\n return combo;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n combo = FindComboBox(control.Controls[i]);\n if (combo != null)\n {\n return combo;\n }\n }\n }\n\n return null;\n }\n\n private void PopulateClaudeRadarModelCombo(ComboBox combo)\n {\n if (combo == null)\n {\n return;\n }\n\n combo.Items.Clear();\n combo.Items.Add(new ClaudeModelOption(string.Empty, \"自动选择\", true, false));\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n bool available = model.Enabled &&\n !string.Equals(model.Status, \"deleted\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase) &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n combo.Items.Add(new ClaudeModelOption(\n key,\n label,\n available,\n string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase)));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (combo.Items.Count > 0 && combo.SelectedIndex < 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static object GetClaudeRadarModelComboValue(ComboBox combo)\n {\n ClaudeModelOption option = combo == null ? null : combo.SelectedItem as ClaudeModelOption;\n return option == null ? string.Empty : option.Key;\n }\n\n private static void SetClaudeRadarModelComboValue(ComboBox combo, string value)\n {\n if (combo == null)\n {\n return;\n }\n\n string modelKey = WidgetSettings.NormalizeClaudeRadarModelKey(value);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ClaudeModelOption option = combo.Items[i] as ClaudeModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(modelKey))\n {\n combo.Items.Add(new ClaudeModelOption(modelKey, modelKey, false, true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static bool IsDisplayDeviceSetting(string propertyName)\n {\n return propertyName != null &&\n propertyName.EndsWith(\"DisplayDeviceName\", StringComparison.Ordinal);\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n EnsureVariantSamplesForPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void EnsureVariantSamplesForPage(CategoryPage page)\n {\n if (this.owner == null || page == null)\n {\n return;\n }\n\n string directory = GetVariantSampleDirectory();\n for (int i = 0; i < page.Editors.Count; i++)\n {\n SettingEditor editor = page.Editors[i];\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker == null)\n {\n continue;\n }\n\n string prefix = GetVariantSamplePrefix(editor.Name);\n if (prefix.Length == 0)\n {\n continue;\n }\n\n try\n {\n if (picker.HasMissingSamples(directory, prefix))\n {\n System.IO.Directory.CreateDirectory(directory);\n RenderVariantSamplesForProperty(editor.Name, directory);\n }\n\n picker.LoadSamples(directory, prefix);\n RelayoutSettingGroup(picker);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n picker.MarkSamplesUnavailable();\n }\n }\n }\n\n private static string GetVariantSampleDirectory()\n {\n return System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName,\n \"variant-samples\",\n \"v\" + ProductIdentity.Version);\n }\n\n private static string GetVariantSamplePrefix(string propertyName)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal)) return \"widget\";\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal)) return \"codexradar\";\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal)) return \"networkmonitor\";\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal)) return \"powerthermal\";\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal)) return \"operation\";\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal)) return \"connectioncheck\";\n return string.Empty;\n }\n\n private static void RenderVariantSamplesForProperty(string propertyName, string directory)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal))\n {\n WidgetForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal))\n {\n CodexRadarForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal))\n {\n NetworkMonitorForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal))\n {\n PowerThermalForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal))\n {\n OperationForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal))\n {\n ConnectionCheckForm.RenderVariantSamples(directory);\n }\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Width = width;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n\n SetDirtyState(false);\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private bool TrySaveSettings()\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n SetDirtyState(false);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n return false;\n }\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n SetClaudeRadarModelGridValue(grid, value as string);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n picker.SetSelectedValue(value);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n string displayDeviceName = value as string;\n if (string.Equals(editor.Property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n string modelKey = CodexRadarModelCatalog.NormalizeModelKey(value as string);\n if (modelKey.Length == 0)\n {\n modelKey = CodexRadarModelCatalog.DefaultModelKey;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ModelOption option = combo.Items[i] as ModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n combo.Items.Add(new ModelOption(\n modelKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, modelKey),\n false,\n true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n SetClaudeRadarModelComboValue(combo, value as string);\n return;\n }\n\n if (IsDisplayDeviceSetting(editor.Property.Name))\n {\n displayDeviceName = WidgetSettings.NormalizeDisplayDeviceName(displayDeviceName);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n DisplayOption option = combo.Items[i] as DisplayOption;\n if (option != null && string.Equals(option.Value, displayDeviceName, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(displayDeviceName))\n {\n combo.Items.Add(new DisplayOption(displayDeviceName, \"未连接 \" + displayDeviceName));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n button.Tag = value is int ? (int)value : 0;\n if (string.Equals(editor.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n button.Text = GetDeepSeekApiKeyButtonText();\n }\n }\n }\n\n private void ApplyResponsiveShellLayout()\n {\n int navWidth = GetNavigationWidthForClientWidth(this.ClientSize.Width);\n if (this.bodyLayout != null && this.bodyLayout.ColumnStyles.Count > 0)\n {\n this.bodyLayout.ColumnStyles[0].Width = navWidth;\n }\n\n if (this.navigationPanel != null)\n {\n int itemWidth = Math.Max(160, navWidth - 24);\n for (int i = 0; i < this.navigationPanel.Controls.Count; i++)\n {\n this.navigationPanel.Controls[i].Width = itemWidth;\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n if (editor.Property == null)\n {\n return null;\n }\n\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n return GetClaudeRadarModelGridValue(grid);\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n return picker.GetSelectedValue();\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n ClaudeModelOption claudeModelOption = combo.SelectedItem as ClaudeModelOption;\n if (claudeModelOption != null)\n {\n return claudeModelOption.Key;\n }\n\n ModelOption modelOption = combo.SelectedItem as ModelOption;\n if (modelOption != null)\n {\n return modelOption.Key;\n }\n\n DisplayOption displayOption = combo.SelectedItem as DisplayOption;\n if (displayOption != null)\n {\n return displayOption.Value;\n }\n\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n return button.Tag is int ? (int)button.Tag : 0;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n SetDirtyState(true);\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n private void OpenGlobalLayoutEditorFromSettings()\n {\n if (this.owner == null)\n {\n return;\n }\n\n this.previewTimer.Stop();\n WidgetSettings settings = ReadSettings();\n WidgetSettings editedSettings;\n if (this.owner.TryEditGlobalLayout(settings, out editedSettings))\n {\n this.baseline = editedSettings.Clone();\n this.baseline.Normalize();\n LoadSettings(this.baseline);\n this.saved = true;\n ShowStatus(\"全局布局已保存\", false);\n return;\n }\n\n this.owner.PreviewSettings(ReadSettings());\n ShowStatus(\"全局编辑已取消\", false);\n }\n\n private void OpenDeepSeekApiKeyDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"DeepSeek 配置\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.ClientSize = new Size(560, 238);\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n Label title = new Label();\n title.Text = \"DeepSeek API Key\";\n title.Font = GetUiFont(12.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(24, 20);\n title.Size = new Size(500, 30);\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label hint = new Label();\n hint.Text = HasDeepSeekEnvironmentApiKeyForUi()\n ? \"检测到 DEEPSEEK_API_KEY 环境变量;环境变量会优先于本地文件。\"\n : \"密钥保存到本地应用数据目录,不写入 settings.ini 或日志。\";\n hint.Font = GetUiFont(8.8f);\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(24, 56);\n hint.Size = new Size(500, 42);\n hint.TextAlign = ContentAlignment.MiddleLeft;\n\n TextBox keyBox = new TextBox();\n keyBox.Location = new Point(24, 104);\n keyBox.Size = new Size(512, 34);\n keyBox.BackColor = ControlBg;\n keyBox.ForeColor = TextSecondary;\n keyBox.BorderStyle = BorderStyle.FixedSingle;\n keyBox.Font = GetUiFont(9.5f);\n keyBox.UseSystemPasswordChar = true;\n keyBox.Text = ReadDeepSeekApiKeyFileForUi();\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(24, 166);\n clear.Width = 112;\n clear.Height = 44;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Location = new Point(302, 166);\n cancel.Width = 112;\n cancel.Height = 44;\n cancel.Click += delegate { dialog.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Location = new Point(424, 166);\n save.Width = 112;\n save.Height = 44;\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(string.Empty, out errorCode))\n {\n keyBox.Text = string.Empty;\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已清除\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置清除失败 \" + errorCode, true);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(keyBox.Text, out errorCode))\n {\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已保存\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置保存失败 \" + errorCode, true);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(keyBox);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void IncrementDeepSeekApiKeyRevision(Button sourceButton)\n {\n if (sourceButton != null)\n {\n int token = sourceButton.Tag is int ? (int)sourceButton.Tag : 0;\n sourceButton.Tag = token == int.MaxValue ? 1 : token + 1;\n sourceButton.Text = GetDeepSeekApiKeyButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static string GetDeepSeekApiKeyButtonText()\n {\n return IsDeepSeekApiKeyConfiguredForUi() ? \"修改配置\" : \"配置\";\n }\n\n private static bool IsDeepSeekApiKeyConfiguredForUi()\n {\n if (HasDeepSeekEnvironmentApiKeyForUi())\n {\n return true;\n }\n\n return ReadDeepSeekApiKeyFileForUi().Length > 0;\n }\n\n private static bool HasDeepSeekEnvironmentApiKeyForUi()\n {\n try\n {\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable)))\n {\n return true;\n }\n\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.User)))\n {\n return true;\n }\n\n return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine));\n }\n catch\n {\n return false;\n }\n }\n\n private static string ReadDeepSeekApiKeyFileForUi()\n {\n try\n {\n string secret;\n bool migrated;\n string errorCode;\n return SecretStore.TryReadOrMigrateSecret(\n CodexRadarForm.DeepSeekApiKeyPath,\n CodexRadarForm.LegacyDeepSeekApiKeyPath,\n SecretStore.TrimSecret,\n out secret,\n out migrated,\n out errorCode)\n ? secret\n : string.Empty;\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static bool TrySaveDeepSeekApiKeyFile(string apiKey, out string errorCode)\n {\n errorCode = string.Empty;\n try\n {\n string trimmed = (apiKey ?? string.Empty).Trim();\n if (trimmed.Length == 0)\n {\n SecretStore.DeleteSecretFiles(CodexRadarForm.DeepSeekApiKeyPath, CodexRadarForm.LegacyDeepSeekApiKeyPath);\n return true;\n }\n\n SecretStore.WriteSecret(CodexRadarForm.DeepSeekApiKeyPath, trimmed);\n SecretStore.DeleteLegacySecretFiles(CodexRadarForm.LegacyDeepSeekApiKeyPath);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n return false;\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void SetDirtyState(bool hasChanges)\n {\n this.dirty = hasChanges;\n if (this.statusLabel == null)\n {\n return;\n }\n\n if (hasChanges)\n {\n this.statusTimer.Stop();\n this.statusLabel.Text = \"有未保存的更改\";\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.Visible = true;\n }\n else if (!this.statusTimer.Enabled)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n if (this.dirty)\n {\n SetDirtyState(true);\n }\n else\n {\n this.statusLabel.Visible = false;\n }\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n bool searching = query.Length > 0;\n int firstMatchingPageIndex = -1;\n bool selectedPageHasVisibleRows = false;\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n if (page.AdvancedHeader != null)\n {\n // While searching, advanced rows surface directly, so the collapse header hides.\n page.AdvancedHeader.Visible = !searching;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool allowed = !group.Advanced || page.AdvancedExpanded || searching;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = allowed && (pageMatch || groupTitleMatch ||\n editor.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n\n if (searching && anyVisible)\n {\n if (firstMatchingPageIndex < 0)\n {\n firstMatchingPageIndex = i;\n }\n\n if (i == this.selectedPageIndex)\n {\n selectedPageHasVisibleRows = true;\n }\n }\n }\n\n if (searching &&\n !selectedPageHasVisibleRows &&\n firstMatchingPageIndex >= 0 &&\n firstMatchingPageIndex != this.selectedPageIndex)\n {\n SelectPage(firstMatchingPageIndex);\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : string.Empty;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return 1;\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return new NumericRange(\n WidgetSettings.MinCodexRadarManualElementOffsetPixels,\n WidgetSettings.MaxCodexRadarManualElementOffsetPixels);\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static bool IsCodexRadarManualElementOffsetSetting(string name)\n {\n return name != null &&\n name.StartsWith(\"CodexRadar\", StringComparison.OrdinalIgnoreCase) &&\n (name.EndsWith(\"OffsetX\", StringComparison.OrdinalIgnoreCase) ||\n name.EndsWith(\"OffsetY\", StringComparison.OrdinalIgnoreCase));\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private void ScheduleDynamicResolutionSizing()\n {\n if (!this.IsHandleCreated)\n {\n ApplyDynamicResolutionSizing(false);\n return;\n }\n\n try\n {\n this.BeginInvoke((MethodInvoker)delegate\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n });\n }\n catch\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n }\n }\n\n private void ApplyDynamicResolutionSizing(bool usePreferredSize)\n {\n Rectangle workArea = GetTargetWorkArea();\n Size minimum = GetMinimumWindowSizeForWorkArea(workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n\n this.MinimumSize = minimum;\n this.MaximumSize = new Size(\n Math.Max(minimum.Width, Math.Max(1, workArea.Width)),\n Math.Max(minimum.Height, Math.Max(1, workArea.Height)));\n\n Size target = usePreferredSize || this.ClientSize.Width <= 0 || this.ClientSize.Height <= 0\n ? preferred\n : this.ClientSize;\n target = new Size(\n Clamp(target.Width, minimum.Width, preferred.Width),\n Clamp(target.Height, minimum.Height, preferred.Height));\n\n if (this.ClientSize != target)\n {\n this.ClientSize = target;\n }\n\n if (this.Visible || this.IsHandleCreated)\n {\n ClampWindowToWorkArea(workArea);\n }\n\n ApplyResponsiveShellLayout();\n }\n\n private Rectangle GetTargetWorkArea()\n {\n Rectangle workArea;\n if (this.owner != null && !this.owner.IsDisposed)\n {\n workArea = Screen.FromControl(this.owner).WorkingArea;\n }\n else if (this.IsHandleCreated)\n {\n workArea = Screen.FromHandle(this.Handle).WorkingArea;\n }\n else\n {\n workArea = Screen.PrimaryScreen.WorkingArea;\n }\n\n EnsureUsableWorkArea(ref workArea);\n return workArea;\n }\n\n private void ClampWindowToWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int left = Clamp(this.Left, workArea.Left, Math.Max(workArea.Left, workArea.Right - this.Width));\n int top = Clamp(this.Top, workArea.Top, Math.Max(workArea.Top, workArea.Bottom - this.Height));\n if (left != this.Left || top != this.Top)\n {\n this.Location = new Point(left, top);\n }\n }\n\n private static Size GetPreferredClientSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Clamp(\n (int)Math.Round(workArea.Width * (PreferredClientWidth / (double)ReferenceWorkAreaWidth), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientWidth, Math.Max(1, workArea.Width)),\n PreferredClientWidth);\n int height = Clamp(\n (int)Math.Round(workArea.Height * (PreferredClientHeight / (double)ReferenceWorkAreaHeight), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientHeight, Math.Max(1, workArea.Height)),\n PreferredClientHeight);\n return FitClientSizeToWorkArea(new Size(width, height), workArea);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n return FitClientSizeToWorkArea(desiredSize, Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size FitClientSizeToWorkArea(Size desiredSize, Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Math.Min(desiredSize.Width, Math.Max(MinimumClientWidth, workArea.Width - ScreenMargin));\n int height = Math.Min(desiredSize.Height, Math.Max(MinimumClientHeight, workArea.Height - ScreenMargin));\n width = Math.Min(width, Math.Max(1, workArea.Width));\n height = Math.Min(height, Math.Max(1, workArea.Height));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n return GetMinimumWindowSizeForWorkArea(Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size GetMinimumWindowSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n return new Size(\n Math.Min(preferred.Width, Math.Max(Math.Min(MinimumClientWidth, workArea.Width), workArea.Width - 128)),\n Math.Min(preferred.Height, Math.Max(Math.Min(MinimumClientHeight, workArea.Height), workArea.Height - 128)));\n }\n\n private static int GetNavigationWidthForClientWidth(int clientWidth)\n {\n if (clientWidth <= 960)\n {\n return CompactNavWidth;\n }\n\n if (clientWidth <= 1280)\n {\n return MediumNavWidth;\n }\n\n return DefaultNavWidth;\n }\n\n private static void EnsureUsableWorkArea(ref Rectangle workArea)\n {\n if (workArea.Width > 0 && workArea.Height > 0)\n {\n return;\n }\n\n workArea = Screen.PrimaryScreen.Bounds;\n if (workArea.Width <= 0 || workArea.Height <= 0)\n {\n workArea = new Rectangle(0, 0, ReferenceWorkAreaWidth, ReferenceWorkAreaHeight);\n }\n }\n\n private static int Clamp(int value, int minimum, int maximum)\n {\n if (maximum < minimum)\n {\n maximum = minimum;\n }\n\n if (value < minimum)\n {\n return minimum;\n }\n\n if (value > maximum)\n {\n return maximum;\n }\n\n return value;\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static string RunOpenCloseStressSelfTest(int iterations)\n {\n int loopCount = Math.Max(1, Math.Min(500, iterations));\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n baseline.CodexRadarEnabled = true;\n baseline.ClaudeRadarEnabled = true;\n baseline.Normalize();\n\n using (Win11SettingsForm warmup = new Win11SettingsForm(null, baseline))\n {\n warmup.StartPosition = FormStartPosition.Manual;\n warmup.Location = new Point(-30000, -30000);\n warmup.Show();\n Application.DoEvents();\n warmup.Close();\n Application.DoEvents();\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters before = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n for (int i = 0; i < loopCount; i++)\n {\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-30000, -30000);\n form.Show();\n Application.DoEvents();\n form.Close();\n Application.DoEvents();\n }\n\n if ((i + 1) % 25 == 0)\n {\n ForceResourceCleanup();\n }\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters after = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n int handleDelta = after.HandleCount - before.HandleCount;\n int gdiDelta = after.GdiObjects - before.GdiObjects;\n int userDelta = after.UserObjects - before.UserObjects;\n if (handleDelta > 80 || gdiDelta > 8 || userDelta > 16)\n {\n throw new InvalidOperationException(\n \"WinUI settings open/close resource growth exceeded threshold. Iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \", HandlesDelta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \", GdiDelta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \", UserDelta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture));\n }\n\n return \"Settings open/close policy: PASS iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \" handles_delta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \" gdi_delta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \" user_delta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture);\n }\n\n internal static void RunSettingsBindingSelfTest()\n {\n VerifySettingsWindowActivationPolicy();\n VerifyUnsavedPreviewConsumePolicy();\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.ShowInTaskbar = false;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n private static void VerifySettingsWindowActivationPolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n if (!form.ShowInTaskbar)\n {\n throw new InvalidOperationException(\"WinUI settings window must be visible in taskbar and Alt+Tab.\");\n }\n }\n }\n\n private static void VerifyUnsavedPreviewConsumePolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n WidgetSettings revertSettings;\n if (!form.TryConsumeUnsavedPreview(out revertSettings) ||\n revertSettings == null ||\n !object.Equals(revertSettings.OperationPrimaryPanelMode, baseline.OperationPrimaryPanelMode))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consume failed.\");\n }\n\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consumed twice.\");\n }\n }\n\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n WidgetSettings revertSettings;\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings owner-closing preview should not be consumed.\");\n }\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private static void ForceResourceCleanup()\n {\n GC.Collect();\n GC.WaitForPendingFinalizers();\n GC.Collect();\n Application.DoEvents();\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"FallbackDisconnectedDisplaysEnabled\",\n \"MainDisplayDeviceName\",\n \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\",\n GlobalLayoutEditCommandName,\n \"Width\",\n \"CodexRadarEnabled\",\n \"CodexRadarSoftwareMode\",\n \"CodexRadarModelKey\",\n \"RadarClockAutoSwitchModelEnabled\",\n \"RadarClockTimeDisplayMode\",\n \"CodexRadarHtmlFallbackEnabled\",\n \"CodexRadarServiceProbeToken\",\n \"ClaudeRadarEnabled\",\n \"ClaudeRadarModelKey\",\n \"ClaudeRadarJsonEnabled\",\n \"ClaudeRadarServiceProbeToken\",\n \"DeepSeekApiKeyRevision\",\n \"AiRequestProtectionAutoEnabled\",\n \"AiRequestProtectionManualBlockEnabled\",\n \"CodexQuotaPlanEnabled\",\n \"CodexQuotaPlanWeeklyComparison\",\n \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\",\n \"CodexQuotaPlanFiveHourThresholdPercent\",\n \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\",\n \"CodexRadarRenderVariant\",\n \"MainWidgetRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\",\n \"OperationPrimaryPanelMode\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyClaudeRadarModelGridPolicy();\n VerifyVariantPickerPolicy();\n VerifyDynamicResolutionSizingPolicy();\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyClaudeRadarModelGridPolicy()\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(\"ClaudeRadarModelKey\", out editor))\n {\n throw new InvalidOperationException(\"Claude Radar model grid editor missing.\");\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid == null)\n {\n throw new InvalidOperationException(\"Claude Radar model selector must use the five-column button grid.\");\n }\n\n if (grid.Controls.Count < ClaudeRadarModelGridColumns ||\n grid.Controls.Count % ClaudeRadarModelGridColumns != 0)\n {\n throw new InvalidOperationException(\"Claude Radar model grid slot count is not a multiple of five.\");\n }\n\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n int rowWidth = ClaudeRadarModelGridColumns * buttonWidth +\n (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n if (rowWidth > grid.Width)\n {\n throw new InvalidOperationException(\"Claude Radar model grid buttons exceed selector width.\");\n }\n\n bool sawAuto = false;\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n throw new InvalidOperationException(\"Claude Radar model grid contains a non-button slot.\");\n }\n\n if (button.Width != buttonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid button width is not responsive.\");\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n if (option == null)\n {\n if (button.Enabled || !string.Equals(button.Text, \"--\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar empty model slot must be disabled --.\");\n }\n continue;\n }\n\n if (option.Key.Length == 0)\n {\n sawAuto = true;\n if (!button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar automatic model slot must be selectable.\");\n }\n }\n else if (!option.Available && button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar unavailable model slot must be disabled.\");\n }\n }\n\n if (!sawAuto)\n {\n throw new InvalidOperationException(\"Claude Radar model grid missing automatic slot.\");\n }\n }\n\n private void VerifyVariantPickerPolicy()\n {\n string[] pickerNames = new string[]\n {\n \"MainWidgetRenderVariant\",\n \"CodexRadarRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\"\n };\n\n for (int i = 0; i < pickerNames.Length; i++)\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(pickerNames[i], out editor))\n {\n throw new InvalidOperationException(\"Render variant picker binding missing: \" + pickerNames[i]);\n }\n\n if (!(editor.Control is VariantPicker))\n {\n throw new InvalidOperationException(\"Render variant must use VariantPicker: \" + pickerNames[i]);\n }\n }\n\n SettingEditor claudeEditor;\n if (this.editors.TryGetValue(\"ClaudeRadarRenderVariant\", out claudeEditor) &&\n claudeEditor.Control is VariantPicker)\n {\n throw new InvalidOperationException(\"Claude Radar render variant must remain a dropdown until it has effect.\");\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n Size originalMinimum = this.MinimumSize;\n int originalPageIndex = this.selectedPageIndex;\n try\n {\n Size[] testSizes = new Size[]\n {\n this.MinimumSize,\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1280, 680)),\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1024, 728))\n };\n\n for (int sizeIndex = 0; sizeIndex < testSizes.Length; sizeIndex++)\n {\n this.MinimumSize = testSizes[sizeIndex];\n this.ClientSize = testSizes[sizeIndex];\n Application.DoEvents();\n VerifyNoVisibleControlClippingAtCurrentSize();\n }\n }\n finally\n {\n this.MinimumSize = originalMinimum;\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n private void VerifyNoVisibleControlClippingAtCurrentSize()\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Name);\n }\n }\n }\n }\n\n private static void VerifyDynamicResolutionSizingPolicy()\n {\n Rectangle[] workAreas = new Rectangle[]\n {\n new Rectangle(0, 60, 2880, 1740),\n new Rectangle(0, 0, 1920, 1040),\n new Rectangle(0, 0, 1600, 860),\n new Rectangle(0, 0, 1366, 728),\n new Rectangle(0, 0, 1280, 680),\n new Rectangle(0, 0, 1024, 728)\n };\n\n for (int i = 0; i < workAreas.Length; i++)\n {\n Size preferred = GetPreferredClientSizeForWorkArea(workAreas[i]);\n Size minimum = GetMinimumWindowSizeForWorkArea(workAreas[i]);\n if (preferred.Width > workAreas[i].Width ||\n preferred.Height > workAreas[i].Height ||\n minimum.Width > workAreas[i].Width ||\n minimum.Height > workAreas[i].Height ||\n minimum.Width > preferred.Width ||\n minimum.Height > preferred.Height)\n {\n throw new InvalidOperationException(\n \"WinUI settings dynamic resolution sizing failed: \" +\n workAreas[i].Width.ToString(CultureInfo.InvariantCulture) +\n \"x\" +\n workAreas[i].Height.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"ClaudeRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"ClaudeRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"CodexRadarManualLeftPercent\", new NumericRange(WidgetSettings.MinCodexRadarManualLeftPercent, WidgetSettings.MaxCodexRadarManualLeftPercent) },\n { \"CodexRadarManualGapPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualGapPixels, WidgetSettings.MaxCodexRadarManualGapPixels) },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualEfficiencyTextWidthPixels, WidgetSettings.MaxCodexRadarManualEfficiencyTextWidthPixels) },\n { \"CodexRadarManualQuotaRowsWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualQuotaRowsWidthPixels, WidgetSettings.MaxCodexRadarManualQuotaRowsWidthPixels) },\n { \"CodexRadarManualIqStatusWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualIqStatusWidthPixels, WidgetSettings.MaxCodexRadarManualIqStatusWidthPixels) },\n { \"CodexRadarManualTextScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualTextScalePercent, WidgetSettings.MaxCodexRadarManualTextScalePercent) },\n { \"CodexRadarManualRingScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualRingScalePercent, WidgetSettings.MaxCodexRadarManualRingScalePercent) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselineValidTasks\", new NumericRange(WidgetSettings.MinCodexModelIqValidTasks, WidgetSettings.MaxCodexModelIqValidTasks) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"OperationPrimaryPanelMode\", \"左侧区域模式\" },\n { \"OperationWindowsButtonEnabled\", \"显示 Windows 按钮\" },\n { \"OperationMemoryPieEnabled\", \"显示内存饼图\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"AiRequestProtectionAutoEnabled\", \"AI 自动阻断\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"AI 手动阻断\" },\n { \"CodexQuotaPlanEnabled\", \"启用额度计划\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度条件\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度阈值\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时条件\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时阈值\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"恢复额度类型\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"恢复上次暂停\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"断开后回退显示器\" },\n { \"MainDisplayDeviceName\", \"主窗口显示器\" },\n { \"CodexRadarDisplayDeviceName\", \"Codex Radar 显示器\" },\n { \"ClaudeRadarDisplayDeviceName\", \"Claude Radar 显示器\" },\n { \"PowerThermalDisplayDeviceName\", \"功耗温度显示器\" },\n { \"NetworkMonitorDisplayDeviceName\", \"网络监控显示器\" },\n { \"ConnectionCheckDisplayDeviceName\", \"连接检测显示器\" },\n { \"OperationDisplayDeviceName\", \"操作面板显示器\" },\n { GlobalLayoutEditCommandName, \"全局编辑\" },\n { \"Width\", \"主窗口宽度\" },\n { \"Height\", \"主窗口高度\" },\n { \"LeftX\", \"主窗口距左边\" },\n { \"BottomY\", \"主窗口距下边\" },\n { \"CodexRadarWidth\", \"Codex Radar 宽度\" },\n { \"CodexRadarHeight\", \"Codex Radar 高度\" },\n { \"CodexRadarLeftX\", \"Codex Radar 距左边\" },\n { \"CodexRadarBottomY\", \"Codex Radar 距下边\" },\n { \"PowerThermalWidth\", \"功耗温度宽度\" },\n { \"PowerThermalHeight\", \"功耗温度高度\" },\n { \"PowerThermalLeftX\", \"功耗温度距左边\" },\n { \"PowerThermalBottomY\", \"功耗温度距下边\" },\n { \"NetworkMonitorWidth\", \"网络监控宽度\" },\n { \"NetworkMonitorHeight\", \"网络监控高度\" },\n { \"NetworkMonitorLeftX\", \"网络监控距左边\" },\n { \"NetworkMonitorBottomY\", \"网络监控距下边\" },\n { \"ConnectionCheckWidth\", \"连接检测宽度\" },\n { \"ConnectionCheckHeight\", \"连接检测高度\" },\n { \"ConnectionCheckLeftX\", \"连接检测距左边\" },\n { \"ConnectionCheckBottomY\", \"连接检测距下边\" },\n { \"OperationLeftOffset\", \"操作面板距左边\" },\n { \"OperationBottomOffset\", \"操作面板距下边\" },\n { \"BackgroundTransparencyPercent\", \"主窗口背景透明度\" },\n { \"ApplicationTransparencyPercent\", \"主窗口整体透明度\" },\n { \"ShowCpu\", \"显示 CPU\" },\n { \"ShowMemory\", \"显示内存\" },\n { \"ShowDisk\", \"显示磁盘\" },\n { \"ShowNetwork\", \"显示网络\" },\n { \"ShowGpu\", \"显示 GPU\" },\n { \"ShowNpu\", \"显示 NPU\" },\n { \"HoverOpacityEnabled\", \"鼠标靠近时隐藏\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"触发距离(像素)\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarSoftwareMode\", \"监控哪个软件\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"时钟过期自动切模型\" },\n { \"RadarClockTimeDisplayMode\", \"时钟时间来源\" },\n { \"CodexRadarEnabled\", \"启用 Codex Radar\" },\n { \"CodexRadarTransparencyPercent\", \"Codex Radar 透明度\" },\n { \"ClaudeRadarEnabled\", \"启用 Claude Radar\" },\n { \"ClaudeRadarWidth\", \"Claude Radar 宽度\" },\n { \"ClaudeRadarHeight\", \"Claude Radar 高度\" },\n { \"ClaudeRadarLeftX\", \"Claude Radar 左侧 X\" },\n { \"ClaudeRadarBottomY\", \"Claude Radar 底部 Y\" },\n { \"ClaudeRadarTransparencyPercent\", \"Claude Radar 背景透明\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 模型\" },\n { \"ClaudeRadarJsonEnabled\", \"Claude Radar JSON\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"首页元数据回退\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"社区体感分\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"本地 7 天额度线回退\" },\n { \"ClaudeRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"ClaudeRadarRandomTestEnabled\", \"Claude 随机测试\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"Claude 随机测试自动刷新\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"ClaudeRadarRenderVariant\", \"外观风格\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"CodexRadarRenderVariant\", \"外观风格\" },\n { \"MainWidgetRenderVariant\", \"外观风格\" },\n { \"NetworkMonitorRenderVariant\", \"外观风格\" },\n { \"PowerThermalRenderVariant\", \"外观风格\" },\n { \"ConnectionCheckRenderVariant\", \"外观风格\" },\n { \"OperationRenderVariant\", \"外观风格\" },\n { \"CodexRadarManualLayoutEnabled\", \"启用手动布局\" },\n { \"CodexRadarManualLeftPercent\", \"左侧区域占比\" },\n { \"CodexRadarManualGapPixels\", \"模块间距\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"效率文字列宽\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"余额列宽\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"IQ 状态列宽\" },\n { \"CodexRadarManualTextScalePercent\", \"文字比例\" },\n { \"CodexRadarManualRingScalePercent\", \"圆环比例\" },\n { \"CodexRadarTimeEfficiencyRingOffsetX\", \"时间环 X\" },\n { \"CodexRadarTimeEfficiencyRingOffsetY\", \"时间环 Y\" },\n { \"CodexRadarTimeEfficiencyTextOffsetX\", \"时间字 X\" },\n { \"CodexRadarTimeEfficiencyTextOffsetY\", \"时间字 Y\" },\n { \"CodexRadarTokenEfficiencyRingOffsetX\", \"Token 环 X\" },\n { \"CodexRadarTokenEfficiencyRingOffsetY\", \"Token 环 Y\" },\n { \"CodexRadarTokenEfficiencyTextOffsetX\", \"Token 字 X\" },\n { \"CodexRadarTokenEfficiencyTextOffsetY\", \"Token 字 Y\" },\n { \"CodexRadarConnectionTopTextOffsetX\", \"连接上字 X\" },\n { \"CodexRadarConnectionTopTextOffsetY\", \"连接上字 Y\" },\n { \"CodexRadarConnectionLineOffsetX\", \"连接线 X\" },\n { \"CodexRadarConnectionLineOffsetY\", \"连接线 Y\" },\n { \"CodexRadarConnectionBottomTextOffsetX\", \"连接下字 X\" },\n { \"CodexRadarConnectionBottomTextOffsetY\", \"连接下字 Y\" },\n { \"CodexRadarFiveHourQuotaRingOffsetX\", \"5h 环 X\" },\n { \"CodexRadarFiveHourQuotaRingOffsetY\", \"5h 环 Y\" },\n { \"CodexRadarFiveHourQuotaTextOffsetX\", \"5h 字 X\" },\n { \"CodexRadarFiveHourQuotaTextOffsetY\", \"5h 字 Y\" },\n { \"CodexRadarWeeklyQuotaRingOffsetX\", \"周环 X\" },\n { \"CodexRadarWeeklyQuotaRingOffsetY\", \"周环 Y\" },\n { \"CodexRadarWeeklyQuotaTextOffsetX\", \"周字 X\" },\n { \"CodexRadarWeeklyQuotaTextOffsetY\", \"周字 Y\" },\n { \"CodexRadarQuotaRadarLineOffsetX\", \"额度线 X\" },\n { \"CodexRadarQuotaRadarLineOffsetY\", \"额度线 Y\" },\n { \"CodexRadarIqRingOffsetX\", \"IQ 环 X\" },\n { \"CodexRadarIqRingOffsetY\", \"IQ 环 Y\" },\n { \"CodexRadarIqTextOffsetX\", \"IQ 字 X\" },\n { \"CodexRadarIqTextOffsetY\", \"IQ 字 Y\" },\n { \"CodexRadarPublicJsonEnabled\", \"公开 JSON 摘要\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"首页 HTML 回退\" },\n { \"CodexRadarRssFallbackEnabled\", \"RSS 重置提醒\" },\n { \"CodexRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"DeepSeekApiKeyRevision\", \"DeepSeek 配置\" },\n { \"CodexModelIqTestEnabled\", \"用测试值代替实时 IQ(调试用)\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过数\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"IQ 基准自动跟随网站\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过数\" },\n { \"CodexModelIqBaselineValidTasks\", \"IQ 基准总题数\" },\n { \"CodexModelEfficiencyTestEnabled\", \"用测试值代替实时效率(调试用)\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"PowerThermalTransparencyPercent\", \"功耗温度透明度\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkMonitorTransparencyPercent\", \"网络监控透明度\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"立即刷新 GFW 检测\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测间隔\" },\n { \"ConnectionCheckTransparencyPercent\", \"连接检测透明度\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"连接检测边框透明度\" },\n { \"ConnectionCheckManualRefreshToken\", \"立即刷新连接检测\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"OperationButtonSize\", \"按钮大小\" },\n { \"OperationBackgroundTransparencyPercent\", \"操作面板透明度\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过数\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过数\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"ForceShowForegroundFpsEnabled\", \"调试用,强制显示前台 FPS 信息。\" },\n { \"AiRequestProtectionAutoEnabled\", \"网络监控判定为 GFW 明确阻断时,阻断本程序发往 OpenAI、ChatGPT、Claude 和 Anthropic 的请求。\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"手动启用后立即阻断本程序相关 AI 请求;也可在左下角程序设置按钮双击打开的 AI 快速选单中切换。\" },\n { \"CodexQuotaPlanEnabled\", \"按本地 Codex 剩余额度缓存自动暂停或恢复选中的 Codex goal。\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度百分比的比较方向;默认小于 3%。\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时额度百分比的比较方向;默认小于 90%。\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"额度计划触发后,选择额度恢复时看周额度、5 小时额度,还是两者都恢复。\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"额度恢复时优先恢复本程序上次因额度计划暂停的 goal;关闭后使用恢复列表。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityRevealDelaySeconds\", \"鼠标离开后继续隐藏的秒数。\" },\n { \"HoverOpacityRevealResetSeconds\", \"短时间重新靠近时,保持隐藏的重置秒数。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"反向隐藏临时恢复后,鼠标移开多久回到隐藏。\" },\n { \"AutoHoverOpacityIdleEnabled\", \"鼠标一段时间不动时自动进入隐藏透明度。\" },\n { \"AutoHoverOpacityIdleSeconds\", \"范围 1-300 秒,达到后自动隐藏。\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"有非 SeelenUI 应用最大化时自动进入隐藏透明度。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarSoftwareMode\", \"自动按前台窗口选择 CODEX 或 CLAUDE;也可强制固定检测软件。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"CodexRadarModelVersion\", \"模型目录版本,日常保持默认。\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"当 Codex/Claude 时钟跨过完整周期仍未获取当前模型 IQ 更新时,自动切到同站点当天最近刷新 IQ 的模型。\" },\n { \"RadarClockTimeDisplayMode\", \"控制 Codex/Claude Radar 时钟中心下方时间:UTC、当前本机时间、上次尝试刷新,或上次实际 IQ 刷新。\" },\n { \"CodexRadarPublicJsonEnabled\", \"读取 current.json 的公开摘要层,包含窗口、预测和 API 可用性说明。\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"当公开 JSON 不含 model_iq 时,从首页明文模块补齐 IQ、效率和模型目录。\" },\n { \"CodexRadarRssFallbackEnabled\", \"读取 feed.xml 的重置提醒;关闭后不会用 RSS 触发额度重置保护。\" },\n { \"CodexRadarServiceProbeToken\", \"点击后探测公开摘要、授权 API、首页 HTML 和 RSS,并写入本地诊断结果。\" },\n { \"CodexRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"CodexModelIqTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelIqTestPassed\", \"手动指定 IQ 测试通过数。\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"开启时自动读取网站有效题数和常态区推导 n/N;关闭后使用下方手动 n/N。\" },\n { \"CodexModelIqBaselineMode\", \"选择 IQ 对比基准的来源。\" },\n { \"CodexModelIqBaselinePassed\", \"手动指定 IQ 基准通过数 n。\" },\n { \"CodexModelIqBaselineValidTasks\", \"手动指定 IQ 基准总题数 N;仅在关闭自动基准时用于 IQ 环。\" },\n { \"CodexModelEfficiencyTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"手动指定 Token 效率测试百分比。\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"手动指定时间效率测试百分比。\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"选择 Token 效率对比基准的来源。\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"手动指定 Token 效率基准通过数。\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"手动指定 Token 效率基准消耗。\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"选择时间效率对比基准的来源。\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"手动指定时间效率基准通过数。\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"手动指定时间效率基准秒数。\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexRadarEnabled\", \"关闭后会释放 Codex Radar 分层窗口;默认开启以保持现有桌面不变。\" },\n { \"DisplayTimeZoneMode\", \"控制窗口里时间文字使用本机、北京时间还是指定时区。\" },\n { \"DisplayTimeZoneId\", \"Windows 时区 ID,指定时区模式下生效。\" },\n { \"ClaudeRadarEnabled\", \"开启独立 Claude Radar 分层窗口;默认关闭,避免升级后改变现有桌面。\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 站点内部 m* 模型键;留空时按站点当前目录自动选择。\" },\n { \"ClaudeRadarJsonEnabled\", \"读取 claudecoderadar.com/data/claude-code-radar.json 作为主数据源。\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"主 JSON 失败或缺少模型元数据时,顺序读取首页 MODEL_NAMES 作为弱回退;不会伪造 IQ、效率或额度。\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"读取 Claude Radar 社区体感分接口,并通过独立映射表关联模型。\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"站点额度趋势不完整时使用本地 7 天 JSONL 历史绘制额度线。\" },\n { \"ClaudeRadarServiceProbeToken\", \"点击后触发 Claude Radar 下次刷新,服务状态显示在窗口右侧。\" },\n { \"ClaudeRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"ClaudeRadarRenderVariant\", \"预留项,当前第一版固定使用 EvenRow 风格。\" },\n { \"MainWidgetRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"CodexRadarRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"NetworkMonitorRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"PowerThermalRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"ConnectionCheckRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"OperationRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"DeepSeekApiKeyRevision\", \"配置 DeepSeek API Key;密钥只写入本地文件,修订号用于触发即时刷新。\" },\n { \"CodexRadarManualLayoutEnabled\", \"开启后下方布局参数实时影响 Codex Radar 内部模块,不需要重启。\" },\n { \"CodexRadarManualLeftPercent\", \"调整效率/IQ/连接流程区与余额区的左右分配。\" },\n { \"CodexRadarManualGapPixels\", \"调整左侧区域和余额区之间的像素间距。\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"调整左侧效率/IQ 圆环右侧文字列宽。\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"调整余额两行圆环和重置时间的总宽度。\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"调整余额区右侧 IQ 圆环与降智文字列宽。\" },\n { \"CodexRadarManualTextScalePercent\", \"只影响 Codex Radar 内部状态文字和余额时间字号。\" },\n { \"CodexRadarManualRingScalePercent\", \"只影响 Codex Radar 内部效率、余额和 IQ 圆环大小。\" },\n { \"PowerThermalAutoSizeEnabled\", \"根据告警数量自动调整功耗温度窗口高度。\" },\n { \"PowerThermalAutoDirection\", \"自动调整时从哪个方向展开。\" },\n { \"PowerThermalVisibleAlertCount\", \"功耗温度窗口中最多显示的告警数量。\" },\n { \"ThermalTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"NetworkStatusTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测的自动刷新间隔。\" },\n { \"GfwProbeManualRefreshToken\", \"点击后请求立刻刷新 GFW 检测。\" },\n { \"ConnectionCheckManualRefreshToken\", \"点击后请求立刻刷新连接检测。\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测的自动刷新间隔。\" },\n { \"CloudEndpointTestSeed\", \"仅用于测试显示效果,日常保持默认。\" },\n { \"CloudStatusRegionMask\", \"云服务状态的地区过滤位掩码,日常保持默认。\" },\n { \"CleanIpBadgeTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"AlertTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"SeelenDockForegroundPulseEnabled\", \"需要时短暂拉前 Seelen Dock,避免被系统窗口压住。\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"指定显示器未连接时,将对应模块回退到当前主显示器。\" },\n { \"MainDisplayDeviceName\", \"留空使用当前主显示器;选择 DISPLAY 后按该显示器工作区定位。\" },\n { \"CodexRadarDisplayDeviceName\", \"留空使用当前主显示器;未连接且不回退时保留原显示器工作区。\" },\n { \"PowerThermalDisplayDeviceName\", \"留空使用当前主显示器;适合把功耗模块固定到副屏。\" },\n { \"NetworkMonitorDisplayDeviceName\", \"留空使用当前主显示器;适合把网络监控固定到副屏。\" },\n { \"ConnectionCheckDisplayDeviceName\", \"留空使用当前主显示器;适合把连接检测固定到副屏。\" },\n { \"OperationDisplayDeviceName\", \"留空使用当前主显示器;操作面板偏移量按目标显示器左下角计算。\" },\n { GlobalLayoutEditCommandName, \"打开全屏布局编辑遮罩,拖拽模块位置;Enter 保存,Esc 放弃。\" },\n { \"Width\", \"逻辑像素,主窗口宽度。\" },\n { \"Height\", \"逻辑像素,主窗口高度。\" },\n { \"LeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"BottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"CodexRadarWidth\", \"逻辑像素,Codex Radar 宽度。\" },\n { \"CodexRadarHeight\", \"逻辑像素,Codex Radar 高度。\" },\n { \"CodexRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"CodexRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ClaudeRadarWidth\", \"逻辑像素,Claude Radar 宽度。\" },\n { \"ClaudeRadarHeight\", \"逻辑像素,Claude Radar 高度。\" },\n { \"ClaudeRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ClaudeRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"PowerThermalWidth\", \"逻辑像素,功耗温度窗口宽度。\" },\n { \"PowerThermalHeight\", \"逻辑像素,功耗温度窗口高度。\" },\n { \"PowerThermalLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"PowerThermalBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"NetworkMonitorWidth\", \"逻辑像素,网络监控窗口宽度。\" },\n { \"NetworkMonitorHeight\", \"逻辑像素,网络监控窗口高度。\" },\n { \"NetworkMonitorLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"NetworkMonitorBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ConnectionCheckWidth\", \"逻辑像素,连接检测窗口宽度。\" },\n { \"ConnectionCheckHeight\", \"逻辑像素,连接检测窗口高度。\" },\n { \"ConnectionCheckLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ConnectionCheckBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"OperationLeftOffset\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"OperationBottomOffset\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"BackgroundTransparencyPercent\", \"只影响主窗口背景底色,数值越高越透明。\" },\n { \"ApplicationTransparencyPercent\", \"影响主窗口整体透明度,数值越高越透明。\" },\n { \"CodexRadarTransparencyPercent\", \"影响 Codex Radar 背景透明度。\" },\n { \"ClaudeRadarTransparencyPercent\", \"影响 Claude Radar 背景透明度。\" },\n { \"PowerThermalTransparencyPercent\", \"影响功耗温度窗口背景透明度。\" },\n { \"NetworkMonitorTransparencyPercent\", \"影响网络监控窗口背景透明度。\" },\n { \"ConnectionCheckTransparencyPercent\", \"影响连接检测窗口背景透明度。\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"影响连接检测三框边框透明度。\" },\n { \"OperationBackgroundTransparencyPercent\", \"影响左下角操作面板背景透明度。\" },\n { \"ShowCpu\", \"在主窗口显示 CPU 指标。\" },\n { \"ShowMemory\", \"在主窗口显示内存指标。\" },\n { \"ShowDisk\", \"在主窗口显示磁盘指标。\" },\n { \"ShowNetwork\", \"在主窗口显示网络指标。\" },\n { \"ShowGpu\", \"在主窗口显示 GPU 指标。\" },\n { \"ShowNpu\", \"在主窗口显示 NPU 指标。\" },\n { \"OperationPrimaryPanelMode\", \"自动模式会按 SeelenUI 运行态在 Windows 按钮和内存饼图之间切换;隐藏会让小按钮移到最左侧。\" },\n { \"OperationButtonSize\", \"左下角操作面板按钮的逻辑像素大小。\" },\n { \"OperationWindowsButtonEnabled\", \"SeelenUI 未运行时会自动隐藏;关闭后始终不显示左侧 Windows 按钮。\" },\n { \"OperationMemoryPieEnabled\", \"左侧 Windows 按钮隐藏时显示物理、虚拟和前台程序内存占用饼图。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.SettingsWarmTheme.ToggleTrackHover : DesignTokens.SettingsWarmTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsWarmTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsWarmTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsWarmTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = false;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = false;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + controlHeight + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(this.Width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, controlHeight);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n int controlTop = top + Math.Max(0, (contentHeight - controlHeight) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, controlHeight);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private int GetValueControlHeight(int controlWidth)\n {\n VariantPicker picker = this.valueControl as VariantPicker;\n if (picker != null)\n {\n return picker.GetPreferredHeightForWidth(controlWidth);\n }\n\n return this.valueControl.Height;\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── AdvancedSectionHeader ────────────────────────────────────────────\n // Clickable \"复杂选项\" divider: everything below it stays collapsed until\n // the user expands it, keeping everyday pages short. Searching bypasses it.\n private sealed class AdvancedSectionHeader : Panel\n {\n private readonly Font titleFont;\n private readonly Font hintFont;\n private bool expanded;\n private bool hover;\n\n public AdvancedSectionHeader(Font titleFont, Font hintFont)\n {\n this.titleFont = titleFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = MicaBase;\n this.Cursor = Cursors.Hand;\n }\n\n public int AdvancedRowCount { get; set; }\n\n public bool Expanded\n {\n get { return this.expanded; }\n set { this.expanded = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(this.hover ? CardHover : CardRest))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(StrokeColor))\n {\n g.DrawPath(pen, path);\n }\n\n // Chevron glyph: E70D = down, E76C = right (Segoe MDL2 / Fluent Icons).\n string chevron = this.expanded ? \"\\uE70D\" : \"\\uE76C\";\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(chevron, icoFont, brush, 20, iconY);\n }\n\n using (SolidBrush brush = new SolidBrush(TextPrimary))\n {\n g.DrawString(\"复杂选项\", this.titleFont, brush, 58, (this.Height - this.titleFont.Height) / 2f - 8);\n }\n\n string hint = this.expanded\n ? \"点击收起\"\n : \"不常用的精细调整与测试项\" + (this.AdvancedRowCount > 0 ? \"(\" + this.AdvancedRowCount.ToString(CultureInfo.InvariantCulture) + \" 项)\" : string.Empty);\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n g.DrawString(hint, this.hintFont, brush, 58, this.Height / 2f + 2);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── VariantPicker ───────────────────────────────────────────────────\n // Renders per-window visual variants as cards. Sample PNGs are loaded lazily by the outer form\n // only for real owner-backed settings windows, so binding self-tests never construct render forms.\n private sealed class VariantPicker : Panel\n {\n private const int CardHeight = 124;\n private const int CardGap = 10;\n private const int ThumbnailHeight = 78;\n\n private readonly Type enumType;\n private readonly Font labelFont;\n private readonly Font hintFont;\n private readonly List<VariantChoice> choices = new List<VariantChoice>();\n private object selectedValue;\n private int hoverIndex = -1;\n private bool samplesAttempted;\n\n public event EventHandler ValueChanged;\n public event EventHandler PreferredHeightChanged;\n\n public VariantPicker(string propertyName, Type enumType, Font labelFont, Font hintFont)\n {\n this.enumType = enumType;\n this.labelFont = labelFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n\n Array values = Enum.GetValues(enumType);\n for (int i = 0; i < values.Length; i++)\n {\n object value = values.GetValue(i);\n this.choices.Add(new VariantChoice(value, new EnumOption(value).ToString()));\n if (i == 0)\n {\n this.selectedValue = value;\n }\n }\n\n UpdatePreferredHeight();\n }\n\n public void SetSelectedValue(object value)\n {\n if (value == null || !this.enumType.IsInstanceOfType(value))\n {\n if (this.choices.Count > 0)\n {\n this.selectedValue = this.choices[0].Value;\n }\n }\n else\n {\n this.selectedValue = value;\n }\n\n this.Invalidate();\n }\n\n public object GetSelectedValue()\n {\n return this.selectedValue;\n }\n\n public int GetPreferredHeightForWidth(int width)\n {\n int columns = GetColumnCount(width);\n int rows = Math.Max(1, (this.choices.Count + columns - 1) / columns);\n return rows * CardHeight + Math.Max(0, rows - 1) * CardGap;\n }\n\n public bool HasMissingSamples(string directory, string prefix)\n {\n if (this.samplesAttempted)\n {\n return false;\n }\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n string path = GetSamplePath(directory, prefix, this.choices[i].Value);\n if (!System.IO.File.Exists(path))\n {\n return true;\n }\n }\n\n return false;\n }\n\n public void LoadSamples(string directory, string prefix)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n VariantChoice choice = this.choices[i];\n string path = GetSamplePath(directory, prefix, choice.Value);\n if (!System.IO.File.Exists(path))\n {\n continue;\n }\n\n try\n {\n using (Image original = Image.FromFile(path))\n {\n Image old = choice.Image;\n choice.Image = new Bitmap(original);\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n catch\n {\n Image old = choice.Image;\n choice.Image = null;\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n public void MarkSamplesUnavailable()\n {\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n UpdatePreferredHeight();\n }\n\n protected override void OnMouseMove(MouseEventArgs e)\n {\n int nextHover = GetChoiceIndexAt(e.Location);\n if (nextHover != this.hoverIndex)\n {\n this.hoverIndex = nextHover;\n this.Invalidate();\n }\n\n base.OnMouseMove(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hoverIndex = -1;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n protected override void OnMouseClick(MouseEventArgs e)\n {\n base.OnMouseClick(e);\n int index = GetChoiceIndexAt(e.Location);\n if (index < 0 || index >= this.choices.Count)\n {\n return;\n }\n\n object nextValue = this.choices[index].Value;\n if (object.Equals(this.selectedValue, nextValue))\n {\n return;\n }\n\n this.selectedValue = nextValue;\n this.Invalidate();\n if (this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.InterpolationMode = InterpolationMode.HighQualityBicubic;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n Rectangle rect = GetCardBounds(i);\n if (rect.Width <= 0 || rect.Height <= 0)\n {\n continue;\n }\n\n VariantChoice choice = this.choices[i];\n bool selected = object.Equals(choice.Value, this.selectedValue);\n bool hover = i == this.hoverIndex;\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(hover ? CardHover : ControlBg))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(selected ? AccentClr : ControlBorder, selected ? 2.0f : 1.0f))\n {\n g.DrawPath(pen, path);\n }\n\n Rectangle thumb = new Rectangle(rect.Left + 8, rect.Top + 8, rect.Width - 16, ThumbnailHeight);\n DrawChoiceThumbnail(g, choice, thumb);\n\n Rectangle labelRect = new Rectangle(rect.Left + 10, thumb.Bottom + 7, rect.Width - 20, 24);\n using (StringFormat format = new StringFormat())\n using (SolidBrush textBrush = new SolidBrush(selected ? TextPrimary : TextSecondary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(choice.Label, this.labelFont, textBrush, labelRect, format);\n }\n }\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (this.choices[i].Image != null)\n {\n this.choices[i].Image.Dispose();\n this.choices[i].Image = null;\n }\n }\n }\n\n base.Dispose(disposing);\n }\n\n private void DrawChoiceThumbnail(Graphics g, VariantChoice choice, Rectangle thumb)\n {\n using (GraphicsPath thumbPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n using (SolidBrush thumbBg = new SolidBrush(MicaBase))\n {\n g.FillPath(thumbBg, thumbPath);\n }\n\n if (choice.Image == null)\n {\n using (StringFormat format = new StringFormat())\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(\"纯文字模式\", this.hintFont, brush, thumb, format);\n }\n return;\n }\n\n Rectangle target = FitImage(choice.Image.Size, thumb);\n GraphicsState state = g.Save();\n using (GraphicsPath clipPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n {\n g.SetClip(clipPath);\n g.DrawImage(choice.Image, target);\n }\n g.Restore(state);\n }\n\n private void UpdatePreferredHeight()\n {\n int oldHeight = this.Height;\n int nextHeight = GetPreferredHeightForWidth(this.Width);\n if (nextHeight != oldHeight)\n {\n this.Height = nextHeight;\n if (this.PreferredHeightChanged != null)\n {\n this.PreferredHeightChanged(this, EventArgs.Empty);\n }\n }\n }\n\n private int GetColumnCount()\n {\n return GetColumnCount(this.Width);\n }\n\n private static int GetColumnCount(int width)\n {\n width = Math.Max(1, width);\n if (width >= 520)\n {\n return 3;\n }\n\n if (width >= 340)\n {\n return 2;\n }\n\n return 1;\n }\n\n private Rectangle GetCardBounds(int index)\n {\n int columns = GetColumnCount();\n int col = index % columns;\n int row = index / columns;\n int cardWidth = Math.Max(86, (Math.Max(1, this.Width) - (columns - 1) * CardGap) / columns);\n return new Rectangle(col * (cardWidth + CardGap), row * (CardHeight + CardGap), cardWidth, CardHeight);\n }\n\n private int GetChoiceIndexAt(Point point)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (GetCardBounds(i).Contains(point))\n {\n return i;\n }\n }\n\n return -1;\n }\n\n private static string GetSamplePath(string directory, string prefix, object value)\n {\n string variant = Convert.ToString(value, CultureInfo.InvariantCulture);\n return System.IO.Path.Combine(directory, prefix + \"-\" + variant.ToLowerInvariant() + \".png\");\n }\n\n private static Rectangle FitImage(Size imageSize, Rectangle bounds)\n {\n if (imageSize.Width <= 0 || imageSize.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0)\n {\n return bounds;\n }\n\n float sx = bounds.Width / (float)imageSize.Width;\n float sy = bounds.Height / (float)imageSize.Height;\n float scale = Math.Min(sx, sy);\n int width = Math.Max(1, (int)Math.Round(imageSize.Width * scale));\n int height = Math.Max(1, (int)Math.Round(imageSize.Height * scale));\n return new Rectangle(\n bounds.Left + (bounds.Width - width) / 2,\n bounds.Top + (bounds.Height - height) / 2,\n width,\n height);\n }\n\n private sealed class VariantChoice\n {\n public VariantChoice(object value, string label)\n {\n this.Value = value;\n this.Label = label ?? string.Empty;\n }\n\n public object Value;\n public string Label;\n public Image Image;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public AdvancedSectionHeader AdvancedHeader;\n public bool AdvancedExpanded;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public bool Advanced;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Name = property == null ? string.Empty : property.Name;\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public SettingEditor(string name, SettingRow card, Control control)\n {\n this.Name = name ?? string.Empty;\n this.Property = null;\n this.Card = card;\n this.Control = control;\n }\n\n public string Name { get; private set; }\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class ModelOption\n {\n public ModelOption(string key, string label, bool available, bool missingFromCatalog)\n {\n this.Key = CodexRadarModelCatalog.NormalizeModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.MissingFromCatalog = missingFromCatalog;\n }\n\n public string Key { get; private set; }\n private string Label { get; set; }\n private bool Available { get; set; }\n private bool MissingFromCatalog { get; set; }\n\n public override string ToString()\n {\n if (this.MissingFromCatalog)\n {\n return this.Label + \"(未在目录)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class ClaudeModelOption\n {\n public ClaudeModelOption(string key, string label, bool available, bool pending)\n {\n this.Key = WidgetSettings.NormalizeClaudeRadarModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.Pending = pending;\n }\n\n public string Key { get; private set; }\n public string Label { get; private set; }\n public bool Available { get; private set; }\n public bool Pending { get; private set; }\n\n public override string ToString()\n {\n if (this.Key.Length == 0)\n {\n return this.Label;\n }\n\n if (this.Pending)\n {\n return this.Label + \"(待映射)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class DisplayOption\n {\n public DisplayOption(string value, string label)\n {\n this.Value = WidgetSettings.NormalizeDisplayDeviceName(value);\n this.Label = label ?? string.Empty;\n }\n\n public string Value { get; private set; }\n private string Label { get; set; }\n\n public override string ToString()\n {\n return this.Label;\n }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n if (this.Value is OperationPrimaryPanelMode)\n {\n OperationPrimaryPanelMode mode = (OperationPrimaryPanelMode)this.Value;\n if (mode == OperationPrimaryPanelMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == OperationPrimaryPanelMode.WindowsButton)\n {\n return \"Windows 按钮\";\n }\n\n if (mode == OperationPrimaryPanelMode.MemoryPie)\n {\n return \"内存饼图\";\n }\n\n if (mode == OperationPrimaryPanelMode.Hidden)\n {\n return \"隐藏\";\n }\n }\n\n if (this.Value is CodexRadarSoftwareMode)\n {\n CodexRadarSoftwareMode mode = (CodexRadarSoftwareMode)this.Value;\n if (mode == CodexRadarSoftwareMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == CodexRadarSoftwareMode.Codex)\n {\n return \"CODEX\";\n }\n\n if (mode == CodexRadarSoftwareMode.Claude)\n {\n return \"CLAUDE\";\n }\n }\n\n if (this.Value is RadarClockTimeDisplayMode)\n {\n RadarClockTimeDisplayMode mode = (RadarClockTimeDisplayMode)this.Value;\n if (mode == RadarClockTimeDisplayMode.Utc)\n {\n return \"UTC\";\n }\n\n if (mode == RadarClockTimeDisplayMode.CurrentLocal)\n {\n return \"当前时间\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastAttemptRefresh)\n {\n return \"上次尝试刷新\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastActualRefresh)\n {\n return \"上次实际刷新\";\n }\n }\n\n if (this.Value is CodexQuotaPlanComparison)\n {\n CodexQuotaPlanComparison comparison = (CodexQuotaPlanComparison)this.Value;\n if (comparison == CodexQuotaPlanComparison.GreaterThan)\n {\n return \"大于\";\n }\n\n return \"小于\";\n }\n\n if (this.Value is CodexQuotaPlanResumeConditionMode)\n {\n CodexQuotaPlanResumeConditionMode mode = (CodexQuotaPlanResumeConditionMode)this.Value;\n if (mode == CodexQuotaPlanResumeConditionMode.WeeklyOnly)\n {\n return \"仅周额度\";\n }\n\n if (mode == CodexQuotaPlanResumeConditionMode.FiveHourOnly)\n {\n return \"仅 5 小时额度\";\n }\n\n return \"周额度与 5 小时额度\";\n }\n\n // All six per-window render-variant enums share member names for the common variants\n // (Classic plus the four OLED-safe restyle schemes); CodexRadarRenderVariant additionally\n // carries EvenGrid/EvenRow. Label by member name once instead of duplicating per enum type.\n if (this.Value is CodexRadarRenderVariant ||\n this.Value is MainWidgetRenderVariant ||\n this.Value is NetworkMonitorRenderVariant ||\n this.Value is PowerThermalRenderVariant ||\n this.Value is ConnectionCheckRenderVariant ||\n this.Value is OperationRenderVariant)\n {\n string variantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (variantName == \"Classic\") return \"经典布局\";\n if (variantName == \"EvenGrid\") return \"均布六格\";\n if (variantName == \"EvenRow\") return \"均布单行\";\n if (variantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (variantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (variantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (variantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n }\n\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "5852ca99aa369be84fee0f9a3357651967327ee0": {"t": 1785756676.4227989, "n": 188310, "h": "f473a2b8fff7543b", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int WmSettingChange = 0x001A;\n private const int WmDisplayChange = 0x007E;\n private const int EmSetCueBanner = 0x1501;\n private const int ReferenceWorkAreaWidth = 2880;\n private const int ReferenceWorkAreaHeight = 1740;\n private const int PreferredClientWidth = 1888;\n private const int PreferredClientHeight = 1312;\n private const int MinimumClientWidth = 760;\n private const int MinimumClientHeight = 560;\n private const int ScreenMargin = 80;\n private const int DefaultNavWidth = 409;\n private const int MediumNavWidth = 300;\n private const int CompactNavWidth = 220;\n private const int NavItemHeight = 60;\n private const int ClaudeRadarModelGridColumns = 5;\n private const int ClaudeRadarModelButtonWidth = 72;\n private const int ClaudeRadarModelButtonHeight = 38;\n private const int ClaudeRadarModelButtonGap = 8;\n private const string ClaudeRadarModelGridName = \"ClaudeRadarModelGrid\";\n private const string GlobalLayoutEditCommandName = \"GlobalLayoutEditCommand\";\n\n private static readonly Color MicaBase = DesignTokens.SettingsWarmTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.SettingsWarmTheme.CardRest;\n private static readonly Color CardHover = DesignTokens.SettingsWarmTheme.CardHover;\n private static readonly Color StrokeColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.SettingsWarmTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.SettingsWarmTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.SettingsWarmTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.SettingsWarmTheme.Accent;\n private static readonly Color AccentHover = DesignTokens.SettingsWarmTheme.AccentHover;\n private static readonly Color AccentPressed = DesignTokens.SettingsWarmTheme.AccentPressed;\n private static readonly Color ErrorClr = DesignTokens.SettingsWarmTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private TableLayoutPanel bodyLayout;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool dirty;\n private bool messageFilterRegistered;\n private bool unsavedPreviewConsumed;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n public bool TryConsumeUnsavedPreview(out WidgetSettings settings)\n {\n settings = null;\n if (this.saved || this.OwnerFormClosing || this.unsavedPreviewConsumed)\n {\n return false;\n }\n\n this.unsavedPreviewConsumed = true;\n settings = this.baseline.Clone();\n settings.Normalize();\n return true;\n }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n ApplicationIcon.ApplyTo(this);\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = true;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n ApplyDynamicResolutionSizing(true);\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n ApplyDynamicResolutionSizing(false);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n ApplyResponsiveShellLayout();\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n if (m.Msg == WmDisplayChange || m.Msg == WmSettingChange)\n {\n ScheduleDynamicResolutionSizing();\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n if (this.dirty && !this.saved && this.owner != null && !this.OwnerFormClosing)\n {\n DialogResult result = MessageBox.Show(\n this,\n \"设置还没有保存。要先保存再关闭吗?\",\n \"未保存的更改\",\n MessageBoxButtons.YesNoCancel,\n MessageBoxIcon.Warning,\n MessageBoxDefaultButton.Button1);\n if (result == DialogResult.Cancel)\n {\n e.Cancel = true;\n return;\n }\n\n if (result == DialogResult.Yes && !TrySaveSettings())\n {\n e.Cancel = true;\n return;\n }\n }\n\n this.previewTimer.Stop();\n WidgetSettings revertSettings;\n if (this.owner != null && TryConsumeUnsavedPreview(out revertSettings))\n {\n try\n {\n this.owner.RevertSettings(revertSettings);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.SettingsWarmTheme.ErrorText; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n this.bodyLayout = body;\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetNavigationWidthForClientWidth(this.ClientSize.Width)));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n SetDirtyState(true);\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n if (TrySaveSettings())\n {\n ShowStatus(\"保存完成\", false);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold));\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n // 分组名以 '!' 开头 = 收进该页底部的「复杂选项」折叠区。\n AddPageGrouped(\"\\uE115\", \"系统\", \"开机启动、性能和窗口基础行为。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\" },\n new string[] { \"AI 请求阻断\", \"AiRequestProtectionAutoEnabled\", \"AiRequestProtectionManualBlockEnabled\" },\n new string[] { \"!Codex 额度计划\", \"CodexQuotaPlanEnabled\", \"CodexQuotaPlanWeeklyComparison\", \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\", \"CodexQuotaPlanFiveHourThresholdPercent\", \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\" },\n new string[] { \"!恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" },\n new string[] { \"!调试\", \"ForceShowForegroundFpsEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7C2\", \"布局与位置\", \"所有浮窗的位置、大小和所在显示器;推荐用可视化编辑直接拖拽。\", new string[][]\n {\n new string[] { \"可视化编辑\", GlobalLayoutEditCommandName },\n new string[] { \"显示器分配\", \"FallbackDisconnectedDisplaysEnabled\", \"MainDisplayDeviceName\", \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\", \"PowerThermalDisplayDeviceName\", \"NetworkMonitorDisplayDeviceName\", \"ConnectionCheckDisplayDeviceName\", \"OperationDisplayDeviceName\" },\n new string[] { \"!主窗口位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"!Codex Radar 位置\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\" },\n new string[] { \"!Claude Radar 位置\", \"ClaudeRadarWidth\", \"ClaudeRadarHeight\", \"ClaudeRadarLeftX\", \"ClaudeRadarBottomY\" },\n new string[] { \"!功耗温度位置\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\" },\n new string[] { \"!网络监控位置\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\" },\n new string[] { \"!连接检测位置\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\" },\n new string[] { \"!操作面板位置\", \"OperationLeftOffset\", \"OperationBottomOffset\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与防烧屏\", \"鼠标靠近时隐藏、空闲自动隐藏和 OLED 防烧屏。\", new string[][]\n {\n new string[] { \"鼠标靠近时隐藏\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" },\n new string[] { \"!延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"!覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" }\n });\n\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口显示哪些指标、透明度和外观。\", new string[][]\n {\n new string[] { \"显示哪些指标\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" },\n new string[] { \"外观风格\", \"MainWidgetRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"Codex 用量监控:模型、时区和外观。\", new string[][]\n {\n // 手动布局与元素偏移分组已从设置界面隐藏(改用渲染变体切换布局)。\n // 对应 WidgetSettings 属性仍保留,Classic 布局若通过 settings.ini 设置仍生效;\n // 均布变体本就忽略这些设置。恢复入口时把下方分组加回,并同步 VerifySelfTest 必需绑定。\n new string[] { \"窗口\", \"CodexRadarEnabled\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarSoftwareMode\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"RadarClockAutoSwitchModelEnabled\", \"RadarClockTimeDisplayMode\", \"DeepSeekApiKeyRevision\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"外观风格\", \"CodexRadarRenderVariant\" },\n new string[] { \"!网站数据源\", \"CodexRadarPublicJsonEnabled\", \"CodexRadarHtmlFallbackEnabled\", \"CodexRadarRssFallbackEnabled\", \"CodexRadarServiceProbeToken\" },\n new string[] { \"!IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineAutoEnabled\", \"CodexModelIqBaselinePassed\", \"CodexModelIqBaselineValidTasks\" },\n new string[] { \"!效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"!随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uE8D4\", \"Claude Radar\", \"Claude 用量监控独立窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"ClaudeRadarEnabled\", \"ClaudeRadarTransparencyPercent\" },\n new string[] { \"模型\", \"ClaudeRadarModelKey\" },\n new string[] { \"!网站数据源\", \"ClaudeRadarJsonEnabled\", \"ClaudeRadarHomepageFallbackEnabled\", \"ClaudeRadarCommunityRatingsEnabled\", \"ClaudeRadarLocalQuotaFallbackEnabled\", \"ClaudeRadarServiceProbeToken\" },\n new string[] { \"!随机测试\", \"ClaudeRadarRandomTestEnabled\", \"ClaudeRadarRandomTestAutoRefresh\", \"ClaudeRadarRandomTestRefreshToken\" },\n new string[] { \"!外观风格(预留)\", \"ClaudeRadarRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"透明度\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"外观风格\", \"PowerThermalRenderVariant\" },\n new string[] { \"!测试\", \"ThermalTestMode\" }\n });\n\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW 检测、云服务和出口身份。\", new string[][]\n {\n new string[] { \"网络监控\", \"NetworkMonitorAdapterId\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\" },\n new string[] { \"连接检测\", \"ConnectionCheckIntervalSeconds\", \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\" },\n new string[] { \"网络监控外观\", \"NetworkMonitorRenderVariant\" },\n new string[] { \"连接检测外观\", \"ConnectionCheckRenderVariant\" },\n new string[] { \"!手动刷新\", \"GfwProbeManualRefreshToken\", \"ConnectionCheckManualRefreshToken\" },\n new string[] { \"!云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"!测试\", \"CleanIpBadgeTestMode\", \"NetworkStatusTestMode\" }\n });\n\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板的按钮、透明度和外观。\", new string[][]\n {\n new string[] { \"按钮与面板\", \"OperationButtonSize\", \"OperationPrimaryPanelMode\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"外观风格\", \"OperationRenderVariant\" },\n new string[] { \"!测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n // A leading '!' marks the group as advanced: it renders collapsed under the\n // per-page \"复杂选项\" header until the user expands it (or searches).\n bool advanced = groupTitle.Length > 0 && groupTitle[0] == '!';\n if (advanced)\n {\n groupTitle = groupTitle.Substring(1);\n }\n\n if (advanced && page.AdvancedHeader == null)\n {\n AdvancedSectionHeader advancedHeader = new AdvancedSectionHeader(GetUiFont(10.0f, FontStyle.Bold), GetUiFont(8.5f));\n advancedHeader.Width = 1152;\n advancedHeader.Height = 64;\n advancedHeader.Margin = new Padding(0, 32, 0, 6);\n advancedHeader.Click += delegate { ToggleAdvancedSection(page); };\n page.AdvancedHeader = advancedHeader;\n stack.Controls.Add(advancedHeader);\n }\n\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n group.Advanced = advanced;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n titleLabel.Visible = !advanced;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n card.Visible = !advanced;\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.AdvancedRowCount = CountAdvancedRows(page);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = Math.Max(160, GetNavigationWidthForClientWidth(this.ClientSize.Width) - 24);\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n private static int CountAdvancedRows(CategoryPage page)\n {\n int count = 0;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n if (page.Groups[g].Advanced)\n {\n count += page.Groups[g].Editors.Count;\n }\n }\n\n return count;\n }\n\n private void ToggleAdvancedSection(CategoryPage page)\n {\n page.AdvancedExpanded = !page.AdvancedExpanded;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Expanded = page.AdvancedExpanded;\n }\n\n ApplyAdvancedVisibility(page);\n LayoutPage(page);\n }\n\n private void ApplyAdvancedVisibility(CategoryPage page)\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n if (query.Length > 0)\n {\n // Search visibility is authoritative while a query is active.\n ApplySearchFilter();\n return;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (!group.Advanced)\n {\n continue;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = page.AdvancedExpanded;\n }\n\n if (group.Card != null)\n {\n group.Card.Visible = page.AdvancedExpanded;\n if (page.AdvancedExpanded)\n {\n group.Card.LayoutRows();\n }\n }\n }\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n if (string.Equals(propertyName, GlobalLayoutEditCommandName, StringComparison.Ordinal))\n {\n return BuildGlobalLayoutEditEditor();\n }\n\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private SettingEditor BuildGlobalLayoutEditEditor()\n {\n Button button = BuildCommandButton(\"全局编辑\", false);\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenGlobalLayoutEditorFromSettings(); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(GlobalLayoutEditCommandName);\n card.HintLabel.Text = GetSettingHint(GlobalLayoutEditCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(GlobalLayoutEditCommandName, card, button);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n if (property.Name.EndsWith(\"RenderVariant\", StringComparison.Ordinal) &&\n !property.Name.StartsWith(\"ClaudeRadar\", StringComparison.Ordinal))\n {\n return BuildVariantPicker(property);\n }\n\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildCodexRadarModelCombo();\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildClaudeRadarModelGridSelector();\n }\n\n if (type == typeof(string) && IsDisplayDeviceSetting(property.Name))\n {\n return BuildDisplayDeviceCombo();\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n if (string.Equals(property.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(GetDeepSeekApiKeyButtonText(), false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate { OpenDeepSeekApiKeyDialog(button); };\n return button;\n }\n\n if (string.Equals(property.Name, \"CodexRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Codex Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (string.Equals(property.Name, \"ClaudeRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Claude Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (type == typeof(int) && property.Name.EndsWith(\"RefreshToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即刷新\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"刷新请求已发送。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n private Control BuildVariantPicker(PropertyInfo property)\n {\n VariantPicker picker = new VariantPicker(property.Name, property.PropertyType, GetUiFont(8.8f, FontStyle.Bold), GetUiFont(8.0f));\n picker.Width = 560;\n picker.ValueChanged += delegate { OnSettingChanged(); };\n picker.PreferredHeightChanged += delegate { RelayoutSettingGroup(picker); };\n return picker;\n }\n\n private Control BuildDisplayDeviceCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n combo.Items.Add(new DisplayOption(string.Empty, \"主显示器/自动\"));\n Screen[] screens = Screen.AllScreens;\n for (int i = 0; i < screens.Length; i++)\n {\n Screen screen = screens[i];\n Rectangle bounds = screen.Bounds;\n string label = string.Format(\n CultureInfo.InvariantCulture,\n \"{0} {1}x{2} @ {3},{4}{5}\",\n screen.DeviceName,\n bounds.Width,\n bounds.Height,\n bounds.Left,\n bounds.Top,\n screen.Primary ? \" 主屏\" : string.Empty);\n combo.Items.Add(new DisplayOption(screen.DeviceName, label));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildCodexRadarModelCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n\n List<CodexRadarModelInfo> models = CodexRadarModelCatalog.LoadModels();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n CodexRadarModelInfo model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = CodexRadarModelCatalog.NormalizeModelKey(model.Key);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n combo.Items.Add(new ModelOption(\n key,\n CodexRadarModelCatalog.GetDisplayLabel(model.Label, key),\n model.Available,\n false));\n keys.Add(key);\n }\n\n string defaultKey = CodexRadarModelCatalog.DefaultModelKey;\n if (!keys.Contains(defaultKey))\n {\n combo.Items.Insert(0, new ModelOption(\n defaultKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, defaultKey),\n true,\n false));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildClaudeRadarModelGridSelector()\n {\n Panel panel = new Panel();\n panel.Width = 400;\n panel.BackColor = Color.Transparent;\n panel.Resize += delegate { LayoutClaudeRadarModelPanel(panel); };\n\n FlowLayoutPanel grid = new FlowLayoutPanel();\n grid.Name = ClaudeRadarModelGridName;\n grid.Width = panel.Width;\n grid.Location = new Point(0, 0);\n grid.Margin = new Padding(0);\n grid.Padding = new Padding(0);\n grid.FlowDirection = FlowDirection.LeftToRight;\n grid.WrapContents = true;\n grid.AutoSize = false;\n grid.BackColor = Color.Transparent;\n panel.Controls.Add(grid);\n\n Button edit = BuildCommandButton(\"编辑映射\", false);\n edit.Width = panel.Width;\n edit.Height = 54;\n edit.Click += delegate\n {\n using (ClaudeRadarModelMapEditorForm dialog = new ClaudeRadarModelMapEditorForm())\n {\n if (dialog.ShowDialog(this) == DialogResult.OK)\n {\n string selected = GetClaudeRadarModelGridValue(grid);\n PopulateClaudeRadarModelGrid(grid, selected);\n LayoutClaudeRadarModelPanel(panel);\n RelayoutSettingGroup(panel);\n ShowStatus(\"Claude Radar 模型映射已保存。\", false);\n OnSettingChanged();\n }\n }\n };\n panel.Controls.Add(edit);\n PopulateClaudeRadarModelGrid(grid, string.Empty);\n LayoutClaudeRadarModelPanel(panel);\n return panel;\n }\n\n private void PopulateClaudeRadarModelGrid(FlowLayoutPanel grid, string selectedKey)\n {\n if (grid == null)\n {\n return;\n }\n\n string normalizedSelected = WidgetSettings.NormalizeClaudeRadarModelKey(selectedKey);\n List<ClaudeModelOption> options = BuildClaudeRadarModelOptions(normalizedSelected);\n bool selectedExists = false;\n for (int i = 0; i < options.Count; i++)\n {\n if (string.Equals(options[i].Key, normalizedSelected, StringComparison.OrdinalIgnoreCase))\n {\n selectedExists = true;\n break;\n }\n }\n\n if (!selectedExists)\n {\n normalizedSelected = string.Empty;\n }\n\n grid.Tag = normalizedSelected;\n grid.SuspendLayout();\n try\n {\n while (grid.Controls.Count > 0)\n {\n Control old = grid.Controls[0];\n grid.Controls.RemoveAt(0);\n old.Dispose();\n }\n\n int slots = Math.Max(\n ClaudeRadarModelGridColumns,\n ((options.Count + ClaudeRadarModelGridColumns - 1) / ClaudeRadarModelGridColumns) * ClaudeRadarModelGridColumns);\n for (int i = 0; i < slots; i++)\n {\n ClaudeModelOption option = i < options.Count ? options[i] : null;\n Button button = BuildClaudeRadarModelButton(option);\n button.Margin = new Padding(0, 0, i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap, ClaudeRadarModelButtonGap);\n grid.Controls.Add(button);\n }\n\n ApplyClaudeRadarModelGridSelection(grid);\n int rows = Math.Max(1, slots / ClaudeRadarModelGridColumns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n }\n finally\n {\n grid.ResumeLayout();\n }\n }\n\n private List<ClaudeModelOption> BuildClaudeRadarModelOptions(string selectedKey)\n {\n List<ClaudeModelOption> options = new List<ClaudeModelOption>();\n options.Add(new ClaudeModelOption(string.Empty, \"自动\", true, false));\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n keys.Add(string.Empty);\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n string status = (model.Status ?? string.Empty).Trim();\n bool deleted = string.Equals(status, \"deleted\", StringComparison.OrdinalIgnoreCase);\n if (deleted && !string.Equals(key, selectedKey, StringComparison.OrdinalIgnoreCase))\n {\n continue;\n }\n\n bool pending = string.Equals(status, \"pending\", StringComparison.OrdinalIgnoreCase);\n bool temporarilyMissing = string.Equals(status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase);\n bool available = model.Enabled &&\n !deleted &&\n !pending &&\n !temporarilyMissing &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n options.Add(new ClaudeModelOption(\n key,\n label,\n available,\n pending || temporarilyMissing));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (!string.IsNullOrEmpty(selectedKey) && !keys.Contains(selectedKey))\n {\n options.Add(new ClaudeModelOption(selectedKey, selectedKey, false, true));\n }\n\n return options;\n }\n\n private Button BuildClaudeRadarModelButton(ClaudeModelOption option)\n {\n Button button = new Button();\n button.Width = ClaudeRadarModelButtonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Tag = option;\n button.Text = option == null ? \"--\" : GetClaudeRadarModelButtonText(option);\n button.AutoEllipsis = true;\n button.TextAlign = ContentAlignment.MiddleCenter;\n button.FlatStyle = FlatStyle.Flat;\n button.UseVisualStyleBackColor = false;\n button.FlatAppearance.BorderSize = 1;\n button.Font = GetUiFont(8.8f, FontStyle.Bold);\n button.Cursor = option != null && option.Available ? Cursors.Hand : Cursors.Default;\n button.Enabled = option != null && option.Available;\n button.Click += delegate\n {\n ClaudeModelOption clicked = button.Tag as ClaudeModelOption;\n if (clicked == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(button.Parent);\n if (grid == null)\n {\n return;\n }\n\n string current = GetClaudeRadarModelGridValue(grid);\n if (string.Equals(current, clicked.Key, StringComparison.OrdinalIgnoreCase))\n {\n return;\n }\n\n grid.Tag = clicked.Key;\n ApplyClaudeRadarModelGridSelection(grid);\n OnSettingChanged();\n };\n return button;\n }\n\n private static string GetClaudeRadarModelButtonText(ClaudeModelOption option)\n {\n if (option == null)\n {\n return \"--\";\n }\n\n if (option.Key.Length == 0)\n {\n return \"自动\";\n }\n\n string label = option.Label ?? option.Key;\n if (label.Length <= 7)\n {\n return label;\n }\n\n return label.Substring(0, 7);\n }\n\n private void ApplyClaudeRadarModelGridSelection(FlowLayoutPanel grid)\n {\n if (grid == null)\n {\n return;\n }\n\n string selectedKey = WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n bool selected = option != null &&\n string.Equals(option.Key, selectedKey, StringComparison.OrdinalIgnoreCase);\n ApplyClaudeRadarModelButtonStyle(button, option, selected);\n }\n }\n\n private static void ApplyClaudeRadarModelButtonStyle(Button button, ClaudeModelOption option, bool selected)\n {\n if (button == null)\n {\n return;\n }\n\n if (option == null)\n {\n button.BackColor = ControlBg;\n button.ForeColor = TextTertiary;\n button.FlatAppearance.BorderColor = ControlBorder;\n return;\n }\n\n if (selected)\n {\n button.BackColor = DesignTokens.SettingsWarmTheme.Accent;\n button.ForeColor = Color.Black;\n button.FlatAppearance.BorderColor = DesignTokens.SettingsWarmTheme.AccentHover;\n return;\n }\n\n button.BackColor = option.Available ? DesignTokens.SettingsWarmTheme.ButtonRest : MicaLayer;\n button.ForeColor = option.Available ? TextSecondary : TextTertiary;\n button.FlatAppearance.BorderColor = option.Pending ? DesignTokens.Colors.Warning : ControlBorder;\n }\n\n private static string GetClaudeRadarModelGridValue(FlowLayoutPanel grid)\n {\n return grid == null ? string.Empty : WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n }\n\n private void SetClaudeRadarModelGridValue(FlowLayoutPanel grid, string value)\n {\n PopulateClaudeRadarModelGrid(grid, value);\n if (grid != null && grid.Parent != null)\n {\n LayoutClaudeRadarModelPanel(grid.Parent);\n }\n }\n\n private static FlowLayoutPanel FindClaudeRadarModelGrid(Control control)\n {\n FlowLayoutPanel grid = control as FlowLayoutPanel;\n if (grid != null && string.Equals(grid.Name, ClaudeRadarModelGridName, StringComparison.Ordinal))\n {\n return grid;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n grid = FindClaudeRadarModelGrid(control.Controls[i]);\n if (grid != null)\n {\n return grid;\n }\n }\n }\n\n return null;\n }\n\n private static void LayoutClaudeRadarModelPanel(Control panel)\n {\n if (panel == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(panel);\n Button edit = null;\n for (int i = 0; i < panel.Controls.Count; i++)\n {\n Button candidate = panel.Controls[i] as Button;\n if (candidate != null && string.Equals(candidate.Text, \"编辑映射\", StringComparison.Ordinal))\n {\n edit = candidate;\n break;\n }\n }\n\n int y = 0;\n if (grid != null)\n {\n grid.Location = new Point(0, 0);\n grid.Width = panel.Width;\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n button.Width = buttonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Margin = new Padding(\n 0,\n 0,\n i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap,\n ClaudeRadarModelButtonGap);\n }\n\n y = grid.Bottom + 10;\n }\n\n if (edit != null)\n {\n edit.Location = new Point(0, y);\n edit.Width = panel.Width;\n y = edit.Bottom;\n }\n\n panel.Height = Math.Max(54, y);\n }\n\n private static int GetClaudeRadarModelButtonWidthForGrid(int gridWidth)\n {\n int gaps = (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n int usable = Math.Max(ClaudeRadarModelGridColumns, gridWidth - gaps);\n return Math.Max(32, usable / ClaudeRadarModelGridColumns);\n }\n\n private static void RelayoutSettingGroup(Control control)\n {\n Control parent = control;\n while (parent != null)\n {\n SettingGroupCard card = parent as SettingGroupCard;\n if (card != null)\n {\n card.LayoutRows();\n return;\n }\n\n parent = parent.Parent;\n }\n }\n\n private static ComboBox FindComboBox(Control control)\n {\n ComboBox combo = control as ComboBox;\n if (combo != null)\n {\n return combo;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n combo = FindComboBox(control.Controls[i]);\n if (combo != null)\n {\n return combo;\n }\n }\n }\n\n return null;\n }\n\n private void PopulateClaudeRadarModelCombo(ComboBox combo)\n {\n if (combo == null)\n {\n return;\n }\n\n combo.Items.Clear();\n combo.Items.Add(new ClaudeModelOption(string.Empty, \"自动选择\", true, false));\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n bool available = model.Enabled &&\n !string.Equals(model.Status, \"deleted\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase) &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n combo.Items.Add(new ClaudeModelOption(\n key,\n label,\n available,\n string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase)));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (combo.Items.Count > 0 && combo.SelectedIndex < 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static object GetClaudeRadarModelComboValue(ComboBox combo)\n {\n ClaudeModelOption option = combo == null ? null : combo.SelectedItem as ClaudeModelOption;\n return option == null ? string.Empty : option.Key;\n }\n\n private static void SetClaudeRadarModelComboValue(ComboBox combo, string value)\n {\n if (combo == null)\n {\n return;\n }\n\n string modelKey = WidgetSettings.NormalizeClaudeRadarModelKey(value);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ClaudeModelOption option = combo.Items[i] as ClaudeModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(modelKey))\n {\n combo.Items.Add(new ClaudeModelOption(modelKey, modelKey, false, true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static bool IsDisplayDeviceSetting(string propertyName)\n {\n return propertyName != null &&\n propertyName.EndsWith(\"DisplayDeviceName\", StringComparison.Ordinal);\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n EnsureVariantSamplesForPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void EnsureVariantSamplesForPage(CategoryPage page)\n {\n if (this.owner == null || page == null)\n {\n return;\n }\n\n string directory = GetVariantSampleDirectory();\n for (int i = 0; i < page.Editors.Count; i++)\n {\n SettingEditor editor = page.Editors[i];\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker == null)\n {\n continue;\n }\n\n string prefix = GetVariantSamplePrefix(editor.Name);\n if (prefix.Length == 0)\n {\n continue;\n }\n\n try\n {\n if (picker.HasMissingSamples(directory, prefix))\n {\n System.IO.Directory.CreateDirectory(directory);\n RenderVariantSamplesForProperty(editor.Name, directory);\n }\n\n picker.LoadSamples(directory, prefix);\n RelayoutSettingGroup(picker);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n picker.MarkSamplesUnavailable();\n }\n }\n }\n\n private static string GetVariantSampleDirectory()\n {\n return System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName,\n \"variant-samples\",\n \"v\" + ProductIdentity.Version);\n }\n\n private static string GetVariantSamplePrefix(string propertyName)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal)) return \"widget\";\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal)) return \"codexradar\";\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal)) return \"networkmonitor\";\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal)) return \"powerthermal\";\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal)) return \"operation\";\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal)) return \"connectioncheck\";\n return string.Empty;\n }\n\n private static void RenderVariantSamplesForProperty(string propertyName, string directory)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal))\n {\n WidgetForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal))\n {\n CodexRadarForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal))\n {\n NetworkMonitorForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal))\n {\n PowerThermalForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal))\n {\n OperationForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal))\n {\n ConnectionCheckForm.RenderVariantSamples(directory);\n }\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Width = width;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n\n SetDirtyState(false);\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private bool TrySaveSettings()\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n SetDirtyState(false);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n return false;\n }\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n SetClaudeRadarModelGridValue(grid, value as string);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n picker.SetSelectedValue(value);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n string displayDeviceName = value as string;\n if (string.Equals(editor.Property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n string modelKey = CodexRadarModelCatalog.NormalizeModelKey(value as string);\n if (modelKey.Length == 0)\n {\n modelKey = CodexRadarModelCatalog.DefaultModelKey;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ModelOption option = combo.Items[i] as ModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n combo.Items.Add(new ModelOption(\n modelKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, modelKey),\n false,\n true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n SetClaudeRadarModelComboValue(combo, value as string);\n return;\n }\n\n if (IsDisplayDeviceSetting(editor.Property.Name))\n {\n displayDeviceName = WidgetSettings.NormalizeDisplayDeviceName(displayDeviceName);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n DisplayOption option = combo.Items[i] as DisplayOption;\n if (option != null && string.Equals(option.Value, displayDeviceName, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(displayDeviceName))\n {\n combo.Items.Add(new DisplayOption(displayDeviceName, \"未连接 \" + displayDeviceName));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n button.Tag = value is int ? (int)value : 0;\n if (string.Equals(editor.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n button.Text = GetDeepSeekApiKeyButtonText();\n }\n }\n }\n\n private void ApplyResponsiveShellLayout()\n {\n int navWidth = GetNavigationWidthForClientWidth(this.ClientSize.Width);\n if (this.bodyLayout != null && this.bodyLayout.ColumnStyles.Count > 0)\n {\n this.bodyLayout.ColumnStyles[0].Width = navWidth;\n }\n\n if (this.navigationPanel != null)\n {\n int itemWidth = Math.Max(160, navWidth - 24);\n for (int i = 0; i < this.navigationPanel.Controls.Count; i++)\n {\n this.navigationPanel.Controls[i].Width = itemWidth;\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n if (editor.Property == null)\n {\n return null;\n }\n\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n return GetClaudeRadarModelGridValue(grid);\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n return picker.GetSelectedValue();\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n ClaudeModelOption claudeModelOption = combo.SelectedItem as ClaudeModelOption;\n if (claudeModelOption != null)\n {\n return claudeModelOption.Key;\n }\n\n ModelOption modelOption = combo.SelectedItem as ModelOption;\n if (modelOption != null)\n {\n return modelOption.Key;\n }\n\n DisplayOption displayOption = combo.SelectedItem as DisplayOption;\n if (displayOption != null)\n {\n return displayOption.Value;\n }\n\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n return button.Tag is int ? (int)button.Tag : 0;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n SetDirtyState(true);\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n private void OpenGlobalLayoutEditorFromSettings()\n {\n if (this.owner == null)\n {\n return;\n }\n\n this.previewTimer.Stop();\n WidgetSettings settings = ReadSettings();\n WidgetSettings editedSettings;\n if (this.owner.TryEditGlobalLayout(settings, out editedSettings))\n {\n this.baseline = editedSettings.Clone();\n this.baseline.Normalize();\n LoadSettings(this.baseline);\n this.saved = true;\n ShowStatus(\"全局布局已保存\", false);\n return;\n }\n\n this.owner.PreviewSettings(ReadSettings());\n ShowStatus(\"全局编辑已取消\", false);\n }\n\n private void OpenDeepSeekApiKeyDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"DeepSeek 配置\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.ClientSize = new Size(560, 238);\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n Label title = new Label();\n title.Text = \"DeepSeek API Key\";\n title.Font = GetUiFont(12.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(24, 20);\n title.Size = new Size(500, 30);\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label hint = new Label();\n hint.Text = HasDeepSeekEnvironmentApiKeyForUi()\n ? \"检测到 DEEPSEEK_API_KEY 环境变量;环境变量会优先于本地文件。\"\n : \"密钥保存到本地应用数据目录,不写入 settings.ini 或日志。\";\n hint.Font = GetUiFont(8.8f);\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(24, 56);\n hint.Size = new Size(500, 42);\n hint.TextAlign = ContentAlignment.MiddleLeft;\n\n TextBox keyBox = new TextBox();\n keyBox.Location = new Point(24, 104);\n keyBox.Size = new Size(512, 34);\n keyBox.BackColor = ControlBg;\n keyBox.ForeColor = TextSecondary;\n keyBox.BorderStyle = BorderStyle.FixedSingle;\n keyBox.Font = GetUiFont(9.5f);\n keyBox.UseSystemPasswordChar = true;\n keyBox.Text = ReadDeepSeekApiKeyFileForUi();\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(24, 166);\n clear.Width = 112;\n clear.Height = 44;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Location = new Point(302, 166);\n cancel.Width = 112;\n cancel.Height = 44;\n cancel.Click += delegate { dialog.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Location = new Point(424, 166);\n save.Width = 112;\n save.Height = 44;\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(string.Empty, out errorCode))\n {\n keyBox.Text = string.Empty;\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已清除\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置清除失败 \" + errorCode, true);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(keyBox.Text, out errorCode))\n {\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已保存\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置保存失败 \" + errorCode, true);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(keyBox);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void IncrementDeepSeekApiKeyRevision(Button sourceButton)\n {\n if (sourceButton != null)\n {\n int token = sourceButton.Tag is int ? (int)sourceButton.Tag : 0;\n sourceButton.Tag = token == int.MaxValue ? 1 : token + 1;\n sourceButton.Text = GetDeepSeekApiKeyButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static string GetDeepSeekApiKeyButtonText()\n {\n return IsDeepSeekApiKeyConfiguredForUi() ? \"修改配置\" : \"配置\";\n }\n\n private static bool IsDeepSeekApiKeyConfiguredForUi()\n {\n if (HasDeepSeekEnvironmentApiKeyForUi())\n {\n return true;\n }\n\n return ReadDeepSeekApiKeyFileForUi().Length > 0;\n }\n\n private static bool HasDeepSeekEnvironmentApiKeyForUi()\n {\n try\n {\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable)))\n {\n return true;\n }\n\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.User)))\n {\n return true;\n }\n\n return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine));\n }\n catch\n {\n return false;\n }\n }\n\n private static string ReadDeepSeekApiKeyFileForUi()\n {\n try\n {\n string path = CodexRadarForm.DeepSeekApiKeyPath;\n if (System.IO.File.Exists(path))\n {\n return (System.IO.File.ReadAllText(path, Encoding.UTF8) ?? string.Empty).Trim();\n }\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static bool TrySaveDeepSeekApiKeyFile(string apiKey, out string errorCode)\n {\n errorCode = string.Empty;\n string tempPath = string.Empty;\n try\n {\n string path = CodexRadarForm.DeepSeekApiKeyPath;\n string directory = System.IO.Path.GetDirectoryName(path);\n if (!string.IsNullOrEmpty(directory))\n {\n System.IO.Directory.CreateDirectory(directory);\n }\n\n string trimmed = (apiKey ?? string.Empty).Trim();\n if (trimmed.Length == 0)\n {\n if (System.IO.File.Exists(path))\n {\n System.IO.File.Delete(path);\n }\n\n return true;\n }\n\n tempPath = path + \".tmp\";\n System.IO.File.WriteAllText(tempPath, trimmed + Environment.NewLine, new UTF8Encoding(false));\n if (System.IO.File.Exists(path))\n {\n System.IO.File.Replace(tempPath, path, null);\n }\n else\n {\n System.IO.File.Move(tempPath, path);\n }\n\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n try\n {\n if (!string.IsNullOrEmpty(tempPath) && System.IO.File.Exists(tempPath))\n {\n System.IO.File.Delete(tempPath);\n }\n }\n catch\n {\n }\n\n return false;\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void SetDirtyState(bool hasChanges)\n {\n this.dirty = hasChanges;\n if (this.statusLabel == null)\n {\n return;\n }\n\n if (hasChanges)\n {\n this.statusTimer.Stop();\n this.statusLabel.Text = \"有未保存的更改\";\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.Visible = true;\n }\n else if (!this.statusTimer.Enabled)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n if (this.dirty)\n {\n SetDirtyState(true);\n }\n else\n {\n this.statusLabel.Visible = false;\n }\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n bool searching = query.Length > 0;\n int firstMatchingPageIndex = -1;\n bool selectedPageHasVisibleRows = false;\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n if (page.AdvancedHeader != null)\n {\n // While searching, advanced rows surface directly, so the collapse header hides.\n page.AdvancedHeader.Visible = !searching;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool allowed = !group.Advanced || page.AdvancedExpanded || searching;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = allowed && (pageMatch || groupTitleMatch ||\n editor.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n\n if (searching && anyVisible)\n {\n if (firstMatchingPageIndex < 0)\n {\n firstMatchingPageIndex = i;\n }\n\n if (i == this.selectedPageIndex)\n {\n selectedPageHasVisibleRows = true;\n }\n }\n }\n\n if (searching &&\n !selectedPageHasVisibleRows &&\n firstMatchingPageIndex >= 0 &&\n firstMatchingPageIndex != this.selectedPageIndex)\n {\n SelectPage(firstMatchingPageIndex);\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : string.Empty;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return 1;\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return new NumericRange(\n WidgetSettings.MinCodexRadarManualElementOffsetPixels,\n WidgetSettings.MaxCodexRadarManualElementOffsetPixels);\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static bool IsCodexRadarManualElementOffsetSetting(string name)\n {\n return name != null &&\n name.StartsWith(\"CodexRadar\", StringComparison.OrdinalIgnoreCase) &&\n (name.EndsWith(\"OffsetX\", StringComparison.OrdinalIgnoreCase) ||\n name.EndsWith(\"OffsetY\", StringComparison.OrdinalIgnoreCase));\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private void ScheduleDynamicResolutionSizing()\n {\n if (!this.IsHandleCreated)\n {\n ApplyDynamicResolutionSizing(false);\n return;\n }\n\n try\n {\n this.BeginInvoke((MethodInvoker)delegate\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n });\n }\n catch\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n }\n }\n\n private void ApplyDynamicResolutionSizing(bool usePreferredSize)\n {\n Rectangle workArea = GetTargetWorkArea();\n Size minimum = GetMinimumWindowSizeForWorkArea(workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n\n this.MinimumSize = minimum;\n this.MaximumSize = new Size(\n Math.Max(minimum.Width, Math.Max(1, workArea.Width)),\n Math.Max(minimum.Height, Math.Max(1, workArea.Height)));\n\n Size target = usePreferredSize || this.ClientSize.Width <= 0 || this.ClientSize.Height <= 0\n ? preferred\n : this.ClientSize;\n target = new Size(\n Clamp(target.Width, minimum.Width, preferred.Width),\n Clamp(target.Height, minimum.Height, preferred.Height));\n\n if (this.ClientSize != target)\n {\n this.ClientSize = target;\n }\n\n if (this.Visible || this.IsHandleCreated)\n {\n ClampWindowToWorkArea(workArea);\n }\n\n ApplyResponsiveShellLayout();\n }\n\n private Rectangle GetTargetWorkArea()\n {\n Rectangle workArea;\n if (this.owner != null && !this.owner.IsDisposed)\n {\n workArea = Screen.FromControl(this.owner).WorkingArea;\n }\n else if (this.IsHandleCreated)\n {\n workArea = Screen.FromHandle(this.Handle).WorkingArea;\n }\n else\n {\n workArea = Screen.PrimaryScreen.WorkingArea;\n }\n\n EnsureUsableWorkArea(ref workArea);\n return workArea;\n }\n\n private void ClampWindowToWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int left = Clamp(this.Left, workArea.Left, Math.Max(workArea.Left, workArea.Right - this.Width));\n int top = Clamp(this.Top, workArea.Top, Math.Max(workArea.Top, workArea.Bottom - this.Height));\n if (left != this.Left || top != this.Top)\n {\n this.Location = new Point(left, top);\n }\n }\n\n private static Size GetPreferredClientSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Clamp(\n (int)Math.Round(workArea.Width * (PreferredClientWidth / (double)ReferenceWorkAreaWidth), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientWidth, Math.Max(1, workArea.Width)),\n PreferredClientWidth);\n int height = Clamp(\n (int)Math.Round(workArea.Height * (PreferredClientHeight / (double)ReferenceWorkAreaHeight), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientHeight, Math.Max(1, workArea.Height)),\n PreferredClientHeight);\n return FitClientSizeToWorkArea(new Size(width, height), workArea);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n return FitClientSizeToWorkArea(desiredSize, Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size FitClientSizeToWorkArea(Size desiredSize, Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Math.Min(desiredSize.Width, Math.Max(MinimumClientWidth, workArea.Width - ScreenMargin));\n int height = Math.Min(desiredSize.Height, Math.Max(MinimumClientHeight, workArea.Height - ScreenMargin));\n width = Math.Min(width, Math.Max(1, workArea.Width));\n height = Math.Min(height, Math.Max(1, workArea.Height));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n return GetMinimumWindowSizeForWorkArea(Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size GetMinimumWindowSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n return new Size(\n Math.Min(preferred.Width, Math.Max(Math.Min(MinimumClientWidth, workArea.Width), workArea.Width - 128)),\n Math.Min(preferred.Height, Math.Max(Math.Min(MinimumClientHeight, workArea.Height), workArea.Height - 128)));\n }\n\n private static int GetNavigationWidthForClientWidth(int clientWidth)\n {\n if (clientWidth <= 960)\n {\n return CompactNavWidth;\n }\n\n if (clientWidth <= 1280)\n {\n return MediumNavWidth;\n }\n\n return DefaultNavWidth;\n }\n\n private static void EnsureUsableWorkArea(ref Rectangle workArea)\n {\n if (workArea.Width > 0 && workArea.Height > 0)\n {\n return;\n }\n\n workArea = Screen.PrimaryScreen.Bounds;\n if (workArea.Width <= 0 || workArea.Height <= 0)\n {\n workArea = new Rectangle(0, 0, ReferenceWorkAreaWidth, ReferenceWorkAreaHeight);\n }\n }\n\n private static int Clamp(int value, int minimum, int maximum)\n {\n if (maximum < minimum)\n {\n maximum = minimum;\n }\n\n if (value < minimum)\n {\n return minimum;\n }\n\n if (value > maximum)\n {\n return maximum;\n }\n\n return value;\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static string RunOpenCloseStressSelfTest(int iterations)\n {\n int loopCount = Math.Max(1, Math.Min(500, iterations));\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n baseline.CodexRadarEnabled = true;\n baseline.ClaudeRadarEnabled = true;\n baseline.Normalize();\n\n using (Win11SettingsForm warmup = new Win11SettingsForm(null, baseline))\n {\n warmup.StartPosition = FormStartPosition.Manual;\n warmup.Location = new Point(-30000, -30000);\n warmup.Show();\n Application.DoEvents();\n warmup.Close();\n Application.DoEvents();\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters before = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n for (int i = 0; i < loopCount; i++)\n {\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-30000, -30000);\n form.Show();\n Application.DoEvents();\n form.Close();\n Application.DoEvents();\n }\n\n if ((i + 1) % 25 == 0)\n {\n ForceResourceCleanup();\n }\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters after = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n int handleDelta = after.HandleCount - before.HandleCount;\n int gdiDelta = after.GdiObjects - before.GdiObjects;\n int userDelta = after.UserObjects - before.UserObjects;\n if (handleDelta > 80 || gdiDelta > 8 || userDelta > 16)\n {\n throw new InvalidOperationException(\n \"WinUI settings open/close resource growth exceeded threshold. Iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \", HandlesDelta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \", GdiDelta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \", UserDelta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture));\n }\n\n return \"Settings open/close policy: PASS iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \" handles_delta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \" gdi_delta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \" user_delta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture);\n }\n\n internal static void RunSettingsBindingSelfTest()\n {\n VerifySettingsWindowActivationPolicy();\n VerifyUnsavedPreviewConsumePolicy();\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.ShowInTaskbar = false;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n private static void VerifySettingsWindowActivationPolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n if (!form.ShowInTaskbar)\n {\n throw new InvalidOperationException(\"WinUI settings window must be visible in taskbar and Alt+Tab.\");\n }\n }\n }\n\n private static void VerifyUnsavedPreviewConsumePolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n WidgetSettings revertSettings;\n if (!form.TryConsumeUnsavedPreview(out revertSettings) ||\n revertSettings == null ||\n !object.Equals(revertSettings.OperationPrimaryPanelMode, baseline.OperationPrimaryPanelMode))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consume failed.\");\n }\n\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consumed twice.\");\n }\n }\n\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n WidgetSettings revertSettings;\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings owner-closing preview should not be consumed.\");\n }\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private static void ForceResourceCleanup()\n {\n GC.Collect();\n GC.WaitForPendingFinalizers();\n GC.Collect();\n Application.DoEvents();\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"FallbackDisconnectedDisplaysEnabled\",\n \"MainDisplayDeviceName\",\n \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\",\n GlobalLayoutEditCommandName,\n \"Width\",\n \"CodexRadarEnabled\",\n \"CodexRadarSoftwareMode\",\n \"CodexRadarModelKey\",\n \"RadarClockAutoSwitchModelEnabled\",\n \"RadarClockTimeDisplayMode\",\n \"CodexRadarHtmlFallbackEnabled\",\n \"CodexRadarServiceProbeToken\",\n \"ClaudeRadarEnabled\",\n \"ClaudeRadarModelKey\",\n \"ClaudeRadarJsonEnabled\",\n \"ClaudeRadarServiceProbeToken\",\n \"DeepSeekApiKeyRevision\",\n \"AiRequestProtectionAutoEnabled\",\n \"AiRequestProtectionManualBlockEnabled\",\n \"CodexQuotaPlanEnabled\",\n \"CodexQuotaPlanWeeklyComparison\",\n \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\",\n \"CodexQuotaPlanFiveHourThresholdPercent\",\n \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\",\n \"CodexRadarRenderVariant\",\n \"MainWidgetRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\",\n \"OperationPrimaryPanelMode\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyClaudeRadarModelGridPolicy();\n VerifyVariantPickerPolicy();\n VerifyDynamicResolutionSizingPolicy();\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyClaudeRadarModelGridPolicy()\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(\"ClaudeRadarModelKey\", out editor))\n {\n throw new InvalidOperationException(\"Claude Radar model grid editor missing.\");\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid == null)\n {\n throw new InvalidOperationException(\"Claude Radar model selector must use the five-column button grid.\");\n }\n\n if (grid.Controls.Count < ClaudeRadarModelGridColumns ||\n grid.Controls.Count % ClaudeRadarModelGridColumns != 0)\n {\n throw new InvalidOperationException(\"Claude Radar model grid slot count is not a multiple of five.\");\n }\n\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n int rowWidth = ClaudeRadarModelGridColumns * buttonWidth +\n (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n if (rowWidth > grid.Width)\n {\n throw new InvalidOperationException(\"Claude Radar model grid buttons exceed selector width.\");\n }\n\n bool sawAuto = false;\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n throw new InvalidOperationException(\"Claude Radar model grid contains a non-button slot.\");\n }\n\n if (button.Width != buttonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid button width is not responsive.\");\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n if (option == null)\n {\n if (button.Enabled || !string.Equals(button.Text, \"--\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar empty model slot must be disabled --.\");\n }\n continue;\n }\n\n if (option.Key.Length == 0)\n {\n sawAuto = true;\n if (!button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar automatic model slot must be selectable.\");\n }\n }\n else if (!option.Available && button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar unavailable model slot must be disabled.\");\n }\n }\n\n if (!sawAuto)\n {\n throw new InvalidOperationException(\"Claude Radar model grid missing automatic slot.\");\n }\n }\n\n private void VerifyVariantPickerPolicy()\n {\n string[] pickerNames = new string[]\n {\n \"MainWidgetRenderVariant\",\n \"CodexRadarRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\"\n };\n\n for (int i = 0; i < pickerNames.Length; i++)\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(pickerNames[i], out editor))\n {\n throw new InvalidOperationException(\"Render variant picker binding missing: \" + pickerNames[i]);\n }\n\n if (!(editor.Control is VariantPicker))\n {\n throw new InvalidOperationException(\"Render variant must use VariantPicker: \" + pickerNames[i]);\n }\n }\n\n SettingEditor claudeEditor;\n if (this.editors.TryGetValue(\"ClaudeRadarRenderVariant\", out claudeEditor) &&\n claudeEditor.Control is VariantPicker)\n {\n throw new InvalidOperationException(\"Claude Radar render variant must remain a dropdown until it has effect.\");\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n Size originalMinimum = this.MinimumSize;\n int originalPageIndex = this.selectedPageIndex;\n try\n {\n Size[] testSizes = new Size[]\n {\n this.MinimumSize,\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1280, 680)),\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1024, 728))\n };\n\n for (int sizeIndex = 0; sizeIndex < testSizes.Length; sizeIndex++)\n {\n this.MinimumSize = testSizes[sizeIndex];\n this.ClientSize = testSizes[sizeIndex];\n Application.DoEvents();\n VerifyNoVisibleControlClippingAtCurrentSize();\n }\n }\n finally\n {\n this.MinimumSize = originalMinimum;\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n private void VerifyNoVisibleControlClippingAtCurrentSize()\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Name);\n }\n }\n }\n }\n\n private static void VerifyDynamicResolutionSizingPolicy()\n {\n Rectangle[] workAreas = new Rectangle[]\n {\n new Rectangle(0, 60, 2880, 1740),\n new Rectangle(0, 0, 1920, 1040),\n new Rectangle(0, 0, 1600, 860),\n new Rectangle(0, 0, 1366, 728),\n new Rectangle(0, 0, 1280, 680),\n new Rectangle(0, 0, 1024, 728)\n };\n\n for (int i = 0; i < workAreas.Length; i++)\n {\n Size preferred = GetPreferredClientSizeForWorkArea(workAreas[i]);\n Size minimum = GetMinimumWindowSizeForWorkArea(workAreas[i]);\n if (preferred.Width > workAreas[i].Width ||\n preferred.Height > workAreas[i].Height ||\n minimum.Width > workAreas[i].Width ||\n minimum.Height > workAreas[i].Height ||\n minimum.Width > preferred.Width ||\n minimum.Height > preferred.Height)\n {\n throw new InvalidOperationException(\n \"WinUI settings dynamic resolution sizing failed: \" +\n workAreas[i].Width.ToString(CultureInfo.InvariantCulture) +\n \"x\" +\n workAreas[i].Height.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"ClaudeRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"ClaudeRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"CodexRadarManualLeftPercent\", new NumericRange(WidgetSettings.MinCodexRadarManualLeftPercent, WidgetSettings.MaxCodexRadarManualLeftPercent) },\n { \"CodexRadarManualGapPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualGapPixels, WidgetSettings.MaxCodexRadarManualGapPixels) },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualEfficiencyTextWidthPixels, WidgetSettings.MaxCodexRadarManualEfficiencyTextWidthPixels) },\n { \"CodexRadarManualQuotaRowsWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualQuotaRowsWidthPixels, WidgetSettings.MaxCodexRadarManualQuotaRowsWidthPixels) },\n { \"CodexRadarManualIqStatusWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualIqStatusWidthPixels, WidgetSettings.MaxCodexRadarManualIqStatusWidthPixels) },\n { \"CodexRadarManualTextScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualTextScalePercent, WidgetSettings.MaxCodexRadarManualTextScalePercent) },\n { \"CodexRadarManualRingScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualRingScalePercent, WidgetSettings.MaxCodexRadarManualRingScalePercent) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselineValidTasks\", new NumericRange(WidgetSettings.MinCodexModelIqValidTasks, WidgetSettings.MaxCodexModelIqValidTasks) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"OperationPrimaryPanelMode\", \"左侧区域模式\" },\n { \"OperationWindowsButtonEnabled\", \"显示 Windows 按钮\" },\n { \"OperationMemoryPieEnabled\", \"显示内存饼图\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"AiRequestProtectionAutoEnabled\", \"AI 自动阻断\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"AI 手动阻断\" },\n { \"CodexQuotaPlanEnabled\", \"启用额度计划\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度条件\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度阈值\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时条件\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时阈值\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"恢复额度类型\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"恢复上次暂停\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"断开后回退显示器\" },\n { \"MainDisplayDeviceName\", \"主窗口显示器\" },\n { \"CodexRadarDisplayDeviceName\", \"Codex Radar 显示器\" },\n { \"ClaudeRadarDisplayDeviceName\", \"Claude Radar 显示器\" },\n { \"PowerThermalDisplayDeviceName\", \"功耗温度显示器\" },\n { \"NetworkMonitorDisplayDeviceName\", \"网络监控显示器\" },\n { \"ConnectionCheckDisplayDeviceName\", \"连接检测显示器\" },\n { \"OperationDisplayDeviceName\", \"操作面板显示器\" },\n { GlobalLayoutEditCommandName, \"全局编辑\" },\n { \"Width\", \"主窗口宽度\" },\n { \"Height\", \"主窗口高度\" },\n { \"LeftX\", \"主窗口距左边\" },\n { \"BottomY\", \"主窗口距下边\" },\n { \"CodexRadarWidth\", \"Codex Radar 宽度\" },\n { \"CodexRadarHeight\", \"Codex Radar 高度\" },\n { \"CodexRadarLeftX\", \"Codex Radar 距左边\" },\n { \"CodexRadarBottomY\", \"Codex Radar 距下边\" },\n { \"PowerThermalWidth\", \"功耗温度宽度\" },\n { \"PowerThermalHeight\", \"功耗温度高度\" },\n { \"PowerThermalLeftX\", \"功耗温度距左边\" },\n { \"PowerThermalBottomY\", \"功耗温度距下边\" },\n { \"NetworkMonitorWidth\", \"网络监控宽度\" },\n { \"NetworkMonitorHeight\", \"网络监控高度\" },\n { \"NetworkMonitorLeftX\", \"网络监控距左边\" },\n { \"NetworkMonitorBottomY\", \"网络监控距下边\" },\n { \"ConnectionCheckWidth\", \"连接检测宽度\" },\n { \"ConnectionCheckHeight\", \"连接检测高度\" },\n { \"ConnectionCheckLeftX\", \"连接检测距左边\" },\n { \"ConnectionCheckBottomY\", \"连接检测距下边\" },\n { \"OperationLeftOffset\", \"操作面板距左边\" },\n { \"OperationBottomOffset\", \"操作面板距下边\" },\n { \"BackgroundTransparencyPercent\", \"主窗口背景透明度\" },\n { \"ApplicationTransparencyPercent\", \"主窗口整体透明度\" },\n { \"ShowCpu\", \"显示 CPU\" },\n { \"ShowMemory\", \"显示内存\" },\n { \"ShowDisk\", \"显示磁盘\" },\n { \"ShowNetwork\", \"显示网络\" },\n { \"ShowGpu\", \"显示 GPU\" },\n { \"ShowNpu\", \"显示 NPU\" },\n { \"HoverOpacityEnabled\", \"鼠标靠近时隐藏\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"触发距离(像素)\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarSoftwareMode\", \"监控哪个软件\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"时钟过期自动切模型\" },\n { \"RadarClockTimeDisplayMode\", \"时钟时间来源\" },\n { \"CodexRadarEnabled\", \"启用 Codex Radar\" },\n { \"CodexRadarTransparencyPercent\", \"Codex Radar 透明度\" },\n { \"ClaudeRadarEnabled\", \"启用 Claude Radar\" },\n { \"ClaudeRadarWidth\", \"Claude Radar 宽度\" },\n { \"ClaudeRadarHeight\", \"Claude Radar 高度\" },\n { \"ClaudeRadarLeftX\", \"Claude Radar 左侧 X\" },\n { \"ClaudeRadarBottomY\", \"Claude Radar 底部 Y\" },\n { \"ClaudeRadarTransparencyPercent\", \"Claude Radar 背景透明\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 模型\" },\n { \"ClaudeRadarJsonEnabled\", \"Claude Radar JSON\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"首页元数据回退\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"社区体感分\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"本地 7 天额度线回退\" },\n { \"ClaudeRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"ClaudeRadarRandomTestEnabled\", \"Claude 随机测试\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"Claude 随机测试自动刷新\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"ClaudeRadarRenderVariant\", \"外观风格\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"CodexRadarRenderVariant\", \"外观风格\" },\n { \"MainWidgetRenderVariant\", \"外观风格\" },\n { \"NetworkMonitorRenderVariant\", \"外观风格\" },\n { \"PowerThermalRenderVariant\", \"外观风格\" },\n { \"ConnectionCheckRenderVariant\", \"外观风格\" },\n { \"OperationRenderVariant\", \"外观风格\" },\n { \"CodexRadarManualLayoutEnabled\", \"启用手动布局\" },\n { \"CodexRadarManualLeftPercent\", \"左侧区域占比\" },\n { \"CodexRadarManualGapPixels\", \"模块间距\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"效率文字列宽\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"余额列宽\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"IQ 状态列宽\" },\n { \"CodexRadarManualTextScalePercent\", \"文字比例\" },\n { \"CodexRadarManualRingScalePercent\", \"圆环比例\" },\n { \"CodexRadarTimeEfficiencyRingOffsetX\", \"时间环 X\" },\n { \"CodexRadarTimeEfficiencyRingOffsetY\", \"时间环 Y\" },\n { \"CodexRadarTimeEfficiencyTextOffsetX\", \"时间字 X\" },\n { \"CodexRadarTimeEfficiencyTextOffsetY\", \"时间字 Y\" },\n { \"CodexRadarTokenEfficiencyRingOffsetX\", \"Token 环 X\" },\n { \"CodexRadarTokenEfficiencyRingOffsetY\", \"Token 环 Y\" },\n { \"CodexRadarTokenEfficiencyTextOffsetX\", \"Token 字 X\" },\n { \"CodexRadarTokenEfficiencyTextOffsetY\", \"Token 字 Y\" },\n { \"CodexRadarConnectionTopTextOffsetX\", \"连接上字 X\" },\n { \"CodexRadarConnectionTopTextOffsetY\", \"连接上字 Y\" },\n { \"CodexRadarConnectionLineOffsetX\", \"连接线 X\" },\n { \"CodexRadarConnectionLineOffsetY\", \"连接线 Y\" },\n { \"CodexRadarConnectionBottomTextOffsetX\", \"连接下字 X\" },\n { \"CodexRadarConnectionBottomTextOffsetY\", \"连接下字 Y\" },\n { \"CodexRadarFiveHourQuotaRingOffsetX\", \"5h 环 X\" },\n { \"CodexRadarFiveHourQuotaRingOffsetY\", \"5h 环 Y\" },\n { \"CodexRadarFiveHourQuotaTextOffsetX\", \"5h 字 X\" },\n { \"CodexRadarFiveHourQuotaTextOffsetY\", \"5h 字 Y\" },\n { \"CodexRadarWeeklyQuotaRingOffsetX\", \"周环 X\" },\n { \"CodexRadarWeeklyQuotaRingOffsetY\", \"周环 Y\" },\n { \"CodexRadarWeeklyQuotaTextOffsetX\", \"周字 X\" },\n { \"CodexRadarWeeklyQuotaTextOffsetY\", \"周字 Y\" },\n { \"CodexRadarQuotaRadarLineOffsetX\", \"额度线 X\" },\n { \"CodexRadarQuotaRadarLineOffsetY\", \"额度线 Y\" },\n { \"CodexRadarIqRingOffsetX\", \"IQ 环 X\" },\n { \"CodexRadarIqRingOffsetY\", \"IQ 环 Y\" },\n { \"CodexRadarIqTextOffsetX\", \"IQ 字 X\" },\n { \"CodexRadarIqTextOffsetY\", \"IQ 字 Y\" },\n { \"CodexRadarPublicJsonEnabled\", \"公开 JSON 摘要\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"首页 HTML 回退\" },\n { \"CodexRadarRssFallbackEnabled\", \"RSS 重置提醒\" },\n { \"CodexRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"DeepSeekApiKeyRevision\", \"DeepSeek 配置\" },\n { \"CodexModelIqTestEnabled\", \"用测试值代替实时 IQ(调试用)\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过数\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"IQ 基准自动跟随网站\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过数\" },\n { \"CodexModelIqBaselineValidTasks\", \"IQ 基准总题数\" },\n { \"CodexModelEfficiencyTestEnabled\", \"用测试值代替实时效率(调试用)\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"PowerThermalTransparencyPercent\", \"功耗温度透明度\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkMonitorTransparencyPercent\", \"网络监控透明度\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"立即刷新 GFW 检测\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测间隔\" },\n { \"ConnectionCheckTransparencyPercent\", \"连接检测透明度\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"连接检测边框透明度\" },\n { \"ConnectionCheckManualRefreshToken\", \"立即刷新连接检测\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"OperationButtonSize\", \"按钮大小\" },\n { \"OperationBackgroundTransparencyPercent\", \"操作面板透明度\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过数\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过数\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"ForceShowForegroundFpsEnabled\", \"调试用,强制显示前台 FPS 信息。\" },\n { \"AiRequestProtectionAutoEnabled\", \"网络监控判定为 GFW 明确阻断时,阻断本程序发往 OpenAI、ChatGPT、Claude 和 Anthropic 的请求。\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"手动启用后立即阻断本程序相关 AI 请求;也可在左下角程序设置按钮双击打开的 AI 快速选单中切换。\" },\n { \"CodexQuotaPlanEnabled\", \"按本地 Codex 剩余额度缓存自动暂停或恢复选中的 Codex goal。\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度百分比的比较方向;默认小于 3%。\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时额度百分比的比较方向;默认小于 90%。\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"额度计划触发后,选择额度恢复时看周额度、5 小时额度,还是两者都恢复。\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"额度恢复时优先恢复本程序上次因额度计划暂停的 goal;关闭后使用恢复列表。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityRevealDelaySeconds\", \"鼠标离开后继续隐藏的秒数。\" },\n { \"HoverOpacityRevealResetSeconds\", \"短时间重新靠近时,保持隐藏的重置秒数。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"反向隐藏临时恢复后,鼠标移开多久回到隐藏。\" },\n { \"AutoHoverOpacityIdleEnabled\", \"鼠标一段时间不动时自动进入隐藏透明度。\" },\n { \"AutoHoverOpacityIdleSeconds\", \"范围 1-300 秒,达到后自动隐藏。\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"有非 SeelenUI 应用最大化时自动进入隐藏透明度。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarSoftwareMode\", \"自动按前台窗口选择 CODEX 或 CLAUDE;也可强制固定检测软件。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"CodexRadarModelVersion\", \"模型目录版本,日常保持默认。\" },\n { \"RadarClockAutoSwitchModelEnabled\", \"当 Codex/Claude 时钟跨过完整周期仍未获取当前模型 IQ 更新时,自动切到同站点当天最近刷新 IQ 的模型。\" },\n { \"RadarClockTimeDisplayMode\", \"控制 Codex/Claude Radar 时钟中心下方时间:UTC、当前本机时间、上次尝试刷新,或上次实际 IQ 刷新。\" },\n { \"CodexRadarPublicJsonEnabled\", \"读取 current.json 的公开摘要层,包含窗口、预测和 API 可用性说明。\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"当公开 JSON 不含 model_iq 时,从首页明文模块补齐 IQ、效率和模型目录。\" },\n { \"CodexRadarRssFallbackEnabled\", \"读取 feed.xml 的重置提醒;关闭后不会用 RSS 触发额度重置保护。\" },\n { \"CodexRadarServiceProbeToken\", \"点击后探测公开摘要、授权 API、首页 HTML 和 RSS,并写入本地诊断结果。\" },\n { \"CodexRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"CodexModelIqTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelIqTestPassed\", \"手动指定 IQ 测试通过数。\" },\n { \"CodexModelIqBaselineAutoEnabled\", \"开启时自动读取网站有效题数和常态区推导 n/N;关闭后使用下方手动 n/N。\" },\n { \"CodexModelIqBaselineMode\", \"选择 IQ 对比基准的来源。\" },\n { \"CodexModelIqBaselinePassed\", \"手动指定 IQ 基准通过数 n。\" },\n { \"CodexModelIqBaselineValidTasks\", \"手动指定 IQ 基准总题数 N;仅在关闭自动基准时用于 IQ 环。\" },\n { \"CodexModelEfficiencyTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"手动指定 Token 效率测试百分比。\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"手动指定时间效率测试百分比。\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"选择 Token 效率对比基准的来源。\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"手动指定 Token 效率基准通过数。\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"手动指定 Token 效率基准消耗。\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"选择时间效率对比基准的来源。\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"手动指定时间效率基准通过数。\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"手动指定时间效率基准秒数。\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexRadarEnabled\", \"关闭后会释放 Codex Radar 分层窗口;默认开启以保持现有桌面不变。\" },\n { \"DisplayTimeZoneMode\", \"控制窗口里时间文字使用本机、北京时间还是指定时区。\" },\n { \"DisplayTimeZoneId\", \"Windows 时区 ID,指定时区模式下生效。\" },\n { \"ClaudeRadarEnabled\", \"开启独立 Claude Radar 分层窗口;默认关闭,避免升级后改变现有桌面。\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 站点内部 m* 模型键;留空时按站点当前目录自动选择。\" },\n { \"ClaudeRadarJsonEnabled\", \"读取 claudecoderadar.com/data/claude-code-radar.json 作为主数据源。\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"主 JSON 失败或缺少模型元数据时,顺序读取首页 MODEL_NAMES 作为弱回退;不会伪造 IQ、效率或额度。\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"读取 Claude Radar 社区体感分接口,并通过独立映射表关联模型。\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"站点额度趋势不完整时使用本地 7 天 JSONL 历史绘制额度线。\" },\n { \"ClaudeRadarServiceProbeToken\", \"点击后触发 Claude Radar 下次刷新,服务状态显示在窗口右侧。\" },\n { \"ClaudeRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"ClaudeRadarRenderVariant\", \"预留项,当前第一版固定使用 EvenRow 风格。\" },\n { \"MainWidgetRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"CodexRadarRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"NetworkMonitorRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"PowerThermalRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"ConnectionCheckRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"OperationRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"DeepSeekApiKeyRevision\", \"配置 DeepSeek API Key;密钥只写入本地文件,修订号用于触发即时刷新。\" },\n { \"CodexRadarManualLayoutEnabled\", \"开启后下方布局参数实时影响 Codex Radar 内部模块,不需要重启。\" },\n { \"CodexRadarManualLeftPercent\", \"调整效率/IQ/连接流程区与余额区的左右分配。\" },\n { \"CodexRadarManualGapPixels\", \"调整左侧区域和余额区之间的像素间距。\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"调整左侧效率/IQ 圆环右侧文字列宽。\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"调整余额两行圆环和重置时间的总宽度。\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"调整余额区右侧 IQ 圆环与降智文字列宽。\" },\n { \"CodexRadarManualTextScalePercent\", \"只影响 Codex Radar 内部状态文字和余额时间字号。\" },\n { \"CodexRadarManualRingScalePercent\", \"只影响 Codex Radar 内部效率、余额和 IQ 圆环大小。\" },\n { \"PowerThermalAutoSizeEnabled\", \"根据告警数量自动调整功耗温度窗口高度。\" },\n { \"PowerThermalAutoDirection\", \"自动调整时从哪个方向展开。\" },\n { \"PowerThermalVisibleAlertCount\", \"功耗温度窗口中最多显示的告警数量。\" },\n { \"ThermalTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"NetworkStatusTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测的自动刷新间隔。\" },\n { \"GfwProbeManualRefreshToken\", \"点击后请求立刻刷新 GFW 检测。\" },\n { \"ConnectionCheckManualRefreshToken\", \"点击后请求立刻刷新连接检测。\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测的自动刷新间隔。\" },\n { \"CloudEndpointTestSeed\", \"仅用于测试显示效果,日常保持默认。\" },\n { \"CloudStatusRegionMask\", \"云服务状态的地区过滤位掩码,日常保持默认。\" },\n { \"CleanIpBadgeTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"AlertTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"SeelenDockForegroundPulseEnabled\", \"需要时短暂拉前 Seelen Dock,避免被系统窗口压住。\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"指定显示器未连接时,将对应模块回退到当前主显示器。\" },\n { \"MainDisplayDeviceName\", \"留空使用当前主显示器;选择 DISPLAY 后按该显示器工作区定位。\" },\n { \"CodexRadarDisplayDeviceName\", \"留空使用当前主显示器;未连接且不回退时保留原显示器工作区。\" },\n { \"PowerThermalDisplayDeviceName\", \"留空使用当前主显示器;适合把功耗模块固定到副屏。\" },\n { \"NetworkMonitorDisplayDeviceName\", \"留空使用当前主显示器;适合把网络监控固定到副屏。\" },\n { \"ConnectionCheckDisplayDeviceName\", \"留空使用当前主显示器;适合把连接检测固定到副屏。\" },\n { \"OperationDisplayDeviceName\", \"留空使用当前主显示器;操作面板偏移量按目标显示器左下角计算。\" },\n { GlobalLayoutEditCommandName, \"打开全屏布局编辑遮罩,拖拽模块位置;Enter 保存,Esc 放弃。\" },\n { \"Width\", \"逻辑像素,主窗口宽度。\" },\n { \"Height\", \"逻辑像素,主窗口高度。\" },\n { \"LeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"BottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"CodexRadarWidth\", \"逻辑像素,Codex Radar 宽度。\" },\n { \"CodexRadarHeight\", \"逻辑像素,Codex Radar 高度。\" },\n { \"CodexRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"CodexRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ClaudeRadarWidth\", \"逻辑像素,Claude Radar 宽度。\" },\n { \"ClaudeRadarHeight\", \"逻辑像素,Claude Radar 高度。\" },\n { \"ClaudeRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ClaudeRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"PowerThermalWidth\", \"逻辑像素,功耗温度窗口宽度。\" },\n { \"PowerThermalHeight\", \"逻辑像素,功耗温度窗口高度。\" },\n { \"PowerThermalLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"PowerThermalBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"NetworkMonitorWidth\", \"逻辑像素,网络监控窗口宽度。\" },\n { \"NetworkMonitorHeight\", \"逻辑像素,网络监控窗口高度。\" },\n { \"NetworkMonitorLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"NetworkMonitorBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ConnectionCheckWidth\", \"逻辑像素,连接检测窗口宽度。\" },\n { \"ConnectionCheckHeight\", \"逻辑像素,连接检测窗口高度。\" },\n { \"ConnectionCheckLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ConnectionCheckBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"OperationLeftOffset\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"OperationBottomOffset\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"BackgroundTransparencyPercent\", \"只影响主窗口背景底色,数值越高越透明。\" },\n { \"ApplicationTransparencyPercent\", \"影响主窗口整体透明度,数值越高越透明。\" },\n { \"CodexRadarTransparencyPercent\", \"影响 Codex Radar 背景透明度。\" },\n { \"ClaudeRadarTransparencyPercent\", \"影响 Claude Radar 背景透明度。\" },\n { \"PowerThermalTransparencyPercent\", \"影响功耗温度窗口背景透明度。\" },\n { \"NetworkMonitorTransparencyPercent\", \"影响网络监控窗口背景透明度。\" },\n { \"ConnectionCheckTransparencyPercent\", \"影响连接检测窗口背景透明度。\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"影响连接检测三框边框透明度。\" },\n { \"OperationBackgroundTransparencyPercent\", \"影响左下角操作面板背景透明度。\" },\n { \"ShowCpu\", \"在主窗口显示 CPU 指标。\" },\n { \"ShowMemory\", \"在主窗口显示内存指标。\" },\n { \"ShowDisk\", \"在主窗口显示磁盘指标。\" },\n { \"ShowNetwork\", \"在主窗口显示网络指标。\" },\n { \"ShowGpu\", \"在主窗口显示 GPU 指标。\" },\n { \"ShowNpu\", \"在主窗口显示 NPU 指标。\" },\n { \"OperationPrimaryPanelMode\", \"自动模式会按 SeelenUI 运行态在 Windows 按钮和内存饼图之间切换;隐藏会让小按钮移到最左侧。\" },\n { \"OperationButtonSize\", \"左下角操作面板按钮的逻辑像素大小。\" },\n { \"OperationWindowsButtonEnabled\", \"SeelenUI 未运行时会自动隐藏;关闭后始终不显示左侧 Windows 按钮。\" },\n { \"OperationMemoryPieEnabled\", \"左侧 Windows 按钮隐藏时显示物理、虚拟和前台程序内存占用饼图。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.SettingsWarmTheme.ToggleTrackHover : DesignTokens.SettingsWarmTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsWarmTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsWarmTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsWarmTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = false;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = false;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + controlHeight + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(this.Width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, controlHeight);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n int controlTop = top + Math.Max(0, (contentHeight - controlHeight) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, controlHeight);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private int GetValueControlHeight(int controlWidth)\n {\n VariantPicker picker = this.valueControl as VariantPicker;\n if (picker != null)\n {\n return picker.GetPreferredHeightForWidth(controlWidth);\n }\n\n return this.valueControl.Height;\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── AdvancedSectionHeader ────────────────────────────────────────────\n // Clickable \"复杂选项\" divider: everything below it stays collapsed until\n // the user expands it, keeping everyday pages short. Searching bypasses it.\n private sealed class AdvancedSectionHeader : Panel\n {\n private readonly Font titleFont;\n private readonly Font hintFont;\n private bool expanded;\n private bool hover;\n\n public AdvancedSectionHeader(Font titleFont, Font hintFont)\n {\n this.titleFont = titleFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = MicaBase;\n this.Cursor = Cursors.Hand;\n }\n\n public int AdvancedRowCount { get; set; }\n\n public bool Expanded\n {\n get { return this.expanded; }\n set { this.expanded = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(this.hover ? CardHover : CardRest))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(StrokeColor))\n {\n g.DrawPath(pen, path);\n }\n\n // Chevron glyph: E70D = down, E76C = right (Segoe MDL2 / Fluent Icons).\n string chevron = this.expanded ? \"\\uE70D\" : \"\\uE76C\";\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(chevron, icoFont, brush, 20, iconY);\n }\n\n using (SolidBrush brush = new SolidBrush(TextPrimary))\n {\n g.DrawString(\"复杂选项\", this.titleFont, brush, 58, (this.Height - this.titleFont.Height) / 2f - 8);\n }\n\n string hint = this.expanded\n ? \"点击收起\"\n : \"不常用的精细调整与测试项\" + (this.AdvancedRowCount > 0 ? \"(\" + this.AdvancedRowCount.ToString(CultureInfo.InvariantCulture) + \" 项)\" : string.Empty);\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n g.DrawString(hint, this.hintFont, brush, 58, this.Height / 2f + 2);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── VariantPicker ───────────────────────────────────────────────────\n // Renders per-window visual variants as cards. Sample PNGs are loaded lazily by the outer form\n // only for real owner-backed settings windows, so binding self-tests never construct render forms.\n private sealed class VariantPicker : Panel\n {\n private const int CardHeight = 124;\n private const int CardGap = 10;\n private const int ThumbnailHeight = 78;\n\n private readonly Type enumType;\n private readonly Font labelFont;\n private readonly Font hintFont;\n private readonly List<VariantChoice> choices = new List<VariantChoice>();\n private object selectedValue;\n private int hoverIndex = -1;\n private bool samplesAttempted;\n\n public event EventHandler ValueChanged;\n public event EventHandler PreferredHeightChanged;\n\n public VariantPicker(string propertyName, Type enumType, Font labelFont, Font hintFont)\n {\n this.enumType = enumType;\n this.labelFont = labelFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n\n Array values = Enum.GetValues(enumType);\n for (int i = 0; i < values.Length; i++)\n {\n object value = values.GetValue(i);\n this.choices.Add(new VariantChoice(value, new EnumOption(value).ToString()));\n if (i == 0)\n {\n this.selectedValue = value;\n }\n }\n\n UpdatePreferredHeight();\n }\n\n public void SetSelectedValue(object value)\n {\n if (value == null || !this.enumType.IsInstanceOfType(value))\n {\n if (this.choices.Count > 0)\n {\n this.selectedValue = this.choices[0].Value;\n }\n }\n else\n {\n this.selectedValue = value;\n }\n\n this.Invalidate();\n }\n\n public object GetSelectedValue()\n {\n return this.selectedValue;\n }\n\n public int GetPreferredHeightForWidth(int width)\n {\n int columns = GetColumnCount(width);\n int rows = Math.Max(1, (this.choices.Count + columns - 1) / columns);\n return rows * CardHeight + Math.Max(0, rows - 1) * CardGap;\n }\n\n public bool HasMissingSamples(string directory, string prefix)\n {\n if (this.samplesAttempted)\n {\n return false;\n }\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n string path = GetSamplePath(directory, prefix, this.choices[i].Value);\n if (!System.IO.File.Exists(path))\n {\n return true;\n }\n }\n\n return false;\n }\n\n public void LoadSamples(string directory, string prefix)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n VariantChoice choice = this.choices[i];\n string path = GetSamplePath(directory, prefix, choice.Value);\n if (!System.IO.File.Exists(path))\n {\n continue;\n }\n\n try\n {\n using (Image original = Image.FromFile(path))\n {\n Image old = choice.Image;\n choice.Image = new Bitmap(original);\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n catch\n {\n Image old = choice.Image;\n choice.Image = null;\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n public void MarkSamplesUnavailable()\n {\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n UpdatePreferredHeight();\n }\n\n protected override void OnMouseMove(MouseEventArgs e)\n {\n int nextHover = GetChoiceIndexAt(e.Location);\n if (nextHover != this.hoverIndex)\n {\n this.hoverIndex = nextHover;\n this.Invalidate();\n }\n\n base.OnMouseMove(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hoverIndex = -1;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n protected override void OnMouseClick(MouseEventArgs e)\n {\n base.OnMouseClick(e);\n int index = GetChoiceIndexAt(e.Location);\n if (index < 0 || index >= this.choices.Count)\n {\n return;\n }\n\n object nextValue = this.choices[index].Value;\n if (object.Equals(this.selectedValue, nextValue))\n {\n return;\n }\n\n this.selectedValue = nextValue;\n this.Invalidate();\n if (this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.InterpolationMode = InterpolationMode.HighQualityBicubic;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n Rectangle rect = GetCardBounds(i);\n if (rect.Width <= 0 || rect.Height <= 0)\n {\n continue;\n }\n\n VariantChoice choice = this.choices[i];\n bool selected = object.Equals(choice.Value, this.selectedValue);\n bool hover = i == this.hoverIndex;\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(hover ? CardHover : ControlBg))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(selected ? AccentClr : ControlBorder, selected ? 2.0f : 1.0f))\n {\n g.DrawPath(pen, path);\n }\n\n Rectangle thumb = new Rectangle(rect.Left + 8, rect.Top + 8, rect.Width - 16, ThumbnailHeight);\n DrawChoiceThumbnail(g, choice, thumb);\n\n Rectangle labelRect = new Rectangle(rect.Left + 10, thumb.Bottom + 7, rect.Width - 20, 24);\n using (StringFormat format = new StringFormat())\n using (SolidBrush textBrush = new SolidBrush(selected ? TextPrimary : TextSecondary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(choice.Label, this.labelFont, textBrush, labelRect, format);\n }\n }\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (this.choices[i].Image != null)\n {\n this.choices[i].Image.Dispose();\n this.choices[i].Image = null;\n }\n }\n }\n\n base.Dispose(disposing);\n }\n\n private void DrawChoiceThumbnail(Graphics g, VariantChoice choice, Rectangle thumb)\n {\n using (GraphicsPath thumbPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n using (SolidBrush thumbBg = new SolidBrush(MicaBase))\n {\n g.FillPath(thumbBg, thumbPath);\n }\n\n if (choice.Image == null)\n {\n using (StringFormat format = new StringFormat())\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(\"纯文字模式\", this.hintFont, brush, thumb, format);\n }\n return;\n }\n\n Rectangle target = FitImage(choice.Image.Size, thumb);\n GraphicsState state = g.Save();\n using (GraphicsPath clipPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n {\n g.SetClip(clipPath);\n g.DrawImage(choice.Image, target);\n }\n g.Restore(state);\n }\n\n private void UpdatePreferredHeight()\n {\n int oldHeight = this.Height;\n int nextHeight = GetPreferredHeightForWidth(this.Width);\n if (nextHeight != oldHeight)\n {\n this.Height = nextHeight;\n if (this.PreferredHeightChanged != null)\n {\n this.PreferredHeightChanged(this, EventArgs.Empty);\n }\n }\n }\n\n private int GetColumnCount()\n {\n return GetColumnCount(this.Width);\n }\n\n private static int GetColumnCount(int width)\n {\n width = Math.Max(1, width);\n if (width >= 520)\n {\n return 3;\n }\n\n if (width >= 340)\n {\n return 2;\n }\n\n return 1;\n }\n\n private Rectangle GetCardBounds(int index)\n {\n int columns = GetColumnCount();\n int col = index % columns;\n int row = index / columns;\n int cardWidth = Math.Max(86, (Math.Max(1, this.Width) - (columns - 1) * CardGap) / columns);\n return new Rectangle(col * (cardWidth + CardGap), row * (CardHeight + CardGap), cardWidth, CardHeight);\n }\n\n private int GetChoiceIndexAt(Point point)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (GetCardBounds(i).Contains(point))\n {\n return i;\n }\n }\n\n return -1;\n }\n\n private static string GetSamplePath(string directory, string prefix, object value)\n {\n string variant = Convert.ToString(value, CultureInfo.InvariantCulture);\n return System.IO.Path.Combine(directory, prefix + \"-\" + variant.ToLowerInvariant() + \".png\");\n }\n\n private static Rectangle FitImage(Size imageSize, Rectangle bounds)\n {\n if (imageSize.Width <= 0 || imageSize.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0)\n {\n return bounds;\n }\n\n float sx = bounds.Width / (float)imageSize.Width;\n float sy = bounds.Height / (float)imageSize.Height;\n float scale = Math.Min(sx, sy);\n int width = Math.Max(1, (int)Math.Round(imageSize.Width * scale));\n int height = Math.Max(1, (int)Math.Round(imageSize.Height * scale));\n return new Rectangle(\n bounds.Left + (bounds.Width - width) / 2,\n bounds.Top + (bounds.Height - height) / 2,\n width,\n height);\n }\n\n private sealed class VariantChoice\n {\n public VariantChoice(object value, string label)\n {\n this.Value = value;\n this.Label = label ?? string.Empty;\n }\n\n public object Value;\n public string Label;\n public Image Image;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public AdvancedSectionHeader AdvancedHeader;\n public bool AdvancedExpanded;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public bool Advanced;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Name = property == null ? string.Empty : property.Name;\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public SettingEditor(string name, SettingRow card, Control control)\n {\n this.Name = name ?? string.Empty;\n this.Property = null;\n this.Card = card;\n this.Control = control;\n }\n\n public string Name { get; private set; }\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class ModelOption\n {\n public ModelOption(string key, string label, bool available, bool missingFromCatalog)\n {\n this.Key = CodexRadarModelCatalog.NormalizeModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.MissingFromCatalog = missingFromCatalog;\n }\n\n public string Key { get; private set; }\n private string Label { get; set; }\n private bool Available { get; set; }\n private bool MissingFromCatalog { get; set; }\n\n public override string ToString()\n {\n if (this.MissingFromCatalog)\n {\n return this.Label + \"(未在目录)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class ClaudeModelOption\n {\n public ClaudeModelOption(string key, string label, bool available, bool pending)\n {\n this.Key = WidgetSettings.NormalizeClaudeRadarModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.Pending = pending;\n }\n\n public string Key { get; private set; }\n public string Label { get; private set; }\n public bool Available { get; private set; }\n public bool Pending { get; private set; }\n\n public override string ToString()\n {\n if (this.Key.Length == 0)\n {\n return this.Label;\n }\n\n if (this.Pending)\n {\n return this.Label + \"(待映射)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class DisplayOption\n {\n public DisplayOption(string value, string label)\n {\n this.Value = WidgetSettings.NormalizeDisplayDeviceName(value);\n this.Label = label ?? string.Empty;\n }\n\n public string Value { get; private set; }\n private string Label { get; set; }\n\n public override string ToString()\n {\n return this.Label;\n }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n if (this.Value is OperationPrimaryPanelMode)\n {\n OperationPrimaryPanelMode mode = (OperationPrimaryPanelMode)this.Value;\n if (mode == OperationPrimaryPanelMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == OperationPrimaryPanelMode.WindowsButton)\n {\n return \"Windows 按钮\";\n }\n\n if (mode == OperationPrimaryPanelMode.MemoryPie)\n {\n return \"内存饼图\";\n }\n\n if (mode == OperationPrimaryPanelMode.Hidden)\n {\n return \"隐藏\";\n }\n }\n\n if (this.Value is CodexRadarSoftwareMode)\n {\n CodexRadarSoftwareMode mode = (CodexRadarSoftwareMode)this.Value;\n if (mode == CodexRadarSoftwareMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == CodexRadarSoftwareMode.Codex)\n {\n return \"CODEX\";\n }\n\n if (mode == CodexRadarSoftwareMode.Claude)\n {\n return \"CLAUDE\";\n }\n }\n\n if (this.Value is RadarClockTimeDisplayMode)\n {\n RadarClockTimeDisplayMode mode = (RadarClockTimeDisplayMode)this.Value;\n if (mode == RadarClockTimeDisplayMode.Utc)\n {\n return \"UTC\";\n }\n\n if (mode == RadarClockTimeDisplayMode.CurrentLocal)\n {\n return \"当前时间\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastAttemptRefresh)\n {\n return \"上次尝试刷新\";\n }\n\n if (mode == RadarClockTimeDisplayMode.LastActualRefresh)\n {\n return \"上次实际刷新\";\n }\n }\n\n if (this.Value is CodexQuotaPlanComparison)\n {\n CodexQuotaPlanComparison comparison = (CodexQuotaPlanComparison)this.Value;\n if (comparison == CodexQuotaPlanComparison.GreaterThan)\n {\n return \"大于\";\n }\n\n return \"小于\";\n }\n\n if (this.Value is CodexQuotaPlanResumeConditionMode)\n {\n CodexQuotaPlanResumeConditionMode mode = (CodexQuotaPlanResumeConditionMode)this.Value;\n if (mode == CodexQuotaPlanResumeConditionMode.WeeklyOnly)\n {\n return \"仅周额度\";\n }\n\n if (mode == CodexQuotaPlanResumeConditionMode.FiveHourOnly)\n {\n return \"仅 5 小时额度\";\n }\n\n return \"周额度与 5 小时额度\";\n }\n\n // All six per-window render-variant enums share member names for the common variants\n // (Classic plus the four OLED-safe restyle schemes); CodexRadarRenderVariant additionally\n // carries EvenGrid/EvenRow. Label by member name once instead of duplicating per enum type.\n if (this.Value is CodexRadarRenderVariant ||\n this.Value is MainWidgetRenderVariant ||\n this.Value is NetworkMonitorRenderVariant ||\n this.Value is PowerThermalRenderVariant ||\n this.Value is ConnectionCheckRenderVariant ||\n this.Value is OperationRenderVariant)\n {\n string variantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (variantName == \"Classic\") return \"经典布局\";\n if (variantName == \"EvenGrid\") return \"均布六格\";\n if (variantName == \"EvenRow\") return \"均布单行\";\n if (variantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (variantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (variantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (variantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n }\n\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "0c4f1c403287b03d48c303a4add2c8fbd9d0410a": {"t": 1785756678.2154536, "n": 186605, "h": "4c76caafcd946625", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int WmSettingChange = 0x001A;\n private const int WmDisplayChange = 0x007E;\n private const int EmSetCueBanner = 0x1501;\n private const int ReferenceWorkAreaWidth = 2880;\n private const int ReferenceWorkAreaHeight = 1740;\n private const int PreferredClientWidth = 1888;\n private const int PreferredClientHeight = 1312;\n private const int MinimumClientWidth = 760;\n private const int MinimumClientHeight = 560;\n private const int ScreenMargin = 80;\n private const int DefaultNavWidth = 409;\n private const int MediumNavWidth = 300;\n private const int CompactNavWidth = 220;\n private const int NavItemHeight = 60;\n private const int ClaudeRadarModelGridColumns = 5;\n private const int ClaudeRadarModelButtonWidth = 72;\n private const int ClaudeRadarModelButtonHeight = 38;\n private const int ClaudeRadarModelButtonGap = 8;\n private const string ClaudeRadarModelGridName = \"ClaudeRadarModelGrid\";\n private const string GlobalLayoutEditCommandName = \"GlobalLayoutEditCommand\";\n\n private static readonly Color MicaBase = DesignTokens.SettingsWarmTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.SettingsWarmTheme.CardRest;\n private static readonly Color CardHover = DesignTokens.SettingsWarmTheme.CardHover;\n private static readonly Color StrokeColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.SettingsWarmTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.SettingsWarmTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.SettingsWarmTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.SettingsWarmTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.SettingsWarmTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.SettingsWarmTheme.Accent;\n private static readonly Color AccentHover = DesignTokens.SettingsWarmTheme.AccentHover;\n private static readonly Color AccentPressed = DesignTokens.SettingsWarmTheme.AccentPressed;\n private static readonly Color ErrorClr = DesignTokens.SettingsWarmTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private TableLayoutPanel bodyLayout;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool dirty;\n private bool messageFilterRegistered;\n private bool unsavedPreviewConsumed;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n public bool TryConsumeUnsavedPreview(out WidgetSettings settings)\n {\n settings = null;\n if (this.saved || this.OwnerFormClosing || this.unsavedPreviewConsumed)\n {\n return false;\n }\n\n this.unsavedPreviewConsumed = true;\n settings = this.baseline.Clone();\n settings.Normalize();\n return true;\n }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n ApplicationIcon.ApplyTo(this);\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = true;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n ApplyDynamicResolutionSizing(true);\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n ApplyDynamicResolutionSizing(false);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnResize(EventArgs e)\n {\n base.OnResize(e);\n ApplyResponsiveShellLayout();\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n if (m.Msg == WmDisplayChange || m.Msg == WmSettingChange)\n {\n ScheduleDynamicResolutionSizing();\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n if (this.dirty && !this.saved && this.owner != null && !this.OwnerFormClosing)\n {\n DialogResult result = MessageBox.Show(\n this,\n \"设置还没有保存。要先保存再关闭吗?\",\n \"未保存的更改\",\n MessageBoxButtons.YesNoCancel,\n MessageBoxIcon.Warning,\n MessageBoxDefaultButton.Button1);\n if (result == DialogResult.Cancel)\n {\n e.Cancel = true;\n return;\n }\n\n if (result == DialogResult.Yes && !TrySaveSettings())\n {\n e.Cancel = true;\n return;\n }\n }\n\n this.previewTimer.Stop();\n WidgetSettings revertSettings;\n if (this.owner != null && TryConsumeUnsavedPreview(out revertSettings))\n {\n try\n {\n this.owner.RevertSettings(revertSettings);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.SettingsWarmTheme.ErrorText; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n this.bodyLayout = body;\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetNavigationWidthForClientWidth(this.ClientSize.Width)));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n SetDirtyState(true);\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n if (TrySaveSettings())\n {\n ShowStatus(\"保存完成\", false);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n return SettingsFluentResources.CreateCommandButton(text, primary, GetUiFont(9.5f, FontStyle.Bold));\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n // 分组名以 '!' 开头 = 收进该页底部的「复杂选项」折叠区。\n AddPageGrouped(\"\\uE115\", \"系统\", \"开机启动、性能和窗口基础行为。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\" },\n new string[] { \"AI 请求阻断\", \"AiRequestProtectionAutoEnabled\", \"AiRequestProtectionManualBlockEnabled\" },\n new string[] { \"!Codex 额度计划\", \"CodexQuotaPlanEnabled\", \"CodexQuotaPlanWeeklyComparison\", \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\", \"CodexQuotaPlanFiveHourThresholdPercent\", \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\" },\n new string[] { \"!恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" },\n new string[] { \"!调试\", \"ForceShowForegroundFpsEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7C2\", \"布局与位置\", \"所有浮窗的位置、大小和所在显示器;推荐用可视化编辑直接拖拽。\", new string[][]\n {\n new string[] { \"可视化编辑\", GlobalLayoutEditCommandName },\n new string[] { \"显示器分配\", \"FallbackDisconnectedDisplaysEnabled\", \"MainDisplayDeviceName\", \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\", \"PowerThermalDisplayDeviceName\", \"NetworkMonitorDisplayDeviceName\", \"ConnectionCheckDisplayDeviceName\", \"OperationDisplayDeviceName\" },\n new string[] { \"!主窗口位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"!Codex Radar 位置\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\" },\n new string[] { \"!Claude Radar 位置\", \"ClaudeRadarWidth\", \"ClaudeRadarHeight\", \"ClaudeRadarLeftX\", \"ClaudeRadarBottomY\" },\n new string[] { \"!功耗温度位置\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\" },\n new string[] { \"!网络监控位置\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\" },\n new string[] { \"!连接检测位置\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\" },\n new string[] { \"!操作面板位置\", \"OperationLeftOffset\", \"OperationBottomOffset\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与防烧屏\", \"鼠标靠近时隐藏、空闲自动隐藏和 OLED 防烧屏。\", new string[][]\n {\n new string[] { \"鼠标靠近时隐藏\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" },\n new string[] { \"!延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"!覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" }\n });\n\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口显示哪些指标、透明度和外观。\", new string[][]\n {\n new string[] { \"显示哪些指标\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" },\n new string[] { \"外观风格\", \"MainWidgetRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"Codex 用量监控:模型、时区和外观。\", new string[][]\n {\n // 手动布局与元素偏移分组已从设置界面隐藏(改用渲染变体切换布局)。\n // 对应 WidgetSettings 属性仍保留,Classic 布局若通过 settings.ini 设置仍生效;\n // 均布变体本就忽略这些设置。恢复入口时把下方分组加回,并同步 VerifySelfTest 必需绑定。\n new string[] { \"窗口\", \"CodexRadarEnabled\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarSoftwareMode\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"DeepSeekApiKeyRevision\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"外观风格\", \"CodexRadarRenderVariant\" },\n new string[] { \"!网站数据源\", \"CodexRadarPublicJsonEnabled\", \"CodexRadarHtmlFallbackEnabled\", \"CodexRadarRssFallbackEnabled\", \"CodexRadarServiceProbeToken\" },\n new string[] { \"!IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineMode\", \"CodexModelIqBaselinePassed\" },\n new string[] { \"!效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"!随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n\n AddPageGrouped(\"\\uE8D4\", \"Claude Radar\", \"Claude 用量监控独立窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"ClaudeRadarEnabled\", \"ClaudeRadarTransparencyPercent\" },\n new string[] { \"模型\", \"ClaudeRadarModelKey\" },\n new string[] { \"!网站数据源\", \"ClaudeRadarJsonEnabled\", \"ClaudeRadarHomepageFallbackEnabled\", \"ClaudeRadarCommunityRatingsEnabled\", \"ClaudeRadarLocalQuotaFallbackEnabled\", \"ClaudeRadarServiceProbeToken\" },\n new string[] { \"!随机测试\", \"ClaudeRadarRandomTestEnabled\", \"ClaudeRadarRandomTestAutoRefresh\", \"ClaudeRadarRandomTestRefreshToken\" },\n new string[] { \"!外观风格(预留)\", \"ClaudeRadarRenderVariant\" }\n });\n\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"透明度\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"外观风格\", \"PowerThermalRenderVariant\" },\n new string[] { \"!测试\", \"ThermalTestMode\" }\n });\n\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW 检测、云服务和出口身份。\", new string[][]\n {\n new string[] { \"网络监控\", \"NetworkMonitorAdapterId\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\" },\n new string[] { \"连接检测\", \"ConnectionCheckIntervalSeconds\", \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\" },\n new string[] { \"网络监控外观\", \"NetworkMonitorRenderVariant\" },\n new string[] { \"连接检测外观\", \"ConnectionCheckRenderVariant\" },\n new string[] { \"!手动刷新\", \"GfwProbeManualRefreshToken\", \"ConnectionCheckManualRefreshToken\" },\n new string[] { \"!云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"!测试\", \"CleanIpBadgeTestMode\", \"NetworkStatusTestMode\" }\n });\n\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板的按钮、透明度和外观。\", new string[][]\n {\n new string[] { \"按钮与面板\", \"OperationButtonSize\", \"OperationPrimaryPanelMode\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"外观风格\", \"OperationRenderVariant\" },\n new string[] { \"!测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n // A leading '!' marks the group as advanced: it renders collapsed under the\n // per-page \"复杂选项\" header until the user expands it (or searches).\n bool advanced = groupTitle.Length > 0 && groupTitle[0] == '!';\n if (advanced)\n {\n groupTitle = groupTitle.Substring(1);\n }\n\n if (advanced && page.AdvancedHeader == null)\n {\n AdvancedSectionHeader advancedHeader = new AdvancedSectionHeader(GetUiFont(10.0f, FontStyle.Bold), GetUiFont(8.5f));\n advancedHeader.Width = 1152;\n advancedHeader.Height = 64;\n advancedHeader.Margin = new Padding(0, 32, 0, 6);\n advancedHeader.Click += delegate { ToggleAdvancedSection(page); };\n page.AdvancedHeader = advancedHeader;\n stack.Controls.Add(advancedHeader);\n }\n\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n group.Advanced = advanced;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n titleLabel.Visible = !advanced;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n card.Visible = !advanced;\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.AdvancedRowCount = CountAdvancedRows(page);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = Math.Max(160, GetNavigationWidthForClientWidth(this.ClientSize.Width) - 24);\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n private static int CountAdvancedRows(CategoryPage page)\n {\n int count = 0;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n if (page.Groups[g].Advanced)\n {\n count += page.Groups[g].Editors.Count;\n }\n }\n\n return count;\n }\n\n private void ToggleAdvancedSection(CategoryPage page)\n {\n page.AdvancedExpanded = !page.AdvancedExpanded;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Expanded = page.AdvancedExpanded;\n }\n\n ApplyAdvancedVisibility(page);\n LayoutPage(page);\n }\n\n private void ApplyAdvancedVisibility(CategoryPage page)\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n if (query.Length > 0)\n {\n // Search visibility is authoritative while a query is active.\n ApplySearchFilter();\n return;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (!group.Advanced)\n {\n continue;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = page.AdvancedExpanded;\n }\n\n if (group.Card != null)\n {\n group.Card.Visible = page.AdvancedExpanded;\n if (page.AdvancedExpanded)\n {\n group.Card.LayoutRows();\n }\n }\n }\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n if (string.Equals(propertyName, GlobalLayoutEditCommandName, StringComparison.Ordinal))\n {\n return BuildGlobalLayoutEditEditor();\n }\n\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private SettingEditor BuildGlobalLayoutEditEditor()\n {\n Button button = BuildCommandButton(\"全局编辑\", false);\n button.Width = 227;\n button.Height = 54;\n button.Click += delegate { OpenGlobalLayoutEditorFromSettings(); };\n\n SettingRow card = new SettingRow(button, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(GlobalLayoutEditCommandName);\n card.HintLabel.Text = GetSettingHint(GlobalLayoutEditCommandName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(GlobalLayoutEditCommandName, card, button);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n if (property.Name.EndsWith(\"RenderVariant\", StringComparison.Ordinal) &&\n !property.Name.StartsWith(\"ClaudeRadar\", StringComparison.Ordinal))\n {\n return BuildVariantPicker(property);\n }\n\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildCodexRadarModelCombo();\n }\n\n if (type == typeof(string) && string.Equals(property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n return BuildClaudeRadarModelGridSelector();\n }\n\n if (type == typeof(string) && IsDisplayDeviceSetting(property.Name))\n {\n return BuildDisplayDeviceCombo();\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n if (string.Equals(property.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(GetDeepSeekApiKeyButtonText(), false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate { OpenDeepSeekApiKeyDialog(button); };\n return button;\n }\n\n if (string.Equals(property.Name, \"CodexRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Codex Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (string.Equals(property.Name, \"ClaudeRadarServiceProbeToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即检测\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"Claude Radar 服务检测已启动,结果写入本地诊断文件。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n if (type == typeof(int) && property.Name.EndsWith(\"RefreshToken\", StringComparison.Ordinal))\n {\n Button button = BuildCommandButton(\"立即刷新\", false);\n button.Width = 227;\n button.Height = 54;\n button.Tag = 0;\n button.Click += delegate\n {\n int token = button.Tag is int ? (int)button.Tag : 0;\n button.Tag = token == int.MaxValue ? 1 : token + 1;\n ShowStatus(\"刷新请求已发送。\", false);\n OnSettingChanged();\n };\n return button;\n }\n\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n private Control BuildVariantPicker(PropertyInfo property)\n {\n VariantPicker picker = new VariantPicker(property.Name, property.PropertyType, GetUiFont(8.8f, FontStyle.Bold), GetUiFont(8.0f));\n picker.Width = 560;\n picker.ValueChanged += delegate { OnSettingChanged(); };\n picker.PreferredHeightChanged += delegate { RelayoutSettingGroup(picker); };\n return picker;\n }\n\n private Control BuildDisplayDeviceCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n combo.Items.Add(new DisplayOption(string.Empty, \"主显示器/自动\"));\n Screen[] screens = Screen.AllScreens;\n for (int i = 0; i < screens.Length; i++)\n {\n Screen screen = screens[i];\n Rectangle bounds = screen.Bounds;\n string label = string.Format(\n CultureInfo.InvariantCulture,\n \"{0} {1}x{2} @ {3},{4}{5}\",\n screen.DeviceName,\n bounds.Width,\n bounds.Height,\n bounds.Left,\n bounds.Top,\n screen.Primary ? \" 主屏\" : string.Empty);\n combo.Items.Add(new DisplayOption(screen.DeviceName, label));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildCodexRadarModelCombo()\n {\n ComboBox combo = new ComboBox();\n combo.Width = 400;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n\n List<CodexRadarModelInfo> models = CodexRadarModelCatalog.LoadModels();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n CodexRadarModelInfo model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = CodexRadarModelCatalog.NormalizeModelKey(model.Key);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n combo.Items.Add(new ModelOption(\n key,\n CodexRadarModelCatalog.GetDisplayLabel(model.Label, key),\n model.Available,\n false));\n keys.Add(key);\n }\n\n string defaultKey = CodexRadarModelCatalog.DefaultModelKey;\n if (!keys.Contains(defaultKey))\n {\n combo.Items.Insert(0, new ModelOption(\n defaultKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, defaultKey),\n true,\n false));\n }\n\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n private Control BuildClaudeRadarModelGridSelector()\n {\n Panel panel = new Panel();\n panel.Width = 400;\n panel.BackColor = Color.Transparent;\n panel.Resize += delegate { LayoutClaudeRadarModelPanel(panel); };\n\n FlowLayoutPanel grid = new FlowLayoutPanel();\n grid.Name = ClaudeRadarModelGridName;\n grid.Width = panel.Width;\n grid.Location = new Point(0, 0);\n grid.Margin = new Padding(0);\n grid.Padding = new Padding(0);\n grid.FlowDirection = FlowDirection.LeftToRight;\n grid.WrapContents = true;\n grid.AutoSize = false;\n grid.BackColor = Color.Transparent;\n panel.Controls.Add(grid);\n\n Button edit = BuildCommandButton(\"编辑映射\", false);\n edit.Width = panel.Width;\n edit.Height = 54;\n edit.Click += delegate\n {\n using (ClaudeRadarModelMapEditorForm dialog = new ClaudeRadarModelMapEditorForm())\n {\n if (dialog.ShowDialog(this) == DialogResult.OK)\n {\n string selected = GetClaudeRadarModelGridValue(grid);\n PopulateClaudeRadarModelGrid(grid, selected);\n LayoutClaudeRadarModelPanel(panel);\n RelayoutSettingGroup(panel);\n ShowStatus(\"Claude Radar 模型映射已保存。\", false);\n OnSettingChanged();\n }\n }\n };\n panel.Controls.Add(edit);\n PopulateClaudeRadarModelGrid(grid, string.Empty);\n LayoutClaudeRadarModelPanel(panel);\n return panel;\n }\n\n private void PopulateClaudeRadarModelGrid(FlowLayoutPanel grid, string selectedKey)\n {\n if (grid == null)\n {\n return;\n }\n\n string normalizedSelected = WidgetSettings.NormalizeClaudeRadarModelKey(selectedKey);\n List<ClaudeModelOption> options = BuildClaudeRadarModelOptions(normalizedSelected);\n bool selectedExists = false;\n for (int i = 0; i < options.Count; i++)\n {\n if (string.Equals(options[i].Key, normalizedSelected, StringComparison.OrdinalIgnoreCase))\n {\n selectedExists = true;\n break;\n }\n }\n\n if (!selectedExists)\n {\n normalizedSelected = string.Empty;\n }\n\n grid.Tag = normalizedSelected;\n grid.SuspendLayout();\n try\n {\n while (grid.Controls.Count > 0)\n {\n Control old = grid.Controls[0];\n grid.Controls.RemoveAt(0);\n old.Dispose();\n }\n\n int slots = Math.Max(\n ClaudeRadarModelGridColumns,\n ((options.Count + ClaudeRadarModelGridColumns - 1) / ClaudeRadarModelGridColumns) * ClaudeRadarModelGridColumns);\n for (int i = 0; i < slots; i++)\n {\n ClaudeModelOption option = i < options.Count ? options[i] : null;\n Button button = BuildClaudeRadarModelButton(option);\n button.Margin = new Padding(0, 0, i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap, ClaudeRadarModelButtonGap);\n grid.Controls.Add(button);\n }\n\n ApplyClaudeRadarModelGridSelection(grid);\n int rows = Math.Max(1, slots / ClaudeRadarModelGridColumns);\n grid.Height = rows * ClaudeRadarModelButtonHeight + Math.Max(0, rows - 1) * ClaudeRadarModelButtonGap;\n }\n finally\n {\n grid.ResumeLayout();\n }\n }\n\n private List<ClaudeModelOption> BuildClaudeRadarModelOptions(string selectedKey)\n {\n List<ClaudeModelOption> options = new List<ClaudeModelOption>();\n options.Add(new ClaudeModelOption(string.Empty, \"自动\", true, false));\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n keys.Add(string.Empty);\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n string status = (model.Status ?? string.Empty).Trim();\n bool deleted = string.Equals(status, \"deleted\", StringComparison.OrdinalIgnoreCase);\n if (deleted && !string.Equals(key, selectedKey, StringComparison.OrdinalIgnoreCase))\n {\n continue;\n }\n\n bool pending = string.Equals(status, \"pending\", StringComparison.OrdinalIgnoreCase);\n bool temporarilyMissing = string.Equals(status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase);\n bool available = model.Enabled &&\n !deleted &&\n !pending &&\n !temporarilyMissing &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n options.Add(new ClaudeModelOption(\n key,\n label,\n available,\n pending || temporarilyMissing));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (!string.IsNullOrEmpty(selectedKey) && !keys.Contains(selectedKey))\n {\n options.Add(new ClaudeModelOption(selectedKey, selectedKey, false, true));\n }\n\n return options;\n }\n\n private Button BuildClaudeRadarModelButton(ClaudeModelOption option)\n {\n Button button = new Button();\n button.Width = ClaudeRadarModelButtonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Tag = option;\n button.Text = option == null ? \"--\" : GetClaudeRadarModelButtonText(option);\n button.AutoEllipsis = true;\n button.TextAlign = ContentAlignment.MiddleCenter;\n button.FlatStyle = FlatStyle.Flat;\n button.UseVisualStyleBackColor = false;\n button.FlatAppearance.BorderSize = 1;\n button.Font = GetUiFont(8.8f, FontStyle.Bold);\n button.Cursor = option != null && option.Available ? Cursors.Hand : Cursors.Default;\n button.Enabled = option != null && option.Available;\n button.Click += delegate\n {\n ClaudeModelOption clicked = button.Tag as ClaudeModelOption;\n if (clicked == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(button.Parent);\n if (grid == null)\n {\n return;\n }\n\n string current = GetClaudeRadarModelGridValue(grid);\n if (string.Equals(current, clicked.Key, StringComparison.OrdinalIgnoreCase))\n {\n return;\n }\n\n grid.Tag = clicked.Key;\n ApplyClaudeRadarModelGridSelection(grid);\n OnSettingChanged();\n };\n return button;\n }\n\n private static string GetClaudeRadarModelButtonText(ClaudeModelOption option)\n {\n if (option == null)\n {\n return \"--\";\n }\n\n if (option.Key.Length == 0)\n {\n return \"自动\";\n }\n\n string label = option.Label ?? option.Key;\n if (label.Length <= 7)\n {\n return label;\n }\n\n return label.Substring(0, 7);\n }\n\n private void ApplyClaudeRadarModelGridSelection(FlowLayoutPanel grid)\n {\n if (grid == null)\n {\n return;\n }\n\n string selectedKey = WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n bool selected = option != null &&\n string.Equals(option.Key, selectedKey, StringComparison.OrdinalIgnoreCase);\n ApplyClaudeRadarModelButtonStyle(button, option, selected);\n }\n }\n\n private static void ApplyClaudeRadarModelButtonStyle(Button button, ClaudeModelOption option, bool selected)\n {\n if (button == null)\n {\n return;\n }\n\n if (option == null)\n {\n button.BackColor = ControlBg;\n button.ForeColor = TextTertiary;\n button.FlatAppearance.BorderColor = ControlBorder;\n return;\n }\n\n if (selected)\n {\n button.BackColor = DesignTokens.SettingsWarmTheme.Accent;\n button.ForeColor = Color.Black;\n button.FlatAppearance.BorderColor = DesignTokens.SettingsWarmTheme.AccentHover;\n return;\n }\n\n button.BackColor = option.Available ? DesignTokens.SettingsWarmTheme.ButtonRest : MicaLayer;\n button.ForeColor = option.Available ? TextSecondary : TextTertiary;\n button.FlatAppearance.BorderColor = option.Pending ? DesignTokens.Colors.Warning : ControlBorder;\n }\n\n private static string GetClaudeRadarModelGridValue(FlowLayoutPanel grid)\n {\n return grid == null ? string.Empty : WidgetSettings.NormalizeClaudeRadarModelKey(grid.Tag as string);\n }\n\n private void SetClaudeRadarModelGridValue(FlowLayoutPanel grid, string value)\n {\n PopulateClaudeRadarModelGrid(grid, value);\n if (grid != null && grid.Parent != null)\n {\n LayoutClaudeRadarModelPanel(grid.Parent);\n }\n }\n\n private static FlowLayoutPanel FindClaudeRadarModelGrid(Control control)\n {\n FlowLayoutPanel grid = control as FlowLayoutPanel;\n if (grid != null && string.Equals(grid.Name, ClaudeRadarModelGridName, StringComparison.Ordinal))\n {\n return grid;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n grid = FindClaudeRadarModelGrid(control.Controls[i]);\n if (grid != null)\n {\n return grid;\n }\n }\n }\n\n return null;\n }\n\n private static void LayoutClaudeRadarModelPanel(Control panel)\n {\n if (panel == null)\n {\n return;\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(panel);\n Button edit = null;\n for (int i = 0; i < panel.Controls.Count; i++)\n {\n Button candidate = panel.Controls[i] as Button;\n if (candidate != null && string.Equals(candidate.Text, \"编辑映射\", StringComparison.Ordinal))\n {\n edit = candidate;\n break;\n }\n }\n\n int y = 0;\n if (grid != null)\n {\n grid.Location = new Point(0, 0);\n grid.Width = panel.Width;\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n continue;\n }\n\n button.Width = buttonWidth;\n button.Height = ClaudeRadarModelButtonHeight;\n button.Margin = new Padding(\n 0,\n 0,\n i % ClaudeRadarModelGridColumns == ClaudeRadarModelGridColumns - 1 ? 0 : ClaudeRadarModelButtonGap,\n ClaudeRadarModelButtonGap);\n }\n\n y = grid.Bottom + 10;\n }\n\n if (edit != null)\n {\n edit.Location = new Point(0, y);\n edit.Width = panel.Width;\n y = edit.Bottom;\n }\n\n panel.Height = Math.Max(54, y);\n }\n\n private static int GetClaudeRadarModelButtonWidthForGrid(int gridWidth)\n {\n int gaps = (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n int usable = Math.Max(ClaudeRadarModelGridColumns, gridWidth - gaps);\n return Math.Max(32, usable / ClaudeRadarModelGridColumns);\n }\n\n private static void RelayoutSettingGroup(Control control)\n {\n Control parent = control;\n while (parent != null)\n {\n SettingGroupCard card = parent as SettingGroupCard;\n if (card != null)\n {\n card.LayoutRows();\n return;\n }\n\n parent = parent.Parent;\n }\n }\n\n private static ComboBox FindComboBox(Control control)\n {\n ComboBox combo = control as ComboBox;\n if (combo != null)\n {\n return combo;\n }\n\n if (control != null)\n {\n for (int i = 0; i < control.Controls.Count; i++)\n {\n combo = FindComboBox(control.Controls[i]);\n if (combo != null)\n {\n return combo;\n }\n }\n }\n\n return null;\n }\n\n private void PopulateClaudeRadarModelCombo(ComboBox combo)\n {\n if (combo == null)\n {\n return;\n }\n\n combo.Items.Clear();\n combo.Items.Add(new ClaudeModelOption(string.Empty, \"自动选择\", true, false));\n try\n {\n List<ClaudeRadarModelEntry> models = ClaudeRadarReader.LoadModelMap();\n HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n for (int i = 0; i < models.Count; i++)\n {\n ClaudeRadarModelEntry model = models[i];\n if (model == null)\n {\n continue;\n }\n\n string key = WidgetSettings.NormalizeClaudeRadarModelKey(model.SourceKey);\n if (key.Length == 0 || keys.Contains(key))\n {\n continue;\n }\n\n bool available = model.Enabled &&\n !string.Equals(model.Status, \"deleted\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase) &&\n !string.Equals(model.Status, \"temporarily_missing\", StringComparison.OrdinalIgnoreCase) &&\n !string.IsNullOrWhiteSpace(model.RatingKey);\n string label = string.IsNullOrWhiteSpace(model.DisplayName)\n ? key\n : model.DisplayName;\n combo.Items.Add(new ClaudeModelOption(\n key,\n label,\n available,\n string.Equals(model.Status, \"pending\", StringComparison.OrdinalIgnoreCase)));\n keys.Add(key);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n\n if (combo.Items.Count > 0 && combo.SelectedIndex < 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static object GetClaudeRadarModelComboValue(ComboBox combo)\n {\n ClaudeModelOption option = combo == null ? null : combo.SelectedItem as ClaudeModelOption;\n return option == null ? string.Empty : option.Key;\n }\n\n private static void SetClaudeRadarModelComboValue(ComboBox combo, string value)\n {\n if (combo == null)\n {\n return;\n }\n\n string modelKey = WidgetSettings.NormalizeClaudeRadarModelKey(value);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ClaudeModelOption option = combo.Items[i] as ClaudeModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(modelKey))\n {\n combo.Items.Add(new ClaudeModelOption(modelKey, modelKey, false, true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n }\n\n private static bool IsDisplayDeviceSetting(string propertyName)\n {\n return propertyName != null &&\n propertyName.EndsWith(\"DisplayDeviceName\", StringComparison.Ordinal);\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n EnsureVariantSamplesForPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void EnsureVariantSamplesForPage(CategoryPage page)\n {\n if (this.owner == null || page == null)\n {\n return;\n }\n\n string directory = GetVariantSampleDirectory();\n for (int i = 0; i < page.Editors.Count; i++)\n {\n SettingEditor editor = page.Editors[i];\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker == null)\n {\n continue;\n }\n\n string prefix = GetVariantSamplePrefix(editor.Name);\n if (prefix.Length == 0)\n {\n continue;\n }\n\n try\n {\n if (picker.HasMissingSamples(directory, prefix))\n {\n System.IO.Directory.CreateDirectory(directory);\n RenderVariantSamplesForProperty(editor.Name, directory);\n }\n\n picker.LoadSamples(directory, prefix);\n RelayoutSettingGroup(picker);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n picker.MarkSamplesUnavailable();\n }\n }\n }\n\n private static string GetVariantSampleDirectory()\n {\n return System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName,\n \"variant-samples\",\n \"v\" + ProductIdentity.Version);\n }\n\n private static string GetVariantSamplePrefix(string propertyName)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal)) return \"widget\";\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal)) return \"codexradar\";\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal)) return \"networkmonitor\";\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal)) return \"powerthermal\";\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal)) return \"operation\";\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal)) return \"connectioncheck\";\n return string.Empty;\n }\n\n private static void RenderVariantSamplesForProperty(string propertyName, string directory)\n {\n if (string.Equals(propertyName, \"MainWidgetRenderVariant\", StringComparison.Ordinal))\n {\n WidgetForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"CodexRadarRenderVariant\", StringComparison.Ordinal))\n {\n CodexRadarForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"NetworkMonitorRenderVariant\", StringComparison.Ordinal))\n {\n NetworkMonitorForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"PowerThermalRenderVariant\", StringComparison.Ordinal))\n {\n PowerThermalForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"OperationRenderVariant\", StringComparison.Ordinal))\n {\n OperationForm.RenderVariantSamples(directory);\n return;\n }\n\n if (string.Equals(propertyName, \"ConnectionCheckRenderVariant\", StringComparison.Ordinal))\n {\n ConnectionCheckForm.RenderVariantSamples(directory);\n }\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n if (page.AdvancedHeader != null)\n {\n page.AdvancedHeader.Width = width;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n\n SetDirtyState(false);\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n if (editor.Property == null)\n {\n continue;\n }\n\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private bool TrySaveSettings()\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n SetDirtyState(false);\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n return false;\n }\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n SetClaudeRadarModelGridValue(grid, value as string);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n picker.SetSelectedValue(value);\n RelayoutSettingGroup(editor.Control);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n string displayDeviceName = value as string;\n if (string.Equals(editor.Property.Name, \"CodexRadarModelKey\", StringComparison.Ordinal))\n {\n string modelKey = CodexRadarModelCatalog.NormalizeModelKey(value as string);\n if (modelKey.Length == 0)\n {\n modelKey = CodexRadarModelCatalog.DefaultModelKey;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n ModelOption option = combo.Items[i] as ModelOption;\n if (option != null && string.Equals(option.Key, modelKey, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n combo.Items.Add(new ModelOption(\n modelKey,\n CodexRadarModelCatalog.GetDisplayLabel(string.Empty, modelKey),\n false,\n true));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n SetClaudeRadarModelComboValue(combo, value as string);\n return;\n }\n\n if (IsDisplayDeviceSetting(editor.Property.Name))\n {\n displayDeviceName = WidgetSettings.NormalizeDisplayDeviceName(displayDeviceName);\n for (int i = 0; i < combo.Items.Count; i++)\n {\n DisplayOption option = combo.Items[i] as DisplayOption;\n if (option != null && string.Equals(option.Value, displayDeviceName, StringComparison.OrdinalIgnoreCase))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (!string.IsNullOrEmpty(displayDeviceName))\n {\n combo.Items.Add(new DisplayOption(displayDeviceName, \"未连接 \" + displayDeviceName));\n combo.SelectedIndex = combo.Items.Count - 1;\n return;\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n button.Tag = value is int ? (int)value : 0;\n if (string.Equals(editor.Name, \"DeepSeekApiKeyRevision\", StringComparison.Ordinal))\n {\n button.Text = GetDeepSeekApiKeyButtonText();\n }\n }\n }\n\n private void ApplyResponsiveShellLayout()\n {\n int navWidth = GetNavigationWidthForClientWidth(this.ClientSize.Width);\n if (this.bodyLayout != null && this.bodyLayout.ColumnStyles.Count > 0)\n {\n this.bodyLayout.ColumnStyles[0].Width = navWidth;\n }\n\n if (this.navigationPanel != null)\n {\n int itemWidth = Math.Max(160, navWidth - 24);\n for (int i = 0; i < this.navigationPanel.Controls.Count; i++)\n {\n this.navigationPanel.Controls[i].Width = itemWidth;\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n if (editor.Property == null)\n {\n return null;\n }\n\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n if (string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid != null)\n {\n return GetClaudeRadarModelGridValue(grid);\n }\n }\n\n VariantPicker picker = editor.Control as VariantPicker;\n if (picker != null)\n {\n return picker.GetSelectedValue();\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo == null && string.Equals(editor.Property.Name, \"ClaudeRadarModelKey\", StringComparison.Ordinal))\n {\n combo = FindComboBox(editor.Control);\n }\n\n if (combo != null)\n {\n ClaudeModelOption claudeModelOption = combo.SelectedItem as ClaudeModelOption;\n if (claudeModelOption != null)\n {\n return claudeModelOption.Key;\n }\n\n ModelOption modelOption = combo.SelectedItem as ModelOption;\n if (modelOption != null)\n {\n return modelOption.Key;\n }\n\n DisplayOption displayOption = combo.SelectedItem as DisplayOption;\n if (displayOption != null)\n {\n return displayOption.Value;\n }\n\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n Button button = editor.Control as Button;\n if (button != null)\n {\n return button.Tag is int ? (int)button.Tag : 0;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n SetDirtyState(true);\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n private void OpenGlobalLayoutEditorFromSettings()\n {\n if (this.owner == null)\n {\n return;\n }\n\n this.previewTimer.Stop();\n WidgetSettings settings = ReadSettings();\n WidgetSettings editedSettings;\n if (this.owner.TryEditGlobalLayout(settings, out editedSettings))\n {\n this.baseline = editedSettings.Clone();\n this.baseline.Normalize();\n LoadSettings(this.baseline);\n this.saved = true;\n ShowStatus(\"全局布局已保存\", false);\n return;\n }\n\n this.owner.PreviewSettings(ReadSettings());\n ShowStatus(\"全局编辑已取消\", false);\n }\n\n private void OpenDeepSeekApiKeyDialog(Button sourceButton)\n {\n Form dialog = new Form();\n try\n {\n dialog.Text = \"DeepSeek 配置\";\n dialog.FormBorderStyle = FormBorderStyle.FixedDialog;\n dialog.StartPosition = FormStartPosition.CenterParent;\n dialog.ShowInTaskbar = false;\n dialog.MaximizeBox = false;\n dialog.MinimizeBox = false;\n dialog.ClientSize = new Size(560, 238);\n dialog.BackColor = MicaBase;\n dialog.ForeColor = TextSecondary;\n dialog.Font = GetUiFont(9.5f);\n\n Label title = new Label();\n title.Text = \"DeepSeek API Key\";\n title.Font = GetUiFont(12.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(24, 20);\n title.Size = new Size(500, 30);\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label hint = new Label();\n hint.Text = HasDeepSeekEnvironmentApiKeyForUi()\n ? \"检测到 DEEPSEEK_API_KEY 环境变量;环境变量会优先于本地文件。\"\n : \"密钥保存到本地应用数据目录,不写入 settings.ini 或日志。\";\n hint.Font = GetUiFont(8.8f);\n hint.ForeColor = TextTertiary;\n hint.BackColor = MicaBase;\n hint.Location = new Point(24, 56);\n hint.Size = new Size(500, 42);\n hint.TextAlign = ContentAlignment.MiddleLeft;\n\n TextBox keyBox = new TextBox();\n keyBox.Location = new Point(24, 104);\n keyBox.Size = new Size(512, 34);\n keyBox.BackColor = ControlBg;\n keyBox.ForeColor = TextSecondary;\n keyBox.BorderStyle = BorderStyle.FixedSingle;\n keyBox.Font = GetUiFont(9.5f);\n keyBox.UseSystemPasswordChar = true;\n keyBox.Text = ReadDeepSeekApiKeyFileForUi();\n\n Button clear = BuildCommandButton(\"清除\", false);\n clear.Location = new Point(24, 166);\n clear.Width = 112;\n clear.Height = 44;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Location = new Point(302, 166);\n cancel.Width = 112;\n cancel.Height = 44;\n cancel.Click += delegate { dialog.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Location = new Point(424, 166);\n save.Width = 112;\n save.Height = 44;\n\n clear.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(string.Empty, out errorCode))\n {\n keyBox.Text = string.Empty;\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已清除\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置清除失败 \" + errorCode, true);\n };\n\n save.Click += delegate\n {\n string errorCode;\n if (TrySaveDeepSeekApiKeyFile(keyBox.Text, out errorCode))\n {\n IncrementDeepSeekApiKeyRevision(sourceButton);\n ShowStatus(\"DeepSeek 配置已保存\", false);\n dialog.Close();\n return;\n }\n\n ShowStatus(\"DeepSeek 配置保存失败 \" + errorCode, true);\n };\n\n dialog.Controls.Add(title);\n dialog.Controls.Add(hint);\n dialog.Controls.Add(keyBox);\n dialog.Controls.Add(clear);\n dialog.Controls.Add(cancel);\n dialog.Controls.Add(save);\n dialog.AcceptButton = save;\n dialog.CancelButton = cancel;\n dialog.ShowDialog(this);\n }\n finally\n {\n dialog.Dispose();\n }\n }\n\n private void IncrementDeepSeekApiKeyRevision(Button sourceButton)\n {\n if (sourceButton != null)\n {\n int token = sourceButton.Tag is int ? (int)sourceButton.Tag : 0;\n sourceButton.Tag = token == int.MaxValue ? 1 : token + 1;\n sourceButton.Text = GetDeepSeekApiKeyButtonText();\n }\n\n OnSettingChanged();\n }\n\n private static string GetDeepSeekApiKeyButtonText()\n {\n return IsDeepSeekApiKeyConfiguredForUi() ? \"修改配置\" : \"配置\";\n }\n\n private static bool IsDeepSeekApiKeyConfiguredForUi()\n {\n if (HasDeepSeekEnvironmentApiKeyForUi())\n {\n return true;\n }\n\n return ReadDeepSeekApiKeyFileForUi().Length > 0;\n }\n\n private static bool HasDeepSeekEnvironmentApiKeyForUi()\n {\n try\n {\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable)))\n {\n return true;\n }\n\n if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.User)))\n {\n return true;\n }\n\n return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(CodexRadarForm.DeepSeekApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine));\n }\n catch\n {\n return false;\n }\n }\n\n private static string ReadDeepSeekApiKeyFileForUi()\n {\n try\n {\n string path = CodexRadarForm.DeepSeekApiKeyPath;\n if (System.IO.File.Exists(path))\n {\n return (System.IO.File.ReadAllText(path, Encoding.UTF8) ?? string.Empty).Trim();\n }\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static bool TrySaveDeepSeekApiKeyFile(string apiKey, out string errorCode)\n {\n errorCode = string.Empty;\n string tempPath = string.Empty;\n try\n {\n string path = CodexRadarForm.DeepSeekApiKeyPath;\n string directory = System.IO.Path.GetDirectoryName(path);\n if (!string.IsNullOrEmpty(directory))\n {\n System.IO.Directory.CreateDirectory(directory);\n }\n\n string trimmed = (apiKey ?? string.Empty).Trim();\n if (trimmed.Length == 0)\n {\n if (System.IO.File.Exists(path))\n {\n System.IO.File.Delete(path);\n }\n\n return true;\n }\n\n tempPath = path + \".tmp\";\n System.IO.File.WriteAllText(tempPath, trimmed + Environment.NewLine, new UTF8Encoding(false));\n if (System.IO.File.Exists(path))\n {\n System.IO.File.Replace(tempPath, path, null);\n }\n else\n {\n System.IO.File.Move(tempPath, path);\n }\n\n return true;\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n errorCode = \"0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture);\n try\n {\n if (!string.IsNullOrEmpty(tempPath) && System.IO.File.Exists(tempPath))\n {\n System.IO.File.Delete(tempPath);\n }\n }\n catch\n {\n }\n\n return false;\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void SetDirtyState(bool hasChanges)\n {\n this.dirty = hasChanges;\n if (this.statusLabel == null)\n {\n return;\n }\n\n if (hasChanges)\n {\n this.statusTimer.Stop();\n this.statusLabel.Text = \"有未保存的更改\";\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.Visible = true;\n }\n else if (!this.statusTimer.Enabled)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n if (this.dirty)\n {\n SetDirtyState(true);\n }\n else\n {\n this.statusLabel.Visible = false;\n }\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n bool searching = query.Length > 0;\n int firstMatchingPageIndex = -1;\n bool selectedPageHasVisibleRows = false;\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n if (page.AdvancedHeader != null)\n {\n // While searching, advanced rows surface directly, so the collapse header hides.\n page.AdvancedHeader.Visible = !searching;\n }\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool allowed = !group.Advanced || page.AdvancedExpanded || searching;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = allowed && (pageMatch || groupTitleMatch ||\n editor.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n\n if (searching && anyVisible)\n {\n if (firstMatchingPageIndex < 0)\n {\n firstMatchingPageIndex = i;\n }\n\n if (i == this.selectedPageIndex)\n {\n selectedPageHasVisibleRows = true;\n }\n }\n }\n\n if (searching &&\n !selectedPageHasVisibleRows &&\n firstMatchingPageIndex >= 0 &&\n firstMatchingPageIndex != this.selectedPageIndex)\n {\n SelectPage(firstMatchingPageIndex);\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : string.Empty;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return 1;\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (IsCodexRadarManualElementOffsetSetting(name))\n {\n return new NumericRange(\n WidgetSettings.MinCodexRadarManualElementOffsetPixels,\n WidgetSettings.MaxCodexRadarManualElementOffsetPixels);\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static bool IsCodexRadarManualElementOffsetSetting(string name)\n {\n return name != null &&\n name.StartsWith(\"CodexRadar\", StringComparison.OrdinalIgnoreCase) &&\n (name.EndsWith(\"OffsetX\", StringComparison.OrdinalIgnoreCase) ||\n name.EndsWith(\"OffsetY\", StringComparison.OrdinalIgnoreCase));\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private void ScheduleDynamicResolutionSizing()\n {\n if (!this.IsHandleCreated)\n {\n ApplyDynamicResolutionSizing(false);\n return;\n }\n\n try\n {\n this.BeginInvoke((MethodInvoker)delegate\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n });\n }\n catch\n {\n if (!this.IsDisposed)\n {\n ApplyDynamicResolutionSizing(false);\n }\n }\n }\n\n private void ApplyDynamicResolutionSizing(bool usePreferredSize)\n {\n Rectangle workArea = GetTargetWorkArea();\n Size minimum = GetMinimumWindowSizeForWorkArea(workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n\n this.MinimumSize = minimum;\n this.MaximumSize = new Size(\n Math.Max(minimum.Width, Math.Max(1, workArea.Width)),\n Math.Max(minimum.Height, Math.Max(1, workArea.Height)));\n\n Size target = usePreferredSize || this.ClientSize.Width <= 0 || this.ClientSize.Height <= 0\n ? preferred\n : this.ClientSize;\n target = new Size(\n Clamp(target.Width, minimum.Width, preferred.Width),\n Clamp(target.Height, minimum.Height, preferred.Height));\n\n if (this.ClientSize != target)\n {\n this.ClientSize = target;\n }\n\n if (this.Visible || this.IsHandleCreated)\n {\n ClampWindowToWorkArea(workArea);\n }\n\n ApplyResponsiveShellLayout();\n }\n\n private Rectangle GetTargetWorkArea()\n {\n Rectangle workArea;\n if (this.owner != null && !this.owner.IsDisposed)\n {\n workArea = Screen.FromControl(this.owner).WorkingArea;\n }\n else if (this.IsHandleCreated)\n {\n workArea = Screen.FromHandle(this.Handle).WorkingArea;\n }\n else\n {\n workArea = Screen.PrimaryScreen.WorkingArea;\n }\n\n EnsureUsableWorkArea(ref workArea);\n return workArea;\n }\n\n private void ClampWindowToWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int left = Clamp(this.Left, workArea.Left, Math.Max(workArea.Left, workArea.Right - this.Width));\n int top = Clamp(this.Top, workArea.Top, Math.Max(workArea.Top, workArea.Bottom - this.Height));\n if (left != this.Left || top != this.Top)\n {\n this.Location = new Point(left, top);\n }\n }\n\n private static Size GetPreferredClientSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Clamp(\n (int)Math.Round(workArea.Width * (PreferredClientWidth / (double)ReferenceWorkAreaWidth), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientWidth, Math.Max(1, workArea.Width)),\n PreferredClientWidth);\n int height = Clamp(\n (int)Math.Round(workArea.Height * (PreferredClientHeight / (double)ReferenceWorkAreaHeight), MidpointRounding.AwayFromZero),\n Math.Min(MinimumClientHeight, Math.Max(1, workArea.Height)),\n PreferredClientHeight);\n return FitClientSizeToWorkArea(new Size(width, height), workArea);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n return FitClientSizeToWorkArea(desiredSize, Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size FitClientSizeToWorkArea(Size desiredSize, Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n int width = Math.Min(desiredSize.Width, Math.Max(MinimumClientWidth, workArea.Width - ScreenMargin));\n int height = Math.Min(desiredSize.Height, Math.Max(MinimumClientHeight, workArea.Height - ScreenMargin));\n width = Math.Min(width, Math.Max(1, workArea.Width));\n height = Math.Min(height, Math.Max(1, workArea.Height));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n return GetMinimumWindowSizeForWorkArea(Screen.PrimaryScreen.WorkingArea);\n }\n\n private static Size GetMinimumWindowSizeForWorkArea(Rectangle workArea)\n {\n EnsureUsableWorkArea(ref workArea);\n Size preferred = GetPreferredClientSizeForWorkArea(workArea);\n return new Size(\n Math.Min(preferred.Width, Math.Max(Math.Min(MinimumClientWidth, workArea.Width), workArea.Width - 128)),\n Math.Min(preferred.Height, Math.Max(Math.Min(MinimumClientHeight, workArea.Height), workArea.Height - 128)));\n }\n\n private static int GetNavigationWidthForClientWidth(int clientWidth)\n {\n if (clientWidth <= 960)\n {\n return CompactNavWidth;\n }\n\n if (clientWidth <= 1280)\n {\n return MediumNavWidth;\n }\n\n return DefaultNavWidth;\n }\n\n private static void EnsureUsableWorkArea(ref Rectangle workArea)\n {\n if (workArea.Width > 0 && workArea.Height > 0)\n {\n return;\n }\n\n workArea = Screen.PrimaryScreen.Bounds;\n if (workArea.Width <= 0 || workArea.Height <= 0)\n {\n workArea = new Rectangle(0, 0, ReferenceWorkAreaWidth, ReferenceWorkAreaHeight);\n }\n }\n\n private static int Clamp(int value, int minimum, int maximum)\n {\n if (maximum < minimum)\n {\n maximum = minimum;\n }\n\n if (value < minimum)\n {\n return minimum;\n }\n\n if (value > maximum)\n {\n return maximum;\n }\n\n return value;\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static string RunOpenCloseStressSelfTest(int iterations)\n {\n int loopCount = Math.Max(1, Math.Min(500, iterations));\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n baseline.CodexRadarEnabled = true;\n baseline.ClaudeRadarEnabled = true;\n baseline.Normalize();\n\n using (Win11SettingsForm warmup = new Win11SettingsForm(null, baseline))\n {\n warmup.StartPosition = FormStartPosition.Manual;\n warmup.Location = new Point(-30000, -30000);\n warmup.Show();\n Application.DoEvents();\n warmup.Close();\n Application.DoEvents();\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters before = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n for (int i = 0; i < loopCount; i++)\n {\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-30000, -30000);\n form.Show();\n Application.DoEvents();\n form.Close();\n Application.DoEvents();\n }\n\n if ((i + 1) % 25 == 0)\n {\n ForceResourceCleanup();\n }\n }\n\n ForceResourceCleanup();\n RadarRuntimeDiagnostics.ResourceCounters after = RadarRuntimeDiagnostics.CaptureCurrentProcessResources();\n int handleDelta = after.HandleCount - before.HandleCount;\n int gdiDelta = after.GdiObjects - before.GdiObjects;\n int userDelta = after.UserObjects - before.UserObjects;\n if (handleDelta > 80 || gdiDelta > 8 || userDelta > 16)\n {\n throw new InvalidOperationException(\n \"WinUI settings open/close resource growth exceeded threshold. Iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \", HandlesDelta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \", GdiDelta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \", UserDelta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture));\n }\n\n return \"Settings open/close policy: PASS iterations=\" +\n loopCount.ToString(CultureInfo.InvariantCulture) +\n \" handles_delta=\" +\n handleDelta.ToString(CultureInfo.InvariantCulture) +\n \" gdi_delta=\" +\n gdiDelta.ToString(CultureInfo.InvariantCulture) +\n \" user_delta=\" +\n userDelta.ToString(CultureInfo.InvariantCulture);\n }\n\n internal static void RunSettingsBindingSelfTest()\n {\n VerifySettingsWindowActivationPolicy();\n VerifyUnsavedPreviewConsumePolicy();\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.ShowInTaskbar = false;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n private static void VerifySettingsWindowActivationPolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n if (!form.ShowInTaskbar)\n {\n throw new InvalidOperationException(\"WinUI settings window must be visible in taskbar and Alt+Tab.\");\n }\n }\n }\n\n private static void VerifyUnsavedPreviewConsumePolicy()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n WidgetSettings revertSettings;\n if (!form.TryConsumeUnsavedPreview(out revertSettings) ||\n revertSettings == null ||\n !object.Equals(revertSettings.OperationPrimaryPanelMode, baseline.OperationPrimaryPanelMode))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consume failed.\");\n }\n\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings unsaved preview consumed twice.\");\n }\n }\n\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n WidgetSettings revertSettings;\n if (form.TryConsumeUnsavedPreview(out revertSettings))\n {\n throw new InvalidOperationException(\"WinUI settings owner-closing preview should not be consumed.\");\n }\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private static void ForceResourceCleanup()\n {\n GC.Collect();\n GC.WaitForPendingFinalizers();\n GC.Collect();\n Application.DoEvents();\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"FallbackDisconnectedDisplaysEnabled\",\n \"MainDisplayDeviceName\",\n \"CodexRadarDisplayDeviceName\",\n \"ClaudeRadarDisplayDeviceName\",\n GlobalLayoutEditCommandName,\n \"Width\",\n \"CodexRadarEnabled\",\n \"CodexRadarSoftwareMode\",\n \"CodexRadarModelKey\",\n \"CodexRadarHtmlFallbackEnabled\",\n \"CodexRadarServiceProbeToken\",\n \"ClaudeRadarEnabled\",\n \"ClaudeRadarModelKey\",\n \"ClaudeRadarJsonEnabled\",\n \"ClaudeRadarServiceProbeToken\",\n \"DeepSeekApiKeyRevision\",\n \"AiRequestProtectionAutoEnabled\",\n \"AiRequestProtectionManualBlockEnabled\",\n \"CodexQuotaPlanEnabled\",\n \"CodexQuotaPlanWeeklyComparison\",\n \"CodexQuotaPlanWeeklyThresholdPercent\",\n \"CodexQuotaPlanFiveHourComparison\",\n \"CodexQuotaPlanFiveHourThresholdPercent\",\n \"CodexQuotaPlanResumeConditionMode\",\n \"CodexQuotaPlanAutoResumePausedGoals\",\n \"CodexRadarRenderVariant\",\n \"MainWidgetRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\",\n \"OperationPrimaryPanelMode\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyClaudeRadarModelGridPolicy();\n VerifyVariantPickerPolicy();\n VerifyDynamicResolutionSizingPolicy();\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyClaudeRadarModelGridPolicy()\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(\"ClaudeRadarModelKey\", out editor))\n {\n throw new InvalidOperationException(\"Claude Radar model grid editor missing.\");\n }\n\n FlowLayoutPanel grid = FindClaudeRadarModelGrid(editor.Control);\n if (grid == null)\n {\n throw new InvalidOperationException(\"Claude Radar model selector must use the five-column button grid.\");\n }\n\n if (grid.Controls.Count < ClaudeRadarModelGridColumns ||\n grid.Controls.Count % ClaudeRadarModelGridColumns != 0)\n {\n throw new InvalidOperationException(\"Claude Radar model grid slot count is not a multiple of five.\");\n }\n\n int buttonWidth = GetClaudeRadarModelButtonWidthForGrid(grid.Width);\n int rowWidth = ClaudeRadarModelGridColumns * buttonWidth +\n (ClaudeRadarModelGridColumns - 1) * ClaudeRadarModelButtonGap;\n if (rowWidth > grid.Width)\n {\n throw new InvalidOperationException(\"Claude Radar model grid buttons exceed selector width.\");\n }\n\n bool sawAuto = false;\n for (int i = 0; i < grid.Controls.Count; i++)\n {\n Button button = grid.Controls[i] as Button;\n if (button == null)\n {\n throw new InvalidOperationException(\"Claude Radar model grid contains a non-button slot.\");\n }\n\n if (button.Width != buttonWidth)\n {\n throw new InvalidOperationException(\"Claude Radar model grid button width is not responsive.\");\n }\n\n ClaudeModelOption option = button.Tag as ClaudeModelOption;\n if (option == null)\n {\n if (button.Enabled || !string.Equals(button.Text, \"--\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"Claude Radar empty model slot must be disabled --.\");\n }\n continue;\n }\n\n if (option.Key.Length == 0)\n {\n sawAuto = true;\n if (!button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar automatic model slot must be selectable.\");\n }\n }\n else if (!option.Available && button.Enabled)\n {\n throw new InvalidOperationException(\"Claude Radar unavailable model slot must be disabled.\");\n }\n }\n\n if (!sawAuto)\n {\n throw new InvalidOperationException(\"Claude Radar model grid missing automatic slot.\");\n }\n }\n\n private void VerifyVariantPickerPolicy()\n {\n string[] pickerNames = new string[]\n {\n \"MainWidgetRenderVariant\",\n \"CodexRadarRenderVariant\",\n \"NetworkMonitorRenderVariant\",\n \"PowerThermalRenderVariant\",\n \"ConnectionCheckRenderVariant\",\n \"OperationRenderVariant\"\n };\n\n for (int i = 0; i < pickerNames.Length; i++)\n {\n SettingEditor editor;\n if (!this.editors.TryGetValue(pickerNames[i], out editor))\n {\n throw new InvalidOperationException(\"Render variant picker binding missing: \" + pickerNames[i]);\n }\n\n if (!(editor.Control is VariantPicker))\n {\n throw new InvalidOperationException(\"Render variant must use VariantPicker: \" + pickerNames[i]);\n }\n }\n\n SettingEditor claudeEditor;\n if (this.editors.TryGetValue(\"ClaudeRadarRenderVariant\", out claudeEditor) &&\n claudeEditor.Control is VariantPicker)\n {\n throw new InvalidOperationException(\"Claude Radar render variant must remain a dropdown until it has effect.\");\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n Size originalMinimum = this.MinimumSize;\n int originalPageIndex = this.selectedPageIndex;\n try\n {\n Size[] testSizes = new Size[]\n {\n this.MinimumSize,\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1280, 680)),\n GetMinimumWindowSizeForWorkArea(new Rectangle(0, 0, 1024, 728))\n };\n\n for (int sizeIndex = 0; sizeIndex < testSizes.Length; sizeIndex++)\n {\n this.MinimumSize = testSizes[sizeIndex];\n this.ClientSize = testSizes[sizeIndex];\n Application.DoEvents();\n VerifyNoVisibleControlClippingAtCurrentSize();\n }\n }\n finally\n {\n this.MinimumSize = originalMinimum;\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n private void VerifyNoVisibleControlClippingAtCurrentSize()\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Name);\n }\n }\n }\n }\n\n private static void VerifyDynamicResolutionSizingPolicy()\n {\n Rectangle[] workAreas = new Rectangle[]\n {\n new Rectangle(0, 60, 2880, 1740),\n new Rectangle(0, 0, 1920, 1040),\n new Rectangle(0, 0, 1600, 860),\n new Rectangle(0, 0, 1366, 728),\n new Rectangle(0, 0, 1280, 680),\n new Rectangle(0, 0, 1024, 728)\n };\n\n for (int i = 0; i < workAreas.Length; i++)\n {\n Size preferred = GetPreferredClientSizeForWorkArea(workAreas[i]);\n Size minimum = GetMinimumWindowSizeForWorkArea(workAreas[i]);\n if (preferred.Width > workAreas[i].Width ||\n preferred.Height > workAreas[i].Height ||\n minimum.Width > workAreas[i].Width ||\n minimum.Height > workAreas[i].Height ||\n minimum.Width > preferred.Width ||\n minimum.Height > preferred.Height)\n {\n throw new InvalidOperationException(\n \"WinUI settings dynamic resolution sizing failed: \" +\n workAreas[i].Width.ToString(CultureInfo.InvariantCulture) +\n \"x\" +\n workAreas[i].Height.ToString(CultureInfo.InvariantCulture));\n }\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"ClaudeRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"ClaudeRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"CodexRadarManualLeftPercent\", new NumericRange(WidgetSettings.MinCodexRadarManualLeftPercent, WidgetSettings.MaxCodexRadarManualLeftPercent) },\n { \"CodexRadarManualGapPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualGapPixels, WidgetSettings.MaxCodexRadarManualGapPixels) },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualEfficiencyTextWidthPixels, WidgetSettings.MaxCodexRadarManualEfficiencyTextWidthPixels) },\n { \"CodexRadarManualQuotaRowsWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualQuotaRowsWidthPixels, WidgetSettings.MaxCodexRadarManualQuotaRowsWidthPixels) },\n { \"CodexRadarManualIqStatusWidthPixels\", new NumericRange(WidgetSettings.MinCodexRadarManualIqStatusWidthPixels, WidgetSettings.MaxCodexRadarManualIqStatusWidthPixels) },\n { \"CodexRadarManualTextScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualTextScalePercent, WidgetSettings.MaxCodexRadarManualTextScalePercent) },\n { \"CodexRadarManualRingScalePercent\", new NumericRange(WidgetSettings.MinCodexRadarManualRingScalePercent, WidgetSettings.MaxCodexRadarManualRingScalePercent) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", new NumericRange(WidgetSettings.MinCodexQuotaPlanThresholdPercent, WidgetSettings.MaxCodexQuotaPlanThresholdPercent) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"OperationPrimaryPanelMode\", \"左侧区域模式\" },\n { \"OperationWindowsButtonEnabled\", \"显示 Windows 按钮\" },\n { \"OperationMemoryPieEnabled\", \"显示内存饼图\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"AiRequestProtectionAutoEnabled\", \"AI 自动阻断\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"AI 手动阻断\" },\n { \"CodexQuotaPlanEnabled\", \"启用额度计划\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度条件\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度阈值\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时条件\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时阈值\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"恢复额度类型\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"恢复上次暂停\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"断开后回退显示器\" },\n { \"MainDisplayDeviceName\", \"主窗口显示器\" },\n { \"CodexRadarDisplayDeviceName\", \"Codex Radar 显示器\" },\n { \"ClaudeRadarDisplayDeviceName\", \"Claude Radar 显示器\" },\n { \"PowerThermalDisplayDeviceName\", \"功耗温度显示器\" },\n { \"NetworkMonitorDisplayDeviceName\", \"网络监控显示器\" },\n { \"ConnectionCheckDisplayDeviceName\", \"连接检测显示器\" },\n { \"OperationDisplayDeviceName\", \"操作面板显示器\" },\n { GlobalLayoutEditCommandName, \"全局编辑\" },\n { \"Width\", \"主窗口宽度\" },\n { \"Height\", \"主窗口高度\" },\n { \"LeftX\", \"主窗口距左边\" },\n { \"BottomY\", \"主窗口距下边\" },\n { \"CodexRadarWidth\", \"Codex Radar 宽度\" },\n { \"CodexRadarHeight\", \"Codex Radar 高度\" },\n { \"CodexRadarLeftX\", \"Codex Radar 距左边\" },\n { \"CodexRadarBottomY\", \"Codex Radar 距下边\" },\n { \"PowerThermalWidth\", \"功耗温度宽度\" },\n { \"PowerThermalHeight\", \"功耗温度高度\" },\n { \"PowerThermalLeftX\", \"功耗温度距左边\" },\n { \"PowerThermalBottomY\", \"功耗温度距下边\" },\n { \"NetworkMonitorWidth\", \"网络监控宽度\" },\n { \"NetworkMonitorHeight\", \"网络监控高度\" },\n { \"NetworkMonitorLeftX\", \"网络监控距左边\" },\n { \"NetworkMonitorBottomY\", \"网络监控距下边\" },\n { \"ConnectionCheckWidth\", \"连接检测宽度\" },\n { \"ConnectionCheckHeight\", \"连接检测高度\" },\n { \"ConnectionCheckLeftX\", \"连接检测距左边\" },\n { \"ConnectionCheckBottomY\", \"连接检测距下边\" },\n { \"OperationLeftOffset\", \"操作面板距左边\" },\n { \"OperationBottomOffset\", \"操作面板距下边\" },\n { \"BackgroundTransparencyPercent\", \"主窗口背景透明度\" },\n { \"ApplicationTransparencyPercent\", \"主窗口整体透明度\" },\n { \"ShowCpu\", \"显示 CPU\" },\n { \"ShowMemory\", \"显示内存\" },\n { \"ShowDisk\", \"显示磁盘\" },\n { \"ShowNetwork\", \"显示网络\" },\n { \"ShowGpu\", \"显示 GPU\" },\n { \"ShowNpu\", \"显示 NPU\" },\n { \"HoverOpacityEnabled\", \"鼠标靠近时隐藏\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"触发距离(像素)\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarSoftwareMode\", \"监控哪个软件\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"CodexRadarEnabled\", \"启用 Codex Radar\" },\n { \"CodexRadarTransparencyPercent\", \"Codex Radar 透明度\" },\n { \"ClaudeRadarEnabled\", \"启用 Claude Radar\" },\n { \"ClaudeRadarWidth\", \"Claude Radar 宽度\" },\n { \"ClaudeRadarHeight\", \"Claude Radar 高度\" },\n { \"ClaudeRadarLeftX\", \"Claude Radar 左侧 X\" },\n { \"ClaudeRadarBottomY\", \"Claude Radar 底部 Y\" },\n { \"ClaudeRadarTransparencyPercent\", \"Claude Radar 背景透明\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 模型\" },\n { \"ClaudeRadarJsonEnabled\", \"Claude Radar JSON\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"首页元数据回退\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"社区体感分\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"本地 7 天额度线回退\" },\n { \"ClaudeRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"ClaudeRadarRandomTestEnabled\", \"Claude 随机测试\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"Claude 随机测试自动刷新\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"ClaudeRadarRenderVariant\", \"外观风格\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"立即刷新随机测试\" },\n { \"CodexRadarRenderVariant\", \"外观风格\" },\n { \"MainWidgetRenderVariant\", \"外观风格\" },\n { \"NetworkMonitorRenderVariant\", \"外观风格\" },\n { \"PowerThermalRenderVariant\", \"外观风格\" },\n { \"ConnectionCheckRenderVariant\", \"外观风格\" },\n { \"OperationRenderVariant\", \"外观风格\" },\n { \"CodexRadarManualLayoutEnabled\", \"启用手动布局\" },\n { \"CodexRadarManualLeftPercent\", \"左侧区域占比\" },\n { \"CodexRadarManualGapPixels\", \"模块间距\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"效率文字列宽\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"余额列宽\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"IQ 状态列宽\" },\n { \"CodexRadarManualTextScalePercent\", \"文字比例\" },\n { \"CodexRadarManualRingScalePercent\", \"圆环比例\" },\n { \"CodexRadarTimeEfficiencyRingOffsetX\", \"时间环 X\" },\n { \"CodexRadarTimeEfficiencyRingOffsetY\", \"时间环 Y\" },\n { \"CodexRadarTimeEfficiencyTextOffsetX\", \"时间字 X\" },\n { \"CodexRadarTimeEfficiencyTextOffsetY\", \"时间字 Y\" },\n { \"CodexRadarTokenEfficiencyRingOffsetX\", \"Token 环 X\" },\n { \"CodexRadarTokenEfficiencyRingOffsetY\", \"Token 环 Y\" },\n { \"CodexRadarTokenEfficiencyTextOffsetX\", \"Token 字 X\" },\n { \"CodexRadarTokenEfficiencyTextOffsetY\", \"Token 字 Y\" },\n { \"CodexRadarConnectionTopTextOffsetX\", \"连接上字 X\" },\n { \"CodexRadarConnectionTopTextOffsetY\", \"连接上字 Y\" },\n { \"CodexRadarConnectionLineOffsetX\", \"连接线 X\" },\n { \"CodexRadarConnectionLineOffsetY\", \"连接线 Y\" },\n { \"CodexRadarConnectionBottomTextOffsetX\", \"连接下字 X\" },\n { \"CodexRadarConnectionBottomTextOffsetY\", \"连接下字 Y\" },\n { \"CodexRadarFiveHourQuotaRingOffsetX\", \"5h 环 X\" },\n { \"CodexRadarFiveHourQuotaRingOffsetY\", \"5h 环 Y\" },\n { \"CodexRadarFiveHourQuotaTextOffsetX\", \"5h 字 X\" },\n { \"CodexRadarFiveHourQuotaTextOffsetY\", \"5h 字 Y\" },\n { \"CodexRadarWeeklyQuotaRingOffsetX\", \"周环 X\" },\n { \"CodexRadarWeeklyQuotaRingOffsetY\", \"周环 Y\" },\n { \"CodexRadarWeeklyQuotaTextOffsetX\", \"周字 X\" },\n { \"CodexRadarWeeklyQuotaTextOffsetY\", \"周字 Y\" },\n { \"CodexRadarQuotaRadarLineOffsetX\", \"额度线 X\" },\n { \"CodexRadarQuotaRadarLineOffsetY\", \"额度线 Y\" },\n { \"CodexRadarIqRingOffsetX\", \"IQ 环 X\" },\n { \"CodexRadarIqRingOffsetY\", \"IQ 环 Y\" },\n { \"CodexRadarIqTextOffsetX\", \"IQ 字 X\" },\n { \"CodexRadarIqTextOffsetY\", \"IQ 字 Y\" },\n { \"CodexRadarPublicJsonEnabled\", \"公开 JSON 摘要\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"首页 HTML 回退\" },\n { \"CodexRadarRssFallbackEnabled\", \"RSS 重置提醒\" },\n { \"CodexRadarServiceProbeToken\", \"测一下数据源是否可用\" },\n { \"DeepSeekApiKeyRevision\", \"DeepSeek 配置\" },\n { \"CodexModelIqTestEnabled\", \"用测试值代替实时 IQ(调试用)\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过数\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过数\" },\n { \"CodexModelEfficiencyTestEnabled\", \"用测试值代替实时效率(调试用)\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"PowerThermalTransparencyPercent\", \"功耗温度透明度\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkMonitorTransparencyPercent\", \"网络监控透明度\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"立即刷新 GFW 检测\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测间隔\" },\n { \"ConnectionCheckTransparencyPercent\", \"连接检测透明度\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"连接检测边框透明度\" },\n { \"ConnectionCheckManualRefreshToken\", \"立即刷新连接检测\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"OperationButtonSize\", \"按钮大小\" },\n { \"OperationBackgroundTransparencyPercent\", \"操作面板透明度\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过数\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过数\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"ForceShowForegroundFpsEnabled\", \"调试用,强制显示前台 FPS 信息。\" },\n { \"AiRequestProtectionAutoEnabled\", \"网络监控判定为 GFW 明确阻断时,阻断本程序发往 OpenAI、ChatGPT、Claude 和 Anthropic 的请求。\" },\n { \"AiRequestProtectionManualBlockEnabled\", \"手动启用后立即阻断本程序相关 AI 请求;也可在左下角程序设置按钮双击打开的 AI 快速选单中切换。\" },\n { \"CodexQuotaPlanEnabled\", \"按本地 Codex 剩余额度缓存自动暂停或恢复选中的 Codex goal。\" },\n { \"CodexQuotaPlanWeeklyComparison\", \"周额度百分比的比较方向;默认小于 3%。\" },\n { \"CodexQuotaPlanWeeklyThresholdPercent\", \"周额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanFiveHourComparison\", \"5 小时额度百分比的比较方向;默认小于 90%。\" },\n { \"CodexQuotaPlanFiveHourThresholdPercent\", \"5 小时额度剩余百分比阈值,范围 0-100。\" },\n { \"CodexQuotaPlanResumeConditionMode\", \"额度计划触发后,选择额度恢复时看周额度、5 小时额度,还是两者都恢复。\" },\n { \"CodexQuotaPlanAutoResumePausedGoals\", \"额度恢复时优先恢复本程序上次因额度计划暂停的 goal;关闭后使用恢复列表。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityRevealDelaySeconds\", \"鼠标离开后继续隐藏的秒数。\" },\n { \"HoverOpacityRevealResetSeconds\", \"短时间重新靠近时,保持隐藏的重置秒数。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"反向隐藏临时恢复后,鼠标移开多久回到隐藏。\" },\n { \"AutoHoverOpacityIdleEnabled\", \"鼠标一段时间不动时自动进入隐藏透明度。\" },\n { \"AutoHoverOpacityIdleSeconds\", \"范围 1-300 秒,达到后自动隐藏。\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"有非 SeelenUI 应用最大化时自动进入隐藏透明度。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarSoftwareMode\", \"自动按前台窗口选择 CODEX 或 CLAUDE;也可强制固定检测软件。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"CodexRadarModelVersion\", \"模型目录版本,日常保持默认。\" },\n { \"CodexRadarPublicJsonEnabled\", \"读取 current.json 的公开摘要层,包含窗口、预测和 API 可用性说明。\" },\n { \"CodexRadarHtmlFallbackEnabled\", \"当公开 JSON 不含 model_iq 时,从首页明文模块补齐 IQ、效率和模型目录。\" },\n { \"CodexRadarRssFallbackEnabled\", \"读取 feed.xml 的重置提醒;关闭后不会用 RSS 触发额度重置保护。\" },\n { \"CodexRadarServiceProbeToken\", \"点击后探测公开摘要、授权 API、首页 HTML 和 RSS,并写入本地诊断结果。\" },\n { \"CodexRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"CodexModelIqTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelIqTestPassed\", \"手动指定 IQ 测试通过数。\" },\n { \"CodexModelIqBaselineMode\", \"选择 IQ 对比基准的来源。\" },\n { \"CodexModelIqBaselinePassed\", \"手动指定 IQ 基准通过数。\" },\n { \"CodexModelEfficiencyTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"手动指定 Token 效率测试百分比。\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"手动指定时间效率测试百分比。\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"选择 Token 效率对比基准的来源。\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"手动指定 Token 效率基准通过数。\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"手动指定 Token 效率基准消耗。\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"选择时间效率对比基准的来源。\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"手动指定时间效率基准通过数。\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"手动指定时间效率基准秒数。\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"低于该百分比时标记为偏低。\" },\n { \"CodexRadarEnabled\", \"关闭后会释放 Codex Radar 分层窗口;默认开启以保持现有桌面不变。\" },\n { \"DisplayTimeZoneMode\", \"控制窗口里时间文字使用本机、北京时间还是指定时区。\" },\n { \"DisplayTimeZoneId\", \"Windows 时区 ID,指定时区模式下生效。\" },\n { \"ClaudeRadarEnabled\", \"开启独立 Claude Radar 分层窗口;默认关闭,避免升级后改变现有桌面。\" },\n { \"ClaudeRadarModelKey\", \"Claude Radar 站点内部 m* 模型键;留空时按站点当前目录自动选择。\" },\n { \"ClaudeRadarJsonEnabled\", \"读取 claudecoderadar.com/data/claude-code-radar.json 作为主数据源。\" },\n { \"ClaudeRadarHomepageFallbackEnabled\", \"主 JSON 失败或缺少模型元数据时,顺序读取首页 MODEL_NAMES 作为弱回退;不会伪造 IQ、效率或额度。\" },\n { \"ClaudeRadarCommunityRatingsEnabled\", \"读取 Claude Radar 社区体感分接口,并通过独立映射表关联模型。\" },\n { \"ClaudeRadarLocalQuotaFallbackEnabled\", \"站点额度趋势不完整时使用本地 7 天 JSONL 历史绘制额度线。\" },\n { \"ClaudeRadarServiceProbeToken\", \"点击后触发 Claude Radar 下次刷新,服务状态显示在窗口右侧。\" },\n { \"ClaudeRadarRandomTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestAutoRefresh\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"ClaudeRadarRandomTestRefreshToken\", \"点击后让随机测试数据立刻换一组。\" },\n { \"ClaudeRadarRenderVariant\", \"预留项,当前第一版固定使用 EvenRow 风格。\" },\n { \"MainWidgetRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"CodexRadarRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"NetworkMonitorRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"PowerThermalRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"ConnectionCheckRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"OperationRenderVariant\", \"切换后立即预览,可随时切回。\" },\n { \"DeepSeekApiKeyRevision\", \"配置 DeepSeek API Key;密钥只写入本地文件,修订号用于触发即时刷新。\" },\n { \"CodexRadarManualLayoutEnabled\", \"开启后下方布局参数实时影响 Codex Radar 内部模块,不需要重启。\" },\n { \"CodexRadarManualLeftPercent\", \"调整效率/IQ/连接流程区与余额区的左右分配。\" },\n { \"CodexRadarManualGapPixels\", \"调整左侧区域和余额区之间的像素间距。\" },\n { \"CodexRadarManualEfficiencyTextWidthPixels\", \"调整左侧效率/IQ 圆环右侧文字列宽。\" },\n { \"CodexRadarManualQuotaRowsWidthPixels\", \"调整余额两行圆环和重置时间的总宽度。\" },\n { \"CodexRadarManualIqStatusWidthPixels\", \"调整余额区右侧 IQ 圆环与降智文字列宽。\" },\n { \"CodexRadarManualTextScalePercent\", \"只影响 Codex Radar 内部状态文字和余额时间字号。\" },\n { \"CodexRadarManualRingScalePercent\", \"只影响 Codex Radar 内部效率、余额和 IQ 圆环大小。\" },\n { \"PowerThermalAutoSizeEnabled\", \"根据告警数量自动调整功耗温度窗口高度。\" },\n { \"PowerThermalAutoDirection\", \"自动调整时从哪个方向展开。\" },\n { \"PowerThermalVisibleAlertCount\", \"功耗温度窗口中最多显示的告警数量。\" },\n { \"ThermalTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"NetworkStatusTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测的自动刷新间隔。\" },\n { \"GfwProbeManualRefreshToken\", \"点击后请求立刻刷新 GFW 检测。\" },\n { \"ConnectionCheckManualRefreshToken\", \"点击后请求立刻刷新连接检测。\" },\n { \"ConnectionCheckIntervalSeconds\", \"连接检测的自动刷新间隔。\" },\n { \"CloudEndpointTestSeed\", \"仅用于测试显示效果,日常保持默认。\" },\n { \"CloudStatusRegionMask\", \"云服务状态的地区过滤位掩码,日常保持默认。\" },\n { \"CleanIpBadgeTestMode\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"AlertTestEnabled\", \"仅用于测试显示效果,日常保持关闭。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"SeelenDockForegroundPulseEnabled\", \"需要时短暂拉前 Seelen Dock,避免被系统窗口压住。\" },\n { \"FallbackDisconnectedDisplaysEnabled\", \"指定显示器未连接时,将对应模块回退到当前主显示器。\" },\n { \"MainDisplayDeviceName\", \"留空使用当前主显示器;选择 DISPLAY 后按该显示器工作区定位。\" },\n { \"CodexRadarDisplayDeviceName\", \"留空使用当前主显示器;未连接且不回退时保留原显示器工作区。\" },\n { \"PowerThermalDisplayDeviceName\", \"留空使用当前主显示器;适合把功耗模块固定到副屏。\" },\n { \"NetworkMonitorDisplayDeviceName\", \"留空使用当前主显示器;适合把网络监控固定到副屏。\" },\n { \"ConnectionCheckDisplayDeviceName\", \"留空使用当前主显示器;适合把连接检测固定到副屏。\" },\n { \"OperationDisplayDeviceName\", \"留空使用当前主显示器;操作面板偏移量按目标显示器左下角计算。\" },\n { GlobalLayoutEditCommandName, \"打开全屏布局编辑遮罩,拖拽模块位置;Enter 保存,Esc 放弃。\" },\n { \"Width\", \"逻辑像素,主窗口宽度。\" },\n { \"Height\", \"逻辑像素,主窗口高度。\" },\n { \"LeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"BottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"CodexRadarWidth\", \"逻辑像素,Codex Radar 宽度。\" },\n { \"CodexRadarHeight\", \"逻辑像素,Codex Radar 高度。\" },\n { \"CodexRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"CodexRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ClaudeRadarWidth\", \"逻辑像素,Claude Radar 宽度。\" },\n { \"ClaudeRadarHeight\", \"逻辑像素,Claude Radar 高度。\" },\n { \"ClaudeRadarLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ClaudeRadarBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"PowerThermalWidth\", \"逻辑像素,功耗温度窗口宽度。\" },\n { \"PowerThermalHeight\", \"逻辑像素,功耗温度窗口高度。\" },\n { \"PowerThermalLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"PowerThermalBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"NetworkMonitorWidth\", \"逻辑像素,网络监控窗口宽度。\" },\n { \"NetworkMonitorHeight\", \"逻辑像素,网络监控窗口高度。\" },\n { \"NetworkMonitorLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"NetworkMonitorBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"ConnectionCheckWidth\", \"逻辑像素,连接检测窗口宽度。\" },\n { \"ConnectionCheckHeight\", \"逻辑像素,连接检测窗口高度。\" },\n { \"ConnectionCheckLeftX\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"ConnectionCheckBottomY\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"OperationLeftOffset\", \"逻辑像素,距目标显示器左边缘。\" },\n { \"OperationBottomOffset\", \"逻辑像素,距目标显示器下边缘。\" },\n { \"BackgroundTransparencyPercent\", \"只影响主窗口背景底色,数值越高越透明。\" },\n { \"ApplicationTransparencyPercent\", \"影响主窗口整体透明度,数值越高越透明。\" },\n { \"CodexRadarTransparencyPercent\", \"影响 Codex Radar 背景透明度。\" },\n { \"ClaudeRadarTransparencyPercent\", \"影响 Claude Radar 背景透明度。\" },\n { \"PowerThermalTransparencyPercent\", \"影响功耗温度窗口背景透明度。\" },\n { \"NetworkMonitorTransparencyPercent\", \"影响网络监控窗口背景透明度。\" },\n { \"ConnectionCheckTransparencyPercent\", \"影响连接检测窗口背景透明度。\" },\n { \"ConnectionCheckBorderTransparencyPercent\", \"影响连接检测三框边框透明度。\" },\n { \"OperationBackgroundTransparencyPercent\", \"影响左下角操作面板背景透明度。\" },\n { \"ShowCpu\", \"在主窗口显示 CPU 指标。\" },\n { \"ShowMemory\", \"在主窗口显示内存指标。\" },\n { \"ShowDisk\", \"在主窗口显示磁盘指标。\" },\n { \"ShowNetwork\", \"在主窗口显示网络指标。\" },\n { \"ShowGpu\", \"在主窗口显示 GPU 指标。\" },\n { \"ShowNpu\", \"在主窗口显示 NPU 指标。\" },\n { \"OperationPrimaryPanelMode\", \"自动模式会按 SeelenUI 运行态在 Windows 按钮和内存饼图之间切换;隐藏会让小按钮移到最左侧。\" },\n { \"OperationButtonSize\", \"左下角操作面板按钮的逻辑像素大小。\" },\n { \"OperationWindowsButtonEnabled\", \"SeelenUI 未运行时会自动隐藏;关闭后始终不显示左侧 Windows 按钮。\" },\n { \"OperationMemoryPieEnabled\", \"左侧 Windows 按钮隐藏时显示物理、虚拟和前台程序内存占用饼图。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.SettingsWarmTheme.ToggleTrackHover : DesignTokens.SettingsWarmTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsWarmTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsWarmTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsWarmTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = false;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = false;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + controlHeight + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n int controlHeight = GetValueControlHeight(controlWidth);\n bool compact = ShouldUseCompactLayout(this.Width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, controlHeight);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, controlHeight);\n int controlTop = top + Math.Max(0, (contentHeight - controlHeight) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, controlHeight);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private int GetValueControlHeight(int controlWidth)\n {\n VariantPicker picker = this.valueControl as VariantPicker;\n if (picker != null)\n {\n return picker.GetPreferredHeightForWidth(controlWidth);\n }\n\n return this.valueControl.Height;\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── AdvancedSectionHeader ────────────────────────────────────────────\n // Clickable \"复杂选项\" divider: everything below it stays collapsed until\n // the user expands it, keeping everyday pages short. Searching bypasses it.\n private sealed class AdvancedSectionHeader : Panel\n {\n private readonly Font titleFont;\n private readonly Font hintFont;\n private bool expanded;\n private bool hover;\n\n public AdvancedSectionHeader(Font titleFont, Font hintFont)\n {\n this.titleFont = titleFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = MicaBase;\n this.Cursor = Cursors.Hand;\n }\n\n public int AdvancedRowCount { get; set; }\n\n public bool Expanded\n {\n get { return this.expanded; }\n set { this.expanded = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(this.hover ? CardHover : CardRest))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(StrokeColor))\n {\n g.DrawPath(pen, path);\n }\n\n // Chevron glyph: E70D = down, E76C = right (Segoe MDL2 / Fluent Icons).\n string chevron = this.expanded ? \"\\uE70D\" : \"\\uE76C\";\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(chevron, icoFont, brush, 20, iconY);\n }\n\n using (SolidBrush brush = new SolidBrush(TextPrimary))\n {\n g.DrawString(\"复杂选项\", this.titleFont, brush, 58, (this.Height - this.titleFont.Height) / 2f - 8);\n }\n\n string hint = this.expanded\n ? \"点击收起\"\n : \"不常用的精细调整与测试项\" + (this.AdvancedRowCount > 0 ? \"(\" + this.AdvancedRowCount.ToString(CultureInfo.InvariantCulture) + \" 项)\" : string.Empty);\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n g.DrawString(hint, this.hintFont, brush, 58, this.Height / 2f + 2);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── VariantPicker ───────────────────────────────────────────────────\n // Renders per-window visual variants as cards. Sample PNGs are loaded lazily by the outer form\n // only for real owner-backed settings windows, so binding self-tests never construct render forms.\n private sealed class VariantPicker : Panel\n {\n private const int CardHeight = 124;\n private const int CardGap = 10;\n private const int ThumbnailHeight = 78;\n\n private readonly Type enumType;\n private readonly Font labelFont;\n private readonly Font hintFont;\n private readonly List<VariantChoice> choices = new List<VariantChoice>();\n private object selectedValue;\n private int hoverIndex = -1;\n private bool samplesAttempted;\n\n public event EventHandler ValueChanged;\n public event EventHandler PreferredHeightChanged;\n\n public VariantPicker(string propertyName, Type enumType, Font labelFont, Font hintFont)\n {\n this.enumType = enumType;\n this.labelFont = labelFont;\n this.hintFont = hintFont;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n\n Array values = Enum.GetValues(enumType);\n for (int i = 0; i < values.Length; i++)\n {\n object value = values.GetValue(i);\n this.choices.Add(new VariantChoice(value, new EnumOption(value).ToString()));\n if (i == 0)\n {\n this.selectedValue = value;\n }\n }\n\n UpdatePreferredHeight();\n }\n\n public void SetSelectedValue(object value)\n {\n if (value == null || !this.enumType.IsInstanceOfType(value))\n {\n if (this.choices.Count > 0)\n {\n this.selectedValue = this.choices[0].Value;\n }\n }\n else\n {\n this.selectedValue = value;\n }\n\n this.Invalidate();\n }\n\n public object GetSelectedValue()\n {\n return this.selectedValue;\n }\n\n public int GetPreferredHeightForWidth(int width)\n {\n int columns = GetColumnCount(width);\n int rows = Math.Max(1, (this.choices.Count + columns - 1) / columns);\n return rows * CardHeight + Math.Max(0, rows - 1) * CardGap;\n }\n\n public bool HasMissingSamples(string directory, string prefix)\n {\n if (this.samplesAttempted)\n {\n return false;\n }\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n string path = GetSamplePath(directory, prefix, this.choices[i].Value);\n if (!System.IO.File.Exists(path))\n {\n return true;\n }\n }\n\n return false;\n }\n\n public void LoadSamples(string directory, string prefix)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n VariantChoice choice = this.choices[i];\n string path = GetSamplePath(directory, prefix, choice.Value);\n if (!System.IO.File.Exists(path))\n {\n continue;\n }\n\n try\n {\n using (Image original = Image.FromFile(path))\n {\n Image old = choice.Image;\n choice.Image = new Bitmap(original);\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n catch\n {\n Image old = choice.Image;\n choice.Image = null;\n if (old != null)\n {\n old.Dispose();\n }\n }\n }\n\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n public void MarkSamplesUnavailable()\n {\n this.samplesAttempted = true;\n this.Invalidate();\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n UpdatePreferredHeight();\n }\n\n protected override void OnMouseMove(MouseEventArgs e)\n {\n int nextHover = GetChoiceIndexAt(e.Location);\n if (nextHover != this.hoverIndex)\n {\n this.hoverIndex = nextHover;\n this.Invalidate();\n }\n\n base.OnMouseMove(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hoverIndex = -1;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n protected override void OnMouseClick(MouseEventArgs e)\n {\n base.OnMouseClick(e);\n int index = GetChoiceIndexAt(e.Location);\n if (index < 0 || index >= this.choices.Count)\n {\n return;\n }\n\n object nextValue = this.choices[index].Value;\n if (object.Equals(this.selectedValue, nextValue))\n {\n return;\n }\n\n this.selectedValue = nextValue;\n this.Invalidate();\n if (this.ValueChanged != null)\n {\n this.ValueChanged(this, EventArgs.Empty);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.InterpolationMode = InterpolationMode.HighQualityBicubic;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n for (int i = 0; i < this.choices.Count; i++)\n {\n Rectangle rect = GetCardBounds(i);\n if (rect.Width <= 0 || rect.Height <= 0)\n {\n continue;\n }\n\n VariantChoice choice = this.choices[i];\n bool selected = object.Equals(choice.Value, this.selectedValue);\n bool hover = i == this.hoverIndex;\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(hover ? CardHover : ControlBg))\n {\n g.FillPath(bg, path);\n }\n\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (Pen pen = new Pen(selected ? AccentClr : ControlBorder, selected ? 2.0f : 1.0f))\n {\n g.DrawPath(pen, path);\n }\n\n Rectangle thumb = new Rectangle(rect.Left + 8, rect.Top + 8, rect.Width - 16, ThumbnailHeight);\n DrawChoiceThumbnail(g, choice, thumb);\n\n Rectangle labelRect = new Rectangle(rect.Left + 10, thumb.Bottom + 7, rect.Width - 20, 24);\n using (StringFormat format = new StringFormat())\n using (SolidBrush textBrush = new SolidBrush(selected ? TextPrimary : TextSecondary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(choice.Label, this.labelFont, textBrush, labelRect, format);\n }\n }\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (this.choices[i].Image != null)\n {\n this.choices[i].Image.Dispose();\n this.choices[i].Image = null;\n }\n }\n }\n\n base.Dispose(disposing);\n }\n\n private void DrawChoiceThumbnail(Graphics g, VariantChoice choice, Rectangle thumb)\n {\n using (GraphicsPath thumbPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n using (SolidBrush thumbBg = new SolidBrush(MicaBase))\n {\n g.FillPath(thumbBg, thumbPath);\n }\n\n if (choice.Image == null)\n {\n using (StringFormat format = new StringFormat())\n using (SolidBrush brush = new SolidBrush(TextTertiary))\n {\n format.Alignment = StringAlignment.Center;\n format.LineAlignment = StringAlignment.Center;\n format.Trimming = StringTrimming.EllipsisCharacter;\n format.FormatFlags = StringFormatFlags.NoWrap;\n g.DrawString(\"纯文字模式\", this.hintFont, brush, thumb, format);\n }\n return;\n }\n\n Rectangle target = FitImage(choice.Image.Size, thumb);\n GraphicsState state = g.Save();\n using (GraphicsPath clipPath = CreateRoundRectangle(thumb, DesignTokens.Radius.SettingsCard))\n {\n g.SetClip(clipPath);\n g.DrawImage(choice.Image, target);\n }\n g.Restore(state);\n }\n\n private void UpdatePreferredHeight()\n {\n int oldHeight = this.Height;\n int nextHeight = GetPreferredHeightForWidth(this.Width);\n if (nextHeight != oldHeight)\n {\n this.Height = nextHeight;\n if (this.PreferredHeightChanged != null)\n {\n this.PreferredHeightChanged(this, EventArgs.Empty);\n }\n }\n }\n\n private int GetColumnCount()\n {\n return GetColumnCount(this.Width);\n }\n\n private static int GetColumnCount(int width)\n {\n width = Math.Max(1, width);\n if (width >= 520)\n {\n return 3;\n }\n\n if (width >= 340)\n {\n return 2;\n }\n\n return 1;\n }\n\n private Rectangle GetCardBounds(int index)\n {\n int columns = GetColumnCount();\n int col = index % columns;\n int row = index / columns;\n int cardWidth = Math.Max(86, (Math.Max(1, this.Width) - (columns - 1) * CardGap) / columns);\n return new Rectangle(col * (cardWidth + CardGap), row * (CardHeight + CardGap), cardWidth, CardHeight);\n }\n\n private int GetChoiceIndexAt(Point point)\n {\n for (int i = 0; i < this.choices.Count; i++)\n {\n if (GetCardBounds(i).Contains(point))\n {\n return i;\n }\n }\n\n return -1;\n }\n\n private static string GetSamplePath(string directory, string prefix, object value)\n {\n string variant = Convert.ToString(value, CultureInfo.InvariantCulture);\n return System.IO.Path.Combine(directory, prefix + \"-\" + variant.ToLowerInvariant() + \".png\");\n }\n\n private static Rectangle FitImage(Size imageSize, Rectangle bounds)\n {\n if (imageSize.Width <= 0 || imageSize.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0)\n {\n return bounds;\n }\n\n float sx = bounds.Width / (float)imageSize.Width;\n float sy = bounds.Height / (float)imageSize.Height;\n float scale = Math.Min(sx, sy);\n int width = Math.Max(1, (int)Math.Round(imageSize.Width * scale));\n int height = Math.Max(1, (int)Math.Round(imageSize.Height * scale));\n return new Rectangle(\n bounds.Left + (bounds.Width - width) / 2,\n bounds.Top + (bounds.Height - height) / 2,\n width,\n height);\n }\n\n private sealed class VariantChoice\n {\n public VariantChoice(object value, string label)\n {\n this.Value = value;\n this.Label = label ?? string.Empty;\n }\n\n public object Value;\n public string Label;\n public Image Image;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public AdvancedSectionHeader AdvancedHeader;\n public bool AdvancedExpanded;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public bool Advanced;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Name = property == null ? string.Empty : property.Name;\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public SettingEditor(string name, SettingRow card, Control control)\n {\n this.Name = name ?? string.Empty;\n this.Property = null;\n this.Card = card;\n this.Control = control;\n }\n\n public string Name { get; private set; }\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class ModelOption\n {\n public ModelOption(string key, string label, bool available, bool missingFromCatalog)\n {\n this.Key = CodexRadarModelCatalog.NormalizeModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.MissingFromCatalog = missingFromCatalog;\n }\n\n public string Key { get; private set; }\n private string Label { get; set; }\n private bool Available { get; set; }\n private bool MissingFromCatalog { get; set; }\n\n public override string ToString()\n {\n if (this.MissingFromCatalog)\n {\n return this.Label + \"(未在目录)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class ClaudeModelOption\n {\n public ClaudeModelOption(string key, string label, bool available, bool pending)\n {\n this.Key = WidgetSettings.NormalizeClaudeRadarModelKey(key);\n this.Label = label ?? string.Empty;\n this.Available = available;\n this.Pending = pending;\n }\n\n public string Key { get; private set; }\n public string Label { get; private set; }\n public bool Available { get; private set; }\n public bool Pending { get; private set; }\n\n public override string ToString()\n {\n if (this.Key.Length == 0)\n {\n return this.Label;\n }\n\n if (this.Pending)\n {\n return this.Label + \"(待映射)\";\n }\n\n return this.Available ? this.Label : this.Label + \"(暂不可用)\";\n }\n }\n\n private sealed class DisplayOption\n {\n public DisplayOption(string value, string label)\n {\n this.Value = WidgetSettings.NormalizeDisplayDeviceName(value);\n this.Label = label ?? string.Empty;\n }\n\n public string Value { get; private set; }\n private string Label { get; set; }\n\n public override string ToString()\n {\n return this.Label;\n }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n if (this.Value is OperationPrimaryPanelMode)\n {\n OperationPrimaryPanelMode mode = (OperationPrimaryPanelMode)this.Value;\n if (mode == OperationPrimaryPanelMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == OperationPrimaryPanelMode.WindowsButton)\n {\n return \"Windows 按钮\";\n }\n\n if (mode == OperationPrimaryPanelMode.MemoryPie)\n {\n return \"内存饼图\";\n }\n\n if (mode == OperationPrimaryPanelMode.Hidden)\n {\n return \"隐藏\";\n }\n }\n\n if (this.Value is CodexRadarSoftwareMode)\n {\n CodexRadarSoftwareMode mode = (CodexRadarSoftwareMode)this.Value;\n if (mode == CodexRadarSoftwareMode.Auto)\n {\n return \"自动\";\n }\n\n if (mode == CodexRadarSoftwareMode.Codex)\n {\n return \"CODEX\";\n }\n\n if (mode == CodexRadarSoftwareMode.Claude)\n {\n return \"CLAUDE\";\n }\n }\n\n if (this.Value is CodexQuotaPlanComparison)\n {\n CodexQuotaPlanComparison comparison = (CodexQuotaPlanComparison)this.Value;\n if (comparison == CodexQuotaPlanComparison.GreaterThan)\n {\n return \"大于\";\n }\n\n return \"小于\";\n }\n\n if (this.Value is CodexQuotaPlanResumeConditionMode)\n {\n CodexQuotaPlanResumeConditionMode mode = (CodexQuotaPlanResumeConditionMode)this.Value;\n if (mode == CodexQuotaPlanResumeConditionMode.WeeklyOnly)\n {\n return \"仅周额度\";\n }\n\n if (mode == CodexQuotaPlanResumeConditionMode.FiveHourOnly)\n {\n return \"仅 5 小时额度\";\n }\n\n return \"周额度与 5 小时额度\";\n }\n\n // All six per-window render-variant enums share member names for the common variants\n // (Classic plus the four OLED-safe restyle schemes); CodexRadarRenderVariant additionally\n // carries EvenGrid/EvenRow. Label by member name once instead of duplicating per enum type.\n if (this.Value is CodexRadarRenderVariant ||\n this.Value is MainWidgetRenderVariant ||\n this.Value is NetworkMonitorRenderVariant ||\n this.Value is PowerThermalRenderVariant ||\n this.Value is ConnectionCheckRenderVariant ||\n this.Value is OperationRenderVariant)\n {\n string variantName = Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n if (variantName == \"Classic\") return \"经典布局\";\n if (variantName == \"EvenGrid\") return \"均布六格\";\n if (variantName == \"EvenRow\") return \"均布单行\";\n if (variantName == \"Typographic\") return \"排版流(OLED 安全)\";\n if (variantName == \"AmberHud\") return \"暗琥珀仪表(OLED 安全)\";\n if (variantName == \"WarmCard\") return \"暖灰暗卡片(OLED 安全)\";\n if (variantName == \"Phosphor\") return \"磷光绿终端(OLED 安全)\";\n }\n\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "66ecfd81fc52aa1b001e4ad2fb398dad8b01851c": {"t": 1785756679.7676446, "n": 78601, "h": "a1bdce7132615d45", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int EmSetCueBanner = 0x1501;\n private const int NavWidth = 409;\n private const int NavItemHeight = 60;\n\n private static readonly Color MicaBase = DesignTokens.NeonGeekTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.NeonGeekTheme.InputBackground;\n private static readonly Color CardRest = DesignTokens.NeonGeekTheme.CardGlassRest;\n private static readonly Color CardHover = DesignTokens.NeonGeekTheme.CardGlassHover;\n private static readonly Color StrokeColor = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.NeonGeekTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.NeonGeekTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.NeonGeekTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.NeonGeekTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.NeonGeekTheme.CyberCyan;\n private static readonly Color AccentHover = DesignTokens.NeonGeekTheme.ElectricPurple;\n private static readonly Color AccentPressed = DesignTokens.NeonGeekTheme.CyberCyan;\n private static readonly Color ErrorClr = DesignTokens.SettingsTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly UiFontCache fontCache = new UiFontCache();\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool messageFilterRegistered;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = false;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n this.MinimumSize = GetMinimumWindowSizeForScreen();\n this.ClientSize = FitClientSizeToScreen(new Size(1888, 1312));\n this.Font = GetUiFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.saved && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.RevertSettings(this.baseline);\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n this.fontCache.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = GetUiFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.NeonGeekTheme.ElectricPurple; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = GetUiFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = GetUiFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, NavWidth));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = GetUiFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n ShowStatus(\"保存完成\", false);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private Button BuildCommandButton(string text, bool primary)\n {\n Button button = new Button();\n button.Text = text; // Keep text so AutoSize works\n button.AutoSize = true;\n button.Padding = new Padding(24, 0, 24, 0); // Adds horizontal padding to AutoSize\n button.Height = 54;\n button.Margin = new Padding(0, 0, 12, 0);\n button.FlatStyle = FlatStyle.Flat;\n button.FlatAppearance.BorderSize = 0;\n button.Font = GetUiFont(9.5f, FontStyle.Bold);\n button.Cursor = Cursors.Hand;\n button.BackColor = MicaBase;\n button.ForeColor = primary ? Color.Black : TextSecondary;\n Color backBase = primary ? AccentClr : DesignTokens.SettingsTheme.ButtonRest;\n Color backHover = primary ? AccentHover : DesignTokens.SettingsTheme.ButtonHover;\n Color backDown = primary ? AccentPressed : DesignTokens.SettingsTheme.ButtonPressed;\n Color borderColor = primary ? AccentClr : ControlBorder;\n\n bool hover = false;\n bool down = false;\n\n button.MouseEnter += (s, e) => { hover = true; button.Invalidate(); };\n button.MouseLeave += (s, e) => { hover = false; button.Invalidate(); };\n button.MouseDown += (s, e) => { down = true; button.Invalidate(); };\n button.MouseUp += (s, e) => { down = false; button.Invalidate(); };\n\n button.Paint += (s, e) =>\n {\n e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;\n e.Graphics.Clear(button.Parent != null ? button.Parent.BackColor : MicaBase);\n Color currentBack = down ? backDown : (hover ? backHover : backBase);\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(0, 0, button.Width - 1, button.Height - 1), 6))\n {\n using (SolidBrush brush = new SolidBrush(currentBack))\n {\n e.Graphics.FillPath(brush, path);\n }\n using (Pen pen = new Pen(borderColor, 1))\n {\n e.Graphics.DrawPath(pen, path);\n }\n }\n TextRenderer.DrawText(e.Graphics, text, button.Font, button.ClientRectangle, button.ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);\n };\n return button;\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n AddPageGrouped(\"\\uE115\", \"系统\", \"启动、性能、可见性和全局恢复。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\", \"ForceShowForegroundFpsEnabled\" },\n new string[] { \"恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" }\n });\n\n AddPageGrouped(\"\\uE7B3\", \"隐藏与鼠标\", \"悬停透明、判定范围、延迟显现和防烧屏。\", new string[][]\n {\n new string[] { \"悬停透明\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" }\n });\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口尺寸、位置、透明度和显示项。\", new string[][]\n {\n new string[] { \"尺寸与位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" },\n new string[] { \"显示项\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" }\n });\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"模型、额度、效率和测试覆盖。\", new string[][]\n {\n new string[] { \"窗口\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineMode\", \"CodexModelIqBaselinePassed\" },\n new string[] { \"效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"测试\", \"ThermalTestMode\" }\n });\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW、云服务和出口身份检测。\", new string[][]\n {\n new string[] { \"网络监控窗口\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"适配器\", \"NetworkMonitorAdapterId\", \"NetworkStatusTestMode\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\", \"GfwProbeManualRefreshToken\" },\n new string[] { \"云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"连接检测\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\",\n \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\", \"ConnectionCheckIntervalSeconds\" },\n new string[] { \"出口身份\", \"ConnectionCheckManualRefreshToken\", \"CleanIpBadgeTestMode\" }\n });\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板尺寸、位置和告警测试。\", new string[][]\n {\n new string[] { \"尺寸与位置\", \"OperationButtonSize\", \"OperationLeftOffset\", \"OperationBottomOffset\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = GetUiFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Property.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = NavWidth - 24;\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = GetUiFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = GetUiFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = GetUiFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control, GetUiFont(10.0f), GetUiFont(8.5f));\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = GetUiFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = GetUiFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = GetUiFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo != null)\n {\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo != null)\n {\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = pageMatch || groupTitleMatch ||\n editor.Property.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Property.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : name;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n Rectangle workArea = Screen.PrimaryScreen.WorkingArea;\n int width = Math.Min(desiredSize.Width, Math.Max(760, workArea.Width - 80));\n int height = Math.Min(desiredSize.Height, Math.Max(560, workArea.Height - 80));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n Rectangle workArea = Screen.PrimaryScreen.WorkingArea;\n return new Size(\n Math.Min(1440, Math.Max(1216, workArea.Width - 128)),\n Math.Min(992, Math.Max(896, workArea.Height - 128)));\n }\n\n private Font GetUiFont(float size)\n {\n return GetUiFont(size, FontStyle.Regular);\n }\n\n private Font GetUiFont(float size, FontStyle style)\n {\n return this.fontCache.GetUiPoint(size, style);\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static void RunSettingsBindingSelfTest()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir);\n string path = System.IO.Path.Combine(dir, \"settings_layout_dump.txt\");\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(path))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n try\n {\n string dir = System.IO.Path.Combine(\n Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),\n ProductIdentity.MachineName);\n System.IO.File.WriteAllText(System.IO.Path.Combine(dir, \"dump_error.txt\"), ex.ToString());\n }\n catch { }\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"Width\",\n \"CodexRadarModelKey\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n int originalPageIndex = this.selectedPageIndex;\n this.ClientSize = new Size(this.MinimumSize.Width, this.MinimumSize.Height);\n Application.DoEvents();\n try\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Property.Name);\n }\n }\n }\n }\n finally\n {\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"HoverOpacityEnabled\", \"悬停透明 95%\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"鼠标判定边长\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"随机测试刷新令牌\" },\n { \"CodexModelIqTestEnabled\", \"覆盖实时 IQ 数据\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过项\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过项\" },\n { \"CodexModelEfficiencyTestEnabled\", \"覆盖实时效率数据\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"GFW 手动刷新令牌\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过项\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过项\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.NeonGeekTheme.ToggleTrackHover : DesignTokens.NeonGeekTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n private bool layoutInProgress;\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n if (this.layoutInProgress) return;\n this.layoutInProgress = true;\n try\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n finally\n {\n this.layoutInProgress = false;\n }\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n if (!this.layoutInProgress) LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private const int CompactLayoutWidthThreshold = 928;\n private const int CompactLayoutRemainingTextThreshold = 320;\n\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl, Font titleFont, Font hintFont)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = titleFont;\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = true;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = hintFont;\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = true;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int pad = this.Padding.Top + this.Padding.Bottom;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n bool compact = ShouldUseCompactLayout(width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + this.valueControl.Height + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, this.valueControl.Height);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n bool compact = ShouldUseCompactLayout(this.Width, controlWidth);\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, this.valueControl.Height);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, this.valueControl.Height);\n int controlTop = top + Math.Max(0, (contentHeight - this.valueControl.Height) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, this.valueControl.Height);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n\n private bool ShouldUseCompactLayout(int width, int controlWidth)\n {\n int availableWidth = width - this.Padding.Left - this.Padding.Right;\n return width < CompactLayoutWidthThreshold || availableWidth - controlWidth < CompactLayoutRemainingTextThreshold;\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}, "9f99fa89c6a5b48bec9eefe3bb8db07e56e8021f": {"t": 1785756681.685824, "n": 78099, "h": "d5f009067a412679", "c": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\ninternal sealed class Win11SettingsForm : Form, IMessageFilter, ISettingsWindow\n{\n private const int PreviewDebounceMs = 75;\n private const int WmMouseWheel = 0x020A;\n private const int EmSetCueBanner = 0x1501;\n private const int NavWidth = 409;\n private const int NavItemHeight = 60;\n\n private static readonly Color MicaBase = DesignTokens.NeonGeekTheme.WindowBase;\n private static readonly Color MicaLayer = DesignTokens.NeonGeekTheme.WindowBase;\n private static readonly Color CardRest = DesignTokens.NeonGeekTheme.CardGlassRest;\n private static readonly Color CardHover = DesignTokens.NeonGeekTheme.CardGlassHover;\n private static readonly Color StrokeColor = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color DividerColor = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color ControlBg = DesignTokens.NeonGeekTheme.InputBackground;\n private static readonly Color ControlBorder = DesignTokens.NeonGeekTheme.DividerLines;\n private static readonly Color TextPrimary = DesignTokens.NeonGeekTheme.TextPrimary;\n private static readonly Color TextSecondary = DesignTokens.NeonGeekTheme.TextSecondary;\n private static readonly Color TextTertiary = DesignTokens.NeonGeekTheme.TextMuted;\n private static readonly Color AccentClr = DesignTokens.NeonGeekTheme.CyberCyan;\n private static readonly Color AccentHover = DesignTokens.NeonGeekTheme.ElectricPurple;\n private static readonly Color AccentPressed = DesignTokens.NeonGeekTheme.CyberCyan;\n private static readonly Color ErrorClr = DesignTokens.SettingsTheme.ErrorText;\n\n private readonly WidgetForm owner;\n private readonly Timer previewTimer;\n private readonly Timer statusTimer;\n private readonly Dictionary<string, SettingEditor> editors = new Dictionary<string, SettingEditor>(StringComparer.Ordinal);\n private readonly List<CategoryPage> pages = new List<CategoryPage>();\n private WidgetSettings baseline;\n private FlowLayoutPanel navigationPanel;\n private Panel contentHost;\n private TextBox searchBox;\n private Label statusLabel;\n private int selectedPageIndex;\n private bool initializing;\n private bool saved;\n private bool messageFilterRegistered;\n private static Font iconFontCache;\n\n public bool OwnerFormClosing { get; set; }\n\n // ── Constructor ──────────────────────────────────────────────────────\n public Win11SettingsForm(WidgetForm owner, WidgetSettings baseline)\n {\n this.owner = owner;\n this.baseline = baseline.Clone();\n this.baseline.Normalize();\n\n this.previewTimer = new Timer();\n this.previewTimer.Interval = PreviewDebounceMs;\n this.previewTimer.Tick += OnPreviewTimerTick;\n this.statusTimer = new Timer();\n this.statusTimer.Interval = 5000;\n this.statusTimer.Tick += OnStatusTimerTick;\n\n this.Text = \"Desktop Codex Assistant 设置\";\n this.FormBorderStyle = FormBorderStyle.None;\n this.StartPosition = FormStartPosition.CenterScreen;\n this.ShowInTaskbar = false;\n this.MaximizeBox = false;\n this.MinimizeBox = false;\n this.MinimumSize = GetMinimumWindowSizeForScreen();\n this.ClientSize = FitClientSizeToScreen(new Size(1888, 1312));\n this.Font = DesignTokens.CreateUIFont(10.0f);\n this.BackColor = MicaBase;\n this.ForeColor = TextSecondary;\n\n BuildShell();\n LoadSettings(this.baseline);\n }\n\n // ── Form Lifecycle ───────────────────────────────────────────────────\n protected override void OnHandleCreated(EventArgs e)\n {\n base.OnHandleCreated(e);\n TryEnableDwmRoundCorners();\n }\n\n protected override void OnShown(EventArgs e)\n {\n base.OnShown(e);\n if (!this.messageFilterRegistered)\n {\n Application.AddMessageFilter(this);\n this.messageFilterRegistered = true;\n }\n }\n\n protected override void OnFormClosing(FormClosingEventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.saved && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.RevertSettings(this.baseline);\n }\n\n base.OnFormClosing(e);\n }\n\n protected override void OnFormClosed(FormClosedEventArgs e)\n {\n if (this.messageFilterRegistered)\n {\n Application.RemoveMessageFilter(this);\n this.messageFilterRegistered = false;\n }\n\n this.previewTimer.Tick -= OnPreviewTimerTick;\n this.previewTimer.Dispose();\n this.statusTimer.Tick -= OnStatusTimerTick;\n this.statusTimer.Dispose();\n base.OnFormClosed(e);\n }\n\n // ── IMessageFilter ───────────────────────────────────────────────────\n public bool PreFilterMessage(ref Message m)\n {\n if (m.Msg != WmMouseWheel || !this.Visible || this.WindowState == FormWindowState.Minimized)\n {\n return false;\n }\n\n if (!this.Bounds.Contains(Cursor.Position))\n {\n return false;\n }\n\n int delta = unchecked((short)((long)m.WParam >> 16));\n FluentScrollPanel page = GetSelectedScrollPage();\n return page != null && page.ScrollByMouseWheelDelta(delta);\n }\n\n protected override void WndProc(ref Message m)\n {\n base.WndProc(ref m);\n const int WM_NCHITTEST = 0x0084;\n const int HTCLIENT = 1;\n const int HTCAPTION = 2;\n if (m.Msg == WM_NCHITTEST && (int)m.Result == HTCLIENT)\n {\n Point screenPoint = new Point(m.LParam.ToInt32());\n Point clientPoint = this.PointToClient(screenPoint);\n if (clientPoint.Y <= 60 && clientPoint.X < this.Width - 60)\n {\n m.Result = (IntPtr)HTCAPTION;\n }\n }\n }\n\n // ── DWM Round Corners (Win11) ────────────────────────────────────────\n private void TryEnableDwmRoundCorners()\n {\n try\n {\n int preference = 2; // DWMWCP_ROUND\n DwmSetWindowAttribute(this.Handle, 33, ref preference, sizeof(int));\n }\n catch { }\n }\n\n // ── Shell Layout ─────────────────────────────────────────────────────\n private void BuildShell()\n {\n TableLayoutPanel root = new TableLayoutPanel();\n root.Dock = DockStyle.Fill;\n root.BackColor = MicaBase;\n root.Padding = new Padding(0);\n root.ColumnCount = 1;\n root.RowCount = 3;\n root.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));\n root.RowStyles.Add(new RowStyle(SizeType.Absolute, 70));\n this.Controls.Add(root);\n\n root.Controls.Add(BuildHeader(), 0, 0);\n root.Controls.Add(BuildBody(), 0, 1);\n root.Controls.Add(BuildFooter(), 0, 2);\n\n Label closeBtn = new Label();\n closeBtn.Text = \"✕\";\n closeBtn.Font = DesignTokens.CreateUIFont(14.0f, FontStyle.Regular);\n closeBtn.ForeColor = TextSecondary;\n closeBtn.BackColor = MicaBase;\n closeBtn.Cursor = Cursors.Hand;\n closeBtn.AutoSize = false;\n closeBtn.Size = new Size(73, 51);\n closeBtn.TextAlign = ContentAlignment.MiddleCenter;\n closeBtn.Location = new Point(this.ClientSize.Width - 73, 0);\n closeBtn.Anchor = AnchorStyles.Top | AnchorStyles.Right;\n closeBtn.Click += delegate { this.Close(); };\n closeBtn.MouseEnter += delegate { closeBtn.BackColor = DesignTokens.NeonGeekTheme.ElectricPurple; closeBtn.ForeColor = Color.White; };\n closeBtn.MouseLeave += delegate { closeBtn.BackColor = MicaBase; closeBtn.ForeColor = TextSecondary; };\n this.Controls.Add(closeBtn);\n closeBtn.BringToFront();\n }\n\n // ── Header ───────────────────────────────────────────────────────────\n private Control BuildHeader()\n {\n TableLayoutPanel header = new TableLayoutPanel();\n header.Dock = DockStyle.Fill;\n header.Margin = new Padding(0);\n header.BackColor = MicaBase;\n header.Padding = new Padding(51, 28, 51, 28);\n header.AutoSize = true;\n header.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n\n header.ColumnCount = 3;\n header.RowCount = 2;\n header.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300));\n header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n header.RowStyles.Add(new RowStyle(SizeType.AutoSize));\n\n Label title = new Label();\n title.Text = \"设置\";\n title.AutoSize = true;\n title.Margin = new Padding(0);\n title.Padding = new Padding(0, 0, 20, 0);\n title.Font = DesignTokens.CreateUIFont(22.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = \"按 Windows 11 设置结构重建,提供全局跨组件配置支持\";\n subtitle.AutoSize = true;\n subtitle.Margin = new Padding(0, 10, 0, 50); // 10px below title, 50px below subtitle\n subtitle.Font = DesignTokens.CreateUIFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n\n this.searchBox = new TextBox();\n this.searchBox.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;\n this.searchBox.Margin = new Padding(0, 10, 0, 0);\n this.searchBox.Height = 54;\n this.searchBox.BackColor = MicaLayer;\n this.searchBox.ForeColor = TextSecondary;\n this.searchBox.BorderStyle = BorderStyle.FixedSingle;\n this.searchBox.Font = DesignTokens.CreateUIFont(10.0f);\n this.searchBox.TextChanged += delegate { ApplySearchFilter(); };\n this.searchBox.HandleCreated += delegate\n {\n try { SendMessage(this.searchBox.Handle, EmSetCueBanner, IntPtr.Zero, \"\\uD83D\\uDD0D 搜索配置...\"); }\n catch { }\n };\n\n header.Controls.Add(title, 0, 0);\n header.Controls.Add(this.searchBox, 1, 0);\n header.Controls.Add(subtitle, 0, 1);\n header.SetColumnSpan(subtitle, 3);\n\n // Enable Dragging\n MouseEventHandler dragHandler = delegate(object sender, MouseEventArgs e) {\n if (e.Button == MouseButtons.Left) {\n ReleaseCapture();\n SendMessage(this.Handle, 0xA1, (IntPtr)2, null);\n }\n };\n header.MouseDown += dragHandler;\n title.MouseDown += dragHandler;\n subtitle.MouseDown += dragHandler;\n\n return header;\n }\n\n // ── Body (Navigation + Content) ──────────────────────────────────────\n private Control BuildBody()\n {\n TableLayoutPanel body = new TableLayoutPanel();\n body.Dock = DockStyle.Fill;\n body.BackColor = MicaBase;\n body.Padding = new Padding(38, 6, 38, 0);\n body.ColumnCount = 2;\n body.RowCount = 1;\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, NavWidth));\n body.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n\n this.navigationPanel = new FlowLayoutPanel();\n this.navigationPanel.Dock = DockStyle.Fill;\n this.navigationPanel.FlowDirection = FlowDirection.TopDown;\n this.navigationPanel.WrapContents = false;\n this.navigationPanel.AutoScroll = true;\n this.navigationPanel.Padding = new Padding(0, 6, 19, 6);\n this.navigationPanel.BackColor = MicaBase;\n\n this.contentHost = new Panel();\n this.contentHost.Dock = DockStyle.Fill;\n this.contentHost.BackColor = MicaBase;\n this.contentHost.Margin = new Padding(0);\n\n body.Controls.Add(this.navigationPanel, 0, 0);\n body.Controls.Add(this.contentHost, 1, 0);\n\n BuildPages();\n SelectPage(0);\n return body;\n }\n\n // ── Footer ───────────────────────────────────────────────────────────\n private Control BuildFooter()\n {\n TableLayoutPanel footer = new TableLayoutPanel();\n footer.Dock = DockStyle.Fill;\n footer.BackColor = MicaBase;\n footer.Padding = new Padding(51, 19, 51, 12);\n footer.AutoSize = true;\n footer.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n footer.ColumnCount = 4;\n footer.RowCount = 1;\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n footer.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));\n\n Button reset = BuildCommandButton(\"重置为默认\", false);\n reset.Dock = DockStyle.Top;\n reset.Click += delegate\n {\n WidgetSettings defaults = WidgetSettings.CreateDefaults();\n LoadSettings(defaults);\n this.saved = false;\n if (this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n };\n\n this.statusLabel = new Label();\n this.statusLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.statusLabel.Font = DesignTokens.CreateUIFont(9.5f, FontStyle.Bold);\n this.statusLabel.ForeColor = AccentClr;\n this.statusLabel.BackColor = MicaBase;\n this.statusLabel.Dock = DockStyle.Fill;\n this.statusLabel.Padding = new Padding(22, 0, 19, 0);\n this.statusLabel.Visible = false;\n\n Button cancel = BuildCommandButton(\"取消\", false);\n cancel.Dock = DockStyle.Top;\n cancel.Click += delegate { this.Close(); };\n\n Button save = BuildCommandButton(\"保存\", true);\n save.Dock = DockStyle.Top;\n save.Click += delegate\n {\n try\n {\n WidgetSettings settings = ReadSettings();\n if (this.owner != null)\n {\n this.owner.SaveSettings(settings);\n }\n\n this.baseline = settings.Clone();\n this.baseline.Normalize();\n this.saved = true;\n ShowStatus(\"保存完成\", false);\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n ShowStatus(\"保存失败 0x\" + ex.HResult.ToString(\"X8\", CultureInfo.InvariantCulture), true);\n }\n };\n\n footer.Controls.Add(reset, 0, 0);\n footer.Controls.Add(this.statusLabel, 1, 0);\n footer.Controls.Add(cancel, 2, 0);\n footer.Controls.Add(save, 3, 0);\n return footer;\n }\n\n private static Button BuildCommandButton(string text, bool primary)\n {\n Button button = new Button();\n button.Text = text; // Keep text so AutoSize works\n button.AutoSize = true;\n button.Padding = new Padding(24, 0, 24, 0); // Adds horizontal padding to AutoSize\n button.Height = 54;\n button.Margin = new Padding(0, 0, 12, 0);\n button.FlatStyle = FlatStyle.Flat;\n button.FlatAppearance.BorderSize = 0;\n button.Font = DesignTokens.CreateUIFont(9.5f, FontStyle.Bold);\n button.Cursor = Cursors.Hand;\n button.BackColor = MicaBase;\n button.ForeColor = primary ? Color.Black : TextSecondary;\n Color backBase = primary ? AccentClr : DesignTokens.SettingsTheme.ButtonRest;\n Color backHover = primary ? AccentHover : DesignTokens.SettingsTheme.ButtonHover;\n Color backDown = primary ? AccentPressed : DesignTokens.SettingsTheme.ButtonPressed;\n Color borderColor = primary ? AccentClr : ControlBorder;\n\n bool hover = false;\n bool down = false;\n\n button.MouseEnter += (s, e) => { hover = true; button.Invalidate(); };\n button.MouseLeave += (s, e) => { hover = false; button.Invalidate(); };\n button.MouseDown += (s, e) => { down = true; button.Invalidate(); };\n button.MouseUp += (s, e) => { down = false; button.Invalidate(); };\n\n button.Paint += (s, e) =>\n {\n e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;\n e.Graphics.Clear(MicaBase); // Clear default drawn text/background\n Color currentBack = down ? backDown : (hover ? backHover : backBase);\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(0, 0, button.Width - 1, button.Height - 1), 6))\n {\n using (SolidBrush brush = new SolidBrush(currentBack))\n {\n e.Graphics.FillPath(brush, path);\n }\n using (Pen pen = new Pen(borderColor, 1))\n {\n e.Graphics.DrawPath(pen, path);\n }\n }\n TextRenderer.DrawText(e.Graphics, text, button.Font, button.ClientRectangle, button.ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);\n };\n return button;\n }\n\n // ── Pages Definition ─────────────────────────────────────────────────\n // Each page uses AddPageGrouped with string[][] where each inner array\n // is [groupTitle, property1, property2, ...].\n private void BuildPages()\n {\n AddPageGrouped(\"\\uE115\", \"系统\", \"启动、性能、可见性和全局恢复。\", new string[][]\n {\n new string[] { \"启动与性能\", \"StartupEnabled\", \"PerformanceMode\" },\n new string[] { \"窗口行为\", \"VisibilityMode\", \"ClickThroughMode\", \"ForceShowForegroundFpsEnabled\" },\n new string[] { \"恢复与保护\", \"SeelenDockForegroundPulseEnabled\", \"WinDRecoveryPulseEnabled\", \"PowerResumeRestartEnabled\" }\n });\n\n {\n CategoryPage sysPage = this.pages[this.pages.Count - 1];\n Button dumpBtn = BuildCommandButton(\"记录窗口排版日志\", false);\n dumpBtn.Width = 200;\n dumpBtn.Margin = new Padding(0, 16, 0, 32);\n dumpBtn.Click += delegate { DumpLayout(); };\n sysPage.Stack.Controls.Add(dumpBtn);\n }\n AddPageGrouped(\"\\uE7B3\", \"隐藏与鼠标\", \"悬停透明、判定范围、延迟显现和防烧屏。\", new string[][]\n {\n new string[] { \"悬停透明\", \"HoverOpacityEnabled\", \"SensitiveMouseModeEnabled\", \"SensitiveMouseRangePixels\" },\n new string[] { \"延迟显现\", \"HoverOpacityRevealDelayEnabled\", \"HoverOpacityRevealDelaySeconds\", \"HoverOpacityRevealResetSeconds\" },\n new string[] { \"覆盖与反向\", \"HoverOpacityCoverEnabled\", \"ReverseHoverOpacityRevealEnabled\", \"ReverseHoverOpacityRestoreDelaySeconds\" },\n new string[] { \"自动隐藏\", \"AutoHoverOpacityIdleEnabled\", \"AutoHoverOpacityIdleSeconds\", \"AutoHoverOpacityMaximizedEnabled\", \"BurnInHiddenModeColorProtectionEnabled\" }\n });\n AddPageGrouped(\"\\uE737\", \"主窗口\", \"主监控窗口尺寸、位置、透明度和显示项。\", new string[][]\n {\n new string[] { \"尺寸与位置\", \"Width\", \"Height\", \"LeftX\", \"BottomY\" },\n new string[] { \"透明度\", \"BackgroundTransparencyPercent\", \"ApplicationTransparencyPercent\" },\n new string[] { \"显示项\", \"ShowCpu\", \"ShowMemory\", \"ShowDisk\", \"ShowNetwork\", \"ShowGpu\", \"ShowNpu\" }\n });\n AddPageGrouped(\"\\uE71E\", \"Codex Radar\", \"模型、额度、效率和测试覆盖。\", new string[][]\n {\n new string[] { \"窗口\", \"CodexRadarWidth\", \"CodexRadarHeight\", \"CodexRadarLeftX\", \"CodexRadarBottomY\", \"CodexRadarTransparencyPercent\" },\n new string[] { \"模型与时区\", \"CodexRadarModelKey\", \"CodexRadarModelVersion\", \"DisplayTimeZoneMode\", \"DisplayTimeZoneId\" },\n new string[] { \"IQ 测试覆盖\", \"CodexModelIqTestEnabled\", \"CodexModelIqTestPassed\", \"CodexModelIqBaselineMode\", \"CodexModelIqBaselinePassed\" },\n new string[] { \"效率测试覆盖\", \"CodexModelEfficiencyTestEnabled\", \"CodexModelTokenEfficiencyTestPercent\", \"CodexModelTimeEfficiencyTestPercent\",\n \"CodexModelTokenEfficiencyBaselineMode\", \"CodexModelTokenEfficiencyBaselinePassed\", \"CodexModelTokenEfficiencyBaselineTokens\",\n \"CodexModelTimeEfficiencyBaselineMode\", \"CodexModelTimeEfficiencyBaselinePassed\", \"CodexModelTimeEfficiencyBaselineSeconds\",\n \"CodexModelTokenEfficiencyLowThresholdPercent\", \"CodexModelTimeEfficiencyLowThresholdPercent\" },\n new string[] { \"随机测试\", \"CodexRadarRandomTestEnabled\", \"CodexRadarRandomTestAutoRefresh\", \"CodexRadarRandomTestRefreshToken\" }\n });\n AddPageGrouped(\"\\uEBB0\", \"功耗与温度\", \"UX3407N / UX3607O 专用功耗温度窗口。\", new string[][]\n {\n new string[] { \"窗口\", \"PowerThermalWidth\", \"PowerThermalHeight\", \"PowerThermalLeftX\", \"PowerThermalBottomY\", \"PowerThermalTransparencyPercent\" },\n new string[] { \"自动布局与告警\", \"PowerThermalAutoSizeEnabled\", \"PowerThermalAutoDirection\", \"PowerThermalVisibleAlertCount\" },\n new string[] { \"测试\", \"ThermalTestMode\" }\n });\n AddPageGrouped(\"\\uE774\", \"网络\", \"网络监控、GFW、云服务和出口身份检测。\", new string[][]\n {\n new string[] { \"网络监控窗口\", \"NetworkMonitorWidth\", \"NetworkMonitorHeight\", \"NetworkMonitorLeftX\", \"NetworkMonitorBottomY\", \"NetworkMonitorTransparencyPercent\" },\n new string[] { \"适配器\", \"NetworkMonitorAdapterId\", \"NetworkStatusTestMode\" },\n new string[] { \"GFW 检测\", \"GfwProbeEnabled\", \"GfwProbeIntervalMinutes\", \"GfwProbeManualRefreshToken\" },\n new string[] { \"云服务端点\", \"CloudEndpointTestSeed\", \"CloudStatusRegionMask\" },\n new string[] { \"连接检测\", \"ConnectionCheckWidth\", \"ConnectionCheckHeight\", \"ConnectionCheckLeftX\", \"ConnectionCheckBottomY\",\n \"ConnectionCheckTransparencyPercent\", \"ConnectionCheckBorderTransparencyPercent\", \"ConnectionCheckIntervalSeconds\" },\n new string[] { \"出口身份\", \"ConnectionCheckManualRefreshToken\", \"CleanIpBadgeTestMode\" }\n });\n AddPageGrouped(\"\\uE700\", \"操作面板\", \"左下角操作面板尺寸、位置和告警测试。\", new string[][]\n {\n new string[] { \"尺寸与位置\", \"OperationButtonSize\", \"OperationLeftOffset\", \"OperationBottomOffset\", \"OperationBackgroundTransparencyPercent\" },\n new string[] { \"测试\", \"AlertTestEnabled\" }\n });\n }\n\n // ── AddPageGrouped ───────────────────────────────────────────────────\n private void AddPageGrouped(string icon, string title, string description, string[][] groups)\n {\n int pageIndex = this.pages.Count;\n CategoryPage page = new CategoryPage();\n page.Title = title;\n page.Description = description;\n\n page.ScrollPanel = new FluentScrollPanel();\n page.ScrollPanel.Dock = DockStyle.Fill;\n page.ScrollPanel.BackColor = MicaBase;\n page.ScrollPanel.AutoScroll = true;\n page.ScrollPanel.Padding = new Padding(0, 0, 16, 0);\n page.ScrollPanel.Visible = false;\n\n FlowLayoutPanel stack = new FlowLayoutPanel();\n stack.Dock = DockStyle.Top;\n stack.FlowDirection = FlowDirection.TopDown;\n stack.WrapContents = false;\n stack.AutoSize = true;\n stack.AutoSizeMode = AutoSizeMode.GrowAndShrink;\n stack.BackColor = MicaBase;\n page.Stack = stack;\n page.ScrollPanel.Controls.Add(stack);\n\n Panel heading = BuildPageHeading(title, description);\n page.Heading = heading;\n stack.Controls.Add(heading);\n\n for (int g = 0; g < groups.Length; g++)\n {\n string[] groupDef = groups[g];\n string groupTitle = groupDef[0];\n SettingGroupData group = new SettingGroupData();\n group.Title = groupTitle;\n\n // Group title label (above the card, Win11 style)\n Label titleLabel = new Label();\n titleLabel.Text = groupTitle;\n titleLabel.Font = DesignTokens.CreateUIFont(10.0f, FontStyle.Bold);\n titleLabel.ForeColor = TextPrimary;\n titleLabel.BackColor = MicaBase;\n titleLabel.AutoSize = false;\n titleLabel.Width = 1152;\n titleLabel.Height = 44;\n titleLabel.Margin = new Padding(0, g == 0 ? 0 : 18, 0, 4);\n titleLabel.TextAlign = ContentAlignment.BottomLeft;\n group.TitleLabel = titleLabel;\n stack.Controls.Add(titleLabel);\n\n // Group card (rounded background, contains rows)\n SettingGroupCard card = new SettingGroupCard();\n card.Width = 1152;\n card.Margin = new Padding(0, 0, 0, 3);\n group.Card = card;\n\n for (int i = 1; i < groupDef.Length; i++)\n {\n SettingEditor editor = BuildEditor(groupDef[i]);\n if (editor != null)\n {\n group.Editors.Add(editor);\n page.Editors.Add(editor);\n card.AddRow(editor.Card);\n this.editors[editor.Property.Name] = editor;\n }\n }\n\n card.LayoutRows();\n stack.Controls.Add(card);\n page.Groups.Add(group);\n }\n\n page.ScrollPanel.Resize += delegate { LayoutPage(page); };\n NavigationItem nav = new NavigationItem(icon, title);\n nav.Width = NavWidth - 24;\n nav.Height = NavItemHeight;\n nav.Margin = new Padding(6, 3, 6, 3);\n nav.Font = DesignTokens.CreateUIFont(10.0f);\n nav.Cursor = Cursors.Hand;\n nav.Click += delegate { SelectPage(pageIndex); };\n page.NavItem = nav;\n this.navigationPanel.Controls.Add(nav);\n this.contentHost.Controls.Add(page.ScrollPanel);\n this.pages.Add(page);\n }\n\n // ── Page Heading ─────────────────────────────────────────────────────\n private Panel BuildPageHeading(string titleText, string description)\n {\n Panel panel = new Panel();\n panel.BackColor = MicaBase;\n panel.Width = 1152;\n panel.Height = 188;\n panel.Margin = new Padding(0, 0, 0, 32);\n\n Label title = new Label();\n title.Text = titleText;\n title.Font = DesignTokens.CreateUIFont(18.0f, FontStyle.Bold);\n title.ForeColor = TextPrimary;\n title.BackColor = MicaBase;\n title.Location = new Point(0, 0);\n title.Size = new Size(680, GetSingleLineHeight(title.Font, 14));\n title.TextAlign = ContentAlignment.MiddleLeft;\n\n Label subtitle = new Label();\n subtitle.Text = description;\n subtitle.Font = DesignTokens.CreateUIFont(9.5f);\n subtitle.ForeColor = TextTertiary;\n subtitle.BackColor = MicaBase;\n subtitle.Location = new Point(1, title.Bottom + 16);\n subtitle.Size = new Size(680, GetSingleLineHeight(subtitle.Font, 10));\n subtitle.TextAlign = ContentAlignment.MiddleLeft;\n\n panel.Resize += delegate\n {\n int width = Math.Max(416, panel.Width - 6);\n title.Width = width;\n subtitle.Width = width;\n };\n panel.Controls.Add(title);\n panel.Controls.Add(subtitle);\n return panel;\n }\n\n // ── Editor Building ──────────────────────────────────────────────────\n private SettingEditor BuildEditor(string propertyName)\n {\n PropertyInfo property = typeof(WidgetSettings).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);\n if (property == null || !property.CanRead || !property.CanWrite || property.PropertyType == typeof(string[]))\n {\n return null;\n }\n\n Control control = BuildValueControl(property);\n SettingRow card = new SettingRow(control);\n card.Width = 1152;\n card.Margin = new Padding(0);\n card.TitleLabel.Text = GetSettingTitle(propertyName);\n card.HintLabel.Text = GetSettingHint(propertyName);\n card.BackColor = Color.Transparent;\n return new SettingEditor(property, card, control);\n }\n\n private Control BuildValueControl(PropertyInfo property)\n {\n Type type = property.PropertyType;\n if (type == typeof(bool))\n {\n ToggleSwitch toggle = new ToggleSwitch();\n toggle.CheckedChanged += delegate { OnSettingChanged(); };\n return toggle;\n }\n\n if (type.IsEnum)\n {\n ComboBox combo = new ComboBox();\n combo.Width = 352;\n combo.Height = 54;\n combo.DropDownStyle = ComboBoxStyle.DropDownList;\n combo.FlatStyle = FlatStyle.Flat;\n combo.BackColor = ControlBg;\n combo.ForeColor = TextSecondary;\n combo.Font = DesignTokens.CreateUIFont(9.5f);\n Array values = Enum.GetValues(type);\n for (int i = 0; i < values.Length; i++)\n {\n combo.Items.Add(new EnumOption(values.GetValue(i)));\n }\n combo.SelectedIndexChanged += delegate { OnSettingChanged(); };\n return combo;\n }\n\n if (type == typeof(int) || type == typeof(double))\n {\n NumericUpDown box = new NumericUpDown();\n box.Width = 227;\n box.Height = 54;\n box.DecimalPlaces = type == typeof(double) ? 1 : 0;\n box.Increment = type == typeof(double) ? 0.1M : GetNumericIncrement(property.Name);\n NumericRange range = GetNumericRange(property.Name, type);\n box.Minimum = range.Minimum;\n box.Maximum = range.Maximum;\n box.BackColor = ControlBg;\n box.ForeColor = TextSecondary;\n box.BorderStyle = BorderStyle.FixedSingle;\n box.Font = DesignTokens.CreateUIFont(9.5f);\n box.ValueChanged += delegate { OnSettingChanged(); };\n return box;\n }\n\n TextBox text = new TextBox();\n text.Width = 400;\n text.Height = 54;\n text.BackColor = ControlBg;\n text.ForeColor = TextSecondary;\n text.BorderStyle = BorderStyle.FixedSingle;\n text.Font = DesignTokens.CreateUIFont(9.5f);\n text.TextChanged += delegate { OnSettingChanged(); };\n return text;\n }\n\n // ── Page Selection & Layout ──────────────────────────────────────────\n private void SelectPage(int pageIndex)\n {\n if (pageIndex < 0 || pageIndex >= this.pages.Count)\n {\n return;\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool active = i == pageIndex;\n page.ScrollPanel.Visible = active;\n if (page.NavItem != null)\n {\n page.NavItem.Selected = active;\n }\n if (active)\n {\n LayoutPage(page);\n }\n }\n\n this.selectedPageIndex = pageIndex;\n this.pages[pageIndex].ScrollPanel.BringToFront();\n }\n\n private void LayoutPage(CategoryPage page)\n {\n int width = Math.Max(360, page.ScrollPanel.ClientSize.Width - page.ScrollPanel.Padding.Left - page.ScrollPanel.Padding.Right - 22);\n page.Stack.Width = width;\n page.Heading.Width = width;\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Width = width;\n }\n if (group.Card != null)\n {\n group.Card.Width = width;\n group.Card.LayoutRows();\n }\n }\n }\n\n // ── Settings I/O ─────────────────────────────────────────────────────\n private void LoadSettings(WidgetSettings settings)\n {\n this.initializing = true;\n try\n {\n foreach (SettingEditor editor in this.editors.Values)\n {\n object value = editor.Property.GetValue(settings, null);\n SetEditorValue(editor, value);\n }\n }\n finally\n {\n this.initializing = false;\n }\n }\n\n private WidgetSettings ReadSettings()\n {\n WidgetSettings settings = this.baseline.Clone();\n foreach (SettingEditor editor in this.editors.Values)\n {\n object value = GetEditorValue(editor);\n if (value != null)\n {\n editor.Property.SetValue(settings, value, null);\n }\n }\n\n settings.Normalize();\n return settings;\n }\n\n private void SetEditorValue(SettingEditor editor, object value)\n {\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n toggle.SetCheckedSilent(value is bool && (bool)value);\n return;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo != null)\n {\n for (int i = 0; i < combo.Items.Count; i++)\n {\n EnumOption option = combo.Items[i] as EnumOption;\n if (option != null && object.Equals(option.Value, value))\n {\n combo.SelectedIndex = i;\n return;\n }\n }\n\n if (combo.Items.Count > 0)\n {\n combo.SelectedIndex = 0;\n }\n\n return;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n decimal next = Convert.ToDecimal(value, CultureInfo.InvariantCulture);\n if (next < number.Minimum)\n {\n next = number.Minimum;\n }\n else if (next > number.Maximum)\n {\n next = number.Maximum;\n }\n\n number.Value = next;\n return;\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n text.Text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;\n }\n }\n\n private object GetEditorValue(SettingEditor editor)\n {\n Type type = editor.Property.PropertyType;\n ToggleSwitch toggle = editor.Control as ToggleSwitch;\n if (toggle != null)\n {\n return toggle.Checked;\n }\n\n ComboBox combo = editor.Control as ComboBox;\n if (combo != null)\n {\n EnumOption option = combo.SelectedItem as EnumOption;\n return option == null ? null : option.Value;\n }\n\n NumericUpDown number = editor.Control as NumericUpDown;\n if (number != null)\n {\n return type == typeof(double) ? (object)Convert.ToDouble(number.Value, CultureInfo.InvariantCulture) : Convert.ToInt32(number.Value, CultureInfo.InvariantCulture);\n }\n\n TextBox text = editor.Control as TextBox;\n if (text != null)\n {\n return text.Text ?? string.Empty;\n }\n\n return null;\n }\n\n // ── Setting Changed & Preview ────────────────────────────────────────\n private void OnSettingChanged()\n {\n if (this.initializing)\n {\n return;\n }\n\n this.saved = false;\n this.previewTimer.Stop();\n this.previewTimer.Start();\n }\n\n private void OnPreviewTimerTick(object sender, EventArgs e)\n {\n this.previewTimer.Stop();\n if (!this.IsDisposed && !this.OwnerFormClosing && this.owner != null)\n {\n this.owner.PreviewSettings(ReadSettings());\n }\n }\n\n // ── Status Toast ─────────────────────────────────────────────────────\n private void ShowStatus(string text, bool error)\n {\n if (this.statusLabel == null)\n {\n return;\n }\n\n this.statusTimer.Stop();\n this.statusLabel.Text = text;\n this.statusLabel.ForeColor = error ? ErrorClr : AccentClr;\n this.statusLabel.Visible = true;\n this.statusTimer.Start();\n }\n\n private void OnStatusTimerTick(object sender, EventArgs e)\n {\n this.statusTimer.Stop();\n if (this.statusLabel != null)\n {\n this.statusLabel.Visible = false;\n }\n }\n\n // ── Search Filter ────────────────────────────────────────────────────\n private void ApplySearchFilter()\n {\n string query = (this.searchBox == null ? string.Empty : this.searchBox.Text ?? string.Empty).Trim();\n for (int i = 0; i < this.pages.Count; i++)\n {\n CategoryPage page = this.pages[i];\n bool pageMatch = query.Length == 0 ||\n page.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n page.Description.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n bool anyVisible = false;\n\n for (int g = 0; g < page.Groups.Count; g++)\n {\n SettingGroupData group = page.Groups[g];\n bool anyRowVisible = false;\n bool groupTitleMatch = group.Title.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n\n for (int j = 0; j < group.Editors.Count; j++)\n {\n SettingEditor editor = group.Editors[j];\n bool match = pageMatch || groupTitleMatch ||\n editor.Property.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 ||\n GetSettingTitle(editor.Property.Name).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;\n editor.Card.Visible = match;\n anyRowVisible |= match;\n }\n\n if (group.TitleLabel != null)\n {\n group.TitleLabel.Visible = anyRowVisible;\n }\n if (group.Card != null)\n {\n group.Card.Visible = anyRowVisible;\n if (anyRowVisible) group.Card.LayoutRows();\n }\n anyVisible |= anyRowVisible;\n }\n\n if (page.NavItem != null)\n {\n page.NavItem.Visible = query.Length == 0 || pageMatch || anyVisible;\n }\n }\n }\n\n private FluentScrollPanel GetSelectedScrollPage()\n {\n if (this.selectedPageIndex < 0 || this.selectedPageIndex >= this.pages.Count)\n {\n return null;\n }\n\n return this.pages[this.selectedPageIndex].ScrollPanel;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n private static string GetSettingTitle(string name)\n {\n string value;\n return SettingTitles.TryGetValue(name, out value) ? value : SplitPascalCase(name);\n }\n\n private static string GetSettingHint(string name)\n {\n string value;\n return SettingHints.TryGetValue(name, out value) ? value : name;\n }\n\n private static decimal GetNumericIncrement(string name)\n {\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return 1;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Percent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return 1;\n }\n\n return 10;\n }\n\n private static NumericRange GetNumericRange(string name, Type type)\n {\n NumericRange range;\n if (NumericRanges.TryGetValue(name, out range))\n {\n return range;\n }\n\n if (name.IndexOf(\"Transparency\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.EndsWith(\"Percent\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, name.IndexOf(\"Efficiency\", StringComparison.OrdinalIgnoreCase) >= 0 ? 200 : 100);\n }\n\n if (name.EndsWith(\"Seconds\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 600);\n }\n\n if (name.EndsWith(\"Minutes\", StringComparison.OrdinalIgnoreCase))\n {\n return new NumericRange(0, 240);\n }\n\n if (name.IndexOf(\"Width\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Height\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(1, 4000);\n }\n\n if (name.IndexOf(\"LeftX\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"BottomY\", StringComparison.OrdinalIgnoreCase) >= 0 ||\n name.IndexOf(\"Offset\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n return new NumericRange(0, 5000);\n }\n\n if (type == typeof(double))\n {\n return new NumericRange(0, 100);\n }\n\n return new NumericRange(0, 100000000);\n }\n\n private static string SplitPascalCase(string name)\n {\n if (string.IsNullOrEmpty(name))\n {\n return string.Empty;\n }\n\n System.Text.StringBuilder builder = new System.Text.StringBuilder(name.Length + 8);\n builder.Append(name[0]);\n for (int i = 1; i < name.Length; i++)\n {\n char c = name[i];\n if (char.IsUpper(c) && !char.IsUpper(name[i - 1]))\n {\n builder.Append(' ');\n }\n\n builder.Append(c);\n }\n\n return builder.ToString();\n }\n\n private static int GetSingleLineHeight(Font font, int verticalPadding)\n {\n return Math.Max(24, TextRenderer.MeasureText(\"测量文字 Ag\", font).Height + verticalPadding);\n }\n\n private static int GetWrappedTextHeight(string text, Font font, int width, int verticalPadding)\n {\n int safeWidth = Math.Max(80, width);\n Size measured = TextRenderer.MeasureText(\n string.IsNullOrEmpty(text) ? \" \" : text,\n font,\n new Size(safeWidth, int.MaxValue),\n TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);\n return Math.Max(GetSingleLineHeight(font, verticalPadding), measured.Height + verticalPadding);\n }\n\n private static Size FitClientSizeToScreen(Size desiredSize)\n {\n Rectangle workArea = Screen.PrimaryScreen.WorkingArea;\n int width = Math.Min(desiredSize.Width, Math.Max(760, workArea.Width - 80));\n int height = Math.Min(desiredSize.Height, Math.Max(560, workArea.Height - 80));\n return new Size(width, height);\n }\n\n private static Size GetMinimumWindowSizeForScreen()\n {\n Rectangle workArea = Screen.PrimaryScreen.WorkingArea;\n return new Size(\n Math.Min(1440, Math.Max(1216, workArea.Width - 128)),\n Math.Min(992, Math.Max(896, workArea.Height - 128)));\n }\n\n private static Font GetIconFont()\n {\n if (iconFontCache != null) return iconFontCache;\n Font f = new Font(\"Segoe Fluent Icons\", 12f);\n if (f.Name.IndexOf(\"Segoe Fluent\", StringComparison.OrdinalIgnoreCase) >= 0)\n {\n iconFontCache = f;\n return f;\n }\n f.Dispose();\n f = new Font(\"Segoe MDL2 Assets\", 12f);\n iconFontCache = f;\n return f;\n }\n\n // ── Self-Test ────────────────────────────────────────────────────────\n internal static void RunSettingsBindingSelfTest()\n {\n WidgetSettings baseline = WidgetSettings.CreateDefaults();\n using (Win11SettingsForm form = new Win11SettingsForm(null, baseline))\n {\n form.OwnerFormClosing = true;\n form.saved = true;\n form.StartPosition = FormStartPosition.Manual;\n form.Location = new Point(-51200, -51200);\n form.Show();\n Application.DoEvents();\n form.VerifySelfTest();\n }\n }\n\n\n private void DumpLayout()\n {\n try\n {\n using (System.IO.StreamWriter w = new System.IO.StreamWriter(\"settings_layout_dump.txt\"))\n {\n DumpControl(this, w, 0);\n }\n }\n catch (Exception ex)\n {\n System.IO.File.WriteAllText(\"dump_error.txt\", ex.ToString());\n }\n }\n\n private void DumpControl(Control c, System.IO.StreamWriter w, int indent)\n {\n string ind = new string(' ', indent * 2);\n w.WriteLine($\"{ind}{c.GetType().Name} '{c.Text}' - Bounds: {c.Bounds}, PrefSize: {c.GetPreferredSize(new Size(c.Width, 0))}, ClientSize: {c.ClientSize}, Margin: {c.Margin}, Padding: {c.Padding}, AutoSize: {c.AutoSize}, Dock: {c.Dock}, Visible: {c.Visible}\");\n if (c is TableLayoutPanel tlp)\n {\n w.WriteLine($\"{ind} RowStyles: {tlp.RowStyles.Count}, ColStyles: {tlp.ColumnStyles.Count}, AutoSizeMode: {tlp.AutoSizeMode}\");\n }\n if (c is FlowLayoutPanel flp)\n {\n w.WriteLine($\"{ind} FlowDirection: {flp.FlowDirection}, WrapContents: {flp.WrapContents}, AutoSizeMode: {flp.AutoSizeMode}\");\n }\n foreach (Control child in c.Controls)\n {\n DumpControl(child, w, indent + 1);\n }\n }\n\n private void VerifySelfTest()\n {\n string[] required = new string[]\n {\n \"PerformanceMode\",\n \"HoverOpacityEnabled\",\n \"HoverOpacityRevealDelayEnabled\",\n \"Width\",\n \"CodexRadarModelKey\",\n \"PowerThermalAutoSizeEnabled\",\n \"GfwProbeIntervalMinutes\",\n \"OperationButtonSize\"\n };\n\n for (int i = 0; i < required.Length; i++)\n {\n if (!this.editors.ContainsKey(required[i]))\n {\n throw new InvalidOperationException(\"WinUI settings binding missing: \" + required[i]);\n }\n }\n\n for (int i = 0; i < this.pages.Count; i++)\n {\n LayoutPage(this.pages[i]);\n }\n\n VerifyNoVisibleControlClipping();\n\n WidgetSettings settings = ReadSettings();\n if (settings == null)\n {\n throw new InvalidOperationException(\"WinUI settings read failed.\");\n }\n\n FluentScrollPanel page = GetSelectedScrollPage();\n if (page == null || !page.ScrollByMouseWheelDelta(-120))\n {\n throw new InvalidOperationException(\"WinUI settings page wheel scroll failed.\");\n }\n }\n\n private void VerifyNoVisibleControlClipping()\n {\n Size originalSize = this.ClientSize;\n int originalPageIndex = this.selectedPageIndex;\n this.ClientSize = new Size(this.MinimumSize.Width, this.MinimumSize.Height);\n Application.DoEvents();\n try\n {\n for (int i = 0; i < this.pages.Count; i++)\n {\n SelectPage(i);\n CategoryPage page = this.pages[i];\n LayoutPage(page);\n for (int j = 0; j < page.Editors.Count; j++)\n {\n SettingEditor editor = page.Editors[j];\n SettingRow card = editor.Card;\n Control control = editor.Control;\n int rightLimit = card.ClientSize.Width - card.Padding.Right + 1;\n int bottomLimit = card.ClientSize.Height - card.Padding.Bottom + 1;\n if (control.Left < card.Padding.Left ||\n control.Right > rightLimit ||\n control.Bottom > bottomLimit ||\n card.TitleLabel.Right > rightLimit ||\n card.HintLabel.Right > rightLimit)\n {\n throw new InvalidOperationException(\"WinUI settings layout clipped: \" + editor.Property.Name);\n }\n }\n }\n }\n finally\n {\n this.ClientSize = originalSize;\n SelectPage(originalPageIndex);\n Application.DoEvents();\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Dictionaries\n // ═════════════════════════════════════════════════════════════════════\n\n private static readonly Dictionary<string, NumericRange> NumericRanges = new Dictionary<string, NumericRange>(StringComparer.Ordinal)\n {\n { \"Width\", new NumericRange(WidgetSettings.MinWidth, WidgetSettings.MaxWidth) },\n { \"Height\", new NumericRange(WidgetSettings.MinHeight, WidgetSettings.MaxHeight) },\n { \"CodexRadarWidth\", new NumericRange(WidgetSettings.MinCodexRadarWidth, WidgetSettings.MaxCodexRadarWidth) },\n { \"CodexRadarHeight\", new NumericRange(WidgetSettings.MinCodexRadarHeight, WidgetSettings.MaxCodexRadarHeight) },\n { \"PowerThermalWidth\", new NumericRange(WidgetSettings.MinPowerThermalWidth, WidgetSettings.MaxPowerThermalWidth) },\n { \"PowerThermalHeight\", new NumericRange(WidgetSettings.MinPowerThermalHeight, WidgetSettings.MaxPowerThermalHeight) },\n { \"PowerThermalVisibleAlertCount\", new NumericRange(WidgetSettings.MinPowerThermalVisibleAlerts, WidgetSettings.MaxPowerThermalVisibleAlerts) },\n { \"NetworkMonitorWidth\", new NumericRange(WidgetSettings.MinNetworkMonitorWidth, WidgetSettings.MaxNetworkMonitorWidth) },\n { \"NetworkMonitorHeight\", new NumericRange(WidgetSettings.MinNetworkMonitorHeight, WidgetSettings.MaxNetworkMonitorHeight) },\n { \"GfwProbeIntervalMinutes\", new NumericRange(WidgetSettings.MinGfwProbeIntervalMinutes, WidgetSettings.MaxGfwProbeIntervalMinutes) },\n { \"ConnectionCheckWidth\", new NumericRange(WidgetSettings.MinConnectionCheckWidth, WidgetSettings.MaxConnectionCheckWidth) },\n { \"ConnectionCheckHeight\", new NumericRange(WidgetSettings.MinConnectionCheckHeight, WidgetSettings.MaxConnectionCheckHeight) },\n { \"ConnectionCheckIntervalSeconds\", new NumericRange(WidgetSettings.MinConnectionCheckIntervalSeconds, WidgetSettings.MaxConnectionCheckIntervalSeconds) },\n { \"ConnectionCheckBorderTransparencyPercent\", new NumericRange(WidgetSettings.MinBorderTransparency, WidgetSettings.MaxBorderTransparency) },\n { \"OperationButtonSize\", new NumericRange(WidgetSettings.MinOperationButtonSize, WidgetSettings.MaxOperationButtonSize) },\n { \"OperationLeftOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"OperationBottomOffset\", new NumericRange(WidgetSettings.MinOperationOffset, WidgetSettings.MaxOperationOffset) },\n { \"SensitiveMouseRangePixels\", new NumericRange(WidgetSettings.MinSensitiveMouseRangePixels, WidgetSettings.MaxSensitiveMouseRangePixels) },\n { \"HoverOpacityRevealDelaySeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealDelaySeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealDelaySeconds) },\n { \"HoverOpacityRevealResetSeconds\", new NumericRange((decimal)WidgetSettings.MinHoverOpacityRevealResetSeconds, (decimal)WidgetSettings.MaxHoverOpacityRevealResetSeconds) },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", new NumericRange(WidgetSettings.MinReverseHoverOpacityRestoreDelaySeconds, WidgetSettings.MaxReverseHoverOpacityRestoreDelaySeconds) },\n { \"AutoHoverOpacityIdleSeconds\", new NumericRange(WidgetSettings.MinAutoHoverOpacityIdleSeconds, WidgetSettings.MaxAutoHoverOpacityIdleSeconds) },\n { \"CodexModelIqTestPassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelIqBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTokenEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) },\n { \"CodexModelTimeEfficiencyBaselinePassed\", new NumericRange(WidgetSettings.MinCodexModelIqPassed, WidgetSettings.MaxCodexModelIqPassed) }\n };\n\n private static readonly Dictionary<string, string> SettingTitles = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"开机启动\" },\n { \"PerformanceMode\", \"性能模式\" },\n { \"VisibilityMode\", \"可见性\" },\n { \"ClickThroughMode\", \"点击穿透\" },\n { \"ForceShowForegroundFpsEnabled\", \"强制显示 FPS\" },\n { \"SeelenDockForegroundPulseEnabled\", \"Seelen Dock 自动拉前\" },\n { \"WinDRecoveryPulseEnabled\", \"Win+D 后延迟拉前\" },\n { \"PowerResumeRestartEnabled\", \"休眠唤醒后重启\" },\n { \"HoverOpacityEnabled\", \"悬停透明 95%\" },\n { \"SensitiveMouseModeEnabled\", \"敏感鼠标模式\" },\n { \"SensitiveMouseRangePixels\", \"鼠标判定边长\" },\n { \"HoverOpacityRevealDelayEnabled\", \"延迟显现\" },\n { \"HoverOpacityRevealDelaySeconds\", \"显现延迟秒数\" },\n { \"HoverOpacityRevealResetSeconds\", \"重置秒数\" },\n { \"HoverOpacityCoverEnabled\", \"覆盖开启\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"反向隐藏\" },\n { \"ReverseHoverOpacityRestoreDelaySeconds\", \"移开后恢复秒数\" },\n { \"AutoHoverOpacityIdleEnabled\", \"空闲自动隐藏\" },\n { \"AutoHoverOpacityIdleSeconds\", \"空闲隐藏秒数\" },\n { \"AutoHoverOpacityMaximizedEnabled\", \"最大化自动隐藏\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏反色防烧屏\" },\n { \"CodexRadarModelKey\", \"Codex Radar 模型\" },\n { \"CodexRadarModelVersion\", \"模型版本\" },\n { \"CodexRadarRandomTestEnabled\", \"随机测试\" },\n { \"CodexRadarRandomTestAutoRefresh\", \"随机测试自动刷新\" },\n { \"CodexRadarRandomTestRefreshToken\", \"随机测试刷新令牌\" },\n { \"CodexModelIqTestEnabled\", \"覆盖实时 IQ 数据\" },\n { \"CodexModelIqTestPassed\", \"IQ 测试通过项\" },\n { \"CodexModelIqBaselineMode\", \"IQ 基准模式\" },\n { \"CodexModelIqBaselinePassed\", \"IQ 基准通过项\" },\n { \"CodexModelEfficiencyTestEnabled\", \"覆盖实时效率数据\" },\n { \"CodexModelTokenEfficiencyTestPercent\", \"Token 效率测试百分比\" },\n { \"CodexModelTimeEfficiencyTestPercent\", \"时间效率测试百分比\" },\n { \"CodexModelTokenEfficiencyBaselineMode\", \"Token 效率基准模式\" },\n { \"CodexModelTimeEfficiencyBaselineMode\", \"时间效率基准模式\" },\n { \"CodexModelTokenEfficiencyBaselineTokens\", \"Token 效率基准值\" },\n { \"CodexModelTimeEfficiencyBaselineSeconds\", \"时间效率基准秒数\" },\n { \"CodexModelTokenEfficiencyLowThresholdPercent\", \"Token 低效阈值\" },\n { \"CodexModelTimeEfficiencyLowThresholdPercent\", \"时间低效阈值\" },\n { \"DisplayTimeZoneId\", \"显示时区 ID\" },\n { \"DisplayTimeZoneMode\", \"显示时区模式\" },\n { \"PowerThermalAutoSizeEnabled\", \"功耗模块自动大小\" },\n { \"PowerThermalAutoDirection\", \"自动大小方向\" },\n { \"PowerThermalVisibleAlertCount\", \"可见告警数量\" },\n { \"NetworkMonitorAdapterId\", \"网络适配器 ID\" },\n { \"NetworkStatusTestMode\", \"网络状态测试\" },\n { \"GfwProbeIntervalMinutes\", \"GFW 检测间隔\" },\n { \"GfwProbeEnabled\", \"启用 GFW 检测\" },\n { \"GfwProbeManualRefreshToken\", \"GFW 手动刷新令牌\" },\n { \"CloudEndpointTestSeed\", \"云服务测试种子\" },\n { \"CloudStatusRegionMask\", \"云服务地区掩码\" },\n { \"CleanIpBadgeTestMode\", \"出口身份测试模式\" },\n { \"ThermalTestMode\", \"温控测试模式\" },\n { \"AlertTestEnabled\", \"告警测试\" },\n { \"CodexModelTokenEfficiencyBaselinePassed\", \"Token 效率基准通过项\" },\n { \"CodexModelTimeEfficiencyBaselinePassed\", \"时间效率基准通过项\" }\n };\n\n private static readonly Dictionary<string, string> SettingHints = new Dictionary<string, string>(StringComparer.Ordinal)\n {\n { \"StartupEnabled\", \"写入当前用户启动项。\" },\n { \"PerformanceMode\", \"控制采样、动画和后台刷新节奏。\" },\n { \"VisibilityMode\", \"决定窗口在桌面、前台或全屏时的行为。\" },\n { \"ClickThroughMode\", \"允许鼠标事件穿透主窗口。\" },\n { \"HoverOpacityEnabled\", \"鼠标接近窗口时进入隐藏透明度。\" },\n { \"SensitiveMouseModeEnabled\", \"使用鼠标周围方形区域判断命中。\" },\n { \"SensitiveMouseRangePixels\", \"范围 10-300,值越大越容易触发。\" },\n { \"HoverOpacityRevealDelayEnabled\", \"鼠标离开后延迟恢复显示。\" },\n { \"HoverOpacityCoverEnabled\", \"自动隐藏时只在移到窗口上方后恢复。\" },\n { \"ReverseHoverOpacityRevealEnabled\", \"手动隐藏下鼠标移入窗口临时恢复。\" },\n { \"BurnInHiddenModeColorProtectionEnabled\", \"隐藏时执行颜色反相和白灰透明化。\" },\n { \"CodexRadarModelKey\", \"动态模型目录中的当前模型键。\" },\n { \"NetworkMonitorAdapterId\", \"留空时自动选择网络适配器。\" },\n { \"GfwProbeEnabled\", \"与云服务检测独立调度。\" },\n { \"PowerResumeRestartEnabled\", \"系统唤醒后自动重启 SeelenUI 和本程序。\" },\n { \"WinDRecoveryPulseEnabled\", \"按 Win+D 后延迟拉前本程序和 SeelenUI。\" }\n };\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Classes — Custom Controls\n // ═════════════════════════════════════════════════════════════════════\n\n // ── ToggleSwitch ─────────────────────────────────────────────────────\n // Authentic Win11-style toggle: pill track, animated sliding knob.\n private sealed class ToggleSwitch : Control\n {\n private bool isChecked;\n private float animProgress;\n private readonly Timer animTimer;\n private bool hover;\n\n public event EventHandler CheckedChanged;\n\n public bool Checked\n {\n get { return this.isChecked; }\n set\n {\n if (this.isChecked == value) return;\n this.isChecked = value;\n StartAnimation();\n if (CheckedChanged != null) CheckedChanged(this, EventArgs.Empty);\n }\n }\n\n public void SetCheckedSilent(bool value)\n {\n this.isChecked = value;\n this.animProgress = value ? 1.0f : 0.0f;\n this.Invalidate();\n }\n\n public ToggleSwitch()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Size = new Size(70, 35);\n this.BackColor = Color.Transparent;\n this.Cursor = Cursors.Hand;\n this.animTimer = new Timer();\n this.animTimer.Interval = 16;\n this.animTimer.Tick += OnAnimTick;\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n\n int radius = this.Height / 2;\n Rectangle trackRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(trackRect, radius))\n {\n if (this.isChecked)\n {\n using (LinearGradientBrush brush = new LinearGradientBrush(trackRect, AccentClr, AccentHover, 0f))\n {\n g.FillPath(brush, path);\n }\n }\n else\n {\n Color trackColor = this.hover ? DesignTokens.NeonGeekTheme.ToggleTrackHover : DesignTokens.NeonGeekTheme.ToggleTrackOff;\n using (SolidBrush brush = new SolidBrush(trackColor))\n {\n g.FillPath(brush, path);\n }\n using (Pen pen = new Pen(ControlBorder, 1.2f))\n {\n g.DrawPath(pen, path);\n }\n }\n }\n\n // Knob\n float knobDiameter = this.Height - 8;\n float knobMinX = 4;\n float knobMaxX = this.Width - knobDiameter - 4;\n float knobX = knobMinX + (knobMaxX - knobMinX) * this.animProgress;\n float knobY = 4;\n\n // Subtle knob shadow\n using (SolidBrush shadow = new SolidBrush(Color.FromArgb(24, 0, 0, 0)))\n {\n g.FillEllipse(shadow, knobX + 0.5f, knobY + 0.8f, knobDiameter, knobDiameter);\n }\n\n using (SolidBrush knobBrush = new SolidBrush(DesignTokens.SettingsTheme.ToggleKnob))\n {\n g.FillEllipse(knobBrush, knobX, knobY, knobDiameter, knobDiameter);\n }\n }\n\n protected override void OnClick(EventArgs e)\n {\n base.OnClick(e);\n this.Checked = !this.isChecked;\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n\n private void StartAnimation()\n {\n this.animTimer.Start();\n }\n\n private void OnAnimTick(object sender, EventArgs e)\n {\n float target = this.isChecked ? 1.0f : 0.0f;\n float step = 0.18f;\n if (Math.Abs(this.animProgress - target) < step)\n {\n this.animProgress = target;\n this.animTimer.Stop();\n }\n else\n {\n this.animProgress += this.isChecked ? step : -step;\n }\n this.Invalidate();\n }\n\n protected override void Dispose(bool disposing)\n {\n if (disposing && this.animTimer != null)\n {\n this.animTimer.Stop();\n this.animTimer.Dispose();\n }\n base.Dispose(disposing);\n }\n }\n\n // ── NavigationItem ────────────────────────────────────────────────────\n // Win11-style nav item with icon glyph, text, indicator bar, and hover.\n private sealed class NavigationItem : Panel\n {\n private readonly string icon;\n private readonly string text;\n private bool selected;\n private bool hover;\n\n public NavigationItem(string icon, string text)\n {\n this.icon = icon;\n this.text = text;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);\n this.Cursor = Cursors.Hand;\n this.BackColor = Color.Transparent;\n }\n\n public bool Selected\n {\n get { return this.selected; }\n set { this.selected = value; this.Invalidate(); }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;\n\n // Background\n Color bg = this.selected ? DesignTokens.SettingsTheme.NavSelectedBg :\n this.hover ? DesignTokens.SettingsTheme.NavHoverBg : Color.Transparent;\n\n if (bg.A > 0)\n {\n using (GraphicsPath path = CreateRoundRectangle(new Rectangle(2, 0, this.Width - 4, this.Height - 1), 5))\n using (SolidBrush brush = new SolidBrush(bg))\n {\n g.FillPath(brush, path);\n }\n }\n\n // Indicator bar (accent-colored pill on left edge)\n if (this.selected)\n {\n int barH = 16;\n int barW = 3;\n int barY = (this.Height - barH) / 2;\n using (GraphicsPath bar = CreateRoundRectangle(new Rectangle(3, barY, barW, barH), 1))\n using (SolidBrush brush = new SolidBrush(AccentClr))\n {\n g.FillPath(brush, bar);\n }\n }\n\n // Icon (Segoe Fluent Icons / MDL2 Assets)\n Color iconColor = this.selected ? TextPrimary : TextSecondary;\n Font icoFont = GetIconFont();\n using (SolidBrush brush = new SolidBrush(iconColor))\n {\n float iconY = (this.Height - icoFont.Height) / 2f + 1;\n g.DrawString(this.icon, icoFont, brush, 24, iconY);\n }\n\n // Text\n Color textColor = this.selected ? TextPrimary : TextSecondary;\n using (SolidBrush brush = new SolidBrush(textColor))\n {\n g.DrawString(this.text, this.Font, brush, 70, (this.Height - this.Font.Height) / 2f);\n }\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n this.hover = false;\n this.Invalidate();\n base.OnMouseLeave(e);\n }\n }\n\n // ── SettingGroupCard ──────────────────────────────────────────────────\n // Rounded-corner container holding multiple SettingRow children.\n // Draws a single card background; rows share the card surface.\n private sealed class SettingGroupCard : Panel\n {\n private readonly List<SettingRow> rows = new List<SettingRow>();\n\n public SettingGroupCard()\n {\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n }\n\n public void AddRow(SettingRow row)\n {\n this.rows.Add(row);\n this.Controls.Add(row);\n }\n\n public void LayoutRows()\n {\n int y = 0;\n bool first = true;\n for (int i = 0; i < this.rows.Count; i++)\n {\n SettingRow row = this.rows[i];\n row.ShowTopDivider = !first;\n int h = row.ComputeDesiredHeight(this.Width);\n row.SetBounds(0, y, this.Width, h);\n y += h;\n first = false;\n }\n this.Height = Math.Max(1, y);\n UpdateClipRegion();\n }\n\n private void UpdateClipRegion()\n {\n if (this.Width > 1 && this.Height > 1)\n {\n using (GraphicsPath rr = CreateRoundRectangle(new Rectangle(0, 0, this.Width, this.Height), DesignTokens.Radius.SettingsCard))\n {\n Region old = this.Region;\n this.Region = new Region(rr);\n if (old != null) old.Dispose();\n }\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n Graphics g = e.Graphics;\n g.SmoothingMode = SmoothingMode.AntiAlias;\n Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);\n using (GraphicsPath path = CreateRoundRectangle(rect, DesignTokens.Radius.SettingsCard))\n using (SolidBrush bg = new SolidBrush(CardRest))\n {\n g.FillPath(bg, path);\n }\n\n // Left edge indicator\n using (GraphicsPath accent = CreateRoundRectangle(new Rectangle(0, 0, 3, this.Height), 1))\n using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, 3, this.Height), AccentClr, AccentHover, 90f))\n {\n g.FillPath(brush, accent);\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutRows();\n }\n }\n\n // ── SettingRow ────────────────────────────────────────────────────────\n // A single setting item within a SettingGroupCard.\n // Left side: title + hint; right side: value control.\n // Draws hover highlight and optional top divider.\n private sealed class SettingRow : Panel\n {\n private readonly Control valueControl;\n private bool hover;\n public bool ShowTopDivider;\n\n public SettingRow(Control valueControl)\n {\n this.valueControl = valueControl;\n this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |\n ControlStyles.OptimizedDoubleBuffer, true);\n this.BackColor = Color.Transparent;\n this.Padding = new Padding(\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY,\n DesignTokens.Spacing.SettingsCardPaddingX,\n DesignTokens.Spacing.SettingsCardPaddingY);\n\n this.TitleLabel = new Label();\n this.TitleLabel.Font = DesignTokens.CreateUIFont(10.0f);\n this.TitleLabel.ForeColor = TextPrimary;\n this.TitleLabel.BackColor = Color.Transparent;\n this.TitleLabel.TextAlign = ContentAlignment.MiddleLeft;\n this.TitleLabel.AutoSize = true;\n\n this.HintLabel = new Label();\n this.HintLabel.Font = DesignTokens.CreateUIFont(8.5f);\n this.HintLabel.ForeColor = TextTertiary;\n this.HintLabel.BackColor = Color.Transparent;\n this.HintLabel.TextAlign = ContentAlignment.TopLeft;\n this.HintLabel.AutoSize = true;\n\n this.Controls.Add(this.TitleLabel);\n this.Controls.Add(this.HintLabel);\n this.Controls.Add(valueControl);\n }\n\n public Label TitleLabel { get; private set; }\n public Label HintLabel { get; private set; }\n\n public int ComputeDesiredHeight(int width)\n {\n int pad = this.Padding.Top + this.Padding.Bottom;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, width - this.Padding.Left - this.Padding.Right));\n bool compact = width < 928 || width - this.Padding.Left - this.Padding.Right - controlWidth < 320;\n\n if (compact)\n {\n int textWidth = Math.Max(120, width - this.Padding.Left - this.Padding.Right);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = this.Padding.Top + titleHeight + hintHeight + 8;\n return Math.Max(80, controlTop + this.valueControl.Height + this.Padding.Bottom);\n }\n else\n {\n int controlLeft = width - this.Padding.Right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - this.Padding.Left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, this.valueControl.Height);\n return Math.Max(60, this.Padding.Top + contentHeight + this.Padding.Bottom);\n }\n }\n\n protected override void OnPaint(PaintEventArgs e)\n {\n if (this.hover)\n {\n using (SolidBrush hbg = new SolidBrush(CardHover))\n {\n e.Graphics.FillRectangle(hbg, this.ClientRectangle);\n }\n }\n\n if (this.ShowTopDivider)\n {\n int x1 = this.Padding.Left;\n int x2 = this.Width - this.Padding.Right;\n using (Pen p = new Pen(DividerColor))\n {\n e.Graphics.DrawLine(p, x1, 0, x2, 0);\n }\n }\n }\n\n protected override void OnResize(EventArgs eventargs)\n {\n base.OnResize(eventargs);\n LayoutChildren();\n }\n\n protected override void OnControlAdded(ControlEventArgs e)\n {\n base.OnControlAdded(e);\n e.Control.MouseEnter += delegate { OnChildMouseEnter(); };\n e.Control.MouseLeave += delegate { OnChildMouseLeave(); };\n }\n\n protected override void OnMouseEnter(EventArgs e)\n {\n this.hover = true;\n this.Invalidate();\n base.OnMouseEnter(e);\n }\n\n protected override void OnMouseLeave(EventArgs e)\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n base.OnMouseLeave(e);\n }\n\n private void OnChildMouseEnter()\n {\n if (!this.hover) { this.hover = true; this.Invalidate(); }\n }\n\n private void OnChildMouseLeave()\n {\n if (!this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))\n {\n this.hover = false;\n this.Invalidate();\n }\n }\n\n private void LayoutChildren()\n {\n int left = this.Padding.Left;\n int top = this.Padding.Top;\n int right = this.Width - this.Padding.Right;\n int controlWidth = Math.Min(this.valueControl.Width, Math.Max(44, this.Width - this.Padding.Left - this.Padding.Right));\n bool compact = this.Width < 580 || this.Width - this.Padding.Left - this.Padding.Right - controlWidth < 320;\n\n if (compact)\n {\n int textWidth = Math.Max(120, right - left);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int controlTop = top + titleHeight + hintHeight + 8;\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n this.valueControl.SetBounds(left, controlTop, controlWidth, this.valueControl.Height);\n }\n else\n {\n int controlLeft = right - controlWidth;\n int textWidth = Math.Max(120, controlLeft - left - 24);\n int titleHeight = GetWrappedTextHeight(this.TitleLabel.Text, this.TitleLabel.Font, textWidth, 6);\n int hintHeight = string.IsNullOrEmpty(this.HintLabel.Text) ? 0 : GetWrappedTextHeight(this.HintLabel.Text, this.HintLabel.Font, textWidth, 4);\n int textHeight = titleHeight + hintHeight;\n int contentHeight = Math.Max(textHeight, this.valueControl.Height);\n int controlTop = top + Math.Max(0, (contentHeight - this.valueControl.Height) / 2);\n this.valueControl.SetBounds(controlLeft, controlTop, controlWidth, this.valueControl.Height);\n this.TitleLabel.SetBounds(left, top, textWidth, titleHeight);\n this.HintLabel.SetBounds(left, top + titleHeight, textWidth, hintHeight);\n }\n }\n }\n\n // ── FluentScrollPanel ────────────────────────────────────────────────\n private sealed class FluentScrollPanel : Panel\n {\n public FluentScrollPanel()\n {\n this.AutoScroll = true;\n }\n\n public bool ScrollByMouseWheelDelta(int delta)\n {\n int old = -this.AutoScrollPosition.Y;\n int next = old - delta;\n if (next < 0)\n {\n next = 0;\n }\n\n this.AutoScrollPosition = new Point(0, next);\n return old != -this.AutoScrollPosition.Y;\n }\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Nested Data Classes\n // ═════════════════════════════════════════════════════════════════════\n\n private sealed class CategoryPage\n {\n public string Title;\n public string Description;\n public NavigationItem NavItem;\n public FluentScrollPanel ScrollPanel;\n public FlowLayoutPanel Stack;\n public Panel Heading;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n public readonly List<SettingGroupData> Groups = new List<SettingGroupData>();\n }\n\n private sealed class SettingGroupData\n {\n public string Title;\n public Label TitleLabel;\n public SettingGroupCard Card;\n public readonly List<SettingEditor> Editors = new List<SettingEditor>();\n }\n\n private sealed class SettingEditor\n {\n public SettingEditor(PropertyInfo property, SettingRow card, Control control)\n {\n this.Property = property;\n this.Card = card;\n this.Control = control;\n }\n\n public PropertyInfo Property { get; private set; }\n public SettingRow Card { get; private set; }\n public Control Control { get; private set; }\n }\n\n private sealed class EnumOption\n {\n public EnumOption(object value)\n {\n this.Value = value;\n }\n\n public object Value { get; private set; }\n\n public override string ToString()\n {\n return Convert.ToString(this.Value, CultureInfo.InvariantCulture);\n }\n }\n\n private struct NumericRange\n {\n public NumericRange(decimal minimum, decimal maximum)\n {\n this.Minimum = minimum;\n this.Maximum = maximum;\n }\n\n public decimal Minimum;\n public decimal Maximum;\n }\n\n // ═════════════════════════════════════════════════════════════════════\n // Static Helpers & P/Invoke\n // ═════════════════════════════════════════════════════════════════════\n\n private static GraphicsPath CreateRoundRectangle(Rectangle bounds, int radius)\n {\n int diameter = Math.Max(1, radius * 2);\n GraphicsPath path = new GraphicsPath();\n path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90);\n path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90);\n path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90);\n path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90);\n path.CloseFigure();\n return path;\n }\n\n [DllImport(\"user32.dll\")]\n private static extern bool ReleaseCapture();\n\n [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)]\n private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);\n\n [DllImport(\"dwmapi.dll\")]\n private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);\n}\n"}}, "Core/DeepSeekBalanceMonitor.cs": {"5d73239e59112a3448573ea87f915bdf2dbe1bc9": {"t": 1785745809.4996147, "n": 38812, "h": "80eb132736a155c2", "c": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.IO;\nusing System.Net;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Web.Script.Serialization;\n\ninternal sealed class DeepSeekBalanceSnapshot\n{\n public bool ApiKeyConfigured { get; set; }\n public bool Known { get; set; }\n public bool IsAvailable { get; set; }\n public string Currency { get; set; }\n public double BalanceCny { get; set; }\n public bool Last24HourUsageKnown { get; set; }\n public double Last24HourUsageCny { get; set; }\n public DateTime CheckedAtUtc { get; set; }\n public DateTime CheckedAtLocal { get; set; }\n public string ErrorCode { get; set; }\n public string ErrorMessage { get; set; }\n public bool RequestRunning { get; set; }\n public bool ServiceKnown { get; set; }\n public bool ServiceIsAvailable { get; set; }\n public string ServiceErrorCode { get; set; }\n public string ServiceErrorMessage { get; set; }\n public bool ServiceRequestRunning { get; set; }\n public DateTime ServiceCheckedAtUtc { get; set; }\n public DateTime ServiceCheckedAtLocal { get; set; }\n\n public static DeepSeekBalanceSnapshot CreateDefault()\n {\n return new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = false,\n Known = false,\n IsAvailable = false,\n Currency = \"CNY\",\n BalanceCny = 0.0,\n Last24HourUsageKnown = false,\n Last24HourUsageCny = 0.0,\n CheckedAtUtc = DateTime.MinValue,\n CheckedAtLocal = DateTime.MinValue,\n ErrorCode = string.Empty,\n ErrorMessage = string.Empty,\n RequestRunning = false,\n ServiceKnown = false,\n ServiceIsAvailable = false,\n ServiceErrorCode = string.Empty,\n ServiceErrorMessage = string.Empty,\n ServiceRequestRunning = false,\n ServiceCheckedAtUtc = DateTime.MinValue,\n ServiceCheckedAtLocal = DateTime.MinValue\n };\n }\n\n public DeepSeekBalanceSnapshot Clone()\n {\n return new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = this.ApiKeyConfigured,\n Known = this.Known,\n IsAvailable = this.IsAvailable,\n Currency = this.Currency,\n BalanceCny = this.BalanceCny,\n Last24HourUsageKnown = this.Last24HourUsageKnown,\n Last24HourUsageCny = this.Last24HourUsageCny,\n CheckedAtUtc = this.CheckedAtUtc,\n CheckedAtLocal = this.CheckedAtLocal,\n ErrorCode = this.ErrorCode,\n ErrorMessage = this.ErrorMessage,\n RequestRunning = this.RequestRunning,\n ServiceKnown = this.ServiceKnown,\n ServiceIsAvailable = this.ServiceIsAvailable,\n ServiceErrorCode = this.ServiceErrorCode,\n ServiceErrorMessage = this.ServiceErrorMessage,\n ServiceRequestRunning = this.ServiceRequestRunning,\n ServiceCheckedAtUtc = this.ServiceCheckedAtUtc,\n ServiceCheckedAtLocal = this.ServiceCheckedAtLocal\n };\n }\n}\n\ninternal static class DeepSeekBalanceMonitor\n{\n internal const string ApiKeyEnvironmentVariable = \"DEEPSEEK_API_KEY\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ApiKeyFileName = \"deepseek-api-key.bin\";\n private const string LegacyApiKeyFileName = \"deepseek-api-key.txt\";\n private const int TimeoutMs = 10000;\n private const int NormalRefreshSeconds = 60;\n private const int ErrorRefreshSeconds = 300;\n private const int HistoryRetentionHours = 48;\n\n private static readonly object syncRoot = new object();\n // DeepSeek balance is shared by every Claude view. The monitor owns the key\n // lookup, single-flight request, 48h history file and callbacks so two\n // windows cannot race the API or append duplicate history samples.\n private static readonly List<DeepSeekBalancePoint> history = new List<DeepSeekBalancePoint>();\n private static readonly Dictionary<string, Action> joinedCallbacks = new Dictionary<string, Action>(StringComparer.OrdinalIgnoreCase);\n private static DeepSeekBalanceSnapshot snapshot = DeepSeekBalanceSnapshot.CreateDefault();\n private static bool requestRunning;\n private static bool historyLoaded;\n private static DateTime nextRefreshUtc = DateTime.MinValue;\n private static string refreshTrigger = \"首次刷新\";\n private static Func<string> apiKeyProviderOverride;\n private static Func<string, DeepSeekBalanceSnapshot> balanceReaderOverride;\n private static Func<DeepSeekBalanceSnapshot> publicApiStatusReaderOverride;\n private static string historyPathOverride;\n\n internal static string ApiKeyPath\n {\n get { return Path.Combine(Logger.DirectoryPath, ApiKeyFileName); }\n }\n\n internal static string LegacyApiKeyPath\n {\n get { return Path.Combine(Logger.DirectoryPath, LegacyApiKeyFileName); }\n }\n\n internal static string HistoryPath\n {\n get\n {\n return string.IsNullOrWhiteSpace(historyPathOverride)\n ? Path.Combine(Logger.DirectoryPath, \"deepseek-balance-history.jsonl\")\n : historyPathOverride;\n }\n }\n\n internal static void RequestRefresh()\n {\n RequestRefresh(\"强制刷新\");\n }\n\n internal static void RequestRefresh(string trigger)\n {\n lock (syncRoot)\n {\n nextRefreshUtc = DateTime.UtcNow;\n refreshTrigger = string.IsNullOrWhiteSpace(trigger) ? \"强制刷新\" : trigger.Trim();\n }\n }\n\n internal static DeepSeekBalanceSnapshot GetSnapshot()\n {\n lock (syncRoot)\n {\n return snapshot == null ? DeepSeekBalanceSnapshot.CreateDefault() : snapshot.Clone();\n }\n }\n\n internal static void SetSnapshotForTest(DeepSeekBalanceSnapshot testSnapshot)\n {\n lock (syncRoot)\n {\n snapshot = testSnapshot == null ? DeepSeekBalanceSnapshot.CreateDefault() : testSnapshot.Clone();\n snapshot.RequestRunning = false;\n snapshot.ServiceRequestRunning = false;\n requestRunning = false;\n nextRefreshUtc = DateTime.UtcNow.AddHours(1.0);\n }\n }\n\n internal static bool RefreshIfNeeded(Action onSnapshotChanged)\n {\n return RefreshIfNeeded(\"unknown\", \"定时间隔\", onSnapshotChanged);\n }\n\n internal static bool RefreshIfNeeded(string consumerId, string trigger, Action onSnapshotChanged)\n {\n DateTime nowUtc = DateTime.UtcNow;\n string apiKey = GetApiKey();\n bool apiKeyConfigured = !string.IsNullOrWhiteSpace(apiKey);\n string effectiveTrigger = string.IsNullOrWhiteSpace(trigger) ? \"定时间隔\" : trigger.Trim();\n\n lock (syncRoot)\n {\n string consumer = NormalizeConsumerId(consumerId);\n if (requestRunning ||\n (nextRefreshUtc != DateTime.MinValue && nowUtc < nextRefreshUtc))\n {\n if (requestRunning && !joinedCallbacks.ContainsKey(consumer))\n {\n joinedCallbacks[consumer] = onSnapshotChanged;\n return true;\n }\n\n return false;\n }\n\n effectiveTrigger = !string.IsNullOrWhiteSpace(refreshTrigger) &&\n !string.Equals(refreshTrigger, \"定时间隔\", StringComparison.Ordinal)\n ? refreshTrigger\n : (string.IsNullOrWhiteSpace(trigger) ? \"定时间隔\" : trigger.Trim());\n refreshTrigger = \"定时间隔\";\n requestRunning = true;\n joinedCallbacks.Clear();\n joinedCallbacks[consumer] = onSnapshotChanged;\n DeepSeekBalanceSnapshot running = snapshot == null\n ? DeepSeekBalanceSnapshot.CreateDefault()\n : snapshot.Clone();\n running.ApiKeyConfigured = apiKeyConfigured;\n running.RequestRunning = apiKeyConfigured;\n running.ServiceRequestRunning = true;\n snapshot = running;\n }\n\n InvokeSnapshotChanged(onSnapshotChanged);\n Task.Run((Action)delegate\n {\n Stopwatch stopwatch = Stopwatch.StartNew();\n DeepSeekBalanceSnapshot next;\n try\n {\n if (apiKeyConfigured)\n {\n next = balanceReaderOverride == null\n ? ReadBalance(apiKey)\n : balanceReaderOverride(apiKey);\n EnsureServiceStatusInitialized(next, true);\n ApplyHistory(next);\n }\n else\n {\n next = publicApiStatusReaderOverride == null\n ? ReadPublicApiStatus()\n : publicApiStatusReaderOverride();\n EnsureNoKeyBalanceState(next);\n }\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n next = BuildErrorSnapshot(\"ERROR\", \"请求失败\");\n ApplyServiceError(next, \"ERROR\", \"请求失败\", false);\n }\n\n stopwatch.Stop();\n List<Action> callbacks;\n List<string> consumers;\n bool serviceHealthy = next.ServiceKnown && next.ServiceIsAvailable;\n bool balanceDoneOrNotNeeded = !next.ApiKeyConfigured || next.Known;\n DateTime nextDueUtc = DateTime.UtcNow.AddSeconds(serviceHealthy && balanceDoneOrNotNeeded ? NormalRefreshSeconds : ErrorRefreshSeconds);\n lock (syncRoot)\n {\n next.RequestRunning = false;\n next.ServiceRequestRunning = false;\n snapshot = next;\n requestRunning = false;\n nextRefreshUtc = nextDueUtc;\n consumers = new List<string>(joinedCallbacks.Keys);\n callbacks = new List<Action>(joinedCallbacks.Values);\n joinedCallbacks.Clear();\n refreshTrigger = serviceHealthy && balanceDoneOrNotNeeded ? \"定时间隔\" : \"异常状态重试\";\n }\n\n NetworkCheckHistoryLogger.LogCompleted(\n \"deepseek_balance\",\n \"deepseek_balance\",\n effectiveTrigger,\n serviceHealthy ? \"Normal\" : (next.ServiceErrorCode ?? string.Empty),\n serviceHealthy,\n (int)Math.Min(int.MaxValue, stopwatch.ElapsedMilliseconds),\n new Dictionary<string, object>\n {\n { \"health\", serviceHealthy ? \"Normal\" : \"Unknown\" },\n { \"service_known\", next.ServiceKnown },\n { \"service_error_code\", next.ServiceErrorCode ?? string.Empty },\n { \"balance_known\", next.Known },\n { \"balance_error_code\", next.ErrorCode ?? string.Empty },\n { \"api_key_configured\", next.ApiKeyConfigured },\n { \"joined_consumers\", string.Join(\",\", consumers.ToArray()) }\n });\n\n InvokeSnapshotChanged(callbacks);\n });\n return true;\n }\n\n internal static void RunSelfTest()\n {\n Func<string> previousKeyProvider = apiKeyProviderOverride;\n Func<string, DeepSeekBalanceSnapshot> previousReader = balanceReaderOverride;\n Func<DeepSeekBalanceSnapshot> previousPublicApiReader = publicApiStatusReaderOverride;\n string previousHistoryPath = historyPathOverride;\n string testDir = Path.Combine(Path.GetTempPath(), ProductIdentity.MachineName + \"-DeepSeekBalanceMonitor-\" + Guid.NewGuid().ToString(\"N\"));\n try\n {\n Directory.CreateDirectory(testDir);\n historyPathOverride = Path.Combine(testDir, \"deepseek-balance-history.jsonl\");\n apiKeyProviderOverride = delegate { return string.Empty; };\n balanceReaderOverride = null;\n publicApiStatusReaderOverride = delegate\n {\n DeepSeekBalanceSnapshot result = DeepSeekBalanceSnapshot.CreateDefault();\n EnsureNoKeyBalanceState(result);\n ApplyServiceHttpStatus(result, 401);\n return result;\n };\n ResetForTest();\n if (!RefreshIfNeeded(\"test\", \"no_key\", null))\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: no-key request did not start.\");\n }\n\n SpinWait.SpinUntil(delegate { return !GetSnapshot().ServiceRequestRunning; }, 5000);\n DeepSeekBalanceSnapshot noKey = GetSnapshot();\n if (noKey.ApiKeyConfigured ||\n !string.Equals(noKey.ErrorCode, \"NO_KEY\", StringComparison.Ordinal) ||\n !noKey.ServiceKnown ||\n !noKey.ServiceIsAvailable)\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: no-key public API state failed.\");\n }\n\n if (!string.Equals(GetHttpErrorReason(401), \"鉴权失败\", StringComparison.Ordinal) ||\n !string.Equals(GetHttpErrorReason(402), \"余额不足\", StringComparison.Ordinal) ||\n !string.Equals(GetHttpErrorReason(429), \"限流\", StringComparison.Ordinal))\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: HTTP error mapping failed.\");\n }\n\n int calls = 0;\n int callbacks = 0;\n ManualResetEventSlim done = new ManualResetEventSlim(false);\n apiKeyProviderOverride = delegate { return \"test-key\"; };\n balanceReaderOverride = delegate(string key)\n {\n Interlocked.Increment(ref calls);\n Thread.Sleep(80);\n return new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = true,\n Known = true,\n IsAvailable = true,\n Currency = \"CNY\",\n BalanceCny = 88.5,\n CheckedAtUtc = DateTime.UtcNow,\n CheckedAtLocal = DateTime.Now\n };\n };\n ResetForTest();\n Action callback = delegate\n {\n if (Interlocked.Increment(ref callbacks) >= 2)\n {\n done.Set();\n }\n };\n if (!RefreshIfNeeded(\"codex_radar\", \"self_test\", callback) ||\n !RefreshIfNeeded(\"claude_radar\", \"self_test\", callback))\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: consumers did not start/join.\");\n }\n\n if (!done.Wait(5000) || calls != 1)\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: joined request did not fan out.\");\n }\n\n DeepSeekBalanceSnapshot cloned = GetSnapshot();\n cloned.BalanceCny = 0.0;\n if (Math.Abs(GetSnapshot().BalanceCny - 88.5) > 0.01)\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: snapshot clone was mutable.\");\n }\n\n DateTime now = DateTime.UtcNow;\n File.WriteAllText(\n historyPathOverride,\n \"{\\\"schema_version\\\":1,\\\"timestamp_utc\\\":\\\"\" + now.AddHours(-49).ToString(\"O\", CultureInfo.InvariantCulture) + \"\\\",\\\"balance_cny\\\":100}\\n\" +\n \"{\\\"schema_version\\\":1,\\\"timestamp_utc\\\":\\\"\" + now.AddHours(-1).ToString(\"O\", CultureInfo.InvariantCulture) + \"\\\",\\\"balance_cny\\\":90}\\n\",\n new UTF8Encoding(false));\n ResetForTest();\n balanceReaderOverride = delegate(string key)\n {\n return new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = true,\n Known = true,\n IsAvailable = true,\n Currency = \"CNY\",\n BalanceCny = 80,\n CheckedAtUtc = now,\n CheckedAtLocal = now.ToLocalTime()\n };\n };\n RequestRefresh(\"history\");\n RefreshIfNeeded(\"history\", \"history\", null);\n SpinWait.SpinUntil(delegate { return !GetSnapshot().RequestRunning; }, 5000);\n string[] lines = File.ReadAllLines(historyPathOverride, Encoding.UTF8);\n if (lines.Length != 2 || !GetSnapshot().Last24HourUsageKnown)\n {\n throw new InvalidOperationException(\"DeepSeek balance self-test: history retention/usage failed.\");\n }\n }\n finally\n {\n apiKeyProviderOverride = previousKeyProvider;\n balanceReaderOverride = previousReader;\n publicApiStatusReaderOverride = previousPublicApiReader;\n historyPathOverride = previousHistoryPath;\n ResetForTest();\n try\n {\n if (Directory.Exists(testDir))\n {\n Directory.Delete(testDir, true);\n }\n }\n catch\n {\n }\n }\n }\n\n internal static string FormatDisplayText(DeepSeekBalanceSnapshot displaySnapshot)\n {\n if (displaySnapshot == null || !displaySnapshot.ApiKeyConfigured)\n {\n return \"DS:--\";\n }\n\n if (!displaySnapshot.Known)\n {\n return displaySnapshot.RequestRunning ? \"DS:...\" : \"DS:--\";\n }\n\n double rounded = Math.Round(Math.Max(0.0, displaySnapshot.BalanceCny), 0, MidpointRounding.AwayFromZero);\n return \"DS:\\uFFE5\" + rounded.ToString(\"0\", CultureInfo.InvariantCulture);\n }\n\n internal static string BuildCacheSignature(DeepSeekBalanceSnapshot displaySnapshot)\n {\n if (displaySnapshot == null)\n {\n return \"ds:null\";\n }\n\n return string.Join(\n \",\",\n new string[]\n {\n displaySnapshot.ApiKeyConfigured ? \"1\" : \"0\",\n displaySnapshot.Known ? \"1\" : \"0\",\n displaySnapshot.IsAvailable ? \"1\" : \"0\",\n displaySnapshot.RequestRunning ? \"1\" : \"0\",\n displaySnapshot.ServiceKnown ? \"1\" : \"0\",\n displaySnapshot.ServiceIsAvailable ? \"1\" : \"0\",\n displaySnapshot.ServiceRequestRunning ? \"1\" : \"0\",\n displaySnapshot.BalanceCny.ToString(\"0.###\", CultureInfo.InvariantCulture),\n displaySnapshot.Last24HourUsageCny.ToString(\"0.###\", CultureInfo.InvariantCulture),\n displaySnapshot.ErrorCode ?? string.Empty,\n displaySnapshot.ServiceErrorCode ?? string.Empty\n });\n }\n\n internal static ColorlessDeepSeekAlert BuildAlert()\n {\n DeepSeekBalanceSnapshot current = GetSnapshot();\n if (current == null)\n {\n return null;\n }\n\n if (current.ServiceRequestRunning && !current.ServiceKnown)\n {\n return new ColorlessDeepSeekAlert(\"deepseek:checking\", \"DeepSeek\", \"检测中\", current);\n }\n\n if (current.ServiceKnown && current.ServiceIsAvailable)\n {\n return null;\n }\n\n if (!string.IsNullOrWhiteSpace(current.ServiceErrorCode))\n {\n string errorCode = current.ServiceErrorCode.Trim();\n return new ColorlessDeepSeekAlert(\n \"deepseek:\" + errorCode,\n \"DeepSeek\",\n string.IsNullOrWhiteSpace(current.ServiceErrorMessage) ? \"请求失败\" : current.ServiceErrorMessage,\n current);\n }\n\n return null;\n }\n\n internal static string GetApiKey()\n {\n if (apiKeyProviderOverride != null)\n {\n return apiKeyProviderOverride() ?? string.Empty;\n }\n\n string key = Environment.GetEnvironmentVariable(ApiKeyEnvironmentVariable);\n if (!string.IsNullOrWhiteSpace(key))\n {\n return key.Trim();\n }\n\n key = Environment.GetEnvironmentVariable(ApiKeyEnvironmentVariable, EnvironmentVariableTarget.User);\n if (!string.IsNullOrWhiteSpace(key))\n {\n return key.Trim();\n }\n\n key = Environment.GetEnvironmentVariable(ApiKeyEnvironmentVariable, EnvironmentVariableTarget.Machine);\n if (!string.IsNullOrWhiteSpace(key))\n {\n return key.Trim();\n }\n\n try\n {\n string secret;\n bool migrated;\n string errorCode;\n return SecretStore.TryReadOrMigrateSecret(\n ApiKeyPath,\n LegacyApiKeyPath,\n SecretStore.TrimSecret,\n out secret,\n out migrated,\n out errorCode)\n ? secret\n : string.Empty;\n }\n catch\n {\n }\n\n return string.Empty;\n }\n\n private static void InvokeSnapshotChanged(Action onSnapshotChanged)\n {\n if (onSnapshotChanged == null)\n {\n return;\n }\n\n try\n {\n onSnapshotChanged();\n }\n catch (Exception ex)\n {\n Program.LogException(ex);\n }\n }\n\n private static void InvokeSnapshotChanged(List<Action> callbacks)\n {\n if (callbacks == null)\n {\n return;\n }\n\n for (int i = 0; i < callbacks.Count; i++)\n {\n InvokeSnapshotChanged(callbacks[i]);\n }\n }\n\n private static string NormalizeConsumerId(string consumerId)\n {\n return string.IsNullOrWhiteSpace(consumerId) ? \"unknown\" : consumerId.Trim();\n }\n\n private static void ResetForTest()\n {\n ResetForTest(true);\n }\n\n private static void ResetForTest(bool clearHistoryFileState)\n {\n lock (syncRoot)\n {\n snapshot = DeepSeekBalanceSnapshot.CreateDefault();\n requestRunning = false;\n nextRefreshUtc = DateTime.MinValue;\n refreshTrigger = \"首次刷新\";\n joinedCallbacks.Clear();\n if (clearHistoryFileState)\n {\n historyLoaded = false;\n history.Clear();\n }\n }\n }\n\n private static DeepSeekBalanceSnapshot ReadPublicApiStatus()\n {\n HttpWebRequest request = CreateDeepSeekApiRequest(BalanceUrl);\n try\n {\n using ((HttpWebResponse)request.GetResponse())\n {\n DeepSeekBalanceSnapshot result = DeepSeekBalanceSnapshot.CreateDefault();\n EnsureNoKeyBalanceState(result);\n ApplyServiceHttpStatus(result, 200);\n return result;\n }\n }\n catch (WebException ex)\n {\n HttpWebResponse response = ex.Response as HttpWebResponse;\n DeepSeekBalanceSnapshot result = DeepSeekBalanceSnapshot.CreateDefault();\n EnsureNoKeyBalanceState(result);\n if (response != null)\n {\n ApplyServiceHttpStatus(result, (int)response.StatusCode);\n response.Close();\n return result;\n }\n\n ApplyServiceError(result, \"NET\", \"无法连接\", false);\n return result;\n }\n }\n\n private static DeepSeekBalanceSnapshot ReadBalance(string apiKey)\n {\n HttpWebRequest request = CreateDeepSeekApiRequest(BalanceUrl);\n request.Headers[\"Authorization\"] = \"Bearer \" + apiKey;\n\n try\n {\n using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())\n using (Stream stream = response.GetResponseStream())\n using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))\n {\n string content = reader.ReadToEnd();\n DeepSeekBalanceSnapshot parsed;\n if (TryParseResponse(content, out parsed))\n {\n ApplyServiceHttpStatus(parsed, (int)response.StatusCode);\n return parsed;\n }\n\n DeepSeekBalanceSnapshot parseError = BuildErrorSnapshot(\"PARSE\", \"解析失败\");\n ApplyServiceError(parseError, \"PARSE\", \"元素缺失\", true);\n return parseError;\n }\n }\n catch (WebException ex)\n {\n HttpWebResponse response = ex.Response as HttpWebResponse;\n if (response != null)\n {\n int statusCode = (int)response.StatusCode;\n DeepSeekBalanceSnapshot error = BuildErrorSnapshot(\n statusCode.ToString(CultureInfo.InvariantCulture),\n GetHttpErrorReason(statusCode));\n ApplyServiceHttpStatus(error, statusCode);\n response.Close();\n return error;\n }\n\n DeepSeekBalanceSnapshot netError = BuildErrorSnapshot(\"NET\", \"无法连接\");\n ApplyServiceError(netError, \"NET\", \"无法连接\", false);\n return netError;\n }\n }\n\n private static HttpWebRequest CreateDeepSeekApiRequest(string url)\n {\n try\n {\n ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;\n }\n catch\n {\n }\n\n HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);\n request.Method = \"GET\";\n request.UserAgent = ProductIdentity.UserAgent;\n request.Timeout = TimeoutMs;\n request.ReadWriteTimeout = TimeoutMs;\n request.Headers[\"Cache-Control\"] = \"no-store, no-cache\";\n return request;\n }\n\n private static void EnsureNoKeyBalanceState(DeepSeekBalanceSnapshot snapshotToUpdate)\n {\n if (snapshotToUpdate == null)\n {\n return;\n }\n\n snapshotToUpdate.ApiKeyConfigured = false;\n snapshotToUpdate.Known = false;\n snapshotToUpdate.IsAvailable = false;\n snapshotToUpdate.Currency = \"CNY\";\n snapshotToUpdate.BalanceCny = 0.0;\n snapshotToUpdate.ErrorCode = \"NO_KEY\";\n snapshotToUpdate.ErrorMessage = \"未配置\";\n snapshotToUpdate.RequestRunning = false;\n }\n\n private static void EnsureServiceStatusInitialized(DeepSeekBalanceSnapshot snapshotToUpdate, bool apiKeyConfigured)\n {\n if (snapshotToUpdate == null)\n {\n return;\n }\n\n snapshotToUpdate.ApiKeyConfigured = apiKeyConfigured;\n if (!snapshotToUpdate.ServiceKnown && string.IsNullOrWhiteSpace(snapshotToUpdate.ServiceErrorCode))\n {\n ApplyServiceHttpStatus(snapshotToUpdate, snapshotToUpdate.Known ? 200 : 500);\n }\n }\n\n private static void ApplyServiceHttpStatus(DeepSeekBalanceSnapshot snapshotToUpdate, int statusCode)\n {\n if (snapshotToUpdate == null)\n {\n return;\n }\n\n if (IsDeepSeekApiReachableStatus(statusCode))\n {\n ApplyServiceNormal(snapshotToUpdate);\n return;\n }\n\n string code = statusCode.ToString(CultureInfo.InvariantCulture);\n ApplyServiceError(snapshotToUpdate, code, GetHttpErrorReason(statusCode), true);\n }\n\n private static bool IsDeepSeekApiReachableStatus(int statusCode)\n {\n // 401/402/422 are account/request-level results from the API gateway. They prove the\n // public API is reachable, so they must not be shown as DeepSeek service failures.\n return statusCode == 200 ||\n statusCode == 400 ||\n statusCode == 401 ||\n statusCode == 402 ||\n statusCode == 422;\n }\n\n private static void ApplyServiceNormal(DeepSeekBalanceSnapshot snapshotToUpdate)\n {\n DateTime nowUtc = DateTime.UtcNow;\n snapshotToUpdate.ServiceKnown = true;\n snapshotToUpdate.ServiceIsAvailable = true;\n snapshotToUpdate.ServiceErrorCode = string.Empty;\n snapshotToUpdate.ServiceErrorMessage = string.Empty;\n snapshotToUpdate.ServiceRequestRunning = false;\n snapshotToUpdate.ServiceCheckedAtUtc = nowUtc;\n snapshotToUpdate.ServiceCheckedAtLocal = DateTime.Now;\n }\n\n private static void ApplyServiceError(\n DeepSeekBalanceSnapshot snapshotToUpdate,\n string errorCode,\n string message,\n bool responseReceived)\n {\n DateTime nowUtc = DateTime.UtcNow;\n snapshotToUpdate.ServiceKnown = responseReceived;\n snapshotToUpdate.ServiceIsAvailable = false;\n snapshotToUpdate.ServiceErrorCode = string.IsNullOrWhiteSpace(errorCode) ? \"ERROR\" : errorCode.Trim();\n snapshotToUpdate.ServiceErrorMessage = string.IsNullOrWhiteSpace(message) ? \"请求失败\" : message.Trim();\n snapshotToUpdate.ServiceRequestRunning = false;\n snapshotToUpdate.ServiceCheckedAtUtc = nowUtc;\n snapshotToUpdate.ServiceCheckedAtLocal = DateTime.Now;\n }\n\n private static bool TryParseResponse(string content, out DeepSeekBalanceSnapshot parsed)\n {\n parsed = null;\n if (string.IsNullOrWhiteSpace(content))\n {\n return false;\n }\n\n Dictionary<string, object> root;\n try\n {\n root = new JavaScriptSerializer().DeserializeObject(content) as Dictionary<string, object>;\n }\n catch\n {\n return false;\n }\n\n if (root == null)\n {\n return false;\n }\n\n object isAvailableObj;\n bool isAvailable = true;\n if (root.TryGetValue(\"is_available\", out isAvailableObj) && isAvailableObj is bool)\n {\n isAvailable = (bool)isAvailableObj;\n }\n\n object balanceInfosObj;\n if (!root.TryGetValue(\"balance_infos\", out balanceInfosObj))\n {\n return false;\n }\n\n object[] balanceInfos = balanceInfosObj as object[];\n if (balanceInfos == null)\n {\n return false;\n }\n\n double balanceCny = 0.0;\n string currency = \"CNY\";\n bool found = false;\n for (int i = 0; i < balanceInfos.Length; i++)\n {\n Dictionary<string, object> info = balanceInfos[i] as Dictionary<string, object>;\n if (info == null)\n {\n continue;\n }\n\n object currencyObj;\n string candidateCurrency = info.TryGetValue(\"currency\", out currencyObj)\n ? Convert.ToString(currencyObj, CultureInfo.InvariantCulture)\n : string.Empty;\n if (!string.Equals(candidateCurrency, \"CNY\", StringComparison.OrdinalIgnoreCase) && found)\n {\n continue;\n }\n\n object balanceObj;\n double parsedBalance;\n if (info.TryGetValue(\"total_balance\", out balanceObj) &&\n TryParseInvariantDouble(Convert.ToString(balanceObj, CultureInfo.InvariantCulture), out parsedBalance))\n {\n balanceCny = parsedBalance;\n currency = string.IsNullOrWhiteSpace(candidateCurrency) ? \"CNY\" : candidateCurrency.ToUpperInvariant();\n found = string.Equals(currency, \"CNY\", StringComparison.OrdinalIgnoreCase);\n if (found)\n {\n break;\n }\n }\n }\n\n if (!found && balanceCny <= 0.0)\n {\n return false;\n }\n\n DateTime nowUtc = DateTime.UtcNow;\n parsed = new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = true,\n Known = true,\n IsAvailable = isAvailable,\n Currency = currency,\n BalanceCny = Math.Max(0.0, balanceCny),\n Last24HourUsageKnown = false,\n Last24HourUsageCny = 0.0,\n CheckedAtUtc = nowUtc,\n CheckedAtLocal = DateTime.Now,\n ErrorCode = string.Empty,\n ErrorMessage = string.Empty,\n RequestRunning = false\n };\n return true;\n }\n\n private static bool TryParseInvariantDouble(string value, out double number)\n {\n return double.TryParse(\n value,\n NumberStyles.Float,\n CultureInfo.InvariantCulture,\n out number);\n }\n\n private static DeepSeekBalanceSnapshot BuildErrorSnapshot(string errorCode, string message)\n {\n return new DeepSeekBalanceSnapshot\n {\n ApiKeyConfigured = true,\n Known = false,\n IsAvailable = false,\n Currency = \"CNY\",\n BalanceCny = 0.0,\n Last24HourUsageKnown = false,\n Last24HourUsageCny = 0.0,\n CheckedAtUtc = DateTime.UtcNow,\n CheckedAtLocal = DateTime.Now,\n ErrorCode = errorCode ?? string.Empty,\n ErrorMessage = string.IsNullOrWhiteSpace(message) ? \"请求失败\" : message,\n RequestRunning = false\n };\n }\n\n private static string GetHttpErrorReason(int statusCode)\n {\n if (statusCode == 401)\n {\n return \"鉴权失败\";\n }\n\n if (statusCode == 402)\n {\n return \"余额不足\";\n }\n\n if (statusCode == 429)\n {\n return \"限流\";\n }\n\n if (statusCode >= 500)\n {\n return \"服务异常\";\n }\n\n return \"HTTP \" + statusCode.ToString(CultureInfo.InvariantCulture);\n }\n\n private static void ApplyHistory(DeepSeekBalanceSnapshot next)\n {\n if (next == null || !next.Known)\n {\n return;\n }\n\n lock (syncRoot)\n {\n EnsureHistoryLoadedLocked();\n DateTime nowUtc = next.CheckedAtUtc == DateTime.MinValue ? DateTime.UtcNow : next.CheckedAtUtc;\n history.Add(new DeepSeekBalancePoint\n {\n TimestampUtc = nowUtc,\n BalanceCny = next.BalanceCny\n });\n\n TrimHistoryLocked(nowUtc);\n next.Last24HourUsageCny = CalculateLast24HourUsageLocked(nowUtc);\n next.Last24HourUsageKnown = true;\n SaveHistoryLocked();\n }\n }\n\n private static void EnsureHistoryLoadedLocked()\n {\n if (historyLoaded)\n {\n return;\n }\n\n historyLoaded = true;\n history.Clear();\n string path = HistoryPath;\n if (!File.Exists(path))\n {\n return;\n }\n\n try\n {\n string[] lines = File.ReadAllLines(path, Encoding.UTF8);\n for (int i = 0; i < lines.Length; i++)\n {\n Dictionary<string, object> row;\n try\n {\n row = new JavaScriptSerializer().DeserializeObject(lines[i]) as Dictionary<string, object>;\n }\n catch\n {\n continue;\n }\n\n if (row == null)\n {\n continue;\n }\n\n object timestampObj;\n object balanceObj;\n DateTime timestampUtc;\n double balance;\n if (row.TryGetValue(\"timestamp_utc\", out timestampObj) &&\n row.TryGetValue(\"balance_cny\", out balanceObj) &&\n DateTime.TryParse(\n Convert.ToString(timestampObj, CultureInfo.InvariantCulture),\n CultureInfo.InvariantCulture,\n DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,\n out timestampUtc) &&\n TryParseInvariantDouble(Convert.ToString(balanceObj, CultureInfo.InvariantCulture), out balance))\n {\n history.Add(new DeepSeekBalancePoint\n {\n TimestampUtc = timestampUtc,\n BalanceCny = Math.Max(0.0, balance)\n });\n }\n }\n }\n catch\n {\n }\n\n history.Sort(delegate(DeepSeekBalancePoint left, DeepSeekBalancePoint right)\n {\n return left.TimestampUtc.CompareTo(right.TimestampUtc);\n });\n TrimHistoryLocked(DateTime.UtcNow);\n }\n\n private static void TrimHistoryLocked(DateTime nowUtc)\n {\n DateTime cutoffUtc = nowUtc.AddHours(-HistoryRetentionHours);\n history.RemoveAll(delegate(DeepSeekBalancePoint point)\n {\n return point == null || point.TimestampUtc < cutoffUtc;\n });\n }\n\n private static double CalculateLast24HourUsageLocked(DateTime nowUtc)\n {\n DateTime cutoffUtc = nowUtc.AddHours(-24.0);\n double used = 0.0;\n DeepSeekBalancePoint previous = null;\n for (int i = 0; i < history.Count; i++)\n {\n DeepSeekBalancePoint point = history[i];\n if (point == null || point.TimestampUtc < cutoffUtc)\n {\n continue;\n }\n\n if (previous != null && point.BalanceCny < previous.BalanceCny)\n {\n used += previous.BalanceCny - point.BalanceCny;\n }\n\n previous = point;\n }\n\n return Math.Max(0.0, used);\n }\n\n private static void SaveHistoryLocked()\n {\n try\n {\n Directory.CreateDirectory(Logger.DirectoryPath);\n StringBuilder builder = new StringBuilder();\n for (int i = 0; i < history.Count; i++)\n {\n DeepSeekBalancePoint point = history[i];\n if (point == null)\n {\n continue;\n }\n\n builder.Append(\"{\\\"schema_version\\\":1,\\\"timestamp_utc\\\":\\\"\");\n builder.Append(point.TimestampUtc.ToUniversalTime().ToString(\"O\", CultureInfo.InvariantCulture));\n builder.Append(\"\\\",\\\"balance_cny\\\":\");\n builder.Append(point.BalanceCny.ToString(\"0.####\", CultureInfo.InvariantCulture));\n builder.AppendLine(\"}\");\n }\n\n File.WriteAllText(HistoryPath, builder.ToString(), Encoding.UTF8);\n }\n catch\n {\n }\n }\n\n private sealed class DeepSeekBalancePoint\n {\n public DateTime TimestampUtc { get; set; }\n public double BalanceCny { get; set; }\n }\n}\n\ninternal sealed class ColorlessDeepSeekAlert\n{\n public ColorlessDeepSeekAlert(string key, string name, string reason, DeepSeekBalanceSnapshot snapshot)\n {\n this.Key = key ?? string.Empty;\n this.Name = name ?? string.Empty;\n this.Reason = reason ?? string.Empty;\n this.Snapshot = snapshot == null ? DeepSeekBalanceSnapshot.CreateDefault() : snapshot.Clone();\n }\n\n public string Key { get; private set; }\n public string Name { get; private set; }\n public string Reason { get; private set; }\n public DeepSeekBalanceSnapshot Snapshot { get; private set; }\n}\n"}}}}