reend-components 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.cjs CHANGED
@@ -8813,201 +8813,10 @@ var chalk = createChalk();
8813
8813
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
8814
8814
  var source_default = chalk;
8815
8815
 
8816
- // src/cli/commands/init.ts
8817
- var import_node_fs2 = require("node:fs");
8818
- var import_node_path2 = require("node:path");
8819
- var import_prompts = __toESM(require_prompts3(), 1);
8820
-
8821
- // src/cli/utils.ts
8822
- var import_node_fs = require("node:fs");
8823
- var import_node_path = require("node:path");
8824
- var CONFIG_FILE = "reend-ui.config.json";
8825
- function readConfig(cwd2) {
8826
- const configPath = (0, import_node_path.join)(cwd2, CONFIG_FILE);
8827
- if (!(0, import_node_fs.existsSync)(configPath)) return null;
8828
- try {
8829
- return JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf-8"));
8830
- } catch {
8831
- return null;
8832
- }
8833
- }
8834
- function writeConfig(cwd2, config) {
8835
- const configPath = (0, import_node_path.join)(cwd2, CONFIG_FILE);
8836
- (0, import_node_fs.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
8837
- }
8838
- function detectFramework(cwd2) {
8839
- const pkgPath = (0, import_node_path.join)(cwd2, "package.json");
8840
- if (!(0, import_node_fs.existsSync)(pkgPath)) return "vite";
8841
- try {
8842
- const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
8843
- const allDeps = {
8844
- ...pkg.dependencies,
8845
- ...pkg.devDependencies
8846
- };
8847
- if (allDeps["next"]) return "next";
8848
- if (allDeps["@remix-run/react"]) return "remix";
8849
- if (allDeps["gatsby"]) return "gatsby";
8850
- return "vite";
8851
- } catch {
8852
- return "vite";
8853
- }
8854
- }
8855
- function detectTypeScript(cwd2) {
8856
- return (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, "tsconfig.json")) || (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, "tsconfig.app.json"));
8857
- }
8858
- function detectTailwindConfig(cwd2) {
8859
- const candidates = [
8860
- "tailwind.config.ts",
8861
- "tailwind.config.js",
8862
- "tailwind.config.mjs"
8863
- ];
8864
- for (const f of candidates) {
8865
- if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, f))) return f;
8866
- }
8867
- return void 0;
8868
- }
8869
- async function fetchText(url) {
8870
- const res = await fetch(url);
8871
- if (!res.ok) {
8872
- throw new Error(`HTTP ${res.status} ${res.statusText} \u2014 ${url}`);
8873
- }
8874
- return res.text();
8875
- }
8876
- var log = {
8877
- info: (msg) => console.log(` ${source_default.cyan("\u25C6")} ${msg}`),
8878
- success: (msg) => console.log(` ${source_default.green("\u25C6")} ${source_default.green(msg)}`),
8879
- warn: (msg) => console.log(` ${source_default.yellow("\u25C7")} ${source_default.yellow(msg)}`),
8880
- error: (msg) => console.error(` ${source_default.red("\u2715")} ${source_default.red(msg)}`),
8881
- step: (msg) => console.log(` ${source_default.dim("\u203A")} ${msg}`),
8882
- blank: () => console.log(),
8883
- header: (msg) => console.log(
8884
- `
8885
- ${source_default.bold.white("\u25C6")} ${source_default.bold.white(msg.toUpperCase())}
8886
- `
8887
- )
8888
- };
8889
-
8890
- // src/cli/commands/init.ts
8891
- async function runInit(cwd2) {
8892
- log.header("ReEnd UI \u2014 Initialize");
8893
- if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(cwd2, "reend-ui.config.json"))) {
8894
- log.warn("reend-ui.config.json already exists.");
8895
- const { overwrite } = await (0, import_prompts.default)({
8896
- type: "confirm",
8897
- name: "overwrite",
8898
- message: "Overwrite existing config?",
8899
- initial: false
8900
- });
8901
- if (!overwrite) {
8902
- log.info("Init cancelled.");
8903
- return;
8904
- }
8905
- }
8906
- const framework = detectFramework(cwd2);
8907
- const isTypescript = detectTypeScript(cwd2);
8908
- const tailwindCfg = detectTailwindConfig(cwd2);
8909
- log.step(`Detected framework: ${source_default.cyan(framework)}`);
8910
- log.step(
8911
- `TypeScript: ${isTypescript ? source_default.green("yes") : source_default.yellow("no")}`
8912
- );
8913
- if (tailwindCfg) log.step(`Tailwind config: ${source_default.cyan(tailwindCfg)}`);
8914
- log.blank();
8915
- const response = await (0, import_prompts.default)([
8916
- {
8917
- type: "text",
8918
- name: "outputDir",
8919
- message: "Component output directory:",
8920
- initial: "components/ui"
8921
- },
8922
- {
8923
- type: "confirm",
8924
- name: "cssVariables",
8925
- message: "Copy CSS design tokens (variables.css)?",
8926
- initial: true
8927
- }
8928
- ]);
8929
- if (!response.outputDir) {
8930
- log.error("Init cancelled.");
8931
- return;
8932
- }
8933
- writeConfig(cwd2, {
8934
- version: "1.0",
8935
- framework,
8936
- typescript: isTypescript,
8937
- outputDir: response.outputDir,
8938
- cssVariables: response.cssVariables,
8939
- tailwindConfig: tailwindCfg
8940
- });
8941
- log.success("Created reend-ui.config.json");
8942
- const outDir = (0, import_node_path2.join)(cwd2, response.outputDir);
8943
- if (!(0, import_node_fs2.existsSync)(outDir)) {
8944
- (0, import_node_fs2.mkdirSync)(outDir, { recursive: true });
8945
- log.success(`Created directory: ${response.outputDir}`);
8946
- }
8947
- if (response.cssVariables) {
8948
- await copyVariablesCss(cwd2, response.outputDir);
8949
- }
8950
- log.blank();
8951
- console.log(source_default.bold.white(" \u25C6 NEXT STEPS\n"));
8952
- console.log(
8953
- source_default.dim(" Add the ReEnd Tailwind preset to your tailwind config:\n")
8954
- );
8955
- console.log(
8956
- source_default.cyan(` // ${tailwindCfg ?? "tailwind.config.ts"}`) + "\n" + source_default.white(" import reendPreset from 'reend-components/tailwind'\n") + source_default.white(" export default {\n") + source_default.white(" presets: [reendPreset],\n") + source_default.white(" // ...your config\n") + source_default.white(" }") + "\n"
8957
- );
8958
- console.log(source_default.dim(" Import CSS variables in your global CSS:\n"));
8959
- console.log(source_default.white(` @import 'reend-components/styles.css';`) + "\n");
8960
- console.log(source_default.dim(" Add components:\n"));
8961
- console.log(source_default.cyan(" npx reend-ui add button") + "\n");
8962
- }
8963
- async function copyVariablesCss(cwd2, outputDir) {
8964
- const stylesDir = (0, import_node_path2.join)(cwd2, "styles");
8965
- if (!(0, import_node_fs2.existsSync)(stylesDir)) {
8966
- (0, import_node_fs2.mkdirSync)(stylesDir, { recursive: true });
8967
- }
8968
- const destPath = (0, import_node_path2.join)(stylesDir, "reend-variables.css");
8969
- try {
8970
- const candidates = [
8971
- (0, import_node_path2.join)(
8972
- cwd2,
8973
- "node_modules",
8974
- "reend-components",
8975
- "src",
8976
- "styles",
8977
- "variables.css"
8978
- )
8979
- ];
8980
- for (const src of candidates) {
8981
- if ((0, import_node_fs2.existsSync)(src)) {
8982
- const content = (0, import_node_fs2.readFileSync)(src, "utf-8");
8983
- (0, import_node_fs2.writeFileSync)(destPath, content, "utf-8");
8984
- log.success("Copied variables.css \u2192 styles/reend-variables.css");
8985
- return;
8986
- }
8987
- }
8988
- const url = "https://raw.githubusercontent.com/VBeatDead/ReEnd-Components/main/src/styles/variables.css";
8989
- const res = await fetch(url);
8990
- if (res.ok) {
8991
- const content = await res.text();
8992
- (0, import_node_fs2.writeFileSync)(destPath, content, "utf-8");
8993
- log.success("Copied variables.css \u2192 styles/reend-variables.css");
8994
- } else {
8995
- log.warn(
8996
- "Could not copy variables.css \u2014 add manually from reend-components/styles.css"
8997
- );
8998
- }
8999
- } catch {
9000
- log.warn(
9001
- "Could not copy variables.css \u2014 add manually from reend-components/styles.css"
9002
- );
9003
- }
9004
- }
9005
-
9006
- // src/cli/commands/add.ts
9007
- var import_node_fs3 = require("node:fs");
9008
- var import_node_path3 = require("node:path");
9009
-
9010
8816
  // src/cli/registry.ts
