Skip to content

feat(overview): create overview page / 创建总览页面#611

Open
edwingao28 wants to merge 4 commits into
masterfrom
feat/executive-overview
Open

feat(overview): create overview page / 创建总览页面#611
edwingao28 wants to merge 4 commits into
masterfrom
feat/executive-overview

Conversation

@edwingao28

@edwingao28 edwingao28 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Adds /overview (and /zh/overview): a server-rendered scorecard that answers one question per model — if you deploy it today, how much capacity does each platform deliver?

What it shows

  • One row per active model, one column per platform: B200 as the reference, then MI355X, B300, GB200 NVL72, GB300 NVL72. Fixed workload: 8K input / 1K output single-turn, speculative decode only.
  • Best validated stack per platform. Each cell is that platform's own best validated deployment — framework, precision and speculative method chosen freely per platform — labeled FRAMEWORK · PRECISION, with the full stack on the link title. This measures deployable capacity, not isolated silicon, and the methodology note on the page says so.
  • Service-level switcher. ?tier=30|75|100 re-renders the whole matrix at another tok/s/user target (default 50). The switcher is plain links, no client JS, so every view is a shareable URL.
  • Deltas never mix stacks quietly. The signed percentage against B200 appears only when both displayed results share one precision and one model release. Otherwise the cell says why: FP4 vs FP8 · no comparable delta, Different releases · no comparable delta.
  • Honest gaps. A side with no exact result at the displayed tier shows plus its reason (no 8K/1K data, cannot reach @50, standard decode only, …). There is never a percentage against a missing number.
  • Leader flips. When the winner changes at 100 tok/s/user, the lower-tier views add one line: At 100, X leads.
  • Every value links to the dashboard filtered to its exact configuration, and every result carries the run dates of the frontier points behind it.

Testing

  • Data rules pinned in overview-data.test.ts: per-platform bucket selection, tier parameterization, mismatch and missing-reason classification. Expected values come from running the real builder — tier values are spline-interpolated, so nothing is hand-computed.
  • A small synthetic fixture is served through the real assembler in fixtures mode; overview.cy.ts covers the matrix, the tier links, mismatch and states, the Chinese sibling, and 390/320px overflow, on Chrome and Firefox.
  • Desktop fits a 1440×900 fold. Mobile renders the same cell components as a two-column list.

中文说明

新增 /overview/zh/overview:服务端渲染的对比页,回答一个问题——现在部署这个模型,各平台能交付多少容量。

  • 每行一个活跃模型,每列一个平台:B200 为参照,另有 MI355X、B300、GB200 NVL72、GB300 NVL72。固定 8K→1K 单轮负载,仅投机解码。
  • 每格是该平台自己的最佳已验证部署(框架、精度、投机方式各自取最优),标注 框架 · 精度,完整配置在链接悬浮提示中。对比对象是完整服务栈,不是单独的芯片,页面方法说明中已写明。
  • ?tier=30|75|100 切换服务档位(默认 50)。切换器是纯链接,无客户端 JS,任何视图都是可转发的 URL。
  • 相对 B200 的差值只在同精度、同版本之间计算;不可比时写明原因,绝不静默混比。
  • 无精确结果的一侧显示 与原因,绝不对缺失数字给出百分比。
  • 100 档赢家换人时,低档视图给出一行提示。
  • 每个数值链接到按该配置精确过滤的仪表板;每个结果携带自己的证据日期。
  • 测试:数据规则逐条单元断言,期望值由真实 builder 生成(档位值为样条插值,不手算);合成 fixture 走真实装配器;Cypress 覆盖矩阵、档位切换、中文页与 390/320px,Chrome 与 Firefox 双跑。

Note

Medium Risk
New public benchmark comparison surface with non-trivial ranking, interpolation, and delta rules; mistakes would misstate platform performance, though behavior is heavily unit- and e2e-tested.

Overview
Adds /overview and /zh/overview: a force-dynamic, server-rendered matrix of active models vs B200, MI355X, B300, GB200, and GB300 on fixed 8K→1K speculative-decode workloads. A new overview-data pipeline (with overview-config-identity, extended tco-feed evidence_date, and getOverviewPageData) picks each platform’s best stack per tier, computes B200 deltas only for same precision and release, and surfaces gaps as with explicit reasons. overview-scorecard renders desktop and mobile from shared cell components; plain ?tier= links and overview-links deep-link to filtered /inference views (full page loads for model detail).

Also registers the route in i18n mirrors and sitemap, extracts getCachedBenchmarks to benchmark-data.server, moves computeToggle to toggle-set, and tightens the header for narrow viewports (44px targets, hide GitHub stars below sm, Minecraft audio toggles in the hamburger). Cypress e2e and Vitest coverage lock matrix semantics, tiers, zh copy, and fixture drift.

