Skip to content

feat: 新增自用模式下的 API 密钥统计功能#4959

Open
SHLE1 wants to merge 5 commits into
QuantumNous:mainfrom
SHLE1:codex/api-key-status
Open

feat: 新增自用模式下的 API 密钥统计功能#4959
SHLE1 wants to merge 5 commits into
QuantumNous:mainfrom
SHLE1:codex/api-key-status

Conversation

@SHLE1
Copy link
Copy Markdown
Contributor

@SHLE1 SHLE1 commented May 19, 2026

📝 变更描述 / Description

新增自用模式下的 API 密钥统计功能

在「数据看板」中新增「API 密钥统计」功能,展示各 API Key 的相关统计图表,方便自用模式下多API用户的数据展示。

核心逻辑:直接对 logs 表按 token_id / token_name 进行小时级聚合,无需新建表或字段。
复用了前端现有的 ConsumptionDistributionChartModelCharts 组件,通过将 token_name 映射到 model_name 字段实现数据复用。

新添加的功能默认关闭,需同时满足两个条件才可见:

  1. 系统已开启自用模式
  2. 管理员在「控制台内容 → 数据仪表盘」中手动启用开关

目的是防止非自用模式下,过多的API导致影响使用。

修复侧边栏视觉问题

使用过程中,发现侧边栏选中项与相邻项的背景会因圆角相接而粘连。

SidebarMenugap-0 改为 gap-0.5 后,消除了该问题。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 / Bug fix — 侧边栏 hover 粘连
  • ✨ 新功能 / New feature — API 密钥统计面板

✅ 提交前检查项 / Checklist

  • 人工确认:已亲自整理并撰写此描述
  • 非重复提交:已确认现有 Issues/PRs 中无相同内容
  • 变更理解:已理解工作原理及可能影响
  • 范围聚焦:未包含无关改动
  • 本地验证:已通过 Docker 镜像全量构建验证,go buildbun builddocker build --no-cache 均通过,并使用生产数据库备份手动验证了接口返回与图表渲染
  • 安全合规:无敏感凭据,符合项目规范

📸 运行证明 / Proof of Work

API 密钥统计页:
CleanShot 2026-05-19 at 14 06 24@2x

数据仪表盘控制开关:
CleanShot 2026-05-19 at 14 07 29@2x

粘连修复效果:
CleanShot 2026-05-19 at 10 52 24@2x

Summary by CodeRabbit

  • New Features
    • API Key Analytics: dashboard "Keys" tab with ranking and trend charts, server endpoints for admin/self token stats, and a system setting/toggle to enable/disable the feature.
  • Documentation
    • Added translations for the API key analytics UI in multiple languages.
  • Style
    • Adjusted sidebar menu spacing for improved layout.
  • Tests
    • Added backend and model tests for access control and time-range handling of token stats.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

Caution

Review failed

Failed to post review comments

Walkthrough

Adds API key consumption statistics: backend aggregation and flag, two HTTP endpoints (admin/self) gated by self-use mode and flag, frontend types/API and KeyCharts UI, chart processing, admin toggle and settings wiring, tests, and translations.

Changes

API Key Consumption Statistics

