Document Runtime v2 architecture evolution - #4
Merged
likun666661 merged 5 commits intoJun 14, 2026
Conversation
Add Runtime v2 implementation skeleton
Fix Runtime v2 runner and flow contracts
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
…llback source label UI side of the Z.ai-only-shows-glm-4.x bug @WAWQAQ flagged. Backend patch lives with @xuan (`packages/runtime/src/model-fetcher.ts` — remove silent fallback catch, throw generalizedErrorMessage instead). This commit makes the renderer: 1. **Auto-fetch after save** — when a connection is saved with a new secret (or the model list is still empty), `save()` kicks `refreshModels({ silent: true })` so the user doesn't land on a row whose dropdown only contains the static fallback list (e.g. Z.ai → just glm-4.7 / 4.6 / 4.5). Closes the discoverability gap. 2. **Toast on refresh failure** — `refreshModels()` now catches and surfaces a structured toast: title "拉取模型失败 · {name}", body "{generalizedErrorMessage} · 当前继续显示静态列表,请确认 API key / Base URL / 代理设置后重试。" Manual refresh also gets a success toast on the count of fetched models so the user sees the action landed. 3. **Fetched-vs-fallback source label** — `.providerModelSource` line under the dropdown explicitly says which list is shown. Fetched → success tone "实时拉取的 N 个模型(最新一次成功)". Fallback → info tone "静态备用列表(N 项)。点「从 API 刷新」拉取该 provider 的 真实模型清单。" Per @kenji's status contract item #4: fetched vs fallback must never look the same. Typecheck + build green.
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
…doc-merge) 1. **iframe sandbox/CSP language tightened** (@kenji #1): - Remove the "separate webContents partition + permissive CSP for iframe" promise — DOM `<iframe>` doesn't get an independent partition without `<webview>` / BrowserView. Don't promise what the DOM can't deliver. - Add `allow-forms` and `allow-modals` to the not-allowed list - Switch from `src="file://..."` to `srcdoc={await readText()}` so content delivery goes through the path-guard read helper - Note that global CSP `default-src 'self'` stays locked — sandbox attribute carries all isolation 2. **Symlink-aware path guard** (@kenji #2): - Both `realpath(artifactRoot)` AND `realpath(target)` required, then containment check - Mirror PR56 open-path-guard which already has symlink-escape + `..` + URL-scheme test coverage 3. **readBinary MIME allow-list** (@kenji #3): - Allowlist: image/png/jpeg/gif/webp/svg+xml, application/pdf - MIME sniffed at read time, NOT trusted from record.kind or record.mimeType - Unknown → `reason: 'unsupported_mime'` + UI shows metadata + "在 Finder 中打开" / "另存为" actions (no raw bytes to renderer) 4. **Deleted tombstone blocks reads** (@kenji #4): - `status: 'deleted'` records immediately return `reason: 'deleted'` from readText / readBinary — even if the underlying file still exists on disk - Surface table updated: deleted row preview shows "此 artifact 已 删除,预览已停止" - Permanent path purge is a separate Settings · 数据 flow 5. **PR108c runtime hook scope** (@kenji #5): - First version does NOT do LLM extractor - Only deterministic outputs: Write→file record, Edit→diff record, Bash with explicit redirect → file record - stdout/stderr NOT auto-promoted (noise) - LLM extractor (parse ```html``` from assistant message) deferred to PR109+ Also: type rename `storagePath` → `relativePath` to enforce contract in the type system; full reason union widened on both read helpers. doc total: 1416 lines.
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
Earlier §9.1.5 #4 promised that iframe `<a href>` clicks would route through PR96 `setWindowOpenHandler` → `shell.openExternal`. That was documentation incorrect — per @kenji's review: without `allow-popups` in the sandbox attribute, iframe link clicks don't bubble up to the parent renderer's window-open handler. They silently fail or get blocked by the sandbox. Replaces #4 with the right MVP shape: - iframe sandbox blocks ALL navigation by default — keep that as the contract - preview top status bar shows "此预览中已禁用外部链接 · {N} 个链接" (count of <a href> in srcdoc), so users aren't confused why clicks do nothing - Users who need to follow links use "在 Finder 中打开" from the artifact toolbar and open the source file in their browser Future enhancements (NOT PR108b): - HTML transform: rewrite <a href> → <button data-maka-link> + inline postMessage script, main renderer is the only shell.openExternal gateway - Or: postMessage allowlist for iframe ↔ main renderer Safety-first MVP: HTML can be VIEWED, not NAVIGATED. Cheap UX sacrifice, clear trust boundary.
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
Consumes the turn-level contract from PR109c. Implements two of @kenji's remaining PR109d review gates: **(d) Failed turn AlertOctagon banner** (@kenji gate #3) TurnView renders a destructive-tone banner above the assistant body when `turn.status === 'failed'`. Distinct from PR58 tool-error banner because it's turn-scoped (the whole turn failed, not just a tool). Copy is generalized Chinese phrase translated from `turn.errorClass` via new pure helper `describeTurnErrorClass()` in `session-status-presentation.ts`: timeout → 请求超时 auth / 401 / 403 → 鉴权失败 rate_limit / rate_* → 触发模型速率限制 network / fetch / econn → 网络错误 provider_unavailable / 5xx → 模型服务暂不可用 tool_failed → 工具调用失败 permission_required → 等待权限确认 (unknown / empty) → 未知错误 10 node:test cases lock the mapping invariant: never leak raw enum identifier, always Chinese, case-insensitive, fallback to "未知错误". **(e) Lineage badges** (@kenji gate #4) - Forward badges sit at the top of the new turn: "重试自 turn ${shortId}" / "重新生成自 turn ${shortId}". Source: `turn.retriedFromTurnId` / `turn.regeneratedFromTurnId` on the turn record itself. - Reverse badges sit at the bottom (above the footer actions): "已重试 → turn ${shortId}" / "已重新生成 → turn ${shortId}". Source: derived UI-side via existing `deriveTurnLineageMap()` — NOT persisted in storage (@kenji review on PR109c kept this side immutable). - Both directions share the same TurnLineageBadge type; UI distinguishes via `data-direction="forward"|"reverse"` for tone (forward = info-tinted, reverse = success-tinted). - Click handler scrolls the target turn into view via `data-turn-id` attribute querySelector (smooth, block center). Files: - `packages/ui/src/components.tsx`: - `TurnView` gains `failedReasonLabel` + `lineageBadges` + `onLineageBadgeClick` props - New `TurnLineageBadge` exported interface - Renders failed banner + forward/reverse badge rows - `ChatView` gains `turnFailedReasonLabels` + `turnLineageBadgesByTurn` + `onLineageBadgeClick` props - `apps/desktop/src/renderer/main.tsx`: - Consolidates `turnFooterActionsByTurn` + `turnFailedReasonLabels` + `turnLineageBadgesByTurn` into a single useMemo (one pass over materialized turns) - `handleLineageBadgeClick()` scrolls target turn via querySelector - `apps/desktop/src/renderer/session-status-presentation.ts` — new `describeTurnErrorClass()` helper - `apps/desktop/src/renderer/styles.css` — `.maka-turn-failed-banner` (destructive), `.maka-turn-lineage-row` + `.maka-turn-lineage-badge` (info-tinted forward, brand-deep-tinted reverse) Per @kenji review (PR109e thread): lineage badges only do scroll/link — no IPC, no pending mask needed. If a future "retry from badge" shortcut is added, it MUST reuse the same `pendingTurnActions` channel from PR109d. Tests: 234 → 243 desktop passing (+9 describeTurnErrorClass). check-a11y 0 violations. Deferred to PR109f: - Branched session banner ("分自 ${parentSessionName}" + aborted starting-point copy "从中断前分支") - `turn-control-history` fixture + smoke Path 15
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
…inese-first labels, stub views for non-implemented modules Implements the IA refactor WAWQAQ locked in msg `b86b47d1`: the sidebar's second part is now a top-level 5-module nav (会话 / 搜索 / 计划 / 技能 / 每日回顾) rather than session filter tabs. Pinned / Archived / Chats become internal Sessions filters. Settings stays at the bottom; a placeholder 版本更新 button sits above it. Per xuan `47e204f2` priorities applied directly: - #2 IA: top-level modules; filters moved into Sessions. - #4 visual hierarchy: nav rows reuse `.maka-nav-row` (transparent bg, accent-tinted selected state, no card stacking). - #5 Chinese-first naming throughout the surface; English stays as accessible keywords in the command palette. - #7 Search nav item placeholder for Phase 4 modal; clicking now switches the section to `'search'` and renders an empty-state stub. Phase 4 will swap the section switch for an `onOpenSearchModal()` callback (already plumbed through props). - #8 disabled / hover / focus states pinned for the Update placeholder (aria-disabled + data-disabled + CSS dim). == Type-level changes == `packages/ui/src/components.tsx`: export type NavSelection = | { section: 'sessions'; filter: SessionFilter } | { section: 'search' } | { section: 'automations' } | { section: 'skills' } | { section: 'daily-review' }; // New module label map (Chinese-first): const MODULE_NAV_LABEL: Record<NavSelection['section'], string> = { sessions: '会话', search: '搜索', automations: '计划', skills: '技能', 'daily-review': '每日回顾', }; == SessionListPanel JSX shape == <aside> <header>新建对话 button</header> <nav.maka-sidebar-modules> 5 module nav rows (NEW) <div.maka-session-filter hidden={!sessions}> Pinned/Chats/Archived <div.maka-session-search hidden={!sessions}> session-name search <section.maka-session-list> ├ skills view (existing, reused for `selection.section==='skills'`) ├ sessions view (existing, when 'sessions') └ STUB_VIEWS[section] (search / automations / daily-review) <footer.maka-session-panel-footer> ├ 版本更新 button (NEW; UI placeholder, aria-disabled by default) └ 设置 button (existing; renamed from "Settings") </aside> Skills no longer appears in the footer — it lives in the top-level module nav alongside the other 4. The chat surface (`ChatPanel`) shows a neutral "从 会话 选择对话" message when the active section is `search` / `automations` / `daily-review`. == Persistence == `apps/desktop/src/renderer/main.tsx readNavSelection`: recognizes `search` / `automations` / `daily-review` as valid section variants when restoring from localStorage. Corrupted entries fall back to `{ section: 'sessions', filter: 'chats' }`. == CSS == `apps/desktop/src/renderer/styles.css`: .maka-session-panel grid-template-rows: auto auto auto auto minmax(0, 1fr) auto; /* Row 2 is the new module nav. Phase 1's `.maka-session-list` grid * fix (auto + minmax(0, 1fr)) and `.maka-list-stack` overflow:auto * are preserved verbatim. */ .maka-sidebar-modules { display: grid; gap: 2px; padding: 4px 8px; } /* Disabled nav-row dimming (used by 版本更新 placeholder). */ .maka-nav-row[aria-disabled="true"], .maka-nav-row[data-disabled="true"] { cursor: not-allowed; opacity: 0.55; } .maka-nav-row[aria-disabled="true"]:hover, .maka-nav-row[data-disabled="true"]:hover { background: transparent; } == Tests == core 392 / storage 50 / runtime 103 / desktop 645 = 1190 pass. Typecheck + build green across all workspaces. Phase 1's CSS contract test still passes (grid + min-height + overflow pins survive the IA refactor). == Out of scope (Phase 3 / 4) == - Session row slimming (~85px → ~32px). Phase 3. - Search modal implementation. Phase 4 (resurrects `useThreadSearch` from `yuejing/pr-search-2.6`; swaps current Search stub for modal trigger). - Real auto-update wiring (Electron updater). Future PR-AUTOUPDATE-0. - Automations / Daily Review feature implementation. Future PR-TIME-* / PR-DAILY-*. Anchors: WAWQAQ msg `b86b47d1` + `f5f6f834` (IA + Skills inclusion + Update button). xuan msg `47e204f2` (8-point priority list); `dc790a54` (Phase 1 scope refinement); `adcf0c95` (per-phase gate workflow). kenji msg `fcd9c54f` (consolidated packet design); `0f7bb872` (scroll P0 architecture). Branch base: `655a09f` (Phase 1 + fixup, on top of main `809875a`).
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
…l + 版本更新 passive (4 nits + capability state CSS) WAWQAQ msg `1893c08e` 没动静 → starting PR-UX-POLISH-1 in parallel with kenji's PR-DESKTOP-SMOKE-0 instead of waiting on the Sidebar resize verdict. Commit lands on top of `yuejing/pr-sidebar-ia-0` so it merges as a stack once Sidebar is greenlit. Per ratified scope (yuejing UX audit msg `9c779b56` + xuan + kenji boundary 1 closed-state enum): 1. Filter input shortcut hint corrected: `筛选会话… F 聚焦` → `筛选会话… ⌘F 聚焦` The actual binding is ⌘F (Meta+F) via the existing `useEffect` in `SessionListPanel`; the lone `F` placeholder was misdocumenting the chord. 2. Composer streaming hint disambiguated from reasoning stream: `Maka 正在思考…` → `Maka 正在回答…` (zh) `Maka is thinking…` → `Maka is responding…` (en) ReasoningPanel still uses `正在思考…` / `Thinking…` for the model's extended-thinking stream. Distinct signals now have distinct copy — composer = output-streaming; ReasoningPanel = reasoning-streaming. 3. Sidebar group label visual gap: Without `display: flex; gap` on `.maka-list-group-label`, the `<span>label</span><span>count</span>` rendered as `会话65` (no separator). Added `display: flex; align-items: baseline; gap: 6px` so the rendered form is `会话 65` (or with future bullet separator: `会话 · 65`). 4. 版本更新 footer entry switched from aria-disabled fake-button to passive `data-state="coming_soon"` informational tag with small `即将推出` badge. Removes the "looks tappable but does nothing" affordance. When `onOpenUpdate` IS wired (future PR-AUTOUPDATE-0), the button form returns automatically. This is the first integration of the capability presentation enum (kenji boundary 1, msg `88a9f29d`): UI consumes the state via props, never derives `operational` from token/config presence. CSS adds `.maka-nav-row[data-state="coming_soon"]` styling + `.maka-nav-row-state-badge` pill for the "即将推出" chip. The closed enum (`available / coming_soon / needs_config / blocked_by_permission / disabled_by_policy / unsupported_platform`) is documented in `notes/pr-ux-polish-1-gate.md`. Eyebrow icon variety (audit P2 #5) skipped — re-examined source and setup heroes already use varied icons (SettingsIcon, KeyRound, Cpu, AlertCircle via SetupHero); only NeedsConnectionHero and ReadyEmptyHero still use Sparkles which is correct for those states. Stale pill tone (audit P1 #4) skipped — already amber-toned (`--warning` token at 0.18 alpha + warning-text); the only weak signal is the 0.7 opacity row dim, which is the intentional secondary signal. Gates: - typecheck clean, UI + renderer build clean - desktop tests: 643 pass / 0 fail (no test additions yet — capability enum contract test arrives in commit 2 alongside the search-service rename + daily-review consolidation + permission-center cleanup + modal backdrop token unification) - core tests: 392 pass Subsequent commits in this PR (commit 2+): - Permission Center "即将可用" hide / change to passive copy - Settings · 搜索服务 → 联网搜索 rename - Daily Review Settings sub-page consolidation - Modal backdrop token unification - `? 快捷键` sidebar chip (small a11y discoverability add) - Extend visible-copy-hygiene-contract with capability enum presentation gate (no `coming_soon` should render as <button> without explicit `data-state="coming_soon"`)
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
…doc-merge) 1. **iframe sandbox/CSP language tightened** (@kenji #1): - Remove the "separate webContents partition + permissive CSP for iframe" promise — DOM `<iframe>` doesn't get an independent partition without `<webview>` / BrowserView. Don't promise what the DOM can't deliver. - Add `allow-forms` and `allow-modals` to the not-allowed list - Switch from `src="file://..."` to `srcdoc={await readText()}` so content delivery goes through the path-guard read helper - Note that global CSP `default-src 'self'` stays locked — sandbox attribute carries all isolation 2. **Symlink-aware path guard** (@kenji #2): - Both `realpath(artifactRoot)` AND `realpath(target)` required, then containment check - Mirror PR56 open-path-guard which already has symlink-escape + `..` + URL-scheme test coverage 3. **readBinary MIME allow-list** (@kenji #3): - Allowlist: image/png/jpeg/gif/webp/svg+xml, application/pdf - MIME sniffed at read time, NOT trusted from record.kind or record.mimeType - Unknown → `reason: 'unsupported_mime'` + UI shows metadata + "在 Finder 中打开" / "另存为" actions (no raw bytes to renderer) 4. **Deleted tombstone blocks reads** (@kenji #4): - `status: 'deleted'` records immediately return `reason: 'deleted'` from readText / readBinary — even if the underlying file still exists on disk - Surface table updated: deleted row preview shows "此 artifact 已 删除,预览已停止" - Permanent path purge is a separate Settings · 数据 flow 5. **PR108c runtime hook scope** (@kenji #5): - First version does NOT do LLM extractor - Only deterministic outputs: Write→file record, Edit→diff record, Bash with explicit redirect → file record - stdout/stderr NOT auto-promoted (noise) - LLM extractor (parse ```html``` from assistant message) deferred to PR109+ Also: type rename `storagePath` → `relativePath` to enforce contract in the type system; full reason union widened on both read helpers. doc total: 1416 lines.
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
Earlier §9.1.5 #4 promised that iframe `<a href>` clicks would route through PR96 `setWindowOpenHandler` → `shell.openExternal`. That was documentation incorrect — per @kenji's review: without `allow-popups` in the sandbox attribute, iframe link clicks don't bubble up to the parent renderer's window-open handler. They silently fail or get blocked by the sandbox. Replaces #4 with the right MVP shape: - iframe sandbox blocks ALL navigation by default — keep that as the contract - preview top status bar shows "此预览中已禁用外部链接 · {N} 个链接" (count of <a href> in srcdoc), so users aren't confused why clicks do nothing - Users who need to follow links use "在 Finder 中打开" from the artifact toolbar and open the source file in their browser Future enhancements (NOT PR108b): - HTML transform: rewrite <a href> → <button data-maka-link> + inline postMessage script, main renderer is the only shell.openExternal gateway - Or: postMessage allowlist for iframe ↔ main renderer Safety-first MVP: HTML can be VIEWED, not NAVIGATED. Cheap UX sacrifice, clear trust boundary.
jackwener
added a commit
that referenced
this pull request
Jun 21, 2026
Consumes the turn-level contract from PR109c. Implements two of @kenji's remaining PR109d review gates: **(d) Failed turn AlertOctagon banner** (@kenji gate #3) TurnView renders a destructive-tone banner above the assistant body when `turn.status === 'failed'`. Distinct from PR58 tool-error banner because it's turn-scoped (the whole turn failed, not just a tool). Copy is generalized Chinese phrase translated from `turn.errorClass` via new pure helper `describeTurnErrorClass()` in `session-status-presentation.ts`: timeout → 请求超时 auth / 401 / 403 → 鉴权失败 rate_limit / rate_* → 触发模型速率限制 network / fetch / econn → 网络错误 provider_unavailable / 5xx → 模型服务暂不可用 tool_failed → 工具调用失败 permission_required → 等待权限确认 (unknown / empty) → 未知错误 10 node:test cases lock the mapping invariant: never leak raw enum identifier, always Chinese, case-insensitive, fallback to "未知错误". **(e) Lineage badges** (@kenji gate #4) - Forward badges sit at the top of the new turn: "重试自 turn ${shortId}" / "重新生成自 turn ${shortId}". Source: `turn.retriedFromTurnId` / `turn.regeneratedFromTurnId` on the turn record itself. - Reverse badges sit at the bottom (above the footer actions): "已重试 → turn ${shortId}" / "已重新生成 → turn ${shortId}". Source: derived UI-side via existing `deriveTurnLineageMap()` — NOT persisted in storage (@kenji review on PR109c kept this side immutable). - Both directions share the same TurnLineageBadge type; UI distinguishes via `data-direction="forward"|"reverse"` for tone (forward = info-tinted, reverse = success-tinted). - Click handler scrolls the target turn into view via `data-turn-id` attribute querySelector (smooth, block center). Files: - `packages/ui/src/components.tsx`: - `TurnView` gains `failedReasonLabel` + `lineageBadges` + `onLineageBadgeClick` props - New `TurnLineageBadge` exported interface - Renders failed banner + forward/reverse badge rows - `ChatView` gains `turnFailedReasonLabels` + `turnLineageBadgesByTurn` + `onLineageBadgeClick` props - `apps/desktop/src/renderer/main.tsx`: - Consolidates `turnFooterActionsByTurn` + `turnFailedReasonLabels` + `turnLineageBadgesByTurn` into a single useMemo (one pass over materialized turns) - `handleLineageBadgeClick()` scrolls target turn via querySelector - `apps/desktop/src/renderer/session-status-presentation.ts` — new `describeTurnErrorClass()` helper - `apps/desktop/src/renderer/styles.css` — `.maka-turn-failed-banner` (destructive), `.maka-turn-lineage-row` + `.maka-turn-lineage-badge` (info-tinted forward, brand-deep-tinted reverse) Per @kenji review (PR109e thread): lineage badges only do scroll/link — no IPC, no pending mask needed. If a future "retry from badge" shortcut is added, it MUST reuse the same `pendingTurnActions` channel from PR109d. Tests: 234 → 243 desktop passing (+9 describeTurnErrorClass). check-a11y 0 violations. Deferred to PR109f: - Branched session banner ("分自 ${parentSessionName}" + aborted starting-point copy "从中断前分支") - `turn-control-history` fixture + smoke Path 15
This was referenced Jun 24, 2026
jackwener
added a commit
that referenced
this pull request
Jun 24, 2026
…scape hatch (#214) Kenji aesthetic-audit reminder 9 (msg `bacde601` 2026-06-24, findings #2 + #3): two chrome design-system holes still leaking gradients / non-token blur into the app. ## #2 — darwin sidebar footer gradient `html[data-os="darwin"] .maka-session-panel-footer` was setting: `background: linear-gradient(180deg, transparent 0%, oklch(.../0.30) 100%)` The panel above already has a 50% glass base + 8px backdrop blur, so the footer doesn't need any additional material — but the 180deg fade IS the same "上面灰下面白" pattern WAWQAQ called out twice before in chat sweep (`1e693dee` / `5d3b10e5`). Replaced with flat `oklch(...) / 0.15` + hairline border-top. ## #3 — `[data-gradual-blur-layer]` block A 56-line block that let any element opt into: - `linear-gradient(to bottom/top, var(--background) → transparent)` - `backdrop-filter: blur(6px)` - raw `z-index: 5` just by wearing the `data-gradual-blur-layer` attribute. Kenji called it a "blur escape hatch" — a future surface could re- introduce blur and a non-token z-index without going through any review of the design system. `grep -rn data-gradual-blur-layer --include=*.tsx` confirms zero JSX consumers — the CSS is dead. Removing the entire block kills the escape hatch + retires one of the four raw z-index numbers kenji flagged in finding #4. `notes/reference-atlas.md` references stay — those describe what reference upstream does, not what we ship. ## Diff 1 file changed, 18 insertions(+), 60 deletions(-) net delete. No JSX touched. No tests touched (no contract pinned this attr). Footer change is darwin-only so non-darwin platforms unaffected. Far from PR #202 styles.css edit area (onboarding ~1500-1700 + cubic-bezier sweep on transition rules). PR #210 touches main.tsx debounce, not styles.css. No merge conflicts.
jackwener
added a commit
that referenced
this pull request
Jun 24, 2026
Kenji aesthetic-audit reminder 9 (msg `bacde601` 2026-06-24, finding #4): four bare integer z-index sites in styles.css (line 407 z-4, 3858 z-2, 7193 z-35, 14463 z-5) sidestep the `--z-*` token scale defined in maka-tokens.css. PR #214 already took out the gradual-blur-layer site (z-5). This PR closes the remaining three. ## Changes 1. `maka-tokens.css`: add two new tokens preserving the previous raw values exactly: - `--z-panel-action: 4` - `--z-settings-fullpage: 35` 2. `styles.css` line 407 (`.maka-workspace-top-actions`): `z-index: 4` → `var(--z-panel-action)`. Identical stacking. 3. `styles.css` line 7193 (`.settingsModal.settingsPage`): `z-index: 35` → `var(--z-settings-fullpage)`. 35 sits between `--z-titlebar` (40) and `--z-sticky` (20) so the OS titlebar still owns top of the stack. Preserved visual behavior exactly. 4. `styles.css` line 3858 (`.maka-skill-featured-art span:nth-child(2)`): bare `z-index: 2` kept. This is local stacking among 3 decorative sticker spans inside a fixed-size container — they never escape the parent and don't need a global token. Added explicit allowlist comment. 5. New `apps/desktop/src/main/__tests__/z-index-contract.test.ts` enforces: - every bare `z-index: <integer>;` must be tokenized or allowlisted (with a documented reason) - allowlist entries must be present in styles.css (no stale entries) - the 10 semantic `--z-*` tokens stay defined in maka-tokens.css Future PRs that re-introduce a bare z-index will fail this test unless they explicitly bump the allowlist. ## Diff 3 files: maka-tokens.css +7, styles.css +17/-2, new test +~110 lines. No visual change. Far from PR #202 (transition sweep), PR #210 (main.tsx debounce), PR #214 (footer/gradual-blur). No conflict. ## Verification Disk still ~100%, couldn't run `pnpm install && pnpm test`. Contract test is greenfield — first run will validate the 4 sites in this same PR.
jackwener
added a commit
that referenced
this pull request
Jun 24, 2026
…ji audit #3+#4) (#219) * test(contract): lock drawer.tsx + tabs.tsx motion escape hatches (kenji audit #3 + #4) Sibling of PR-FE-BUG-HUNT-12 (which locked ui.tsx). Closes kenji's audit reminder 4-6 findings #3 + #4 (msg `6cc0e04d` 2026-06-24): ## drawer.tsx escape hatches (5) - `cubic-bezier(0.32,0.72,0,1)` ×2 — iOS-style settle curve, raw on backdrop + popup. Should move to `--ease-drawer` token. - `duration-450` ×2 — drawer settle duration. Doesn't match any current `--duration-*` token. Should tokenize. - `transition-[transform,box-shadow,height,background-color]` ×1 — animating `height` is layout-trigger. Intentional because drawer snap points (peek / half / full) drive variable height and `transform: scaleY` would distort children. Layout-property transition is acknowledged here. - `backdrop-blur-sm` ×1 — same finding as kenji #6. - `z-50` ×1 — same convention as ui.tsx; pending tokenization. ## tabs.tsx escape hatches (3) - `transition-[width,translate]` ×1 — animating `width` is layout- trigger. Cleaner refactor is `translate + scaleX` with measured base width, but that needs measurement infrastructure not in place. Layout transition acknowledged. - `duration-200` ×1 — matches `--duration-base` (200ms) by value but uses bare Tailwind utility. - `ease-in-out` ×1 — generic Tailwind easing, not project's `--ease-out-strong`. ## Approach Same as PR-FE-BUG-HUNT-12 (ui.tsx contract): pin EXACT count of each allowlisted pattern. Adding new sites OR removing stale allowlist entries both fail. Plus a "no other layout transitions" sweep that verifies no NEW `transition-[<bracketed-list>]` showed up beyond the allowlisted ones. Not touching source — touches primitive wrappers; each tokenization needs kenji / WAWQAQ review per primitive. Contract locks perimeter. ## Diff 1 new file, ~140 lines test. No source change, no visual change. No overlap with PR-FE-BUG-HUNT-12 (different file scope) or any other open PR. ## Verification Disk still ~100%, couldn't run tests. Greenfield contract is self-validating — first run confirms counts match current file state. The allowlist counts were derived from grep + manual read. * test(self-review): fix wrong occurrence counts in primitives contract Verified counts via `grep -o ... | wc -l` before pushing PR #219 to review and caught my own miscounts: - drawer.tsx `z-50`: 1 → actual 3 (gesture wrapper, backdrop scrim, cushion overlay all stack at z-50) - drawer.tsx had TWO additional `transition-[<bracketed>]` patterns I missed: `transition-[transform,box-shadow,height,background-color, margin,padding]` (drawer popup bottom-edge — worse layout trigger than the base, adds margin+padding) and `transition-[background- color,box-shadow]` (drawer-internal switch handle — paint-only, safe) - tabs.tsx had ONE additional `transition-[color,background-color, box-shadow]` pattern (tabs trigger paint transition — paint-only, safe) All five additions added to the allowlist with explicit reason. The `no unexpected transition-[<bracketed>]` sweep rewritten to subtract the full allowlist from the found set (set-difference) instead of naively iterating, so it doesn't false-fail when there are multiple paint-only patterns. Pre-push count verification is now baked into the test contract itself — if a future maintainer changes a count without re-running, the test will fail loudly.
jackwener
added a commit
that referenced
this pull request
Jun 25, 2026
Per @kenji audit `26a221be` against #237 main `4e0247f6`: **#1 — bottom 「想先看看效果?」 hero buttons left-aligned.** PR #236 right-aligned the top 「打开每日回顾」 button via `.settingsFeatureStatusHeroActions` but the bottom 「生成每日回顾 / 生成深度分析」 row still used inline `style={{ display: 'flex', gap: 8, marginTop: 8 }}`, so it stayed glued to the paragraph margin. Route the bottom row through the same class. Add `gap: 8px` to the class so multi-button clusters keep proper spacing. **#2 — 执行时间 disabled when the master switch is off.** The disable condition was `formDisabled || savingKey === 'executeTime' || !(effectiveConfig?.enabled ?? false)`. That last clause blocked the common UX of "pick a time first, then turn it on". Drop it — the time value is harmless to edit while disabled (it doesn't fire until the switch flips), and the disabled state was misread as "broken UI". **#3 — 分析模型 default-row label still verbose.** WAWQAQ's directive: "所有模型选择只用模型名". PR #236 went from `使用对话默认模型(Codex OAuth · email · gpt-5.5)` to `对话默认(gpt-5.5)` — better, but the model id parenthetical still mixes "selectable model" with "follow chat default". Per @kenji's "don't conflate the two semantics", the default row becomes plain `跟随对话默认`; the explicit options carry the model id. **#4 — duplicate model ids across connections looked unselectable.** When two enabled connections both expose `gpt-5.5`, the flat `model`-only labels read as two identical entries. Detect the collision at build time and append `· ${connection.name}` ONLY to the colliding entries; unique model ids stay terse per WAWQAQ's directive. Deferred (with @kenji concurrence): - **#5** (`data-control-width` semantic API replacing CSS-selector width patches) — pure refactor, no end-result change. WAWQAQ explicitly said 「只看最后的结果」. - **#6** (collapse Daily Review hero card pile into a single grouped settings surface) — bigger visual restructure judgment call; needs WAWQAQ sign-off before swinging at the whole page layout.
6 tasks
jackwener
added a commit
that referenced
this pull request
Jun 25, 2026
@kenji audit thread `#my-ai:4821a792` msg `e4cfbfb0` vs main `71d88454`. WAWQAQ msg `782a1663` + `继续`: bundle these, don't split. **#1 — Memory page top control rows had no outer card surface.** The three control rows (本地 MEMORY.md / 模型上下文可读取 / 项目指令 文件) were direct children of `.settingsStructuredPage`, which is transparent. `.settingsFormRow` only carries a hairline bottom border, so users saw three floating rows without the grouped-card visual every other settings surface uses. Wrap them in `.settingsRows` so they inherit the 12px outer border + radius + overflow:hidden treatment. **#2 — Plan Reminder delivery picker showed text-only IM channels.** Settings → 机器人对话 switched to real brand logos (Iconify simple-icons) in #BOT-SETTINGS-UI-0, but the chat-side Plan Reminder delivery flow's `平台` select was still rendering plain Chinese labels. Same channels should read the same everywhere. - Move `BOT_BRAND` to `packages/ui/src/bot-brand.ts` so both surfaces share a single brand-metadata source instead of one duplicating the other. - Extend `PlanReminderSelect` to accept a third tuple slot (optional `ReactNode` icon); `PlanReminderPanel` now passes an `<IconifyIcon icon={brand.iconifyId}>` per provider with the existing offline glyph fallback. - Update `SettingsModal.tsx` to import `BOT_BRAND` from `@maka/ui` instead of declaring a local copy. **#5 — Two inline `style={{ marginTop }}` spacing patches.** - `Alert variant="error" style={{ marginTop: 12 }}` → `className= "settingsSurfaceAlert"`. - `<div className="settingsActionRow" style={{ marginTop: 8 }}>` → `className="settingsActionRow settingsNoticeAction"`. The new classes pin the spacing decision in CSS where the design tokens live, instead of inline JSX. Deferred per kenji's own scope note + WAWQAQ 「只看最后的结果」: - **#3** (`RadioCard` / `ChoiceCard` primitive for theme/palette picker) — pure design-system refactor, no visible delta on Settings → 外观. - **#4** (`SettingsSelect` uplift to rich-option contract) — bigger cross-cutting refactor; current fix covers the immediate model-name and disambiguation issues kenji raised in round 1. - **#6** (layout-property transitions in sidebar/tabs/accordion) — kenji explicitly flagged these as visual-smoke-gated; will land alongside the smoke config in a separate PR.
4 tasks
jackwener
added a commit
that referenced
this pull request
Jun 25, 2026
… dvh (#250) @kenji audit msg `232aec0f` (`#my-ai:c28a6293`). Round 3 against `af66ebb7` post-#249. **#2 — Plan Reminder select collapsed state was text-only.** PR #247 added a brand icon to each `<SelectItem>`, but `<SelectValue />` defaulted to rendering just the label string — open dropdown showed a logo, collapsed trigger showed plain text. Build a `value → { label, icon }` lookup inside `PlanReminderSelect` and pass a function-child to `SelectValue` so the picked state renders the same icon + label row the dropdown items render. **#6 — `100vh` residue in renderer CSS.** Four sites still on `100vh`: - `maka-tokens.css:760` `.maka-shell` `height` - `maka-tokens.css:1941` `.maka-modal` `max-height: calc(100vh - 80px)` - `styles.css:5028` `.maka-help-modal` `max-height: calc(100vh - 96px)` - `styles.css:13121` `.maka-onboarding-stack` `min-height: calc(100vh - 84px)` Replace each with `100dvh` so Electron browser frames with dynamic chrome (or a future mobile-style viewport) don't get half a viewport of height calculation drift. Other renderer surfaces already use `100dvh`; this brings the legacy four into line. Deferred per kenji's own scope: - **#1** feishu/dingtalk official-brand-kit sourcing — separate PR. - **#3 / #4** SettingsSelect / Segmented / ChoiceCard primitive unification — design-system refactor; no end-result delta on its own. - **#5** layout-property transitions in sidebar/tabs/accordion — kenji explicitly gated on visual smoke harness.
anaconda110
added a commit
to anaconda110/maka-agent
that referenced
this pull request
Aug 3, 2026
…NEW_ARCHITECTURE_ENABLED (P0 apache#4) MainApplication.kt unconditionally called DefaultNewArchitectureEntryPoint.load() in its companion init, but build.gradle sets react { newArchEnabled = false } and gradle.properties sets newArchEnabled=false. Calling load() with the new architecture disabled at build time raises 'Unable to load script. Make sure you're running Metro' or crashes because no native codegen code was compiled. Gate the call behind BuildConfig.IS_NEW_ARCHITECTURE_ENABLED to mirror the RN 0.79 official template, so load() is a no-op when newArchEnabled=false.
anaconda110
added a commit
to anaconda110/maka-agent
that referenced
this pull request
Aug 3, 2026
… (P1 apache#5) The previous MainApplication.kt diverged from the RN 0.79.7 official template (@react-native-community/template@0.79.7) in ways that risk compile/runtime failure under the bridgeless ReactHost path: - reactNativeHost was a plain ReactNativeHost anonymous object. The getDefaultReactHost(context, reactNativeHost) overload (DefaultReactHost.kt:287) requires a DefaultReactNativeHost (it does `require(reactNativeHost is DefaultReactNativeHost)`), so this would throw at runtime. Switched to DefaultReactNativeHost, overriding isNewArchEnabled/isHermesEnabled from BuildConfig so the host reports the correct engine/arch flags. - getReactHost() was declared as `override fun getReactHost(): ReactHost`, but ReactApplication#reactHost is a property (`val reactHost: ReactHost?`). Changed to `override val reactHost: ReactHost` (Kotlin property form), matching the template and the interface. - MainApplication did not implement ReactApplication. Added the interface so the RN gradle plugin / tooling can locate the host via the standard contract. - SoLoader.init used the legacy 2-arg SoLoader.init(this, false). Switched to the template's SoLoader.init(this, OpenSourceMergedSoMapping), which is the 0.79.7 public API for so-merging JNI_OnLoad registration. - DefaultNewArchitectureEntryPoint.load() is now imported statically and called as load(), matching the template; the IS_NEW_ARCHITECTURE_ENABLED gate is preserved (P0 apache#4). Aligns with .hive/android-code-review.md P1 apache#5.
This was referenced Aug 20, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Verification