plankui 0.1.2 → 0.1.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 (3) hide show
  1. package/README.md +13 -9
  2. package/index.mjs +39 -4
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -37,7 +37,7 @@ npx plankui@latest init
37
37
  npx plankui@latest add button select dialog
38
38
  ```
39
39
 
40
- That's it. `init` sets up the design tokens once, and `add` drops each component into `components/ui`, together with every PlankUI file it builds on and the npm packages it needs.
40
+ That's it. `init` asks whether to build on **Radix UI or Base UI** and sets up the design tokens once, and `add` drops each component into `components/ui`, together with every PlankUI file it builds on and the npm packages it needs.
41
41
 
42
42
  ```tsx
43
43
  import { Button } from "@/components/ui/button";
@@ -51,6 +51,7 @@ export function Save() {
51
51
 
52
52
  - **You own the code.** Components land in your repo as plain TypeScript. Read them, change any line, never wait on a package upgrade.
53
53
  - **Beautiful by default.** Six themes and five component styles (Soft, Flat, Liquid glass, Keycap, Brutal), in light and dark, from one tokens file.
54
+ - **Radix UI or Base UI.** Every component built on primitives comes in both, with the same props. Choose once at `init`.
54
55
  - **Accessible.** Keyboard support, focus rings, ARIA roles and reduced motion handled for you.
55
56
  - **Agent ready.** The [PlankUI MCP server](https://www.npmjs.com/package/@plankui/mcp) gives Cursor, Claude Code, VS Code and other agents the real source of every component.
56
57
 
@@ -58,7 +59,7 @@ export function Save() {
58
59
 
59
60
  | Command | What it does |
60
61
  | --- | --- |
61
- | `plankui init` | Writes `tokens.css`, `styles.css` and the `cn` helper, and installs `clsx` and `tailwind-merge` |
62
+ | `plankui init` | Asks Radix UI or Base UI (saved in `plankui.json`), writes `tokens.css`, `styles.css` and the `cn` helper, and installs `clsx` and `tailwind-merge` |
62
63
  | `plankui add <names…>` | Copies components and the PlankUI files they build on, then installs their npm packages |
63
64
  | `plankui list` | Lists every component you can add |
64
65
 
@@ -69,6 +70,7 @@ export function Save() {
69
70
  | `--yes`, `-y` | Answer every prompt with its default and keep files that already exist. Use it in CI and for coding agents |
70
71
  | `--overwrite` | Replace files that already exist |
71
72
  | `--no-install` | Write the files but skip installing npm packages |
73
+ | `--base`, `--radix` | Use Base UI or Radix UI primitives for this run, whatever `plankui.json` says |
72
74
  | `--cwd <dir>` | Run in another folder |
73
75
  | `--registry <url>` | Read components from another registry |
74
76
 
@@ -76,16 +78,18 @@ The CLI picks your package manager (npm, pnpm, yarn or bun) from your lockfile,
76
78
 
77
79
  ## Components
78
80
 
79
- 35 components today, with more every week.
81
+ 72 components today, with more every week.
80
82
 
81
83
  | | |
82
84
  | --- | --- |
83
- | **Actions** | [Button](https://plankui.com/docs/components/button), [Segmented control](https://plankui.com/docs/components/segmented), [Dropdown menu](https://plankui.com/docs/components/dropdown-menu), [Button group](https://plankui.com/docs/components/button-group), [Copy button](https://plankui.com/docs/components/copy-button) |
84
- | **Forms** | [Input](https://plankui.com/docs/components/input), [Textarea](https://plankui.com/docs/components/textarea), [Select](https://plankui.com/docs/components/select), [Checkbox](https://plankui.com/docs/components/checkbox), [Radio group](https://plankui.com/docs/components/radio-group), [Switch](https://plankui.com/docs/components/switch), [Slider](https://plankui.com/docs/components/slider), [Combobox](https://plankui.com/docs/components/combobox), [Input OTP](https://plankui.com/docs/components/input-otp), [Password input](https://plankui.com/docs/components/password-input) |
85
- | **Overlays** | [Dialog](https://plankui.com/docs/components/dialog), [Popover](https://plankui.com/docs/components/popover), [Tooltip](https://plankui.com/docs/components/tooltip), [Sheet](https://plankui.com/docs/components/sheet), [Alert dialog](https://plankui.com/docs/components/alert-dialog) |
86
- | **Feedback** | [Toast](https://plankui.com/docs/components/toast), [Alert](https://plankui.com/docs/components/alert), [Progress](https://plankui.com/docs/components/progress), [Skeleton](https://plankui.com/docs/components/skeleton), [Spinner](https://plankui.com/docs/components/spinner) |
87
- | **Display** | [Card](https://plankui.com/docs/components/card), [Avatar](https://plankui.com/docs/components/avatar), [Badge](https://plankui.com/docs/components/badge), [Kbd](https://plankui.com/docs/components/kbd), [Separator](https://plankui.com/docs/components/separator), [Table](https://plankui.com/docs/components/table) |
88
- | **Navigation** | [Tabs](https://plankui.com/docs/components/tabs), [Accordion](https://plankui.com/docs/components/accordion), [Breadcrumb](https://plankui.com/docs/components/breadcrumb), [Pagination](https://plankui.com/docs/components/pagination) |
85
+ | **Actions** | [Button](https://plankui.com/docs/components/button), [Segmented control](https://plankui.com/docs/components/segmented), [Dropdown menu](https://plankui.com/docs/components/dropdown-menu), [Toggle](https://plankui.com/docs/components/toggle), [Command](https://plankui.com/docs/components/command), [Button group](https://plankui.com/docs/components/button-group), [Copy button](https://plankui.com/docs/components/copy-button), [Theme toggle](https://plankui.com/docs/components/theme-toggle) |
86
+ | **Forms** | [Textarea counter](https://plankui.com/docs/components/textarea-counter), [Native select](https://plankui.com/docs/components/native-select), [Input](https://plankui.com/docs/components/input), [Textarea](https://plankui.com/docs/components/textarea), [Select](https://plankui.com/docs/components/select), [Checkbox](https://plankui.com/docs/components/checkbox), [Radio group](https://plankui.com/docs/components/radio-group), [Switch](https://plankui.com/docs/components/switch), [Slider](https://plankui.com/docs/components/slider), [Combobox](https://plankui.com/docs/components/combobox), [Number input](https://plankui.com/docs/components/number-input), [Input OTP](https://plankui.com/docs/components/input-otp), [Calendar](https://plankui.com/docs/components/calendar), [Password input](https://plankui.com/docs/components/password-input), [Tag input](https://plankui.com/docs/components/tag-input), [Rating](https://plankui.com/docs/components/rating), [Search field](https://plankui.com/docs/components/search-field), [Time field](https://plankui.com/docs/components/time-field) |
87
+ | **Overlays** | [Dialog](https://plankui.com/docs/components/dialog), [Popover](https://plankui.com/docs/components/popover), [Tooltip](https://plankui.com/docs/components/tooltip), [Sheet](https://plankui.com/docs/components/sheet), [Alert dialog](https://plankui.com/docs/components/alert-dialog), [Hover card](https://plankui.com/docs/components/hover-card), [Context menu](https://plankui.com/docs/components/context-menu) |
88
+ | **Feedback** | [Status indicator](https://plankui.com/docs/components/status-indicator), [Toast](https://plankui.com/docs/components/toast), [Alert](https://plankui.com/docs/components/alert), [Progress](https://plankui.com/docs/components/progress), [Skeleton](https://plankui.com/docs/components/skeleton), [Spinner](https://plankui.com/docs/components/spinner), [Empty state](https://plankui.com/docs/components/empty-state), [Announcement](https://plankui.com/docs/components/announcement), [Progress circle](https://plankui.com/docs/components/progress-circle) |
89
+ | **Display** | [Scroll area](https://plankui.com/docs/components/scroll-area), [Disclosure](https://plankui.com/docs/components/disclosure), [Description list](https://plankui.com/docs/components/description-list), [Card](https://plankui.com/docs/components/card), [Avatar](https://plankui.com/docs/components/avatar), [Badge](https://plankui.com/docs/components/badge), [Kbd](https://plankui.com/docs/components/kbd), [Separator](https://plankui.com/docs/components/separator), [Table](https://plankui.com/docs/components/table), [Stat](https://plankui.com/docs/components/stat), [Chip](https://plankui.com/docs/components/chip), [Count up](https://plankui.com/docs/components/count-up), [Timeline](https://plankui.com/docs/components/timeline), [Divider](https://plankui.com/docs/components/divider) |
90
+ | **Navigation** | [Tabs](https://plankui.com/docs/components/tabs), [Accordion](https://plankui.com/docs/components/accordion), [Breadcrumb](https://plankui.com/docs/components/breadcrumb), [Pagination](https://plankui.com/docs/components/pagination), [Stepper](https://plankui.com/docs/components/stepper) |
91
+ | **Charts** | [Bar chart](https://plankui.com/docs/components/bar-chart), [Sparkline](https://plankui.com/docs/components/sparkline), [Line chart](https://plankui.com/docs/components/line-chart), [Bar list](https://plankui.com/docs/components/bar-list), [Stage bars](https://plankui.com/docs/components/stage-bars), [Chart card](https://plankui.com/docs/components/chart-card), [Steps card](https://plankui.com/docs/components/steps-card), [Most active days](https://plankui.com/docs/components/active-days-card) |
92
+ | **Blocks** | [Task list](https://plankui.com/docs/components/task-list), [Stat cards](https://plankui.com/docs/components/stat-cards), [Agent limits](https://plankui.com/docs/components/agent-limits) |
89
93
 
90
94
  ## Themes and styles
91
95
 
package/index.mjs CHANGED
@@ -4,6 +4,7 @@
4
4
  //
5
5
  // npx plankui@latest init
6
6
  // npx plankui@latest add button select dialog
7
+ // npx plankui@latest add select --base (the Base UI version)
7
8
  // npx plankui@latest list
8
9
  // npx plankui@latest login YOUR_LICENSE_KEY
9
10
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
@@ -12,7 +13,7 @@ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
12
13
  import { spawnSync } from "node:child_process";
13
14
  import { createInterface } from "node:readline/promises";
14
15
 
15
- const VERSION = "0.1.2";
16
+ const VERSION = "0.1.4";
16
17
  const DEFAULT_REGISTRY = "https://plankui.com/r";
17
18
 
18
19
  /* ── Arguments ── */
@@ -31,6 +32,8 @@ const yes = flags.has("--yes") || flags.has("-y");
31
32
  const overwrite = flags.has("--overwrite");
32
33
  const skipInstall = flags.has("--no-install");
33
34
  const cwd = opt("cwd", process.cwd());
35
+ /** Primitives asked for on this run: --base or --radix. Otherwise plankui.json decides. */
36
+ const libFlag = flags.has("--base") ? "base" : flags.has("--radix") ? "radix" : null;
34
37
  const registry = (opt("registry", process.env.PLANKUI_REGISTRY) ?? DEFAULT_REGISTRY).replace(/\/$/, "");
35
38
 
36
39
  /* ── Output ── */
@@ -53,6 +56,29 @@ async function confirm(question, fallback) {
53
56
  return answer ? answer.startsWith("y") : fallback;
54
57
  }
55
58
 
59
+ /** Pick one of two answers. With --yes or no terminal, takes the first. */
60
+ async function choose(question, options) {
61
+ if (yes || !process.stdin.isTTY) return options[0].value;
62
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
63
+ say(question);
64
+ options.forEach((o, i) => say(` ${i + 1}. ${o.label}${i === 0 ? dim(" (default)") : ""}`));
65
+ const answer = (await rl.question(dim("Choose 1 or 2: "))).trim();
66
+ rl.close();
67
+ return options[Number(answer) - 1]?.value ?? options[0].value;
68
+ }
69
+
70
+ /* ── Project settings: plankui.json ── */
71
+ const settingsFile = () => join(cwd, "plankui.json");
72
+ function settings() {
73
+ try {
74
+ return JSON.parse(readFileSync(settingsFile(), "utf8"));
75
+ } catch {
76
+ return {};
77
+ }
78
+ }
79
+ /** "radix" or "base": the flag, then plankui.json, then Radix. */
80
+ const primitives = () => libFlag ?? (settings().primitives === "base" ? "base" : "radix");
81
+
56
82
  /* ── Project ── */
57
83
  function project() {
58
84
  const pkgPath = join(cwd, "package.json");
@@ -207,6 +233,12 @@ async function init() {
207
233
  say(bold("PlankUI") + dim(` init, into ${relative(cwd, base) || "."}`));
208
234
  if (!deps.tailwindcss) say(`${yellow("note")} Tailwind CSS v4 is not in package.json. PlankUI needs it: see the framework guides.`);
209
235
  if (!deps.react) fail("React is not in package.json. PlankUI is for React 19 projects.");
236
+ const lib = libFlag ?? (await choose("Which primitives should the components use?", [
237
+ { value: "radix", label: "Radix UI" },
238
+ { value: "base", label: "Base UI" },
239
+ ]));
240
+ writeFileSync(settingsFile(), JSON.stringify({ ...settings(), primitives: lib }, null, 2) + "\n");
241
+ say(`${green("saved")} plankui.json ${dim(`(components use ${lib === "base" ? "Base UI" : "Radix UI"})`)}`);
210
242
  const spec = await fetchJson("init");
211
243
  if (!spec) fail("The registry has no init file.");
212
244
  for (const [state, file] of await writeFiles(base, spec.files)) say(`${state} ${file}`);
@@ -228,12 +260,14 @@ async function add() {
228
260
  if (!(await confirm("Add components anyway?", true))) process.exit(1);
229
261
  }
230
262
  // Resolve every PlankUI file the requested components build on.
263
+ // With Base UI, each component comes from base/ when it has a Base UI version, otherwise the shared file.
264
+ const lib = primitives();
231
265
  const queue = [...names.map((n) => n.toLowerCase())];
232
266
  const seen = new Map();
233
267
  while (queue.length) {
234
268
  const name = queue.shift();
235
269
  if (seen.has(name)) continue;
236
- let item = await fetchJson(name);
270
+ let item = (lib === "base" ? await fetchJson(`base/${name}`) : null) ?? (await fetchJson(name));
237
271
  if (!item) fail(`No component called "${name}". Run ${green("npx plankui@latest list")} to see them all.`);
238
272
  const key = item.pro ? licenceKey() : null;
239
273
  if (key) item = (await fetchPro(name, key)) ?? item;
@@ -250,7 +284,7 @@ async function add() {
250
284
  }
251
285
  if (!free.length) process.exit(locked.length ? 1 : 0);
252
286
  const extra = free.filter((i) => !names.includes(i.name)).map((i) => i.name);
253
- say(bold("PlankUI") + dim(` add ${free.map((i) => i.name).join(", ")}`) + (extra.length ? dim(` (needed by your picks: ${extra.join(", ")})`) : ""));
287
+ say(bold("PlankUI") + dim(` add ${free.map((i) => i.name).join(", ")}`) + (extra.length ? dim(` (needed by your picks: ${extra.join(", ")})`) : "") + (lib === "base" ? dim(", Base UI") : ""));
254
288
  for (const [state, file] of await writeFiles(base, free.flatMap((i) => i.files))) say(`${state} ${file}`);
255
289
  install(pm, free.flatMap((i) => i.dependencies), deps);
256
290
  say();
@@ -280,7 +314,7 @@ const help = () => {
280
314
  say(`${bold("plankui")} ${VERSION}: add PlankUI components to your React project.
281
315
 
282
316
  ${bold("Commands")}
283
- init Write tokens.css, styles.css and the cn helper, install shared packages
317
+ init Choose Radix UI or Base UI, write tokens.css, styles.css and the cn helper
284
318
  add <names...> Copy components and every PlankUI file they build on, install their packages
285
319
  list Show every component
286
320
  login <key> Save your PlankUI Pro licence key (once per machine)
@@ -290,6 +324,7 @@ ${bold("Options")}
290
324
  --yes, -y Answer every prompt with its default (keeps files that already exist)
291
325
  --overwrite Replace files that already exist
292
326
  --no-install Write files but skip installing npm packages
327
+ --base, --radix Use Base UI or Radix UI primitives (init saves the choice in plankui.json)
293
328
  --cwd <dir> Run in another folder
294
329
  --registry <url> Read components from another registry`);
295
330
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plankui",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Add accessible React components for Tailwind CSS v4 to your project. Six themes, five styles, and you own the code.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,7 +28,8 @@
28
28
  "dark-mode",
29
29
  "themes",
30
30
  "copy-paste",
31
- "plankui"
31
+ "plankui",
32
+ "base-ui"
32
33
  ],
33
34
  "homepage": "https://plankui.com/docs/installation",
34
35
  "license": "MIT",