Skip to content

Commit bc7a556

Browse files
author
catlog22
committed
feat: 增加DeepWiki页面和侧边栏导航支持,更新Hook管理功能以支持作用域和索引
1 parent fb4f6e7 commit bc7a556

9 files changed

Lines changed: 78 additions & 22 deletions

File tree

ccw/frontend/src/components/deepwiki/DocumentViewer.tsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
// ========================================
44
// Displays DeepWiki documentation content with table of contents
55

6-
import { useMemo, useState } from 'react';
6+
import { useMemo, useState, useCallback } from 'react';
77
import { useIntl } from 'react-intl';
88
import { FileText, Hash, Clock, Sparkles, AlertCircle, Link2, Check } from 'lucide-react';
99
import { Card } from '@/components/ui/Card';
1010
import { Badge } from '@/components/ui/Badge';
11-
import { Button } from '@/components/ui/Button';
1211
import { cn } from '@/lib/utils';
1312
import type { DeepWikiSymbol, DeepWikiDoc } from '@/hooks/useDeepWiki';
1413

@@ -276,20 +275,41 @@ export function DocumentViewer({
276275
</h4>
277276
<nav className="space-y-1">
278277
{symbols.map(symbol => (
279-
<a
278+
<div
280279
key={symbol.name}
281-
href={`#${symbol.anchor.replace('#', '')}`}
282-
className={cn(
283-
'block text-xs py-1.5 px-2 rounded transition-colors',
284-
'text-muted-foreground hover:text-foreground hover:bg-muted/50',
285-
'font-mono'
286-
)}
280+
className="group flex items-center gap-1"
287281
>
288-
<span className={cn('mr-1', getSymbolTypeColor(symbol.type))}>
289-
{getSymbolTypeIcon(symbol.type)}
290-
</span>
291-
{symbol.name}
292-
</a>
282+
<a
283+
href={`#${symbol.anchor.replace('#', '')}`}
284+
className={cn(
285+
'flex-1 text-xs py-1.5 px-2 rounded transition-colors',
286+
'text-muted-foreground hover:text-foreground hover:bg-muted/50',
287+
'font-mono'
288+
)}
289+
>
290+
<span className={cn('mr-1', getSymbolTypeColor(symbol.type))}>
291+
{getSymbolTypeIcon(symbol.type)}
292+
</span>
293+
{symbol.name}
294+
</a>
295+
<button
296+
onClick={() => copyDeepLink(symbol.name, symbol.anchor)}
297+
className={cn(
298+
'opacity-0 group-hover:opacity-100 p-1 rounded transition-all',
299+
'hover:bg-muted/50',
300+
copiedSymbol === symbol.name
301+
? 'text-green-500'
302+
: 'text-muted-foreground hover:text-foreground'
303+
)}
304+
title={copiedSymbol === symbol.name ? 'Copied!' : 'Copy deep link'}
305+
>
306+
{copiedSymbol === symbol.name ? (
307+
<Check className="w-3 h-3" />
308+
) : (
309+
<Link2 className="w-3 h-3" />
310+
)}
311+
</button>
312+
</div>
293313
))}
294314
</nav>
295315
</div>

ccw/frontend/src/components/hook/HookCard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface HookCardData {
3030
matcher?: string;
3131
command?: string;
3232
script?: string;
33+
scope?: 'global' | 'project';
34+
index?: number;
3335
}
3436

3537
export interface HookCardProps {

ccw/frontend/src/components/layout/Sidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
FileSearch,
2929
ScrollText,
3030
Clock,
31+
BookOpen,
3132
} from 'lucide-react';
3233
import { cn } from '@/lib/utils';
3334
import { Button } from '@/components/ui/Button';
@@ -92,6 +93,7 @@ const navGroupDefinitions: NavGroupDef[] = [
9293
icon: Brain,
9394
items: [
9495
{ path: '/memory', labelKey: 'navigation.main.memory', icon: Brain },
96+
{ path: '/deepwiki', labelKey: 'navigation.main.deepwiki', icon: BookOpen },
9597
{ path: '/skills', labelKey: 'navigation.main.skills', icon: Sparkles },
9698
{ path: '/commands', labelKey: 'navigation.main.commands', icon: Terminal },
9799
{ path: '/settings/rules', labelKey: 'navigation.main.rules', icon: Shield },

ccw/frontend/src/hooks/useCli.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,23 +514,33 @@ export function useToggleHook() {
514514

515515
export function useDeleteHook() {
516516
const queryClient = useQueryClient();
517+
const projectPath = useWorkflowStore(selectProjectPath);
517518

518519
const mutation = useMutation({
519-
mutationFn: (hookName: string) => deleteHook(hookName),
520-
onMutate: async (hookName) => {
520+
mutationFn: (hook: { name: string; scope?: 'global' | 'project'; trigger: string; index?: number }) => {
521+
const scope = hook.scope || 'project';
522+
const hookIndex = hook.index ?? 0;
523+
return deleteHook({
524+
projectPath: projectPath || undefined,
525+
scope,
526+
event: hook.trigger,
527+
hookIndex,
528+
});
529+
},
530+
onMutate: async (hook) => {
521531
await queryClient.cancelQueries({ queryKey: hooksKeys.all });
522532
const previousHooks = queryClient.getQueryData<HooksResponse>(hooksKeys.lists());
523533

524534
queryClient.setQueryData<HooksResponse>(hooksKeys.lists(), (old) => {
525535
if (!old) return old;
526536
return {
527-
hooks: old.hooks.filter((h) => h.name !== hookName),
537+
hooks: old.hooks.filter((h) => h.name !== hook.name),
528538
};
529539
});
530540

531541
return { previousHooks };
532542
},
533-
onError: (_error, _hookName, context) => {
543+
onError: (_error, _hook, context) => {
534544
if (context?.previousHooks) {
535545
queryClient.setQueryData(hooksKeys.lists(), context.previousHooks);
536546
}

ccw/frontend/src/lib/api.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4407,9 +4407,15 @@ export async function updateHookConfig(
44074407
/**
44084408
* Delete a hook
44094409
*/
4410-
export async function deleteHook(hookName: string): Promise<void> {
4411-
return fetchApi<void>(`/api/hooks/delete/${encodeURIComponent(hookName)}`, {
4410+
export async function deleteHook(params: {
4411+
projectPath?: string;
4412+
scope: 'global' | 'project';
4413+
event: string;
4414+
hookIndex: number;
4415+
}): Promise<{ success: boolean }> {
4416+
return fetchApi<{ success: boolean }>('/api/hooks', {
44124417
method: 'DELETE',
4418+
body: JSON.stringify(params),
44134419
});
44144420
}
44154421

ccw/frontend/src/locales/en/navigation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"skills": "Skills",
2424
"commands": "Commands",
2525
"memory": "Memory",
26+
"deepwiki": "DeepWiki",
2627
"prompts": "Prompt History",
2728
"settings": "Settings",
2829
"mcp": "MCP Servers",

ccw/frontend/src/locales/zh/navigation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"skills": "技能",
2424
"commands": "命令",
2525
"memory": "记忆",
26+
"deepwiki": "DeepWiki",
2627
"prompts": "提示历史",
2728
"settings": "设置",
2829
"mcp": "MCP 服务器",

ccw/frontend/src/pages/DeepWikiPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ export function DeepWikiPage() {
177177
symbols={symbols}
178178
isLoading={docLoading}
179179
error={docError}
180+
filePath={selectedFile ?? undefined}
180181
/>
181182
</div>
182183
)}

ccw/frontend/src/pages/HookManagerPage.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function isHookTriggerType(value: string): value is HookTriggerType {
5353
return ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop', 'Notification', 'SubagentStart', 'SubagentStop', 'PreCompact', 'SessionEnd', 'PostToolUseFailure', 'PermissionRequest'].includes(value);
5454
}
5555

56-
function toHookCardData(hook: { name: string; description?: string; enabled: boolean; trigger: string; matcher?: string; command?: string; script?: string }): HookCardData | null {
56+
function toHookCardData(hook: { name: string; description?: string; enabled: boolean; trigger: string; matcher?: string; command?: string; script?: string; scope?: 'global' | 'project'; index?: number }): HookCardData | null {
5757
if (!isHookTriggerType(hook.trigger)) {
5858
return null;
5959
}
@@ -64,6 +64,8 @@ function toHookCardData(hook: { name: string; description?: string; enabled: boo
6464
trigger: hook.trigger,
6565
matcher: hook.matcher,
6666
command: hook.command || hook.script,
67+
scope: hook.scope,
68+
index: hook.index,
6769
};
6870
}
6971

@@ -200,8 +202,19 @@ export function HookManagerPage() {
200202
};
201203

202204
const handleDeleteClick = async (hookName: string) => {
205+
// Find the hook in filteredHooks to get scope and index
206+
const hook = filteredHooks.find(h => h.name === hookName);
207+
if (!hook) {
208+
console.error('Hook not found:', hookName);
209+
return;
210+
}
203211
try {
204-
await deleteHook(hookName);
212+
await deleteHook({
213+
name: hook.name,
214+
scope: hook.scope,
215+
trigger: hook.trigger,
216+
index: hook.index,
217+
});
205218
} catch (error) {
206219
console.error('Failed to delete hook:', error);
207220
}

0 commit comments

Comments
 (0)