@madebywild/wvk 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Wireframe kit — accessible React components, 183 icons, and design tokens that map 1:1 to the wild Wireframe Kit Figma library.
4
4
 
5
+ ## Quick start (new project)
6
+
7
+ Scaffold a fresh Next.js 16 / React 19 / Tailwind v4 app that ships with `@madebywild/wvk` already wired up — `ThemeProvider`, design tokens, the package stylesheet, and a small `Header` composition:
8
+
9
+ ```bash
10
+ npx @madebywild/wvk create-next-app my-wvk-app
11
+ cd my-wvk-app
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
16
+ Then open [http://localhost:3000](http://localhost:3000). The scaffold also writes an `AGENTS.md` so your agent harness (Claude Code, Cursor, Copilot, etc.) picks up the wvk authoring rules — pass `--no-agents` to skip that, or `--force` to scaffold into a non-empty directory.
17
+
5
18
  ## Install
6
19
 
7
20
  ```bash
package/bin/wvk.mjs CHANGED
@@ -1,19 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { createHash } from "node:crypto";
3
3
  import {
4
+ cpSync,
4
5
  existsSync,
5
6
  mkdirSync,
6
7
  readFileSync,
8
+ readdirSync,
9
+ renameSync,
7
10
  statSync,
11
+ unlinkSync,
8
12
  writeFileSync,
9
13
  } from "node:fs";
10
- import { dirname, relative, resolve } from "node:path";
14
+ import { basename, dirname, join, relative, resolve } from "node:path";
11
15
  import { fileURLToPath } from "node:url";
12
16
 
13
17
  const PKG_NAME = "@madebywild/wvk";
14
18
  const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
15
19
  const PKG_ROOT = resolve(SCRIPT_DIR, "..");
16
20
  const SOURCE = resolve(PKG_ROOT, "AGENTS.md");
21
+ const TEMPLATES_ROOT = resolve(PKG_ROOT, "templates");
17
22
  const PKG_JSON = JSON.parse(
18
23
  readFileSync(resolve(PKG_ROOT, "package.json"), "utf8"),
19
24
  );
@@ -25,6 +30,9 @@ const BEGIN_RE = new RegExp(
25
30
  );
26
31
  const END_MARK = `<!-- END ${PKG_NAME} AGENTS.md -->`;
27
32
 
33
+ // Valid npm package name per https://github.com/npm/validate-npm-package-name
34
+ const NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
35
+
28
36
  function escapeRegex(s) {
29
37
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30
38
  }
@@ -33,19 +41,28 @@ function usage() {
33
41
  return `wvk — ${PKG_NAME} CLI
34
42
 
35
43
  Usage:
44
+ wvk create-next-app <name> [--force] [--no-agents]
36
45
  wvk copy-harness [--force] [--append] [--out <path>]
37
46
  wvk --help
38
47
  wvk --version
39
48
 
40
49
  Commands:
41
- copy-harness Copy this package's AGENTS.md into your project so your
42
- agent harness picks up the wvk authoring rules.
50
+ create-next-app Scaffold a new Next.js 16 / React 19 app preconfigured with
51
+ ${PKG_NAME}, Tailwind v4, ThemeProvider, and a Header. Drops
52
+ the package's AGENTS.md into the new project so a consumer's
53
+ agent harness picks up the wvk authoring rules.
54
+ copy-harness Copy this package's AGENTS.md into your project so your
55
+ agent harness picks up the wvk authoring rules.
43
56
 
44
57
  Options:
45
- --out <path> Destination path (default: ./AGENTS.md). Parent dirs are created.
46
- --force Overwrite the destination if it exists. Mutually exclusive with --append.
47
- --append Merge the rules into the destination as a delimited, idempotent block.
48
- Re-running is safe: the block is replaced in place, never duplicated.
58
+ --out <path> (copy-harness) Destination path (default: ./AGENTS.md). Parent dirs are created.
59
+ --force Overwrite an existing destination. For create-next-app, allow
60
+ scaffolding into an existing non-empty directory.
61
+ Mutually exclusive with --append on copy-harness.
62
+ --append (copy-harness) Merge the rules into the destination as a
63
+ delimited, idempotent block. Re-running is safe: the block is
64
+ replaced in place, never duplicated.
65
+ --no-agents (create-next-app) Skip writing AGENTS.md into the new project.
49
66
  `;
50
67
  }
