@prmichaelsen/acp-visualizer 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Link, useRouterState } from '@tanstack/react-router'
|
|
2
|
-
import { LayoutDashboard, Flag, CheckSquare, Clock, Search, PenTool, Puzzle, FileBarChart, Github, X } from 'lucide-react'
|
|
2
|
+
import { LayoutDashboard, Flag, CheckSquare, Clock, Search, PenTool, Puzzle, FileBarChart, Github, X, ChevronLeft, ChevronRight } from 'lucide-react'
|
|
3
3
|
import { ProjectSelector } from './ProjectSelector'
|
|
4
4
|
import { GitHubInput } from './GitHubInput'
|
|
5
5
|
import { GitHubAuth } from './GitHubAuth'
|
|
@@ -22,28 +22,50 @@ interface SidebarProps {
|
|
|
22
22
|
onProjectSelect?: (projectId: string) => void
|
|
23
23
|
onGitHubLoad?: (owner: string, repo: string) => Promise<void>
|
|
24
24
|
onClose?: () => void
|
|
25
|
+
isCollapsed?: boolean
|
|
26
|
+
onToggleCollapse?: () => void
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
export function Sidebar({ projects = [], currentProject = null, onProjectSelect, onGitHubLoad, onClose }: SidebarProps) {
|
|
29
|
+
export function Sidebar({ projects = [], currentProject = null, onProjectSelect, onGitHubLoad, onClose, isCollapsed = false, onToggleCollapse }: SidebarProps) {
|
|
28
30
|
const location = useRouterState({ select: (s) => s.location })
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
|
-
<nav className=
|
|
33
|
+
<nav className={`w-full h-auto max-h-[80vh] lg:h-full border-t lg:border-t-0 lg:border-r border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-950 flex flex-col shrink-0 rounded-t-2xl lg:rounded-none overflow-y-auto transition-all duration-300 ${
|
|
34
|
+
isCollapsed ? 'lg:w-16' : 'lg:w-56'
|
|
35
|
+
}`}>
|
|
32
36
|
<div className="p-4 border-b border-gray-200 dark:border-gray-800 flex items-center justify-between">
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<button
|
|
38
|
-
onClick={onClose}
|
|
39
|
-
className="lg:hidden p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"
|
|
40
|
-
aria-label="Close menu"
|
|
41
|
-
>
|
|
42
|
-
<X className="w-4 h-4 text-gray-700 dark:text-gray-300" />
|
|
43
|
-
</button>
|
|
37
|
+
{!isCollapsed && (
|
|
38
|
+
<span className="text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide">
|
|
39
|
+
ACP Visualizer
|
|
40
|
+
</span>
|
|
44
41
|
)}
|
|
42
|
+
<div className="flex items-center gap-2">
|
|
43
|
+
{onToggleCollapse && (
|
|
44
|
+
<button
|
|
45
|
+
onClick={onToggleCollapse}
|
|
46
|
+
className="hidden lg:block p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"
|
|
47
|
+
aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
|
|
48
|
+
title={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
|
|
49
|
+
>
|
|
50
|
+
{isCollapsed ? (
|
|
51
|
+
<ChevronRight className="w-4 h-4 text-gray-700 dark:text-gray-300" />
|
|
52
|
+
) : (
|
|
53
|
+
<ChevronLeft className="w-4 h-4 text-gray-700 dark:text-gray-300" />
|
|
54
|
+
)}
|
|
55
|
+
</button>
|
|
56
|
+
)}
|
|
57
|
+
{onClose && (
|
|
58
|
+
<button
|
|
59
|
+
onClick={onClose}
|
|
60
|
+
className="lg:hidden p-1.5 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"
|
|
61
|
+
aria-label="Close menu"
|
|
62
|
+
>
|
|
63
|
+
<X className="w-4 h-4 text-gray-700 dark:text-gray-300" />
|
|
64
|
+
</button>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
45
67
|
</div>
|
|
46
|
-
{projects.length > 1 && onProjectSelect && (
|
|
68
|
+
{projects.length > 1 && onProjectSelect && !isCollapsed && (
|
|
47
69
|
<div className="px-3 pt-3">
|
|
48
70
|
<ProjectSelector
|
|
49
71
|
projects={projects}
|
|
@@ -63,34 +85,39 @@ export function Sidebar({ projects = [], currentProject = null, onProjectSelect,
|
|
|
63
85
|
<Link
|
|
64
86
|
key={item.to}
|
|
65
87
|
to={item.to}
|
|
66
|
-
className={`flex items-center gap-3
|
|
88
|
+
className={`flex items-center gap-3 py-2 text-sm transition-colors ${
|
|
89
|
+
isCollapsed ? 'px-4 justify-center' : 'px-4'
|
|
90
|
+
} ${
|
|
67
91
|
isActive
|
|
68
92
|
? 'text-gray-900 dark:text-gray-100 bg-gray-200 dark:bg-gray-800/50'
|
|
69
93
|
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:bg-gray-200/50 dark:hover:bg-gray-800/30'
|
|
70
94
|
}`}
|
|
71
95
|
onClick={onClose}
|
|
96
|
+
title={isCollapsed ? item.label : undefined}
|
|
72
97
|
>
|
|
73
98
|
<item.icon className="w-4 h-4" />
|
|
74
|
-
{item.label}
|
|
99
|
+
{!isCollapsed && item.label}
|
|
75
100
|
</Link>
|
|
76
101
|
)
|
|
77
102
|
})}
|
|
78
103
|
</div>
|
|
79
|
-
<hr className="border-gray-200 dark:border-gray-800" />
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
{!isCollapsed && <hr className="border-gray-200 dark:border-gray-800" />}
|
|
105
|
+
{!isCollapsed && (
|
|
106
|
+
<div className="p-3 space-y-2">
|
|
107
|
+
<Link
|
|
108
|
+
to="/search"
|
|
109
|
+
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 dark:text-gray-500 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-800 rounded-md hover:text-gray-900 dark:hover:text-gray-300 hover:border-gray-400 dark:hover:border-gray-600 transition-colors"
|
|
110
|
+
onClick={onClose}
|
|
111
|
+
>
|
|
112
|
+
<Search className="w-4 h-4" />
|
|
113
|
+
Search...
|
|
114
|
+
</Link>
|
|
115
|
+
<GitHubAuth />
|
|
116
|
+
{onGitHubLoad && (
|
|
117
|
+
<GitHubInput onLoad={onGitHubLoad} />
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
94
121
|
</nav>
|
|
95
122
|
)
|
|
96
123
|
}
|
|
@@ -18,13 +18,23 @@ interface SidePanelContextValue {
|
|
|
18
18
|
const SidePanelContext = createContext<SidePanelContextValue | undefined>(undefined)
|
|
19
19
|
|
|
20
20
|
const MIN_WIDTH = 300
|
|
21
|
-
const MAX_WIDTH = 800
|
|
22
21
|
const DEFAULT_WIDTH = 500
|
|
22
|
+
const STORAGE_KEY = 'acp-visualizer.side-panel-size'
|
|
23
|
+
|
|
24
|
+
function loadWidth(): number {
|
|
25
|
+
if (typeof window === 'undefined') return DEFAULT_WIDTH
|
|
26
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
27
|
+
if (stored) {
|
|
28
|
+
const parsed = Number(stored)
|
|
29
|
+
if (!isNaN(parsed) && parsed >= MIN_WIDTH) return parsed
|
|
30
|
+
}
|
|
31
|
+
return DEFAULT_WIDTH
|
|
32
|
+
}
|
|
23
33
|
|
|
24
34
|
export function SidePanelProvider({ children }: { children: ReactNode }) {
|
|
25
35
|
const [content, setContent] = useState<PanelContent>(null)
|
|
26
36
|
const [isOpen, setIsOpen] = useState(false)
|
|
27
|
-
const [width, setWidthState] = useState(
|
|
37
|
+
const [width, setWidthState] = useState(loadWidth)
|
|
28
38
|
|
|
29
39
|
const openMilestone = (id: string) => {
|
|
30
40
|
setContent({ type: 'milestone', id })
|
|
@@ -42,8 +52,9 @@ export function SidePanelProvider({ children }: { children: ReactNode }) {
|
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
const setWidth = (newWidth: number) => {
|
|
45
|
-
const
|
|
46
|
-
setWidthState(
|
|
55
|
+
const clamped = Math.max(MIN_WIDTH, newWidth)
|
|
56
|
+
setWidthState(clamped)
|
|
57
|
+
localStorage.setItem(STORAGE_KEY, String(clamped))
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
return (
|
package/src/routes/__root.tsx
CHANGED
|
@@ -112,6 +112,7 @@ function RootLayout() {
|
|
|
112
112
|
)
|
|
113
113
|
const [initialLoadDone, setInitialLoadDone] = useState(false)
|
|
114
114
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
115
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
|
|
115
116
|
|
|
116
117
|
// On mount, check for ?repo= param and auto-load
|
|
117
118
|
useEffect(() => {
|
|
@@ -196,6 +197,8 @@ function RootLayout() {
|
|
|
196
197
|
onProjectSelect={handleProjectSwitch}
|
|
197
198
|
onGitHubLoad={handleGitHubLoad}
|
|
198
199
|
onClose={() => setMobileMenuOpen(false)}
|
|
200
|
+
isCollapsed={sidebarCollapsed}
|
|
201
|
+
onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)}
|
|
199
202
|
/>
|
|
200
203
|
</div>
|
|
201
204
|
|