dithercharts 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # dithercharts
4
4
 
5
- The CLI for [DitherCharts](https://dithercharts.com), dithered chart components for React. It copies chart source into your React app, so you own and can edit every line.
5
+ The CLI for [DitherCharts](https://dithercharts.com), dithered chart and form control components for React. It copies their source into your React app, so you own and can edit every line.
6
6
 
7
7
  You need a DitherCharts Pro subscription, Node 20 or later, and a React 19 app with TypeScript. Tailwind and shadcn/ui are optional.
8
8
 
@@ -14,13 +14,13 @@ Log in once per machine. Your browser opens to confirm a code.
14
14
  npx dithercharts login
15
15
  ```
16
16
 
17
- Then add charts from your app's folder:
17
+ Then add charts and controls from your app's folder:
18
18
 
19
19
  ```sh
20
- npx dithercharts add area-chart
20
+ npx dithercharts add area-chart select
21
21
  ```
22
22
 
23
- Files land in `src/components/dithercharts/` (or `components/dithercharts/` without a `src` folder), and the CLI installs the chart's npm dependencies with your package manager.
23
+ Files land in `src/components/dithercharts/` (or `components/dithercharts/` without a `src` folder), and the CLI installs any npm dependencies with your package manager. Run `npx dithercharts list` for every name.
24
24
 
25
25
  ## Commands
26
26
 
@@ -29,10 +29,10 @@ Files land in `src/components/dithercharts/` (or `components/dithercharts/` with
29
29
  | `login` | Sign in through the browser and save a key |
30
30
  | `logout` | Revoke this machine's key and forget it |
31
31
  | `whoami` | Show the signed-in account and plan |
32
- | `list` | List the charts and what's installed here |
33
- | `add <names...>` | Install charts into this project |
32
+ | `list` | List the charts and controls, and what's installed here |
33
+ | `add <names...>` | Install charts and controls into this project |
34
34
 
35
- `add` options: `--all`, `--dir <path>`, `--yes`, `--overwrite`, `--no-install`.
35
+ `add` options: `--all`, `--dir <path>`, `--yes`, `--overwrite`, `--no-install`. `--yes` asks nothing, so AI agents should pass it: it takes the default folder and keeps files you changed.
36
36
 
37
37
  In CI, set `DITHERCHARTS_API_KEY` instead of running `login`.
38
38
 
package/dist/cli.js CHANGED
@@ -18,11 +18,11 @@ Commands:
18
18
  login Sign in through the browser and save a key
19
19
  logout Revoke this machine's key and forget it
20
20
  whoami Show the signed-in account and plan
21
- list List the charts and what's installed here
22
- add <names...> Install charts into this project
23
- --all Install every chart
21
+ list List the charts and controls, and what's installed here
22
+ add <names...> Install charts and controls into this project
23
+ --all Install every chart and control
24
24
  --dir <path> Folder to install into
25
- --yes, -y Accept the default folder
25
+ --yes, -y Ask nothing: use the default folder, keep local changes
26
26
  --overwrite Replace files you changed locally
27
27
  --no-install Print the dependency install command instead of running it
28
28
 
@@ -31,7 +31,7 @@ Environment:
31
31
  DITHERCHARTS_URL Server to use (default ${DEFAULT_BASE_URL})`;
32
32
  const version = () => JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
33
33
  function welcome() {
34
- const side = { 4: "Welcome to DitherCharts", 5: `v${version()} · dithered charts for React` };
34
+ const side = { 4: "Welcome to DitherCharts", 5: `v${version()} · dithered charts and controls for React` };
35
35
  return LOGO.split("\n").map((line, i) => (side[i] ? `${line.padEnd(20)}${side[i]}` : line)).join("\n");
36
36
  }
37
37
  function runCommand(cmd, cwd) {
@@ -32,7 +32,7 @@ export async function add(opts, deps) {
32
32
  const items = await Promise.all(resolveOrder(index, names).map((name) => deps.api.item(name)));
33
33
  const results = await applyFiles(planFiles(join(root, dir), items), {
34
34
  overwrite: opts.overwrite,
35
- ask: deps.interactive ? deps.askOverwrite : null,
35
+ ask: deps.interactive && !opts.yes ? deps.askOverwrite : null,
36
36
  });
37
37
  for (const r of results) {
38
38
  deps.log(`${MARK[r.result]} ${relative(root, r.path)}${r.result === "skipped" ? " (local changes kept; --overwrite replaces)" : ""}`);
@@ -55,7 +55,7 @@ export async function add(opts, deps) {
55
55
  deps.log(`$ ${cmd.join(" ")}`);
56
56
  deps.run(cmd, root);
57
57
  }
58
- const base = importBase(dir);
58
+ const base = importBase(root, dir);
59
59
  deps.log("");
60
60
  for (const [owner, sheet] of STYLESHEETS) {
61
61
  if (items.some((i) => i.name === owner))
@@ -3,10 +3,24 @@ export async function list(deps) {
3
3
  const index = await deps.api.index();
4
4
  const root = findProjectRoot(deps.cwd);
5
5
  const installed = (root && readConfig(root)?.items) || {};
6
- for (const item of index.items) {
7
- const have = installed[item.name];
8
- const note = !have ? "" : have === index.version ? ` [installed ${have}]` : ` [installed ${have}, update: ${index.version}]`;
9
- deps.log(`${item.name.padEnd(14)} ${item.description}${note}`);
10
- }
6
+ // Items others depend on (engine, core, controls) come along with them, so they're listed apart.
7
+ const shared = new Set(index.items.flatMap((item) => item.registryDependencies));
8
+ const mine = index.items.filter((item) => !shared.has(item.name));
9
+ const isControl = (item) => item.registryDependencies.includes("controls");
10
+ const groups = [
11
+ ["Charts", mine.filter((item) => !isControl(item))],
12
+ ["Form controls", mine.filter(isControl)],
13
+ ["Shared (add installs these for you)", index.items.filter((item) => shared.has(item.name))],
14
+ ];
15
+ groups.forEach(([heading, items], i) => {
16
+ if (i > 0)
17
+ deps.log("");
18
+ deps.log(heading);
19
+ for (const item of items) {
20
+ const have = installed[item.name];
21
+ const note = !have ? "" : have === index.version ? ` [installed ${have}]` : ` [installed ${have}, update: ${index.version}]`;
22
+ deps.log(` ${item.name.padEnd(14)} ${item.description}${note}`);
23
+ }
24
+ });
11
25
  return 0;
12
26
  }
package/dist/errors.js CHANGED
@@ -25,6 +25,6 @@ export function messageFor(status, body, base, name) {
25
25
  if (status === 403)
26
26
  return "This key can't install charts. Log in with `npx dithercharts login` to get a personal key.";
27
27
  if (status === 404)
28
- return name ? `No chart named "${name}". Run \`npx dithercharts list\`.` : "Not found on the server.";
28
+ return name ? `No chart or control named "${name}". Run \`npx dithercharts list\`.` : "Not found on the server.";
29
29
  return `The server answered ${status}. Nothing was written; try again.`;
30
30
  }
package/dist/install.js CHANGED
@@ -8,7 +8,7 @@ export function resolveOrder(index, names) {
8
8
  const visit = (name) => {
9
9
  const item = byName.get(name);
10
10
  if (!item)
11
- throw new CliError(`No chart named "${name}". Run \`npx dithercharts list\`.`);
11
+ throw new CliError(`No chart or control named "${name}". Run \`npx dithercharts list\`.`);
12
12
  if (order.includes(name))
13
13
  return;
14
14
  item.registryDependencies.forEach(visit);
package/dist/project.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
2
+ import { dirname, join, posix } from "node:path";
3
3
  export const CONFIG_FILE = "dithercharts.json";
4
4
  /** The nearest folder at or above `cwd` with a package.json. */
5
5
  export function findProjectRoot(cwd) {
@@ -46,8 +46,36 @@ export function reactMajor(pkg) {
46
46
  const major = range?.match(/\d+/)?.[0];
47
47
  return major ? Number(major) : null;
48
48
  }
49
- /** How app code imports the folder, assuming the common `@/` alias. */
50
- export const importBase = (dir) => `@/${dir.replace(/^src\//, "")}`;
49
+ // tsconfig files allow comments and trailing commas. Strings are matched first and kept, since "@/*" holds "/*".
50
+ const COMMENT = /("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
51
+ const TRAILING_COMMA = /("(?:\\.|[^"\\])*")|,(?=\s*[}\]])/g;
52
+ function readTsconfig(path) {
53
+ try {
54
+ const text = readFileSync(path, "utf8").replace(COMMENT, (_, str) => str ?? "").replace(TRAILING_COMMA, (_, str) => str ?? "");
55
+ return JSON.parse(text).compilerOptions;
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
61
+ /** How app code imports the folder: through the tsconfig alias that reaches it ("~/" in T3 apps), else `@/`. */
62
+ export function importBase(root, dir) {
63
+ const folder = posix.normalize(dir);
64
+ // Vite apps keep their compiler options in tsconfig.app.json.
65
+ for (const file of ["tsconfig.json", "tsconfig.app.json"]) {
66
+ const options = readTsconfig(join(root, file));
67
+ for (const [alias, [target] = []] of Object.entries(options?.paths ?? {})) {
68
+ if (!alias.endsWith("/*") || (!target?.endsWith("/*") && target !== "*"))
69
+ continue;
70
+ const base = posix.normalize(posix.join(options?.baseUrl ?? ".", target.slice(0, -1))).replace(/\/$/, "");
71
+ if (base === ".")
72
+ return alias.slice(0, -1) + folder;
73
+ if (folder.startsWith(`${base}/`))
74
+ return alias.slice(0, -1) + folder.slice(base.length + 1);
75
+ }
76
+ }
77
+ return `@/${folder.replace(/^src\//, "")}`;
78
+ }
51
79
  /** "area-chart" → "AreaChart". */
52
80
  export const componentName = (item) => item
53
81
  .split("-")
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dithercharts",
3
- "version": "0.1.3",
4
- "description": "Install DitherCharts Pro charts into your React app.",
3
+ "version": "0.1.5",
4
+ "description": "Install DitherCharts Pro charts and form controls into your React app.",
5
5
  "keywords": [
6
6
  "dithercharts",
7
7
  "charts",
Binary file