@prmichaelsen/acp-visualizer 0.9.4 → 0.10.0

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/acp-visualizer",
3
- "version": "0.9.4",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Browser-based dashboard for visualizing ACP progress.yaml data",
6
6
  "bin": {
@@ -1,3 +1,4 @@
1
+ import { useState, useEffect } from 'react'
1
2
  import {
2
3
  BarChart,
3
4
  Bar,
@@ -15,12 +15,12 @@ interface FilterBarProps {
15
15
 
16
16
  export function FilterBar({ status, onStatusChange }: FilterBarProps) {
17
17
  return (
18
- <div className="flex gap-1 p-1 bg-gray-800/50 rounded-lg">
18
+ <div className="flex flex-wrap gap-2 p-2 bg-gray-800/50 rounded-lg">
19
19
  {statusOptions.map((opt) => (
20
20
  <button
21
21
  key={opt.value}
22
22
  onClick={() => onStatusChange(opt.value)}
23
- className={`px-3 py-1 text-xs rounded-md transition-colors ${
23
+ className={`px-4 py-2 text-sm rounded-md transition-colors whitespace-nowrap min-w-[44px] min-h-[44px] flex items-center justify-center ${
24
24
  status === opt.value
25
25
  ? 'bg-gray-700 text-gray-100 shadow-sm'
26
26
  : 'text-gray-500 hover:text-gray-300'
@@ -90,10 +90,11 @@ export function MilestoneKanban({ milestones, tasks }: MilestoneKanbanProps) {
90
90
  coreStatuses.has(col.status) ||
91
91
  milestones.some((m) => m.status === col.status),
92
92
  )
93
- const gridCols =
94
- activeColumns.length <= 3
95
- ? 'grid-cols-3'
96
- : 'grid-cols-4'
93
+
94
+ // Responsive grid: 1 col mobile, 2 cols tablet, 3-4 cols desktop
95
+ const gridCols = activeColumns.length <= 3
96
+ ? 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3'
97
+ : 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4'
97
98
 
98
99
  return (
99
100
  <div className={`grid ${gridCols} gap-4 min-h-[300px]`}>
@@ -26,7 +26,7 @@ export function Sidebar({ projects = [], currentProject = null, onProjectSelect,
26
26
  const location = useRouterState({ select: (s) => s.location })
27
27
 
28
28
  return (
29
- <nav className="w-56 h-full border-r border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-950 flex flex-col shrink-0">
29
+ <nav className="w-full lg:w-56 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-hidden">
30
30
  <div className="p-4 border-b border-gray-200 dark:border-gray-800 flex items-center justify-between">
31
31
  <span className="text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide">
32
32
  ACP Visualizer
@@ -50,7 +50,7 @@ export function Sidebar({ projects = [], currentProject = null, onProjectSelect,
50
50
  />
51
51
  </div>
52
52
  )}
53
- <div className="flex-1 py-2">
53
+ <div className="flex-1 py-2 overflow-y-auto">
54
54
  {navItems.map((item) => {
55
55
  const isActive =
56
56
  item.to === '/'
@@ -66,6 +66,7 @@ export function Sidebar({ projects = [], currentProject = null, onProjectSelect,
66
66
  ? 'text-gray-900 dark:text-gray-100 bg-gray-200 dark:bg-gray-800/50'
67
67
  : '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'
68
68
  }`}
69
+ onClick={onClose}
69
70
  >
70
71
  <item.icon className="w-4 h-4" />
71
72
  {item.label}
@@ -77,6 +78,7 @@ export function Sidebar({ projects = [], currentProject = null, onProjectSelect,
77
78
  <Link
78
79
  to="/search"
79
80
  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"
81
+ onClick={onClose}
80
82
  >
81
83
  <Search className="w-4 h-4" />
82
84
  Search...
@@ -15,12 +15,12 @@ const views: Array<{ id: ViewMode; label: string }> = [
15
15
 
16
16
  export function ViewToggle({ value, onChange }: ViewToggleProps) {
17
17
  return (
18
- <div className="flex gap-1 bg-gray-900 border border-gray-800 rounded-lg p-0.5">
18
+ <div className="flex gap-1 bg-gray-900 border border-gray-800 rounded-lg p-0.5 overflow-x-auto scrollbar-hide">
19
19
  {views.map((v) => (
20
20
  <button
21
21
  key={v.id}
22
22
  onClick={() => onChange(v.id)}
23
- className={`px-3 py-1 text-xs rounded-md transition-colors ${
23
+ className={`px-4 py-2 text-sm rounded-md transition-colors whitespace-nowrap min-w-[44px] min-h-[44px] flex items-center justify-center ${
24
24
  value === v.id
25
25
  ? 'bg-gray-700 text-gray-100'
26
26
  : 'text-gray-500 hover:text-gray-300'
@@ -161,11 +161,15 @@ function RootLayout() {
161
161
  <div className="flex h-screen bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100">
162
162
  {/* Mobile Menu Button - Bottom Right (Thumb Zone) */}
163
163
  <button
164
- onClick={() => setMobileMenuOpen(true)}
165
- className="lg:hidden fixed bottom-6 right-6 z-50 p-3 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-lg"
166
- aria-label="Open menu"
164
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
165
+ className="lg:hidden fixed bottom-6 right-6 z-50 p-3 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-lg transition-transform duration-200"
166
+ aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
167
167
  >
168
- <Menu className="w-6 h-6 text-gray-900 dark:text-gray-100" />
168
+ {mobileMenuOpen ? (
169
+ <X className="w-6 h-6 text-gray-900 dark:text-gray-100" />
170
+ ) : (
171
+ <Menu className="w-6 h-6 text-gray-900 dark:text-gray-100" />
172
+ )}
169
173
  </button>
170
174
 
171
175
  {/* Mobile Backdrop */}
@@ -176,10 +180,10 @@ function RootLayout() {
176
180
  />
177
181
  )}
178
182
 
179
- {/* Sidebar - Desktop: Always visible | Mobile: Drawer */}
183
+ {/* Sidebar - Desktop: Always visible | Mobile: Bottom Drawer */}
180
184
  <div
181
- className={`fixed lg:relative inset-y-0 left-0 z-50 transition-transform duration-300 lg:translate-x-0 ${
182
- mobileMenuOpen ? 'translate-x-0' : '-translate-x-full'
185
+ className={`fixed lg:relative bottom-0 left-0 right-0 lg:inset-y-0 lg:right-auto z-50 transition-transform duration-300 lg:translate-y-0 ${
186
+ mobileMenuOpen ? 'translate-y-0' : 'translate-y-full'
183
187
  }`}
184
188
  >
185
189
  <Sidebar