@kubuild/renderer 0.0.1 → 0.1.0
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/LICENSE +21 -0
- package/dist/animation.d.ts +26 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/code-generator.d.ts +47 -0
- package/dist/code-generator.d.ts.map +1 -0
- package/dist/error-boundary.d.ts.map +1 -1
- package/dist/index.cjs +751 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +741 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44754 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderer.d.ts.map +1 -1
- package/dist/styles.d.ts +8 -3
- package/dist/styles.d.ts.map +1 -1
- package/package.json +13 -14
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
ANIMATION_KEYFRAMES_CSS: () => ANIMATION_KEYFRAMES_CSS,
|
|
23
24
|
ComponentErrorBoundary: () => ComponentErrorBoundary,
|
|
24
25
|
DEFAULT_BREAKPOINTS: () => DEFAULT_BREAKPOINTS,
|
|
25
26
|
DEFAULT_CSS_RESET: () => DEFAULT_CSS_RESET,
|
|
@@ -32,11 +33,19 @@ __export(index_exports, {
|
|
|
32
33
|
NodeRenderer: () => NodeRenderer,
|
|
33
34
|
PreviewViewportAdapter: () => PreviewViewportAdapter,
|
|
34
35
|
RenderContextProvider: () => RenderContextProvider,
|
|
36
|
+
collectAnimationStylesCss: () => collectAnimationStylesCss,
|
|
35
37
|
collectStateStylesCss: () => collectStateStylesCss,
|
|
36
38
|
createMinimalRenderContext: () => createMinimalRenderContext,
|
|
37
39
|
createRenderContext: () => createRenderContext,
|
|
38
40
|
dispatchAction: () => dispatchAction,
|
|
41
|
+
generateDocumentCss: () => generateDocumentCss,
|
|
42
|
+
generateSemanticHtml: () => generateSemanticHtml,
|
|
43
|
+
generateStandaloneHtml: () => generateStandaloneHtml,
|
|
44
|
+
getEntranceAnimationCss: () => getEntranceAnimationCss,
|
|
45
|
+
getHoverEffectCss: () => getHoverEffectCss,
|
|
46
|
+
getLoopEffectCss: () => getLoopEffectCss,
|
|
39
47
|
isActionRegistered: () => isActionRegistered,
|
|
48
|
+
replayNodeAnimation: () => replayNodeAnimation,
|
|
40
49
|
resolveActionPayload: () => resolveActionPayload,
|
|
41
50
|
resolveActionPayloadDetailed: () => resolveActionPayloadDetailed,
|
|
42
51
|
resolveAssetSync: () => resolveAssetSync,
|
|
@@ -268,7 +277,7 @@ function dispatchAction(options) {
|
|
|
268
277
|
|
|
269
278
|
// src/renderer.tsx
|
|
270
279
|
var import_core3 = require("@kubuild/core");
|
|
271
|
-
var
|
|
280
|
+
var import_lucide_react2 = require("lucide-react");
|
|
272
281
|
|
|
273
282
|
// src/styles.ts
|
|
274
283
|
function resolveNodeStyles(styles, viewport = "desktop") {
|
|
@@ -283,17 +292,18 @@ function escapeCssValue(value) {
|
|
|
283
292
|
function escapeCssIdent(value) {
|
|
284
293
|
return value.replace(/["\\\]]/g, "\\$&");
|
|
285
294
|
}
|
|
286
|
-
function styleDefinitionToCssDeclarations(styleDefinition) {
|
|
295
|
+
function styleDefinitionToCssDeclarations(styleDefinition, options) {
|
|
287
296
|
if (!styleDefinition || typeof styleDefinition !== "object") return "";
|
|
288
297
|
const declarations = [];
|
|
298
|
+
const suffix = options?.important ? " !important" : "";
|
|
289
299
|
for (const [key, value] of Object.entries(styleDefinition)) {
|
|
290
300
|
if (value === null || value === void 0 || value === "") continue;
|
|
291
301
|
const property = key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
292
|
-
declarations.push(`${property}: ${escapeCssValue(value)};`);
|
|
302
|
+
declarations.push(`${property}: ${escapeCssValue(value)}${suffix};`);
|
|
293
303
|
}
|
|
294
304
|
return declarations.join(" ");
|
|
295
305
|
}
|
|
296
|
-
function collectStateStylesCss(document) {
|
|
306
|
+
function collectStateStylesCss(document, options = { important: true }) {
|
|
297
307
|
if (!document?.document) return "";
|
|
298
308
|
const rules = [];
|
|
299
309
|
const walk = (node) => {
|
|
@@ -301,7 +311,8 @@ function collectStateStylesCss(document) {
|
|
|
301
311
|
if (states && typeof states === "object") {
|
|
302
312
|
for (const [state, styleDefinition] of Object.entries(states)) {
|
|
303
313
|
const declarations = styleDefinitionToCssDeclarations(
|
|
304
|
-
styleDefinition
|
|
314
|
+
styleDefinition,
|
|
315
|
+
{ important: options.important !== false }
|
|
305
316
|
);
|
|
306
317
|
if (!declarations) continue;
|
|
307
318
|
const safeState = /^::?[a-zA-Z-]+$/.test(state) ? state : null;
|
|
@@ -342,8 +353,208 @@ p, h1, h2, h3, h4, h5, h6 {
|
|
|
342
353
|
}
|
|
343
354
|
`.trim();
|
|
344
355
|
|
|
356
|
+
// src/animation.ts
|
|
357
|
+
var ANIMATION_KEYFRAMES_CSS = `
|
|
358
|
+
/* Entrance / AOS Keyframes */
|
|
359
|
+
@keyframes kb-anim-fade {
|
|
360
|
+
from { opacity: 0; }
|
|
361
|
+
to { opacity: 1; }
|
|
362
|
+
}
|
|
363
|
+
@keyframes kb-anim-fade-up {
|
|
364
|
+
from { opacity: 0; transform: translateY(24px); }
|
|
365
|
+
to { opacity: 1; transform: translateY(0); }
|
|
366
|
+
}
|
|
367
|
+
@keyframes kb-anim-fade-down {
|
|
368
|
+
from { opacity: 0; transform: translateY(-24px); }
|
|
369
|
+
to { opacity: 1; transform: translateY(0); }
|
|
370
|
+
}
|
|
371
|
+
@keyframes kb-anim-fade-left {
|
|
372
|
+
from { opacity: 0; transform: translateX(24px); }
|
|
373
|
+
to { opacity: 1; transform: translateX(0); }
|
|
374
|
+
}
|
|
375
|
+
@keyframes kb-anim-fade-right {
|
|
376
|
+
from { opacity: 0; transform: translateX(-24px); }
|
|
377
|
+
to { opacity: 1; transform: translateX(0); }
|
|
378
|
+
}
|
|
379
|
+
@keyframes kb-anim-zoom-in {
|
|
380
|
+
from { opacity: 0; transform: scale(0.92); }
|
|
381
|
+
to { opacity: 1; transform: scale(1); }
|
|
382
|
+
}
|
|
383
|
+
@keyframes kb-anim-zoom-out {
|
|
384
|
+
from { opacity: 0; transform: scale(1.08); }
|
|
385
|
+
to { opacity: 1; transform: scale(1); }
|
|
386
|
+
}
|
|
387
|
+
@keyframes kb-anim-slide-up {
|
|
388
|
+
from { transform: translateY(100%); }
|
|
389
|
+
to { transform: translateY(0); }
|
|
390
|
+
}
|
|
391
|
+
@keyframes kb-anim-slide-down {
|
|
392
|
+
from { transform: translateY(-100%); }
|
|
393
|
+
to { transform: translateY(0); }
|
|
394
|
+
}
|
|
395
|
+
@keyframes kb-anim-slide-left {
|
|
396
|
+
from { transform: translateX(100%); }
|
|
397
|
+
to { transform: translateX(0); }
|
|
398
|
+
}
|
|
399
|
+
@keyframes kb-anim-slide-right {
|
|
400
|
+
from { transform: translateX(-100%); }
|
|
401
|
+
to { transform: translateX(0); }
|
|
402
|
+
}
|
|
403
|
+
@keyframes kb-anim-flip-up {
|
|
404
|
+
from { opacity: 0; transform: perspective(600px) rotateX(45deg); }
|
|
405
|
+
to { opacity: 1; transform: perspective(600px) rotateX(0deg); }
|
|
406
|
+
}
|
|
407
|
+
@keyframes kb-anim-flip-down {
|
|
408
|
+
from { opacity: 0; transform: perspective(600px) rotateX(-45deg); }
|
|
409
|
+
to { opacity: 1; transform: perspective(600px) rotateX(0deg); }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Continuous Loop Keyframes */
|
|
413
|
+
@keyframes kb-loop-pulse {
|
|
414
|
+
0%, 100% { transform: scale(1); opacity: 1; }
|
|
415
|
+
50% { transform: scale(1.05); opacity: 0.9; }
|
|
416
|
+
}
|
|
417
|
+
@keyframes kb-loop-bounce {
|
|
418
|
+
0%, 100% { transform: translateY(0); }
|
|
419
|
+
50% { transform: translateY(-8px); }
|
|
420
|
+
}
|
|
421
|
+
@keyframes kb-loop-spin {
|
|
422
|
+
from { transform: rotate(0deg); }
|
|
423
|
+
to { transform: rotate(360deg); }
|
|
424
|
+
}
|
|
425
|
+
@keyframes kb-loop-float {
|
|
426
|
+
0%, 100% { transform: translateY(0); }
|
|
427
|
+
50% { transform: translateY(-6px); }
|
|
428
|
+
}
|
|
429
|
+
@keyframes kb-loop-shimmer {
|
|
430
|
+
0%, 100% { opacity: 0.75; }
|
|
431
|
+
50% { opacity: 1; }
|
|
432
|
+
}
|
|
433
|
+
`.trim();
|
|
434
|
+
function escapeCssIdent2(value) {
|
|
435
|
+
return value.replace(/["\\\]]/g, "\\$&");
|
|
436
|
+
}
|
|
437
|
+
function getHoverEffectCss(nodeId, hoverEffect) {
|
|
438
|
+
const safeId = escapeCssIdent2(nodeId);
|
|
439
|
+
const rules = [];
|
|
440
|
+
switch (hoverEffect) {
|
|
441
|
+
case "lift":
|
|
442
|
+
rules.push(
|
|
443
|
+
`[data-kubuild-node="${safeId}"] { transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.25s ease !important; will-change: transform; }`,
|
|
444
|
+
`[data-kubuild-node="${safeId}"]:hover { transform: translateY(-4px) !important; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1) !important; }`
|
|
445
|
+
);
|
|
446
|
+
break;
|
|
447
|
+
case "scale":
|
|
448
|
+
rules.push(
|
|
449
|
+
`[data-kubuild-node="${safeId}"] { transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; will-change: transform; }`,
|
|
450
|
+
`[data-kubuild-node="${safeId}"]:hover { transform: scale(1.04) !important; }`
|
|
451
|
+
);
|
|
452
|
+
break;
|
|
453
|
+
case "glow":
|
|
454
|
+
rules.push(
|
|
455
|
+
`[data-kubuild-node="${safeId}"] { transition: box-shadow 0.25s ease !important; }`,
|
|
456
|
+
`[data-kubuild-node="${safeId}"]:hover { box-shadow: 0 0 20px 2px rgba(59, 130, 246, 0.5) !important; }`
|
|
457
|
+
);
|
|
458
|
+
break;
|
|
459
|
+
case "tilt":
|
|
460
|
+
rules.push(
|
|
461
|
+
`[data-kubuild-node="${safeId}"] { transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important; will-change: transform; }`,
|
|
462
|
+
`[data-kubuild-node="${safeId}"]:hover { transform: rotate(2deg) scale(1.02) !important; }`
|
|
463
|
+
);
|
|
464
|
+
break;
|
|
465
|
+
default:
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
return rules;
|
|
469
|
+
}
|
|
470
|
+
function getLoopEffectCss(nodeId, loopEffect) {
|
|
471
|
+
const safeId = escapeCssIdent2(nodeId);
|
|
472
|
+
const rules = [];
|
|
473
|
+
switch (loopEffect) {
|
|
474
|
+
case "pulse":
|
|
475
|
+
rules.push(
|
|
476
|
+
`[data-kubuild-node="${safeId}"] { animation: kb-loop-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important; }`
|
|
477
|
+
);
|
|
478
|
+
break;
|
|
479
|
+
case "bounce":
|
|
480
|
+
rules.push(
|
|
481
|
+
`[data-kubuild-node="${safeId}"] { animation: kb-loop-bounce 1.5s ease-in-out infinite !important; }`
|
|
482
|
+
);
|
|
483
|
+
break;
|
|
484
|
+
case "spin":
|
|
485
|
+
rules.push(
|
|
486
|
+
`[data-kubuild-node="${safeId}"] { animation: kb-loop-spin 3s linear infinite !important; }`
|
|
487
|
+
);
|
|
488
|
+
break;
|
|
489
|
+
case "float":
|
|
490
|
+
rules.push(
|
|
491
|
+
`[data-kubuild-node="${safeId}"] { animation: kb-loop-float 3s ease-in-out infinite !important; }`
|
|
492
|
+
);
|
|
493
|
+
break;
|
|
494
|
+
case "shimmer":
|
|
495
|
+
rules.push(
|
|
496
|
+
`[data-kubuild-node="${safeId}"] { animation: kb-loop-shimmer 2s ease-in-out infinite !important; }`
|
|
497
|
+
);
|
|
498
|
+
break;
|
|
499
|
+
default:
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
return rules;
|
|
503
|
+
}
|
|
504
|
+
function getEntranceAnimationCss(nodeId, anim) {
|
|
505
|
+
if (!anim.type || anim.type === "none") return [];
|
|
506
|
+
const safeId = escapeCssIdent2(nodeId);
|
|
507
|
+
const duration = typeof anim.duration === "number" ? anim.duration : 600;
|
|
508
|
+
const delay = typeof anim.delay === "number" ? anim.delay : 0;
|
|
509
|
+
const easing = anim.easing || "ease-out";
|
|
510
|
+
return [
|
|
511
|
+
`[data-kubuild-node="${safeId}"] { animation-name: kb-anim-${anim.type} !important; animation-duration: ${duration}ms !important; animation-delay: ${delay}ms !important; animation-timing-function: ${easing} !important; animation-fill-mode: both !important; }`
|
|
512
|
+
];
|
|
513
|
+
}
|
|
514
|
+
function collectAnimationStylesCss(document) {
|
|
515
|
+
if (!document?.document) return "";
|
|
516
|
+
const rules = [];
|
|
517
|
+
let hasAnyAnimation = false;
|
|
518
|
+
const walk = (node) => {
|
|
519
|
+
const anim = node.animation;
|
|
520
|
+
if (anim) {
|
|
521
|
+
if (anim.hoverEffect && anim.hoverEffect !== "none") {
|
|
522
|
+
rules.push(...getHoverEffectCss(node.id, anim.hoverEffect));
|
|
523
|
+
hasAnyAnimation = true;
|
|
524
|
+
}
|
|
525
|
+
if (anim.loopEffect && anim.loopEffect !== "none") {
|
|
526
|
+
rules.push(...getLoopEffectCss(node.id, anim.loopEffect));
|
|
527
|
+
hasAnyAnimation = true;
|
|
528
|
+
}
|
|
529
|
+
if (anim.type && anim.type !== "none") {
|
|
530
|
+
rules.push(...getEntranceAnimationCss(node.id, anim));
|
|
531
|
+
hasAnyAnimation = true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
node.children?.forEach(walk);
|
|
535
|
+
};
|
|
536
|
+
walk(document.document);
|
|
537
|
+
if (!hasAnyAnimation) return "";
|
|
538
|
+
return `${ANIMATION_KEYFRAMES_CSS}
|
|
539
|
+
|
|
540
|
+
${rules.join("\n")}`;
|
|
541
|
+
}
|
|
542
|
+
function replayNodeAnimation(nodeId, rootElement) {
|
|
543
|
+
const root = rootElement || (typeof window !== "undefined" ? window.document : null);
|
|
544
|
+
if (!root) return false;
|
|
545
|
+
const el = root.querySelector(`[data-kubuild-node="${escapeCssIdent2(nodeId)}"]`);
|
|
546
|
+
if (!el) return false;
|
|
547
|
+
const currentAnimation = el.style.animation;
|
|
548
|
+
el.style.animation = "none";
|
|
549
|
+
void el.offsetWidth;
|
|
550
|
+
el.style.animation = currentAnimation;
|
|
551
|
+
el.dispatchEvent(new CustomEvent("kubuild:replay-animation", { bubbles: true, detail: { nodeId } }));
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
|
|
345
555
|
// src/error-boundary.tsx
|
|
346
556
|
var import_react2 = require("react");
|
|
557
|
+
var import_lucide_react = require("lucide-react");
|
|
347
558
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
348
559
|
var ComponentErrorBoundary = class extends import_react2.Component {
|
|
349
560
|
constructor(props) {
|
|
@@ -380,10 +591,13 @@ var ComponentErrorBoundary = class extends import_react2.Component {
|
|
|
380
591
|
lineHeight: "1.4"
|
|
381
592
|
},
|
|
382
593
|
children: [
|
|
383
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontWeight: 600, marginBottom: "4px" }, children: [
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontWeight: 600, marginBottom: "4px", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.AlertTriangle, { size: 14, "aria-hidden": "true" }),
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
597
|
+
"Component Render Error: <",
|
|
598
|
+
componentType,
|
|
599
|
+
">"
|
|
600
|
+
] })
|
|
387
601
|
] }),
|
|
388
602
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontSize: "11px", color: "#7f1d1d", wordBreak: "break-all" }, children: [
|
|
389
603
|
"Node ID: ",
|
|
@@ -1245,7 +1459,7 @@ function NodeRenderer({
|
|
|
1245
1459
|
const color = typeof resolvedProps.color === "string" ? resolvedProps.color : typeof props.color === "string" ? props.color : "currentColor";
|
|
1246
1460
|
const strokeWidth = typeof resolvedProps.strokeWidth === "number" ? resolvedProps.strokeWidth : typeof props.strokeWidth === "number" ? props.strokeWidth : 2;
|
|
1247
1461
|
const pascalName = toPascalCase(name);
|
|
1248
|
-
const IconComponent =
|
|
1462
|
+
const IconComponent = import_lucide_react2.icons[pascalName];
|
|
1249
1463
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1250
1464
|
"span",
|
|
1251
1465
|
{
|
|
@@ -1642,9 +1856,12 @@ function NodeRenderer({
|
|
|
1642
1856
|
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
1643
1857
|
},
|
|
1644
1858
|
onClick: handleClick,
|
|
1645
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, fontSize: "13px" }, children: [
|
|
1646
|
-
|
|
1647
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1859
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, fontSize: "13px", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Package, { size: 14, "aria-hidden": "true" }),
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
1862
|
+
"Collection: expected an array at ",
|
|
1863
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: sourceKey ?? "(missing sourceKey)" })
|
|
1864
|
+
] })
|
|
1648
1865
|
] })
|
|
1649
1866
|
}
|
|
1650
1867
|
);
|
|
@@ -1695,9 +1912,12 @@ function NodeRenderer({
|
|
|
1695
1912
|
},
|
|
1696
1913
|
onClick: handleClick,
|
|
1697
1914
|
children: [
|
|
1698
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, fontSize: "13px", marginBottom: "4px" }, children: [
|
|
1699
|
-
|
|
1700
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, fontSize: "13px", marginBottom: "4px", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Puzzle, { size: 14, "aria-hidden": "true" }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
1918
|
+
"Unknown Component: ",
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: node.type })
|
|
1920
|
+
] })
|
|
1701
1921
|
] }),
|
|
1702
1922
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "11px", color: "#b45309", marginBottom: childrenElements ? "8px" : 0 }, children: [
|
|
1703
1923
|
"Node ID: ",
|
|
@@ -1743,10 +1963,13 @@ function NodeRenderer({
|
|
|
1743
1963
|
lineHeight: "1.4"
|
|
1744
1964
|
},
|
|
1745
1965
|
children: [
|
|
1746
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, marginBottom: "4px" }, children: [
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, marginBottom: "4px", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.AlertTriangle, { size: 14, "aria-hidden": "true" }),
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
1969
|
+
"Component Render Error: <",
|
|
1970
|
+
node.type,
|
|
1971
|
+
">"
|
|
1972
|
+
] })
|
|
1750
1973
|
] }),
|
|
1751
1974
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "11px", color: "#7f1d1d", wordBreak: "break-all" }, children: [
|
|
1752
1975
|
"Node ID: ",
|
|
@@ -1791,6 +2014,10 @@ var KubuildRenderer = ({
|
|
|
1791
2014
|
const css = collectStateStylesCss(document);
|
|
1792
2015
|
return css ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { "data-kubuild-state-styles": true, children: css }) : null;
|
|
1793
2016
|
})(),
|
|
2017
|
+
(() => {
|
|
2018
|
+
const animCss = collectAnimationStylesCss(document);
|
|
2019
|
+
return animCss ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { "data-kubuild-animation-styles": true, children: animCss }) : null;
|
|
2020
|
+
})(),
|
|
1794
2021
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1795
2022
|
NodeRenderer,
|
|
1796
2023
|
{
|
|
@@ -2050,8 +2277,504 @@ var PreviewViewportAdapter = ({
|
|
|
2050
2277
|
);
|
|
2051
2278
|
};
|
|
2052
2279
|
var KubuildPreviewViewport = PreviewViewportAdapter;
|
|
2280
|
+
|
|
2281
|
+
// src/code-generator.ts
|
|
2282
|
+
function escapeHtml(str) {
|
|
2283
|
+
if (str === null || str === void 0) return "";
|
|
2284
|
+
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2285
|
+
}
|
|
2286
|
+
function escapeAttr(str) {
|
|
2287
|
+
return escapeHtml(str);
|
|
2288
|
+
}
|
|
2289
|
+
function getYouTubeId2(url) {
|
|
2290
|
+
if (!url || typeof url !== "string") return null;
|
|
2291
|
+
const match = url.match(
|
|
2292
|
+
/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i
|
|
2293
|
+
);
|
|
2294
|
+
return match ? match[1] : null;
|
|
2295
|
+
}
|
|
2296
|
+
function getVimeoId2(url) {
|
|
2297
|
+
if (!url || typeof url !== "string") return null;
|
|
2298
|
+
const match = url.match(
|
|
2299
|
+
/(?:vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|video\/|))(\d+)/i
|
|
2300
|
+
);
|
|
2301
|
+
return match ? match[3] : null;
|
|
2302
|
+
}
|
|
2303
|
+
function renderNodeToHtml(node, indentLevel, options) {
|
|
2304
|
+
const indent = " ".repeat(indentLevel * (options.indentSize ?? 2));
|
|
2305
|
+
const innerIndent = " ".repeat((indentLevel + 1) * (options.indentSize ?? 2));
|
|
2306
|
+
const props = node.props || {};
|
|
2307
|
+
const children = node.children || [];
|
|
2308
|
+
const includeNodeClass = options.includeNodeClasses !== false;
|
|
2309
|
+
const nodeClass = includeNodeClass ? `kb-node-${node.id}` : "";
|
|
2310
|
+
const buildClass = (...classes) => {
|
|
2311
|
+
return classes.filter(Boolean).join(" ");
|
|
2312
|
+
};
|
|
2313
|
+
const idAttr = props.id ? ` id="${escapeAttr(props.id)}"` : "";
|
|
2314
|
+
switch (node.type) {
|
|
2315
|
+
case "page": {
|
|
2316
|
+
const tag = options.rootTag || "main";
|
|
2317
|
+
const cls = buildClass("kb-page", nodeClass);
|
|
2318
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2319
|
+
if (!childHtml) {
|
|
2320
|
+
return `${indent}<${tag} class="${cls}"${idAttr}></${tag}>`;
|
|
2321
|
+
}
|
|
2322
|
+
return `${indent}<${tag} class="${cls}"${idAttr}>
|
|
2323
|
+
${childHtml}
|
|
2324
|
+
${indent}</${tag}>`;
|
|
2325
|
+
}
|
|
2326
|
+
case "section": {
|
|
2327
|
+
const ariaLabel = props.ariaLabel ? ` aria-label="${escapeAttr(props.ariaLabel)}"` : "";
|
|
2328
|
+
const cls = buildClass("kb-section", nodeClass);
|
|
2329
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2330
|
+
if (!childHtml) {
|
|
2331
|
+
return `${indent}<section class="${cls}"${idAttr}${ariaLabel}></section>`;
|
|
2332
|
+
}
|
|
2333
|
+
return `${indent}<section class="${cls}"${idAttr}${ariaLabel}>
|
|
2334
|
+
${childHtml}
|
|
2335
|
+
${indent}</section>`;
|
|
2336
|
+
}
|
|
2337
|
+
case "container": {
|
|
2338
|
+
const cls = buildClass("kb-container", nodeClass);
|
|
2339
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2340
|
+
if (!childHtml) {
|
|
2341
|
+
return `${indent}<div class="${cls}"${idAttr}></div>`;
|
|
2342
|
+
}
|
|
2343
|
+
return `${indent}<div class="${cls}"${idAttr}>
|
|
2344
|
+
${childHtml}
|
|
2345
|
+
${indent}</div>`;
|
|
2346
|
+
}
|
|
2347
|
+
case "columns": {
|
|
2348
|
+
const cls = buildClass("kb-columns", nodeClass);
|
|
2349
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2350
|
+
if (!childHtml) {
|
|
2351
|
+
return `${indent}<div class="${cls}"${idAttr}></div>`;
|
|
2352
|
+
}
|
|
2353
|
+
return `${indent}<div class="${cls}"${idAttr}>
|
|
2354
|
+
${childHtml}
|
|
2355
|
+
${indent}</div>`;
|
|
2356
|
+
}
|
|
2357
|
+
case "heading": {
|
|
2358
|
+
let level = "h2";
|
|
2359
|
+
if (typeof props.level === "string" && /^h[1-6]$/i.test(props.level)) {
|
|
2360
|
+
level = props.level.toLowerCase();
|
|
2361
|
+
} else if (typeof props.level === "number" && props.level >= 1 && props.level <= 6) {
|
|
2362
|
+
level = `h${props.level}`;
|
|
2363
|
+
} else if (typeof props.tag === "string" && /^h[1-6]$/i.test(props.tag)) {
|
|
2364
|
+
level = props.tag.toLowerCase();
|
|
2365
|
+
}
|
|
2366
|
+
const text = props.text ?? props.value ?? props.content ?? "Heading";
|
|
2367
|
+
const cls = buildClass("kb-heading", nodeClass);
|
|
2368
|
+
return `${indent}<${level} class="${cls}"${idAttr}>${escapeHtml(text)}</${level}>`;
|
|
2369
|
+
}
|
|
2370
|
+
case "paragraph": {
|
|
2371
|
+
const text = props.text ?? props.value ?? props.content ?? "";
|
|
2372
|
+
const cls = buildClass("kb-paragraph", nodeClass);
|
|
2373
|
+
return `${indent}<p class="${cls}"${idAttr}>${escapeHtml(text)}</p>`;
|
|
2374
|
+
}
|
|
2375
|
+
case "text": {
|
|
2376
|
+
const tag = props.as || "p";
|
|
2377
|
+
const text = props.text ?? props.value ?? props.content ?? "";
|
|
2378
|
+
const cls = buildClass("kb-text", nodeClass);
|
|
2379
|
+
return `${indent}<${tag} class="${cls}"${idAttr}>${escapeHtml(text)}</${tag}>`;
|
|
2380
|
+
}
|
|
2381
|
+
case "link": {
|
|
2382
|
+
const href = props.href ? ` href="${escapeAttr(props.href)}"` : ' href="#"';
|
|
2383
|
+
const target = props.target ? ` target="${escapeAttr(props.target)}"` : "";
|
|
2384
|
+
const rel = props.rel ? ` rel="${escapeAttr(props.rel)}"` : target.includes("_blank") ? ' rel="noopener noreferrer"' : "";
|
|
2385
|
+
const text = props.text ?? props.label ?? props.value;
|
|
2386
|
+
const cls = buildClass("kb-link", nodeClass);
|
|
2387
|
+
if (children.length > 0) {
|
|
2388
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2389
|
+
return `${indent}<a class="${cls}"${idAttr}${href}${target}${rel}>
|
|
2390
|
+
${childHtml}
|
|
2391
|
+
${indent}</a>`;
|
|
2392
|
+
}
|
|
2393
|
+
return `${indent}<a class="${cls}"${idAttr}${href}${target}${rel}>${escapeHtml(text ?? "Link")}</a>`;
|
|
2394
|
+
}
|
|
2395
|
+
case "blockquote": {
|
|
2396
|
+
const cite = props.cite ? ` cite="${escapeAttr(props.cite)}"` : "";
|
|
2397
|
+
const quote = props.quote ?? props.text ?? props.value;
|
|
2398
|
+
const author = props.author ?? props.citeAuthor;
|
|
2399
|
+
const cls = buildClass("kb-blockquote", nodeClass);
|
|
2400
|
+
if (quote || author) {
|
|
2401
|
+
const quoteHtml = quote ? `${innerIndent}<p>${escapeHtml(quote)}</p>` : "";
|
|
2402
|
+
const authorHtml = author ? `${innerIndent}<cite>${escapeHtml(author)}</cite>` : "";
|
|
2403
|
+
const parts = [quoteHtml, authorHtml].filter(Boolean).join("\n");
|
|
2404
|
+
return `${indent}<blockquote class="${cls}"${idAttr}${cite}>
|
|
2405
|
+
${parts}
|
|
2406
|
+
${indent}</blockquote>`;
|
|
2407
|
+
}
|
|
2408
|
+
if (children.length > 0) {
|
|
2409
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2410
|
+
return `${indent}<blockquote class="${cls}"${idAttr}${cite}>
|
|
2411
|
+
${childHtml}
|
|
2412
|
+
${indent}</blockquote>`;
|
|
2413
|
+
}
|
|
2414
|
+
return `${indent}<blockquote class="${cls}"${idAttr}${cite}></blockquote>`;
|
|
2415
|
+
}
|
|
2416
|
+
case "badge": {
|
|
2417
|
+
const text = props.text ?? props.label ?? props.value ?? "Badge";
|
|
2418
|
+
const cls = buildClass("kb-badge", nodeClass);
|
|
2419
|
+
return `${indent}<span class="${cls}"${idAttr}>${escapeHtml(text)}</span>`;
|
|
2420
|
+
}
|
|
2421
|
+
case "code-block": {
|
|
2422
|
+
const code = props.code ?? props.text ?? props.value ?? "";
|
|
2423
|
+
const lang = props.language || props.lang;
|
|
2424
|
+
const langClass = lang ? ` class="language-${escapeAttr(lang)}"` : "";
|
|
2425
|
+
const cls = buildClass("kb-code-block", nodeClass);
|
|
2426
|
+
return `${indent}<pre class="${cls}"${idAttr}><code${langClass}>${escapeHtml(code)}</code></pre>`;
|
|
2427
|
+
}
|
|
2428
|
+
case "divider": {
|
|
2429
|
+
const cls = buildClass("kb-divider", nodeClass);
|
|
2430
|
+
const text = props.text ?? props.label;
|
|
2431
|
+
if (text) {
|
|
2432
|
+
return `${indent}<div class="${cls}"${idAttr} role="separator"><span>${escapeHtml(text)}</span></div>`;
|
|
2433
|
+
}
|
|
2434
|
+
return `${indent}<hr class="${cls}"${idAttr} />`;
|
|
2435
|
+
}
|
|
2436
|
+
case "spacer": {
|
|
2437
|
+
const cls = buildClass("kb-spacer", nodeClass);
|
|
2438
|
+
return `${indent}<div class="${cls}"${idAttr} aria-hidden="true"></div>`;
|
|
2439
|
+
}
|
|
2440
|
+
case "image": {
|
|
2441
|
+
const src = props.src ? ` src="${escapeAttr(props.src)}"` : ' src=""';
|
|
2442
|
+
const alt = props.alt ? ` alt="${escapeAttr(props.alt)}"` : ' alt=""';
|
|
2443
|
+
const loading = props.loading ? ` loading="${escapeAttr(props.loading)}"` : ' loading="lazy"';
|
|
2444
|
+
const cls = buildClass("kb-image", nodeClass);
|
|
2445
|
+
return `${indent}<img class="${cls}"${idAttr}${src}${alt}${loading} />`;
|
|
2446
|
+
}
|
|
2447
|
+
case "video": {
|
|
2448
|
+
const src = props.src || "";
|
|
2449
|
+
const ytId = getYouTubeId2(src);
|
|
2450
|
+
const vmId = getVimeoId2(src);
|
|
2451
|
+
const cls = buildClass("kb-video", nodeClass);
|
|
2452
|
+
if (ytId) {
|
|
2453
|
+
return `${indent}<div class="kb-video-wrapper ${nodeClass}"${idAttr}>
|
|
2454
|
+
${innerIndent}<iframe src="https://www.youtube-nocookie.com/embed/${escapeAttr(ytId)}" title="${escapeAttr(props.title || "Video player")}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
|
2455
|
+
${indent}</div>`;
|
|
2456
|
+
}
|
|
2457
|
+
if (vmId) {
|
|
2458
|
+
return `${indent}<div class="kb-video-wrapper ${nodeClass}"${idAttr}>
|
|
2459
|
+
${innerIndent}<iframe src="https://player.vimeo.com/video/${escapeAttr(vmId)}" title="${escapeAttr(props.title || "Video player")}" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>
|
|
2460
|
+
${indent}</div>`;
|
|
2461
|
+
}
|
|
2462
|
+
const poster = props.poster ? ` poster="${escapeAttr(props.poster)}"` : "";
|
|
2463
|
+
const controls = props.controls !== false ? " controls" : "";
|
|
2464
|
+
const autoplay = props.autoplay ? " autoplay" : "";
|
|
2465
|
+
const loop = props.loop ? " loop" : "";
|
|
2466
|
+
const muted = props.muted ? " muted" : "";
|
|
2467
|
+
const videoSrc = src ? ` src="${escapeAttr(src)}"` : "";
|
|
2468
|
+
return `${indent}<video class="${cls}"${idAttr}${videoSrc}${poster}${controls}${autoplay}${loop}${muted}></video>`;
|
|
2469
|
+
}
|
|
2470
|
+
case "icon": {
|
|
2471
|
+
const name = props.name || props.icon || "star";
|
|
2472
|
+
const ariaLabel = props.ariaLabel ? ` aria-label="${escapeAttr(props.ariaLabel)}"` : ' aria-hidden="true"';
|
|
2473
|
+
const cls = buildClass("kb-icon", nodeClass);
|
|
2474
|
+
return `${indent}<span class="${cls}"${idAttr}${ariaLabel} data-icon="${escapeAttr(name)}"></span>`;
|
|
2475
|
+
}
|
|
2476
|
+
case "html-embed": {
|
|
2477
|
+
const rawHtml = props.html ?? props.content ?? "";
|
|
2478
|
+
const cls = buildClass("kb-html-embed", nodeClass);
|
|
2479
|
+
if (!rawHtml) {
|
|
2480
|
+
return `${indent}<div class="${cls}"${idAttr}></div>`;
|
|
2481
|
+
}
|
|
2482
|
+
return `${indent}<div class="${cls}"${idAttr}>
|
|
2483
|
+
${innerIndent}${rawHtml}
|
|
2484
|
+
${indent}</div>`;
|
|
2485
|
+
}
|
|
2486
|
+
case "button": {
|
|
2487
|
+
const label = props.label ?? props.text ?? props.value ?? "Button";
|
|
2488
|
+
const type = props.type ? ` type="${escapeAttr(props.type)}"` : ' type="button"';
|
|
2489
|
+
const disabled = props.disabled ? " disabled" : "";
|
|
2490
|
+
const cls = buildClass("kb-button", nodeClass);
|
|
2491
|
+
if (props.href) {
|
|
2492
|
+
const href = ` href="${escapeAttr(props.href)}"`;
|
|
2493
|
+
const target = props.target ? ` target="${escapeAttr(props.target)}"` : "";
|
|
2494
|
+
return `${indent}<a class="${cls}"${idAttr}${href}${target}>${escapeHtml(label)}</a>`;
|
|
2495
|
+
}
|
|
2496
|
+
return `${indent}<button class="${cls}"${idAttr}${type}${disabled}>${escapeHtml(label)}</button>`;
|
|
2497
|
+
}
|
|
2498
|
+
case "form": {
|
|
2499
|
+
const action = props.action ? ` action="${escapeAttr(props.action)}"` : "";
|
|
2500
|
+
const method = props.method ? ` method="${escapeAttr(props.method)}"` : ' method="POST"';
|
|
2501
|
+
const cls = buildClass("kb-form", nodeClass);
|
|
2502
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2503
|
+
if (!childHtml) {
|
|
2504
|
+
return `${indent}<form class="${cls}"${idAttr}${action}${method}></form>`;
|
|
2505
|
+
}
|
|
2506
|
+
return `${indent}<form class="${cls}"${idAttr}${action}${method}>
|
|
2507
|
+
${childHtml}
|
|
2508
|
+
${indent}</form>`;
|
|
2509
|
+
}
|
|
2510
|
+
case "input": {
|
|
2511
|
+
const type = props.type ? ` type="${escapeAttr(props.type)}"` : ' type="text"';
|
|
2512
|
+
const name = props.name ? ` name="${escapeAttr(props.name)}"` : "";
|
|
2513
|
+
const placeholder = props.placeholder ? ` placeholder="${escapeAttr(props.placeholder)}"` : "";
|
|
2514
|
+
const value = props.value !== void 0 ? ` value="${escapeAttr(props.value)}"` : "";
|
|
2515
|
+
const required = props.required ? " required" : "";
|
|
2516
|
+
const disabled = props.disabled ? " disabled" : "";
|
|
2517
|
+
const cls = buildClass("kb-input", nodeClass);
|
|
2518
|
+
return `${indent}<input class="${cls}"${idAttr}${type}${name}${placeholder}${value}${required}${disabled} />`;
|
|
2519
|
+
}
|
|
2520
|
+
case "textarea": {
|
|
2521
|
+
const name = props.name ? ` name="${escapeAttr(props.name)}"` : "";
|
|
2522
|
+
const placeholder = props.placeholder ? ` placeholder="${escapeAttr(props.placeholder)}"` : "";
|
|
2523
|
+
const rows = props.rows ? ` rows="${escapeAttr(props.rows)}"` : ' rows="4"';
|
|
2524
|
+
const value = props.value ?? props.defaultValue ?? "";
|
|
2525
|
+
const required = props.required ? " required" : "";
|
|
2526
|
+
const disabled = props.disabled ? " disabled" : "";
|
|
2527
|
+
const cls = buildClass("kb-textarea", nodeClass);
|
|
2528
|
+
return `${indent}<textarea class="${cls}"${idAttr}${name}${placeholder}${rows}${required}${disabled}>${escapeHtml(value)}</textarea>`;
|
|
2529
|
+
}
|
|
2530
|
+
case "select": {
|
|
2531
|
+
const name = props.name ? ` name="${escapeAttr(props.name)}"` : "";
|
|
2532
|
+
const required = props.required ? " required" : "";
|
|
2533
|
+
const disabled = props.disabled ? " disabled" : "";
|
|
2534
|
+
const cls = buildClass("kb-select", nodeClass);
|
|
2535
|
+
const optionsList = Array.isArray(props.options) ? props.options : [];
|
|
2536
|
+
const optionIndent = " ".repeat((indentLevel + 1) * (options.indentSize ?? 2));
|
|
2537
|
+
const optionsHtml = optionsList.map((opt) => {
|
|
2538
|
+
const optVal = typeof opt === "object" ? opt.value : opt;
|
|
2539
|
+
const optLabel = typeof opt === "object" ? opt.label : opt;
|
|
2540
|
+
const selected = props.value === optVal || props.defaultValue === optVal ? " selected" : "";
|
|
2541
|
+
return `${optionIndent}<option value="${escapeAttr(optVal)}"${selected}>${escapeHtml(optLabel)}</option>`;
|
|
2542
|
+
}).join("\n");
|
|
2543
|
+
if (!optionsHtml) {
|
|
2544
|
+
return `${indent}<select class="${cls}"${idAttr}${name}${required}${disabled}></select>`;
|
|
2545
|
+
}
|
|
2546
|
+
return `${indent}<select class="${cls}"${idAttr}${name}${required}${disabled}>
|
|
2547
|
+
${optionsHtml}
|
|
2548
|
+
${indent}</select>`;
|
|
2549
|
+
}
|
|
2550
|
+
case "checkbox": {
|
|
2551
|
+
const name = props.name ? ` name="${escapeAttr(props.name)}"` : "";
|
|
2552
|
+
const checked = props.checked || props.defaultChecked ? " checked" : "";
|
|
2553
|
+
const label = props.label ?? props.text ?? "";
|
|
2554
|
+
const cls = buildClass("kb-checkbox-label", nodeClass);
|
|
2555
|
+
return `${indent}<label class="${cls}"${idAttr}><input type="checkbox"${name}${checked} /><span>${escapeHtml(label)}</span></label>`;
|
|
2556
|
+
}
|
|
2557
|
+
case "radio": {
|
|
2558
|
+
const name = props.name ? ` name="${escapeAttr(props.name)}"` : "";
|
|
2559
|
+
const value = props.value ? ` value="${escapeAttr(props.value)}"` : "";
|
|
2560
|
+
const checked = props.checked || props.defaultChecked ? " checked" : "";
|
|
2561
|
+
const label = props.label ?? props.text ?? "";
|
|
2562
|
+
const cls = buildClass("kb-radio-label", nodeClass);
|
|
2563
|
+
return `${indent}<label class="${cls}"${idAttr}><input type="radio"${name}${value}${checked} /><span>${escapeHtml(label)}</span></label>`;
|
|
2564
|
+
}
|
|
2565
|
+
case "list": {
|
|
2566
|
+
const tag = props.tag === "ol" || props.type === "ol" || props.ordered ? "ol" : "ul";
|
|
2567
|
+
const cls = buildClass("kb-list", nodeClass);
|
|
2568
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2569
|
+
if (!childHtml) {
|
|
2570
|
+
return `${indent}<${tag} class="${cls}"${idAttr}></${tag}>`;
|
|
2571
|
+
}
|
|
2572
|
+
return `${indent}<${tag} class="${cls}"${idAttr}>
|
|
2573
|
+
${childHtml}
|
|
2574
|
+
${indent}</${tag}>`;
|
|
2575
|
+
}
|
|
2576
|
+
case "list-item": {
|
|
2577
|
+
const text = props.text ?? props.value;
|
|
2578
|
+
const cls = buildClass("kb-list-item", nodeClass);
|
|
2579
|
+
if (children.length > 0) {
|
|
2580
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2581
|
+
return `${indent}<li class="${cls}"${idAttr}>
|
|
2582
|
+
${childHtml}
|
|
2583
|
+
${indent}</li>`;
|
|
2584
|
+
}
|
|
2585
|
+
return `${indent}<li class="${cls}"${idAttr}>${escapeHtml(text ?? "List item")}</li>`;
|
|
2586
|
+
}
|
|
2587
|
+
case "table": {
|
|
2588
|
+
const cls = buildClass("kb-table", nodeClass);
|
|
2589
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2590
|
+
if (!childHtml) {
|
|
2591
|
+
return `${indent}<table class="${cls}"${idAttr}></table>`;
|
|
2592
|
+
}
|
|
2593
|
+
return `${indent}<table class="${cls}"${idAttr}>
|
|
2594
|
+
${childHtml}
|
|
2595
|
+
${indent}</table>`;
|
|
2596
|
+
}
|
|
2597
|
+
case "table-row": {
|
|
2598
|
+
const cls = buildClass("kb-table-row", nodeClass);
|
|
2599
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2600
|
+
if (!childHtml) {
|
|
2601
|
+
return `${indent}<tr class="${cls}"${idAttr}></tr>`;
|
|
2602
|
+
}
|
|
2603
|
+
return `${indent}<tr class="${cls}"${idAttr}>
|
|
2604
|
+
${childHtml}
|
|
2605
|
+
${indent}</tr>`;
|
|
2606
|
+
}
|
|
2607
|
+
case "table-cell": {
|
|
2608
|
+
const isHeader = props.isHeader || props.type === "header" || props.tag === "th";
|
|
2609
|
+
const tag = isHeader ? "th" : "td";
|
|
2610
|
+
const colSpan = props.colSpan && Number(props.colSpan) > 1 ? ` colspan="${escapeAttr(props.colSpan)}"` : "";
|
|
2611
|
+
const rowSpan = props.rowSpan && Number(props.rowSpan) > 1 ? ` rowspan="${escapeAttr(props.rowSpan)}"` : "";
|
|
2612
|
+
const text = props.text ?? props.value ?? "";
|
|
2613
|
+
const cls = buildClass("kb-table-cell", nodeClass);
|
|
2614
|
+
if (children.length > 0) {
|
|
2615
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2616
|
+
return `${indent}<${tag} class="${cls}"${idAttr}${colSpan}${rowSpan}>
|
|
2617
|
+
${childHtml}
|
|
2618
|
+
${indent}</${tag}>`;
|
|
2619
|
+
}
|
|
2620
|
+
return `${indent}<${tag} class="${cls}"${idAttr}${colSpan}${rowSpan}>${escapeHtml(text)}</${tag}>`;
|
|
2621
|
+
}
|
|
2622
|
+
case "collection": {
|
|
2623
|
+
const cls = buildClass("kb-collection", nodeClass);
|
|
2624
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2625
|
+
if (!childHtml) {
|
|
2626
|
+
return `${indent}<div class="${cls}"${idAttr}></div>`;
|
|
2627
|
+
}
|
|
2628
|
+
return `${indent}<div class="${cls}"${idAttr}>
|
|
2629
|
+
${childHtml}
|
|
2630
|
+
${indent}</div>`;
|
|
2631
|
+
}
|
|
2632
|
+
default: {
|
|
2633
|
+
const cls = buildClass(`kb-${node.type}`, nodeClass);
|
|
2634
|
+
const childHtml = children.map((child) => renderNodeToHtml(child, indentLevel + 1, options)).join("\n");
|
|
2635
|
+
if (!childHtml) {
|
|
2636
|
+
return `${indent}<div class="${cls}"${idAttr}></div>`;
|
|
2637
|
+
}
|
|
2638
|
+
return `${indent}<div class="${cls}"${idAttr}>
|
|
2639
|
+
${childHtml}
|
|
2640
|
+
${indent}</div>`;
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function generateSemanticHtml(docOrNode, options = {}) {
|
|
2645
|
+
const rootNode = "document" in docOrNode ? docOrNode.document : docOrNode;
|
|
2646
|
+
if (!rootNode) return "";
|
|
2647
|
+
return renderNodeToHtml(rootNode, 0, options);
|
|
2648
|
+
}
|
|
2649
|
+
function formatCssRule(selector, declarationsStr, indent = "") {
|
|
2650
|
+
if (!declarationsStr.trim()) return "";
|
|
2651
|
+
const rules = declarationsStr.split(";").map((r) => r.trim()).filter(Boolean);
|
|
2652
|
+
if (rules.length === 0) return "";
|
|
2653
|
+
const indentedRules = rules.map((r) => `${indent} ${r};`).join("\n");
|
|
2654
|
+
return `${indent}${selector} {
|
|
2655
|
+
${indentedRules}
|
|
2656
|
+
${indent}}`;
|
|
2657
|
+
}
|
|
2658
|
+
function generateDocumentCss(docOrNode, options = {}) {
|
|
2659
|
+
const rootNode = "document" in docOrNode ? docOrNode.document : docOrNode;
|
|
2660
|
+
if (!rootNode) return "";
|
|
2661
|
+
const classPrefix = options.classPrefix || "kb-node-";
|
|
2662
|
+
const baseRules = [];
|
|
2663
|
+
const tabletRules = [];
|
|
2664
|
+
const mobileRules = [];
|
|
2665
|
+
const stateRules = [];
|
|
2666
|
+
const walk = (node) => {
|
|
2667
|
+
const selector = `.${classPrefix}${node.id}`;
|
|
2668
|
+
if (node.styles) {
|
|
2669
|
+
const baseStyles = {
|
|
2670
|
+
...node.styles.base || {},
|
|
2671
|
+
...node.styles.desktop || {}
|
|
2672
|
+
};
|
|
2673
|
+
const baseDecls = styleDefinitionToCssDeclarations(baseStyles);
|
|
2674
|
+
if (baseDecls) {
|
|
2675
|
+
baseRules.push(formatCssRule(selector, baseDecls));
|
|
2676
|
+
}
|
|
2677
|
+
if (node.styles.tablet) {
|
|
2678
|
+
const tabletDecls = styleDefinitionToCssDeclarations(node.styles.tablet);
|
|
2679
|
+
if (tabletDecls) {
|
|
2680
|
+
tabletRules.push(formatCssRule(selector, tabletDecls, " "));
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
if (node.styles.mobile) {
|
|
2684
|
+
const mobileDecls = styleDefinitionToCssDeclarations(node.styles.mobile);
|
|
2685
|
+
if (mobileDecls) {
|
|
2686
|
+
mobileRules.push(formatCssRule(selector, mobileDecls, " "));
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
if (node.styles.states && typeof node.styles.states === "object") {
|
|
2690
|
+
for (const [state, styleDef] of Object.entries(node.styles.states)) {
|
|
2691
|
+
if (!styleDef) continue;
|
|
2692
|
+
const safeState = /^::?[a-zA-Z-]+$/.test(state) ? state : null;
|
|
2693
|
+
if (!safeState) continue;
|
|
2694
|
+
const stateDecls = styleDefinitionToCssDeclarations(styleDef);
|
|
2695
|
+
if (stateDecls) {
|
|
2696
|
+
stateRules.push(formatCssRule(`${selector}${safeState}`, stateDecls));
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
node.children?.forEach(walk);
|
|
2702
|
+
};
|
|
2703
|
+
walk(rootNode);
|
|
2704
|
+
const sections = [];
|
|
2705
|
+
if (options.includeReset !== false) {
|
|
2706
|
+
sections.push(`/* ==========================================================================
|
|
2707
|
+
Baseline Reset & Typography Standards
|
|
2708
|
+
========================================================================== */
|
|
2709
|
+
${DEFAULT_CSS_RESET}`);
|
|
2710
|
+
}
|
|
2711
|
+
if (baseRules.length > 0) {
|
|
2712
|
+
sections.push(`/* ==========================================================================
|
|
2713
|
+
Component Styles
|
|
2714
|
+
========================================================================== */
|
|
2715
|
+
${baseRules.join("\n\n")}`);
|
|
2716
|
+
}
|
|
2717
|
+
if (stateRules.length > 0) {
|
|
2718
|
+
sections.push(`/* ==========================================================================
|
|
2719
|
+
Interactive & Hover States
|
|
2720
|
+
========================================================================== */
|
|
2721
|
+
${stateRules.join("\n\n")}`);
|
|
2722
|
+
}
|
|
2723
|
+
if (tabletRules.length > 0) {
|
|
2724
|
+
sections.push(`/* ==========================================================================
|
|
2725
|
+
Tablet Breakpoint (max-width: 1024px)
|
|
2726
|
+
========================================================================== */
|
|
2727
|
+
@media (max-width: 1024px) {
|
|
2728
|
+
${tabletRules.join("\n\n")}
|
|
2729
|
+
}`);
|
|
2730
|
+
}
|
|
2731
|
+
if (mobileRules.length > 0) {
|
|
2732
|
+
sections.push(`/* ==========================================================================
|
|
2733
|
+
Mobile Breakpoint (max-width: 640px)
|
|
2734
|
+
========================================================================== */
|
|
2735
|
+
@media (max-width: 640px) {
|
|
2736
|
+
${mobileRules.join("\n\n")}
|
|
2737
|
+
}`);
|
|
2738
|
+
}
|
|
2739
|
+
const pageDoc = "document" in docOrNode ? docOrNode : { schema: "stora.page", version: "1.0.0", document: rootNode };
|
|
2740
|
+
const animStyles = collectAnimationStylesCss(pageDoc);
|
|
2741
|
+
if (animStyles) {
|
|
2742
|
+
sections.push(`/* ==========================================================================
|
|
2743
|
+
Animations & Motion Keyframes
|
|
2744
|
+
========================================================================== */
|
|
2745
|
+
${animStyles}`);
|
|
2746
|
+
}
|
|
2747
|
+
return sections.join("\n\n");
|
|
2748
|
+
}
|
|
2749
|
+
function generateStandaloneHtml(doc, options = {}) {
|
|
2750
|
+
const title = escapeHtml(doc.metadata?.title || "KUBUILD Page");
|
|
2751
|
+
const description = doc.metadata?.description ? escapeAttr(doc.metadata.description) : "";
|
|
2752
|
+
const author = doc.metadata?.author ? escapeAttr(doc.metadata.author) : "";
|
|
2753
|
+
const lang = options.lang || "en";
|
|
2754
|
+
const css = generateDocumentCss(doc, options.cssOptions);
|
|
2755
|
+
const html = generateSemanticHtml(doc, options.htmlOptions);
|
|
2756
|
+
const metaDescription = description ? ` <meta name="description" content="${description}">
|
|
2757
|
+
` : "";
|
|
2758
|
+
const metaAuthor = author ? ` <meta name="author" content="${author}">
|
|
2759
|
+
` : "";
|
|
2760
|
+
return `<!DOCTYPE html>
|
|
2761
|
+
<html lang="${lang}">
|
|
2762
|
+
<head>
|
|
2763
|
+
<meta charset="UTF-8">
|
|
2764
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2765
|
+
<title>${title}</title>
|
|
2766
|
+
${metaDescription}${metaAuthor} <style>
|
|
2767
|
+
${css}
|
|
2768
|
+
</style>
|
|
2769
|
+
</head>
|
|
2770
|
+
<body>
|
|
2771
|
+
${html}
|
|
2772
|
+
</body>
|
|
2773
|
+
</html>`;
|
|
2774
|
+
}
|
|
2053
2775
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2054
2776
|
0 && (module.exports = {
|
|
2777
|
+
ANIMATION_KEYFRAMES_CSS,
|
|
2055
2778
|
ComponentErrorBoundary,
|
|
2056
2779
|
DEFAULT_BREAKPOINTS,
|
|
2057
2780
|
DEFAULT_CSS_RESET,
|
|
@@ -2064,11 +2787,19 @@ var KubuildPreviewViewport = PreviewViewportAdapter;
|
|
|
2064
2787
|
NodeRenderer,
|
|
2065
2788
|
PreviewViewportAdapter,
|
|
2066
2789
|
RenderContextProvider,
|
|
2790
|
+
collectAnimationStylesCss,
|
|
2067
2791
|
collectStateStylesCss,
|
|
2068
2792
|
createMinimalRenderContext,
|
|
2069
2793
|
createRenderContext,
|
|
2070
2794
|
dispatchAction,
|
|
2795
|
+
generateDocumentCss,
|
|
2796
|
+
generateSemanticHtml,
|
|
2797
|
+
generateStandaloneHtml,
|
|
2798
|
+
getEntranceAnimationCss,
|
|
2799
|
+
getHoverEffectCss,
|
|
2800
|
+
getLoopEffectCss,
|
|
2071
2801
|
isActionRegistered,
|
|
2802
|
+
replayNodeAnimation,
|
|
2072
2803
|
resolveActionPayload,
|
|
2073
2804
|
resolveActionPayloadDetailed,
|
|
2074
2805
|
resolveAssetSync,
|