Skip to content

Refactor Sidepanel into Composables and Components#47

Merged
catx1726 merged 10 commits into
mainfrom
issue-46
Jun 2, 2026
Merged

Refactor Sidepanel into Composables and Components#47
catx1726 merged 10 commits into
mainfrom
issue-46

Conversation

@catx1726

@catx1726 catx1726 commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Closes #46. Modularize Sidepanel for better maintainability.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

代码审查报告

概述

本次 PR 旨在将 Sidepanel.vue 模块化,拆分为多个可维护的组件和 composables,以提高代码可读性和可维护性。整体方向正确,代码结构清晰,测试覆盖良好。以下是我的审查意见。

设计 (Design)

整体设计良好,将大型单文件组件拆分为职责清晰的子组件和 composables,符合 Vue 3 的最佳实践。

功能 (Functionality)

Blocking:Sidepanel.vue 中,refreshAllMarks 函数被 onMounted 中的 refreshListener 调用,但 refreshAllMarks 函数内部直接修改了 marksByUrl 的值。这会导致 useSidepanelData 中的 watch 监听器触发,进而重建 structuredMarks。然而,refreshAllMarks 函数现在位于 Sidepanel.vue 中,而不是 composable 中。如果 refreshAllMarks 在组件卸载后仍然被调用(例如,在 onUnmounted 之后收到消息),可能会导致内存泄漏或错误。建议将 refreshAllMarks 的逻辑也集成到 useSidepanelData 中,或者确保在组件卸载时正确清理消息监听器。

Nit:Sidepanel.vuehandleToggleGroupMenu 函数中,使用 ${url}|${title} 作为 activeGroupMenu 的值,这可能导致 URL 或标题中包含 | 字符时出现冲突。建议使用更可靠的唯一标识符,例如 encodeURIComponent(url) + '|' + encodeURIComponent(title),或者直接使用一个对象来存储状态。

复杂性 (Complexity)

Nit: TagFolder.vuePageSection.vue 组件接收了过多的 props,并且通过事件向上传递了大量事件。这虽然实现了组件化,但可能导致组件之间的耦合度较高。考虑是否可以将一些状态管理(如 collapsedStates, expandedTexts 等)提升到更上层的 composable 中,或者使用 provide/inject 来减少 props 的传递层级。

测试 (Tests)

测试覆盖良好,新增了针对 composables 和 tagTree 逻辑的单元测试。测试用例设计合理,覆盖了主要功能和边界情况。

Nit: useSidepanelData.spec.ts 中使用了 vi.useFakeTimers()vi.advanceTimersByTime(50) 来测试 debounce 逻辑,但测试中直接断言 structuredMarks.value 在 debounce 后等于 { mocked: true, count: 1 }。这依赖于 mock 的 buildTagTree 函数。建议在测试中也验证 buildTagTree 被调用的次数,以确保 debounce 机制正确工作。

命名 (Naming)

命名清晰,符合 Vue 和 TypeScript 的命名规范。

注释 (Comments)

注释较少,但代码本身可读性较好,大部分逻辑可以通过代码理解。

Nit:useMarkActions.ts 中,gotoMark 函数中的注释 // Notify content script 可以更具体一些,例如说明在删除标记后需要通知 content script 移除高亮。

风格 (Style)

代码风格基本符合 Vue 和 TypeScript 的规范。

Nit:Sidepanel.vue 的模板中,部分事件处理函数(如 @edit-mark)使用了内联箭头函数。虽然这很常见,但为了更好的可读性和性能,建议将这些逻辑提取为组件的方法。

一致性 (Consistency)

代码风格与现有代码库基本一致。

文档 (Documentation)

SOP 合规检查: PR 关联了 Issue #46,但 docs/superpowers/ 目录下未发现对应的 Spec/Plan 文档。建议在后续开发中,为较大的重构或新功能添加相应的设计文档,以便团队成员更好地理解变更背景和设计思路。

总结

本次 PR 成功地将 Sidepanel.vue 模块化,显著提高了代码的可维护性和可测试性。代码质量较高,测试覆盖良好。

  • 严重问题 (Blocking): 1
  • 轻微建议 (Nit): 4

LGTM (在解决上述 Blocking 问题后)

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

