@carefully-built/cli 0.1.1 → 0.1.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 +101 -80
- package/dist/index.mjs +8 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/registry/ui/avatar/manifest.json +33 -0
- package/registry/ui/avatar/primitives/avatar.tsx +64 -0
- package/registry/ui/avatar/utils/cn.ts +6 -0
- package/registry/ui/button/manifest.json +24 -5
- package/registry/ui/button/utils/cn.ts +6 -0
- package/registry/ui/calendar/manifest.json +35 -0
- package/registry/ui/calendar/primitives/button.tsx +89 -0
- package/registry/ui/calendar/primitives/calendar.tsx +68 -0
- package/registry/ui/calendar/utils/cn.ts +6 -0
- package/registry/ui/card/manifest.json +36 -0
- package/registry/ui/card/primitives/card.tsx +80 -0
- package/registry/ui/card/utils/cn.ts +6 -0
- package/registry/ui/chip/manifest.json +36 -0
- package/registry/ui/chip/primitives/chip-utils.ts +10 -0
- package/registry/ui/chip/primitives/chip.tsx +74 -0
- package/registry/ui/chip/utils/cn.ts +6 -0
- package/registry/ui/chip-utils/manifest.json +33 -0
- package/registry/ui/chip-utils/primitives/chip-utils.ts +10 -0
- package/registry/ui/chip-utils/utils/cn.ts +6 -0
- package/registry/ui/date-display/manifest.json +33 -0
- package/registry/ui/date-display/utils/cn.ts +6 -0
- package/registry/ui/date-display/utils/date-display.ts +61 -0
- package/registry/ui/dialog/manifest.json +43 -0
- package/registry/ui/dialog/primitives/button.tsx +89 -0
- package/registry/ui/dialog/primitives/dialog.tsx +147 -0
- package/registry/ui/dialog/utils/cn.ts +6 -0
- package/registry/ui/display-date/manifest.json +36 -0
- package/registry/ui/display-date/primitives/display-date.tsx +20 -0
- package/registry/ui/display-date/utils/cn.ts +6 -0
- package/registry/ui/display-date/utils/date-display.ts +61 -0
- package/registry/ui/drawer/manifest.json +37 -0
- package/registry/ui/drawer/primitives/drawer.tsx +99 -0
- package/registry/ui/drawer/utils/cn.ts +6 -0
- package/registry/ui/dropdown-menu/manifest.json +37 -0
- package/registry/ui/dropdown-menu/primitives/dropdown-menu.tsx +140 -0
- package/registry/ui/dropdown-menu/utils/cn.ts +6 -0
- package/registry/ui/empty-state/empty-state/collection-empty-state.ts +29 -0
- package/registry/ui/empty-state/empty-state/empty-state-card.tsx +72 -0
- package/registry/ui/empty-state/empty-state/index.ts +8 -0
- package/registry/ui/empty-state/empty-state/initial-empty-state.tsx +36 -0
- package/registry/ui/empty-state/empty-state/no-results-state.tsx +20 -0
- package/registry/ui/empty-state/manifest.json +63 -0
- package/registry/ui/empty-state/primitives/button.tsx +89 -0
- package/registry/ui/empty-state/primitives/card.tsx +80 -0
- package/registry/ui/empty-state/utils/cn.ts +6 -0
- package/registry/ui/error-page/error-page/error-code.tsx +16 -0
- package/registry/ui/error-page/error-page/error-page-content.ts +75 -0
- package/registry/ui/error-page/error-page/index.ts +19 -0
- package/registry/ui/error-page/error-page/posthog-error-capture.ts +83 -0
- package/registry/ui/error-page/error-page/saas-error-page.tsx +146 -0
- package/registry/ui/error-page/manifest.json +64 -0
- package/registry/ui/error-page/primitives/button.tsx +89 -0
- package/registry/ui/error-page/utils/cn.ts +6 -0
- package/registry/ui/field-detail-row/manifest.json +32 -0
- package/registry/ui/field-detail-row/primitives/field-detail-row.tsx +28 -0
- package/registry/ui/field-detail-row/utils/cn.ts +6 -0
- package/registry/ui/file-dropzone/manifest.json +35 -0
- package/registry/ui/file-dropzone/primitives/button.tsx +89 -0
- package/registry/ui/file-dropzone/primitives/file-dropzone.tsx +236 -0
- package/registry/ui/file-dropzone/utils/cn.ts +6 -0
- package/registry/ui/help-info-button/manifest.json +72 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/help-info-button/primitives/button.tsx +89 -0
- package/registry/ui/help-info-button/primitives/drawer.tsx +99 -0
- package/registry/ui/help-info-button/primitives/help-info-button.tsx +63 -0
- package/registry/ui/help-info-button/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/help-info-button/primitives/sheet.tsx +103 -0
- package/registry/ui/help-info-button/primitives/tooltip.tsx +57 -0
- package/registry/ui/help-info-button/utils/cn.ts +6 -0
- package/registry/ui/help-info-button/utils/use-media-query.ts +28 -0
- package/registry/ui/input/manifest.json +31 -0
- package/registry/ui/input/primitives/input.tsx +19 -0
- package/registry/ui/input/utils/cn.ts +6 -0
- package/registry/ui/keyboard-shortcut-hint/manifest.json +32 -0
- package/registry/ui/keyboard-shortcut-hint/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/keyboard-shortcut-hint/utils/cn.ts +6 -0
- package/registry/ui/label/manifest.json +31 -0
- package/registry/ui/label/primitives/label.tsx +21 -0
- package/registry/ui/label/utils/cn.ts +6 -0
- package/registry/ui/pagination/manifest.json +36 -0
- package/registry/ui/pagination/primitives/button.tsx +89 -0
- package/registry/ui/pagination/primitives/pagination.tsx +143 -0
- package/registry/ui/pagination/utils/cn.ts +6 -0
- package/registry/ui/popover/manifest.json +33 -0
- package/registry/ui/popover/primitives/popover.tsx +46 -0
- package/registry/ui/popover/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet/manifest.json +66 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/responsive-sheet/primitives/button.tsx +89 -0
- package/registry/ui/responsive-sheet/primitives/drawer.tsx +99 -0
- package/registry/ui/responsive-sheet/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/responsive-sheet/primitives/sheet.tsx +103 -0
- package/registry/ui/responsive-sheet/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet/utils/use-media-query.ts +28 -0
- package/registry/ui/responsive-sheet.footer/manifest.json +40 -0
- package/registry/ui/responsive-sheet.footer/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/responsive-sheet.footer/primitives/button.tsx +89 -0
- package/registry/ui/responsive-sheet.footer/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/responsive-sheet.footer/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet.shortcuts/manifest.json +34 -0
- package/registry/ui/responsive-sheet.shortcuts/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/responsive-sheet.shortcuts/utils/cn.ts +6 -0
- package/registry/ui/scroll-fade-area/manifest.json +31 -0
- package/registry/ui/scroll-fade-area/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/scroll-fade-area/utils/cn.ts +6 -0
- package/registry/ui/search/manifest.json +35 -0
- package/registry/ui/search/utils/cn.ts +6 -0
- package/registry/ui/search/utils/search.ts +227 -0
- package/registry/ui/searchable-select/manifest.json +48 -0
- package/registry/ui/searchable-select/primitives/input.tsx +19 -0
- package/registry/ui/searchable-select/search/searchable-select-position.ts +95 -0
- package/registry/ui/searchable-select/search/searchable-select.tsx +431 -0
- package/registry/ui/searchable-select/utils/cn.ts +6 -0
- package/registry/ui/searchable-select/utils/search.ts +227 -0
- package/registry/ui/searchable-select-position/manifest.json +32 -0
- package/registry/ui/searchable-select-position/search/searchable-select-position.ts +95 -0
- package/registry/ui/searchable-select-position/utils/cn.ts +6 -0
- package/registry/ui/segmented-toggle/manifest.json +41 -0
- package/registry/ui/segmented-toggle/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/segmented-toggle/primitives/segmented-toggle.tsx +106 -0
- package/registry/ui/segmented-toggle/primitives/tabs.tsx +97 -0
- package/registry/ui/segmented-toggle/utils/cn.ts +6 -0
- package/registry/ui/select/manifest.json +37 -0
- package/registry/ui/select/primitives/select.tsx +142 -0
- package/registry/ui/select/utils/cn.ts +6 -0
- package/registry/ui/sheet/manifest.json +39 -0
- package/registry/ui/sheet/primitives/button.tsx +89 -0
- package/registry/ui/sheet/primitives/sheet.tsx +103 -0
- package/registry/ui/sheet/utils/cn.ts +6 -0
- package/registry/ui/skeleton/manifest.json +31 -0
- package/registry/ui/skeleton/primitives/skeleton.tsx +13 -0
- package/registry/ui/skeleton/utils/cn.ts +6 -0
- package/registry/ui/smart-table/manifest.json +115 -0
- package/registry/ui/smart-table/primitives/button.tsx +89 -0
- package/registry/ui/smart-table/primitives/card.tsx +80 -0
- package/registry/ui/smart-table/primitives/display-date.tsx +20 -0
- package/registry/ui/smart-table/primitives/pagination.tsx +143 -0
- package/registry/ui/smart-table/primitives/skeleton.tsx +13 -0
- package/registry/ui/smart-table/primitives/table.tsx +92 -0
- package/registry/ui/smart-table/primitives/tooltip.tsx +57 -0
- package/registry/ui/smart-table/smart-table/DesktopView.tsx +343 -0
- package/registry/ui/smart-table/smart-table/MobileView.tsx +170 -0
- package/registry/ui/smart-table/smart-table/SmartTable.tsx +85 -0
- package/registry/ui/smart-table/smart-table/SmartTableActions.tsx +71 -0
- package/registry/ui/smart-table/smart-table/TruncatedContent.tsx +147 -0
- package/registry/ui/smart-table/smart-table/index.ts +15 -0
- package/registry/ui/smart-table/smart-table/sorting.ts +148 -0
- package/registry/ui/smart-table/smart-table/truncated-content.utils.ts +22 -0
- package/registry/ui/smart-table/smart-table/types.ts +95 -0
- package/registry/ui/smart-table/smart-table/utils.ts +150 -0
- package/registry/ui/smart-table/utils/cn.ts +6 -0
- package/registry/ui/smart-table/utils/date-display.ts +61 -0
- package/registry/ui/smart-table/utils/use-media-query.ts +28 -0
- package/registry/ui/switch/manifest.json +31 -0
- package/registry/ui/switch/primitives/switch.tsx +31 -0
- package/registry/ui/switch/utils/cn.ts +6 -0
- package/registry/ui/table/manifest.json +38 -0
- package/registry/ui/table/primitives/table.tsx +92 -0
- package/registry/ui/table/utils/cn.ts +6 -0
- package/registry/ui/table-toolbar/manifest.json +93 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/table-toolbar/primitives/button.tsx +89 -0
- package/registry/ui/table-toolbar/primitives/drawer.tsx +99 -0
- package/registry/ui/table-toolbar/primitives/input.tsx +19 -0
- package/registry/ui/table-toolbar/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/table-toolbar/primitives/sheet.tsx +103 -0
- package/registry/ui/table-toolbar/search/searchable-select-position.ts +95 -0
- package/registry/ui/table-toolbar/search/searchable-select.tsx +431 -0
- package/registry/ui/table-toolbar/table-toolbar/index.ts +9 -0
- package/registry/ui/table-toolbar/table-toolbar/table-toolbar.tsx +552 -0
- package/registry/ui/table-toolbar/utils/cn.ts +6 -0
- package/registry/ui/table-toolbar/utils/search.ts +227 -0
- package/registry/ui/table-toolbar/utils/use-media-query.ts +28 -0
- package/registry/ui/tabs/manifest.json +40 -0
- package/registry/ui/tabs/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/tabs/primitives/tabs.tsx +97 -0
- package/registry/ui/tabs/utils/cn.ts +6 -0
- package/registry/ui/textarea/manifest.json +31 -0
- package/registry/ui/textarea/primitives/textarea.tsx +18 -0
- package/registry/ui/textarea/utils/cn.ts +6 -0
- package/registry/ui/tooltip/manifest.json +34 -0
- package/registry/ui/tooltip/primitives/tooltip.tsx +57 -0
- package/registry/ui/tooltip/utils/cn.ts +6 -0
- package/registry/ui/use-media-query/manifest.json +32 -0
- package/registry/ui/use-media-query/utils/cn.ts +6 -0
- package/registry/ui/use-media-query/utils/use-media-query.ts +28 -0
- package/registry/ui/user-picker/manifest.json +52 -0
- package/registry/ui/user-picker/primitives/avatar.tsx +64 -0
- package/registry/ui/user-picker/primitives/button.tsx +89 -0
- package/registry/ui/user-picker/primitives/input.tsx +19 -0
- package/registry/ui/user-picker/primitives/popover.tsx +46 -0
- package/registry/ui/user-picker/primitives/user-picker-utils.ts +113 -0
- package/registry/ui/user-picker/primitives/user-picker.tsx +226 -0
- package/registry/ui/user-picker/utils/cn.ts +6 -0
- package/registry/ui/user-picker-utils/manifest.json +38 -0
- package/registry/ui/user-picker-utils/primitives/user-picker-utils.ts +113 -0
- package/registry/ui/user-picker-utils/utils/cn.ts +6 -0
- package/assets/hero.png +0 -0
- package/registry/ui/button/cn.ts +0 -6
- /package/registry/ui/button/{button.tsx → primitives/button.tsx} +0 -0
package/README.md
CHANGED
|
@@ -1,122 +1,148 @@
|
|
|
1
1
|
# @carefully-built/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Carefully Built SaaS Kit is meant to be used in two complementary ways:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. **Managed package imports** from packages such as `@carefully-built/ui` when you want shared, upgradeable components.
|
|
6
|
+
2. **Editable source ejection** with this CLI when a component needs to become local app code.
|
|
6
7
|
|
|
7
|
-
The CLI
|
|
8
|
+
The CLI does not replace the package imports. It gives you the shadcn-style path for the same Carefully Built components.
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Component Previews
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/smarttable.png" alt="Smart Table" width="360" />
|
|
13
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/responsivesheet.png" alt="Responsive Sheet" width="360" />
|
|
14
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/kanban.png" alt="Kanban" width="360" />
|
|
15
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/charts.png" alt="Charts" width="360" />
|
|
16
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/notifications.png" alt="Notifications" width="360" />
|
|
17
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/search.png" alt="Search" width="360" />
|
|
18
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/calendar.png" alt="Calendar" width="360" />
|
|
19
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/maps.png" alt="Maps" width="360" />
|
|
20
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/superadmin.png" alt="Superadmin" width="360" />
|
|
21
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/widgets.png" alt="Widgets" width="360" />
|
|
22
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/file-dropper.png" alt="Files" width="360" />
|
|
23
|
+
<img src="https://raw.githubusercontent.com/AlessandroDodi/carefully-built-saas-kit/main/docs/saas-kit/images/theme-switcher.png" alt="Theme" width="360" />
|
|
12
24
|
|
|
13
|
-
|
|
25
|
+
## Managed Package Imports
|
|
26
|
+
|
|
27
|
+
Use package imports when you want the kit to stay shared and receive fixes through npm updates.
|
|
14
28
|
|
|
15
29
|
```bash
|
|
16
30
|
bun add @carefully-built/ui
|
|
17
31
|
```
|
|
18
32
|
|
|
19
33
|
```tsx
|
|
20
|
-
import { Button, SmartTable, TableToolbar } from "@carefully-built/ui";
|
|
34
|
+
import { Button, SmartTable, TableToolbar, ResponsiveSheet } from "@carefully-built/ui";
|
|
21
35
|
```
|
|
22
36
|
|
|
23
|
-
This is the
|
|
37
|
+
This mode is the default for stable primitives, CRUD surfaces, tables, overlays, hooks, and utilities that should remain consistent across apps.
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
## Editable Source With The CLI
|
|
26
40
|
|
|
27
|
-
Use the CLI when a component
|
|
41
|
+
Use the CLI when a component should become local code that you can edit directly.
|
|
28
42
|
|
|
29
43
|
```bash
|
|
44
|
+
bunx @carefully-built/cli list
|
|
30
45
|
bunx @carefully-built/cli add button
|
|
46
|
+
bunx @carefully-built/cli add smart-table
|
|
47
|
+
bunx @carefully-built/cli add responsive-sheet
|
|
31
48
|
```
|
|
32
49
|
|
|
33
|
-
The CLI
|
|
34
|
-
|
|
35
|
-
## Install And Commands
|
|
36
|
-
|
|
37
|
-
You can run it without installing:
|
|
50
|
+
The CLI copies the component source and its local dependency closure into your app. It preserves normal app imports such as `@/components/ui/button` and `@/lib/utils`.
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
bunx @carefully-built/cli list
|
|
41
|
-
bunx @carefully-built/cli add button
|
|
42
|
-
```
|
|
52
|
+
It reads common shadcn project conventions:
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
- `components.json` aliases such as `ui: "@/components/ui"` and `utils: "@/lib/utils"`
|
|
55
|
+
- `tsconfig.json` paths such as `@/* -> ./src/*`
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
npx @carefully-built/cli list
|
|
48
|
-
npx @carefully-built/cli add button
|
|
49
|
-
```
|
|
57
|
+
So a `src` app receives files under `src/components/ui` and `src/lib`; a root-style app receives files under `components/ui` and `lib`.
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
Use `--overwrite` only when replacing local files intentionally:
|
|
52
60
|
|
|
53
61
|
```bash
|
|
54
62
|
bunx @carefully-built/cli add button --overwrite
|
|
55
63
|
```
|
|
56
64
|
|
|
57
|
-
##
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
## Full Registry
|
|
66
|
+
|
|
67
|
+
Every public UI module exported by `@carefully-built/ui` is available through the CLI registry.
|
|
68
|
+
|
|
69
|
+
| Registry entry | Managed import | Editable source |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `avatar` | `import { Avatar, AvatarFallback, AvatarImage } from "@carefully-built/ui"` | `bunx @carefully-built/cli add avatar` |
|
|
72
|
+
| `button` | `import { Button, ButtonSize, ButtonVariant, buttonVariants } from "@carefully-built/ui"` | `bunx @carefully-built/cli add button` |
|
|
73
|
+
| `calendar` | `import { Calendar } from "@carefully-built/ui"` | `bunx @carefully-built/cli add calendar` |
|
|
74
|
+
| `card` | `import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@carefully-built/ui"` | `bunx @carefully-built/cli add card` |
|
|
75
|
+
| `chip` | `import { Chip, ChipButton } from "@carefully-built/ui"` | `bunx @carefully-built/cli add chip` |
|
|
76
|
+
| `chip-utils` | `import { CHIP_CLASS_NAMES, ChipSize, getChipClassName } from "@carefully-built/ui"` | `bunx @carefully-built/cli add chip-utils` |
|
|
77
|
+
| `date-display` | `import { DateDisplayValue, formatAbsoluteDate, formatDisplayDate } from "@carefully-built/ui"` | `bunx @carefully-built/cli add date-display` |
|
|
78
|
+
| `dialog` | `import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay } from "@carefully-built/ui"` | `bunx @carefully-built/cli add dialog` |
|
|
79
|
+
| `display-date` | `import { DisplayDate } from "@carefully-built/ui"` | `bunx @carefully-built/cli add display-date` |
|
|
80
|
+
| `drawer` | `import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerOverlay, DrawerPortal } from "@carefully-built/ui"` | `bunx @carefully-built/cli add drawer` |
|
|
81
|
+
| `dropdown-menu` | `import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator } from "@carefully-built/ui"` | `bunx @carefully-built/cli add dropdown-menu` |
|
|
82
|
+
| `empty-state` | `import { CollectionEmptyState, EmptyStateCard, InitialEmptyState, NoResultsState, ResolveCollectionEmptyStateOptions, resolveCollectionEmptyState } from "@carefully-built/ui"` | `bunx @carefully-built/cli add empty-state` |
|
|
83
|
+
| `error-page` | `import { ErrorCode, ErrorPageContent, ErrorPageKind, PostHogErrorCapturePayload, ResolveErrorPageContentOptions, SaasErrorPage } from "@carefully-built/ui"` | `bunx @carefully-built/cli add error-page` |
|
|
84
|
+
| `field-detail-row` | `import { FieldDetailRow } from "@carefully-built/ui"` | `bunx @carefully-built/cli add field-detail-row` |
|
|
85
|
+
| `file-dropzone` | `import { FileDropzone } from "@carefully-built/ui"` | `bunx @carefully-built/cli add file-dropzone` |
|
|
86
|
+
| `help-info-button` | `import { HelpInfoButton } from "@carefully-built/ui"` | `bunx @carefully-built/cli add help-info-button` |
|
|
87
|
+
| `input` | `import { Input } from "@carefully-built/ui"` | `bunx @carefully-built/cli add input` |
|
|
88
|
+
| `keyboard-shortcut-hint` | `import { KeyboardKeycap, ShortcutModifierKeycap } from "@carefully-built/ui"` | `bunx @carefully-built/cli add keyboard-shortcut-hint` |
|
|
89
|
+
| `label` | `import { Label } from "@carefully-built/ui"` | `bunx @carefully-built/cli add label` |
|
|
90
|
+
| `pagination` | `import { Pagination } from "@carefully-built/ui"` | `bunx @carefully-built/cli add pagination` |
|
|
91
|
+
| `popover` | `import { Popover, PopoverContent, PopoverTrigger } from "@carefully-built/ui"` | `bunx @carefully-built/cli add popover` |
|
|
92
|
+
| `responsive-sheet` | `import { ResponsiveSheet, SheetOutsideInteractionGuard } from "@carefully-built/ui"` | `bunx @carefully-built/cli add responsive-sheet` |
|
|
93
|
+
| `responsive-sheet.footer` | `import { DesktopConfirmShortcutHint, SheetActionFooter } from "@carefully-built/ui"` | `bunx @carefully-built/cli add responsive-sheet.footer` |
|
|
94
|
+
| `responsive-sheet.shortcuts` | `import { getDesktopShortcutModifierLabel, isAllowedConfirmShortcutEvent, useDesktopConfirmShortcut, useDesktopShortcutModifierLabel } from "@carefully-built/ui"` | `bunx @carefully-built/cli add responsive-sheet.shortcuts` |
|
|
95
|
+
| `scroll-fade-area` | `import { ScrollFadeArea } from "@carefully-built/ui"` | `bunx @carefully-built/cli add scroll-fade-area` |
|
|
96
|
+
| `search` | `import { SearchTextPart, buildSearchText, filterAndRankBySearch, rankBySearch, scoreFuzzyMatch } from "@carefully-built/ui"` | `bunx @carefully-built/cli add search` |
|
|
97
|
+
| `searchable-select` | `import { AUTO_SEARCHABLE_SELECT_THRESHOLD, SearchableSelect, SearchableSelectOption, getSearchableSelectPortalContainer, isSearchableSelectPointerInside } from "@carefully-built/ui"` | `bunx @carefully-built/cli add searchable-select` |
|
|
98
|
+
| `searchable-select-position` | `import { SearchableSelectRect, resolveSearchableSelectDropdownPosition } from "@carefully-built/ui"` | `bunx @carefully-built/cli add searchable-select-position` |
|
|
99
|
+
| `segmented-toggle` | `import { SegmentedToggle, SegmentedToggleOption } from "@carefully-built/ui"` | `bunx @carefully-built/cli add segmented-toggle` |
|
|
100
|
+
| `select` | `import { Select, SelectContent, SelectItem, SelectScrollDownButton, SelectScrollUpButton, SelectTrigger } from "@carefully-built/ui"` | `bunx @carefully-built/cli add select` |
|
|
101
|
+
| `sheet` | `import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@carefully-built/ui"` | `bunx @carefully-built/cli add sheet` |
|
|
102
|
+
| `skeleton` | `import { Skeleton } from "@carefully-built/ui"` | `bunx @carefully-built/cli add skeleton` |
|
|
103
|
+
| `smart-table` | `import { ActionHandlers, ActionType, Column, ColumnAlign, PaginationConfig, SmartTable } from "@carefully-built/ui"` | `bunx @carefully-built/cli add smart-table` |
|
|
104
|
+
| `switch` | `import { Switch } from "@carefully-built/ui"` | `bunx @carefully-built/cli add switch` |
|
|
105
|
+
| `table` | `import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead } from "@carefully-built/ui"` | `bunx @carefully-built/cli add table` |
|
|
106
|
+
| `table-toolbar` | `import { CustomTableToolbarFilter, FilterConfig, FilterDropdown, FilterOption, SearchInput, TableToolbar } from "@carefully-built/ui"` | `bunx @carefully-built/cli add table-toolbar` |
|
|
107
|
+
| `tabs` | `import { Tabs, TabsContent, TabsList, TabsScrollArea, TabsTrigger, tabsListVariants } from "@carefully-built/ui"` | `bunx @carefully-built/cli add tabs` |
|
|
108
|
+
| `textarea` | `import { Textarea } from "@carefully-built/ui"` | `bunx @carefully-built/cli add textarea` |
|
|
109
|
+
| `tooltip` | `import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@carefully-built/ui"` | `bunx @carefully-built/cli add tooltip` |
|
|
110
|
+
| `use-media-query` | `import { useIsMobile, useMediaQuery } from "@carefully-built/ui"` | `bunx @carefully-built/cli add use-media-query` |
|
|
111
|
+
| `user-picker` | `import { UserPicker } from "@carefully-built/ui"` | `bunx @carefully-built/cli add user-picker` |
|
|
112
|
+
| `user-picker-utils` | `import { UserPickerCopy, UserPickerOption, buildUserInitials, filterSelectableUsers, filterUsersBySearch, formatSelectedUserSummary } from "@carefully-built/ui"` | `bunx @carefully-built/cli add user-picker-utils` |
|
|
113
|
+
|
|
114
|
+
Some entries export several related components, hooks, helpers, and types from one module. The import column shows the main exported symbols; TypeScript autocomplete will show the full API after installing `@carefully-built/ui`.
|
|
115
|
+
|
|
116
|
+
## Dependency Notes
|
|
117
|
+
|
|
118
|
+
The CLI prints dependency hints after copying. Depending on the component, your app may need packages such as:
|
|
76
119
|
|
|
77
120
|
- `class-variance-authority`
|
|
78
121
|
- `clsx`
|
|
79
122
|
- `tailwind-merge`
|
|
80
123
|
- `react`
|
|
124
|
+
- `react-dom`
|
|
81
125
|
- `radix-ui`
|
|
126
|
+
- `lucide-react`
|
|
127
|
+
- `react-day-picker`
|
|
128
|
+
- `vaul`
|
|
82
129
|
|
|
83
|
-
|
|
130
|
+
Most Carefully Built apps already have these through the shared kit stack.
|
|
84
131
|
|
|
85
|
-
##
|
|
132
|
+
## Import vs Eject
|
|
86
133
|
|
|
87
134
|
Import from `@carefully-built/ui` when:
|
|
88
135
|
|
|
89
|
-
- you want package updates and bug fixes
|
|
90
|
-
- the component
|
|
91
|
-
-
|
|
136
|
+
- you want package updates and bug fixes
|
|
137
|
+
- the component should stay shared across apps
|
|
138
|
+
- product-specific differences can be handled with props, slots, `className`, or `classes`
|
|
92
139
|
|
|
93
140
|
Use `@carefully-built/cli add` when:
|
|
94
141
|
|
|
95
|
-
- the component
|
|
96
|
-
- design
|
|
97
|
-
- you want to
|
|
98
|
-
- the component should follow the
|
|
99
|
-
|
|
100
|
-
## Current Registry
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
bunx @carefully-built/cli list
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Available now:
|
|
107
|
-
|
|
108
|
-
- `button`
|
|
109
|
-
|
|
110
|
-
More registry entries should be added deliberately, starting with low-risk primitives and then moving toward composed SaaS surfaces such as tables, toolbars, forms, and shells.
|
|
111
|
-
|
|
112
|
-
## Project Assumptions
|
|
113
|
-
|
|
114
|
-
The generated components assume a modern React app with:
|
|
115
|
-
|
|
116
|
-
- React 18 or 19
|
|
117
|
-
- TypeScript
|
|
118
|
-
- Tailwind-compatible design tokens such as `bg-primary`, `text-primary-foreground`, `border`, `ring`, and `muted`
|
|
119
|
-
- an alias for `@/lib/utils`, or a project structure where the CLI can resolve that alias through `components.json` and `tsconfig.json`
|
|
142
|
+
- the component needs local product behavior
|
|
143
|
+
- design needs to diverge from the shared package
|
|
144
|
+
- you want to own and edit every line in the consuming app
|
|
145
|
+
- the component should follow the app's normal source-control workflow
|
|
120
146
|
|
|
121
147
|
## Development
|
|
122
148
|
|
|
@@ -124,6 +150,7 @@ From the monorepo root:
|
|
|
124
150
|
|
|
125
151
|
```bash
|
|
126
152
|
bun install
|
|
153
|
+
bun run --cwd packages/cli registry:build
|
|
127
154
|
bun run --cwd packages/cli test
|
|
128
155
|
bun run --cwd packages/cli typecheck
|
|
129
156
|
bun run --cwd packages/cli build
|
|
@@ -135,9 +162,3 @@ Before publishing:
|
|
|
135
162
|
cd packages/cli
|
|
136
163
|
npm publish --dry-run --access public
|
|
137
164
|
```
|
|
138
|
-
|
|
139
|
-
Publish:
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
npm publish --access public
|
|
143
|
-
```
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { constants, readFileSync } from "node:fs";
|
|
2
|
+
import { constants, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { access, copyFile, mkdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, normalize } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
//#region src/registry.ts
|
|
8
8
|
const registryRoot = join(join(dirname(fileURLToPath(import.meta.url)), ".."), "registry");
|
|
9
|
-
const componentNames = ["button"];
|
|
10
9
|
function listRegistryComponents() {
|
|
11
|
-
return
|
|
10
|
+
return readdirSync(join(registryRoot, "ui"), { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
12
11
|
}
|
|
13
12
|
function getRegistryComponent(componentName) {
|
|
14
|
-
if (!componentNames.includes(componentName)) return;
|
|
15
13
|
const manifestPath = join(registryRoot, "ui", componentName, "manifest.json");
|
|
16
|
-
|
|
14
|
+
let manifest;
|
|
15
|
+
try {
|
|
16
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
17
|
+
} catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
17
20
|
return {
|
|
18
21
|
...manifest,
|
|
19
22
|
files: manifest.files.map((file) => ({
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["created: string[]","overwritten: string[]","skipped: string[]"],"sources":["../src/registry.ts","../src/add.ts","../src/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface RegistryFile {\n readonly source: string;\n readonly target: string;\n}\n\nexport interface RegistryComponent {\n readonly name: string;\n readonly description: string;\n readonly dependencies: readonly string[];\n readonly peerDependencies: readonly string[];\n readonly files: readonly RegistryFile[];\n}\n\nconst packageRoot = join(dirname(fileURLToPath(import.meta.url)), \"..\");\nconst registryRoot = join(packageRoot, \"registry\");\nconst componentNames = [\"button\"] as const;\n\nexport function listRegistryComponents(): string[] {\n return [...componentNames];\n}\n\nexport function getRegistryComponent(\n componentName: string,\n): RegistryComponent | undefined {\n if (!componentNames.includes(componentName as (typeof componentNames)[number])) {\n return undefined;\n }\n\n const manifestPath = join(registryRoot, \"ui\", componentName, \"manifest.json\");\n const manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as Omit<\n RegistryComponent,\n \"files\"\n > & {\n files: readonly RegistryFile[];\n };\n\n return {\n ...manifest,\n files: manifest.files.map((file) => ({\n ...file,\n source: join(registryRoot, \"ui\", componentName, file.source),\n })),\n };\n}\n","import { constants } from \"node:fs\";\nimport { access, copyFile, mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, normalize } from \"node:path\";\n\nimport { getRegistryComponent } from \"./registry\";\n\nexport interface AddComponentOptions {\n readonly componentName: string;\n readonly cwd: string;\n readonly overwrite: boolean;\n}\n\nexport interface AddComponentResult {\n readonly componentName: string;\n readonly created: string[];\n readonly overwritten: string[];\n readonly skipped: string[];\n readonly dependencies: readonly string[];\n readonly peerDependencies: readonly string[];\n}\n\nexport async function addComponent({\n componentName,\n cwd,\n overwrite,\n}: AddComponentOptions): Promise<AddComponentResult> {\n const component = getRegistryComponent(componentName);\n\n if (!component) {\n throw new Error(`Unknown component \"${componentName}\"`);\n }\n\n const created: string[] = [];\n const overwritten: string[] = [];\n const skipped: string[] = [];\n const projectConfig = await readProjectConfig(cwd);\n\n for (const file of component.files) {\n const target = resolveTargetPath(file.target, projectConfig);\n const targetPath = join(cwd, target);\n const exists = await fileExists(targetPath);\n\n if (exists && !overwrite) {\n skipped.push(target);\n continue;\n }\n\n await mkdir(dirname(targetPath), { recursive: true });\n await copyFile(file.source, targetPath);\n\n if (exists) {\n overwritten.push(target);\n } else {\n created.push(target);\n }\n }\n\n return {\n componentName: component.name,\n created,\n overwritten,\n skipped,\n dependencies: component.dependencies,\n peerDependencies: component.peerDependencies,\n };\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\ninterface ProjectConfig {\n readonly uiAlias?: string;\n readonly utilsAlias?: string;\n readonly aliasPrefix?: string;\n readonly aliasTarget?: string;\n}\n\nasync function readProjectConfig(cwd: string): Promise<ProjectConfig> {\n const [componentsJson, tsconfigJson] = await Promise.all([\n readJsonFile(join(cwd, \"components.json\")),\n readJsonFile(join(cwd, \"tsconfig.json\")),\n ]);\n\n return {\n uiAlias: readString(componentsJson, [\"aliases\", \"ui\"]),\n utilsAlias: readString(componentsJson, [\"aliases\", \"utils\"]),\n ...readPrimaryTsconfigAlias(tsconfigJson),\n };\n}\n\nfunction resolveTargetPath(target: string, config: ProjectConfig): string {\n if (target === \"lib/utils.ts\" && config.utilsAlias) {\n return resolveAliasPath(`${config.utilsAlias}.ts`, config);\n }\n\n if (target.startsWith(\"components/ui/\") && config.uiAlias) {\n const fileName = target.slice(\"components/ui/\".length);\n return resolveAliasPath(`${config.uiAlias}/${fileName}`, config);\n }\n\n return target;\n}\n\nfunction resolveAliasPath(path: string, config: ProjectConfig): string {\n if (\n config.aliasPrefix &&\n config.aliasTarget &&\n path.startsWith(config.aliasPrefix)\n ) {\n return cleanPath(path.replace(config.aliasPrefix, config.aliasTarget));\n }\n\n if (path.startsWith(\"@/\")) {\n return cleanPath(path.slice(2));\n }\n\n return cleanPath(path);\n}\n\nfunction readPrimaryTsconfigAlias(value: unknown): Pick<\n ProjectConfig,\n \"aliasPrefix\" | \"aliasTarget\"\n> {\n const paths = readRecord(readRecord(value, \"compilerOptions\"), \"paths\");\n const aliasTargets = readArray(paths, \"@/*\");\n const aliasTarget = aliasTargets.find((entry) => typeof entry === \"string\");\n\n if (typeof aliasTarget !== \"string\") {\n return {};\n }\n\n return {\n aliasPrefix: \"@/\",\n aliasTarget: aliasTarget.replace(/\\/\\*$/, \"/\"),\n };\n}\n\nasync function readJsonFile(path: string): Promise<unknown> {\n try {\n return JSON.parse(stripJsonComments(await readFile(path, \"utf8\")));\n } catch {\n return undefined;\n }\n}\n\nfunction stripJsonComments(source: string): string {\n return source.replace(/^\\s*\\/\\/.*$/gm, \"\");\n}\n\nfunction readString(value: unknown, path: readonly string[]): string | undefined {\n let current = value;\n\n for (const key of path) {\n current = readRecord(current, key);\n }\n\n return typeof current === \"string\" ? current : undefined;\n}\n\nfunction readRecord(value: unknown, key: string): unknown {\n if (!value || typeof value !== \"object\") {\n return undefined;\n }\n\n return (value as Record<string, unknown>)[key];\n}\n\nfunction readArray(value: unknown, key: string): unknown[] {\n const array = readRecord(value, key);\n return Array.isArray(array) ? array : [];\n}\n\nfunction cleanPath(path: string): string {\n return normalize(path).replace(/^\\.\\//, \"\");\n}\n","#!/usr/bin/env node\nimport { addComponent } from \"./add\";\nimport { listRegistryComponents } from \"./registry\";\n\ninterface ParsedArgs {\n readonly command?: string;\n readonly componentName?: string;\n readonly overwrite: boolean;\n readonly help: boolean;\n}\n\nexport async function runCli(argv = process.argv.slice(2)): Promise<void> {\n const args = parseArgs(argv);\n\n if (args.help || !args.command) {\n printHelp();\n return;\n }\n\n if (args.command === \"list\") {\n for (const componentName of listRegistryComponents()) {\n console.log(componentName);\n }\n return;\n }\n\n if (args.command === \"add\") {\n if (!args.componentName) {\n throw new Error(\"Missing component name. Example: carefully-built add button\");\n }\n\n const result = await addComponent({\n componentName: args.componentName,\n cwd: process.cwd(),\n overwrite: args.overwrite,\n });\n\n printAddResult(result);\n return;\n }\n\n throw new Error(`Unknown command \"${args.command}\"`);\n}\n\nfunction parseArgs(argv: readonly string[]): ParsedArgs {\n return {\n command: argv[0],\n componentName: argv[1]?.startsWith(\"-\") ? undefined : argv[1],\n overwrite: argv.includes(\"--overwrite\"),\n help: argv.includes(\"--help\") || argv.includes(\"-h\"),\n };\n}\n\nfunction printHelp(): void {\n console.log(`carefully-built\n\nUsage:\n carefully-built list\n carefully-built add <component> [--overwrite]\n\nComponents:\n ${listRegistryComponents().join(\", \")}\n`);\n}\n\nfunction printAddResult(result: Awaited<ReturnType<typeof addComponent>>): void {\n for (const file of result.created) {\n console.log(`created ${file}`);\n }\n for (const file of result.overwritten) {\n console.log(`overwrote ${file}`);\n }\n for (const file of result.skipped) {\n console.log(`skipped ${file}`);\n }\n\n if (result.dependencies.length > 0) {\n console.log(`dependencies: ${result.dependencies.join(\", \")}`);\n }\n if (result.peerDependencies.length > 0) {\n console.log(`peer dependencies: ${result.peerDependencies.join(\", \")}`);\n }\n}\n\nrunCli().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["manifest: Omit<RegistryComponent, \"files\"> & {\n files: readonly RegistryFile[];\n }","created: string[]","overwritten: string[]","skipped: string[]"],"sources":["../src/registry.ts","../src/add.ts","../src/index.ts"],"sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface RegistryFile {\n readonly source: string;\n readonly target: string;\n}\n\nexport interface RegistryComponent {\n readonly name: string;\n readonly description: string;\n readonly importPath?: string;\n readonly exports?: readonly string[];\n readonly dependencies: readonly string[];\n readonly peerDependencies: readonly string[];\n readonly files: readonly RegistryFile[];\n}\n\nconst packageRoot = join(dirname(fileURLToPath(import.meta.url)), \"..\");\nconst registryRoot = join(packageRoot, \"registry\");\n\nexport function listRegistryComponents(): string[] {\n return readdirSync(join(registryRoot, \"ui\"), { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n}\n\nexport function getRegistryComponent(\n componentName: string,\n): RegistryComponent | undefined {\n const manifestPath = join(registryRoot, \"ui\", componentName, \"manifest.json\");\n\n let manifest: Omit<RegistryComponent, \"files\"> & {\n files: readonly RegistryFile[];\n };\n\n try {\n manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as typeof manifest;\n } catch {\n return undefined;\n }\n\n return {\n ...manifest,\n files: manifest.files.map((file) => ({\n ...file,\n source: join(registryRoot, \"ui\", componentName, file.source),\n })),\n };\n}\n","import { constants } from \"node:fs\";\nimport { access, copyFile, mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, normalize } from \"node:path\";\n\nimport { getRegistryComponent } from \"./registry\";\n\nexport interface AddComponentOptions {\n readonly componentName: string;\n readonly cwd: string;\n readonly overwrite: boolean;\n}\n\nexport interface AddComponentResult {\n readonly componentName: string;\n readonly created: string[];\n readonly overwritten: string[];\n readonly skipped: string[];\n readonly dependencies: readonly string[];\n readonly peerDependencies: readonly string[];\n}\n\nexport async function addComponent({\n componentName,\n cwd,\n overwrite,\n}: AddComponentOptions): Promise<AddComponentResult> {\n const component = getRegistryComponent(componentName);\n\n if (!component) {\n throw new Error(`Unknown component \"${componentName}\"`);\n }\n\n const created: string[] = [];\n const overwritten: string[] = [];\n const skipped: string[] = [];\n const projectConfig = await readProjectConfig(cwd);\n\n for (const file of component.files) {\n const target = resolveTargetPath(file.target, projectConfig);\n const targetPath = join(cwd, target);\n const exists = await fileExists(targetPath);\n\n if (exists && !overwrite) {\n skipped.push(target);\n continue;\n }\n\n await mkdir(dirname(targetPath), { recursive: true });\n await copyFile(file.source, targetPath);\n\n if (exists) {\n overwritten.push(target);\n } else {\n created.push(target);\n }\n }\n\n return {\n componentName: component.name,\n created,\n overwritten,\n skipped,\n dependencies: component.dependencies,\n peerDependencies: component.peerDependencies,\n };\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\ninterface ProjectConfig {\n readonly uiAlias?: string;\n readonly utilsAlias?: string;\n readonly aliasPrefix?: string;\n readonly aliasTarget?: string;\n}\n\nasync function readProjectConfig(cwd: string): Promise<ProjectConfig> {\n const [componentsJson, tsconfigJson] = await Promise.all([\n readJsonFile(join(cwd, \"components.json\")),\n readJsonFile(join(cwd, \"tsconfig.json\")),\n ]);\n\n return {\n uiAlias: readString(componentsJson, [\"aliases\", \"ui\"]),\n utilsAlias: readString(componentsJson, [\"aliases\", \"utils\"]),\n ...readPrimaryTsconfigAlias(tsconfigJson),\n };\n}\n\nfunction resolveTargetPath(target: string, config: ProjectConfig): string {\n if (target === \"lib/utils.ts\" && config.utilsAlias) {\n return resolveAliasPath(`${config.utilsAlias}.ts`, config);\n }\n\n if (target.startsWith(\"components/ui/\") && config.uiAlias) {\n const fileName = target.slice(\"components/ui/\".length);\n return resolveAliasPath(`${config.uiAlias}/${fileName}`, config);\n }\n\n return target;\n}\n\nfunction resolveAliasPath(path: string, config: ProjectConfig): string {\n if (\n config.aliasPrefix &&\n config.aliasTarget &&\n path.startsWith(config.aliasPrefix)\n ) {\n return cleanPath(path.replace(config.aliasPrefix, config.aliasTarget));\n }\n\n if (path.startsWith(\"@/\")) {\n return cleanPath(path.slice(2));\n }\n\n return cleanPath(path);\n}\n\nfunction readPrimaryTsconfigAlias(value: unknown): Pick<\n ProjectConfig,\n \"aliasPrefix\" | \"aliasTarget\"\n> {\n const paths = readRecord(readRecord(value, \"compilerOptions\"), \"paths\");\n const aliasTargets = readArray(paths, \"@/*\");\n const aliasTarget = aliasTargets.find((entry) => typeof entry === \"string\");\n\n if (typeof aliasTarget !== \"string\") {\n return {};\n }\n\n return {\n aliasPrefix: \"@/\",\n aliasTarget: aliasTarget.replace(/\\/\\*$/, \"/\"),\n };\n}\n\nasync function readJsonFile(path: string): Promise<unknown> {\n try {\n return JSON.parse(stripJsonComments(await readFile(path, \"utf8\")));\n } catch {\n return undefined;\n }\n}\n\nfunction stripJsonComments(source: string): string {\n return source.replace(/^\\s*\\/\\/.*$/gm, \"\");\n}\n\nfunction readString(value: unknown, path: readonly string[]): string | undefined {\n let current = value;\n\n for (const key of path) {\n current = readRecord(current, key);\n }\n\n return typeof current === \"string\" ? current : undefined;\n}\n\nfunction readRecord(value: unknown, key: string): unknown {\n if (!value || typeof value !== \"object\") {\n return undefined;\n }\n\n return (value as Record<string, unknown>)[key];\n}\n\nfunction readArray(value: unknown, key: string): unknown[] {\n const array = readRecord(value, key);\n return Array.isArray(array) ? array : [];\n}\n\nfunction cleanPath(path: string): string {\n return normalize(path).replace(/^\\.\\//, \"\");\n}\n","#!/usr/bin/env node\nimport { addComponent } from \"./add\";\nimport { listRegistryComponents } from \"./registry\";\n\ninterface ParsedArgs {\n readonly command?: string;\n readonly componentName?: string;\n readonly overwrite: boolean;\n readonly help: boolean;\n}\n\nexport async function runCli(argv = process.argv.slice(2)): Promise<void> {\n const args = parseArgs(argv);\n\n if (args.help || !args.command) {\n printHelp();\n return;\n }\n\n if (args.command === \"list\") {\n for (const componentName of listRegistryComponents()) {\n console.log(componentName);\n }\n return;\n }\n\n if (args.command === \"add\") {\n if (!args.componentName) {\n throw new Error(\"Missing component name. Example: carefully-built add button\");\n }\n\n const result = await addComponent({\n componentName: args.componentName,\n cwd: process.cwd(),\n overwrite: args.overwrite,\n });\n\n printAddResult(result);\n return;\n }\n\n throw new Error(`Unknown command \"${args.command}\"`);\n}\n\nfunction parseArgs(argv: readonly string[]): ParsedArgs {\n return {\n command: argv[0],\n componentName: argv[1]?.startsWith(\"-\") ? undefined : argv[1],\n overwrite: argv.includes(\"--overwrite\"),\n help: argv.includes(\"--help\") || argv.includes(\"-h\"),\n };\n}\n\nfunction printHelp(): void {\n console.log(`carefully-built\n\nUsage:\n carefully-built list\n carefully-built add <component> [--overwrite]\n\nComponents:\n ${listRegistryComponents().join(\", \")}\n`);\n}\n\nfunction printAddResult(result: Awaited<ReturnType<typeof addComponent>>): void {\n for (const file of result.created) {\n console.log(`created ${file}`);\n }\n for (const file of result.overwritten) {\n console.log(`overwrote ${file}`);\n }\n for (const file of result.skipped) {\n console.log(`skipped ${file}`);\n }\n\n if (result.dependencies.length > 0) {\n console.log(`dependencies: ${result.dependencies.join(\", \")}`);\n }\n if (result.peerDependencies.length > 0) {\n console.log(`peer dependencies: ${result.peerDependencies.join(\", \")}`);\n }\n}\n\nrunCli().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carefully-built/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Add Carefully Built SaaS components to apps as editable source, with package imports when you want managed upgrades.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"carefully-built": "dist/index.mjs"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"assets",
|
|
29
28
|
"dist",
|
|
30
29
|
"registry",
|
|
31
30
|
"README.md"
|
|
@@ -40,7 +39,8 @@
|
|
|
40
39
|
},
|
|
41
40
|
"scripts": {
|
|
42
41
|
"build": "tsdown src/index.ts --format esm --dts",
|
|
43
|
-
"prepublishOnly": "bun run typecheck && bun run test && bun run build",
|
|
42
|
+
"prepublishOnly": "bun run registry:build && bun run typecheck && bun run test && bun run build",
|
|
43
|
+
"registry:build": "node scripts/build-registry.mjs",
|
|
44
44
|
"test": "bun test",
|
|
45
45
|
"typecheck": "tsc --noEmit"
|
|
46
46
|
},
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avatar",
|
|
3
|
+
"description": "Editable source registry entry for avatar.",
|
|
4
|
+
"importPath": "@carefully-built/ui",
|
|
5
|
+
"exports": [
|
|
6
|
+
"Avatar",
|
|
7
|
+
"AvatarFallback",
|
|
8
|
+
"AvatarImage"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": [
|
|
11
|
+
"class-variance-authority",
|
|
12
|
+
"clsx",
|
|
13
|
+
"tailwind-merge"
|
|
14
|
+
],
|
|
15
|
+
"peerDependencies": [
|
|
16
|
+
"react",
|
|
17
|
+
"react-dom",
|
|
18
|
+
"radix-ui",
|
|
19
|
+
"lucide-react",
|
|
20
|
+
"react-day-picker",
|
|
21
|
+
"vaul"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
{
|
|
25
|
+
"source": "primitives/avatar.tsx",
|
|
26
|
+
"target": "components/ui/avatar.tsx"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"source": "utils/cn.ts",
|
|
30
|
+
"target": "lib/utils.ts"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Avatar as AvatarPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
size = "default",
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
|
13
|
+
size?: "default" | "sm" | "lg"
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<AvatarPrimitive.Root
|
|
17
|
+
data-slot="avatar"
|
|
18
|
+
data-size={size}
|
|
19
|
+
className={cn(
|
|
20
|
+
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AvatarImage({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
32
|
+
return (
|
|
33
|
+
<AvatarPrimitive.Image
|
|
34
|
+
data-slot="avatar-image"
|
|
35
|
+
className={cn(
|
|
36
|
+
"rounded-full aspect-square size-full object-cover",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function AvatarFallback({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
48
|
+
return (
|
|
49
|
+
<AvatarPrimitive.Fallback
|
|
50
|
+
data-slot="avatar-fallback"
|
|
51
|
+
className={cn(
|
|
52
|
+
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
Avatar,
|
|
62
|
+
AvatarImage,
|
|
63
|
+
AvatarFallback
|
|
64
|
+
}
|
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "button",
|
|
3
|
-
"description": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Editable source registry entry for button.",
|
|
4
|
+
"importPath": "@carefully-built/ui",
|
|
5
|
+
"exports": [
|
|
6
|
+
"Button",
|
|
7
|
+
"ButtonProps",
|
|
8
|
+
"ButtonSize",
|
|
9
|
+
"ButtonVariant",
|
|
10
|
+
"buttonVariants"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": [
|
|
13
|
+
"class-variance-authority",
|
|
14
|
+
"clsx",
|
|
15
|
+
"tailwind-merge"
|
|
16
|
+
],
|
|
17
|
+
"peerDependencies": [
|
|
18
|
+
"react",
|
|
19
|
+
"react-dom",
|
|
20
|
+
"radix-ui",
|
|
21
|
+
"lucide-react",
|
|
22
|
+
"react-day-picker",
|
|
23
|
+
"vaul"
|
|
24
|
+
],
|
|
6
25
|
"files": [
|
|
7
26
|
{
|
|
8
|
-
"source": "button.tsx",
|
|
27
|
+
"source": "primitives/button.tsx",
|
|
9
28
|
"target": "components/ui/button.tsx"
|
|
10
29
|
},
|
|
11
30
|
{
|
|
12
|
-
"source": "cn.ts",
|
|
31
|
+
"source": "utils/cn.ts",
|
|
13
32
|
"target": "lib/utils.ts"
|
|
14
33
|
}
|
|
15
34
|
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "calendar",
|
|
3
|
+
"description": "Editable source registry entry for calendar.",
|
|
4
|
+
"importPath": "@carefully-built/ui",
|
|
5
|
+
"exports": [
|
|
6
|
+
"Calendar"
|
|
7
|
+
],
|
|
8
|
+
"dependencies": [
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"clsx",
|
|
11
|
+
"tailwind-merge"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": [
|
|
14
|
+
"react",
|
|
15
|
+
"react-dom",
|
|
16
|
+
"radix-ui",
|
|
17
|
+
"lucide-react",
|
|
18
|
+
"react-day-picker",
|
|
19
|
+
"vaul"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
{
|
|
23
|
+
"source": "primitives/button.tsx",
|
|
24
|
+
"target": "components/ui/button.tsx"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"source": "primitives/calendar.tsx",
|
|
28
|
+
"target": "components/ui/calendar.tsx"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"source": "utils/cn.ts",
|
|
32
|
+
"target": "lib/utils.ts"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { Slot } from "radix-ui";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground hover:brightness-90 [a]:hover:bg-primary/80",
|
|
14
|
+
outline:
|
|
15
|
+
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
16
|
+
secondary:
|
|
17
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
20
|
+
destructive:
|
|
21
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default:
|
|
26
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
28
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
29
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
30
|
+
icon: "size-8",
|
|
31
|
+
"icon-xs":
|
|
32
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
"icon-sm":
|
|
34
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
35
|
+
"icon-lg": "size-9",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default",
|
|
40
|
+
size: "default",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
type ButtonVariant =
|
|
46
|
+
| "default"
|
|
47
|
+
| "outline"
|
|
48
|
+
| "secondary"
|
|
49
|
+
| "ghost"
|
|
50
|
+
| "destructive"
|
|
51
|
+
| "link";
|
|
52
|
+
type ButtonSize =
|
|
53
|
+
| "default"
|
|
54
|
+
| "xs"
|
|
55
|
+
| "sm"
|
|
56
|
+
| "lg"
|
|
57
|
+
| "icon"
|
|
58
|
+
| "icon-xs"
|
|
59
|
+
| "icon-sm"
|
|
60
|
+
| "icon-lg";
|
|
61
|
+
|
|
62
|
+
interface ButtonProps extends React.ComponentProps<"button"> {
|
|
63
|
+
readonly asChild?: boolean;
|
|
64
|
+
readonly size?: ButtonSize;
|
|
65
|
+
readonly variant?: ButtonVariant;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function Button({
|
|
69
|
+
className,
|
|
70
|
+
variant = "default",
|
|
71
|
+
size = "default",
|
|
72
|
+
asChild = false,
|
|
73
|
+
...props
|
|
74
|
+
}: ButtonProps) {
|
|
75
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Comp
|
|
79
|
+
data-slot="button"
|
|
80
|
+
data-variant={variant}
|
|
81
|
+
data-size={size}
|
|
82
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { Button, buttonVariants };
|
|
89
|
+
export type { ButtonProps, ButtonSize, ButtonVariant };
|