Reviewed by Cursor Bugbot for commit 7378a3b. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

You don't have permission to create a Preview Deployment for this Vercel project: inferencemax-app.

View Documentation: https://vercel.com/docs/accounts/team-members-and-roles

@edwingao28

Copy link
Copy Markdown
Collaborator Author

Preview status update:

  • The Vercel build has not started because my current SemiAnalysisAI Vercel seat is Pro Viewer, which can inspect previews but cannot create them. Both the Git integration and a manual CLI deploy return You don't have permission to create a Preview Deployment. I need Developer (or equivalent preview-deploy) permission before retriggering.
  • As an interim real-data check, I fetched the current public benchmark rows for all five active model families and ran them through this PR's exact reducer. It completed successfully with one precision per model: DeepSeek V4 Pro (FP4 / GB300), Kimi (FP4 / GB200), MiniMax M3 (FP8 / GB200), Qwen3.5 (FP8 / GB300), while GLM5.2 correctly remained the empty fixed-sequence 8K/1K row.

The protected Vercel Preview is still required for final server-loader and responsive validation.

@edwingao28 edwingao28 changed the title feat(overview): add executive inference scorecard / 新增高管推理总览评分页 feat(overview): add overview inference scorecard / 新增推理总览评分页 Jul 21, 2026
@edwingao28
edwingao28 force-pushed the feat/executive-overview branch from 5160d07 to a854318 Compare July 21, 2026 20:42
@edwingao28 edwingao28 changed the title feat(overview): add overview inference scorecard / 新增推理总览评分页 feat(overview): create overview page / 创建总览页面 Jul 21, 2026
@edwingao28
edwingao28 force-pushed the feat/executive-overview branch from a854318 to 785c1af Compare July 21, 2026 21:16
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment Jul 23, 2026 2:03am

Request Review

@edwingao28
edwingao28 force-pushed the feat/executive-overview branch from 4d85aba to eec67f3 Compare July 22, 2026 21:05
@edwingao28
edwingao28 marked this pull request as ready for review July 22, 2026 21:07
@edwingao28
edwingao28 requested a review from adibarra as a code owner July 22, 2026 21:07
Comment thread packages/app/src/lib/tco-feed.ts
Comment thread packages/app/src/components/overview/overview-scorecard.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eec67f33dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +553 to +555
const secondaryMeasured = new Set(model.secondary?.measuredHardware);
const entries = model.notRanked
.filter((entry) => !secondaryMeasured.has(entry.hardwareLabel))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep unrendered secondary hardware in the not-ranked line

When the secondary precision is in ranked mode, measuredHardware still includes every hardware with any row at that precision, but the secondary section only renders exact ranked chips. For a hardware that has no primary exact @50 result and only has secondary rows that are standard-decode or otherwise lack an exact @50 read, this filter removes its model.notRanked entry even though no secondary chip or coverage line names it, so that hardware silently disappears instead of showing its reason.

Useful? React with 👍 / 👎.

Comment on lines +223 to +226
for (let i = 0; i < last; i++) {
if (frontier[i].interactivity <= tier) lo = i;
}
return [frontier[lo], frontier[lo + 1]];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Collapse evidence dates for exact interior tier hits

For tiers that exactly match an interior frontier knot, the loop still returns that knot plus the next one, so the overview/TCO JSON reports a date range even though the displayed value is an exact measured point. This shows misleading evidence dates whenever, for example, a config has knots at 30/50/75/100 and the 50 tok/s/user row's date differs from the 75 row; the bracket should collapse to the matching knot before falling back to an interpolation pair.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @edwingao28's task in 5m 20s —— View job


Review — PR #611 Executive Overview page

Runtime verified on localhost:3000 (real read-only DB): /overview and /zh/overview both render real ranked data, evidence dates, leader-change badges, coverage lines and not-ranked reasons; 0 console errors; no horizontal overflow at 320px. CI green (Lint / Unit / E2E). This is careful, well-tested work — the data-layer invariants are pinned by focused unit tests and a builder-driven e2e fixture.