8817
+ var CLI_VERSION = "1.2.0";
8818
+ var GITHUB_RAW_ROOT = "https://raw.githubusercontent.com/VBeatDead/ReEnd-Components";
8819
+ var BASE_DEPS = ["clsx", "tailwind-merge", "class-variance-authority"];
9011
8820
  var REGISTRY = {
9012
8821
  // ── Core (Tier 1) ───────────────────────────────────────────────────────
9013
8822
  button: {
@@ -9274,8 +9083,8 @@ var REGISTRY = {
9274
9083
  name: "session-timeout-modal",
9275
9084
  displayName: "SessionTimeoutModal",
9276
9085
  type: "core",
9277
- files: ["session-timeout-modal.tsx"],
9278
- deps: [],
9086
+ files: ["session-timeout-modal.tsx", "dialog.tsx", "button.tsx"],
9087
+ deps: ["@radix-ui/react-dialog", "@radix-ui/react-slot"],
9279
9088
  description: "Session expiry modal with CountdownTimer"
9280
9089
  },
9281
9090
  "file-upload": {
@@ -9430,7 +9239,7 @@ var REGISTRY = {
9430
9239
  displayName: "TacticalPanel",
9431
9240
  type: "signature",
9432
9241
  files: ["signature/tactical-panel.tsx"],
9433
- deps: [],
9242
+ deps: ["lucide-react"],
9434
9243
  description: "HUD-style panel with status, headerAction, collapsible"
9435
9244
  },
9436
9245
  "holo-card": {
@@ -9446,7 +9255,7 @@ var REGISTRY = {
9446
9255
  displayName: "DataStream",
9447
9256
  type: "signature",
9448
9257
  files: ["signature/data-stream.tsx"],
9449
- deps: ["framer-motion"],
9258
+ deps: ["framer-motion", "lucide-react"],
9450
9259
  description: "Scrolling data feed terminal with speed/messageType"
9451
9260
  },
9452
9261
  "tactical-badge": {
@@ -9462,7 +9271,7 @@ var REGISTRY = {
9462
9271
  displayName: "WarningBanner",
9463
9272
  type: "signature",
9464
9273
  files: ["signature/warning-banner.tsx"],
9465
- deps: [],
9274
+ deps: ["lucide-react"],
9466
9275
  description: "Alert banner with caution/alert/critical severity"
9467
9276
  },
9468
9277
  "scan-divider": {
@@ -9607,13 +9416,252 @@ var REGISTRY = {
9607
9416
  displayName: "ThemeSwitcher",
9608
9417
  type: "core",
9609
9418
  files: ["theme-switcher.tsx"],
9610
- deps: [],
9419
+ deps: ["lucide-react"],
9611
9420
  description: "Light/dark theme toggle with localStorage persistence"
9421
+ },
9422
+ toast: {
9423
+ name: "toast",
9424
+ displayName: "Toast",
9425
+ type: "core",
9426
+ files: ["toast.tsx", "toaster.tsx"],
9427
+ hooks: ["use-toast.ts"],
9428
+ deps: ["@radix-ui/react-toast", "lucide-react"],
9429
+ description: "Radix toast primitives + Toaster portal + useToast hook (imperative API)"
9612
9430
  }
9613
9431
  };
9432
+ function getFileUrl(filePath, ref = "main") {
9433
+ return `${GITHUB_RAW_ROOT}/${ref}/src/components/ui/${filePath}`;
9434
+ }
9435
+ function getHookUrl(filePath, ref = "main") {
9436
+ return `${GITHUB_RAW_ROOT}/${ref}/src/hooks/${filePath}`;
9437
+ }
9438
+
9439
+ // src/cli/commands/init.ts
9440
+ var import_node_fs2 = require("node:fs");
9441
+ var import_node_path2 = require("node:path");
9442
+ var import_prompts = __toESM(require_prompts3(), 1);
9443
+
9444
+ // src/cli/utils.ts
9445
+ var import_node_fs = require("node:fs");
9446
+ var import_node_path = require("node:path");
9447
+ var CONFIG_FILE = "reend-ui.config.json";
9448
+ function readConfig(cwd2) {
9449
+ const configPath = (0, import_node_path.join)(cwd2, CONFIG_FILE);
9450
+ if (!(0, import_node_fs.existsSync)(configPath)) return null;
9451
+ try {
9452
+ return JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf-8"));
9453
+ } catch {
9454
+ return null;
9455
+ }
9456
+ }
9457
+ function writeConfig(cwd2, config) {
9458
+ const configPath = (0, import_node_path.join)(cwd2, CONFIG_FILE);
9459
+ (0, import_node_fs.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
9460
+ }
9461
+ function detectFramework(cwd2) {
9462
+ const pkgPath = (0, import_node_path.join)(cwd2, "package.json");
9463
+ if (!(0, import_node_fs.existsSync)(pkgPath)) return "vite";
9464
+ try {
9465
+ const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
9466
+ const allDeps = {
9467
+ ...pkg.dependencies,
9468
+ ...pkg.devDependencies
9469
+ };
9470
+ if (allDeps["next"]) return "next";
9471
+ if (allDeps["@remix-run/react"]) return "remix";
9472
+ if (allDeps["gatsby"]) return "gatsby";
9473
+ return "vite";
9474
+ } catch {
9475
+ return "vite";
9476
+ }
9477
+ }
9478
+ function detectTypeScript(cwd2) {
9479
+ return (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, "tsconfig.json")) || (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, "tsconfig.app.json"));
9480
+ }
9481
+ function detectTailwindConfig(cwd2) {
9482
+ const candidates = [
9483
+ "tailwind.config.ts",
9484
+ "tailwind.config.js",
9485
+ "tailwind.config.mjs"
9486
+ ];
9487
+ for (const f of candidates) {
9488
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd2, f))) return f;
9489
+ }
9490
+ return void 0;
9491
+ }
9492
+ async function fetchText(url) {
9493
+ const res = await fetch(url);
9494
+ if (!res.ok) {
9495
+ throw new Error(`HTTP ${res.status} ${res.statusText} \u2014 ${url}`);
9496
+ }
9497
+ return res.text();
9498
+ }
9499
+ var log = {
9500
+ info: (msg) => console.log(` ${source_default.cyan("\u25C6")} ${msg}`),
9501
+ success: (msg) => console.log(` ${source_default.green("\u25C6")} ${source_default.green(msg)}`),
9502
+ warn: (msg) => console.log(` ${source_default.yellow("\u25C7")} ${source_default.yellow(msg)}`),
9503
+ error: (msg) => console.error(` ${source_default.red("\u2715")} ${source_default.red(msg)}`),
9504
+ step: (msg) => console.log(` ${source_default.dim("\u203A")} ${msg}`),
9505
+ blank: () => console.log(),
9506
+ header: (msg) => console.log(
9507
+ `
9508
+ ${source_default.bold.white("\u25C6")} ${source_default.bold.white(msg.toUpperCase())}
9509
+ `
9510
+ )
9511
+ };
9512
+
9513
+ // src/cli/commands/init.ts
9514
+ async function runInit(cwd2) {
9515
+ log.header("ReEnd UI \u2014 Initialize");
9516
+ if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(cwd2, "reend-ui.config.json"))) {
9517
+ log.warn("reend-ui.config.json already exists.");
9518
+ const { overwrite } = await (0, import_prompts.default)({
9519
+ type: "confirm",
9520
+ name: "overwrite",
9521
+ message: "Overwrite existing config?",
9522
+ initial: false
9523
+ });
9524
+ if (!overwrite) {
9525
+ log.info("Init cancelled.");
9526
+ return;
9527
+ }
9528
+ }
9529
+ const framework = detectFramework(cwd2);
9530
+ const isTypescript = detectTypeScript(cwd2);
9531
+ const tailwindCfg = detectTailwindConfig(cwd2);
9532
+ log.step(`Detected framework: ${source_default.cyan(framework)}`);
9533
+ log.step(
9534
+ `TypeScript: ${isTypescript ? source_default.green("yes") : source_default.yellow("no")}`
9535
+ );
9536
+ if (tailwindCfg) log.step(`Tailwind config: ${source_default.cyan(tailwindCfg)}`);
9537
+ log.blank();
9538
+ const response = await (0, import_prompts.default)([
9539
+ {
9540
+ type: "text",
9541
+ name: "outputDir",
9542
+ message: "Component output directory:",
9543
+ initial: "components/ui"
9544
+ },
9545
+ {
9546
+ type: "confirm",
9547
+ name: "cssVariables",
9548
+ message: "Copy CSS design tokens (variables.css)?",
9549
+ initial: true
9550
+ }
9551
+ ]);
9552
+ if (!response.outputDir) {
9553
+ log.error("Init cancelled.");
9554
+ return;
9555
+ }
9556
+ writeConfig(cwd2, {
9557
+ version: "1.0",
9558
+ framework,
9559
+ typescript: isTypescript,
9560
+ outputDir: response.outputDir,
9561
+ cssVariables: response.cssVariables,
9562
+ tailwindConfig: tailwindCfg
9563
+ });
9564
+ log.success("Created reend-ui.config.json");
9565
+ const outDir = (0, import_node_path2.join)(cwd2, response.outputDir);
9566
+ if (!(0, import_node_fs2.existsSync)(outDir)) {
9567
+ (0, import_node_fs2.mkdirSync)(outDir, { recursive: true });
9568
+ log.success(`Created directory: ${response.outputDir}`);
9569
+ }
9570
+ const projectBase = (0, import_node_path2.join)(outDir, "..", "..");
9571
+ createCnUtility(projectBase);
9572
+ if (response.cssVariables) {
9573
+ await copyVariablesCss(cwd2, projectBase);
9574
+ }
9575
+ log.blank();
9576
+ console.log(source_default.bold.white(" \u25C6 NEXT STEPS\n"));
9577
+ console.log(source_default.dim(" Install the base dependencies used by every component:\n"));
9578
+ console.log(
9579
+ source_default.cyan(" npm install clsx tailwind-merge class-variance-authority") + "\n"
9580
+ );
9581
+ console.log(
9582
+ source_default.dim(" Add the ReEnd Tailwind preset to your tailwind config:\n")
9583
+ );
9584
+ console.log(
9585
+ source_default.cyan(` // ${tailwindCfg ?? "tailwind.config.ts"}`) + "\n" + source_default.white(" import reendPreset from 'reend-components/tailwind'\n") + source_default.white(" export default {\n") + source_default.white(" presets: [reendPreset],\n") + source_default.white(" // ...your config\n") + source_default.white(" }") + "\n"
9586
+ );
9587
+ console.log(source_default.dim(" Import CSS variables in your global CSS:\n"));
9588
+ console.log(source_default.white(` @import 'reend-components/styles.css';`) + "\n");
9589
+ console.log(source_default.dim(" Add components:\n"));
9590
+ console.log(source_default.cyan(" npx reend-ui add button") + "\n");
9591
+ }
9592
+ var CN_UTILITY = `import { clsx, type ClassValue } from "clsx";
9593
+ import { twMerge } from "tailwind-merge";
9594
+
9595
+ export function cn(...inputs: ClassValue[]) {
9596
+ return twMerge(clsx(inputs));
9597
+ }
9598
+ `;
9599
+ function createCnUtility(projectBase) {
9600
+ const libDir = (0, import_node_path2.join)(projectBase, "lib");
9601
+ const destPath = (0, import_node_path2.join)(libDir, "utils.ts");
9602
+ if ((0, import_node_fs2.existsSync)(destPath)) {
9603
+ log.step("lib/utils.ts already exists \u2014 keeping yours");
9604
+ return;
9605
+ }
9606
+ if (!(0, import_node_fs2.existsSync)(libDir)) {
9607
+ (0, import_node_fs2.mkdirSync)(libDir, { recursive: true });
9608
+ }
9609
+ (0, import_node_fs2.writeFileSync)(destPath, CN_UTILITY, "utf-8");
9610
+ log.success("Created lib/utils.ts (cn helper)");
9611
+ }
9612
+ async function copyVariablesCss(cwd2, projectBase) {
9613
+ const stylesDir = (0, import_node_path2.join)(projectBase, "styles");
9614
+ if (!(0, import_node_fs2.existsSync)(stylesDir)) {
9615
+ (0, import_node_fs2.mkdirSync)(stylesDir, { recursive: true });
9616
+ }
9617
+ const destPath = (0, import_node_path2.join)(stylesDir, "reend-variables.css");
9618
+ try {
9619
+ const candidates = [
9620
+ (0, import_node_path2.join)(
9621
+ cwd2,
9622
+ "node_modules",
9623
+ "reend-components",
9624
+ "src",
9625
+ "styles",
9626
+ "variables.css"
9627
+ )
9628
+ ];
9629
+ for (const src of candidates) {
9630
+ if ((0, import_node_fs2.existsSync)(src)) {
9631
+ const content = (0, import_node_fs2.readFileSync)(src, "utf-8");
9632
+ (0, import_node_fs2.writeFileSync)(destPath, content, "utf-8");
9633
+ log.success("Copied variables.css \u2192 styles/reend-variables.css");
9634
+ return;
9635
+ }
9636
+ }
9637
+ const url = "https://raw.githubusercontent.com/VBeatDead/ReEnd-Components/main/src/styles/variables.css";
9638
+ const res = await fetch(url);
9639
+ if (res.ok) {
9640
+ const content = await res.text();
9641
+ (0, import_node_fs2.writeFileSync)(destPath, content, "utf-8");
9642
+ log.success("Copied variables.css \u2192 styles/reend-variables.css");
9643
+ } else {
9644
+ log.warn(
9645
+ "Could not copy variables.css \u2014 add manually from reend-components/styles.css"
9646
+ );
9647
+ }
9648
+ } catch {
9649
+ log.warn(
9650
+ "Could not copy variables.css \u2014 add manually from reend-components/styles.css"
9651
+ );
9652
+ }
9653
+ }
9614
9654
 
