mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
- Added `resume-strategy.ts` to determine optimal resume approaches including native, prompt concatenation, and hybrid modes. - Introduced `determineResumeStrategy` function to evaluate various resume scenarios. - Created utility functions for building context prefixes and formatting outputs in plain, YAML, and JSON formats. - Added `session-content-parser.ts` to parse native CLI tool session files supporting Gemini/Qwen JSON and Codex JSONL formats. - Implemented parsing logic for different session formats, including error handling for invalid lines. - Provided functions to format conversations and extract user-assistant pairs from parsed sessions.
793 lines
47 KiB
HTML
793 lines
47 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" data-theme="light">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>CCW Dashboard</title>
|
|
<!-- Tailwind CSS (本地) -->
|
|
<script src="./assets/js/tailwind.js"></script>
|
|
<script>
|
|
tailwind.config = {
|
|
darkMode: ['class', '[data-theme="dark"]'],
|
|
safelist: [
|
|
// Background colors
|
|
'bg-card', 'bg-background', 'bg-hover', 'bg-accent', 'bg-muted', 'bg-primary', 'bg-success', 'bg-warning',
|
|
'bg-success-light', 'bg-warning-light', 'bg-primary-light', 'bg-sidebar-background', 'bg-destructive',
|
|
'bg-destructive/5', 'bg-destructive/10', 'bg-warning/5',
|
|
'bg-info', 'bg-info-light', 'bg-indigo', 'bg-indigo-light', 'bg-orange', 'bg-orange-light',
|
|
// Text colors
|
|
'text-foreground', 'text-muted-foreground', 'text-primary', 'text-card-foreground', 'text-success', 'text-warning',
|
|
'text-primary-foreground', 'text-accent-foreground', 'text-sidebar-foreground', 'text-destructive',
|
|
'text-info', 'text-indigo', 'text-orange',
|
|
// Border colors
|
|
'border', 'border-border', 'border-primary', 'border-success', 'border-warning', 'border-muted',
|
|
'border-l-success', 'border-l-warning', 'border-l-muted-foreground', 'border-l-primary',
|
|
// Layout
|
|
'rounded', 'rounded-lg', 'rounded-md', 'rounded-full', 'shadow', 'shadow-sm', 'shadow-md', 'shadow-lg',
|
|
'p-2', 'p-3', 'p-4', 'p-5', 'px-3', 'px-4', 'px-5', 'py-2', 'py-3', 'py-4',
|
|
'm-2', 'mb-2', 'mb-3', 'mb-4', 'mt-2', 'mt-3', 'mt-4', 'mx-2', 'my-2',
|
|
'gap-2', 'gap-3', 'gap-4', 'space-y-2', 'space-y-3',
|
|
// Flex & Grid
|
|
'flex', 'flex-1', 'flex-col', 'flex-wrap', 'items-center', 'items-start', 'justify-between', 'justify-center',
|
|
'grid', 'grid-cols-1', 'grid-cols-2', 'grid-cols-3',
|
|
// Sizing
|
|
'w-full', 'w-5', 'w-8', 'h-2', 'h-5', 'h-8', 'min-w-0', 'max-w-full',
|
|
// Text
|
|
'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl',
|
|
'font-medium', 'font-semibold', 'font-bold', 'font-mono', 'truncate', 'uppercase',
|
|
// States & Transitions
|
|
'hover:shadow-md', 'hover:bg-hover', 'hover:-translate-y-1', 'hover:text-foreground',
|
|
'transition-all', 'duration-200', 'duration-300', 'cursor-pointer',
|
|
// Opacity & visibility
|
|
'opacity-50', 'opacity-80', 'hidden', 'block', 'inline', 'inline-flex',
|
|
// Position
|
|
'relative', 'absolute', 'fixed', 'sticky', 'top-0', 'right-0', 'left-0', 'bottom-0',
|
|
'z-10', 'z-40', 'z-50', 'overflow-hidden', 'overflow-y-auto',
|
|
],
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
background: 'hsl(var(--background))',
|
|
foreground: 'hsl(var(--foreground))',
|
|
card: 'hsl(var(--card))',
|
|
'card-foreground': 'hsl(var(--card-foreground))',
|
|
border: 'hsl(var(--border))',
|
|
input: 'hsl(var(--input))',
|
|
ring: 'hsl(var(--ring))',
|
|
primary: 'hsl(var(--primary))',
|
|
'primary-foreground': 'hsl(var(--primary-foreground))',
|
|
'primary-light': 'hsl(var(--primary-light))',
|
|
secondary: 'hsl(var(--secondary))',
|
|
'secondary-foreground': 'hsl(var(--secondary-foreground))',
|
|
accent: 'hsl(var(--accent))',
|
|
'accent-foreground': 'hsl(var(--accent-foreground))',
|
|
destructive: 'hsl(var(--destructive))',
|
|
'destructive-foreground': 'hsl(var(--destructive-foreground))',
|
|
muted: 'hsl(var(--muted))',
|
|
'muted-foreground': 'hsl(var(--muted-foreground))',
|
|
'sidebar-background': 'hsl(var(--sidebar-background))',
|
|
'sidebar-foreground': 'hsl(var(--sidebar-foreground))',
|
|
hover: 'hsl(var(--hover))',
|
|
success: 'hsl(var(--success))',
|
|
'success-light': 'hsl(var(--success-light))',
|
|
warning: 'hsl(var(--warning))',
|
|
'warning-light': 'hsl(var(--warning-light))',
|
|
info: 'hsl(var(--info))',
|
|
'info-light': 'hsl(var(--info-light))',
|
|
indigo: 'hsl(var(--indigo))',
|
|
'indigo-light': 'hsl(var(--indigo-light))',
|
|
orange: 'hsl(var(--orange))',
|
|
'orange-light': 'hsl(var(--orange-light))',
|
|
},
|
|
fontFamily: {
|
|
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
|
|
mono: ['Consolas', 'Monaco', 'Courier New', 'monospace'],
|
|
},
|
|
boxShadow: {
|
|
'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
|
'DEFAULT': '0 2px 8px rgb(0 0 0 / 0.08)',
|
|
'md': '0 4px 12px rgb(0 0 0 / 0.1)',
|
|
'lg': '0 8px 24px rgb(0 0 0 / 0.12)',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style>
|
|
/* CSS Custom Properties - Light Mode */
|
|
:root {
|
|
--background: 0 0% 98%;
|
|
--foreground: 0 0% 13%;
|
|
--card: 0 0% 100%;
|
|
--card-foreground: 0 0% 13%;
|
|
--border: 0 0% 90%;
|
|
--input: 0 0% 90%;
|
|
--ring: 220 65% 50%;
|
|
--primary: 220 65% 50%;
|
|
--primary-foreground: 0 0% 100%;
|
|
--primary-light: 220 65% 95%;
|
|
--secondary: 220 60% 65%;
|
|
--secondary-foreground: 0 0% 100%;
|
|
--accent: 220 40% 95%;
|
|
--accent-foreground: 0 0% 13%;
|
|
--destructive: 8 75% 55%;
|
|
--destructive-foreground: 0 0% 100%;
|
|
--muted: 0 0% 96%;
|
|
--muted-foreground: 0 0% 45%;
|
|
--sidebar-background: 0 0% 97%;
|
|
--sidebar-foreground: 0 0% 13%;
|
|
--hover: 0 0% 93%;
|
|
--success: 142 71% 45%;
|
|
--success-light: 142 76% 90%;
|
|
--warning: 38 92% 50%;
|
|
--warning-light: 48 96% 89%;
|
|
--info: 210 80% 55%;
|
|
--info-light: 210 80% 92%;
|
|
--indigo: 239 65% 60%;
|
|
--indigo-light: 239 65% 92%;
|
|
--orange: 25 90% 55%;
|
|
--orange-light: 25 90% 92%;
|
|
}
|
|
|
|
/* Dark Mode */
|
|
[data-theme="dark"] {
|
|
--background: 220 13% 10%;
|
|
--foreground: 0 0% 90%;
|
|
--card: 220 13% 14%;
|
|
--card-foreground: 0 0% 90%;
|
|
--border: 220 13% 20%;
|
|
--input: 220 13% 20%;
|
|
--ring: 220 65% 55%;
|
|
--primary: 220 65% 55%;
|
|
--primary-foreground: 0 0% 100%;
|
|
--primary-light: 220 50% 25%;
|
|
--secondary: 220 60% 60%;
|
|
--secondary-foreground: 0 0% 100%;
|
|
--accent: 220 30% 20%;
|
|
--accent-foreground: 0 0% 90%;
|
|
--destructive: 8 70% 50%;
|
|
--destructive-foreground: 0 0% 100%;
|
|
--muted: 220 13% 18%;
|
|
--muted-foreground: 0 0% 55%;
|
|
--sidebar-background: 220 13% 12%;
|
|
--sidebar-foreground: 0 0% 90%;
|
|
--hover: 220 13% 22%;
|
|
--success: 142 71% 40%;
|
|
--success-light: 142 50% 20%;
|
|
--warning: 38 85% 45%;
|
|
--warning-light: 40 50% 20%;
|
|
--info: 210 75% 50%;
|
|
--info-light: 210 50% 20%;
|
|
--indigo: 239 60% 55%;
|
|
--indigo-light: 239 40% 20%;
|
|
--orange: 25 85% 50%;
|
|
--orange-light: 25 50% 20%;
|
|
}
|
|
|
|
/* Scrollbar styling */
|
|
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 3px; }
|
|
::-webkit-scrollbar-thumb:hover { background: hsl(var(--muted-foreground)); }
|
|
|
|
/* Sidebar collapse state */
|
|
.sidebar.collapsed { width: 60px; }
|
|
.sidebar.collapsed .nav-text,
|
|
.sidebar.collapsed .nav-section-title,
|
|
.sidebar.collapsed .badge,
|
|
.sidebar.collapsed .toggle-text { display: none; }
|
|
.sidebar.collapsed .nav-section-header { justify-content: center; padding: 12px 0; }
|
|
.sidebar.collapsed .nav-item { justify-content: center; padding: 10px 0; }
|
|
.sidebar.collapsed .toggle-icon { transform: rotate(180deg); }
|
|
|
|
/* Path menu open state */
|
|
.path-menu.open { display: block; }
|
|
|
|
/* Mobile sidebar */
|
|
@media (max-width: 768px) {
|
|
.sidebar {
|
|
position: fixed;
|
|
left: -260px;
|
|
top: 56px;
|
|
height: calc(100vh - 56px);
|
|
z-index: 200;
|
|
box-shadow: 0 8px 24px rgb(0 0 0 / 0.15);
|
|
}
|
|
.sidebar.open { left: 0; }
|
|
.sidebar-overlay.open { display: block; }
|
|
.menu-toggle-btn { display: block !important; }
|
|
}
|
|
|
|
/* Task drawer */
|
|
.task-detail-drawer {
|
|
transform: translateX(100%);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
.task-detail-drawer.open { transform: translateX(0); }
|
|
.drawer-overlay.active { display: block; }
|
|
|
|
/* Unified Icon System */
|
|
.nav-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
flex-shrink: 0;
|
|
stroke-width: 2;
|
|
}
|
|
.nav-section-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
flex-shrink: 0;
|
|
stroke-width: 2;
|
|
opacity: 0.8;
|
|
}
|
|
.sidebar.collapsed .nav-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
/* Icon Animations */
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
.animate-spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
/* Injected from dashboard-css/*.css modules */
|
|
{{CSS_CONTENT}}
|
|
</style>
|
|
</head>
|
|
<body class="font-sans bg-background text-foreground leading-normal">
|
|
<div class="flex flex-col min-h-screen">
|
|
<!-- Top Bar -->
|
|
<header class="flex items-center justify-between px-5 h-14 bg-card border-b border-border sticky top-0 z-50 shadow-sm">
|
|
<div class="flex items-center gap-4">
|
|
<button class="hidden md:hidden p-2 text-foreground hover:bg-hover rounded menu-toggle-btn" id="menuToggle">
|
|
<i data-lucide="menu" class="w-5 h-5"></i>
|
|
</button>
|
|
<div class="flex items-center gap-2 text-lg font-semibold text-primary">
|
|
<i data-lucide="workflow" class="w-6 h-6"></i>
|
|
<span class="hidden sm:inline">Claude Code Workflow</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Side Actions -->
|
|
<div class="flex items-center gap-3">
|
|
<!-- Path Selector -->
|
|
<div class="flex items-center gap-2 relative">
|
|
<label class="hidden sm:inline text-sm text-muted-foreground" data-i18n="header.project">Project:</label>
|
|
<div class="relative">
|
|
<button class="flex items-center gap-2 px-3 py-1.5 bg-background border border-border rounded text-sm text-foreground hover:bg-hover max-w-[300px]" id="pathButton">
|
|
<span class="truncate max-w-[240px]" id="currentPath">{{PROJECT_PATH}}</span>
|
|
<span class="text-xs text-muted-foreground">▼</span>
|
|
</button>
|
|
<div class="path-menu hidden absolute top-full right-0 mt-1 bg-card border border-border rounded-lg shadow-lg min-w-[280px] z-50" id="pathMenu">
|
|
<div class="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide" data-i18n="header.recentProjects">Recent Projects</div>
|
|
<div id="recentPaths" class="border-t border-border">
|
|
<!-- Dynamic recent paths -->
|
|
</div>
|
|
<div class="p-2 border-t border-border">
|
|
<button class="w-full flex items-center justify-center gap-2 px-3 py-2 bg-background border border-border rounded text-sm text-muted-foreground hover:bg-hover" id="browsePath">
|
|
<i data-lucide="folder-open" class="w-4 h-4"></i>
|
|
<span data-i18n="header.browse">Browse...</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Refresh Button -->
|
|
<button class="refresh-btn p-1.5 text-muted-foreground hover:text-foreground hover:bg-hover rounded" id="refreshWorkspace" data-i18n-title="header.refreshWorkspace" title="Refresh workspace">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
|
<path d="M3 3v5h5"/>
|
|
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
|
|
<path d="M16 21h5v-5"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Language Toggle -->
|
|
<button class="p-2 hover:bg-hover rounded flex items-center justify-center text-sm font-medium min-w-[40px]" id="langToggle" data-i18n-title="header.language" title="Language" onclick="switchLang(currentLang === 'zh' ? 'en' : 'zh')">
|
|
EN
|
|
</button>
|
|
|
|
<!-- Theme Toggle -->
|
|
<button class="p-2 hover:bg-hover rounded flex items-center justify-center" id="themeToggle" data-i18n-title="header.toggleTheme" title="Toggle theme">
|
|
<i data-lucide="moon" class="w-5 h-5 theme-icon-dark"></i>
|
|
<i data-lucide="sun" class="w-5 h-5 theme-icon-light hidden"></i>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Sidebar Overlay (mobile) -->
|
|
<div class="sidebar-overlay hidden fixed inset-0 bg-black/50 z-40" id="sidebarOverlay"></div>
|
|
|
|
<!-- Main Layout -->
|
|
<div class="flex flex-1">
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar w-64 bg-sidebar-background border-r border-border flex flex-col sticky top-14 h-[calc(100vh-56px)] overflow-y-auto transition-all duration-300" id="sidebar">
|
|
<nav class="flex-1 py-3">
|
|
<!-- Project Overview Section -->
|
|
<div class="mb-2" id="projectOverviewNav">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="layout-dashboard" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.project">Project</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="project-overview" data-tooltip="Project Overview">
|
|
<i data-lucide="bar-chart-3" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.overview">Overview</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="explorer" data-tooltip="File Explorer">
|
|
<i data-lucide="folder-tree" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.explorer">Explorer</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="cli-manager" data-tooltip="CLI Tools Status">
|
|
<i data-lucide="terminal" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.status">Status</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeCliTools">0/3</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="cli-history" data-tooltip="CLI Execution History">
|
|
<i data-lucide="history" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.history">History</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Sessions Section -->
|
|
<div class="mb-2">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="history" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.sessions">Sessions</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors active" data-filter="all" data-tooltip="All Sessions">
|
|
<i data-lucide="list" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.all">All</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeAll">0</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-filter="active" data-tooltip="Active Sessions">
|
|
<i data-lucide="play-circle" class="nav-icon text-success"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.active">Active</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-success-light text-success" id="badgeActive">0</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-filter="archived" data-tooltip="Archived Sessions">
|
|
<i data-lucide="archive" class="nav-icon text-info"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.archived">Archived</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-info-light text-info" id="badgeArchived">0</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Lite Tasks Section -->
|
|
<div class="mb-2" id="liteTasksNav">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="zap" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.liteTasks">Lite Tasks</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-lite="lite-plan" data-tooltip="Lite Plan Sessions">
|
|
<i data-lucide="file-edit" class="nav-icon text-indigo"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.litePlan">Lite Plan</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-indigo-light text-indigo" id="badgeLitePlan">0</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-lite="lite-fix" data-tooltip="Lite Fix Sessions">
|
|
<i data-lucide="wrench" class="nav-icon text-orange"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.liteFix">Lite Fix</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-orange-light text-orange" id="badgeLiteFix">0</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- MCP Servers Section -->
|
|
<div class="mb-2" id="mcpServersNav">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="plug" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.mcpServers">MCP Servers</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="mcp-manager" data-tooltip="MCP Server Management">
|
|
<i data-lucide="settings" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.manage">Manage</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeMcpServers">0</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Hooks Section -->
|
|
<div class="mb-2" id="hooksNav">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="webhook" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.hooks">Hooks</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="hook-manager" data-tooltip="Hook Management">
|
|
<i data-lucide="cable" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.manage">Manage</span>
|
|
<span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeHooks">0</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Memory Section -->
|
|
<div class="mb-2" id="memoryNav">
|
|
<div class="flex items-center px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
|
<i data-lucide="brain" class="nav-section-icon mr-2"></i>
|
|
<span class="nav-section-title" data-i18n="nav.memory">Memory</span>
|
|
</div>
|
|
<ul class="space-y-0.5">
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="memory" data-tooltip="Context Memory">
|
|
<i data-lucide="database" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.contextMemory">Context</span>
|
|
</li>
|
|
<li class="nav-item flex items-center gap-2 mx-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="prompt-history" data-tooltip="Prompt History">
|
|
<i data-lucide="message-square" class="nav-icon"></i>
|
|
<span class="nav-text flex-1" data-i18n="nav.promptHistory">Prompts</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
<!-- Sidebar Footer -->
|
|
<div class="p-3 border-t border-border">
|
|
<button class="flex items-center justify-center gap-2 w-full px-3 py-2 border border-border rounded text-sm text-muted-foreground hover:bg-hover transition-colors" id="sidebarToggle">
|
|
<i data-lucide="panel-left-close" class="toggle-icon w-4 h-4 transition-transform duration-300"></i>
|
|
<span class="toggle-text" data-i18n="nav.collapse">Collapse</span>
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Content Area -->
|
|
<main class="flex-1 p-6 overflow-y-auto min-w-0">
|
|
<!-- Stats Section: Left Metrics + Right Carousel -->
|
|
<section id="statsGrid" class="stats-section flex gap-4 mb-6">
|
|
<!-- Left: 4 Metrics Grid -->
|
|
<div class="stats-metrics grid grid-cols-2 gap-3 shrink-0">
|
|
<div class="bg-card border border-border rounded-lg p-4 text-center hover:shadow-md transition-all duration-200 min-w-[140px]">
|
|
<div class="flex justify-center mb-1 text-primary"><i data-lucide="layers" class="w-5 h-5"></i></div>
|
|
<div class="text-2xl font-bold text-foreground" id="statTotalSessions">0</div>
|
|
<div class="text-xs text-muted-foreground mt-1" data-i18n="stats.totalSessions">Total Sessions</div>
|
|
</div>
|
|
<div class="bg-card border border-border rounded-lg p-4 text-center hover:shadow-md transition-all duration-200 min-w-[140px]">
|
|
<div class="flex justify-center mb-1 text-success"><i data-lucide="activity" class="w-5 h-5"></i></div>
|
|
<div class="text-2xl font-bold text-foreground" id="statActiveSessions">0</div>
|
|
<div class="text-xs text-muted-foreground mt-1" data-i18n="stats.activeSessions">Active Sessions</div>
|
|
</div>
|
|
<div class="bg-card border border-border rounded-lg p-4 text-center hover:shadow-md transition-all duration-200 min-w-[140px]">
|
|
<div class="flex justify-center mb-1 text-primary"><i data-lucide="clipboard-list" class="w-5 h-5"></i></div>
|
|
<div class="text-2xl font-bold text-foreground" id="statTotalTasks">0</div>
|
|
<div class="text-xs text-muted-foreground mt-1" data-i18n="stats.totalTasks">Total Tasks</div>
|
|
</div>
|
|
<div class="bg-card border border-border rounded-lg p-4 text-center hover:shadow-md transition-all duration-200 min-w-[140px]">
|
|
<div class="flex justify-center mb-1 text-success"><i data-lucide="check-circle-2" class="w-5 h-5"></i></div>
|
|
<div class="text-2xl font-bold text-foreground" id="statCompletedTasks">0</div>
|
|
<div class="text-xs text-muted-foreground mt-1" data-i18n="stats.completedTasks">Completed Tasks</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right: Active Session Carousel (Image-style with dots) -->
|
|
<div class="stats-carousel flex-1 bg-card border border-border rounded-lg overflow-hidden min-h-[180px] flex flex-col relative">
|
|
<!-- Carousel Content (Full height) -->
|
|
<div class="carousel-content flex-1 relative overflow-hidden" id="carouselContent">
|
|
<!-- Dynamic carousel slides -->
|
|
<div class="carousel-empty flex items-center justify-center h-full text-muted-foreground">
|
|
<div class="text-center">
|
|
<div class="flex justify-center mb-2"><i data-lucide="target" class="w-8 h-8"></i></div>
|
|
<p class="text-sm" data-i18n="carousel.noActiveSessions">No active sessions</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom: Dots Indicator & Controls -->
|
|
<div class="carousel-footer flex items-center justify-center gap-3 py-2 border-t border-border bg-muted/20">
|
|
<!-- Previous Button -->
|
|
<button class="carousel-btn p-1 rounded hover:bg-hover text-muted-foreground hover:text-foreground" id="carouselPrev" data-i18n-title="carousel.previous" title="Previous">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
|
|
</button>
|
|
|
|
<!-- Dots Indicator -->
|
|
<div class="carousel-dots flex items-center gap-1.5" id="carouselDots">
|
|
<!-- Dots will be rendered dynamically -->
|
|
</div>
|
|
|
|
<!-- Next Button -->
|
|
<button class="carousel-btn p-1 rounded hover:bg-hover text-muted-foreground hover:text-foreground" id="carouselNext" data-i18n-title="carousel.next" title="Next">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
|
</button>
|
|
|
|
<!-- Pause Button -->
|
|
<button class="carousel-btn p-1 rounded hover:bg-hover text-muted-foreground hover:text-foreground ml-1" id="carouselPause" data-i18n-title="carousel.pause" title="Pause auto-play">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" id="carouselPauseIcon"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Content Header -->
|
|
<div class="flex items-center justify-between flex-wrap gap-3 mb-5">
|
|
<h2 class="text-xl font-semibold text-foreground" id="contentTitle" data-i18n="title.allSessions">All Sessions</h2>
|
|
<div class="relative">
|
|
<i data-lucide="search" class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground"></i>
|
|
<input type="text" data-i18n-placeholder="search.placeholder" placeholder="Search..." id="searchInput"
|
|
class="pl-9 pr-4 py-2 w-60 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content Container -->
|
|
<section class="main-content" id="mainContent">
|
|
<!-- Dynamic content: sessions grid or session detail page -->
|
|
</section>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer class="flex items-center justify-between px-5 h-10 bg-card border-t border-border text-xs text-muted-foreground">
|
|
<div><span data-i18n="footer.generated">Generated:</span> <span id="generatedAt">-</span></div>
|
|
<div data-i18n="footer.version">CCW Dashboard v1.0</div>
|
|
</footer>
|
|
|
|
<!-- Task Detail Drawer -->
|
|
<div class="task-detail-drawer fixed top-0 right-0 w-1/2 max-w-full h-full bg-card border-l border-border shadow-lg z-50 flex flex-col" id="taskDetailDrawer">
|
|
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground" id="drawerTaskTitle" data-i18n="tab.tasks">Task Details</h3>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeTaskDrawer()">×</button>
|
|
</div>
|
|
<div class="flex-1 overflow-y-auto p-5" id="drawerContent">
|
|
<!-- Dynamic content -->
|
|
</div>
|
|
</div>
|
|
<div class="drawer-overlay hidden fixed inset-0 bg-black/50 z-40" id="drawerOverlay" onclick="closeTaskDrawer()"></div>
|
|
</div>
|
|
|
|
<!-- Markdown Preview Modal -->
|
|
<div id="markdownModal" class="markdown-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="markdown-modal-backdrop absolute inset-0 bg-black/60" onclick="closeMarkdownModal()"></div>
|
|
<div class="markdown-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-4xl h-[85vh] flex flex-col">
|
|
<div class="markdown-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground" id="markdownModalTitle" data-i18n="modal.contentPreview">Content Preview</h3>
|
|
<div class="flex items-center gap-2">
|
|
<div class="flex bg-muted rounded-lg p-0.5">
|
|
<button id="mdTabRaw" class="md-tab-btn px-3 py-1 text-sm rounded-md transition-colors" onclick="switchMarkdownTab('raw')" data-i18n="modal.raw">Raw</button>
|
|
<button id="mdTabPreview" class="md-tab-btn px-3 py-1 text-sm rounded-md transition-colors active" onclick="switchMarkdownTab('preview')" data-i18n="modal.preview">Preview</button>
|
|
</div>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeMarkdownModal()">×</button>
|
|
</div>
|
|
</div>
|
|
<div class="markdown-modal-body flex-1 overflow-auto p-4">
|
|
<pre id="markdownRaw" class="hidden whitespace-pre-wrap text-sm font-mono text-foreground bg-muted p-4 rounded-lg overflow-auto h-full"></pre>
|
|
<div id="markdownPreview" class="markdown-preview prose prose-sm max-w-none"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MCP Server Create Modal -->
|
|
<div id="mcpCreateModal" class="mcp-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="mcp-modal-backdrop absolute inset-0 bg-black/60" onclick="closeMcpCreateModal()"></div>
|
|
<div class="mcp-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-lg flex flex-col">
|
|
<div class="mcp-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<div class="flex items-center gap-3">
|
|
<h3 class="text-lg font-semibold text-foreground" data-i18n="mcp.createTitle">Create MCP Server</h3>
|
|
<div class="flex bg-muted rounded-lg p-0.5">
|
|
<button id="mcpTabForm" class="mcp-tab-btn px-3 py-1 text-sm rounded-md transition-colors active" onclick="switchMcpCreateTab('form')" data-i18n="mcp.form">Form</button>
|
|
<button id="mcpTabJson" class="mcp-tab-btn px-3 py-1 text-sm rounded-md transition-colors" onclick="switchMcpCreateTab('json')" data-i18n="mcp.json">JSON</button>
|
|
</div>
|
|
</div>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeMcpCreateModal()">×</button>
|
|
</div>
|
|
<!-- Form Mode -->
|
|
<div id="mcpFormMode" class="mcp-modal-body p-4 space-y-4">
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1"><span data-i18n="mcp.serverName">Server Name</span> <span class="text-destructive">*</span></label>
|
|
<input type="text" id="mcpServerName" data-i18n-placeholder="mcp.serverNamePlaceholder" placeholder="e.g., my-mcp-server"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1"><span data-i18n="mcp.command">Command</span> <span class="text-destructive">*</span></label>
|
|
<input type="text" id="mcpServerCommand" data-i18n-placeholder="mcp.commandPlaceholder" placeholder="e.g., npx, uvx, node, python"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1">Arguments (one per line)</label>
|
|
<textarea id="mcpServerArgs" placeholder="e.g., -y @smithery/cli@latest run exa" rows="4"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm font-mono focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 resize-none"></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1" data-i18n="mcp.envVars">Environment Variables (KEY=VALUE per line)</label>
|
|
<textarea id="mcpServerEnv" placeholder="e.g., API_KEY=your-api-key DEBUG=true" rows="3"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm font-mono focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 resize-none"></textarea>
|
|
</div>
|
|
</div>
|
|
<!-- JSON Mode -->
|
|
<div id="mcpJsonMode" class="mcp-modal-body p-4 space-y-4 hidden">
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1" data-i18n="mcp.pasteJson">Paste MCP Server JSON Configuration</label>
|
|
<textarea id="mcpServerJson" placeholder='{
|
|
"servers": {
|
|
"my-server": {
|
|
"command": "npx",
|
|
"args": ["-y", "@package/server"],
|
|
"env": {
|
|
"API_KEY": "your-key"
|
|
}
|
|
}
|
|
}
|
|
}' rows="12"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm font-mono focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 resize-none"></textarea>
|
|
<p class="text-xs text-muted-foreground mt-2">Supports <code class="bg-muted px-1 rounded">{"servers": {...}}</code>, <code class="bg-muted px-1 rounded">{"mcpServers": {...}}</code>, and direct server config formats.</p>
|
|
</div>
|
|
<div id="mcpJsonPreview" class="hidden">
|
|
<label class="block text-sm font-medium text-foreground mb-2">Preview (servers to be added):</label>
|
|
<div id="mcpJsonPreviewContent" class="bg-muted rounded-lg p-3 text-sm space-y-2 max-h-32 overflow-y-auto"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mcp-modal-footer flex justify-end gap-2 px-4 py-3 border-t border-border">
|
|
<button class="px-4 py-2 text-sm bg-muted text-foreground rounded-lg hover:bg-hover transition-colors" onclick="closeMcpCreateModal()" data-i18n="common.cancel">Cancel</button>
|
|
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity" onclick="submitMcpCreate()" data-i18n="common.create">Create</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hook Create Modal -->
|
|
<div id="hookCreateModal" class="hook-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="hook-modal-backdrop absolute inset-0 bg-black/60" onclick="closeHookCreateModal()"></div>
|
|
<div class="hook-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-lg flex flex-col max-h-[90vh]">
|
|
<div class="hook-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground" id="hookModalTitle" data-i18n="hook.createTitle">Create Hook</h3>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeHookCreateModal()">×</button>
|
|
</div>
|
|
<div class="hook-modal-body p-4 space-y-4 overflow-y-auto">
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1"><span data-i18n="hook.event">Hook Event</span> <span class="text-destructive">*</span></label>
|
|
<select id="hookEvent" class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20">
|
|
<option value="" data-i18n="hook.selectEvent">Select an event...</option>
|
|
<option value="PreToolUse" data-i18n="hook.preToolUse">PreToolUse - Before a tool is executed</option>
|
|
<option value="PostToolUse" data-i18n="hook.postToolUse">PostToolUse - After a tool completes</option>
|
|
<option value="Notification" data-i18n="hook.notification">Notification - On notifications</option>
|
|
<option value="Stop" data-i18n="hook.stop">Stop - When agent stops</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1">Matcher (optional)</label>
|
|
<input type="text" id="hookMatcher" placeholder="e.g., Write, Edit, Bash (leave empty for all)"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20">
|
|
<p class="text-xs text-muted-foreground mt-1">Tool name to match. Leave empty to match all tools.</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1">Command <span class="text-destructive">*</span></label>
|
|
<input type="text" id="hookCommand" placeholder="e.g., curl, bash, node"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1">Arguments (one per line)</label>
|
|
<textarea id="hookArgs" placeholder="e.g., -X POST http://localhost:3456/api/hook" rows="4"
|
|
class="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground text-sm font-mono focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 resize-none"></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-1">Scope</label>
|
|
<div class="flex gap-4">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="hookScope" value="project" checked class="text-primary focus:ring-primary">
|
|
<span class="text-sm text-foreground">Project (.claude/settings.json)</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="hookScope" value="global" class="text-primary focus:ring-primary">
|
|
<span class="text-sm text-foreground">Global (~/.claude/settings.json)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="block text-sm font-medium text-foreground mb-2">Quick Templates</label>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<button class="hook-template-btn px-3 py-2 text-xs bg-muted text-foreground rounded border border-border hover:bg-hover transition-colors text-left" onclick="applyHookTemplate('ccw-notify')">
|
|
<span class="font-medium">CCW Notify</span>
|
|
<span class="block text-muted-foreground">Notify dashboard on Write</span>
|
|
</button>
|
|
<button class="hook-template-btn px-3 py-2 text-xs bg-muted text-foreground rounded border border-border hover:bg-hover transition-colors text-left" onclick="applyHookTemplate('log-tool')">
|
|
<span class="font-medium">Log Tool Usage</span>
|
|
<span class="block text-muted-foreground">Log all tool executions</span>
|
|
</button>
|
|
<button class="hook-template-btn px-3 py-2 text-xs bg-muted text-foreground rounded border border-border hover:bg-hover transition-colors text-left" onclick="applyHookTemplate('lint-check')">
|
|
<span class="font-medium">Lint Check</span>
|
|
<span class="block text-muted-foreground">Run eslint on file changes</span>
|
|
</button>
|
|
<button class="hook-template-btn px-3 py-2 text-xs bg-muted text-foreground rounded border border-border hover:bg-hover transition-colors text-left" onclick="applyHookTemplate('git-add')">
|
|
<span class="font-medium">Git Add</span>
|
|
<span class="block text-muted-foreground">Auto stage written files</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="hook-modal-footer flex justify-end gap-2 px-4 py-3 border-t border-border">
|
|
<button class="px-4 py-2 text-sm bg-muted text-foreground rounded-lg hover:bg-hover transition-colors" onclick="closeHookCreateModal()">Cancel</button>
|
|
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity" onclick="submitHookCreate()">Create</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hook Wizard Modal -->
|
|
<div id="hookWizardModal" class="hook-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="hook-modal-backdrop absolute inset-0 bg-black/60" onclick="closeHookWizardModal()"></div>
|
|
<div class="hook-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-xl flex flex-col max-h-[90vh]">
|
|
<div class="hook-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground">Hook Wizard</h3>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeHookWizardModal()">×</button>
|
|
</div>
|
|
<div class="hook-modal-body p-4 overflow-y-auto" id="wizardModalContent">
|
|
<!-- Dynamic wizard content -->
|
|
</div>
|
|
<div class="hook-modal-footer flex justify-end gap-2 px-4 py-3 border-t border-border">
|
|
<button class="px-4 py-2 text-sm bg-muted text-foreground rounded-lg hover:bg-hover transition-colors" onclick="closeHookWizardModal()">Cancel</button>
|
|
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity" onclick="submitHookWizard()">Install Hook</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template View Modal -->
|
|
<div id="templateViewModal" class="hook-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="hook-modal-backdrop absolute inset-0 bg-black/60" onclick="closeTemplateViewModal()"></div>
|
|
<div class="hook-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-md flex flex-col max-h-[80vh]">
|
|
<div class="hook-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground">Template Details</h3>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeTemplateViewModal()">×</button>
|
|
</div>
|
|
<div class="hook-modal-body p-4 overflow-y-auto" id="templateViewContent">
|
|
<!-- Dynamic template content -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Update CLAUDE.md Modal -->
|
|
<div id="updateClaudeMdModal" class="claude-md-modal hidden fixed inset-0 z-[100] flex items-center justify-center">
|
|
<div class="claude-md-modal-backdrop absolute inset-0 bg-black/60" onclick="closeUpdateClaudeMdModal()"></div>
|
|
<div class="claude-md-modal-content relative bg-card border border-border rounded-lg shadow-2xl w-[90vw] max-w-md flex flex-col">
|
|
<div class="claude-md-modal-header flex items-center justify-between px-4 py-3 border-b border-border">
|
|
<h3 class="text-lg font-semibold text-foreground">Update CLAUDE.md</h3>
|
|
<button class="w-8 h-8 flex items-center justify-center text-xl text-muted-foreground hover:text-foreground hover:bg-hover rounded" onclick="closeUpdateClaudeMdModal()">×</button>
|
|
</div>
|
|
<div class="claude-md-modal-body p-4 space-y-4">
|
|
<div class="claude-md-form-group">
|
|
<label>Target Directory</label>
|
|
<div class="claude-md-target-path" id="claudeMdTargetPath">-</div>
|
|
</div>
|
|
<div class="claude-md-form-group">
|
|
<label for="claudeMdTool">CLI Tool</label>
|
|
<select id="claudeMdTool">
|
|
<option value="gemini">Gemini (gemini-2.5-flash)</option>
|
|
<option value="qwen">Qwen (coder-model)</option>
|
|
<option value="codex">Codex (gpt5-codex)</option>
|
|
</select>
|
|
</div>
|
|
<div class="claude-md-form-group">
|
|
<label for="claudeMdStrategy">Strategy</label>
|
|
<select id="claudeMdStrategy">
|
|
<option value="single-layer">Single Layer - Current dir + child CLAUDE.md refs</option>
|
|
<option value="multi-layer">Multi Layer - Generate CLAUDE.md in all subdirs</option>
|
|
</select>
|
|
</div>
|
|
<div class="claude-md-status" id="claudeMdStatus"></div>
|
|
</div>
|
|
<div class="claude-md-modal-footer flex justify-end gap-2 px-4 py-3 border-t border-border">
|
|
<button class="px-4 py-2 text-sm bg-muted text-foreground rounded-lg hover:bg-hover transition-colors" onclick="closeUpdateClaudeMdModal()">Cancel</button>
|
|
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity" id="claudeMdExecuteBtn" onclick="executeUpdateClaudeMd()">Execute</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Lucide Icons (本地) -->
|
|
<script src="./assets/js/lucide.min.js"></script>
|
|
<!-- D3.js for Flowchart (本地) -->
|
|
<script src="./assets/js/d3.min.js"></script>
|
|
<!-- Marked.js for Markdown rendering (本地) -->
|
|
<script src="./assets/js/marked.min.js"></script>
|
|
<!-- Highlight.js for Syntax Highlighting (本地) -->
|
|
<link rel="stylesheet" href="./assets/css/github-dark.min.css" id="hljs-theme-dark">
|
|
<link rel="stylesheet" href="./assets/css/github.min.css" id="hljs-theme-light" disabled>
|
|
<script src="./assets/js/highlight.min.js"></script>
|
|
|
|
<script>
|
|
{{JS_CONTENT}}
|
|
</script>
|
|
</body>
|
|
</html>
|