From 16020541042054560181dcbe4d572fa044421cc6 Mon Sep 17 00:00:00 2001 From: yyhhyyyyyy Date: Wed, 27 May 2026 10:23:15 +0800 Subject: [PATCH 1/2] fix(settings): simplify danger zone entry --- .../data-settings-danger-zone-entry/plan.md | 19 +++++ .../data-settings-danger-zone-entry/spec.md | 19 +++++ .../data-settings-danger-zone-entry/tasks.md | 7 ++ .../settings/components/DataSettings.vue | 78 ++++++++----------- test/renderer/components/DataSettings.test.ts | 57 +++++++++----- 5 files changed, 116 insertions(+), 64 deletions(-) create mode 100644 docs/features/data-settings-danger-zone-entry/plan.md create mode 100644 docs/features/data-settings-danger-zone-entry/spec.md create mode 100644 docs/features/data-settings-danger-zone-entry/tasks.md diff --git a/docs/features/data-settings-danger-zone-entry/plan.md b/docs/features/data-settings-danger-zone-entry/plan.md new file mode 100644 index 000000000..0662abb85 --- /dev/null +++ b/docs/features/data-settings-danger-zone-entry/plan.md @@ -0,0 +1,19 @@ +# Implementation Plan + +## UI + +- Replace the three always-visible Danger Zone destructive buttons in `DataSettings.vue` with one outline reset entry. +- Keep the existing confirmation dialog and radio choices as the authoritative place for choosing the reset type. +- Add stable test IDs for the reset entry and dialog choices. + +## Behavior + +- Opening the reset dialog resets the selected type to `chat`. +- Confirmation continues to call `devicePresenter.resetDataByType(resetType.value)`. +- Existing disabled states for import and backup still gate both the entry and confirmation action. + +## Validation + +- Update focused renderer tests for the single entry and dialog options. +- Verify selected reset type still reaches the presenter. +- Run format, i18n, lint, and the focused Data Settings test. diff --git a/docs/features/data-settings-danger-zone-entry/spec.md b/docs/features/data-settings-danger-zone-entry/spec.md new file mode 100644 index 000000000..f0bb19038 --- /dev/null +++ b/docs/features/data-settings-danger-zone-entry/spec.md @@ -0,0 +1,19 @@ +# Data Settings Danger Zone Entry + +## User Story + +用户在数据设置页查看常规数据操作时,Danger Zone 不应以三个高权重红色按钮长期占据页面注意力;只有主动进入重置流程后,才需要看到具体的破坏性重置类型。 + +## Acceptance Criteria + +- Danger Zone 默认只显示一个低噪声的重置入口。 +- 重置入口使用 outline/destructive text 风格,不使用大面积红色背景。 +- 具体重置类型仍在确认弹窗中选择,包含聊天、知识库、配置和完全重置。 +- 默认重置类型为聊天数据。 +- 现有重置调用语义保持不变。 + +## Non-goals + +- 不改变任何重置数据语义。 +- 不新增 IPC、Presenter 方法或持久化格式。 +- 不调整 YoBrowser、数据库修复、模型配置更新等相邻操作。 diff --git a/docs/features/data-settings-danger-zone-entry/tasks.md b/docs/features/data-settings-danger-zone-entry/tasks.md new file mode 100644 index 000000000..555a39c5d --- /dev/null +++ b/docs/features/data-settings-danger-zone-entry/tasks.md @@ -0,0 +1,7 @@ +# Tasks + +- [x] Add SDD artifacts. +- [x] Replace the Danger Zone outer button group with one reset entry. +- [x] Keep dialog reset choices visible and testable. +- [x] Update renderer coverage for layout and reset behavior. +- [x] Run validation commands. diff --git a/src/renderer/settings/components/DataSettings.vue b/src/renderer/settings/components/DataSettings.vue index 2c71a1dc7..56c1ca23d 100644 --- a/src/renderer/settings/components/DataSettings.vue +++ b/src/renderer/settings/components/DataSettings.vue @@ -496,44 +496,18 @@ -
- - - -
+ {{ t('settings.data.resetConfirmTitle') }} @@ -544,7 +518,9 @@
@@ -558,7 +534,11 @@
@@ -572,7 +552,11 @@
@@ -586,7 +570,9 @@
@@ -1449,8 +1435,12 @@ const closeResetDialog = () => { resetType.value = 'chat' } -const openResetDialog = (type: 'chat' | 'knowledge' | 'all') => { - resetType.value = type +const openResetDialog = () => { + if (isResetActionDisabled.value) { + return + } + + resetType.value = 'chat' isResetDialogOpen.value = true } diff --git a/test/renderer/components/DataSettings.test.ts b/test/renderer/components/DataSettings.test.ts index 1dff59feb..662e9ce30 100644 --- a/test/renderer/components/DataSettings.test.ts +++ b/test/renderer/components/DataSettings.test.ts @@ -272,14 +272,8 @@ const findRefreshButton = (wrapper: ReturnType) => const findRepairButton = (wrapper: ReturnType) => findButtonByText(wrapper, 'settings.data.databaseRepair', 'Repair database') -const findResetAllButton = (wrapper: ReturnType) => - findButtonByText(wrapper, 'settings.data.resetAll', 'Reset all data') - -const findResetKnowledgeButton = (wrapper: ReturnType) => - findButtonByText(wrapper, 'settings.data.resetKnowledgeData', 'Reset knowledge data') - -const findResetChatButton = (wrapper: ReturnType) => - findButtonByText(wrapper, 'settings.data.resetChatData', 'Reset chat data') +const findResetEntryButton = (wrapper: ReturnType) => + findButtonByText(wrapper, 'settings.data.resetData', 'Reset data') const findResetConfirmButton = (wrapper: ReturnType) => findButtonByText(wrapper, 'settings.data.confirmReset', 'Reset confirm') @@ -307,22 +301,24 @@ describe('DataSettings', () => { expect(wrapper.text()).toContain('settings.data.dangerZone.title') expect(wrapper.text()).toContain('settings.data.resetChatData') expect(wrapper.text()).toContain('settings.data.resetKnowledgeData') + expect(wrapper.text()).toContain('settings.data.resetConfig') expect(wrapper.text()).toContain('settings.data.resetAll') expect(wrapper.text()).toContain('settings.data.yoBrowser.title') expect(wrapper.text()).toContain('settings.data.databaseEncryption.systemCredentialStore') }) - it('keeps long danger zone labels within taller wrapping buttons', async () => { + it('renders a quiet danger zone entry and keeps reset choices in the dialog', async () => { const { wrapper } = await setup() - const resetButtons = [findResetChatButton(wrapper), findResetKnowledgeButton(wrapper)] + const resetEntry = findResetEntryButton(wrapper) - for (const button of resetButtons) { - expect(button.classes()).toContain('min-h-12') - expect(button.classes()).toContain('whitespace-normal') - expect(button.find('span').classes()).toContain('min-w-0') - expect(button.find('span').classes()).toContain('leading-tight') - } + expect(resetEntry.attributes('variant')).toBe('outline') + expect(resetEntry.classes()).toContain('text-destructive') + expect(resetEntry.classes()).toContain('border-destructive/30') + expect(wrapper.find('[data-testid="danger-zone-reset-option-chat"]').exists()).toBe(true) + expect(wrapper.find('[data-testid="danger-zone-reset-option-knowledge"]').exists()).toBe(true) + expect(wrapper.find('[data-testid="danger-zone-reset-option-config"]').exists()).toBe(true) + expect(wrapper.find('[data-testid="danger-zone-reset-option-all"]').exists()).toBe(true) }) it('updates privacy mode from the data settings page', async () => { @@ -588,9 +584,8 @@ describe('DataSettings', () => { syncStore.syncEnabled = false await nextTick() - expect(findResetChatButton(wrapper).attributes('disabled')).toBeUndefined() - expect(findResetKnowledgeButton(wrapper).attributes('disabled')).toBeUndefined() - expect(findResetAllButton(wrapper).attributes('disabled')).toBeUndefined() + expect(findResetEntryButton(wrapper).attributes('disabled')).toBeUndefined() + expect(findResetConfirmButton(wrapper).attributes('disabled')).toBeUndefined() }) it('disables reset actions during import and blocks the reset handler', async () => { @@ -599,7 +594,7 @@ describe('DataSettings', () => { syncStore.isImporting = true await nextTick() - expect(findResetAllButton(wrapper).attributes('disabled')).toBeDefined() + expect(findResetEntryButton(wrapper).attributes('disabled')).toBeDefined() expect(findResetConfirmButton(wrapper).attributes('disabled')).toBeDefined() findResetConfirmButton(wrapper).vm.$emit('click') @@ -607,4 +602,26 @@ describe('DataSettings', () => { expect(presenterMocks.devicePresenter.resetDataByType).not.toHaveBeenCalled() }) + + it('defaults reset type to chat when opening the reset dialog', async () => { + const { wrapper, presenterMocks } = await setup() + + await wrapper.find('[data-testid="danger-zone-reset-option-all"]').trigger('click') + await findResetEntryButton(wrapper).trigger('click') + await findResetConfirmButton(wrapper).trigger('click') + await flushPromises() + + expect(presenterMocks.devicePresenter.resetDataByType).toHaveBeenCalledWith('chat') + }) + + it('calls resetDataByType with the selected dialog reset type', async () => { + const { wrapper, presenterMocks } = await setup() + + await findResetEntryButton(wrapper).trigger('click') + await wrapper.find('[data-testid="danger-zone-reset-option-knowledge"]').trigger('click') + await findResetConfirmButton(wrapper).trigger('click') + await flushPromises() + + expect(presenterMocks.devicePresenter.resetDataByType).toHaveBeenCalledWith('knowledge') + }) }) From 2e5a460e5f80b402e2b3e13208cd7065bfa5cad6 Mon Sep 17 00:00:00 2001 From: yyhhyyyyyy Date: Wed, 27 May 2026 10:31:21 +0800 Subject: [PATCH 2/2] chore: update mise.toml --- mise.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.toml b/mise.toml index bf2e25ace..49e254f35 100644 --- a/mise.toml +++ b/mise.toml @@ -1,3 +1,3 @@ [tools] node = "24.14.1" -pnpm = "10.13.1" +pnpm = "10.33.4"