9615
9655
  // src/cli/commands/add.ts
9616
- var GITHUB_RAW_BASE = "https://raw.githubusercontent.com/VBeatDead/ReEnd-Components/main/src/components/ui";
9656
+ var import_node_fs3 = require("node:fs");
9657
+ var import_node_path3 = require("node:path");
9658
+ async function fetchRegistryFile(urlFor) {
9659
+ try {
9660
+ return await fetchText(urlFor(`v${CLI_VERSION}`));
9661
+ } catch {
9662
+ return fetchText(urlFor("main"));
9663
+ }
9664
+ }
9617
9665
  async function runAdd(components, cwd2, opts) {
9618
9666
  log.header("ReEnd UI \u2014 Add Components");
9619
9667
  const config = readConfig(cwd2);
@@ -9622,6 +9670,8 @@ async function runAdd(components, cwd2, opts) {
9622
9670
  process.exit(1);
9623
9671
  }
9624
9672
  const outputDir = (0, import_node_path3.join)(cwd2, config.outputDir);
9673
+ const projectBase = (0, import_node_path3.join)(outputDir, "..", "..");
9674
+ const hooksDir = (0, import_node_path3.join)(projectBase, "hooks");
9625
9675
  const depsToInstall = /* @__PURE__ */ new Set();
9626
9676
  const added = [];
9627
9677
  const skipped = [];
@@ -9634,28 +9684,41 @@ async function runAdd(components, cwd2, opts) {
9634
9684
  continue;
9635
9685
  }
9636
9686
  log.info(`Adding ${source_default.cyan(entry.displayName)}...`);
9687
+ const targets = [
9688
+ ...entry.files.map((file) => ({
9689
+ file,
9690
+ dest: (0, import_node_path3.join)(outputDir, file),
9691
+ hook: false
9692
+ })),
9693
+ ...(entry.hooks ?? []).map((file) => ({
9694
+ file,
9695
+ dest: (0, import_node_path3.join)(hooksDir, file),
9696
+ hook: true
9697
+ }))
9698
+ ];
9637
9699
  let componentSuccess = true;
9638
- for (const file of entry.files) {
9639
- const destPath = (0, import_node_path3.join)(outputDir, file);
9640
- const destDir = (0, import_node_path3.dirname)(destPath);
9641
- if ((0, import_node_fs3.existsSync)(destPath) && !opts.overwrite) {
9642
- log.warn(` ${file} already exists \u2014 use --overwrite to replace`);
9700
+ for (const { file, dest, hook } of targets) {
9701
+ const destDir = (0, import_node_path3.dirname)(dest);
9702
+ const label = hook ? `hooks/${file}` : file;
9703
+ if ((0, import_node_fs3.existsSync)(dest) && !opts.overwrite) {
9704
+ log.warn(` ${label} already exists \u2014 use --overwrite to replace`);
9643
9705
  skipped.push(entry.displayName);
9644
9706
  componentSuccess = false;
9645
9707
  continue;
9646
9708
  }
9647
9709
  try {
9648
- const url = `${GITHUB_RAW_BASE}/${file}`;
9649
- log.step(`Fetching ${file}...`);
9650
- const content = await fetchText(url);
9710
+ log.step(`Fetching ${label}...`);
9711
+ const content = await fetchRegistryFile(
9712
+ (ref) => hook ? getHookUrl(file, ref) : getFileUrl(file, ref)
9713
+ );
9651
9714
  if (!(0, import_node_fs3.existsSync)(destDir)) {
9652
9715
  (0, import_node_fs3.mkdirSync)(destDir, { recursive: true });
9653
9716
  }
9654
- (0, import_node_fs3.writeFileSync)(destPath, content, "utf-8");
9655
- log.success(` Added \u2192 ${config.outputDir}/${file}`);
9717
+ (0, import_node_fs3.writeFileSync)(dest, content, "utf-8");
9718
+ log.success(` Added \u2192 ${label}`);
9656
9719
  } catch (err) {
9657
9720
  const message = err instanceof Error ? err.message : String(err);
9658
- log.error(` Failed to fetch ${file}: ${message}`);
9721
+ log.error(` Failed to fetch ${label}: ${message}`);
9659
9722
  componentSuccess = false;
9660
9723
  failed.push(entry.displayName);
9661
9724
  }
@@ -9677,22 +9740,21 @@ async function runAdd(components, cwd2, opts) {
9677
9740
  if (failed.length > 0) {
9678
9741
  log.error(`Failed: ${failed.join(", ")}`);
9679
9742
  }
9743
+ if (added.length > 0) {
9744
+ for (const dep of BASE_DEPS) {
9745
+ depsToInstall.add(dep);
9746
+ }
9747
+ }
9680
9748
  if (depsToInstall.size > 0) {
9681
9749
  const deps = Array.from(depsToInstall).join(" ");
9682
9750
  log.blank();
9683
- console.log(source_default.bold.yellow(" \u26A1 Install peer dependencies:\n"));
9751
+ console.log(source_default.bold.yellow(" \u26A1 Install dependencies:\n"));
9684
9752
  console.log(source_default.cyan(` npm install ${deps}`) + "\n");
9685
9753
  }
9686
- if (added.length > 0) {
9754
+ if (added.length > 0 && !(0, import_node_fs3.existsSync)((0, import_node_path3.join)(projectBase, "lib", "utils.ts"))) {
9687
9755
  log.blank();
9688
- console.log(
9689
- source_default.dim(
9690
- " Components require the cn() utility. Add to your project:\n"
9691
- )
9692
- );
9693
- console.log(source_default.white(" import { cn } from 'reend-components'") + "\n");
9694
- console.log(
9695
- source_default.dim(" Or copy the cn utility from: lib/utils.ts") + "\n"
9756
+ log.warn(
9757
+ "lib/utils.ts (cn helper) not found \u2014 run `npx reend-ui init` to create it."
9696
9758
  );
9697
9759
  }
9698
9760
  }
@@ -9746,10 +9808,9 @@ async function runUpdate(components, cwd2) {
9746
9808
  const outputDir = (0, import_node_path4.join)(cwd2, config.outputDir);
9747
9809
  let targets = components;
9748
9810
  if (targets.length === 0) {
9749
- targets = Object.keys(REGISTRY).filter((name) => {
9750
- const entry = REGISTRY[name];
9751
- return entry.files.some((f) => (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, f)));
9752
- });
9811
+ targets = Object.keys(REGISTRY).filter(
9812
+ (name) => (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, REGISTRY[name].files[0]))
9813
+ );
9753
9814
  if (targets.length === 0) {
9754
9815
  log.warn("No installed components found. Use: npx reend-ui add <name>");
9755
9816
  return;
@@ -9766,7 +9827,7 @@ async function runUpdate(components, cwd2) {
9766
9827
  log.error(`Unknown component: "${name}"`);
9767
9828
  continue;
9768
9829
  }
9769
- const isInstalled = entry.files.some((f) => (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, f)));
9830
+ const isInstalled = (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, entry.files[0]));
9770
9831
  if (isInstalled) {
9771
9832
  existing.push(name);
9772
9833
  } else {
@@ -9790,7 +9851,7 @@ program2.name("reend-ui").description(
9790
9851
  source_default.bold.white(
9791
9852
  "\u25C6 ReEnd UI \u2014 Arknights: Endfield React Component Library\n"
9792
9853
  ) + source_default.dim(" Shadcn-style copy-paste workflow for ReEnd components.\n")
9793
- ).version("1.1.0");
9854
+ ).version(CLI_VERSION);
9794
9855
  program2.command("init").description("Initialize ReEnd UI in your project").action(async () => {
9795
9856
  await runInit(cwd);
9796
9857
  });