prjct-cli 0.18.2 → 0.19.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 (243) hide show
  1. package/CHANGELOG.md +40 -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/commands/done.md +57 -258
  11. package/templates/commands/now.md +72 -277
  12. package/templates/commands/ship.md +55 -261
  13. package/templates/commands/test.md +328 -21
  14. package/templates/global/CLAUDE.md +40 -205
  15. package/templates/global/docs/agents.md +88 -0
  16. package/templates/global/docs/architecture.md +103 -0
  17. package/templates/global/docs/commands.md +98 -0
  18. package/templates/global/docs/validation.md +95 -0
  19. package/templates/mcp-config.json +36 -0
  20. package/bin/dev.js +0 -216
  21. package/bin/serve.js +0 -361
  22. package/packages/web/README.md +0 -36
  23. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  24. package/packages/web/app/api/claude/status/route.ts +0 -34
  25. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  26. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  27. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  28. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  29. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  30. package/packages/web/app/api/projects/route.ts +0 -16
  31. package/packages/web/app/api/sessions/current/route.ts +0 -132
  32. package/packages/web/app/api/sessions/history/route.ts +0 -204
  33. package/packages/web/app/error.tsx +0 -34
  34. package/packages/web/app/favicon.ico +0 -0
  35. package/packages/web/app/globals.css +0 -198
  36. package/packages/web/app/layout.tsx +0 -53
  37. package/packages/web/app/loading.tsx +0 -7
  38. package/packages/web/app/not-found.tsx +0 -25
  39. package/packages/web/app/page.tsx +0 -12
  40. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  41. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  42. package/packages/web/app/project/[id]/error.tsx +0 -41
  43. package/packages/web/app/project/[id]/loading.tsx +0 -9
  44. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  45. package/packages/web/app/project/[id]/page.tsx +0 -384
  46. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  47. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  48. package/packages/web/app/sessions/page.tsx +0 -165
  49. package/packages/web/app/settings/page.tsx +0 -151
  50. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  51. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  52. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  53. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  54. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  55. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  56. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  57. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  58. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  59. package/packages/web/components/AgentsCard/index.ts +0 -2
  60. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  61. package/packages/web/components/AppSidebar/index.ts +0 -1
  62. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  63. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  64. package/packages/web/components/BackLink/index.ts +0 -2
  65. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  66. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  67. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  68. package/packages/web/components/BentoCard/index.ts +0 -2
  69. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  70. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  71. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  72. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  73. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  74. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  75. package/packages/web/components/BentoGrid/index.ts +0 -2
  76. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  77. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  78. package/packages/web/components/BlockersCard/index.ts +0 -2
  79. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  80. package/packages/web/components/CommandBar/index.ts +0 -1
  81. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  82. package/packages/web/components/CommandButton/index.ts +0 -1
  83. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  84. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  85. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  86. package/packages/web/components/DashboardContent/index.ts +0 -1
  87. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  88. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  89. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  90. package/packages/web/components/DateGroup/index.ts +0 -2
  91. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  92. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  93. package/packages/web/components/EmptyState/index.ts +0 -2
  94. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  95. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  96. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  97. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  98. package/packages/web/components/EventRow/index.ts +0 -2
  99. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  100. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  101. package/packages/web/components/ExpandButton/index.ts +0 -2
  102. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  103. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  104. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  105. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  106. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  107. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  108. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  109. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  110. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  111. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  112. package/packages/web/components/HeroSection/index.ts +0 -2
  113. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  114. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  115. package/packages/web/components/IdeasCard/index.ts +0 -2
  116. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  117. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  118. package/packages/web/components/InsightMessage/index.ts +0 -2
  119. package/packages/web/components/Logo/Logo.tsx +0 -65
  120. package/packages/web/components/Logo/index.ts +0 -1
  121. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  122. package/packages/web/components/MarkdownContent/index.ts +0 -1
  123. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  124. package/packages/web/components/MasonryGrid/index.ts +0 -1
  125. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  126. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  127. package/packages/web/components/MomentumWidget/index.ts +0 -2
  128. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  129. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  130. package/packages/web/components/NowCard/index.ts +0 -2
  131. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  132. package/packages/web/components/PageHeader/index.ts +0 -1
  133. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  134. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  135. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  136. package/packages/web/components/ProgressRing/index.ts +0 -2
  137. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  138. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  139. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  140. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  141. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  142. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  143. package/packages/web/components/Providers/Providers.tsx +0 -48
  144. package/packages/web/components/Providers/index.ts +0 -1
  145. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  146. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  147. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  148. package/packages/web/components/QueueCard/index.ts +0 -2
  149. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  150. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  151. package/packages/web/components/RecoverCard/index.ts +0 -2
  152. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  153. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  154. package/packages/web/components/RoadmapCard/index.ts +0 -2
  155. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  156. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  157. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  158. package/packages/web/components/ShipsCard/index.ts +0 -2
  159. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  160. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  161. package/packages/web/components/SparklineChart/index.ts +0 -2
  162. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  163. package/packages/web/components/StatsMasonry/index.ts +0 -1
  164. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  165. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  166. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  167. package/packages/web/components/StreakCard/index.ts +0 -2
  168. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  169. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  170. package/packages/web/components/TasksCounter/index.ts +0 -2
  171. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  172. package/packages/web/components/TechStackBadges/index.ts +0 -1
  173. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  174. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  175. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  176. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  177. package/packages/web/components/TerminalDock/index.ts +0 -2
  178. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  179. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  180. package/packages/web/components/TerminalTabs/index.ts +0 -1
  181. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  182. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  183. package/packages/web/components/VelocityBadge/index.ts +0 -2
  184. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  185. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  186. package/packages/web/components/VelocityCard/index.ts +0 -2
  187. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  188. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  189. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  190. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  191. package/packages/web/components/WeeklyReports/index.ts +0 -4
  192. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  193. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  194. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  195. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  196. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  197. package/packages/web/components/ui/badge.tsx +0 -46
  198. package/packages/web/components/ui/button.tsx +0 -60
  199. package/packages/web/components/ui/card.tsx +0 -92
  200. package/packages/web/components/ui/chart.tsx +0 -385
  201. package/packages/web/components/ui/dialog.tsx +0 -143
  202. package/packages/web/components/ui/drawer.tsx +0 -135
  203. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  204. package/packages/web/components/ui/input.tsx +0 -21
  205. package/packages/web/components/ui/scroll-area.tsx +0 -58
  206. package/packages/web/components/ui/select.tsx +0 -187
  207. package/packages/web/components/ui/sheet.tsx +0 -139
  208. package/packages/web/components/ui/tabs.tsx +0 -66
  209. package/packages/web/components/ui/tooltip.tsx +0 -61
  210. package/packages/web/components.json +0 -22
  211. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  212. package/packages/web/context/TerminalContext.tsx +0 -45
  213. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  214. package/packages/web/eslint.config.mjs +0 -18
  215. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  216. package/packages/web/hooks/useProjectStats.ts +0 -93
  217. package/packages/web/hooks/useProjects.ts +0 -73
  218. package/packages/web/lib/actions/projects.ts +0 -15
  219. package/packages/web/lib/commands.ts +0 -81
  220. package/packages/web/lib/format.ts +0 -23
  221. package/packages/web/lib/generate-week-report.ts +0 -285
  222. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  223. package/packages/web/lib/project-colors.ts +0 -58
  224. package/packages/web/lib/projects.ts +0 -506
  225. package/packages/web/lib/pty.ts +0 -101
  226. package/packages/web/lib/query-config.ts +0 -44
  227. package/packages/web/lib/services/index.ts +0 -9
  228. package/packages/web/lib/services/projects.server.ts +0 -66
  229. package/packages/web/lib/services/stats.server.ts +0 -562
  230. package/packages/web/lib/unified-loader.ts +0 -396
  231. package/packages/web/lib/utils.ts +0 -6
  232. package/packages/web/next-env.d.ts +0 -6
  233. package/packages/web/next.config.ts +0 -7
  234. package/packages/web/package.json +0 -57
  235. package/packages/web/postcss.config.mjs +0 -7
  236. package/packages/web/public/file.svg +0 -1
  237. package/packages/web/public/globe.svg +0 -1
  238. package/packages/web/public/next.svg +0 -1
  239. package/packages/web/public/vercel.svg +0 -1
  240. package/packages/web/public/window.svg +0 -1
  241. package/packages/web/server.ts +0 -312
  242. package/packages/web/tsconfig.json +0 -34
  243. 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'