Layer / File(s) Summary
Backend model: flag, data shape, query
common/constants.go, model/log_token_stat.go, model/option.go
Adds ApiKeyStatsEnabled, TokenQuotaData, time-range normalization, and GetLogStatsByToken DB aggregation; wires flag into option init/update.
Backend API: handlers and routes
controller/log.go, controller/misc.go, router/api-router.go
Adds admin /log/stat/tokens and user /log/self/stat/tokens handlers gated by ApiKeyStatsEnabled and self-use mode; GetStatus exposes api_key_stats_enabled.
Backend tests
model/log_token_stat_test.go, controller/log_token_stat_test.go
Unit tests validate time-range normalization and endpoint gating behavior under different flag/self-use settings.
Frontend type contracts & API wrapper
web/default/src/features/auth/types.ts, web/default/src/features/dashboard/types.ts, web/default/src/features/system-settings/types.ts, web/default/src/features/dashboard/api.ts
Adds TokenQuotaDataItem, ProcessedTokenChartData, extends SystemStatus/ContentSettings, and implements getTokenQuotaData to fetch admin/self token stats.
Dashboard section and visibility
web/default/src/features/dashboard/section-registry.tsx, web/default/src/features/dashboard/index.tsx
Registers keys dashboard section, lazy-loads KeyCharts, and conditions tab visibility on api_key_stats_enabled.
Key charts UI
web/default/src/features/dashboard/components/keys/key-charts.tsx
New KeyCharts component with time-range/granularity controls, top-N selection, React Query fetching, stat cards, and chart rendering.
Chart processing & model-charts
web/default/src/features/dashboard/lib/charts.ts, web/default/src/features/dashboard/components/models/model-charts.tsx, web/default/src/features/dashboard/lib/index.ts
Adds processTokenChartData and getTokenDisplayKey; produces ranking and trend chart specs; ModelCharts accepts optional title.
Admin settings and option management
web/default/src/features/system-settings/content/*, web/default/src/features/system-settings/hooks/use-update-option.ts
Adds ApiKeyStatsEnabled default and conditional form field; marks option as status-related for cache invalidation.
Internationalization
web/default/src/i18n/locales/*
Adds translation keys for API key analytics across supported locales and normalizes footer keys.
UI polish
web/default/src/components/ui/sidebar.tsx
Adjusts sidebar spacing from gap-0 to gap-0.5.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested reviewers

  • seefs001

🐰 A rabbit built dashboards so bright,
With tokens and stats in the light,
From backend to front-end they flow,
Charts rank the top keys in a row,
Statistics now sparkle at night!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: 新增自用模式下的 API 密钥统计功能' accurately describes the main feature addition: API key statistics functionality in self-use mode, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
web/default/src/i18n/locales/en.json (1)

342-344: ⚡ Quick win

Use hierarchical i18n keys for the new API-key analytics strings.

These new entries should follow semantic hierarchical keys (for example, dashboard.apiKeyStats.title, dashboard.apiKeyStats.ranking, etc.) instead of full-sentence/source-text keys, to keep i18n key naming consistent and maintainable.

As per coding guidelines, "Use hierarchical and semantically clear translation key names such as dashboard.overview.title and maintain naming consistency".

Also applies to: 1374-1374, 3632-3632, 4033-4033, 4326-4326

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/i18n/locales/en.json` around lines 342 - 344, Replace the
flat translation keys "API Key Analytics", "API Key Consumption Ranking", and
"API Key Consumption Trend" with hierarchical, semantic keys (for example
dashboard.apiKeyStats.title, dashboard.apiKeyStats.ranking, and
dashboard.apiKeyStats.trend) in en.json and update any code that consumes those
keys to use the new names; also apply the same hierarchical naming convention to
the other mentioned occurrences (lines referenced: 1374, 3632, 4033, 4326) so
all API-key analytics entries follow dashboard.apiKeyStats.*.
web/default/src/i18n/locales/zh.json (1)

342-344: 🏗️ Heavy lift

Use hierarchical i18n keys for new API-key analytics entries.

These newly added keys use phrase-as-key style and break the required naming convention. Please migrate them to semantic namespaces (for example, dashboard.apiKey.analytics.title, dashboard.apiKey.ranking.title, settings.dashboard.apiKeyStats.enable) and keep aliases only if backward compatibility is needed.

As per coding guidelines, "Use hierarchical and semantically clear translation key names such as dashboard.overview.title and maintain naming consistency".

Also applies to: 1374-1374, 3632-3632, 4033-4033, 4326-4326

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/i18n/locales/zh.json` around lines 342 - 344, The three new
phrase-as-key entries ("API Key Analytics", "API Key Consumption Ranking", "API
Key Consumption Trend") violate the project's hierarchical i18n convention;
replace them with semantic namespace keys such as
dashboard.apiKey.analytics.title, dashboard.apiKey.ranking.title, and
dashboard.apiKey.trend.title in zh.json, move their Chinese translations under
those new keys, and optionally keep the original phrase keys as short-lived
aliases mapping to the new keys if backward compatibility is required (apply the
same migration pattern to the other mentioned occurrences).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/log.go`:
- Around line 154-167: The endpoint handlers (e.g., GetLogStatsByToken and the
similar handler around lines 170-184 that call model.GetLogStatsByToken) must
enforce the ApiKeyStatsEnabled feature flag (and self-use mode rules) before
performing the query; add a guard at the top of each handler that checks
config.ApiKeyStatsEnabled (or the appropriate self-use check) and return an HTTP
403/404 JSON response (using common.ApiError or c.JSON) when disabled, avoiding
the call to model.GetLogStatsByToken so stats are not exposed via direct API
calls.

In `@model/log_token_stat.go`:
- Around line 19-43: GetLogStatsByToken currently allows unbounded full-history
scans when startTime and endTime are both zero; change the function to guard by
requiring a bounded time range: if startTime and endTime are both zero, set a
sensible default window (e.g., endTime = now, startTime = now - 24h) and enforce
a maximum allowed range (e.g., cap range to 30 days by adjusting startTime if
endTime-startTime exceeds maxRange); apply these normalized startTime/endTime
values before building the LOG_DB query so the resulting query (used by
GetLogStatsByToken and the query variable) is always time-bounded.

In `@web/default/src/features/dashboard/components/keys/key-charts.tsx`:
- Around line 45-48: The code keys per-API-key data by token_name which
collapses distinct keys with the same label; update the mapping functions (e.g.,
mapToQuotaData and any other mappers that currently use token_name as the unique
key) to use a stable identifier like token_id (or a composite like
`${token_id}:${token_name}`) for uniqueness — set model_name/series key to
`key-${item.token_id ?? 'unknown'}` (and keep token_name as a separate display
label field if needed) so Top-N, trends and totals no longer merge different
keys with identical names.

In `@web/default/src/i18n/locales/ja.json`:
- Line 3632: The Japanese translation for the string "Show API key consumption
statistics tab in the dashboard. Only available in self-use mode." uses the
Chinese term "自用モード"; update the value in ja.json to use a proper Japanese term
such as "セルフユースモード" (or "セルフユース") so the entry reads:
ダッシュボードにAPIキー消費統計タブを表示します。セルフユースモードでのみ利用できます。, keeping the rest of the sentence
unchanged and ensuring UTF-8 encoding.

In `@web/default/src/i18n/locales/ru.json`:
- Line 4326: Update the Russian translation value for the key "View API key
consumption statistics and charts" in ru.json to include the missing "and
charts" portion; replace the current value "Просмотр статистики потребления
API-ключей" with a complete translation such as "Просмотр статистики потребления
API-ключей и диаграмм" (or another appropriate Russian phrasing that includes
both statistics and charts).

In `@web/default/src/i18n/locales/vi.json`:
- Line 4033: The vi locale entry for the key "Top Keys" is still in English;
update the value for the "Top Keys" key in web/default/src/i18n/locales/vi.json
to a proper Vietnamese translation (e.g., "Các khóa hàng đầu" or another
context-appropriate phrase) so the UI is fully localized for the vi locale.

---

Nitpick comments:
In `@web/default/src/i18n/locales/en.json`:
- Around line 342-344: Replace the flat translation keys "API Key Analytics",
"API Key Consumption Ranking", and "API Key Consumption Trend" with
hierarchical, semantic keys (for example dashboard.apiKeyStats.title,
dashboard.apiKeyStats.ranking, and dashboard.apiKeyStats.trend) in en.json and
update any code that consumes those keys to use the new names; also apply the
same hierarchical naming convention to the other mentioned occurrences (lines
referenced: 1374, 3632, 4033, 4326) so all API-key analytics entries follow
dashboard.apiKeyStats.*.

In `@web/default/src/i18n/locales/zh.json`:
- Around line 342-344: The three new phrase-as-key entries ("API Key Analytics",
"API Key Consumption Ranking", "API Key Consumption Trend") violate the
project's hierarchical i18n convention; replace them with semantic namespace
keys such as dashboard.apiKey.analytics.title, dashboard.apiKey.ranking.title,
and dashboard.apiKey.trend.title in zh.json, move their Chinese translations
under those new keys, and optionally keep the original phrase keys as
short-lived aliases mapping to the new keys if backward compatibility is
required (apply the same migration pattern to the other mentioned occurrences).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f837b07-96e6-4760-93b8-910b502c2224

📥 Commits

Reviewing files that changed from the base of the PR and between 0936e25 and 83eb895.

📒 Files selected for processing (27)
  • common/constants.go
  • controller/log.go
  • controller/misc.go
  • model/log_token_stat.go
  • model/option.go
  • router/api-router.go
  • web/default/src/components/ui/sidebar.tsx
  • web/default/src/features/auth/types.ts
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/keys/key-charts.tsx
  • web/default/src/features/dashboard/components/models/model-charts.tsx
  • web/default/src/features/dashboard/index.tsx
  • web/default/src/features/dashboard/lib/charts.ts
  • web/default/src/features/dashboard/lib/index.ts
  • web/default/src/features/dashboard/section-registry.tsx
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/system-settings/content/dashboard-section.tsx
  • web/default/src/features/system-settings/content/index.tsx
  • web/default/src/features/system-settings/content/section-registry.tsx
  • web/default/src/features/system-settings/hooks/use-update-option.ts
  • web/default/src/features/system-settings/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json

Comment thread controller/log.go
Comment thread model/log_token_stat.go
Comment thread web/default/src/features/dashboard/components/keys/key-charts.tsx
Comment thread web/default/src/i18n/locales/ja.json Outdated
Comment thread web/default/src/i18n/locales/ru.json Outdated
Comment thread web/default/src/i18n/locales/vi.json Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
controller/log_token_stat_test.go (1)

3-26: ⚡ Quick win

Serialize global flag overrides in tests.

withApiKeyStatsSettings mutates package-level globals; adding a mutex prevents cross-test interference if these tests (or neighboring ones) are parallelized later.

Suggested patch
 import (
 	"net/http"
 	"net/http/httptest"
+	"sync"
 	"testing"
@@
 	"github.com/stretchr/testify/require"
 )
 
+var apiKeyStatsSettingsMu sync.Mutex
+
 func withApiKeyStatsSettings(t *testing.T, selfUseModeEnabled, apiKeyStatsEnabled bool) {
 	t.Helper()
+	apiKeyStatsSettingsMu.Lock()
 
 	originalSelfUseModeEnabled := operation_setting.SelfUseModeEnabled
 	originalApiKeyStatsEnabled := common.ApiKeyStatsEnabled
 	operation_setting.SelfUseModeEnabled = selfUseModeEnabled
 	common.ApiKeyStatsEnabled = apiKeyStatsEnabled
 	t.Cleanup(func() {
 		operation_setting.SelfUseModeEnabled = originalSelfUseModeEnabled
 		common.ApiKeyStatsEnabled = originalApiKeyStatsEnabled
+		apiKeyStatsSettingsMu.Unlock()
 	})
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/log_token_stat_test.go` around lines 3 - 26,
withApiKeyStatsSettings currently mutates package-level globals
(operation_setting.SelfUseModeEnabled and common.ApiKeyStatsEnabled) without
synchronization; add a package-level mutex (e.g., var apiKeyStatsMu sync.Mutex)
and acquire it at the start of withApiKeyStatsSettings, then release it in the
t.Cleanup closure (unlock inside the cleanup after restoring original values) so
the global overrides are serialized across tests and cannot race when tests run
in parallel.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/default/src/features/dashboard/components/keys/key-charts.tsx`:
- Around line 186-236: These toggle buttons do not expose their selected state
to assistive tech; update each button rendering for the presets,
TIME_GRANULARITY_OPTIONS and TOP_KEY_LIMIT_OPTIONS to include an aria-pressed
attribute based on the current state (use selectedRange for the preset buttons,
timeGranularity for the granularity buttons, and topKeyLimit for the top-key
buttons) so that handleRangeChange, handleGranularityChange and setTopKeyLimit
still work as-is but screen readers will know which option is active.

---

Nitpick comments:
In `@controller/log_token_stat_test.go`:
- Around line 3-26: withApiKeyStatsSettings currently mutates package-level
globals (operation_setting.SelfUseModeEnabled and common.ApiKeyStatsEnabled)
without synchronization; add a package-level mutex (e.g., var apiKeyStatsMu
sync.Mutex) and acquire it at the start of withApiKeyStatsSettings, then release
it in the t.Cleanup closure (unlock inside the cleanup after restoring original
values) so the global overrides are serialized across tests and cannot race when
tests run in parallel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4fc5e288-1453-4d10-8fe7-0b186a69966b

📥 Commits

Reviewing files that changed from the base of the PR and between 83eb895 and c1142dd.

📒 Files selected for processing (9)
  • controller/log.go
  • controller/log_token_stat_test.go
  • model/log_token_stat.go
  • model/log_token_stat_test.go
  • web/default/src/features/dashboard/components/keys/key-charts.tsx
  • web/default/src/features/dashboard/lib/charts.ts
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • controller/log.go
  • web/default/src/features/dashboard/lib/charts.ts
  • model/log_token_stat.go
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json

Comment on lines +186 to +236
<button
key={preset.days}
type='button'
onClick={() => handleRangeChange(preset.days)}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
selectedRange === preset.days
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t(preset.label)}
</button>
))}
</div>

{/* Time granularity */}
<div className='flex shrink-0 items-center gap-1.5 rounded-lg border p-0.5'>
{TIME_GRANULARITY_OPTIONS.map((opt) => (
<button
key={opt.value}
type='button'
onClick={() => handleGranularityChange(opt.value as TimeGranularity)}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
timeGranularity === opt.value
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t(opt.label)}
</button>
))}
</div>

