1import{r as n,j as t,b as ee,a as se,H as ke,L as Se}from"./app-DP13319Q.js";import{F as T,c as Ce,Z as Ee,h as Me,aV as le,a4 as Ie,aW as Be,aM as Re,aX as je,S as Pe,ai as He,aY as Le,ar as Ae,aZ as Te,aE as De,a_ as ze,a$ as Ne,ab as Oe,aG as _e,az as Ye,b0 as qe,aT as Fe,aR as We,aw as Ge,b1 as Ue,P as Xe,af as $e,b2 as Ze}from"./index-CtyZ6W6U.js";import{i as Ke}from"./index-DQkUn08L.js";const Q=`<!DOCTYPE html> 2<html lang="en"> 3<head> 4<title>Embed Row & Masonry Builder</title> 5<meta name="viewport" content="width=device-width, initial-scale=1" /> 6<link rel="preconnect" href="https://fonts.googleapis.com"> 7<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 8<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet"> 9 10<style> 11 :root { 12 --paper: #f3f4f1; 13 --paper-raised: #ffffff; 14 --ink: #14171a; 15 --ink-soft: #5b6066; 16 --ink-faint: #8b9096; 17 --line: #d9dbd4; 18 --line-strong: #c2c5bd; 19 --accent: #2f6fed; 20 --accent-ink: #ffffff; 21 --accent-soft: #e4ecfd; 22 --success: #1f8f5f; 23 --success-soft: #e2f4ea; 24 --code-bg: #0f1216; 25 --code-ink: #d9dee5; 26 --code-line: #262b32; 27 --code-accent: #7fb0ff; 28 --shadow: 0 1px 2px rgba(20, 23, 26, 0.04), 0 8px 24px -12px rgba(20, 23, 26, 0.12); 29 --radius: 10px; 30 } 31 32 @media (prefers-color-scheme: dark) { 33 :root:not([data-theme="light"]) { 34 --paper: #15171a; 35 --paper-raised: #1d2024; 36 --ink: #edeeea; 37 --ink-soft: #a3a8ad; 38 --ink-faint: #6b7176; 39 --line: #2c2f34; 40 --line-strong: #3b3f45; 41 --accent: #6c98ff; 42 --accent-ink: #0f1115; 43 --accent-soft: #212a3b; 44 --success: #4fbf8f; 45 --success-soft: #16261f; 46 --code-bg: #0a0c0e; 47 --code-ink: #dbe1e7; 48 --code-line: #22262b; 49 --code-accent: #8cbaff; 50 --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -12px rgba(0, 0, 0, 0.5); 51 } 52 } 53 54 :root[data-theme="dark"] { 55 --paper: #15171a; 56 --paper-raised: #1d2024; 57 --ink: #edeeea; 58 --ink-soft: #a3a8ad; 59 --ink-faint: #6b7176; 60 --line: #2c2f34; 61 --line-strong: #3b3f45; 62 --accent: #6c98ff; 63 --accent-ink: #0f1115; 64 --accent-soft: #212a3b; 65 --success: #4fbf8f; 66 --success-soft: #16261f; 67 --code-bg: #0a0c0e; 68 --code-ink: #dbe1e7; 69 --code-line: #22262b; 70 --code-accent: #8cbaff; 71 --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -12px rgba(0, 0, 0, 0.5); 72 } 73 74 * { box-sizing: border-box; } 75 76 body { 77 margin: 0; 78 background: var(--paper); 79 color: var(--ink); 80 font-family: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif; 81 -webkit-font-smoothing: antialiased; 82 min-height: 100vh; 83 } 84 85 .wrap { 86 max-width: 1180px; 87 margin: 0 auto; 88 padding: 32px 20px 70px; 89 } 90 91 header.top { 92 display: flex; 93 justify-content: space-between; 94 align-items: flex-start; 95 gap: 20px; 96 margin-bottom: 40px; 97 } 98 99 .brand-mark { 100 display: flex; 101 align-items: center; 102 gap: 12px; 103 } 104 105 .brand-mark .glyph { 106 display: flex; 107 gap: 3px; 108 flex-shrink: 0; 109 } 110 111 .brand-mark .glyph span { 112 width: 7px; 113 height: 26px; 114 border-radius: 2px; 115 background: var(--accent); 116 } 117 .brand-mark .glyph span:nth-child(1) { opacity: 0.45; } 118 .brand-mark .glyph span:nth-child(2) { opacity: 0.75; } 119 .brand-mark .glyph span:nth-child(3) { opacity: 1; } 120 121 h1 { 122 font-size: 22px; 123 font-weight: 600; 124 letter-spacing: -0.01em; 125 margin: 0 0 4px; 126 text-wrap: balance; 127 } 128 129 .subtitle { 130 margin: 0; 131 color: var(--ink-soft); 132 font-size: 14.5px; 133 max-width: 46ch; 134 line-height: 1.5; 135 } 136 137 .theme-toggle { 138 border: 1px solid var(--line-strong); 139 background: var(--paper-raised); 140 color: var(--ink-soft); 141 width: 38px; 142 height: 38px; 143 border-radius: 9px; 144 display: flex; 145 align-items: center; 146 justify-content: center; 147 cursor: pointer; 148 flex-shrink: 0; 149 font-size: 16px; 150 transition: border-color 0.15s ease, color 0.15s ease; 151 } 152 .theme-toggle:hover { border-color: var(--accent); color: var(--accent); } 153 .theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } 154 155 section { margin-bottom: 36px; } 156 157 .section-label { 158 display: flex; 159 align-items: center; 160 justify-content: space-between; 161 gap: 12px; 162 margin-bottom: 12px; 163 } 164 165 .eyebrow { 166 font-family: "IBM Plex Mono", ui-monospace, monospace; 167 font-size: 11.5px; 168 letter-spacing: 0.08em; 169 text-transform: uppercase; 170 color: var(--ink-faint); 171 } 172 173 .slots { 174 display: grid; 175 grid-template-columns: repeat(4, minmax(0, 1fr)); 176 gap: 14px; 177 } 178 179 @media (max-width: 1080px) { 180 .slots { 181 grid-template-columns: repeat(2, minmax(0, 1fr)); 182 } 183 } 184 185 @media (max-width: 580px) { 186 .slots { 187 grid-template-columns: 1fr; 188 } 189 } 190 191 .slot-card { 192 background: var(--paper-raised); 193 border: 1px solid var(--line); 194 border-radius: var(--radius); 195 box-shadow: var(--shadow); 196 display: flex; 197 flex-direction: column; 198 overflow: hidden; 199 min-width: 0; 200 position: relative; 201 transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), 202 box-shadow 0.18s ease, 203 opacity 0.18s ease, 204 border-color 0.18s ease; 205 } 206 207 .slot-card.is-dragging { 208 opacity: 0.55; 209 cursor: grabbing !important; 210 } 211 212 .slot-card.is-drag-placeholder { 213 opacity: 0.35; 214 border-style: dashed; 215 border-color: var(--accent); 216 box-shadow: none; 217 } 218 219 .slot-drag-ghost { 220 position: fixed; 221 pointer-events: none; 222 z-index: 999999; 223 opacity: 0.92; 224 transform: rotate(2deg) scale(1.03); 225 box-shadow: 0 22px 48px -12px rgba(20, 23, 26, 0.32), 0 6px 14px -4px rgba(20, 23, 26, 0.18); 226 border-radius: var(--radius); 227 transition: none; 228 will-change: transform; 229 overflow: hidden; 230 } 231 .slot-drag-ghost .slot-input { 232 color: transparent !important; 233 background: transparent !important; 234 } 235 .slot-drag-ghost .slot-input::placeholder { 236 color: transparent !important; 237 } 238 239 .slot-drop-indicator { 240 position: absolute; 241 top: 0; 242 bottom: 0; 243 width: 3px; 244 background: var(--accent); 245 border-radius: 2px; 246 pointer-events: none; 247 z-index: 5; 248 box-shadow: 0 0 0 2px var(--accent-soft); 249 animation: pulseIndicator 1.2s ease-in-out infinite; 250 } 251 252 .slot-drop-indicator.indicator-left { left: -8px; } 253 .slot-drop-indicator.indicator-right { right: -8px; } 254 255 @keyframes pulseIndicator { 256 0%, 100% { opacity: 1; } 257 50% { opacity: 0.6; } 258 } 259 260 .slot-head { 261 display: flex; 262 align-items: center; 263 gap: 8px; 264 padding: 9px 12px; 265 border-bottom: 1px solid var(--line); 266 } 267 268 .slot-drag-handle { 269 cursor: grab; 270 color: var(--ink-faint); 271 font-size: 14px; 272 line-height: 1; 273 padding: 3px 4px; 274 user-select: none; 275 -webkit-user-select: none; 276 flex-shrink: 0; 277 transition: color 0.15s ease, background 0.15s ease; 278 display: flex; 279 align-items: center; 280 justify-content: center; 281 border-radius: 4px; 282 touch-action: none; 283 } 284 .slot-drag-handle:hover { 285 color: var(--accent); 286 background: var(--accent-soft); 287 } 288 .slot-drag-handle:active { cursor: grabbing; } 289 290 .slot-num { 291 font-family: "IBM Plex Mono", ui-monospace, monospace; 292 font-size: 11.5px; 293 font-weight: 600; 294 color: var(--accent-ink); 295 background: var(--accent);
296 width: 20px; 297 height: 20px; 298 border-radius: 5px; 299 display: flex; 300 align-items: center; 301 justify-content: center; 302 flex-shrink: 0; 303 } 304 305 .slot-title { 306 font-size: 12.5px; 307 font-weight: 600; 308 white-space: nowrap; 309 overflow: hidden; 310 text-overflow: ellipsis; 311 } 312 313 .slot-status { 314 margin-left: auto; 315 font-size: 10.5px; 316 font-family: "IBM Plex Mono", ui-monospace, monospace; 317 color: var(--ink-faint); 318 } 319 .slot-status.filled { color: var(--success); } 320 321 .remove-slot-btn { 322 border: none; 323 background: transparent; 324 color: var(--ink-faint); 325 cursor: pointer; 326 font-size: 15px; 327 line-height: 1; 328 padding: 2px 4px; 329 border-radius: 4px; 330 margin-left: 2px; 331 transition: color 0.15s ease, background 0.15s ease; 332 } 333 .remove-slot-btn:hover { 334 color: #e5484d; 335 background: rgba(229, 72, 77, 0.12); 336 } 337 338 textarea.slot-input { 339 border: none; 340 resize: vertical; 341 min-height: 110px; 342 padding: 10px 12px; 343 font-family: "IBM Plex Mono", ui-monospace, monospace; 344 font-size: 11.5px; 345 line-height: 1.5; 346 color: var(--ink); 347 background: transparent; 348 width: 100%; 349 box-sizing: border-box; 350 } 351 textarea.slot-input::placeholder { color: var(--ink-faint); } 352 textarea.slot-input:focus { 353 outline: none; 354 background: var(--accent-soft); 355 } 356 357 .controls-row { 358 display: flex; 359 align-items: center; 360 justify-content: space-between; 361 flex-wrap: wrap; 362 gap: 16px; 363 } 364 365 .gap-control { 366 display: inline-flex; 367 background: var(--paper-raised); 368 border: 1px solid var(--line); 369 border-radius: 8px; 370 padding: 3px; 371 gap: 2px; 372 } 373 374 .gap-control button { 375 border: none; 376 background: transparent; 377 color: var(--ink-soft); 378 font-family: "IBM Plex Sans", sans-serif; 379 font-size: 12.5px; 380 font-weight: 500; 381 padding: 7px 12px; 382 border-radius: 6px; 383 cursor: pointer; 384 transition: background 0.15s ease, color 0.15s ease; 385 } 386 .gap-control button:hover { color: var(--ink); } 387 .gap-control button[aria-pressed="true"] { 388 background: var(--accent); 389 color: var(--accent-ink); 390 } 391 .gap-control button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } 392 393 .masonry-controls-panel { 394 margin-top: 14px; 395 padding: 16px 18px; 396 background: var(--paper-raised); 397 border: 1px solid var(--line); 398 border-radius: var(--radius); 399 box-shadow: var(--shadow); 400 display: flex; 401 flex-direction: column; 402 gap: 14px; 403 animation: fadeInPanel 0.18s ease; 404 } 405 406 @keyframes fadeInPanel { 407 from { opacity: 0; transform: translateY(-4px); } 408 to { opacity: 1; transform: translateY(0); } 409 } 410 411 .masonry-controls-header { 412 display: flex; 413 align-items: center; 414 justify-content: space-between; 415 flex-wrap: wrap; 416 gap: 10px; 417 padding-bottom: 12px; 418 border-bottom: 1px solid var(--line); 419 } 420 421 .masonry-panel-badge { 422 display: inline-flex; 423 align-items: center; 424 gap: 8px; 425 font-size: 13.5px; 426 font-weight: 600; 427 color: var(--ink); 428 } 429 .masonry-panel-badge svg { 430 color: var(--accent); 431 } 432 433 .masonry-panel-hint { 434 font-size: 11.5px; 435 color: var(--ink-faint); 436 } 437 438 .masonry-presets-bar { 439 display: flex; 440 align-items: center; 441 flex-wrap: wrap; 442 gap: 8px; 443 padding: 10px 12px; 444 background: var(--paper); 445 border: 1px solid var(--line); 446 border-radius: 8px; 447 } 448 449 .masonry-presets-label { 450 font-size: 11px; 451 font-family: "IBM Plex Mono", monospace; 452 font-weight: 600; 453 text-transform: uppercase; 454 letter-spacing: 0.06em; 455 color: var(--ink-soft); 456 margin-right: 4px; 457 } 458 459 .masonry-preset-pill { 460 font-family: "IBM Plex Sans", sans-serif; 461 font-size: 12px; 462 font-weight: 600; 463 padding: 5px 11px; 464 border-radius: 6px; 465 border: 1px solid var(--line); 466 background: var(--paper-raised); 467 color: var(--ink-soft); 468 cursor: pointer; 469 transition: all 0.15s ease; 470 display: inline-flex; 471 align-items: center; 472 gap: 5px; 473 } 474 .masonry-preset-pill:hover { 475 border-color: var(--accent); 476 color: var(--ink); 477 transform: translateY(-1px); 478 } 479 .masonry-preset-pill[data-active="true"] { 480 background: var(--accent-soft); 481 border-color: var(--accent); 482 color: var(--accent); 483 font-weight: 700; 484 } 485 486 .masonry-controls-grid { 487 display: grid; 488 grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); 489 gap: 14px; 490 align-items: start; 491 } 492 493 .masonry-control-group { 494 display: flex; 495 flex-direction: column; 496 gap: 6px; 497 } 498 499 .masonry-control-group .gap-control { 500 flex-wrap: wrap; 501 } 502 503 .action-buttons { 504 display: flex; 505 align-items: center; 506 gap: 14px; 507 } 508 509 button.btn { 510 font-family: "IBM Plex Sans", sans-serif; 511 font-size: 14px; 512 font-weight: 700; 513 border-radius: 10px; 514 padding: 10px 20px; 515 cursor: pointer; 516 border: none; 517 transition: background 0.15s ease, opacity 0.15s ease, transform 0.05s ease; 518 display: inline-flex; 519 align-items: center; 520 gap: 8px; 521 line-height: 1.2; 522 } 523 button.btn:active { transform: translateY(1px); } 524 button.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } 525 526 .btn-text { 527 background: transparent; 528 color: var(--ink); 529 font-weight: 600; 530 padding: 10px 12px; 531 } 532 .btn-text:hover { color: var(--accent); opacity: 0.85; } 533 534 .btn-primary { 535 background: #2b6bf3; 536 color: #ffffff; 537 } 538 .btn-primary:hover { background: #1e59e0; } 539 540 .btn-convo { 541 background: #00a66c; 542 color: #ffffff; 543 } 544 .btn-convo:hover { background: #008f5d; } 545 .btn-convo:disabled { opacity: 0.65;
545 cursor: not-allowed; } 546 547 .add-slot-bottom-wrap { 548 margin-top: 14px; 549 display: flex; 550 align-items: center; 551 justify-content: flex-start; 552 } 553 554 .btn-add-slot-bottom { 555 font-family: "IBM Plex Sans", -apple-system, sans-serif; 556 font-size: 13px; 557 font-weight: 600; 558 color: var(--ink); 559 background: var(--paper-raised); 560 border: 1px dashed var(--line-strong); 561 border-radius: 8px; 562 padding: 8px 16px; 563 cursor: pointer; 564 display: inline-flex; 565 align-items: center; 566 gap: 7px; 567 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); 568 transition: all 0.15s ease; 569 } 570 .btn-add-slot-bottom:hover { 571 border-color: var(--accent); 572 color: var(--accent); 573 background: var(--accent-soft); 574 transform: translateY(-1px); 575 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06); 576 } 577 .btn-add-slot-bottom:active { 578 transform: translateY(0); 579 } 580 581 @keyframes spin { 582 from { transform: rotate(0deg); } 583 to { transform: rotate(360deg); } 584 } 585 586 .stage { 587 background: var(--paper-raised); 588 border: 1px solid var(--line); 589 border-radius: var(--radius); 590 box-shadow: var(--shadow); 591 padding: 20px; 592 overflow-x: auto; 593 overflow-y: auto; 594 max-height: 80vh; 595 -webkit-overflow-scrolling: touch; 596 } 597 598 .stage-row { 599 display: grid; 600 align-items: start; 601 min-width: min-content; 602 width: 100%; 603 } 604 605 .stage-row.is-single-embed, 606 .stage-row:has(> .stage-cell:only-child) { 607 max-width: 640px !important; 608 width: 100% !important; 609 margin: 0 auto !important; 610 min-width: 0 !important; 611 grid-template-columns: 1fr !important; 612 } 613 614 .masonry-preview { 615 display: flex; 616 flex-direction: row; 617 align-items: flex-start; 618 width: 100%; 619 overflow-x: auto; 620 -webkit-overflow-scrolling: touch; 621 } 622 623 .masonry-preview.is-single-embed { 624 max-width: 640px !important; 625 width: 100% !important; 626 margin: 0 auto !important; 627 min-width: 0 !important; 628 } 629 630 .masonry-preview-col { 631 flex: 1 1 0; 632 display: flex; 633 flex-direction: column; 634 min-width: 0; 635 } 636 637 .masonry-stage-cell { 638 display: flex !important; 639 flex-direction: column !important; 640 align-items: stretch !important; 641 justify-content: stretch !important; 642 width: 100% !important; 643 box-sizing: border-box !important; 644 vertical-align: top; 645 transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.2s ease; 646 } 647 648 .masonry-stage-cell[data-mstyle="card"] { 649 background: var(--paper-raised); 650 border: 1px solid var(--line); 651 box-shadow: 0 4px 14px -2px rgba(0, 0, 0, 0.05), 0 2px 6px -1px rgba(0, 0, 0, 0.02); 652 } 653 654 .masonry-stage-cell[data-mstyle="minimal"] { 655 background: transparent; 656 border: 1px dashed var(--line); 657 box-shadow: none; 658 } 659 660 .masonry-stage-cell[data-mstyle="outline"] { 661 background: var(--paper-raised); 662 border: 1px solid var(--line-strong); 663 box-shadow: none; 664 } 665 666 .masonry-stage-cell[data-mstyle="glass"] { 667 background: rgba(255, 255, 255, 0.65); 668 backdrop-filter: blur(12px); 669 -webkit-backdrop-filter: blur(12px); 670 border: 1px solid rgba(255, 255, 255, 0.45); 671 box-shadow: 0 8px 24px 0 rgba(0, 0, 0, 0.06); 672 } 673 674 .masonry-stage-cell[data-mhover="lift"]:hover { 675 transform: translateY(-3px); 676 box-shadow: 0 10px 24px -4px rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.04); 677 } 678 679 .masonry-stage-cell[data-mhover="glow"]:hover { 680 border-color: var(--accent); 681 box-shadow: 0 0 0 3px var(--accent-soft), 0 4px 12px rgba(0, 0, 0, 0.06); 682 } 683 684 .masonry-slot-badge { 685 position: absolute; 686 top: 6px; 687 right: 6px; 688 font-family: "IBM Plex Mono", monospace; 689 font-size: 10px; 690 font-weight: 600; 691 padding: 2px 6px; 692 border-radius: 4px; 693 background: var(--accent); 694 color: var(--accent-ink); 695 opacity: 0.85; 696 pointer-events: none; 697 z-index: 2; 698 } 699 700 @media (max-width: 760px) { 701 .stage-row { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)) !important; } 702 .stage-row.is-single-embed, 703 .stage-row:has(> .stage-cell:only-child), 704 .masonry-preview.is-single-embed { 705 max-width: 100% !important; 706 } 707 .masonry-preview { flex-direction: column !important; } 708 .masonry-preview-col { width: 100% !important; } 709 } 710 711 .stage-cell { 712 min-width: 240px; 713 box-sizing: border-box; 714 border-radius: 12px; 715 background: var(--paper-raised); 716 border: 1px solid var(--line); 717 box-shadow: 0 2px 10px -2px rgba(0, 0, 0, 0.05); 718 padding: 10px; 719 display: flex; flex-direction: column; 720 transition: box-shadow 0.15s ease; 721 resize: both; 722 overflow: auto; 723 max-width: 100%; 724 min-height: 180px; 725 height: auto; 726 position: relative; 727 } 728 .stage-cell:hover { 729 box-shadow: 0 6px 18px -4px rgba(0, 0, 0, 0.08); 730 } 731 732 .stage-cell.is-resizing iframe, 733 .carousel-viewport.is-resizing iframe { 734 pointer-events: none !important; 735 } 736 737 .stage-cell iframe { 738 width: 100%; 739 min-width: 0; 740 min-height: 0; 741 flex: 1 1 auto; 742 height: 100%; 743 border: 1px dashed var(--line-strong); 744 border-radius: 8px; 745 background: var(--paper); 746 overflow: auto; 747 display: block; 748 box-sizing: border-box; 749 } 750 751 .stage-empty { 752 min-height: 120px; 753 height: 100%; 754 flex: 1; 755 min-width: 220px; 756 border: 1px dashed var(--line-strong); 757 border-radius: 8px; 758 display: flex; 759 align-items: center; 760 justify-content: center; 761 text-align: center; 762 padding: 16px; 763 color: var(--ink-faint); 764 font-size: 12.5px; 765 line-height: 1.5; 766 box-sizing: border-box; 767 } 768 769 .carousel-preview { 770 display: flex; 771 flex-direction: column; 772 gap: 14px; 773 width: 100%; 774 max-width: 640px; 775 margin: 0 auto; 776 box-sizing: border-box; 777 } 778
779 .carousel-controls { 780 order: -1; 781 display: flex; 782 align-items: center; 783 justify-content: center; 784 gap: 12px; 785 margin: 0 auto 4px auto; 786 padding: 5px 14px; 787 background: var(--paper); 788 border: 1px solid var(--line); 789 border-radius: 9999px; 790 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); 791 } 792 793 .carousel-viewport { 794 order: 1; 795 position: relative; 796 border: 1px solid var(--line); 797 border-radius: 12px; 798 background: #ffffff; 799 overflow: auto; 800 resize: both; 801 box-shadow: 0 4px 16px -2px rgba(0, 0, 0, 0.06), 0 2px 6px -1px rgba(0, 0, 0, 0.03); 802 width: 100%; 803 min-width: 280px; 804 min-height: 180px; 805 height: auto; 806 display: flex; 807 flex-direction: column; 808 align-items: stretch; 809 justify-content: stretch; 810 } 811 812 .carousel-viewport iframe { 813 width: 100%; 814 height: 100%; 815 min-height: 0; 816 flex: 1 1 auto; 817 border: none; 818 display: block; 819 background: #ffffff; 820 } 821 822 .carousel-arrow { 823 width: 34px; 824 height: 34px; 825 border-radius: 50%; 826 border: 1px solid var(--line-strong); 827 background: var(--paper-raised); 828 color: var(--ink); 829 cursor: pointer; 830 font-size: 18px; 831 font-weight: 600; 832 line-height: 1; 833 display: flex; 834 align-items: center; 835 justify-content: center; 836 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 837 transition: all 0.15s ease; 838 } 839 .carousel-arrow:hover:not(:disabled) { 840 border-color: var(--accent); 841 color: #ffffff; 842 background: var(--accent); 843 transform: translateY(-1px); 844 box-shadow: 0 4px 10px rgba(47, 111, 237, 0.25); 845 } 846 .carousel-arrow:active:not(:disabled) { 847 transform: translateY(0px) scale(0.95); 848 } 849 .carousel-arrow:disabled { opacity: 0.35; cursor: not-allowed; } 850 .carousel-arrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } 851 852 .carousel-dots { display: flex; align-items: center; gap: 7px; padding: 0 4px; } 853 .carousel-dots button { 854 width: 8px; 855 height: 8px; 856 border-radius: 50%; 857 border: none; 858 background: var(--line-strong); 859 padding: 0; 860 cursor: pointer; 861 transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); 862 } 863 .carousel-dots button.is-active { 864 background: var(--accent); 865 width: 22px; 866 border-radius: 9999px; 867 } 868 .carousel-dots button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } 869 870 .carousel-counter { 871 text-align: center; 872 font-family: "IBM Plex Mono", ui-monospace, monospace; 873 font-size: 12px; 874 color: var(--ink-faint); 875 } 876 877 .code-panel { 878 background: var(--code-bg); 879 border-radius: var(--radius); 880 border: 1px solid var(--code-line); 881 overflow: hidden; 882 } 883 884 .code-panel-head { 885 display: flex; 886 align-items: center; 887 justify-content: space-between; 888 padding: 10px 14px; 889 border-bottom: 1px solid var(--code-line); 890 } 891 892 .code-panel-head .dots { display: flex; gap: 6px; } 893 .code-panel-head .dots span { 894 width: 9px; height: 9px; border-radius: 50%; 895 background: var(--code-line); 896 } 897 898 .copy-btn { 899 font-family: "IBM Plex Mono", ui-monospace, monospace; 900 font-size: 12px; 901 font-weight: 500; 902 color: var(--code-ink); 903 background: transparent; 904 border: 1px solid var(--code-line); 905 border-radius: 6px; 906 padding: 6px 12px; 907 cursor: pointer; 908 display: inline-flex; 909 align-items: center; 910 gap: 6px; 911 transition: border-color 0.15s ease, color 0.15s ease; 912 } 913 .copy-btn:hover { border-color: var(--code-accent); color: var(--code-accent); } 914 .copy-btn:focus-visible { outline: 2px solid var(--code-accent); outline-offset: 2px; } 915 .copy-btn.copied { color: var(--success); border-color: var(--success); } 916 917 pre.code-out { 918 margin: 0; 919 padding: 16px; 920 overflow-x: auto; 921 font-family: "IBM Plex Mono", ui-monospace, monospace; 922 font-size: 12.5px; 923 line-height: 1.6; 924 color: var(--code-ink); 925 tab-size: 2; 926 white-space: pre; 927 } 928 929 footer.hint { 930 margin-top: 44px; 931 padding-top: 20px; 932 border-top: 1px solid var(--line); 933 color: var(--ink-faint); 934 font-size: 12.5px; 935 line-height: 1.6; 936 } 937 938 @media (prefers-reduced-motion: reduce) { 939 * { transition: none !important; } 940 } 941</style> 942</head> 943<body> 944 945<div class="wrap"> 946 <header class="top"> 947 <div class="brand-mark"> 948 <div class="glyph"><span></span><span></span><span></span></div> 949 <div> 950 <h1>Embed Row & Masonry Builder</h1> 951 <p class="subtitle">Paste embed snippets, preview them together, and generate complete HTML files for Row, Carousel, or Masonry layouts (Max 4 columns per row). Drag the â ¿ handle to reorder slots.</p> 952 </div> 953 </div> 954 <button class="theme-toggle" id="themeToggle" type="button" aria-label="Toggle color theme" title="Toggle color theme">â</button> 955 </header> 956 957 <section> 958 <div class="section-label">
959 <span class="eyebrow">Slots â left to right order (drag to reorder)</span> 960 <button class="btn btn-text" id="addSlotBtn" type="button" style="padding: 5px 12px; font-size: 12px;">+ Add on top slot</button> 961 </div> 962 <div class="slots" id="slotsContainer"></div> 963 <div class="add-slot-bottom-wrap"> 964 <button class="btn-add-slot-bottom" id="addSlotBtnBottom" type="button"> 965 <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> 966 <line x1="12" y1="5" x2="12" y2="19"></line> 967 <line x1="5" y1="12" x2="19" y2="12"></line> 968 </svg> 969 <span>Add on bottom slot</span> 970 </button> 971 </div> 972 </section> 973 974 <section> 975 <div class="controls-row"> 976 <div style="display:flex; gap:24px; flex-wrap:wrap;"> 977 <div> 978 <div class="eyebrow" style="margin-bottom:8px;">Layout</div> 979 <div class="gap-control" id="modeControl" role="group" aria-label="Layout"> 980 <button type="button" data-mode="row" aria-pressed="true">Row</button> 981 <button type="button" data-mode="carousel" aria-pressed="false">Carousel</button> 982 <button type="button" data-mode="masonry" aria-pressed="false">Masonry</button> 983 </div> 984 </div> 985 <div id="gapControlWrap"> 986 <div class="eyebrow" style="margin-bottom:8px;">Spacing between embeds</div> 987 <div class="gap-control" role="group" aria-label="Spacing between embeds"> 988 <button type="button" data-gap="8" aria-pressed="false">Compact</button> 989 <button type="button" data-gap="24" aria-pressed="true">Comfortable</button> 990 <button type="button" data-gap="40" aria-pressed="false">Spacious</button> 991 </div> 992 </div> 993 </div> 994 995 <div class="action-buttons"> 996 <button class="btn btn-text" id="resetBtn" type="button">Clear all</button> 997 <button class="btn btn-primary" id="generateBtn" type="button">Generate HTML</button> 998 <button class="btn btn-convo" id="convoBtn" type="button"> 999 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> 1000 <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path> 1001 </svg> 1002 <span id="convoBtnLabel">Convo</span> 1003 </button> 1004 </div> 1005 </div> 1006 1007 <div class="masonry-controls-panel" id="masonryControlsPanel" style="display:none;"> 1008 <div class="masonry-controls-header"> 1009 <div class="masonry-panel-badge"> 1010 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> 1011 <rect x="3" y="3" width="7" height="9" rx="1"></rect> 1012 <rect x="14" y="3" width="7" height="5" rx="1"></rect> 1013 <rect x="14" y="12" width="7" height="9" rx="1"></rect> 1014 <rect x="3" y="16" width="7" height="5" rx="1"></rect> 1015 </svg> 1016 <span>Masonry Layout & Card Customizer</span> 1017 </div> 1018 <span class="masonry-panel-hint">Configure grid columns, spacing, card surfaces, and responsive breakpoints</span> 1019 </div> 1020 1021 <div class="masonry-presets-bar"> 1022 <span class="masonry-presets-label">â¡ Presets:</span> 1023 <button type="button" class="masonry-preset-pill" data-preset="default" data-active="true">ð¨ Default Cards</button> 1024 <button type="button" class="masonry-preset-pill" data-preset="fullbleed" data-active="false">ð¼ï¸ Full-Bleed Media</button> 1025 <button type="button" class="masonry-preset-pill" data-preset="bento" data-active="false">ðï¸ Bento Board</button> 1026 <button type="button" class="masonry-preset-pill" data-preset="social" data-active="false">ð± Social Feed</button> 1027 <button type="button" class="masonry-preset-pill" data-preset="minimal" data-active="false">⨠Clean Minimal</button> 1028 </div> 1029 1030 <div class="masonry-controls-grid"> 1031 <div class="masonry-control-group"> 1032 <div class="eyebrow" style="margin-bottom:6px;">Grid Columns</div> 1033 <div class="gap-control" id="masonryColsControl" role="group" aria-label="Masonry Columns"> 1034 <button type="button" data-mcols="auto" aria-pressed="true">Auto</button> 1035 <button type="button" data-mcols="1" aria-pressed="false">1 Col</button> 1036 <button type="button" data-mcols="2" aria-pressed="false">2 Cols</button> 1037 <button type="button" data-mcols="3" aria-pressed="false">3 Cols</button> 1038 <button type="button" data-mcols="4" aria-pressed="false">4 Cols</button> 1039 <button type="button" data-mcols="5" aria-pressed="false">5 Cols</button> 1040 </div> 1041 </div> 1042 1043 <div class="masonry-control-group"> 1044 <div class="eyebrow" style="margin-bottom:6px;">
1044Column & Gutter Spacing</div> 1045 <div class="gap-control" id="masonryGapControl" role="group" aria-label="Masonry Spacing"> 1046 <button type="button" data-mgap="0" aria-pressed="false">0px (Seamless)</button> 1047 <button type="button" data-mgap="8" aria-pressed="false">8px</button> 1048 <button type="button" data-mgap="16" aria-pressed="false">16px</button> 1049 <button type="button" data-mgap="24" aria-pressed="true">24px</button> 1050 <button type="button" data-mgap="36" aria-pressed="false">36px</button> 1051 <button type="button" data-mgap="48" aria-pressed="false">48px</button> 1052 </div> 1053 </div> 1054 1055 <div class="masonry-control-group"> 1056 <div class="eyebrow" style="margin-bottom:6px;">Card Padding</div> 1057 <div class="gap-control" id="masonryPaddingControl" role="group" aria-label="Item Card Padding"> 1058 <button type="button" data-mpadding="0" aria-pressed="false">0px (Flush)</button> 1059 <button type="button" data-mpadding="6" aria-pressed="false">6px</button> 1060 <button type="button" data-mpadding="12" aria-pressed="true">12px</button> 1061 <button type="button" data-mpadding="18" aria-pressed="false">18px</button> 1062 <button type="button" data-mpadding="24" aria-pressed="false">24px</button> 1063 </div> 1064 </div> 1065 1066 <div class="masonry-control-group"> 1067 <div class="eyebrow" style="margin-bottom:6px;">Corner Radius</div> 1068 <div class="gap-control" id="masonryRadiusControl" role="group" aria-label="Item Card Corners"> 1069 <button type="button" data-mradius="0" aria-pressed="false">0px (Sharp)</button> 1070 <button type="button" data-mradius="6" aria-pressed="false">6px (Subtle)</button> 1071 <button type="button" data-mradius="12" aria-pressed="true">12px (Smooth)</button> 1072 <button type="button" data-mradius="18" aria-pressed="false">18px (Modern)</button> 1073 <button type="button" data-mradius="26" aria-pressed="false">26px (Pill)</button> 1074 </div> 1075 </div> 1076 1077 <div class="masonry-control-group"> 1078 <div class="eyebrow" style="margin-bottom:6px;">Card Surface</div> 1079 <div class="gap-control" id="masonryStyleControl" role="group" aria-label="Card Surface Style"> 1080 <button type="button" data-mstyle="card" aria-pressed="true">Elevated Card</button> 1081 <button type="button" data-mstyle="minimal" aria-pressed="false">Minimal (No Card)</button> 1082 <button type="button" data-mstyle="outline" aria-pressed="false">Outline Only</button> 1083 <button type="button" data-mstyle="glass" aria-pressed="false">Frosted Glass</button> 1084 </div> 1085 </div> 1086 1087 <div class="masonry-control-group"> 1088 <div class="eyebrow" style="margin-bottom:6px;">Hover Interaction</div> 1089 <div class="gap-control" id="masonryHoverControl" role="group" aria-label="Hover Interaction"> 1090 <button type="button" data-mhover="lift" aria-pressed="true">⨠3D Lift</button> 1091 <button type="button" data-mhover="glow" aria-pressed="false">ð Glow</button> 1092 <button type="button" data-mhover="none" aria-pressed="false">Static</button> 1093 </div> 1094 </div> 1095 </div> 1096 </div> 1097 </section> 1098 1099 <section> 1100 <div class="section-label"> 1101 <span class="eyebrow">Live preview</span> 1102 </div> 1103 <div class="stage"> 1104 <div class="stage-row" id="stageRow"></div> 1105 </div> 1106 </section> 1107 1108 <section id="outputSection" style="display:none;"> 1109 <div class="section-label"> 1110 <span class="eyebrow">Generated HTML</span> 1111 <button class="copy-btn" id="copyBtn" type="button">Copy code</button> 1112 </div> 1113 <div class="code-panel"> 1114 <div class="code-panel-head"> 1115 <div class="dots"><span></span><span></span><span></span></div> 1116 <span class="eyebrow" id="codeFileLabel" style="color:var(--code-ink); opacity:0.5;">row.html</span> 1117 </div> 1118 <pre class="code-out" id="codeOut"></pre> 1119 </div> 1120 </section> 1121 1122 <footer class="hint"> 1123 Generates a complete standalone HTML document. Maximum 4 columns per row with automatic mobile responsiveness. 1124 </footer> 1125</div> 1126 1127<script> 1128(function () { 1129 'use strict'; 1130 1131 var STORAGE_KEY = 'embed-row:slots:v5'; 1132 var THEME_KEY = 'embed-row:theme:v1'; 1133 var slotsContainer = document.getElementById('slotsContainer'); 1134 var stageRow = document.getElementById('stageRow'); 1135 var codeOut = document.getElementById('codeOut'); 1136 var codeFileLabel = document.getElementById('codeFileLabel'); 1137 var outputSection = document.getElementById('outputSection'); 1138 var gapButtons = Array.prototype.slice.call(document.querySelectorAll('#gapControlWrap .gap-control button')); 1139 var gapControlWrap = document.getElementById('gapControlWrap'); 1140 var modeButtons = Array.prototype.slice.call(document.querySelectorAll('#modeControl button')); 1141 var addSlotBtn = document.getElementById('addSlotBtn'); 1142 var addSlotBtnBottom = document.getElementById('addSlotBtnBottom'); 1143 var currentGap = 24; 1144 var layoutMode = 'row'; 1145 var carouselIndex = 0; 1146 var carouselWidth = null; 1147 var carouselHeight = null; 1148 var slotsData = []; 1149 var isEditMode = false;
1150 var editId = null; 1151 var editSlug = null; 1152 1153 var dragState = null; 1154 var DRAG_THRESHOLD = 6; 1155 var DRAG_HOLD_MS = 0; 1156 1157 var MASONRY_CONFIG_KEY = 'embed-row:masonry-cfg:v2'; 1158 var masonryCols = 'auto'; 1159 var masonryGap = 24; 1160 var masonryPadding = 12; 1161 var masonryRadius = 12; 1162 var masonryStyle = 'card'; 1163 var masonryHover = 'lift'; 1164 var activePreset = 'default'; 1165 1166 var masonryControlsPanel = document.getElementById('masonryControlsPanel'); 1167 var masonryColsButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryColsControl button')); 1168 var masonryGapButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryGapControl button')); 1169 var masonryPaddingButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryPaddingControl button')); 1170 var masonryRadiusButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryRadiusControl button')); 1171 var masonryStyleButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryStyleControl button')); 1172 var masonryHoverButtons = Array.prototype.slice.call(document.querySelectorAll('#masonryHoverControl button')); 1173 var masonryPresetButtons = Array.prototype.slice.call(document.querySelectorAll('.masonry-preset-pill')); 1174 1175 var MASONRY_PRESETS = { 1176 default: { cols: 'auto', gap: 24, padding: 12, radius: 12, style: 'card', hover: 'lift' }, 1177 fullbleed: { cols: '3', gap: 16, padding: 0, radius: 8, style: 'minimal', hover: 'lift' }, 1178 bento: { cols: '4', gap: 8, padding: 8, radius: 6, style: 'outline', hover: 'glow' }, 1179 social: { cols: '2', gap: 16, padding: 12, radius: 18, style: 'card', hover: 'lift' }, 1180 minimal: { cols: 'auto', gap: 24, padding: 0, radius: 0, style: 'minimal', hover: 'none' } 1181 }; 1182 1183 window.addEventListener('message', function (e) { 1184 if (e.data && e.data.type === 'EZ_SET_EDIT_MODE') { 1185 isEditMode = !!e.data.isEdit; 1186 editId = e.data.editId || null; 1187 editSlug = e.data.editSlug || null; 1188 if (e.data.isEdit) { 1189 var labelSpan = document.getElementById('convoBtnLabel'); 1190 if (labelSpan) labelSpan.textContent = 'Update'; 1191 } 1192 if (e.data.initialHtml && typeof e.data.initialHtml === 'string' && e.data.initialHtml.trim()) { 1193 try { 1194 var parser = new DOMParser(); 1195 var doc = parser.parseFromString(e.data.initialHtml, 'text/html'); 1196 1197 if (doc.querySelector('.masonry-grid, .masonry-item')) { 1198 var mBtn = document.querySelector('#modeControl button[data-mode="masonry"]'); 1199 if (mBtn) mBtn.click(); 1200 } else if (doc.querySelector('.ec-track, .ec-slide, .embed-carousel')) { 1201 var cBtn = document.querySelector('#modeControl button[data-mode="carousel"]'); 1202 if (cBtn) cBtn.click(); 1203 } else { 1204 var rBtn = document.querySelector('#modeControl button[data-mode="row"]'); 1205 if (rBtn) rBtn.click(); 1206 } 1207 1208 var items = doc.querySelectorAll('.embed-row-item, .masonry-item, .ec-slide'); 1209 if (items && items.length > 0) { 1210 slotsContainer.innerHTML = ''; 1211 slotsData = []; 1212 items.forEach(function (item) { 1213 var w = null; 1214 var h = null; 1215 if (item.style && item.style.width) { 1216 var pw = parseInt(item.style.width, 10); 1217 if (pw > 0) w = pw; 1218 } 1219 if (item.style && item.style.height) { 1220 var ph = parseInt(item.style.height, 10); 1221 if (ph > 0) h = ph; 1222 } 1223 addSlot(item.innerHTML.trim(), false, w, h); 1224 }); 1225 renderPreview(); 1226 } else { 1227 var mediaNodes = doc.querySelectorAll('iframe, video, blockquote, twitter-tweet, audio, embed, object'); 1228 if (mediaNodes && mediaNodes.length > 0) { 1229 slotsContainer.innerHTML = ''; 1230 slotsData = []; 1231 mediaNodes.forEach(function (node) { 1232 addSlot(node.outerHTML.trim(), false); 1233 }); 1234 renderPreview(); 1235 } else { 1236 var bodyContent = extractBodyContent(e.data.initialHtml); 1237 if (bodyContent && bodyContent.trim().length > 0) { 1238 slotsContainer.innerHTML = ''; 1239 slotsData = []; 1240 addSlot(bodyContent.trim(), false); 1241 renderPreview(); 1242 } 1243 } 1244 } 1245 } catch (err) { 1246 console.error('Failed to parse initial embed html:', err); 1247 } 1248 } 1249 } 1250 }); 1251 1252 window.addEventListener('message', function (e) { 1253 if (e.data && e.data.type === 'EZ_TRIGGER_SAVE') { 1254 var convoBtn = document.getElementById('convoBtn'); 1255 if (convoBtn) { 1256 convoBtn.click(); 1257 } 1258 } 1259 }); 1260 1261 function parseSnippetHeight(code) { 1262 if (!code) return null; 1263 var match = code.match(/height=["']?(d+)(?:px)?["']?/i); 1264 if (match && match[1]) { 1265 var val = parseInt(match[1], 10); 1266 if (val > 50 && val < 2000) return val; 1267 } 1268 var styleMatch = code.match(/heights*:s*(d+)px/i); 1269 if (styleMatch && styleMatch[1]) { 1270 var sVal = parseInt(styleMatch[1], 10); 1271 if (sVal > 50 && sVal < 2000) return sVal; 1272 } 1273 return null; 1274 } 1275 1276 function extractBodyContent(raw) { 1277 if (!raw) return ''; 1278 var trimmed = raw.trim(); 1279 1280 if (!/<!DOCTYPEs+html/i.test(trimmed) && !/<html[s>]/i.test(trimmed)) { 1281 return trimmed; 1282 } 1283 1284 try { 1285 var parser = new DOMParser(); 1286 var doc = parser.parseFromString(trimmed, 'text/html'); 1287 return doc.body ? doc.body.innerHTML.trim() : trimmed; 1288 } catch (e) { 1289 return trimmed; 1290 } 1291 } 1292 1293 function updateMasonryButtonsUI() { 1294 masonryColsButtons.forEach(function (b) { 1295 b.setAttribute('aria-pressed', b.getAttribute('data-mcols') === masonryCols ? 'true' : 'false'); 1296 }); 1297 masonryGapButtons.forEach(function (b) { 1298 b.setAttribute('aria-pressed', parseInt(b.getAttribute('data-mgap'), 10) === masonryGap ? 'true' : 'false'); 1299 }); 1300 masonryPaddingButtons.forEach(function (b) { 1301 b.setAttribute('aria-pressed', parseInt(b.getAttribute('data-mpadding'), 10) === masonryPadding ? 'true' : 'false'); 1302 }); 1303 masonryRadiusButtons.forEach(function (b) { 1304 b.setAttribute('aria-pressed', parseInt(b.getAttribute('data-mradius'), 10) === masonryRadius ? 'true' : 'false'); 1305 }); 1306 masonryStyleButtons.forEach(function (b) { 1307 b.setAttribute('aria-pressed', b.getAttribute('data-mstyle') === masonryStyle ? 'true' : 'false'); 1308 }); 1309 masonryHoverButtons.forEach(function (b) { 1310 b.setAttribute('aria-pressed', b.getAttribute('data-mhover') === masonryHover ? 'true' : 'false'); 1311 }); 1312 masonryPresetButtons.forEach(function (b) { 1313 b.setAttribute('data-active', b.getAttribute('data-preset') === activePreset ? 'true' : 'false'); 1314 }); 1315 } 1316 1317 function applyMasonryPreset(presetKey) { 1318 if (!MASONRY_PRESETS[presetKey]) return; 1319 var p = MASONRY_PRESETS[presetKey]; 1320 masonryCols = p.cols; 1321 masonryGap = p.gap; 1322 masonryPadding = p.padding; 1323 masonryRadius = p.radius; 1324 masonryStyle = p.style; 1325 masonryHover = p.hover; 1326 activePreset = presetKey; 1327 updateMasonryButtonsUI(); 1328 saveMasonryConfig(); 1329 if (layoutMode === 'masonry') { 1330 renderPreview(); 1331 if (outputSection.style.display !== 'none') renderCode(); 1332 } 1333 } 1334 1335 function saveMasonryConfig() { 1336 try { 1337 localStorage.setItem(MASONRY_CONFIG_KEY, JSON.stringify({ 1338 cols: masonryCols, 1339 gap: masonryGap, 1340 padding: masonryPadding, 1341 radius: masonryRadius, 1342 style: masonryStyle, 1343 hover: masonryHover, 1344 preset: activePreset 1345 })); 1346 } catch (e) {} 1347 } 1348 1349 function loadMasonryConfig() { 1350 try { 1351 var raw = localStorage.getItem(MASONRY_CONFIG_KEY); 1352 if (raw) { 1353 var cfg = JSON.parse(raw); 1354 if (cfg.cols) masonryCols = cfg.cols; 1355 if (cfg.gap !== undefined) masonryGap = parseInt(cfg.gap, 10); 1356 if (cfg.padding !== undefined) masonryPadding = parseInt(cfg.padding, 10);
1357 if (cfg.radius !== undefined) masonryRadius = parseInt(cfg.radius, 10); 1358 if (cfg.style) masonryStyle = cfg.style; 1359 if (cfg.hover) masonryHover = cfg.hover; 1360 if (cfg.preset) activePreset = cfg.preset; 1361 } 1362 } catch (e) {} 1363 updateMasonryButtonsUI(); 1364 } 1365 1366 var placeholders = [ 1367 '<iframe src="https://example.com/widget-1" width="100%" height="220"></iframe>', 1368 '<blockquote class="reviews-widget">â¦</blockquote>\\n<script src="https://example.com/reviews.js"><' + '/script>', 1369 '<div id="chat-widget" data-id="12345"></div>\\n<script src="https://example.com/chat.js"><' + '/script>', 1370 '<iframe src="https://example.com/widget-4" width="100%" height="220"></iframe>' 1371 ]; 1372 1373 function createSlotCard(val, index, customWidth, customHeight) { 1374 var card = document.createElement('div'); 1375 card.className = 'slot-card'; 1376 1377 var head = document.createElement('div'); 1378 head.className = 'slot-head'; 1379 1380 var dragHandle = document.createElement('span'); 1381 dragHandle.className = 'slot-drag-handle'; 1382 dragHandle.textContent = 'â ¿'; 1383 dragHandle.title = 'Drag to reorder'; 1384 1385 var num = document.createElement('span'); 1386 num.className = 'slot-num'; 1387 num.textContent = index + 1; 1388 1389 var title = document.createElement('span'); 1390 title.className = 'slot-title'; 1391 title.textContent = 'Embed ' + (index + 1); 1392 1393 var status = document.createElement('span'); 1394 status.className = 'slot-status'; 1395 status.textContent = val.trim() ? 'ready' : 'empty'; 1396 if (val.trim()) status.classList.add('filled'); 1397 1398 var removeBtn = document.createElement('button'); 1399 removeBtn.type = 'button'; 1400 removeBtn.className = 'remove-slot-btn'; 1401 removeBtn.title = 'Remove slot'; 1402 removeBtn.textContent = 'Ã'; 1403 removeBtn.addEventListener('click', function () { 1404 removeSlot(card); 1405 }); 1406 1407 head.appendChild(dragHandle); 1408 head.appendChild(num); 1409 head.appendChild(title); 1410 head.appendChild(status); 1411 head.appendChild(removeBtn); 1412 1413 var input = document.createElement('textarea'); 1414 input.className = 'slot-input'; 1415 input.spellcheck = false; 1416 input.placeholder = placeholders[index % placeholders.length]; 1417 input.value = val; 1418 1419 input.addEventListener('input', onInput); 1420 1421 card.appendChild(head); 1422 card.appendChild(input); 1423 1424 var slotObj = { 1425 card: card, 1426 input: input, 1427 status: status, 1428 num: num, 1429 title: title, 1430 removeBtn: removeBtn, 1431 dragHandle: dragHandle, 1432 customWidth: customWidth || null, 1433 customHeight: customHeight || null 1434 }; 1435 1436 attachPointerDrag(slotObj); 1437 1438 return slotObj; 1439 } 1440 1441 function attachPointerDrag(slotObj) { 1442 var handle = slotObj.dragHandle; 1443 1444 handle.addEventListener('pointerdown', function (e) { 1445 if (e.button !== 0 && e.pointerType === 'mouse') return; 1446 1447 var sourceIndex = slotsData.indexOf(slotObj); 1448 if (sourceIndex === -1) return; 1449 1450 e.preventDefault(); 1451 try { handle.setPointerCapture(e.pointerId); } catch (err) {} 1452 1453 dragState = { 1454 sourceIndex: sourceIndex, 1455 pointerId: e.pointerId, 1456 startX: e.clientX, 1457 startY: e.clientY, 1458 ghostEl: null, 1459 ghostOffsetX: 0, 1460 ghostOffsetY: 0, 1461 placeholderEl: null, 1462 currentTargetIndex: -1, 1463 currentInsertBefore: true, 1464 started: false 1465 }; 1466 }); 1467 1468 handle.addEventListener('pointermove', function (e) { 1469 if (!dragState || dragState.pointerId !== e.pointerId) return; 1470 1471 if (!dragState.started) { 1472 var dx = e.clientX - dragState.startX; 1473 var dy = e.clientY - dragState.startY; 1474 if (Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return; 1475 beginDrag(e); 1476 } 1477 1478 if (dragState.started) { 1479 e.preventDefault(); 1480 updateGhostPosition(e.clientX, e.clientY); 1481 updateDropTarget(e.clientX, e.clientY); 1482 } 1483 }); 1484 1485 handle.addEventListener('pointerup', function (e) { 1486 if (!dragState || dragState.pointerId !== e.pointerId) return; 1487 if (dragState.started) { 1488 endDrag(true); 1489 } else { 1490 dragState = null; 1491 } 1492 }); 1493 1494 handle.addEventListener('pointercancel', function (e) { 1495 if (!dragState || dragState.pointerId !== e.pointerId) return; 1496 if (dragState.started) { 1497 endDrag(false); 1498 } else { 1499 dragState = null; 1500 } 1501 }); 1502 1503 handle.addEventListener('lostpointercapture', function (e) { 1504 if (!dragState || dragState.pointerId !== e.pointerId) return; 1505 if (dragState.started) { 1506 endDrag(false); 1507 } else { 1508 dragState = null; 1509 } 1510 }); 1511 } 1512 1513 function beginDrag(e) { 1514 if (!dragState) return; 1515 dragState.started = true; 1516 1517 var sourceSlot = slotsData[dragState.sourceIndex]; 1518 if (!sourceSlot) return; 1519 1520 var srcRect = sourceSlot.card.getBoundingClientRect(); 1521 1522 var ghost = sourceSlot.card.cloneNode(true); 1523 ghost.classList.add('slot-drag-ghost'); 1524 ghost.classList.remove('is-dragging'); 1525 ghost.style.width = srcRect.width + 'px'; 1526 ghost.style.height = srcRect.height + 'px'; 1527 ghost.style.left = '0px'; 1528 ghost.style.top = '0px'; 1529 document.body.appendChild(ghost); 1530 dragState.ghostEl = ghost; 1531 1532 dragState.ghostOffsetX = e.clientX - srcRect.left; 1533 dragState.ghostOffsetY = e.clientY - srcRect.top; 1534 1535 sourceSlot.card.classList.add('is-drag-placeholder'); 1536 1537 document.body.style.userSelect = 'none'; 1538 document.body.style.webkitUserSelect = 'none'; 1539 document.body.style.cursor = 'grabbing'; 1540 1541 updateGhostPosition(e.clientX, e.clientY); 1542 } 1543 1544 function updateGhostPosition(clientX, clientY) { 1545 if (!dragState || !dragState.ghostEl) return; 1546 var gx = clientX - dragState.ghostOffsetX; 1547 var gy = clientY - dragState.ghostOffsetY; 1548 dragState.ghostEl.style.transform = 'translate3d(' + gx + 'px, ' + gy + 'px, 0) rotate(2deg) scale(1.03)'; 1549 } 1550 1551 function clearDropIndicators() { 1552 slotsData.forEach(function (o) { 1553 var ind = o.card.querySelector('.slot-drop-indicator'); 1554 if (ind && ind.parentNode) ind.parentNode.removeChild(ind); 1555 o.card.classList.remove('is-drag-over'); 1556 }); 1557 } 1558
1559 function updateDropTarget(clientX, clientY) { 1560 if (!dragState) return; 1561 1562 var targetIndex = -1; 1563 var insertBefore = true; 1564 1565 for (var i = 0; i < slotsData.length; i++) { 1566 var card = slotsData[i].card; 1567 var rect = card.getBoundingClientRect(); 1568 if (clientX >= rect.left && clientX <= rect.right && 1569 clientY >= rect.top - 20 && clientY <= rect.bottom + 20) { 1570 targetIndex = i; 1571 insertBefore = clientX < (rect.left + rect.width / 2); 1572 break; 1573 } 1574 } 1575 1576 if (targetIndex === -1) { 1577 clearDropIndicators(); 1578 dragState.currentTargetIndex = -1; 1579 return; 1580 } 1581 1582 if (dragState.currentTargetIndex === targetIndex && 1583 dragState.currentInsertBefore === insertBefore) { 1584 return; 1585 } 1586 1587 dragState.currentTargetIndex = targetIndex; 1588 dragState.currentInsertBefore = insertBefore; 1589 1590 clearDropIndicators(); 1591 1592 var targetCard = slotsData[targetIndex].card; 1593 var indicator = document.createElement('div'); 1594 indicator.className = 'slot-drop-indicator ' + (insertBefore ? 'indicator-left' : 'indicator-right'); 1595 targetCard.appendChild(indicator); 1596 targetCard.classList.add('is-drag-over'); 1597 } 1598 1599 function endDrag(commit) { 1600 if (!dragState) return; 1601 1602 var sourceIndex = dragState.sourceIndex; 1603 var targetIndex = dragState.currentTargetIndex; 1604 var insertBefore = dragState.currentInsertBefore; 1605 1606 if (dragState.ghostEl && dragState.ghostEl.parentNode) { 1607 dragState.ghostEl.parentNode.removeChild(dragState.ghostEl); 1608 } 1609 1610 clearDropIndicators(); 1611 1612 if (slotsData[sourceIndex]) { 1613 slotsData[sourceIndex].card.classList.remove('is-drag-placeholder'); 1614 } 1615 1616 document.body.style.userSelect = ''; 1617 document.body.style.webkitUserSelect = ''; 1618 document.body.style.cursor = ''; 1619 1620 if (commit && targetIndex !== -1) { 1621 var insertIndex = insertBefore ? targetIndex : targetIndex + 1; 1622 if (sourceIndex < insertIndex) insertIndex -= 1; 1623 1624 if (insertIndex !== sourceIndex) { 1625 reorderSlots(sourceIndex, insertIndex); 1626 } 1627 } 1628 1629 dragState = null; 1630 } 1631 1632 function reorderSlots(fromIndex, toIndex) { 1633 if (fromIndex === toIndex) return; 1634 if (fromIndex < 0 || fromIndex >= slotsData.length) return; 1635 if (toIndex < 0 || toIndex >= slotsData.length) return; 1636 1637 var moved = slotsData.splice(fromIndex, 1)[0]; 1638 slotsData.splice(toIndex, 0, moved); 1639 1640 slotsData.forEach(function (o) { 1641 slotsContainer.appendChild(o.card); 1642 }); 1643 1644 reindexSlots(); 1645 updateStatuses(); 1646 saveSlots(); 1647 renderPreview(); 1648 if (outputSection.style.display !== 'none') renderCode(); 1649 } 1650 1651 function addSlot(val, shouldFocus, customWidth, customHeight, insertAtStart) { 1652 var slotObj = createSlotCard(val || '', insertAtStart ? 0 : slotsData.length, customWidth, customHeight); 1653 1654 if (insertAtStart) { 1655 slotsData.unshift(slotObj); 1656 slotsContainer.insertBefore(slotObj.card, slotsContainer.firstChild); 1657 reindexSlots(); 1658 } else { 1659 slotsData.push(slotObj); 1660 slotsContainer.appendChild(slotObj.card); 1661 } 1662 1663 updateRemoveButtons(); 1664 onInput(); 1665 if (shouldFocus) { 1666 slotObj.card.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); 1667 setTimeout(function () { 1668 try { slotObj.input.focus(); } catch (e) {} 1669 }, 80); 1670 } 1671 } 1672 1673 function removeSlot(cardEl) { 1674 if (slotsData.length <= 1) return; 1675 var idx = -1; 1676 for (var i = 0; i < slotsData.length; i++) { 1677 if (slotsData[i].card === cardEl) { idx = i; break; } 1678 } 1679 if (idx !== -1) { 1680 slotsContainer.removeChild(cardEl); 1681 slotsData.splice(idx, 1); 1682 reindexSlots(); 1683 updateRemoveButtons(); 1684 onInput(); 1685 } 1686 } 1687 1688 function reindexSlots() { 1689 slotsData.forEach(function (obj, i) { 1690 obj.num.textContent = i + 1; 1691 obj.title.textContent = 'Embed ' + (i + 1); 1692 }); 1693 } 1694 1695 function updateRemoveButtons() { 1696 slotsData.forEach(function (obj) { 1697 obj.removeBtn.style.display = slotsData.length > 1 ? '' : 'none'; 1698 }); 1699 } 1700 1701 function saveSlots() { 1702 try { 1703 var data = slotsData.map(function (obj) { 1704 return { 1705 code: obj.input.value, 1706 width: obj.customWidth || null, 1707 height: obj.customHeight || null 1708 }; 1709 }); 1710 localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); 1711 } catch (e) {} 1712 } 1713 1714 function loadSlots() { 1715 try { 1716 var raw = localStorage.getItem(STORAGE_KEY); 1717 if (raw) { 1718 var data = JSON.parse(raw); 1719 if (Array.isArray(data) && data.length > 0) { 1720 data.forEach(function (val) { 1721 if (typeof val === 'string') { 1722 addSlot(val); 1723 } else if (val && typeof val === 'object') { 1724 addSlot(val.code || '', false, val.width, val.height); 1725 } 1726 }); 1727 return; 1728 } 1729 } 1730 } catch (e) {} 1731 addSlot(''); 1732 addSlot(''); 1733 addSlot(''); 1734 addSlot(''); 1735 } 1736 1737 function applyTheme(theme) { 1738 if (theme === 'dark' || theme === 'light') { 1739 document.documentElement.setAttribute('data-theme', theme); 1740 } else { 1741 document.documentElement.removeAttribute('data-theme'); 1742 } 1743 } 1744 1745 function initTheme() { 1746 try { 1747 var saved = localStorage.getItem(THEME_KEY); 1748 if (saved) applyTheme(saved); 1749 } catch (e) {} 1750 } 1751 1752 document.getElementById('themeToggle').addEventListener('click', function () { 1753 var current = document.documentElement.getAttribute('data-theme'); 1754 var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 1755 var effectiveDark = current === 'dark' || (!current && prefersDark); 1756 var next = effectiveDark ? 'light' : 'dark'; 1757 applyTheme(next); 1758 try { localStorage.setItem(THEME_KEY, next); } catch (e) {} 1759 }); 1760 1761 if (addSlotBtn) { 1762 addSlotBtn.addEventListener('click', function () { 1763 addSlot('', true, null, null, true); 1764 }); 1765 } 1766 1767 if (addSlotBtnBottom) { 1768 addSlotBtnBottom.addEventListener('click', function () { 1769 addSlot('', true, null, null, false); 1770 }); 1771 } 1772 1773 gapButtons.forEach(function (btn) { 1774 btn.addEventListener('click', function () { 1775 gapButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1776 btn.setAttribute('aria-pressed', 'true'); 1777 currentGap = parseInt(btn.getAttribute('data-gap'), 10); 1778 renderPreview(); 1779 if (outputSection.style.display !== 'none') renderCode(); 1780 }); 1781 }); 1782 1783 modeButtons.forEach(function (btn) { 1784 btn.addEventListener('click', function () { 1785 modeButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1786 btn.setAttribute('aria-pressed', 'true'); 1787 layoutMode = btn.getAttribute('data-mode'); 1788 if (layoutMode === 'masonry') { 1789 gapControlWrap.style.display = 'none';
1790 if (masonryControlsPanel) masonryControlsPanel.style.display = ''; 1791 } else if (layoutMode === 'carousel') { 1792 gapControlWrap.style.display = 'none'; 1793 if (masonryControlsPanel) masonryControlsPanel.style.display = 'none'; 1794 } else { 1795 gapControlWrap.style.display = ''; 1796 if (masonryControlsPanel) masonryControlsPanel.style.display = 'none'; 1797 } 1798 carouselIndex = 0; 1799 renderPreview(); 1800 if (outputSection.style.display !== 'none') renderCode(); 1801 }); 1802 }); 1803 1804 masonryPresetButtons.forEach(function (btn) { 1805 btn.addEventListener('click', function () { 1806 var presetKey = btn.getAttribute('data-preset'); 1807 applyMasonryPreset(presetKey); 1808 }); 1809 }); 1810 1811 masonryColsButtons.forEach(function (btn) { 1812 btn.addEventListener('click', function () { 1813 masonryColsButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1814 btn.setAttribute('aria-pressed', 'true'); 1815 masonryCols = btn.getAttribute('data-mcols') || 'auto'; 1816 activePreset = 'custom'; 1817 updateMasonryButtonsUI(); 1818 saveMasonryConfig(); 1819 if (layoutMode === 'masonry') { 1820 renderPreview(); 1821 if (outputSection.style.display !== 'none') renderCode(); 1822 } 1823 }); 1824 }); 1825 1826 masonryGapButtons.forEach(function (btn) { 1827 btn.addEventListener('click', function () { 1828 masonryGapButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1829 btn.setAttribute('aria-pressed', 'true'); 1830 masonryGap = parseInt(btn.getAttribute('data-mgap'), 10) || 0; 1831 activePreset = 'custom'; 1832 updateMasonryButtonsUI(); 1833 saveMasonryConfig(); 1834 if (layoutMode === 'masonry') { 1835 renderPreview(); 1836 if (outputSection.style.display !== 'none') renderCode(); 1837 } 1838 }); 1839 }); 1840 1841 masonryPaddingButtons.forEach(function (btn) { 1842 btn.addEventListener('click', function () { 1843 masonryPaddingButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1844 btn.setAttribute('aria-pressed', 'true'); 1845 masonryPadding = parseInt(btn.getAttribute('data-mpadding'), 10) || 0; 1846 activePreset = 'custom'; 1847 updateMasonryButtonsUI(); 1848 saveMasonryConfig(); 1849 if (layoutMode === 'masonry') { 1850 renderPreview(); 1851 if (outputSection.style.display !== 'none') renderCode(); 1852 } 1853 }); 1854 }); 1855 1856 masonryRadiusButtons.forEach(function (btn) { 1857 btn.addEventListener('click', function () { 1858 masonryRadiusButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1859 btn.setAttribute('aria-pressed', 'true'); 1860 masonryRadius = parseInt(btn.getAttribute('data-mradius'), 10) || 0; 1861 activePreset = 'custom'; 1862 updateMasonryButtonsUI(); 1863 saveMasonryConfig(); 1864 if (layoutMode === 'masonry') { 1865 renderPreview(); 1866 if (outputSection.style.display !== 'none') renderCode(); 1867 } 1868 }); 1869 }); 1870 1871 masonryStyleButtons.forEach(function (btn) { 1872 btn.addEventListener('click', function () { 1873 masonryStyleButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1874 btn.setAttribute('aria-pressed', 'true'); 1875 masonryStyle = btn.getAttribute('data-mstyle') || 'card'; 1876 activePreset = 'custom'; 1877 updateMasonryButtonsUI(); 1878 saveMasonryConfig(); 1879 if (layoutMode === 'masonry') { 1880 renderPreview(); 1881 if (outputSection.style.display !== 'none') renderCode(); 1882 } 1883 }); 1884 }); 1885 1886 masonryHoverButtons.forEach(function (btn) { 1887 btn.addEventListener('click', function () { 1888 masonryHoverButtons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); }); 1889 btn.setAttribute('aria-pressed', 'true'); 1890 masonryHover = btn.getAttribute('data-mhover') || 'none'; 1891 activePreset = 'custom'; 1892 updateMasonryButtonsUI(); 1893 saveMasonryConfig(); 1894 if (layoutMode === 'masonry') { 1895 renderPreview(); 1896 if (outputSection.style.display !== 'none') renderCode(); 1897 } 1898 }); 1899 }); 1900 1901 function buildFrameDoc(code) { 1902 return '<!doctype html><html><head><meta charset="utf-8">' + 1903 '<meta name="viewport" content="width=device-width, initial-scale=1">' + 1904 '<base target="_blank">' + 1905 '<style>' + 1906 '*,*::before,*::after{box-sizing:border-box;}' + 1907 'html{width:100%;margin:0;padding:0;background:transparent;}' + 1908 'body{margin:0;padding:8px;width:100%;box-sizing:border-box;font-family:system-ui,-apple-system,sans-serif;' + 1909 'color:#0f172a;background:transparent;display:block;overflow:hidden;}
1909' + 1910 'img{max-width:100%!important;height:auto!important;margin:0 auto;display:block;border-radius:6px;border:0;}' + 1911 'video,iframe,embed,object{max-width:100%!important;margin:0 auto;display:block;border-radius:6px;border:0;}' + 1912 'blockquote{max-width:100%!important;margin:0 auto!important;border-radius:6px;border:0;}' + 1913 '</style>' + 1914 '<script>' + 1915 '(function(){' + 1916 ' function sendSize(){' + 1917 ' var body = document.body, html = document.documentElement;' + 1918 ' var h = Math.max(body.scrollHeight, body.offsetHeight, html.scrollHeight, html.offsetHeight);' + 1919 ' if(h > 0){ window.parent.postMessage({ type: "EZ_FRAME_RESIZE", height: h }, "*"); }' + 1920 ' }' + 1921 ' window.addEventListener("load", function(){ sendSize(); setTimeout(sendSize, 300); setTimeout(sendSize, 1000); });' + 1922 ' window.addEventListener("resize", sendSize);' + 1923 ' if(window.ResizeObserver){ new ResizeObserver(sendSize).observe(document.body); }' + 1924 '})();' + 1925 '<' + '/script>' + 1926 '</head><body>' + code + '</body></html>'; 1927 } 1928 1929 window.addEventListener('message', function (e) { 1930 if (e.data && e.data.type === 'EZ_FRAME_RESIZE' && e.data.height) { 1931 var frames = document.querySelectorAll('.stage-cell iframe, .carousel-viewport iframe'); 1932 for (var i = 0; i < frames.length; i++) { 1933 if (frames[i].contentWindow === e.source) { 1934 var cell = frames[i].closest('.stage-cell, .carousel-viewport'); 1935 if (cell && !cell.classList.contains('is-resizing')) { 1936 var targetH = Math.min(Math.max(e.data.height + 24, 180), 800); 1937 cell.style.height = targetH + 'px'; 1938 } 1939 } 1940 } 1941 } 1942 }); 1943 1944 function renderPreview() { 1945 if (layoutMode === 'carousel') renderCarouselPreview(); 1946 else if (layoutMode === 'masonry') renderMasonryPreview(); 1947 else renderRowPreview(); 1948 } 1949 1950 function setupResizableCell(cell, obj, i) { 1951 var parsedH = parseSnippetHeight(obj.input.value); 1952 if (obj.customWidth) cell.style.width = obj.customWidth + 'px'; 1953 if (obj.customHeight) cell.style.height = obj.customHeight + 'px'; 1954 else if (parsedH) cell.style.height = (parsedH + 24) + 'px'; 1955 1956 cell.addEventListener('pointerdown', function (e) { 1957 var rect = cell.getBoundingClientRect(); 1958 if (e.clientX > rect.right - 30 && e.clientY > rect.bottom - 30) { 1959 cell.classList.add('is-resizing'); 1960 var onUp = function () { 1961 cell.classList.remove('is-resizing'); 1962 window.removeEventListener('pointerup', onUp); 1963 window.removeEventListener('pointercancel', onUp); 1964 }; 1965 window.addEventListener('pointerup', onUp); 1966 window.addEventListener('pointercancel', onUp); 1967 } 1968 }); 1969 1970 var isInitial = true; 1971 if (window.ResizeObserver) { 1972 var ro = new ResizeObserver(function () { 1973 if (isInitial) { 1974 isInitial = false; 1975 return; 1976 } 1977 if (cell.style.width || cell.style.height) { 1978 var w = Math.round(cell.getBoundingClientRect().width || cell.offsetWidth); 1979 var h = Math.round(cell.getBoundingClientRect().height || cell.offsetHeight); 1980 if (w > 50 && h > 50) { 1981 obj.customWidth = w; 1982 obj.customHeight = h; 1983 saveSlots(); 1984 if (outputSection.style.display !== 'none') renderCode(); 1985 } 1986 } 1987 }); 1988 ro.observe(cell); 1989 } 1990 1991 cell.addEventListener('pointerup', function () { 1992 if (cell.style.width || cell.style.height) { 1993 var w = Math.round(cell.getBoundingClientRect().width || cell.offsetWidth); 1994 var h = Math.round(cell.getBoundingClientRect().height || cell.offsetHeight); 1995 if (w > 50) obj.customWidth = w; 1996 if (h > 50) obj.customHeight = h; 1997 saveSlots(); 1998 if (outputSection.style.display !== 'none') renderCode(); 1999 } 2000 }); 2001 } 2002 2003 function renderRowPreview() { 2004 var maxCols = Math.min(slotsData.length, 4); 2005 var isSingle = slotsData.length === 1; 2006 stageRow.className = 'stage-row' + (isSingle ? ' is-single-embed' : ''); 2007 stageRow.style.display = 'grid'; 2008 stageRow.style.columnCount = ''; 2009 stageRow.style.columnGap = ''; 2010 stageRow.style.gap = currentGap + 'px'; 2011 stageRow.style.gridTemplateColumns = isSingle ? '1fr' : 'repeat(' + maxCols + ', minmax(280px, 1fr))'; 2012 stageRow.style.minWidth = isSingle ? '0' : (maxCols * 280 + (maxCols - 1) * currentGap) + 'px'; 2013 stageRow.style.maxWidth = isSingle ? '640px' : ''; 2014 stageRow.style.margin = '0 auto'; 2015 stageRow.innerHTML = ''; 2016 2017 slotsData.forEach(function (obj, i) { 2018 var cell = document.createElement('div'); 2019 cell.className = 'stage-cell'; 2020 var code = extractBodyContent(obj.input.value); 2021 if (code) { 2022 var frame = document.createElement('iframe'); 2023 frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals allow-presentation allow-top-navigation-by-user-activation'); 2024 frame.setAttribute('srcdoc', buildFrameDoc(code)); 2025 frame.title = 'Preview of slot ' + (i + 1); 2026 cell.appendChild(frame); 2027 } else { 2028 var empty = document.createElement('div'); 2029 empty.className = 'stage-empty'; 2030 empty.textContent = 'Slot ' + (i + 1) + ' â paste embed code above to preview it here'; 2031 cell.appendChild(empty); 2032 } 2033 setupResizableCell(cell, obj, i); 2034 stageRow.appendChild(cell); 2035 }); 2036 } 2037 2038 function renderMasonryPreview() { 2039 var cols = masonryCols === 'auto' ? Math.min(slotsData.length, 4) : parseInt(masonryCols, 10); 2040 if (!cols || cols < 1) cols = 1; 2041 var isSingle = slotsData.length === 1 && (masonryCols === 'auto' || masonryCols === '1'); 2042 2043 stageRow.className = 'masonry-preview' + (isSingle ? ' is-single-embed' : ''); 2044 stageRow.style.display = 'flex'; 2045 stageRow.style.flexDirection = 'row'; 2046 stageRow.style.alignItems = 'flex-start'; 2047 stageRow.style.gap = masonryGap + 'px'; 2048 stageRow.style.columnCount = ''; 2049 stageRow.style.columnGap = ''; 2050 stageRow.style.gridTemplateColumns = ''; 2051 stageRow.style.minWidth = isSingle ? '0' : '100%'; 2052 stageRow.style.maxWidth = isSingle ? '640px' : '100%'; 2053 stageRow.style.margin = '0 auto'; 2054 stageRow.innerHTML = ''; 2055 2056 var colEls = []; 2057 for (var c = 0; c < cols; c++) { 2058 var colDiv = document.createElement('div'); 2059 colDiv.className = 'masonry-preview-col'; 2060 colDiv.style.flex = '1 1 0'; 2061 colDiv.style.display = 'flex'; 2062 colDiv.style.flexDirection = 'column'; 2063 colDiv.style.gap = masonryGap + 'px'; 2064 colDiv.style.minWidth = '0'; 2065 stageRow.appendChild(colDiv); 2066 colEls.push(colDiv); 2067 } 2068 2069 slotsData.forEach(function (obj, i) { 2070 var targetCol = colEls[i % cols]; 2071 2072 var cell = document.createElement('div'); 2073 cell.className = 'stage-cell masonry-stage-cell'; 2074 cell.setAttribute('data-mstyle', masonryStyle); 2075 cell.setAttribute('data-mhover', masonryHover); 2076 cell.style.padding = masonryPadding + 'px'; 2077 cell.style.borderRadius = masonryRadius + 'px'; 2078 2079 var badge = document.createElement('span'); 2080 badge.className = 'masonry-slot-badge';
2081 badge.textContent = '#' + (i + 1); 2082 cell.appendChild(badge); 2083 2084 var code = extractBodyContent(obj.input.value); 2085 if (code) { 2086 var frame = document.createElement('iframe'); 2087 frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals allow-presentation allow-top-navigation-by-user-activation'); 2088 frame.setAttribute('srcdoc', buildFrameDoc(code)); 2089 frame.title = 'Preview of slot ' + (i + 1); 2090 cell.appendChild(frame); 2091 } else { 2092 var empty = document.createElement('div'); 2093 empty.className = 'stage-empty'; 2094 empty.textContent = 'Slot ' + (i + 1) + ' â paste embed code above to preview it here'; 2095 cell.appendChild(empty); 2096 } 2097 setupResizableCell(cell, obj, i); 2098 targetCol.appendChild(cell); 2099 }); 2100 } 2101 2102 function filledSlots() { 2103 return slotsData 2104 .map(function (obj, i) { 2105 return { 2106 i: i, 2107 code: extractBodyContent(obj.input.value), 2108 width: obj.customWidth || null, 2109 height: obj.customHeight || null 2110 }; 2111 }) 2112 .filter(function (s) { return s.code; }); 2113 } 2114 2115 function renderCarouselPreview() { 2116 stageRow.className = 'carousel-preview'; 2117 stageRow.style.display = 'flex'; 2118 stageRow.style.flexDirection = 'column'; 2119 stageRow.style.columnCount = ''; 2120 stageRow.style.columnGap = ''; 2121 stageRow.style.gap = '14px'; 2122 stageRow.style.gridTemplateColumns = ''; 2123 stageRow.style.minWidth = '0'; 2124 stageRow.style.width = '100%'; 2125 stageRow.style.maxWidth = '640px'; 2126 stageRow.style.margin = '0 auto'; 2127 stageRow.innerHTML = ''; 2128 var filled = filledSlots(); 2129 2130 if (!filled.length) { 2131 var empty = document.createElement('div'); 2132 empty.className = 'stage-empty'; 2133 empty.style.height = '360px'; 2134 empty.style.minWidth = '100%'; 2135 empty.textContent = 'Paste embed code into at least one slot to preview the carousel'; 2136 stageRow.appendChild(empty); 2137 return; 2138 } 2139 2140 if (carouselIndex >= filled.length) carouselIndex = 0; 2141 2142 if (filled.length > 1) { 2143 var controls = document.createElement('div'); 2144 controls.className = 'carousel-controls'; 2145 2146 var prevBtn = document.createElement('button'); 2147 prevBtn.type = 'button'; 2148 prevBtn.className = 'carousel-arrow'; 2149 prevBtn.setAttribute('aria-label', 'Previous clip'); 2150 prevBtn.textContent = 'â¹'; 2151 prevBtn.addEventListener('click', function () { 2152 carouselIndex = (carouselIndex - 1 + filled.length) % filled.length; 2153 renderCarouselPreview(); 2154 }); 2155 2156 var dots = document.createElement('div'); 2157 dots.className = 'carousel-dots'; 2158 filled.forEach(function (s, idx) { 2159 var d = document.createElement('button'); 2160 d.type = 'button'; 2161 d.setAttribute('aria-label', 'Go to clip ' + (idx + 1)); 2162 if (idx === carouselIndex) d.className = 'is-active'; 2163 d.addEventListener('click', function () { 2164 carouselIndex = idx; 2165 renderCarouselPreview(); 2166 }); 2167 dots.appendChild(d); 2168 }); 2169 2170 var nextBtn = document.createElement('button'); 2171 nextBtn.type = 'button'; 2172 nextBtn.className = 'carousel-arrow'; 2173 nextBtn.setAttribute('aria-label', 'Next clip'); 2174 nextBtn.textContent = 'âº'; 2175 nextBtn.addEventListener('click', function () { 2176 carouselIndex = (carouselIndex + 1) % filled.length; 2177 renderCarouselPreview(); 2178 }); 2179 2180 controls.appendChild(prevBtn); 2181 controls.appendChild(dots); 2182 controls.appendChild(nextBtn); 2183 stageRow.appendChild(controls); 2184 } 2185 2186 var viewport = document.createElement('div'); 2187 viewport.className = 'carousel-viewport'; 2188 if (carouselWidth) viewport.style.width = carouselWidth + 'px'; 2189 if (carouselHeight) viewport.style.height = carouselHeight + 'px'; 2190 2191 var frame = document.createElement('iframe'); 2192 frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals allow-presentation allow-top-navigation-by-user-activation'); 2193 frame.setAttribute('srcdoc', buildFrameDoc(filled[carouselIndex].code)); 2194 frame.title = 'Preview of clip ' + (carouselIndex + 1) + ' of ' + filled.length; 2195 viewport.appendChild(frame); 2196 stageRow.appendChild(viewport); 2197 2198 viewport.addEventListener('pointerdown', function (e) { 2199 var rect = viewport.getBoundingClientRect(); 2200 if (e.clientX > rect.right - 30 && e.clientY > rect.bottom - 30) { 2201 viewport.classList.add('is-resizing'); 2202 var onUp = function () { 2203 viewport.classList.remove('is-resizing'); 2204 window.removeEventListener('pointerup', onUp); 2205 window.removeEventListener('pointercancel', onUp); 2206 }; 2207 window.addEventListener('pointerup', onUp); 2208 window.addEventListener('pointercancel', onUp); 2209 } 2210 }); 2211 2212 var isInitialVp = true; 2213 if (window.ResizeObserver) { 2214 var roVp = new ResizeObserver(function () { 2215 if (isInitialVp) { 2216 isInitialVp = false;
2217 return; 2218 } 2219 if (viewport.style.width || viewport.style.height) { 2220 var w = Math.round(viewport.getBoundingClientRect().width || viewport.offsetWidth); 2221 var h = Math.round(viewport.getBoundingClientRect().height || viewport.offsetHeight); 2222 if (w > 50) carouselWidth = w; 2223 if (h > 50) carouselHeight = h; 2224 if (outputSection.style.display !== 'none') renderCode(); 2225 } 2226 }); 2227 roVp.observe(viewport); 2228 } 2229 2230 viewport.addEventListener('pointerup', function () { 2231 if (viewport.style.width || viewport.style.height) { 2232 var w = Math.round(viewport.getBoundingClientRect().width || viewport.offsetWidth); 2233 var h = Math.round(viewport.getBoundingClientRect().height || viewport.offsetHeight); 2234 if (w > 50) carouselWidth = w; 2235 if (h > 50) carouselHeight = h; 2236 if (outputSection.style.display !== 'none') renderCode(); 2237 } 2238 }); 2239 } 2240 2241 function updateStatuses() { 2242 slotsData.forEach(function (obj) { 2243 var filled = extractBodyContent(obj.input.value).length > 0; 2244 obj.status.textContent = filled ? 'ready' : 'empty'; 2245 obj.status.classList.toggle('filled', filled); 2246 }); 2247 } 2248 2249 function indent(code, spaces) { 2250 var pad = new Array(spaces + 1).join(' '); 2251 return code.split('\\n').map(function (line) { return pad + line; }).join('\\n'); 2252 } 2253 2254 function buildRowHTML() { 2255 var filled = filledSlots(); 2256 if (!filled.length) { 2257 return '<!DOCTYPE html>\\n<html lang="en">\\n<head>\\n <meta charset="UTF-8">\\n <title>Embed Row</title>\\n</head>\\n<body>\\n <!-- Paste embed code into at least one slot above -->\\n</body>\\n</html>'; 2258 } 2259 2260 var uid = 'embed-row-' + Math.random().toString(36).slice(2, 8); 2261 var cols = Math.min(filled.length, 4); 2262 var isSingle = filled.length === 1; 2263 2264 var itemsHtml = filled.map(function (s) { 2265 var styleParts = []; 2266 if (s.width) styleParts.push('width: ' + s.width + 'px'); 2267 if (s.height) styleParts.push('height: ' + s.height + 'px'); 2268 var styleAttr = styleParts.length ? ' style="' + styleParts.join('; ') + ';"' : ''; 2269 return ' <div class="embed-row-item' + (isSingle ? ' is-single' : '') + '"' + styleAttr + '>\\n' + indent(s.code, 6) + '\\n </div>'; 2270 }).join('\\n'); 2271 2272 return '<!DOCTYPE html>\\n' + 2273 '<html lang="en">\\n' + 2274 '<head>\\n' + 2275 ' <meta charset="UTF-8">\\n' + 2276 ' <meta name="viewport" content="width=device-width, initial-scale=1.0">\\n' + 2277 ' <title>Side-by-Side Embeds</title>\\n' + 2278 ' <style>\\n' + 2279 ' *, *::before, *::after {\\n' + 2280 ' box-sizing: border-box;\\n' + 2281 ' }\\n' + 2282 ' html, body {\\n' + 2283 ' margin: 0;\\n' + 2284 ' padding: 0;\\n' + 2285 ' font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\\n' + 2286 ' background: #f8fafc;\\n' + 2287 ' color: #0f172a;\\n' + 2288 ' min-height: 100vh;\\n' + 2289 ' overflow-x: auto;\\n' + 2290 ' -webkit-overflow-scrolling: touch;\\n' + 2291 ' }\\n' + 2292 ' .embed-scroll-container {\\n' + 2293 ' width: 100%;\\n' + 2294 ' max-width: 1400px;\\n' + 2295 ' margin: 0 auto;\\n' + 2296 ' overflow-x: auto;\\n' + 2297 ' overflow-y: hidden;\\n' + 2298 ' padding: 24px 20px;\\n' + 2299 ' box-sizing: border-box;\\n' + 2300 ' -webkit-overflow-scrolling: touch;\\n' + 2301 ' }\\n' + 2302 ' #' + uid + ' {\\n' + 2303 ' display: grid;\\n' + 2304 ' grid-template-columns: ' + (isSingle ? '1fr' : 'repeat(' + cols + ', minmax(280px, 1fr))') + ';\\n' + 2305 ' gap: ' + currentGap + 'px;\\n' + 2306 ' width: 100%;\\n' + 2307 (isSingle ? ' max-width: 640px;\\n margin: 0 auto;\\n' : '') + 2308 ' box-sizing: border-box;\\n' + 2309 ' align-items: start;\\n' + 2310 ' }\\n' + 2311 ' #' + uid + ' .embed-row-item {\\n' + 2312 ' min-width: 240px;\\n' + 2313 ' box-sizing: border-box;\\n' + 2314 ' border-radius: 14px;\\n' + 2315 ' background: #ffffff;\\n' + 2316 ' border: 1px solid #e2e8f0;\\n' + 2317 ' box-shadow: 0 4px 14px -2px rgba(0, 0, 0, 0.05), 0 2px 6px -1px rgba(0, 0, 0, 0.02);\\n' + 2318 ' padding: 12px;\\n' + 2319 ' display: flex;\\n' + 2320 ' flex-direction: column;\\n' + 2321 ' align-items: stretch;\\n' + 2322 ' justify-content: stretch;\\n' + 2323 ' resize: both;\\n' + 2324 ' overflow: auto;\\n' + 2325 ' min-height: 180px;\\n' + 2326 ' max-width: 100%;\\n' + 2327 ' transition: box-shadow 0.2s ease;\\n' + 2328 ' }\\n' + 2329 ' #' + uid + ' .embed-row-item:hover {\\n' + 2330 ' box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.08), 0 3px 8px -2px rgba(0, 0, 0, 0.04);\\n' + 2331 ' transform: translateY(-2px);\\n' + 2332 ' }\\n' + 2333 ' #' + uid + ' .embed-row-item > * {\\n' + 2334 ' width: 100% !important;\\n' + 2335 ' height: 100% !important;\\n' + 2336 ' min-height: 0 !important;\\n' + 2337 ' max-width: 100% !important;\\n' + 2338 ' max-height: 100% !important;\\n' + 2339 ' flex: 1 1 auto !important;\\n' + 2340 ' box-sizing: border-box !important;\\n' + 2341 ' }\\n' + 2342 ' #' + uid + ' .embed-row-item iframe,\\n' + 2343 ' #' + uid + ' .embed-row-item video,\\n' + 2344 ' #' + uid + ' .embed-row-item embed,\\n' + 2345 ' #' + uid + ' .embed-row-item object {\\n' + 2346 ' width: 100% !important;\\n' + 2347 ' height: 100% !important;\\n' + 2348 ' min-height: 0 !important;\\n' + 2349 ' flex: 1 1 auto !important;\\n' + 2350 ' margin: 0 auto !important;\\n' + 2351 ' border: 0 !important;\\n' + 2352 ' border-radius: 8px;\\n' + 2353 ' display: block;\\n' + 2354 ' }\\n' + 2355 ' #' + uid + ' .embed-row-item img {\\n' + 2356 ' width: 100% !important;\\n' + 2357 ' height: 100% !important;\\n' + 2358 ' object-fit: contain !important;\\n' + 2359 ' min-height: 0 !important;\\n' + 2360 ' flex: 1 1 auto !important;\\n' + 2361 ' margin: 0 auto !important;\\n' + 2362 ' border: 0 !important;\\n' + 2363 ' border-radius: 8px;\\n' + 2364 ' display: block;\\n' + 2365 ' }\\n' + 2366 ' #' + uid + ' .embed-row-item blockquote {\\n' + 2367 ' width: 100% !important;\\n' + 2368 ' max-width: 100% !important;\\n' + 2369 ' max-height: 100% !important;\\n' + 2370 ' overflow: auto !important;\\n' + 2371 ' margin: 0 auto !important;\\n' + 2372 ' border: 0 !important;\\n' + 2373 ' border-radius: 8px;\\n' + 2374 ' display: block;\\n' + 2375 ' }\\n' + 2376 ' #' + uid + ' .embed-row-item div > iframe,\\n' + 2377 ' #' + uid + ' .embed-row-item div > video,\\n' + 2378 ' #' + uid + ' .embed-row-item div > embed,\\n' + 2379 ' #' + uid + ' .embed-row-item div > object {\\n' + 2380 ' width: 100% !important;\\n' + 2381 ' height: 100% !important;\\n' + 2382 ' min-height: 0 !important;\\n' + 2383 ' }\\n' + 2384 ' #' + uid + ' .embed-row-item div > img {\\n' + 2385 ' width: 100% !important;\\n' + 2386 ' height: 100% !important;\\n' + 2387 ' object-fit: contain !important;\\n' + 2388 ' }\\n' + 2389 ' @media (min-width: 769px) {\\n' + 2390 ' #' + uid + ':has(> .embed-row-item:only-child), #' + uid + '.is-single {\\n' + 2391 ' max-width: 640px !important;\\n' + 2392 ' width: 100% !important;\\n' + 2393 ' margin: 0 auto !important;\\n' + 2394 ' }\\n' + 2395 ' }\\n' + 2396 ' @media (max-width: 1100px) {\\n' + 2397 ' #' + uid + ' {\\n' + 2398 ' grid-template-columns: ' + (isSingle ? '1fr' : 'repeat(' + Math.min(cols, 3) + ', minmax(260px, 1fr))') + ';\\n' + 2399 ' }\\n' + 2400 ' }\\n' + 2401 ' @media (max-width: 768px) {\\n' + 2402 ' #' + uid + ' {\\n' + 2403 ' grid-template-columns: 1fr;\\n' + 2404 ' gap: ' + Math.max(12, Math.round(currentGap * 0.75
2404)) + 'px;\\n' + 2405 ' max-width: 100% !important;\\n' + 2406 ' }\\n' + 2407 ' .embed-scroll-container {\\n' + 2408 ' padding: 16px 12px;\\n' + 2409 ' }\\n' + 2410 ' }\\n' + 2411 ' </style>\\n' + 2412 '</head>\\n' + 2413 '<body>\\n' + 2414 ' <div class="embed-scroll-container">\\n' + 2415 ' <div class="embed-row-wrapper' + (isSingle ? ' is-single' : '') + '" id="' + uid + '">\\n' + 2416 itemsHtml + '\\n' + 2417 ' </div>\\n' + 2418 ' </div>\\n' + 2419 '</body>\\n' + 2420 '</html>'; 2421 } 2422 2423 function buildMasonryHTML() { 2424 var filled = filledSlots(); 2425 if (!filled.length) { 2426 return '<!DOCTYPE html>\\n<html lang="en">\\n<head>\\n <meta charset="UTF-8">\\n <title>Masonry Embeds</title>\\n</head>\\n<body>\\n <!-- Paste embed code into at least one slot above -->\\n</body>\\n</html>'; 2427 } 2428 2429 var uid = 'embed-masonry-' + Math.random().toString(36).slice(2, 8); 2430 var cols = masonryCols === 'auto' ? Math.min(filled.length, 4) : parseInt(masonryCols, 10); 2431 if (!cols || cols < 1) cols = 1; 2432 var isSingle = filled.length === 1 && (masonryCols === 'auto' || masonryCols === '1'); 2433 2434 var surfaceCss = ''; 2435 if (masonryStyle === 'minimal') { 2436 surfaceCss = ' background: transparent;\\n border: none;\\n box-shadow: none;'; 2437 } else if (masonryStyle === 'outline') { 2438 surfaceCss = ' background: #ffffff;\\n border: 1px solid #cbd5e1;\\n box-shadow: none;'; 2439 } else if (masonryStyle === 'glass') { 2440 surfaceCss = ' background: rgba(255, 255, 255, 0.75);\\n backdrop-filter: blur(12px);\\n -webkit-backdrop-filter: blur(12px);\\n border: 1px solid rgba(255, 255, 255, 0.5);\\n box-shadow: 0 8px 24px 0 rgba(0, 0, 0, 0.06);'; 2441 } else { 2442 surfaceCss = ' background: #ffffff;\\n border: 1px solid #e2e8f0;\\n box-shadow: 0 4px 14px -2px rgba(0, 0, 0, 0.05), 0 2px 6px -1px rgba(0, 0, 0, 0.02);'; 2443 } 2444 2445 var hoverCss = ''; 2446 if (masonryHover === 'lift') { 2447 hoverCss = ' #' + uid + ' .masonry-item:hover {\\n' + 2448 ' transform: translateY(-3px);\\n' + 2449 ' box-shadow: 0 10px 24px -4px rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.04);\\n' + 2450 ' }\\n'; 2451 } else if (masonryHover === 'glow') { 2452 hoverCss = ' #' + uid + ' .masonry-item:hover {\\n' + 2453 ' border-color: #2563eb;\\n' + 2454 ' box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18), 0 4px 12px rgba(0, 0, 0, 0.06);\\n' + 2455 ' }\\n'; 2456 } 2457 2458 var colBuckets = []; 2459 for (var c = 0; c < cols; c++) { 2460 colBuckets.push([]); 2461 } 2462 filled.forEach(function (s, idx) { 2463 colBuckets[idx % cols].push(s); 2464 }); 2465 2466 var colsHtml = colBuckets.map(function (bucket, colIdx) { 2467 var itemsHtml = bucket.map(function (s) { 2468 var styleParts = []; 2469 if (s.width) styleParts.push('width: ' + s.width + 'px'); 2470 if (s.height) styleParts.push('height: ' + s.height + 'px'); 2471 var styleAttr = styleParts.length ? ' style="' + styleParts.join('; ') + ';"' : ''; 2472 return ' <div class="masonry-item' + (isSingle ? ' is-single' : '') + '"' + styleAttr + '>\\n' + indent(s.code, 8) + '\\n </div>'; 2473 }).join('\\n'); 2474 2475 return ' <div class="masonry-col">\\n' + itemsHtml + '\\n </div>'; 2476 }).join('\\n'); 2477 2478 return '<!DOCTYPE html>\\n' + 2479 '<html lang="en">\\n' + 2480 '<head>\\n' + 2481 ' <meta charset="UTF-8">\\n' + 2482 ' <meta name="viewport" content="width=device-width, initial-scale=1.0">\\n' + 2483 ' <title>Masonry Embeds</title>\\n' + 2484 ' <style>\\n' + 2485 ' *, *::before, *::after {\\n' + 2486 ' box-sizing: border-box;\\n' + 2487 ' }\\n' + 2488 ' html, body {\\n' + 2489 ' margin: 0;\\n' + 2490 ' padding: 0;\\n' + 2491 ' font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\\n' + 2492 ' background: #f8fafc;\\n' + 2493 ' color: #0f172a;\\n' + 2494 ' min-height: 100vh;\\n' + 2495 ' overflow-x: auto;\\n' + 2496 ' -webkit-overflow-scrolling: touch;\\n' + 2497 ' }\\n' + 2498 ' .masonry-scroll-container {\\n' + 2499 ' width: 100%;\\n' + 2500 ' max-width: 1400px;\\n' + 2501 ' margin: 0 auto;\\n' + 2502 ' overflow-x: auto;\\n' + 2503 ' padding: 24px 20px;\\n' + 2504 ' box-sizing: border-box;\\n' + 2505 ' -webkit-overflow-scrolling: touch;\\n' + 2506 ' }\\n' + 2507 ' #' + uid + ' {\\n' + 2508 ' display: flex;\\n' + 2509 ' flex-direction: row;\\n' + 2510 ' align-items: flex-start;\\n' + 2511 ' gap: ' + masonryGap + 'px;\\n' + 2512 ' width: 100%;\\n' + 2513 (isSingle ? ' max-width: 640px;\\n margin: 0 auto;\\n' : '') + 2514 ' box-sizing: border-box;\\n' + 2515 ' }\\n' + 2516 ' #' + uid + ' .masonry-col {\\n' + 2517 ' flex: 1 1 0;\\n' + 2518 ' display: flex;\\n' + 2519 ' flex-direction: column;\\n' + 2520 ' gap: ' + masonryGap + 'px;\\n' + 2521 ' min-width: 0;\\n' + 2522 ' }\\n' + 2523 ' #' + uid + ' .masonry-item {\\n' + 2524 ' display: flex;\\n' + 2525 ' flex-direction: column;\\n' + 2526 ' align-items: stretch;\\n' + 2527 ' justify-content: stretch;\\n' + 2528 ' width: 100%;\\n' + 2529 ' box-sizing: border-box;\\n' + 2530 ' min-width: 200px;\\n' + 2531 ' border-radius: ' + masonryRadius + 'px;\\n' + 2532 surfaceCss + '\\n' + 2533 ' padding: ' + masonryPadding + 'px;\\n' + 2534 ' resize: both;\\n' + 2535 ' overflow: auto;\\n' + 2536 ' min-height: 180px;\\n' + 2537 ' max-width: 100%;\\n' + 2538 ' transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;\\n' + 2539 ' }\\n' + 2540 hoverCss + 2541 ' #' + uid + ' .masonry-item > * {\\n' + 2542 ' width: 100% !important;\\n' + 2543 ' height: 100% !important;\\n' + 2544 ' min-height: 0 !important;\\n' + 2545 ' max-width: 100% !important;\\n' + 2546 ' max-height: 100% !important;\\n' + 2547 ' flex: 1 1 auto !important;\\n' + 2548 ' box-sizing: border-box !important;\\n' + 2549 ' }\\n' + 2550 ' #' + uid + ' .masonry-item iframe,\\n' + 2551 ' #' + uid + ' .masonry-item video,\\n' + 2552 ' #' + uid + ' .masonry-item embed,\\n' + 2553 ' #' + uid + ' .masonry-item object {\\n' + 2554 ' width: 100% !important;\\n' + 2555 ' height: 100% !important;\\n' + 2556 ' min-height: 0 !important;\\n' + 2557 ' flex: 1 1 auto !important;\\n' + 2558 ' margin: 0 auto !important;\\n' + 2559 ' border: 0 !important;\\n' + 2560 ' border-radius: inherit;\\n' + 2561 ' display: block;\\n' + 2562 ' }\\n' + 2563 ' #' + uid + ' .masonry-item img {\\n' + 2564 ' width: 100% !important;\\n' + 2565 ' height: 100% !important;\\n' + 2566 ' object-fit: contain !important;\\n' + 2567 ' min-height: 0 !important;\\n' + 2568 ' flex: 1 1 auto !important;\\n' + 2569 ' margin: 0 auto !important;\\n' + 2570 ' border: 0 !important;\\n' + 2571 ' border-radius: inherit;\\n' + 2572 ' display: block;\\n' + 2573 ' }\\n' + 2574 ' #' + uid + ' .masonry-item blockquote {\\n' + 2575 ' width: 100% !important;\\n' + 2576 ' max-width: 100% !important;\\n' + 2577 ' max-height: 100% !important;\\n' + 2578 ' overflow: auto !important;\\n' + 2579 ' margin: 0 auto !important;\\n' + 2580 ' border: 0 !important;\\n' + 2581 ' border-radius: inherit;\\n' + 2582 ' display: block;\\n' + 2583 ' }\\n' + 2584 ' #' + uid + ' .masonry-item div > iframe,\\n' + 2585 ' #' + uid + ' .masonry-item div > video,\\n' + 2586 ' #' + uid + ' .masonry-item div > embed,\\n' + 2587 ' #' + uid + ' .masonry-item div > object {\\n' + 2588 ' width: 100% !important;\\n' + 2589 ' height: 100% !important;\\n' + 2590 ' min-height: 0 !important;\\n' + 2591 ' }\\n' + 2592 ' #' + uid + ' .masonry-item div > img {\\n' + 2593 ' width: 100% !important;\\n' + 2594 ' height: 100% !important;\\n' + 2595 ' object-fit: contain !important;\\n' + 2596 ' }\\n' + 2597 ' @media (max-width: 768px) {\\n' + 2598 ' #' + uid + ' {\\n' + 2599 ' flex-direction: column;\\n' + 2600 ' gap: ' + Math.max(8, Math.round(masonryGap * 0.6)) + 'px;\\n' + 2601 ' max-width: 100% !important;\\n' + 2602 ' }\\n' + 2603 ' #' + uid + ' .masonry-col {\\n' + 2604 ' width: 100%;\\n' + 2605 ' }\\n' + 2606 ' #' + uid + ' .masonry-item {\\n' + 2607 ' padding: ' + Math.max(6, Math.round(masonryPadding * 0.75)) + 'px;\\n' + 2608 ' }\\n' + 2609 ' .masonry-scroll-container {\\n' + 2610 ' padding: 16px 10px;\\n' + 2611 ' }\\n' + 2612 ' }\\n' + 2613 ' </style>\\n' + 2614 '</head>\\n' + 2615 '<body>\\n' + 2616 ' <div class="masonry-scroll-container">\\n' + 2617 ' <div class="masonry-wrapper' + (isSingle ? ' is-single' : '') + '" id="' + uid + '">\\n' +
2618 colsHtml + '\\n' + 2619 ' </div>\\n' + 2620 ' </div>\\n' + 2621 '</body>\\n' + 2622 '</html>'; 2623 } 2624 2625 function buildCarouselHTML() { 2626 var filled = filledSlots(); 2627 if (!filled.length) { 2628 return '<!DOCTYPE html>\\n<html lang="en">\\n<head>\\n <meta charset="UTF-8">\\n <title>Embed Carousel</title>\\n</head>\\n<body>\\n <!-- Paste embed code into at least one slot above -->\\n</body>\\n</html>'; 2629 } 2630 2631 var uid = 'embed-carousel-' + Math.random().toString(36).slice(2, 8); 2632 var multi = filled.length > 1; 2633 2634 var slidesHtml = filled.map(function (s, i) { 2635 return ' <div class="ec-slide" data-active="' + (i === 0 ? 'true' : 'false') + '">\\n' + 2636 indent(s.code, 8) + '\\n </div>'; 2637 }).join('\\n'); 2638 2639 var dotsHtml = filled.map(function (_, i) { 2640 return ' <button type="button" class="ec-dot' + (i === 0 ? ' is-active' : '') + 2641 '" aria-label="Go to slide ' + (i + 1) + '"></button>'; 2642 }).join('\\n'); 2643 2644 var controlsHtml = multi 2645 ? ' <div class="ec-controls">\\n' + 2646 ' <button type="button" class="ec-prev" aria-label="Previous clip">â¹</button>\\n' + 2647 ' <div class="ec-dots">\\n' + dotsHtml + '\\n </div>\\n' + 2648 ' <button type="button" class="ec-next" aria-label="Next clip">âº</button>\\n' + 2649 ' </div>\\n' 2650 : ''; 2651 2652 var controlsCss = multi 2653 ? ' #' + uid + ' { display: flex; flex-direction: column; width: 100%; max-width: 680px; margin: 0 auto; box-sizing: border-box; gap: 14px; }\\n' + 2654 ' #' + uid + ' .ec-controls { order: -1; display: flex; align-items: center; justify-content: center; gap: 12px; margin: 0 auto 4px auto; padding: 5px 14px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 9999px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); width: fit-content; }\\n' + 2655 ' #' + uid + ' .ec-prev, #' + uid + ' .ec-next { width: 36px; height: 36px; border-radius: 50%; border: 1px solid #cbd5e1; background: #ffffff; color: #1e293b; cursor: pointer; font-size: 18px; font-weight: 600; line-height: 1; display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); transition: all 0.15s ease; }\\n' + 2656 ' #' + uid + ' .ec-prev:hover, #' + uid + ' .ec-next:hover { border-color: #2563eb; background: #2563eb; color: #ffffff; transform: translateY(-1px); box-shadow: 0 4px 10px rgba(37, 99, 235, 0.25); }\\n' + 2657 ' #' + uid + ' .ec-prev:active, #' + uid + ' .ec-next:active { transform: translateY(0px) scale(0.95); }\\n' + 2658 ' #' + uid + ' .ec-dots { display: flex; align-items: center; gap: 7px; padding: 0 4px; }\\n' + 2659 ' #' + uid + ' .ec-dots button { width: 8px; height: 8px; border-radius: 50%; border: none; background: #cbd5e1; padding: 0; cursor: pointer; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); }\\n' + 2660 ' #' + uid + ' .ec-dots button.is-active { background: #2563eb; width: 22px; border-radius: 9999px; }\\n' + 2661 ' #' + uid + ' .ec-viewport { order: 1; position: relative; overflow: auto; -webkit-overflow-scrolling: touch; border-radius: 14px; background: #ffffff; box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.08), 0 2px 6px -1px rgba(0, 0, 0, 0.04); border: 1px solid #e2e8f0; width: 100%; min-width: 280px; min-height: 180px; padding: 12px; box-sizing: border-box; resize: both; display: flex; flex-direction: column; align-items: stretch; justify-content: stretch; }\\n' 2662 : ' #' + uid + ' { width: 100%; max-width: 680px; margin: 0 auto; box-sizing: border-box; }\\n' + 2663 ' #' + uid + ' .ec-viewport { position: relative; overflow: auto; -webkit-overflow-scrolling: touch; border-radius: 14px; background: #ffffff; box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.08), 0 2px 6px -1px rgba(0, 0, 0, 0.04); border: 1px solid #e2e8f0; width: 100%; min-width: 280px; min-height: 180px; padding: 12px; box-sizing: border-box; resize: both; display: flex; flex-direction: column; align-items: stretch; justify-content: stretch; }\\n'; 2664 2665 var script = multi 2666 ? ' <script>\\n' + 2667 ' (function () {\\n' + 2668 ' var root = document.getElementById(\\'' + uid + '\\');\\n' + 2669 ' if (!root) return;\\n' + 2670 ' var slides = root.querySelectorAll(\\'.ec-slide\\');\\n' + 2671 ' var dots = root.querySelectorAll(\\'.ec-dot\\');\\n' + 2672 ' var prev = root.querySelector(\\'.ec-prev\\');\\n' + 2673 ' var next = root.querySelector(\\'.ec-next\\');\\n' + 2674 ' var idx = 0;\\n' + 2675 ' function show(i) {\\n' + 2676 ' idx = (i + slides.length) % slides.length;\\n' + 2677 ' for (var j = 0; j < slides.length; j++) slides[j].setAttribute(\\'data-active\\', j === idx ? \\'true\\' : \\'false\\');\\n' + 2678 ' for (var k = 0; k < dots.length; k++) dots[k].classList.toggle(\\'is-active\\', k === idx);\\n' + 2679 ' }\\n' + 2680 ' if (prev) prev.addEventListener(\\'click\\', function () { show(idx - 1); });\\n' + 2681 ' if (next) next.addEventListener(\\'click\\', function () { show(idx + 1); });\\n' + 2682 ' for (var d = 0; d < dots.length; d++) {\\n' + 2683 ' (function (n) { dots[n].addEventListener(\\'click\\', function () { show(n); }); })(d);\\n' + 2684 ' }\\n' + 2685 ' root.setAttribute(\\'tabindex\\', \\'0\\');\\n' + 2686 ' root.addEventListener(\\'keydown\\', function (e) {\\n' + 2687 ' if (e.key === \\'ArrowLeft\\') show(idx - 1);\\n' +
2688 ' if (e.key === \\'ArrowRight\\') show(idx + 1);\\n' + 2689 ' });\\n' + 2690 ' var startX = 0;\\n' + 2691 ' root.addEventListener(\\'touchstart\\', function (e) { if (e.touches[0]) startX = e.touches[0].clientX; }, { passive: true });\\n' + 2692 ' root.addEventListener(\\'touchend\\', function (e) {\\n' + 2693 ' if (!e.changedTouches[0]) return;\\n' + 2694 ' var diffX = e.changedTouches[0].clientX - startX;\\n' + 2695 ' if (Math.abs(diffX) > 45) {\\n' + 2696 ' if (diffX > 0) show(idx - 1); else show(idx + 1);\\n' + 2697 ' }\\n' + 2698 ' }, { passive: true });\\n' + 2699 ' })();\\n' + 2700 ' <' + '/script>' 2701 : ''; 2702 2703 var viewportStyle = ''; 2704 if (carouselWidth || carouselHeight) { 2705 var vpParts = []; 2706 if (carouselWidth) vpParts.push('width: ' + carouselWidth + 'px'); 2707 if (carouselHeight) vpParts.push('height: ' + carouselHeight + 'px'); 2708 viewportStyle = ' style="' + vpParts.join('; ') + ';"'; 2709 } 2710 2711 return '<!DOCTYPE html>\\n' + 2712 '<html lang="en">\\n' + 2713 '<head>\\n' + 2714 ' <meta charset="UTF-8">\\n' + 2715 ' <meta name="viewport" content="width=device-width, initial-scale=1.0">\\n' + 2716 ' <title>Embed Carousel</title>\\n' + 2717 ' <style>\\n' + 2718 ' html, body {\\n' + 2719 ' margin: 0;\\n' + 2720 ' padding: 24px 16px;\\n' + 2721 ' font-family: system-ui, -apple-system, sans-serif;\\n' + 2722 ' background-color: #f1f5f9;\\n' + 2723 ' min-height: 100vh;\\n' + 2724 ' display: flex;\\n' + 2725 ' flex-direction: column;\\n' + 2726 ' align-items: center;\\n' + 2727 ' box-sizing: border-box;\\n' + 2728 ' overflow-x: hidden;\\n' + 2729 ' overflow-y: auto;\\n' + 2730 ' }\\n' + 2731 ' *, *::before, *::after { box-sizing: border-box; }\\n' + 2732 ' #' + uid + ' { width: 100%; max-width: 680px; margin: 0 auto; box-sizing: border-box; }\\n' + 2733 ' #' + uid + ' .ec-slide { display: none; width: 100%; height: 100%; min-height: 100%; align-items: stretch; justify-content: stretch; overflow: auto; -webkit-overflow-scrolling: touch; }\\n' + 2734 ' #' + uid + ' .ec-slide[data-active="true"] { display: flex; flex-direction: column; }\\n' + 2735 ' #' + uid + ' .ec-slide > * { width: 100% !important; height: 100% !important; min-height: 0 !important; max-width: 100% !important; max-height: 100% !important; flex: 1 1 auto !important; box-sizing: border-box !important; }\\n' + 2736 ' #' + uid + ' .ec-slide iframe, #' + uid + ' .ec-slide video, #' + uid + ' .ec-slide embed, #' + uid + ' .ec-slide object { width: 100% !important; height: 100% !important; min-height: 0 !important; flex: 1 1 auto !important; border: 0 !important; border-radius: 10px; display: block; margin: 0 auto; }\\n' + 2737 ' #' + uid + ' .ec-slide img { width: 100% !important; height: 100% !important; object-fit: contain !important; min-height: 0 !important; flex: 1 1 auto !important; border: 0 !important; border-radius: 10px; display: block; margin: 0 auto; }\\n' + 2738 ' #' + uid + ' .ec-slide blockquote { width: 100% !important; max-width: 100% !important; max-height: 100% !important; overflow: auto !important; border: 0 !important; border-radius: 10px; display: block; margin: 0 auto; }\\n' + 2739 ' #' + uid + ' .ec-slide div > iframe, #' + uid + ' .ec-slide div > video, #' + uid + ' .ec-slide div > embed, #' + uid + ' .ec-slide div > object { width: 100% !important; height: 100% !important; min-height: 0 !important; }\\n' + 2740 ' #' + uid + ' .ec-slide div > img { width: 100% !important; height: 100% !important; object-fit: contain !important; }\\n' + 2741 controlsCss + 2742 ' </style>\\n' + 2743 '</head>\\n' + 2744 '<body>\\n' + 2745 ' <div class="ec" id="' + uid + '">\\n' + 2746 controlsHtml + 2747 ' <div class="ec-viewport"' + viewportStyle + '>\\n' + slidesHtml + '\\n </div>\\n' + 2748 ' </div>\\n' + 2749 (script ? '\\n' + script + '\\n' : '') + 2750 '</body>\\n' + 2751 '</html>'; 2752 } 2753 2754 function renderCode() { 2755 var html; 2756 var filename; 2757 if (layoutMode === 'carousel') { 2758 html = buildCarouselHTML(); 2759 filename = 'carousel.html'; 2760 } else if (layoutMode === 'masonry') { 2761 html = buildMasonryHTML(); 2762 filename = 'masonry.html'; 2763 } else { 2764 html = buildRowHTML(); 2765 filename = 'row.html'; 2766 } 2767 2768 codeOut.textContent = html; 2769 codeOut.dataset.raw = html; 2770 codeFileLabel.textContent = filename; 2771 } 2772 2773 var debounceHandle = null; 2774 function onInput() { 2775 saveSlots(); 2776 updateStatuses(); 2777 clearTimeout(debounceHandle); 2778 debounceHandle = setTimeout(renderPreview, 350); 2779 if (outputSection.style.display !== 'none') renderCode(); 2780 } 2781 2782 document.getElementById('generateBtn').addEventListener('click', function () { 2783 renderCode(); 2784 outputSection.style.display = ''; 2785 outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); 2786 }); 2787 2788 var convoBtn = document.getElementById('convoBtn'); 2789 if (convoBtn) { 2790 convoBtn.addEventListener('click', function () { 2791 var html = layoutMode === 'carousel' ? buildCarouselHTML() : (layoutMode === 'masonry' ? buildMasonryHTML() : buildRowHTML()); 2792 2793 convoBtn.disabled = true; 2794 var originalBtnContent = convoBtn.innerHTML; 2795 convoBtn.innerHTML = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="animation:spin 1s linear infinite;"><circle cx="12" cy="12" r="10" stroke-opacity="0.25"></circle><path d="M12 2a10 10 0 0 1 10 10"></path></svg><span>Processing...</span>'; 2796
2797 try { 2798 if (window.parent && window.parent !== window) { 2799 window.parent.postMessage({ 2800 type: 'EZ_CREATE_CONVO_HTML', 2801 html: html, 2802 content_type: 'embed', 2803 isEdit: !!isEditMode, 2804 editId: editId, 2805 editSlug: editSlug 2806 }, '*'); 2807 return; 2808 } 2809 } catch (e) {} 2810 2811 if (isEditMode && (editId || editSlug)) { 2812 var targetMsgIdentifier = editId || editSlug; 2813 fetch('/ai/message/' + encodeURIComponent(targetMsgIdentifier) + '/update-content', { 2814 method: 'PATCH', 2815 headers: headers, 2816 body: JSON.stringify({ content: html, content_type: 'embed' }) 2817 }) 2818 .then(function (res) { return res.json(); }) 2819 .then(function (data) { 2820 if (data && data.success) { 2821 convoBtn.innerHTML = '<span>Updated!</span>'; 2822 setTimeout(function () { 2823 convoBtn.disabled = false; 2824 convoBtn.innerHTML = originalBtnContent; 2825 }, 1500); 2826 } else { 2827 convoBtn.disabled = false; 2828 convoBtn.innerHTML = originalBtnContent; 2829 alert((data && data.error) || 'Failed to update embed content.'); 2830 } 2831 }) 2832 .catch(function (err) { 2833 console.error(err); 2834 convoBtn.disabled = false; 2835 convoBtn.innerHTML = originalBtnContent; 2836 alert('Failed to update embed content.'); 2837 }); 2838 return; 2839 } 2840 2841 var csrfToken = ''; 2842 try { 2843 if (window.parent && window.parent.document) { 2844 var metaParent = window.parent.document.querySelector('meta[name="csrf-token"]'); 2845 if (metaParent) csrfToken = metaParent.getAttribute('content') || ''; 2846 } 2847 } catch (e) {} 2848 if (!csrfToken) { 2849 var metaLocal = document.querySelector('meta[name="csrf-token"]'); 2850 if (metaLocal) csrfToken = metaLocal.getAttribute('content') || ''; 2851 } 2852 2853 var headers = { 2854 'Content-Type': 'application/json', 2855 'Accept': 'application/json', 2856 'X-Requested-With': 'XMLHttpRequest' 2857 }; 2858 if (csrfToken) { 2859 headers['X-CSRF-TOKEN'] = csrfToken; 2860 } 2861 2862 fetch('/content/comment', { 2863 method: 'POST', 2864 headers: headers, 2865 body: JSON.stringify({ content: html, content_type: 'embed' }) 2866 }) 2867 .then(function (res) { 2868 return res.json().catch(function () { 2869 return { success: res.ok, redirected: res.redirected, url: res.url }; 2870 }); 2871 }) 2872 .then(function (data) { 2873 if (data && (data.slug || data.conversation_id)) { 2874 var targetSlug = data.slug || data.conversation_id; 2875 var targetUrl = '/X/' + encodeURIComponent(targetSlug); 2876 try { 2877 if (window.parent && window.parent !== window) { 2878 window.parent.location.href = targetUrl; 2879 return; 2880 } 2881 } catch (e) {} 2882 window.location.href = targetUrl; 2883 } else if (data && data.comment && data.comment.slug) { 2884 var targetUrl2 = '/X/' + encodeURIComponent(data.comment.slug); 2885 try { 2886 if (window.parent && window.parent !== window) { 2887 window.parent.location.href = targetUrl2; 2888 return; 2889 } 2890 } catch (e) {} 2891 window.location.href = targetUrl2; 2892 } else { 2893 submitFallbackForm(html, csrfToken); 2894 } 2895 }) 2896 .catch(function () { 2897 submitFallbackForm(html, csrfToken); 2898 }); 2899 2900 function submitFallbackForm(contentHtml, token) { 2901 try { 2902 var form = document.createElement('form'); 2903 form.method = 'POST'; 2904 form.action = '/content/comment'; 2905 if (window.parent && window.parent !== window) { 2906 form.target = '_top'; 2907 } 2908 if (token) { 2909 var tokenInput = document.createElement('input'); 2910 tokenInput.type = 'hidden'; 2911 tokenInput.name = '_token'; 2912 tokenInput.value = token; 2913 form.appendChild(tokenInput); 2914 } 2915 var contentInput = document.createElement('input'); 2916 contentInput.type = 'hidden'; 2917 contentInput.name = 'content'; 2918 contentInput.value = contentHtml; 2919 form.appendChild(contentInput); 2920 var contentTypeInput = document.createElement('input'); 2921 contentTypeInput.type = 'hidden'; 2922 contentTypeInput.name = 'content_type'; 2923 contentTypeInput.value = 'embed'; 2924 form.appendChild(contentTypeInput); 2925 document.body.appendChild(form); 2926 form.submit(); 2927 } catch (err) { 2928 convoBtn.disabled = false;
2929 convoBtn.innerHTML = originalBtnContent; 2930 alert('Failed to save conversation. Please try again.'); 2931 } 2932 } 2933 }); 2934 } 2935 2936 document.getElementById('resetBtn').addEventListener('click', function () { 2937 slotsData.forEach(function (obj) { 2938 obj.input.value = ''; 2939 obj.customWidth = null; 2940 obj.customHeight = null; 2941 }); 2942 carouselWidth = null; 2943 carouselHeight = null; 2944 saveSlots(); 2945 updateStatuses(); 2946 renderPreview(); 2947 outputSection.style.display = 'none'; 2948 }); 2949 2950 var copyBtn = document.getElementById('copyBtn'); 2951 copyBtn.addEventListener('click', function () { 2952 var text = codeOut.dataset.raw || codeOut.textContent || ''; 2953 function showCopied() { 2954 var original = 'Copy code'; 2955 copyBtn.textContent = 'Copied â'; 2956 copyBtn.classList.add('copied'); 2957 setTimeout(function () { 2958 copyBtn.textContent = original; 2959 copyBtn.classList.remove('copied'); 2960 }, 1600); 2961 } 2962 function fallbackCopy() { 2963 try { 2964 var ta = document.createElement('textarea'); 2965 ta.value = text; 2966 ta.style.position = 'fixed'; 2967 ta.style.opacity = '0'; 2968 document.body.appendChild(ta); 2969 ta.focus(); 2970 ta.select(); 2971 var ok = document.execCommand('copy'); 2972 document.body.removeChild(ta); 2973 if (ok) { showCopied(); return; } 2974 } catch (e) {} 2975 copyBtn.textContent = 'Select & press â/Ctrl+C'; 2976 setTimeout(function () { copyBtn.textContent = 'Copy code'; }, 2200); 2977 } 2978 if (navigator.clipboard && navigator.clipboard.writeText) { 2979 navigator.clipboard.writeText(text).then(showCopied, fallbackCopy); 2980 } else { 2981 fallbackCopy(); 2982 } 2983 }); 2984 2985 initTheme(); 2986 loadMasonryConfig(); 2987 loadSlots(); 2988 try { 2989 if (window.parent && window.parent !== window) { 2990 window.parent.postMessage({ type: 'EZ_BUILDER_READY' }, '*'); 2991 } 2992 } catch (e) {} 2993})(); 2994<\/script> 2995 2996</body> 2997</html>`,Ve=({isOpen:o,onClose:d,parentSlug:v,conversationId:a,onChildConvoCreated:M,onCommentCreated:y,isEdit:C=!1,editId:p,editSlug:b,initialHtml:w,onUpdate:j})=>{const f=n.useRef(null),h=n.useCallback(()=>{f.current&&f.current.contentWindow&&f.current.contentWindow.postMessage({type:"EZ_SET_EDIT_MODE",isEdit:!!C,editId:p||null,editSlug:b||null,initialHtml:w||""},"*")},[C,p,b,w]);if(n.useEffect(()=>{if(!o)return;const c=setTimeout(h,100),r=setTimeout(h,300),k=setTimeout(h,600),E=setTimeout(h,1200);return()=>{clearTimeout(c),clearTimeout(r),clearTimeout(k),clearTimeout(E)}},[o,h]),n.useEffect(()=>{if(!o)return;const c=async r=>{var k;if(!(!f.current||!f.current.contentWindow)&&r.source===f.current.contentWindow){if(r.data&&r.data.type==="EZ_BUILDER_READY"){h();return}if(r.data&&r.data.type==="EZ_CREATE_CONVO_HTML"&&r.data.html){const E=r.data.html;if(C||j||r.data.isEdit){if(j)try{await j(E),d();return}catch(s){console.error("Failed in onUpdate:",s)}const x=p||r.data.editId||b||r.data.editSlug||v;if(x)try{const s=await ee.patch(`/ai/message/${x}/update-content`,{content:E,content_type:"embed"});if(s.data&&s.data.success){window.dispatchEvent(new CustomEvent("ez:conversation-updated",{detail:s.data})),d();return}}catch(s){console.error("Failed to update embed message:",s)}return}if(M)try{await M(E,"embed"),d();return}catch(x){console.error("Failed in onChildConvoCreated:",x)}try{const x={content:E,content_type:"embed",parent_slug:null,conversation_id:a||null},s=await ee.post("/content/comment",x,{headers:{Accept:"application/json","X-CSRF-TOKEN":((k=document.querySelector('meta[name="csrf-token"]'))==null?void 0:k.getAttribute("content"))||""}});if(s.data&&s.data.success){if(y&&s.data.conversation_messages){y(s.data.conversation_messages),d();return}if(v||a){d(),window.dispatchEvent(new CustomEvent("ez:conversation-updated",{detail:s.data}));return}s.data.slug?window.location.href="/X/"+encodeURIComponent(s.data.slug):s.data.conversation_id&&(window.location.href="/X/"+encodeURIComponent(s.data.conversation_id))}}catch(x){console.error("Failed to create conversation from message:",x)}}}};return window.addEventListener("message",c),()=>window.removeEventListener("message",c)},[o,d,v,a,M,y,C,p,b,j,h]),n.useEffect(()=>{if(!o)return;const c=r=>{r.key==="Escape"&&d()};return window.addEventListener("keydown",c),()=>window.removeEventListener("keydown",c)},[o,d]),!o)return null;const _=()=>{try{const c=new Blob([Q],{type:"text/html;charset=utf-8"}),r=URL.createObjectURL(c),k=window.open(r,"_blank");k&&k.focus()}catch{const c=window.open();c&&(c.document.open(),c.document.write(Q),c.document.close())}},Y=()=>{f.current&&f.current.contentWindow&&f.current.contentWindow.postMessage({type:"EZ_TRIGGER_SAVE"},"*")};return t.jsx("div",{className:"fixed inset-0 z-[100000] bg-black/80 backdrop-blur-md flex items-center justify-center p-2 sm:p-4 md:p-6 animate-in fade-in duration-200",onClick:d,children:t.jsxs("div",{className:"flex flex-col w-full max-w-[1240px] h-[92vh] max-h-[920px] bg-slate-900 rounded-2xl shadow-2xl overflow-hidden border border-slate-700/80 transition-all duration-300",onClick:c=>c.stopPropagation(),children:[t.jsxs("div",{className:"bg-slate-950 px-5 py-3 flex items-center justify-between border-b border-slate-800 select-none",children:[t.jsxs("div",{className:"flex items-center space-x-3",children:[t.jsxs("div",{className:"flex items-center space-x-1.5",children:[t.jsx("button",{type:"button",onClick:d,className:"w-3.5 h-3.5 rounded-full bg-[#ff5f56] hover:opacity-80 transition-opacity cursor-pointer border-0",title:"Close popup"}),t.jsx("span",{className:"w-3.5 h-3.5 rounded-full bg-[#ffbd2e] inline-block"}),t.jsx("span",{className:"w-3.5 h-3.5 rounded-full bg-[#27c93f] inline-block"})]}),t.jsx("div",{className:"h-4 w-px bg-slate-800 mx-1"}),t.jsxs("div",{className:"flex items-center space-x-2",children:[t.jsx(T,{icon:Ke,className:"text-orange-500 text-sm"}),t.jsx("span",{className:"text-sm font-bold text-white tracking-wide",children:"Embed Row & Masonry Builder"})]})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("button",{type:"button",onClick:Y,className:"px-3 py-1.5 text-xs rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white font-medium flex items-center gap-1.5 transition-colors cursor-pointer",title:"Save and create conversation",children:[t.jsx(T,{icon:Ce}),t.jsx("span",{children:"Save"})]}),t.jsxs("button",{type:"button",onClick:_,className:"px-3 py-1.5 text-xs rounded-lg bg-slate-800 hover:bg-slate-700 text-emerald-400 hover:text-emerald-300 transition-colors flex items-center gap-1.5 font-medium border border-slate-700 cursor-pointer",title:"Open in a new tab",children:[t.jsx(T,{icon:Ee}),t.jsx("span",{className:"hidden sm:inline",children:"Open Tab"})]}),t.jsxs("button",{type:"button",onClick:d,className:"px-3 py-1.5 text-xs rounded-lg bg-rose-600 hover:bg-rose-700 text-white font-medium flex items-center gap-1.5 transition-colors cursor-pointer",title:"Close modal",children:[t.jsx(T,{icon:Me}),t.jsx("span",{children:"Close"})]})]})]}),t.jsx("div",{className:"flex-1 w-full h-full bg-white relative overflow-hidden",children:t.jsx("iframe",{ref:f,srcDoc:Q,title:"Embed Row & Masonry Builder",className:"w-full h-full border-0 block",style:{backgroundColor:"#f3f4f1"},sandbox:"allow-scripts allow-same-origin allow-popups allow-forms allow-modals",allow:"autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share; fullscreen",allowFullScreen:!0,onLoad:h})})]})})},A={width:120,height:120,padding:20},Je=.25,Qe=350,et=2e3,de=4,ce=[{id:"1",angleStart:288,angleEnd:360,label:"1",color:"#f43f5e",gradient:["#f43f5e","#e11d48"],labelAngle:324},{id:"2",angleStart:0,angleEnd:72,label:"2",color:"#f59e0b",gradient:["#f59e0b","#d97706"],labelAngle:36},{id:"3",angleStart:72,angleEnd:144,label:"3",color:"#10b981",gradient:["#10b981","#059669"],labelAngle:108},{id:"4",angleStart:144,angleEnd:216,label:"4",color:"#3b82f6",gradient:["#3b82f6","#2563eb"],labelAngle:180},{id:"5",angleStart:216,angleEnd:288,label:"5",color:"#8b5cf6",gradient:["#8b5cf6","#7c3aed"],labelAngle:252}].map(o=>{const d=o.labelAngle*Math.PI/180,v=32,a=o.angleStart*Math.PI/180,M=o.angleEnd*Math.PI/180,y=(50+46*Math.cos(a)).toFixed(2),C=(50+46*Math.sin(a)).toFixed(2),p=(50+46*Math.cos(M)).toFixed(2),b=(50+46*Math.sin(M)).toFixed(2),w=`M 50 50 L ${y} ${C} A 46 46 0 0 1 ${p} ${b} Z`;return{...o,pathData:w,x:(50+v*Math.cos(d)).toFixed(2),y:(50+v*Math.sin(d)).toFixed(2)}}),tt={1:{bg:"linear-gradient(135deg, rgba(244,63,94,0.95), rgba(225,29,72,0.95))",glow:"rgba(244,63,94,0.35)",border:"rgba(244,63,94,0.6)"}
2997,2:{bg:"linear-gradient(135deg, rgba(245,158,11,0.95), rgba(217,119,6,0.95))",glow:"rgba(245,158,11,0.35)",border:"rgba(245,158,11,0.6)"},3:{bg:"linear-gradient(135deg, rgba(16,185,129,0.95), rgba(5,150,105,0.95))",glow:"rgba(16,185,129,0.35)",border:"rgba(16,185,129,0.6)"},4:{bg:"linear-gradient(135deg, rgba(59,130,246,0.95), rgba(37,99,235,0.95))",glow:"rgba(59,130,246,0.35)",border:"rgba(59,130,246,0.6)"},5:{bg:"linear-gradient(135deg, rgba(139,92,246,0.95), rgba(124,58,237,0.95))",glow:"rgba(139,92,246,0.35)",border:"rgba(139,92,246,0.6)"}},nt=({message:o,visible:d})=>t.jsxs("div",{id:"click-message",className:d?"visible":"",role:"status","aria-live":"polite",children:[t.jsx("div",{className:"msg-icon",children:"â¦"}),t.jsx("span",{children:o})]}),at=({onLogoClick:o,onAvatarClick:d,isTopCorner:v})=>t.jsxs("div",{className:`logo-section-container absolute left-1/2 -translate-x-1/2 flex flex-col items-center gap-2.5 pointer-events-auto z-30 transition-all duration-500 ${v?"top-[calc(100%+14px)]":"bottom-[calc(100%+14px)]"}`,onPointerDown:a=>a.stopPropagation(),onMouseDown:a=>a.stopPropagation(),onTouchStart:a=>a.stopPropagation(),children:[t.jsxs("div",{className:"w-[3.6rem] h-[3.6rem] rounded-full flex items-center justify-center overflow-hidden relative avatar-glow cursor-pointer hover:scale-105 active:scale-95 transition-transform",onClick:d,onPointerDown:a=>a.stopPropagation(),onMouseDown:a=>a.stopPropagation(),onTouchStart:a=>a.stopPropagation(),role:"button",tabIndex:0,"aria-label":"Open Embed Row & Masonry Builder",title:"Click to open Embed Row & Masonry Builder",onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),d(a))},children:[t.jsx("div",{className:"absolute inset-0 rounded-full bg-gradient-to-tr from-[#059669] via-[#34d399] to-[#10b981] opacity-90"}),t.jsx("img",{src:"https://ez.wiki/logo.gif",className:"w-[78%] h-[78%] object-contain relative z-10 filter drop-shadow-lg pointer-events-none",alt:"ez.wiki Logo"})]}),t.jsx("div",{className:"split-logo",children:t.jsx("span",{className:"logo-ez cursor-pointer select-none",onClick:a=>o(a,"/dashboard","EZ Dashboard"),role:"button",tabIndex:0,onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&o(a,"/dashboard","EZ Dashboard")},children:"EZ.WIKI"})})]}),lt=({auth:o,parentSlug:d,conversationId:v,onChildConvoCreated:a,onCommentCreated:M})=>{const y=n.useRef(null),C=n.useRef(null),p=n.useRef(null),b=n.useRef(null),w=n.useRef(),[j,f]=n.useState("0.00"),[h,_]=n.useState(!1),[Y,c]=n.useState(null),[r,k]=n.useState(null),[E,x]=n.useState({visible:!1,text:""}),[s,te]=n.useState(!1),[ne,ae]=n.useState(!1),[q,oe]=n.useState(null),[P,pe]=n.useState("br"),[I,ue]=n.useState(null),[F,me]=n.useState({width:window.innerWidth,height:window.innerHeight}),[X,fe]=n.useState(!1);n.useEffect(()=>{fe(!0)},[]),n.useEffect(()=>{let e=!0;return(async()=>{if(o!=null&&o.user){_(!0),c(null);try{const m=await ee.get("/buy-bee/balance");e&&f(Number(m.data.balance||0).toFixed(2))}catch{e&&(f("0.00"),c("Failed to load balance"))}finally{e&&_(!1)}}})(),()=>{e=!1}},[o==null?void 0:o.user]),n.useEffect(()=>{const e=()=>me({width:window.innerWidth,height:window.innerHeight});return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[]),n.useEffect(()=>()=>{w.current&&clearTimeout(w.current)},[]);const $=n.useMemo(()=>h?"Loading...":Y?"--.-- EZ$":`${j} EZ$`,[h,Y,j]),ge=n.useMemo(()=>{const e=[{href:"/",action:"HOME",icon:le,label:"Home"},{href:"/login",action:"SIGN IN",icon:Ie,label:"Sign In"},{href:"/register",action:"SIGN UP",icon:Be,label:"Sign Up"}];return o!=null&&o.user?{1:[{href:"/aihome",action:"HOME",icon:le,label:"Home"},{href:"/dashboard",action:"DASHBOARD",icon:Ue,label:"Dashboard"},{href:"/settings/profile",action:"PROFILE",icon:Xe,label:"Profile"},{href:"/purchase",action:$,icon:$e,label:$}],2:[{href:"/sendbee",action:"Send EZ$",icon:qe,label:"Send EZ$"},{href:"/purchasehistory",action:"EZ$ Purchase History",icon:Fe,label:"EZ$ Purchase History"},{href:"/ez-transfer",action:"EZ TRANSFER",icon:We,label:"EZ Transfer"},{href:"/transfer/history",action:"EZ TRANSFER HISTORY",icon:Ge,label:"Transfer History"}],3:[{href:"/marketplace",action:"DOMAIN MART",icon:Ne,label:"Domain Mart"},{href:"/my-coupons",action:"Coupon Usage",icon:Oe,label:"Coupon Usage"},{href:"/handlepurchasehistory",action:"Handle Purchase History",icon:_e,label:"Handle Purchase History"},{href:"/handlesellhistory",action:"Handle Sell History",icon:Ye,label:"Handle Sell History"}],4:[{href:"/token-transactions",action:"EZ$ Transaction History",icon:Ae,label:"EZ$ Transaction History"},{href:"/incentivehistory",action:"Incentive History",icon:Te,label:"Incentive History"},{href:"/pending-transfers",action:"Pending Transaction",icon:De,label:"Pending Transaction"},{href:"/refund-transfers",action:"Refund History",icon:ze,label:"Refund History"}],5:[{href:"/",action:"New Conversation",icon:Re,label:"New Conversation"},{href:"/public/ai/history",action:"Slug Wall",icon:je,label:"Slug Wall"},{href:"/ai/history",action:"Slug Management",icon:Pe,label:"Slug Management"},{href:"/domains",action:"Domain Management",icon:He,label:"Domain Management"},{href:"/logout",action:"Logout",icon:Le,label:"Logout",method:"post",isDestructive:!0}]}:{1:e,2:e,3:e,4:e,5:e}},[o==null?void 0:o.user,$]),Z=n.useMemo(()=>
2997{if(P)return P;if(I){const e=I.left+A.width/2,l=I.top+A.height/2,{width:m,height:S}=F;return l<S/2?e<m/2?"tl":"tr":e<m/2?"bl":"br"}return"br"},[P,I,F]),K=Z==="tr"||Z==="tl",B=n.useCallback(()=>k(null),[]),W=n.useCallback(e=>{k(l=>l===e?null:e)},[]),re=n.useCallback(e=>{w.current&&clearTimeout(w.current),x({visible:!0,text:e}),w.current=setTimeout(()=>{x(l=>({...l,visible:!1}))},et)},[]),G=n.useCallback((e,l,m="get")=>{re(`Navigating to ${l}...`),B(),setTimeout(()=>{m==="post"?se.post(e):se.visit(e)},Qe)},[B,re]),ie=n.useCallback((e,l,m,S="get")=>{e.preventDefault(),e.stopPropagation(),G(m,l,S)},[G]),be=n.useCallback((e,l,m)=>{e.preventDefault(),e.stopPropagation(),G(l,m)},[G]),he=n.useCallback(e=>{e.preventDefault(),e.stopPropagation(),B(),ae(!0)},[B]),xe=n.useCallback(e=>{(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),W(r||"1"))},[r,W]);n.useEffect(()=>{const e=C.current,l=y.current;if(!e||!l)return;const m=i=>{if(!p.current)return;const g=i.clientX-p.current.pointerX,u=i.clientY-p.current.pointerY;if(!p.current.hasMoved&&(Math.abs(g)>de||Math.abs(u)>de)&&(p.current.hasMoved=!0,te(!0),B()),p.current.hasMoved){let D=p.current.elementY+u,z=p.current.elementX+g;const{width:V,height:J}=F,N=l.offsetWidth||120,O=l.offsetHeight||120;z=Math.max(A.padding,Math.min(z,V-N-A.padding)),D=Math.max(A.padding,Math.min(D,J-O-A.padding));const U={top:D,left:z};oe(U),b.current=U}},S=i=>{var g;window.removeEventListener("pointermove",m),window.removeEventListener("pointerup",S);try{e.releasePointerCapture(i.pointerId)}catch{}if((g=p.current)!=null&&g.hasMoved)b.current&&(ue(b.current),pe(null));else{const u=e.getBoundingClientRect(),D=i.clientX-u.left,z=i.clientY-u.top,V=u.width/2,J=u.height/2,N=D-V,O=z-J,U=Math.sqrt(N*N+O*O),we=u.width*Je;if(U>=we){const R=(Math.atan2(O,N)*(180/Math.PI)+360)%360;let L;R>=288||R<18?L="1":R>=18&&R<90?L="2":R>=90&&R<162?L="3":R>=162&&R<234?L="4":L="5",W(L)}}p.current=null,te(!1),oe(null),b.current=null},H=i=>{if(i.button!==0)return;const g=i.target;if(g&&g.closest(".logo-section-container, .avatar-glow, .split-logo, .logo-ez, .logo-wiki"))return;e.setPointerCapture(i.pointerId);const u=l.getBoundingClientRect();p.current={pointerX:i.clientX,pointerY:i.clientY,elementX:u.left,elementY:u.top,hasMoved:!1},window.addEventListener("pointermove",m),window.addEventListener("pointerup",S)};return e.addEventListener("pointerdown",H),()=>{e.removeEventListener("pointerdown",H),window.removeEventListener("pointermove",m),window.removeEventListener("pointerup",S)}},[B,W,F]),n.useEffect(()=>{if(!r)return;const e=l=>{s||y.current&&!y.current.contains(l.target)&&B()};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}},[r,B,s]);const ve=n.useCallback(()=>{const e="24px";return s&&q?{position:"fixed",transform:`translate3d(${q.left}px, ${q.top}px, 0)`,transition:"none",zIndex:9999}:I?{position:"fixed",top:`${I.top}px`,left:`${I.left}px`,transition:"none"}:{position:"fixed",...P?{br:{bottom:e,right:e},bl:{bottom:e,left:e},tr:{top:e,right:e},tl:{top:e,left:e}}[P]:{},transition:X?"all 0.5s cubic-bezier(0.23, 1, 0.32, 1)":"none"}},[s,q,I,P,X]),ye=n.useMemo(()=>{const e=["group z-50 select-none"];return r&&e.push(`q${r}-open`,"menu-open"),K&&e.push("tl-open","tr-open"),s&&e.push("dragging"),e.join(" ")},[r,K,s]);return t.jsxs(t.Fragment,{children:[t.jsxs(ke,{children:[t.jsx("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"}),t.jsx("style",{children:` 2998 #draggable-menu-container { 2999 z-index: 9999 !important; 3000 backface-visibility: hidden; 3001 will-change: transform, top, left, bottom, right; 3002 } 3003 3004 /* Ambient glow behind the button */ 3005 #draggable-menu-container::before { 3006 content: ''; 3007 position: absolute; 3008 inset: -20px; 3009 border-radius: 50%; 3010 background: radial-gradient(circle, rgba(52,211,153,0.15) 0%, transparent 70%); 3011 pointer-events: none; 3012 z-index: -1; 3013 opacity: 0; 3014 transition: opacity 0.5s ease; 3015 } 3016 3017 #draggable-menu-container.menu-open::before { 3018 opacity: 1; 3019 } 3020 3021 #main-button { 3022 width: 7.6rem; 3023 height: 7.6rem; 3024 box-shadow: 3025 0 0 0 1px rgba(255,255,255,0.1), 3026 0 20px 50px rgba(0,0,0,0.4), 3027 0 8px 20px rgba(0,0,0,0.3), 3028 inset 0 1px 0 rgba(255,255,255,0.15), 3029 inset 0 -1px 0 rgba(0,0,0,0.2); 3030 transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1), box-shadow 0.4s ease; 3031 touch-action: none; 3032 position: relative; 3033 background: #0f172a; 3034 border-radius: 9999px; 3035 outline: none; 3036 border: 2.5px solid rgba(255,255,255,0.9); 3037 overflow: visible; 3038 } 3039 3040 #main-button:focus-visible { 3041 box-shadow: 3042 0 0 0 4px rgba(52,211,153,0.4), 3043 0 20px 50px rgba(0,0,0,0.4), 3044 inset 0 1px 0 rgba(255,255,255,0.15); 3045 } 3046 3047 #main-button:hover { 3048 transform: scale(1.06); 3049 box-shadow: 3050 0 0 0 1px rgba(255,255,255,0.15), 3051 0 25px 60px rgba(0,0,0,0.5), 3052 0 0 30px rgba(52,211,153,0.25), 3053 inset 0 1px 0 rgba(255,255,255,0.2); 3054 } 3055 3056 #main-button:active { 3057 transform: scale(0.97); 3058 } 3059 3060 #main-button svg { 3061 overflow: visible !important; 3062 } 3063 3064 /* Wedge slices with individual gradients */ 3065 .wedge-slice { 3066 transition: all 0.25s cubic-bezier(0.23, 1, 0.32, 1); 3067 stroke: rgba(255,255,255,0.35); 3068 stroke-width: 1.5px; 3069 cursor: pointer; 3070 filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); 3071 } 3072 3073 .wedge-slice:hover { 3074 filter: brightness(1.2) drop-shadow(0 0 12px rgba(255,255,255,0.6)); 3075 stroke: rgba(255,255,255,0.8); 3076 stroke-width: 2px; 3077 } 3078 3079 /* Active wedge glow ring */ 3080 .active-wedge-ring { 3081 opacity: 0; 3082 transition: opacity 0.3s ease; 3083 pointer-events: none; 3084 } 3085 3086 .group.q1-open .ring-q1, 3087 .group.q2-open .ring-q2, 3088 .group.q3-open .ring-q3,
3089 .group.q4-open .ring-q4, 3090 .group.q5-open .ring-q5 { 3091 opacity: 1; 3092 } 3093 3094 /* Center hub */ 3095 .center-hub { 3096 width: 30%; 3097 height: 30%; 3098 background: #0f172a; 3099 border-radius: 50%; 3100 border: 2px solid rgba(255,255,255,0.8); 3101 z-index: 20; 3102 display: flex; 3103 align-items: center; 3104 justify-content: center; 3105 box-shadow: 3106 inset 0 2px 8px rgba(0,0,0,0.5), 3107 0 0 0 3px rgba(52,211,153,0.15), 3108 0 2px 8px rgba(0,0,0,0.3); 3109 pointer-events: none; 3110 position: relative; 3111 } 3112 3113 .center-hub::after { 3114 content: ''; 3115 position: absolute; 3116 inset: -6px; 3117 border-radius: 50%; 3118 border: 1.5px solid rgba(52,211,153,0.3); 3119 animation: hub-pulse 2.5s ease-in-out infinite; 3120 } 3121 3122 @keyframes hub-pulse { 3123 0%, 100% { transform: scale(1); opacity: 0.6; } 3124 50% { transform: scale(1.08); opacity: 0; } 3125 } 3126 3127 /* Menu items - color-coded per quadrant with glassmorphism */ 3128 .menu-item { 3129 display: flex; 3130 align-items: center; 3131 gap: 0.75rem; 3132 padding: 0.55rem 1rem; 3133 border-radius: 1rem; 3134 font-weight: 600; 3135 transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1); 3136 position: relative; 3137 overflow: hidden; 3138 min-width: 195px; 3139 font-size: 0.8rem; 3140 backdrop-filter: blur(24px) saturate(1.4); 3141 -webkit-backdrop-filter: blur(24px) saturate(1.4); 3142 border: 1px solid rgba(255,255,255,0.25); 3143 border-top: 1px solid rgba(255,255,255,0.5); 3144 color: #ffffff !important; 3145 font-variant-numeric: tabular-nums; 3146 text-shadow: 0 1px 3px rgba(0,0,0,0.25); 3147 box-shadow: 3148 0 8px 32px rgba(0,0,0,0.25), 3149 inset 0 1px 0 rgba(255,255,255,0.25); 3150 } 3151 3152 .menu-item::before { 3153 content: ''; 3154 position: absolute; 3155 inset: 0; 3156 border-radius: inherit; 3157 opacity: 0; 3158 transition: opacity 0.3s ease; 3159 background: linear-gradient(135deg, rgba(255,255,255,0.15), transparent); 3160 } 3161 3162 .menu-item:hover::before, 3163 .menu-item:focus-visible::before { 3164 opacity: 1; 3165 } 3166 3167 .menu-item:hover, .menu-item:focus-visible { 3168 transform: translateY(-3px) scale(1.03); 3169 box-shadow: 3170 0 16px 40px rgba(0,0,0,0.35), 3171 0 0 20px var(--item-glow, rgba(16,185,129,0.3)), 3172 inset 0 1px 0 rgba(255,255,255,0.4); 3173 border-color: rgba(255,255,255,0.6) !important; 3174 } 3175 3176 .menu-item .icon-wrapper { 3177 display: flex; 3178 align-items: center; 3179 justify-content: center; 3180 width: 1.7rem; 3181 height: 1.7rem; 3182 font-size: 0.78rem; 3183 border-radius: 0.55rem; 3184 transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1); 3185 background: rgba(255,255,255,0.18) !important; 3186 border: 1px solid rgba(255,255,255,0.4); 3187 color: #ffffff; 3188 flex-shrink: 0; 3189 box-shadow: inset 0 1px 2px rgba(255,255,255,0.1); 3190 } 3191 3192 .menu-item:hover .icon-wrapper, 3193 .menu-item:focus-visible .icon-wrapper { 3194 transform: scale(1.15) rotate(-6deg); 3195 background: rgba(255,255,255,0.35) !important; 3196 border-color: rgba(255,255,255,0.7) !important; 3197 box-shadow: 0 0 12px rgba(255,255,255,0.4); 3198 } 3199 3200 .menu-item.destructive { 3201 --item-glow: rgba(244,63,94,0.4); 3202 background: linear-gradient(135deg, rgba(244,63,94,0.92), rgba(190,18,60,0.92)) !important; 3203 border-color: rgba(244,63,94,0.4); 3204 } 3205 3206 .menu-item.destructive .icon-wrapper { 3207 background: rgba(255,255,255,0.15) !important; 3208 border-color: rgba(255,255,255,0.4) !important; 3209 } 3210 3211 .menu-item.destructive:hover { 3212 background: linear-gradient(135deg, rgba(251,113,133,0.95), rgba(244,63,94,0.95)) !important; 3213 border-color: rgba(255,255,255,0.6) !important; 3214 } 3215 3216 /* Staggered entrance animation */ 3217 @keyframes slide-in-blur { 3218 0% { transform: translateY(12px) scale(0.92); opacity: 0; filter: blur(6px); } 3219 100% { transform: translateY(0) scale(1); opacity: 1; filter: blur(0); } 3220 } 3221 3222 .quadrant-menu.open .menu-item { 3223 animation: slide-in-blur 0.35s cubic-bezier(0.23, 1, 0.32, 1) both; 3224 } 3225 3226 .quadrant-menu.open .menu-item:nth-child(1) { animation-delay: 0.02s; } 3227 .quadrant-menu.open .menu-item:nth-child(2) { animation-delay: 0.05s; } 3228 .quadrant-menu.open .menu-item:nth-child(3) { animation-delay: 0.08s; } 3229 .quadrant-menu.open .menu-item:nth-child(4) { animation-delay: 0.11s; } 3230 .quadrant-menu.open .menu-item:nth-child(5) { animation-delay: 0.14s; } 3231 .quadrant-menu.open .menu-item:nth-child(6) { animation-delay: 0.17s; } 3232 3233 /* Float animation */ 3234 @keyframes float { 3235 0%, 100% { transform: translateY(0); } 3236 50% { transform: translateY(-6px); } 3237 } 3238 3239 #draggable-menu-container:not(.menu-open):not(.dragging) { 3240 animation: float 5s ease-in-out infinite; 3241 } 3242 3243 #draggable-menu-container.dragging { 3244 animation: none; 3245 opacity: 0.88; 3246 cursor: grabbing !important; 3247 } 3248 3249 #draggable-menu-container.dragging #main-button { 3250 box-shadow: 3251 0 0 0 2px rgba(52,211,153,0.3), 3252 0 30px 70px rgba(0,0,0,0.5), 3253 inset 0 1px 0 rgba(255,255,255,0.1); 3254 } 3255 3256 /* Click message / Toast */ 3257 #click-message { 3258 position: fixed; 3259 bottom: 2.5rem; 3260 left: 50%;
3261 transform: translate(-50%, 40px) scale(0.9); 3262 padding: 0.7rem 1.6rem 0.7rem 1.2rem; 3263 border-radius: 1rem; 3264 font-weight: 600; 3265 transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); 3266 background: rgba(15,23,42,0.92); 3267 border: 1px solid rgba(52,211,153,0.4); 3268 color: #ffffff; 3269 backdrop-filter: blur(24px) saturate(1.3); 3270 -webkit-backdrop-filter: blur(24px) saturate(1.3); 3271 box-shadow: 3272 0 20px 50px rgba(0,0,0,0.4), 3273 0 0 30px rgba(52,211,153,0.15), 3274 inset 0 1px 0 rgba(255,255,255,0.1); 3275 opacity: 0; 3276 pointer-events: none; 3277 z-index: 10000; 3278 font-size: 0.85rem; 3279 text-shadow: 0 1px 2px rgba(0,0,0,0.2); 3280 display: flex; 3281 align-items: center; 3282 gap: 0.5rem; 3283 letter-spacing: 0.01em; 3284 } 3285 3286 #click-message .msg-icon { 3287 width: 1.4rem; 3288 height: 1.4rem; 3289 display: flex; 3290 align-items: center; 3291 justify-content: center; 3292 border-radius: 0.4rem; 3293 background: rgba(52,211,153,0.2); 3294 color: #34d399; 3295 font-size: 0.75rem; 3296 flex-shrink: 0; 3297 } 3298 3299 #click-message.visible { 3300 transform: translate(-50%, 0) scale(1); 3301 opacity: 1; 3302 } 3303 3304 /* Quadrant menus */ 3305 .quadrant-menu { 3306 position: absolute; 3307 display: none; 3308 flex-direction: column; 3309 gap: 0.35rem; 3310 opacity: 0; 3311 visibility: hidden; 3312 pointer-events: none; 3313 transition: opacity 0.25s ease, visibility 0s linear 0.25s, transform 0.3s ease; 3314 z-index: 20; 3315 max-height: calc(100vh - 160px); 3316 overflow-y: auto; 3317 padding: 6px; 3318 scrollbar-width: none; 3319 } 3320 3321 .quadrant-menu.open { 3322 display: flex; 3323 opacity: 1; 3324 visibility: visible; 3325 pointer-events: auto; 3326 transition-delay: 0s; 3327 } 3328 3329 .quadrant-menu::-webkit-scrollbar { display: none; } 3330 3331 .group.open .quadrant-menu { pointer-events: auto; } 3332 3333 .group.q1-open #menu-q1, 3334 .group.q2-open #menu-q2, 3335 .group.q3-open #menu-q3, 3336 .group.q4-open #menu-q4, 3337 .group.q5-open #menu-q5 { 3338 display: flex; 3339 opacity: 1; 3340 visibility: visible; 3341 pointer-events: auto; 3342 transition-delay: 0s; 3343 } 3344 3345 .group.tr-open .quadrant-menu, 3346 .group.tl-open .quadrant-menu { 3347 flex-direction: column-reverse; 3348 } 3349 3350 /* Logo styling */ 3351 .split-logo { 3352 display: flex; 3353 gap: 0.35rem; 3354 background: transparent; 3355 position: relative; 3356 z-index: 30; 3357 pointer-events: auto; 3358 } 3359 3360 .logo-ez { 3361 padding: 0.4rem 0.9rem; 3362 border-radius: 9999px; 3363 transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1); 3364 font-weight: 800; 3365 font-size: 0.68rem; 3366 position: relative; 3367 z-index: 31; 3368 cursor: pointer; 3369 text-align: center; 3370 min-width: 3.2rem; 3371 border: 1.5px solid rgba(255,255,255,0.5); 3372 letter-spacing: 0.08em; 3373 background: linear-gradient(135deg, #10b981, #047857); 3374 color: #ffffff; 3375 box-shadow: 3376 0 4px 15px rgba(5,150,105,0.35), 3377 0 0 10px rgba(52,211,153,0.15), 3378 inset 0 1px 0 rgba(255,255,255,0.2); 3379 text-shadow: 0 1px 2px rgba(0,0,0,0.2); 3380 } 3381 3382 .logo-ez:hover { 3383 transform: translateY(-2px) scale(1.08); 3384 filter: brightness(1.15); 3385 box-shadow: 3386 0 8px 25px rgba(5,150,105,0.45), 3387 0 0 20px rgba(52,211,153,0.3), 3388 inset 0 1px 0 rgba(255,255,255,0.3); 3389 border-color: rgba(255,255,255,0.8); 3390 } 3391 3392 /* Avatar glow */ 3393 .avatar-glow { 3394 box-shadow: 3395 0 0 0 2px rgba(255,255,255,0.8), 3396 0 0 25px rgba(52,211,153,0.5), 3397 0 0 50px rgba(52,211,153,0.2); 3398 animation: avatar-pulse 3s infinite ease-in-out; 3399 } 3400 3401 @keyframes avatar-pulse { 3402 0%, 100% { 3403 box-shadow: 3404 0 0 0 2px rgba(255,255,255,0.8), 3405 0 0 20px rgba(52,211,153,0.4), 3406 0 0 40px rgba(52,211,153,0.15); 3407 } 3408 50% { 3409 box-shadow: 3410 0 0 0 2px rgba(255,255,255,0.9), 3411 0 0 35px rgba(52,211,153,0.7), 3412 0 0 70px rgba(52,211,153,0.3); 3413 } 3414 } 3415 3416 /* Wedge numbers */ 3417 .wedge-number-outer { 3418 font-family: 'SF Pro Display', system-ui, -apple-system, sans-serif; 3419 text-anchor: middle; 3420 dominant-baseline: central; 3421 pointer-events: none; 3422 font-size: 14px; 3423 font-weight: 800; 3424 fill: rgba(255,255,255,0.85); 3425 transition: all 0.25s cubic-bezier(0.23, 1, 0.32, 1); 3426 text-shadow: 0 1px 4px rgba(0,0,0,0.5); 3427 } 3428 3429 .active-num-1, .active-num-2, .active-num-3, .active-num-4, .active-num-5 { 3430 fill: #ffffff !important; 3431 opacity: 1 !important; 3432 filter: drop-shadow(0 0 10px rgba(255,255,255,0.9)); 3433 font-size: 16px; 3434 font-weight: 900; 3435 } 3436 `})]}),t.jsx(nt,{message:E.text,visible:E.visible}),ne&&t.jsx(Ve,{isOpen:ne,onClose:()=>ae(!1),parentSlug:d,conversationId:v,onChildConvoCreated:a,onCommentCreated:M}),t.jsxs("div",{id:"draggable-menu-container",ref:y,className:ye,style:ve(),children:[t.jsxs("div",{id:"main-button",ref:C,className:`relative z-10 cursor-pointer flex items-center justify-center ${r?`q${r}-active`:""}`,role:"button","aria-label":"Quadrant Menu - Click to open, drag to move","aria-haspopup":"true","aria-expanded":!!r,tabIndex:0,onKeyDown:xe,children:[t.jsx(at,{onLogoClick:be,onAvatarClick:he,isTopCorner:K}),t.jsxs("svg",{viewBox:"0 0 100 100",className:"w-full h-full select-none absolute inset-0 overflow-visible pointer-events-none",children:[t.jsx("circle",{cx:"50",cy:"50",r:"49",fill:"none",stroke:"rgba(255,255,255,0.08)",strokeWidth:"0.5"}),t.jsx("circle",{cx:"50",cy:"50",r:"48",fill:"#0f172a"}),ce.map(e=>t.jsx("path",{d:e.pathData,fill:"none",stroke:e.color,strokeWidth:"3",className:`active-wedge-ring ring-q${e.id}`,style:{filter:`drop-shadow(0 0 8px ${e.color})`}},`ring-${e.id}`)),ce.map(e=>t.jsxs("g",{children:[t.jsx("defs",{children:t.jsxs("linearGradient",{id:`grad-${e.id}`,x1:"0%",y1:"0%",x2:"100%",y2:"100%",children:[t.jsx("stop",{offset:"0%",stopColor:e.gradient[0]}),t.jsx("stop",{offset:"100%",stopColor:e.gradient[1]})]})}),t.jsx("path",{d:e.pathData,fill:`url(#grad-${e.id})`,className:`wedge-slice ${r===e.id?"opacity-100":"opacity-90"}`}),t.jsx("text",{className:`wedge-number-outer ${r===e.id?`active-num-${e.id}`:""}`,x:e.x,y:e.y,children:e.label})]},e.id))]}),t.jsx("div",{className:"center-hub","aria-hidden":"true",children:t.jsx(T,{icon:Ze,className:"text-white/70 text-[10px]"})})]}),Object.entries(ge).map(([e,l])=>{const m={br:"bottom-full left-1/2 -translate-x-1/2 mb-4",bl:"bottom-full right-1/2 translate-x-1/2 mb-4",tr:"top-full left-1/2 -translate-x-1/2 mt-4",tl:"top-full right-1/2 translate-x-1/2 mt-4"}[Z],S=X&&r===e,H=tt[e];return S?t.jsx("div",{id:`menu-q${e}`,className:`quadrant-menu ${m} open`,role:"menu","aria-labelledby":"main-button",style:{display:"flex",opacity:1,visibility:"visible",pointerEvents:"auto"},children:l.map((i,g)=>t.jsxs(Se,{href:i.href,method:i.method||"get",as:i.method==="post"?"button":"a",className:`menu-item ${i.isDestructive?"destructive":""}`,title:i.label,role:"menuitem","aria-label":i.label,onClick:u=>ie(u,i.action,i.href,i.method),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&ie(u,i.action,i.href,i.method)},tabIndex:S?0:-1,style:{animationDelay:`${g*.03}s`,background:i.isDestructive?void 0:H.bg,"--item-glow":H.glow,borderColor:i.isDestructive?void 0:H.border},children:[t.jsx("div",{className:"icon-wrapper",children:t.jsx(T,{icon:i.icon})}),t.jsxs("span",{className:"truncate",children:[`${e}.${g+1}`," ",i.label]})]},`${i.href}-${g}`))},e):null})]})]})};export{lt as D,Ve as E};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.