21 KiB
Reference Settings — full reverse-engineer (2026-06-23)
Archived on 2026-07-13. This reverse-engineering snapshot is provenance, not the current Settings contract.
Cross-checked by 4 parallel Explore agents on the extracted bundle at
/tmp/qoder-asar/out/renderer/assets/.
This note captures the reference's Settings module so future Maka PRs can align without re-doing the dig. Naming hygiene applies: do not surface the reference product name in code, commits, or end-user copy.
1. Shell shape
Settings is a full-pane modal that takes over the workspace area. It is NOT a small overlay sheet.
role="dialog"+aria-modal="true"- Marker attributes:
data-modal="agents-settings",data-canvas-dialog="true",data-settings-layout="page" - Outer shell:
flex h-full min-h-0 w-full flex-col overflow-hidden bg-background select-none - The rest of the chat surface stays visible behind a subtle backdrop; the modal does not float over a blurred mask — it occupies the canvas.
Two-rail layout
| Region | Width / sizing | Notes |
|---|---|---|
| Nav rail | w-[256px] shrink-0 |
Left side, bg-transparent, full height |
| Content | flex-1 min-w-0 overflow-hidden |
Right side, scrolls on the inner column |
| Gap | var(--agents-content-area-gap) 4px |
Margin between rail and content plate |
Content pane class: agents-settings-wide-content:
.agents-settings-wide-content {
background: var(--agents-content-area-bg);
margin: var(--agents-content-area-gap)
var(--agents-content-area-gap)
var(--agents-content-area-gap) 0;
border-radius: var(--agents-content-area-radius); /* 6px */
}
Content body inner wrapper (where the page lives):
mx-auto min-h-[200px] max-w-3xl pb-16 pt-10
max-w-3xl = 48rem / 768px. That's the optical reading column for any
settings page body.
Responsive
| Breakpoint | Behavior |
|---|---|
| ≥ 1200px | Two-rail (256px rail + content) |
| 900–1199 | Two-rail, tightened |
| < 900px | Compact: rail collapses; header shows back-arrow + title |
2. Nav rail
The rail is a flat vertical list (no group headings — purely flat).
Items shown to the end user (in order)
preferences— Preferences (the big aggregator)profile— Profile / accountsystem— System (intentionally near-empty placeholder)keyboard— Keyboard shortcutsvm— Secure workspace / VM modeexperimental— Experimental flagspermissions— System capability togglesconnector— Integrations / connectorsappshot— App snapshot (macOS only, gated byplatforms: ["darwin"])
Hidden / dev-only tabs
appearance (handled via the Preferences page itself), privacy,
debug, hiddenConnector, awareness, appUpdate, models,
customModels, commands, agents, worktrees, quickpick,
voiceInput, wechat, beta, channels, legokit, archived — all
exposed: false.
Tab registry lives in index-C02cm0ok.js around offset ~213400 (look
for Su=[]).
Nav-row recipe
<!-- inactive -->
<button class="squircle-md text-text-secondary/80
hover:bg-[var(--settings-nav-row-selected-bg)]
hover:text-text/80">
<Icon size=… />
<span>Label</span>
</button>
<!-- active -->
<button class="squircle-md
bg-[var(--settings-nav-row-selected-bg)]
text-text/80">
…
</button>
- Icon library: Lucide
- Active state is a fill (not border / left-bar), with
--settings-nav-row-selected-bg=var(--color-fill-secondary) - Typography: text-sm (14px), normal weight at rest; no weight bump on active (color/background carry the state)
- Padding (educated from sibling rows):
px-4 py-3 - Gap between glyph + label:
gap-3 - Border radius:
squircle-md(custom corner, not pure rounded-md)
3. Per-tab content inventory
3.1 Preferences (the kitchen sink)
Globals offset ~76380–80000. This single tab carries the bulk of the end-user controls:
| Control | Type | Notes |
|---|---|---|
| Language | select | UI locale |
| Extended Thinking | switch | Bigger reasoning budget, disables streaming |
| Launch at Login (autoLaunch) | switch | Auto-start |
| Close Window Behavior | radio / select | Ask / Minimize-to-tray / Quit |
| Desktop Notifications | switch | Native OS notifications |
| Sound Notifications | switch | Audio on task completion |
| Include Co-Authored-By | switch | Adds Co-authored-by: trailer to git commits |
| Quick Switch (Ctrl/Cmd+Tab) | radio | Workspaces vs Agents |
| Auto-advance after archive | radio | Next / Prev / Close |
| Default Mode (new agent) | radio | Agent / Plan |
| Preview Mode (artifactPreview) | radio | New window / Right sidebar |
| Expand tool calls by default | switch | Tool-block collapsed vs expanded |
| Show tool steps in IM channels | switch | Whether bot-reply surfaces include tool-call sub-steps |
| Tool execution limit | number input | Soft pause when one turn exceeds N tool calls |
| Keep System Awake (preventSleep) | switch | While Agent works |
| Network Proxy | proxy block | System / Manual; URL input + test button + scheme validation |
| Chat Settings (sub-section) | composite | Theme brightness, interface style, glass effects, font face, font size, chat width |
Key insight: reference does NOT split "system-level" toggles (autoLaunch / preventSleep / notifications / proxy / closeWindowAction) into a separate System tab. They live alongside chat/agent toggles in ONE long Preferences page.
3.2 System
Title only. No fields. Placeholder for future use. The page exists in the nav but its body is intentionally empty / "to be defined." Confirmed by walking the bundle.
3.3 Profile
Two sections:
- Avatar: glyph (first char or emoji) + background color (preset palette of ~11 tones)
- Account & Subscription: email (copy), Account UID (copy), product UUID (copy), subscription tier (read-only), pricing-page link, logout
3.4 Appearance (appearance i18n key — actually rendered inside
Preferences in production)
| Control | Type | Options |
|---|---|---|
| Interface theme | select | System pref / Light / Dark / Light Glass / Light Clear (win) / Dark Glass / Dark Clear (win) / Classic Dark / Parchment Light / Parchment Dark / From editors |
| Light variant | select | Per-mode theme |
| Dark variant | select | Per-mode theme |
| Workspace icon in sidebar | switch | Project glyph in the sidebar workspace list |
| Always expand to-do list | switch | Full vs compact to-do view |
3.5 Keyboard
- Search field across all shortcuts
- Per-action row with primary + alternative binding
- "Reset all to defaults"
- Categories: General / Chats (workspaces) / Agents
- General: Show shortcuts / Open settings / Toggle sidebar / Undo archive
- Chats: Quick switch
- Agents: Create new task / Search all tasks / Search in current task / Prev tab / Next tab / Focus input / Toggle input focus / Send / New line
3.6 VM (Secure workspace)
Brief description: "dedicated space on your computer for running tasks — faster, more reliable, on-device." Enable/disable toggle.
3.7 Experimental
Beta feature toggles. Per-flag rows; roster managed in the renderer component, not visible as a literal array in the bundle.
3.8 Permissions
"System permissions QoderWork needs on this Mac." Capability rows (filesystem / network / mic / screen recording / accessibility) with "granted / not granted" status and a "request" CTA.
3.9 Connector
Integrations with external apps. Builtin connectors / market connectors / custom connectors. Each row: brand glyph, status, enable toggle, "configure" button.
3.10 App Snapshot
macOS-only. Frontmost-app screenshot capture for chat context. Needs Accessibility + Screen Recording perms. Permission status row + capture preview.
4. Repeating visual patterns inside a page
Reference does NOT use bordered card containers around groups of
controls. The default presentation is a flat row list inside the
max-w-3xl column.
4.0 Content-column padding — WAWQAQ msg 2c810f2d 2026-06-23
WAWQAQ called out the left/right whitespace as visibly wider in reference than in Maka. Concrete numbers from a second RE pass:
- The content column is
mx-auto max-w-3xl pt-10 pb-16(= 768px wide, 40px top, 64px bottom). No explicitpx-Nis applied inside the column — the side whitespace comes from the column NOT filling the right pane. - At the typical desktop window width (~1280px viewport, 256px nav rail), the right pane is ~1024px. A 768px centered column leaves ~128px of whitespace on each side. That's the "wider padding" effect.
- Inside each row, padding is
px-5 py-4(20px / 16px). Rows do NOT carry their own border, background plate, or shadow — separation is pure spacing.
Maka delta found 2026-06-23:
.settingsStructuredPagehadgap: 16pxbut NOmax-width/margin: 0 auto— rows stretched to fill the right pane. Fixed tomax-width: 768px; margin: 0 auto; padding: 40px 24px 64px..settingsFormRowhadborder-bottom: 1px solid var(--border)andpadding: 6px 0— both wrong. Reference uses spacing-only andpx-5 py-4. Fixed topadding: 16px 20px, no border-bottom..settingsRowwas sharing.providerCardchrome (bordered, tinted, lifted). Provider cards keep their chrome (they ARE cards);.settingsRowflattened to a flex row.- Label was 12px / 600 weight; bumped to 14px / 500. Sub-text 11px → 12px. Matches reference
text-sm font-medium+text-xs text-text-quaternary.
4.1 Row
flex items-center gap-3 px-4 py-3 /* group */
├── label column
│ ├── truncate text-sm font-medium text-text (title)
│ └── mt-0.5 truncate text-xs text-text-quaternary (hint)
└── control (right-aligned)
- No border / no card background per row by default
- Visual separation between rows is achieved by spacing alone (no border-bottom)
- Hover state moves via the parent
.group(e.g., shows a kebab menu)
4.2 Sub-section heading inside a tab
text-sm font-semibold text-text
mb-4
4.3 Switch
- ~20×20px wrapper,
rounded-md - On:
bg-primary/20 ring-1 ring-primary/40 scale-[0.97] - Off: subtle gray fill (
bg-gray-100 dark:bg-neutral-600) with inset shadow - Transition:
transition-all duration-200
4.4 Input
w-full px-3 py-2 text-sm bg-background border rounded-md
border-border-tertiary/80
focus:outline-none focus:ring-2 focus:ring-primary/50
- Error:
border-error-border - Password / icon suffix:
pr-9
4.5 Button
- Primary:
bg-primary text-primary-foreground - Secondary:
bg-muted text-foreground - Outline:
border border-border text-foreground hover:bg-muted - Destructive:
text-error hover:bg-error-bg - Icon-only sizes:
size-7 rounded-md(28px square),size-8 rounded-full(32 round)
4.6 Chip / badge
rounded bg-fill-tertiary px-1.5 py-0.5 text-[10px] font-medium text-text-tertiary
4.7 Content scroll
The page body owns its scroll:
min-h-0 flex-1 overflow-y-auto scrollbar-none
Custom scrollbar is hidden. The inner content column has
pb-16 bottom padding so the last row never hugs the modal edge.
5. Tokens used inside Settings
| Token | Value |
|---|---|
--agents-content-area-bg |
var(--color-bg-container) |
--agents-content-area-gap |
4px |
--agents-content-area-radius |
12px |
--agents-layout-bg |
var(--surface-canvas) (flat neutral shell — DO NOT paint a 172deg gradient here, see warning below) |
--settings-nav-row-selected-bg |
var(--color-fill-secondary) |
⚠️ DO NOT paint a 172deg gradient on the layout/shell background.
The RE notes here historically said
--agents-layout-bgislinear-gradient(172deg, var(--color-fill-tertiary) 19.61%, var(--color-bg-container) 81.35%). That value was an artifact of the bundle CSS we copied, not what the live reference product actually paints. WAWQAQ has called this out four times in a row (msgs1e693dee/5d3b10e5/486b5611/4a1b8c13) — "谁让你他妈的用渐变的啊? 参考实现就没有啊". Every time we re-added the gradient because some atlas/RE note claimed it was canon.Source-side enforcement:
apps/desktop/src/main/__tests__/chat-chrome-no-gradient-contract.test.tsfails if any of.appFrame,html[data-os="darwin"] .maka-session-panel, or the floating panel reintroduces a gradient / border-right / smaller-than-12px radius. If a future RE iteration finds a new place where the reference product genuinely does paint a gradient, please update the contract test with the new exception in the same PR, don't silently lift the assertion.Use a flat neutral shell (
var(--surface-canvas)) behind the white content surface. Do not collapse the shell to the same white as the content card: that removes the visible lower radius and lets the bug come back even without a border or gradient.Rendered-pixel enforcement: after capturing
sidebar-long-sessions/light-1280-motion, runnpm --workspace @maka/desktop run screenshots:chat-chrome:check. It verifies the actual PNG has a visible shell/content delta, no dark one-pixel seam, and readable bottom surface corners.Surface radius is 12px (was historically noted as 6px). 6px was geometrically there but optically invisible — bottom corners of the floating content card didn't read against a same-color shell.
Color tokens consumed inside settings:
--color-text(primary text)--color-text-secondary--color-text-tertiary(hints)--color-text-quaternary(de-emphasized)--color-border(strong divider)--color-border-tertiary(input borders)--color-fill-secondary(active row fill)--color-fill-tertiary(chip background)--color-bg-container(page surface)--color-primary,--color-primary/20,--color-primary/40(focus, switch on, ring)
6. Routing, state, behaviors
- Active tab is stored in the URL:
?view=settings&tab=preferences. No localStorage for the active tab — round-tripping the URL restores the page. - Deeplink scheme: external triggers can deep-link to a specific tab:
qoder-work://settings/preferences. Maka's equivalent could bemaka://settings/<tab>. - Default tab when opened with no
tabparam:preferences. - Modal close is escape-key + click-outside backdrop; focus is trapped
inside
[role="dialog"]. - Telemetry:
settings_tab_viewfires when a tab is selected;settings_heartbeatticks every 300s with a feature-flag snapshot. - Saves: per-control, no explicit batch / "save" button visible. Renderer dispatches IPC writes per change; toasts surface failures.
7. Implications for Maka
This RE matters because WAWQAQ's ask — "add a 系统设置 section" — is ALMOST CORRECT but the deeper truth from the reference is:
Reference does NOT scatter system-level toggles into their own
narrow pages. It collapses them into ONE long Preferences page,
and reserves a near-empty system tab for future expansion.
Maka today has 17 nav items in Settings (some of them — 网络,
健康, 开放网关, 语音模型, 每日回顾, 记忆 — are single-purpose
pages that mirror exactly the "wasteful single-purpose page" pattern
WAWQAQ flagged for 网络).
The closer-to-reference move is to consolidate, not add another section. Concrete proposal:
Proposed consolidated Maka Settings nav (target: ~9 items)
| Group | Item | Holds |
|---|---|---|
| 基础 | 通用 | Aggregator: 隐身模式, 启动行为, 新对话模式, 默认模型, 通知, 网络代理, 保持唤醒, 自动更新, 关闭窗口行为 |
| 基础 | 外观 | 个性化 + theme + palette + font (merge 主题 + 个性化) |
| AI | 模型 | Provider connections + OAuth + per-model config |
| AI | 记忆与回顾 | Merge 记忆 + 每日回顾 |
| AI | 语音与网关 | Merge 语音模型 + 开放网关 |
| 集成 | 机器人对话 | Telegram / WeChat / Slack bot config |
| 集成 | 联网搜索 | Tavily provider config |
| 数据/系统 | 数据 | Export / import / wipe |
| 数据/系统 | 权限与能力 | Capability toggles + filesystem allowlist |
| 其他 | 健康 | Diagnostic / status / logs |
| 其他 | 关于 | Version + license + acknowledgements |
Specifically: drop the standalone 网络 page; move proxy into 通用.
That's the minimum WAWQAQ asked for, but ride the same PR to merge
主题 + 个性化 into 外观 per reference, since 外观 is reference's
canonical name for that grouping.
Settings page-body recipe to copy
For ANY settings page body, switch to:
max-w-3xl mx-auto pt-10 pb-16inner column- Flat row list (
flex items-center gap-3 px-4 py-3per row) text-sm font-mediumtitle +text-xs text-text-quaternaryhint- No bordered card around row groups
- Sub-section heading:
text-sm font-semibold mb-4 - Switches / inputs / chips per §4 above
This is the visual "reference 真好看" pattern WAWQAQ asked us to study — the page is uncluttered because rows have NO chrome, only spacing.
8. PR plan suggestion
Two PRs, ship in sequence:
PR-SETTINGS-ALIGN-IA-0 — Information architecture consolidation
- Drop standalone
网络page; fold proxy into a "网络代理" sub-section of 通用 - Merge
主题 + 个性化→外观 - Merge
记忆 + 每日回顾→记忆与回顾 - Merge
语音模型 + 开放网关→语音与网关 - Update SETTINGS_NAV + section render switch + all deeplinks (
maka://settings/...) - Pin via contract test: nav id allowlist + deeplink mapping
PR-SETTINGS-PAGE-BODY-0 — Visual pattern conversion
- Convert every page body from
settingsStructuredPage / SettingRowcard chrome to the flat-row +max-w-3xlrecipe - Add
.maka-settings-row,.maka-settings-pageprimitives in styles.css - Sweep all SettingsPage components to use them
Tackle them in this order so the IA stops shifting while we're polishing the bodies.