51
68
 
@@ -55,11 +72,12 @@ function fail(msg, code = 1) {
55
72
  }
56
73
 
57
74
  function parseArgs(argv) {
58
- const args = { _: [], force: false, append: false, out: null };
75
+ const args = { _: [], force: false, append: false, out: null, noAgents: false };
59
76
  for (let i = 0; i < argv.length; i++) {
60
77
  const a = argv[i];
61
78
  if (a === "--force") args.force = true;
62
79
  else if (a === "--append") args.append = true;
80
+ else if (a === "--no-agents") args.noAgents = true;
63
81
  else if (a === "--out") {
64
82
  const v = argv[++i];
65
83
  if (!v) fail("--out requires a path");
@@ -172,6 +190,110 @@ function report(destRel, mode) {
172
190
  }
173
191
  }
174
192
 
193
+ function isDirEmpty(dir) {
194
+ try {
195
+ return readdirSync(dir).length === 0;
196
+ } catch {
197
+ return true;
198
+ }
199
+ }
200
+
201
+ function createNextApp(args) {
202
+ if (args.append) {
203
+ fail("--append is not supported for create-next-app");
204
+ }
205
+ const name = args._[1];
206
+ if (!name) {
207
+ fail("create-next-app requires a project name. Example: wvk create-next-app my-app");
208
+ }
209
+ if (!NAME_RE.test(name) && name !== ".") {
210
+ fail(
211
+ `invalid project name: ${name}\n` +
212
+ `wvk: must be a valid npm package name (lowercase, no spaces, may start with @scope/).`,
213
+ );
214
+ }
215
+ const templateRoot = resolve(TEMPLATES_ROOT, "next-app");
216
+ if (!existsSync(templateRoot)) {
217
+ fail(`scaffolding template missing in package at ${templateRoot}`);
218
+ }
219
+
220
+ const targetDir =
221
+ name === "." ? process.cwd() : resolve(process.cwd(), name);
222
+ const targetRel = relative(process.cwd(), targetDir) || ".";
223
+ const projectName = name === "." ? basename(targetDir) : name;
224
+ if (!NAME_RE.test(projectName)) {
225
+ fail(
226
+ `derived project name "${projectName}" is not a valid npm package name. ` +
227
+ `Pass an explicit name: wvk create-next-app <name>`,
228
+ );
229
+ }
230
+
231
+ if (existsSync(targetDir)) {
232
+ if (!statSync(targetDir).isDirectory()) {
233
+ fail(`${targetRel} exists and is not a directory`);
234
+ }
235
+ if (!isDirEmpty(targetDir) && !args.force) {
236
+ fail(
237
+ `${targetRel} is not empty. Re-run with --force to scaffold into it anyway ` +
238
+ `(existing files with the same name will be overwritten).`,
239
+ );
240
+ }
241
+ } else {
242
+ mkdirSync(targetDir, { recursive: true });
243
+ }
244
+
245
+ // Copy every file from the template tree. cpSync with recursive copies
246
+ // directories without descending into anything that doesn't exist.
247
+ cpSync(templateRoot, targetDir, { recursive: true, force: true });
248
+
249
+ // npm strips a literal `.gitignore` from packed tarballs (it's renamed to
250
+ // `.npmignore`). The template ships it as `gitignore`; rename on copy. If
251
+ // the caller already had a `.gitignore` (only possible with --force), keep
252
+ // theirs and drop ours so we don't leave an orphan `gitignore` lying around.
253
+ const giSrc = join(targetDir, "gitignore");
254
+ const giDst = join(targetDir, ".gitignore");
255
+ if (existsSync(giSrc)) {
256
+ if (existsSync(giDst)) {
257
+ unlinkSync(giSrc);
258
+ } else {
259
+ renameSync(giSrc, giDst);
260
+ }
261
+ }
262
+
263
+ // Set the `name` field in the new app's package.json to the requested name.
264
+ const targetPkgPath = join(targetDir, "package.json");
265
+ if (existsSync(targetPkgPath)) {
266
+ const pkg = JSON.parse(readFileSync(targetPkgPath, "utf8"));
267
+ pkg.name = projectName;
268
+ writeFileSync(targetPkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
269
+ }
270
+
271
+ // Drop the package's AGENTS.md into the project so the consumer's agent
272
+ // harness picks up the wvk authoring rules. Sourced from PKG_ROOT (same
273
+ // file `wvk copy-harness` writes), not from the template, so the two
274
+ // never drift. Opt out with --no-agents.
275
+ if (!args.noAgents) {
276
+ if (!existsSync(SOURCE)) {
277
+ fail(`source AGENTS.md missing in package at ${SOURCE}`);
278
+ }
279
+ const agentsBody = readFileSync(SOURCE, "utf8");
280
+ writeFileSync(join(targetDir, "AGENTS.md"), agentsBody);
281
+ }
282
+
283
+ process.stdout.write(`wvk: scaffolded ${PKG_NAME} Next.js app in ${targetRel}\n`);
284
+ if (!args.noAgents) {
285
+ process.stdout.write(
286
+ `wvk: wrote AGENTS.md so your agent harness picks up the wvk authoring rules\n`,
287
+ );
288
+ }
289
+ process.stdout.write(`wvk: next steps:\n`);
290
+ if (targetRel !== ".") {
291
+ process.stdout.write(` cd ${targetRel}\n`);
292
+ }
293
+ process.stdout.write(` npm install # or pnpm install\n`);
294
+ process.stdout.write(` npm run dev # then open http://localhost:3000\n`);
295
+ }
296
+
175
297
  function main() {
176
298
  const args = parseArgs(process.argv.slice(2));
177
299
  if (args.help) {
@@ -191,6 +313,10 @@ function main() {
191
313
  copyHarness(args);
192
314
  return;
193
315
  }
316
+ if (cmd === "create-next-app") {
317
+ createNextApp(args);
318
+ return;
319
+ }
194
320
  process.stderr.write(`wvk: unknown command: ${cmd}\n\n${usage()}`);
195
321
  process.exit(1);
196
322
  }
package/dist/index.cjs CHANGED
@@ -4958,7 +4958,7 @@ BottomTabBarItem.displayName = "BottomTabBarItem";
4958
4958
 
4959
4959
  // src/components/ui/button.tsx
4960
4960
  var React3 = __toESM(require("react"), 1);
4961
- var import_use_render = require("@base-ui-components/react/use-render");
4961
+ var import_use_render = require("@base-ui/react/use-render");
4962
4962
  var import_class_variance_authority2 = require("class-variance-authority");
4963
4963
  var import_jsx_runtime186 = require("react/jsx-runtime");
4964
4964
  var buttonVariants = (0, import_class_variance_authority2.cva)(
@@ -5045,7 +5045,7 @@ Button.displayName = "Button";
5045
5045
 
5046
5046
  // src/components/ui/checkbox.tsx
5047
5047
  var React4 = __toESM(require("react"), 1);
5048
- var import_checkbox = require("@base-ui-components/react/checkbox");
5048
+ var import_checkbox = require("@base-ui/react/checkbox");
5049
5049
  var import_framer_motion2 = require("framer-motion");
5050
5050
  var import_class_variance_authority3 = require("class-variance-authority");
5051
5051
  var import_jsx_runtime187 = require("react/jsx-runtime");
@@ -5153,7 +5153,7 @@ var React6 = __toESM(require("react"), 1);
5153
5153
 
5154
5154
  // src/components/ui/tooltip.tsx
5155
5155
  var React5 = __toESM(require("react"), 1);
5156
- var import_tooltip = require("@base-ui-components/react/tooltip");
5156
+ var import_tooltip = require("@base-ui/react/tooltip");
5157
5157
  var import_class_variance_authority4 = require("class-variance-authority");
5158
5158
  var import_jsx_runtime188 = require("react/jsx-runtime");
5159
5159
  var tooltipVariants = (0, import_class_variance_authority4.cva)("relative rounded-[var(--radius-md)] w-max", {
@@ -5772,7 +5772,7 @@ VideoControls.displayName = "VideoControls";
5772
5772
 
5773
5773
  // src/components/ui/menu.tsx
5774
5774
  var React12 = __toESM(require("react"), 1);
5775
- var import_menu = require("@base-ui-components/react/menu");
5775
+ var import_menu = require("@base-ui/react/menu");
5776
5776
  var import_jsx_runtime196 = require("react/jsx-runtime");
5777
5777
  var Menu2 = import_menu.Menu.Root;
5778
5778
  var MenuTrigger = import_menu.Menu.Trigger;
@@ -5876,8 +5876,8 @@ Pagination.displayName = "Pagination";
5876
5876
 
5877
5877
  // src/components/ui/progress-bar.tsx
5878
5878
  var React14 = __toESM(require("react"), 1);
5879
- var import_progress = require("@base-ui-components/react/progress");
5880
- var import_meter = require("@base-ui-components/react/meter");
5879
+ var import_progress = require("@base-ui/react/progress");
5880
+ var import_meter = require("@base-ui/react/meter");
5881
5881
  var import_jsx_runtime198 = require("react/jsx-runtime");
5882
5882
  var ProgressBar = React14.forwardRef(
5883
5883
  ({ className, value = 0, ...props }, ref) => {
@@ -5912,8 +5912,8 @@ Meter.displayName = "Meter";
5912
5912
 
5913
5913
  // src/components/ui/radio.tsx
5914
5914
  var React15 = __toESM(require("react"), 1);
5915
- var import_radio = require("@base-ui-components/react/radio");
5916
- var import_radio_group = require("@base-ui-components/react/radio-group");
5915
+ var import_radio = require("@base-ui/react/radio");
5916
+ var import_radio_group = require("@base-ui/react/radio-group");
5917
5917
  var import_framer_motion4 = require("framer-motion");
5918
5918
  var import_class_variance_authority10 = require("class-variance-authority");
5919
5919
  var import_jsx_runtime199 = require("react/jsx-runtime");
@@ -6046,8 +6046,8 @@ RadioGroupItem.displayName = "RadioGroupItem";
6046
6046
 
6047
6047
  // src/components/ui/segmented-control.tsx
6048
6048
  var React16 = __toESM(require("react"), 1);
6049
- var import_toggle_group = require("@base-ui-components/react/toggle-group");
6050
- var import_toggle = require("@base-ui-components/react/toggle");
6049
+ var import_toggle_group = require("@base-ui/react/toggle-group");
6050
+ var import_toggle = require("@base-ui/react/toggle");
6051
6051
  var import_class_variance_authority11 = require("class-variance-authority");
6052
6052
  var import_jsx_runtime200 = require("react/jsx-runtime");
6053
6053
  var controlVariants = (0, import_class_variance_authority11.cva)(
@@ -6226,7 +6226,7 @@ SegmentedControlTrigger.displayName = "SegmentedControlTrigger";
6226
6226
 
6227
6227
  // src/components/ui/select.tsx
6228
6228
  var React17 = __toESM(require("react"), 1);
6229
- var import_select = require("@base-ui-components/react/select");
6229
+ var import_select = require("@base-ui/react/select");
6230
6230
  var import_class_variance_authority12 = require("class-variance-authority");
6231
6231
  var import_jsx_runtime201 = require("react/jsx-runtime");
6232
6232
  var selectShellVariants = (0, import_class_variance_authority12.cva)(
@@ -6520,7 +6520,7 @@ SelectSeparator.displayName = "SelectSeparator";
6520
6520
 
6521
6521
  // src/components/ui/slider.tsx
6522
6522
  var React18 = __toESM(require("react"), 1);
6523
- var import_slider = require("@base-ui-components/react/slider");
6523
+ var import_slider = require("@base-ui/react/slider");
6524
6524
  var import_jsx_runtime202 = require("react/jsx-runtime");
6525
6525
  var thumbClass = "absolute top-1/2 -translate-y-1/2 -translate-x-1/2 w-4 h-4 rounded-[var(--radius-full)] bg-white border-2 border-primary cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background dark:bg-primary-foreground";
6526
6526
  var Slider = React18.forwardRef(
@@ -6738,7 +6738,7 @@ StarRating.displayName = "StarRating";
6738
6738
 
6739
6739
  // src/components/ui/tabs.tsx
6740
6740
  var React21 = __toESM(require("react"), 1);
6741
- var import_tabs = require("@base-ui-components/react/tabs");
6741
+ var import_tabs = require("@base-ui/react/tabs");
6742
6742
  var import_class_variance_authority14 = require("class-variance-authority");
6743
6743
  var import_jsx_runtime205 = require("react/jsx-runtime");
6744
6744
  var tabListVariants = (0, import_class_variance_authority14.cva)(
@@ -6987,7 +6987,7 @@ Tag2.displayName = "Tag";
6987
6987
 
6988
6988
  // src/components/ui/text-area.tsx
6989
6989
  var React23 = __toESM(require("react"), 1);
6990
- var import_field = require("@base-ui-components/react/field");
6990
+ var import_field = require("@base-ui/react/field");
6991
6991
  var import_class_variance_authority16 = require("class-variance-authority");
6992
6992
  var import_jsx_runtime207 = require("react/jsx-runtime");
6993
6993
  var textAreaShellVariants = (0, import_class_variance_authority16.cva)(
@@ -7169,7 +7169,7 @@ TextArea.displayName = "TextArea";
7169
7169
 
7170
7170
  // src/components/ui/text-input.tsx
7171
7171
  var React24 = __toESM(require("react"), 1);
7172
- var import_input = require("@base-ui-components/react/input");
7172
+ var import_input = require("@base-ui/react/input");
7173
7173
  var import_class_variance_authority17 = require("class-variance-authority");
7174
7174
  var import_jsx_runtime208 = require("react/jsx-runtime");
7175
7175
  var textInputShellVariants = (0, import_class_variance_authority17.cva)(
@@ -7311,6 +7311,9 @@ var TextInput = React24.forwardRef(
7311
7311
  "data-wvk-input": "shell",
7312
7312
  style: { cursor: disabled ? "not-allowed" : "var(--wvk-cursor-text)" },
7313
7313
  className: cn(textInputShellVariants({ inputStyle, size }), className),
7314
+ onClick: () => {
7315
+ if (!disabled) innerRef.current?.focus();
7316
+ },
7314
7317
  children: [
7315
7318
  leadingIcon ? /* @__PURE__ */ (0, import_jsx_runtime208.jsx)(
7316
7319
  "span",
@@ -7382,7 +7385,7 @@ TextInput.displayName = "TextInput";
7382
7385
 
7383
7386
  // src/components/ui/toggle-switch.tsx
7384
7387
  var React25 = __toESM(require("react"), 1);
7385
- var import_switch = require("@base-ui-components/react/switch");
7388
+ var import_switch = require("@base-ui/react/switch");
7386
7389
  var import_framer_motion5 = require("framer-motion");
7387
7390
  var import_class_variance_authority18 = require("class-variance-authority");
7388
7391
  var import_jsx_runtime209 = require("react/jsx-runtime");