@prmichaelsen/acp-visualizer 0.9.0 → 0.9.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/acp-visualizer",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "description": "Browser-based dashboard for visualizing ACP progress.yaml data",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from 'react'
2
2
  import dagre from 'dagre'
3
+ import { formatTaskName, formatMilestoneName } from '../lib/display'
3
4
  import type { ProgressData, Task, Status } from '../lib/types'
4
5
 
5
6
  interface DependencyGraphProps {
@@ -43,7 +44,7 @@ function buildGraph(data: ProgressData): { nodes: GraphNode[]; edges: GraphEdge[
43
44
  for (const milestone of data.milestones) {
44
45
  const tasks = data.tasks[milestone.id] || []
45
46
  for (const task of tasks) {
46
- allTasks.push({ ...task, milestoneName: milestone.name })
47
+ allTasks.push({ ...task, milestoneName: formatMilestoneName(milestone) })
47
48
  }
48
49
  }
49
50
 
@@ -80,7 +81,7 @@ function buildGraph(data: ProgressData): { nodes: GraphNode[]; edges: GraphEdge[
80
81
  const node = g.node(String(task.id))
81
82
  return {
82
83
  id: String(task.id),
83
- label: task.name,
84
+ label: formatTaskName(task),
84
85
  status: task.status,
85
86
  milestone: task.milestoneName,
86
87
  x: node.x,
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from 'react'
2
2
  import { StatusBadge } from './StatusBadge'
3
+ import { formatMilestoneName } from '../lib/display'
3
4
  import type { Milestone, Task } from '../lib/types'
4
5
 
5
6
  interface MilestoneGanttProps {
@@ -118,17 +119,17 @@ export function MilestoneGantt({ milestones, tasks }: MilestoneGanttProps) {
118
119
  : 'bg-gray-500/30 border-gray-500/40'
119
120
 
120
121
  return (
121
- <div key={milestone.id} className="flex items-center h-12 px-3 hover:bg-gray-800/20">
122
+ <div key={milestone.id} className="flex items-center h-12 px-3 hover:bg-gray-200/20 dark:hover:bg-gray-800/20">
122
123
  {/* Label */}
123
124
  <div className="w-48 shrink-0 flex items-center gap-2">
124
- <span className="text-xs text-gray-300 truncate">{milestone.name}</span>
125
+ <span className="text-xs text-gray-700 dark:text-gray-300 truncate">{formatMilestoneName(milestone)}</span>
125
126
  </div>
126
127
  {/* Bar area */}
127
128
  <div className="flex-1 relative h-6">
128
129
  <div
129
130
  className={`absolute top-1 h-4 rounded-sm border ${barColor} transition-all`}
130
131
  style={{ left: `${barStart}%`, width: `${barWidth}%` }}
131
- title={`${milestone.name}: ${start ? formatDate(start) : '?'} → ${end ? formatDate(end) : '?'} (${milestone.progress}%)`}
132
+ title={`${formatMilestoneName(milestone)}: ${start ? formatDate(start) : '?'} → ${end ? formatDate(end) : '?'} (${milestone.progress}%)`}
132
133
  >
133
134
  {/* Progress fill within bar */}
134
135
  <div
@@ -4,6 +4,7 @@ import { PriorityBadge } from './PriorityBadge'
4
4
  import { ProgressBar } from './ProgressBar'
5
5
  import { PreviewButton } from './PreviewButton'
6
6
  import { TaskList } from './TaskList'
7
+ import { formatMilestoneName } from '../lib/display'
7
8
  import type { Milestone, Task, Status } from '../lib/types'
8
9
  import { useState } from 'react'
9
10
  import { ChevronDown, ChevronRight } from 'lucide-react'
@@ -38,7 +39,7 @@ function KanbanCard({
38
39
  params={{ milestoneId: milestone.id }}
39
40
  className="text-sm font-medium leading-tight text-gray-900 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
40
41
  >
41
- {milestone.name}
42
+ {formatMilestoneName(milestone)}
42
43
  </Link>
43
44
  <PreviewButton type="milestone" id={milestone.id} />
44
45
  </div>
@@ -8,6 +8,7 @@ import { StatusDot } from './StatusDot'
8
8
  import { PriorityBadge } from './PriorityBadge'
9
9
  import { MarkdownContent, buildLinkMap } from './MarkdownContent'
10
10
  import { getMarkdownContent, resolveMilestoneFile } from '../services/markdown.service'
11
+ import { formatMilestoneName } from '../lib/display'
11
12
  import type { MarkdownResult, ResolveFileResult } from '../services/markdown.service'
12
13
 
13
14
  interface MilestonePreviewProps {
@@ -89,7 +90,7 @@ export function MilestonePreview({ milestoneId }: MilestonePreviewProps) {
89
90
  return (
90
91
  <div>
91
92
  <div className="flex items-start justify-between mb-4">
92
- <h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{milestone.name}</h1>
93
+ <h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{formatMilestoneName(milestone)}</h1>
93
94
  <Link
94
95
  to="/milestones/$milestoneId"
95
96
  params={{ milestoneId }}
@@ -14,6 +14,7 @@ import { PriorityBadge } from './PriorityBadge'
14
14
  import { ProgressBar } from './ProgressBar'
15
15
  import { PreviewButton } from './PreviewButton'
16
16
  import { TaskList } from './TaskList'
17
+ import { formatMilestoneName } from '../lib/display'
17
18
  import type { Milestone, Task } from '../lib/types'
18
19
 
19
20
  const columnHelper = createColumnHelper<Milestone>()
@@ -62,7 +63,7 @@ export function MilestoneTable({ milestones, tasks }: MilestoneTableProps) {
62
63
  className="text-sm font-medium text-gray-900 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
63
64
  onClick={(e) => e.stopPropagation()}
64
65
  >
65
- {info.getValue()}
66
+ {formatMilestoneName(info.row.original)}
66
67
  </Link>
67
68
  <PreviewButton type="milestone" id={info.row.original.id} />
68
69
  </div>
@@ -7,6 +7,7 @@ import { ProgressBar } from './ProgressBar'
7
7
  import { PreviewButton } from './PreviewButton'
8
8
  import { TaskList } from './TaskList'
9
9
  import { useCollapse } from '../lib/useCollapse'
10
+ import { formatMilestoneName } from '../lib/display'
10
11
  import type { Milestone, Task } from '../lib/types'
11
12
 
12
13
  interface MilestoneTreeProps {
@@ -45,7 +46,7 @@ function MilestoneTreeRow({
45
46
  className="text-sm font-medium text-gray-900 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
46
47
  onClick={(e) => e.stopPropagation()}
47
48
  >
48
- {milestone.name}
49
+ {formatMilestoneName(milestone)}
49
50
  </Link>
50
51
  <PreviewButton type="milestone" id={milestone.id} />
51
52
  </div>
@@ -1,4 +1,4 @@
1
- import { Eye } from 'lucide-react'
1
+ import { PanelRight } from 'lucide-react'
2
2
  import { useSidePanel } from '../contexts/SidePanelContext'
3
3
 
4
4
  interface PreviewButtonProps {
@@ -27,7 +27,7 @@ export function PreviewButton({ type, id, className = '' }: PreviewButtonProps)
27
27
  title={`Preview ${type}`}
28
28
  aria-label={`Preview ${type}`}
29
29
  >
30
- <Eye className="w-3.5 h-3.5 text-gray-500 dark:text-gray-400" />
30
+ <PanelRight className="w-3.5 h-3.5 text-gray-500 dark:text-gray-400" />
31
31
  </button>
32
32
  )
33
33
  }
@@ -6,6 +6,11 @@ import { TaskPreview } from './TaskPreview'
6
6
  export function SidePanel() {
7
7
  const { content, isOpen, close } = useSidePanel()
8
8
 
9
+ // Don't render at all until first open
10
+ if (!content && !isOpen) {
11
+ return null
12
+ }
13
+
9
14
  return (
10
15
  <>
11
16
  {/* Backdrop */}
@@ -18,8 +23,8 @@ export function SidePanel() {
18
23
 
19
24
  {/* Panel */}
20
25
  <div
21
- className={`fixed top-0 right-0 h-full w-full max-w-2xl bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-800 shadow-2xl z-50 transition-transform duration-300 overflow-auto ${
22
- isOpen ? 'translate-x-0' : 'translate-x-full'
26
+ className={`fixed top-0 right-0 h-full w-full max-w-2xl bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-800 shadow-2xl z-50 transition-transform duration-300 overflow-auto translate-x-full ${
27
+ isOpen ? '!translate-x-0' : ''
23
28
  }`}
24
29
  >
25
30
  {/* Header */}
@@ -3,6 +3,7 @@ import { StatusDot } from './StatusDot'
3
3
  import { PriorityBadge } from './PriorityBadge'
4
4
  import { PreviewButton } from './PreviewButton'
5
5
  import { ExtraFieldsBadge } from './ExtraFieldsBadge'
6
+ import { formatTaskName } from '../lib/display'
6
7
  import type { Task } from '../lib/types'
7
8
 
8
9
  export function TaskList({ tasks }: { tasks: Task[] }) {
@@ -26,7 +27,7 @@ export function TaskList({ tasks }: { tasks: Task[] }) {
26
27
  task.status === 'completed' ? 'text-gray-500 dark:text-gray-500' : 'text-gray-900 dark:text-gray-200'
27
28
  }`}
28
29
  >
29
- {task.name}
30
+ {formatTaskName(task)}
30
31
  </Link>
31
32
  <PreviewButton type="task" id={task.id} />
32
33
  <PriorityBadge priority={task.priority} />
@@ -6,6 +6,7 @@ import { DetailHeader } from './DetailHeader'
6
6
  import { PriorityBadge } from './PriorityBadge'
7
7
  import { MarkdownContent, buildLinkMap } from './MarkdownContent'
8
8
  import { getMarkdownContent, resolveTaskFile } from '../services/markdown.service'
9
+ import { formatTaskName, formatMilestoneName } from '../lib/display'
9
10
  import type { MarkdownResult } from '../services/markdown.service'
10
11
 
11
12
  interface TaskPreviewProps {
@@ -100,7 +101,7 @@ export function TaskPreview({ taskId }: TaskPreviewProps) {
100
101
  params={{ milestoneId: milestone.id }}
101
102
  className="text-blue-500 dark:text-blue-400 hover:underline"
102
103
  >
103
- {milestone.id.replace('milestone_', 'M')} — {milestone.name}
104
+ {formatMilestoneName(milestone)}
104
105
  </Link>
105
106
  ),
106
107
  },
@@ -109,7 +110,7 @@ export function TaskPreview({ taskId }: TaskPreviewProps) {
109
110
  return (
110
111
  <div>
111
112
  <div className="flex items-start justify-between mb-4">
112
- <h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{task.name}</h1>
113
+ <h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{formatTaskName(task)}</h1>
113
114
  <Link
114
115
  to="/tasks/$taskId"
115
116
  params={{ taskId }}
@@ -0,0 +1,51 @@
1
+ import type { Milestone, Task } from './types'
2
+
3
+ /**
4
+ * Extract milestone number from ID (e.g., "M1" -> "1", "milestone_1" -> "1")
5
+ */
6
+ export function getMilestoneNumber(id: string): string {
7
+ // Handle M1, M2, M3... format
8
+ if (/^M\d+$/.test(id)) {
9
+ return id.substring(1)
10
+ }
11
+ // Handle milestone_1, milestone_2... format
12
+ if (/^milestone_\d+$/.test(id)) {
13
+ return id.replace('milestone_', '')
14
+ }
15
+ // Fallback: try to extract any number
16
+ const match = id.match(/\d+/)
17
+ return match ? match[0] : id
18
+ }
19
+
20
+ /**
21
+ * Extract task number from ID (e.g., "task_1" -> "1", "79" -> "79")
22
+ */
23
+ export function getTaskNumber(id: string): string {
24
+ // Handle task_1, task_2... format
25
+ if (/^task_\d+$/.test(id)) {
26
+ return id.replace('task_', '')
27
+ }
28
+ // Handle numeric IDs
29
+ if (/^\d+$/.test(id)) {
30
+ return id
31
+ }
32
+ // Fallback: try to extract any number
33
+ const match = id.match(/\d+/)
34
+ return match ? match[0] : id
35
+ }
36
+
37
+ /**
38
+ * Format milestone display name with prefix (e.g., "M1 — Project Setup")
39
+ */
40
+ export function formatMilestoneName(milestone: Milestone): string {
41
+ const num = getMilestoneNumber(milestone.id)
42
+ return `M${num} — ${milestone.name}`
43
+ }
44
+
45
+ /**
46
+ * Format task display name with prefix (e.g., "T1 — Install dependencies")
47
+ */
48
+ export function formatTaskName(task: Task): string {
49
+ const num = getTaskNumber(task.id)
50
+ return `T${num} — ${task.name}`
51
+ }
@@ -2,6 +2,17 @@ import { useState, useEffect } from 'react'
2
2
 
3
3
  type Theme = 'dark' | 'light'
4
4
 
5
+ // Initialize theme immediately on load to prevent flash
6
+ if (typeof window !== 'undefined') {
7
+ const stored = localStorage.getItem('theme')
8
+ const initialTheme = (stored === 'light' || stored === 'dark') ? stored : 'dark'
9
+ if (initialTheme === 'dark') {
10
+ document.documentElement.classList.add('dark')
11
+ } else {
12
+ document.documentElement.classList.remove('dark')
13
+ }
14
+ }
15
+
5
16
  export function useTheme() {
6
17
  const [theme, setTheme] = useState<Theme>(() => {
7
18
  if (typeof window === 'undefined') return 'dark'
@@ -8,6 +8,7 @@ import { StatusDot } from '../components/StatusDot'
8
8
  import { PriorityBadge } from '../components/PriorityBadge'
9
9
  import { MarkdownContent, buildLinkMap } from '../components/MarkdownContent'
10
10
  import { getMarkdownContent, resolveMilestoneFile } from '../services/markdown.service'
11
+ import { formatMilestoneName, formatTaskName } from '../lib/display'
11
12
  import type { MarkdownResult, ResolveFileResult } from '../services/markdown.service'
12
13
 
13
14
  export const Route = createFileRoute('/milestones/$milestoneId')({
@@ -93,11 +94,11 @@ function MilestoneDetailPage() {
93
94
  <Breadcrumb
94
95
  items={[
95
96
  { label: 'Milestones', href: '/milestones' },
96
- { label: `${milestone.id.replace('milestone_', 'M')} — ${milestone.name}` },
97
+ { label: formatMilestoneName(milestone) },
97
98
  ]}
98
99
  />
99
100
 
100
- <h1 className="text-xl font-semibold text-gray-100 mb-3">{milestone.name}</h1>
101
+ <h1 className="text-xl font-semibold text-gray-100 dark:text-gray-100 mb-3">{formatMilestoneName(milestone)}</h1>
101
102
 
102
103
  <div className="flex items-center gap-3 mb-4">
103
104
  <div className="flex-1 max-w-xs">
@@ -133,21 +134,21 @@ function MilestoneDetailPage() {
133
134
  <h2 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
134
135
  Tasks
135
136
  </h2>
136
- <div className="bg-gray-900/50 border border-gray-800 rounded-xl divide-y divide-gray-800">
137
+ <div className="bg-gray-100 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-800 rounded-xl divide-y divide-gray-200 dark:divide-gray-800">
137
138
  {tasks.map((task) => (
138
139
  <Link
139
140
  key={task.id}
140
141
  to="/tasks/$taskId"
141
142
  params={{ taskId: task.id }}
142
- className="flex items-center gap-2 px-4 py-2.5 text-sm hover:bg-gray-800/50 transition-colors first:rounded-t-xl last:rounded-b-xl"
143
+ className="flex items-center gap-2 px-4 py-2.5 text-sm hover:bg-gray-200/50 dark:hover:bg-gray-800/50 transition-colors first:rounded-t-xl last:rounded-b-xl"
143
144
  >
144
145
  <StatusDot status={task.status} />
145
- <span className={task.status === 'completed' ? 'text-gray-500' : 'text-gray-200'}>
146
- {task.name}
146
+ <span className={task.status === 'completed' ? 'text-gray-500 dark:text-gray-500' : 'text-gray-900 dark:text-gray-200'}>
147
+ {formatTaskName(task)}
147
148
  </span>
148
149
  <PriorityBadge priority={task.priority} />
149
150
  {task.estimated_hours && (
150
- <span className="text-xs text-gray-600 ml-auto">{task.estimated_hours}h</span>
151
+ <span className="text-xs text-gray-600 dark:text-gray-600 ml-auto">{task.estimated_hours}h</span>
151
152
  )}
152
153
  </Link>
153
154
  ))}
@@ -5,6 +5,7 @@ import { StatusBadge } from '../components/StatusBadge'
5
5
  import { StatusDot } from '../components/StatusDot'
6
6
  import { buildSearchIndex } from '../lib/search'
7
7
  import { useProgressData } from '../contexts/ProgressContext'
8
+ import { formatTaskName, formatMilestoneName } from '../lib/display'
8
9
 
9
10
  export const Route = createFileRoute('/search')({
10
11
  component: SearchPage,
@@ -45,21 +46,21 @@ function SearchPage() {
45
46
  {results.map((result, i) => (
46
47
  <div
47
48
  key={i}
48
- className="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3"
49
+ className="bg-gray-100 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-800 rounded-lg px-4 py-3"
49
50
  >
50
51
  <div className="flex items-center gap-3">
51
52
  {result.item.type === 'task' && result.item.task ? (
52
53
  <>
53
54
  <StatusDot status={result.item.task.status} />
54
- <span className="text-sm">{result.item.task.name}</span>
55
- <span className="text-xs text-gray-600 ml-auto">
56
- {result.item.milestone.name}
55
+ <span className="text-sm text-gray-900 dark:text-gray-200">{formatTaskName(result.item.task)}</span>
56
+ <span className="text-xs text-gray-600 dark:text-gray-600 ml-auto">
57
+ {formatMilestoneName(result.item.milestone)}
57
58
  </span>
58
59
  </>
59
60
  ) : (
60
61
  <>
61
- <span className="text-sm font-medium">
62
- {result.item.milestone.name}
62
+ <span className="text-sm font-medium text-gray-900 dark:text-gray-200">
63
+ {formatMilestoneName(result.item.milestone)}
63
64
  </span>
64
65
  <StatusBadge status={result.item.milestone.status} />
65
66
  </>
@@ -7,6 +7,7 @@ import { PriorityBadge } from '../components/PriorityBadge'
7
7
  import { MarkdownContent, buildLinkMap } from '../components/MarkdownContent'
8
8
  import { getMarkdownContent } from '../services/markdown.service'
9
9
  import { resolveTaskFile } from '../services/markdown.service'
10
+ import { formatTaskName, formatMilestoneName } from '../lib/display'
10
11
  import type { MarkdownResult } from '../services/markdown.service'
11
12
 
12
13
  export const Route = createFileRoute('/tasks/$taskId')({
@@ -109,9 +110,9 @@ function TaskDetailPage() {
109
110
  <Link
110
111
  to="/milestones/$milestoneId"
111
112
  params={{ milestoneId: milestone.id }}
112
- className="text-blue-400 hover:underline"
113
+ className="text-blue-500 dark:text-blue-400 hover:underline"
113
114
  >
114
- {milestone.id.replace('milestone_', 'M')} — {milestone.name}
115
+ {formatMilestoneName(milestone)}
115
116
  </Link>
116
117
  ),
117
118
  },
@@ -122,12 +123,12 @@ function TaskDetailPage() {
122
123
  <Breadcrumb
123
124
  items={[
124
125
  { label: 'Milestones', href: '/milestones' },
125
- { label: `${milestone.id.replace('milestone_', 'M')} — ${milestone.name}`, href: `/milestones/${milestone.id}` },
126
- { label: task.name },
126
+ { label: formatMilestoneName(milestone), href: `/milestones/${milestone.id}` },
127
+ { label: formatTaskName(task) },
127
128
  ]}
128
129
  />
129
130
 
130
- <h1 className="text-xl font-semibold text-gray-100 mb-3">{task.name}</h1>
131
+ <h1 className="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">{formatTaskName(task)}</h1>
131
132
 
132
133
  <div className="flex items-center gap-2 mb-4">
133
134
  <PriorityBadge priority={task.priority} />
@@ -152,14 +153,14 @@ function TaskDetailPage() {
152
153
 
153
154
  {/* Prev / Next navigation */}
154
155
  {(siblings.prev || siblings.next) && (
155
- <div className="mt-8 flex items-center justify-between border-t border-gray-800 pt-4">
156
+ <div className="mt-8 flex items-center justify-between border-t border-gray-200 dark:border-gray-800 pt-4">
156
157
  {siblings.prev ? (
157
158
  <Link
158
159
  to="/tasks/$taskId"
159
160
  params={{ taskId: siblings.prev.id }}
160
- className="text-sm text-gray-400 hover:text-gray-200 transition-colors"
161
+ className="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors"
161
162
  >
162
- ← {siblings.prev.name}
163
+ ← {formatTaskName(siblings.prev)}
163
164
  </Link>
164
165
  ) : (
165
166
  <span />
@@ -168,9 +169,9 @@ function TaskDetailPage() {
168
169
  <Link
169
170
  to="/tasks/$taskId"
170
171
  params={{ taskId: siblings.next.id }}
171
- className="text-sm text-gray-400 hover:text-gray-200 transition-colors"
172
+ className="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors"
172
173
  >
173
- {siblings.next.name} →
174
+ {formatTaskName(siblings.next)} →
174
175
  </Link>
175
176
  ) : (
176
177
  <span />
@@ -2,6 +2,7 @@ import { createFileRoute, Link } from '@tanstack/react-router'
2
2
  import { StatusDot } from '../components/StatusDot'
3
3
  import { ExtraFieldsBadge } from '../components/ExtraFieldsBadge'
4
4
  import { useProgressData } from '../contexts/ProgressContext'
5
+ import { formatTaskName, formatMilestoneName } from '../lib/display'
5
6
  import type { Task } from '../lib/types'
6
7
 
7
8
  export const Route = createFileRoute('/tasks/')({
@@ -14,7 +15,7 @@ function TasksPage() {
14
15
  if (!progressData) {
15
16
  return (
16
17
  <div className="p-6">
17
- <p className="text-gray-600 text-sm">No data loaded</p>
18
+ <p className="text-gray-600 dark:text-gray-600 text-sm">No data loaded</p>
18
19
  </div>
19
20
  )
20
21
  }
@@ -23,7 +24,7 @@ function TasksPage() {
23
24
  for (const milestone of progressData.milestones) {
24
25
  const tasks = progressData.tasks[milestone.id] || []
25
26
  for (const task of tasks) {
26
- allTasks.push({ ...task, milestoneName: milestone.name })
27
+ allTasks.push({ ...task, milestoneName: formatMilestoneName(milestone) })
27
28
  }
28
29
  }
29
30
 
@@ -32,24 +33,24 @@ function TasksPage() {
32
33
  <h2 className="text-lg font-semibold mb-4">
33
34
  All Tasks ({allTasks.length})
34
35
  </h2>
35
- <div className="border border-gray-800 rounded-lg overflow-hidden">
36
+ <div className="border border-gray-200 dark:border-gray-800 rounded-lg overflow-hidden">
36
37
  {allTasks.map((task) => (
37
38
  <Link
38
39
  key={task.id}
39
40
  to="/tasks/$taskId"
40
41
  params={{ taskId: task.id }}
41
- className="flex items-center gap-3 px-4 py-2.5 border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors"
42
+ className="flex items-center gap-3 px-4 py-2.5 border-b border-gray-200 dark:border-gray-800/50 hover:bg-gray-200/50 dark:hover:bg-gray-800/30 transition-colors"
42
43
  >
43
44
  <StatusDot status={task.status} />
44
45
  <span
45
46
  className={`flex-1 text-sm ${
46
- task.status === 'completed' ? 'text-gray-500' : 'text-gray-200'
47
+ task.status === 'completed' ? 'text-gray-500 dark:text-gray-500' : 'text-gray-900 dark:text-gray-200'
47
48
  }`}
48
49
  >
49
- {task.name}
50
+ {formatTaskName(task)}
50
51
  </span>
51
- <span className="text-xs text-gray-600">{task.milestoneName}</span>
52
- <span className="text-xs text-gray-500 font-mono w-8 text-right">
52
+ <span className="text-xs text-gray-600 dark:text-gray-600">{task.milestoneName}</span>
53
+ <span className="text-xs text-gray-500 dark:text-gray-500 font-mono w-8 text-right">
53
54
  {task.estimated_hours}h
54
55
  </span>
55
56
  <ExtraFieldsBadge fields={task.extra} />
package/src/styles.css CHANGED
@@ -2,6 +2,8 @@
2
2
  @plugin "@tailwindcss/typography";
3
3
  @import "highlight.js/styles/github-dark.css";
4
4
 
5
+ @variant dark (&:where(.dark, .dark *));
6
+
5
7
  html {
6
8
  @apply bg-white dark:bg-gray-950;
7
9
  }