prjct-cli 0.11.5 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +81 -25
- package/bin/dev.js +1 -1
- package/bin/generate-views.js +209 -0
- package/bin/migrate-to-json.js +742 -0
- package/bin/prjct +5 -5
- package/bin/serve.js +226 -50
- package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
- package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
- package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
- package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
- package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
- package/core/agentic/agent-router.ts +137 -0
- package/core/agentic/chain-of-thought.ts +228 -0
- package/core/agentic/command-executor/command-executor.ts +384 -0
- package/core/agentic/command-executor/index.ts +16 -0
- package/core/agentic/command-executor/status-signal.ts +38 -0
- package/core/agentic/command-executor/types.ts +79 -0
- package/core/agentic/command-executor.ts +8 -0
- package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
- package/core/agentic/context-filter.ts +365 -0
- package/core/agentic/ground-truth/index.ts +76 -0
- package/core/agentic/ground-truth/types.ts +33 -0
- package/core/agentic/ground-truth/utils.ts +48 -0
- package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
- package/core/agentic/ground-truth/verifiers/done.ts +75 -0
- package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
- package/core/agentic/ground-truth/verifiers/index.ts +37 -0
- package/core/agentic/ground-truth/verifiers/init.ts +52 -0
- package/core/agentic/ground-truth/verifiers/now.ts +57 -0
- package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
- package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
- package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
- package/core/agentic/ground-truth/verifiers.ts +6 -0
- package/core/agentic/ground-truth.ts +8 -0
- package/core/agentic/loop-detector/error-analysis.ts +97 -0
- package/core/agentic/loop-detector/hallucination.ts +71 -0
- package/core/agentic/loop-detector/index.ts +41 -0
- package/core/agentic/loop-detector/loop-detector.ts +222 -0
- package/core/agentic/loop-detector/types.ts +66 -0
- package/core/agentic/loop-detector.ts +8 -0
- package/core/agentic/memory-system/history.ts +53 -0
- package/core/agentic/memory-system/index.ts +192 -0
- package/core/agentic/memory-system/patterns.ts +156 -0
- package/core/agentic/memory-system/semantic-memories.ts +277 -0
- package/core/agentic/memory-system/session.ts +21 -0
- package/core/agentic/memory-system/types.ts +159 -0
- package/core/agentic/memory-system.ts +8 -0
- package/core/agentic/parallel-tools.ts +165 -0
- package/core/agentic/plan-mode/approval.ts +57 -0
- package/core/agentic/plan-mode/constants.ts +44 -0
- package/core/agentic/plan-mode/index.ts +28 -0
- package/core/agentic/plan-mode/plan-mode.ts +406 -0
- package/core/agentic/plan-mode/types.ts +193 -0
- package/core/agentic/plan-mode.ts +8 -0
- package/core/agentic/prompt-builder.ts +566 -0
- package/core/agentic/response-templates.ts +164 -0
- package/core/agentic/semantic-compression.ts +273 -0
- package/core/agentic/services.ts +206 -0
- package/core/agentic/smart-context.ts +476 -0
- package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
- package/core/agentic/think-blocks.ts +202 -0
- package/core/agentic/tool-registry.ts +119 -0
- package/core/agentic/validation-rules.ts +313 -0
- package/core/agents/index.ts +28 -0
- package/core/agents/performance.ts +444 -0
- package/core/agents/types.ts +126 -0
- package/core/bus/{index.js → index.ts} +57 -61
- package/core/command-registry/categories.ts +23 -0
- package/core/command-registry/commands.ts +15 -0
- package/core/command-registry/core-commands.ts +319 -0
- package/core/command-registry/index.ts +158 -0
- package/core/command-registry/optional-commands.ts +119 -0
- package/core/command-registry/setup-commands.ts +53 -0
- package/core/command-registry/types.ts +59 -0
- package/core/command-registry.ts +9 -0
- package/core/commands/analysis.ts +298 -0
- package/core/commands/analytics.ts +288 -0
- package/core/commands/base.ts +273 -0
- package/core/commands/index.ts +211 -0
- package/core/commands/maintenance.ts +226 -0
- package/core/commands/planning.ts +311 -0
- package/core/commands/setup.ts +309 -0
- package/core/commands/shipping.ts +188 -0
- package/core/commands/types.ts +183 -0
- package/core/commands/workflow.ts +226 -0
- package/core/commands.ts +11 -0
- package/core/constants/formats.ts +187 -0
- package/core/constants/index.ts +7 -0
- package/core/{context-sync.js → context-sync.ts} +59 -26
- package/core/data/agents-manager.ts +76 -0
- package/core/data/analysis-manager.ts +83 -0
- package/core/data/base-manager.ts +156 -0
- package/core/data/ideas-manager.ts +81 -0
- package/core/data/index.ts +32 -0
- package/core/data/outcomes-manager.ts +96 -0
- package/core/data/project-manager.ts +75 -0
- package/core/data/roadmap-manager.ts +118 -0
- package/core/data/shipped-manager.ts +65 -0
- package/core/data/state-manager.ts +214 -0
- package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
- package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
- package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
- package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
- package/core/domain/{analyzer.js → analyzer.ts} +91 -85
- package/core/domain/{architect-session.js → architect-session.ts} +49 -34
- package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
- package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
- package/core/domain/{product-standards.js → product-standards.ts} +40 -26
- package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
- package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
- package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
- package/core/domain/task-stack/index.ts +19 -0
- package/core/domain/task-stack/parser.ts +86 -0
- package/core/domain/task-stack/storage.ts +123 -0
- package/core/domain/task-stack/task-stack.ts +340 -0
- package/core/domain/task-stack/types.ts +51 -0
- package/core/domain/task-stack.ts +8 -0
- package/core/{index.js → index.ts} +61 -18
- package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
- package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
- package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
- package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
- package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
- package/core/infrastructure/command-installer/global-config.ts +106 -0
- package/core/infrastructure/command-installer/index.ts +25 -0
- package/core/infrastructure/command-installer/types.ts +41 -0
- package/core/infrastructure/command-installer.ts +8 -0
- package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
- package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
- package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
- package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
- package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
- package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
- package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
- package/core/infrastructure/legacy-installer-detector.ts +7 -0
- package/core/infrastructure/migrator/file-operations.ts +125 -0
- package/core/infrastructure/migrator/index.ts +288 -0
- package/core/infrastructure/migrator/project-scanner.ts +89 -0
- package/core/infrastructure/migrator/reports.ts +117 -0
- package/core/infrastructure/migrator/types.ts +124 -0
- package/core/infrastructure/migrator/validation.ts +94 -0
- package/core/infrastructure/migrator/version-migration.ts +117 -0
- package/core/infrastructure/migrator.ts +10 -0
- package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
- package/core/infrastructure/session-manager/index.ts +23 -0
- package/core/infrastructure/session-manager/migration.ts +88 -0
- package/core/infrastructure/session-manager/session-manager.ts +307 -0
- package/core/infrastructure/session-manager/types.ts +45 -0
- package/core/infrastructure/session-manager.ts +8 -0
- package/core/infrastructure/{setup.js → setup.ts} +29 -21
- package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
- package/core/outcomes/analyzer.ts +333 -0
- package/core/outcomes/index.ts +34 -0
- package/core/outcomes/recorder.ts +194 -0
- package/core/outcomes/types.ts +145 -0
- package/core/plugin/{hooks.js → hooks.ts} +56 -58
- package/core/plugin/{index.js → index.ts} +19 -8
- package/core/plugin/{loader.js → loader.ts} +87 -69
- package/core/plugin/{registry.js → registry.ts} +49 -45
- package/core/plugins/{webhook.js → webhook.ts} +43 -27
- package/core/schemas/agents.ts +27 -0
- package/core/schemas/analysis.ts +41 -0
- package/core/schemas/ideas.ts +83 -0
- package/core/schemas/index.ts +73 -0
- package/core/schemas/outcomes.ts +22 -0
- package/core/schemas/project.ts +26 -0
- package/core/schemas/roadmap.ts +90 -0
- package/core/schemas/shipped.ts +82 -0
- package/core/schemas/state.ts +107 -0
- package/core/session/index.ts +17 -0
- package/core/session/{metrics.js → metrics.ts} +64 -46
- package/core/session/{index.js → session-manager.ts} +51 -117
- package/core/session/types.ts +29 -0
- package/core/session/utils.ts +57 -0
- package/core/state/index.ts +25 -0
- package/core/state/manager.ts +376 -0
- package/core/state/types.ts +185 -0
- package/core/tsconfig.json +22 -0
- package/core/types/index.ts +506 -0
- package/core/utils/{animations.js → animations.ts} +74 -28
- package/core/utils/{branding.js → branding.ts} +29 -4
- package/core/utils/{date-helper.js → date-helper.ts} +31 -74
- package/core/utils/file-helper.ts +262 -0
- package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
- package/core/utils/{logger.js → logger.ts} +24 -12
- package/core/utils/{output.js → output.ts} +25 -13
- package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
- package/core/utils/{session-helper.js → session-helper.ts} +79 -66
- package/core/utils/{version.js → version.ts} +23 -31
- package/core/view-generator.ts +536 -0
- package/package.json +23 -17
- package/packages/shared/.turbo/turbo-build.log +14 -0
- package/packages/shared/dist/index.d.ts +8 -613
- package/packages/shared/dist/index.d.ts.map +1 -0
- package/packages/shared/dist/index.js +4110 -118
- package/packages/shared/dist/schemas.d.ts +408 -0
- package/packages/shared/dist/schemas.d.ts.map +1 -0
- package/packages/shared/dist/types.d.ts +144 -0
- package/packages/shared/dist/types.d.ts.map +1 -0
- package/packages/shared/dist/unified.d.ts +139 -0
- package/packages/shared/dist/unified.d.ts.map +1 -0
- package/packages/shared/dist/utils.d.ts +60 -0
- package/packages/shared/dist/utils.d.ts.map +1 -0
- package/packages/shared/package.json +4 -4
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/unified.ts +174 -0
- package/packages/web/app/api/claude/sessions/route.ts +1 -1
- package/packages/web/app/api/claude/status/route.ts +1 -1
- package/packages/web/app/api/migrate/route.ts +46 -0
- package/packages/web/app/api/projects/[id]/route.ts +1 -1
- package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
- package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
- package/packages/web/app/api/projects/route.ts +1 -1
- package/packages/web/app/api/settings/route.ts +97 -0
- package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
- package/packages/web/app/globals.css +38 -0
- package/packages/web/app/layout.tsx +10 -2
- package/packages/web/app/page.tsx +9 -224
- package/packages/web/app/project/[id]/page.tsx +191 -63
- package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
- package/packages/web/app/project/[id]/stats/page.tsx +204 -163
- package/packages/web/app/settings/page.tsx +222 -2
- package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
- package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
- package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
- package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
- package/packages/web/components/ActivityTimeline/index.ts +2 -0
- package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
- package/packages/web/components/AgentsCard/index.ts +2 -0
- package/packages/web/components/AppSidebar/AppSidebar.tsx +190 -0
- package/packages/web/components/AppSidebar/index.ts +1 -0
- package/packages/web/components/BackLink/BackLink.tsx +18 -0
- package/packages/web/components/BackLink/BackLink.types.ts +5 -0
- package/packages/web/components/BackLink/index.ts +2 -0
- package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
- package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
- package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
- package/packages/web/components/BentoCard/index.ts +2 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
- package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
- package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
- package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
- package/packages/web/components/BentoGrid/index.ts +2 -0
- package/packages/web/components/CommandButton/index.ts +1 -0
- package/packages/web/components/ConnectionStatus/index.ts +1 -0
- package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
- package/packages/web/components/DashboardContent/index.ts +1 -0
- package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
- package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
- package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
- package/packages/web/components/DateGroup/index.ts +2 -0
- package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
- package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
- package/packages/web/components/EmptyState/index.ts +2 -0
- package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
- package/packages/web/components/EventRow/EventRow.tsx +49 -0
- package/packages/web/components/EventRow/EventRow.types.ts +7 -0
- package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
- package/packages/web/components/EventRow/index.ts +2 -0
- package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
- package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
- package/packages/web/components/ExpandButton/index.ts +2 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
- package/packages/web/components/HealthGradientBackground/index.ts +2 -0
- package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
- package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
- package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
- package/packages/web/components/HeroSection/hooks/index.ts +2 -0
- package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
- package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
- package/packages/web/components/HeroSection/index.ts +2 -0
- package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
- package/packages/web/components/IdeasCard/index.ts +2 -0
- package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
- package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
- package/packages/web/components/InsightMessage/index.ts +2 -0
- package/packages/web/components/Logo/index.ts +1 -0
- package/packages/web/components/MarkdownContent/index.ts +1 -0
- package/packages/web/components/NowCard/NowCard.tsx +93 -0
- package/packages/web/components/NowCard/NowCard.types.ts +15 -0
- package/packages/web/components/NowCard/index.ts +2 -0
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
- package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
- package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
- package/packages/web/components/ProgressRing/index.ts +2 -0
- package/packages/web/components/ProjectAvatar/index.ts +1 -0
- package/packages/web/components/Providers/index.ts +1 -0
- package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
- package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
- package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
- package/packages/web/components/QueueCard/index.ts +2 -0
- package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
- package/packages/web/components/RoadmapCard/index.ts +2 -0
- package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
- package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
- package/packages/web/components/ShipsCard/index.ts +2 -0
- package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
- package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
- package/packages/web/components/SparklineChart/index.ts +2 -0
- package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
- package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
- package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
- package/packages/web/components/StreakCard/index.ts +2 -0
- package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
- package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
- package/packages/web/components/TasksCounter/index.ts +2 -0
- package/packages/web/components/TechStackBadges/index.ts +1 -0
- package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
- package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
- package/packages/web/components/TerminalTabs/index.ts +1 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
- package/packages/web/components/VelocityBadge/index.ts +2 -0
- package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
- package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
- package/packages/web/components/VelocityCard/index.ts +2 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
- package/packages/web/components/WeeklySparkline/index.ts +2 -0
- package/packages/web/components/ui/input.tsx +21 -0
- package/packages/web/context/TerminalTabsContext.tsx +46 -1
- package/packages/web/hooks/useClaudeTerminal.ts +71 -21
- package/packages/web/hooks/useProjectStats.ts +55 -0
- package/packages/web/hooks/useProjects.ts +6 -6
- package/packages/web/lib/actions/projects.ts +15 -0
- package/packages/web/lib/json-loader.ts +630 -0
- package/packages/web/lib/services/index.ts +9 -0
- package/packages/web/lib/services/migration.server.ts +600 -0
- package/packages/web/lib/services/projects.server.ts +52 -0
- package/packages/web/lib/services/stats.server.ts +264 -0
- package/packages/web/lib/unified-loader.ts +396 -0
- package/packages/web/package.json +10 -7
- package/packages/web/server.ts +58 -8
- package/templates/commands/done.md +76 -32
- package/templates/commands/feature.md +121 -47
- package/templates/commands/idea.md +81 -8
- package/templates/commands/now.md +41 -17
- package/templates/commands/ship.md +64 -25
- package/templates/commands/sync.md +28 -3
- package/core/agentic/agent-router.js +0 -140
- package/core/agentic/chain-of-thought.js +0 -578
- package/core/agentic/command-executor.js +0 -417
- package/core/agentic/context-filter.js +0 -354
- package/core/agentic/ground-truth.js +0 -591
- package/core/agentic/loop-detector.js +0 -406
- package/core/agentic/memory-system.js +0 -845
- package/core/agentic/parallel-tools.js +0 -366
- package/core/agentic/plan-mode.js +0 -572
- package/core/agentic/prompt-builder.js +0 -352
- package/core/agentic/response-templates.js +0 -290
- package/core/agentic/semantic-compression.js +0 -517
- package/core/agentic/think-blocks.js +0 -657
- package/core/agentic/tool-registry.js +0 -184
- package/core/agentic/validation-rules.js +0 -380
- package/core/command-registry.js +0 -698
- package/core/commands.js +0 -2237
- package/core/domain/task-stack.js +0 -497
- package/core/infrastructure/legacy-installer-detector.js +0 -546
- package/core/infrastructure/migrator.js +0 -796
- package/core/infrastructure/session-manager.js +0 -390
- package/core/utils/file-helper.js +0 -329
- package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
- package/packages/web/app/api/stats/route.ts +0 -38
- package/packages/web/components/AppSidebar.tsx +0 -113
- package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
- package/packages/web/components/stats/AgentsCard.tsx +0 -56
- package/packages/web/components/stats/BentoCard.tsx +0 -88
- package/packages/web/components/stats/HeroSection.tsx +0 -172
- package/packages/web/components/stats/NowCard.tsx +0 -71
- package/packages/web/components/stats/QueueCard.tsx +0 -58
- package/packages/web/components/stats/VelocityCard.tsx +0 -60
- package/packages/web/components/stats/index.ts +0 -17
- package/packages/web/hooks/useStats.ts +0 -28
- /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
- /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
- /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
- /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
- /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
- /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
- /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
|
@@ -1,227 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { getProjects } from '@/lib/services/projects.server'
|
|
2
|
+
import { getGlobalStats } from '@/lib/services/stats.server'
|
|
3
|
+
import { DashboardContent } from '@/components/DashboardContent'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { TechStackBadges } from '@/components/TechStackBadges'
|
|
9
|
-
import { formatRelativeTime, formatPath } from '@/lib/format'
|
|
10
|
-
import { useProjects, useDeleteProject, type Project } from '@/hooks/useProjects'
|
|
11
|
-
import { useStats } from '@/hooks/useStats'
|
|
12
|
-
import {
|
|
13
|
-
MoreHorizontal,
|
|
14
|
-
Trash2,
|
|
15
|
-
AlertTriangle,
|
|
16
|
-
Target,
|
|
17
|
-
Lightbulb,
|
|
18
|
-
ListTodo,
|
|
19
|
-
Zap,
|
|
20
|
-
FolderGit2
|
|
21
|
-
} from 'lucide-react'
|
|
22
|
-
import {
|
|
23
|
-
DropdownMenu,
|
|
24
|
-
DropdownMenuContent,
|
|
25
|
-
DropdownMenuItem,
|
|
26
|
-
DropdownMenuTrigger,
|
|
27
|
-
} from '@/components/ui/dropdown-menu'
|
|
28
|
-
import {
|
|
29
|
-
AlertDialog,
|
|
30
|
-
AlertDialogAction,
|
|
31
|
-
AlertDialogCancel,
|
|
32
|
-
AlertDialogContent,
|
|
33
|
-
AlertDialogDescription,
|
|
34
|
-
AlertDialogFooter,
|
|
35
|
-
AlertDialogHeader,
|
|
36
|
-
AlertDialogTitle,
|
|
37
|
-
} from '@/components/ui/alert-dialog'
|
|
5
|
+
export default async function Dashboard() {
|
|
6
|
+
const [projects, stats] = await Promise.all([
|
|
7
|
+
getProjects(),
|
|
8
|
+
getGlobalStats()
|
|
9
|
+
])
|
|
38
10
|
|
|
39
|
-
|
|
40
|
-
const [projectToDelete, setProjectToDelete] = useState<Project | null>(null)
|
|
41
|
-
|
|
42
|
-
const { data: projects, isLoading: projectsLoading } = useProjects()
|
|
43
|
-
const { data: stats } = useStats()
|
|
44
|
-
const deleteMutation = useDeleteProject()
|
|
45
|
-
|
|
46
|
-
const handleDeleteClick = useCallback((project: Project, e: React.MouseEvent) => {
|
|
47
|
-
e.preventDefault()
|
|
48
|
-
e.stopPropagation()
|
|
49
|
-
setProjectToDelete(project)
|
|
50
|
-
}, [])
|
|
51
|
-
|
|
52
|
-
const handleConfirmDelete = useCallback(() => {
|
|
53
|
-
if (projectToDelete) {
|
|
54
|
-
deleteMutation.mutate(projectToDelete.id, {
|
|
55
|
-
onSuccess: () => setProjectToDelete(null)
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
}, [projectToDelete, deleteMutation])
|
|
59
|
-
|
|
60
|
-
if (projectsLoading) {
|
|
61
|
-
return (
|
|
62
|
-
<div className="flex items-center justify-center h-full">
|
|
63
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
|
|
64
|
-
</div>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const projectCount = projects?.length || 0
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div className="p-8 h-full overflow-auto">
|
|
72
|
-
<p className="text-muted-foreground">Hola! {stats?.userName || 'Developer'}</p>
|
|
73
|
-
|
|
74
|
-
<h1 className="text-8xl font-bold tracking-tighter tabular-nums mt-2">{projectCount}</h1>
|
|
75
|
-
<p className="text-muted-foreground mt-1">{projectCount === 1 ? 'project' : 'projects'}</p>
|
|
76
|
-
|
|
77
|
-
<section className="mt-8">
|
|
78
|
-
<SessionsChart />
|
|
79
|
-
</section>
|
|
80
|
-
|
|
81
|
-
<section className="mt-12">
|
|
82
|
-
<h2 className="font-bold uppercase tracking-wide text-muted-foreground mb-6">Active Projects</h2>
|
|
83
|
-
|
|
84
|
-
{projects?.length === 0 ? (
|
|
85
|
-
<div className="border-2 border-dashed border-border rounded-xl p-8 text-center">
|
|
86
|
-
<p className="text-muted-foreground">
|
|
87
|
-
No projects yet. Initialize with <code className="bg-muted px-2 py-1 rounded font-mono">/p:init</code>
|
|
88
|
-
</p>
|
|
89
|
-
</div>
|
|
90
|
-
) : (
|
|
91
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
92
|
-
{projects?.map((project: Project) => (
|
|
93
|
-
<ProjectCard key={project.id} project={project} onDeleteClick={handleDeleteClick} />
|
|
94
|
-
))}
|
|
95
|
-
</div>
|
|
96
|
-
)}
|
|
97
|
-
</section>
|
|
98
|
-
|
|
99
|
-
<AlertDialog open={!!projectToDelete} onOpenChange={(open: boolean) => !open && setProjectToDelete(null)}>
|
|
100
|
-
<AlertDialogContent>
|
|
101
|
-
<AlertDialogHeader>
|
|
102
|
-
<AlertDialogTitle className="flex items-center gap-2">
|
|
103
|
-
<AlertTriangle className="w-5 h-5 text-destructive" />
|
|
104
|
-
Delete Project?
|
|
105
|
-
</AlertDialogTitle>
|
|
106
|
-
<AlertDialogDescription>
|
|
107
|
-
<strong>"{projectToDelete?.name}"</strong> will be moved to trash.
|
|
108
|
-
<br />
|
|
109
|
-
<span className="text-muted-foreground text-sm">
|
|
110
|
-
Location: ~/.prjct-cli/.trash/{projectToDelete?.id}
|
|
111
|
-
</span>
|
|
112
|
-
</AlertDialogDescription>
|
|
113
|
-
</AlertDialogHeader>
|
|
114
|
-
<AlertDialogFooter>
|
|
115
|
-
<AlertDialogCancel disabled={deleteMutation.isPending}>Cancel</AlertDialogCancel>
|
|
116
|
-
<AlertDialogAction
|
|
117
|
-
onClick={handleConfirmDelete}
|
|
118
|
-
disabled={deleteMutation.isPending}
|
|
119
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
120
|
-
>
|
|
121
|
-
{deleteMutation.isPending ? 'Deleting...' : 'Delete'}
|
|
122
|
-
</AlertDialogAction>
|
|
123
|
-
</AlertDialogFooter>
|
|
124
|
-
</AlertDialogContent>
|
|
125
|
-
</AlertDialog>
|
|
126
|
-
</div>
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface ProjectCardProps {
|
|
131
|
-
project: Project
|
|
132
|
-
onDeleteClick: (project: Project, e: React.MouseEvent) => void
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function ProjectCard({ project, onDeleteClick }: ProjectCardProps) {
|
|
136
|
-
const hasStats = project.currentTask || project.nextTasksCount || project.ideasCount || project.lastActivity
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<div className="group relative bg-card border border-border rounded-lg overflow-hidden hover:border-primary/50 transition-all">
|
|
140
|
-
{project.hasActiveSession && <div className="absolute top-0 left-0 right-0 h-0.5 bg-green-500" />}
|
|
141
|
-
|
|
142
|
-
<Link href={`/project/${project.id}`} className="block p-4">
|
|
143
|
-
<div className="flex items-start gap-3">
|
|
144
|
-
<ProjectAvatar projectId={project.id} name={project.name} iconPath={project.iconPath} size="lg" />
|
|
145
|
-
|
|
146
|
-
<div className="flex-1 min-w-0">
|
|
147
|
-
<div className="flex items-center gap-2">
|
|
148
|
-
<h3 className="font-bold truncate">{project.name}</h3>
|
|
149
|
-
{project.hasActiveSession && (
|
|
150
|
-
<span className="flex h-2 w-2">
|
|
151
|
-
<span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-green-400 opacity-75" />
|
|
152
|
-
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
|
|
153
|
-
</span>
|
|
154
|
-
)}
|
|
155
|
-
</div>
|
|
156
|
-
{project.repoPath && (
|
|
157
|
-
<p className="text-xs text-muted-foreground truncate mt-0.5 flex items-center gap-1">
|
|
158
|
-
<FolderGit2 className="w-3 h-3 shrink-0" />
|
|
159
|
-
{formatPath(project.repoPath)}
|
|
160
|
-
</p>
|
|
161
|
-
)}
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
|
|
165
|
-
{project.currentTask && (
|
|
166
|
-
<div className="mt-3 p-2 bg-primary/5 rounded border border-primary/10">
|
|
167
|
-
<div className="flex items-start gap-2">
|
|
168
|
-
<Target className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
|
|
169
|
-
<span className="text-sm text-foreground line-clamp-2">{project.currentTask}</span>
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
)}
|
|
173
|
-
|
|
174
|
-
{hasStats && (
|
|
175
|
-
<div className="mt-3 flex items-center gap-3 text-xs text-muted-foreground">
|
|
176
|
-
{(project.nextTasksCount ?? 0) > 0 && (
|
|
177
|
-
<div className="flex items-center gap-1">
|
|
178
|
-
<ListTodo className="w-3.5 h-3.5" />
|
|
179
|
-
<span>{project.nextTasksCount} queued</span>
|
|
180
|
-
</div>
|
|
181
|
-
)}
|
|
182
|
-
{(project.ideasCount ?? 0) > 0 && (
|
|
183
|
-
<div className="flex items-center gap-1">
|
|
184
|
-
<Lightbulb className="w-3.5 h-3.5" />
|
|
185
|
-
<span>{project.ideasCount} ideas</span>
|
|
186
|
-
</div>
|
|
187
|
-
)}
|
|
188
|
-
{project.lastActivity && (
|
|
189
|
-
<div className="flex items-center gap-1 ml-auto">
|
|
190
|
-
<Zap className="w-3.5 h-3.5" />
|
|
191
|
-
<span>{formatRelativeTime(project.lastActivity)}</span>
|
|
192
|
-
</div>
|
|
193
|
-
)}
|
|
194
|
-
</div>
|
|
195
|
-
)}
|
|
196
|
-
|
|
197
|
-
{project.techStack && project.techStack.length > 0 && (
|
|
198
|
-
<div className="mt-3">
|
|
199
|
-
<TechStackBadges techStack={project.techStack} />
|
|
200
|
-
</div>
|
|
201
|
-
)}
|
|
202
|
-
</Link>
|
|
203
|
-
|
|
204
|
-
<DropdownMenu>
|
|
205
|
-
<DropdownMenuTrigger asChild>
|
|
206
|
-
<Button
|
|
207
|
-
variant="ghost"
|
|
208
|
-
size="icon-sm"
|
|
209
|
-
className="absolute top-3 right-3 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
210
|
-
onClick={(e) => e.preventDefault()}
|
|
211
|
-
>
|
|
212
|
-
<MoreHorizontal className="w-4 h-4" />
|
|
213
|
-
</Button>
|
|
214
|
-
</DropdownMenuTrigger>
|
|
215
|
-
<DropdownMenuContent align="end">
|
|
216
|
-
<DropdownMenuItem
|
|
217
|
-
className="text-destructive focus:text-destructive"
|
|
218
|
-
onClick={(e: React.MouseEvent) => onDeleteClick(project, e)}
|
|
219
|
-
>
|
|
220
|
-
<Trash2 className="w-4 h-4 mr-2" />
|
|
221
|
-
Delete
|
|
222
|
-
</DropdownMenuItem>
|
|
223
|
-
</DropdownMenuContent>
|
|
224
|
-
</DropdownMenu>
|
|
225
|
-
</div>
|
|
226
|
-
)
|
|
11
|
+
return <DashboardContent projects={projects} stats={stats} />
|
|
227
12
|
}
|
|
@@ -8,6 +8,7 @@ import { TerminalTabs } from '@/components/TerminalTabs'
|
|
|
8
8
|
import { Button } from '@/components/ui/button'
|
|
9
9
|
import { Badge } from '@/components/ui/badge'
|
|
10
10
|
import { TooltipProvider } from '@/components/ui/tooltip'
|
|
11
|
+
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
|
|
11
12
|
import {
|
|
12
13
|
AlertDialog,
|
|
13
14
|
AlertDialogAction,
|
|
@@ -41,8 +42,11 @@ import {
|
|
|
41
42
|
Activity,
|
|
42
43
|
History,
|
|
43
44
|
Undo2,
|
|
44
|
-
Redo2
|
|
45
|
+
Redo2,
|
|
46
|
+
Command,
|
|
47
|
+
X
|
|
45
48
|
} from 'lucide-react'
|
|
49
|
+
import { cn } from '@/lib/utils'
|
|
46
50
|
|
|
47
51
|
// Commands ordered by real developer workflow
|
|
48
52
|
const WORKFLOW_COMMANDS = [
|
|
@@ -64,11 +68,77 @@ const WORKFLOW_COMMANDS = [
|
|
|
64
68
|
|
|
65
69
|
const COMMAND_GROUPS = ['work', 'session', 'plan', 'ship', 'status', 'recovery'] as const
|
|
66
70
|
|
|
71
|
+
// Command sidebar content - shared between desktop and mobile
|
|
72
|
+
function CommandSidebarContent({
|
|
73
|
+
projectId,
|
|
74
|
+
project,
|
|
75
|
+
isActiveConnected,
|
|
76
|
+
sendCommandToActive,
|
|
77
|
+
onCommandClick
|
|
78
|
+
}: {
|
|
79
|
+
projectId: string
|
|
80
|
+
project: { name?: string; iconPath?: string | null }
|
|
81
|
+
isActiveConnected: boolean
|
|
82
|
+
sendCommandToActive: (cmd: string) => void
|
|
83
|
+
onCommandClick?: () => void
|
|
84
|
+
}) {
|
|
85
|
+
const router = useRouter()
|
|
86
|
+
|
|
87
|
+
const handleCommand = (cmd: string) => {
|
|
88
|
+
sendCommandToActive(cmd)
|
|
89
|
+
onCommandClick?.()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<div className="h-14 flex items-center justify-center border-b border-border w-full">
|
|
95
|
+
<ProjectAvatar
|
|
96
|
+
projectId={projectId}
|
|
97
|
+
name={project.name || projectId}
|
|
98
|
+
iconPath={project.iconPath}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div className="flex-1 flex flex-col gap-1 overflow-auto py-3">
|
|
103
|
+
{/* Stats button - navigates to stats page */}
|
|
104
|
+
<CommandButton
|
|
105
|
+
cmd="Project stats"
|
|
106
|
+
icon={BarChart3}
|
|
107
|
+
tip="Stats"
|
|
108
|
+
disabled={false}
|
|
109
|
+
onClick={() => {
|
|
110
|
+
router.push(`/project/${projectId}/stats`)
|
|
111
|
+
onCommandClick?.()
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
<div className="border-b border-border w-8 my-2 mx-auto" />
|
|
115
|
+
|
|
116
|
+
{COMMAND_GROUPS.map((group, groupIndex) => (
|
|
117
|
+
<div key={group} className="flex flex-col items-center">
|
|
118
|
+
{WORKFLOW_COMMANDS.filter(c => c.group === group).map(({ cmd, icon, tip }) => (
|
|
119
|
+
<CommandButton
|
|
120
|
+
key={cmd}
|
|
121
|
+
cmd={cmd}
|
|
122
|
+
icon={icon}
|
|
123
|
+
tip={tip}
|
|
124
|
+
disabled={!isActiveConnected}
|
|
125
|
+
onClick={() => handleCommand(cmd)}
|
|
126
|
+
/>
|
|
127
|
+
))}
|
|
128
|
+
{groupIndex < COMMAND_GROUPS.length - 1 && (
|
|
129
|
+
<div className="border-b border-border w-8 my-2" />
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
</div>
|
|
134
|
+
</>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
67
138
|
// Inner component that uses the terminal context
|
|
68
139
|
function ProjectPageContent({ projectId, project }: { projectId: string; project: NonNullable<ReturnType<typeof useProject>['data']> }) {
|
|
69
140
|
const router = useRouter()
|
|
70
|
-
const [
|
|
71
|
-
const deleteMutation = useDeleteProject()
|
|
141
|
+
const [commandSheetOpen, setCommandSheetOpen] = useState(false)
|
|
72
142
|
|
|
73
143
|
const {
|
|
74
144
|
sessions,
|
|
@@ -84,74 +154,56 @@ function ProjectPageContent({ projectId, project }: { projectId: string; project
|
|
|
84
154
|
<div className="h-full">
|
|
85
155
|
<TooltipProvider>
|
|
86
156
|
<div className="flex h-full">
|
|
87
|
-
{/* Sidebar */}
|
|
88
|
-
<aside className="w-14 border-r border-border flex
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div className="flex-1 flex flex-col gap-1 overflow-auto py-3">
|
|
98
|
-
{/* Stats button - navigates to stats page */}
|
|
99
|
-
<CommandButton
|
|
100
|
-
cmd="Project stats"
|
|
101
|
-
icon={BarChart3}
|
|
102
|
-
tip="Stats"
|
|
103
|
-
disabled={false}
|
|
104
|
-
onClick={() => router.push(`/project/${projectId}/stats`)}
|
|
105
|
-
/>
|
|
106
|
-
<div className="border-b border-border w-8 my-2 mx-auto" />
|
|
107
|
-
|
|
108
|
-
{COMMAND_GROUPS.map((group, groupIndex) => (
|
|
109
|
-
<div key={group} className="flex flex-col items-center">
|
|
110
|
-
{WORKFLOW_COMMANDS.filter(c => c.group === group).map(({ cmd, icon, tip }) => (
|
|
111
|
-
<CommandButton
|
|
112
|
-
key={cmd}
|
|
113
|
-
cmd={cmd}
|
|
114
|
-
icon={icon}
|
|
115
|
-
tip={tip}
|
|
116
|
-
disabled={!isActiveConnected}
|
|
117
|
-
onClick={() => sendCommandToActive(cmd)}
|
|
118
|
-
/>
|
|
119
|
-
))}
|
|
120
|
-
{groupIndex < COMMAND_GROUPS.length - 1 && (
|
|
121
|
-
<div className="border-b border-border w-8 my-2" />
|
|
122
|
-
)}
|
|
123
|
-
</div>
|
|
124
|
-
))}
|
|
125
|
-
</div>
|
|
157
|
+
{/* Desktop Sidebar - hidden on mobile */}
|
|
158
|
+
<aside className="hidden md:flex w-14 border-r border-border flex-col bg-card/50 items-center">
|
|
159
|
+
<CommandSidebarContent
|
|
160
|
+
projectId={projectId}
|
|
161
|
+
project={project}
|
|
162
|
+
isActiveConnected={isActiveConnected}
|
|
163
|
+
sendCommandToActive={sendCommandToActive}
|
|
164
|
+
/>
|
|
126
165
|
</aside>
|
|
127
166
|
|
|
128
167
|
{/* Main */}
|
|
129
|
-
<main className="flex-1 flex flex-col">
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
<main className="flex-1 flex flex-col min-w-0">
|
|
169
|
+
{/* Header - Responsive */}
|
|
170
|
+
<header className="h-auto md:h-14 flex flex-col md:flex-row md:items-center justify-between px-3 md:px-4 py-2 md:py-0 border-b border-border bg-card gap-2">
|
|
171
|
+
{/* Mobile: Add padding for hamburger menu */}
|
|
172
|
+
<div className="flex items-center gap-3 pl-12 md:pl-0">
|
|
173
|
+
{/* Mobile: Show project avatar */}
|
|
174
|
+
<div className="md:hidden">
|
|
175
|
+
<ProjectAvatar
|
|
176
|
+
projectId={projectId}
|
|
177
|
+
name={project.name || projectId}
|
|
178
|
+
iconPath={project.iconPath}
|
|
179
|
+
size="sm"
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
<div className="flex flex-col min-w-0">
|
|
133
183
|
<div className="flex items-center gap-2">
|
|
134
|
-
<span className="font-bold leading-tight">{project.name || projectId}</span>
|
|
184
|
+
<span className="font-bold leading-tight truncate">{project.name || projectId}</span>
|
|
135
185
|
{project.version && (
|
|
136
|
-
<Badge variant="outline" className="text-[10px] px-1.5 py-0 font-mono">
|
|
186
|
+
<Badge variant="outline" className="text-[10px] px-1.5 py-0 font-mono shrink-0">
|
|
137
187
|
v{project.version}
|
|
138
188
|
</Badge>
|
|
139
189
|
)}
|
|
140
190
|
</div>
|
|
141
191
|
{project.repoPath && (
|
|
142
|
-
<span className="text-xs text-muted-foreground leading-tight flex items-center gap-1">
|
|
143
|
-
<FolderGit2 className="w-3 h-3" />
|
|
144
|
-
{formatPath(project.repoPath)}
|
|
192
|
+
<span className="text-xs text-muted-foreground leading-tight flex items-center gap-1 truncate">
|
|
193
|
+
<FolderGit2 className="w-3 h-3 shrink-0" />
|
|
194
|
+
<span className="truncate">{formatPath(project.repoPath)}</span>
|
|
145
195
|
</span>
|
|
146
196
|
)}
|
|
147
197
|
</div>
|
|
148
198
|
{hasActiveSessions && (
|
|
149
|
-
<Badge variant="outline" className="text-green-500 border-green-500/50">
|
|
199
|
+
<Badge variant="outline" className="text-green-500 border-green-500/50 shrink-0">
|
|
150
200
|
{sessions.filter(s => s.isConnected).length} active
|
|
151
201
|
</Badge>
|
|
152
202
|
)}
|
|
153
203
|
</div>
|
|
154
|
-
|
|
204
|
+
|
|
205
|
+
{/* Desktop only: metadata and tech stack */}
|
|
206
|
+
<div className="hidden md:flex items-center gap-4">
|
|
155
207
|
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
|
156
208
|
{project.stack && <span>{project.stack}</span>}
|
|
157
209
|
{project.filesCount && (
|
|
@@ -166,12 +218,86 @@ function ProjectPageContent({ projectId, project }: { projectId: string; project
|
|
|
166
218
|
</header>
|
|
167
219
|
|
|
168
220
|
{/* Terminal tabs area */}
|
|
169
|
-
<div className="flex-1">
|
|
221
|
+
<div className="flex-1 min-h-0">
|
|
170
222
|
<TerminalTabs projectDir={project.repoPath || project.path || '/tmp'} />
|
|
171
223
|
</div>
|
|
172
224
|
</main>
|
|
173
225
|
</div>
|
|
174
226
|
|
|
227
|
+
{/* Mobile: Floating Action Button for commands */}
|
|
228
|
+
<div className="md:hidden fixed bottom-4 right-4 z-50">
|
|
229
|
+
<Sheet open={commandSheetOpen} onOpenChange={setCommandSheetOpen}>
|
|
230
|
+
<SheetTrigger asChild>
|
|
231
|
+
<button
|
|
232
|
+
className={cn(
|
|
233
|
+
"h-14 w-14 rounded-full shadow-lg flex items-center justify-center transition-all",
|
|
234
|
+
isActiveConnected
|
|
235
|
+
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
236
|
+
: "bg-muted text-muted-foreground"
|
|
237
|
+
)}
|
|
238
|
+
aria-label="Open commands"
|
|
239
|
+
>
|
|
240
|
+
<Command className="h-6 w-6" />
|
|
241
|
+
</button>
|
|
242
|
+
</SheetTrigger>
|
|
243
|
+
<SheetContent side="bottom" className="h-[70vh] rounded-t-2xl px-0">
|
|
244
|
+
<div className="flex flex-col h-full">
|
|
245
|
+
<div className="px-4 pb-2 border-b border-border">
|
|
246
|
+
<div className="flex items-center justify-between">
|
|
247
|
+
<h3 className="font-semibold">Commands</h3>
|
|
248
|
+
<Badge variant={isActiveConnected ? "default" : "secondary"}>
|
|
249
|
+
{isActiveConnected ? "Connected" : "Disconnected"}
|
|
250
|
+
</Badge>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
{/* Command grid for mobile */}
|
|
255
|
+
<div className="flex-1 overflow-auto p-4">
|
|
256
|
+
<div className="grid grid-cols-4 gap-3">
|
|
257
|
+
{/* Stats button */}
|
|
258
|
+
<button
|
|
259
|
+
onClick={() => {
|
|
260
|
+
router.push(`/project/${projectId}/stats`)
|
|
261
|
+
setCommandSheetOpen(false)
|
|
262
|
+
}}
|
|
263
|
+
className="flex flex-col items-center gap-1.5 p-3 rounded-lg hover:bg-accent transition-colors"
|
|
264
|
+
>
|
|
265
|
+
<div className="h-10 w-10 rounded-full bg-accent flex items-center justify-center">
|
|
266
|
+
<BarChart3 className="h-5 w-5" />
|
|
267
|
+
</div>
|
|
268
|
+
<span className="text-xs text-muted-foreground">Stats</span>
|
|
269
|
+
</button>
|
|
270
|
+
|
|
271
|
+
{WORKFLOW_COMMANDS.map(({ cmd, icon: Icon, tip }) => (
|
|
272
|
+
<button
|
|
273
|
+
key={cmd}
|
|
274
|
+
onClick={() => {
|
|
275
|
+
sendCommandToActive(cmd)
|
|
276
|
+
setCommandSheetOpen(false)
|
|
277
|
+
}}
|
|
278
|
+
disabled={!isActiveConnected}
|
|
279
|
+
className={cn(
|
|
280
|
+
"flex flex-col items-center gap-1.5 p-3 rounded-lg transition-colors",
|
|
281
|
+
isActiveConnected
|
|
282
|
+
? "hover:bg-accent"
|
|
283
|
+
: "opacity-50 cursor-not-allowed"
|
|
284
|
+
)}
|
|
285
|
+
>
|
|
286
|
+
<div className={cn(
|
|
287
|
+
"h-10 w-10 rounded-full flex items-center justify-center",
|
|
288
|
+
isActiveConnected ? "bg-accent" : "bg-muted"
|
|
289
|
+
)}>
|
|
290
|
+
<Icon className="h-5 w-5" />
|
|
291
|
+
</div>
|
|
292
|
+
<span className="text-xs text-muted-foreground">{tip}</span>
|
|
293
|
+
</button>
|
|
294
|
+
))}
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</SheetContent>
|
|
299
|
+
</Sheet>
|
|
300
|
+
</div>
|
|
175
301
|
</TooltipProvider>
|
|
176
302
|
</div>
|
|
177
303
|
)
|
|
@@ -195,16 +321,16 @@ export default function ProjectPage({ params }: { params: Promise<{ id: string }
|
|
|
195
321
|
|
|
196
322
|
if (!project) {
|
|
197
323
|
return (
|
|
198
|
-
<div className="flex items-center justify-center h-full">
|
|
199
|
-
<div className="text-center space-y-4">
|
|
324
|
+
<div className="flex items-center justify-center h-full p-4">
|
|
325
|
+
<div className="text-center space-y-4 max-w-sm">
|
|
200
326
|
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mx-auto">
|
|
201
327
|
<AlertTriangle className="w-8 h-8 text-muted-foreground" />
|
|
202
328
|
</div>
|
|
203
329
|
<div>
|
|
204
330
|
<h2 className="text-lg font-medium">Project not found</h2>
|
|
205
|
-
<p className="text-sm text-muted-foreground mt-1">ID: {projectId}</p>
|
|
331
|
+
<p className="text-sm text-muted-foreground mt-1 break-all">ID: {projectId}</p>
|
|
206
332
|
</div>
|
|
207
|
-
<div className="flex gap-2 justify-center">
|
|
333
|
+
<div className="flex flex-col sm:flex-row gap-2 justify-center">
|
|
208
334
|
<Button variant="outline" onClick={() => router.push('/')}>
|
|
209
335
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
210
336
|
Back to Dashboard
|
|
@@ -216,7 +342,7 @@ export default function ProjectPage({ params }: { params: Promise<{ id: string }
|
|
|
216
342
|
</div>
|
|
217
343
|
|
|
218
344
|
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
|
219
|
-
<AlertDialogContent>
|
|
345
|
+
<AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
|
|
220
346
|
<AlertDialogHeader>
|
|
221
347
|
<AlertDialogTitle className="flex items-center gap-2">
|
|
222
348
|
<AlertTriangle className="w-5 h-5 text-destructive" />
|
|
@@ -225,15 +351,17 @@ export default function ProjectPage({ params }: { params: Promise<{ id: string }
|
|
|
225
351
|
<AlertDialogDescription>
|
|
226
352
|
This will move the project storage to trash.
|
|
227
353
|
<br />
|
|
228
|
-
<span className="text-muted-foreground text-sm">ID: {projectId}</span>
|
|
354
|
+
<span className="text-muted-foreground text-sm break-all">ID: {projectId}</span>
|
|
229
355
|
</AlertDialogDescription>
|
|
230
356
|
</AlertDialogHeader>
|
|
231
|
-
<AlertDialogFooter>
|
|
232
|
-
<AlertDialogCancel disabled={deleteMutation.isPending}>
|
|
357
|
+
<AlertDialogFooter className="flex-col sm:flex-row gap-2">
|
|
358
|
+
<AlertDialogCancel disabled={deleteMutation.isPending} className="w-full sm:w-auto">
|
|
359
|
+
Cancel
|
|
360
|
+
</AlertDialogCancel>
|
|
233
361
|
<AlertDialogAction
|
|
234
362
|
onClick={() => deleteMutation.mutate(projectId, { onSuccess: () => router.push('/') })}
|
|
235
363
|
disabled={deleteMutation.isPending}
|
|
236
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
364
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
|
|
237
365
|
>
|
|
238
366
|
{deleteMutation.isPending ? 'Deleting...' : 'Delete'}
|
|
239
367
|
</AlertDialogAction>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BentoGrid } from '@/components/BentoGrid'
|
|
2
|
+
import { BentoCardSkeleton } from '@/components/BentoCardSkeleton'
|
|
3
|
+
|
|
4
|
+
export default function StatsLoading() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="p-4 md:p-8 space-y-6 md:space-y-8">
|
|
7
|
+
{/* Hero skeleton */}
|
|
8
|
+
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pl-10 md:pl-0">
|
|
9
|
+
<div className="h-16 w-16 md:h-20 md:w-20 rounded-full bg-muted animate-pulse" />
|
|
10
|
+
<div className="space-y-3 text-center sm:text-left">
|
|
11
|
+
<div className="h-12 md:h-16 w-24 md:w-32 bg-muted rounded animate-pulse mx-auto sm:mx-0" />
|
|
12
|
+
<div className="h-4 w-40 md:w-48 bg-muted rounded animate-pulse mx-auto sm:mx-0" />
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
{/* Bento grid skeleton */}
|
|
17
|
+
<BentoGrid>
|
|
18
|
+
<BentoCardSkeleton size="2x2" />
|
|
19
|
+
<BentoCardSkeleton size="1x1" />
|
|
20
|
+
<BentoCardSkeleton size="2x2" />
|
|
21
|
+
<BentoCardSkeleton size="1x1" />
|
|
22
|
+
<BentoCardSkeleton size="1x2" />
|
|
23
|
+
<BentoCardSkeleton size="1x2" />
|
|
24
|
+
<BentoCardSkeleton size="1x1" />
|
|
25
|
+
<BentoCardSkeleton size="1x1" />
|
|
26
|
+
</BentoGrid>
|
|
27
|
+
|
|
28
|
+
{/* Timeline skeleton */}
|
|
29
|
+
<div className="space-y-3">
|
|
30
|
+
<div className="h-4 w-24 bg-muted rounded animate-pulse" />
|
|
31
|
+
<div className="space-y-2">
|
|
32
|
+
{Array.from({ length: 5 }).map((_, i) => (
|
|
33
|
+
<div key={i} className="flex items-center gap-3">
|
|
34
|
+
<div className="h-2 w-2 rounded-full bg-muted animate-pulse" />
|
|
35
|
+
<div className="h-4 flex-1 bg-muted rounded animate-pulse" />
|
|
36
|
+
<div className="h-3 w-16 bg-muted rounded animate-pulse" />
|
|
37
|
+
</div>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|