@designtools/next-plugin 0.1.3 → 0.1.7

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.
@@ -2,7 +2,9 @@
2
2
  import "./chunk-Y6FXYEAI.mjs";
3
3
 
4
4
  // src/codesurface.tsx
5
- import { useEffect, useRef } from "react";
5
+ import { useEffect, useRef, useState, createElement } from "react";
6
+ import { createPortal } from "react-dom";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
6
8
  function CodeSurface() {
7
9
  const stateRef = useRef({
8
10
  selectionMode: false,
@@ -18,6 +20,25 @@ function CodeSurface() {
18
20
  tooltip: null,
19
21
  selectedOverlay: null
20
22
  });
23
+ const [previewComponent, setPreviewComponent] = useState(null);
24
+ const [previewCombinations, setPreviewCombinations] = useState([]);
25
+ const [previewDefaultChildren, setPreviewDefaultChildren] = useState("");
26
+ const [previewError, setPreviewError] = useState(null);
27
+ const [showPreview, setShowPreview] = useState(false);
28
+ const setPreviewRef = useRef({
29
+ setPreviewComponent,
30
+ setPreviewCombinations,
31
+ setPreviewDefaultChildren,
32
+ setPreviewError,
33
+ setShowPreview
34
+ });
35
+ setPreviewRef.current = {
36
+ setPreviewComponent,
37
+ setPreviewCombinations,
38
+ setPreviewDefaultChildren,
39
+ setPreviewError,
40
+ setShowPreview
41
+ };
21
42
  useEffect(() => {
22
43
  const s = stateRef.current;
23
44
  s.highlightOverlay = document.createElement("div");
@@ -501,6 +522,36 @@ function CodeSurface() {
501
522
  "text-transform",
502
523
  "white-space"
503
524
  ];
525
+ function findComponentFiberAbove(el) {
526
+ const fiber = getFiber(el);
527
+ if (!fiber) return null;
528
+ let candidate = fiber.return;
529
+ while (candidate) {
530
+ const tag = candidate.tag;
531
+ if (tag === 0 || tag === 1 || tag === 11 || tag === 14 || tag === 15) {
532
+ if (findOwnHostElement(candidate) === el) return candidate;
533
+ }
534
+ candidate = candidate.return;
535
+ }
536
+ return null;
537
+ }
538
+ function extractFiberProps(fiber) {
539
+ const props = fiber?.memoizedProps;
540
+ if (!props || typeof props !== "object") return null;
541
+ const skipKeys = /* @__PURE__ */ new Set(["children", "ref", "key", "className", "style"]);
542
+ const result = {};
543
+ let count = 0;
544
+ for (const k of Object.keys(props)) {
545
+ if (skipKeys.has(k)) continue;
546
+ if (k.startsWith("data-")) continue;
547
+ const v = props[k];
548
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
549
+ result[k] = v;
550
+ count++;
551
+ }
552
+ }
553
+ return count > 0 ? result : null;
554
+ }
504
555
  function extractElementData(el) {
505
556
  const computed = getComputedStyle(el);
506
557
  const rect = el.getBoundingClientRect();
@@ -539,8 +590,9 @@ function CodeSurface() {
539
590
  let instanceSourceLine = null;
540
591
  let instanceSourceCol = null;
541
592
  let componentName = null;
593
+ const dataSlot = el.getAttribute("data-slot");
542
594
  const instanceSource = el.getAttribute("data-instance-source");
543
- if (instanceSource && el.getAttribute("data-slot")) {
595
+ if (instanceSource && dataSlot) {
544
596
  const lc = instanceSource.lastIndexOf(":");
545
597
  const slc = instanceSource.lastIndexOf(":", lc - 1);
546
598
  if (slc > 0) {
@@ -548,8 +600,14 @@ function CodeSurface() {
548
600
  instanceSourceLine = parseInt(instanceSource.slice(slc + 1, lc), 10);
549
601
  instanceSourceCol = parseInt(instanceSource.slice(lc + 1), 10);
550
602
  }
551
- const slot = el.getAttribute("data-slot") || "";
552
- componentName = slot.split("-").map((s2) => s2.charAt(0).toUpperCase() + s2.slice(1)).join("");
603
+ componentName = dataSlot.split("-").map((s2) => s2.charAt(0).toUpperCase() + s2.slice(1)).join("");
604
+ }
605
+ let fiberProps = null;
606
+ if (dataSlot) {
607
+ const compFiber = findComponentFiberAbove(el);
608
+ if (compFiber) {
609
+ fiberProps = extractFiberProps(compFiber);
610
+ }
553
611
  }
554
612
  return {
555
613
  tag: el.tagName.toLowerCase(),
@@ -566,7 +624,8 @@ function CodeSurface() {
566
624
  instanceSourceFile,
567
625
  instanceSourceLine,
568
626
  instanceSourceCol,
569
- componentName
627
+ componentName,
628
+ fiberProps
570
629
  };
571
630
  }
572
631
  function selectElement(el) {
@@ -618,6 +677,23 @@ function CodeSurface() {
618
677
  }
619
678
  tick();
620
679
  }
680
+ function hideSelectionOverlays() {
681
+ if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
682
+ if (s.tooltip) s.tooltip.style.display = "none";
683
+ if (s.selectedOverlay) s.selectedOverlay.style.display = "none";
684
+ s.hoveredElement = null;
685
+ }
686
+ function enterSelectionMode() {
687
+ s.selectionMode = true;
688
+ document.body.style.cursor = "crosshair";
689
+ }
690
+ function exitSelectionMode() {
691
+ s.selectionMode = false;
692
+ document.body.style.cursor = "";
693
+ if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
694
+ if (s.tooltip) s.tooltip.style.display = "none";
695
+ s.hoveredElement = null;
696
+ }
621
697
  function onMouseMove(e) {
622
698
  if (!s.selectionMode || !s.highlightOverlay || !s.tooltip) return;
623
699
  const el = document.elementFromPoint(e.clientX, e.clientY);
@@ -653,15 +729,10 @@ function CodeSurface() {
653
729
  if (!msg || !msg.type || !msg.type.startsWith("tool:")) return;
654
730
  switch (msg.type) {
655
731
  case "tool:enterSelectionMode":
656
- s.selectionMode = true;
657
- document.body.style.cursor = "crosshair";
732
+ enterSelectionMode();
658
733
  break;
659
734
  case "tool:exitSelectionMode":
660
- s.selectionMode = false;
661
- document.body.style.cursor = "";
662
- if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
663
- if (s.tooltip) s.tooltip.style.display = "none";
664
- s.hoveredElement = null;
735
+ exitSelectionMode();
665
736
  break;
666
737
  case "tool:previewInlineStyle": {
667
738
  if (s.selectedElement && s.selectedElement instanceof HTMLElement) {
@@ -759,6 +830,55 @@ function CodeSurface() {
759
830
  }
760
831
  break;
761
832
  }
833
+ case "tool:renderPreview": {
834
+ const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg;
835
+ const currentRegistry = window.__DESIGNTOOLS_REGISTRY__;
836
+ if (!currentRegistry) {
837
+ setPreviewRef.current.setPreviewError("No component registry available. Ensure designtools-registry.ts is imported.");
838
+ setPreviewRef.current.setShowPreview(true);
839
+ return;
840
+ }
841
+ const loader = currentRegistry[componentPath];
842
+ if (!loader) {
843
+ setPreviewRef.current.setPreviewError(
844
+ `Component "${componentPath}" not found in registry. Available: ${Object.keys(currentRegistry).join(", ")}`
845
+ );
846
+ setPreviewRef.current.setShowPreview(true);
847
+ return;
848
+ }
849
+ exitSelectionMode();
850
+ hideSelectionOverlays();
851
+ loader().then((mod) => {
852
+ const Comp = mod[exportName] || mod.default;
853
+ if (!Comp) {
854
+ setPreviewRef.current.setPreviewError(`Export "${exportName}" not found in ${componentPath}`);
855
+ setPreviewRef.current.setShowPreview(true);
856
+ return;
857
+ }
858
+ setPreviewRef.current.setPreviewError(null);
859
+ setPreviewRef.current.setPreviewComponent(() => Comp);
860
+ setPreviewRef.current.setPreviewCombinations(combos || []);
861
+ setPreviewRef.current.setPreviewDefaultChildren(children || exportName);
862
+ setPreviewRef.current.setShowPreview(true);
863
+ window.parent.postMessage(
864
+ { type: "tool:previewReady", cellCount: (combos || []).length },
865
+ "*"
866
+ );
867
+ }).catch((err) => {
868
+ setPreviewRef.current.setPreviewError(`Failed to load component: ${err.message}`);
869
+ setPreviewRef.current.setShowPreview(true);
870
+ });
871
+ break;
872
+ }
873
+ case "tool:exitPreview": {
874
+ setPreviewRef.current.setShowPreview(false);
875
+ setPreviewRef.current.setPreviewComponent(null);
876
+ setPreviewRef.current.setPreviewCombinations([]);
877
+ setPreviewRef.current.setPreviewDefaultChildren("");
878
+ setPreviewRef.current.setPreviewError(null);
879
+ enterSelectionMode();
880
+ break;
881
+ }
762
882
  }
763
883
  }
764
884
  function notifyPathChanged() {
@@ -787,7 +907,67 @@ function CodeSurface() {
787
907
  s.selectedOverlay?.remove();
788
908
  };
789
909
  }, []);
790
- return null;
910
+ if (!showPreview) return null;
911
+ if (previewError) {
912
+ return createPortal(
913
+ /* @__PURE__ */ jsx("div", { style: {
914
+ position: "fixed",
915
+ inset: 0,
916
+ zIndex: 999999,
917
+ background: "var(--background, white)",
918
+ overflow: "auto",
919
+ padding: 32
920
+ }, children: /* @__PURE__ */ jsx("div", { style: { color: "var(--destructive, #ef4444)", fontFamily: "monospace", fontSize: 14 }, children: previewError }) }),
921
+ document.body
922
+ );
923
+ }
924
+ if (!previewComponent) {
925
+ return createPortal(
926
+ /* @__PURE__ */ jsx("div", { style: {
927
+ position: "fixed",
928
+ inset: 0,
929
+ zIndex: 999999,
930
+ background: "var(--background, white)",
931
+ overflow: "auto",
932
+ padding: 32
933
+ }, children: /* @__PURE__ */ jsx("div", { style: { color: "var(--muted-foreground, #888)", fontFamily: "inherit", fontSize: 14 }, children: "Loading component..." }) }),
934
+ document.body
935
+ );
936
+ }
937
+ const Component = previewComponent;
938
+ return createPortal(
939
+ /* @__PURE__ */ jsx("div", { style: {
940
+ position: "fixed",
941
+ inset: 0,
942
+ zIndex: 999999,
943
+ background: "var(--background, white)",
944
+ overflow: "auto",
945
+ padding: 32
946
+ }, children: /* @__PURE__ */ jsx("div", { style: {
947
+ display: "grid",
948
+ gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
949
+ gap: 24
950
+ }, children: previewCombinations.map((combo, i) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
951
+ /* @__PURE__ */ jsx("div", { style: {
952
+ fontSize: 11,
953
+ fontWeight: 600,
954
+ color: "var(--muted-foreground, #888)",
955
+ textTransform: "uppercase",
956
+ letterSpacing: "0.05em"
957
+ }, children: combo.label }),
958
+ /* @__PURE__ */ jsx("div", { style: {
959
+ padding: 16,
960
+ border: "1px solid var(--border, #e5e7eb)",
961
+ borderRadius: 8,
962
+ display: "flex",
963
+ alignItems: "center",
964
+ justifyContent: "center",
965
+ minHeight: 64,
966
+ background: "var(--card, var(--background, #fff))"
967
+ }, children: createElement(Component, combo.props, previewDefaultChildren) })
968
+ ] }, i)) }) }),
969
+ document.body
970
+ );
791
971
  }
792
972
  export {
793
973
  CodeSurface
package/dist/index.js CHANGED
@@ -38,27 +38,30 @@ var import_path2 = __toESM(require("path"));
38
38
  // src/preview-route.ts
39
39
  var import_fs = __toESM(require("fs"));
40
40
  var import_path = __toESM(require("path"));
41
- var PREVIEW_DIR = "designtools-preview";
42
- function generatePreviewRoute(appDir) {
41
+ var REGISTRY_FILE = "designtools-registry.ts";
42
+ function generateComponentRegistry(appDir) {
43
43
  const projectRoot = import_path.default.dirname(appDir);
44
- const previewDir = import_path.default.join(appDir, PREVIEW_DIR);
45
44
  const componentPaths = discoverComponentFiles(projectRoot);
46
- import_fs.default.mkdirSync(previewDir, { recursive: true });
47
- import_fs.default.writeFileSync(
48
- import_path.default.join(previewDir, "layout.tsx"),
49
- getLayoutTemplate(),
50
- "utf-8"
51
- );
52
- import_fs.default.writeFileSync(
53
- import_path.default.join(previewDir, "page.tsx"),
54
- getPageTemplate(componentPaths),
55
- "utf-8"
56
- );
45
+ const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
46
+ const content = `"use client";
47
+ // Auto-generated by @designtools/next-plugin \u2014 do not edit
48
+
49
+ const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
50
+ ${registryEntries}
51
+ };
52
+
53
+ // Self-register on window so CodeSurface can access it without
54
+ // passing functions through the RSC serialization boundary.
55
+ if (typeof window !== "undefined") {
56
+ (window as any).__DESIGNTOOLS_REGISTRY__ = COMPONENT_REGISTRY;
57
+ }
58
+ `;
59
+ import_fs.default.writeFileSync(import_path.default.join(appDir, REGISTRY_FILE), content, "utf-8");
57
60
  ensureGitignore(projectRoot);
58
61
  }
59
62
  function ensureGitignore(projectRoot) {
60
63
  const gitignorePath = import_path.default.join(projectRoot, ".gitignore");
61
- const entry = "app/designtools-preview";
64
+ const entry = "app/designtools-registry.ts";
62
65
  try {
63
66
  const existing = import_fs.default.existsSync(gitignorePath) ? import_fs.default.readFileSync(gitignorePath, "utf-8") : "";
64
67
  if (!existing.includes(entry)) {
@@ -81,145 +84,6 @@ function discoverComponentFiles(projectRoot) {
81
84
  }
82
85
  return [];
83
86
  }
84
- function getLayoutTemplate() {
85
- return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
86
- export default function PreviewLayout({ children }: { children: React.ReactNode }) {
87
- return (
88
- <div style={{ padding: 32, background: "var(--background, #fff)", minHeight: "100vh" }}>
89
- {children}
90
- </div>
91
- );
92
- }
93
- `;
94
- }
95
- function getPageTemplate(componentPaths) {
96
- const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
97
- return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
98
- "use client";
99
-
100
- import { useState, useEffect, useCallback, createElement } from "react";
101
-
102
- /* Static import registry \u2014 webpack can analyze these imports */
103
- const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
104
- ${registryEntries}
105
- };
106
-
107
- interface Combination {
108
- label: string;
109
- props: Record<string, string>;
110
- }
111
-
112
- interface RenderMsg {
113
- type: "tool:renderPreview";
114
- componentPath: string;
115
- exportName: string;
116
- combinations: Combination[];
117
- defaultChildren: string;
118
- }
119
-
120
- export default function PreviewPage() {
121
- const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
122
- const [combinations, setCombinations] = useState<Combination[]>([]);
123
- const [defaultChildren, setDefaultChildren] = useState("");
124
- const [error, setError] = useState<string | null>(null);
125
-
126
- const handleMessage = useCallback(async (e: MessageEvent) => {
127
- const msg = e.data;
128
- if (msg?.type !== "tool:renderPreview") return;
129
-
130
- const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg as RenderMsg;
131
-
132
- try {
133
- setError(null);
134
- setCombinations(combos);
135
- setDefaultChildren(children || exportName);
136
-
137
- const loader = COMPONENT_REGISTRY[componentPath];
138
- if (!loader) {
139
- setError(\`Component "\${componentPath}" not found in registry. Available: \${Object.keys(COMPONENT_REGISTRY).join(", ")}\`);
140
- return;
141
- }
142
-
143
- const mod = await loader();
144
- const Comp = mod[exportName] || mod.default;
145
- if (!Comp) {
146
- setError(\`Export "\${exportName}" not found in \${componentPath}\`);
147
- return;
148
- }
149
-
150
- setComponent(() => Comp);
151
-
152
- // Notify editor that preview is ready
153
- window.parent.postMessage(
154
- { type: "tool:previewReady", cellCount: combos.length },
155
- "*"
156
- );
157
- } catch (err: any) {
158
- setError(\`Failed to load component: \${err.message}\`);
159
- }
160
- }, []);
161
-
162
- useEffect(() => {
163
- window.addEventListener("message", handleMessage);
164
- // Signal readiness to the editor
165
- window.parent.postMessage({ type: "tool:injectedReady" }, "*");
166
- return () => window.removeEventListener("message", handleMessage);
167
- }, [handleMessage]);
168
-
169
- if (error) {
170
- return (
171
- <div style={{ padding: 32, color: "#ef4444", fontFamily: "monospace", fontSize: 14 }}>
172
- {error}
173
- </div>
174
- );
175
- }
176
-
177
- if (!Component) {
178
- return (
179
- <div style={{ padding: 32, color: "#888", fontFamily: "system-ui", fontSize: 14 }}>
180
- Waiting for component\u2026
181
- </div>
182
- );
183
- }
184
-
185
- return (
186
- <div style={{ fontFamily: "system-ui" }}>
187
- <div style={{
188
- display: "grid",
189
- gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
190
- gap: 24,
191
- }}>
192
- {combinations.map((combo, i) => (
193
- <div key={i} style={{ display: "flex", flexDirection: "column", gap: 8 }}>
194
- <div style={{
195
- fontSize: 11,
196
- fontWeight: 600,
197
- color: "#888",
198
- textTransform: "uppercase",
199
- letterSpacing: "0.05em",
200
- }}>
201
- {combo.label}
202
- </div>
203
- <div style={{
204
- padding: 16,
205
- border: "1px solid #e5e7eb",
206
- borderRadius: 8,
207
- display: "flex",
208
- alignItems: "center",
209
- justifyContent: "center",
210
- minHeight: 64,
211
- background: "#fff",
212
- }}>
213
- {createElement(Component, combo.props, defaultChildren)}
214
- </div>
215
- </div>
216
- ))}
217
- </div>
218
- </div>
219
- );
220
- }
221
- `;
222
- }
223
87
 
224
88
  // src/index.ts
225
89
  function withDesigntools(nextConfig = {}) {
@@ -253,7 +117,7 @@ function withDesigntools(nextConfig = {}) {
253
117
  });
254
118
  const appDir = import_path2.default.resolve(context.dir, "app");
255
119
  try {
256
- generatePreviewRoute(appDir);
120
+ generateComponentRegistry(appDir);
257
121
  } catch {
258
122
  }
259
123
  }
package/dist/index.mjs CHANGED
@@ -6,27 +6,30 @@ import path2 from "path";
6
6
  // src/preview-route.ts
7
7
  import fs from "fs";
8
8
  import path from "path";
9
- var PREVIEW_DIR = "designtools-preview";
10
- function generatePreviewRoute(appDir) {
9
+ var REGISTRY_FILE = "designtools-registry.ts";
10
+ function generateComponentRegistry(appDir) {
11
11
  const projectRoot = path.dirname(appDir);
12
- const previewDir = path.join(appDir, PREVIEW_DIR);
13
12
  const componentPaths = discoverComponentFiles(projectRoot);
14
- fs.mkdirSync(previewDir, { recursive: true });
15
- fs.writeFileSync(
16
- path.join(previewDir, "layout.tsx"),
17
- getLayoutTemplate(),
18
- "utf-8"
19
- );
20
- fs.writeFileSync(
21
- path.join(previewDir, "page.tsx"),
22
- getPageTemplate(componentPaths),
23
- "utf-8"
24
- );
13
+ const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
14
+ const content = `"use client";
15
+ // Auto-generated by @designtools/next-plugin \u2014 do not edit
16
+
17
+ const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
18
+ ${registryEntries}
19
+ };
20
+
21
+ // Self-register on window so CodeSurface can access it without
22
+ // passing functions through the RSC serialization boundary.
23
+ if (typeof window !== "undefined") {
24
+ (window as any).__DESIGNTOOLS_REGISTRY__ = COMPONENT_REGISTRY;
25
+ }
26
+ `;
27
+ fs.writeFileSync(path.join(appDir, REGISTRY_FILE), content, "utf-8");
25
28
  ensureGitignore(projectRoot);
26
29
  }
27
30
  function ensureGitignore(projectRoot) {
28
31
  const gitignorePath = path.join(projectRoot, ".gitignore");
29
- const entry = "app/designtools-preview";
32
+ const entry = "app/designtools-registry.ts";
30
33
  try {
31
34
  const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
32
35
  if (!existing.includes(entry)) {
@@ -49,145 +52,6 @@ function discoverComponentFiles(projectRoot) {
49
52
  }
50
53
  return [];
51
54
  }
52
- function getLayoutTemplate() {
53
- return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
54
- export default function PreviewLayout({ children }: { children: React.ReactNode }) {
55
- return (
56
- <div style={{ padding: 32, background: "var(--background, #fff)", minHeight: "100vh" }}>
57
- {children}
58
- </div>
59
- );
60
- }
61
- `;
62
- }
63
- function getPageTemplate(componentPaths) {
64
- const registryEntries = componentPaths.map((p) => ` "${p}": () => import("@/${p}"),`).join("\n");
65
- return `// Auto-generated by @designtools/next-plugin \u2014 do not edit
66
- "use client";
67
-
68
- import { useState, useEffect, useCallback, createElement } from "react";
69
-
70
- /* Static import registry \u2014 webpack can analyze these imports */
71
- const COMPONENT_REGISTRY: Record<string, () => Promise<any>> = {
72
- ${registryEntries}
73
- };
74
-
75
- interface Combination {
76
- label: string;
77
- props: Record<string, string>;
78
- }
79
-
80
- interface RenderMsg {
81
- type: "tool:renderPreview";
82
- componentPath: string;
83
- exportName: string;
84
- combinations: Combination[];
85
- defaultChildren: string;
86
- }
87
-
88
- export default function PreviewPage() {
89
- const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
90
- const [combinations, setCombinations] = useState<Combination[]>([]);
91
- const [defaultChildren, setDefaultChildren] = useState("");
92
- const [error, setError] = useState<string | null>(null);
93
-
94
- const handleMessage = useCallback(async (e: MessageEvent) => {
95
- const msg = e.data;
96
- if (msg?.type !== "tool:renderPreview") return;
97
-
98
- const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg as RenderMsg;
99
-
100
- try {
101
- setError(null);
102
- setCombinations(combos);
103
- setDefaultChildren(children || exportName);
104
-
105
- const loader = COMPONENT_REGISTRY[componentPath];
106
- if (!loader) {
107
- setError(\`Component "\${componentPath}" not found in registry. Available: \${Object.keys(COMPONENT_REGISTRY).join(", ")}\`);
108
- return;
109
- }
110
-
111
- const mod = await loader();
112
- const Comp = mod[exportName] || mod.default;
113
- if (!Comp) {
114
- setError(\`Export "\${exportName}" not found in \${componentPath}\`);
115
- return;
116
- }
117
-
118
- setComponent(() => Comp);
119
-
120
- // Notify editor that preview is ready
121
- window.parent.postMessage(
122
- { type: "tool:previewReady", cellCount: combos.length },
123
- "*"
124
- );
125
- } catch (err: any) {
126
- setError(\`Failed to load component: \${err.message}\`);
127
- }
128
- }, []);
129
-
130
- useEffect(() => {
131
- window.addEventListener("message", handleMessage);
132
- // Signal readiness to the editor
133
- window.parent.postMessage({ type: "tool:injectedReady" }, "*");
134
- return () => window.removeEventListener("message", handleMessage);
135
- }, [handleMessage]);
136
-
137
- if (error) {
138
- return (
139
- <div style={{ padding: 32, color: "#ef4444", fontFamily: "monospace", fontSize: 14 }}>
140
- {error}
141
- </div>
142
- );
143
- }
144
-
145
- if (!Component) {
146
- return (
147
- <div style={{ padding: 32, color: "#888", fontFamily: "system-ui", fontSize: 14 }}>
148
- Waiting for component\u2026
149
- </div>
150
- );
151
- }
152
-
153
- return (
154
- <div style={{ fontFamily: "system-ui" }}>
155
- <div style={{
156
- display: "grid",
157
- gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
158
- gap: 24,
159
- }}>
160
- {combinations.map((combo, i) => (
161
- <div key={i} style={{ display: "flex", flexDirection: "column", gap: 8 }}>
162
- <div style={{
163
- fontSize: 11,
164
- fontWeight: 600,
165
- color: "#888",
166
- textTransform: "uppercase",
167
- letterSpacing: "0.05em",
168
- }}>
169
- {combo.label}
170
- </div>
171
- <div style={{
172
- padding: 16,
173
- border: "1px solid #e5e7eb",
174
- borderRadius: 8,
175
- display: "flex",
176
- alignItems: "center",
177
- justifyContent: "center",
178
- minHeight: 64,
179
- background: "#fff",
180
- }}>
181
- {createElement(Component, combo.props, defaultChildren)}
182
- </div>
183
- </div>
184
- ))}
185
- </div>
186
- </div>
187
- );
188
- }
189
- `;
190
- }
191
55
 
192
56
  // src/index.ts
193
57
  function withDesigntools(nextConfig = {}) {
@@ -221,7 +85,7 @@ function withDesigntools(nextConfig = {}) {
221
85
  });
222
86
  const appDir = path2.resolve(context.dir, "app");
223
87
  try {
224
- generatePreviewRoute(appDir);
88
+ generateComponentRegistry(appDir);
225
89
  } catch {
226
90
  }
227
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designtools/next-plugin",
3
- "version": "0.1.3",
3
+ "version": "0.1.7",
4
4
  "main": "dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -13,7 +13,7 @@
13
13
  }
14
14
  },
15
15
  "scripts": {
16
- "build": "tsup src/index.ts src/loader.ts src/codesurface.tsx src/codesurface-mount-loader.ts --format cjs,esm --dts --external @babel/core --external react --external react/jsx-runtime"
16
+ "build": "tsup src/index.ts src/loader.ts src/codesurface.tsx src/codesurface-mount-loader.ts --format cjs,esm --dts --external @babel/core --external react --external react/jsx-runtime --external react-dom"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "next": ">=14.0.0",