代码审查报告

总体评价

这是一个高质量的模块化重构,成功将 800+ 行的单体 Sidepanel.vue 拆分为 5 个 composables 和 5 个 UI 组件。整体架构清晰,职责分离合理,测试覆盖完善。代码质量显著提升,符合代码健康度改进的目标。

SOP 合规检查

  • PR 关联 Issue: Closes #46 - 正确关联
  • ⚠️ 文档检查: 未在 docs/superpowers/ 目录下找到对应的 Spec/Plan 文档。建议在后续类似重构中,提前创建设计文档以记录架构决策。

详细审查

严重问题 (Blocking)

1. [功能] Sidepanel.vue 中的 Tag Picker Dialog 状态管理存在潜在问题

位置: src/sidepanel/Sidepanel.vue:226-232

<div
  v-if="tagPickerUrl"
  class="fixed inset-0 bg-black/40 flex items-center justify-center z-50"
  @click.self="tagPickerUrl = null"
>

问题: 这里直接使用 tagPickerUrl 作为 dialog 显示的条件,但 useTagActions 中的 tagPickerUrl 在 dialog 关闭后没有被重置为 null。当用户点击 "完成" 按钮时,只是将 tagPickerUrl 设为 null,但 tagPickerMarkId 没有被清理。这可能导致下次打开时状态残留。

建议: 创建一个统一的 closeTagPicker 函数来同时重置 tagPickerUrltagPickerMarkId

function closeTagPicker() {
  tagPickerUrl.value = null
  tagPickerMarkId.value = null
}

并在 "完成" 按钮和背景点击时调用此函数。


2. [功能] useTagActions 中的 togglePageTag 存在竞态条件

位置: src/sidepanel/composables/useTagActions.ts:72-98

async function togglePageTag(tagId: string) {
  if (!tagPickerUrl.value) return
  const url = tagPickerUrl.value
  const marks = marksByUrl.value[url]
  if (!marks) return
  // ... 发送多个 update-mark-details 请求
}

问题: 当用户快速切换标签时,tagPickerUrl 可能在异步操作执行期间发生变化,导致操作在错误的 URL 上执行。虽然当前代码在函数入口处捕获了 tagPickerUrl 的快照,但 marksByUrl.value[url] 的引用可能在执行过程中被外部修改。

建议: 考虑使用 structuredClonetoRaw 来创建数据的快照,或者添加一个简单的版本号机制来检测状态变化:

async function togglePageTag(tagId: string) {
  const currentUrl = tagPickerUrl.value
  if (!currentUrl) return
  
  const marksSnapshot = toRaw(marksByUrl.value[currentUrl])
  if (!marksSnapshot) return
  
  // 使用 marksSnapshot 进行操作
}

轻微建议 (Nit)

1. [设计] useUIState 中的 tagPickerVisible 未使用

位置: src/sidepanel/composables/useUIState.ts:14

const tagPickerVisible = ref(false)

问题: 这个 ref 被定义但从未在 composable 外部使用。Sidepanel.vue 使用 tagPickerUrl 来控制 dialog 的显示。这是一个死代码。

建议: 移除未使用的 tagPickerVisible,或者将其与 tagPickerUrl 的状态同步。


2. [命名] useTagActions 中的 _editingTagId 命名不一致

位置: src/sidepanel/Sidepanel.vue:70

const {
  newTagName,
  tagPickerUrl,
  editingTagId: _editingTagId,
  // ...
} = useTagActions()

问题: 使用下划线前缀 _editingTagId 表示未使用,但这是 TypeScript 中的一种约定,而不是强制性的。如果确实不需要,应该考虑是否应该从返回对象中排除。

建议: 如果 editingTagIdSidepanel.vue 中不需要直接访问,可以考虑不在 composable 的返回值中暴露它,或者使用 // @ts-ignore 注释。


3. [测试] useSidepanelData.spec.ts 的测试不够健壮

位置: src/sidepanel/composables/__tests__/useSidepanelData.spec.ts:41-44

vi.advanceTimersByTime(50)
await nextTick()
expect(structuredMarks.value).toEqual({ mocked: true, count: 1 })