I confirmed the two open bot findings (Cursor Bugbot + Codex agree on both) and posted precise fixes inline:

  1. 🟡 tco-feed.ts bracketKnots — an exact hit on an interior frontier knot still returns a bracketing pair, so evidence_date becomes a cross-day range for a value that was actually a single measured point. Contradicts the PR's "same-day read shows one date" promise. Low trigger probability on the live overview (float interactivity rarely lands exactly on 30/50/75/100) but real in the shared helper behind the public tco-feed API. → collapse to the single knot on an exact interior match.
  2. 🟡 overview-scorecard.tsx NotRankedLine — in ranked secondary state the dedup against the full measuredHardware list drops secondary-measured hardware that has no exact @50 read at either precision, so it appears in neither the ranked chips nor the not-ranked line — breaking the "every hardware accounted for" guarantee. Not reproducible on today's real data (all secondaries are coverage), but the ranked branch is live code. → only dedup against measuredHardware in coverage state; suggest a unit test for the ranked case.

Both are 🟡 (correctness/honesty of displayed evidence, not crashes). No other blocking issues found — no missing-test gaps (new lib code has colocated tests, the UI has an e2e), no resource/hook/security concerns in the new code.


  • Read the diff & new modules
  • Assess the two open bot findings — both confirmed
  • Check CI status — ✅ green
  • Runtime verify /overview and /zh/overview
  • Post review

Comment on lines +222 to +226
let lo = 0;
for (let i = 0; i < last; i++) {
if (frontier[i].interactivity <= tier) lo = i;
}
return [frontier[lo], frontier[lo + 1]];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 WARNING: Exact interior-knot reads report a cross-day evidence range they didn't interpolate. (Confirms the Cursor Bugbot / Codex finding.)

Why it matters: When a tier lands exactly on an interior frontier knot, hermiteInterpolate returns that knot's measured throughput exactly — it's a single measured point, no interpolation. But bracketKnots only collapses to one knot at the two endpoints; for an interior exact hit it still returns [frontier[lo], frontier[lo+1]], so evidenceRange widens to {from, to} spanning two dates. The overview then renders a date range for a value the PR body promises is a same-day exact read ("Same-day reads show one date, cross-day interpolations show a range"). Low trigger probability for the live overview (interactivity is a float rarely exactly 30/50/75/100), but this is the shared helper backing the public tco-feed API too.

Fix:

Suggested change
let lo = 0;
for (let i = 0; i < last; i++) {
if (frontier[i].interactivity <= tier) lo = i;
}
return [frontier[lo], frontier[lo + 1]];
let lo = 0;
for (let i = 0; i < last; i++) {
if (frontier[i].interactivity <= tier) lo = i;
}
// An exact hit on an interior knot is a measured point, not an interpolation —
// collapse to that single knot so its evidence date isn't widened to a range.
if (frontier[lo].interactivity === tier) return [frontier[lo], frontier[lo]];
return [frontier[lo], frontier[lo + 1]];

// Hardware already named by the secondary section — its ranked rows or its
// coverage 'measured' list — is not repeated here; dedup against the secondary's
// measured labels so each hardware surfaces in exactly one place.
const secondaryMeasured = new Set(model.secondary?.measuredHardware);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 WARNING: A ranked secondary silently drops secondary-measured hardware from the not-ranked line, breaking the page's "every hardware accounted for" guarantee. (Confirms the Cursor Bugbot / Codex finding.)

Why it matters: secondaryMeasured is built from model.secondary.measuredHardware, which lists every hardware with any row at the secondary precision. When secondary.state === 'coverage' this is correct — the coverage line names all of them. But when secondary.state === 'ranked', SecondarySection renders only the exact-@50 ranked chips (overview-scorecard.tsx:417-426), not the full measured list. A hardware that (a) has no exact @50 read at the primary precision and (b) is measured at the secondary precision but has no exact @50 secondary read (e.g. standard-decode-only or clamped there) is:

  • not an exact primary chip,
  • not a ranked secondary chip,
  • filtered out of NotRankedLine here because its label is in measuredHardware,
  • and gets no coverage line (that only renders in coverage state).

→ it disappears entirely. buildNotRanked (overview-data.ts:846-867) already excludes only the exact-@50 secondary hardware, so in ranked state this extra measuredHardware dedup is over-aggressive.

Not reproducible on today's real data (every model resolves to a single ranked precision — all secondaries are coverage), but the ranked branch is live code and this contradicts a headline invariant of the feature.

Fix: only dedup against the full measured list in coverage state; buildNotRanked already handles the ranked case.

Suggested change
const secondaryMeasured = new Set(model.secondary?.measuredHardware);
// Only the coverage-state secondary names its full measured list. A ranked
// secondary shows just exact-@50 chips, and buildNotRanked has already
// excluded those — so deduping against measuredHardware there would wrongly
// drop secondary-measured hardware that has no exact @50 read at all.
const secondaryMeasured = new Set(
model.secondary?.state === 'coverage' ? model.secondary.measuredHardware : [],
);

