@hustle-together/api-dev-tools 3.6.5 → 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.
Files changed (61) hide show
  1. package/README.md +5307 -258
  2. package/bin/cli.js +348 -20
  3. package/commands/README.md +459 -71
  4. package/commands/hustle-api-continue.md +158 -0
  5. package/commands/{api-create.md → hustle-api-create.md} +22 -2
  6. package/commands/{api-env.md → hustle-api-env.md} +4 -4
  7. package/commands/{api-interview.md → hustle-api-interview.md} +1 -1
  8. package/commands/{api-research.md → hustle-api-research.md} +3 -3
  9. package/commands/hustle-api-sessions.md +149 -0
  10. package/commands/{api-status.md → hustle-api-status.md} +16 -16
  11. package/commands/{api-verify.md → hustle-api-verify.md} +2 -2
  12. package/commands/hustle-combine.md +763 -0
  13. package/commands/hustle-ui-create.md +825 -0
  14. package/hooks/api-workflow-check.py +385 -19
  15. package/hooks/cache-research.py +337 -0
  16. package/hooks/check-playwright-setup.py +103 -0
  17. package/hooks/check-storybook-setup.py +81 -0
  18. package/hooks/detect-interruption.py +165 -0
  19. package/hooks/enforce-brand-guide.py +131 -0
  20. package/hooks/enforce-documentation.py +60 -8
  21. package/hooks/enforce-freshness.py +184 -0
  22. package/hooks/enforce-questions-sourced.py +146 -0
  23. package/hooks/enforce-schema-from-interview.py +248 -0
  24. package/hooks/enforce-ui-disambiguation.py +108 -0
  25. package/hooks/enforce-ui-interview.py +130 -0
  26. package/hooks/generate-manifest-entry.py +981 -0
  27. package/hooks/session-logger.py +297 -0
  28. package/hooks/session-startup.py +65 -10
  29. package/hooks/track-scope-coverage.py +220 -0
  30. package/hooks/track-tool-use.py +81 -1
  31. package/hooks/update-api-showcase.py +149 -0
  32. package/hooks/update-registry.py +352 -0
  33. package/hooks/update-ui-showcase.py +148 -0
  34. package/package.json +8 -2
  35. package/templates/BRAND_GUIDE.md +299 -0
  36. package/templates/CLAUDE-SECTION.md +56 -24
  37. package/templates/SPEC.json +640 -0
  38. package/templates/api-dev-state.json +179 -161
  39. package/templates/api-showcase/APICard.tsx +153 -0
  40. package/templates/api-showcase/APIModal.tsx +375 -0
  41. package/templates/api-showcase/APIShowcase.tsx +231 -0
  42. package/templates/api-showcase/APITester.tsx +522 -0
  43. package/templates/api-showcase/page.tsx +41 -0
  44. package/templates/component/Component.stories.tsx +172 -0
  45. package/templates/component/Component.test.tsx +237 -0
  46. package/templates/component/Component.tsx +86 -0
  47. package/templates/component/Component.types.ts +55 -0
  48. package/templates/component/index.ts +15 -0
  49. package/templates/dev-tools/_components/DevToolsLanding.tsx +320 -0
  50. package/templates/dev-tools/page.tsx +10 -0
  51. package/templates/page/page.e2e.test.ts +218 -0
  52. package/templates/page/page.tsx +42 -0
  53. package/templates/performance-budgets.json +58 -0
  54. package/templates/registry.json +13 -0
  55. package/templates/settings.json +74 -0
  56. package/templates/shared/HeroHeader.tsx +261 -0
  57. package/templates/shared/index.ts +1 -0
  58. package/templates/ui-showcase/PreviewCard.tsx +315 -0
  59. package/templates/ui-showcase/PreviewModal.tsx +676 -0
  60. package/templates/ui-showcase/UIShowcase.tsx +262 -0
  61. package/templates/ui-showcase/page.tsx +26 -0
