prjct-cli 0.13.3 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/bin/prjct +10 -13
- package/core/agentic/memory-system/semantic-memories.ts +2 -1
- package/core/agentic/plan-mode/plan-mode.ts +2 -1
- package/core/agentic/prompt-builder.ts +22 -43
- package/core/agentic/services.ts +5 -5
- package/core/agentic/smart-context.ts +7 -2
- package/core/command-registry/core-commands.ts +54 -29
- package/core/command-registry/optional-commands.ts +64 -0
- package/core/command-registry/setup-commands.ts +18 -3
- package/core/commands/analysis.ts +21 -68
- package/core/commands/analytics.ts +247 -213
- package/core/commands/base.ts +1 -1
- package/core/commands/index.ts +41 -36
- package/core/commands/maintenance.ts +300 -31
- package/core/commands/planning.ts +233 -22
- package/core/commands/setup.ts +3 -8
- package/core/commands/shipping.ts +14 -18
- package/core/commands/types.ts +8 -6
- package/core/commands/workflow.ts +105 -100
- package/core/context/generator.ts +317 -0
- package/core/context-sync.ts +7 -350
- package/core/data/index.ts +13 -32
- package/core/data/md-ideas-manager.ts +155 -0
- package/core/data/md-queue-manager.ts +4 -3
- package/core/data/md-shipped-manager.ts +90 -0
- package/core/data/md-state-manager.ts +11 -7
- package/core/domain/agent-generator.ts +23 -63
- package/core/events/index.ts +143 -0
- package/core/index.ts +17 -14
- package/core/infrastructure/capability-installer.ts +13 -149
- package/core/infrastructure/migrator/project-scanner.ts +2 -1
- package/core/infrastructure/path-manager.ts +4 -6
- package/core/infrastructure/setup.ts +3 -0
- package/core/infrastructure/uuid-migration.ts +750 -0
- package/core/outcomes/recorder.ts +2 -1
- package/core/plugin/loader.ts +4 -7
- package/core/plugin/registry.ts +3 -3
- package/core/schemas/index.ts +23 -25
- package/core/schemas/state.ts +1 -0
- package/core/serializers/ideas-serializer.ts +187 -0
- package/core/serializers/index.ts +16 -0
- package/core/serializers/shipped-serializer.ts +108 -0
- package/core/session/utils.ts +3 -9
- package/core/storage/ideas-storage.ts +273 -0
- package/core/storage/index.ts +204 -0
- package/core/storage/queue-storage.ts +297 -0
- package/core/storage/shipped-storage.ts +223 -0
- package/core/storage/state-storage.ts +235 -0
- package/core/storage/storage-manager.ts +175 -0
- package/package.json +1 -1
- package/packages/web/app/api/projects/[id]/momentum/route.ts +257 -0
- package/packages/web/app/api/sessions/current/route.ts +132 -0
- package/packages/web/app/api/sessions/history/route.ts +96 -14
- package/packages/web/app/globals.css +5 -0
- package/packages/web/app/layout.tsx +2 -0
- package/packages/web/app/project/[id]/code/layout.tsx +18 -0
- package/packages/web/app/project/[id]/code/page.tsx +408 -0
- package/packages/web/app/project/[id]/page.tsx +359 -389
- package/packages/web/app/project/[id]/reports/page.tsx +59 -0
- package/packages/web/app/project/[id]/reports/print/page.tsx +58 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -1
- package/packages/web/components/AgentsCard/AgentsCard.tsx +64 -34
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +1 -0
- package/packages/web/components/AppSidebar/AppSidebar.tsx +135 -11
- package/packages/web/components/BentoCard/BentoCard.constants.ts +3 -3
- package/packages/web/components/BentoCard/BentoCard.tsx +2 -1
- package/packages/web/components/BentoGrid/BentoGrid.tsx +2 -2
- package/packages/web/components/BlockersCard/BlockersCard.tsx +65 -57
- package/packages/web/components/BlockersCard/BlockersCard.types.ts +1 -0
- package/packages/web/components/CommandBar/CommandBar.tsx +67 -0
- package/packages/web/components/CommandBar/index.ts +1 -0
- package/packages/web/components/DashboardContent/DashboardContent.tsx +35 -5
- package/packages/web/components/DateGroup/DateGroup.tsx +1 -1
- package/packages/web/components/EmptyState/EmptyState.tsx +39 -21
- package/packages/web/components/EmptyState/EmptyState.types.ts +1 -0
- package/packages/web/components/EventRow/EventRow.tsx +4 -4
- package/packages/web/components/EventRow/EventRow.utils.ts +3 -3
- package/packages/web/components/HeroSection/HeroSection.tsx +52 -15
- package/packages/web/components/HeroSection/HeroSection.types.ts +4 -4
- package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -3
- package/packages/web/components/IdeasCard/IdeasCard.tsx +94 -27
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +1 -0
- package/packages/web/components/MasonryGrid/MasonryGrid.tsx +18 -0
- package/packages/web/components/MasonryGrid/index.ts +1 -0
- package/packages/web/components/MomentumWidget/MomentumWidget.tsx +119 -0
- package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +16 -0
- package/packages/web/components/MomentumWidget/index.ts +2 -0
- package/packages/web/components/NowCard/NowCard.tsx +81 -56
- package/packages/web/components/NowCard/NowCard.types.ts +1 -0
- package/packages/web/components/PageHeader/PageHeader.tsx +24 -0
- package/packages/web/components/PageHeader/index.ts +1 -0
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +2 -2
- package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +2 -2
- package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +37 -0
- package/packages/web/components/ProjectColorDot/index.ts +1 -0
- package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +104 -0
- package/packages/web/components/ProjectSelectorModal/index.ts +1 -0
- package/packages/web/components/Providers/Providers.tsx +4 -1
- package/packages/web/components/QueueCard/QueueCard.tsx +78 -25
- package/packages/web/components/QueueCard/QueueCard.types.ts +1 -0
- package/packages/web/components/QueueCard/QueueCard.utils.ts +3 -3
- package/packages/web/components/RecoverCard/RecoverCard.tsx +72 -0
- package/packages/web/components/RecoverCard/RecoverCard.types.ts +16 -0
- package/packages/web/components/RecoverCard/index.ts +2 -0
- package/packages/web/components/RoadmapCard/RoadmapCard.tsx +101 -33
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +1 -0
- package/packages/web/components/ShipsCard/ShipsCard.tsx +71 -28
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +2 -0
- package/packages/web/components/SparklineChart/SparklineChart.tsx +20 -18
- package/packages/web/components/StatsMasonry/StatsMasonry.tsx +95 -0
- package/packages/web/components/StatsMasonry/index.ts +1 -0
- package/packages/web/components/StreakCard/StreakCard.tsx +37 -35
- package/packages/web/components/TasksCounter/TasksCounter.tsx +1 -1
- package/packages/web/components/TechStackBadges/TechStackBadges.tsx +12 -4
- package/packages/web/components/TerminalDock/DockToggleTab.tsx +29 -0
- package/packages/web/components/TerminalDock/TerminalDock.tsx +386 -0
- package/packages/web/components/TerminalDock/TerminalDockTab.tsx +130 -0
- package/packages/web/components/TerminalDock/TerminalTabBar.tsx +142 -0
- package/packages/web/components/TerminalDock/index.ts +2 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +8 -3
- package/packages/web/components/VelocityCard/VelocityCard.tsx +49 -47
- package/packages/web/components/WeeklyReports/PrintableReport.tsx +259 -0
- package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +187 -0
- package/packages/web/components/WeeklyReports/WeekCalendar.tsx +288 -0
- package/packages/web/components/WeeklyReports/WeeklyReports.tsx +149 -0
- package/packages/web/components/WeeklyReports/index.ts +4 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +16 -4
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +1 -0
- package/packages/web/components/charts/SessionsChart.tsx +6 -3
- package/packages/web/components/ui/dialog.tsx +143 -0
- package/packages/web/components/ui/drawer.tsx +135 -0
- package/packages/web/components/ui/select.tsx +187 -0
- package/packages/web/context/GlobalTerminalContext.tsx +538 -0
- package/packages/web/lib/commands.ts +81 -0
- package/packages/web/lib/generate-week-report.ts +285 -0
- package/packages/web/lib/parse-prjct-files.ts +56 -55
- package/packages/web/lib/project-colors.ts +58 -0
- package/packages/web/lib/projects.ts +58 -5
- package/packages/web/lib/services/projects.server.ts +11 -1
- package/packages/web/next-env.d.ts +1 -1
- package/packages/web/package.json +5 -1
- package/templates/commands/analyze.md +39 -3
- package/templates/commands/ask.md +58 -3
- package/templates/commands/bug.md +117 -26
- package/templates/commands/dash.md +95 -158
- package/templates/commands/done.md +130 -148
- package/templates/commands/feature.md +125 -103
- package/templates/commands/git.md +18 -3
- package/templates/commands/idea.md +121 -38
- package/templates/commands/init.md +124 -20
- package/templates/commands/migrate-all.md +63 -28
- package/templates/commands/migrate.md +140 -0
- package/templates/commands/next.md +115 -5
- package/templates/commands/now.md +146 -82
- package/templates/commands/pause.md +89 -74
- package/templates/commands/redo.md +6 -4
- package/templates/commands/resume.md +141 -59
- package/templates/commands/ship.md +103 -231
- package/templates/commands/spec.md +98 -8
- package/templates/commands/suggest.md +22 -2
- package/templates/commands/sync.md +192 -203
- package/templates/commands/undo.md +6 -4
- package/core/data/agents-manager.ts +0 -76
- package/core/data/analysis-manager.ts +0 -83
- package/core/data/base-manager.ts +0 -156
- package/core/data/ideas-manager.ts +0 -81
- package/core/data/outcomes-manager.ts +0 -96
- package/core/data/project-manager.ts +0 -75
- package/core/data/roadmap-manager.ts +0 -118
- package/core/data/shipped-manager.ts +0 -65
- package/core/data/state-manager.ts +0 -214
- package/core/state/index.ts +0 -25
- package/core/state/manager.ts +0 -376
- package/core/state/types.ts +0 -185
- package/core/utils/project-capabilities.ts +0 -156
- package/core/view-generator.ts +0 -536
- package/packages/web/app/project/[id]/stats/loading.tsx +0 -43
- package/packages/web/app/project/[id]/stats/page.tsx +0 -253
- package/templates/agent-assignment.md +0 -72
- package/templates/analysis/project-analysis.md +0 -78
- package/templates/checklists/accessibility.md +0 -33
- package/templates/commands/build.md +0 -17
- package/templates/commands/decision.md +0 -226
- package/templates/commands/fix.md +0 -79
- package/templates/commands/help.md +0 -61
- package/templates/commands/progress.md +0 -14
- package/templates/commands/recap.md +0 -14
- package/templates/commands/roadmap.md +0 -52
- package/templates/commands/status.md +0 -17
- package/templates/commands/task.md +0 -63
- package/templates/commands/work.md +0 -44
- package/templates/commands/workflow.md +0 -12
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { SparklineChart } from '@/components/SparklineChart'
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
|
6
|
+
import { Flame, TrendingUp, Activity, Heart } from 'lucide-react'
|
|
7
|
+
import { cn } from '@/lib/utils'
|
|
8
|
+
import type { MomentumWidgetProps, MomentumData, MomentumStatus } from './MomentumWidget.types'
|
|
9
|
+
|
|
10
|
+
const statusConfig: Record<MomentumStatus, {
|
|
11
|
+
color: string
|
|
12
|
+
bgColor: string
|
|
13
|
+
textColor: string
|
|
14
|
+
icon: typeof Flame
|
|
15
|
+
}> = {
|
|
16
|
+
// Growing - you're killing it!
|
|
17
|
+
hot: {
|
|
18
|
+
color: '#22c55e',
|
|
19
|
+
bgColor: 'bg-green-500/10',
|
|
20
|
+
textColor: 'text-green-500',
|
|
21
|
+
icon: Flame
|
|
22
|
+
},
|
|
23
|
+
// Normal activity - neutral tones
|
|
24
|
+
active: {
|
|
25
|
+
color: '#a1a1aa',
|
|
26
|
+
bgColor: 'bg-muted',
|
|
27
|
+
textColor: 'text-muted-foreground',
|
|
28
|
+
icon: Activity
|
|
29
|
+
},
|
|
30
|
+
// Slight slowdown - still neutral
|
|
31
|
+
cooling: {
|
|
32
|
+
color: '#a1a1aa',
|
|
33
|
+
bgColor: 'bg-muted',
|
|
34
|
+
textColor: 'text-muted-foreground',
|
|
35
|
+
icon: TrendingUp
|
|
36
|
+
},
|
|
37
|
+
// Abandoned (7+ days) - red alert
|
|
38
|
+
cold: {
|
|
39
|
+
color: '#ef4444',
|
|
40
|
+
bgColor: 'bg-red-500/10',
|
|
41
|
+
textColor: 'text-red-500',
|
|
42
|
+
icon: Heart
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function MomentumWidget({ projectId }: MomentumWidgetProps) {
|
|
47
|
+
const [data, setData] = useState<MomentumData | null>(null)
|
|
48
|
+
const [loading, setLoading] = useState(true)
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
async function fetchMomentum() {
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(`/api/projects/${projectId}/momentum`)
|
|
54
|
+
const json = await res.json()
|
|
55
|
+
if (json.success) {
|
|
56
|
+
setData(json.data)
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Failed to fetch momentum:', error)
|
|
60
|
+
} finally {
|
|
61
|
+
setLoading(false)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fetchMomentum()
|
|
66
|
+
}, [projectId])
|
|
67
|
+
|
|
68
|
+
if (loading) {
|
|
69
|
+
return (
|
|
70
|
+
<div className="flex items-center gap-2 h-10 animate-pulse">
|
|
71
|
+
<div className="h-10 w-32 bg-muted rounded" />
|
|
72
|
+
<div className="h-4 w-16 bg-muted rounded" />
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!data) return null
|
|
78
|
+
|
|
79
|
+
const config = statusConfig[data.status]
|
|
80
|
+
const Icon = config.icon
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Tooltip>
|
|
84
|
+
<TooltipTrigger asChild>
|
|
85
|
+
<div className={cn(
|
|
86
|
+
'flex items-center gap-2 px-2 py-1 rounded-md cursor-default transition-colors',
|
|
87
|
+
config.bgColor
|
|
88
|
+
)}>
|
|
89
|
+
{/* Mini sparkline - matching button width for visual weight */}
|
|
90
|
+
<div className="w-32 h-10">
|
|
91
|
+
<SparklineChart
|
|
92
|
+
data={data.dailyTasks}
|
|
93
|
+
color={config.color}
|
|
94
|
+
height={40}
|
|
95
|
+
showArea={true}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{/* Status badge */}
|
|
100
|
+
<div className={cn('flex items-center gap-1.5', config.textColor)}>
|
|
101
|
+
<Icon className="w-4 h-4" />
|
|
102
|
+
<span className="text-sm font-medium whitespace-nowrap">
|
|
103
|
+
{data.message}
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</TooltipTrigger>
|
|
108
|
+
<TooltipContent side="bottom" className="text-xs">
|
|
109
|
+
<div className="space-y-1">
|
|
110
|
+
<p className="font-medium">7-day activity</p>
|
|
111
|
+
<p>{data.totalTasks} tasks, {data.totalShips} ships</p>
|
|
112
|
+
{data.streak > 0 && (
|
|
113
|
+
<p className="text-foreground font-medium">{data.streak} day streak!</p>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
</TooltipContent>
|
|
117
|
+
</Tooltip>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type MomentumStatus = 'hot' | 'active' | 'cooling' | 'cold'
|
|
2
|
+
|
|
3
|
+
export interface MomentumData {
|
|
4
|
+
dailyTasks: number[]
|
|
5
|
+
totalTasks: number
|
|
6
|
+
totalShips: number
|
|
7
|
+
lastActivityDate: string | null
|
|
8
|
+
daysSinceActivity: number
|
|
9
|
+
streak: number
|
|
10
|
+
status: MomentumStatus
|
|
11
|
+
message: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MomentumWidgetProps {
|
|
15
|
+
projectId: string
|
|
16
|
+
}
|
|
@@ -1,84 +1,108 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
2
4
|
import { EmptyState } from '@/components/EmptyState'
|
|
3
|
-
import { Target, Clock, Bot, Pause } from 'lucide-react'
|
|
5
|
+
import { Target, Clock, Bot, Pause, Play, CheckCircle2 } from 'lucide-react'
|
|
4
6
|
import { cn } from '@/lib/utils'
|
|
5
7
|
import type { NowCardProps } from './NowCard.types'
|
|
6
8
|
|
|
7
|
-
export function NowCard({ currentTask, className }: NowCardProps) {
|
|
9
|
+
export function NowCard({ currentTask, codeHref, className }: NowCardProps) {
|
|
8
10
|
return (
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
<div className={cn(
|
|
12
|
+
'relative overflow-hidden rounded-xl border bg-card p-4',
|
|
13
|
+
className
|
|
14
|
+
)}>
|
|
15
|
+
<div className="flex items-center justify-between mb-3">
|
|
16
|
+
<div className="flex items-center gap-2">
|
|
17
|
+
<Target className="h-4 w-4 text-muted-foreground" />
|
|
18
|
+
<span className="text-xs font-bold uppercase tracking-[0.15em] text-muted-foreground">
|
|
19
|
+
Now
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
{currentTask && (
|
|
23
|
+
<div className="flex items-center gap-2">
|
|
19
24
|
{currentTask.pausedAt ? (
|
|
20
|
-
|
|
21
|
-
<Pause className="w-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</span>
|
|
25
|
-
</>
|
|
25
|
+
<span className="inline-flex items-center gap-1 text-xs font-semibold text-muted-foreground bg-muted px-2 py-0.5 rounded-full">
|
|
26
|
+
<Pause className="w-2.5 h-2.5" />
|
|
27
|
+
Paused
|
|
28
|
+
</span>
|
|
26
29
|
) : (
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</span>
|
|
32
|
-
</>
|
|
30
|
+
<span className="inline-flex items-center gap-1 text-xs font-semibold text-foreground bg-muted px-2 py-0.5 rounded-full">
|
|
31
|
+
<Play className="w-2.5 h-2.5 fill-current" />
|
|
32
|
+
Working
|
|
33
|
+
</span>
|
|
33
34
|
)}
|
|
34
35
|
</div>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
{currentTask ? (
|
|
40
|
+
<div className="space-y-3">
|
|
41
|
+
<p className="text-sm font-medium leading-tight break-words">
|
|
37
42
|
{currentTask.task}
|
|
38
43
|
</p>
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
currentTask.agentConfidence >= 0.8 ? 'bg-emerald-500/10 text-emerald-600' :
|
|
48
|
-
currentTask.agentConfidence >= 0.5 ? 'bg-amber-500/10 text-amber-600' :
|
|
49
|
-
'bg-muted text-muted-foreground'
|
|
50
|
-
)}>
|
|
51
|
-
{Math.round(currentTask.agentConfidence * 100)}% confidence
|
|
52
|
-
</span>
|
|
53
|
-
)}
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
45
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
46
|
+
{currentTask.agent && (
|
|
47
|
+
<div className="inline-flex items-center gap-1.5 text-xs text-muted-foreground bg-muted/50 px-2 py-1 rounded">
|
|
48
|
+
<Bot className="h-3 w-3" />
|
|
49
|
+
<span className="font-mono">{currentTask.agent}</span>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
<span className="text-sm">{currentTask.duration}</span>
|
|
53
|
+
{currentTask.startedAt && (
|
|
54
|
+
<div className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
55
|
+
<Clock className="h-3 w-3" />
|
|
56
|
+
<span>Started {new Date(currentTask.startedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
|
|
62
57
|
</div>
|
|
63
58
|
)}
|
|
59
|
+
|
|
64
60
|
{currentTask.estimatedDuration && (
|
|
65
|
-
<span className="text-xs">
|
|
66
|
-
|
|
61
|
+
<span className="text-xs text-muted-foreground">
|
|
62
|
+
Est. {currentTask.estimatedDuration}
|
|
67
63
|
</span>
|
|
68
64
|
)}
|
|
69
65
|
</div>
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
{/* Progress indicator */}
|
|
68
|
+
<div className="pt-1">
|
|
72
69
|
<div className="h-1.5 bg-muted rounded-full overflow-hidden">
|
|
73
70
|
<div
|
|
74
|
-
className=
|
|
75
|
-
|
|
76
|
-
currentTask.pausedAt ? "bg-muted-foreground" : "bg-amber-500 animate-pulse"
|
|
77
|
-
)}
|
|
78
|
-
style={{ width: '60%' }}
|
|
71
|
+
className="h-full rounded-full transition-all duration-300 bg-foreground"
|
|
72
|
+
style={{ width: currentTask.pausedAt ? '30%' : '60%' }}
|
|
79
73
|
/>
|
|
80
74
|
</div>
|
|
81
75
|
</div>
|
|
76
|
+
|
|
77
|
+
{/* Action buttons */}
|
|
78
|
+
{codeHref && (
|
|
79
|
+
<div className="flex gap-2 pt-2">
|
|
80
|
+
<Link
|
|
81
|
+
href={`${codeHref}?cmd=p.%20done`}
|
|
82
|
+
className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
|
|
83
|
+
>
|
|
84
|
+
<CheckCircle2 className="h-3.5 w-3.5" />
|
|
85
|
+
Done
|
|
86
|
+
</Link>
|
|
87
|
+
{currentTask.pausedAt ? (
|
|
88
|
+
<Link
|
|
89
|
+
href={`${codeHref}?cmd=p.%20resume`}
|
|
90
|
+
className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
|
|
91
|
+
>
|
|
92
|
+
<Play className="h-3.5 w-3.5" />
|
|
93
|
+
Resume
|
|
94
|
+
</Link>
|
|
95
|
+
) : (
|
|
96
|
+
<Link
|
|
97
|
+
href={`${codeHref}?cmd=p.%20pause`}
|
|
98
|
+
className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
|
|
99
|
+
>
|
|
100
|
+
<Pause className="h-3.5 w-3.5" />
|
|
101
|
+
Pause
|
|
102
|
+
</Link>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
82
106
|
</div>
|
|
83
107
|
) : (
|
|
84
108
|
<EmptyState
|
|
@@ -86,8 +110,9 @@ export function NowCard({ currentTask, className }: NowCardProps) {
|
|
|
86
110
|
title="No active task"
|
|
87
111
|
description="Start working on something"
|
|
88
112
|
command="/p:now"
|
|
113
|
+
href={codeHref}
|
|
89
114
|
/>
|
|
90
115
|
)}
|
|
91
|
-
</
|
|
116
|
+
</div>
|
|
92
117
|
)
|
|
93
118
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getProjectBgClass } from '@/lib/project-colors'
|
|
2
|
+
|
|
3
|
+
interface PageHeaderProps {
|
|
4
|
+
projectId: string
|
|
5
|
+
projectName: string
|
|
6
|
+
section?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function PageHeader({ projectId, projectName, section }: PageHeaderProps) {
|
|
10
|
+
const colorBg = getProjectBgClass(projectId)
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="flex items-center gap-3 mb-6">
|
|
14
|
+
<div className={`w-3 h-3 rounded-full shrink-0 ${colorBg}`} />
|
|
15
|
+
<h1 className="text-2xl font-semibold">{projectName}</h1>
|
|
16
|
+
{section && (
|
|
17
|
+
<>
|
|
18
|
+
<span className="text-muted-foreground/50">/</span>
|
|
19
|
+
<span className="text-xl text-muted-foreground">{section}</span>
|
|
20
|
+
</>
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PageHeader } from './PageHeader'
|
|
@@ -6,7 +6,7 @@ export const PROGRESS_RING_SIZES: Record<ProgressRingSize, {
|
|
|
6
6
|
viewBox: number
|
|
7
7
|
radius: number
|
|
8
8
|
}> = {
|
|
9
|
-
sm: { container: 'h-8 w-8', text: 'text-
|
|
9
|
+
sm: { container: 'h-8 w-8', text: 'text-xs', viewBox: 36, radius: 14 },
|
|
10
10
|
md: { container: 'h-12 w-12', text: 'text-xs', viewBox: 36, radius: 14 },
|
|
11
11
|
lg: { container: 'h-16 w-16', text: 'text-sm', viewBox: 36, radius: 14 },
|
|
12
12
|
xl: { container: 'h-20 w-20', text: 'text-base', viewBox: 36, radius: 14 },
|
|
@@ -16,5 +16,5 @@ export const PROGRESS_RING_COLOR_STYLES: Record<AccentColor, string> = {
|
|
|
16
16
|
default: 'text-foreground',
|
|
17
17
|
success: 'text-emerald-500',
|
|
18
18
|
warning: 'text-amber-500',
|
|
19
|
-
destructive: 'text-
|
|
19
|
+
destructive: 'text-red-500',
|
|
20
20
|
}
|
|
@@ -28,7 +28,7 @@ export function ProjectAvatar({
|
|
|
28
28
|
return (
|
|
29
29
|
<div
|
|
30
30
|
className={cn(
|
|
31
|
-
'rounded-lg
|
|
31
|
+
'rounded-lg flex items-center justify-center overflow-hidden shrink-0 bg-muted border border-border',
|
|
32
32
|
sizeClasses[size],
|
|
33
33
|
className
|
|
34
34
|
)}
|
|
@@ -46,7 +46,7 @@ export function ProjectAvatar({
|
|
|
46
46
|
}}
|
|
47
47
|
/>
|
|
48
48
|
) : null}
|
|
49
|
-
<span className={cn('font-
|
|
49
|
+
<span className={cn('font-medium text-muted-foreground', iconPath ? 'hidden' : '')}>
|
|
50
50
|
{initials}
|
|
51
51
|
</span>
|
|
52
52
|
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
import { getProjectColor } from '@/lib/project-colors'
|
|
5
|
+
|
|
6
|
+
interface ProjectColorDotProps {
|
|
7
|
+
projectId: string
|
|
8
|
+
size?: 'sm' | 'md' | 'lg' | 'xl'
|
|
9
|
+
className?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const sizeClasses = {
|
|
13
|
+
sm: 'w-2 h-2',
|
|
14
|
+
md: 'w-3 h-3',
|
|
15
|
+
lg: 'w-4 h-4',
|
|
16
|
+
xl: 'w-5 h-5',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ProjectColorDot({
|
|
20
|
+
projectId,
|
|
21
|
+
size = 'md',
|
|
22
|
+
className
|
|
23
|
+
}: ProjectColorDotProps) {
|
|
24
|
+
const color = getProjectColor(projectId)
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
className={cn(
|
|
29
|
+
'rounded-full shrink-0',
|
|
30
|
+
sizeClasses[size],
|
|
31
|
+
color.bg,
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
aria-hidden="true"
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProjectColorDot } from './ProjectColorDot'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { useProjects } from '@/hooks/useProjects'
|
|
5
|
+
import {
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
DialogDescription,
|
|
11
|
+
} from '@/components/ui/dialog'
|
|
12
|
+
import { Input } from '@/components/ui/input'
|
|
13
|
+
import { ProjectAvatar } from '@/components/ProjectAvatar'
|
|
14
|
+
import { Loader2, Search, FolderGit2 } from 'lucide-react'
|
|
15
|
+
import { cn } from '@/lib/utils'
|
|
16
|
+
import { formatPath } from '@/lib/format'
|
|
17
|
+
|
|
18
|
+
interface ProjectSelectorModalProps {
|
|
19
|
+
isOpen: boolean
|
|
20
|
+
onClose: () => void
|
|
21
|
+
onSelectProject: (projectId: string, projectName: string, projectPath: string) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ProjectSelectorModal({
|
|
25
|
+
isOpen,
|
|
26
|
+
onClose,
|
|
27
|
+
onSelectProject,
|
|
28
|
+
}: ProjectSelectorModalProps) {
|
|
29
|
+
const [search, setSearch] = useState('')
|
|
30
|
+
const { data: projects, isLoading } = useProjects()
|
|
31
|
+
|
|
32
|
+
const filteredProjects = projects?.filter((project) =>
|
|
33
|
+
project.name?.toLowerCase().includes(search.toLowerCase()) ||
|
|
34
|
+
project.repoPath?.toLowerCase().includes(search.toLowerCase())
|
|
35
|
+
) || []
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
39
|
+
<DialogContent className="sm:max-w-md">
|
|
40
|
+
<DialogHeader>
|
|
41
|
+
<DialogTitle>Select a Project</DialogTitle>
|
|
42
|
+
<DialogDescription>
|
|
43
|
+
Choose a project to open a terminal session
|
|
44
|
+
</DialogDescription>
|
|
45
|
+
</DialogHeader>
|
|
46
|
+
|
|
47
|
+
<div className="relative">
|
|
48
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
49
|
+
<Input
|
|
50
|
+
placeholder="Search projects..."
|
|
51
|
+
value={search}
|
|
52
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
53
|
+
className="pl-9"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="max-h-[300px] overflow-y-auto space-y-1">
|
|
58
|
+
{isLoading ? (
|
|
59
|
+
<div className="flex items-center justify-center py-8">
|
|
60
|
+
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
61
|
+
</div>
|
|
62
|
+
) : filteredProjects.length === 0 ? (
|
|
63
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
64
|
+
{search ? 'No projects found' : 'No projects available'}
|
|
65
|
+
</div>
|
|
66
|
+
) : (
|
|
67
|
+
filteredProjects.map((project) => (
|
|
68
|
+
<button
|
|
69
|
+
key={project.id}
|
|
70
|
+
onClick={() => {
|
|
71
|
+
const projectPath = project.repoPath || project.path || '/tmp'
|
|
72
|
+
const projectName = project.name || project.id
|
|
73
|
+
onSelectProject(project.id, projectName, projectPath)
|
|
74
|
+
}}
|
|
75
|
+
className={cn(
|
|
76
|
+
'w-full flex items-center gap-3 p-3 rounded-lg transition-colors',
|
|
77
|
+
'hover:bg-accent text-left'
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<ProjectAvatar
|
|
81
|
+
projectId={project.id}
|
|
82
|
+
name={project.name || project.id}
|
|
83
|
+
iconPath={project.iconPath}
|
|
84
|
+
size="sm"
|
|
85
|
+
/>
|
|
86
|
+
<div className="flex-1 min-w-0">
|
|
87
|
+
<div className="font-medium truncate">
|
|
88
|
+
{project.name || project.id}
|
|
89
|
+
</div>
|
|
90
|
+
{project.repoPath && (
|
|
91
|
+
<div className="text-xs text-muted-foreground flex items-center gap-1 truncate">
|
|
92
|
+
<FolderGit2 className="h-3 w-3 shrink-0" />
|
|
93
|
+
<span className="truncate">{formatPath(project.repoPath)}</span>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
</button>
|
|
98
|
+
))
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</DialogContent>
|
|
102
|
+
</Dialog>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProjectSelectorModal } from './ProjectSelectorModal'
|
|
@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
|
4
4
|
import { useState, type ReactNode } from 'react'
|
|
5
5
|
import { ThemeProvider, type ThemeProviderProps } from 'next-themes'
|
|
6
6
|
import { TerminalProvider } from '@/context/TerminalContext'
|
|
7
|
+
import { GlobalTerminalProvider } from '@/context/GlobalTerminalContext'
|
|
7
8
|
|
|
8
9
|
function ThemeWrapper({ children, ...props }: ThemeProviderProps & { children: ReactNode }) {
|
|
9
10
|
return <ThemeProvider {...props}>{children}</ThemeProvider>
|
|
@@ -37,7 +38,9 @@ export function Providers({ children }: { children: ReactNode }) {
|
|
|
37
38
|
disableTransitionOnChange
|
|
38
39
|
>
|
|
39
40
|
<TerminalProvider>
|
|
40
|
-
|
|
41
|
+
<GlobalTerminalProvider>
|
|
42
|
+
{children}
|
|
43
|
+
</GlobalTerminalProvider>
|
|
41
44
|
</TerminalProvider>
|
|
42
45
|
</ThemeWrapper>
|
|
43
46
|
</QueryClientProvider>
|