@carefully-built/cli 0.1.0 → 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 +148 -7
- package/dist/index.mjs +71 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -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/registry/ui/button/cn.ts +0 -6
- /package/registry/ui/button/{button.tsx → primitives/button.tsx} +0 -0
package/README.md
CHANGED
|
@@ -1,23 +1,164 @@
|
|
|
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.
|
|
7
|
+
|
|
8
|
+
The CLI does not replace the package imports. It gives you the shadcn-style path for the same Carefully Built components.
|
|
9
|
+
|
|
10
|
+
## Component Previews
|
|
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" />
|
|
24
|
+
|
|
25
|
+
## Managed Package Imports
|
|
26
|
+
|
|
27
|
+
Use package imports when you want the kit to stay shared and receive fixes through npm updates.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun add @carefully-built/ui
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { Button, SmartTable, TableToolbar, ResponsiveSheet } from "@carefully-built/ui";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This mode is the default for stable primitives, CRUD surfaces, tables, overlays, hooks, and utilities that should remain consistent across apps.
|
|
38
|
+
|
|
39
|
+
## Editable Source With The CLI
|
|
40
|
+
|
|
41
|
+
Use the CLI when a component should become local code that you can edit directly.
|
|
6
42
|
|
|
7
43
|
```bash
|
|
8
44
|
bunx @carefully-built/cli list
|
|
9
45
|
bunx @carefully-built/cli add button
|
|
46
|
+
bunx @carefully-built/cli add smart-table
|
|
47
|
+
bunx @carefully-built/cli add responsive-sheet
|
|
10
48
|
```
|
|
11
49
|
|
|
12
|
-
|
|
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`.
|
|
51
|
+
|
|
52
|
+
It reads common shadcn project conventions:
|
|
53
|
+
|
|
54
|
+
- `components.json` aliases such as `ui: "@/components/ui"` and `utils: "@/lib/utils"`
|
|
55
|
+
- `tsconfig.json` paths such as `@/* -> ./src/*`
|
|
56
|
+
|
|
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`.
|
|
58
|
+
|
|
59
|
+
Use `--overwrite` only when replacing local files intentionally:
|
|
13
60
|
|
|
14
61
|
```bash
|
|
15
62
|
bunx @carefully-built/cli add button --overwrite
|
|
16
63
|
```
|
|
17
64
|
|
|
18
|
-
|
|
65
|
+
## Full Registry
|
|
66
|
+
|
|
67
|
+
Every public UI module exported by `@carefully-built/ui` is available through the CLI registry.
|
|
19
68
|
|
|
20
|
-
|
|
21
|
-
|
|
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` |
|
|
22
113
|
|
|
23
|
-
|
|
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:
|
|
119
|
+
|
|
120
|
+
- `class-variance-authority`
|
|
121
|
+
- `clsx`
|
|
122
|
+
- `tailwind-merge`
|
|
123
|
+
- `react`
|
|
124
|
+
- `react-dom`
|
|
125
|
+
- `radix-ui`
|
|
126
|
+
- `lucide-react`
|
|
127
|
+
- `react-day-picker`
|
|
128
|
+
- `vaul`
|
|
129
|
+
|
|
130
|
+
Most Carefully Built apps already have these through the shared kit stack.
|
|
131
|
+
|
|
132
|
+
## Import vs Eject
|
|
133
|
+
|
|
134
|
+
Import from `@carefully-built/ui` when:
|
|
135
|
+
|
|
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`
|
|
139
|
+
|
|
140
|
+
Use `@carefully-built/cli add` when:
|
|
141
|
+
|
|
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
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
From the monorepo root:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
bun install
|
|
153
|
+
bun run --cwd packages/cli registry:build
|
|
154
|
+
bun run --cwd packages/cli test
|
|
155
|
+
bun run --cwd packages/cli typecheck
|
|
156
|
+
bun run --cwd packages/cli build
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Before publishing:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd packages/cli
|
|
163
|
+
npm publish --dry-run --access public
|
|
164
|
+
```
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { constants, readFileSync } from "node:fs";
|
|
3
|
-
import { access, copyFile, mkdir } from "node:fs/promises";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { constants, readFileSync, readdirSync } from "node:fs";
|
|
3
|
+
import { access, copyFile, mkdir, readFile } from "node:fs/promises";
|
|
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) => ({
|
|
@@ -31,17 +34,19 @@ async function addComponent({ componentName, cwd, overwrite }) {
|
|
|
31
34
|
const created = [];
|
|
32
35
|
const overwritten = [];
|
|
33
36
|
const skipped = [];
|
|
37
|
+
const projectConfig = await readProjectConfig(cwd);
|
|
34
38
|
for (const file of component.files) {
|
|
35
|
-
const
|
|
39
|
+
const target = resolveTargetPath(file.target, projectConfig);
|
|
40
|
+
const targetPath = join(cwd, target);
|
|
36
41
|
const exists = await fileExists(targetPath);
|
|
37
42
|
if (exists && !overwrite) {
|
|
38
|
-
skipped.push(
|
|
43
|
+
skipped.push(target);
|
|
39
44
|
continue;
|
|
40
45
|
}
|
|
41
46
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
42
47
|
await copyFile(file.source, targetPath);
|
|
43
|
-
if (exists) overwritten.push(
|
|
44
|
-
else created.push(
|
|
48
|
+
if (exists) overwritten.push(target);
|
|
49
|
+
else created.push(target);
|
|
45
50
|
}
|
|
46
51
|
return {
|
|
47
52
|
componentName: component.name,
|
|
@@ -60,6 +65,61 @@ async function fileExists(path) {
|
|
|
60
65
|
return false;
|
|
61
66
|
}
|
|
62
67
|
}
|
|
68
|
+
async function readProjectConfig(cwd) {
|
|
69
|
+
const [componentsJson, tsconfigJson] = await Promise.all([readJsonFile(join(cwd, "components.json")), readJsonFile(join(cwd, "tsconfig.json"))]);
|
|
70
|
+
return {
|
|
71
|
+
uiAlias: readString(componentsJson, ["aliases", "ui"]),
|
|
72
|
+
utilsAlias: readString(componentsJson, ["aliases", "utils"]),
|
|
73
|
+
...readPrimaryTsconfigAlias(tsconfigJson)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function resolveTargetPath(target, config) {
|
|
77
|
+
if (target === "lib/utils.ts" && config.utilsAlias) return resolveAliasPath(`${config.utilsAlias}.ts`, config);
|
|
78
|
+
if (target.startsWith("components/ui/") && config.uiAlias) {
|
|
79
|
+
const fileName = target.slice(14);
|
|
80
|
+
return resolveAliasPath(`${config.uiAlias}/${fileName}`, config);
|
|
81
|
+
}
|
|
82
|
+
return target;
|
|
83
|
+
}
|
|
84
|
+
function resolveAliasPath(path, config) {
|
|
85
|
+
if (config.aliasPrefix && config.aliasTarget && path.startsWith(config.aliasPrefix)) return cleanPath(path.replace(config.aliasPrefix, config.aliasTarget));
|
|
86
|
+
if (path.startsWith("@/")) return cleanPath(path.slice(2));
|
|
87
|
+
return cleanPath(path);
|
|
88
|
+
}
|
|
89
|
+
function readPrimaryTsconfigAlias(value) {
|
|
90
|
+
const aliasTarget = readArray(readRecord(readRecord(value, "compilerOptions"), "paths"), "@/*").find((entry) => typeof entry === "string");
|
|
91
|
+
if (typeof aliasTarget !== "string") return {};
|
|
92
|
+
return {
|
|
93
|
+
aliasPrefix: "@/",
|
|
94
|
+
aliasTarget: aliasTarget.replace(/\/\*$/, "/")
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function readJsonFile(path) {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(stripJsonComments(await readFile(path, "utf8")));
|
|
100
|
+
} catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function stripJsonComments(source) {
|
|
105
|
+
return source.replace(/^\s*\/\/.*$/gm, "");
|
|
106
|
+
}
|
|
107
|
+
function readString(value, path) {
|
|
108
|
+
let current = value;
|
|
109
|
+
for (const key of path) current = readRecord(current, key);
|
|
110
|
+
return typeof current === "string" ? current : void 0;
|
|
111
|
+
}
|
|
112
|
+
function readRecord(value, key) {
|
|
113
|
+
if (!value || typeof value !== "object") return;
|
|
114
|
+
return value[key];
|
|
115
|
+
}
|
|
116
|
+
function readArray(value, key) {
|
|
117
|
+
const array = readRecord(value, key);
|
|
118
|
+
return Array.isArray(array) ? array : [];
|
|
119
|
+
}
|
|
120
|
+
function cleanPath(path) {
|
|
121
|
+
return normalize(path).replace(/^\.\//, "");
|
|
122
|
+
}
|
|
63
123
|
|
|
64
124
|
//#endregion
|
|
65
125
|
//#region src/index.ts
|
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 } from \"node:fs/promises\";\nimport { dirname, join } 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\n for (const file of component.files) {\n const targetPath = join(cwd, file.target);\n const exists = await fileExists(targetPath);\n\n if (exists && !overwrite) {\n skipped.push(file.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(file.target);\n } else {\n created.push(file.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","#!/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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carefully-built/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
7
7
|
"author": "Alessandro Dodi",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsdown src/index.ts --format esm --dts",
|
|
42
|
-
"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",
|
|
43
44
|
"test": "bun test",
|
|
44
45
|
"typecheck": "tsc --noEmit"
|
|
45
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 };
|