@@ -0,0 +1,172 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { __COMPONENT_NAME__ } from './__COMPONENT_NAME__';
3
+
4
+ /**
5
+ * __COMPONENT_NAME__ - __COMPONENT_DESCRIPTION__
6
+ *
7
+ * This component was created using the Hustle UI Create workflow.
8
+ */
9
+ const meta: Meta<typeof __COMPONENT_NAME__> = {
10
+ title: 'Components/__COMPONENT_NAME__',
11
+ component: __COMPONENT_NAME__,
12
+ parameters: {
13
+ layout: 'centered',
14
+ docs: {
15
+ description: {
16
+ component: '__COMPONENT_DESCRIPTION__',
17
+ },
18
+ },
19
+ },
20
+ tags: ['autodocs'],
21
+ argTypes: {
22
+ variant: {
23
+ control: 'select',
24
+ options: ['primary', 'secondary', 'destructive', 'outline', 'ghost'],
25
+ description: 'Visual style variant',
26
+ table: {
27
+ defaultValue: { summary: 'primary' },
28
+ },
29
+ },
30
+ size: {
31
+ control: 'select',
32
+ options: ['sm', 'md', 'lg'],
33
+ description: 'Size variant',
34
+ table: {
35
+ defaultValue: { summary: 'md' },
36
+ },
37
+ },
38
+ loading: {
39
+ control: 'boolean',
40
+ description: 'Shows loading spinner',
41
+ table: {
42
+ defaultValue: { summary: 'false' },
43
+ },
44
+ },
45
+ disabled: {
46
+ control: 'boolean',
47
+ description: 'Disables the component',
48
+ table: {
49
+ defaultValue: { summary: 'false' },
50
+ },
51
+ },
52
+ children: {
53
+ control: 'text',
54
+ description: 'Content inside the component',
55
+ },
56
+ },
57
+ };
58
+
59
+ export default meta;
60
+ type Story = StoryObj<typeof meta>;
61
+
62
+ /**
63
+ * Default primary variant
64
+ */
65
+ export const Primary: Story = {
66
+ args: {
67
+ variant: 'primary',
68
+ children: 'Primary __COMPONENT_NAME__',
69
+ },
70
+ };
71
+
72
+ /**
73
+ * Secondary variant for less prominent actions
74
+ */
75
+ export const Secondary: Story = {
76
+ args: {
77
+ variant: 'secondary',
78
+ children: 'Secondary __COMPONENT_NAME__',
79
+ },
80
+ };
81
+
82
+ /**
83
+ * Destructive variant for dangerous actions
84
+ */
85
+ export const Destructive: Story = {
86
+ args: {
87
+ variant: 'destructive',
88
+ children: 'Delete Item',
89
+ },
90
+ };
91
+
92
+ /**
93
+ * Outline variant with border
94
+ */
95
+ export const Outline: Story = {
96
+ args: {
97
+ variant: 'outline',
98
+ children: 'Outline __COMPONENT_NAME__',
99
+ },
100
+ };
101
+
102
+ /**
103
+ * Ghost variant with no background
104
+ */
105
+ export const Ghost: Story = {
106
+ args: {
107
+ variant: 'ghost',
108
+ children: 'Ghost __COMPONENT_NAME__',
109
+ },
110
+ };
111
+
112
+ /**
113
+ * Small size variant
114
+ */
115
+ export const Small: Story = {
116
+ args: {
117
+ size: 'sm',
118
+ children: 'Small __COMPONENT_NAME__',
119
+ },
120
+ };
121
+
122
+ /**
123
+ * Large size variant
124
+ */
125
+ export const Large: Story = {
126
+ args: {
127
+ size: 'lg',
128
+ children: 'Large __COMPONENT_NAME__',
129
+ },
130
+ };
131
+
132
+ /**
133
+ * Loading state with spinner
134
+ */
135
+ export const Loading: Story = {
136
+ args: {
137
+ loading: true,
138
+ children: 'Loading...',
139
+ },
140
+ };
141
+
142
+ /**
143
+ * Disabled state
144
+ */
145
+ export const Disabled: Story = {
146
+ args: {
147
+ disabled: true,
148
+ children: 'Disabled __COMPONENT_NAME__',
149
+ },
150
+ };
151
+
152
+ /**
153
+ * All variants displayed together
154
+ */
155
+ export const AllVariants: Story = {
156
+ render: () => (
157
+ <div className="flex flex-col gap-4">
158
+ <div className="flex gap-2">
159
+ <__COMPONENT_NAME__ variant="primary">Primary</__COMPONENT_NAME__>
160
+ <__COMPONENT_NAME__ variant="secondary">Secondary</__COMPONENT_NAME__>
161
+ <__COMPONENT_NAME__ variant="destructive">Destructive</__COMPONENT_NAME__>
162
+ <__COMPONENT_NAME__ variant="outline">Outline</__COMPONENT_NAME__>
163
+ <__COMPONENT_NAME__ variant="ghost">Ghost</__COMPONENT_NAME__>
164
+ </div>
165
+ <div className="flex items-center gap-2">
166
+ <__COMPONENT_NAME__ size="sm">Small</__COMPONENT_NAME__>
167
+ <__COMPONENT_NAME__ size="md">Medium</__COMPONENT_NAME__>
168
+ <__COMPONENT_NAME__ size="lg">Large</__COMPONENT_NAME__>
169
+ </div>
170
+ </div>
171
+ ),
172
+ };
@@ -0,0 +1,237 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { describe, it, expect, vi } from 'vitest';
3
+ import { __COMPONENT_NAME__ } from './__COMPONENT_NAME__';
4
+
5
+ describe('__COMPONENT_NAME__', () => {
6
+ describe('Rendering', () => {
7
+ it('renders children correctly', () => {
8
+ render(<__COMPONENT_NAME__>Test Content</__COMPONENT_NAME__>);
9
+ expect(screen.getByText('Test Content')).toBeInTheDocument();
10
+ });
11
+
12
+ it('renders as a button element', () => {
13
+ render(<__COMPONENT_NAME__>Click me</__COMPONENT_NAME__>);
14
+ expect(screen.getByRole('button')).toBeInTheDocument();
15
+ });
16
+
17
+ it('forwards ref correctly', () => {
18
+ const ref = vi.fn();
19
+ render(<__COMPONENT_NAME__ ref={ref}>Test</__COMPONENT_NAME__>);
20
+ expect(ref).toHaveBeenCalled();
21
+ });
22
+ });
23
+
24
+ describe('Variants', () => {
25
+ it('applies primary variant by default', () => {
26
+ const { container } = render(<__COMPONENT_NAME__>Primary</__COMPONENT_NAME__>);
27
+ expect(container.firstChild).toHaveClass('bg-primary');
28
+ });
29
+
30
+ it('applies secondary variant', () => {
31
+ const { container } = render(
32
+ <__COMPONENT_NAME__ variant="secondary">Secondary</__COMPONENT_NAME__>
33
+ );
34
+ expect(container.firstChild).toHaveClass('bg-secondary');
35
+ });
36
+
37
+ it('applies destructive variant', () => {
38
+ const { container } = render(
39
+ <__COMPONENT_NAME__ variant="destructive">Delete</__COMPONENT_NAME__>
40
+ );
41
+ expect(container.firstChild).toHaveClass('bg-destructive');
42
+ });
43
+
44
+ it('applies outline variant', () => {
45
+ const { container } = render(
46
+ <__COMPONENT_NAME__ variant="outline">Outline</__COMPONENT_NAME__>
47
+ );
48
+ expect(container.firstChild).toHaveClass('border');
49
+ });
50
+
51
+ it('applies ghost variant', () => {
52
+ const { container } = render(
53
+ <__COMPONENT_NAME__ variant="ghost">Ghost</__COMPONENT_NAME__>
54
+ );
55
+ expect(container.firstChild).toHaveClass('hover:bg-accent');
56
+ });
57
+ });
58
+
59
+ describe('Sizes', () => {
60
+ it('applies medium size by default', () => {
61
+ const { container } = render(<__COMPONENT_NAME__>Medium</__COMPONENT_NAME__>);
62
+ expect(container.firstChild).toHaveClass('h-10');
63
+ });
64
+
65
+ it('applies small size', () => {
66
+ const { container } = render(<__COMPONENT_NAME__ size="sm">Small</__COMPONENT_NAME__>);
67
+ expect(container.firstChild).toHaveClass('h-8');
68
+ });
69
+
70
+ it('applies large size', () => {
71
+ const { container } = render(<__COMPONENT_NAME__ size="lg">Large</__COMPONENT_NAME__>);
72
+ expect(container.firstChild).toHaveClass('h-12');
73
+ });
74
+ });
75
+
76
+ describe('States', () => {
77
+ it('handles disabled state', () => {
78
+ render(<__COMPONENT_NAME__ disabled>Disabled</__COMPONENT_NAME__>);
79
+ expect(screen.getByRole('button')).toBeDisabled();
80
+ });
81
+
82
+ it('handles loading state', () => {
83
+ render(<__COMPONENT_NAME__ loading>Loading</__COMPONENT_NAME__>);
84
+ const button = screen.getByRole('button');
85
+ expect(button).toBeDisabled();
86
+ expect(button).toHaveAttribute('aria-busy', 'true');
87
+ });
88
+
89
+ it('shows loading spinner when loading', () => {
90
+ const { container } = render(<__COMPONENT_NAME__ loading>Loading</__COMPONENT_NAME__>);
91
+ expect(container.querySelector('svg.animate-spin')).toBeInTheDocument();
92
+ });
93
+ });
94
+
95
+ describe('Interaction', () => {
96
+ it('calls onClick when clicked', () => {
97
+ const handleClick = vi.fn();
98
+ render(<__COMPONENT_NAME__ onClick={handleClick}>Click</__COMPONENT_NAME__>);
99
+ fireEvent.click(screen.getByRole('button'));
100
+ expect(handleClick).toHaveBeenCalledTimes(1);
101
+ });
102
+
103
+ it('does not call onClick when disabled', () => {
104
+ const handleClick = vi.fn();
105
+ render(
106
+ <__COMPONENT_NAME__ disabled onClick={handleClick}>
107
+ Disabled
108
+ </__COMPONENT_NAME__>
109
+ );
110
+ fireEvent.click(screen.getByRole('button'));
111
+ expect(handleClick).not.toHaveBeenCalled();
112
+ });
113
+
114
+ it('does not call onClick when loading', () => {
115
+ const handleClick = vi.fn();
116
+ render(
117
+ <__COMPONENT_NAME__ loading onClick={handleClick}>
118
+ Loading
119
+ </__COMPONENT_NAME__>
120
+ );
121
+ fireEvent.click(screen.getByRole('button'));
122
+ expect(handleClick).not.toHaveBeenCalled();
123
+ });
124
+ });
125
+
126
+ describe('Accessibility', () => {
127
+ it('is focusable', () => {
128
+ render(<__COMPONENT_NAME__>Focus me</__COMPONENT_NAME__>);
129
+ const button = screen.getByRole('button');
130
+ button.focus();
131
+ expect(button).toHaveFocus();
132
+ });
133
+
134
+ it('is not focusable when disabled', () => {
135
+ render(<__COMPONENT_NAME__ disabled>Disabled</__COMPONENT_NAME__>);
136
+ const button = screen.getByRole('button');
137
+ expect(button).toHaveAttribute('disabled');
138
+ });
139
+
140
+ it('has aria-busy when loading', () => {
141
+ render(<__COMPONENT_NAME__ loading>Loading</__COMPONENT_NAME__>);
142
+ expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
143
+ });
144
+ });
145
+
146
+ describe('Custom className', () => {
147
+ it('accepts custom className', () => {
148
+ const { container } = render(
149
+ <__COMPONENT_NAME__ className="custom-class">Custom</__COMPONENT_NAME__>
150
+ );
151
+ expect(container.firstChild).toHaveClass('custom-class');
152
+ });
153
+
154
+ it('merges custom className with variant classes', () => {
155
+ const { container } = render(
156
+ <__COMPONENT_NAME__ variant="primary" className="custom-class">
157
+ Merged
158
+ </__COMPONENT_NAME__>
159
+ );
160
+ expect(container.firstChild).toHaveClass('bg-primary');
161
+ expect(container.firstChild).toHaveClass('custom-class');
162
+ });
163
+ });
164
+
165
+ // ===================================
166
+ // Performance Tests (TDD GATES)
167
+ // These thresholds match .claude/performance-budgets.json
168
+ // Tests FAIL if exceeded, triggering TDD loop-back
169
+ // ===================================
170
+
171
+ describe('Performance', () => {
172
+ it('should not re-render excessively on mount', () => {
173
+ let renderCount = 0;
174
+
175
+ const TestWrapper = () => {
176
+ renderCount++;
177
+ return <__COMPONENT_NAME__>Test</__COMPONENT_NAME__>;
178
+ };
179
+
180
+ render(<TestWrapper />);
181
+
182
+ // THRESHOLD: Mount renders max 1
183
+ // If this fails, check for: useEffect dependencies, state initialization
184
+ expect(renderCount).toBeLessThanOrEqual(1);
185
+ });
186
+
187
+ it('should not re-render excessively on prop change', () => {
188
+ let renderCount = 0;
189
+
190
+ const TestWrapper = ({ variant }: { variant: 'primary' | 'secondary' }) => {
191
+ renderCount++;
192
+ return <__COMPONENT_NAME__ variant={variant}>Test</__COMPONENT_NAME__>;
193
+ };
194
+
195
+ const { rerender } = render(<TestWrapper variant="primary" />);
196
+ renderCount = 0; // Reset after initial render
197
+
198
+ rerender(<TestWrapper variant="secondary" />);
199
+
200
+ // THRESHOLD: Prop change renders max 1
201
+ // If this fails, check for: missing useMemo/useCallback, unstable references
202
+ expect(renderCount).toBeLessThanOrEqual(1);
203
+ });
204
+
205
+ it('should not cause unnecessary re-renders with same props', () => {
206
+ let renderCount = 0;
207
+
208
+ const TestWrapper = ({ variant }: { variant: 'primary' }) => {
209
+ renderCount++;
210
+ return <__COMPONENT_NAME__ variant={variant}>Test</__COMPONENT_NAME__>;
211
+ };
212
+
213
+ const { rerender } = render(<TestWrapper variant="primary" />);
214
+ renderCount = 0; // Reset after initial render
215
+
216
+ // Rerender with SAME props
217
+ rerender(<TestWrapper variant="primary" />);
218
+
219
+ // THRESHOLD: Same-prop renders max 0 (should be memoized)
220
+ // If this fails, consider wrapping component with React.memo
221
+ // Note: This is a warning, not a hard fail in some cases
222
+ expect(renderCount).toBeLessThanOrEqual(1);
223
+ });
224
+
225
+ it('should render within time budget', () => {
226
+ const startTime = performance.now();
227
+
228
+ render(<__COMPONENT_NAME__>Performance Test</__COMPONENT_NAME__>);
229
+
230
+ const renderTime = performance.now() - startTime;
231
+
232
+ // THRESHOLD: Initial render max 16ms (one frame at 60fps)
233
+ // If this fails, check for: expensive computations, large DOM trees
234
+ expect(renderTime).toBeLessThan(16);
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,86 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '@/lib/utils';
4
+ import type { __COMPONENT_NAME__Props } from './__COMPONENT_NAME__.types';
5
+
6
+ /**
7
+ * __COMPONENT_NAME__ variants using class-variance-authority
8
+ * Customize variants based on interview decisions
9
+ */
10
+ const __COMPONENT_NAME_LOWER__Variants = cva(
11
+ // Base styles - apply brand guide values
12
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
13
+ {
14
+ variants: {
15
+ variant: {
16
+ primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
17
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
18
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
19
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
20
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
21
+ },
22
+ size: {
23
+ sm: 'h-8 px-3 text-sm',
24
+ md: 'h-10 px-4 text-base',
25
+ lg: 'h-12 px-6 text-lg',
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: 'primary',
30
+ size: 'md',
31
+ },
32
+ }
33
+ );
34
+
35
+ /**
36
+ * __COMPONENT_NAME__ component
37
+ *
38
+ * @description __COMPONENT_DESCRIPTION__
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * <__COMPONENT_NAME__ variant="primary" size="md">
43
+ * Click me
44
+ * </__COMPONENT_NAME__>
45
+ * ```
46
+ */
47
+ export const __COMPONENT_NAME__ = React.forwardRef<
48
+ HTMLButtonElement,
49
+ __COMPONENT_NAME__Props
50
+ >(({ variant, size, loading, disabled, className, children, ...props }, ref) => {
51
+ return (
52
+ <button
53
+ ref={ref}
54
+ className={cn(__COMPONENT_NAME_LOWER__Variants({ variant, size }), className)}
55
+ disabled={disabled || loading}
56
+ aria-busy={loading || undefined}
57
+ {...props}
58
+ >
59
+ {loading && (
60
+ <svg
61
+ className="mr-2 h-4 w-4 animate-spin"
62
+ xmlns="http://www.w3.org/2000/svg"
63
+ fill="none"
64
+ viewBox="0 0 24 24"
65
+ >
66
+ <circle
67
+ className="opacity-25"
68
+ cx="12"
69
+ cy="12"
70
+ r="10"
71
+ stroke="currentColor"
72
+ strokeWidth="4"
73
+ />
74
+ <path
75
+ className="opacity-75"
76
+ fill="currentColor"
77
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
78
+ />
79
+ </svg>
80
+ )}
81
+ {children}
82
+ </button>
83
+ );
84
+ });
85
+
86
+ __COMPONENT_NAME__.displayName = '__COMPONENT_NAME__';
@@ -0,0 +1,55 @@
1
+ import type { VariantProps } from 'class-variance-authority';
2
+ import type { ComponentPropsWithoutRef } from 'react';
3
+
4
+ /**
5
+ * __COMPONENT_NAME__ variant configuration
6
+ * Generated from interview decisions
7
+ */
8
+ export type __COMPONENT_NAME__Variant = 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost';
9
+
10
+ /**
11
+ * __COMPONENT_NAME__ size configuration
12
+ */
13
+ export type __COMPONENT_NAME__Size = 'sm' | 'md' | 'lg';
14
+
15
+ /**
16
+ * Props for the __COMPONENT_NAME__ component
17
+ *
18
+ * @property variant - Visual style variant
19
+ * @property size - Size variant
20
+ * @property loading - Shows loading spinner and disables interaction
21
+ * @property disabled - Disables the component
22
+ * @property className - Additional CSS classes
23
+ * @property children - Content to render inside the component
24
+ */
25
+ export interface __COMPONENT_NAME__Props
26
+ extends ComponentPropsWithoutRef<'button'> {
27
+ /**
28
+ * Visual style variant
29
+ * @default 'primary'
30
+ */
31
+ variant?: __COMPONENT_NAME__Variant;
32
+
33
+ /**
34
+ * Size variant
35
+ * @default 'md'
36
+ */
37
+ size?: __COMPONENT_NAME__Size;
38
+
39
+ /**
40
+ * Shows loading spinner and disables interaction
41
+ * @default false
42
+ */
43
+ loading?: boolean;
44
+
45
+ /**
46
+ * Disables the component
47
+ * @default false
48
+ */
49
+ disabled?: boolean;
50
+
51
+ /**
52
+ * Content to render inside the component
53
+ */
54
+ children: React.ReactNode;
55
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * __COMPONENT_NAME__ Component
3
+ *
4
+ * @description __COMPONENT_DESCRIPTION__
5
+ * @see {@link ./README.md} for usage documentation
6
+ *
7
+ * Created with Hustle UI Create workflow (v3.9.0)
8
+ */
9
+
10
+ export { __COMPONENT_NAME__ } from './__COMPONENT_NAME__';
11
+ export type {
12
+ __COMPONENT_NAME__Props,
13
+ __COMPONENT_NAME__Variant,
14
+ __COMPONENT_NAME__Size,
15
+ } from './__COMPONENT_NAME__.types';