diff --git a/apps/desktop/src/main/__tests__/health-center-copy.test.ts b/apps/desktop/src/main/__tests__/health-center-copy.test.ts new file mode 100644 index 0000000000..e8f7a6aa5f --- /dev/null +++ b/apps/desktop/src/main/__tests__/health-center-copy.test.ts @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { getHealthCenterCopy } from '../../renderer/locales/settings-health-copy.js'; + +test('labels blocker counts as global across filtered health views', () => { + assert.equal( + getHealthCenterCopy('zh').blockers.send(1, 6), + '全部健康信号中,1/6 条会阻塞发送', + ); + assert.equal( + getHealthCenterCopy('en').blockers.send(1, 6), + 'Across all health signals, 1 of 6 blocks sending', + ); +}); diff --git a/apps/desktop/src/main/__tests__/permission-center-copy.test.ts b/apps/desktop/src/main/__tests__/permission-center-copy.test.ts new file mode 100644 index 0000000000..6333051056 --- /dev/null +++ b/apps/desktop/src/main/__tests__/permission-center-copy.test.ts @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { getPermissionCenterCopy } from '../../renderer/locales/permission-center-copy.js'; + +test('presents a granted OS permission as a verified success', () => { + assert.equal(getPermissionCenterCopy('zh').osStates.granted.tone, 'success'); + assert.equal(getPermissionCenterCopy('en').osStates.granted.tone, 'success'); +}); diff --git a/apps/desktop/src/renderer/locales/permission-center-copy.ts b/apps/desktop/src/renderer/locales/permission-center-copy.ts index 56d264a697..3a2e4713ea 100644 --- a/apps/desktop/src/renderer/locales/permission-center-copy.ts +++ b/apps/desktop/src/renderer/locales/permission-center-copy.ts @@ -54,6 +54,7 @@ export type PermissionCenterCopy = { lastRead: string; detectAgain: string; summaryAria: string; + summaryFilterAria(label: string, count: number, selected: boolean): string; granted: string; pending: string; denied: string; @@ -112,7 +113,7 @@ const PERMISSION_CENTER_COPY = { }, osStates: { unsupported: { label: '当前平台不支持', tone: 'neutral' }, unknown: { label: '无法读取状态', tone: 'neutral' }, - not_determined: { label: '等待授权', tone: 'attention' }, denied: { label: '已拒绝', tone: 'error' }, granted: { label: '已授权', tone: 'neutral' }, + not_determined: { label: '等待授权', tone: 'attention' }, denied: { label: '已拒绝', tone: 'error' }, granted: { label: '已授权', tone: 'success' }, }, loading: '正在加载权限快照', readFailed: '无法读取权限快照', noData: '权限服务未返回数据。', readAgain: '重新读取', actionFailed: '权限操作失败', @@ -126,7 +127,7 @@ const PERMISSION_CENTER_COPY = { failed: '权限操作未成功,请稍后重试。', }, title: '权限与能力', subtitle: '查看 Maka 需要的系统权限和当前授权状态,直接从这里前往「系统设置 → 隐私与安全性」完成授权或撤销,不必自己翻菜单。', - lastRead: '最近读取:', detectAgain: '重新检测', summaryAria: '权限概览', granted: '已授权', pending: '等待授权', denied: '已拒绝', other: '未知 / 不支持', + lastRead: '最近读取:', detectAgain: '重新检测', summaryAria: '按授权状态筛选系统权限', summaryFilterAria: (label, count, selected) => selected ? `${label} ${count} 项,当前筛选;再次按下显示全部` : `仅显示${label}权限,共 ${count} 项`, granted: '已授权', pending: '等待授权', denied: '已拒绝', other: '未知 / 不支持', osSection: '系统权限', osSectionHelp: 'Maka 读到的 OS 级权限状态。点击右侧按钮可以直接前往「系统设置 → 隐私与安全性」对应分区。', osListAria: '系统权限列表', capabilitiesSection: '功能能力', capabilitiesHelp: '每个能力的就绪状态由「功能开关 · 配置 · 系统权限 · 运行态探测」共同决定。', capabilityListAria: '功能能力列表', @@ -159,7 +160,7 @@ const PERMISSION_CENTER_COPY = { }, osStates: { unsupported: { label: 'Unsupported on this platform', tone: 'neutral' }, unknown: { label: 'Status unavailable', tone: 'neutral' }, - not_determined: { label: 'Waiting for permission', tone: 'attention' }, denied: { label: 'Denied', tone: 'error' }, granted: { label: 'Granted', tone: 'neutral' }, + not_determined: { label: 'Waiting for permission', tone: 'attention' }, denied: { label: 'Denied', tone: 'error' }, granted: { label: 'Granted', tone: 'success' }, }, loading: 'Loading permission snapshot', readFailed: 'Could not read permission snapshot', noData: 'The permission service returned no data.', readAgain: 'Read again', actionFailed: 'Permission action failed', @@ -173,7 +174,7 @@ const PERMISSION_CENTER_COPY = { failed: 'The permission action did not succeed. Try again later.', }, title: 'Permissions and capabilities', subtitle: 'Review the system permissions Maka needs and their current state. Open the matching Privacy & Security section directly to grant or revoke access.', - lastRead: 'Last read: ', detectAgain: 'Check again', summaryAria: 'Permission overview', granted: 'Granted', pending: 'Waiting', denied: 'Denied', other: 'Unknown / unsupported', + lastRead: 'Last read: ', detectAgain: 'Check again', summaryAria: 'Filter system permissions by authorization status', summaryFilterAria: (label, count, selected) => selected ? `${label}, ${count}; filter selected. Press again to show all permissions` : `Show only ${label.toLowerCase()} permissions, ${count}`, granted: 'Granted', pending: 'Waiting', denied: 'Denied', other: 'Unknown / unsupported', osSection: 'System permissions', osSectionHelp: 'OS-level permission states reported to Maka. Use the action on the right to open the matching Privacy & Security section in System Settings.', osListAria: 'System permission list', capabilitiesSection: 'Feature capabilities', capabilitiesHelp: 'Each readiness state combines the feature toggle, configuration, system permissions, and runtime probe.', capabilityListAria: 'Feature capability list', diff --git a/apps/desktop/src/renderer/locales/settings-health-copy.ts b/apps/desktop/src/renderer/locales/settings-health-copy.ts index bb7336ebb6..6bb4aebbb9 100644 --- a/apps/desktop/src/renderer/locales/settings-health-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-health-copy.ts @@ -44,7 +44,11 @@ export type HealthCenterCopy = { lastRead: string; refresh: string; summaryAria: string; - blockers: { send(count: number): string; capability(count: number): string }; + summaryFilterAria(label: string, count: number, selected: boolean): string; + blockers: { + send(count: number, totalCount: number): string; + capability(count: number, totalCount: number): string; + }; layerAria(label: string): string; layerListAria(label: string): string; footnote: string; @@ -86,8 +90,11 @@ const SETTINGS_HEALTH_COPY = { zh: { loading: '正在加载健康快照', readFailed: '无法读取健康快照', noData: '健康服务未返回数据。', readAgain: '重新读取', title: '健康中心', subtitle: '各项能力当前的运行状况检查。', - badge: '只读快照', lastRead: '最近一次读取:', refresh: '刷新', summaryAria: '健康摘要', - blockers: { send: (count) => `${count} 条健康信号会阻塞发送`, capability: (count) => `${count} 条健康信号会阻塞能力` }, + badge: '只读快照', lastRead: '最近一次读取:', refresh: '刷新', summaryAria: '按状态筛选健康信号', summaryFilterAria: (label, count, selected) => selected ? `${label} ${count} 项,当前筛选;再次按下显示全部` : `仅显示${label}健康信号,共 ${count} 项`, + blockers: { + send: (count, totalCount) => `全部健康信号中,${count}/${totalCount} 条会阻塞发送`, + capability: (count, totalCount) => `全部健康信号中,${count}/${totalCount} 条会阻塞能力`, + }, layerAria: (label) => `${label}健康信号`, layerListAria: (label) => `${label}健康信号列表`, footnote: '本页不直接执行测试、修复或权限变更;它只汇总当前已记录的健康信号。需要处理问题时,请进入对应设置页或重新触发相关功能。', layers: layersZh, @@ -102,8 +109,11 @@ const SETTINGS_HEALTH_COPY = { en: { loading: 'Loading health snapshot', readFailed: 'Could not read health snapshot', noData: 'The health service returned no data.', readAgain: 'Read again', title: 'Health center', subtitle: 'How each capability is currently doing.', - badge: 'Read-only snapshot', lastRead: 'Last read: ', refresh: 'Refresh', summaryAria: 'Health summary', - blockers: { send: (count) => `${count} health ${count === 1 ? 'signal blocks' : 'signals block'} sending`, capability: (count) => `${count} health ${count === 1 ? 'signal blocks' : 'signals block'} capabilities` }, + badge: 'Read-only snapshot', lastRead: 'Last read: ', refresh: 'Refresh', summaryAria: 'Filter health signals by status', summaryFilterAria: (label, count, selected) => selected ? `${label}, ${count}; filter selected. Press again to show all signals` : `Show only ${label.toLowerCase()} health signals, ${count}`, + blockers: { + send: (count, totalCount) => `Across all health signals, ${count} of ${totalCount} ${count === 1 ? 'blocks' : 'block'} sending`, + capability: (count, totalCount) => `Across all health signals, ${count} of ${totalCount} ${count === 1 ? 'blocks' : 'block'} capabilities`, + }, layerAria: (label) => `${label} health signals`, layerListAria: (label) => `${label} health signal list`, footnote: 'This page does not run tests, repairs, or permission changes. It only summarizes recorded health signals. Open the relevant settings page or retry the related feature to address an issue.', layers: layersEn, diff --git a/apps/desktop/src/renderer/settings/health-center-page.tsx b/apps/desktop/src/renderer/settings/health-center-page.tsx index 5651ffcb55..6159882f4c 100644 --- a/apps/desktop/src/renderer/settings/health-center-page.tsx +++ b/apps/desktop/src/renderer/settings/health-center-page.tsx @@ -21,6 +21,7 @@ import { useEffect, useState } from 'react'; import type { HealthSignal, HealthSignalLayer, + HealthSignalStatus, HealthSnapshot, } from '@maka/core/health'; import { HEALTH_SIGNAL_LAYERS } from '@maka/core/health'; @@ -32,6 +33,10 @@ import { SettingsPage, SettingsRow, SettingsSection } from './settings-section'; import { SettingsSkeletonStack } from './settings-skeleton'; import { dotForStatus } from '@maka/ui'; import { useRuntimeHostSettingsTarget } from './runtime-host-settings-target.js'; +import { + SettingsStatusSummaryFilter, + type SettingsStatusSummaryOption, +} from './settings-status-summary-filter'; /** * PR-UI-9 — Health Center read-only page. Consumes `window.maka.health.getSnapshot()` @@ -56,6 +61,7 @@ export function HealthCenterPage() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [refreshTick, setRefreshTick] = useState(0); + const [signalFilter, setSignalFilter] = useState(null); useEffect(() => { let cancelled = false; @@ -78,6 +84,14 @@ export function HealthCenterPage() { }; }, [host, locale, refreshTick]); + useEffect(() => { + if (!snapshot) return; + setSignalFilter((current) => { + if (!current) return current; + return snapshot.signals.some((signal) => signal.status === current) ? current : null; + }); + }, [snapshot]); + if (loading) { return ( @@ -97,15 +111,18 @@ export function HealthCenterPage() { } const healthCheckedAtMs = snapshot.checkedAt; - const signalsByLayer = groupSignalsByLayer(snapshot.signals); + const visibleSignals = signalFilter + ? snapshot.signals.filter((signal) => signal.status === signalFilter) + : snapshot.signals; + const signalsByLayer = groupSignalsByLayer(visibleSignals); const blocksSendCount = snapshot.signals.filter((signal) => signal.blocksSend).length; const blocksCapabilityCount = snapshot.signals.filter((signal) => signal.blocksCapability).length; - const summaryParts: Array<{ key: string; label: string; count: number; tone: 'neutral' | 'warning' | 'destructive' }> = [ - { key: 'ok', label: copy.statuses.ok.label, count: snapshot.summary.ok, tone: 'neutral' }, - { key: 'info', label: copy.statuses.info.label, count: snapshot.summary.info, tone: 'neutral' }, - { key: 'warning', label: copy.statuses.warning.label, count: snapshot.summary.warning, tone: 'warning' }, - { key: 'error', label: copy.statuses.error.label, count: snapshot.summary.error, tone: 'destructive' }, - { key: 'unknown', label: copy.statuses.unknown.label, count: snapshot.summary.unknown, tone: 'neutral' }, + const summaryParts: Array> = [ + { value: 'ok', label: copy.statuses.ok.label, count: snapshot.summary.ok, tone: 'neutral' }, + { value: 'info', label: copy.statuses.info.label, count: snapshot.summary.info, tone: 'neutral' }, + { value: 'warning', label: copy.statuses.warning.label, count: snapshot.summary.warning, tone: 'warning' }, + { value: 'error', label: copy.statuses.error.label, count: snapshot.summary.error, tone: 'destructive' }, + { value: 'unknown', label: copy.statuses.unknown.label, count: snapshot.summary.unknown, tone: 'neutral' }, ]; return ( @@ -132,28 +149,30 @@ export function HealthCenterPage() { )} > -

- {summaryParts.map((part) => ( - 0 ? part.tone : 'neutral'}> - {part.label} {part.count} - - ))} -

+ + value={signalFilter} + options={summaryParts} + label={copy.summaryAria} + optionLabel={(option, selected) => copy.summaryFilterAria(option.label, option.count, selected)} + onChange={setSignalFilter} + /> {blocksSendCount > 0 && ( 0 ? copy.blockers.capability(blocksCapabilityCount) : undefined} + title={copy.blockers.send(blocksSendCount, snapshot.signals.length)} + description={blocksCapabilityCount > 0 + ? copy.blockers.capability(blocksCapabilityCount, snapshot.signals.length) + : undefined} /> )} {blocksSendCount === 0 && blocksCapabilityCount > 0 && ( )} diff --git a/apps/desktop/src/renderer/settings/permission-center-page.tsx b/apps/desktop/src/renderer/settings/permission-center-page.tsx index 2465554b00..990026f785 100644 --- a/apps/desktop/src/renderer/settings/permission-center-page.tsx +++ b/apps/desktop/src/renderer/settings/permission-center-page.tsx @@ -61,6 +61,10 @@ import { import { dotForStatus } from '@maka/ui'; import { SettingsSkeletonStack } from './settings-skeleton'; import { useActionGuard } from './use-action-guard'; +import { + SettingsStatusSummaryFilter, + type SettingsStatusSummaryOption, +} from './settings-status-summary-filter'; /** * PR-UI-8 — Permission Center read-only page. Consumes `window.maka.permissions.getSnapshot()` @@ -88,6 +92,8 @@ const OS_PERMISSION_ICONS: Record> = automation: MousePointer2, }; +type PermissionStatusFilter = 'granted' | 'pending' | 'denied' | 'other'; + export function PermissionCenterPage() { const host = useRuntimeHostSettingsTarget(); const locale = useUiLocale(); @@ -98,6 +104,7 @@ export function PermissionCenterPage() { const [error, setError] = useState(null); const [refreshTick, setRefreshTick] = useState(0); const [pendingPermAction, setPendingPermAction] = useState(null); + const [permissionFilter, setPermissionFilter] = useState(null); const reportHostError = useRuntimeHostSettingsErrorReporter(); const mountedRef = useMountedRef(); const permissionActionGuard = useActionGuard(); @@ -140,6 +147,14 @@ export function PermissionCenterPage() { }; }, []); + useEffect(() => { + if (!permissions) return; + setPermissionFilter((current) => { + if (!current) return current; + return permissionIdsForFilter(permissions, current).length > 0 ? current : null; + }); + }, [permissions]); + async function runPermissionAction( permId: OsPermissionId, kind: 'request' | 'openSettings' | 'dragGrant', @@ -206,6 +221,15 @@ export function PermissionCenterPage() { const checkedAtMs = capabilities.checkedAt; const counts = summarizePermissionStatuses(permissions); + const visiblePermissionIds = permissionFilter + ? permissionIdsForFilter(permissions, permissionFilter) + : OS_PERMISSION_IDS; + const summaryFilters: Array> = [ + { value: 'granted', label: copy.granted, count: counts.granted, tone: 'success' }, + { value: 'pending', label: copy.pending, count: counts.pending, tone: 'warning' }, + { value: 'denied', label: copy.denied, count: counts.denied, tone: 'destructive' }, + { value: 'other', label: copy.other, count: counts.other, tone: 'neutral' }, + ]; return ( @@ -235,14 +259,15 @@ export function PermissionCenterPage() { )} > -

- {copy.granted} {counts.granted} - 0 ? 'warning' : 'neutral'}>{copy.pending} {counts.pending} - 0 ? 'destructive' : 'neutral'}>{copy.denied} {counts.denied} - {copy.other} {counts.other} -

+ + value={permissionFilter} + options={summaryFilters} + label={copy.summaryAria} + optionLabel={(option, selected) => copy.summaryFilterAria(option.label, option.count, selected)} + onChange={setPermissionFilter} + /> - {OS_PERMISSION_IDS.map((id) => ( + {visiblePermissionIds.map((id) => ( { + const status = snapshot.permissions[id]?.status; + if (filter === 'pending') return status === 'not_determined'; + if (filter === 'other') return status !== 'granted' && status !== 'not_determined' && status !== 'denied'; + return status === filter; + }); +} + function permissionActionFailureCopy(reason: string, message: string | undefined, copy: PermissionCenterCopy): string { switch (reason) { case 'invalid_id': @@ -619,6 +656,7 @@ function OsPermissionRow(props: { return ( = { + value: Value; + label: string; + count: number; + tone: SummaryTone; +}; + +export function SettingsStatusSummaryFilter(props: { + value: Value | null; + options: readonly SettingsStatusSummaryOption[]; + label: string; + optionLabel(option: SettingsStatusSummaryOption, selected: boolean): string; + onChange(value: Value | null): void; +}) { + return ( +
+ {props.options.map((option) => { + const selected = props.value === option.value; + return ( + + ); + })} +
+ ); +} diff --git a/apps/desktop/src/renderer/styles/settings/health.css b/apps/desktop/src/renderer/styles/settings/health.css index ccf79576af..588547ab78 100644 --- a/apps/desktop/src/renderer/styles/settings/health.css +++ b/apps/desktop/src/renderer/styles/settings/health.css @@ -32,7 +32,7 @@ ============================================================================= */ /* One quiet tabular line instead of a five-tile dashboard. Color only where - it is a signal: warning/error counts above zero. */ + it is a signal: non-zero success/warning/error counts. */ .settingsHealthSummaryLine { margin: 0; padding-block: var(--space-2); @@ -44,6 +44,10 @@ color: var(--foreground-secondary); } +.settingsHealthSummaryLine [data-tone="success"] { + color: var(--success-text); + } + .settingsHealthSummaryLine [data-tone="warning"] { color: var(--warning-text, var(--info-text)); } @@ -52,6 +56,23 @@ color: var(--destructive-text); } +.settingsStatusSummaryFilters { + gap: var(--space-1); + } + +.settingsStatusSummaryFilter { + border: var(--border-width-hairline) solid transparent; + font-variant-numeric: inherit; + transition: + background-color var(--duration-quick) var(--ease-standard), + border-color var(--duration-quick) var(--ease-standard); + } + +.settingsStatusSummaryFilter[aria-pressed="true"] { + border-color: var(--border); + background: var(--state-selected-bg); + } + .settingsHealthSignalIdentity, .settingsHealthSignalMeta { display: flex; diff --git a/apps/desktop/stories/settings/settings-pages.stories.tsx b/apps/desktop/stories/settings/settings-pages.stories.tsx index 54c552751f..3839455e21 100644 --- a/apps/desktop/stories/settings/settings-pages.stories.tsx +++ b/apps/desktop/stories/settings/settings-pages.stories.tsx @@ -1483,6 +1483,21 @@ export const PermissionCenterDiagnosticsExpanded: Story = { decorators: [withSettingsBridge], render: () => , play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const grantedFilter = await canvas.findByRole('button', { name: /^仅显示已授权权限/ }); + expect(grantedFilter).toHaveAttribute('aria-pressed', 'false'); + await userEvent.click(grantedFilter); + await waitFor(() => { + expect(grantedFilter).toHaveAttribute('aria-pressed', 'true'); + expect(canvasElement.querySelectorAll('[data-permission-id]')).toHaveLength(2); + expect(canvasElement.querySelector('[data-permission-id="screen_recording"]')).not.toBeInTheDocument(); + }); + await userEvent.click(grantedFilter); + await waitFor(() => { + expect(grantedFilter).toHaveAttribute('aria-pressed', 'false'); + expect(canvasElement.querySelectorAll('[data-permission-id]')).toHaveLength(4); + }); + // Scoped through `data-readiness` — the capability rows' own attribute — so // the story cannot latch onto some other expandable button on the page. const trigger = await waitForStoryButton( @@ -1504,6 +1519,24 @@ export const PermissionCenterDiagnosticsExpanded: Story = { export const HealthCenter: Story = { decorators: [withSettingsBridge], render: () => , + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const errorFilter = await canvas.findByRole('button', { name: /^仅显示错误健康信号/ }); + expect(errorFilter).toHaveAttribute('aria-pressed', 'false'); + await userEvent.click(errorFilter); + await waitFor(() => { + expect(errorFilter).toHaveAttribute('aria-pressed', 'true'); + expect(canvas.getByText('OpenAI Review')).toBeInTheDocument(); + expect(canvas.queryByText('Z.AI Live')).not.toBeInTheDocument(); + expect(canvas.getByText('全部健康信号中,1/6 条会阻塞发送')).toBeInTheDocument(); + expect(canvas.getByText('全部健康信号中,1/6 条会阻塞能力')).toBeInTheDocument(); + }); + await userEvent.click(errorFilter); + await waitFor(() => { + expect(errorFilter).toHaveAttribute('aria-pressed', 'false'); + expect(canvas.getByText('Z.AI Live')).toBeInTheDocument(); + }); + }, }; // Real path: 设置 → 关于 (also reachable from 反馈 in the topbar). export const About: Story = { diff --git a/docs/astryx-surface-file-inventory.md b/docs/astryx-surface-file-inventory.md index 9119fcd6a8..d471ff8e30 100644 --- a/docs/astryx-surface-file-inventory.md +++ b/docs/astryx-surface-file-inventory.md @@ -5,7 +5,7 @@ Each row is one on-disk product surface file. Regenerated inventory must stay in Wiki bar: Design Conventions · API Use-the-System · Theming · Container Padding. -**Totals:** 201 files — blocker 0, polish 0, aligned 201. +**Totals:** 202 files — blocker 0, polish 0, aligned 202. ## Exclusions (explicit) @@ -104,6 +104,7 @@ Wiki bar: Design Conventions · API Use-the-System · Theming · Container Paddi | `apps/desktop/src/renderer/settings/settings-rows.tsx` | settings-module | none | aligned — no raw controls; no Astryx JSX usage | aligned | | `apps/desktop/src/renderer/settings/settings-section.tsx` | settings-module | Divider, HStack, Heading, Item, Text, VStack | aligned — uses Astryx (Divider, HStack, Heading, Item, Text, VStack) | aligned | | `apps/desktop/src/renderer/settings/settings-skeleton.tsx` | settings-module | none | aligned — no raw controls; no Astryx JSX usage | aligned | +| `apps/desktop/src/renderer/settings/settings-status-summary-filter.tsx` | settings-module | Button | aligned — uses Astryx (Button) | aligned | | `apps/desktop/src/renderer/settings/settings-surface.tsx` | settings-module | Badge, Banner, Button, IconButton, Layout, LayoutContent, LayoutHeader, LayoutPanel, Selector, SideNav | aligned — uses Astryx (Badge, Banner, Button, IconButton, Layout, LayoutContent, LayoutHeader, LayoutPanel) | aligned | | `apps/desktop/src/renderer/settings/subagent-settings-page.tsx` | settings-page | Badge, Banner, Button, EmptyState, HStack, IconButton, Selector, VStack | aligned — uses Astryx (Badge, Banner, Button, EmptyState, HStack, IconButton, Selector, VStack) | aligned | | `apps/desktop/src/renderer/settings/tasks-settings-page.tsx` | settings-page | Button, EmptyState, HStack, List, ListItem, TextInput | aligned — uses Astryx (Button, EmptyState, HStack, List, ListItem, TextInput) | aligned | diff --git a/docs/astryx-surface-file-inventory.paths b/docs/astryx-surface-file-inventory.paths index 762e8a3cf9..c26bb527e5 100644 --- a/docs/astryx-surface-file-inventory.paths +++ b/docs/astryx-surface-file-inventory.paths @@ -76,6 +76,7 @@ apps/desktop/src/renderer/settings/settings-route-header.tsx apps/desktop/src/renderer/settings/settings-rows.tsx apps/desktop/src/renderer/settings/settings-section.tsx apps/desktop/src/renderer/settings/settings-skeleton.tsx +apps/desktop/src/renderer/settings/settings-status-summary-filter.tsx apps/desktop/src/renderer/settings/settings-surface.tsx apps/desktop/src/renderer/settings/subagent-settings-page.tsx apps/desktop/src/renderer/settings/tasks-settings-page.tsx