prjct-cli 0.18.2 → 0.20.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.
Files changed (246) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/CLAUDE.md +74 -211
  3. package/core/agentic/prompt-builder.ts +3 -7
  4. package/core/command-registry/optional-commands.ts +0 -20
  5. package/core/infrastructure/command-installer/command-installer.ts +8 -1
  6. package/core/infrastructure/command-installer/global-config.ts +31 -1
  7. package/core/infrastructure/command-installer/index.ts +1 -1
  8. package/core/infrastructure/setup.ts +3 -0
  9. package/package.json +3 -17
  10. package/templates/agentic/agents/uxui.md +210 -0
  11. package/templates/commands/bug.md +219 -41
  12. package/templates/commands/done.md +57 -258
  13. package/templates/commands/feature.md +368 -80
  14. package/templates/commands/now.md +72 -277
  15. package/templates/commands/ship.md +167 -246
  16. package/templates/commands/sync.md +62 -3
  17. package/templates/commands/test.md +160 -20
  18. package/templates/global/CLAUDE.md +40 -205
  19. package/templates/global/docs/agents.md +88 -0
  20. package/templates/global/docs/architecture.md +103 -0
  21. package/templates/global/docs/commands.md +98 -0
  22. package/templates/global/docs/validation.md +95 -0
  23. package/bin/dev.js +0 -216
  24. package/bin/serve.js +0 -361
  25. package/packages/web/README.md +0 -36
  26. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  27. package/packages/web/app/api/claude/status/route.ts +0 -34
  28. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  29. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  30. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  31. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  32. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  33. package/packages/web/app/api/projects/route.ts +0 -16
  34. package/packages/web/app/api/sessions/current/route.ts +0 -132
  35. package/packages/web/app/api/sessions/history/route.ts +0 -204
  36. package/packages/web/app/error.tsx +0 -34
  37. package/packages/web/app/favicon.ico +0 -0
  38. package/packages/web/app/globals.css +0 -198
  39. package/packages/web/app/layout.tsx +0 -53
  40. package/packages/web/app/loading.tsx +0 -7
  41. package/packages/web/app/not-found.tsx +0 -25
  42. package/packages/web/app/page.tsx +0 -12
  43. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  44. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  45. package/packages/web/app/project/[id]/error.tsx +0 -41
  46. package/packages/web/app/project/[id]/loading.tsx +0 -9
  47. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  48. package/packages/web/app/project/[id]/page.tsx +0 -384
  49. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  50. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  51. package/packages/web/app/sessions/page.tsx +0 -165
  52. package/packages/web/app/settings/page.tsx +0 -151
  53. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  54. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  55. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  56. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  57. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  58. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  59. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  60. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  61. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  62. package/packages/web/components/AgentsCard/index.ts +0 -2
  63. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  64. package/packages/web/components/AppSidebar/index.ts +0 -1
  65. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  66. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  67. package/packages/web/components/BackLink/index.ts +0 -2
  68. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  69. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  70. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  71. package/packages/web/components/BentoCard/index.ts +0 -2
  72. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  73. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  74. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  75. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  76. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  77. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  78. package/packages/web/components/BentoGrid/index.ts +0 -2
  79. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  80. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  81. package/packages/web/components/BlockersCard/index.ts +0 -2
  82. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  83. package/packages/web/components/CommandBar/index.ts +0 -1
  84. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  85. package/packages/web/components/CommandButton/index.ts +0 -1
  86. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  87. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  88. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  89. package/packages/web/components/DashboardContent/index.ts +0 -1
  90. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  91. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  92. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  93. package/packages/web/components/DateGroup/index.ts +0 -2
  94. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  95. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  96. package/packages/web/components/EmptyState/index.ts +0 -2
  97. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  98. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  99. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  100. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  101. package/packages/web/components/EventRow/index.ts +0 -2
  102. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  103. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  104. package/packages/web/components/ExpandButton/index.ts +0 -2
  105. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  106. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  107. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  108. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  109. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  110. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  111. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  112. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  113. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  114. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  115. package/packages/web/components/HeroSection/index.ts +0 -2
  116. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  117. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  118. package/packages/web/components/IdeasCard/index.ts +0 -2
  119. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  120. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  121. package/packages/web/components/InsightMessage/index.ts +0 -2
  122. package/packages/web/components/Logo/Logo.tsx +0 -65
  123. package/packages/web/components/Logo/index.ts +0 -1
  124. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  125. package/packages/web/components/MarkdownContent/index.ts +0 -1
  126. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  127. package/packages/web/components/MasonryGrid/index.ts +0 -1
  128. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  129. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  130. package/packages/web/components/MomentumWidget/index.ts +0 -2
  131. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  132. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  133. package/packages/web/components/NowCard/index.ts +0 -2
  134. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  135. package/packages/web/components/PageHeader/index.ts +0 -1
  136. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  137. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  138. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  139. package/packages/web/components/ProgressRing/index.ts +0 -2
  140. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  141. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  142. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  143. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  144. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  145. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  146. package/packages/web/components/Providers/Providers.tsx +0 -48
  147. package/packages/web/components/Providers/index.ts +0 -1
  148. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  149. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  150. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  151. package/packages/web/components/QueueCard/index.ts +0 -2
  152. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  153. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  154. package/packages/web/components/RecoverCard/index.ts +0 -2
  155. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  156. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  157. package/packages/web/components/RoadmapCard/index.ts +0 -2
  158. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  159. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  160. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  161. package/packages/web/components/ShipsCard/index.ts +0 -2
  162. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  163. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  164. package/packages/web/components/SparklineChart/index.ts +0 -2
  165. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  166. package/packages/web/components/StatsMasonry/index.ts +0 -1
  167. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  168. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  169. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  170. package/packages/web/components/StreakCard/index.ts +0 -2
  171. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  172. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  173. package/packages/web/components/TasksCounter/index.ts +0 -2
  174. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  175. package/packages/web/components/TechStackBadges/index.ts +0 -1
  176. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  177. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  178. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  179. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  180. package/packages/web/components/TerminalDock/index.ts +0 -2
  181. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  182. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  183. package/packages/web/components/TerminalTabs/index.ts +0 -1
  184. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  185. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  186. package/packages/web/components/VelocityBadge/index.ts +0 -2
  187. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  188. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  189. package/packages/web/components/VelocityCard/index.ts +0 -2
  190. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  191. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  192. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  193. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  194. package/packages/web/components/WeeklyReports/index.ts +0 -4
  195. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  196. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  197. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  198. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  199. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  200. package/packages/web/components/ui/badge.tsx +0 -46
  201. package/packages/web/components/ui/button.tsx +0 -60
  202. package/packages/web/components/ui/card.tsx +0 -92
  203. package/packages/web/components/ui/chart.tsx +0 -385
  204. package/packages/web/components/ui/dialog.tsx +0 -143
  205. package/packages/web/components/ui/drawer.tsx +0 -135
  206. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  207. package/packages/web/components/ui/input.tsx +0 -21
  208. package/packages/web/components/ui/scroll-area.tsx +0 -58
  209. package/packages/web/components/ui/select.tsx +0 -187
  210. package/packages/web/components/ui/sheet.tsx +0 -139
  211. package/packages/web/components/ui/tabs.tsx +0 -66
  212. package/packages/web/components/ui/tooltip.tsx +0 -61
  213. package/packages/web/components.json +0 -22
  214. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  215. package/packages/web/context/TerminalContext.tsx +0 -45
  216. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  217. package/packages/web/eslint.config.mjs +0 -18
  218. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  219. package/packages/web/hooks/useProjectStats.ts +0 -93
  220. package/packages/web/hooks/useProjects.ts +0 -73
  221. package/packages/web/lib/actions/projects.ts +0 -15
  222. package/packages/web/lib/commands.ts +0 -81
  223. package/packages/web/lib/format.ts +0 -23
  224. package/packages/web/lib/generate-week-report.ts +0 -285
  225. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  226. package/packages/web/lib/project-colors.ts +0 -58
  227. package/packages/web/lib/projects.ts +0 -506
  228. package/packages/web/lib/pty.ts +0 -101
  229. package/packages/web/lib/query-config.ts +0 -44
  230. package/packages/web/lib/services/index.ts +0 -9
  231. package/packages/web/lib/services/projects.server.ts +0 -66
  232. package/packages/web/lib/services/stats.server.ts +0 -562
  233. package/packages/web/lib/unified-loader.ts +0 -396
  234. package/packages/web/lib/utils.ts +0 -6
  235. package/packages/web/next-env.d.ts +0 -6
  236. package/packages/web/next.config.ts +0 -7
  237. package/packages/web/package.json +0 -57
  238. package/packages/web/postcss.config.mjs +0 -7
  239. package/packages/web/public/file.svg +0 -1
  240. package/packages/web/public/globe.svg +0 -1
  241. package/packages/web/public/next.svg +0 -1
  242. package/packages/web/public/vercel.svg +0 -1
  243. package/packages/web/public/window.svg +0 -1
  244. package/packages/web/server.ts +0 -312
  245. package/packages/web/tsconfig.json +0 -34
  246. package/templates/commands/serve.md +0 -121