问题: 测试依赖于 setTimeout 的精确计时,这在 CI 环境中可能不稳定。此外,测试没有验证 buildTagTree 是否被正确调用。

建议: 使用 vi.advanceTimersByTime 后,可以添加对 buildTagTree 调用参数的验证:

import { buildTagTree } from '~/logic/tagTree'

// 在测试中
vi.advanceTimersByTime(50)
await nextTick()
expect(buildTagTree).toHaveBeenCalledWith({ url1: [] }, {})

4. [可维护性] useMarkActions 中的 URL 规范化逻辑重复

位置: src/sidepanel/composables/useMarkActions.ts:20-25

问题: getNormalizedUrl 函数在 useMarkActions 中定义,但类似的 URL 规范化逻辑也存在于 Sidepanel.vuebroadcastRefreshToTabs 函数中(虽然没有显式调用,但存在隐式的 URL 比较)。如果未来需要修改 URL 规范化规则,需要在多处修改。

建议: 将 getNormalizedUrl 提取到共享的 utility 模块(如 ~/logic/url.ts)中,以便在 composable 和组件之间共享。


5. [样式] Sidepanel.vue 中的 handleToggleMarkMenuhandleToggleUrlMenu 函数

位置: src/sidepanel/Sidepanel.vue:130-136

function handleToggleMarkMenu(markId: string) {
  activeMarkMenu.value = activeMarkMenu.value === markId ? null : markId
}

function handleToggleUrlMenu(url: string) {
  activeUrlMenu.value = activeUrlMenu.value === url ? null : url
}

问题: 这些函数与 useUIState 中的 closeMenus 功能重叠,但 useUIState 没有提供 toggle 函数。这导致 toggle 逻辑分散在组件和 composable 之间。

建议: 考虑在 useUIState 中添加通用的 toggle 函数,或者将这些 toggle 逻辑也移到 composable 中。


6. [文档] 缺少对新组件的 JSDoc 注释

问题: 新创建的组件和 composable 缺少文档注释。虽然代码本身相对清晰,但添加简短的 JSDoc 可以帮助新开发者快速理解每个 composable 的职责。

建议: 为每个 composable 添加简短的 JSDoc:

/**
 * 管理侧边栏的核心数据状态,包括标记树构建和后台数据同步。
 * 通过 watch 监听 marksByUrl 和 tagsMetadata 的变化,自动重建标记树。
 * 使用 debounce 机制避免频繁重建。
 */
export function useSidepanelData() { ... }

总结

这是一个优秀的重构工作,显著提升了代码的可维护性和可测试性。主要问题集中在状态管理的边界情况处理上,建议在合并前修复。

类别 数量
严重问题 (Blocking) 2
轻微建议 (Nit) 6

LGTM 意见: 在修复上述 2 个 Blocking 问题后,本 PR 可以合并。代码整体质量提升明显,符合代码健康度改进的目标。

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

好的,作为一名 Google 资深工程师,我已经仔细审阅了这份代码变更。本次重构的目标明确,旨在将庞大的 Sidepanel.vue 模块化,以提升可维护性和可测试性。总体而言,这是一个非常优秀的重构,代码质量很高,结构清晰,测试覆盖也相当不错。下面是我的详细审查意见。


代码审查报告

总体评价

本次重构非常成功。将超过 800 行的单体文件拆分为 5 个职责单一的 Composables 和 5 个聚焦的 UI 组件,极大地提升了代码的可读性、可维护性和可测试性。事件驱动(Emit)的组件通信模式虽然略显繁琐,但对于保持组件的纯净性和可追踪性来说是一个正确的选择。新增的单元测试覆盖了核心逻辑,这是非常值得肯定的。

我看到了开发者对代码健康度的深刻理解,尤其是在处理生命周期安全、状态正交性以及组件通信成本等关键挑战时所做的权衡。这份 CL 毫无疑问地提升了系统的整体代码健康度

设计 (Design)

  • 优秀:模块化的架构设计非常合理。useSidepanelData 负责数据获取与监听,useUIState 管理纯 UI 状态,useTagActionsuseMarkActions 封装了业务操作,useStorageMonitor 管理存储。这种关注点分离是 Vue 3 Composition API 的最佳实践。