Worth a unit test in overview-data.test.ts pinning a ranked secondary where a third hardware measured at the secondary precision (no exact @50) still surfaces in notRanked.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @edwingao28's task in 2m 13s —— View job


Re-review — PR #611 Executive Overview page

Re-reviewing the one new commit since my last review (69ae73c — "infinity placeholder chips for missing results").

What changed: NotRankedLine was reworked from a Not ranked: … sentence into per-hardware <label> ∞ chips, with the reason carried in title/aria-label and a ∞ = no comparable result legend added to the methodology footer. E2E updated accordingly.

Runtime verified on localhost:3000 (real read-only DB): /overview renders 50 ∞ chips, each with its reason in title/aria (no exact @50 result, no 8K/1K data, standard decode only, …), the legend is present, no ∞% is ever rendered, and there are 0 console errors. CI is green (Lint / Unit / Typecheck / E2E chrome+firefox / Component). The new rendering itself is correct.

However — neither open finding from the last round was addressed:

  1. 🟡 overview-scorecard.tsx:553 — ranked secondary still drops hardware. The ∞ rewrite touched these exact lines but kept the unconditional new Set(model.secondary?.measuredHardware) dedup. In a ranked secondary (which renders only exact-@50 chips), secondary-measured hardware without an exact @50 read now disappears from every surface — no chip, no reason. buildNotRanked already excludes the exact-@50 ranked hardware, so the dedup is only correct in coverage state. Re-flagged inline with a suggestion + unit-test note. Not reproducible on today's data (all secondaries are coverage) but the branch is live.
  2. 🟡 tco-feed.ts:226 bracketKnots — exact interior-knot reads still report a cross-day range. Unchanged since eec67f3; the existing inline thread + suggestion (collapse to the single knot on an exact interior match) still applies. Contradicts the PR's "same-day reads show one date" promise for the public tco-feed API.

No new issues introduced by the ∞-chip commit. Both remaining items are 🟡 (honesty of displayed evidence, not crashes) — recommend fixing both before merge and adding the ranked-secondary unit test.

  • Diff new commit 69ae73c vs reviewed eec67f3
  • Check the two open findings — both still unaddressed
  • Review new ∞-chip logic — correct, no new issues
  • Check CI status — ✅ green
  • Runtime verify /overview — ∞ chips + legend render, 0 console errors
  • Post review
    · feat/executive-overview

// Hardware already named by the secondary section — its ranked rows or its
// coverage 'measured' list — is not repeated here; dedup against the secondary's
// measured labels so each hardware surfaces in exactly one place.
const secondaryMeasured = new Set(model.secondary?.measuredHardware);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 WARNING (still open after 69ae73c): the ∞-chip rewrite kept the over-aggressive dedup, so a ranked secondary still drops secondary-measured hardware from the not-ranked line.

