@qrdx/cli 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/dist/index.js +117 -30
  3. package/package.json +5 -3
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # qrdx-cli
1
+ # @qrdx/cli
2
2
 
3
3
  QR code generator CLI for developers. Generate styled, scannable QR codes as SVG or PNG — fully non-interactive, CI-friendly.
4
4
 
@@ -9,7 +9,7 @@ QR code generator CLI for developers. Generate styled, scannable QR codes as SVG
9
9
  npm install -g qrdx
10
10
 
11
11
  # Or install the standalone CLI package
12
- npm install -g qrdx-cli
12
+ npm install -g @qrdx/cli
13
13
  ```
14
14
 
15
15
  Or run without installing:
@@ -72,9 +72,9 @@ qrdx generate
72
72
 
73
73
  **Body** (`--body`): `square` `circle` `circle-large` `diamond` `circle-mixed` `pacman` `rounded` `small-square` `vertical-line`
74
74
 
75
- **Eye** (`--eye`): `square` `rounded` `gear` `circle` `diya` `cushion` `boxy` `pointed`
75
+ **Eye** (`--eye`): `square` `rounded` `gear` `circle` `diya` `extra-rounded` `message` `pointy` `curly`
76
76
 
77
- **Dot** (`--dot`): `square` `rounded` `circle` `heart` `diamond` `star`
77
+ **Dot** (`--dot`): `square` `rounded` `circle` `diamond` `message` `message-reverse` `diya` `diya-reverse` `rounded-triangle` `star` `banner`
78
78
 
79
79
  ## Interactive mode
80
80
 
package/dist/index.js CHANGED
@@ -55,6 +55,16 @@ var RESET = "\x1B[0m";
55
55
  var TEXT = "\x1B[38;5;145m";
56
56
  var GREEN = "\x1B[38;5;114m";
57
57
  var DIM = "\x1B[38;5;102m";
58
+ function parseColorFlag(value) {
59
+ const trimmed = value.trim();
60
+ if (trimmed.startsWith("{")) {
61
+ try {
62
+ return JSON.parse(trimmed);
63
+ } catch {
64
+ }
65
+ }
66
+ return trimmed;
67
+ }
58
68
  var IS_TTY = Boolean(process.stdin.isTTY);
59
69
  function parseFlags(args) {
60
70
  const flags = {};
@@ -93,19 +103,19 @@ function parseFlags(args) {
93
103
  i += 2;
94
104
  break;
95
105
  case "--fg":
96
- flags.fg = next;
106
+ flags.fg = next ? parseColorFlag(next) : void 0;
97
107
  i += 2;
98
108
  break;
99
109
  case "--bg":
100
- flags.bg = next;
110
+ flags.bg = next ? parseColorFlag(next) : void 0;
101
111
  i += 2;
102
112
  break;
103
113
  case "--eye-color":
104
- flags.eyeColor = next;
114
+ flags.eyeColor = next ? parseColorFlag(next) : void 0;
105
115
  i += 2;
106
116
  break;
107
117
  case "--dot-color":
108
- flags.dotColor = next;
118
+ flags.dotColor = next ? parseColorFlag(next) : void 0;
109
119
  i += 2;
110
120
  break;
111
121
  case "--logo":
@@ -113,7 +123,7 @@ function parseFlags(args) {
113
123
  i += 2;
114
124
  break;
115
125
  case "--margin":
116
- flags.margin = next ? Number.parseInt(next, 10) : 0;
126
+ flags.margin = next ? Number.parseInt(next, 10) : qrdx.DEFAULT_MARGIN;
117
127
  i += 2;
118
128
  break;
119
129
  case "--size":
@@ -124,6 +134,10 @@ function parseFlags(args) {
124
134
  flags.noLogo = true;
125
135
  i++;
126
136
  break;
137
+ case "--template":
138
+ flags.template = next;
139
+ i += 2;
140
+ break;
127
141
  default:
128
142
  i++;
129
143
  }
@@ -148,7 +162,7 @@ async function runGenerate(args) {
148
162
  const flags = parseFlags(args);
149
163
  const nonInteractive = !IS_TTY;
150
164
  const hasAdvancedFlags = Boolean(
151
- flags.body || flags.eye || flags.dot || flags.fg || flags.bg || flags.eyeColor || flags.dotColor || flags.logo || flags.level || flags.margin !== void 0
165
+ flags.body || flags.eye || flags.dot || flags.fg || flags.bg || flags.eyeColor || flags.dotColor || flags.logo || flags.level || flags.margin !== void 0 || flags.template
152
166
  );
153
167
  p__namespace.intro(`${TEXT}qrdx${RESET} ${DIM}\u2014 QR Code Generator${RESET}`);
154
168
  let value = flags.value;
@@ -209,7 +223,8 @@ async function runGenerate(args) {
209
223
  let eyeColor = flags.eyeColor;
210
224
  let dotColor = flags.dotColor;
211
225
  let logo = flags.logo;
212
- let margin = flags.margin ?? 0;
226
+ let margin = flags.margin ?? qrdx.DEFAULT_MARGIN;
227
+ let template = flags.template;
213
228
  if (wantAdvanced && !nonInteractive) {
214
229
  if (!flags.level) {
215
230
  const res = await p__namespace.select({
@@ -266,42 +281,75 @@ async function runGenerate(args) {
266
281
  }
267
282
  if (!flags.fg) {
268
283
  const res = await p__namespace.text({
269
- message: "Foreground color",
284
+ message: `Foreground color ${DIM}(hex or JSON gradient)${RESET}`,
270
285
  placeholder: "#000000",
271
286
  defaultValue: "#000000",
272
287
  validate(v) {
273
- if (v && !/^#[0-9a-fA-F]{3,8}$/.test(v)) {
274
- return "Enter a valid hex color (e.g. #000000)";
288
+ if (!v) {
289
+ return void 0;
290
+ }
291
+ if (v.trim().startsWith("{")) {
292
+ try {
293
+ JSON.parse(v.trim());
294
+ } catch {
295
+ return "Enter a valid hex color or JSON gradient object";
296
+ }
297
+ return void 0;
298
+ }
299
+ if (!/^#[0-9a-fA-F]{3,8}$/.test(v)) {
300
+ return "Enter a valid hex color (e.g. #000000) or JSON gradient";
275
301
  }
276
302
  }
277
303
  });
278
304
  if (isCancel2(res)) {
279
305
  handleCancel();
280
306
  }
281
- fg = res || "#000000";
307
+ fg = parseColorFlag(res || "#000000");
282
308
  }
283
309
  if (!flags.bg) {
284
310
  const res = await p__namespace.text({
285
- message: "Background color",
311
+ message: `Background color ${DIM}(hex or JSON gradient)${RESET}`,
286
312
  placeholder: "#ffffff",
287
313
  defaultValue: "#ffffff",
288
314
  validate(v) {
289
- if (v && !/^#[0-9a-fA-F]{3,8}$/.test(v)) {
290
- return "Enter a valid hex color (e.g. #ffffff)";
315
+ if (!v) {
316
+ return void 0;
317
+ }
318
+ if (v.trim().startsWith("{")) {
319
+ try {
320
+ JSON.parse(v.trim());
321
+ } catch {
322
+ return "Enter a valid hex color or JSON gradient object";
323
+ }
324
+ return void 0;
325
+ }
326
+ if (!/^#[0-9a-fA-F]{3,8}$/.test(v)) {
327
+ return "Enter a valid hex color (e.g. #ffffff) or JSON gradient";
291
328
  }
292
329
  }
293
330
  });
294
331
  if (isCancel2(res)) {
295
332
  handleCancel();
296
333
  }
297
- bg = res || "#ffffff";
334
+ bg = parseColorFlag(res || "#ffffff");
298
335
  }
299
336
  if (eyeColor === void 0) {
300
337
  const res = await p__namespace.text({
301
338
  message: `Corner eye color ${DIM}(leave blank to match foreground)${RESET}`,
302
- placeholder: fg,
339
+ placeholder: "leave blank",
303
340
  validate(v) {
304
- if (v && !/^#[0-9a-fA-F]{3,8}$/.test(v)) {
341
+ if (!v) {
342
+ return void 0;
343
+ }
344
+ if (v.trim().startsWith("{")) {
345
+ try {
346
+ JSON.parse(v.trim());
347
+ } catch {
348
+ return "Enter a valid hex color or JSON gradient object";
349
+ }
350
+ return void 0;
351
+ }
352
+ if (!/^#[0-9a-fA-F]{3,8}$/.test(v)) {
305
353
  return "Enter a valid hex color or leave blank";
306
354
  }
307
355
  }
@@ -309,14 +357,26 @@ async function runGenerate(args) {
309
357
  if (isCancel2(res)) {
310
358
  handleCancel();
311
359
  }
312
- eyeColor = res || void 0;
360
+ const eyeVal = res;
361
+ eyeColor = eyeVal ? parseColorFlag(eyeVal) : void 0;
313
362
  }
314
363
  if (dotColor === void 0) {
315
364
  const res = await p__namespace.text({
316
365
  message: `Corner dot color ${DIM}(leave blank to match foreground)${RESET}`,
317
- placeholder: fg,
366
+ placeholder: "leave blank",
318
367
  validate(v) {
319
- if (v && !/^#[0-9a-fA-F]{3,8}$/.test(v)) {
368
+ if (!v) {
369
+ return void 0;
370
+ }
371
+ if (v.trim().startsWith("{")) {
372
+ try {
373
+ JSON.parse(v.trim());
374
+ } catch {
375
+ return "Enter a valid hex color or JSON gradient object";
376
+ }
377
+ return void 0;
378
+ }
379
+ if (!/^#[0-9a-fA-F]{3,8}$/.test(v)) {
320
380
  return "Enter a valid hex color or leave blank";
321
381
  }
322
382
  }
@@ -324,7 +384,23 @@ async function runGenerate(args) {
324
384
  if (isCancel2(res)) {
325
385
  handleCancel();
326
386
  }
327
- dotColor = res || void 0;
387
+ const dotVal = res;
388
+ dotColor = dotVal ? parseColorFlag(dotVal) : void 0;
389
+ }
390
+ if (!flags.template) {
391
+ const templateIds = qrdx.getTemplateIds();
392
+ const res = await p__namespace.select({
393
+ message: `Template ${DIM}(decorative wrapper around the QR code)${RESET}`,
394
+ options: [
395
+ { value: "", label: "none" },
396
+ ...templateIds.filter((id) => id !== "default").map((id) => ({ value: id, label: id }))
397
+ ],
398
+ initialValue: ""
399
+ });
400
+ if (isCancel2(res)) {
401
+ handleCancel();
402
+ }
403
+ template = res || void 0;
328
404
  }
329
405
  if (logo === void 0 && !flags.noLogo) {
330
406
  const addLogo = await p__namespace.confirm({
@@ -358,8 +434,8 @@ async function runGenerate(args) {
358
434
  if (flags.margin === void 0) {
359
435
  const res = await p__namespace.text({
360
436
  message: "Quiet zone margin (modules)",
361
- placeholder: "0",
362
- defaultValue: "0",
437
+ placeholder: String(qrdx.DEFAULT_MARGIN),
438
+ defaultValue: String(qrdx.DEFAULT_MARGIN),
363
439
  validate(v) {
364
440
  if (v && Number.isNaN(Number.parseInt(v, 10))) {
365
441
  return "Enter a number";
@@ -372,7 +448,7 @@ async function runGenerate(args) {
372
448
  if (isCancel2(res)) {
373
449
  handleCancel();
374
450
  }
375
- margin = Number.parseInt(res || "0", 10);
451
+ margin = Number.parseInt(res || String(qrdx.DEFAULT_MARGIN), 10);
376
452
  }
377
453
  }
378
454
  const spinner2 = p__namespace.spinner();
@@ -390,6 +466,7 @@ async function runGenerate(args) {
390
466
  ...dotColor ? { dotColor } : {},
391
467
  margin,
392
468
  size,
469
+ ...template ? { templateId: template } : {},
393
470
  ...logo ? {
394
471
  imageSettings: {
395
472
  src: logo,
@@ -473,7 +550,7 @@ function showBanner() {
473
550
  ` ${DIM2}$${RESET2} ${TEXT2}qrdx --version${RESET2} ${DIM2}Show version${RESET2}`
474
551
  );
475
552
  console.log();
476
- console.log(` ${DIM2}try:${RESET2} npx qrdx-cli generate "https://qrdx.dev"`);
553
+ console.log(` ${DIM2}try:${RESET2} npx @qrdx/cli generate "https://qrdx.dev"`);
477
554
  console.log();
478
555
  }
479
556
  function showHelp() {
@@ -498,13 +575,20 @@ ${BOLD}Generate Options:${RESET2}
498
575
  ${DIM2}square, rounded, circle, diamond, message,${RESET2}
499
576
  ${DIM2}message-reverse, diya, diya-reverse,${RESET2}
500
577
  ${DIM2}rounded-triangle, star, banner${RESET2}
501
- --fg Foreground color ${DIM2}(default: #000000)${RESET2}
502
- --bg Background color ${DIM2}(default: #ffffff)${RESET2}
503
- --eye-color Corner eye color ${DIM2}(default: matches --fg)${RESET2}
504
- --dot-color Corner dot color ${DIM2}(default: matches --fg)${RESET2}
578
+ --fg Foreground color \u2014 hex or JSON gradient ${DIM2}(default: #000000)${RESET2}
579
+ --bg Background color \u2014 hex or JSON gradient ${DIM2}(default: #ffffff)${RESET2}
580
+ --eye-color Corner eye color \u2014 hex or JSON gradient ${DIM2}(default: matches --fg)${RESET2}
581
+ --dot-color Corner dot color \u2014 hex or JSON gradient ${DIM2}(default: matches --fg)${RESET2}
505
582
  --logo Logo URL for center image
506
583
  --margin Quiet zone margin in modules ${DIM2}(default: 0)${RESET2}
507
584
  --size Output pixel size for SVG/PNG ${DIM2}(default: 512)${RESET2}
585
+ --template Decorative template wrapper:
586
+ ${DIM2}Arrow, StandardBox, SquareBorder, StrikedBox, Halloween${RESET2}
587
+
588
+ ${BOLD}Gradient Colors:${RESET2}
589
+ Pass a JSON object to --fg / --bg / --eye-color / --dot-color for gradients:
590
+ ${DIM2}Linear: '{"type":"linear","stops":[{"color":"#f00","offset":0},{"color":"#00f","offset":100}],"angle":45}'${RESET2}
591
+ ${DIM2}Radial: '{"type":"radial","stops":[{"color":"#f00","offset":0},{"color":"#00f","offset":100}]}'${RESET2}
508
592
 
509
593
  ${BOLD}Global Options:${RESET2}
510
594
  --help, -h Show this help message
@@ -516,7 +600,10 @@ ${BOLD}Examples:${RESET2}
516
600
  ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" -o qr.svg
517
601
  ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" -o qr.png --body circle --eye gear --dot star
518
602
  ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --fg "#ff0000" --bg "#ffffff" --size 1024
519
- ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --logo "https://qrdx.dev/logo.png" -o branded.svg
603
+ ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --logo "https://qrdx.dev/logo.png" --level H -o branded.svg
604
+ ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --template Arrow -o arrow.svg
605
+ ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --template Halloween --body circle --eye gear -o halloween.svg
606
+ ${DIM2}$${RESET2} qrdx generate "https://qrdx.dev" --fg '{"type":"linear","stops":[{"color":"#6366f1","offset":0},{"color":"#ec4899","offset":100}],"angle":135}' -o gradient.svg
520
607
 
521
608
  Visit ${TEXT2}https://qrdx.dev${RESET2} for more.
522
609
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qrdx/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "QR code generator CLI for developers",
5
5
  "bin": {
6
6
  "qrdx": "./dist/index.js"
@@ -14,8 +14,10 @@
14
14
  "picocolors": "^1.1.1",
15
15
  "react": "^19.0.0",
16
16
  "react-dom": "^19.0.0",
17
- "sharp": "^0.34.2",
18
- "qrdx": "0.0.43"
17
+ "qrdx": "0.0.46"
18
+ },
19
+ "optionalDependencies": {
20
+ "sharp": "^0.34.2"
19
21
  },
20
22
  "devDependencies": {
21
23
  "@types/node": "^25.1.0",