mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix: Prevent plugin detail hook mismatch during reload
Plugin actions refresh the selected plugin detail after mutating enabled state. The detail component can enter its loading branch while a plugin is still selected, so every hook must run before that early return. Constraint: React hook order must stay identical across the detail and loading render paths. Rejected: Clear the selected plugin before reload | would hide the current context and add UI churn during short refreshes. Confidence: high Scope-risk: narrow Directive: Keep PluginDetail hooks before loading and empty-state returns when adding detail-derived memoization. Tested: cd desktop && bun run test src/__tests__/pluginsSettings.test.tsx Tested: cd desktop && bun run lint Tested: cd desktop && bun run build Not-tested: Full desktop Vitest suite has pre-existing locale assertion failures unrelated to this plugin fix.
This commit is contained in:
parent
6d40b2befa
commit
77d2cbfab8
@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { Settings } from '../pages/Settings'
|
||||
@ -338,6 +338,59 @@ describe('Settings > Plugins tab', () => {
|
||||
expect(screen.getByText('Uninstall')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('keeps plugin detail hook order stable while the selected plugin reloads', () => {
|
||||
usePluginStore.setState({
|
||||
selectedPlugin: {
|
||||
id: 'github@claude-plugins-official',
|
||||
name: 'github',
|
||||
marketplace: 'claude-plugins-official',
|
||||
scope: 'user',
|
||||
enabled: false,
|
||||
hasErrors: false,
|
||||
isBuiltin: false,
|
||||
description: 'GitHub integration',
|
||||
componentCounts: {
|
||||
commands: 1,
|
||||
agents: 0,
|
||||
skills: 0,
|
||||
hooks: 0,
|
||||
mcpServers: 0,
|
||||
lspServers: 0,
|
||||
},
|
||||
capabilities: {
|
||||
commands: ['review-pr'],
|
||||
agents: [],
|
||||
skills: [],
|
||||
hooks: [],
|
||||
mcpServers: [],
|
||||
lspServers: [],
|
||||
},
|
||||
commandEntries: [
|
||||
{
|
||||
name: 'review-pr',
|
||||
description: 'Review the current pull request.',
|
||||
},
|
||||
],
|
||||
agentEntries: [],
|
||||
hookEntries: [],
|
||||
skillEntries: [],
|
||||
mcpServerEntries: [],
|
||||
errors: [],
|
||||
},
|
||||
})
|
||||
|
||||
const { container } = render(<Settings />)
|
||||
switchToPluginsTab()
|
||||
|
||||
expect(screen.getByText('GitHub integration')).toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
usePluginStore.setState({ isDetailLoading: true })
|
||||
})
|
||||
|
||||
expect(container.querySelector('.animate-spin')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('navigates plugin skills into the shared Skills page flow', () => {
|
||||
usePluginStore.setState({
|
||||
selectedPlugin: {
|
||||
|
||||
@ -42,6 +42,15 @@ export function PluginDetail() {
|
||||
const activeSession = sessions.find((session) => session.id === activeSessionId)
|
||||
const currentWorkDir = activeSession?.workDir || undefined
|
||||
|
||||
const otherCapabilityItems = useMemo(
|
||||
() =>
|
||||
CAPABILITY_ORDER.map((key) => ({
|
||||
key,
|
||||
items: selectedPlugin?.capabilities[key] ?? [],
|
||||
})),
|
||||
[selectedPlugin],
|
||||
)
|
||||
|
||||
if (isDetailLoading) {
|
||||
return (
|
||||
<div className="flex justify-center py-12">
|
||||
@ -54,14 +63,6 @@ export function PluginDetail() {
|
||||
|
||||
const canMutate = selectedPlugin.scope !== 'managed' && selectedPlugin.scope !== 'builtin'
|
||||
const canNavigateSharedCapabilities = selectedPlugin.enabled
|
||||
const otherCapabilityItems = useMemo(
|
||||
() =>
|
||||
CAPABILITY_ORDER.map((key) => ({
|
||||
key,
|
||||
items: selectedPlugin.capabilities[key],
|
||||
})),
|
||||
[selectedPlugin],
|
||||
)
|
||||
|
||||
const runAction = async (key: string, fn: () => Promise<string>) => {
|
||||
setActionKey(key)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user