myoperator-ui 0.0.2 → 0.0.4

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 (2) hide show
  1. package/dist/index.js +231 -50
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,22 +11,70 @@ import prompts from "prompts";
11
11
  import ora from "ora";
12
12
 
13
13
  // src/utils/registry.ts
14
- async function getRegistry() {
15
- return {
16
- button: {
17
- name: "button",
18
- description: "A customizable button component with variants, sizes, and icons",
19
- dependencies: [
20
- "@radix-ui/react-slot",
21
- "class-variance-authority",
22
- "clsx",
23
- "tailwind-merge",
24
- "lucide-react"
25
- ],
26
- files: [
27
- {
28
- name: "button.tsx",
29
- content: `import * as React from "react"
14
+ function prefixTailwindClasses(content, prefix) {
15
+ if (!prefix) return content;
16
+ const patterns = [
17
+ // Layout & Display
18
+ /\b(flex|inline-flex|block|inline-block|grid|inline-grid|hidden|table|contents)\b/g,
19
+ // Flexbox & Grid
20
+ /\b(items-|justify-|gap-|flex-|grid-|col-|row-|place-|self-|order-)/g,
21
+ // Spacing
22
+ /\b([mp][trblxy]?-\d+|[mp][trblxy]?-\[)/g,
23
+ /\b(space-[xy]-)/g,
24
+ // Sizing
25
+ /\b([wh]-(full|screen|auto|min|max|fit|\d+|px|\[))/g,
26
+ /\b(min-[wh]-|max-[wh]-)/g,
27
+ // Typography
28
+ /\b(text-(?:xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl|left|center|right|justify|wrap|nowrap|ellipsis|clip))\b/g,
29
+ /\b(font-(?:sans|serif|mono|thin|extralight|light|normal|medium|semibold|bold|extrabold|black))\b/g,
30
+ /\b(leading-|tracking-|whitespace-)/g,
31
+ // Colors (bg, text, border colors)
32
+ /\b(bg-|text-|border-|ring-|from-|via-|to-|fill-|stroke-)(?!\[)/g,
33
+ // Borders & Rings
34
+ /\b(border|rounded|ring|outline)(?:-|$)/g,
35
+ // Effects
36
+ /\b(shadow|opacity-|blur-|brightness-|contrast-|grayscale|invert|saturate|sepia)/g,
37
+ // Transitions & Animations
38
+ /\b(transition|duration-|ease-|delay-|animate-)/g,
39
+ // Transforms
40
+ /\b(scale-|rotate-|translate-|skew-|origin-|transform)/g,
41
+ // Interactivity
42
+ /\b(cursor-|pointer-events-|resize|select-|scroll-)/g,
43
+ // Pseudo-classes prefixes
44
+ /\b(hover:|focus:|active:|disabled:|focus-visible:|focus-within:|group-hover:|peer-)/g,
45
+ // Responsive prefixes
46
+ /\b(sm:|md:|lg:|xl:|2xl:)/g,
47
+ // Dark mode
48
+ /\b(dark:)/g,
49
+ // Other common utilities
50
+ /\b(overflow-|z-|relative|absolute|fixed|sticky|inset-|top-|right-|bottom-|left-)/g,
51
+ /\b(underline|line-through|no-underline|underline-offset-)/g,
52
+ /\b(sr-only|not-sr-only)\b/g
53
+ ];
54
+ let result = content;
55
+ result = result.replace(
56
+ /(className=["'`{]|cn\(["'`])([^"'`}]+)(["'`}])/g,
57
+ (match, start, classes, end) => {
58
+ let prefixedClasses = classes;
59
+ prefixedClasses = prefixedClasses.replace(
60
+ /(?<=^|\s)([a-z])/g,
61
+ (m, char) => `${prefix}${char}`
62
+ );
63
+ prefixedClasses = prefixedClasses.replace(
64
+ /(?<=^|\s)-([a-z])/g,
65
+ (m, char) => `-${prefix}${char}`
66
+ );
67
+ prefixedClasses = prefixedClasses.replace(
68
+ new RegExp(`${prefix}(hover|focus|active|disabled|focus-visible|focus-within|group-hover|sm|md|lg|xl|2xl|dark):${prefix}`, "g"),
69
+ `${prefix}$1:`
70
+ );
71
+ return start + prefixedClasses + end;
72
+ }
73
+ );
74
+ return result;
75
+ }
76
+ async function getRegistry(prefix = "") {
77
+ const buttonContent = prefixTailwindClasses(`import * as React from "react"
30
78
  import { Slot } from "@radix-ui/react-slot"
31
79
  import { cva, type VariantProps } from "class-variance-authority"
32
80
  import { Loader2 } from "lucide-react"
@@ -114,7 +162,22 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
114
162
  Button.displayName = "Button"
115
163
 
116
164
  export { Button, buttonVariants }
117
- `
165
+ `, prefix);
166
+ return {
167
+ button: {
168
+ name: "button",
169
+ description: "A customizable button component with variants, sizes, and icons",
170
+ dependencies: [
171
+ "@radix-ui/react-slot",
172
+ "class-variance-authority",
173
+ "clsx",
174
+ "tailwind-merge",
175
+ "lucide-react"
176
+ ],
177
+ files: [
178
+ {
179
+ name: "button.tsx",
180
+ content: buttonContent
118
181
  }
119
182
  ]
120
183
  }
@@ -130,7 +193,9 @@ async function add(components, options) {
130
193
  console.log(chalk.yellow(" Run `npx myoperator-ui init` first.\n"));
131
194
  process.exit(1);
132
195
  }
133
- const registry = await getRegistry();
196
+ const config = await fs.readJson(configPath);
197
+ const prefix = config.tailwind?.prefix || "";
198
+ const registry = await getRegistry(prefix);
134
199
  const availableComponents = Object.keys(registry);
135
200
  if (!components || components.length === 0) {
136
201
  const { selectedComponents } = await prompts({
@@ -158,7 +223,6 @@ async function add(components, options) {
158
223
  `));
159
224
  process.exit(1);
160
225
  }
161
- const config = await fs.readJson(configPath);
162
226
  const componentsDir = path.join(cwd, options.path);
163
227
  if (!options.yes) {
164
228
  const { confirm } = await prompts({
@@ -220,7 +284,86 @@ import fs2 from "fs-extra";
220
284
  import path2 from "path";
221
285
  import prompts2 from "prompts";
222
286
  import ora2 from "ora";
223
- var CSS_VARIABLES = `@tailwind base;
287
+ var CSS_VARIABLES_V4 = `@import "tailwindcss";
288
+
289
+ @theme {
290
+ --color-background: hsl(0 0% 100%);
291
+ --color-foreground: hsl(222.2 84% 4.9%);
292
+ --color-card: hsl(0 0% 100%);
293
+ --color-card-foreground: hsl(222.2 84% 4.9%);
294
+ --color-popover: hsl(0 0% 100%);
295
+ --color-popover-foreground: hsl(222.2 84% 4.9%);
296
+ --color-primary: hsl(222.2 47.4% 11.2%);
297
+ --color-primary-foreground: hsl(210 40% 98%);
298
+ --color-secondary: hsl(210 40% 96.1%);
299
+ --color-secondary-foreground: hsl(222.2 47.4% 11.2%);
300
+ --color-muted: hsl(210 40% 96.1%);
301
+ --color-muted-foreground: hsl(215.4 16.3% 46.9%);
302
+ --color-accent: hsl(210 40% 96.1%);
303
+ --color-accent-foreground: hsl(222.2 47.4% 11.2%);
304
+ --color-destructive: hsl(0 84.2% 60.2%);
305
+ --color-destructive-foreground: hsl(210 40% 98%);
306
+ --color-border: hsl(214.3 31.8% 91.4%);
307
+ --color-input: hsl(214.3 31.8% 91.4%);
308
+ --color-ring: hsl(222.2 84% 4.9%);
309
+ --radius: 0.5rem;
310
+ }
311
+
312
+ :root {
313
+ --background: 0 0% 100%;
314
+ --foreground: 222.2 84% 4.9%;
315
+ --card: 0 0% 100%;
316
+ --card-foreground: 222.2 84% 4.9%;
317
+ --popover: 0 0% 100%;
318
+ --popover-foreground: 222.2 84% 4.9%;
319
+ --primary: 222.2 47.4% 11.2%;
320
+ --primary-foreground: 210 40% 98%;
321
+ --secondary: 210 40% 96.1%;
322
+ --secondary-foreground: 222.2 47.4% 11.2%;
323
+ --muted: 210 40% 96.1%;
324
+ --muted-foreground: 215.4 16.3% 46.9%;
325
+ --accent: 210 40% 96.1%;
326
+ --accent-foreground: 222.2 47.4% 11.2%;
327
+ --destructive: 0 84.2% 60.2%;
328
+ --destructive-foreground: 210 40% 98%;
329
+ --border: 214.3 31.8% 91.4%;
330
+ --input: 214.3 31.8% 91.4%;
331
+ --ring: 222.2 84% 4.9%;
332
+ --radius: 0.5rem;
333
+ }
334
+
335
+ .dark {
336
+ --background: 222.2 84% 4.9%;
337
+ --foreground: 210 40% 98%;
338
+ --card: 222.2 84% 4.9%;
339
+ --card-foreground: 210 40% 98%;
340
+ --popover: 222.2 84% 4.9%;
341
+ --popover-foreground: 210 40% 98%;
342
+ --primary: 210 40% 98%;
343
+ --primary-foreground: 222.2 47.4% 11.2%;
344
+ --secondary: 217.2 32.6% 17.5%;
345
+ --secondary-foreground: 210 40% 98%;
346
+ --muted: 217.2 32.6% 17.5%;
347
+ --muted-foreground: 215 20.2% 65.1%;
348
+ --accent: 217.2 32.6% 17.5%;
349
+ --accent-foreground: 210 40% 98%;
350
+ --destructive: 0 62.8% 30.6%;
351
+ --destructive-foreground: 210 40% 98%;
352
+ --border: 217.2 32.6% 17.5%;
353
+ --input: 217.2 32.6% 17.5%;
354
+ --ring: 212.7 26.8% 83.9%;
355
+ }
356
+
357
+ * {
358
+ border-color: hsl(var(--border));
359
+ }
360
+
361
+ body {
362
+ background-color: hsl(var(--background));
363
+ color: hsl(var(--foreground));
364
+ }
365
+ `;
366
+ var CSS_VARIABLES_V3 = `@tailwind base;
224
367
  @tailwind components;
225
368
  @tailwind utilities;
226
369
 
@@ -280,9 +423,10 @@ var CSS_VARIABLES = `@tailwind base;
280
423
  }
281
424
  }
282
425
  `;
283
- var TAILWIND_CONFIG = `/** @type {import('tailwindcss').Config} */
426
+ var getTailwindConfig = (prefix) => `/** @type {import('tailwindcss').Config} */
284
427
  export default {
285
428
  darkMode: ["class"],
429
+ ${prefix ? `prefix: "${prefix}",` : ""}
286
430
  content: [
287
431
  "./index.html",
288
432
  "./src/**/*.{js,ts,jsx,tsx}",
@@ -362,7 +506,8 @@ var DEFAULT_CONFIG = {
362
506
  config: "tailwind.config.js",
363
507
  css: "src/index.css",
364
508
  baseColor: "slate",
365
- cssVariables: true
509
+ cssVariables: true,
510
+ prefix: ""
366
511
  },
367
512
  aliases: {
368
513
  components: "@/components",
@@ -387,6 +532,28 @@ async function init() {
387
532
  }
388
533
  }
389
534
  const response = await prompts2([
535
+ {
536
+ type: "select",
537
+ name: "tailwindVersion",
538
+ message: "Which Tailwind CSS version are you using?",
539
+ choices: [
540
+ { title: "Tailwind CSS v4 (latest)", value: "v4" },
541
+ { title: "Tailwind CSS v3", value: "v3" }
542
+ ],
543
+ initial: 0
544
+ },
545
+ {
546
+ type: "confirm",
547
+ name: "usePrefix",
548
+ message: "Use a prefix for Tailwind classes? (recommended if using Bootstrap/other CSS frameworks)",
549
+ initial: false
550
+ },
551
+ {
552
+ type: (prev) => prev ? "text" : null,
553
+ name: "prefix",
554
+ message: "Enter prefix for Tailwind classes:",
555
+ initial: "tw-"
556
+ },
390
557
  {
391
558
  type: "text",
392
559
  name: "componentsPath",
@@ -400,7 +567,7 @@ async function init() {
400
567
  initial: "src/lib/utils.ts"
401
568
  },
402
569
  {
403
- type: "text",
570
+ type: (prev, values) => values.tailwindVersion === "v3" ? "text" : null,
404
571
  name: "tailwindConfig",
405
572
  message: "Where is your tailwind.config.js?",
406
573
  initial: "tailwind.config.js"
@@ -414,12 +581,14 @@ async function init() {
414
581
  ]);
415
582
  const spinner = ora2("Initializing project...").start();
416
583
  try {
584
+ const prefix = response.usePrefix ? response.prefix : "";
417
585
  const config = {
418
586
  ...DEFAULT_CONFIG,
419
587
  tailwind: {
420
588
  ...DEFAULT_CONFIG.tailwind,
421
- config: response.tailwindConfig,
422
- css: response.globalCss
589
+ config: response.tailwindConfig || "tailwind.config.js",
590
+ css: response.globalCss,
591
+ prefix
423
592
  },
424
593
  aliases: {
425
594
  ...DEFAULT_CONFIG.aliases,
@@ -444,10 +613,11 @@ export function cn(...inputs: ClassValue[]) {
444
613
  const componentsPath = path2.join(cwd, response.componentsPath);
445
614
  await fs2.ensureDir(componentsPath);
446
615
  const globalCssPath = path2.join(cwd, response.globalCss);
616
+ const cssContent = response.tailwindVersion === "v4" ? CSS_VARIABLES_V4 : CSS_VARIABLES_V3;
447
617
  let cssUpdated = false;
448
618
  if (!await fs2.pathExists(globalCssPath)) {
449
619
  await fs2.ensureDir(path2.dirname(globalCssPath));
450
- await fs2.writeFile(globalCssPath, CSS_VARIABLES);
620
+ await fs2.writeFile(globalCssPath, cssContent);
451
621
  cssUpdated = true;
452
622
  } else {
453
623
  const existingCss = await fs2.readFile(globalCssPath, "utf-8");
@@ -461,30 +631,32 @@ export function cn(...inputs: ClassValue[]) {
461
631
  });
462
632
  spinner.start("Initializing project...");
463
633
  if (updateCss) {
464
- await fs2.writeFile(globalCssPath, CSS_VARIABLES);
634
+ await fs2.writeFile(globalCssPath, cssContent);
465
635
  cssUpdated = true;
466
636
  }
467
637
  }
468
638
  }
469
- const tailwindConfigPath = path2.join(cwd, response.tailwindConfig);
470
639
  let tailwindUpdated = false;
471
- if (!await fs2.pathExists(tailwindConfigPath)) {
472
- await fs2.writeFile(tailwindConfigPath, TAILWIND_CONFIG);
473
- tailwindUpdated = true;
474
- } else {
475
- const existingConfig = await fs2.readFile(tailwindConfigPath, "utf-8");
476
- if (!existingConfig.includes("hsl(var(--destructive))") && !existingConfig.includes("hsl(var(--ring))")) {
477
- spinner.stop();
478
- const { updateTailwind } = await prompts2({
479
- type: "confirm",
480
- name: "updateTailwind",
481
- message: `${response.tailwindConfig} exists. Update with myOperator UI theme colors?`,
482
- initial: true
483
- });
484
- spinner.start("Initializing project...");
485
- if (updateTailwind) {
486
- await fs2.writeFile(tailwindConfigPath, TAILWIND_CONFIG);
487
- tailwindUpdated = true;
640
+ if (response.tailwindVersion === "v3" && response.tailwindConfig) {
641
+ const tailwindConfigPath = path2.join(cwd, response.tailwindConfig);
642
+ if (!await fs2.pathExists(tailwindConfigPath)) {
643
+ await fs2.writeFile(tailwindConfigPath, getTailwindConfig(prefix));
644
+ tailwindUpdated = true;
645
+ } else {
646
+ const existingConfig = await fs2.readFile(tailwindConfigPath, "utf-8");
647
+ if (!existingConfig.includes("hsl(var(--destructive))") && !existingConfig.includes("hsl(var(--ring))")) {
648
+ spinner.stop();
649
+ const { updateTailwind } = await prompts2({
650
+ type: "confirm",
651
+ name: "updateTailwind",
652
+ message: `${response.tailwindConfig} exists. Update with myOperator UI theme colors?`,
653
+ initial: true
654
+ });
655
+ spinner.start("Initializing project...");
656
+ if (updateTailwind) {
657
+ await fs2.writeFile(tailwindConfigPath, getTailwindConfig(prefix));
658
+ tailwindUpdated = true;
659
+ }
488
660
  }
489
661
  }
490
662
  }
@@ -529,11 +701,20 @@ export function cn(...inputs: ClassValue[]) {
529
701
  console.log("");
530
702
  console.log(chalk2.bold(" Next steps:\n"));
531
703
  console.log(" 1. Install core dependencies:");
532
- console.log(chalk2.cyan(" npm install clsx tailwind-merge class-variance-authority @radix-ui/react-slot lucide-react tailwindcss-animate\n"));
533
- console.log(" 2. Install PostCSS plugin (if not already installed):");
534
- console.log(chalk2.cyan(" npm install -D @tailwindcss/postcss\n"));
535
- console.log(" 3. Add your first component:");
536
- console.log(chalk2.cyan(" npx myoperator-ui add button\n"));
704
+ if (response.tailwindVersion === "v4") {
705
+ console.log(chalk2.cyan(" npm install clsx tailwind-merge class-variance-authority @radix-ui/react-slot lucide-react\n"));
706
+ } else {
707
+ console.log(chalk2.cyan(" npm install clsx tailwind-merge class-variance-authority @radix-ui/react-slot lucide-react tailwindcss-animate\n"));
708
+ }
709
+ if (response.tailwindVersion === "v4") {
710
+ console.log(" 2. Add your first component:");
711
+ console.log(chalk2.cyan(" npx myoperator-ui add button\n"));
712
+ } else {
713
+ console.log(" 2. Install PostCSS plugin (if not already installed):");
714
+ console.log(chalk2.cyan(" npm install -D @tailwindcss/postcss\n"));
715
+ console.log(" 3. Add your first component:");
716
+ console.log(chalk2.cyan(" npx myoperator-ui add button\n"));
717
+ }
537
718
  } catch (error) {
538
719
  spinner.fail("Failed to initialize project");
539
720
  console.error(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-ui",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "CLI for adding myOperator UI components to your project",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",