@seedgrid/fe-components 2026.3.2 → 2026.3.3-2

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.
@@ -7,12 +7,43 @@ import { SgCard } from "../layout/SgCard";
7
7
  function cn(...parts) {
8
8
  return parts.filter(Boolean).join(" ");
9
9
  }
10
- const DEFAULT_SEEDGRID_DEPENDENCY = "2026.3.1";
11
- const DEFAULT_SEEDGRID_PEER_DEPENDENCIES = {
12
- "@codesandbox/sandpack-react": "^2.20.0",
13
- "react-hook-form": "^7.0.0",
14
- "lucide-react": "^0.468.0",
10
+ const DEFAULT_SEEDGRID_DEPENDENCY = "latest";
11
+ const DEFAULT_SANDPACK_BUNDLER_URL = "https://sandpack.seedgrid.com.br";
12
+ const DEFAULT_SANDPACK_BUNDLER_TIMEOUT_MS = 60000;
13
+ const DEFAULT_SANDBOX_BASE_DEPENDENCIES = {
14
+ react: "18.2.0",
15
+ "react-dom": "18.2.0"
16
+ };
17
+ const DEFAULT_SEEDGRID_RUNTIME_DEPENDENCIES = {
18
+ "react-hook-form": "^7.0.0"
19
+ };
20
+ // lucide-react is a heavy peer dep (~1400 icons). For non-full presets we shim it with
21
+ // a generic SVG placeholder to avoid OOM in the browser bundler.
22
+ const SANDPACK_LUCIDE_REACT_SHIM_INDEX_JS = `const React = require("react");
23
+ const Icon = function(props) {
24
+ return React.createElement("svg", {
25
+ xmlns: "http://www.w3.org/2000/svg",
26
+ width: props.size || props.width || 16,
27
+ height: props.size || props.height || 16,
28
+ viewBox: "0 0 24 24",
29
+ fill: "none",
30
+ stroke: props.color || "currentColor",
31
+ strokeWidth: props.strokeWidth || 2,
32
+ className: props.className
33
+ });
34
+ };
35
+ const proxy = new Proxy({}, {
36
+ get: function(_, key) {
37
+ if (key === "__esModule") return true;
38
+ return Icon;
39
+ }
40
+ });
41
+ module.exports = proxy;
42
+ `;
43
+ const DEFAULT_SEEDGRID_EDITOR_DEPENDENCIES = {
44
+ "@tiptap/core": "^2.9.1",
15
45
  "@tiptap/react": "^2.9.1",
46
+ "@tiptap/pm": "^2.9.1",
16
47
  "@tiptap/starter-kit": "^2.9.1",
17
48
  "@tiptap/extension-underline": "^2.9.1",
18
49
  "@tiptap/extension-link": "^2.9.1",
@@ -25,10 +56,255 @@ const DEFAULT_SEEDGRID_PEER_DEPENDENCIES = {
25
56
  "@tiptap/extension-superscript": "^2.9.1",
26
57
  "@tiptap/extension-font-family": "^2.9.1"
27
58
  };
59
+ const DEFAULT_SANDBOX_HOST_DEPENDENCIES = {
60
+ "@codesandbox/sandpack-react": "^2.20.0"
61
+ };
62
+ const DEFAULT_SANDPACK_POLYFILLS = {
63
+ assert: "^2.1.0",
64
+ process: "^0.11.10",
65
+ util: "^0.12.5"
66
+ };
67
+ const TIPTAP_SHIM_PACKAGES = [
68
+ "@tiptap/core",
69
+ "@tiptap/pm",
70
+ "@tiptap/starter-kit",
71
+ "@tiptap/extension-underline",
72
+ "@tiptap/extension-link",
73
+ "@tiptap/extension-image",
74
+ "@tiptap/extension-text-align",
75
+ "@tiptap/extension-text-style",
76
+ "@tiptap/extension-color",
77
+ "@tiptap/extension-highlight",
78
+ "@tiptap/extension-subscript",
79
+ "@tiptap/extension-superscript",
80
+ "@tiptap/extension-font-family"
81
+ ];
28
82
  const SANDPACK_EXTERNAL_RESOURCES = [
29
83
  // Prebuilt utility CSS so classes from @seedgrid/fe-components can render inside Sandpack
30
84
  "https://unpkg.com/tailwindcss@2.2.19/dist/tailwind.min.css"
31
85
  ];
86
+ const SANDPACK_QRCODE_SHIM_INDEX_JS = `const makeError = () =>
87
+ new Error(
88
+ "qrcode (node build) is not supported in Sandpack browser runtime. Update @seedgrid/fe-components to a browser-safe QR implementation."
89
+ );
90
+
91
+ const qrcodeShim = {
92
+ toDataURL() {
93
+ return Promise.reject(makeError());
94
+ }
95
+ };
96
+
97
+ export const toDataURL = (...args) => qrcodeShim.toDataURL(...args);
98
+ export default qrcodeShim;
99
+ `;
100
+ const SANDPACK_MARKDOWN_IT_BIN_SHIM = `import markdownit from "../index.mjs";
101
+
102
+ // Browser-safe shim: Sandpack must not execute markdown-it CLI entrypoint.
103
+ // Re-export parser factory so accidental imports of the bin path remain harmless.
104
+ export default markdownit;
105
+ export { markdownit };
106
+ `;
107
+ const SANDPACK_SANDBOX_SANDPACK_REACT_SHIM_INDEX_JS = `export const SandpackProvider = ({ children }) => children ?? null;
108
+ export const SandpackCodeEditor = () => null;
109
+ export const SandpackPreview = () => null;
110
+ export const useSandpack = () => ({
111
+ sandpack: {
112
+ activeFile: "/App.tsx",
113
+ files: { "/App.tsx": { code: "" } },
114
+ runSandpack: async () => {},
115
+ clients: {},
116
+ status: "idle"
117
+ },
118
+ dispatch: () => {},
119
+ listen: () => () => {}
120
+ });
121
+ export default {};
122
+ `;
123
+ const SANDPACK_TIPTAP_REACT_SHIM_INDEX_JS = `export const EditorContent = () => null;
124
+ export const BubbleMenu = () => null;
125
+ export const FloatingMenu = () => null;
126
+ export const useEditor = () => null;
127
+ export default {};
128
+ `;
129
+ const SANDPACK_TIPTAP_EXTENSION_SHIM_INDEX_JS = `const extension = {
130
+ configure() {
131
+ return extension;
132
+ },
133
+ extend() {
134
+ return extension;
135
+ }
136
+ };
137
+ export default extension;
138
+ `;
139
+ const SANDPACK_SEEDGRID_TEXT_EDITOR_SHIM_INDEX_JS = `import * as React from "react";
140
+
141
+ export function SgTextEditor() {
142
+ return React.createElement(
143
+ "div",
144
+ {
145
+ style: {
146
+ padding: "0.75rem",
147
+ border: "1px solid #e4e4e7",
148
+ borderRadius: "0.5rem",
149
+ fontSize: "0.875rem",
150
+ color: "#6b7280",
151
+ background: "#f9fafb"
152
+ }
153
+ },
154
+ "SgTextEditor is disabled in this sandbox preset."
155
+ );
156
+ }
157
+
158
+ export default SgTextEditor;
159
+ `;
160
+ const SANDPACK_ASSERT_SHIM_INDEX_JS = `function fail(message) {
161
+ throw new Error(message || "Assertion failed");
162
+ }
163
+
164
+ function assert(value, message) {
165
+ if (!value) fail(message);
166
+ }
167
+
168
+ assert.ok = assert;
169
+ assert.equal = function equal(actual, expected, message) {
170
+ if (actual != expected) fail(message || "Expected values to be loosely equal");
171
+ };
172
+ assert.strictEqual = function strictEqual(actual, expected, message) {
173
+ if (actual !== expected) fail(message || "Expected values to be strictly equal");
174
+ };
175
+ assert.notEqual = function notEqual(actual, expected, message) {
176
+ if (actual == expected) fail(message || "Expected values to be different");
177
+ };
178
+ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
179
+ if (actual === expected) fail(message || "Expected values to be different");
180
+ };
181
+ assert.deepEqual = function deepEqual(actual, expected, message) {
182
+ try {
183
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
184
+ fail(message || "Expected values to be deeply equal");
185
+ }
186
+ } catch (_) {
187
+ if (actual !== expected) fail(message || "Expected values to be deeply equal");
188
+ }
189
+ };
190
+ assert.throws = function throwsAssertion(fn, message) {
191
+ var didThrow = false;
192
+ try { fn(); } catch (_) { didThrow = true; }
193
+ if (!didThrow) fail(message || "Expected function to throw");
194
+ };
195
+ assert.fail = fail;
196
+
197
+ module.exports = assert;
198
+ module.exports.default = assert;
199
+ `;
200
+ const SANDPACK_UTIL_SHIM_INDEX_JS = `const inspect = function inspect(value) {
201
+ try {
202
+ return JSON.stringify(value);
203
+ } catch (_) {
204
+ return String(value);
205
+ }
206
+ };
207
+
208
+ inspect.custom = typeof Symbol !== "undefined" && Symbol.for
209
+ ? Symbol.for("nodejs.util.inspect.custom")
210
+ : "@@nodejs.util.inspect.custom";
211
+
212
+ function deprecate(fn, message) {
213
+ var warned = false;
214
+ return function deprecatedWrapper() {
215
+ if (!warned && typeof console !== "undefined" && typeof console.warn === "function") {
216
+ console.warn(message);
217
+ warned = true;
218
+ }
219
+ return fn.apply(this, arguments);
220
+ };
221
+ }
222
+
223
+ function format() {
224
+ return Array.prototype.map.call(arguments, function (item) {
225
+ return String(item);
226
+ }).join(" ");
227
+ }
228
+
229
+ const utilShim = { inspect, deprecate, format };
230
+ module.exports = utilShim;
231
+ module.exports.default = utilShim;
232
+ `;
233
+ const SANDPACK_PATH_SHIM_INDEX_JS = `function normalize(input) {
234
+ return String(input || "").replace(/\\\\/g, "/");
235
+ }
236
+
237
+ function basename(input) {
238
+ var text = normalize(input);
239
+ var pieces = text.split("/").filter(Boolean);
240
+ return pieces.length ? pieces[pieces.length - 1] : "";
241
+ }
242
+
243
+ function dirname(input) {
244
+ var text = normalize(input);
245
+ var index = text.lastIndexOf("/");
246
+ if (index <= 0) return ".";
247
+ return text.slice(0, index);
248
+ }
249
+
250
+ function join() {
251
+ return Array.prototype
252
+ .map.call(arguments, normalize)
253
+ .filter(Boolean)
254
+ .join("/")
255
+ .replace(/\\/{2,}/g, "/");
256
+ }
257
+
258
+ function resolve() {
259
+ return join.apply(null, arguments);
260
+ }
261
+
262
+ const pathShim = {
263
+ sep: "/",
264
+ delimiter: ":",
265
+ basename,
266
+ dirname,
267
+ join,
268
+ resolve,
269
+ normalize
270
+ };
271
+
272
+ module.exports = pathShim;
273
+ module.exports.default = pathShim;
274
+ `;
275
+ const SANDPACK_FS_SHIM_INDEX_JS = `function notSupported(name) {
276
+ throw new Error("fs." + name + " is not supported in Sandpack browser runtime.");
277
+ }
278
+
279
+ const fsShim = {
280
+ openSync: function openSync() { return notSupported("openSync"); },
281
+ createReadStream: function createReadStream() { return notSupported("createReadStream"); },
282
+ createWriteStream: function createWriteStream() { return notSupported("createWriteStream"); },
283
+ readFileSync: function readFileSync() { return notSupported("readFileSync"); }
284
+ };
285
+
286
+ module.exports = fsShim;
287
+ module.exports.default = fsShim;
288
+ `;
289
+ const SANDPACK_PROCESS_SHIM_INDEX_JS = `const processShim = {
290
+ env: {},
291
+ argv: ["browser"],
292
+ stdout: { columns: 80 },
293
+ stderr: { columns: 80 },
294
+ cwd: function cwd() { return "/"; },
295
+ nextTick: function nextTick(fn) {
296
+ var args = Array.prototype.slice.call(arguments, 1);
297
+ return Promise.resolve().then(function () { return fn.apply(null, args); });
298
+ }
299
+ };
300
+
301
+ if (typeof globalThis !== "undefined" && !globalThis.process) {
302
+ globalThis.process = processShim;
303
+ }
304
+
305
+ module.exports = processShim;
306
+ module.exports.default = processShim;
307
+ `;
32
308
  const SANDPACK_FALLBACK_THEME_VARS = {
33
309
  "--background": "0 0% 100%",
34
310
  "--foreground": "222.2 84% 4.9%",
@@ -103,6 +379,100 @@ body {
103
379
  .size-4 { width: 1rem; height: 1rem; }
104
380
  .size-5 { width: 1.25rem; height: 1.25rem; }
105
381
  `;
382
+ const SANDPACK_HOST_STYLES_CSS = `
383
+ .sg-playground-preview .sp-cube-wrapper {
384
+ top: 8px !important;
385
+ right: 8px !important;
386
+ bottom: auto !important;
387
+ left: auto !important;
388
+ }
389
+ `;
390
+ function normalizeUrl(raw, fallback) {
391
+ const value = raw?.trim();
392
+ if (!value)
393
+ return fallback;
394
+ try {
395
+ const parsed = new URL(value);
396
+ return parsed.toString().replace(/\/+$/, "");
397
+ }
398
+ catch {
399
+ return fallback;
400
+ }
401
+ }
402
+ function normalizeTimeoutMs(value, fallback) {
403
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
404
+ return fallback;
405
+ }
406
+ return Math.round(value);
407
+ }
408
+ function parseBooleanFlag(value, fallback) {
409
+ const normalized = value?.trim().toLowerCase();
410
+ if (!normalized)
411
+ return fallback;
412
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
413
+ return true;
414
+ }
415
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
416
+ return false;
417
+ }
418
+ return fallback;
419
+ }
420
+ function resolveDefaultNpmRegistriesFromEnv() {
421
+ const registryUrlRaw = process.env.NEXT_PUBLIC_SANDPACK_NPM_REGISTRY_URL?.trim();
422
+ if (!registryUrlRaw)
423
+ return undefined;
424
+ const scopesRaw = process.env.NEXT_PUBLIC_SANDPACK_NPM_REGISTRY_SCOPES;
425
+ const enabledScopes = (scopesRaw ?? "@seedgrid")
426
+ .split(",")
427
+ .map((scope) => scope.trim())
428
+ .filter(Boolean);
429
+ const limitToScopes = parseBooleanFlag(process.env.NEXT_PUBLIC_SANDPACK_NPM_REGISTRY_LIMIT_TO_SCOPES, true);
430
+ const proxyEnabled = parseBooleanFlag(process.env.NEXT_PUBLIC_SANDPACK_NPM_REGISTRY_PROXY_ENABLED, false);
431
+ const registryAuthToken = process.env.NEXT_PUBLIC_SANDPACK_NPM_REGISTRY_AUTH_TOKEN?.trim();
432
+ return [
433
+ {
434
+ enabledScopes,
435
+ limitToScopes,
436
+ registryUrl: registryUrlRaw.replace(/\/+$/, ""),
437
+ proxyEnabled,
438
+ ...(registryAuthToken ? { registryAuthToken } : {})
439
+ }
440
+ ];
441
+ }
442
+ function buildCjsModule(value) {
443
+ const serialized = JSON.stringify(value);
444
+ return `const data = ${serialized};
445
+ module.exports = data;
446
+ module.exports.default = data;
447
+ `;
448
+ }
449
+ const SANDPACK_MIN_COMPONENT_MESSAGES = {
450
+ "components.actions.clear": "Clear",
451
+ "components.actions.cancel": "Cancel",
452
+ "components.actions.confirm": "Confirm",
453
+ "components.inputs.required": "Required field.",
454
+ "components.inputs.maxLength": "Maximum {max} characters.",
455
+ "components.inputs.minLength": "Minimum {min} characters.",
456
+ "components.inputs.minWords": "Minimum {min} words.",
457
+ "components.inputs.email.invalid": "Invalid email.",
458
+ "components.inputs.phone.invalid": "Invalid phone.",
459
+ "components.inputs.date.invalid": "Invalid date.",
460
+ "components.inputs.number.min": "Value must be at least {min}.",
461
+ "components.inputs.number.max": "Value must be at most {max}.",
462
+ "components.password.show": "Show password",
463
+ "components.password.hide": "Hide password",
464
+ "components.autocomplete.empty": "No records found.",
465
+ "components.autocomplete.loading": "Loading...",
466
+ "components.rating.cancel": "Cancel rating",
467
+ "components.radiogroup.cancel": "No option"
468
+ };
469
+ const SANDPACK_SEEDGRID_PT_BR_JSON_SHIM = buildCjsModule(SANDPACK_MIN_COMPONENT_MESSAGES);
470
+ const SANDPACK_SEEDGRID_PT_PT_JSON_SHIM = buildCjsModule(SANDPACK_MIN_COMPONENT_MESSAGES);
471
+ const SANDPACK_SEEDGRID_EN_US_JSON_SHIM = buildCjsModule(SANDPACK_MIN_COMPONENT_MESSAGES);
472
+ const SANDPACK_SEEDGRID_ES_JSON_SHIM = buildCjsModule(SANDPACK_MIN_COMPONENT_MESSAGES);
473
+ const SANDPACK_SEEDGRID_BLOCKED_EMAIL_DOMAINS_JSON_SHIM = buildCjsModule({
474
+ blockedEmailDomains: []
475
+ });
106
476
  function parseRgbParts(raw) {
107
477
  const value = raw.trim();
108
478
  if (!value)
@@ -303,13 +673,45 @@ function CopyButton() {
303
673
  }
304
674
  function RunButton({ onRun }) {
305
675
  const { sandpack } = useSandpack();
306
- return (_jsx(SgButton, { severity: "primary", size: "sm", onClick: () => {
307
- sandpack.runSandpack();
308
- onRun?.();
309
- }, children: "Run" }));
676
+ const [running, setRunning] = React.useState(false);
677
+ const handleRun = React.useCallback(async () => {
678
+ if (running)
679
+ return;
680
+ setRunning(true);
681
+ onRun?.();
682
+ try {
683
+ if (typeof window !== "undefined") {
684
+ await new Promise((resolve) => {
685
+ window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
686
+ });
687
+ }
688
+ // On lazy init + hidden preview scenarios, iframe/client registration can lag by a tick.
689
+ for (let attempt = 0; attempt < 3; attempt += 1) {
690
+ await sandpack.runSandpack();
691
+ if (sandpack.status === "running" ||
692
+ sandpack.status === "done" ||
693
+ Object.keys(sandpack.clients ?? {}).length > 0) {
694
+ break;
695
+ }
696
+ await new Promise((resolve) => setTimeout(resolve, 120));
697
+ }
698
+ }
699
+ catch (error) {
700
+ console.error("[SgPlayground] Failed to run Sandpack", error);
701
+ }
702
+ finally {
703
+ if (typeof window !== "undefined") {
704
+ window.setTimeout(() => setRunning(false), 150);
705
+ }
706
+ else {
707
+ setRunning(false);
708
+ }
709
+ }
710
+ }, [onRun, running, sandpack]);
711
+ return (_jsx(SgButton, { severity: "primary", size: "sm", loading: running, onClick: handleRun, children: running ? "Running" : "Run" }));
310
712
  }
311
713
  export default function SgPlayground(props) {
312
- const { code, interactive = false, codeContract = "renderBody", title, description, height = 360, expandedHeight = "70vh", expandable = true, resizable = true, resizeAxis = "vertical", previewPadding, className, dependencies, defaultImports, previewWrapperClassName = "h-[420px] rounded-xl border border-border bg-muted/30 p-3", seedgridDependency, withCard = true, collapsible = true, defaultOpen = true, cardId } = props;
714
+ const { code, interactive = false, codeContract = "renderBody", preset = "auto", title, description, height = 360, expandedHeight = "70vh", expandable = true, resizable = true, resizeAxis = "vertical", previewPadding, className, dependencies, defaultImports, previewWrapperClassName = "h-[420px] rounded-xl border border-border bg-muted/30 p-3", seedgridDependency, bundlerURL, bundlerTimeoutMs, npmRegistries, withCard = true, collapsible = true, defaultOpen = true, cardId } = props;
313
715
  const effectivePreviewPadding = normalizeCssSize(previewPadding ?? (codeContract === "appFile" ? 12 : 0));
314
716
  const [sandpackStylesCss, setSandpackStylesCss] = React.useState(() => buildSandpackStylesCss({}, effectivePreviewPadding));
315
717
  const [isExpanded, setIsExpanded] = React.useState(false);
@@ -359,16 +761,168 @@ export default function SgPlayground(props) {
359
761
  const resolvedSeedgridDependency = seedgridDependency ??
360
762
  process.env.NEXT_PUBLIC_SANDPACK_SEEDGRID_DEPENDENCY ??
361
763
  DEFAULT_SEEDGRID_DEPENDENCY;
764
+ const requestedDeps = dependencies ?? {};
765
+ const requestedDepKeys = Object.keys(requestedDeps);
766
+ const resolvedBundlerURL = normalizeUrl(bundlerURL ?? process.env.NEXT_PUBLIC_SANDPACK_BUNDLER_URL, DEFAULT_SANDPACK_BUNDLER_URL);
767
+ const resolvedBundlerTimeoutMs = normalizeTimeoutMs(bundlerTimeoutMs ??
768
+ Number(process.env.NEXT_PUBLIC_SANDPACK_BUNDLER_TIMEOUT_MS ?? Number.NaN), DEFAULT_SANDPACK_BUNDLER_TIMEOUT_MS);
769
+ const resolvedNpmRegistries = React.useMemo(() => npmRegistries ?? resolveDefaultNpmRegistriesFromEnv(), [npmRegistries]);
770
+ React.useEffect(() => {
771
+ if (!interactive)
772
+ return;
773
+ if (!parseBooleanFlag(process.env.NEXT_PUBLIC_SANDPACK_DEBUG, false))
774
+ return;
775
+ console.info("[SgPlayground] Sandpack runtime config", {
776
+ bundlerURL: resolvedBundlerURL,
777
+ bundlerTimeoutMs: resolvedBundlerTimeoutMs,
778
+ npmRegistries: (resolvedNpmRegistries ?? []).map((registry) => ({
779
+ enabledScopes: registry.enabledScopes,
780
+ limitToScopes: registry.limitToScopes,
781
+ registryUrl: registry.registryUrl,
782
+ proxyEnabled: registry.proxyEnabled
783
+ }))
784
+ });
785
+ }, [interactive, resolvedBundlerTimeoutMs, resolvedBundlerURL, resolvedNpmRegistries]);
786
+ const codeUsesSeedgrid = /from\s+["']@seedgrid\/fe-components["']/.test(appTsx);
787
+ const codeUsesTextEditor = /\bSgTextEditor\b/.test(appTsx);
788
+ const codeUsesPlaygroundComponent = /\bSgPlayground\b/.test(appTsx);
789
+ const resolvedPreset = preset === "auto"
790
+ ? codeUsesTextEditor
791
+ ? "editor"
792
+ : codeUsesSeedgrid
793
+ ? "seedgrid"
794
+ : "basic"
795
+ : preset;
796
+ const includeSeedgridDependency = resolvedPreset === "seedgrid" ||
797
+ resolvedPreset === "editor" ||
798
+ resolvedPreset === "full" ||
799
+ requestedDepKeys.includes("@seedgrid/fe-components");
800
+ const includeEditorDependencies = resolvedPreset === "editor" ||
801
+ resolvedPreset === "full" ||
802
+ codeUsesTextEditor ||
803
+ requestedDepKeys.some((dep) => dep.startsWith("@tiptap/"));
804
+ const includeSandpackReactDependency = resolvedPreset === "full" ||
805
+ codeUsesPlaygroundComponent ||
806
+ requestedDepKeys.includes("@codesandbox/sandpack-react");
807
+ const shouldShimSandpackReact = includeSeedgridDependency && !includeSandpackReactDependency;
808
+ const shouldShimTiptap = includeSeedgridDependency && !includeEditorDependencies;
809
+ const shouldIncludeNodePolyfills = includeEditorDependencies;
810
+ // Shim lucide-react for all non-full presets to avoid OOM from bundling ~1400 icon components.
811
+ // Full preset gets the real package so icons render correctly.
812
+ const shouldShimLucide = includeSeedgridDependency && resolvedPreset !== "full" && !requestedDepKeys.includes("lucide-react");
362
813
  const files = {
363
814
  "/App.tsx": { code: appTsx, active: true },
364
815
  "/styles.css": { code: sandpackStylesCss || buildSandpackStylesCss({}, effectivePreviewPadding) }
365
816
  };
817
+ if (includeSeedgridDependency) {
818
+ // Intercept the package entry point and redirect to the pre-compiled sandbox bundle
819
+ // (dist/sandbox.cjs) instead of the tsc barrel file (dist/index.js).
820
+ // This makes the Sandpack bundler fetch and process ONE file instead of 200+ individual files.
821
+ // The real package.json from npm is left intact so version resolution works normally.
822
+ // Requires @seedgrid/fe-components to be built with: pnpm run build:sandbox
823
+ files["/node_modules/@seedgrid/fe-components/dist/index.js"] = {
824
+ code: `module.exports = require("./sandbox.cjs");`,
825
+ hidden: true
826
+ };
827
+ // Compatibility shim for legacy @seedgrid/fe-components builds that still import "qrcode" (node-only path).
828
+ files["/node_modules/qrcode/index.js"] = { code: SANDPACK_QRCODE_SHIM_INDEX_JS, hidden: true };
829
+ // Sandpack runtime can evaluate JSON files as plain JS modules.
830
+ // Provide CJS-compatible shims to keep @seedgrid/fe-components i18n/validators working.
831
+ files["/node_modules/@seedgrid/fe-components/dist/i18n/pt-BR.json"] = {
832
+ code: SANDPACK_SEEDGRID_PT_BR_JSON_SHIM,
833
+ hidden: true
834
+ };
835
+ files["/node_modules/@seedgrid/fe-components/dist/i18n/pt-PT.json"] = {
836
+ code: SANDPACK_SEEDGRID_PT_PT_JSON_SHIM,
837
+ hidden: true
838
+ };
839
+ files["/node_modules/@seedgrid/fe-components/dist/i18n/en-US.json"] = {
840
+ code: SANDPACK_SEEDGRID_EN_US_JSON_SHIM,
841
+ hidden: true
842
+ };
843
+ files["/node_modules/@seedgrid/fe-components/dist/i18n/es.json"] = {
844
+ code: SANDPACK_SEEDGRID_ES_JSON_SHIM,
845
+ hidden: true
846
+ };
847
+ files["/node_modules/@seedgrid/fe-components/dist/blocked-email-domains.json"] = {
848
+ code: SANDPACK_SEEDGRID_BLOCKED_EMAIL_DOMAINS_JSON_SHIM,
849
+ hidden: true
850
+ };
851
+ }
852
+ if (shouldIncludeNodePolyfills) {
853
+ // markdown-it CLI entry uses node:fs and breaks in browser runtime.
854
+ files["/node_modules/markdown-it/bin/markdown-it.mjs"] = {
855
+ code: SANDPACK_MARKDOWN_IT_BIN_SHIM,
856
+ hidden: true
857
+ };
858
+ // Node builtin compatibility shims used by transitive dependencies (e.g. argparse from markdown-it/tiptap).
859
+ files["/node_modules/assert/index.js"] = { code: SANDPACK_ASSERT_SHIM_INDEX_JS, hidden: true };
860
+ files["/node_modules/util/index.js"] = { code: SANDPACK_UTIL_SHIM_INDEX_JS, hidden: true };
861
+ files["/node_modules/path/index.js"] = { code: SANDPACK_PATH_SHIM_INDEX_JS, hidden: true };
862
+ files["/node_modules/fs/index.js"] = { code: SANDPACK_FS_SHIM_INDEX_JS, hidden: true };
863
+ files["/node_modules/process/index.js"] = { code: SANDPACK_PROCESS_SHIM_INDEX_JS, hidden: true };
864
+ }
865
+ if (shouldShimSandpackReact) {
866
+ files["/node_modules/@codesandbox/sandpack-react/index.js"] = {
867
+ code: SANDPACK_SANDBOX_SANDPACK_REACT_SHIM_INDEX_JS,
868
+ hidden: true
869
+ };
870
+ }
871
+ if (shouldShimTiptap) {
872
+ files["/node_modules/@seedgrid/fe-components/dist/inputs/SgTextEditor.js"] = {
873
+ code: SANDPACK_SEEDGRID_TEXT_EDITOR_SHIM_INDEX_JS,
874
+ hidden: true
875
+ };
876
+ files["/node_modules/@seedgrid/fe-components/dist/inputs/SgTextEditor.mjs"] = {
877
+ code: SANDPACK_SEEDGRID_TEXT_EDITOR_SHIM_INDEX_JS,
878
+ hidden: true
879
+ };
880
+ files["/node_modules/@tiptap/react/package.json"] = {
881
+ code: JSON.stringify({ name: "@tiptap/react", version: "0.0.0-shim", main: "index.js", module: "index.mjs" }),
882
+ hidden: true
883
+ };
884
+ files["/node_modules/@tiptap/react/index.js"] = {
885
+ code: SANDPACK_TIPTAP_REACT_SHIM_INDEX_JS,
886
+ hidden: true
887
+ };
888
+ files["/node_modules/@tiptap/react/index.mjs"] = {
889
+ code: SANDPACK_TIPTAP_REACT_SHIM_INDEX_JS,
890
+ hidden: true
891
+ };
892
+ for (const packageName of TIPTAP_SHIM_PACKAGES) {
893
+ files[`/node_modules/${packageName}/package.json`] = {
894
+ code: JSON.stringify({ name: packageName, version: "0.0.0-shim", main: "index.js", module: "index.mjs" }),
895
+ hidden: true
896
+ };
897
+ files[`/node_modules/${packageName}/index.js`] = {
898
+ code: SANDPACK_TIPTAP_EXTENSION_SHIM_INDEX_JS,
899
+ hidden: true
900
+ };
901
+ files[`/node_modules/${packageName}/index.mjs`] = {
902
+ code: SANDPACK_TIPTAP_EXTENSION_SHIM_INDEX_JS,
903
+ hidden: true
904
+ };
905
+ }
906
+ }
907
+ if (shouldShimLucide) {
908
+ files["/node_modules/lucide-react/package.json"] = {
909
+ code: JSON.stringify({ name: "lucide-react", version: "0.0.0-shim", main: "index.js" }),
910
+ hidden: true
911
+ };
912
+ files["/node_modules/lucide-react/index.js"] = {
913
+ code: SANDPACK_LUCIDE_REACT_SHIM_INDEX_JS,
914
+ hidden: true
915
+ };
916
+ }
366
917
  const deps = {
367
- react: "^19.0.0",
368
- "react-dom": "^19.0.0",
369
- ...DEFAULT_SEEDGRID_PEER_DEPENDENCIES,
370
- "@seedgrid/fe-components": resolvedSeedgridDependency,
371
- ...(dependencies ?? {})
918
+ ...DEFAULT_SANDBOX_BASE_DEPENDENCIES,
919
+ ...(includeSeedgridDependency ? DEFAULT_SEEDGRID_RUNTIME_DEPENDENCIES : {}),
920
+ ...(includeSeedgridDependency && !shouldShimLucide ? { "lucide-react": "^0.468.0" } : {}),
921
+ ...(includeEditorDependencies ? DEFAULT_SEEDGRID_EDITOR_DEPENDENCIES : {}),
922
+ ...(includeSandpackReactDependency ? DEFAULT_SANDBOX_HOST_DEPENDENCIES : {}),
923
+ ...(shouldIncludeNodePolyfills ? DEFAULT_SANDPACK_POLYFILLS : {}),
924
+ ...(includeSeedgridDependency ? { "@seedgrid/fe-components": resolvedSeedgridDependency } : {}),
925
+ ...requestedDeps
372
926
  };
373
927
  const currentHeight = isExpanded ? expandedHeight : height;
374
928
  const resizeClass = !resizable
@@ -378,18 +932,27 @@ export default function SgPlayground(props) {
378
932
  : resizeAxis === "horizontal"
379
933
  ? "resize-x"
380
934
  : "resize";
381
- const content = interactive ? (_jsx("div", { className: cn(withCard ? "" : "rounded-lg border border-border", withCard ? undefined : className), children: _jsxs(SandpackProvider, { template: "react-ts", files: files, customSetup: { dependencies: deps }, options: {
382
- autorun: false,
383
- initMode: "lazy",
384
- bundlerTimeOut: 60000,
385
- activeFile: "/App.tsx",
386
- visibleFiles: ["/App.tsx"],
387
- externalResources: SANDPACK_EXTERNAL_RESOURCES
388
- }, children: [_jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [withCard ? null : _jsx("span", { className: "text-sm font-medium", children: title ?? "Example" }), _jsx("span", { className: "text-xs text-muted-foreground", children: codeContract === "renderBody" ? "editable snippet" : "editable App.tsx" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [expandable ? (_jsx(SgButton, { appearance: "outline", size: "sm", onClick: () => setIsExpanded((prev) => !prev), children: isExpanded ? "Reduzir" : "Expandir" })) : null, _jsx(RunButton, { onRun: () => setActivePanel("preview") })] })] }), _jsxs("div", { className: "flex md:hidden border-b border-border", children: [_jsx("button", { type: "button", className: cn("flex-1 py-2 text-sm font-medium border-b-2 -mb-px transition-colors", activePanel === "code"
935
+ const sandpackCustomSetup = React.useMemo(() => ({
936
+ dependencies: deps,
937
+ entry: "/index.tsx",
938
+ ...(resolvedNpmRegistries ? { npmRegistries: resolvedNpmRegistries } : {})
939
+ }), [deps, resolvedNpmRegistries]);
940
+ const sandpackOptions = React.useMemo(() => ({
941
+ autorun: false,
942
+ initMode: "lazy",
943
+ bundlerURL: resolvedBundlerURL,
944
+ // Keep both keys while sandpack typings/runtime differ across versions.
945
+ bundlerTimeOut: resolvedBundlerTimeoutMs,
946
+ bundlerTimeout: resolvedBundlerTimeoutMs,
947
+ activeFile: "/App.tsx",
948
+ visibleFiles: ["/App.tsx"],
949
+ externalResources: includeSeedgridDependency ? SANDPACK_EXTERNAL_RESOURCES : []
950
+ }), [includeSeedgridDependency, resolvedBundlerTimeoutMs, resolvedBundlerURL]);
951
+ const content = interactive ? (_jsx("div", { className: cn(withCard ? "" : "rounded-lg border border-border", withCard ? undefined : className), children: _jsxs(SandpackProvider, { template: "react-ts", files: files, customSetup: sandpackCustomSetup, options: sandpackOptions, children: [_jsx("style", { children: SANDPACK_HOST_STYLES_CSS }), _jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [withCard ? null : _jsx("span", { className: "text-sm font-medium", children: title ?? "Example" }), _jsx("span", { className: "text-xs text-muted-foreground", children: codeContract === "renderBody" ? "editable snippet" : "editable App.tsx" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [expandable ? (_jsx(SgButton, { appearance: "outline", size: "sm", onClick: () => setIsExpanded((prev) => !prev), children: isExpanded ? "Reduzir" : "Expandir" })) : null, _jsx(RunButton, { onRun: () => setActivePanel("preview") })] })] }), _jsxs("div", { className: "flex md:hidden border-b border-border", children: [_jsx("button", { type: "button", className: cn("flex-1 py-2 text-sm font-medium border-b-2 -mb-px transition-colors", activePanel === "code"
389
952
  ? "border-primary text-foreground"
390
953
  : "border-transparent text-muted-foreground hover:text-foreground"), onClick: () => setActivePanel("code"), children: "C\u00F3digo" }), _jsx("button", { type: "button", className: cn("flex-1 py-2 text-sm font-medium border-b-2 -mb-px transition-colors", activePanel === "preview"
391
954
  ? "border-primary text-foreground"
392
- : "border-transparent text-muted-foreground hover:text-foreground"), onClick: () => setActivePanel("preview"), children: "Preview" })] }), _jsxs("div", { className: cn("grid overflow-auto min-h-[260px]", "grid-cols-1 md:grid-cols-2", resizeClass), style: { height: currentHeight }, children: [_jsxs("div", { className: cn("min-w-0 md:border-r border-border", activePanel !== "code" ? "hidden md:block" : ""), children: [_jsx(SandpackCodeEditor, { showLineNumbers: true, wrapContent: true, showTabs: false, showRunButton: false, style: { height: "100%" } }), _jsx("div", { className: "flex justify-end border-t border-border px-3 py-2", children: _jsx(CopyButton, {}) })] }), _jsx("div", { className: cn("min-w-0", activePanel !== "preview" ? "hidden md:block" : ""), children: _jsx(SandpackPreview, { style: { height: "100%" }, showOpenInCodeSandbox: false, showRefreshButton: false, showRestartButton: false }) })] })] }) })) : (_jsxs("div", { className: cn(withCard ? undefined : "space-y-2", withCard ? undefined : className), children: [withCard ? null : title ? _jsx("div", { className: "text-sm font-medium", children: title }) : null, _jsx(ReadonlyBlock, { code: code })] }));
955
+ : "border-transparent text-muted-foreground hover:text-foreground"), onClick: () => setActivePanel("preview"), children: "Preview" })] }), _jsxs("div", { className: cn("grid overflow-auto min-h-[260px]", "grid-cols-1 md:grid-cols-2", resizeClass), style: { height: currentHeight }, children: [_jsxs("div", { className: cn("min-w-0 md:border-r border-border", activePanel !== "code" ? "hidden md:block" : ""), children: [_jsx(SandpackCodeEditor, { showLineNumbers: true, wrapContent: true, showTabs: false, showRunButton: false, style: { height: "100%" } }), _jsx("div", { className: "flex justify-end border-t border-border px-3 py-2", children: _jsx(CopyButton, {}) })] }), _jsx("div", { className: cn("min-w-0", activePanel !== "preview" ? "hidden md:block" : ""), children: _jsx(SandpackPreview, { className: "sg-playground-preview", style: { height: "100%" }, showOpenInCodeSandbox: false, showRefreshButton: false, showRestartButton: false }) })] })] }) })) : (_jsxs("div", { className: cn(withCard ? undefined : "space-y-2", withCard ? undefined : className), children: [withCard ? null : title ? _jsx("div", { className: "text-sm font-medium", children: title }) : null, _jsx(ReadonlyBlock, { code: code })] }));
393
956
  if (!withCard)
394
957
  return content;
395
958
  return (_jsx(SgCard, { id: cardId, title: title ?? "Codigo", description: description, collapsible: collapsible, defaultOpen: defaultOpen, className: cn("rounded-lg", className), bodyClassName: "p-0", children: content }));