{t('settingsWidthTitle')}
@@ -277,27 +282,25 @@ export function SideCardSection({ store, service }: SideCardSectionProps) {
- {/* 侧边栏内容: one row per registered tab type, icon + type id + switch,
- plus each tab's declaratively-declared nested toggles (shown only
- while the tab itself is enabled). */}
+ {/* 侧边栏内容: one card per registered tab type, icon + type id; the
+ card's `settings.toggles` render as nested smaller cards while the
+ tab itself is enabled. */}
{viewers.map(viewer => (
- {renderRow({
+ {renderCard({
title: textOf(viewer.title) || viewer.id,
desc: viewer.exts.length === 0 ? t('settingsViewerCatchAll') : viewer.exts.join(' · '),
icon: iconOf(viewer.icon, 16),
- checked: prefs.viewersEnabled[viewer.id] !== false,
- label: textOf(viewer.title) || viewer.id,
+ enabled: prefs.viewersEnabled[viewer.id] !== false,
onToggle: (next) => { onToggleViewer(viewer.id, next) },
})}
diff --git a/src/client/locales.ts b/src/client/locales.ts
index 6850a31..a6140cc 100644
--- a/src/client/locales.ts
+++ b/src/client/locales.ts
@@ -123,6 +123,7 @@ const zh = {
settingsToolsDesc: '开启后,模型可通过 terminal_create 等 8 个工具创建并操作侧边栏终端(默认关闭)',
settingsTabsTitle: '侧边栏内容',
settingsViewersTitle: '文件预览',
+ settingsGeneralTitle: '常规',
settingsViewerCatchAll: '兜底:任意文件',
viewerImage: '图片',
viewerPdf: 'PDF',
@@ -267,6 +268,7 @@ const en: Record = {
settingsToolsDesc: 'When enabled, the model can create and drive sidebar terminals through the 8 terminal_* tools (off by default)',
settingsTabsTitle: 'Sidebar content',
settingsViewersTitle: 'File viewers',
+ settingsGeneralTitle: 'General',
settingsViewerCatchAll: 'Catch-all: any file',
viewerImage: 'Image',
viewerPdf: 'PDF',
diff --git a/tests/side-card-section.spec.tsx b/tests/side-card-section.spec.tsx
index 8c11774..3b70119 100644
--- a/tests/side-card-section.spec.tsx
+++ b/tests/side-card-section.spec.tsx
@@ -1,11 +1,13 @@
/**
* Side card settings section render tests: the section is DECLARATIVE —
- * every row (icon, title, type id, extensions, enable switch) derives from
+ * every card (icon, title, type id, extensions, on/off state) derives from
* the sidebar service's tab/viewer registries instead of hardcoded copy.
- * These specs pin that derivation with a fake store + a small registry:
+ * The toggles are CARDS: the whole card is the switch, the visual state IS
+ * the state (highlighted = enabled), announced via `aria-pressed`. These
+ * specs pin that derivation with a fake store + a small registry:
* registered tabs/viewers appear with their declared icon/title/settings,
- * disabled states render unchecked, and a disabled tab hides its declared
- * nested toggles.
+ * disabled states render pressed=false, and a disabled tab hides its
+ * declared nested cards.
*
* Rendered with renderToString (mount effects — the settings RPC sync — do
* not run in SSR; the initial store prefs are the render input).
@@ -60,58 +62,61 @@ function renderSection(store: SidebarStore, service: BetterSidebarService): stri
))
}
+/** Count `aria-pressed` occurrences of one value in the rendered HTML. */
+function pressedCount(html: string, value: string): number {
+ return html.match(new RegExp(`aria-pressed="${value}"`, 'g'))?.length ?? 0
+}
+
describe('SideCardSection declarative inventory', () => {
- it('renders one row per registered tab: icon + title + type id + switch', () => {
+ it('renders one card per registered tab: icon + title + type id + pressed state', () => {
const { store, service } = mount()
const html = renderSection(store, service)
expect(html).toContain('data-icon="explorer"')
expect(html).toContain('>Explorer<')
- // The type id is the row's desc (the declarative "type" surface).
+ // The type id is the card's desc (the declarative "type" surface).
expect(html).toContain('>explorer<')
expect(html).toContain('data-icon="subagent"')
expect(html).toContain('>Subagents<')
- // Both tabs are enabled by default: their switches render checked.
- expect(html.match(/type="checkbox"/g)?.length).toBeGreaterThanOrEqual(4)
+ // Default prefs: openByDefault + both tabs + the nested auto-open + the
+ // image viewer are all enabled → 5 cards pressed, none pressed=false.
+ expect(pressedCount(html, 'true')).toBe(5)
+ expect(pressedCount(html, 'false')).toBe(0)
})
- it('renders the declared nested toggle under an enabled tab', () => {
+ it('renders the declared nested card under an enabled tab', () => {
const { store, service } = mount()
const html = renderSection(store, service)
// The subagent tab's declared related setting appears while enabled,
- // bound to its prefs key (default true → checked).
+ // bound to its prefs key (default true → pressed).
expect(html).toContain('Auto-open Subagents')
expect(html).toContain('Expand on new subagent')
})
- it('hides the nested toggles while the parent tab is disabled', () => {
+ it('hides the nested cards while the parent tab is disabled', () => {
const { store, service } = mount()
store.setPrefs({ ...store.getPrefs(), tabsEnabled: { subagent: false } })
const html = renderSection(store, service)
- // The parent row stays (so the user can re-enable it) but unchecked.
+ // The parent card stays (so the user can re-enable it) but pressed=false.
expect(html).toContain('>Subagents<')
expect(html).not.toContain('Auto-open Subagents')
+ expect(pressedCount(html, 'false')).toBe(1)
})
- it('renders one row per registered viewer: icon + title + exts + switch', () => {
+ it('renders one card per registered viewer: icon + title + exts', () => {
const { store, service } = mount()
const html = renderSection(store, service)
expect(html).toContain('data-icon="image"')
expect(html).toContain('>Image<')
- // The covered extensions are the row's desc.
+ // The covered extensions are the card's desc.
expect(html).toContain('png · jpg')
})
- it('a disabled viewer renders unchecked', () => {
+ it('a disabled viewer renders pressed=false', () => {
const { store, service } = mount()
store.setPrefs({ ...store.getPrefs(), viewersEnabled: { image: false } })
const html = renderSection(store, service)
expect(html).toContain('>Image<')
- })
-
- it('a disabled tab renders unchecked (its switch reflects the prefs map)', () => {
- const { store, service } = mount()
- store.setPrefs({ ...store.getPrefs(), tabsEnabled: { explorer: false } })
- const html = renderSection(store, service)
- expect(html).toContain('>explorer<')
+ expect(pressedCount(html, 'false')).toBe(1)
+ expect(pressedCount(html, 'true')).toBe(4)
})
})