reend-components 1.0.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/README.md +172 -98
- package/dist/bin/cli.cjs +533 -229
- package/dist/lib/index.cjs +4 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.ts +574 -19
- package/dist/lib/index.mjs +4528 -1404
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/style.css +1 -1
- package/dist/tailwind/index.cjs +492 -0
- package/dist/tailwind/index.mjs +462 -0
- package/package.json +27 -8
- package/src/styles/utilities.css +580 -0
- package/src/styles/variables.css +250 -14
- package/src/tailwind-preset.ts +193 -15
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: {
|
|
@@ -9034,6 +8843,14 @@ var REGISTRY = {
|
|
|
9034
8843
|
deps: [],
|
|
9035
8844
|
description: "Corner bracket decoration with 6 sub-components"
|
|
9036
8845
|
},
|
|
8846
|
+
"link-card": {
|
|
8847
|
+
name: "link-card",
|
|
8848
|
+
displayName: "LinkCard",
|
|
8849
|
+
type: "core",
|
|
8850
|
+
files: ["card.tsx"],
|
|
8851
|
+
deps: [],
|
|
8852
|
+
description: "Navigation card with icon, title, and animated arrow (\u2192). Uses clip-corner hover effect."
|
|
8853
|
+
},
|
|
9037
8854
|
input: {
|
|
9038
8855
|
name: "input",
|
|
9039
8856
|
displayName: "Input",
|
|
@@ -9148,6 +8965,14 @@ var REGISTRY = {
|
|
|
9148
8965
|
deps: ["@radix-ui/react-tooltip"],
|
|
9149
8966
|
description: "Tooltip with Radix primitives"
|
|
9150
8967
|
},
|
|
8968
|
+
sonner: {
|
|
8969
|
+
name: "sonner",
|
|
8970
|
+
displayName: "Sonner",
|
|
8971
|
+
type: "core",
|
|
8972
|
+
files: ["sonner.tsx"],
|
|
8973
|
+
deps: ["sonner"],
|
|
8974
|
+
description: "Toast notifications with notify helper, 5 types, auto-dismiss"
|
|
8975
|
+
},
|
|
9151
8976
|
// ── Core (Tier 6 — Extended) ────────────────────────────────────────────
|
|
9152
8977
|
skeleton: {
|
|
9153
8978
|
name: "skeleton",
|
|
@@ -9155,7 +8980,7 @@ var REGISTRY = {
|
|
|
9155
8980
|
type: "core",
|
|
9156
8981
|
files: ["skeleton.tsx"],
|
|
9157
8982
|
deps: [],
|
|
9158
|
-
description: "SkeletonLine/Text/Avatar/Card with shimmer animation"
|
|
8983
|
+
description: "SkeletonLine/Text/Avatar/Card/Image/TableRow with shimmer animation"
|
|
9159
8984
|
},
|
|
9160
8985
|
"empty-state": {
|
|
9161
8986
|
name: "empty-state",
|
|
@@ -9163,7 +8988,7 @@ var REGISTRY = {
|
|
|
9163
8988
|
type: "core",
|
|
9164
8989
|
files: ["empty-state.tsx"],
|
|
9165
8990
|
deps: [],
|
|
9166
|
-
description: "
|
|
8991
|
+
description: "8 context variants: search/empty-list/no-notifications/error-loading/no-permission/empty-filter/inbox-zero/no-connection"
|
|
9167
8992
|
},
|
|
9168
8993
|
alert: {
|
|
9169
8994
|
name: "alert",
|
|
@@ -9171,7 +8996,7 @@ var REGISTRY = {
|
|
|
9171
8996
|
type: "core",
|
|
9172
8997
|
files: ["alert.tsx"],
|
|
9173
8998
|
deps: [],
|
|
9174
|
-
description: "4 variants: info/success/warning/error, dismissible, ARIA"
|
|
8999
|
+
description: "4 variants: info/success/warning/error, dismissible, ARIA, TopBanner"
|
|
9175
9000
|
},
|
|
9176
9001
|
timeline: {
|
|
9177
9002
|
name: "timeline",
|
|
@@ -9213,6 +9038,185 @@ var REGISTRY = {
|
|
|
9213
9038
|
deps: [],
|
|
9214
9039
|
description: "+/\u2212 stepper input with min/max/step, keyboard support"
|
|
9215
9040
|
},
|
|
9041
|
+
// ── Core (P1 New — v1.1.0) ──────────────────────────────────────────────
|
|
9042
|
+
"view-toggle": {
|
|
9043
|
+
name: "view-toggle",
|
|
9044
|
+
displayName: "ViewToggle",
|
|
9045
|
+
type: "core",
|
|
9046
|
+
files: ["view-toggle.tsx"],
|
|
9047
|
+
deps: [],
|
|
9048
|
+
description: "Grid/List toggle with localStorage persistence"
|
|
9049
|
+
},
|
|
9050
|
+
"filter-bar": {
|
|
9051
|
+
name: "filter-bar",
|
|
9052
|
+
displayName: "FilterBar",
|
|
9053
|
+
type: "core",
|
|
9054
|
+
files: ["filter-bar.tsx"],
|
|
9055
|
+
deps: [],
|
|
9056
|
+
description: "Filter bar with chips and clear-all"
|
|
9057
|
+
},
|
|
9058
|
+
"otp-input": {
|
|
9059
|
+
name: "otp-input",
|
|
9060
|
+
displayName: "OTPInput",
|
|
9061
|
+
type: "core",
|
|
9062
|
+
files: ["otp-input.tsx"],
|
|
9063
|
+
deps: [],
|
|
9064
|
+
description: "OTP/PIN input with auto-advance, paste, shake-on-error"
|
|
9065
|
+
},
|
|
9066
|
+
"date-picker": {
|
|
9067
|
+
name: "date-picker",
|
|
9068
|
+
displayName: "DatePicker",
|
|
9069
|
+
type: "core",
|
|
9070
|
+
files: ["date-picker.tsx"],
|
|
9071
|
+
deps: ["@radix-ui/react-popover"],
|
|
9072
|
+
description: "Date picker with Endfield styling"
|
|
9073
|
+
},
|
|
9074
|
+
rating: {
|
|
9075
|
+
name: "rating",
|
|
9076
|
+
displayName: "Rating",
|
|
9077
|
+
type: "core",
|
|
9078
|
+
files: ["rating.tsx"],
|
|
9079
|
+
deps: [],
|
|
9080
|
+
description: "Star/diamond rating with \u25C6/\u25C7 indicators"
|
|
9081
|
+
},
|
|
9082
|
+
"session-timeout-modal": {
|
|
9083
|
+
name: "session-timeout-modal",
|
|
9084
|
+
displayName: "SessionTimeoutModal",
|
|
9085
|
+
type: "core",
|
|
9086
|
+
files: ["session-timeout-modal.tsx", "dialog.tsx", "button.tsx"],
|
|
9087
|
+
deps: ["@radix-ui/react-dialog", "@radix-ui/react-slot"],
|
|
9088
|
+
description: "Session expiry modal with CountdownTimer"
|
|
9089
|
+
},
|
|
9090
|
+
"file-upload": {
|
|
9091
|
+
name: "file-upload",
|
|
9092
|
+
displayName: "FileUpload",
|
|
9093
|
+
type: "core",
|
|
9094
|
+
files: ["file-upload.tsx"],
|
|
9095
|
+
deps: [],
|
|
9096
|
+
description: "Drag-and-drop file upload with 8 states"
|
|
9097
|
+
},
|
|
9098
|
+
"bottom-sheet": {
|
|
9099
|
+
name: "bottom-sheet",
|
|
9100
|
+
displayName: "BottomSheet",
|
|
9101
|
+
type: "core",
|
|
9102
|
+
files: ["bottom-sheet.tsx"],
|
|
9103
|
+
deps: [],
|
|
9104
|
+
description: "Mobile bottom sheet with slide-up animation"
|
|
9105
|
+
},
|
|
9106
|
+
carousel: {
|
|
9107
|
+
name: "carousel",
|
|
9108
|
+
displayName: "Carousel",
|
|
9109
|
+
type: "core",
|
|
9110
|
+
files: ["carousel.tsx"],
|
|
9111
|
+
deps: [],
|
|
9112
|
+
description: "CSS scroll-snap carousel with \u25C6/\u25C7 dots"
|
|
9113
|
+
},
|
|
9114
|
+
resizable: {
|
|
9115
|
+
name: "resizable",
|
|
9116
|
+
displayName: "ResizablePanels",
|
|
9117
|
+
type: "core",
|
|
9118
|
+
files: ["resizable.tsx"],
|
|
9119
|
+
deps: ["react-resizable-panels"],
|
|
9120
|
+
description: "Resizable panel layout with Endfield handle"
|
|
9121
|
+
},
|
|
9122
|
+
chart: {
|
|
9123
|
+
name: "chart",
|
|
9124
|
+
displayName: "Chart",
|
|
9125
|
+
type: "core",
|
|
9126
|
+
files: ["chart.tsx"],
|
|
9127
|
+
deps: ["recharts"],
|
|
9128
|
+
description: "Recharts wrapper with Endfield palette (--chart-1 to --chart-8)"
|
|
9129
|
+
},
|
|
9130
|
+
footer: {
|
|
9131
|
+
name: "footer",
|
|
9132
|
+
displayName: "Footer",
|
|
9133
|
+
type: "core",
|
|
9134
|
+
files: ["footer.tsx"],
|
|
9135
|
+
deps: [],
|
|
9136
|
+
description: "Footer with FooterColumn and FooterLink, 3-column grid, brand bar"
|
|
9137
|
+
},
|
|
9138
|
+
// ── Data Display ───────────────────────────────────────────────────────
|
|
9139
|
+
table: {
|
|
9140
|
+
name: "table",
|
|
9141
|
+
displayName: "Table",
|
|
9142
|
+
type: "core",
|
|
9143
|
+
files: ["table.tsx"],
|
|
9144
|
+
deps: [],
|
|
9145
|
+
description: "Data table with sortable columns, sticky header, hover/selected states, empty slot"
|
|
9146
|
+
},
|
|
9147
|
+
list: {
|
|
9148
|
+
name: "list",
|
|
9149
|
+
displayName: "List",
|
|
9150
|
+
type: "core",
|
|
9151
|
+
files: ["list.tsx"],
|
|
9152
|
+
deps: [],
|
|
9153
|
+
description: "Diamond bullet, numbered (01/02), description, and link list variants"
|
|
9154
|
+
},
|
|
9155
|
+
stat: {
|
|
9156
|
+
name: "stat",
|
|
9157
|
+
displayName: "Stat",
|
|
9158
|
+
type: "core",
|
|
9159
|
+
files: ["stat.tsx"],
|
|
9160
|
+
deps: [],
|
|
9161
|
+
description: "Stat/metric card with Orbitron value, trend indicators, StatGrid layout"
|
|
9162
|
+
},
|
|
9163
|
+
// ── Overlay & Utility (Part 7) ─────────────────────────────────────────
|
|
9164
|
+
"copy-clipboard": {
|
|
9165
|
+
name: "copy-clipboard",
|
|
9166
|
+
displayName: "CopyClipboard",
|
|
9167
|
+
type: "core",
|
|
9168
|
+
files: ["copy-clipboard.tsx"],
|
|
9169
|
+
deps: [],
|
|
9170
|
+
description: "Copy-to-clipboard button with \u2713 feedback and configurable reset delay"
|
|
9171
|
+
},
|
|
9172
|
+
"back-to-top": {
|
|
9173
|
+
name: "back-to-top",
|
|
9174
|
+
displayName: "BackToTop",
|
|
9175
|
+
type: "core",
|
|
9176
|
+
files: ["back-to-top.tsx"],
|
|
9177
|
+
deps: [],
|
|
9178
|
+
description: "Fixed back-to-top button with scroll threshold and smooth behavior"
|
|
9179
|
+
},
|
|
9180
|
+
"scroll-progress": {
|
|
9181
|
+
name: "scroll-progress",
|
|
9182
|
+
displayName: "ScrollProgress",
|
|
9183
|
+
type: "core",
|
|
9184
|
+
files: ["scroll-progress.tsx"],
|
|
9185
|
+
deps: [],
|
|
9186
|
+
description: "Fixed top progress bar tracking page scroll position (progressbar ARIA)"
|
|
9187
|
+
},
|
|
9188
|
+
"cookie-consent": {
|
|
9189
|
+
name: "cookie-consent",
|
|
9190
|
+
displayName: "CookieConsent",
|
|
9191
|
+
type: "core",
|
|
9192
|
+
files: ["cookie-consent.tsx"],
|
|
9193
|
+
deps: [],
|
|
9194
|
+
description: "Fixed bottom cookie consent banner with onAccept / onCustomize callbacks"
|
|
9195
|
+
},
|
|
9196
|
+
"command-palette": {
|
|
9197
|
+
name: "command-palette",
|
|
9198
|
+
displayName: "CommandPalette",
|
|
9199
|
+
type: "core",
|
|
9200
|
+
files: ["command-palette.tsx"],
|
|
9201
|
+
deps: [],
|
|
9202
|
+
description: "\u2318K command palette with search, grouped results, arrow-key navigation"
|
|
9203
|
+
},
|
|
9204
|
+
dropdown: {
|
|
9205
|
+
name: "dropdown",
|
|
9206
|
+
displayName: "Dropdown",
|
|
9207
|
+
type: "core",
|
|
9208
|
+
files: ["dropdown.tsx"],
|
|
9209
|
+
deps: ["@radix-ui/react-popover"],
|
|
9210
|
+
description: "Dropdown menu with grouped items and danger variant (Radix Popover base)"
|
|
9211
|
+
},
|
|
9212
|
+
"context-menu": {
|
|
9213
|
+
name: "context-menu",
|
|
9214
|
+
displayName: "ContextMenu",
|
|
9215
|
+
type: "core",
|
|
9216
|
+
files: ["context-menu.tsx"],
|
|
9217
|
+
deps: [],
|
|
9218
|
+
description: "Right-click context menu with keyboard shortcuts and portal rendering"
|
|
9219
|
+
},
|
|
9216
9220
|
// ── Signature (Phase 0) ─────────────────────────────────────────────────
|
|
9217
9221
|
"glitch-text": {
|
|
9218
9222
|
name: "glitch-text",
|
|
@@ -9235,7 +9239,7 @@ var REGISTRY = {
|
|
|
9235
9239
|
displayName: "TacticalPanel",
|
|
9236
9240
|
type: "signature",
|
|
9237
9241
|
files: ["signature/tactical-panel.tsx"],
|
|
9238
|
-
deps: [],
|
|
9242
|
+
deps: ["lucide-react"],
|
|
9239
9243
|
description: "HUD-style panel with status, headerAction, collapsible"
|
|
9240
9244
|
},
|
|
9241
9245
|
"holo-card": {
|
|
@@ -9243,7 +9247,7 @@ var REGISTRY = {
|
|
|
9243
9247
|
displayName: "HoloCard",
|
|
9244
9248
|
type: "signature",
|
|
9245
9249
|
files: ["signature/holo-card.tsx"],
|
|
9246
|
-
deps: [
|
|
9250
|
+
deps: [],
|
|
9247
9251
|
description: "Holographic stat card with tilt hover effect"
|
|
9248
9252
|
},
|
|
9249
9253
|
"data-stream": {
|
|
@@ -9251,7 +9255,7 @@ var REGISTRY = {
|
|
|
9251
9255
|
displayName: "DataStream",
|
|
9252
9256
|
type: "signature",
|
|
9253
9257
|
files: ["signature/data-stream.tsx"],
|
|
9254
|
-
deps: [],
|
|
9258
|
+
deps: ["framer-motion", "lucide-react"],
|
|
9255
9259
|
description: "Scrolling data feed terminal with speed/messageType"
|
|
9256
9260
|
},
|
|
9257
9261
|
"tactical-badge": {
|
|
@@ -9267,7 +9271,7 @@ var REGISTRY = {
|
|
|
9267
9271
|
displayName: "WarningBanner",
|
|
9268
9272
|
type: "signature",
|
|
9269
9273
|
files: ["signature/warning-banner.tsx"],
|
|
9270
|
-
deps: [],
|
|
9274
|
+
deps: ["lucide-react"],
|
|
9271
9275
|
description: "Alert banner with caution/alert/critical severity"
|
|
9272
9276
|
},
|
|
9273
9277
|
"scan-divider": {
|
|
@@ -9366,11 +9370,298 @@ var REGISTRY = {
|
|
|
9366
9370
|
files: ["signature/countdown-timer.tsx"],
|
|
9367
9371
|
deps: [],
|
|
9368
9372
|
description: "Tactical mission countdown, onComplete, 3 sizes"
|
|
9373
|
+
},
|
|
9374
|
+
"rich-text-editor": {
|
|
9375
|
+
name: "rich-text-editor",
|
|
9376
|
+
displayName: "RichTextEditor",
|
|
9377
|
+
type: "core",
|
|
9378
|
+
files: ["rich-text-editor.tsx"],
|
|
9379
|
+
deps: [],
|
|
9380
|
+
description: "WYSIWYG markdown editor with toolbar (B/I/U/S/H1-H3/quote/list/link/divider), character count, and MARKDOWN/PREVIEW toggle"
|
|
9381
|
+
},
|
|
9382
|
+
"sort-control": {
|
|
9383
|
+
name: "sort-control",
|
|
9384
|
+
displayName: "SortControl",
|
|
9385
|
+
type: "core",
|
|
9386
|
+
files: ["sort-control.tsx"],
|
|
9387
|
+
deps: [],
|
|
9388
|
+
description: "Sort options row with tri-state direction cycling (asc/desc/none) and \u25B2/\u25BC indicators"
|
|
9389
|
+
},
|
|
9390
|
+
"pull-to-refresh": {
|
|
9391
|
+
name: "pull-to-refresh",
|
|
9392
|
+
displayName: "PullToRefresh",
|
|
9393
|
+
type: "core",
|
|
9394
|
+
files: ["pull-to-refresh.tsx"],
|
|
9395
|
+
deps: [],
|
|
9396
|
+
description: "Mobile pull-to-refresh with diamond spinner, phase machine (idle\u2192pulling\u2192threshold\u2192refreshing\u2192complete), haptic feedback"
|
|
9397
|
+
},
|
|
9398
|
+
"swipeable-item": {
|
|
9399
|
+
name: "swipeable-item",
|
|
9400
|
+
displayName: "SwipeableItem",
|
|
9401
|
+
type: "core",
|
|
9402
|
+
files: ["swipeable-item.tsx"],
|
|
9403
|
+
deps: [],
|
|
9404
|
+
description: "Touch-swipeable list item with left/right action reveal, 80/160px thresholds, haptic feedback"
|
|
9405
|
+
},
|
|
9406
|
+
"spoiler-block": {
|
|
9407
|
+
name: "spoiler-block",
|
|
9408
|
+
displayName: "SpoilerBlock",
|
|
9409
|
+
type: "core",
|
|
9410
|
+
files: ["spoiler-block.tsx"],
|
|
9411
|
+
deps: [],
|
|
9412
|
+
description: "Spoiler content block with blur overlay and reveal toggle"
|
|
9413
|
+
},
|
|
9414
|
+
"theme-switcher": {
|
|
9415
|
+
name: "theme-switcher",
|
|
9416
|
+
displayName: "ThemeSwitcher",
|
|
9417
|
+
type: "core",
|
|
9418
|
+
files: ["theme-switcher.tsx"],
|
|
9419
|
+
deps: ["lucide-react"],
|
|
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)"
|
|
9369
9430
|
}
|
|
9370
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
|
+
}
|
|
9371
9654
|
|
|
9372
9655
|
// src/cli/commands/add.ts
|
|
9373
|
-
var
|
|
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
|
+
}
|
|
9374
9665
|
async function runAdd(components, cwd2, opts) {
|
|
9375
9666
|
log.header("ReEnd UI \u2014 Add Components");
|
|
9376
9667
|
const config = readConfig(cwd2);
|
|
@@ -9379,6 +9670,8 @@ async function runAdd(components, cwd2, opts) {
|
|
|
9379
9670
|
process.exit(1);
|
|
9380
9671
|
}
|
|
9381
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");
|
|
9382
9675
|
const depsToInstall = /* @__PURE__ */ new Set();
|
|
9383
9676
|
const added = [];
|
|
9384
9677
|
const skipped = [];
|
|
@@ -9391,28 +9684,41 @@ async function runAdd(components, cwd2, opts) {
|
|
|
9391
9684
|
continue;
|
|
9392
9685
|
}
|
|
9393
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
|
+
];
|
|
9394
9699
|
let componentSuccess = true;
|
|
9395
|
-
for (const file of
|
|
9396
|
-
const
|
|
9397
|
-
const
|
|
9398
|
-
if ((0, import_node_fs3.existsSync)(
|
|
9399
|
-
log.warn(` ${
|
|
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`);
|
|
9400
9705
|
skipped.push(entry.displayName);
|
|
9401
9706
|
componentSuccess = false;
|
|
9402
9707
|
continue;
|
|
9403
9708
|
}
|
|
9404
9709
|
try {
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9710
|
+
log.step(`Fetching ${label}...`);
|
|
9711
|
+
const content = await fetchRegistryFile(
|
|
9712
|
+
(ref) => hook ? getHookUrl(file, ref) : getFileUrl(file, ref)
|
|
9713
|
+
);
|
|
9408
9714
|
if (!(0, import_node_fs3.existsSync)(destDir)) {
|
|
9409
9715
|
(0, import_node_fs3.mkdirSync)(destDir, { recursive: true });
|
|
9410
9716
|
}
|
|
9411
|
-
(0, import_node_fs3.writeFileSync)(
|
|
9412
|
-
log.success(` Added \u2192 ${
|
|
9717
|
+
(0, import_node_fs3.writeFileSync)(dest, content, "utf-8");
|
|
9718
|
+
log.success(` Added \u2192 ${label}`);
|
|
9413
9719
|
} catch (err) {
|
|
9414
9720
|
const message = err instanceof Error ? err.message : String(err);
|
|
9415
|
-
log.error(` Failed to fetch ${
|
|
9721
|
+
log.error(` Failed to fetch ${label}: ${message}`);
|
|
9416
9722
|
componentSuccess = false;
|
|
9417
9723
|
failed.push(entry.displayName);
|
|
9418
9724
|
}
|
|
@@ -9434,22 +9740,21 @@ async function runAdd(components, cwd2, opts) {
|
|
|
9434
9740
|
if (failed.length > 0) {
|
|
9435
9741
|
log.error(`Failed: ${failed.join(", ")}`);
|
|
9436
9742
|
}
|
|
9743
|
+
if (added.length > 0) {
|
|
9744
|
+
for (const dep of BASE_DEPS) {
|
|
9745
|
+
depsToInstall.add(dep);
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9437
9748
|
if (depsToInstall.size > 0) {
|
|
9438
9749
|
const deps = Array.from(depsToInstall).join(" ");
|
|
9439
9750
|
log.blank();
|
|
9440
|
-
console.log(source_default.bold.yellow(" \u26A1 Install
|
|
9751
|
+
console.log(source_default.bold.yellow(" \u26A1 Install dependencies:\n"));
|
|
9441
9752
|
console.log(source_default.cyan(` npm install ${deps}`) + "\n");
|
|
9442
9753
|
}
|
|
9443
|
-
if (added.length > 0) {
|
|
9754
|
+
if (added.length > 0 && !(0, import_node_fs3.existsSync)((0, import_node_path3.join)(projectBase, "lib", "utils.ts"))) {
|
|
9444
9755
|
log.blank();
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
" Components require the cn() utility. Add to your project:\n"
|
|
9448
|
-
)
|
|
9449
|
-
);
|
|
9450
|
-
console.log(source_default.white(" import { cn } from 'reend-components'") + "\n");
|
|
9451
|
-
console.log(
|
|
9452
|
-
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."
|
|
9453
9758
|
);
|
|
9454
9759
|
}
|
|
9455
9760
|
}
|
|
@@ -9503,10 +9808,9 @@ async function runUpdate(components, cwd2) {
|
|
|
9503
9808
|
const outputDir = (0, import_node_path4.join)(cwd2, config.outputDir);
|
|
9504
9809
|
let targets = components;
|
|
9505
9810
|
if (targets.length === 0) {
|
|
9506
|
-
targets = Object.keys(REGISTRY).filter(
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
});
|
|
9811
|
+
targets = Object.keys(REGISTRY).filter(
|
|
9812
|
+
(name) => (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, REGISTRY[name].files[0]))
|
|
9813
|
+
);
|
|
9510
9814
|
if (targets.length === 0) {
|
|
9511
9815
|
log.warn("No installed components found. Use: npx reend-ui add <name>");
|
|
9512
9816
|
return;
|
|
@@ -9523,7 +9827,7 @@ async function runUpdate(components, cwd2) {
|
|
|
9523
9827
|
log.error(`Unknown component: "${name}"`);
|
|
9524
9828
|
continue;
|
|
9525
9829
|
}
|
|
9526
|
-
const isInstalled =
|
|
9830
|
+
const isInstalled = (0, import_node_fs4.existsSync)((0, import_node_path4.join)(outputDir, entry.files[0]));
|
|
9527
9831
|
if (isInstalled) {
|
|
9528
9832
|
existing.push(name);
|
|
9529
9833
|
} else {
|
|
@@ -9547,7 +9851,7 @@ program2.name("reend-ui").description(
|
|
|
9547
9851
|
source_default.bold.white(
|
|
9548
9852
|
"\u25C6 ReEnd UI \u2014 Arknights: Endfield React Component Library\n"
|
|
9549
9853
|
) + source_default.dim(" Shadcn-style copy-paste workflow for ReEnd components.\n")
|
|
9550
|
-
).version(
|
|
9854
|
+
).version(CLI_VERSION);
|
|
9551
9855
|
program2.command("init").description("Initialize ReEnd UI in your project").action(async () => {
|
|
9552
9856
|
await runInit(cwd);
|
|
9553
9857
|
});
|