功能 (Functionality)

  • 优秀:核心功能得到了保留和正确迁移。refreshAllMarks 和消息监听器被正确集成到 useSidepanelData 中,并确保了生命周期安全。broadcastRefreshToTabs 函数在 Sidepanel.vue 中被保留,用于处理全局刷新,逻辑清晰。

复杂性 (Complexity)

  • 优秀:重构显著降低了 Sidepanel.vue 的复杂性。新的组件和 Composables 职责单一,易于理解和修改。没有发现过度工程化的迹象。

测试 (Tests)

  • 优秀:为 buildTagTreeuseMarkActionsuseSidepanelDatauseStorageMonitoruseTagActionsuseUIState 添加了单元测试。这些测试覆盖了核心逻辑和边界情况,是本次重构的一大亮点。

命名 (Naming)

  • 良好:命名清晰且具有描述性,如 useSidepanelDataTagFolderMarkItemhandleToggleMarkMenu 等事件处理函数名称明确了其用途。

注释 (Comments)

  • 良好:代码中的注释(如 // Logic Composables, // Event Handlers)有助于理解代码结构。Self-Reflection 部分非常有价值,体现了开发者的深度思考。

风格 (Style)

  • 良好:整体代码风格与项目保持一致。使用了 @click.stop 来阻止事件冒泡,这是处理嵌套菜单的正确做法。

一致性 (Consistency)

  • 优秀:重构后的代码在组件间使用了一致的 v-model 和事件发射模式。

文档 (Documentation)

  • 良好:更新了 ops_changelog.mdNIT_ROADMAP.md,记录了本次重构和后续的改进建议。

具体审查意见

严重问题 (Blocking)

  1. [Blocking] Sidepanel.vue 中的 tagPickerUrl 状态管理存在潜在问题
  • 文件: src/sidepanel/Sidepanel.vue
  • 行号: 约 250-300
  • 问题描述: 在模板中,Tag Picker Dialog 的显示条件由 v-if="tagPickerUrl" 控制。然而,tagPickerUrl 是由 useTagActions composable 提供的。当用户关闭 Dialog 时,closeTagPicker() 会将 tagPickerUrl 设为 null,这会导致 Dialog 消失。但是,如果用户通过点击 Dialog 外部(@click.self="closeTagPicker")关闭,这没问题。然而,tagPickerVisible 这个状态(在 useUIState 中定义)似乎没有被使用,而 v-if 直接依赖于 tagPickerUrl。这本身不是 bug,但 tagPickerUrl 同时承担了“是否显示”和“为哪个 URL 显示”两个职责,这可能会在未来引入问题。例如,如果其他地方意外修改了 tagPickerUrl,Dialog 会意外关闭或打开。
  • 建议: 将“是否显示”和“显示内容”的状态分离。使用一个独立的 isTagPickerVisible ref 来控制 Dialog 的显示,而 tagPickerUrltagPickerMarkId 仅用于传递数据。这会使状态管理更清晰、更健壮。
  • 修改示例:
    // useUIState.ts
    const tagPickerVisible = ref(false)
    
    // useTagActions.ts
    function openTagPicker(url: string, markId: string | null = null) {
      tagPickerUrl.value = url
      tagPickerMarkId.value = markId
      tagPickerVisible.value = true // 从 useUIState 导入或通过参数传入
    }
    
    function closeTagPicker() {
      tagPickerUrl.value = null
      tagPickerMarkId.value = null
      tagPickerVisible.value = false
    }
    
    // Sidepanel.vue 模板
    <div v-if="tagPickerVisible" ...>
  1. [Blocking] useMarkActions 中的 removeMark 函数未处理 sendMessage 失败的情况
  • 文件: src/sidepanel/composables/useMarkActions.ts
  • 行号: 约 60
  • 问题描述: removeMark 函数在调用 sendMessage('remove-mark', mark, 'background') 后,没有检查其返回值或捕获可能的异常。如果后台脚本处理失败(例如,由于存储错误),前端将无法得知,并会继续尝试通知 content script,导致状态不一致。
  • 建议: 对 sendMessage 调用添加 try-catch 或检查其返回值。如果后台操作失败,应停止后续操作,并向用户显示错误提示(例如,通过一个全局的错误处理机制或返回一个错误状态)。当前代码虽然有一个 result 检查,但仅针对 (result as any).success === false 的情况,这不够健壮。
  • 修改示例:
    async function removeMark(mark: Mark) {
      if (!confirm('确定要删除此标记吗?'))
        return
      try {
        const result = await sendMessage('remove-mark', mark, 'background')
        if (result && (result as any).success === false) {
          console.error('Failed to remove mark:', (result as any)?.error)
          // TODO: 显示用户友好的错误提示
          return
        }
      } catch (error) {
        console.error('Failed to send remove-mark message:', error)
        // TODO: 显示用户友好的错误提示
        return
      }
      // ... 后续通知 content script 的逻辑
    }

轻微建议 (Nit)

  1. [Nit] useSidepanelData 中的 refreshListener 应使用 shallowRef 优化
  • 文件: src/sidepanel/composables/useSidepanelData.ts
  • 行号: 约 10
  • 问题描述: structuredMarks 是一个大型的响应式对象。每次 buildTagTree 返回新对象时,Vue 都会深度追踪其所有属性。如果这个对象非常庞大,可能会带来性能开销。
  • 建议: 考虑使用 shallowRef 来存储 structuredMarks。由于 buildTagTree 每次都会返回一个全新的对象,Vue 只需要追踪这个顶层引用的变化,而无需深入追踪嵌套对象的属性。这可以显著减少大型数据结构的响应式开销。
  • 修改示例:
    import { shallowRef } from 'vue'
    const structuredMarks = shallowRef<TagTree>({ ... })
    // ... 在 watch 回调中
    structuredMarks.value = buildTagTree(marksByUrl.value, tagsMetadata.value)
  1. [Nit] PageSection.vue 中的 getLevelClassgetLevelBorderStyle 函数可以提取为共享工具函数
  • 文件: src/sidepanel/components/PageSection.vue
  • 行号: 约 50-75
  • 问题描述: 这两个函数是纯函数,不依赖于组件的任何响应式状态。它们目前被定义在组件内部,如果未来有其他组件也需要类似的样式映射,就需要重复定义。
  • 建议: 将这两个函数提取到一个独立的工具文件(例如 src/sidepanel/utils/styleHelpers.ts)中,并在需要的地方导入。这遵循了 DRY 原则,并使组件更专注于 UI 逻辑。
  1. [Nit] 考虑为 TagFolderPageSection 组件添加 display: contents 或类似优化
  • 文件: src/sidepanel/components/TagFolder.vue, src/sidepanel/components/PageSection.vue
  • 问题描述: 当前组件树是 Sidepanel.vue -> TagFolder -> PageSection -> MarkItem。每个组件都包裹了一层 DOM 元素(如 details, section, li)。对于大型列表,这可能会导致 DOM 节点过多。
  • 建议: 如果这些包裹层只是为了逻辑分组而不需要特定的样式,可以考虑在 TagFolderPageSection 的根元素上使用 display: contents(如果浏览器兼容性允许),或者将它们的模板直接内联到父组件中(但这会牺牲模块化)。这是一个可选的性能优化点,当前架构的清晰度比微小的性能提升更重要,所以作为 Nit 提出。
  1. [Nit] useMarkActions 中的 getNormalizedUrl 函数与 src/logic/url.ts 中可能存在的函数重复
  • 文件: src/sidepanel/composables/useMarkActions.ts
  • 行号: 约 20
  • 问题描述: 开发者自己在 NIT_ROADMAP.md 中提到了 getNormalizedUrl 在多处重复定义的问题,并建议提取到共享模块。这是一个很好的自我发现。
  • 建议: 既然已经意识到了这个问题,建议在当前 CL 中就完成这个提取工作,或者至少创建一个 TODO 注释并关联一个 Issue。这可以立即消除代码重复,并提升一致性。
  1. [Nit] 测试覆盖率可以进一步提升
  • 文件: src/sidepanel/composables/__tests__/
  • 问题描述: 当前的测试覆盖了主要 composable 的核心功能,非常好。但可以进一步覆盖一些边界情况,例如:
    • useMarkActionsexportToMarkdownexportTagFolder 函数(可以验证生成的 Markdown 字符串格式)。
    • useSidepanelDataonUnmounted 是否正确清理了监听器和定时器。
    • useStorageMonitorcleanupOldMarkscleanupUselessMarks 在用户取消确认时的行为。

总结

  • 严重问题 (Blocking): 2
  • 轻微建议 (Nit): 5

本次重构非常出色,代码质量很高。请优先解决上述两个 Blocking 问题,它们关系到应用的健壮性和状态管理的清晰度。对于 Nit 建议,请根据您的时间和优先级酌情处理,它们主要是为了进一步提升代码质量和性能。

在修复 Blocking 问题后,我认为这个 CL 已经准备好合并了。感谢您为提升代码库健康度所做的出色工作!

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

好的,作为一名 Google 资深工程师,我将根据您提供的《审查标准》和《审查清单》对本次变更进行代码审查。

总体评价

本次变更的核心目标是将庞大的单体组件 Sidepanel.vue 模块化为多个功能单一的 Composables 和 UI 组件。这是一个非常正确的方向,极大地提升了代码的可维护性、可读性和可测试性。整体设计思路清晰,架构合理,测试覆盖也比较全面。代码质量很高,我非常认可作者在重构中付出的努力和对软件设计原则的坚持。

从整体来看,本次变更显著提升了系统的代码健康度。尽管存在一些可以改进的地方,但根据我们的审查原则,我倾向于批准这个 CL。


审查意见

1. 设计 (Design)

  • LGTM: 将 800+ 行的单体组件拆分为 5 个领域驱动的 Composables 和 5 个 UI 组件,这种关注点分离的设计非常出色。每个 Composable 和组件都职责单一,接口清晰,很好地遵循了 Vue 3 组合式 API 的最佳实践。
  • LGTM: 采用 Props 和 Emit 进行组件间通信,避免了全局 Store 带来的复杂性,保持了组件的纯净性和可追踪性。虽然事件转发略显繁琐,但对于这个规模的组件树来说,这是一个权衡得当的选择。

2. 功能 (Functionality)

  • LGTM: 从代码逻辑上看,核心功能(标签管理、标记操作、存储监控)都被正确地提取和封装。测试用例也覆盖了主要的功能路径和边界情况,例如 buildTagTree 的空数据、多标签、已删除标记等场景。

3. 复杂度 (Complexity)

  • Nit: useMarkActions.ts 中的 exportToMarkdownexportTagFolder 函数存在大量重复逻辑。 这两个函数都用于生成 Markdown 内容,核心区别仅在于标题格式和遍历的数据结构。可以考虑提取一个通用的 generateMarkdownexportToMarkdown 工具函数,接受一个配置对象(如标题模板、数据源)来减少重复。这符合 DRY 原则,也使得未来添加新的导出功能(如导出单个分组)更加容易。

    // 建议:提取一个通用的导出函数
    function exportToMarkdown(config: { title: string; contentGenerator: () => string }) {
      const markdown = `**${config.title}**\n\n---\n\n${config.contentGenerator()}`
      downloadMarkdown(markdown, config.title)
    }

4. 测试 (Tests)

  • LGTM: 测试覆盖了所有新创建的 Composables (useMarkActions, useSidepanelData, useStorageMonitor, useTagActions, useUIState) 和核心逻辑 buildTagTree。测试用例设计合理,Mock 得当,能够有效验证代码的正确性。
  • Nit: 在 useSidepanelData.spec.ts 中,测试了 structuredMarks 在存储变化后的更新。可以考虑增加一个测试,验证组件在 onUnmounted 后,即使存储发生变化,structuredMarks 也不会再更新。这可以确保防内存泄漏的机制是有效的。

5. 命名 (Naming)

  • LGTM: 文件名、函数名、变量名都清晰易懂,准确地反映了其用途。例如 useSidepanelData, useUIState, TagFolder, MarkItem 等。

6. 注释 (Comments)

  • LGTM: 代码中的注释精简且有效,主要集中在解释“为什么”这么做(例如 useSidepanelData.ts 中关于 watchdebounce 的注释),而不是“是什么”。这符合我们的注释原则。

7. 风格 (Style)

  • Nit: 在 Sidepanel.vue 中,事件处理函数 handleToggleMarkMenu, handleToggleUrlMenu 等,其内部逻辑非常简单(仅一行三元表达式)。对于这种简单的转发逻辑,直接在模板中使用内联表达式可能会更简洁,例如 @toggle-mark-menu="id => activeMarkMenu = activeMarkMenu === id ? null : id"。当然,当前的方式更有利于在将来扩展逻辑,所以这只是一个个人风格偏好。

8. 一致性 (Consistency)

  • LGTM: 代码风格与项目中已有的代码保持一致,例如使用了 ~ 路径别名,采用了 Vue 3 <script setup> 语法等。

9. 文档 (Documentation)

  • LGTM: 变更包含了详细的 docs/superpowers/plans/docs/superpowers/specs/ 文档,清晰地描述了重构的目标、架构和实施计划。同时更新了 NIT_ROADMAP.md,将已完成的任务标记为 [已完成],并添加了新的改进建议。文档工作做得非常出色。

10. 每一行代码 (Every Line)

  • LGTM: 我审查了所有变更的代码,没有发现明显的逻辑错误或潜在的 Bug。

11. 上下文 (Context)

  • LGTM: 从整个系统的角度来看,这次重构是成功的。它没有引入新的复杂性,反而通过模块化降低了系统的整体复杂度,提升了代码健康度。

12. SOP 合规检查

  • LGTM: PR 关联了 Issue (Closes #46),并且 docs/superpowers/ 目录下存在对应的 2026-06-01-sidepanel-refactoring.md (Plan) 和 2026-06-01-sidepanel-refactoring-design.md (Spec) 文档。完全符合 SOP 要求。

总结

本次重构工作非常出色,设计精良,实现稳健,测试充分,文档齐全。它毫无疑问地提升了项目的整体代码健康度。

  • 严重问题 (Blocking): 0
  • 轻微建议 (Nit): 2
  • LGTM: 同意批准。 建议作者在后续的 CL 中考虑对 useMarkActions.ts 中的 Markdown 导出逻辑进行去重优化。

@catx1726 catx1726 merged commit b2f20ba into main Jun 2, 2026
3 checks passed
github-actions Bot added a commit that referenced this pull request Jun 2, 2026
catx1726 added a commit that referenced this pull request Jun 10, 2026
Replace the passive disambiguation modal with active recovery status
management. Restoration now branches by confidence level:

- High (>=95%): auto-restore with default style, mark as 'restored'
- Medium (85-95%): auto-restore with pending-confirm dashed style
- Low (<85% or no candidates): skip highlight, mark as 'needs-recalibration'

Also fixes L3 search boundary filtering issues:
- Remove 'div' from structureBoundaries query to reduce over-filtering
- Add fallback to boundary-free search when all combos are filtered
- Clamp suggestRange results and fix LocalAligner endMin overflow

Issue #47
catx1726 added a commit that referenced this pull request Jun 10, 2026
Replace the passive disambiguation modal with active recovery status
management. Restoration now branches by confidence level:

- High (>=95%): auto-restore with default style, mark as 'restored'
- Medium (85-95%): auto-restore with pending-confirm dashed style
- Low (<85% or no candidates): skip highlight, mark as 'needs-recalibration'

Also fixes L3 search boundary filtering issues:
- Remove 'div' from structureBoundaries query to reduce over-filtering
- Add fallback to boundary-free search when all combos are filtered
- Clamp suggestRange results and fix LocalAligner endMin overflow

Issue #47
catx1726 added a commit that referenced this pull request Jun 10, 2026
Replace the passive disambiguation modal with active recovery status
management. Restoration now branches by confidence level:

- High (>=95%): auto-restore with default style, mark as 'restored'
- Medium (85-95%): auto-restore with pending-confirm dashed style
- Low (<85% or no candidates): skip highlight, mark as 'needs-recalibration'

Also fixes L3 search boundary filtering issues:
- Remove 'div' from structureBoundaries query to reduce over-filtering
- Add fallback to boundary-free search when all combos are filtered
- Clamp suggestRange results and fix LocalAligner endMin overflow

Issue #47
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.

Refactor Sidepanel into Composables and Components

1 participant