@designtools/next-plugin 0.1.6 → 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.
- package/README.md +58 -0
- package/dist/chunk-6DZX6EAA.mjs +37 -0
- package/dist/codesurface-mount-loader.js +6 -4
- package/dist/codesurface-mount-loader.mjs +6 -4
- package/dist/codesurface.d.mts +3 -1
- package/dist/codesurface.d.ts +3 -1
- package/dist/codesurface.js +192 -12
- package/dist/codesurface.mjs +193 -13
- package/dist/index.js +19 -155
- package/dist/index.mjs +19 -155
- package/package.json +2 -2
- package/src/codesurface-mount-loader.ts +10 -6
- package/src/codesurface.tsx +254 -25
- package/src/index.ts +3 -5
- package/src/preview-route.ts +30 -174
package/dist/codesurface.mjs
CHANGED
|
@@ -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 &&
|
|
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
|
-
|
|
552
|
-
|
|
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
|
-
|
|
657
|
-
document.body.style.cursor = "crosshair";
|
|
732
|
+
enterSelectionMode();
|
|
658
733
|
break;
|
|
659
734
|
case "tool:exitSelectionMode":
|
|
660
|
-
|
|
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
|
|
42
|
-
function
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
|
10
|
-
function
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
+
"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",
|