@@ -1,284 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useCallback, useTransition } from 'react'
4
- import Link from 'next/link'
5
- import { useRouter } from 'next/navigation'
6
- import { SessionsChart } from '@/components/charts/SessionsChart'
7
- import { Button } from '@/components/ui/button'
8
- import { ProgressRing } from '@/components/ProgressRing'
9
- import { TechStackBadges } from '@/components/TechStackBadges'
10
- import { formatRelativeTime, formatPath } from '@/lib/format'
11
- import {
12
- MoreHorizontal,
13
- Trash2,
14
- AlertTriangle,
15
- Target,
16
- Lightbulb,
17
- ListTodo,
18
- Zap,
19
- FolderGit2,
20
- Play,
21
- FileText
22
- } from 'lucide-react'
23
- import {
24
- DropdownMenu,
25
- DropdownMenuContent,
26
- DropdownMenuItem,
27
- DropdownMenuTrigger,
28
- } from '@/components/ui/dropdown-menu'
29
- import {
30
- AlertDialog,
31
- AlertDialogAction,
32
- AlertDialogCancel,
33
- AlertDialogContent,
34
- AlertDialogDescription,
35
- AlertDialogFooter,
36
- AlertDialogHeader,
37
- AlertDialogTitle,
38
- } from '@/components/ui/alert-dialog'
39
- import { deleteProject } from '@/lib/actions/projects'
40
-
41
- // Traffic light colors based on completion rate
42
- type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
43
- function getCompletionColor(rate: number): AccentColor {
44
- if (rate >= 75) return 'success'
45
- if (rate >= 25) return 'warning'
46
- return 'destructive'
47
- }
48
-
49
- export interface Project {
50
- id: string
51
- name: string
52
- path: string
53
- repoPath?: string | null
54
- currentTask?: string | null
55
- hasActiveSession?: boolean
56
- lastActivity?: string | null
57
- ideasCount?: number
58
- nextTasksCount?: number
59
- shippedCount?: number
60
- techStack?: string[]
61
- iconPath?: string | null
62
- version?: string
63
- stack?: string
64
- filesCount?: number
65
- commitsCount?: number
66
- }
67
-
68
- export interface GlobalStats {
69
- userName: string
70
- totalProjects: number
71
- }
72
-
73
- interface DashboardContentProps {
74
- projects: Project[]
75
- stats: GlobalStats
76
- }
77
-
78
- export function DashboardContent({ projects, stats }: DashboardContentProps) {
79
- const [projectToDelete, setProjectToDelete] = useState<Project | null>(null)
80
- const [isPending, startTransition] = useTransition()
81
- const router = useRouter()
82
-
83
- const handleDeleteClick = useCallback((project: Project, e: React.MouseEvent) => {
84
- e.preventDefault()
85
- e.stopPropagation()
86
- setProjectToDelete(project)
87
- }, [])
88
-
89
- const handleConfirmDelete = useCallback(() => {
90
- if (projectToDelete) {
91
- startTransition(async () => {
92
- await deleteProject(projectToDelete.id)
93
- setProjectToDelete(null)
94
- router.refresh()
95
- })
96
- }
97
- }, [projectToDelete, router])
98
-
99
- const projectCount = projects?.length || 0
100
-
101
- return (
102
- <div className="p-4 sm:p-6 md:p-8 h-full overflow-auto">
103
- {/* Mobile: Add padding for hamburger menu */}
104
- <div className="pl-10 md:pl-0">
105
- <p className="text-sm sm:text-base text-muted-foreground">Hola! {stats?.userName || 'Developer'}</p>
106
-
107
- <h1 className="text-6xl sm:text-7xl md:text-8xl font-bold tracking-tighter tabular-nums mt-2">{projectCount}</h1>
108
- <p className="text-sm sm:text-base text-muted-foreground mt-1">{projectCount === 1 ? 'project' : 'projects'}</p>
109
- </div>
110
-
111
- <section className="mt-6 sm:mt-8">
112
- <SessionsChart />
113
- </section>
114
-
115
- <section className="mt-8 sm:mt-10 md:mt-12">
116
- <h2 className="font-bold uppercase tracking-wide text-muted-foreground mb-4 sm:mb-6 text-sm">Active Projects</h2>
117
-
118
- {projects?.length === 0 ? (
119
- <div className="border-2 border-dashed border-border rounded-xl p-6 sm:p-8 text-center">
120
- <p className="text-muted-foreground text-sm sm:text-base">
121
- No projects yet. Initialize with <code className="bg-muted px-2 py-1 rounded font-mono text-xs sm:text-sm">/p:init</code>
122
- </p>
123
- </div>
124
- ) : (
125
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
126
- {projects?.map((project: Project) => (
127
- <ProjectCard key={project.id} project={project} onDeleteClick={handleDeleteClick} />
128
- ))}
129
- </div>
130
- )}
131
- </section>
132
-
133
- <AlertDialog open={!!projectToDelete} onOpenChange={(open: boolean) => !open && setProjectToDelete(null)}>
134
- <AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
135
- <AlertDialogHeader>
136
- <AlertDialogTitle className="flex items-center gap-2">
137
- <AlertTriangle className="w-5 h-5 text-destructive shrink-0" />
138
- Delete Project?
139
- </AlertDialogTitle>
140
- <AlertDialogDescription>
141
- <strong>&quot;{projectToDelete?.name}&quot;</strong> will be moved to trash.
142
- <br />
143
- <span className="text-muted-foreground text-xs sm:text-sm break-all">
144
- Location: ~/.prjct-cli/.trash/{projectToDelete?.id}
145
- </span>
146
- </AlertDialogDescription>
147
- </AlertDialogHeader>
148
- <AlertDialogFooter className="flex-col sm:flex-row gap-2">
149
- <AlertDialogCancel disabled={isPending} className="w-full sm:w-auto">Cancel</AlertDialogCancel>
150
- <AlertDialogAction
151
- onClick={handleConfirmDelete}
152
- disabled={isPending}
153
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
154
- >
155
- {isPending ? 'Deleting...' : 'Delete'}
156
- </AlertDialogAction>
157
- </AlertDialogFooter>
158
- </AlertDialogContent>
159
- </AlertDialog>
160
-
161
- {/* Bottom safe area padding */}
162
- <div className="h-6 sm:h-8" />
163
- </div>
164
- )
165
- }
166
-
167
- interface ProjectCardProps {
168
- project: Project
169
- onDeleteClick: (project: Project, e: React.MouseEvent) => void
170
- }
171
-
172
- function ProjectCard({ project, onDeleteClick }: ProjectCardProps) {
173
- const hasStats = project.currentTask || project.nextTasksCount || project.ideasCount || project.lastActivity
174
-
175
- // Calculate completion rate
176
- const shipped = project.shippedCount ?? 0
177
- const pending = project.nextTasksCount ?? 0
178
- const total = shipped + pending
179
- const completionRate = total > 0 ? Math.round((shipped / total) * 100) : 0
180
- const completionColor = getCompletionColor(completionRate)
181
-
182
- return (
183
- <div className="group relative bg-card border border-border rounded-lg overflow-hidden hover:border-primary/50 active:scale-[0.99] transition-all">
184
- {project.hasActiveSession && <div className="absolute top-0 left-0 right-0 h-0.5 bg-green-500" />}
185
-
186
- <Link href={`/project/${project.id}`} className="block p-3 sm:p-4">
187
- <div className="flex items-start gap-3">
188
- <ProgressRing value={completionRate} size="md" accentColor={completionColor} className="shrink-0" />
189
-
190
- <div className="flex-1 min-w-0">
191
- <div className="flex items-center gap-2">
192
- <h3 className="font-bold truncate text-sm sm:text-base">{project.name}</h3>
193
- {project.hasActiveSession && (
194
- <span className="flex h-2 w-2 shrink-0">
195
- <span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-green-400 opacity-75" />
196
- <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
197
- </span>
198
- )}
199
- </div>
200
- {project.repoPath && (
201
- <p className="text-xs text-muted-foreground truncate mt-0.5 flex items-center gap-1">
202
- <FolderGit2 className="w-3 h-3 shrink-0" />
203
- {formatPath(project.repoPath)}
204
- </p>
205
- )}
206
- </div>
207
-
208
- {/* Mobile: Always show menu button */}
209
- <DropdownMenu>
210
- <DropdownMenuTrigger asChild>
211
- <Button
212
- variant="ghost"
213
- size="icon-sm"
214
- className="shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
215
- onClick={(e) => e.preventDefault()}
216
- >
217
- <MoreHorizontal className="w-4 h-4" />
218
- </Button>
219
- </DropdownMenuTrigger>
220
- <DropdownMenuContent align="end">
221
- <DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
222
- <Link href={`/project/${project.id}/code`}>
223
- <Play className="w-4 h-4 mr-2" />
224
- Start working
225
- </Link>
226
- </DropdownMenuItem>
227
- <DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
228
- <Link href={`/project/${project.id}/reports`}>
229
- <FileText className="w-4 h-4 mr-2" />
230
- Reports
231
- </Link>
232
- </DropdownMenuItem>
233
- <DropdownMenuItem
234
- className="text-destructive focus:text-destructive min-h-[44px] sm:min-h-0"
235
- onClick={(e: React.MouseEvent) => onDeleteClick(project, e)}
236
- >
237
- <Trash2 className="w-4 h-4 mr-2" />
238
- Delete
239
- </DropdownMenuItem>
240
- </DropdownMenuContent>
241
- </DropdownMenu>
242
- </div>
243
-
244
- {project.currentTask && (
245
- <div className="mt-3 p-2 bg-primary/5 rounded border border-primary/10">
246
- <div className="flex items-start gap-2">
247
- <Target className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
248
- <span className="text-xs sm:text-sm text-foreground line-clamp-2">{project.currentTask}</span>
249
- </div>
250
- </div>
251
- )}
252
-
253
- {hasStats && (
254
- <div className="mt-3 flex flex-wrap items-center gap-2 sm:gap-3 text-xs text-muted-foreground">
255
- {(project.nextTasksCount ?? 0) > 0 && (
256
- <div className="flex items-center gap-1">
257
- <ListTodo className="w-3.5 h-3.5" />
258
- <span>{project.nextTasksCount} queued</span>
259
- </div>
260
- )}
261
- {(project.ideasCount ?? 0) > 0 && (
262
- <div className="flex items-center gap-1">
263
- <Lightbulb className="w-3.5 h-3.5" />
264
- <span>{project.ideasCount} ideas</span>
265
- </div>
266
- )}
267
- {project.lastActivity && (
268
- <div className="flex items-center gap-1 ml-auto">
269
- <Zap className="w-3.5 h-3.5" />
270
- <span>{formatRelativeTime(project.lastActivity)}</span>
271
- </div>
272
- )}
273
- </div>
274
- )}
275
-
276
- {project.techStack && project.techStack.length > 0 && (
277
- <div className="mt-3">
278
- <TechStackBadges techStack={project.techStack} />
279
- </div>
280
- )}
281
- </Link>
282
- </div>
283
- )
284
- }
@@ -1 +0,0 @@
1
- export { DashboardContent } from './DashboardContent'
@@ -1,18 +0,0 @@
1
- import { EventRow } from '@/components/EventRow'
2
- import { formatTimelineDate } from './DateGroup.utils'
3
- import type { DateGroupProps } from './DateGroup.types'
4
-
5
- export function DateGroup({ dateKey, events }: DateGroupProps) {
6
- return (
7
- <div>
8
- <p className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-2">
9
- {formatTimelineDate(dateKey + 'T00:00:00')}
10
- </p>
11
- <div className="space-y-1">
12
- {events.map((event, i) => (
13
- <EventRow key={i} event={event} />
14
- ))}
15
- </div>
16
- </div>
17
- )
18
- }
@@ -1,6 +0,0 @@
1
- import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
-
3
- export interface DateGroupProps {
4
- dateKey: string
5
- events: TimelineEvent[]
6
- }
@@ -1,11 +0,0 @@
1
- export function formatTimelineDate(dateString: string): string {
2
- const date = new Date(dateString)
3
- const today = new Date()
4
- const yesterday = new Date(today)
5
- yesterday.setDate(yesterday.getDate() - 1)
6
-
7
- if (date.toDateString() === today.toDateString()) return 'Today'
8
- if (date.toDateString() === yesterday.toDateString()) return 'Yesterday'
9
-
10
- return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })
11
- }
@@ -1,2 +0,0 @@
1
- export { DateGroup } from './DateGroup'
2
- export type { DateGroupProps } from './DateGroup.types'
@@ -1,76 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { cn } from '@/lib/utils'
5
- import { ArrowRight } from 'lucide-react'
6
- import type { EmptyStateProps } from './EmptyState.types'
7
-
8
- export function EmptyState({
9
- icon: Icon,
10
- title,
11
- description,
12
- command,
13
- href,
14
- className,
15
- compact = false,
16
- }: EmptyStateProps) {
17
- // Command button content
18
- const CommandButton = href ? (
19
- <Link
20
- href={href}
21
- className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
22
- >
23
- {command}
24
- <ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
25
- </Link>
26
- ) : command ? (
27
- <button
28
- onClick={() => navigator.clipboard.writeText(command)}
29
- className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs font-mono hover:bg-muted/80"
30
- >
31
- {command}
32
- </button>
33
- ) : null
34
-
35
- if (compact) {
36
- return (
37
- <div className={cn('flex items-center gap-2 text-muted-foreground', className)}>
38
- <Icon className="h-4 w-4" />
39
- <span className="text-sm">{title}</span>
40
- {CommandButton}
41
- </div>
42
- )
43
- }
44
-
45
- return (
46
- <div className={cn('flex flex-col items-center justify-center text-center py-4', className)}>
47
- <div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3">
48
- <Icon className="h-5 w-5 text-muted-foreground" />
49
- </div>
50
- <p className="text-sm font-medium text-muted-foreground">{title}</p>
51
- {description && (
52
- <p className="text-xs text-muted-foreground/70 mt-1">{description}</p>
53
- )}
54
- {command && (
55
- <div className="mt-2">
56
- {href ? (
57
- <Link
58
- href={href}
59
- className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
60
- >
61
- {command}
62
- <ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
63
- </Link>
64
- ) : (
65
- <button
66
- onClick={() => navigator.clipboard.writeText(command)}
67
- className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-muted text-xs font-mono text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors"
68
- >
69
- {command}
70
- </button>
71
- )}
72
- </div>
73
- )}
74
- </div>
75
- )
76
- }
@@ -1,11 +0,0 @@
1
- import type { LucideIcon } from 'lucide-react'
2
-
3
- export interface EmptyStateProps {
4
- icon: LucideIcon
5
- title: string
6
- description?: string
7
- command?: string
8
- href?: string
9
- className?: string
10
- compact?: boolean
11
- }
@@ -1,2 +0,0 @@
1
- export { EmptyState } from './EmptyState'
2
- export type { EmptyStateProps } from './EmptyState.types'
@@ -1,10 +0,0 @@
1
- import { Activity, CheckCircle2, Rocket, Target, RefreshCw } from 'lucide-react'
2
- import type { EventIconName } from './EventRow.types'
3
-
4
- export const EVENT_ICON_MAP: Record<EventIconName, typeof Activity> = {
5
- check: CheckCircle2,
6
- target: Target,
7
- rocket: Rocket,
8
- refresh: RefreshCw,
9
- activity: Activity,
10
- }
@@ -1,49 +0,0 @@
1
- import { cn } from '@/lib/utils'
2
- import { EVENT_ICON_MAP } from './EventRow.constants'
3
- import {
4
- formatTime,
5
- getEventIconName,
6
- getEventColor,
7
- getEventBadge,
8
- getEventLabel,
9
- } from './EventRow.utils'
10
- import type { EventRowProps } from './EventRow.types'
11
-
12
- export function EventRow({ event }: EventRowProps) {
13
- const iconName = getEventIconName(event.type)
14
- const Icon = EVENT_ICON_MAP[iconName]
15
- const duration = 'duration' in event && typeof event.duration === 'string' ? event.duration : null
16
-
17
- return (
18
- <div className="flex items-center gap-2 sm:gap-3 py-2 sm:py-1.5 px-2 -mx-2 rounded-md hover:bg-muted/50 active:bg-muted/70 transition-colors group">
19
- {event.ts && (
20
- <span className="hidden sm:block text-xs text-muted-foreground w-14 shrink-0 tabular-nums">
21
- {formatTime(event.ts)}
22
- </span>
23
- )}
24
-
25
- <Icon className={cn('h-4 w-4 sm:h-3.5 sm:w-3.5 shrink-0', getEventColor(event.type))} />
26
-
27
- <div className="flex-1 min-w-0">
28
- <span className="text-sm truncate block group-hover:text-foreground transition-colors">
29
- {getEventLabel(event)}
30
- </span>
31
- {event.ts && (
32
- <span className="sm:hidden text-xs text-muted-foreground">
33
- {formatTime(event.ts)}
34
- </span>
35
- )}
36
- </div>
37
-
38
- <span className="text-xs font-bold tracking-wider text-muted-foreground shrink-0">
39
- {getEventBadge(event.type)}
40
- </span>
41
-
42
- {duration && (
43
- <span className="text-xs text-muted-foreground shrink-0">
44
- {duration}
45
- </span>
46
- )}
47
- </div>
48
- )
49
- }
@@ -1,7 +0,0 @@
1
- import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
-
3
- export type EventIconName = 'check' | 'target' | 'rocket' | 'refresh' | 'activity'
4
-
5
- export interface EventRowProps {
6
- event: TimelineEvent
7
- }
@@ -1,49 +0,0 @@
1
- import type { EventIconName } from './EventRow.types'
2
-
3
- export function formatTime(dateString: string): string {
4
- const date = new Date(dateString)
5
- return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })
6
- }
7
-
8
- export function getEventIconName(type: string): EventIconName {
9
- const iconMap: Record<string, EventIconName> = {
10
- task_complete: 'check',
11
- task_start: 'target',
12
- feature_ship: 'rocket',
13
- sync: 'refresh',
14
- }
15
- return iconMap[type] ?? 'activity'
16
- }
17
-
18
- export function getEventColor(type: string): string {
19
- const colorMap: Record<string, string> = {
20
- task_complete: 'text-muted-foreground',
21
- task_start: 'text-muted-foreground',
22
- feature_ship: 'text-muted-foreground',
23
- sync: 'text-muted-foreground',
24
- }
25
- return colorMap[type] ?? 'text-muted-foreground'
26
- }
27
-
28
- export function getEventBadge(type: string): string {
29
- const badgeMap: Record<string, string> = {
30
- task_complete: 'DONE',
31
- task_start: 'START',
32
- feature_ship: 'SHIP',
33
- sync: 'SYNC',
34
- }
35
- return badgeMap[type] ?? type.toUpperCase()
36
- }
37
-
38
- export function getEventLabel(event: unknown): string {
39
- const e = event as { type?: string; task?: string; name?: string }
40
- const type = e.type ?? ''
41
-
42
- const labelMap: Record<string, string> = {
43
- task_complete: e.task ?? 'Task completed',
44
- task_start: e.task ?? 'Task started',
45
- feature_ship: e.name ?? 'Feature shipped',
46
- sync: 'Project synced',
47
- }
48
- return labelMap[type] ?? type
49
- }
@@ -1,2 +0,0 @@
1
- export { EventRow } from './EventRow'
2
- export type { EventRowProps } from './EventRow.types'
@@ -1,18 +0,0 @@
1
- import { ChevronDown } from 'lucide-react'
2
- import { cn } from '@/lib/utils'
3
- import { Button } from '@/components/ui/button'
4
- import type { ExpandButtonProps } from './ExpandButton.types'
5
-
6
- export function ExpandButton({ expanded, totalCount, collapsedLimit, onToggle }: ExpandButtonProps) {
7
- return (
8
- <Button
9
- variant="ghost"
10
- size="sm"
11
- onClick={onToggle}
12
- className="w-full text-muted-foreground min-h-[44px]"
13
- >
14
- <ChevronDown className={cn('h-4 w-4 mr-1 transition-transform', expanded && 'rotate-180')} />
15
- {expanded ? 'Show less' : `Show ${totalCount - collapsedLimit} more`}
16
- </Button>
17
- )
18
- }
@@ -1,6 +0,0 @@
1
- export interface ExpandButtonProps {
2
- expanded: boolean
3
- totalCount: number
4
- collapsedLimit: number
5
- onToggle: () => void
6
- }
@@ -1,2 +0,0 @@
1
- export { ExpandButton } from './ExpandButton'
2
- export type { ExpandButtonProps } from './ExpandButton.types'
@@ -1,14 +0,0 @@
1
- import { cn } from '@/lib/utils'
2
- import { getHealthGradient } from './HealthGradientBackground.utils'
3
- import type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'
4
-
5
- export function HealthGradientBackground({ score }: HealthGradientBackgroundProps) {
6
- return (
7
- <div
8
- className={cn(
9
- 'absolute inset-0 -m-4 md:-m-8 rounded-2xl opacity-30 blur-3xl transition-colors duration-1000',
10
- getHealthGradient(score)
11
- )}
12
- />
13
- )
14
- }
@@ -1,5 +0,0 @@
1
- export type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
2
-
3
- export interface HealthGradientBackgroundProps {
4
- score: number
5
- }
@@ -1,13 +0,0 @@
1
- import type { AccentColor } from './HealthGradientBackground.types'
2
-
3
- export function getHealthColor(score: number): AccentColor {
4
- if (score >= 70) return 'success'
5
- if (score >= 40) return 'warning'
6
- return 'destructive'
7
- }
8
-
9
- export function getHealthGradient(score: number): string {
10
- if (score >= 70) return 'bg-gradient-to-br from-emerald-500/20 to-transparent'
11
- if (score >= 40) return 'bg-gradient-to-br from-amber-500/20 to-transparent'
12
- return 'bg-gradient-to-br from-destructive/20 to-transparent'
13
- }
@@ -1,2 +0,0 @@
1
- export { HealthGradientBackground } from './HealthGradientBackground'
2
- export type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'