@journal-ds/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +1108 -0
  5. package/package.json +61 -0
  6. package/registry/accordion.tsx +66 -0
  7. package/registry/alert-dialog.tsx +157 -0
  8. package/registry/alert.tsx +66 -0
  9. package/registry/aspect-ratio.tsx +11 -0
  10. package/registry/avatar.tsx +53 -0
  11. package/registry/badge.tsx +46 -0
  12. package/registry/breadcrumb.tsx +109 -0
  13. package/registry/button.tsx +59 -0
  14. package/registry/calendar.tsx +213 -0
  15. package/registry/card.tsx +92 -0
  16. package/registry/carousel.tsx +241 -0
  17. package/registry/chart.tsx +353 -0
  18. package/registry/checkbox.tsx +32 -0
  19. package/registry/collapsible.tsx +33 -0
  20. package/registry/command.tsx +186 -0
  21. package/registry/context-menu.tsx +252 -0
  22. package/registry/dialog.tsx +143 -0
  23. package/registry/drawer.tsx +135 -0
  24. package/registry/dropdown-menu.tsx +257 -0
  25. package/registry/form.tsx +167 -0
  26. package/registry/hover-card.tsx +44 -0
  27. package/registry/input-otp.tsx +77 -0
  28. package/registry/input.tsx +21 -0
  29. package/registry/label.tsx +24 -0
  30. package/registry/menubar.tsx +276 -0
  31. package/registry/navigation-menu.tsx +168 -0
  32. package/registry/pagination.tsx +127 -0
  33. package/registry/popover.tsx +48 -0
  34. package/registry/progress.tsx +31 -0
  35. package/registry/radio-group.tsx +45 -0
  36. package/registry/resizable.tsx +56 -0
  37. package/registry/scroll-area.tsx +58 -0
  38. package/registry/select.tsx +185 -0
  39. package/registry/separator.tsx +28 -0
  40. package/registry/sheet.tsx +139 -0
  41. package/registry/sidebar.tsx +726 -0
  42. package/registry/skeleton.tsx +13 -0
  43. package/registry/slider.tsx +63 -0
  44. package/registry/sonner.tsx +25 -0
  45. package/registry/switch.tsx +31 -0
  46. package/registry/table.tsx +116 -0
  47. package/registry/tabs.tsx +66 -0
  48. package/registry/textarea.tsx +18 -0
  49. package/registry/toast.tsx +129 -0
  50. package/registry/toaster.tsx +35 -0
  51. package/registry/toggle-group.tsx +73 -0
  52. package/registry/toggle.tsx +47 -0
  53. package/registry/tooltip.tsx +61 -0
  54. package/registry/use-mobile.ts +19 -0
  55. package/registry/use-toast.ts +194 -0
  56. package/registry/utils.ts +6 -0
  57. package/templates/globals.css +322 -0
  58. package/templates/utils.ts +6 -0
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener("change", onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener("change", onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -0,0 +1,194 @@
1
+ "use client"
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from "react"
5
+
6
+ import type {
7
+ ToastActionElement,
8
+ ToastProps,
9
+ } from "../components/toast"
10
+
11
+ const TOAST_LIMIT = 1
12
+ const TOAST_REMOVE_DELAY = 1000000
13
+
14
+ type ToasterToast = ToastProps & {
15
+ id: string
16
+ title?: React.ReactNode
17
+ description?: React.ReactNode
18
+ action?: ToastActionElement
19
+ }
20
+
21
+ const actionTypes = {
22
+ ADD_TOAST: "ADD_TOAST",
23
+ UPDATE_TOAST: "UPDATE_TOAST",
24
+ DISMISS_TOAST: "DISMISS_TOAST",
25
+ REMOVE_TOAST: "REMOVE_TOAST",
26
+ } as const
27
+
28
+ let count = 0
29
+
30
+ function genId() {
31
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
32
+ return count.toString()
33
+ }
34
+
35
+ type ActionType = typeof actionTypes
36
+
37
+ type Action =
38
+ | {
39
+ type: ActionType["ADD_TOAST"]
40
+ toast: ToasterToast
41
+ }
42
+ | {
43
+ type: ActionType["UPDATE_TOAST"]
44
+ toast: Partial<ToasterToast>
45
+ }
46
+ | {
47
+ type: ActionType["DISMISS_TOAST"]
48
+ toastId?: ToasterToast["id"]
49
+ }
50
+ | {
51
+ type: ActionType["REMOVE_TOAST"]
52
+ toastId?: ToasterToast["id"]
53
+ }
54
+
55
+ interface State {
56
+ toasts: ToasterToast[]
57
+ }
58
+
59
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
60
+
61
+ const addToRemoveQueue = (toastId: string) => {
62
+ if (toastTimeouts.has(toastId)) {
63
+ return
64
+ }
65
+
66
+ const timeout = setTimeout(() => {
67
+ toastTimeouts.delete(toastId)
68
+ dispatch({
69
+ type: "REMOVE_TOAST",
70
+ toastId: toastId,
71
+ })
72
+ }, TOAST_REMOVE_DELAY)
73
+
74
+ toastTimeouts.set(toastId, timeout)
75
+ }
76
+
77
+ export const reducer = (state: State, action: Action): State => {
78
+ switch (action.type) {
79
+ case "ADD_TOAST":
80
+ return {
81
+ ...state,
82
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
83
+ }
84
+
85
+ case "UPDATE_TOAST":
86
+ return {
87
+ ...state,
88
+ toasts: state.toasts.map((t) =>
89
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
90
+ ),
91
+ }
92
+
93
+ case "DISMISS_TOAST": {
94
+ const { toastId } = action
95
+
96
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
97
+ // but I'll keep it here for simplicity
98
+ if (toastId) {
99
+ addToRemoveQueue(toastId)
100
+ } else {
101
+ state.toasts.forEach((toast) => {
102
+ addToRemoveQueue(toast.id)
103
+ })
104
+ }
105
+
106
+ return {
107
+ ...state,
108
+ toasts: state.toasts.map((t) =>
109
+ t.id === toastId || toastId === undefined
110
+ ? {
111
+ ...t,
112
+ open: false,
113
+ }
114
+ : t
115
+ ),
116
+ }
117
+ }
118
+ case "REMOVE_TOAST":
119
+ if (action.toastId === undefined) {
120
+ return {
121
+ ...state,
122
+ toasts: [],
123
+ }
124
+ }
125
+ return {
126
+ ...state,
127
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
128
+ }
129
+ }
130
+ }
131
+
132
+ const listeners: Array<(state: State) => void> = []
133
+
134
+ let memoryState: State = { toasts: [] }
135
+
136
+ function dispatch(action: Action) {
137
+ memoryState = reducer(memoryState, action)
138
+ listeners.forEach((listener) => {
139
+ listener(memoryState)
140
+ })
141
+ }
142
+
143
+ type Toast = Omit<ToasterToast, "id">
144
+
145
+ function toast({ ...props }: Toast) {
146
+ const id = genId()
147
+
148
+ const update = (props: ToasterToast) =>
149
+ dispatch({
150
+ type: "UPDATE_TOAST",
151
+ toast: { ...props, id },
152
+ })
153
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
154
+
155
+ dispatch({
156
+ type: "ADD_TOAST",
157
+ toast: {
158
+ ...props,
159
+ id,
160
+ open: true,
161
+ onOpenChange: (open) => {
162
+ if (!open) dismiss()
163
+ },
164
+ },
165
+ })
166
+
167
+ return {
168
+ id: id,
169
+ dismiss,
170
+ update,
171
+ }
172
+ }
173
+
174
+ function useToast() {
175
+ const [state, setState] = React.useState<State>(memoryState)
176
+
177
+ React.useEffect(() => {
178
+ listeners.push(setState)
179
+ return () => {
180
+ const index = listeners.indexOf(setState)
181
+ if (index > -1) {
182
+ listeners.splice(index, 1)
183
+ }
184
+ }
185
+ }, [state])
186
+
187
+ return {
188
+ ...state,
189
+ toast,
190
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
191
+ }
192
+ }
193
+
194
+ export { useToast, toast }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,322 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ /* ── Journal Design System Tokens ── */
8
+
9
+ /* Fonts */
10
+ --font-serif: var(--font-lora);
11
+ --font-display: var(--font-playfair);
12
+ --font-mono: var(--font-geist-mono);
13
+
14
+ /* Core semantic colors */
15
+ --color-background: var(--background);
16
+ --color-foreground: var(--foreground);
17
+ --color-card: var(--card);
18
+ --color-card-foreground: var(--card-foreground);
19
+ --color-popover: var(--popover);
20
+ --color-popover-foreground: var(--popover-foreground);
21
+ --color-primary: var(--primary);
22
+ --color-primary-foreground: var(--primary-foreground);
23
+ --color-secondary: var(--secondary);
24
+ --color-secondary-foreground: var(--secondary-foreground);
25
+ --color-muted: var(--muted);
26
+ --color-muted-foreground: var(--muted-foreground);
27
+ --color-accent: var(--accent);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-destructive: var(--destructive);
30
+ --color-border: var(--border);
31
+ --color-input: var(--input);
32
+ --color-ring: var(--ring);
33
+
34
+ /* Journal-specific palette */
35
+ --color-journal-paper: var(--journal-paper);
36
+ --color-journal-paper-dark: var(--journal-paper-dark);
37
+ --color-journal-ink: var(--journal-ink);
38
+ --color-journal-ink-light: var(--journal-ink-light);
39
+ --color-journal-sepia: var(--journal-sepia);
40
+ --color-journal-warm: var(--journal-warm);
41
+ --color-journal-burgundy: var(--journal-burgundy);
42
+ --color-journal-forest: var(--journal-forest);
43
+ --color-journal-gold: var(--journal-gold);
44
+ --color-journal-rule: var(--journal-rule);
45
+ --color-journal-margin: var(--journal-margin);
46
+ --color-journal-stain: var(--journal-stain);
47
+
48
+ /* Chart colors */
49
+ --color-chart-1: var(--chart-1);
50
+ --color-chart-2: var(--chart-2);
51
+ --color-chart-3: var(--chart-3);
52
+ --color-chart-4: var(--chart-4);
53
+ --color-chart-5: var(--chart-5);
54
+
55
+ /* Sidebar colors */
56
+ --color-sidebar-ring: var(--sidebar-ring);
57
+ --color-sidebar-border: var(--sidebar-border);
58
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
59
+ --color-sidebar-accent: var(--sidebar-accent);
60
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
61
+ --color-sidebar-primary: var(--sidebar-primary);
62
+ --color-sidebar-foreground: var(--sidebar-foreground);
63
+ --color-sidebar: var(--sidebar);
64
+
65
+ /* Radii */
66
+ --radius-sm: calc(var(--radius) - 4px);
67
+ --radius-md: calc(var(--radius) - 2px);
68
+ --radius-lg: var(--radius);
69
+ --radius-xl: calc(var(--radius) + 4px);
70
+ }
71
+
72
+ :root {
73
+ --radius: 0.25rem;
74
+
75
+ /* Journal Light Theme */
76
+ --background: #FDFAF5;
77
+ --foreground: #2C1810;
78
+ --card: #FFFCF7;
79
+ --card-foreground: #2C1810;
80
+ --popover: #FFFCF7;
81
+ --popover-foreground: #2C1810;
82
+ --primary: #5C1A1B;
83
+ --primary-foreground: #FDFAF5;
84
+ --secondary: #F0E6D3;
85
+ --secondary-foreground: #3D2B1F;
86
+ --muted: #EDE4D3;
87
+ --muted-foreground: #7A6B5D;
88
+ --accent: #D4C5A9;
89
+ --accent-foreground: #2C1810;
90
+ --destructive: #9B2C2C;
91
+ --border: #D4C5A9;
92
+ --input: #D4C5A9;
93
+ --ring: #5C1A1B;
94
+ --chart-1: #5C1A1B;
95
+ --chart-2: #2D5F3E;
96
+ --chart-3: #B8860B;
97
+ --chart-4: #6B4226;
98
+ --chart-5: #8B7355;
99
+
100
+ /* Journal-specific tokens */
101
+ --journal-paper: #FDFAF5;
102
+ --journal-paper-dark: #F0E6D3;
103
+ --journal-ink: #2C1810;
104
+ --journal-ink-light: #5A4636;
105
+ --journal-sepia: #8B7355;
106
+ --journal-warm: #D4C5A9;
107
+ --journal-burgundy: #5C1A1B;
108
+ --journal-forest: #2D5F3E;
109
+ --journal-gold: #B8860B;
110
+ --journal-rule: #D9CBBA;
111
+ --journal-margin: #C9B99A;
112
+ --journal-stain: #E8DCC8;
113
+
114
+ --sidebar: #F5EDDC;
115
+ --sidebar-foreground: #2C1810;
116
+ --sidebar-primary: #5C1A1B;
117
+ --sidebar-primary-foreground: #FDFAF5;
118
+ --sidebar-accent: #E8DCC8;
119
+ --sidebar-accent-foreground: #2C1810;
120
+ --sidebar-border: #D4C5A9;
121
+ --sidebar-ring: #5C1A1B;
122
+ }
123
+
124
+ .dark {
125
+ --background: #1A1410;
126
+ --foreground: #E8DCC8;
127
+ --card: #221C16;
128
+ --card-foreground: #E8DCC8;
129
+ --popover: #221C16;
130
+ --popover-foreground: #E8DCC8;
131
+ --primary: #D4A574;
132
+ --primary-foreground: #1A1410;
133
+ --secondary: #2D2518;
134
+ --secondary-foreground: #E8DCC8;
135
+ --muted: #2D2518;
136
+ --muted-foreground: #A89880;
137
+ --accent: #3D3225;
138
+ --accent-foreground: #E8DCC8;
139
+ --destructive: #E57373;
140
+ --border: #3D3225;
141
+ --input: #3D3225;
142
+ --ring: #D4A574;
143
+ --chart-1: #D4A574;
144
+ --chart-2: #6B9B7A;
145
+ --chart-3: #DAA520;
146
+ --chart-4: #A0724A;
147
+ --chart-5: #8B7355;
148
+
149
+ --journal-paper: #1A1410;
150
+ --journal-paper-dark: #221C16;
151
+ --journal-ink: #E8DCC8;
152
+ --journal-ink-light: #B8A898;
153
+ --journal-sepia: #A89880;
154
+ --journal-warm: #3D3225;
155
+ --journal-burgundy: #D4A574;
156
+ --journal-forest: #6B9B7A;
157
+ --journal-gold: #DAA520;
158
+ --journal-rule: #3D3225;
159
+ --journal-margin: #2D2518;
160
+ --journal-stain: #332A1F;
161
+
162
+ --sidebar: #221C16;
163
+ --sidebar-foreground: #E8DCC8;
164
+ --sidebar-primary: #D4A574;
165
+ --sidebar-primary-foreground: #1A1410;
166
+ --sidebar-accent: #2D2518;
167
+ --sidebar-accent-foreground: #E8DCC8;
168
+ --sidebar-border: #3D3225;
169
+ --sidebar-ring: #D4A574;
170
+ }
171
+
172
+ @layer base {
173
+ * {
174
+ @apply border-border outline-ring/50;
175
+ }
176
+ body {
177
+ @apply bg-background text-foreground;
178
+ font-family: var(--font-lora), "Lora", Georgia, serif;
179
+ }
180
+ /* Headings always render in the display serif. */
181
+ h1, h2, h3, h4, h5, h6 {
182
+ font-family: var(--font-playfair), "Playfair Display", Georgia, serif;
183
+ }
184
+ /* Code & pre use the mono stack. */
185
+ code, pre, kbd {
186
+ font-family: var(--font-geist-mono), "JetBrains Mono", ui-monospace, monospace;
187
+ }
188
+ }
189
+
190
+ /* ── Journal Utility Classes ── */
191
+
192
+ /* Lined paper effect */
193
+ @layer utilities {
194
+ .journal-lined {
195
+ background-image:
196
+ repeating-linear-gradient(
197
+ to bottom,
198
+ transparent,
199
+ transparent 31px,
200
+ var(--journal-rule) 31px,
201
+ var(--journal-rule) 32px
202
+ );
203
+ background-position: 0 16px;
204
+ }
205
+
206
+ .journal-lined-tight {
207
+ background-image:
208
+ repeating-linear-gradient(
209
+ to bottom,
210
+ transparent,
211
+ transparent 23px,
212
+ var(--journal-rule) 23px,
213
+ var(--journal-rule) 24px
214
+ );
215
+ background-position: 0 12px;
216
+ }
217
+
218
+ /* Margin line (red/colored line on left) */
219
+ .journal-margin-line {
220
+ border-left: 2px solid var(--journal-burgundy);
221
+ padding-left: 2rem;
222
+ }
223
+
224
+ /* Paper texture overlay */
225
+ .journal-texture {
226
+ position: relative;
227
+ }
228
+ .journal-texture::before {
229
+ content: "";
230
+ position: absolute;
231
+ inset: 0;
232
+ background:
233
+ radial-gradient(ellipse at 20% 50%, var(--journal-stain) 0%, transparent 50%),
234
+ radial-gradient(ellipse at 80% 20%, var(--journal-stain) 0%, transparent 40%),
235
+ radial-gradient(ellipse at 60% 80%, var(--journal-stain) 0%, transparent 45%);
236
+ opacity: 0.3;
237
+ pointer-events: none;
238
+ z-index: 0;
239
+ }
240
+ .journal-texture > * {
241
+ position: relative;
242
+ z-index: 1;
243
+ }
244
+
245
+ /* Torn paper edge */
246
+ .journal-torn-top {
247
+ mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 1200 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0,0 L1200,0 L1200,5 Q1150,15 1100,5 Q1050,18 1000,5 Q950,12 900,5 Q850,18 800,5 Q750,15 700,5 Q650,18 600,5 Q550,12 500,5 Q450,18 400,5 Q350,15 300,5 Q250,12 200,5 Q150,18 100,5 Q50,12 0,5 Z' fill='white'/%3E%3C/svg%3E");
248
+ mask-size: 100% 20px;
249
+ mask-repeat: no-repeat;
250
+ mask-position: top;
251
+ padding-top: 20px;
252
+ }
253
+
254
+ .journal-torn-bottom {
255
+ mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 1200 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0,15 Q50,5 100,15 Q150,2 200,15 Q250,8 300,15 Q350,2 400,15 Q450,5 500,15 Q550,2 600,15 Q650,8 700,15 Q750,2 800,15 Q850,5 900,15 Q950,2 1000,15 Q1050,8 1100,15 Q1150,2 1200,15 L1200,20 L0,20 Z' fill='white'/%3E%3C/svg%3E");
256
+ mask-size: 100% 20px;
257
+ mask-repeat: no-repeat;
258
+ mask-position: bottom;
259
+ padding-bottom: 20px;
260
+ }
261
+
262
+ /* Aging vignette */
263
+ .journal-vignette {
264
+ box-shadow: inset 0 0 100px rgba(44, 24, 16, 0.08);
265
+ }
266
+
267
+ /* Drop cap initial */
268
+ .journal-drop-cap::first-letter {
269
+ float: left;
270
+ font-family: var(--font-display), 'Playfair Display', serif;
271
+ font-size: 3.5em;
272
+ line-height: 0.8;
273
+ padding-right: 0.1em;
274
+ padding-top: 0.05em;
275
+ color: var(--journal-burgundy);
276
+ font-weight: 700;
277
+ }
278
+
279
+ /* Notebook spiral binding holes */
280
+ .journal-spiral {
281
+ background-image:
282
+ radial-gradient(circle at 24px 0px, var(--border) 6px, transparent 6px);
283
+ background-size: 100% 80px;
284
+ background-repeat: repeat-y;
285
+ padding-top: 8px;
286
+ padding-left: 48px;
287
+ }
288
+
289
+ /* Editor's small caps eyebrow label. */
290
+ .journal-eyebrow {
291
+ font-family: var(--font-geist-mono), "JetBrains Mono", monospace;
292
+ text-transform: uppercase;
293
+ letter-spacing: 0.2em;
294
+ font-size: 0.7rem;
295
+ font-weight: 600;
296
+ color: var(--journal-sepia);
297
+ }
298
+ }
299
+
300
+ /* Custom scrollbar for code blocks and scroll areas. */
301
+ .journal-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
302
+ .journal-scroll::-webkit-scrollbar-track { background: transparent; }
303
+ .journal-scroll::-webkit-scrollbar-thumb {
304
+ background: var(--journal-warm);
305
+ border-radius: 9999px;
306
+ }
307
+ .journal-scroll::-webkit-scrollbar-thumb:hover { background: var(--journal-sepia); }
308
+
309
+ /* Selection — burgundy on paper. */
310
+ ::selection {
311
+ background: var(--journal-burgundy);
312
+ color: var(--journal-paper);
313
+ }
314
+
315
+ /* Syntax highlight tokens for the docs CodeBlock.
316
+ Colors are tuned for the dark ink code panels. */
317
+ .tok-keyword { color: #D4A574; font-weight: 500; }
318
+ .tok-string { color: #C9B99A; }
319
+ .tok-comment { color: #8B7355; font-style: italic; }
320
+ .tok-tag { color: #E57373; }
321
+ .tok-number { color: #DAA520; }
322
+ .tok-fn { color: #B8A898; }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }