ply-ui-cli 1.1.122 → 1.1.124

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 CHANGED
@@ -6,7 +6,7 @@ CLI for **Ply** at [ply-ui.com](https://ply-ui.com) — Angular + Tailwind CSS c
6
6
  ♿ **Accessibility (ACR)**: [ply-ui.com/accessibility](https://ply-ui.com/accessibility)
7
7
  🖥️ **SSR-safe**: the docs site prerenders every catalog route, enforced by a blocking CI job · [how we verify it](https://ply-ui.com/learn/angular-ssr-safe-component-library/)
8
8
  🎨 **Figma Design System**: [Figma Community File](https://www.figma.com/community/file/1662825988518656661)
9
- ✨ **See what you can build for free**: [Live Admin Dashboard Demo](https://base-ui-free-dashboard-demo.pages.dev/app/dashboard) (Source code: [base-ui-free-dashboard](https://github.com/lussos/base-ui-free-dashboard))
9
+ ✨ **See what you can build for free**: [Live Admin Dashboard Demo](https://demo.ply-ui.com/app/dashboard) (Source code: [ply-free-dashboard](https://github.com/ply-ui-ng/ply-free-dashboard))
10
10
 
11
11
  ```bash
12
12
  npx ply-ui-cli init
@@ -17,9 +17,9 @@ The old npm name `base-ui-cli` is **deprecated** (name collision with [MUI Base
17
17
 
18
18
  ## What's new
19
19
 
20
- - **3-way merge** — `npx ply-ui-cli update --merge` (or the interactive Merge choice) uses `git merge-file` with the install snapshot under `.base-ui/snapshots`. Aliases: `sheet` → `drawer`, `carousel` → `slider`.
20
+ - **3-way merge** — `npx ply-ui-cli update --merge` (or the interactive Merge choice) uses `git merge-file` with the install snapshot under `.ply-ui/snapshots`. Aliases: `sheet` → `drawer`, `carousel` → `slider`.
21
21
  - **add picker** — `npx ply-ui-cli add` with no names opens a catalog filter + type-to-select. `--yes` still requires explicit names.
22
- - **doctor** — `npx ply-ui-cli doctor` checks `base-ui.json`, Tailwind v4 `@source` paths, `base-ui.css`, CDK, and icon sprites. Read-only.
22
+ - **doctor** — `npx ply-ui-cli doctor` checks `ply-ui.json`, Tailwind v4 `@source` paths, `ply-ui.css`, CDK, and icon sprites. Read-only.
23
23
  - **hover-card**, **menubar**, **loading-overlay**, **currency-input** (free) — hover preview panel, application menubar, blocking spinner overlay, locale-aware currency field.
24
24
  - **kbd**, **aspect-ratio**, **scroll-area**, **cookie-banner** (free) — keyboard glyph, ratio wrapper, themed overflow pane, SSR-safe consent banner.
25
25
  - **chat**, **combobox**, **tags-input**, **time-picker** (free) — AI chat kit, searchable combobox, chip tags field, and HH:mm time picker.
@@ -56,10 +56,10 @@ outside a server render.
56
56
 
57
57
  Sets up your project:
58
58
 
59
- - writes `base-ui.json` (component path aliases),
60
- - creates `base-ui.css` (CDK overlay styles, keyframes, autofill fixes) and imports it from your global stylesheet,
59
+ - writes `ply-ui.json` (component path aliases),
60
+ - creates `ply-ui.css` (CDK overlay styles, keyframes, autofill fixes) and imports it from your global stylesheet,
61
61
  - downloads the icon sprites (`icons.svg`, `icons-filled.svg`) into your assets folder,
62
- - writes `.cursor/rules/base-ui.mdc` and `.vscode/base-ui.code-snippets` when those files are missing (Cursor / Windsurf / VS Code). Skip MCP? These files still teach the assistant how to write Ply code.
62
+ - writes `.cursor/rules/ply-ui.mdc` and `.vscode/ply-ui.code-snippets` when those files are missing (Cursor / Windsurf / VS Code). Skip MCP? These files still teach the assistant how to write Ply code.
63
63
 
64
64
  ```bash
65
65
  npx ply-ui-cli init # interactive
@@ -119,13 +119,13 @@ npx ply-ui-cli update # interactive — asks about each conflict
119
119
  npx ply-ui-cli update card # update one component
120
120
  npx ply-ui-cli update --yes # non-interactive; conflicts and local-only edits are left untouched
121
121
  npx ply-ui-cli update --yes --force # non-interactive; conflicts take the upstream version
122
- npx ply-ui-cli update --yes --merge # conflicts: 3-way merge using .base-ui/snapshots
122
+ npx ply-ui-cli update --yes --merge # conflicts: 3-way merge using .ply-ui/snapshots
123
123
  npx ply-ui-cli update --yes --overwrite-local # also revert files you edited that upstream did not change
124
124
  ```
125
125
 
126
126
  `--force` only resolves **conflicts** (both sides changed). It does not discard local-only edits. `--merge` needs the snapshot written by `add` (re-add with `--overwrite` if you installed before snapshots existed). Dirty merges leave conflict markers and exit 1.
127
127
 
128
- Update tracking uses `base-ui-lock.json` (created by `init`), which records a hash of each installed file. Components installed before this existed aren't tracked — re-run `add <name> --overwrite` to start tracking them.
128
+ Update tracking uses `ply-ui-lock.json` (created by `init`), which records a hash of each installed file. Components installed before this existed aren't tracked — re-run `add <name> --overwrite` to start tracking them.
129
129
 
130
130
  ### `changelog`
131
131
 
@@ -140,7 +140,7 @@ Unknown names get suggestions from the registry. If the item exists but has no m
140
140
 
141
141
  ### `doctor`
142
142
 
143
- Verifies that your project is set up correctly for Ply. It checks `base-ui.json`, the Angular workspace, `@angular/cdk`, Tailwind v4 and its `@source` paths, the `base-ui.css` import, the icon sprites, and the components directory. Read-only — nothing is written.
143
+ Verifies that your project is set up correctly for Ply. It checks `ply-ui.json`, the Angular workspace, `@angular/cdk`, Tailwind v4 and its `@source` paths, the `ply-ui.css` import, the icon sprites, and the components directory. Read-only — nothing is written.
144
144
 
145
145
  ```bash
146
146
  npx ply-ui-cli doctor
@@ -163,7 +163,7 @@ Get a license and browse live previews of every component at [ply-ui.com](https:
163
163
 
164
164
  ## Configuration
165
165
 
166
- `base-ui.json` (created by `init`):
166
+ `ply-ui.json` (created by `init`):
167
167
 
168
168
  ```json
169
169
  {
@@ -252,10 +252,10 @@ A one-time Pro license is $99. The Relay workspace, Motif storefront, and Motif
252
252
  npx ply-ui-cli init once per project, then npx ply-ui-cli add <name>. Omit the name in a terminal to pick from the catalog (filter, then type to select). --yes and CI need explicit names. Components land in src/app/components/<name>/ by default and are yours.
253
253
 
254
254
  **How do I verify my Ply setup?**
255
- Run npx ply-ui-cli doctor. It checks base-ui.json, the Angular workspace, @angular/cdk, Tailwind v4 @source paths, the base-ui.css import, icon sprites, and the components directory — read-only, nothing is written.
255
+ Run npx ply-ui-cli doctor. It checks ply-ui.json, the Angular workspace, @angular/cdk, Tailwind v4 @source paths, the ply-ui.css import, icon sprites, and the components directory — read-only, nothing is written.
256
256
 
257
257
  **Can AI agents install Ply?**
258
- Yes. npx -y ply-ui-mcp is stdio for Cursor, Claude (Code/Desktop), Kimi, VS Code, Windsurf, and other local hosts. ChatGPT and Gemini chat apps use Streamable HTTP: npx -y ply-ui-mcp --http --read-only (catalog tools) or local --http without --read-only so add/init/update can see the project. Pro add uses PLY_LICENSE_KEY on the MCP process. Editors that skip MCP can copy the Cursor rule and snippet pack from https://ply-ui.com/getting-started/#editor-rules.
258
+ Yes. npx -y ply-ui-mcp is stdio for Cursor, Claude (Code/Desktop), Kimi, VS Code, Windsurf, and other local hosts. ChatGPT and Gemini chat apps use the Streamable HTTP connector at https://mcp.ply-ui.com/mcp (catalog tools) or a local npx -y ply-ui-mcp --http without --read-only so add/init/update can see the project. Pro add uses PLY_LICENSE_KEY on the MCP process. Editors that skip MCP can copy the Cursor rule and snippet pack from https://ply-ui.com/getting-started/#editor-rules.
259
259
 
260
260
  **What is the risk if Ply disappears?**
261
261
  Near zero. Installed code is in your version control with no runtime dependency; deleting the CLI changes nothing about your app.
@@ -33,30 +33,49 @@ __export(add_exports, {
33
33
  add: () => add
34
34
  });
35
35
  module.exports = __toCommonJS(add_exports);
36
- var import_fs4 = __toESM(require("fs"));
36
+ var import_fs5 = __toESM(require("fs"));
37
37
  var import_path5 = __toESM(require("path"));
38
38
  var import_vendor2 = require("../vendor");
39
39
  var import_registry = require("../registry");
40
40
 
41
41
  // src/lockfile.ts
42
+ var import_fs2 = __toESM(require("fs"));
43
+ var import_crypto = __toESM(require("crypto"));
44
+
45
+ // src/paths.ts
42
46
  var import_fs = __toESM(require("fs"));
43
47
  var import_path = __toESM(require("path"));
44
- var import_crypto = __toESM(require("crypto"));
45
- var LOCKFILE_NAME = "base-ui-lock.json";
48
+ var CONFIG_FILE = "ply-ui.json";
49
+ var LEGACY_CONFIG_FILE = "base-ui.json";
50
+ var LOCKFILE_NAME = "ply-ui-lock.json";
51
+ var LEGACY_LOCKFILE_NAME = "base-ui-lock.json";
52
+ var SNAPSHOT_ROOT = ".ply-ui/snapshots";
53
+ function firstExisting(cwd, names) {
54
+ for (const name of names) {
55
+ const resolved = import_path.default.resolve(cwd, name);
56
+ if (import_fs.default.existsSync(resolved)) return resolved;
57
+ }
58
+ return null;
59
+ }
60
+ function configFilePath(cwd = process.cwd()) {
61
+ return firstExisting(cwd, [CONFIG_FILE, LEGACY_CONFIG_FILE]);
62
+ }
63
+ function lockfilePath(cwd = process.cwd()) {
64
+ return firstExisting(cwd, [LOCKFILE_NAME, LEGACY_LOCKFILE_NAME]) || import_path.default.resolve(cwd, LOCKFILE_NAME);
65
+ }
66
+
67
+ // src/lockfile.ts
46
68
  var LOCKFILE_VERSION = 1;
47
69
  function hashContent(content) {
48
70
  return import_crypto.default.createHash("sha256").update(content, "utf8").digest("hex");
49
71
  }
50
- function lockfilePath() {
51
- return import_path.default.resolve(process.cwd(), LOCKFILE_NAME);
52
- }
53
72
  function readLockFile() {
54
73
  const p = lockfilePath();
55
- if (!import_fs.default.existsSync(p)) {
74
+ if (!import_fs2.default.existsSync(p)) {
56
75
  return { version: LOCKFILE_VERSION, components: {} };
57
76
  }
58
77
  try {
59
- const parsed = JSON.parse(import_fs.default.readFileSync(p, "utf8"));
78
+ const parsed = JSON.parse(import_fs2.default.readFileSync(p, "utf8"));
60
79
  if (parsed && typeof parsed === "object" && parsed.components) {
61
80
  return { version: parsed.version ?? LOCKFILE_VERSION, components: parsed.components };
62
81
  }
@@ -65,7 +84,7 @@ function readLockFile() {
65
84
  return { version: LOCKFILE_VERSION, components: {} };
66
85
  }
67
86
  function writeLockFile(lock) {
68
- import_fs.default.writeFileSync(lockfilePath(), JSON.stringify(lock, null, 2) + "\n", "utf8");
87
+ import_fs2.default.writeFileSync(lockfilePath(), JSON.stringify(lock, null, 2) + "\n", "utf8");
69
88
  }
70
89
  function recordComponent(lock, name, tier, files) {
71
90
  const fileHashes = {};
@@ -80,7 +99,7 @@ function recordComponent(lock, name, tier, files) {
80
99
  }
81
100
 
82
101
  // src/config.ts
83
- var import_fs2 = __toESM(require("fs"));
102
+ var import_fs3 = __toESM(require("fs"));
84
103
  var import_path2 = __toESM(require("path"));
85
104
 
86
105
  // src/brand.ts
@@ -91,19 +110,21 @@ var MCP_NPX = `npx -y ${MCP_PACKAGE}`;
91
110
 
92
111
  // src/config.ts
93
112
  function readConfig() {
94
- const configPath = import_path2.default.resolve(process.cwd(), "base-ui.json");
95
- if (!import_fs2.default.existsSync(configPath)) {
96
- console.error(`base-ui.json not found. Please run "${CLI_NPX} init" first.`);
113
+ const resolved = configFilePath();
114
+ if (!resolved) {
115
+ console.error(
116
+ `${CONFIG_FILE} not found (also looked for ${LEGACY_CONFIG_FILE}). Please run "${CLI_NPX} init" first.`
117
+ );
97
118
  return null;
98
119
  }
99
120
  try {
100
- const config = JSON.parse(import_fs2.default.readFileSync(configPath, "utf8"));
121
+ const config = JSON.parse(import_fs3.default.readFileSync(resolved, "utf8"));
101
122
  if (!config || !config.aliases || !config.aliases.components) {
102
- throw new Error("Missing aliases.components in base-ui.json");
123
+ throw new Error(`Missing aliases.components in ${import_path2.default.basename(resolved)}`);
103
124
  }
104
125
  return config;
105
126
  } catch (err) {
106
- console.error(`Error reading base-ui.json: ${err.message}`);
127
+ console.error(`Error reading ${import_path2.default.basename(resolved)}: ${err.message}`);
107
128
  return null;
108
129
  }
109
130
  }
@@ -227,7 +248,12 @@ var COMPONENT_ALIASES = {
227
248
  "image-slider": "slider",
228
249
  range: "range-slider",
229
250
  "slider-input": "range-slider",
230
- "input-slider": "range-slider"
251
+ "input-slider": "range-slider",
252
+ "base-list-item": "ply-list-item",
253
+ "base-tab-icon": "ply-tab-icon",
254
+ "base-group-button": "ply-group-button",
255
+ "base-dropdown-menu-item": "ply-dropdown-menu-item",
256
+ "base-context-menu-item": "ply-context-menu-item"
231
257
  };
232
258
  function canonicalComponentName(name) {
233
259
  const key = name.trim().toLowerCase();
@@ -235,29 +261,28 @@ function canonicalComponentName(name) {
235
261
  }
236
262
 
237
263
  // src/snapshot.ts
238
- var import_fs3 = __toESM(require("fs"));
264
+ var import_fs4 = __toESM(require("fs"));
239
265
  var import_path4 = __toESM(require("path"));
240
- var SNAPSHOT_ROOT = ".base-ui/snapshots";
241
- function snapshotPath(component, file) {
242
- return import_path4.default.resolve(process.cwd(), SNAPSHOT_ROOT, component, file);
266
+ function snapshotPath(component, file, root) {
267
+ return import_path4.default.resolve(process.cwd(), root, component, file);
243
268
  }
244
269
  function writeSnapshot(component, file, content) {
245
270
  if (!isSafeFileName(file) || !isSafeFileName(component)) return;
246
- const dest = snapshotPath(component, file);
247
- import_fs3.default.mkdirSync(import_path4.default.dirname(dest), { recursive: true });
248
- import_fs3.default.writeFileSync(dest, content, "utf8");
271
+ const dest = snapshotPath(component, file, SNAPSHOT_ROOT);
272
+ import_fs4.default.mkdirSync(import_path4.default.dirname(dest), { recursive: true });
273
+ import_fs4.default.writeFileSync(dest, content, "utf8");
249
274
  }
250
275
 
251
276
  // src/commands/add.ts
252
277
  function detectPackageManager(cwd) {
253
- if (import_fs4.default.existsSync(import_path5.default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
254
- if (import_fs4.default.existsSync(import_path5.default.join(cwd, "yarn.lock"))) return "yarn";
255
- if (import_fs4.default.existsSync(import_path5.default.join(cwd, "bun.lockb")) || import_fs4.default.existsSync(import_path5.default.join(cwd, "bun.lock"))) return "bun";
278
+ if (import_fs5.default.existsSync(import_path5.default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
279
+ if (import_fs5.default.existsSync(import_path5.default.join(cwd, "yarn.lock"))) return "yarn";
280
+ if (import_fs5.default.existsSync(import_path5.default.join(cwd, "bun.lockb")) || import_fs5.default.existsSync(import_path5.default.join(cwd, "bun.lock"))) return "bun";
256
281
  return "npm";
257
282
  }
258
283
  function installedNpmDeps(cwd) {
259
284
  try {
260
- const pkg = JSON.parse(import_fs4.default.readFileSync(import_path5.default.join(cwd, "package.json"), "utf8"));
285
+ const pkg = JSON.parse(import_fs5.default.readFileSync(import_path5.default.join(cwd, "package.json"), "utf8"));
261
286
  return /* @__PURE__ */ new Set([
262
287
  ...Object.keys(pkg.dependencies || {}),
263
288
  ...Object.keys(pkg.devDependencies || {})
@@ -334,7 +359,7 @@ async function add(components, options = {}) {
334
359
  return;
335
360
  }
336
361
  const targetDir = import_path5.default.join(componentsRoot, name);
337
- if (!requested.has(name) && import_fs4.default.existsSync(targetDir)) continue;
362
+ if (!requested.has(name) && import_fs5.default.existsSync(targetDir)) continue;
338
363
  toInstall.set(name, entry);
339
364
  queue.push(...entry.registryDependencies);
340
365
  }
@@ -349,7 +374,7 @@ async function add(components, options = {}) {
349
374
  }
350
375
  for (const name of requested) {
351
376
  const targetDir = import_path5.default.join(componentsRoot, name);
352
- if (!import_fs4.default.existsSync(targetDir)) continue;
377
+ if (!import_fs5.default.existsSync(targetDir)) continue;
353
378
  if (options.overwrite || options.yes) continue;
354
379
  const res = await (0, import_vendor2.prompts)({
355
380
  type: "confirm",
@@ -395,8 +420,8 @@ async function add(components, options = {}) {
395
420
  try {
396
421
  for (const file of item.files) {
397
422
  const filePath = import_path5.default.join(targetDir, file.name);
398
- import_fs4.default.mkdirSync(import_path5.default.dirname(filePath), { recursive: true });
399
- import_fs4.default.writeFileSync(filePath, file.content, "utf8");
423
+ import_fs5.default.mkdirSync(import_path5.default.dirname(filePath), { recursive: true });
424
+ import_fs5.default.writeFileSync(filePath, file.content, "utf8");
400
425
  writeSnapshot(name, file.name, file.content);
401
426
  }
402
427
  } catch (err) {
@@ -37,8 +37,8 @@ var import_path3 = __toESM(require("path"));
37
37
  var import_vendor = require("../vendor");
38
38
 
39
39
  // src/config.ts
40
- var import_fs = __toESM(require("fs"));
41
- var import_path = __toESM(require("path"));
40
+ var import_fs2 = __toESM(require("fs"));
41
+ var import_path2 = __toESM(require("path"));
42
42
 
43
43
  // src/brand.ts
44
44
  var CLI_BIN = "ply-ui-cli";
@@ -46,40 +46,58 @@ var CLI_NPX = `npx ${CLI_BIN}`;
46
46
  var MCP_PACKAGE = "ply-ui-mcp";
47
47
  var MCP_NPX = `npx -y ${MCP_PACKAGE}`;
48
48
 
49
+ // src/paths.ts
50
+ var import_fs = __toESM(require("fs"));
51
+ var import_path = __toESM(require("path"));
52
+ var CONFIG_FILE = "ply-ui.json";
53
+ var LEGACY_CONFIG_FILE = "base-ui.json";
54
+ var LOCKFILE_NAME = "ply-ui-lock.json";
55
+ var LEGACY_LOCKFILE_NAME = "base-ui-lock.json";
56
+ function firstExisting(cwd, names) {
57
+ for (const name of names) {
58
+ const resolved = import_path.default.resolve(cwd, name);
59
+ if (import_fs.default.existsSync(resolved)) return resolved;
60
+ }
61
+ return null;
62
+ }
63
+ function configFilePath(cwd = process.cwd()) {
64
+ return firstExisting(cwd, [CONFIG_FILE, LEGACY_CONFIG_FILE]);
65
+ }
66
+ function lockfilePath(cwd = process.cwd()) {
67
+ return firstExisting(cwd, [LOCKFILE_NAME, LEGACY_LOCKFILE_NAME]) || import_path.default.resolve(cwd, LOCKFILE_NAME);
68
+ }
69
+
49
70
  // src/config.ts
50
71
  function readConfig() {
51
- const configPath = import_path.default.resolve(process.cwd(), "base-ui.json");
52
- if (!import_fs.default.existsSync(configPath)) {
53
- console.error(`base-ui.json not found. Please run "${CLI_NPX} init" first.`);
72
+ const resolved = configFilePath();
73
+ if (!resolved) {
74
+ console.error(
75
+ `${CONFIG_FILE} not found (also looked for ${LEGACY_CONFIG_FILE}). Please run "${CLI_NPX} init" first.`
76
+ );
54
77
  return null;
55
78
  }
56
79
  try {
57
- const config = JSON.parse(import_fs.default.readFileSync(configPath, "utf8"));
80
+ const config = JSON.parse(import_fs2.default.readFileSync(resolved, "utf8"));
58
81
  if (!config || !config.aliases || !config.aliases.components) {
59
- throw new Error("Missing aliases.components in base-ui.json");
82
+ throw new Error(`Missing aliases.components in ${import_path2.default.basename(resolved)}`);
60
83
  }
61
84
  return config;
62
85
  } catch (err) {
63
- console.error(`Error reading base-ui.json: ${err.message}`);
86
+ console.error(`Error reading ${import_path2.default.basename(resolved)}: ${err.message}`);
64
87
  return null;
65
88
  }
66
89
  }
67
90
 
68
91
  // src/lockfile.ts
69
- var import_fs2 = __toESM(require("fs"));
70
- var import_path2 = __toESM(require("path"));
71
- var LOCKFILE_NAME = "base-ui-lock.json";
92
+ var import_fs3 = __toESM(require("fs"));
72
93
  var LOCKFILE_VERSION = 1;
73
- function lockfilePath() {
74
- return import_path2.default.resolve(process.cwd(), LOCKFILE_NAME);
75
- }
76
94
  function readLockFile() {
77
95
  const p = lockfilePath();
78
- if (!import_fs2.default.existsSync(p)) {
96
+ if (!import_fs3.default.existsSync(p)) {
79
97
  return { version: LOCKFILE_VERSION, components: {} };
80
98
  }
81
99
  try {
82
- const parsed = JSON.parse(import_fs2.default.readFileSync(p, "utf8"));
100
+ const parsed = JSON.parse(import_fs3.default.readFileSync(p, "utf8"));
83
101
  if (parsed && typeof parsed === "object" && parsed.components) {
84
102
  return { version: parsed.version ?? LOCKFILE_VERSION, components: parsed.components };
85
103
  }
@@ -33,8 +33,8 @@ __export(doctor_exports, {
33
33
  doctor: () => doctor
34
34
  });
35
35
  module.exports = __toCommonJS(doctor_exports);
36
- var import_fs = __toESM(require("fs"));
37
- var import_path = __toESM(require("path"));
36
+ var import_fs2 = __toESM(require("fs"));
37
+ var import_path2 = __toESM(require("path"));
38
38
  var import_vendor = require("../vendor");
39
39
  var import_registry = require("../registry");
40
40
 
@@ -44,6 +44,27 @@ var CLI_NPX = `npx ${CLI_BIN}`;
44
44
  var MCP_PACKAGE = "ply-ui-mcp";
45
45
  var MCP_NPX = `npx -y ${MCP_PACKAGE}`;
46
46
 
47
+ // src/paths.ts
48
+ var import_fs = __toESM(require("fs"));
49
+ var import_path = __toESM(require("path"));
50
+ var CONFIG_FILE = "ply-ui.json";
51
+ var LEGACY_CONFIG_FILE = "base-ui.json";
52
+ var CSS_FILE = "ply-ui.css";
53
+ var LEGACY_CSS_FILE = "base-ui.css";
54
+ function firstExisting(cwd, names) {
55
+ for (const name of names) {
56
+ const resolved = import_path.default.resolve(cwd, name);
57
+ if (import_fs.default.existsSync(resolved)) return resolved;
58
+ }
59
+ return null;
60
+ }
61
+ function configFilePath(cwd = process.cwd()) {
62
+ return firstExisting(cwd, [CONFIG_FILE, LEGACY_CONFIG_FILE]);
63
+ }
64
+ function cssImportCandidates() {
65
+ return [`@import './${CSS_FILE}'`, `@import './${LEGACY_CSS_FILE}'`];
66
+ }
67
+
47
68
  // src/commands/doctor.ts
48
69
  var ICON_SPRITES = ["icons.svg", "icons-filled.svg"];
49
70
  function pickAngularProject(angularJson) {
@@ -56,7 +77,7 @@ function pickAngularProject(angularJson) {
56
77
  function resolveAssetsDir(cwd, angularJson) {
57
78
  const picked = pickAngularProject(angularJson);
58
79
  const buildOptions = picked?.project?.architect?.build?.options;
59
- if (!buildOptions) return import_path.default.resolve(cwd, "src/assets");
80
+ if (!buildOptions) return import_path2.default.resolve(cwd, "src/assets");
60
81
  const assets = buildOptions.assets || [];
61
82
  const hasSrcAssets = assets.some(
62
83
  (a) => a === "src/assets" || typeof a === "object" && a?.input === "src/assets"
@@ -64,68 +85,68 @@ function resolveAssetsDir(cwd, angularJson) {
64
85
  const publicEntry = assets.find(
65
86
  (a) => a === "public" || typeof a === "object" && a?.input === "public"
66
87
  );
67
- if (hasSrcAssets) return import_path.default.resolve(cwd, "src/assets");
68
- if (publicEntry) return import_path.default.resolve(cwd, "public/assets");
69
- return import_path.default.resolve(cwd, "src/assets");
88
+ if (hasSrcAssets) return import_path2.default.resolve(cwd, "src/assets");
89
+ if (publicEntry) return import_path2.default.resolve(cwd, "public/assets");
90
+ return import_path2.default.resolve(cwd, "src/assets");
70
91
  }
71
92
  function readJsonSafe(filePath) {
72
93
  try {
73
- return JSON.parse(import_fs.default.readFileSync(filePath, "utf8"));
94
+ return JSON.parse(import_fs2.default.readFileSync(filePath, "utf8"));
74
95
  } catch {
75
96
  return null;
76
97
  }
77
98
  }
78
99
  function readTextSafe(filePath) {
79
100
  try {
80
- return import_fs.default.readFileSync(filePath, "utf8");
101
+ return import_fs2.default.readFileSync(filePath, "utf8");
81
102
  } catch {
82
103
  return null;
83
104
  }
84
105
  }
85
106
  function sourceCovers(source, tailwindDir, targetDir) {
86
107
  const raw = source.replace(/[*?[\]{}].*$/, "");
87
- const resolved = import_path.default.resolve(tailwindDir, raw);
88
- const resolvedDir = raw.endsWith("/") || raw.endsWith("\\") || import_path.default.extname(raw) === "" ? resolved : import_path.default.dirname(resolved);
89
- return targetDir === resolvedDir || targetDir.startsWith(resolvedDir + import_path.default.sep);
108
+ const resolved = import_path2.default.resolve(tailwindDir, raw);
109
+ const resolvedDir = raw.endsWith("/") || raw.endsWith("\\") || import_path2.default.extname(raw) === "" ? resolved : import_path2.default.dirname(resolved);
110
+ return targetDir === resolvedDir || targetDir.startsWith(resolvedDir + import_path2.default.sep);
90
111
  }
91
112
  async function doctor(options = {}) {
92
113
  const cwd = options.cwd || process.cwd();
93
- const relative = (filePath) => import_path.default.relative(cwd, filePath) || ".";
114
+ const relative = (filePath) => import_path2.default.relative(cwd, filePath) || ".";
94
115
  const spinner = (0, import_vendor.ora)("Checking Ply setup...").start();
95
116
  const checks = [];
96
- const configPath = import_path.default.resolve(cwd, "base-ui.json");
117
+ const configPath = configFilePath(cwd);
97
118
  let config = null;
98
- if (!import_fs.default.existsSync(configPath)) {
119
+ if (!configPath) {
99
120
  checks.push({
100
- name: "base-ui.json",
121
+ name: CONFIG_FILE,
101
122
  status: "error",
102
- message: `Not found. Run "${CLI_NPX} init" first.`
123
+ message: `Not found (also looked for ${LEGACY_CONFIG_FILE}). Run "${CLI_NPX} init" first.`
103
124
  });
104
125
  } else {
105
126
  config = readJsonSafe(configPath);
106
127
  if (!config) {
107
128
  checks.push({
108
- name: "base-ui.json",
129
+ name: CONFIG_FILE,
109
130
  status: "error",
110
131
  message: `${relative(configPath)} exists but is not valid JSON.`
111
132
  });
112
133
  } else if (!config.aliases?.components) {
113
134
  checks.push({
114
- name: "base-ui.json",
135
+ name: CONFIG_FILE,
115
136
  status: "error",
116
137
  message: `${relative(configPath)} is missing aliases.components.`
117
138
  });
118
139
  } else {
119
140
  checks.push({
120
- name: "base-ui.json",
141
+ name: CONFIG_FILE,
121
142
  status: "ok",
122
143
  message: `${relative(configPath)} exists and aliases.components is set.`
123
144
  });
124
145
  }
125
146
  }
126
- const angularJsonPath = import_path.default.resolve(cwd, "angular.json");
147
+ const angularJsonPath = import_path2.default.resolve(cwd, "angular.json");
127
148
  let angularJson = null;
128
- if (!import_fs.default.existsSync(angularJsonPath)) {
149
+ if (!import_fs2.default.existsSync(angularJsonPath)) {
129
150
  checks.push({
130
151
  name: "Angular workspace",
131
152
  status: "warn",
@@ -148,7 +169,7 @@ async function doctor(options = {}) {
148
169
  });
149
170
  }
150
171
  }
151
- const packageJsonPath = import_path.default.resolve(cwd, "package.json");
172
+ const packageJsonPath = import_path2.default.resolve(cwd, "package.json");
152
173
  const packageJson = readJsonSafe(packageJsonPath);
153
174
  if (!packageJson) {
154
175
  checks.push({
@@ -175,13 +196,13 @@ async function doctor(options = {}) {
175
196
  });
176
197
  }
177
198
  }
178
- const tailwindPath = import_path.default.resolve(cwd, config?.tailwind?.config || "src/tailwind.css");
199
+ const tailwindPath = import_path2.default.resolve(cwd, config?.tailwind?.config || "src/tailwind.css");
179
200
  const tailwindContent = readTextSafe(tailwindPath);
180
201
  if (!tailwindContent) {
181
202
  checks.push({
182
203
  name: "Tailwind entry",
183
204
  status: "error",
184
- message: `${relative(tailwindPath)} not found. Set tailwind.config in base-ui.json or create it.`
205
+ message: `${relative(tailwindPath)} not found. Set tailwind.config in ${CONFIG_FILE} or create it.`
185
206
  });
186
207
  } else {
187
208
  checks.push({
@@ -211,8 +232,8 @@ async function doctor(options = {}) {
211
232
  });
212
233
  } else {
213
234
  const componentsAlias2 = config?.aliases?.components || "src/app/components";
214
- const componentsDir = import_path.default.resolve(cwd, componentsAlias2);
215
- const tailwindDir = import_path.default.dirname(tailwindPath);
235
+ const componentsDir = import_path2.default.resolve(cwd, componentsAlias2);
236
+ const tailwindDir = import_path2.default.dirname(tailwindPath);
216
237
  const hasComponentSource = sourceMatches.some((m) => {
217
238
  const raw = m.match(/@source\s+["']([^"']+)["']/)?.[1];
218
239
  if (!raw) return false;
@@ -233,46 +254,50 @@ async function doctor(options = {}) {
233
254
  }
234
255
  }
235
256
  }
236
- const stylesPath = import_path.default.resolve(cwd, config?.tailwind?.css || "src/styles.scss");
257
+ const stylesPath = import_path2.default.resolve(cwd, config?.tailwind?.css || "src/styles.scss");
237
258
  const stylesContent = readTextSafe(stylesPath);
238
259
  if (!stylesContent) {
239
260
  checks.push({
240
261
  name: "Global styles",
241
262
  status: "error",
242
- message: `${relative(stylesPath)} not found. Set tailwind.css in base-ui.json or create it.`
263
+ message: `${relative(stylesPath)} not found. Set tailwind.css in ${CONFIG_FILE} or create it.`
243
264
  });
244
265
  } else {
245
- const baseCssImport = "@import './base-ui.css'";
246
- if (!stylesContent.includes(baseCssImport)) {
266
+ const cssImports = cssImportCandidates();
267
+ const hasCssImport = cssImports.some((line) => stylesContent.includes(line));
268
+ if (!hasCssImport) {
247
269
  checks.push({
248
270
  name: "Global styles",
249
271
  status: "warn",
250
- message: `${relative(stylesPath)} does not import ${baseCssImport}. Ply global styles will be missing.`
272
+ message: `${relative(stylesPath)} does not import './${CSS_FILE}'. Ply global styles will be missing.`
251
273
  });
252
274
  } else {
253
275
  checks.push({
254
276
  name: "Global styles",
255
277
  status: "ok",
256
- message: `${relative(stylesPath)} imports base-ui.css.`
278
+ message: `${relative(stylesPath)} imports Ply global styles.`
257
279
  });
258
280
  }
259
- const baseCssPath = import_path.default.join(import_path.default.dirname(stylesPath), "base-ui.css");
260
- if (!import_fs.default.existsSync(baseCssPath)) {
281
+ const stylesDir = import_path2.default.dirname(stylesPath);
282
+ const cssPath = import_path2.default.join(stylesDir, CSS_FILE);
283
+ const legacyCssPath = import_path2.default.join(stylesDir, LEGACY_CSS_FILE);
284
+ const resolvedCss = import_fs2.default.existsSync(cssPath) ? cssPath : import_fs2.default.existsSync(legacyCssPath) ? legacyCssPath : null;
285
+ if (!resolvedCss) {
261
286
  checks.push({
262
- name: "base-ui.css",
287
+ name: CSS_FILE,
263
288
  status: "error",
264
- message: `${relative(baseCssPath)} not found. Run "${CLI_NPX} init" to create it.`
289
+ message: `${relative(cssPath)} not found. Run "${CLI_NPX} init" to create it.`
265
290
  });
266
291
  } else {
267
292
  checks.push({
268
- name: "base-ui.css",
293
+ name: CSS_FILE,
269
294
  status: "ok",
270
- message: `${relative(baseCssPath)} exists.`
295
+ message: `${relative(resolvedCss)} exists.`
271
296
  });
272
297
  }
273
298
  }
274
- const assetsDir = angularJson ? resolveAssetsDir(cwd, angularJson) : import_path.default.resolve(cwd, "src/assets");
275
- const missingSprites = ICON_SPRITES.filter((s) => !import_fs.default.existsSync(import_path.default.join(assetsDir, s)));
299
+ const assetsDir = angularJson ? resolveAssetsDir(cwd, angularJson) : import_path2.default.resolve(cwd, "src/assets");
300
+ const missingSprites = ICON_SPRITES.filter((s) => !import_fs2.default.existsSync(import_path2.default.join(assetsDir, s)));
276
301
  if (missingSprites.length) {
277
302
  checks.push({
278
303
  name: "Icon sprites",
@@ -288,8 +313,8 @@ async function doctor(options = {}) {
288
313
  }
289
314
  const componentsAlias = config?.aliases?.components;
290
315
  if (componentsAlias) {
291
- const componentsDir = import_path.default.resolve(cwd, componentsAlias);
292
- if (!import_fs.default.existsSync(componentsDir)) {
316
+ const componentsDir = import_path2.default.resolve(cwd, componentsAlias);
317
+ if (!import_fs2.default.existsSync(componentsDir)) {
293
318
  checks.push({
294
319
  name: "Components directory",
295
320
  status: "warn",