uix-ui 0.1.0 → 0.2.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.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # uix-ui
2
+
3
+ The CLI for [uix-ui](https://uixdesign.pagestro.com/ui): open-source React components and Tailwind CSS v4 themes
4
+ that you copy into your project and own. No dependencies.
5
+
6
+ Needs React 18 or 19, Tailwind CSS v4 and Node 18+. Run in your app's folder:
7
+
8
+ ```bash
9
+ npx uix-ui@latest init --preset lagoon-stretch # theme, cn() helper, components.json
10
+ npx uix-ui@latest add button card input # copy components into src/components/ui
11
+ npx uix-ui@latest theme beetroot-harrow # switch theme
12
+ npx uix-ui@latest list # all components and themes
13
+ ```
14
+
15
+ ```tsx
16
+ import { Button } from "@/components/ui/button";
17
+ ```
18
+
19
+ Dark mode: `<html data-theme="dark">`. Options: `--overwrite` (add), `--no-install` (print the install command
20
+ instead of running it), `--registry <url|folder>` (init).
21
+
22
+ UIX Pro (marketing blocks, nine visual styles, 210 more themes): set your key, then add them by name.
23
+
24
+ ```bash
25
+ export UIX_LICENSE=<your key>
26
+ npx uix-ui@latest add hero pricing-card skins
27
+ npx uix-ui@latest theme acid-prompt
28
+ ```
29
+
30
+ Docs, live components and all 42 themes: https://uixdesign.pagestro.com/ui · Source: https://github.com/mdrakibshekh/uix-ui · MIT
package/bin/uix-ui.mjs CHANGED
@@ -15,7 +15,8 @@ const HELP = `uix-ui: UIX components and themes for React + Tailwind v4
15
15
 
16
16
  --overwrite replace files that already exist (add)
17
17
  --no-install don't run the package manager, just print the command
18
- Pro items: set UIX_LICENSE=<your key> and add @uix-pro/<name>.`;
18
+ UIX Pro: set UIX_LICENSE=<your key>, then add Pro items by name (hero, pricing-card, skins…) or switch to a
19
+ Pro theme (npx uix-ui theme acid-prompt). \`list\` shows them all.`;
19
20
 
20
21
  const cwd = process.cwd();
21
22
  const argv = process.argv.slice(2);
@@ -54,28 +55,56 @@ function source(cfg, name) {
54
55
  return { ns, item, url: url.replace('{name}', item), headers: Object.fromEntries(Object.entries(headers).map(([k, v]) => [k, env(v)])) };
55
56
  }
56
57
 
58
+ const PRICING = 'https://uixdesign.pagestro.com/pricing';
59
+ const missing = (src) => Object.assign(new Error(`"${src.item}" is not in the registry (${src.url}).`), { missing: true });
60
+
57
61
  async function load(src) {
58
62
  if (!/^https?:/.test(src.url)) {
59
- if (!existsSync(src.url)) die(`"${src.item}" is not in the registry (${src.url}).`);
63
+ if (!existsSync(src.url)) throw missing(src);
60
64
  return JSON.parse(readFileSync(src.url, 'utf8'));
61
65
  }
62
66
  const res = await fetch(src.url, { headers: src.headers });
63
- if (res.status === 401 || res.status === 403) die(`"${src.item}" is a UIX Pro item. Set UIX_LICENSE to your key (https://uixdesign.pagestro.com/pricing).`);
67
+ if (res.status === 401) die(process.env.UIX_LICENSE ? `UIX_LICENSE is set, but that key wasn't found. Check it in your UIX account (${PRICING}).` : `${src.item.startsWith('theme-') ? `The "${src.item.slice(6)}" theme` : `"${src.item}"`} is part of UIX Pro. Set UIX_LICENSE to your key (${PRICING}).`);
68
+ if (res.status === 403) die(`"${src.item}" needs an active UIX Pro licence; this key's plan has ended (${PRICING}).`);
69
+ if (res.status === 404) throw missing(src);
64
70
  if (!res.ok) die(`"${src.item}" could not be loaded (${res.status} from ${src.url}).`);
65
71
  return res.json();
66
72
  }
67
73
 
74
+ /** The Pro index (names only, no key needed), or null when there is no Pro registry. */
75
+ let proIndex;
76
+ async function loadProIndex(cfg) {
77
+ if (proIndex !== undefined) return proIndex;
78
+ if (!cfg.registries?.['@uix-pro']) return (proIndex = null);
79
+ const ps = source(cfg, '@uix-pro/x');
80
+ return (proIndex = await load({ ...ps, item: 'registry.json', url: ps.url.replace(/\/x\.json$/, '/registry.json'), headers: {} }).catch(() => null));
81
+ }
82
+
83
+ /** A bare name that the free registry doesn't have is looked up in Pro (`hero` → `@uix-pro/hero`). */
84
+ async function loadAny(cfg, src) {
85
+ try { return { src, it: await load(src) }; } catch (e) {
86
+ if (!e.missing || src.ns !== '@uix') throw e;
87
+ const index = await loadProIndex(cfg);
88
+ if (!index?.items.some(i => i.name === src.item)) throw Object.assign(new Error(`"${src.item.replace(/^theme-/, '')}" is not a uix-ui component or theme. See the list: npx uix-ui list`), { missing: true });
89
+ const pro = source(cfg, `@uix-pro/${src.item}`);
90
+ return { src: pro, it: await load(pro) };
91
+ }
92
+ }
93
+
68
94
  /** Loads the items and everything they depend on, dependencies first. */
69
95
  async function resolveAll(cfg, list) {
70
96
  const seen = new Map();
71
97
  const visit = async (name) => {
72
- const src = source(cfg, name);
98
+ let src = source(cfg, name);
73
99
  const key = `${src.ns}/${src.item}`;
74
100
  if (seen.has(key)) return;
75
101
  seen.set(key, null);
76
- const it = await load(src);
102
+ const bare = !name.startsWith('@');
103
+ const got = bare ? await loadAny(cfg, src) : { src, it: await load(src) };
104
+ const it = got.it;
105
+ if (got.src !== src) { seen.delete(key); src = got.src; seen.set(`${src.ns}/${src.item}`, null); }
77
106
  for (const d of it.registryDependencies ?? []) await visit(d.startsWith('@') ? d : `${src.ns}/${d}`);
78
- seen.delete(key); seen.set(key, it);
107
+ seen.delete(`${src.ns}/${src.item}`); seen.set(`${src.ns}/${src.item}`, it);
79
108
  };
80
109
  for (const n of list) await visit(n);
81
110
  return [...seen.values()];
@@ -102,6 +131,20 @@ function place(cfg, items, overwrite) {
102
131
  write(file, code);
103
132
  say(` wrote ${shown}`);
104
133
  }
134
+ for (const it of items) for (const f of it.files) if (/\.css$/.test(f.path) && !/(^|\/)uix\.css$/.test(f.path)) importCss(cfg, target(cfg, f));
135
+ }
136
+
137
+ /** Adds `@import "<file>";` to the app's main CSS, after the theme import (visual styles, backdrops). */
138
+ function importCss(cfg, file) {
139
+ const cssFile = join(cwd, cfg.tailwind?.css ?? 'src/index.css');
140
+ if (!existsSync(cssFile)) return;
141
+ let src = readFileSync(cssFile, 'utf8');
142
+ const imp = `@import "${rel(cssFile, file)}";`;
143
+ if (src.includes(imp)) return;
144
+ const last = [...src.matchAll(/@import [^;\n]+;?/g)].pop(); // after the theme and any earlier style sheets
145
+ src = last ? src.slice(0, last.index + last[0].length) + `\n${imp}` + src.slice(last.index + last[0].length) : `${imp}\n${src}`;
146
+ write(cssFile, src);
147
+ say(` updated ${relative(cwd, cssFile)}`);
105
148
  }
106
149
 
107
150
  function install(items) {
@@ -144,7 +187,7 @@ async function init() {
144
187
  if (!tw) say('Note: Tailwind CSS v4 is not installed yet (https://tailwindcss.com/docs/installation).');
145
188
  else if (/^[\^~]?3/.test(tw)) die('UIX themes need Tailwind CSS v4 (found v3).');
146
189
 
147
- const css = ['src/index.css', 'src/app/globals.css', 'app/globals.css', 'src/styles/globals.css', 'src/main.css'].find(f => existsSync(join(cwd, f))) ?? 'src/index.css';
190
+ const css = ['src/index.css', 'src/app/globals.css', 'app/globals.css', 'src/styles/globals.css', 'src/styles/global.css', 'src/main.css', 'app/app.css', 'app/tailwind.css'].find(f => existsSync(join(cwd, f))) ?? 'src/index.css';
148
191
  const local = opts.registry && !/^https?:/.test(opts.registry);
149
192
  const registry = opts.registry ? (local ? resolve(cwd, opts.registry) : opts.registry).replace(/\/$/, '') : DEFAULT_REGISTRY;
150
193
  const cfg = existsSync(join(cwd, 'components.json')) ? readJson('components.json') : {
@@ -192,7 +235,12 @@ async function main() {
192
235
  const index = await load({ ...src, item: 'registry.json', url: src.url.replace(/\/r\/x\.json$/, '/registry.json') });
193
236
  const comps = index.items.filter(i => i.type === 'registry:ui' && i.name !== 'icons').map(i => i.name);
194
237
  const themes = index.items.filter(i => i.name.startsWith('theme')).map(i => i.name === 'theme' ? 'uix' : i.name.slice(6));
195
- return say(`Components (${comps.length}): ${comps.join(', ')}\n\nThemes (${themes.length}): ${themes.join(', ')}`);
238
+ say(`Components (${comps.length}): ${comps.join(', ')}\n\nThemes (${themes.length}): ${themes.join(', ')}`);
239
+ const pro = await loadProIndex(cfg);
240
+ if (!pro) return;
241
+ const blocks = pro.items.filter(i => !i.name.startsWith('theme-')).map(i => i.name);
242
+ const proThemes = pro.items.filter(i => i.name.startsWith('theme-')).map(i => i.name.slice(6));
243
+ return say(`\nUIX Pro, with UIX_LICENSE (${PRICING}):\n Blocks and styles (${blocks.length}): ${blocks.join(', ')}\n Themes (${proThemes.length}): ${proThemes.join(', ')}`);
196
244
  }
197
245
  die(`unknown command "${cmd}".\n\n${HELP}`);
198
246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uix-ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Add UIX components and themes to your React + Tailwind app: npx uix-ui init.",
5
5
  "license": "MIT",
6
6
  "type": "module",