truecanvas 0.4.0 → 0.4.1

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeComponents
3
- } from "./chunk-WU7NBTJH.js";
3
+ } from "./chunk-2IIAMOFH.js";
4
4
 
5
5
  // src/core/catalog-worker.ts
6
6
  import { parentPort, workerData } from "worker_threads";
@@ -86,19 +86,27 @@ async function analyzeComponents(root, globs, libraries = []) {
86
86
  if (!sf) continue;
87
87
  const moduleSymbol = checker.getSymbolAtLocation(sf);
88
88
  if (!moduleSymbol) continue;
89
+ const named = /* @__PURE__ */ new Set();
90
+ const found = [];
89
91
  for (let sym of checker.getExportsOfModule(moduleSymbol)) {
92
+ const exported = sym.getName();
90
93
  if (sym.flags & ts.SymbolFlags.Alias) sym = checker.getAliasedSymbol(sym);
91
- const name = sym.getName();
92
- if (!/^[A-Z]/.test(name) || !(sym.flags & ts.SymbolFlags.Value)) continue;
93
94
  const decl = sym.valueDeclaration ?? sym.declarations?.[0];
94
95
  if (!decl) continue;
96
+ const own = decl.name;
97
+ const name = sym.getName() === "default" && own && ts.isIdentifier(own) ? own.text : sym.getName();
98
+ if (!/^[A-Z]/.test(name) || !(sym.flags & ts.SymbolFlags.Value)) continue;
95
99
  const type = checker.getTypeOfSymbolAtLocation(sym, decl);
96
100
  const sig = type.getCallSignatures()[0];
97
101
  if (!sig) continue;
98
102
  const ret = checker.typeToString(sig.getReturnType());
99
103
  if (!/Element|ReactNode|ReactPortal|null|any/.test(ret) || /^Promise</.test(ret)) continue;
100
- out.push(describe(ts, checker, sym, decl, sig, library ?? posix(path2.relative(root, file)), library));
104
+ const spec = { ...describe(ts, checker, sym, decl, sig, library ?? posix(path2.relative(root, file)), library), name };
105
+ if (exported === "default") spec.defaultExport = true;
106
+ else named.add(spec.name);
107
+ found.push(spec);
101
108
  }
109
+ out.push(...found.filter((s) => !(s.defaultExport && named.has(s.name))));
102
110
  }
103
111
  for (const spec of out) applyProfile(spec);
104
112
  if (libraries.length) await attachPresets(root, out);
@@ -12,7 +12,7 @@ import {
12
12
  importSpecifier,
13
13
  posix,
14
14
  resolveAliasPath
15
- } from "./chunk-WU7NBTJH.js";
15
+ } from "./chunk-2IIAMOFH.js";
16
16
  import {
17
17
  ICON_LIBRARIES,
18
18
  iconLibrary,
@@ -549,10 +549,14 @@ ${ind}${close}`);
549
549
  this.selectPaths.push(node.path);
550
550
  }
551
551
  added = /* @__PURE__ */ new Set();
552
- ensureImport(name, from) {
552
+ ensureImport(name, from, isDefault = false) {
553
553
  if (this.doc.imports.some((i) => i.names.includes(name) || i.defaultName === name)) return;
554
554
  if (this.added.has(name)) return;
555
555
  this.added.add(name);
556
+ if (isDefault) {
557
+ this.addImportLine(`import ${name} from ${JSON.stringify(from)};`);
558
+ return;
559
+ }
556
560
  const same = this.doc.imports.find((i) => i.source === from && !i.typeOnly && i.lastSpecifierEnd !== null);
557
561
  if (same) {
558
562
  this.s.appendLeft(same.lastSpecifierEnd, `, ${name}`);
@@ -954,6 +958,7 @@ function componentRegistry(config, specs) {
954
958
  from = posix(path.relative(routeDir, path.join(config.root, c.file))).replace(/\.(tsx|ts|jsx|js)$/, "");
955
959
  if (!from.startsWith(".")) from = `./${from}`;
956
960
  }
961
+ if (c.defaultExport) return ` ${JSON.stringify(c.name)}: lazy(() => import(${JSON.stringify(from)})),`;
957
962
  return ` ${JSON.stringify(c.name)}: lazy(() => import(${JSON.stringify(from)}).then((m) => ({ default: m[${JSON.stringify(c.name)}] }))),`;
958
963
  });
959
964
  return `"use client";
@@ -2757,7 +2762,7 @@ var Workspace = class {
2757
2762
  const spec = this.catalog.get(cmd.component);
2758
2763
  if (!spec) throw new EditError(`Unknown component "${cmd.component}". Use list_components.`);
2759
2764
  const jsx = componentJsx(spec, cmd.props, cmd.text);
2760
- ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file));
2765
+ ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file), spec.defaultExport);
2761
2766
  ed.insertChild(parent, cmd.index, jsx);
2762
2767
  return `Inserted ${spec.name}`;
2763
2768
  }
