@prmichaelsen/acp-visualizer 0.1.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/README.md +68 -0
- package/agent/commands/acp.clarification-address.md +417 -0
- package/agent/commands/acp.clarification-capture.md +386 -0
- package/agent/commands/acp.clarification-create.md +437 -0
- package/agent/commands/acp.clarifications-research.md +326 -0
- package/agent/commands/acp.command-create.md +432 -0
- package/agent/commands/acp.design-create.md +286 -0
- package/agent/commands/acp.design-reference.md +355 -0
- package/agent/commands/acp.handoff.md +270 -0
- package/agent/commands/acp.index.md +423 -0
- package/agent/commands/acp.init.md +546 -0
- package/agent/commands/acp.package-create.md +895 -0
- package/agent/commands/acp.package-info.md +212 -0
- package/agent/commands/acp.package-install.md +539 -0
- package/agent/commands/acp.package-list.md +280 -0
- package/agent/commands/acp.package-publish.md +541 -0
- package/agent/commands/acp.package-remove.md +293 -0
- package/agent/commands/acp.package-search.md +307 -0
- package/agent/commands/acp.package-update.md +361 -0
- package/agent/commands/acp.package-validate.md +540 -0
- package/agent/commands/acp.pattern-create.md +386 -0
- package/agent/commands/acp.plan.md +587 -0
- package/agent/commands/acp.proceed.md +882 -0
- package/agent/commands/acp.project-create.md +675 -0
- package/agent/commands/acp.project-info.md +312 -0
- package/agent/commands/acp.project-list.md +226 -0
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-set.md +227 -0
- package/agent/commands/acp.project-update.md +307 -0
- package/agent/commands/acp.projects-restore.md +228 -0
- package/agent/commands/acp.projects-sync.md +347 -0
- package/agent/commands/acp.report.md +407 -0
- package/agent/commands/acp.resume.md +239 -0
- package/agent/commands/acp.sessions.md +301 -0
- package/agent/commands/acp.status.md +293 -0
- package/agent/commands/acp.sync.md +364 -0
- package/agent/commands/acp.task-create.md +500 -0
- package/agent/commands/acp.update.md +302 -0
- package/agent/commands/acp.validate.md +466 -0
- package/agent/commands/acp.version-check-for-updates.md +276 -0
- package/agent/commands/acp.version-check.md +191 -0
- package/agent/commands/acp.version-update.md +289 -0
- package/agent/commands/command.template.md +339 -0
- package/agent/commands/git.commit.md +526 -0
- package/agent/commands/git.init.md +514 -0
- package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
- package/agent/commands/tanstack-cloudflare.tail.md +275 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/design.template.md +154 -0
- package/agent/design/local.dashboard-layout-routing.md +288 -0
- package/agent/design/local.data-model-yaml-parsing.md +310 -0
- package/agent/design/local.search-filtering.md +331 -0
- package/agent/design/local.server-api-auto-refresh.md +235 -0
- package/agent/design/local.table-tree-views.md +299 -0
- package/agent/design/local.visualizer-requirements.md +349 -0
- package/agent/design/requirements.template.md +387 -0
- package/agent/index/.gitkeep +0 -0
- package/agent/index/acp.core.yaml +137 -0
- package/agent/index/local.main.template.yaml +37 -0
- package/agent/manifest.template.yaml +13 -0
- package/agent/manifest.yaml +302 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
- package/agent/milestones/milestone-1-{title}.template.md +206 -0
- package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
- package/agent/package.template.yaml +86 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.template.md +1237 -0
- package/agent/patterns/pattern.template.md +382 -0
- package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
- package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
- package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
- package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
- package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
- package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
- package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
- package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
- package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
- package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
- package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
- package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
- package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
- package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
- package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
- package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
- package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
- package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
- package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
- package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
- package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
- package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
- package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
- package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
- package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
- package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
- package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
- package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
- package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
- package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
- package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
- package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
- package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
- package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
- package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
- package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
- package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
- package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
- package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
- package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
- package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
- package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
- package/agent/progress.template.yaml +161 -0
- package/agent/progress.yaml +145 -0
- package/agent/schemas/package.schema.yaml +276 -0
- package/agent/scripts/acp.common.sh +1781 -0
- package/agent/scripts/acp.install.sh +333 -0
- package/agent/scripts/acp.package-create.sh +924 -0
- package/agent/scripts/acp.package-info.sh +288 -0
- package/agent/scripts/acp.package-install.sh +893 -0
- package/agent/scripts/acp.package-list.sh +311 -0
- package/agent/scripts/acp.package-publish.sh +420 -0
- package/agent/scripts/acp.package-remove.sh +348 -0
- package/agent/scripts/acp.package-search.sh +156 -0
- package/agent/scripts/acp.package-update.sh +517 -0
- package/agent/scripts/acp.package-validate.sh +1018 -0
- package/agent/scripts/acp.uninstall.sh +85 -0
- package/agent/scripts/acp.version-check-for-updates.sh +98 -0
- package/agent/scripts/acp.version-check.sh +47 -0
- package/agent/scripts/acp.version-update.sh +176 -0
- package/agent/scripts/acp.yaml-parser.sh +985 -0
- package/agent/scripts/acp.yaml-validate.sh +205 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
- package/agent/tasks/task-1-{title}.template.md +244 -0
- package/bin/visualize.mjs +84 -0
- package/package.json +48 -0
- package/src/components/ExtraFieldsBadge.tsx +15 -0
- package/src/components/FilterBar.tsx +33 -0
- package/src/components/Header.tsx +23 -0
- package/src/components/MilestoneTable.tsx +167 -0
- package/src/components/MilestoneTree.tsx +84 -0
- package/src/components/ProgressBar.tsx +20 -0
- package/src/components/SearchInput.tsx +22 -0
- package/src/components/Sidebar.tsx +54 -0
- package/src/components/StatusBadge.tsx +23 -0
- package/src/components/StatusDot.tsx +12 -0
- package/src/components/TaskList.tsx +36 -0
- package/src/components/ViewToggle.tsx +31 -0
- package/src/lib/config.ts +8 -0
- package/src/lib/file-watcher.ts +43 -0
- package/src/lib/search.ts +48 -0
- package/src/lib/types.ts +73 -0
- package/src/lib/useAutoRefresh.ts +31 -0
- package/src/lib/useCollapse.ts +31 -0
- package/src/lib/useFilteredData.ts +55 -0
- package/src/lib/yaml-loader-real.spec.ts +47 -0
- package/src/lib/yaml-loader.spec.ts +201 -0
- package/src/lib/yaml-loader.ts +265 -0
- package/src/routeTree.gen.ts +140 -0
- package/src/router.tsx +10 -0
- package/src/routes/__root.tsx +75 -0
- package/src/routes/api/watch.ts +29 -0
- package/src/routes/index.tsx +115 -0
- package/src/routes/milestones.tsx +50 -0
- package/src/routes/search.tsx +84 -0
- package/src/routes/tasks.tsx +63 -0
- package/src/services/progress-database.service.ts +46 -0
- package/src/styles.css +25 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +16 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as TasksRouteImport } from './routes/tasks'
|
|
13
|
+
import { Route as SearchRouteImport } from './routes/search'
|
|
14
|
+
import { Route as MilestonesRouteImport } from './routes/milestones'
|
|
15
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
16
|
+
import { Route as ApiWatchRouteImport } from './routes/api/watch'
|
|
17
|
+
|
|
18
|
+
const TasksRoute = TasksRouteImport.update({
|
|
19
|
+
id: '/tasks',
|
|
20
|
+
path: '/tasks',
|
|
21
|
+
getParentRoute: () => rootRouteImport,
|
|
22
|
+
} as any)
|
|
23
|
+
const SearchRoute = SearchRouteImport.update({
|
|
24
|
+
id: '/search',
|
|
25
|
+
path: '/search',
|
|
26
|
+
getParentRoute: () => rootRouteImport,
|
|
27
|
+
} as any)
|
|
28
|
+
const MilestonesRoute = MilestonesRouteImport.update({
|
|
29
|
+
id: '/milestones',
|
|
30
|
+
path: '/milestones',
|
|
31
|
+
getParentRoute: () => rootRouteImport,
|
|
32
|
+
} as any)
|
|
33
|
+
const IndexRoute = IndexRouteImport.update({
|
|
34
|
+
id: '/',
|
|
35
|
+
path: '/',
|
|
36
|
+
getParentRoute: () => rootRouteImport,
|
|
37
|
+
} as any)
|
|
38
|
+
const ApiWatchRoute = ApiWatchRouteImport.update({
|
|
39
|
+
id: '/api/watch',
|
|
40
|
+
path: '/api/watch',
|
|
41
|
+
getParentRoute: () => rootRouteImport,
|
|
42
|
+
} as any)
|
|
43
|
+
|
|
44
|
+
export interface FileRoutesByFullPath {
|
|
45
|
+
'/': typeof IndexRoute
|
|
46
|
+
'/milestones': typeof MilestonesRoute
|
|
47
|
+
'/search': typeof SearchRoute
|
|
48
|
+
'/tasks': typeof TasksRoute
|
|
49
|
+
'/api/watch': typeof ApiWatchRoute
|
|
50
|
+
}
|
|
51
|
+
export interface FileRoutesByTo {
|
|
52
|
+
'/': typeof IndexRoute
|
|
53
|
+
'/milestones': typeof MilestonesRoute
|
|
54
|
+
'/search': typeof SearchRoute
|
|
55
|
+
'/tasks': typeof TasksRoute
|
|
56
|
+
'/api/watch': typeof ApiWatchRoute
|
|
57
|
+
}
|
|
58
|
+
export interface FileRoutesById {
|
|
59
|
+
__root__: typeof rootRouteImport
|
|
60
|
+
'/': typeof IndexRoute
|
|
61
|
+
'/milestones': typeof MilestonesRoute
|
|
62
|
+
'/search': typeof SearchRoute
|
|
63
|
+
'/tasks': typeof TasksRoute
|
|
64
|
+
'/api/watch': typeof ApiWatchRoute
|
|
65
|
+
}
|
|
66
|
+
export interface FileRouteTypes {
|
|
67
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
68
|
+
fullPaths: '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
|
|
69
|
+
fileRoutesByTo: FileRoutesByTo
|
|
70
|
+
to: '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
|
|
71
|
+
id: '__root__' | '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
|
|
72
|
+
fileRoutesById: FileRoutesById
|
|
73
|
+
}
|
|
74
|
+
export interface RootRouteChildren {
|
|
75
|
+
IndexRoute: typeof IndexRoute
|
|
76
|
+
MilestonesRoute: typeof MilestonesRoute
|
|
77
|
+
SearchRoute: typeof SearchRoute
|
|
78
|
+
TasksRoute: typeof TasksRoute
|
|
79
|
+
ApiWatchRoute: typeof ApiWatchRoute
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare module '@tanstack/react-router' {
|
|
83
|
+
interface FileRoutesByPath {
|
|
84
|
+
'/tasks': {
|
|
85
|
+
id: '/tasks'
|
|
86
|
+
path: '/tasks'
|
|
87
|
+
fullPath: '/tasks'
|
|
88
|
+
preLoaderRoute: typeof TasksRouteImport
|
|
89
|
+
parentRoute: typeof rootRouteImport
|
|
90
|
+
}
|
|
91
|
+
'/search': {
|
|
92
|
+
id: '/search'
|
|
93
|
+
path: '/search'
|
|
94
|
+
fullPath: '/search'
|
|
95
|
+
preLoaderRoute: typeof SearchRouteImport
|
|
96
|
+
parentRoute: typeof rootRouteImport
|
|
97
|
+
}
|
|
98
|
+
'/milestones': {
|
|
99
|
+
id: '/milestones'
|
|
100
|
+
path: '/milestones'
|
|
101
|
+
fullPath: '/milestones'
|
|
102
|
+
preLoaderRoute: typeof MilestonesRouteImport
|
|
103
|
+
parentRoute: typeof rootRouteImport
|
|
104
|
+
}
|
|
105
|
+
'/': {
|
|
106
|
+
id: '/'
|
|
107
|
+
path: '/'
|
|
108
|
+
fullPath: '/'
|
|
109
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
110
|
+
parentRoute: typeof rootRouteImport
|
|
111
|
+
}
|
|
112
|
+
'/api/watch': {
|
|
113
|
+
id: '/api/watch'
|
|
114
|
+
path: '/api/watch'
|
|
115
|
+
fullPath: '/api/watch'
|
|
116
|
+
preLoaderRoute: typeof ApiWatchRouteImport
|
|
117
|
+
parentRoute: typeof rootRouteImport
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
123
|
+
IndexRoute: IndexRoute,
|
|
124
|
+
MilestonesRoute: MilestonesRoute,
|
|
125
|
+
SearchRoute: SearchRoute,
|
|
126
|
+
TasksRoute: TasksRoute,
|
|
127
|
+
ApiWatchRoute: ApiWatchRoute,
|
|
128
|
+
}
|
|
129
|
+
export const routeTree = rootRouteImport
|
|
130
|
+
._addFileChildren(rootRouteChildren)
|
|
131
|
+
._addFileTypes<FileRouteTypes>()
|
|
132
|
+
|
|
133
|
+
import type { getRouter } from './router.tsx'
|
|
134
|
+
import type { createStart } from '@tanstack/react-start'
|
|
135
|
+
declare module '@tanstack/react-start' {
|
|
136
|
+
interface Register {
|
|
137
|
+
ssr: true
|
|
138
|
+
router: Awaited<ReturnType<typeof getRouter>>
|
|
139
|
+
}
|
|
140
|
+
}
|
package/src/router.tsx
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { HeadContent, Scripts, createRootRoute, Outlet } from '@tanstack/react-router'
|
|
2
|
+
import { useAutoRefresh } from '@/lib/useAutoRefresh'
|
|
3
|
+
import { Sidebar } from '@/components/Sidebar'
|
|
4
|
+
import { Header } from '@/components/Header'
|
|
5
|
+
import { ProgressDatabaseService } from '@/services/progress-database.service'
|
|
6
|
+
import type { ProgressData } from '@/lib/types'
|
|
7
|
+
|
|
8
|
+
import appCss from '../styles.css?url'
|
|
9
|
+
|
|
10
|
+
export const Route = createRootRoute({
|
|
11
|
+
beforeLoad: async () => {
|
|
12
|
+
let progressData: ProgressData | null = null
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const result = ProgressDatabaseService.getProgressData()
|
|
16
|
+
if (result.ok) {
|
|
17
|
+
progressData = result.data
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('[Root] Failed to load progress data:', error)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return { progressData }
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
head: () => ({
|
|
27
|
+
meta: [
|
|
28
|
+
{ charSet: 'utf-8' },
|
|
29
|
+
{
|
|
30
|
+
name: 'viewport',
|
|
31
|
+
content: 'width=device-width, initial-scale=1',
|
|
32
|
+
},
|
|
33
|
+
{ title: 'ACP Progress Visualizer' },
|
|
34
|
+
{
|
|
35
|
+
name: 'description',
|
|
36
|
+
content: 'Browser-based dashboard for visualizing ACP progress.yaml data',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
links: [
|
|
40
|
+
{ rel: 'stylesheet', href: appCss },
|
|
41
|
+
],
|
|
42
|
+
}),
|
|
43
|
+
|
|
44
|
+
shellComponent: RootDocument,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
function AutoRefresh() {
|
|
48
|
+
useAutoRefresh()
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
53
|
+
const { progressData } = Route.useRouteContext()
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<html lang="en">
|
|
57
|
+
<head>
|
|
58
|
+
<HeadContent />
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<AutoRefresh />
|
|
62
|
+
<div className="flex h-screen bg-gray-950 text-gray-100">
|
|
63
|
+
<Sidebar />
|
|
64
|
+
<div className="flex-1 flex flex-col overflow-hidden">
|
|
65
|
+
<Header data={progressData} />
|
|
66
|
+
<main className="flex-1 overflow-auto">
|
|
67
|
+
{children}
|
|
68
|
+
</main>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<Scripts />
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { getFileWatcher } from '@/lib/file-watcher'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/api/watch')({
|
|
5
|
+
server: {
|
|
6
|
+
handlers: {
|
|
7
|
+
GET: async () => {
|
|
8
|
+
const watcher = getFileWatcher()
|
|
9
|
+
|
|
10
|
+
const stream = new ReadableStream({
|
|
11
|
+
start(controller) {
|
|
12
|
+
watcher.addClient(controller)
|
|
13
|
+
},
|
|
14
|
+
cancel(controller) {
|
|
15
|
+
watcher.removeClient(controller)
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return new Response(stream, {
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'text/event-stream',
|
|
22
|
+
'Cache-Control': 'no-cache',
|
|
23
|
+
'Connection': 'keep-alive',
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { StatusBadge } from '@/components/StatusBadge'
|
|
3
|
+
import { ProgressBar } from '@/components/ProgressBar'
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute('/')({
|
|
6
|
+
component: HomePage,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
function HomePage() {
|
|
10
|
+
const { progressData } = Route.useRouteContext()
|
|
11
|
+
|
|
12
|
+
if (!progressData) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="p-6">
|
|
15
|
+
<div className="p-4 bg-red-900/20 border border-red-800 rounded-lg">
|
|
16
|
+
<p className="text-red-400 font-medium">Failed to load progress data</p>
|
|
17
|
+
<p className="text-red-400/70 text-sm mt-1">
|
|
18
|
+
Check that progress.yaml exists and is valid YAML
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const data = progressData
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="p-6 space-y-6">
|
|
29
|
+
{/* Project Metadata */}
|
|
30
|
+
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5">
|
|
31
|
+
<div className="flex items-start justify-between">
|
|
32
|
+
<div>
|
|
33
|
+
<h2 className="text-xl font-bold">{data.project.name}</h2>
|
|
34
|
+
<p className="text-gray-400 text-sm mt-1">{data.project.description}</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="text-right">
|
|
37
|
+
<span className="text-xs text-gray-500 font-mono">v{data.project.version}</span>
|
|
38
|
+
<div className="mt-1">
|
|
39
|
+
<StatusBadge status={data.project.status} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div className="mt-4">
|
|
44
|
+
<div className="flex items-center gap-3">
|
|
45
|
+
<span className="text-xs text-gray-500">Overall Progress</span>
|
|
46
|
+
<div className="flex-1">
|
|
47
|
+
<ProgressBar value={data.progress.overall} />
|
|
48
|
+
</div>
|
|
49
|
+
<span className="text-sm font-mono text-gray-300">{data.progress.overall}%</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{/* Milestones Summary */}
|
|
55
|
+
<div>
|
|
56
|
+
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
|
|
57
|
+
Milestones ({data.milestones.length})
|
|
58
|
+
</h3>
|
|
59
|
+
<div className="space-y-2">
|
|
60
|
+
{data.milestones.map((m) => (
|
|
61
|
+
<div
|
|
62
|
+
key={m.id}
|
|
63
|
+
className="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3 flex items-center gap-4"
|
|
64
|
+
>
|
|
65
|
+
<span className="flex-1 text-sm font-medium">{m.name}</span>
|
|
66
|
+
<StatusBadge status={m.status} />
|
|
67
|
+
<div className="w-24">
|
|
68
|
+
<ProgressBar value={m.progress} size="sm" />
|
|
69
|
+
</div>
|
|
70
|
+
<span className="text-xs text-gray-500 font-mono w-16 text-right">
|
|
71
|
+
{m.tasks_completed}/{m.tasks_total}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
{data.milestones.length === 0 && (
|
|
76
|
+
<p className="text-gray-600 text-sm">No milestones defined</p>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Next Steps */}
|
|
82
|
+
{data.next_steps.length > 0 && (
|
|
83
|
+
<div>
|
|
84
|
+
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
|
|
85
|
+
Next Steps
|
|
86
|
+
</h3>
|
|
87
|
+
<ul className="space-y-1.5">
|
|
88
|
+
{data.next_steps.map((step, i) => (
|
|
89
|
+
<li key={i} className="text-sm text-gray-300 flex items-start gap-2">
|
|
90
|
+
<span className="text-gray-600 mt-0.5">•</span>
|
|
91
|
+
{step}
|
|
92
|
+
</li>
|
|
93
|
+
))}
|
|
94
|
+
</ul>
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{/* Blockers */}
|
|
99
|
+
{data.current_blockers.length > 0 && (
|
|
100
|
+
<div className="bg-red-900/10 border border-red-800/30 rounded-lg p-4">
|
|
101
|
+
<h3 className="text-sm font-semibold text-red-400 uppercase tracking-wider mb-2">
|
|
102
|
+
Blockers
|
|
103
|
+
</h3>
|
|
104
|
+
<ul className="space-y-1">
|
|
105
|
+
{data.current_blockers.map((blocker, i) => (
|
|
106
|
+
<li key={i} className="text-sm text-red-300">
|
|
107
|
+
{blocker}
|
|
108
|
+
</li>
|
|
109
|
+
))}
|
|
110
|
+
</ul>
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import { MilestoneTable } from '@/components/MilestoneTable'
|
|
4
|
+
import { MilestoneTree } from '@/components/MilestoneTree'
|
|
5
|
+
import { ViewToggle } from '@/components/ViewToggle'
|
|
6
|
+
import { FilterBar } from '@/components/FilterBar'
|
|
7
|
+
import { SearchInput } from '@/components/SearchInput'
|
|
8
|
+
import { useFilteredData } from '@/lib/useFilteredData'
|
|
9
|
+
import type { Status } from '@/lib/types'
|
|
10
|
+
|
|
11
|
+
export const Route = createFileRoute('/milestones')({
|
|
12
|
+
component: MilestonesPage,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
function MilestonesPage() {
|
|
16
|
+
const { progressData } = Route.useRouteContext()
|
|
17
|
+
const [view, setView] = useState<'table' | 'tree'>('table')
|
|
18
|
+
const [status, setStatus] = useState<Status | 'all'>('all')
|
|
19
|
+
const [search, setSearch] = useState('')
|
|
20
|
+
|
|
21
|
+
const filtered = useFilteredData(progressData, { status, search })
|
|
22
|
+
|
|
23
|
+
if (!filtered) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="p-6">
|
|
26
|
+
<p className="text-gray-600 text-sm">No data loaded</p>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="p-6">
|
|
33
|
+
<div className="flex items-center justify-between mb-4">
|
|
34
|
+
<h2 className="text-lg font-semibold">Milestones</h2>
|
|
35
|
+
<ViewToggle value={view} onChange={setView} />
|
|
36
|
+
</div>
|
|
37
|
+
<div className="flex items-center gap-3 mb-4">
|
|
38
|
+
<FilterBar status={status} onStatusChange={setStatus} />
|
|
39
|
+
<div className="w-64">
|
|
40
|
+
<SearchInput value={search} onChange={setSearch} placeholder="Filter milestones..." />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
{view === 'table' ? (
|
|
44
|
+
<MilestoneTable milestones={filtered.milestones} tasks={filtered.tasks} />
|
|
45
|
+
) : (
|
|
46
|
+
<MilestoneTree milestones={filtered.milestones} tasks={filtered.tasks} />
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useState, useMemo } from 'react'
|
|
3
|
+
import { SearchInput } from '@/components/SearchInput'
|
|
4
|
+
import { StatusBadge } from '@/components/StatusBadge'
|
|
5
|
+
import { StatusDot } from '@/components/StatusDot'
|
|
6
|
+
import { buildSearchIndex } from '@/lib/search'
|
|
7
|
+
|
|
8
|
+
export const Route = createFileRoute('/search')({
|
|
9
|
+
component: SearchPage,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
function SearchPage() {
|
|
13
|
+
const { progressData } = Route.useRouteContext()
|
|
14
|
+
const [query, setQuery] = useState('')
|
|
15
|
+
|
|
16
|
+
const results = useMemo(() => {
|
|
17
|
+
if (!progressData || !query.trim()) return []
|
|
18
|
+
const index = buildSearchIndex(progressData)
|
|
19
|
+
return index.search(query).slice(0, 20)
|
|
20
|
+
}, [progressData, query])
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="p-6">
|
|
24
|
+
<h2 className="text-lg font-semibold mb-4">Search</h2>
|
|
25
|
+
<div className="max-w-md mb-6">
|
|
26
|
+
<SearchInput
|
|
27
|
+
value={query}
|
|
28
|
+
onChange={setQuery}
|
|
29
|
+
placeholder="Search milestones and tasks..."
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{query.trim() && results.length === 0 && (
|
|
34
|
+
<p className="text-gray-500 text-sm">
|
|
35
|
+
No results for "{query}"
|
|
36
|
+
</p>
|
|
37
|
+
)}
|
|
38
|
+
|
|
39
|
+
{results.length > 0 && (
|
|
40
|
+
<div className="space-y-2">
|
|
41
|
+
<p className="text-xs text-gray-500 mb-3">
|
|
42
|
+
{results.length} result{results.length !== 1 ? 's' : ''}
|
|
43
|
+
</p>
|
|
44
|
+
{results.map((result, i) => (
|
|
45
|
+
<div
|
|
46
|
+
key={i}
|
|
47
|
+
className="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3"
|
|
48
|
+
>
|
|
49
|
+
<div className="flex items-center gap-3">
|
|
50
|
+
{result.item.type === 'task' && result.item.task ? (
|
|
51
|
+
<>
|
|
52
|
+
<StatusDot status={result.item.task.status} />
|
|
53
|
+
<span className="text-sm">{result.item.task.name}</span>
|
|
54
|
+
<span className="text-xs text-gray-600 ml-auto">
|
|
55
|
+
{result.item.milestone.name}
|
|
56
|
+
</span>
|
|
57
|
+
</>
|
|
58
|
+
) : (
|
|
59
|
+
<>
|
|
60
|
+
<span className="text-sm font-medium">
|
|
61
|
+
{result.item.milestone.name}
|
|
62
|
+
</span>
|
|
63
|
+
<StatusBadge status={result.item.milestone.status} />
|
|
64
|
+
</>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
{result.item.notes && (
|
|
68
|
+
<p className="text-xs text-gray-500 mt-1 truncate">
|
|
69
|
+
{result.item.notes}
|
|
70
|
+
</p>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{!query.trim() && (
|
|
78
|
+
<p className="text-gray-600 text-sm">
|
|
79
|
+
Type to search across milestones and tasks
|
|
80
|
+
</p>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { StatusDot } from '@/components/StatusDot'
|
|
3
|
+
import { ExtraFieldsBadge } from '@/components/ExtraFieldsBadge'
|
|
4
|
+
import type { Task } from '@/lib/types'
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute('/tasks')({
|
|
7
|
+
component: TasksPage,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function TasksPage() {
|
|
11
|
+
const { progressData } = Route.useRouteContext()
|
|
12
|
+
|
|
13
|
+
if (!progressData) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="p-6">
|
|
16
|
+
<p className="text-gray-600 text-sm">No data loaded</p>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const allTasks: Array<Task & { milestoneName: string }> = []
|
|
22
|
+
for (const milestone of progressData.milestones) {
|
|
23
|
+
const tasks = progressData.tasks[milestone.id] || []
|
|
24
|
+
for (const task of tasks) {
|
|
25
|
+
allTasks.push({ ...task, milestoneName: milestone.name })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="p-6">
|
|
31
|
+
<h2 className="text-lg font-semibold mb-4">
|
|
32
|
+
All Tasks ({allTasks.length})
|
|
33
|
+
</h2>
|
|
34
|
+
<div className="border border-gray-800 rounded-lg overflow-hidden">
|
|
35
|
+
{allTasks.map((task) => (
|
|
36
|
+
<div
|
|
37
|
+
key={task.id}
|
|
38
|
+
className="flex items-center gap-3 px-4 py-2.5 border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors"
|
|
39
|
+
>
|
|
40
|
+
<StatusDot status={task.status} />
|
|
41
|
+
<span
|
|
42
|
+
className={`flex-1 text-sm ${
|
|
43
|
+
task.status === 'completed' ? 'text-gray-500' : 'text-gray-200'
|
|
44
|
+
}`}
|
|
45
|
+
>
|
|
46
|
+
{task.name}
|
|
47
|
+
</span>
|
|
48
|
+
<span className="text-xs text-gray-600">{task.milestoneName}</span>
|
|
49
|
+
<span className="text-xs text-gray-500 font-mono w-8 text-right">
|
|
50
|
+
{task.estimated_hours}h
|
|
51
|
+
</span>
|
|
52
|
+
<ExtraFieldsBadge fields={task.extra} />
|
|
53
|
+
</div>
|
|
54
|
+
))}
|
|
55
|
+
{allTasks.length === 0 && (
|
|
56
|
+
<div className="px-4 py-6 text-center">
|
|
57
|
+
<p className="text-gray-600 text-sm">No tasks defined</p>
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
import { parseProgressYaml } from '@/lib/yaml-loader'
|
|
3
|
+
import { getProgressYamlPath } from '@/lib/config'
|
|
4
|
+
import type { ProgressData } from '@/lib/types'
|
|
5
|
+
|
|
6
|
+
export type ProgressResult =
|
|
7
|
+
| { ok: true; data: ProgressData }
|
|
8
|
+
| { ok: false; error: 'FILE_NOT_FOUND' | 'PARSE_ERROR'; message: string; path: string }
|
|
9
|
+
|
|
10
|
+
export class ProgressDatabaseService {
|
|
11
|
+
static getProgressData(): ProgressResult {
|
|
12
|
+
const filePath = getProgressYamlPath()
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(filePath, 'utf-8')
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const data = parseProgressYaml(raw)
|
|
19
|
+
return { ok: true, data }
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
error: 'PARSE_ERROR',
|
|
24
|
+
message: err instanceof Error ? err.message : 'Failed to parse YAML',
|
|
25
|
+
path: filePath,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
30
|
+
if (code === 'ENOENT') {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
error: 'FILE_NOT_FOUND',
|
|
34
|
+
message: `progress.yaml not found at: ${filePath}`,
|
|
35
|
+
path: filePath,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: 'PARSE_ERROR',
|
|
41
|
+
message: err instanceof Error ? err.message : 'Failed to read file',
|
|
42
|
+
path: filePath,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
html {
|
|
4
|
+
@apply bg-gray-950;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
@apply m-0 bg-gray-950 min-h-screen text-gray-100;
|
|
9
|
+
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
|
10
|
+
-webkit-font-smoothing: antialiased;
|
|
11
|
+
-moz-osx-font-smoothing: grayscale;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
code {
|
|
15
|
+
font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Hide scrollbar while keeping scroll functionality */
|
|
19
|
+
.scrollbar-hide {
|
|
20
|
+
-ms-overflow-style: none;
|
|
21
|
+
scrollbar-width: none;
|
|
22
|
+
}
|
|
23
|
+
.scrollbar-hide::-webkit-scrollbar {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
8
|
+
"types": ["vite/client"],
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": false,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedSideEffectImports": true,
|
|
19
|
+
"baseUrl": ".",
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": ["./src/*"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
3
|
+
import viteReact from '@vitejs/plugin-react'
|
|
4
|
+
import viteTsConfigPaths from 'vite-tsconfig-paths'
|
|
5
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
viteTsConfigPaths({
|
|
10
|
+
projects: ['./tsconfig.json'],
|
|
11
|
+
}),
|
|
12
|
+
tailwindcss(),
|
|
13
|
+
tanstackStart(),
|
|
14
|
+
viteReact(),
|
|
15
|
+
],
|
|
16
|
+
})
|