@designtools/next-plugin 0.1.6 → 0.1.8

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 ADDED
@@ -0,0 +1,58 @@
1
+ # @designtools/next-plugin
2
+
3
+ Next.js config wrapper for [@designtools/codesurface](../codesurface). Adds source location annotations and mounts the selection overlay — development only.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -D @designtools/next-plugin
9
+ ```
10
+
11
+ ## Peer dependencies
12
+
13
+ - `next` >= 14
14
+ - `react` >= 18
15
+
16
+ ## Usage
17
+
18
+ Wrap your Next.js config with `withDesigntools`:
19
+
20
+ ```ts
21
+ // next.config.ts
22
+ import { withDesigntools } from "@designtools/next-plugin";
23
+
24
+ const nextConfig = {
25
+ // your existing config
26
+ };
27
+
28
+ export default withDesigntools(nextConfig);
29
+ ```
30
+
31
+ If you already have a `webpack` customization, it will still be called — `withDesigntools` chains onto it.
32
+
33
+ ## What it does
34
+
35
+ In development (`next dev`), the plugin adds two webpack loaders:
36
+
37
+ ### 1. Source annotation loader
38
+
39
+ Runs a Babel pass over every `.tsx` / `.jsx` file (excluding `node_modules`) that adds a `data-source="file:line:col"` attribute to each JSX element. This maps rendered DOM elements back to their source location so the editor can open the right file when you select an element.
40
+
41
+ SWC stays enabled as the primary compiler — Babel is only used for this annotation pass.
42
+
43
+ ### 2. CodeSurface mount loader
44
+
45
+ Transforms your root layout file (`app/layout.tsx` or `src/app/layout.tsx`) to auto-mount the `<CodeSurface />` selection overlay component. This component handles element selection, hover highlighting, inline style previews, and postMessage communication with the editor UI.
46
+
47
+ Both loaders are skipped entirely in production builds.
48
+
49
+ ## Exports
50
+
51
+ | Export | Description |
52
+ |--------|-------------|
53
+ | `@designtools/next-plugin` | `withDesigntools()` config wrapper |
54
+ | `@designtools/next-plugin/codesurface` | `<CodeSurface />` React component (mounted automatically — you shouldn't need to import this directly) |
55
+
56
+ ## License
57
+
58
+ CC-BY-NC-4.0
@@ -0,0 +1,37 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __commonJS = (cb, mod) => function __require2() {
14
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ export {
34
+ __require,
35
+ __commonJS,
36
+ __toESM
37
+ };
@@ -33,14 +33,16 @@ function codesurfaceMountLoader(source) {
33
33
  callback(null, source);
34
34
  return;
35
35
  }
36
- const importStatement = `import { CodeSurface } from "@designtools/next-plugin/codesurface";
37
- `;
36
+ const importStatements = [
37
+ `import { CodeSurface } from "@designtools/next-plugin/codesurface";`,
38
+ `import "./designtools-registry";`
39
+ ].join("\n") + "\n";
38
40
  let modified = source;
39
41
  const firstImportIndex = source.indexOf("import ");
40
42
  if (firstImportIndex !== -1) {
41
- modified = source.slice(0, firstImportIndex) + importStatement + source.slice(firstImportIndex);
43
+ modified = source.slice(0, firstImportIndex) + importStatements + source.slice(firstImportIndex);
42
44
  } else {
43
- modified = importStatement + source;
45
+ modified = importStatements + source;
44
46
  }
45
47
  modified = modified.replace(
46
48
  /(\{children\})/,
@@ -11,14 +11,16 @@ function codesurfaceMountLoader(source) {
11
11
  callback(null, source);
12
12
  return;
13
13
  }
14
- const importStatement = `import { CodeSurface } from "@designtools/next-plugin/codesurface";
15
- `;
14
+ const importStatements = [
15
+ `import { CodeSurface } from "@designtools/next-plugin/codesurface";`,
16
+ `import "./designtools-registry";`
17
+ ].join("\n") + "\n";
16
18
  let modified = source;
17
19
  const firstImportIndex = source.indexOf("import ");
18
20
  if (firstImportIndex !== -1) {
19
- modified = source.slice(0, firstImportIndex) + importStatement + source.slice(firstImportIndex);
21
+ modified = source.slice(0, firstImportIndex) + importStatements + source.slice(firstImportIndex);
20
22
  } else {
21
- modified = importStatement + source;
23
+ modified = importStatements + source;
22
24
  }
23
25
  modified = modified.replace(
24
26
  /(\{children\})/,
@@ -1,3 +1,5 @@
1
- declare function CodeSurface(): null;
1
+ import * as react from 'react';
2
+
3
+ declare function CodeSurface(): react.ReactPortal | null;
2
4
 
3
5
  export { CodeSurface };
@@ -1,3 +1,5 @@
1
- declare function CodeSurface(): null;
1
+ import * as react from 'react';
2
+
3
+ declare function CodeSurface(): react.ReactPortal | null;
2
4
 
3
5
  export { CodeSurface };
@@ -25,6 +25,8 @@ __export(codesurface_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(codesurface_exports);
27
27
  var import_react = require("react");
28
+ var import_react_dom = require("react-dom");
29
+ var import_jsx_runtime = require("react/jsx-runtime");
28
30
  function CodeSurface() {
29
31
  const stateRef = (0, import_react.useRef)({
30
32
  selectionMode: false,
@@ -40,6 +42,25 @@ function CodeSurface() {
40
42
  tooltip: null,
41
43
  selectedOverlay: null
42
44
  });
45
+ const [previewComponent, setPreviewComponent] = (0, import_react.useState)(null);
46
+ const [previewCombinations, setPreviewCombinations] = (0, import_react.useState)([]);
47
+ const [previewDefaultChildren, setPreviewDefaultChildren] = (0, import_react.useState)("");
48
+ const [previewError, setPreviewError] = (0, import_react.useState)(null);
49
+ const [showPreview, setShowPreview] = (0, import_react.useState)(false);
50
+ const setPreviewRef = (0, import_react.useRef)({
51
+ setPreviewComponent,
52
+ setPreviewCombinations,
53
+ setPreviewDefaultChildren,
54
+ setPreviewError,
55
+ setShowPreview
56
+ });
57
+ setPreviewRef.current = {
58
+ setPreviewComponent,
59
+ setPreviewCombinations,
60
+ setPreviewDefaultChildren,
61
+ setPreviewError,
62
+ setShowPreview
63
+ };
43
64
  (0, import_react.useEffect)(() => {
44
65
  const s = stateRef.current;
45
66
  s.highlightOverlay = document.createElement("div");
@@ -523,6 +544,36 @@ function CodeSurface() {
523
544
  "text-transform",
524
545
  "white-space"
525
546
  ];
547
+ function findComponentFiberAbove(el) {
548
+ const fiber = getFiber(el);
549
+ if (!fiber) return null;
550
+ let candidate = fiber.return;
551
+ while (candidate) {
552
+ const tag = candidate.tag;
553
+ if (tag === 0 || tag === 1 || tag === 11 || tag === 14 || tag === 15) {
554
+ if (findOwnHostElement(candidate) === el) return candidate;
555
+ }
556
+ candidate = candidate.return;
557
+ }
558
+ return null;
559
+ }
560
+ function extractFiberProps(fiber) {
561
+ const props = fiber?.memoizedProps;
562
+ if (!props || typeof props !== "object") return null;
563
+ const skipKeys = /* @__PURE__ */ new Set(["children", "ref", "key", "className", "style"]);
564
+ const result = {};
565
+ let count = 0;
566
+ for (const k of Object.keys(props)) {
567
+ if (skipKeys.has(k)) continue;
568
+ if (k.startsWith("data-")) continue;
569
+ const v = props[k];
570
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
571
+ result[k] = v;
572
+ count++;
573
+ }
574
+ }
575
+ return count > 0 ? result : null;
576
+ }
526
577
  function extractElementData(el) {
527
578
  const computed = getComputedStyle(el);
528
579
  const rect = el.getBoundingClientRect();
@@ -561,8 +612,9 @@ function CodeSurface() {
561
612
  let instanceSourceLine = null;
562
613
  let instanceSourceCol = null;
563
614
  let componentName = null;
615
+ const dataSlot = el.getAttribute("data-slot");
564
616
  const instanceSource = el.getAttribute("data-instance-source");
565
- if (instanceSource && el.getAttribute("data-slot")) {
617
+ if (instanceSource && dataSlot) {
566
618
  const lc = instanceSource.lastIndexOf(":");
567
619
  const slc = instanceSource.lastIndexOf(":", lc - 1);
568
620
  if (slc > 0) {
@@ -570,8 +622,14 @@ function CodeSurface() {
570
622
  instanceSourceLine = parseInt(instanceSource.slice(slc + 1, lc), 10);
571
623
  instanceSourceCol = parseInt(instanceSource.slice(lc + 1), 10);
572
624
  }
573
- const slot = el.getAttribute("data-slot") || "";
574
- componentName = slot.split("-").map((s2) => s2.charAt(0).toUpperCase() + s2.slice(1)).join("");
625
+ componentName = dataSlot.split("-").map((s2) => s2.charAt(0).toUpperCase() + s2.slice(1)).join("");
626
+ }
627
+ let fiberProps = null;
628
+ if (dataSlot) {
629
+ const compFiber = findComponentFiberAbove(el);
630
+ if (compFiber) {
631
+ fiberProps = extractFiberProps(compFiber);
632
+ }
575
633
  }
576
634
  return {
577
635
  tag: el.tagName.toLowerCase(),
@@ -588,7 +646,8 @@ function CodeSurface() {
588
646
  instanceSourceFile,
589
647
  instanceSourceLine,
590
648
  instanceSourceCol,
591
- componentName
649
+ componentName,
650
+ fiberProps
592
651
  };
593
652
  }
594
653
  function selectElement(el) {
@@ -640,6 +699,23 @@ function CodeSurface() {
640
699
  }
641
700
  tick();
642
701
  }
702
+ function hideSelectionOverlays() {
703
+ if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
704
+ if (s.tooltip) s.tooltip.style.display = "none";
705
+ if (s.selectedOverlay) s.selectedOverlay.style.display = "none";
706
+ s.hoveredElement = null;
707
+ }
708
+ function enterSelectionMode() {
709
+ s.selectionMode = true;
710
+ document.body.style.cursor = "crosshair";
711
+ }
712
+ function exitSelectionMode() {
713
+ s.selectionMode = false;
714
+ document.body.style.cursor = "";
715
+ if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
716
+ if (s.tooltip) s.tooltip.style.display = "none";
717
+ s.hoveredElement = null;
718
+ }
643
719
  function onMouseMove(e) {
644
720
  if (!s.selectionMode || !s.highlightOverlay || !s.tooltip) return;
645
721
  const el = document.elementFromPoint(e.clientX, e.clientY);
@@ -675,15 +751,10 @@ function CodeSurface() {
675
751
  if (!msg || !msg.type || !msg.type.startsWith("tool:")) return;
676
752
  switch (msg.type) {
677
753
  case "tool:enterSelectionMode":
678
- s.selectionMode = true;
679
- document.body.style.cursor = "crosshair";
754
+ enterSelectionMode();
680
755
  break;
681
756
  case "tool:exitSelectionMode":
682
- s.selectionMode = false;
683
- document.body.style.cursor = "";
684
- if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
685
- if (s.tooltip) s.tooltip.style.display = "none";
686
- s.hoveredElement = null;
757
+ exitSelectionMode();
687
758
  break;
688
759
  case "tool:previewInlineStyle": {
689
760
  if (s.selectedElement && s.selectedElement instanceof HTMLElement) {
@@ -781,6 +852,55 @@ function CodeSurface() {
781
852
  }
782
853
  break;
783
854
  }
855
+ case "tool:renderPreview": {
856
+ const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg;
857
+ const currentRegistry = window.__DESIGNTOOLS_REGISTRY__;
858
+ if (!currentRegistry) {
859
+ setPreviewRef.current.setPreviewError("No component registry available. Ensure designtools-registry.ts is imported.");
860
+ setPreviewRef.current.setShowPreview(true);
861
+ return;
862
+ }
863
+ const loader = currentRegistry[componentPath];
864
+ if (!loader) {
865
+ setPreviewRef.current.setPreviewError(
866
+ `Component "${componentPath}" not found in registry. Available: ${Object.keys(currentRegistry).join(", ")}`
867
+ );
868
+ setPreviewRef.current.setShowPreview(true);
869
+ return;
870
+ }
871
+ exitSelectionMode();
872
+ hideSelectionOverlays();
873
+ loader().then((mod) => {
874
+ const Comp = mod[exportName] || mod.default;
875
+ if (!Comp) {
876
+ setPreviewRef.current.setPreviewError(`Export "${exportName}" not found in ${componentPath}`);
877
+ setPreviewRef.current.setShowPreview(true);
878
+ return;
879
+ }
880
+ setPreviewRef.current.setPreviewError(null);
881
+ setPreviewRef.current.setPreviewComponent(() => Comp);
882
+ setPreviewRef.current.setPreviewCombinations(combos || []);
883
+ setPreviewRef.current.setPreviewDefaultChildren(children || exportName);
884
+ setPreviewRef.current.setShowPreview(true);
885
+ window.parent.postMessage(
886
+ { type: "tool:previewReady", cellCount: (combos || []).length },
887
+ "*"
888
+ );
889
+ }).catch((err) => {
890
+ setPreviewRef.current.setPreviewError(`Failed to load component: ${err.message}`);
891
+ setPreviewRef.current.setShowPreview(true);
892
+ });
893
+ break;
894
+ }
895
+ case "tool:exitPreview": {
896
+ setPreviewRef.current.setShowPreview(false);
897
+ setPreviewRef.current.setPreviewComponent(null);
898
+ setPreviewRef.current.setPreviewCombinations([]);
899
+ setPreviewRef.current.setPreviewDefaultChildren("");
900
+ setPreviewRef.current.setPreviewError(null);
901
+ enterSelectionMode();
902
+ break;
903
+ }
784
904
  }
785
905
  }
786
906
  function notifyPathChanged() {
@@ -809,7 +929,67 @@ function CodeSurface() {
809
929
  s.selectedOverlay?.remove();
810
930
  };
811
931
  }, []);
812
- return null;
932
+ if (!showPreview) return null;
933
+ if (previewError) {
934
+ return (0, import_react_dom.createPortal)(
935
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
936
+ position: "fixed",
937
+ inset: 0,
938
+ zIndex: 999999,
939
+ background: "var(--background, white)",
940
+ overflow: "auto",
941
+ padding: 32
942
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "var(--destructive, #ef4444)", fontFamily: "monospace", fontSize: 14 }, children: previewError }) }),
943
+ document.body
944
+ );
945
+ }
946
+ if (!previewComponent) {
947
+ return (0, import_react_dom.createPortal)(
948
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
949
+ position: "fixed",
950
+ inset: 0,
951
+ zIndex: 999999,
952
+ background: "var(--background, white)",
953
+ overflow: "auto",
954
+ padding: 32
955
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "var(--muted-foreground, #888)", fontFamily: "inherit", fontSize: 14 }, children: "Loading component..." }) }),
956
+ document.body
957
+ );
958
+ }
959
+ const Component = previewComponent;
960
+ return (0, import_react_dom.createPortal)(
961
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
962
+ position: "fixed",
963
+ inset: 0,
964
+ zIndex: 999999,
965
+ background: "var(--background, white)",
966
+ overflow: "auto",
967
+ padding: 32
968
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
969
+ display: "grid",
970
+ gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
971
+ gap: 24
972
+ }, children: previewCombinations.map((combo, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
974
+ fontSize: 11,
975
+ fontWeight: 600,
976
+ color: "var(--muted-foreground, #888)",
977
+ textTransform: "uppercase",
978
+ letterSpacing: "0.05em"
979
+ }, children: combo.label }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
981
+ padding: 16,
982
+ border: "1px solid var(--border, #e5e7eb)",
983
+ borderRadius: 8,
984
+ display: "flex",
985
+ alignItems: "center",
986
+ justifyContent: "center",
987
+ minHeight: 64,
988
+ background: "var(--card, var(--background, #fff))"
989
+ }, children: (0, import_react.createElement)(Component, combo.props, previewDefaultChildren) })
990
+ ] }, i)) }) }),
991
+ document.body
992
+ );
813
993
  }
814
994
  // Annotate the CommonJS export names for ESM import in node:
815
995
  0 && (module.exports = {