@@ -2893,7 +2898,7 @@ var Workspace = class {
2893
2898
  const preset = cmd.preset ? spec.presets?.find((p) => p.name.toLowerCase() === cmd.preset.toLowerCase()) : void 0;
2894
2899
  if (cmd.preset && !preset) throw new EditError(`${spec.name} has no preset "${cmd.preset}". Presets: ${spec.presets?.map((p) => p.name).join(", ") || "none"}`);
2895
2900
  const props = { className: "pointer-events-none absolute inset-0 -z-10 h-full w-full", ...preset ? presetDiff(spec, preset.props) : {} };
2896
- ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file));
2901
+ ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file), spec.defaultExport);
2897
2902
  if (parent.kind === "element") {
2898
2903
  const cls = parent.props.className;
2899
2904
  if (cls === void 0 || cls.kind === "string") {
@@ -2924,7 +2929,7 @@ var Workspace = class {
2924
2929
  if (!prop) throw new EditError(`${spec.name} has no prop "${cmd.prop}".`);
2925
2930
  const values = prop.type === "enum" ? prop.options : prop.type === "boolean" ? [false, true] : [];
2926
2931
  if (!values.length) throw new EditError(`Prop "${cmd.prop}" is ${prop.typeText}; variants need a union of string literals or a boolean.`);
2927
- ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file));
2932
+ ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file), spec.defaultExport);
2928
2933
  const label = (v) => typeof v === "string" ? v[0].toUpperCase() + v.slice(1) : `${prop.name}: ${v}`;
2929
2934
  const rows = indentBlock(values.map((v) => componentJsx(spec, { ...cmd.props, [prop.name]: v }, spec.acceptsChildren ? label(v) : void 0)).join("\n"));
2930
2935
  const right = rightEdge(doc);
@@ -2943,7 +2948,7 @@ ${rows}
2943
2948
  ed.selectPaths.push(existing.path);
2944
2949
  return `Opened ${spec.name}`;
2945
2950
  }
2946
- ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file));
2951
+ ed.ensureImport(spec.name, importSpecifier(this.config.root, rel, spec.file), spec.defaultExport);
2947
2952
  const right = rightEdge(doc);
2948
2953
  const top = doc.frames.length ? Math.min(...doc.frames.map((f) => f.y)) : 0;
2949
2954
  ed.insertFrame(
@@ -3083,7 +3088,7 @@ ${rows}
3083
3088
  if (ed.doc.imports.some((i) => i.names.includes(name) || i.defaultName === name)) continue;
3084
3089
  const spec = this.catalog.get(name);
3085
3090
  if (!spec) throw new EditError(`Unknown component <${name}>. It is not imported in the canvas and not in the component catalog. Use list_components.`);
3086
- ed.ensureImport(name, importSpecifier(this.config.root, rel, spec.file));
3091
+ ed.ensureImport(name, importSpecifier(this.config.root, rel, spec.file), spec.defaultExport);
3087
3092
  }
3088
3093
  }
3089
3094
  cleanCache = /* @__PURE__ */ new Map();
package/dist/cli.js CHANGED
@@ -32,11 +32,11 @@ import {
32
32
  syncComponentRegistry,
33
33
  toolEnv,
34
34
  writeCompare
35
- } from "./chunk-OVYSWA2F.js";
35
+ } from "./chunk-A4QIAVTG.js";
36
36
  import {
37
37
  loadConfig
38
38
  } from "./chunk-PBKX3SBC.js";
39
- import "./chunk-WU7NBTJH.js";
39
+ import "./chunk-2IIAMOFH.js";
40
40
  import {
41
41
  ICON_LIBRARIES,
42
42
  iconLibraries,
@@ -139,6 +139,8 @@ interface ComponentSpec {
139
139
  props: PropSpec[];
140
140
  acceptsClassName: boolean;
141
141
  acceptsChildren: boolean;
142
+ /** exported as the module's default (`export default Hero`): imported as `import Hero from` */
143
+ defaultExport?: boolean;
142
144
  }
143
145
 
144
146
  declare function parseCanvas(name: string, file: string, source: string): CanvasDoc;
@@ -215,7 +217,7 @@ declare class CanvasEditor {
215
217
  unwrap(node: CanvasNode): void;
216
218
  replace(node: CanvasNode, jsx: string): void;
217
219
  private added;
218
- ensureImport(name: string, from: string): void;
220
+ ensureImport(name: string, from: string, isDefault?: boolean): void;
219
221
  /**
220
222
  * Imports `name` from `from` and returns the local name to write in JSX. When
221
223
  * the name is already taken by something else (another import, a local
@@ -28,7 +28,7 @@ import {
28
28
  viteStylesheets,
29
29
  wrapConfigExport,
30
30
  writeCompare
31
- } from "../chunk-OVYSWA2F.js";
31
+ } from "../chunk-A4QIAVTG.js";
32
32
  import {
33
33
  injectIds
34
34
  } from "../chunk-TSHD2S36.js";
@@ -37,7 +37,7 @@ import {
37
37
  } from "../chunk-PBKX3SBC.js";
38
38
  import {
39
39
  Catalog
40
- } from "../chunk-WU7NBTJH.js";
40
+ } from "../chunk-2IIAMOFH.js";
41
41
  import {
42
42
  ICON_LIBRARIES,
43
43
  iconLibraries,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "truecanvas",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Design with your real React components: a Figma-like canvas for Next.js and Vite where layers are your .tsx and agents edit through MCP.",
5
5
  "license": "MIT",
6
6
  "type": "module",