@hustle-together/api-dev-tools 3.6.4 → 3.9.2
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 +5307 -258
- package/bin/cli.js +348 -20
- package/commands/README.md +459 -71
- package/commands/hustle-api-continue.md +158 -0
- package/commands/{api-create.md → hustle-api-create.md} +22 -2
- package/commands/{api-env.md → hustle-api-env.md} +4 -4
- package/commands/{api-interview.md → hustle-api-interview.md} +1 -1
- package/commands/{api-research.md → hustle-api-research.md} +3 -3
- package/commands/hustle-api-sessions.md +149 -0
- package/commands/{api-status.md → hustle-api-status.md} +16 -16
- package/commands/{api-verify.md → hustle-api-verify.md} +2 -2
- package/commands/hustle-combine.md +763 -0
- package/commands/hustle-ui-create.md +825 -0
- package/hooks/api-workflow-check.py +385 -19
- package/hooks/cache-research.py +337 -0
- package/hooks/check-playwright-setup.py +103 -0
- package/hooks/check-storybook-setup.py +81 -0
- package/hooks/detect-interruption.py +165 -0
- package/hooks/enforce-brand-guide.py +131 -0
- package/hooks/enforce-documentation.py +60 -8
- package/hooks/enforce-freshness.py +184 -0
- package/hooks/enforce-questions-sourced.py +146 -0
- package/hooks/enforce-schema-from-interview.py +248 -0
- package/hooks/enforce-ui-disambiguation.py +108 -0
- package/hooks/enforce-ui-interview.py +130 -0
- package/hooks/generate-manifest-entry.py +981 -0
- package/hooks/session-logger.py +297 -0
- package/hooks/session-startup.py +65 -10
- package/hooks/track-scope-coverage.py +220 -0
- package/hooks/track-tool-use.py +81 -1
- package/hooks/update-api-showcase.py +149 -0
- package/hooks/update-registry.py +352 -0
- package/hooks/update-ui-showcase.py +148 -0
- package/package.json +8 -2
- package/templates/BRAND_GUIDE.md +299 -0
- package/templates/CLAUDE-SECTION.md +56 -24
- package/templates/SPEC.json +640 -0
- package/templates/api-dev-state.json +179 -161
- package/templates/api-showcase/APICard.tsx +153 -0
- package/templates/api-showcase/APIModal.tsx +375 -0
- package/templates/api-showcase/APIShowcase.tsx +231 -0
- package/templates/api-showcase/APITester.tsx +522 -0
- package/templates/api-showcase/page.tsx +41 -0
- package/templates/component/Component.stories.tsx +172 -0
- package/templates/component/Component.test.tsx +237 -0
- package/templates/component/Component.tsx +86 -0
- package/templates/component/Component.types.ts +55 -0
- package/templates/component/index.ts +15 -0
- package/templates/dev-tools/_components/DevToolsLanding.tsx +320 -0
- package/templates/dev-tools/page.tsx +10 -0
- package/templates/page/page.e2e.test.ts +218 -0
- package/templates/page/page.tsx +42 -0
- package/templates/performance-budgets.json +58 -0
- package/templates/registry.json +13 -0
- package/templates/settings.json +74 -0
- package/templates/shared/HeroHeader.tsx +261 -0
- package/templates/shared/index.ts +1 -0
- package/templates/ui-showcase/PreviewCard.tsx +315 -0
- package/templates/ui-showcase/PreviewModal.tsx +676 -0
- package/templates/ui-showcase/UIShowcase.tsx +262 -0
- package/templates/ui-showcase/page.tsx +26 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useMemo } from 'react';
|
|
4
|
+
import { HeroHeader } from '../shared/HeroHeader';
|
|
5
|
+
import { PreviewCard } from './PreviewCard';
|
|
6
|
+
import { PreviewModal } from './PreviewModal';
|
|
7
|
+
|
|
8
|
+
// Import registry - this will be updated by the CLI when components are created
|
|
9
|
+
// Note: In production, this could be fetched from an API route
|
|
10
|
+
import registry from '@/../.claude/registry.json';
|
|
11
|
+
|
|
12
|
+
type FilterType = 'all' | 'components' | 'pages';
|
|
13
|
+
|
|
14
|
+
interface RegistryItem {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
file?: string;
|
|
18
|
+
route?: string;
|
|
19
|
+
story?: string;
|
|
20
|
+
tests?: string;
|
|
21
|
+
variants?: string[];
|
|
22
|
+
status?: string;
|
|
23
|
+
created_at?: string;
|
|
24
|
+
uses_components?: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Registry {
|
|
28
|
+
version: string;
|
|
29
|
+
apis: Record<string, any>;
|
|
30
|
+
components: Record<string, RegistryItem>;
|
|
31
|
+
pages: Record<string, RegistryItem>;
|
|
32
|
+
combined: Record<string, any>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* UI Showcase Component
|
|
37
|
+
*
|
|
38
|
+
* Displays a grid of all components and pages from the registry.
|
|
39
|
+
* Click any card to open a modal with live preview.
|
|
40
|
+
*
|
|
41
|
+
* Features:
|
|
42
|
+
* - Animated 3D grid hero header
|
|
43
|
+
* - Grid layout (like website portfolio)
|
|
44
|
+
* - Filter tabs: All, Components, Pages
|
|
45
|
+
* - Search functionality
|
|
46
|
+
* - Modal preview with Sandpack (components) or iframe (pages)
|
|
47
|
+
*
|
|
48
|
+
* Created with Hustle API Dev Tools (v3.9.2)
|
|
49
|
+
*/
|
|
50
|
+
export function UIShowcase() {
|
|
51
|
+
const [filter, setFilter] = useState<FilterType>('all');
|
|
52
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
53
|
+
const [selectedItem, setSelectedItem] = useState<{
|
|
54
|
+
id: string;
|
|
55
|
+
type: 'component' | 'page';
|
|
56
|
+
data: RegistryItem;
|
|
57
|
+
} | null>(null);
|
|
58
|
+
|
|
59
|
+
// Type the registry
|
|
60
|
+
const typedRegistry = registry as Registry;
|
|
61
|
+
|
|
62
|
+
// Combine components and pages into a single list
|
|
63
|
+
const allItems = useMemo(() => {
|
|
64
|
+
const items: Array<{
|
|
65
|
+
id: string;
|
|
66
|
+
type: 'component' | 'page';
|
|
67
|
+
data: RegistryItem;
|
|
68
|
+
}> = [];
|
|
69
|
+
|
|
70
|
+
// Add components
|
|
71
|
+
Object.entries(typedRegistry.components || {}).forEach(([id, data]) => {
|
|
72
|
+
items.push({ id, type: 'component', data });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Add pages
|
|
76
|
+
Object.entries(typedRegistry.pages || {}).forEach(([id, data]) => {
|
|
77
|
+
items.push({ id, type: 'page', data });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return items;
|
|
81
|
+
}, [typedRegistry]);
|
|
82
|
+
|
|
83
|
+
// Filter items based on type and search
|
|
84
|
+
const filteredItems = useMemo(() => {
|
|
85
|
+
return allItems.filter((item) => {
|
|
86
|
+
// Type filter
|
|
87
|
+
if (filter !== 'all' && filter !== `${item.type}s`) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Search filter
|
|
92
|
+
if (searchQuery) {
|
|
93
|
+
const query = searchQuery.toLowerCase();
|
|
94
|
+
const matchesName = item.data.name?.toLowerCase().includes(query);
|
|
95
|
+
const matchesDescription = item.data.description?.toLowerCase().includes(query);
|
|
96
|
+
return matchesName || matchesDescription;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
}, [allItems, filter, searchQuery]);
|
|
102
|
+
|
|
103
|
+
// Count by type
|
|
104
|
+
const componentCount = Object.keys(typedRegistry.components || {}).length;
|
|
105
|
+
const pageCount = Object.keys(typedRegistry.pages || {}).length;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className="min-h-screen bg-white dark:bg-[#050505]">
|
|
109
|
+
{/* Hero Header */}
|
|
110
|
+
<HeroHeader
|
|
111
|
+
title="UI Showcase"
|
|
112
|
+
badge="Component Library"
|
|
113
|
+
description={
|
|
114
|
+
<>
|
|
115
|
+
Live preview and testing for all{' '}
|
|
116
|
+
<strong>Hustle</strong> components and pages.
|
|
117
|
+
</>
|
|
118
|
+
}
|
|
119
|
+
/>
|
|
120
|
+
|
|
121
|
+
{/* Filter Bar */}
|
|
122
|
+
<div className="sticky top-0 z-10 border-b-2 border-black bg-white/95 backdrop-blur supports-[backdrop-filter]:bg-white/60 dark:border-gray-600 dark:bg-[#050505]/95">
|
|
123
|
+
<div className="container mx-auto px-4 py-4">
|
|
124
|
+
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
125
|
+
{/* Stats */}
|
|
126
|
+
<div className="flex items-center gap-4">
|
|
127
|
+
<span className="text-sm text-gray-600 dark:text-gray-400">
|
|
128
|
+
<strong className="text-black dark:text-white">{componentCount}</strong> components
|
|
129
|
+
</span>
|
|
130
|
+
<span className="h-4 w-px bg-black dark:bg-gray-600" />
|
|
131
|
+
<span className="text-sm text-gray-600 dark:text-gray-400">
|
|
132
|
+
<strong className="text-black dark:text-white">{pageCount}</strong> pages
|
|
133
|
+
</span>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
{/* Search */}
|
|
137
|
+
<div className="flex items-center gap-4">
|
|
138
|
+
<input
|
|
139
|
+
type="search"
|
|
140
|
+
placeholder="Search..."
|
|
141
|
+
value={searchQuery}
|
|
142
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
143
|
+
className="h-9 w-full border-2 border-black bg-white px-3 text-sm placeholder:text-gray-500 focus:border-[#BA0C2F] focus:outline-none dark:border-gray-600 dark:bg-gray-800 dark:text-white sm:w-64"
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{/* Filter Tabs */}
|
|
149
|
+
<div className="mt-4 flex gap-2">
|
|
150
|
+
<button
|
|
151
|
+
onClick={() => setFilter('all')}
|
|
152
|
+
className={`border-2 px-3 py-1.5 text-sm font-bold transition-colors ${
|
|
153
|
+
filter === 'all'
|
|
154
|
+
? 'border-[#BA0C2F] bg-[#BA0C2F] text-white'
|
|
155
|
+
: 'border-black bg-white text-black hover:border-[#BA0C2F] dark:border-gray-600 dark:bg-gray-800 dark:text-white'
|
|
156
|
+
}`}
|
|
157
|
+
>
|
|
158
|
+
All ({allItems.length})
|
|
159
|
+
</button>
|
|
160
|
+
<button
|
|
161
|
+
onClick={() => setFilter('components')}
|
|
162
|
+
className={`border-2 px-3 py-1.5 text-sm font-bold transition-colors ${
|
|
163
|
+
filter === 'components'
|
|
164
|
+
? 'border-[#BA0C2F] bg-[#BA0C2F] text-white'
|
|
165
|
+
: 'border-black bg-white text-black hover:border-[#BA0C2F] dark:border-gray-600 dark:bg-gray-800 dark:text-white'
|
|
166
|
+
}`}
|
|
167
|
+
>
|
|
168
|
+
Components ({componentCount})
|
|
169
|
+
</button>
|
|
170
|
+
<button
|
|
171
|
+
onClick={() => setFilter('pages')}
|
|
172
|
+
className={`border-2 px-3 py-1.5 text-sm font-bold transition-colors ${
|
|
173
|
+
filter === 'pages'
|
|
174
|
+
? 'border-[#BA0C2F] bg-[#BA0C2F] text-white'
|
|
175
|
+
: 'border-black bg-white text-black hover:border-[#BA0C2F] dark:border-gray-600 dark:bg-gray-800 dark:text-white'
|
|
176
|
+
}`}
|
|
177
|
+
>
|
|
178
|
+
Pages ({pageCount})
|
|
179
|
+
</button>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Grid */}
|
|
185
|
+
<main className="container mx-auto px-4 py-8">
|
|
186
|
+
{filteredItems.length === 0 ? (
|
|
187
|
+
<div className="flex flex-col items-center justify-center border-2 border-dashed border-black py-16 text-center dark:border-gray-600">
|
|
188
|
+
<div className="mb-4 border-2 border-black bg-gray-100 p-4 dark:border-gray-600 dark:bg-gray-800">
|
|
189
|
+
<svg
|
|
190
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
191
|
+
width="32"
|
|
192
|
+
height="32"
|
|
193
|
+
viewBox="0 0 24 24"
|
|
194
|
+
fill="none"
|
|
195
|
+
stroke="currentColor"
|
|
196
|
+
strokeWidth="2"
|
|
197
|
+
strokeLinecap="round"
|
|
198
|
+
strokeLinejoin="round"
|
|
199
|
+
className="text-gray-500"
|
|
200
|
+
>
|
|
201
|
+
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
|
|
202
|
+
<circle cx="9" cy="9" r="2" />
|
|
203
|
+
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
|
|
204
|
+
</svg>
|
|
205
|
+
</div>
|
|
206
|
+
<h2 className="text-xl font-bold text-black dark:text-white">
|
|
207
|
+
{searchQuery ? 'No results found' : 'No items yet'}
|
|
208
|
+
</h2>
|
|
209
|
+
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
210
|
+
{searchQuery
|
|
211
|
+
? `No components or pages match "${searchQuery}"`
|
|
212
|
+
: 'Run /ui-create to add components and pages'}
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
) : (
|
|
216
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
217
|
+
{filteredItems.map((item) => (
|
|
218
|
+
<PreviewCard
|
|
219
|
+
key={`${item.type}-${item.id}`}
|
|
220
|
+
id={item.id}
|
|
221
|
+
type={item.type}
|
|
222
|
+
name={item.data.name || item.id}
|
|
223
|
+
description={item.data.description}
|
|
224
|
+
variants={item.data.variants}
|
|
225
|
+
usesComponents={item.data.uses_components}
|
|
226
|
+
route={item.data.route}
|
|
227
|
+
file={item.data.file}
|
|
228
|
+
onClick={() => setSelectedItem(item)}
|
|
229
|
+
/>
|
|
230
|
+
))}
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
</main>
|
|
234
|
+
|
|
235
|
+
{/* Modal */}
|
|
236
|
+
{selectedItem && (
|
|
237
|
+
<PreviewModal
|
|
238
|
+
id={selectedItem.id}
|
|
239
|
+
type={selectedItem.type}
|
|
240
|
+
data={selectedItem.data}
|
|
241
|
+
onClose={() => setSelectedItem(null)}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{/* Footer */}
|
|
246
|
+
<footer className="border-t-2 border-black py-6 text-center text-sm text-gray-600 dark:border-gray-600 dark:text-gray-400">
|
|
247
|
+
<p>
|
|
248
|
+
Created with{' '}
|
|
249
|
+
<a
|
|
250
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
251
|
+
target="_blank"
|
|
252
|
+
rel="noopener noreferrer"
|
|
253
|
+
className="font-bold text-black hover:text-[#BA0C2F] dark:text-white"
|
|
254
|
+
>
|
|
255
|
+
Hustle API Dev Tools
|
|
256
|
+
</a>{' '}
|
|
257
|
+
v3.9.2
|
|
258
|
+
</p>
|
|
259
|
+
</footer>
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
import { UIShowcase } from './UIShowcase';
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: 'UI Showcase',
|
|
6
|
+
description: 'Preview all components and pages created with Hustle UI Create',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* UI Showcase Page
|
|
11
|
+
*
|
|
12
|
+
* Auto-generated page that displays all components and pages from the registry.
|
|
13
|
+
* Grid layout with modal preview for each element.
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Animated 3D grid hero header
|
|
17
|
+
* - Grid layout showing all registered components and pages
|
|
18
|
+
* - Interactive preview with Sandpack
|
|
19
|
+
* - Variant switching
|
|
20
|
+
* - Responsive viewport testing
|
|
21
|
+
*
|
|
22
|
+
* Created with Hustle API Dev Tools (v3.9.2)
|
|
23
|
+
*/
|
|
24
|
+
export default function UIShowcasePage() {
|
|
25
|
+
return <UIShowcase />;
|
|
26
|
+
}
|