@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,288 @@
|
|
|
1
|
+
# Dashboard Layout & Routing
|
|
2
|
+
|
|
3
|
+
**Concept**: Root layout structure, navigation, routing, and visual design system for the admin dashboard
|
|
4
|
+
**Created**: 2026-03-14
|
|
5
|
+
**Status**: Design Specification
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Defines the application shell — root layout, sidebar navigation, header, routing structure, and the visual design tokens (colors, typography, spacing) that create a cohesive Linear/Vercel-style admin dashboard. This design establishes the container that all view components render within.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Problem Statement
|
|
16
|
+
|
|
17
|
+
The visualizer needs:
|
|
18
|
+
- A persistent navigation structure for switching between views (dashboard, milestones, tasks, search)
|
|
19
|
+
- Consistent visual language across all pages
|
|
20
|
+
- Information-dense layout optimized for desktop developer use
|
|
21
|
+
- Clear visual hierarchy for status indicators (completed/in-progress/not-started)
|
|
22
|
+
|
|
23
|
+
Without a defined layout system, views would be implemented ad-hoc with inconsistent spacing, typography, and navigation patterns.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Solution
|
|
28
|
+
|
|
29
|
+
A minimal sidebar + content area layout with:
|
|
30
|
+
1. Fixed left sidebar for navigation
|
|
31
|
+
2. Content area with page-specific views
|
|
32
|
+
3. Shared design tokens via Tailwind CSS configuration
|
|
33
|
+
4. TanStack Router file-based routing
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Implementation
|
|
38
|
+
|
|
39
|
+
### Route Structure
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
app/routes/
|
|
43
|
+
├── __root.tsx # Root layout (sidebar + content shell)
|
|
44
|
+
├── index.tsx # Dashboard home (project overview + summary)
|
|
45
|
+
├── milestones.tsx # Milestone views (table/tree toggle)
|
|
46
|
+
├── milestones.$id.tsx # Single milestone detail (tasks list)
|
|
47
|
+
├── tasks.tsx # All tasks view
|
|
48
|
+
├── activity.tsx # Recent work timeline (P1)
|
|
49
|
+
└── search.tsx # Global search results
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Root Layout
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// app/routes/__root.tsx
|
|
56
|
+
|
|
57
|
+
export const Route = createRootRoute({
|
|
58
|
+
component: RootLayout,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function RootLayout() {
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex h-screen bg-gray-950 text-gray-100">
|
|
64
|
+
<Sidebar />
|
|
65
|
+
<main className="flex-1 overflow-auto">
|
|
66
|
+
<Header />
|
|
67
|
+
<div className="p-6">
|
|
68
|
+
<Outlet />
|
|
69
|
+
</div>
|
|
70
|
+
</main>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Sidebar
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// app/components/Sidebar.tsx
|
|
80
|
+
|
|
81
|
+
const navItems = [
|
|
82
|
+
{ to: '/', icon: LayoutDashboard, label: 'Overview' },
|
|
83
|
+
{ to: '/milestones', icon: Flag, label: 'Milestones' },
|
|
84
|
+
{ to: '/tasks', icon: CheckSquare, label: 'Tasks' },
|
|
85
|
+
{ to: '/activity', icon: Clock, label: 'Activity' }, // P1
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
function Sidebar() {
|
|
89
|
+
return (
|
|
90
|
+
<nav className="w-56 border-r border-gray-800 bg-gray-950 flex flex-col">
|
|
91
|
+
<div className="p-4 border-b border-gray-800">
|
|
92
|
+
<span className="text-sm font-semibold text-gray-300 tracking-wide">
|
|
93
|
+
ACP Visualizer
|
|
94
|
+
</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="flex-1 py-2">
|
|
97
|
+
{navItems.map(item => (
|
|
98
|
+
<NavLink key={item.to} {...item} />
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
<div className="p-4 border-t border-gray-800">
|
|
102
|
+
<SearchTrigger />
|
|
103
|
+
</div>
|
|
104
|
+
</nav>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Header
|
|
110
|
+
|
|
111
|
+
Displays current project name, version, status badge, and overall progress:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
// app/components/Header.tsx
|
|
115
|
+
|
|
116
|
+
function Header() {
|
|
117
|
+
// Project metadata from route loader
|
|
118
|
+
return (
|
|
119
|
+
<header className="h-14 border-b border-gray-800 flex items-center px-6 gap-4">
|
|
120
|
+
<h1 className="text-sm font-medium text-gray-200">{project.name}</h1>
|
|
121
|
+
<span className="text-xs text-gray-500">v{project.version}</span>
|
|
122
|
+
<StatusBadge status={project.status} />
|
|
123
|
+
<div className="ml-auto">
|
|
124
|
+
<ProgressBar value={progress.overall} size="sm" />
|
|
125
|
+
</div>
|
|
126
|
+
</header>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Design Tokens (Tailwind Config)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// tailwind.config.ts
|
|
135
|
+
|
|
136
|
+
export default {
|
|
137
|
+
theme: {
|
|
138
|
+
extend: {
|
|
139
|
+
colors: {
|
|
140
|
+
status: {
|
|
141
|
+
completed: '#22c55e', // green-500
|
|
142
|
+
in_progress: '#3b82f6', // blue-500
|
|
143
|
+
not_started: '#6b7280', // gray-500
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
fontFamily: {
|
|
147
|
+
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
|
|
148
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Visual Design Rules
|
|
156
|
+
|
|
157
|
+
| Element | Style |
|
|
158
|
+
|---------|-------|
|
|
159
|
+
| Background | `gray-950` (near-black) |
|
|
160
|
+
| Surface/cards | `gray-900` with `gray-800` borders |
|
|
161
|
+
| Primary text | `gray-100` |
|
|
162
|
+
| Secondary text | `gray-400` |
|
|
163
|
+
| Data values | Monospace font (`font-mono`) |
|
|
164
|
+
| Status: completed | Green badge/dot |
|
|
165
|
+
| Status: in_progress | Blue badge/dot |
|
|
166
|
+
| Status: not_started | Gray badge/dot |
|
|
167
|
+
| Interactive hover | `gray-800` background |
|
|
168
|
+
| Spacing | 4px grid (Tailwind default) |
|
|
169
|
+
|
|
170
|
+
### StatusBadge Component
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// app/components/StatusBadge.tsx
|
|
174
|
+
|
|
175
|
+
const statusStyles = {
|
|
176
|
+
completed: 'bg-green-500/15 text-green-400 border-green-500/20',
|
|
177
|
+
in_progress: 'bg-blue-500/15 text-blue-400 border-blue-500/20',
|
|
178
|
+
not_started: 'bg-gray-500/15 text-gray-500 border-gray-500/20',
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const statusLabels = {
|
|
182
|
+
completed: 'Completed',
|
|
183
|
+
in_progress: 'In Progress',
|
|
184
|
+
not_started: 'Not Started',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export function StatusBadge({ status }: { status: Status }) {
|
|
188
|
+
return (
|
|
189
|
+
<span className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs border ${statusStyles[status]}`}>
|
|
190
|
+
{statusLabels[status]}
|
|
191
|
+
</span>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### ProgressBar Component
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
// app/components/ProgressBar.tsx
|
|
200
|
+
|
|
201
|
+
export function ProgressBar({ value, size = 'md' }: { value: number; size?: 'sm' | 'md' }) {
|
|
202
|
+
const height = size === 'sm' ? 'h-1.5' : 'h-2.5';
|
|
203
|
+
return (
|
|
204
|
+
<div className={`w-full bg-gray-800 rounded-full ${height} overflow-hidden`}>
|
|
205
|
+
<div
|
|
206
|
+
className={`${height} rounded-full transition-all duration-300 ${
|
|
207
|
+
value === 100 ? 'bg-green-500' : 'bg-blue-500'
|
|
208
|
+
}`}
|
|
209
|
+
style={{ width: `${Math.min(100, Math.max(0, value))}%` }}
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Dashboard Home (Overview Page)
|
|
217
|
+
|
|
218
|
+
The index route shows a single-page summary:
|
|
219
|
+
- Project metadata card (name, version, status, dates)
|
|
220
|
+
- Overall progress bar
|
|
221
|
+
- Milestone summary (compact list with progress bars)
|
|
222
|
+
- Next steps list
|
|
223
|
+
- Current blockers (if any)
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Benefits
|
|
228
|
+
|
|
229
|
+
- **Consistent**: All pages share the same visual language
|
|
230
|
+
- **Information-dense**: Compact layout maximizes data visibility
|
|
231
|
+
- **Navigable**: Sidebar provides constant orientation
|
|
232
|
+
- **Extensible**: Adding new routes/views follows established patterns
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Trade-offs
|
|
237
|
+
|
|
238
|
+
- **Desktop-only**: Fixed sidebar layout doesn't collapse for mobile (acceptable per requirements)
|
|
239
|
+
- **Dark theme only**: No light mode toggle in P0 (future consideration)
|
|
240
|
+
- **Minimal branding**: Intentionally plain — data is the focus
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Applicable Patterns
|
|
245
|
+
|
|
246
|
+
| Pattern | How It Applies |
|
|
247
|
+
|---------|----------------|
|
|
248
|
+
| [`tanstack-cloudflare.unified-header`](../patterns/tanstack-cloudflare.unified-header.md) | Adopt the fixed header + content offset pattern. Use `pt-14` on main content. For view switching (table/tree), use `SubHeaderTabs` rendered as children of the header. Adapt for sidebar layout: the pattern's `max-w-3xl` centering shifts to sidebar + full-width content area, but the fixed positioning, safe-area handling, and tab structure apply directly. |
|
|
249
|
+
| [`tanstack-cloudflare.nextjs-to-tanstack-routing`](../patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md) | Reference for TanStack Router file-based routing conventions: `$id` for dynamic params, `__root.tsx` for layout, `beforeLoad` for data, `meta()` for page metadata. Ensures routes follow established patterns. |
|
|
250
|
+
| [`tanstack-cloudflare.card-and-list`](../patterns/tanstack-cloudflare.card-and-list.md) | Adopt consistent card styling for dashboard summary cards: `bg-gray-900/50 backdrop-blur-sm border border-gray-800 rounded-xl p-4`. Text hierarchy: title `text-white font-semibold`, secondary `text-gray-400 text-sm`, meta `text-gray-500 text-xs`. |
|
|
251
|
+
| [`tanstack-cloudflare.toast-system`](../patterns/tanstack-cloudflare.toast-system.md) | Use for error feedback (YAML parse errors, file-not-found) and auto-refresh status notifications. `useToast()` for direct calls, `StandaloneToastContainer` at z-60 in root layout. |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Dependencies
|
|
256
|
+
|
|
257
|
+
- Tailwind CSS
|
|
258
|
+
- TanStack Router (file-based routing)
|
|
259
|
+
- lucide-react for icons (lightweight, tree-shakeable)
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Testing Strategy
|
|
264
|
+
|
|
265
|
+
- **Component tests**: Sidebar renders correct nav items, StatusBadge renders correct colors
|
|
266
|
+
- **Visual regression**: Screenshot tests for layout at standard viewport sizes
|
|
267
|
+
- **Route tests**: Each route resolves and renders without errors
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Migration Path
|
|
272
|
+
|
|
273
|
+
N/A — greenfield project.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Future Considerations
|
|
278
|
+
|
|
279
|
+
- **Dark/light mode toggle**: Add theme context and Tailwind dark mode classes
|
|
280
|
+
- **Collapsible sidebar**: Icon-only mode for more content space
|
|
281
|
+
- **Breadcrumbs**: For nested routes (milestone → task detail)
|
|
282
|
+
- **Keyboard navigation**: `Cmd+K` for search, arrow keys for nav
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
**Status**: Design Specification
|
|
287
|
+
**Recommendation**: Implement alongside or immediately after server API — provides the shell for all views
|
|
288
|
+
**Related Documents**: local.visualizer-requirements.md, tanstack-cloudflare.unified-header, tanstack-cloudflare.nextjs-to-tanstack-routing, tanstack-cloudflare.card-and-list, tanstack-cloudflare.toast-system
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Data Model & YAML Parsing
|
|
2
|
+
|
|
3
|
+
**Concept**: TypeScript data model for progress.yaml and YAML-to-structured-data parsing pipeline
|
|
4
|
+
**Created**: 2026-03-14
|
|
5
|
+
**Status**: Design Specification
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Defines the TypeScript interfaces that represent ACP `progress.yaml` data and the parsing pipeline that converts raw YAML into typed, normalized structures suitable for rendering in the visualizer's views. This is the foundational data layer that all views, search, and filtering depend on.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Problem Statement
|
|
16
|
+
|
|
17
|
+
ACP `progress.yaml` files are large (1800+ lines), loosely structured YAML **maintained by AI agents, not programmatic YAML operations**. This means:
|
|
18
|
+
- Fields may be missing, renamed, or added ad-hoc by different agents
|
|
19
|
+
- Unexpected/custom properties may appear at any level (e.g., an agent adds `priority: high` to a task)
|
|
20
|
+
- Key names may drift slightly (e.g., `estimated_hours` vs `est_hours` vs `hours`)
|
|
21
|
+
- Structural variations exist across projects (arrays vs objects, nested vs flat)
|
|
22
|
+
|
|
23
|
+
The visualizer needs:
|
|
24
|
+
- Strong TypeScript types for compile-time safety across all views
|
|
25
|
+
- **Lenient parsing** that extracts known fields without crashing on unknown ones
|
|
26
|
+
- **Passthrough of unrecognized properties** so they can be displayed in a "raw data" fallback
|
|
27
|
+
- Normalization of optional/missing fields to prevent runtime errors
|
|
28
|
+
- Consistent ID generation for entities that don't have explicit IDs in YAML
|
|
29
|
+
- Computed fields (e.g., task counts, completion percentages) derived from raw data
|
|
30
|
+
|
|
31
|
+
Without a well-defined but flexible data model, the visualizer would either crash on real-world YAML or silently lose agent-added context.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Solution
|
|
36
|
+
|
|
37
|
+
Create a `yaml-loader.ts` module in `app/lib/` that:
|
|
38
|
+
|
|
39
|
+
1. Accepts raw YAML string input
|
|
40
|
+
2. Parses via `js-yaml` into untyped JavaScript objects
|
|
41
|
+
3. Validates and normalizes into strongly-typed TypeScript interfaces
|
|
42
|
+
4. Computes derived fields (progress percentages, task counts)
|
|
43
|
+
5. Returns a single `ProgressData` object consumed by all views
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Implementation
|
|
48
|
+
|
|
49
|
+
### TypeScript Interfaces
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// app/lib/types.ts
|
|
53
|
+
|
|
54
|
+
export type Status = 'completed' | 'in_progress' | 'not_started';
|
|
55
|
+
|
|
56
|
+
export interface ProgressData {
|
|
57
|
+
project: ProjectMetadata;
|
|
58
|
+
milestones: Milestone[];
|
|
59
|
+
tasks: Record<string, Task[]>; // keyed by milestone_id
|
|
60
|
+
recent_work: WorkEntry[];
|
|
61
|
+
next_steps: string[];
|
|
62
|
+
notes: string[];
|
|
63
|
+
current_blockers: string[];
|
|
64
|
+
documentation: DocumentationStats;
|
|
65
|
+
progress: ProgressSummary;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Unknown properties from agent-maintained YAML are preserved here
|
|
69
|
+
// so views can display them in raw/debug panels without data loss.
|
|
70
|
+
export type ExtraFields = Record<string, unknown>;
|
|
71
|
+
|
|
72
|
+
export interface ProjectMetadata {
|
|
73
|
+
name: string;
|
|
74
|
+
version: string;
|
|
75
|
+
started: string;
|
|
76
|
+
status: Status;
|
|
77
|
+
current_milestone?: string;
|
|
78
|
+
description: string;
|
|
79
|
+
extra: ExtraFields; // any unrecognized project-level fields
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface Milestone {
|
|
83
|
+
id: string;
|
|
84
|
+
name: string;
|
|
85
|
+
status: Status;
|
|
86
|
+
progress: number; // 0-100
|
|
87
|
+
started: string | null;
|
|
88
|
+
completed: string | null;
|
|
89
|
+
estimated_weeks: string;
|
|
90
|
+
tasks_completed: number;
|
|
91
|
+
tasks_total: number;
|
|
92
|
+
notes: string;
|
|
93
|
+
extra: ExtraFields;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface Task {
|
|
97
|
+
id: string;
|
|
98
|
+
name: string;
|
|
99
|
+
status: Status;
|
|
100
|
+
milestone_id: string;
|
|
101
|
+
file: string;
|
|
102
|
+
estimated_hours: string;
|
|
103
|
+
completed_date: string | null;
|
|
104
|
+
notes: string;
|
|
105
|
+
extra: ExtraFields;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface WorkEntry {
|
|
109
|
+
date: string;
|
|
110
|
+
description: string;
|
|
111
|
+
items: string[];
|
|
112
|
+
extra: ExtraFields;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DocumentationStats {
|
|
116
|
+
design_documents: number;
|
|
117
|
+
milestone_documents: number;
|
|
118
|
+
pattern_documents: number;
|
|
119
|
+
task_documents: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ProgressSummary {
|
|
123
|
+
planning: number;
|
|
124
|
+
implementation: number;
|
|
125
|
+
overall: number;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### YAML Parsing & Normalization
|
|
130
|
+
|
|
131
|
+
The parser follows a "extract known, preserve unknown" strategy:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// app/lib/yaml-loader.ts
|
|
135
|
+
|
|
136
|
+
import yaml from 'js-yaml';
|
|
137
|
+
import type { ProgressData, Milestone, Task, Status, ExtraFields } from './types';
|
|
138
|
+
|
|
139
|
+
export function parseProgressYaml(raw: string): ProgressData {
|
|
140
|
+
const doc = yaml.load(raw) as Record<string, unknown>;
|
|
141
|
+
|
|
142
|
+
const project = normalizeProject(doc.project);
|
|
143
|
+
const milestones = normalizeMilestones(doc.milestones);
|
|
144
|
+
const tasks = normalizeTasks(doc.tasks, milestones);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
project,
|
|
148
|
+
milestones,
|
|
149
|
+
tasks,
|
|
150
|
+
recent_work: normalizeWorkEntries(doc.recent_work),
|
|
151
|
+
next_steps: normalizeStringArray(doc.next_steps),
|
|
152
|
+
notes: normalizeStringArray(doc.notes),
|
|
153
|
+
current_blockers: normalizeStringArray(doc.current_blockers),
|
|
154
|
+
documentation: normalizeDocStats(doc.documentation),
|
|
155
|
+
progress: normalizeProgress(doc.progress),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- Core extraction helpers ---
|
|
160
|
+
|
|
161
|
+
/** Extract known keys from an object, return the rest as ExtraFields */
|
|
162
|
+
function extractKnown<T extends Record<string, unknown>>(
|
|
163
|
+
obj: Record<string, unknown>,
|
|
164
|
+
knownKeys: string[],
|
|
165
|
+
): { known: Partial<T>; extra: ExtraFields } {
|
|
166
|
+
const known: Record<string, unknown> = {};
|
|
167
|
+
const extra: ExtraFields = {};
|
|
168
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
169
|
+
if (knownKeys.includes(key)) {
|
|
170
|
+
known[key] = value;
|
|
171
|
+
} else {
|
|
172
|
+
extra[key] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { known: known as Partial<T>, extra };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function normalizeStatus(value: unknown): Status {
|
|
179
|
+
const s = String(value || 'not_started')
|
|
180
|
+
.toLowerCase()
|
|
181
|
+
.replace(/[\s-]/g, '_'); // handle "in progress", "in-progress"
|
|
182
|
+
if (s === 'completed' || s === 'done' || s === 'complete') return 'completed';
|
|
183
|
+
if (s === 'in_progress' || s === 'active' || s === 'wip') return 'in_progress';
|
|
184
|
+
return 'not_started';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeStringArray(value: unknown): string[] {
|
|
188
|
+
if (!value) return [];
|
|
189
|
+
if (typeof value === 'string') return [value]; // agent wrote a single string instead of array
|
|
190
|
+
if (!Array.isArray(value)) return [];
|
|
191
|
+
return value.map(String);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function safeString(value: unknown, fallback = ''): string {
|
|
195
|
+
if (value == null) return fallback;
|
|
196
|
+
return String(value);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function safeNumber(value: unknown, fallback = 0): number {
|
|
200
|
+
const n = Number(value);
|
|
201
|
+
return Number.isFinite(n) ? n : fallback;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Handling Agent-Maintained YAML Drift
|
|
206
|
+
|
|
207
|
+
Since progress.yaml files are written and updated by AI agents (not schema-validated tooling), the parser must handle common drift patterns:
|
|
208
|
+
|
|
209
|
+
| Drift Pattern | Example | Handling |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| Unknown properties | `priority: high` on a task | Preserved in `extra` field, available for display |
|
|
212
|
+
| Variant key names | `est_hours` instead of `estimated_hours` | Alias map checked during normalization |
|
|
213
|
+
| Status value variants | `"done"`, `"active"`, `"wip"` | Fuzzy-matched to canonical Status enum |
|
|
214
|
+
| String-for-array | `notes: "some note"` instead of `notes: ["some note"]` | Wrapped in array automatically |
|
|
215
|
+
| Missing sections | No `milestones` key at all | Defaults to empty array |
|
|
216
|
+
| Unexpected nesting | Tasks as nested objects vs flat arrays | Both structures accepted |
|
|
217
|
+
| Extra top-level keys | `custom_metrics:` section | Ignored gracefully (not in ProgressData but no crash) |
|
|
218
|
+
|
|
219
|
+
**Key alias map** (checked in normalizers):
|
|
220
|
+
```typescript
|
|
221
|
+
const TASK_ALIASES: Record<string, string> = {
|
|
222
|
+
est_hours: 'estimated_hours',
|
|
223
|
+
hours: 'estimated_hours',
|
|
224
|
+
estimate: 'estimated_hours',
|
|
225
|
+
completed: 'completed_date',
|
|
226
|
+
done_date: 'completed_date',
|
|
227
|
+
filename: 'file',
|
|
228
|
+
path: 'file',
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Computed Fields
|
|
233
|
+
|
|
234
|
+
The parser computes:
|
|
235
|
+
- `milestone.progress` from task completion ratios when not explicitly set
|
|
236
|
+
- `milestone.tasks_completed` / `tasks_total` from the tasks map
|
|
237
|
+
- `progress.overall` from milestone-level progress if not set
|
|
238
|
+
|
|
239
|
+
### Error Handling
|
|
240
|
+
|
|
241
|
+
- Missing top-level keys default to empty arrays/objects
|
|
242
|
+
- Invalid status values fuzzy-match to nearest canonical value, fallback to `'not_started'`
|
|
243
|
+
- Missing dates normalize to `null`
|
|
244
|
+
- Numeric fields default to `0`
|
|
245
|
+
- Unknown properties are preserved in `extra` fields, never discarded silently
|
|
246
|
+
- Entire parse is wrapped in try/catch — on total failure, returns a minimal ProgressData with error metadata
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Benefits
|
|
251
|
+
|
|
252
|
+
- **Type safety**: All view components get compile-time guarantees
|
|
253
|
+
- **Single source of truth**: One parsing function, consistent across all views
|
|
254
|
+
- **Defensive**: Gracefully handles incomplete or malformed YAML
|
|
255
|
+
- **Testable**: Pure function with no side effects
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Trade-offs
|
|
260
|
+
|
|
261
|
+
- **Extra fields add payload**: Preserving unknown properties increases data size, but keeps agent context available for display
|
|
262
|
+
- **Alias map maintenance**: New agent drift patterns may require adding aliases over time
|
|
263
|
+
- **Upfront parsing cost**: Entire file is parsed on each load (mitigated by small file sizes, typically <100KB)
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Applicable Patterns
|
|
268
|
+
|
|
269
|
+
| Pattern | How It Applies |
|
|
270
|
+
|---------|----------------|
|
|
271
|
+
| [`tanstack-cloudflare.zod-schema-validation`](../patterns/tanstack-cloudflare.zod-schema-validation.md) | Defines how to structure schemas as single source of truth. Use `z.infer<>` for derived types. For this project: Zod schemas could optionally validate known fields via `safeParse` while the `extractKnown` helper preserves extras — a hybrid approach that gets runtime validation on known fields without rejecting agent drift. Place schemas in `app/lib/schemas/`. |
|
|
272
|
+
| [`tanstack-cloudflare.library-services`](../patterns/tanstack-cloudflare.library-services.md) | YAML parsing should be encapsulated in a service class (`ProgressDataService`) rather than called directly from components or routes. The service provides `getProgressData()` which handles file reading, parsing, normalization, and error handling. Components never import `yaml-loader.ts` directly. |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Dependencies
|
|
277
|
+
|
|
278
|
+
- `js-yaml` — YAML parsing library
|
|
279
|
+
- `zod` — optional runtime validation for known fields (see Applicable Patterns)
|
|
280
|
+
- No other external dependencies
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Testing Strategy
|
|
285
|
+
|
|
286
|
+
- **Unit tests**: Parse sample progress.yaml files with various completeness levels
|
|
287
|
+
- **Drift tests**: YAML with unknown fields, variant key names, status aliases — verify `extra` preservation and alias resolution
|
|
288
|
+
- **Edge cases**: Empty milestones array, missing tasks key, null dates, unknown status values, string-for-array, completely empty file
|
|
289
|
+
- **Snapshot tests**: Known YAML input → expected ProgressData output
|
|
290
|
+
- **Real-world tests**: Parse actual progress.yaml files from ACP core and other projects to catch unexpected structures
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Migration Path
|
|
295
|
+
|
|
296
|
+
N/A — greenfield project.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Future Considerations
|
|
301
|
+
|
|
302
|
+
- Zod schema validation for runtime type checking (could replace manual normalization)
|
|
303
|
+
- Support for progress.yaml schema versioning if ACP introduces breaking changes
|
|
304
|
+
- Streaming parser for extremely large files (unlikely to be needed)
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
**Status**: Design Specification
|
|
309
|
+
**Recommendation**: Implement as first task — all other features depend on this
|
|
310
|
+
**Related Documents**: local.visualizer-requirements.md, tanstack-cloudflare.zod-schema-validation, tanstack-cloudflare.library-services
|