<div className='flex shrink-0 items-center gap-1.5 rounded-lg border p-0.5'>
<span className='text-muted-foreground px-2 text-xs font-medium'>
{t('Top Keys')}
</span>
{TOP_KEY_LIMIT_OPTIONS.map((limit) => (
<button
key={limit}
type='button'
onClick={() => setTopKeyLimit(limit)}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
topKeyLimit === limit
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t('Top {{count}}', { count: limit })}
</button>
))}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Expose selected state on toggle buttons with aria-pressed.

These button groups behave like toggles; without aria-pressed, screen readers can’t reliably announce which option is active.

Suggested patch
           {TIME_RANGE_PRESETS.map((preset) => (
             <button
               key={preset.days}
               type='button'
+              aria-pressed={selectedRange === preset.days}
               onClick={() => handleRangeChange(preset.days)}
               className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
@@
           {TIME_GRANULARITY_OPTIONS.map((opt) => (
             <button
               key={opt.value}
               type='button'
+              aria-pressed={timeGranularity === opt.value}
               onClick={() => handleGranularityChange(opt.value as TimeGranularity)}
               className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
@@
           {TOP_KEY_LIMIT_OPTIONS.map((limit) => (
             <button
               key={limit}
               type='button'
+              aria-pressed={topKeyLimit === limit}
               onClick={() => setTopKeyLimit(limit)}
               className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
As per coding guidelines: "Ensure keyboard operability and logical focus order; use ARIA attributes when necessary (`aria-label`, `aria-expanded`, `aria-hidden`)."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/components/keys/key-charts.tsx` around
lines 186 - 236, These toggle buttons do not expose their selected state to
assistive tech; update each button rendering for the presets,
TIME_GRANULARITY_OPTIONS and TOP_KEY_LIMIT_OPTIONS to include an aria-pressed
attribute based on the current state (use selectedRange for the preset buttons,
timeGranularity for the granularity buttons, and topKeyLimit for the top-key
buttons) so that handleRangeChange, handleGranularityChange and setTopKeyLimit
still work as-is but screen readers will know which option is active.

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