Why it matters: measuredHardware lists every hardware with a row at the secondary precision, but a ranked secondary renders only exact-@50 chips (overview-scorecard.tsx:417-426). A hardware that has no exact @50 read at the primary and no exact @50 read at the secondary (e.g. standard-decode-only there) is not a primary chip, not a ranked secondary chip, and now filtered out of this ∞ line too — so it gets no chip and no reason anywhere, breaking the "every hardware accounted for" guarantee. buildNotRanked (overview-data.ts:846-857) already excludes only the exact-@50 ranked secondary hardware, so this extra dedup is only correct in coverage state. (Not reproducible on today's real data — all secondaries resolve to coverage — but the ranked branch is live code.)

Fix: only dedup against the full measured list in coverage state.

Suggested change
const secondaryMeasured = new Set(model.secondary?.measuredHardware);
// Only a coverage-state secondary names its full measured list; a ranked
// secondary shows just exact-@50 chips, which buildNotRanked already excludes.
// Deduping against the full measured list in ranked state would drop
// secondary-measured hardware that has no exact @50 read from every surface.
const secondaryMeasured = new Set(
model.secondary?.state === 'coverage' ? model.secondary.measuredHardware : [],
);

Worth a unit test in overview-data.test.ts pinning a ranked secondary where a third hardware measured at the secondary precision (no exact @50) still surfaces in notRanked.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @edwingao28's task in 2m 11s —— View job


Re-review — PR #611 Executive Overview page

Re-reviewing the new commit 1725e80 (per-platform matrix with tier switcher and stack labels), which is a substantial rewrite of the overview data + UI layers.

Both previously-flagged findings are resolved:

  1. tco-feed.ts bracketKnots interior-knot range — now collapses to a single knot on an exact interior match (tco-feed.ts:224: if (frontier[i].interactivity === tier) return [frontier[i], frontier[i]]). An exact measured point at an interior tier no longer reports a cross-day evidence range. tco-feed.test.ts adds coverage for this.
  2. overview-scorecard.tsx ranked-secondary drops hardware — the entire primary/secondary/NotRankedLine/measuredHardware structure the finding was about is gone. The rewrite is a per-platform matrix: each hardware column independently selects its own best qualified bucket (dbModel × precision, exact-first, tie → FP4), and any hardware with no exact read shows with an explicit reason (no exact @tier result, standard decode only, no 8K/1K data, cannot reach @tier, INT4/BF16 only). The "every hardware accounted for" guarantee now holds structurally — every cell is either an exact read or a reasoned .

Runtime verified on localhost:3000 (real read-only DB):

  • /overview renders the full matrix with real ranked data, precision badges (TRTLLM · FP4, SGLang · FP4, ATOM¹ · FP4), signed deltas vs B200, per-result evidence dates, reasons, and At 100, <hw> leads transition lines. Filtered dashboard links carry correct g_model/i_gpus/i_spec/i_prec/g_runid params.
  • Tier switcher (?tier=30/75/100) re-renders server-side; /zh/overview?tier=75 renders fully translated. 0 console errors on all views.

CI: green — Lint, Typecheck+Unit, Component, E2E chrome (all shards) + firefox all passing (a couple of jobs still finishing, none failing).

No new blocking or warning issues found in the rewrite. The data-layer invariants (exact-vs-clamped classification via isExactTierRead, cohort/precision isolation, evidence-date sourcing) are pinned by the updated overview-data.test.ts, and the builder-driven overview.cy.ts fixture exercises the UI. 🟢 LGTM.

Comment thread packages/app/src/components/overview/overview-scorecard.tsx Fixed
Comment thread packages/app/src/components/overview/overview-detail-link.tsx Outdated
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @edwingao28's task in 56s —— View job


Re-review — PR #611 Executive Overview page

Re-reviewing the one new commit 7378a3b (drop dead dashboard-link helper and unreachable date guard) since my last review of 1725e80 (LGTM). This commit only touches two files (+7 / −49) and addresses the two low-severity findings from the previous round.

Both findings resolved, and both changes verified safe:

  1. Unused OverviewDashboardLink / EVIDENCE_LINK_CLASS removed (overview-detail-link.tsx) — confirmed no remaining references anywhere in packages/app/src/ (grep → none). The matrix cells build their evidence anchors inline via buildOverviewDashboardHref (overview-scorecard.tsx:224), which is unchanged. Dead export cleanly excised; the surviving OverviewDetailLink comment was tidied, no behavior change.
  2. Unreachable evidenceDate === null guard removed (overview-scorecard.tsx:126) — formatEvidenceDate now takes a non-null { from; to } and returns string. The sole caller already guards null before rendering (overview-scorecard.tsx:240: {evidenceDate === null ? null : (…formatEvidenceDate(…)…)}), so TypeScript narrows evidenceDate to non-null at the call site — the removed branch was genuinely dead. Matches the CodeQL finding.

CI: green across the board — Lint, Typecheck+Unit, Component, CodeQL, E2E chrome (4 shards) + firefox (4 shards), Vercel Preview all passing. (Claude review / Cursor Bugbot still pending, as expected.)

No new issues introduced. The two data-honesty findings from earlier rounds (tco-feed interior-knot range, ranked-secondary hardware drop) were already resolved by the 1725e80 rewrite. 🟢 LGTM.

  • Diff 7378a3b vs previously-reviewed 1725e80
  • Verify the two low-severity findings (unused helper, unreachable guard) are resolved
  • Confirm no new issues introduced — call-site null-guard makes the tightened signature safe; removed exports have zero references
  • Check CI status — ✅ green
    · feat/executive-overview

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7378a3b. Configure here.

};
}),
latestDate: feed[0].latest_date,
oldestFrontierDate: feed[0].oldest_frontier_date,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused config fields computed

Low Severity

buildConfigResult populates several OverviewConfigResult fields that nothing in the overview UI or link builders reads, including offloadMode, isMultinode, GPU counts, totalGpu, parallelism, image, and oldestFrontierDate. That adds maintenance surface and wasted work on every page assemble.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7378a3b. Configure here.

))}
<span className="flex items-center gap-2 px-3 sm:hidden">
<MinecraftToggles />
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Menu audio controls undersized

Low Severity

This PR moves MinecraftToggles into the mobile menu for the 320px layout and raises other header controls to a 44px touch target, but the audio buttons still use compact p-2 sizing. In minecraft mode on small screens those menu controls remain below the WCAG minimum the new tests encode.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7378a3b. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant