@akii09/pdfx-cli 0.1.3 → 0.1.5

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 +792 -275
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/index.ts
9
+ import { readFileSync } from "fs";
10
+ import { join } from "path";
11
+ import { fileURLToPath } from "url";
9
12
  import { Command } from "commander";
10
13
 
11
14
  // src/commands/add.ts
@@ -489,8 +492,8 @@ function getErrorMap() {
489
492
 
490
493
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
491
494
  var makeIssue = (params) => {
492
- const { data, path: path11, errorMaps, issueData } = params;
493
- const fullPath = [...path11, ...issueData.path || []];
495
+ const { data, path: path12, errorMaps, issueData } = params;
496
+ const fullPath = [...path12, ...issueData.path || []];
494
497
  const fullIssue = {
495
498
  ...issueData,
496
499
  path: fullPath
@@ -606,11 +609,11 @@ var errorUtil;
606
609
 
607
610
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
608
611
  var ParseInputLazyPath = class {
609
- constructor(parent, value, path11, key) {
612
+ constructor(parent, value, path12, key) {
610
613
  this._cachedPath = [];
611
614
  this.parent = parent;
612
615
  this.data = value;
613
- this._path = path11;
616
+ this._path = path12;
614
617
  this._key = key;
615
618
  }
616
619
  get path() {
@@ -989,11 +992,11 @@ function datetimeRegex(args) {
989
992
  regex = `${regex}(${opts.join("|")})`;
990
993
  return new RegExp(`^${regex}$`);
991
994
  }
992
- function isValidIP(ip, version) {
993
- if ((version === "v4" || !version) && ipv4Regex.test(ip)) {
995
+ function isValidIP(ip, version2) {
996
+ if ((version2 === "v4" || !version2) && ipv4Regex.test(ip)) {
994
997
  return true;
995
998
  }
996
- if ((version === "v6" || !version) && ipv6Regex.test(ip)) {
999
+ if ((version2 === "v6" || !version2) && ipv6Regex.test(ip)) {
997
1000
  return true;
998
1001
  }
999
1002
  return false;
@@ -1020,11 +1023,11 @@ function isValidJWT(jwt, alg) {
1020
1023
  return false;
1021
1024
  }
1022
1025
  }
1023
- function isValidCidr(ip, version) {
1024
- if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) {
1026
+ function isValidCidr(ip, version2) {
1027
+ if ((version2 === "v4" || !version2) && ipv4CidrRegex.test(ip)) {
1025
1028
  return true;
1026
1029
  }
1027
- if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) {
1030
+ if ((version2 === "v6" || !version2) && ipv6CidrRegex.test(ip)) {
1028
1031
  return true;
1029
1032
  }
1030
1033
  return false;
@@ -4151,7 +4154,8 @@ var registryFileTypes = [
4151
4154
  "registry:component",
4152
4155
  "registry:lib",
4153
4156
  "registry:style",
4154
- "registry:template"
4157
+ "registry:template",
4158
+ "registry:block"
4155
4159
  ];
4156
4160
  var registryFileSchema = external_exports.object({
4157
4161
  path: external_exports.string().min(1),
@@ -4498,14 +4502,14 @@ function getInstalledVersion(packageName, cwd = process.cwd()) {
4498
4502
  ...pkg.dependencies,
4499
4503
  ...pkg.devDependencies
4500
4504
  };
4501
- const version = deps[packageName];
4502
- if (!version) return null;
4503
- return semver.clean(version) || semver.coerce(version)?.version || null;
4505
+ const version2 = deps[packageName];
4506
+ if (!version2) return null;
4507
+ return semver.clean(version2) || semver.coerce(version2)?.version || null;
4504
4508
  }
4505
4509
  function validateReactPdfRenderer(cwd = process.cwd()) {
4506
- const version = getInstalledVersion("@react-pdf/renderer", cwd);
4510
+ const version2 = getInstalledVersion("@react-pdf/renderer", cwd);
4507
4511
  const required = REQUIRED_VERSIONS["@react-pdf/renderer"];
4508
- if (!version) {
4512
+ if (!version2) {
4509
4513
  return {
4510
4514
  valid: false,
4511
4515
  installed: false,
@@ -4513,19 +4517,19 @@ function validateReactPdfRenderer(cwd = process.cwd()) {
4513
4517
  message: "@react-pdf/renderer is not installed"
4514
4518
  };
4515
4519
  }
4516
- const isCompatible = semver.satisfies(version, required);
4520
+ const isCompatible = semver.satisfies(version2, required);
4517
4521
  return {
4518
4522
  valid: isCompatible,
4519
4523
  installed: true,
4520
- currentVersion: version,
4524
+ currentVersion: version2,
4521
4525
  requiredVersion: required,
4522
- message: isCompatible ? "@react-pdf/renderer version is compatible" : `@react-pdf/renderer version ${version} does not meet requirement ${required}`
4526
+ message: isCompatible ? "@react-pdf/renderer version is compatible" : `@react-pdf/renderer version ${version2} does not meet requirement ${required}`
4523
4527
  };
4524
4528
  }
4525
4529
  function validateReact(cwd = process.cwd()) {
4526
- const version = getInstalledVersion("react", cwd);
4530
+ const version2 = getInstalledVersion("react", cwd);
4527
4531
  const required = REQUIRED_VERSIONS.react;
4528
- if (!version) {
4532
+ if (!version2) {
4529
4533
  return {
4530
4534
  valid: false,
4531
4535
  installed: false,
@@ -4533,26 +4537,26 @@ function validateReact(cwd = process.cwd()) {
4533
4537
  message: "React is not installed"
4534
4538
  };
4535
4539
  }
4536
- const isCompatible = semver.satisfies(version, required);
4540
+ const isCompatible = semver.satisfies(version2, required);
4537
4541
  return {
4538
4542
  valid: isCompatible,
4539
4543
  installed: true,
4540
- currentVersion: version,
4544
+ currentVersion: version2,
4541
4545
  requiredVersion: required,
4542
- message: isCompatible ? "React version is compatible" : `React version ${version} does not meet requirement ${required}`
4546
+ message: isCompatible ? "React version is compatible" : `React version ${version2} does not meet requirement ${required}`
4543
4547
  };
4544
4548
  }
4545
4549
  function validateNodeVersion() {
4546
- const version = process.version;
4550
+ const version2 = process.version;
4547
4551
  const required = REQUIRED_VERSIONS.node;
4548
- const cleanVersion = semver.clean(version);
4552
+ const cleanVersion = semver.clean(version2);
4549
4553
  if (!cleanVersion) {
4550
4554
  return {
4551
4555
  valid: false,
4552
4556
  installed: true,
4553
- currentVersion: version,
4557
+ currentVersion: version2,
4554
4558
  requiredVersion: required,
4555
- message: `Unable to parse Node.js version: ${version}`
4559
+ message: `Unable to parse Node.js version: ${version2}`
4556
4560
  };
4557
4561
  }
4558
4562
  const isCompatible = semver.satisfies(cleanVersion, required);
@@ -4901,7 +4905,9 @@ async function fetchComponent(name, registryUrl) {
4901
4905
  } catch (err) {
4902
4906
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
4903
4907
  throw new NetworkError(
4904
- isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
4908
+ isTimeout ? `Registry request timed out after 10 seconds.
4909
+ ${chalk.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${registryUrl}
4910
+ ${chalk.dim("Verify the URL is correct and you have internet access.")}`
4905
4911
  );
4906
4912
  }
4907
4913
  if (!response.ok) {
@@ -4991,6 +4997,12 @@ async function installComponent(name, config, force) {
4991
4997
  return;
4992
4998
  }
4993
4999
  async function add(components, options = {}) {
5000
+ if (!components || components.length === 0) {
5001
+ console.error(chalk.red("Error: Component name required"));
5002
+ console.log(chalk.dim("Usage: pdfx add <component...>"));
5003
+ console.log(chalk.dim("Example: pdfx add heading text table\n"));
5004
+ process.exit(1);
5005
+ }
4994
5006
  const reactPdfCheck = validateReactPdfRenderer();
4995
5007
  if (!reactPdfCheck.installed) {
4996
5008
  console.error(chalk.red("\nError: @react-pdf/renderer is not installed\n"));
@@ -5018,6 +5030,9 @@ async function add(components, options = {}) {
5018
5030
  let config;
5019
5031
  try {
5020
5032
  config = readConfig(configPath);
5033
+ if (options.registry) {
5034
+ config = { ...config, registry: options.registry };
5035
+ }
5021
5036
  } catch (error) {
5022
5037
  if (error instanceof ConfigError) {
5023
5038
  console.error(chalk.red(error.message));
@@ -5075,13 +5090,338 @@ async function add(components, options = {}) {
5075
5090
  }
5076
5091
  }
5077
5092
 
5078
- // src/commands/diff.ts
5079
- import fs4 from "fs";
5093
+ // src/commands/block.ts
5080
5094
  import path4 from "path";
5081
5095
  import chalk2 from "chalk";
5082
5096
  import ora2 from "ora";
5083
- var FETCH_TIMEOUT_MS = 1e4;
5084
- async function diff(components) {
5097
+ import prompts2 from "prompts";
5098
+
5099
+ // src/constants.ts
5100
+ var DEFAULTS = {
5101
+ REGISTRY_URL: "https://pdfx.akashpise.dev/r",
5102
+ SCHEMA_URL: "https://pdfx.akashpise.dev/schema.json",
5103
+ COMPONENT_DIR: "./src/components/pdfx",
5104
+ THEME_FILE: "./src/lib/pdfx-theme.ts",
5105
+ TEMPLATE_DIR: "./src/templates/pdfx"
5106
+ };
5107
+ var REGISTRY_SUBPATHS = {
5108
+ TEMPLATES: "templates"
5109
+ };
5110
+
5111
+ // src/commands/block.ts
5112
+ function readConfig2(configPath) {
5113
+ const raw = readJsonFile(configPath);
5114
+ const result = configSchema.safeParse(raw);
5115
+ if (!result.success) {
5116
+ const issues = result.error.issues.map((i) => i.message).join(", ");
5117
+ throw new ConfigError(
5118
+ `Invalid pdfx.json: ${issues}`,
5119
+ `Fix the config or re-run ${chalk2.cyan("pdfx init")}`
5120
+ );
5121
+ }
5122
+ return result.data;
5123
+ }
5124
+ async function fetchBlock(name, registryUrl) {
5125
+ const url = `${registryUrl}/${REGISTRY_SUBPATHS.TEMPLATES}/${name}.json`;
5126
+ let response;
5127
+ try {
5128
+ response = await fetch(url, { signal: AbortSignal.timeout(1e4) });
5129
+ } catch (err) {
5130
+ const isTimeout = err instanceof Error && err.name === "TimeoutError";
5131
+ throw new NetworkError(
5132
+ isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
5133
+ );
5134
+ }
5135
+ if (!response.ok) {
5136
+ throw new RegistryError(
5137
+ response.status === 404 ? `Block "${name}" not found in registry` : `Registry returned HTTP ${response.status}`
5138
+ );
5139
+ }
5140
+ let data;
5141
+ try {
5142
+ data = await response.json();
5143
+ } catch {
5144
+ throw new RegistryError(`Invalid response for "${name}": not valid JSON`);
5145
+ }
5146
+ const result = registryItemSchema.safeParse(data);
5147
+ if (!result.success) {
5148
+ throw new RegistryError(
5149
+ `Invalid registry entry for "${name}": ${result.error.issues[0]?.message}`
5150
+ );
5151
+ }
5152
+ return result.data;
5153
+ }
5154
+ async function fetchComponent2(name, registryUrl) {
5155
+ const url = `${registryUrl}/${name}.json`;
5156
+ let response;
5157
+ try {
5158
+ response = await fetch(url, { signal: AbortSignal.timeout(1e4) });
5159
+ } catch (err) {
5160
+ const isTimeout = err instanceof Error && err.name === "TimeoutError";
5161
+ throw new NetworkError(
5162
+ isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
5163
+ );
5164
+ }
5165
+ if (!response.ok) {
5166
+ throw new RegistryError(
5167
+ response.status === 404 ? `Component "${name}" not found in registry` : `Registry returned HTTP ${response.status}`
5168
+ );
5169
+ }
5170
+ let data;
5171
+ try {
5172
+ data = await response.json();
5173
+ } catch {
5174
+ throw new RegistryError(`Invalid response for "${name}": not valid JSON`);
5175
+ }
5176
+ const result = registryItemSchema.safeParse(data);
5177
+ if (!result.success) {
5178
+ throw new RegistryError(
5179
+ `Invalid registry entry for "${name}": ${result.error.issues[0]?.message}`
5180
+ );
5181
+ }
5182
+ return result.data;
5183
+ }
5184
+ function resolveBlockImports(content, blockName, config) {
5185
+ const cwd = process.cwd();
5186
+ const blockSubdir = path4.resolve(cwd, config.templateDir ?? DEFAULTS.TEMPLATE_DIR, blockName);
5187
+ let result = content.replace(
5188
+ /from '\.\.\/\.\.\/components\/pdfx\/([a-z][a-z0-9-]*)\/pdfx-([a-z][a-z0-9-]*)'/g,
5189
+ (_match, componentName) => {
5190
+ const absCompFile = path4.resolve(
5191
+ cwd,
5192
+ config.componentDir,
5193
+ componentName,
5194
+ `pdfx-${componentName}`
5195
+ );
5196
+ let rel = path4.relative(blockSubdir, absCompFile);
5197
+ if (!rel.startsWith(".")) rel = `./${rel}`;
5198
+ return `from '${rel}'`;
5199
+ }
5200
+ );
5201
+ if (config.theme) {
5202
+ const absThemePath = path4.resolve(cwd, config.theme).replace(/\.tsx?$/, "");
5203
+ let relTheme = path4.relative(blockSubdir, absThemePath);
5204
+ if (!relTheme.startsWith(".")) relTheme = `./${relTheme}`;
5205
+ const absContextPath = path4.join(
5206
+ path4.dirname(path4.resolve(cwd, config.theme)),
5207
+ "pdfx-theme-context"
5208
+ );
5209
+ let relContext = path4.relative(blockSubdir, absContextPath);
5210
+ if (!relContext.startsWith(".")) relContext = `./${relContext}`;
5211
+ result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme'/g, `from '${relTheme}'`);
5212
+ result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme-context'/g, `from '${relContext}'`);
5213
+ }
5214
+ return result;
5215
+ }
5216
+ async function resolveConflict(fileName, currentDecision) {
5217
+ if (currentDecision === "overwrite-all") return "overwrite-all";
5218
+ const { action } = await prompts2({
5219
+ type: "select",
5220
+ name: "action",
5221
+ message: `${chalk2.yellow(fileName)} already exists. What would you like to do?`,
5222
+ choices: [
5223
+ { title: "Skip", value: "skip", description: "Keep the existing file unchanged" },
5224
+ { title: "Overwrite", value: "overwrite", description: "Replace this file only" },
5225
+ {
5226
+ title: "Overwrite all",
5227
+ value: "overwrite-all",
5228
+ description: "Replace all conflicting files"
5229
+ }
5230
+ ],
5231
+ initial: 0
5232
+ });
5233
+ if (!action) throw new ValidationError("Cancelled by user");
5234
+ return action;
5235
+ }
5236
+ async function ensurePeerComponents(block, config, force) {
5237
+ const installedPeers = [];
5238
+ const peerWarnings = [];
5239
+ if (!block.peerComponents || block.peerComponents.length === 0) {
5240
+ return { installedPeers, peerWarnings };
5241
+ }
5242
+ const componentBaseDir = path4.resolve(process.cwd(), config.componentDir);
5243
+ for (const componentName of block.peerComponents) {
5244
+ const componentDir = path4.join(componentBaseDir, componentName);
5245
+ const expectedMain = path4.join(componentDir, `pdfx-${componentName}.tsx`);
5246
+ if (checkFileExists(componentDir)) {
5247
+ if (!checkFileExists(expectedMain)) {
5248
+ peerWarnings.push(
5249
+ `${componentName}: directory exists but expected file missing (${path4.basename(expectedMain)})`
5250
+ );
5251
+ } else {
5252
+ peerWarnings.push(
5253
+ `${componentName}: already exists, skipped install (use "pdfx diff ${componentName}" to verify freshness)`
5254
+ );
5255
+ }
5256
+ continue;
5257
+ }
5258
+ const component = await fetchComponent2(componentName, config.registry);
5259
+ ensureDir(componentDir);
5260
+ const componentRelDir = path4.join(config.componentDir, component.name);
5261
+ for (const file of component.files) {
5262
+ const fileName = path4.basename(file.path);
5263
+ const filePath = safePath(componentDir, fileName);
5264
+ let content = file.content;
5265
+ if (config.theme && (content.includes("pdfx-theme") || content.includes("pdfx-theme-context"))) {
5266
+ content = resolveThemeImport(componentRelDir, config.theme, content);
5267
+ }
5268
+ if (checkFileExists(filePath) && !force) {
5269
+ peerWarnings.push(
5270
+ `${componentName}: skipped writing ${fileName} because it already exists (use --force to overwrite)`
5271
+ );
5272
+ continue;
5273
+ }
5274
+ writeFile(filePath, content);
5275
+ }
5276
+ installedPeers.push(componentName);
5277
+ }
5278
+ return { installedPeers, peerWarnings };
5279
+ }
5280
+ async function installBlock(name, config, force) {
5281
+ const block = await fetchBlock(name, config.registry);
5282
+ const peerResult = await ensurePeerComponents(block, config, force);
5283
+ const blockBaseDir = path4.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
5284
+ const blockDir = path4.join(blockBaseDir, block.name);
5285
+ ensureDir(blockDir);
5286
+ const filesToWrite = [];
5287
+ for (const file of block.files) {
5288
+ const fileName = path4.basename(file.path);
5289
+ const filePath = safePath(blockDir, fileName);
5290
+ let content = file.content;
5291
+ if (/\.(tsx?|jsx?)$/.test(fileName) && content.includes("../../")) {
5292
+ content = resolveBlockImports(content, block.name, config);
5293
+ }
5294
+ filesToWrite.push({ filePath, content });
5295
+ }
5296
+ if (!force) {
5297
+ let globalDecision = null;
5298
+ const resolved = [];
5299
+ for (const file of filesToWrite) {
5300
+ if (checkFileExists(file.filePath)) {
5301
+ const decision = await resolveConflict(path4.basename(file.filePath), globalDecision);
5302
+ if (decision === "overwrite-all") {
5303
+ globalDecision = "overwrite-all";
5304
+ }
5305
+ resolved.push({ ...file, skip: decision === "skip" });
5306
+ } else {
5307
+ resolved.push({ ...file, skip: false });
5308
+ }
5309
+ }
5310
+ for (const file of resolved) {
5311
+ if (!file.skip) {
5312
+ writeFile(file.filePath, file.content);
5313
+ } else {
5314
+ console.log(chalk2.dim(` skipped ${path4.basename(file.filePath)}`));
5315
+ }
5316
+ }
5317
+ } else {
5318
+ for (const file of filesToWrite) {
5319
+ writeFile(file.filePath, file.content);
5320
+ }
5321
+ }
5322
+ if (block.peerComponents && block.peerComponents.length > 0) {
5323
+ const componentBaseDir = path4.resolve(process.cwd(), config.componentDir);
5324
+ const missing = [];
5325
+ for (const comp of block.peerComponents) {
5326
+ const compDir = path4.join(componentBaseDir, comp);
5327
+ if (!checkFileExists(compDir)) {
5328
+ missing.push(comp);
5329
+ }
5330
+ }
5331
+ if (missing.length > 0) {
5332
+ console.log();
5333
+ console.log(chalk2.yellow(" Missing peer components:"));
5334
+ for (const comp of missing) {
5335
+ console.log(chalk2.dim(` ${comp} \u2192 run: ${chalk2.cyan(`pdfx add ${comp}`)}`));
5336
+ }
5337
+ }
5338
+ }
5339
+ if (config.theme) {
5340
+ const absThemePath = path4.resolve(process.cwd(), config.theme);
5341
+ const contextPath = path4.join(path4.dirname(absThemePath), "pdfx-theme-context.tsx");
5342
+ if (!checkFileExists(contextPath)) {
5343
+ ensureDir(path4.dirname(contextPath));
5344
+ writeFile(contextPath, generateThemeContextFile());
5345
+ }
5346
+ }
5347
+ return peerResult;
5348
+ }
5349
+ async function blockAdd(names, options = {}) {
5350
+ const configPath = path4.join(process.cwd(), "pdfx.json");
5351
+ if (!checkFileExists(configPath)) {
5352
+ console.error(chalk2.red("Error: pdfx.json not found"));
5353
+ console.log(chalk2.yellow("Run: pdfx init"));
5354
+ process.exit(1);
5355
+ }
5356
+ let config;
5357
+ try {
5358
+ config = readConfig2(configPath);
5359
+ } catch (error) {
5360
+ if (error instanceof ConfigError) {
5361
+ console.error(chalk2.red(error.message));
5362
+ if (error.suggestion) console.log(chalk2.yellow(` Hint: ${error.suggestion}`));
5363
+ } else {
5364
+ const message = error instanceof Error ? error.message : String(error);
5365
+ console.error(chalk2.red(message));
5366
+ }
5367
+ process.exit(1);
5368
+ }
5369
+ const force = options.force ?? false;
5370
+ const failed = [];
5371
+ for (const blockName of names) {
5372
+ const nameResult = componentNameSchema.safeParse(blockName);
5373
+ if (!nameResult.success) {
5374
+ console.error(chalk2.red(`Invalid block name: "${blockName}"`));
5375
+ console.log(
5376
+ chalk2.dim(' Names must be lowercase alphanumeric with hyphens (e.g., "invoice-classic")')
5377
+ );
5378
+ failed.push(blockName);
5379
+ continue;
5380
+ }
5381
+ const spinner = ora2(`Adding block ${blockName}...`).start();
5382
+ try {
5383
+ const result = await installBlock(blockName, config, force);
5384
+ spinner.succeed(`Added block ${chalk2.cyan(blockName)}`);
5385
+ if (result.installedPeers.length > 0) {
5386
+ console.log(
5387
+ chalk2.green(` Installed required components: ${result.installedPeers.join(", ")}`)
5388
+ );
5389
+ }
5390
+ for (const warning of result.peerWarnings) {
5391
+ console.log(chalk2.yellow(` Warning: ${warning}`));
5392
+ }
5393
+ } catch (error) {
5394
+ if (error instanceof ValidationError && error.message.includes("Cancelled")) {
5395
+ spinner.info("Cancelled");
5396
+ process.exit(0);
5397
+ } else if (error instanceof NetworkError || error instanceof RegistryError || error instanceof ValidationError) {
5398
+ spinner.fail(error.message);
5399
+ if (error.suggestion) {
5400
+ console.log(chalk2.dim(` Hint: ${error.suggestion}`));
5401
+ }
5402
+ } else {
5403
+ spinner.fail(`Failed to add block ${blockName}`);
5404
+ const message = error instanceof Error ? error.message : String(error);
5405
+ console.error(chalk2.dim(` ${message}`));
5406
+ }
5407
+ failed.push(blockName);
5408
+ }
5409
+ }
5410
+ console.log();
5411
+ if (failed.length > 0) {
5412
+ console.log(chalk2.yellow(`Failed: ${failed.join(", ")}`));
5413
+ }
5414
+ if (failed.length < names.length) {
5415
+ const resolvedDir = path4.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
5416
+ console.log(chalk2.green("Done!"));
5417
+ console.log(chalk2.dim(`Blocks installed to: ${resolvedDir}
5418
+ `));
5419
+ }
5420
+ if (failed.length > 0) {
5421
+ process.exit(1);
5422
+ }
5423
+ }
5424
+ async function blockList() {
5085
5425
  const configPath = path4.join(process.cwd(), "pdfx.json");
5086
5426
  if (!checkFileExists(configPath)) {
5087
5427
  console.error(chalk2.red("Error: pdfx.json not found"));
@@ -5095,14 +5435,85 @@ async function diff(components) {
5095
5435
  process.exit(1);
5096
5436
  }
5097
5437
  const config = configResult.data;
5098
- const targetDir = path4.resolve(process.cwd(), config.componentDir);
5438
+ const spinner = ora2("Fetching block list...").start();
5439
+ try {
5440
+ let response;
5441
+ try {
5442
+ response = await fetch(`${config.registry}/index.json`, {
5443
+ signal: AbortSignal.timeout(1e4)
5444
+ });
5445
+ } catch (err) {
5446
+ const isTimeout = err instanceof Error && err.name === "TimeoutError";
5447
+ throw new NetworkError(
5448
+ isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
5449
+ );
5450
+ }
5451
+ if (!response.ok) {
5452
+ throw new RegistryError(`Registry returned HTTP ${response.status}`);
5453
+ }
5454
+ const data = await response.json();
5455
+ const result = registrySchema.safeParse(data);
5456
+ if (!result.success) {
5457
+ throw new RegistryError("Invalid registry format");
5458
+ }
5459
+ spinner.stop();
5460
+ const blocks = result.data.items.filter((item) => item.type === "registry:block");
5461
+ if (blocks.length === 0) {
5462
+ console.log(chalk2.dim("\n No blocks available in the registry.\n"));
5463
+ return;
5464
+ }
5465
+ const blockBaseDir = path4.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
5466
+ console.log(chalk2.bold(`
5467
+ Available Blocks (${blocks.length})
5468
+ `));
5469
+ for (const item of blocks) {
5470
+ const blockDir = path4.join(blockBaseDir, item.name);
5471
+ const installed = checkFileExists(blockDir);
5472
+ const status = installed ? chalk2.green("[installed]") : chalk2.dim("[not installed]");
5473
+ console.log(` ${chalk2.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
5474
+ console.log(` ${"".padEnd(22)} ${status}`);
5475
+ if (item.peerComponents && item.peerComponents.length > 0) {
5476
+ console.log(chalk2.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
5477
+ }
5478
+ console.log();
5479
+ }
5480
+ console.log(chalk2.dim(` Install with: ${chalk2.cyan("pdfx block add <block-name>")}
5481
+ `));
5482
+ } catch (error) {
5483
+ const message = error instanceof Error ? error.message : String(error);
5484
+ spinner.fail(message);
5485
+ process.exit(1);
5486
+ }
5487
+ }
5488
+
5489
+ // src/commands/diff.ts
5490
+ import fs4 from "fs";
5491
+ import path5 from "path";
5492
+ import chalk3 from "chalk";
5493
+ import ora3 from "ora";
5494
+ var FETCH_TIMEOUT_MS = 1e4;
5495
+ async function diff(components) {
5496
+ const configPath = path5.join(process.cwd(), "pdfx.json");
5497
+ if (!checkFileExists(configPath)) {
5498
+ console.error(chalk3.red("Error: pdfx.json not found"));
5499
+ console.log(chalk3.yellow("Run: pdfx init"));
5500
+ process.exit(1);
5501
+ }
5502
+ const raw = readJsonFile(configPath);
5503
+ const configResult = configSchema.safeParse(raw);
5504
+ if (!configResult.success) {
5505
+ console.error(chalk3.red("Invalid pdfx.json"));
5506
+ process.exit(1);
5507
+ }
5508
+ const config = configResult.data;
5509
+ const targetDir = path5.resolve(process.cwd(), config.componentDir);
5099
5510
  for (const componentName of components) {
5100
5511
  const nameResult = componentNameSchema.safeParse(componentName);
5101
5512
  if (!nameResult.success) {
5102
- console.error(chalk2.red(`Invalid component name: "${componentName}"`));
5513
+ console.error(chalk3.red(`Invalid component name: "${componentName}"`));
5103
5514
  continue;
5104
5515
  }
5105
- const spinner = ora2(`Comparing ${componentName}...`).start();
5516
+ const spinner = ora3(`Comparing ${componentName}...`).start();
5106
5517
  try {
5107
5518
  let response;
5108
5519
  try {
@@ -5112,7 +5523,9 @@ async function diff(components) {
5112
5523
  } catch (err) {
5113
5524
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
5114
5525
  throw new NetworkError(
5115
- isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
5526
+ isTimeout ? `Registry request timed out after 10 seconds.
5527
+ ${chalk3.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
5528
+ ${chalk3.dim("Verify the URL is correct and you have internet access.")}`
5116
5529
  );
5117
5530
  }
5118
5531
  if (!response.ok) {
@@ -5127,28 +5540,33 @@ async function diff(components) {
5127
5540
  }
5128
5541
  const component = result.data;
5129
5542
  spinner.stop();
5130
- const componentSubDir = path4.join(targetDir, component.name);
5543
+ const componentSubDir = path5.join(targetDir, component.name);
5131
5544
  for (const file of component.files) {
5132
- const fileName = path4.basename(file.path);
5545
+ const fileName = path5.basename(file.path);
5133
5546
  const localPath = safePath(componentSubDir, fileName);
5134
5547
  if (!checkFileExists(localPath)) {
5135
- console.log(chalk2.yellow(` ${fileName}: not installed locally`));
5548
+ console.log(chalk3.yellow(` ${fileName}: not installed locally`));
5136
5549
  continue;
5137
5550
  }
5138
5551
  const localContent = fs4.readFileSync(localPath, "utf-8");
5139
5552
  const registryContent = config.theme && (file.content.includes("pdfx-theme") || file.content.includes("pdfx-theme-context")) ? resolveThemeImport(
5140
- path4.join(config.componentDir, component.name),
5553
+ path5.join(config.componentDir, component.name),
5141
5554
  config.theme,
5142
5555
  file.content
5143
5556
  ) : file.content;
5144
5557
  if (localContent === registryContent) {
5145
- console.log(chalk2.green(` ${fileName}: up to date`));
5558
+ console.log(chalk3.green(` ${fileName}: up to date`));
5146
5559
  } else {
5147
- console.log(chalk2.yellow(` ${fileName}: differs from registry`));
5560
+ console.log(chalk3.yellow(` ${fileName}: differs from registry`));
5148
5561
  const localLines = localContent.split("\n");
5149
5562
  const registryLines = registryContent.split("\n");
5150
- console.log(chalk2.dim(` Local: ${localLines.length} lines`));
5151
- console.log(chalk2.dim(` Registry: ${registryLines.length} lines`));
5563
+ const lineDiff = localLines.length - registryLines.length;
5564
+ console.log(chalk3.dim(` Local: ${localLines.length} lines`));
5565
+ console.log(chalk3.dim(` Registry: ${registryLines.length} lines`));
5566
+ if (lineDiff !== 0) {
5567
+ const diffText = lineDiff > 0 ? `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} added locally` : `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} removed locally`;
5568
+ console.log(chalk3.dim(` \u2192 ${diffText}`));
5569
+ }
5152
5570
  }
5153
5571
  }
5154
5572
  console.log();
@@ -5161,32 +5579,20 @@ async function diff(components) {
5161
5579
 
5162
5580
  // src/commands/init.ts
5163
5581
  import fs7 from "fs";
5164
- import path7 from "path";
5165
- import chalk5 from "chalk";
5166
- import ora4 from "ora";
5167
- import prompts3 from "prompts";
5168
-
5169
- // src/constants.ts
5170
- var DEFAULTS = {
5171
- REGISTRY_URL: "https://pdfx.akashpise.dev/r",
5172
- SCHEMA_URL: "https://pdfx.akashpise.dev/schema.json",
5173
- COMPONENT_DIR: "./src/components/pdfx",
5174
- THEME_FILE: "./src/lib/pdfx-theme.ts",
5175
- TEMPLATE_DIR: "./src/templates/pdfx"
5176
- };
5177
- var REGISTRY_SUBPATHS = {
5178
- TEMPLATES: "templates"
5179
- };
5582
+ import path8 from "path";
5583
+ import chalk6 from "chalk";
5584
+ import ora5 from "ora";
5585
+ import prompts4 from "prompts";
5180
5586
 
5181
5587
  // src/utils/install-dependencies.ts
5182
- import chalk3 from "chalk";
5588
+ import chalk4 from "chalk";
5183
5589
  import { execa } from "execa";
5184
- import ora3 from "ora";
5185
- import prompts2 from "prompts";
5590
+ import ora4 from "ora";
5591
+ import prompts3 from "prompts";
5186
5592
 
5187
5593
  // src/utils/package-manager.ts
5188
5594
  import fs5 from "fs";
5189
- import path5 from "path";
5595
+ import path6 from "path";
5190
5596
  var PACKAGE_MANAGERS = {
5191
5597
  pnpm: {
5192
5598
  name: "pnpm",
@@ -5213,7 +5619,7 @@ function detectPackageManager(cwd = process.cwd()) {
5213
5619
  const managers = ["pnpm", "yarn", "bun", "npm"];
5214
5620
  for (const manager of managers) {
5215
5621
  const info = PACKAGE_MANAGERS[manager];
5216
- const lockfilePath = path5.join(cwd, info.lockfile);
5622
+ const lockfilePath = path6.join(cwd, info.lockfile);
5217
5623
  if (fs5.existsSync(lockfilePath)) {
5218
5624
  return info;
5219
5625
  }
@@ -5237,10 +5643,10 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
5237
5643
  const pm = detectPackageManager(cwd);
5238
5644
  const packageName = "@react-pdf/renderer";
5239
5645
  const installCmd = getInstallCommand(pm.name, [packageName]);
5240
- console.log(chalk3.yellow("\n \u26A0 @react-pdf/renderer is required but not installed\n"));
5241
- console.log(chalk3.dim(` This command will run: ${installCmd}
5646
+ console.log(chalk4.yellow("\n \u26A0 @react-pdf/renderer is required but not installed\n"));
5647
+ console.log(chalk4.dim(` This command will run: ${installCmd}
5242
5648
  `));
5243
- const { shouldInstall } = await prompts2({
5649
+ const { shouldInstall } = await prompts3({
5244
5650
  type: "confirm",
5245
5651
  name: "shouldInstall",
5246
5652
  message: "Install @react-pdf/renderer now?",
@@ -5250,10 +5656,10 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
5250
5656
  return {
5251
5657
  success: false,
5252
5658
  message: `Installation cancelled. Please install manually:
5253
- ${chalk3.cyan(installCmd)}`
5659
+ ${chalk4.cyan(installCmd)}`
5254
5660
  };
5255
5661
  }
5256
- const spinner = ora3("Installing @react-pdf/renderer...").start();
5662
+ const spinner = ora4("Installing @react-pdf/renderer...").start();
5257
5663
  try {
5258
5664
  await execa(pm.name, ["add", packageName], {
5259
5665
  cwd,
@@ -5270,7 +5676,7 @@ async function promptAndInstallReactPdf(validation, cwd = process.cwd()) {
5270
5676
  return {
5271
5677
  success: false,
5272
5678
  message: `Installation failed: ${message}
5273
- Try manually: ${chalk3.cyan(installCmd)}`
5679
+ Try manually: ${chalk4.cyan(installCmd)}`
5274
5680
  };
5275
5681
  }
5276
5682
  }
@@ -5278,7 +5684,7 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
5278
5684
  if (!validation.installed) {
5279
5685
  const result = await promptAndInstallReactPdf(validation, cwd);
5280
5686
  if (!result.success) {
5281
- console.error(chalk3.red(`
5687
+ console.error(chalk4.red(`
5282
5688
  ${result.message}
5283
5689
  `));
5284
5690
  return false;
@@ -5287,10 +5693,10 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
5287
5693
  }
5288
5694
  if (!validation.valid) {
5289
5695
  console.log(
5290
- chalk3.yellow(
5696
+ chalk4.yellow(
5291
5697
  `
5292
5698
  \u26A0 ${validation.message}
5293
- ${chalk3.dim("\u2192")} You may encounter compatibility issues
5699
+ ${chalk4.dim("\u2192")} You may encounter compatibility issues
5294
5700
  `
5295
5701
  )
5296
5702
  );
@@ -5299,13 +5705,13 @@ async function ensureReactPdfRenderer(validation, cwd = process.cwd()) {
5299
5705
  }
5300
5706
 
5301
5707
  // src/utils/pre-flight.ts
5302
- import chalk4 from "chalk";
5708
+ import chalk5 from "chalk";
5303
5709
 
5304
5710
  // src/utils/environment-validator.ts
5305
5711
  import fs6 from "fs";
5306
- import path6 from "path";
5712
+ import path7 from "path";
5307
5713
  function validatePackageJson(cwd = process.cwd()) {
5308
- const pkgPath = path6.join(cwd, "package.json");
5714
+ const pkgPath = path7.join(cwd, "package.json");
5309
5715
  const exists = fs6.existsSync(pkgPath);
5310
5716
  return {
5311
5717
  valid: exists,
@@ -5314,7 +5720,7 @@ function validatePackageJson(cwd = process.cwd()) {
5314
5720
  };
5315
5721
  }
5316
5722
  function validateReactProject(cwd = process.cwd()) {
5317
- const pkgPath = path6.join(cwd, "package.json");
5723
+ const pkgPath = path7.join(cwd, "package.json");
5318
5724
  if (!fs6.existsSync(pkgPath)) {
5319
5725
  return {
5320
5726
  valid: false,
@@ -5341,7 +5747,7 @@ function validateReactProject(cwd = process.cwd()) {
5341
5747
  }
5342
5748
  }
5343
5749
  function validatePdfxConfig(cwd = process.cwd()) {
5344
- const configPath = path6.join(cwd, "pdfx.json");
5750
+ const configPath = path7.join(cwd, "pdfx.json");
5345
5751
  const exists = fs6.existsSync(configPath);
5346
5752
  return {
5347
5753
  valid: true,
@@ -5366,43 +5772,43 @@ function runPreFlightChecks(cwd = process.cwd()) {
5366
5772
  if (!environment.hasPackageJson.valid) {
5367
5773
  blockingErrors.push(
5368
5774
  `${environment.hasPackageJson.message}
5369
- ${chalk4.dim("\u2192")} ${environment.hasPackageJson.fixCommand}`
5775
+ ${chalk5.dim("\u2192")} ${environment.hasPackageJson.fixCommand}`
5370
5776
  );
5371
- }
5372
- if (!environment.isReactProject.valid) {
5777
+ } else if (!environment.isReactProject.valid) {
5373
5778
  blockingErrors.push(
5374
5779
  `${environment.isReactProject.message}
5375
- ${chalk4.dim("\u2192")} ${environment.isReactProject.fixCommand}`
5780
+ ${chalk5.dim("\u2192")} ${environment.isReactProject.fixCommand}`
5376
5781
  );
5782
+ } else {
5783
+ if (!dependencies.react.valid && dependencies.react.installed) {
5784
+ warnings.push(
5785
+ `${dependencies.react.message}
5786
+ ${chalk5.dim("\u2192")} Current: ${dependencies.react.currentVersion}, Required: ${dependencies.react.requiredVersion}`
5787
+ );
5788
+ } else if (!dependencies.react.installed) {
5789
+ blockingErrors.push(
5790
+ `${dependencies.react.message}
5791
+ ${chalk5.dim("\u2192")} Install React: npm install react react-dom`
5792
+ );
5793
+ }
5377
5794
  }
5378
5795
  if (!dependencies.nodeJs.valid) {
5379
5796
  blockingErrors.push(
5380
5797
  `${dependencies.nodeJs.message}
5381
- ${chalk4.dim("\u2192")} Current: ${dependencies.nodeJs.currentVersion}, Required: ${dependencies.nodeJs.requiredVersion}
5382
- ${chalk4.dim("\u2192")} Visit https://nodejs.org to upgrade`
5383
- );
5384
- }
5385
- if (!dependencies.react.valid && dependencies.react.installed) {
5386
- warnings.push(
5387
- `${dependencies.react.message}
5388
- ${chalk4.dim("\u2192")} Current: ${dependencies.react.currentVersion}, Required: ${dependencies.react.requiredVersion}`
5389
- );
5390
- } else if (!dependencies.react.installed) {
5391
- blockingErrors.push(
5392
- `${dependencies.react.message}
5393
- ${chalk4.dim("\u2192")} This should have been caught by React project check`
5798
+ ${chalk5.dim("\u2192")} Current: ${dependencies.nodeJs.currentVersion}, Required: ${dependencies.nodeJs.requiredVersion}
5799
+ ${chalk5.dim("\u2192")} Visit https://nodejs.org to upgrade`
5394
5800
  );
5395
5801
  }
5396
5802
  if (dependencies.reactPdfRenderer.installed && !dependencies.reactPdfRenderer.valid) {
5397
5803
  warnings.push(
5398
5804
  `${dependencies.reactPdfRenderer.message}
5399
- ${chalk4.dim("\u2192")} Consider upgrading: npm install @react-pdf/renderer@latest`
5805
+ ${chalk5.dim("\u2192")} Consider upgrading: npm install @react-pdf/renderer@latest`
5400
5806
  );
5401
5807
  }
5402
5808
  if (dependencies.typescript && !dependencies.typescript.valid) {
5403
5809
  warnings.push(
5404
5810
  `${dependencies.typescript.message}
5405
- ${chalk4.dim("\u2192")} Install types: npm install -D @types/react-pdf`
5811
+ ${chalk5.dim("\u2192")} Install types: npm install -D @types/react-pdf`
5406
5812
  );
5407
5813
  }
5408
5814
  return {
@@ -5414,70 +5820,88 @@ function runPreFlightChecks(cwd = process.cwd()) {
5414
5820
  };
5415
5821
  }
5416
5822
  function displayPreFlightResults(result) {
5417
- console.log(chalk4.bold("\n Pre-flight Checks:\n"));
5823
+ console.log(chalk5.bold("\n Pre-flight Checks:\n"));
5418
5824
  if (result.blockingErrors.length > 0) {
5419
- console.log(chalk4.red(" \u2717 Blocking Issues:\n"));
5825
+ console.log(chalk5.red(" \u2717 Blocking Issues:\n"));
5420
5826
  for (const error of result.blockingErrors) {
5421
- console.log(chalk4.red(` \u2022 ${error}
5827
+ console.log(chalk5.red(` \u2022 ${error}
5422
5828
  `));
5423
5829
  }
5424
5830
  }
5425
5831
  if (result.warnings.length > 0) {
5426
- console.log(chalk4.yellow(" \u26A0 Warnings:\n"));
5832
+ console.log(chalk5.yellow(" \u26A0 Warnings:\n"));
5427
5833
  for (const warning of result.warnings) {
5428
- console.log(chalk4.yellow(` \u2022 ${warning}
5834
+ console.log(chalk5.yellow(` \u2022 ${warning}
5429
5835
  `));
5430
5836
  }
5431
5837
  }
5432
5838
  if (result.blockingErrors.length === 0 && result.warnings.length === 0) {
5433
- console.log(chalk4.green(" \u2713 All checks passed!\n"));
5839
+ console.log(chalk5.green(" \u2713 All checks passed!\n"));
5434
5840
  }
5435
5841
  }
5436
5842
 
5437
5843
  // src/commands/init.ts
5438
5844
  async function init() {
5439
- console.log(chalk5.bold.cyan("\n Welcome to the pdfx cli\n"));
5845
+ console.log(chalk6.bold.cyan("\n Welcome to the pdfx cli\n"));
5440
5846
  const preFlightResult = runPreFlightChecks();
5441
5847
  displayPreFlightResults(preFlightResult);
5442
5848
  if (!preFlightResult.canProceed) {
5443
5849
  console.error(
5444
- chalk5.red("\n Cannot proceed due to blocking issues. Please fix them and try again.\n")
5850
+ chalk6.red("\n Cannot proceed due to blocking issues. Please fix them and try again.\n")
5445
5851
  );
5446
5852
  process.exit(1);
5447
5853
  }
5448
5854
  const hasReactPdf = await ensureReactPdfRenderer(preFlightResult.dependencies.reactPdfRenderer);
5449
5855
  if (!hasReactPdf) {
5450
5856
  console.error(
5451
- chalk5.red("\n @react-pdf/renderer is required. Please install it and try again.\n")
5857
+ chalk6.red("\n @react-pdf/renderer is required. Please install it and try again.\n")
5452
5858
  );
5453
5859
  process.exit(1);
5454
5860
  }
5455
- const existingConfig = path7.join(process.cwd(), "pdfx.json");
5861
+ const existingConfig = path8.join(process.cwd(), "pdfx.json");
5456
5862
  if (fs7.existsSync(existingConfig)) {
5457
- const { overwrite } = await prompts3({
5863
+ const { overwrite } = await prompts4({
5458
5864
  type: "confirm",
5459
5865
  name: "overwrite",
5460
5866
  message: "pdfx.json already exists. Overwrite?",
5461
5867
  initial: false
5462
5868
  });
5463
5869
  if (!overwrite) {
5464
- console.log(chalk5.yellow("Init cancelled \u2014 existing config preserved."));
5870
+ console.log(chalk6.yellow("Init cancelled \u2014 existing config preserved."));
5465
5871
  return;
5466
5872
  }
5467
5873
  }
5468
- const answers = await prompts3(
5874
+ const answers = await prompts4(
5469
5875
  [
5470
5876
  {
5471
5877
  type: "text",
5472
5878
  name: "componentDir",
5473
5879
  message: "Where should we install components?",
5474
- initial: DEFAULTS.COMPONENT_DIR
5880
+ initial: DEFAULTS.COMPONENT_DIR,
5881
+ validate: (value) => {
5882
+ if (!value || value.trim().length === 0) {
5883
+ return "Component directory is required";
5884
+ }
5885
+ if (path8.isAbsolute(value)) {
5886
+ return "Please use a relative path (e.g., ./src/components/pdfx)";
5887
+ }
5888
+ if (!value.startsWith(".")) {
5889
+ return "Path should start with ./ or ../ (e.g., ./src/components/pdfx)";
5890
+ }
5891
+ return true;
5892
+ }
5475
5893
  },
5476
5894
  {
5477
5895
  type: "text",
5478
5896
  name: "registry",
5479
5897
  message: "Registry URL:",
5480
- initial: DEFAULTS.REGISTRY_URL
5898
+ initial: DEFAULTS.REGISTRY_URL,
5899
+ validate: (value) => {
5900
+ if (!value || !value.startsWith("http")) {
5901
+ return "Please enter a valid HTTP(S) URL";
5902
+ }
5903
+ return true;
5904
+ }
5481
5905
  },
5482
5906
  {
5483
5907
  type: "select",
@@ -5506,18 +5930,30 @@ async function init() {
5506
5930
  type: "text",
5507
5931
  name: "themePath",
5508
5932
  message: "Where should we create the theme file?",
5509
- initial: DEFAULTS.THEME_FILE
5933
+ initial: DEFAULTS.THEME_FILE,
5934
+ validate: (value) => {
5935
+ if (!value || value.trim().length === 0) {
5936
+ return "Theme path is required";
5937
+ }
5938
+ if (path8.isAbsolute(value)) {
5939
+ return "Please use a relative path (e.g., ./src/lib/pdfx-theme.ts)";
5940
+ }
5941
+ if (!value.endsWith(".ts") && !value.endsWith(".tsx")) {
5942
+ return "Theme file must have .ts or .tsx extension (e.g., ./src/lib/pdfx-theme.ts)";
5943
+ }
5944
+ return true;
5945
+ }
5510
5946
  }
5511
5947
  ],
5512
5948
  {
5513
5949
  onCancel: () => {
5514
- console.log(chalk5.yellow("\nSetup cancelled."));
5950
+ console.log(chalk6.yellow("\nSetup cancelled."));
5515
5951
  process.exit(0);
5516
5952
  }
5517
5953
  }
5518
5954
  );
5519
5955
  if (!answers.componentDir || !answers.registry) {
5520
- console.error(chalk5.red("Missing required fields. Run pdfx init again."));
5956
+ console.error(chalk6.red("Missing required fields. Run pdfx init again."));
5521
5957
  process.exit(1);
5522
5958
  }
5523
5959
  const config = {
@@ -5529,54 +5965,63 @@ async function init() {
5529
5965
  const validation = configSchema.safeParse(config);
5530
5966
  if (!validation.success) {
5531
5967
  const issues = validation.error.issues.map((i) => i.message).join(", ");
5532
- console.error(chalk5.red(`Invalid configuration: ${issues}`));
5968
+ console.error(chalk6.red(`Invalid configuration: ${issues}`));
5533
5969
  process.exit(1);
5534
5970
  }
5535
- const spinner = ora4("Creating config and theme files...").start();
5971
+ const spinner = ora5("Creating config and theme files...").start();
5536
5972
  try {
5537
- fs7.writeFileSync(path7.join(process.cwd(), "pdfx.json"), JSON.stringify(config, null, 2));
5973
+ const componentDirPath = path8.resolve(process.cwd(), answers.componentDir);
5974
+ ensureDir(componentDirPath);
5975
+ fs7.writeFileSync(path8.join(process.cwd(), "pdfx.json"), JSON.stringify(config, null, 2));
5538
5976
  const presetName = answers.themePreset || "professional";
5539
5977
  const preset = themePresets[presetName];
5540
- const themePath = path7.resolve(process.cwd(), config.theme);
5541
- ensureDir(path7.dirname(themePath));
5978
+ const themePath = path8.resolve(process.cwd(), config.theme);
5979
+ ensureDir(path8.dirname(themePath));
5542
5980
  fs7.writeFileSync(themePath, generateThemeFile(preset), "utf-8");
5543
- const contextPath = path7.join(path7.dirname(themePath), "pdfx-theme-context.tsx");
5981
+ const contextPath = path8.join(path8.dirname(themePath), "pdfx-theme-context.tsx");
5544
5982
  fs7.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
5545
5983
  spinner.succeed(`Created pdfx.json + ${config.theme} (${presetName} theme)`);
5546
- console.log(chalk5.green("\nSuccess! You can now run:"));
5547
- console.log(chalk5.cyan(" pdfx add heading"));
5548
- console.log(chalk5.dim(`
5549
- Components: ${path7.resolve(process.cwd(), answers.componentDir)}`));
5550
- console.log(chalk5.dim(` Theme: ${path7.resolve(process.cwd(), config.theme)}
5984
+ console.log(chalk6.green("\nSuccess! You can now run:"));
5985
+ console.log(chalk6.cyan(" pdfx add heading"));
5986
+ console.log(chalk6.dim(`
5987
+ Components: ${path8.resolve(process.cwd(), answers.componentDir)}`));
5988
+ console.log(chalk6.dim(` Theme: ${path8.resolve(process.cwd(), config.theme)}
5551
5989
  `));
5552
5990
  } catch (error) {
5553
5991
  spinner.fail("Failed to create config");
5554
5992
  const message = error instanceof Error ? error.message : String(error);
5555
- console.error(chalk5.dim(` ${message}`));
5993
+ console.error(chalk6.dim(` ${message}`));
5556
5994
  process.exit(1);
5557
5995
  }
5558
5996
  }
5559
5997
 
5560
5998
  // src/commands/list.ts
5561
- import path8 from "path";
5562
- import chalk6 from "chalk";
5563
- import ora5 from "ora";
5999
+ import path9 from "path";
6000
+ import chalk7 from "chalk";
6001
+ import ora6 from "ora";
5564
6002
  var FETCH_TIMEOUT_MS2 = 1e4;
5565
6003
  async function list() {
5566
- const configPath = path8.join(process.cwd(), "pdfx.json");
5567
- if (!checkFileExists(configPath)) {
5568
- console.error(chalk6.red("Error: pdfx.json not found"));
5569
- console.log(chalk6.yellow("Run: pdfx init"));
5570
- process.exit(1);
5571
- }
5572
- const raw = readJsonFile(configPath);
5573
- const configResult = configSchema.safeParse(raw);
5574
- if (!configResult.success) {
5575
- console.error(chalk6.red("Invalid pdfx.json"));
5576
- process.exit(1);
6004
+ const configPath = path9.join(process.cwd(), "pdfx.json");
6005
+ let config;
6006
+ let hasLocalProject = false;
6007
+ if (checkFileExists(configPath)) {
6008
+ const raw = readJsonFile(configPath);
6009
+ const configResult = configSchema.safeParse(raw);
6010
+ if (!configResult.success) {
6011
+ console.error(chalk7.red("Invalid pdfx.json"));
6012
+ process.exit(1);
6013
+ }
6014
+ config = configResult.data;
6015
+ hasLocalProject = true;
6016
+ } else {
6017
+ config = {
6018
+ registry: "https://pdfx.akashpise.dev/r",
6019
+ componentDir: "./src/components/pdfx",
6020
+ theme: "./src/lib/pdfx-theme.ts"
6021
+ };
6022
+ console.log(chalk7.dim("No pdfx.json found. Listing from default registry.\n"));
5577
6023
  }
5578
- const config = configResult.data;
5579
- const spinner = ora5("Fetching component list...").start();
6024
+ const spinner = ora6("Fetching registry...").start();
5580
6025
  try {
5581
6026
  let response;
5582
6027
  try {
@@ -5586,7 +6031,9 @@ async function list() {
5586
6031
  } catch (err) {
5587
6032
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
5588
6033
  throw new NetworkError(
5589
- isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
6034
+ isTimeout ? `Registry request timed out after 10 seconds.
6035
+ ${chalk7.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
6036
+ ${chalk7.dim("Verify the URL is correct and you have internet access.")}`
5590
6037
  );
5591
6038
  }
5592
6039
  if (!response.ok) {
@@ -5598,20 +6045,63 @@ async function list() {
5598
6045
  throw new RegistryError("Invalid registry format");
5599
6046
  }
5600
6047
  spinner.stop();
5601
- const targetDir = path8.resolve(process.cwd(), config.componentDir);
5602
6048
  const components = result.data.items.filter((item) => item.type === "registry:ui");
5603
- console.log(chalk6.bold(`
5604
- Available Components (${components.length})
5605
- `));
6049
+ const templates = result.data.items.filter((item) => item.type === "registry:template");
6050
+ const blocks = result.data.items.filter((item) => item.type === "registry:block");
6051
+ const componentBaseDir = path9.resolve(process.cwd(), config.componentDir);
6052
+ const templateBaseDir = path9.resolve(process.cwd(), config.templateDir ?? "./src/templates");
6053
+ console.log(chalk7.bold(`
6054
+ Components (${components.length})`));
6055
+ console.log(chalk7.dim(" Install with: pdfx add <component>\n"));
5606
6056
  for (const item of components) {
5607
- const componentSubDir = path8.join(targetDir, item.name);
6057
+ const componentSubDir = path9.join(componentBaseDir, item.name);
5608
6058
  const localPath = safePath(componentSubDir, `pdfx-${item.name}.tsx`);
5609
- const installed = checkFileExists(localPath);
5610
- const status = installed ? chalk6.green("[installed]") : chalk6.dim("[not installed]");
5611
- console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
5612
- console.log(` ${"".padEnd(20)} ${status}`);
6059
+ const installed = hasLocalProject && checkFileExists(localPath);
6060
+ const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
6061
+ console.log(` ${chalk7.cyan(item.name.padEnd(20))} ${item.description}`);
6062
+ if (hasLocalProject) {
6063
+ console.log(` ${"".padEnd(20)} ${status}`);
6064
+ }
6065
+ console.log();
6066
+ }
6067
+ console.log(chalk7.bold(` Templates (${templates.length})`));
6068
+ console.log(chalk7.dim(" Data-driven. Install with: pdfx template add <template>\n"));
6069
+ for (const item of templates) {
6070
+ const templateDir = path9.join(templateBaseDir, item.name);
6071
+ const installed = hasLocalProject && checkFileExists(templateDir);
6072
+ const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
6073
+ console.log(` ${chalk7.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
6074
+ if (hasLocalProject) {
6075
+ console.log(` ${"".padEnd(22)} ${status}`);
6076
+ }
5613
6077
  console.log();
5614
6078
  }
6079
+ console.log(chalk7.bold(` Blocks (${blocks.length})`));
6080
+ console.log(chalk7.dim(" Copy-paste designs. Install with: pdfx block add <block>\n"));
6081
+ for (const item of blocks) {
6082
+ const blockDir = path9.join(templateBaseDir, item.name);
6083
+ const installed = hasLocalProject && checkFileExists(blockDir);
6084
+ const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
6085
+ console.log(` ${chalk7.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
6086
+ if (hasLocalProject) {
6087
+ console.log(` ${"".padEnd(22)} ${status}`);
6088
+ }
6089
+ if (item.peerComponents && item.peerComponents.length > 0) {
6090
+ console.log(chalk7.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
6091
+ }
6092
+ console.log();
6093
+ }
6094
+ console.log(chalk7.dim(" Quick Start:"));
6095
+ console.log(
6096
+ chalk7.dim(` pdfx add heading table ${chalk7.dim("# Add components")}`)
6097
+ );
6098
+ console.log(
6099
+ chalk7.dim(` pdfx template add invoice-template ${chalk7.dim("# Add data-driven template")}`)
6100
+ );
6101
+ console.log(
6102
+ chalk7.dim(` pdfx block add invoice-classic ${chalk7.dim("# Add copy-paste block")}`)
6103
+ );
6104
+ console.log();
5615
6105
  } catch (error) {
5616
6106
  const message = error instanceof Error ? error.message : String(error);
5617
6107
  spinner.fail(message);
@@ -5620,18 +6110,18 @@ async function list() {
5620
6110
  }
5621
6111
 
5622
6112
  // src/commands/template.ts
5623
- import path9 from "path";
5624
- import chalk7 from "chalk";
5625
- import ora6 from "ora";
5626
- import prompts4 from "prompts";
5627
- function readConfig2(configPath) {
6113
+ import path10 from "path";
6114
+ import chalk8 from "chalk";
6115
+ import ora7 from "ora";
6116
+ import prompts5 from "prompts";
6117
+ function readConfig3(configPath) {
5628
6118
  const raw = readJsonFile(configPath);
5629
6119
  const result = configSchema.safeParse(raw);
5630
6120
  if (!result.success) {
5631
6121
  const issues = result.error.issues.map((i) => i.message).join(", ");
5632
6122
  throw new ConfigError(
5633
6123
  `Invalid pdfx.json: ${issues}`,
5634
- `Fix the config or re-run ${chalk7.cyan("pdfx init")}`
6124
+ `Fix the config or re-run ${chalk8.cyan("pdfx init")}`
5635
6125
  );
5636
6126
  }
5637
6127
  return result.data;
@@ -5666,7 +6156,7 @@ async function fetchTemplate(name, registryUrl) {
5666
6156
  }
5667
6157
  return result.data;
5668
6158
  }
5669
- async function fetchComponent2(name, registryUrl) {
6159
+ async function fetchComponent3(name, registryUrl) {
5670
6160
  const url = `${registryUrl}/${name}.json`;
5671
6161
  let response;
5672
6162
  try {
@@ -5698,7 +6188,7 @@ async function fetchComponent2(name, registryUrl) {
5698
6188
  }
5699
6189
  function resolveTemplateImports(content, templateName, config) {
5700
6190
  const cwd = process.cwd();
5701
- const templateSubdir = path9.resolve(
6191
+ const templateSubdir = path10.resolve(
5702
6192
  cwd,
5703
6193
  config.templateDir ?? DEFAULTS.TEMPLATE_DIR,
5704
6194
  templateName
@@ -5706,38 +6196,38 @@ function resolveTemplateImports(content, templateName, config) {
5706
6196
  let result = content.replace(
5707
6197
  /from '\.\.\/\.\.\/components\/pdfx\/([a-z][a-z0-9-]*)\/pdfx-([a-z][a-z0-9-]*)'/g,
5708
6198
  (_match, componentName) => {
5709
- const absCompFile = path9.resolve(
6199
+ const absCompFile = path10.resolve(
5710
6200
  cwd,
5711
6201
  config.componentDir,
5712
6202
  componentName,
5713
6203
  `pdfx-${componentName}`
5714
6204
  );
5715
- let rel = path9.relative(templateSubdir, absCompFile);
6205
+ let rel = path10.relative(templateSubdir, absCompFile);
5716
6206
  if (!rel.startsWith(".")) rel = `./${rel}`;
5717
6207
  return `from '${rel}'`;
5718
6208
  }
5719
6209
  );
5720
6210
  if (config.theme) {
5721
- const absThemePath = path9.resolve(cwd, config.theme).replace(/\.tsx?$/, "");
5722
- let relTheme = path9.relative(templateSubdir, absThemePath);
6211
+ const absThemePath = path10.resolve(cwd, config.theme).replace(/\.tsx?$/, "");
6212
+ let relTheme = path10.relative(templateSubdir, absThemePath);
5723
6213
  if (!relTheme.startsWith(".")) relTheme = `./${relTheme}`;
5724
- const absContextPath = path9.join(
5725
- path9.dirname(path9.resolve(cwd, config.theme)),
6214
+ const absContextPath = path10.join(
6215
+ path10.dirname(path10.resolve(cwd, config.theme)),
5726
6216
  "pdfx-theme-context"
5727
6217
  );
5728
- let relContext = path9.relative(templateSubdir, absContextPath);
6218
+ let relContext = path10.relative(templateSubdir, absContextPath);
5729
6219
  if (!relContext.startsWith(".")) relContext = `./${relContext}`;
5730
6220
  result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme'/g, `from '${relTheme}'`);
5731
6221
  result = result.replace(/from '\.\.\/\.\.\/lib\/pdfx-theme-context'/g, `from '${relContext}'`);
5732
6222
  }
5733
6223
  return result;
5734
6224
  }
5735
- async function resolveConflict(fileName, currentDecision) {
6225
+ async function resolveConflict2(fileName, currentDecision) {
5736
6226
  if (currentDecision === "overwrite-all") return "overwrite-all";
5737
- const { action } = await prompts4({
6227
+ const { action } = await prompts5({
5738
6228
  type: "select",
5739
6229
  name: "action",
5740
- message: `${chalk7.yellow(fileName)} already exists. What would you like to do?`,
6230
+ message: `${chalk8.yellow(fileName)} already exists. What would you like to do?`,
5741
6231
  choices: [
5742
6232
  { title: "Skip", value: "skip", description: "Keep the existing file unchanged" },
5743
6233
  { title: "Overwrite", value: "overwrite", description: "Replace this file only" },
@@ -5752,20 +6242,20 @@ async function resolveConflict(fileName, currentDecision) {
5752
6242
  if (!action) throw new ValidationError("Cancelled by user");
5753
6243
  return action;
5754
6244
  }
5755
- async function ensurePeerComponents(template, config, force) {
6245
+ async function ensurePeerComponents2(template, config, force) {
5756
6246
  const installedPeers = [];
5757
6247
  const peerWarnings = [];
5758
6248
  if (!template.peerComponents || template.peerComponents.length === 0) {
5759
6249
  return { installedPeers, peerWarnings };
5760
6250
  }
5761
- const componentBaseDir = path9.resolve(process.cwd(), config.componentDir);
6251
+ const componentBaseDir = path10.resolve(process.cwd(), config.componentDir);
5762
6252
  for (const componentName of template.peerComponents) {
5763
- const componentDir = path9.join(componentBaseDir, componentName);
5764
- const expectedMain = path9.join(componentDir, `pdfx-${componentName}.tsx`);
6253
+ const componentDir = path10.join(componentBaseDir, componentName);
6254
+ const expectedMain = path10.join(componentDir, `pdfx-${componentName}.tsx`);
5765
6255
  if (checkFileExists(componentDir)) {
5766
6256
  if (!checkFileExists(expectedMain)) {
5767
6257
  peerWarnings.push(
5768
- `${componentName}: directory exists but expected file missing (${path9.basename(expectedMain)})`
6258
+ `${componentName}: directory exists but expected file missing (${path10.basename(expectedMain)})`
5769
6259
  );
5770
6260
  } else {
5771
6261
  peerWarnings.push(
@@ -5774,11 +6264,11 @@ async function ensurePeerComponents(template, config, force) {
5774
6264
  }
5775
6265
  continue;
5776
6266
  }
5777
- const component = await fetchComponent2(componentName, config.registry);
6267
+ const component = await fetchComponent3(componentName, config.registry);
5778
6268
  ensureDir(componentDir);
5779
- const componentRelDir = path9.join(config.componentDir, component.name);
6269
+ const componentRelDir = path10.join(config.componentDir, component.name);
5780
6270
  for (const file of component.files) {
5781
- const fileName = path9.basename(file.path);
6271
+ const fileName = path10.basename(file.path);
5782
6272
  const filePath = safePath(componentDir, fileName);
5783
6273
  let content = file.content;
5784
6274
  if (config.theme && (content.includes("pdfx-theme") || content.includes("pdfx-theme-context"))) {
@@ -5798,13 +6288,13 @@ async function ensurePeerComponents(template, config, force) {
5798
6288
  }
5799
6289
  async function installTemplate(name, config, force) {
5800
6290
  const template = await fetchTemplate(name, config.registry);
5801
- const peerResult = await ensurePeerComponents(template, config, force);
5802
- const templateBaseDir = path9.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
5803
- const templateDir = path9.join(templateBaseDir, template.name);
6291
+ const peerResult = await ensurePeerComponents2(template, config, force);
6292
+ const templateBaseDir = path10.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
6293
+ const templateDir = path10.join(templateBaseDir, template.name);
5804
6294
  ensureDir(templateDir);
5805
6295
  const filesToWrite = [];
5806
6296
  for (const file of template.files) {
5807
- const fileName = path9.basename(file.path);
6297
+ const fileName = path10.basename(file.path);
5808
6298
  const filePath = safePath(templateDir, fileName);
5809
6299
  let content = file.content;
5810
6300
  if (/\.(tsx?|jsx?)$/.test(fileName) && content.includes("../../")) {
@@ -5817,7 +6307,7 @@ async function installTemplate(name, config, force) {
5817
6307
  const resolved = [];
5818
6308
  for (const file of filesToWrite) {
5819
6309
  if (checkFileExists(file.filePath)) {
5820
- const decision = await resolveConflict(path9.basename(file.filePath), globalDecision);
6310
+ const decision = await resolveConflict2(path10.basename(file.filePath), globalDecision);
5821
6311
  if (decision === "overwrite-all") {
5822
6312
  globalDecision = "overwrite-all";
5823
6313
  }
@@ -5830,7 +6320,7 @@ async function installTemplate(name, config, force) {
5830
6320
  if (!file.skip) {
5831
6321
  writeFile(file.filePath, file.content);
5832
6322
  } else {
5833
- console.log(chalk7.dim(` skipped ${path9.basename(file.filePath)}`));
6323
+ console.log(chalk8.dim(` skipped ${path10.basename(file.filePath)}`));
5834
6324
  }
5835
6325
  }
5836
6326
  } else {
@@ -5839,49 +6329,49 @@ async function installTemplate(name, config, force) {
5839
6329
  }
5840
6330
  }
5841
6331
  if (template.peerComponents && template.peerComponents.length > 0) {
5842
- const componentBaseDir = path9.resolve(process.cwd(), config.componentDir);
6332
+ const componentBaseDir = path10.resolve(process.cwd(), config.componentDir);
5843
6333
  const missing = [];
5844
6334
  for (const comp of template.peerComponents) {
5845
- const compDir = path9.join(componentBaseDir, comp);
6335
+ const compDir = path10.join(componentBaseDir, comp);
5846
6336
  if (!checkFileExists(compDir)) {
5847
6337
  missing.push(comp);
5848
6338
  }
5849
6339
  }
5850
6340
  if (missing.length > 0) {
5851
6341
  console.log();
5852
- console.log(chalk7.yellow(" Missing peer components:"));
6342
+ console.log(chalk8.yellow(" Missing peer components:"));
5853
6343
  for (const comp of missing) {
5854
- console.log(chalk7.dim(` ${comp} \u2192 run: ${chalk7.cyan(`pdfx add ${comp}`)}`));
6344
+ console.log(chalk8.dim(` ${comp} \u2192 run: ${chalk8.cyan(`pdfx add ${comp}`)}`));
5855
6345
  }
5856
6346
  }
5857
6347
  }
5858
6348
  if (config.theme) {
5859
- const absThemePath = path9.resolve(process.cwd(), config.theme);
5860
- const contextPath = path9.join(path9.dirname(absThemePath), "pdfx-theme-context.tsx");
6349
+ const absThemePath = path10.resolve(process.cwd(), config.theme);
6350
+ const contextPath = path10.join(path10.dirname(absThemePath), "pdfx-theme-context.tsx");
5861
6351
  if (!checkFileExists(contextPath)) {
5862
- ensureDir(path9.dirname(contextPath));
6352
+ ensureDir(path10.dirname(contextPath));
5863
6353
  writeFile(contextPath, generateThemeContextFile());
5864
6354
  }
5865
6355
  }
5866
6356
  return peerResult;
5867
6357
  }
5868
6358
  async function templateAdd(names, options = {}) {
5869
- const configPath = path9.join(process.cwd(), "pdfx.json");
6359
+ const configPath = path10.join(process.cwd(), "pdfx.json");
5870
6360
  if (!checkFileExists(configPath)) {
5871
- console.error(chalk7.red("Error: pdfx.json not found"));
5872
- console.log(chalk7.yellow("Run: pdfx init"));
6361
+ console.error(chalk8.red("Error: pdfx.json not found"));
6362
+ console.log(chalk8.yellow("Run: pdfx init"));
5873
6363
  process.exit(1);
5874
6364
  }
5875
6365
  let config;
5876
6366
  try {
5877
- config = readConfig2(configPath);
6367
+ config = readConfig3(configPath);
5878
6368
  } catch (error) {
5879
6369
  if (error instanceof ConfigError) {
5880
- console.error(chalk7.red(error.message));
5881
- if (error.suggestion) console.log(chalk7.yellow(` Hint: ${error.suggestion}`));
6370
+ console.error(chalk8.red(error.message));
6371
+ if (error.suggestion) console.log(chalk8.yellow(` Hint: ${error.suggestion}`));
5882
6372
  } else {
5883
6373
  const message = error instanceof Error ? error.message : String(error);
5884
- console.error(chalk7.red(message));
6374
+ console.error(chalk8.red(message));
5885
6375
  }
5886
6376
  process.exit(1);
5887
6377
  }
@@ -5890,24 +6380,24 @@ async function templateAdd(names, options = {}) {
5890
6380
  for (const templateName of names) {
5891
6381
  const nameResult = componentNameSchema.safeParse(templateName);
5892
6382
  if (!nameResult.success) {
5893
- console.error(chalk7.red(`Invalid template name: "${templateName}"`));
6383
+ console.error(chalk8.red(`Invalid template name: "${templateName}"`));
5894
6384
  console.log(
5895
- chalk7.dim(' Names must be lowercase alphanumeric with hyphens (e.g., "invoice-classic")')
6385
+ chalk8.dim(' Names must be lowercase alphanumeric with hyphens (e.g., "invoice-classic")')
5896
6386
  );
5897
6387
  failed.push(templateName);
5898
6388
  continue;
5899
6389
  }
5900
- const spinner = ora6(`Adding template ${templateName}...`).start();
6390
+ const spinner = ora7(`Adding template ${templateName}...`).start();
5901
6391
  try {
5902
6392
  const result = await installTemplate(templateName, config, force);
5903
- spinner.succeed(`Added template ${chalk7.cyan(templateName)}`);
6393
+ spinner.succeed(`Added template ${chalk8.cyan(templateName)}`);
5904
6394
  if (result.installedPeers.length > 0) {
5905
6395
  console.log(
5906
- chalk7.green(` Installed required components: ${result.installedPeers.join(", ")}`)
6396
+ chalk8.green(` Installed required components: ${result.installedPeers.join(", ")}`)
5907
6397
  );
5908
6398
  }
5909
6399
  for (const warning of result.peerWarnings) {
5910
- console.log(chalk7.yellow(` Warning: ${warning}`));
6400
+ console.log(chalk8.yellow(` Warning: ${warning}`));
5911
6401
  }
5912
6402
  } catch (error) {
5913
6403
  if (error instanceof ValidationError && error.message.includes("Cancelled")) {
@@ -5916,24 +6406,24 @@ async function templateAdd(names, options = {}) {
5916
6406
  } else if (error instanceof NetworkError || error instanceof RegistryError || error instanceof ValidationError) {
5917
6407
  spinner.fail(error.message);
5918
6408
  if (error.suggestion) {
5919
- console.log(chalk7.dim(` Hint: ${error.suggestion}`));
6409
+ console.log(chalk8.dim(` Hint: ${error.suggestion}`));
5920
6410
  }
5921
6411
  } else {
5922
6412
  spinner.fail(`Failed to add template ${templateName}`);
5923
6413
  const message = error instanceof Error ? error.message : String(error);
5924
- console.error(chalk7.dim(` ${message}`));
6414
+ console.error(chalk8.dim(` ${message}`));
5925
6415
  }
5926
6416
  failed.push(templateName);
5927
6417
  }
5928
6418
  }
5929
6419
  console.log();
5930
6420
  if (failed.length > 0) {
5931
- console.log(chalk7.yellow(`Failed: ${failed.join(", ")}`));
6421
+ console.log(chalk8.yellow(`Failed: ${failed.join(", ")}`));
5932
6422
  }
5933
6423
  if (failed.length < names.length) {
5934
- const resolvedDir = path9.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
5935
- console.log(chalk7.green("Done!"));
5936
- console.log(chalk7.dim(`Templates installed to: ${resolvedDir}
6424
+ const resolvedDir = path10.resolve(process.cwd(), config.templateDir ?? DEFAULTS.TEMPLATE_DIR);
6425
+ console.log(chalk8.green("Done!"));
6426
+ console.log(chalk8.dim(`Templates installed to: ${resolvedDir}
5937
6427
  `));
5938
6428
  }
5939
6429
  if (failed.length > 0) {
@@ -5941,20 +6431,20 @@ async function templateAdd(names, options = {}) {
5941
6431
  }
5942
6432
  }
5943
6433
  async function templateList() {
5944
- const configPath = path9.join(process.cwd(), "pdfx.json");
6434
+ const configPath = path10.join(process.cwd(), "pdfx.json");
5945
6435
  if (!checkFileExists(configPath)) {
5946
- console.error(chalk7.red("Error: pdfx.json not found"));
5947
- console.log(chalk7.yellow("Run: pdfx init"));
6436
+ console.error(chalk8.red("Error: pdfx.json not found"));
6437
+ console.log(chalk8.yellow("Run: pdfx init"));
5948
6438
  process.exit(1);
5949
6439
  }
5950
6440
  const raw = readJsonFile(configPath);
5951
6441
  const configResult = configSchema.safeParse(raw);
5952
6442
  if (!configResult.success) {
5953
- console.error(chalk7.red("Invalid pdfx.json"));
6443
+ console.error(chalk8.red("Invalid pdfx.json"));
5954
6444
  process.exit(1);
5955
6445
  }
5956
6446
  const config = configResult.data;
5957
- const spinner = ora6("Fetching template list...").start();
6447
+ const spinner = ora7("Fetching template list...").start();
5958
6448
  try {
5959
6449
  let response;
5960
6450
  try {
@@ -5978,28 +6468,28 @@ async function templateList() {
5978
6468
  spinner.stop();
5979
6469
  const templates = result.data.items.filter((item) => item.type === "registry:template");
5980
6470
  if (templates.length === 0) {
5981
- console.log(chalk7.dim("\n No templates available in the registry.\n"));
6471
+ console.log(chalk8.dim("\n No templates available in the registry.\n"));
5982
6472
  return;
5983
6473
  }
5984
- const templateBaseDir = path9.resolve(
6474
+ const templateBaseDir = path10.resolve(
5985
6475
  process.cwd(),
5986
6476
  config.templateDir ?? DEFAULTS.TEMPLATE_DIR
5987
6477
  );
5988
- console.log(chalk7.bold(`
6478
+ console.log(chalk8.bold(`
5989
6479
  Available Templates (${templates.length})
5990
6480
  `));
5991
6481
  for (const item of templates) {
5992
- const templateDir = path9.join(templateBaseDir, item.name);
6482
+ const templateDir = path10.join(templateBaseDir, item.name);
5993
6483
  const installed = checkFileExists(templateDir);
5994
- const status = installed ? chalk7.green("[installed]") : chalk7.dim("[not installed]");
5995
- console.log(` ${chalk7.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
6484
+ const status = installed ? chalk8.green("[installed]") : chalk8.dim("[not installed]");
6485
+ console.log(` ${chalk8.cyan(item.name.padEnd(22))} ${item.description ?? ""}`);
5996
6486
  console.log(` ${"".padEnd(22)} ${status}`);
5997
6487
  if (item.peerComponents && item.peerComponents.length > 0) {
5998
- console.log(chalk7.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
6488
+ console.log(chalk8.dim(` ${"".padEnd(22)} requires: ${item.peerComponents.join(", ")}`));
5999
6489
  }
6000
6490
  console.log();
6001
6491
  }
6002
- console.log(chalk7.dim(` Install with: ${chalk7.cyan("pdfx template add <template-name>")}
6492
+ console.log(chalk8.dim(` Install with: ${chalk8.cyan("pdfx template add <template-name>")}
6003
6493
  `));
6004
6494
  } catch (error) {
6005
6495
  const message = error instanceof Error ? error.message : String(error);
@@ -6010,14 +6500,22 @@ async function templateList() {
6010
6500
 
6011
6501
  // src/commands/theme.ts
6012
6502
  import fs8 from "fs";
6013
- import path10 from "path";
6014
- import chalk8 from "chalk";
6015
- import ora7 from "ora";
6016
- import prompts5 from "prompts";
6503
+ import path11 from "path";
6504
+ import chalk9 from "chalk";
6505
+ import ora8 from "ora";
6506
+ import prompts6 from "prompts";
6017
6507
  import ts from "typescript";
6018
6508
  async function themeInit() {
6019
- console.log(chalk8.bold.cyan("\n PDFx Theme Setup\n"));
6020
- const answers = await prompts5(
6509
+ const configPath = path11.join(process.cwd(), "pdfx.json");
6510
+ if (!fs8.existsSync(configPath)) {
6511
+ console.error(chalk9.red("\nError: pdfx.json not found"));
6512
+ console.log(chalk9.yellow("\n PDFx is not initialized in this project.\n"));
6513
+ console.log(chalk9.cyan(" Run: pdfx init"));
6514
+ console.log(chalk9.dim(" This will set up your project configuration and theme.\n"));
6515
+ process.exit(1);
6516
+ }
6517
+ console.log(chalk9.bold.cyan("\n PDFx Theme Setup\n"));
6518
+ const answers = await prompts6(
6021
6519
  [
6022
6520
  {
6023
6521
  type: "select",
@@ -6051,102 +6549,104 @@ async function themeInit() {
6051
6549
  ],
6052
6550
  {
6053
6551
  onCancel: () => {
6054
- console.log(chalk8.yellow("\nTheme setup cancelled."));
6552
+ console.log(chalk9.yellow("\nTheme setup cancelled."));
6055
6553
  process.exit(0);
6056
6554
  }
6057
6555
  }
6058
6556
  );
6059
6557
  if (!answers.preset || !answers.themePath) {
6060
- console.error(chalk8.red("Missing required fields."));
6558
+ console.error(chalk9.red("Missing required fields."));
6061
6559
  process.exit(1);
6062
6560
  }
6063
6561
  const presetName = answers.preset;
6064
6562
  const themePath = answers.themePath;
6065
6563
  const preset = themePresets[presetName];
6066
- const spinner = ora7(`Scaffolding ${presetName} theme...`).start();
6564
+ const spinner = ora8(`Scaffolding ${presetName} theme...`).start();
6067
6565
  try {
6068
- const absThemePath = path10.resolve(process.cwd(), themePath);
6069
- ensureDir(path10.dirname(absThemePath));
6566
+ const absThemePath = path11.resolve(process.cwd(), themePath);
6567
+ ensureDir(path11.dirname(absThemePath));
6070
6568
  fs8.writeFileSync(absThemePath, generateThemeFile(preset), "utf-8");
6071
- const contextPath = path10.join(path10.dirname(absThemePath), "pdfx-theme-context.tsx");
6569
+ const contextPath = path11.join(path11.dirname(absThemePath), "pdfx-theme-context.tsx");
6072
6570
  fs8.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
6073
6571
  spinner.succeed(`Created ${themePath} with ${presetName} theme`);
6074
- const configPath = path10.join(process.cwd(), "pdfx.json");
6075
- if (fs8.existsSync(configPath)) {
6572
+ const configPath2 = path11.join(process.cwd(), "pdfx.json");
6573
+ if (fs8.existsSync(configPath2)) {
6076
6574
  try {
6077
- const rawConfig = readJsonFile(configPath);
6575
+ const rawConfig = readJsonFile(configPath2);
6078
6576
  const result = configSchema.safeParse(rawConfig);
6079
6577
  if (result.success) {
6080
6578
  const updatedConfig = { ...result.data, theme: themePath };
6081
- fs8.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2), "utf-8");
6082
- console.log(chalk8.green(" Updated pdfx.json with theme path"));
6579
+ fs8.writeFileSync(configPath2, JSON.stringify(updatedConfig, null, 2), "utf-8");
6580
+ console.log(chalk9.green(" Updated pdfx.json with theme path"));
6083
6581
  }
6084
6582
  } catch {
6085
- console.log(chalk8.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
6583
+ console.log(chalk9.yellow(' Could not update pdfx.json \u2014 add "theme" field manually'));
6086
6584
  }
6087
6585
  }
6088
- console.log(chalk8.dim(`
6586
+ console.log(chalk9.dim(`
6089
6587
  Edit ${themePath} to customize your theme.
6090
6588
  `));
6091
6589
  } catch (error) {
6092
6590
  spinner.fail("Failed to create theme file");
6093
6591
  const message = error instanceof Error ? error.message : String(error);
6094
- console.error(chalk8.dim(` ${message}`));
6592
+ console.error(chalk9.dim(` ${message}`));
6095
6593
  process.exit(1);
6096
6594
  }
6097
6595
  }
6098
6596
  async function themeSwitch(presetName) {
6597
+ const resolvedPreset = presetName === "default" ? "professional" : presetName;
6099
6598
  const validPresets = Object.keys(themePresets);
6100
- if (!validPresets.includes(presetName)) {
6101
- console.error(
6102
- chalk8.red(`Invalid preset: "${presetName}". Valid presets: ${validPresets.join(", ")}`)
6103
- );
6599
+ if (!validPresets.includes(resolvedPreset)) {
6600
+ console.error(chalk9.red(`\u2716 Invalid theme preset: "${presetName}"`));
6601
+ console.log(chalk9.dim(` Available presets: ${validPresets.join(", ")}, default
6602
+ `));
6603
+ console.log(chalk9.dim(" Usage: pdfx theme switch <preset>"));
6104
6604
  process.exit(1);
6105
6605
  }
6106
- const validatedPreset = presetName;
6107
- const configPath = path10.join(process.cwd(), "pdfx.json");
6606
+ const validatedPreset = resolvedPreset;
6607
+ const configPath = path11.join(process.cwd(), "pdfx.json");
6108
6608
  if (!fs8.existsSync(configPath)) {
6109
- console.error(chalk8.red('No pdfx.json found. Run "pdfx init" first.'));
6609
+ console.error(chalk9.red('No pdfx.json found. Run "pdfx init" first.'));
6110
6610
  process.exit(1);
6111
6611
  }
6112
6612
  const rawConfig = readJsonFile(configPath);
6113
6613
  const result = configSchema.safeParse(rawConfig);
6114
6614
  if (!result.success) {
6115
- console.error(chalk8.red("Invalid pdfx.json configuration."));
6615
+ console.error(chalk9.red("Invalid pdfx.json configuration."));
6116
6616
  process.exit(1);
6117
6617
  }
6118
6618
  const config = result.data;
6119
6619
  if (!config.theme) {
6120
6620
  console.error(
6121
- chalk8.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
6621
+ chalk9.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
6122
6622
  );
6123
6623
  process.exit(1);
6124
6624
  }
6125
- const answer = await prompts5({
6625
+ const answer = await prompts6({
6126
6626
  type: "confirm",
6127
6627
  name: "confirm",
6128
6628
  message: `This will overwrite ${config.theme} with the ${validatedPreset} preset. Continue?`,
6129
6629
  initial: false
6130
6630
  });
6131
6631
  if (!answer.confirm) {
6132
- console.log(chalk8.yellow("Cancelled."));
6632
+ console.log(chalk9.yellow("Cancelled."));
6133
6633
  return;
6134
6634
  }
6135
- const spinner = ora7(`Switching to ${validatedPreset} theme...`).start();
6635
+ const spinner = ora8(`Switching to ${validatedPreset} theme...`).start();
6136
6636
  try {
6137
6637
  const preset = themePresets[validatedPreset];
6138
- const absThemePath = path10.resolve(process.cwd(), config.theme);
6638
+ const absThemePath = path11.resolve(process.cwd(), config.theme);
6139
6639
  fs8.writeFileSync(absThemePath, generateThemeFile(preset), "utf-8");
6140
- const contextPath = path10.join(path10.dirname(absThemePath), "pdfx-theme-context.tsx");
6640
+ const contextPath = path11.join(path11.dirname(absThemePath), "pdfx-theme-context.tsx");
6141
6641
  if (!fs8.existsSync(contextPath)) {
6142
- ensureDir(path10.dirname(contextPath));
6642
+ ensureDir(path11.dirname(contextPath));
6143
6643
  fs8.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
6144
6644
  }
6145
6645
  spinner.succeed(`Switched to ${validatedPreset} theme`);
6146
6646
  } catch (error) {
6147
6647
  spinner.fail("Failed to switch theme");
6148
6648
  const message = error instanceof Error ? error.message : String(error);
6149
- console.error(chalk8.dim(` ${message}`));
6649
+ console.error(chalk9.dim(` ${message}`));
6150
6650
  process.exit(1);
6151
6651
  }
6152
6652
  }
@@ -6214,54 +6714,68 @@ function parseThemeObject(themePath) {
6214
6714
  throw new Error("No exported `theme` object found.");
6215
6715
  }
6216
6716
  async function themeValidate() {
6217
- const configPath = path10.join(process.cwd(), "pdfx.json");
6717
+ const configPath = path11.join(process.cwd(), "pdfx.json");
6218
6718
  if (!fs8.existsSync(configPath)) {
6219
- console.error(chalk8.red('No pdfx.json found. Run "pdfx init" first.'));
6719
+ console.error(chalk9.red('No pdfx.json found. Run "pdfx init" first.'));
6220
6720
  process.exit(1);
6221
6721
  }
6222
6722
  const rawConfig = readJsonFile(configPath);
6223
6723
  const configResult = configSchema.safeParse(rawConfig);
6224
6724
  if (!configResult.success) {
6225
- console.error(chalk8.red("Invalid pdfx.json configuration."));
6725
+ console.error(chalk9.red("Invalid pdfx.json configuration."));
6226
6726
  process.exit(1);
6227
6727
  }
6228
6728
  if (!configResult.data.theme) {
6229
6729
  console.error(
6230
- chalk8.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
6730
+ chalk9.red('No theme path in pdfx.json. Run "pdfx theme init" to set up theming.')
6231
6731
  );
6232
6732
  process.exit(1);
6233
6733
  }
6234
- const absThemePath = path10.resolve(process.cwd(), configResult.data.theme);
6734
+ const absThemePath = path11.resolve(process.cwd(), configResult.data.theme);
6235
6735
  if (!fs8.existsSync(absThemePath)) {
6236
- console.error(chalk8.red(`Theme file not found: ${configResult.data.theme}`));
6736
+ console.error(chalk9.red(`Theme file not found: ${configResult.data.theme}`));
6237
6737
  process.exit(1);
6238
6738
  }
6239
- const spinner = ora7("Validating theme file...").start();
6739
+ const spinner = ora8("Validating theme file...").start();
6240
6740
  try {
6241
6741
  const parsedTheme = parseThemeObject(absThemePath);
6242
6742
  const result = themeSchema.safeParse(parsedTheme);
6243
6743
  if (!result.success) {
6244
- const message = result.error.issues.map((issue) => issue.message).join(", ");
6245
- spinner.fail(`Theme validation failed: ${message}`);
6744
+ const issues = result.error.issues.map((issue) => ` \u2192 ${chalk9.yellow(issue.path.join("."))}: ${issue.message}`).join("\n");
6745
+ spinner.fail("Theme validation failed");
6746
+ console.log(chalk9.red("\n Missing or invalid fields:\n"));
6747
+ console.log(issues);
6748
+ console.log(chalk9.dim("\n Fix these fields in your theme file and run validate again.\n"));
6246
6749
  process.exit(1);
6247
6750
  }
6248
6751
  spinner.succeed("Theme file is valid");
6249
- console.log(chalk8.dim(`
6752
+ console.log(chalk9.dim(`
6250
6753
  Validated: ${configResult.data.theme}
6251
6754
  `));
6252
6755
  } catch (error) {
6253
6756
  spinner.fail("Failed to validate theme");
6254
6757
  const message = error instanceof Error ? error.message : String(error);
6255
- console.error(chalk8.dim(` ${message}`));
6758
+ console.error(chalk9.dim(` ${message}`));
6256
6759
  process.exit(1);
6257
6760
  }
6258
6761
  }
6259
6762
 
6260
6763
  // src/index.ts
6261
6764
  var program = new Command();
6262
- program.name("pdfx").description("CLI for PDFx components").version("0.1.0");
6765
+ var __filename = fileURLToPath(import.meta.url);
6766
+ var __dirname = join(__filename, "..");
6767
+ var packageJsonPath = join(__dirname, "../package.json");
6768
+ var version = "0.1.3";
6769
+ try {
6770
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
6771
+ version = pkg.version;
6772
+ } catch {
6773
+ }
6774
+ program.name("pdfx").description("CLI for PDFx components").version(version);
6263
6775
  program.command("init").description("Initialize pdfx in your project").action(init);
6264
- program.command("add <components...>").description("Add components to your project").option("-f, --force", "Overwrite existing files without prompting").action((components, options) => add(components, options));
6776
+ program.command("add <components...>").description("Add components to your project").option("-f, --force", "Overwrite existing files without prompting").option("-r, --registry <url>", "Override registry URL").action(
6777
+ (components, options) => add(components, options)
6778
+ );
6265
6779
  program.command("list").description("List available components from registry").action(list);
6266
6780
  program.command("diff <components...>").description("Compare local components with registry versions").action(diff);
6267
6781
  var themeCmd = program.command("theme").description("Manage PDF themes");
@@ -6271,4 +6785,7 @@ themeCmd.command("validate").description("Validate your theme file").action(them
6271
6785
  var templateCmd = program.command("template").description("Manage PDF templates");
6272
6786
  templateCmd.command("add <templates...>").description("Add a template to your project").option("-f, --force", "Overwrite existing files without prompting").action((templates, options) => templateAdd(templates, options));
6273
6787
  templateCmd.command("list").description("List available templates from registry").action(templateList);
6788
+ var blockCmd = program.command("block").description("Manage PDF blocks (copy-paste designs)");
6789
+ blockCmd.command("add <blocks...>").description("Add a block to your project").option("-f, --force", "Overwrite existing files without prompting").action((blocks, options) => blockAdd(blocks, options));
6790
+ blockCmd.command("list").description("List available blocks from registry").action(blockList);
6274
6791
  program.parse();