@kywi-software/core 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/AGENT-PATTERNS.md +221 -16
  2. package/README.md +23 -5
  3. package/dist/admin/layout-editor/column-overlay.d.ts +14 -1
  4. package/dist/admin/layout-editor/column-overlay.d.ts.map +1 -1
  5. package/dist/admin/layout-editor/column-overlay.js +5 -2
  6. package/dist/admin/layout-editor/column-overlay.js.map +1 -1
  7. package/dist/admin/layout-editor/editor-canvas.d.ts +34 -1
  8. package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
  9. package/dist/admin/layout-editor/editor-canvas.js +156 -16
  10. package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
  11. package/dist/admin/layout-editor/editor-state.d.ts +57 -2
  12. package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
  13. package/dist/admin/layout-editor/editor-state.js +125 -53
  14. package/dist/admin/layout-editor/editor-state.js.map +1 -1
  15. package/dist/admin/layout-editor/editor.css +487 -44
  16. package/dist/admin/layout-editor/index.d.ts +1 -1
  17. package/dist/admin/layout-editor/index.d.ts.map +1 -1
  18. package/dist/admin/layout-editor/index.js.map +1 -1
  19. package/dist/admin/layout-editor/inline-text.d.ts +44 -0
  20. package/dist/admin/layout-editor/inline-text.d.ts.map +1 -0
  21. package/dist/admin/layout-editor/inline-text.js +67 -0
  22. package/dist/admin/layout-editor/inline-text.js.map +1 -0
  23. package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
  24. package/dist/admin/layout-editor/layout-editor.js +23 -31
  25. package/dist/admin/layout-editor/layout-editor.js.map +1 -1
  26. package/dist/admin/layout-editor/module-overlay.d.ts +32 -1
  27. package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
  28. package/dist/admin/layout-editor/module-overlay.js +108 -6
  29. package/dist/admin/layout-editor/module-overlay.js.map +1 -1
  30. package/dist/admin/layout-editor/node-lookup.d.ts +94 -0
  31. package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -0
  32. package/dist/admin/layout-editor/node-lookup.js +142 -0
  33. package/dist/admin/layout-editor/node-lookup.js.map +1 -0
  34. package/dist/admin/layout-editor/overlay-shell.d.ts +19 -1
  35. package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
  36. package/dist/admin/layout-editor/overlay-shell.js +177 -69
  37. package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
  38. package/dist/admin/layout-editor/props-panel.d.ts +9 -2
  39. package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
  40. package/dist/admin/layout-editor/props-panel.js +13 -5
  41. package/dist/admin/layout-editor/props-panel.js.map +1 -1
  42. package/dist/admin/layout-editor/region-overlay.d.ts +8 -1
  43. package/dist/admin/layout-editor/region-overlay.d.ts.map +1 -1
  44. package/dist/admin/layout-editor/region-overlay.js +4 -1
  45. package/dist/admin/layout-editor/region-overlay.js.map +1 -1
  46. package/dist/admin/layout-editor/section-insert-line.d.ts +7 -1
  47. package/dist/admin/layout-editor/section-insert-line.d.ts.map +1 -1
  48. package/dist/admin/layout-editor/section-insert-line.js +4 -1
  49. package/dist/admin/layout-editor/section-insert-line.js.map +1 -1
  50. package/dist/admin/layout-editor/section-overlay.d.ts +13 -1
  51. package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
  52. package/dist/admin/layout-editor/section-overlay.js +24 -12
  53. package/dist/admin/layout-editor/section-overlay.js.map +1 -1
  54. package/dist/admin/layout-editor/section-variant-container-config.d.ts +29 -0
  55. package/dist/admin/layout-editor/section-variant-container-config.d.ts.map +1 -0
  56. package/dist/admin/layout-editor/section-variant-container-config.js +82 -0
  57. package/dist/admin/layout-editor/section-variant-container-config.js.map +1 -0
  58. package/dist/admin/styles/admin.css +5 -2
  59. package/dist/admin/surfaces/content/content-edit.d.ts.map +1 -1
  60. package/dist/admin/surfaces/content/content-edit.js +8 -1
  61. package/dist/admin/surfaces/content/content-edit.js.map +1 -1
  62. package/dist/db/content-queries.d.ts +35 -0
  63. package/dist/db/content-queries.d.ts.map +1 -1
  64. package/dist/db/content-queries.js +175 -36
  65. package/dist/db/content-queries.js.map +1 -1
  66. package/dist/layout/registry.d.ts +8 -1
  67. package/dist/layout/registry.d.ts.map +1 -1
  68. package/dist/layout/registry.js +8 -1
  69. package/dist/layout/registry.js.map +1 -1
  70. package/dist/nav/index.d.ts +1 -1
  71. package/dist/nav/index.d.ts.map +1 -1
  72. package/dist/nav/index.js +4 -1
  73. package/dist/nav/index.js.map +1 -1
  74. package/dist/nav/url-resolver.d.ts +27 -0
  75. package/dist/nav/url-resolver.d.ts.map +1 -1
  76. package/dist/nav/url-resolver.js +36 -1
  77. package/dist/nav/url-resolver.js.map +1 -1
  78. package/dist/scope/edit-mode-hooks.d.ts.map +1 -1
  79. package/dist/scope/edit-mode-hooks.js +27 -1
  80. package/dist/scope/edit-mode-hooks.js.map +1 -1
  81. package/dist/scope/inline-editing.d.ts.map +1 -1
  82. package/dist/scope/inline-editing.js +2 -1
  83. package/dist/scope/inline-editing.js.map +1 -1
  84. package/dist/site/styles.css +147 -4
  85. package/package.json +1 -1
@@ -118,8 +118,12 @@
118
118
  overflow: hidden;
119
119
  }
120
120
 
121
+ /* Scoped to the CHROME, never the shell: in the front-of-site editor the shell
122
+ wraps the real page, and a blanket `box-sizing: border-box` there would
123
+ silently re-lay-out the site the moment editing started (kywi-cms#94). */
121
124
  .kywi-editor-root *,
122
- .kywi-overlay-shell * {
125
+ .kywi-overlay-chrome,
126
+ .kywi-overlay-chrome * {
123
127
  box-sizing: border-box;
124
128
  }
125
129
 
@@ -127,8 +131,7 @@
127
131
  .kywi-editor-palette-list,
128
132
  .kywi-editor-props-panel-scroll,
129
133
  .kywi-editor-props-body,
130
- .kywi-overlay-left-panel,
131
- .kywi-overlay-right-panel {
134
+ .kywi-overlay-rail {
132
135
  scrollbar-width: thin;
133
136
  scrollbar-color: var(--kywi-editor-border-strong) transparent;
134
137
  }
@@ -241,11 +244,11 @@
241
244
  background: var(--kywi-editor-panel-raised);
242
245
  }
243
246
 
244
- :where(.kywi-editor-root, .kywi-overlay-shell) button:focus-visible,
245
- :where(.kywi-editor-root, .kywi-overlay-shell) input:focus-visible,
246
- :where(.kywi-editor-root, .kywi-overlay-shell) select:focus-visible,
247
- :where(.kywi-editor-root, .kywi-overlay-shell) textarea:focus-visible,
248
- :where(.kywi-editor-root, .kywi-overlay-shell) [tabindex]:focus-visible {
247
+ :where(.kywi-editor-root, .kywi-overlay-chrome) button:focus-visible,
248
+ :where(.kywi-editor-root, .kywi-overlay-chrome) input:focus-visible,
249
+ :where(.kywi-editor-root, .kywi-overlay-chrome) select:focus-visible,
250
+ :where(.kywi-editor-root, .kywi-overlay-chrome) textarea:focus-visible,
251
+ :where(.kywi-editor-root, .kywi-overlay-chrome) [tabindex]:focus-visible {
249
252
  outline: none;
250
253
  box-shadow: var(--kywi-editor-focus);
251
254
  }
@@ -671,6 +674,133 @@
671
674
  }
672
675
  .kywi-editor-section-toolbar-btn svg { flex-shrink: 0; }
673
676
 
677
+ /* ── Section-level variant container (kywi-cms#92) ─────────────────────────── */
678
+ /* A personalized / A-B'd band renders as its own block in document order, with
679
+ an arm switcher; the open arm's sections use the ordinary section chrome. */
680
+ .kywi-editor-vc {
681
+ position: relative;
682
+ border: 1px solid var(--kywi-editor-region-border);
683
+ border-radius: var(--kywi-editor-radius-lg);
684
+ margin-bottom: var(--kywi-editor-sp-3);
685
+ background: var(--kywi-editor-accent-light);
686
+ box-shadow: var(--kywi-editor-card-shadow);
687
+ transition: border-color var(--kywi-editor-ease), box-shadow var(--kywi-editor-ease);
688
+ }
689
+ .kywi-editor-vc--selected {
690
+ border-color: var(--kywi-editor-selected-border);
691
+ box-shadow: 0 0 0 2px var(--kywi-editor-accent-ring);
692
+ }
693
+
694
+ .kywi-editor-vc-toolbar {
695
+ display: flex;
696
+ align-items: center;
697
+ gap: var(--kywi-editor-sp-2);
698
+ min-height: 34px;
699
+ padding: 0 var(--kywi-editor-sp-2) 0 var(--kywi-editor-sp-3);
700
+ border-bottom: 1px solid var(--kywi-editor-region-border);
701
+ border-radius: var(--kywi-editor-radius-lg) var(--kywi-editor-radius-lg) 0 0;
702
+ cursor: pointer;
703
+ }
704
+ .kywi-editor-vc-badge {
705
+ display: inline-flex;
706
+ align-items: center;
707
+ height: 18px;
708
+ padding: 0 8px;
709
+ border-radius: var(--kywi-editor-radius-pill);
710
+ background: var(--kywi-editor-accent);
711
+ color: var(--kywi-editor-on-accent);
712
+ font-size: 9.5px;
713
+ font-weight: 700;
714
+ letter-spacing: 0.08em;
715
+ text-transform: uppercase;
716
+ white-space: nowrap;
717
+ }
718
+ .kywi-editor-vc-title {
719
+ font-size: 10.5px;
720
+ font-weight: 700;
721
+ letter-spacing: 0.08em;
722
+ text-transform: uppercase;
723
+ color: var(--kywi-editor-card-ink);
724
+ user-select: none;
725
+ }
726
+ .kywi-editor-vc-arm-count {
727
+ font-size: 11px;
728
+ color: var(--kywi-editor-card-muted);
729
+ user-select: none;
730
+ }
731
+ .kywi-editor-vc-toolbar-actions {
732
+ margin-left: auto;
733
+ display: inline-flex;
734
+ align-items: center;
735
+ gap: 4px;
736
+ }
737
+
738
+ .kywi-editor-vc-arms {
739
+ display: flex;
740
+ flex-wrap: wrap;
741
+ gap: 4px;
742
+ padding: var(--kywi-editor-sp-2) var(--kywi-editor-sp-3) 0;
743
+ }
744
+ .kywi-editor-vc-arm-btn {
745
+ display: inline-flex;
746
+ align-items: center;
747
+ gap: 6px;
748
+ height: 26px;
749
+ padding: 0 10px;
750
+ border-radius: var(--kywi-editor-radius-pill);
751
+ border: 1px solid var(--kywi-editor-card-border);
752
+ background: var(--kywi-editor-surface);
753
+ color: var(--kywi-editor-card-ink);
754
+ font-size: 11.5px;
755
+ font-weight: 600;
756
+ font-family: var(--kywi-editor-font);
757
+ cursor: pointer;
758
+ transition: background var(--kywi-editor-ease), color var(--kywi-editor-ease), border-color var(--kywi-editor-ease);
759
+ }
760
+ .kywi-editor-vc-arm-btn:hover {
761
+ border-color: var(--kywi-editor-region-border);
762
+ color: var(--kywi-editor-accent-press);
763
+ }
764
+ .kywi-editor-vc-arm-btn--active {
765
+ background: var(--kywi-editor-accent);
766
+ border-color: var(--kywi-editor-accent);
767
+ color: var(--kywi-editor-on-accent);
768
+ }
769
+ .kywi-editor-vc-arm-weight {
770
+ font-size: 10px;
771
+ font-weight: 700;
772
+ opacity: 0.75;
773
+ }
774
+
775
+ .kywi-editor-vc-arm-body {
776
+ padding: var(--kywi-editor-sp-2) var(--kywi-editor-sp-3) var(--kywi-editor-sp-3);
777
+ }
778
+ .kywi-editor-vc-arm-empty {
779
+ margin: 0 0 var(--kywi-editor-sp-2);
780
+ padding: var(--kywi-editor-sp-3);
781
+ border: 1.5px dashed var(--kywi-editor-region-border);
782
+ border-radius: var(--kywi-editor-radius);
783
+ font-size: 12px;
784
+ color: var(--kywi-editor-card-muted);
785
+ text-align: center;
786
+ }
787
+ .kywi-editor-vc-add-section {
788
+ display: inline-flex;
789
+ align-items: center;
790
+ height: 26px;
791
+ padding: 0 10px;
792
+ border-radius: var(--kywi-editor-radius-sm);
793
+ border: 1px dashed var(--kywi-editor-region-border);
794
+ background: transparent;
795
+ color: var(--kywi-editor-accent-press);
796
+ font-size: 11.5px;
797
+ font-weight: 600;
798
+ font-family: var(--kywi-editor-font);
799
+ cursor: pointer;
800
+ transition: background var(--kywi-editor-ease);
801
+ }
802
+ .kywi-editor-vc-add-section:hover { background: var(--kywi-editor-accent-light); }
803
+
674
804
  /* ── Section insert line ───────────────────────────────────────────────────── */
675
805
  .kywi-editor-section-insert {
676
806
  position: relative;
@@ -1489,6 +1619,14 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1489
1619
  font-size: 13px;
1490
1620
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
1491
1621
  }
1622
+ .kywi-edit-toolbar-mark {
1623
+ font-weight: 700;
1624
+ letter-spacing: 0.01em;
1625
+ color: var(--kywi-editor-text-strong);
1626
+ padding-right: 4px;
1627
+ border-right: 1px solid var(--kywi-editor-border-strong);
1628
+ margin-right: 2px;
1629
+ }
1492
1630
  .kywi-edit-toolbar-status {
1493
1631
  display: inline-flex;
1494
1632
  align-items: center;
@@ -1504,6 +1642,12 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1504
1642
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
1505
1643
  display: inline-block;
1506
1644
  }
1645
+ /* Draft (and any non-published status) reads as amber, not the "all good"
1646
+ published green — the dot is the at-a-glance signal before the label loads. */
1647
+ .kywi-edit-toolbar-status-dot--draft {
1648
+ background: var(--kywi-editor-warning);
1649
+ box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2);
1650
+ }
1507
1651
  .kywi-edit-toolbar-title { font-weight: 650; color: var(--kywi-editor-text-strong); }
1508
1652
  .kywi-edit-toolbar-spacer { flex: 1; }
1509
1653
  .kywi-edit-toolbar-edit,
@@ -1533,31 +1677,85 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1533
1677
  .kywi-edit-toolbar-link:hover { filter: brightness(1.08); border-color: var(--kywi-editor-accent); }
1534
1678
  .kywi-frontend-edit--browsing { padding-top: 44px; }
1535
1679
 
1536
- /* ── Overlay shell ─────────────────────────────────────────────────────────── */
1537
- .kywi-overlay-shell {
1538
- position: fixed;
1539
- inset: 0;
1540
- z-index: 1000;
1541
- display: flex;
1542
- flex-direction: column;
1543
- background: var(--kywi-editor-bg);
1680
+ /* ============================================================================
1681
+ In-place front-of-site editor (kywi-cms#94).
1682
+
1683
+ The editor is NOT a modal over the page. `.kywi-overlay-shell` contributes no
1684
+ box at all (`display: contents`), so the page below keeps its own document
1685
+ flow, real width and own stylesheet — the canvas emits the public renderer's
1686
+ markup (`.kywi-region` / `.kywi-section` / `.kywi-column` /
1687
+ `.kywi-module-wrapper`) and the site styles it exactly as it does when nobody
1688
+ is editing. Everything the editor adds is either a fixed rail docked to a
1689
+ viewport edge (`.kywi-overlay-chrome`) or an absolutely-positioned affordance
1690
+ that paints over the page without displacing it (`.kywi-ip-*`).
1691
+ ========================================================================== */
1692
+
1693
+ :root {
1694
+ --kywi-overlay-bar-height: 46px;
1695
+ --kywi-overlay-rail-left: var(--kywi-editor-panel-width-left);
1696
+ --kywi-overlay-rail-right: var(--kywi-editor-panel-width-right);
1697
+ }
1698
+
1699
+ /* The fixed edit bar would otherwise cover the site's own header — offset the
1700
+ document for exactly as long as the editor is mounted, the same way the
1701
+ browse-mode toolbar does it (kywi-cms#93). */
1702
+ .kywi-frontend-edit--editing { padding-top: var(--kywi-overlay-bar-height); }
1703
+
1704
+ .kywi-overlay-shell { display: contents; }
1705
+
1706
+ /* Admin-shell guard. admin.css used to group `.kywi-overlay-shell` with its
1707
+ pickers (`position: fixed; inset: 0` + scrim + blur) — that grouping is what
1708
+ made this editor modal. It is removed there; this re-asserts the in-place
1709
+ behaviour at higher specificity so a stale admin.css can never box the page
1710
+ again, and so a host that still wraps the editor in `.kywi-admin-shell`
1711
+ (which is a full-viewport flex surface) gets no surprise chrome. */
1712
+ .kywi-admin-shell .kywi-overlay-shell {
1713
+ position: static;
1714
+ inset: auto;
1715
+ display: contents;
1716
+ padding: 0;
1717
+ background: none;
1718
+ backdrop-filter: none;
1719
+ -webkit-backdrop-filter: none;
1720
+ animation: none;
1721
+ }
1722
+
1723
+ /* Modals still want the admin design system's selectors to match, but not its
1724
+ full-viewport box — hosting them under a `display: contents` shell gives the
1725
+ styling without the surface. */
1726
+ .kywi-admin-shell.kywi-overlay-dialog-host { display: contents; }
1727
+
1728
+ /* ── Chrome baseline ────────────────────────────────────────────────────────
1729
+ Typography and box-sizing live on the CHROME, never on the shell: anything
1730
+ set on the shell would inherit straight into the page and change how the
1731
+ site renders. */
1732
+ .kywi-overlay-chrome {
1544
1733
  font-family: var(--kywi-editor-font);
1545
1734
  font-size: 13px;
1735
+ line-height: 1.45;
1546
1736
  color: var(--kywi-editor-panel-text);
1547
1737
  -webkit-font-smoothing: antialiased;
1548
1738
  }
1739
+
1740
+ /* ── Edit bar (fixed, top) ─────────────────────────────────────────────────── */
1549
1741
  .kywi-overlay-toolbar {
1550
- height: var(--kywi-editor-toolbar-height);
1551
- flex-shrink: 0;
1742
+ position: fixed;
1743
+ top: 0;
1744
+ left: 0;
1745
+ right: 0;
1746
+ height: var(--kywi-overlay-bar-height);
1747
+ z-index: var(--kywi-z-overlay, 1000);
1552
1748
  display: flex;
1553
1749
  align-items: center;
1554
1750
  gap: var(--kywi-editor-sp-2);
1555
1751
  padding: 0 var(--kywi-editor-sp-3);
1556
1752
  background: linear-gradient(180deg, #191922, var(--kywi-editor-panel-chrome));
1557
1753
  border-bottom: 1px solid var(--kywi-editor-border);
1558
- color: var(--kywi-editor-panel-text);
1754
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
1559
1755
  font-size: 12.5px;
1756
+ overflow-x: auto;
1560
1757
  }
1758
+ .kywi-overlay-toolbar > * { flex-shrink: 0; }
1561
1759
  .kywi-overlay-page-title {
1562
1760
  display: inline-flex;
1563
1761
  align-items: center;
@@ -1578,7 +1776,26 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1578
1776
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
1579
1777
  flex-shrink: 0;
1580
1778
  }
1581
- .kywi-overlay-toolbar-spacer { flex: 1; }
1779
+ .kywi-overlay-toolbar-spacer { flex: 1 1 auto; min-width: var(--kywi-editor-sp-3); }
1780
+
1781
+ /* The way out. Deliberately the last control and visually distinct — the #94
1782
+ complaint was that there was no obvious way to leave. */
1783
+ .kywi-overlay-toolbar-done {
1784
+ border-color: var(--kywi-editor-panel-faint);
1785
+ color: var(--kywi-editor-text-strong);
1786
+ font-weight: 650;
1787
+ }
1788
+ .kywi-overlay-toolbar-done:hover {
1789
+ background: var(--kywi-editor-accent);
1790
+ border-color: transparent;
1791
+ color: #fff;
1792
+ }
1793
+
1794
+ .kywi-editor-iconbtn--on {
1795
+ border-color: var(--kywi-editor-accent);
1796
+ color: var(--kywi-editor-text-strong);
1797
+ background: var(--kywi-editor-accent-light);
1798
+ }
1582
1799
 
1583
1800
  .kywi-overlay-preview-toggle {
1584
1801
  display: inline-flex;
@@ -1604,36 +1821,262 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1604
1821
  .kywi-overlay-preview-btn:hover { color: var(--kywi-editor-text-strong); }
1605
1822
  .kywi-overlay-preview-btn--active { background: var(--kywi-editor-accent); color: #fff; }
1606
1823
 
1607
- .kywi-overlay-body {
1608
- flex: 1;
1824
+ /* ── Rails (fixed, over the viewport margins) ───────────────────────────────
1825
+ Rails float ABOVE the page; they never reserve layout width, so the page
1826
+ underneath keeps the width it really has. */
1827
+ .kywi-overlay-rail {
1828
+ position: fixed;
1829
+ top: var(--kywi-overlay-bar-height);
1830
+ bottom: 0;
1831
+ z-index: calc(var(--kywi-z-overlay, 1000) - 1);
1609
1832
  display: flex;
1610
- min-height: 0;
1833
+ flex-direction: column;
1611
1834
  overflow: hidden;
1612
- }
1613
- .kywi-overlay-left-panel {
1614
- width: var(--kywi-editor-panel-width-left);
1615
- flex-shrink: 0;
1616
- overflow-y: auto;
1617
1835
  background: var(--kywi-editor-panel-bg);
1618
- border-right: 1px solid var(--kywi-editor-border);
1836
+ box-shadow: var(--kywi-editor-shadow-2);
1837
+ transition: width var(--kywi-editor-ease), transform var(--kywi-editor-ease),
1838
+ opacity var(--kywi-editor-ease);
1619
1839
  }
1620
- .kywi-overlay-left-panel .kywi-editor-palette { width: 100%; border-right: none; }
1621
- .kywi-overlay-canvas {
1622
- flex: 1;
1623
- min-width: 0;
1624
- overflow-y: auto;
1625
- margin: 0 auto;
1626
- background: var(--kywi-editor-bg);
1840
+ .kywi-overlay-rail--left {
1841
+ left: 0;
1842
+ width: var(--kywi-overlay-rail-left);
1843
+ border-right: 1px solid var(--kywi-editor-border);
1627
1844
  }
1628
- .kywi-overlay-canvas .kywi-editor-canvas-content { height: 100%; }
1629
- .kywi-overlay-right-panel {
1630
- width: var(--kywi-editor-panel-width-right);
1631
- flex-shrink: 0;
1632
- overflow-y: auto;
1633
- background: var(--kywi-editor-panel-bg);
1845
+ .kywi-overlay-rail--right {
1846
+ right: 0;
1847
+ width: var(--kywi-overlay-rail-right);
1634
1848
  border-left: 1px solid var(--kywi-editor-border);
1635
1849
  }
1636
- .kywi-overlay-right-panel .kywi-editor-props-panel { width: 100%; border-left: none; height: 100%; }
1850
+ /* Same width-collapse precedent as `.kywi-editor-props-panel--hidden`.
1851
+ `visibility: hidden` (not just opacity) also takes the rail's controls out of
1852
+ the tab order, so a collapsed rail can't be reached by keyboard. */
1853
+ .kywi-overlay-rail--collapsed {
1854
+ width: 0;
1855
+ opacity: 0;
1856
+ visibility: hidden;
1857
+ pointer-events: none;
1858
+ }
1859
+ .kywi-overlay-rail--left.kywi-overlay-rail--collapsed { transform: translateX(-100%); }
1860
+ .kywi-overlay-rail--right.kywi-overlay-rail--collapsed { transform: translateX(100%); }
1861
+ .kywi-overlay-rail > * { flex: 1; min-height: 0; }
1862
+ .kywi-overlay-rail .kywi-editor-palette { width: 100%; border-right: none; }
1863
+ .kywi-overlay-rail .kywi-editor-props-panel { width: 100%; border-left: none; height: 100%; }
1864
+ /* The rail owns the collapse; the panel inside must not also zero its width. */
1865
+ .kywi-overlay-rail .kywi-editor-props-panel--hidden { width: 100%; }
1866
+
1867
+ /* ── The page ──────────────────────────────────────────────────────────────── */
1868
+ .kywi-ip-page { position: relative; }
1869
+ /* Tablet / mobile preview constrains the page — it stays THE PAGE, centred, not
1870
+ a letterbox floating in editor chrome. */
1871
+ .kywi-ip-page--constrained {
1872
+ margin-left: auto;
1873
+ margin-right: auto;
1874
+ box-shadow: 0 0 0 1px var(--kywi-editor-border-strong);
1875
+ }
1876
+ .kywi-ip-canvas { position: relative; }
1877
+
1878
+ /* ── Selection + hover affordances ─────────────────────────────────────────
1879
+ `outline` (never `border`) so an affordance can never move the page by a
1880
+ pixel. Section → container → module reads as progressively stronger. */
1881
+ .kywi-ip-section,
1882
+ .kywi-ip-vc,
1883
+ .kywi-ip-module { position: relative; }
1884
+
1885
+ .kywi-ip-section:hover {
1886
+ outline: 1px dashed var(--kywi-editor-region-border);
1887
+ outline-offset: -1px;
1888
+ }
1889
+ .kywi-ip-section--selected,
1890
+ .kywi-ip-section--selected:hover {
1891
+ outline: 2px solid var(--kywi-editor-accent);
1892
+ outline-offset: -2px;
1893
+ }
1894
+
1895
+ .kywi-ip-vc:hover {
1896
+ outline: 1px dashed var(--kywi-editor-accent);
1897
+ outline-offset: 2px;
1898
+ }
1899
+ .kywi-ip-vc--selected,
1900
+ .kywi-ip-vc--selected:hover {
1901
+ outline: 2px solid var(--kywi-editor-accent);
1902
+ outline-offset: 2px;
1903
+ }
1904
+
1905
+ .kywi-ip-module:hover {
1906
+ outline: 1.5px solid var(--kywi-editor-region-border);
1907
+ outline-offset: 2px;
1908
+ }
1909
+ .kywi-ip-module--selected,
1910
+ .kywi-ip-module--selected:hover {
1911
+ outline: 2px solid var(--kywi-editor-accent);
1912
+ outline-offset: 2px;
1913
+ }
1914
+ .kywi-ip-module--editing,
1915
+ .kywi-ip-module--editing:hover,
1916
+ .kywi-ip-module--editing:focus {
1917
+ outline: 2px solid var(--kywi-editor-accent);
1918
+ outline-offset: 3px;
1919
+ cursor: text;
1920
+ box-shadow: 0 0 0 6px var(--kywi-editor-accent-ring);
1921
+ }
1922
+
1923
+ /* ── Floating chrome (chips, section/container bars, handles) ───────────────
1924
+ Invisible AND click-through until its owner is hovered or selected, so the
1925
+ page never collects stray editor hit-targets. */
1926
+ .kywi-ip-chrome,
1927
+ .kywi-ip-chip {
1928
+ position: absolute;
1929
+ z-index: 6;
1930
+ font-family: var(--kywi-editor-font);
1931
+ opacity: 0;
1932
+ pointer-events: none;
1933
+ transition: opacity var(--kywi-editor-ease);
1934
+ }
1935
+ .kywi-ip-section:hover > .kywi-ip-section-chrome,
1936
+ .kywi-ip-section--selected > .kywi-ip-section-chrome,
1937
+ .kywi-ip-vc:hover > .kywi-ip-vc-chrome,
1938
+ .kywi-ip-vc--selected > .kywi-ip-vc-chrome,
1939
+ .kywi-ip-module:hover > .kywi-ip-chip,
1940
+ .kywi-ip-module--selected > .kywi-ip-chip,
1941
+ .kywi-ip-module:hover > .kywi-ip-module-actions,
1942
+ .kywi-ip-module--selected > .kywi-ip-module-actions {
1943
+ opacity: 1;
1944
+ pointer-events: auto;
1945
+ }
1946
+
1947
+ .kywi-ip-section-chrome,
1948
+ .kywi-ip-vc-chrome {
1949
+ top: 0;
1950
+ left: 0;
1951
+ transform: translateY(-100%);
1952
+ display: flex;
1953
+ align-items: center;
1954
+ gap: 6px;
1955
+ min-height: 26px;
1956
+ padding: 3px 8px;
1957
+ border-radius: var(--kywi-editor-radius-sm) var(--kywi-editor-radius-sm) 0 0;
1958
+ background: var(--kywi-editor-panel-chrome);
1959
+ color: var(--kywi-editor-panel-text);
1960
+ font-size: 11px;
1961
+ line-height: 1.3;
1962
+ white-space: nowrap;
1963
+ box-shadow: var(--kywi-editor-shadow-1);
1964
+ }
1965
+ .kywi-ip-vc-chrome { flex-wrap: wrap; max-width: 100%; white-space: normal; }
1966
+ /* Chrome controls are always live once the bar itself is visible. */
1967
+ .kywi-ip-chrome .kywi-editor-section-toolbar-actions { opacity: 1; }
1968
+ .kywi-ip-chrome .kywi-editor-section-title,
1969
+ .kywi-ip-chrome .kywi-editor-vc-title,
1970
+ .kywi-ip-chrome .kywi-editor-vc-arm-count { color: var(--kywi-editor-panel-text); }
1971
+ .kywi-ip-chrome .kywi-editor-section-handle { color: var(--kywi-editor-panel-muted); }
1972
+ /* The shared toolbar button is inked for the light artboard card; on the dark
1973
+ floating bar it has to be re-inked or it reads as dark-on-dark. */
1974
+ .kywi-ip-chrome .kywi-editor-section-toolbar-btn { color: var(--kywi-editor-panel-text); }
1975
+ .kywi-ip-chrome .kywi-editor-section-toolbar-btn:hover {
1976
+ background: var(--kywi-editor-accent);
1977
+ border-color: transparent;
1978
+ color: #fff;
1979
+ }
1980
+ /* …but delete stays destructive-red on hover, as it is in the artboard. */
1981
+ .kywi-ip-chrome .kywi-editor-section-toolbar-btn:last-child:hover {
1982
+ background: var(--kywi-editor-danger-strong);
1983
+ border-color: transparent;
1984
+ color: #fff;
1985
+ }
1986
+ .kywi-ip-chrome .kywi-editor-vc-arms { border: none; padding: 0; margin: 0; background: none; }
1987
+
1988
+ .kywi-ip-chip {
1989
+ top: 0;
1990
+ left: 0;
1991
+ transform: translateY(-100%);
1992
+ display: inline-flex;
1993
+ align-items: center;
1994
+ height: 18px;
1995
+ padding: 0 8px;
1996
+ border-radius: var(--kywi-editor-radius-pill);
1997
+ background: var(--kywi-editor-accent);
1998
+ color: #fff;
1999
+ font-size: 9.5px;
2000
+ font-weight: 700;
2001
+ letter-spacing: 0.05em;
2002
+ text-transform: capitalize;
2003
+ box-shadow: var(--kywi-editor-shadow-1);
2004
+ }
2005
+
2006
+ .kywi-ip-module-actions {
2007
+ top: 0;
2008
+ right: 0;
2009
+ display: flex;
2010
+ gap: 4px;
2011
+ padding: 3px;
2012
+ border-radius: var(--kywi-editor-radius-sm);
2013
+ background: rgba(20, 20, 29, 0.92);
2014
+ box-shadow: var(--kywi-editor-shadow-1);
2015
+ }
2016
+
2017
+ /* ── Empty column + section insert ─────────────────────────────────────────── */
2018
+ .kywi-ip-empty-column {
2019
+ display: flex;
2020
+ align-items: center;
2021
+ justify-content: center;
2022
+ min-height: 56px;
2023
+ border: 1.5px dashed var(--kywi-editor-region-border);
2024
+ border-radius: var(--kywi-editor-radius);
2025
+ color: var(--kywi-editor-accent);
2026
+ font-family: var(--kywi-editor-font);
2027
+ font-size: 11.5px;
2028
+ font-weight: 500;
2029
+ }
2030
+
2031
+ /* Zero-height: the insert affordance floats in the gap after the last node so
2032
+ it can never add a band of editor whitespace to the real page. */
2033
+ .kywi-ip-insert { position: relative; height: 0; }
2034
+ .kywi-ip-insert-btn {
2035
+ position: absolute;
2036
+ top: 2px;
2037
+ left: 50%;
2038
+ transform: translateX(-50%);
2039
+ z-index: 7;
2040
+ width: 26px;
2041
+ height: 26px;
2042
+ display: inline-flex;
2043
+ align-items: center;
2044
+ justify-content: center;
2045
+ border-radius: 50%;
2046
+ border: 1px solid var(--kywi-editor-border-strong);
2047
+ background: var(--kywi-editor-panel-chrome);
2048
+ color: #fff;
2049
+ font-family: var(--kywi-editor-font);
2050
+ font-size: 15px;
2051
+ line-height: 1;
2052
+ cursor: pointer;
2053
+ /* Faint rather than invisible: an opacity-0 button would still swallow the
2054
+ clicks under it, which on a real page is worse than a quiet affordance. */
2055
+ opacity: 0;
2056
+ transition: opacity var(--kywi-editor-ease), background var(--kywi-editor-ease);
2057
+ }
2058
+ .kywi-ip-region:hover .kywi-ip-insert-btn { opacity: 0.45; }
2059
+ .kywi-ip-insert-btn:hover,
2060
+ .kywi-ip-insert-btn:focus-visible { opacity: 1; background: var(--kywi-editor-accent); }
2061
+
2062
+ .kywi-ip-add-arm-section {
2063
+ display: block;
2064
+ width: 100%;
2065
+ margin-top: var(--kywi-editor-sp-2);
2066
+ padding: 6px 10px;
2067
+ border: 1px dashed var(--kywi-editor-region-border);
2068
+ border-radius: var(--kywi-editor-radius-sm);
2069
+ background: transparent;
2070
+ color: var(--kywi-editor-accent);
2071
+ font-family: var(--kywi-editor-font);
2072
+ font-size: 11.5px;
2073
+ font-weight: 600;
2074
+ cursor: pointer;
2075
+ opacity: 0;
2076
+ transition: opacity var(--kywi-editor-ease);
2077
+ }
2078
+ .kywi-ip-vc:hover .kywi-ip-add-arm-section,
2079
+ .kywi-ip-vc--selected .kywi-ip-add-arm-section { opacity: 1; }
1637
2080
 
1638
2081
  /* ============================================================================
1639
2082
  Admin-shell override guard.
@@ -1714,5 +2157,5 @@ input[type="color"].kywi-token-raw-input { height: 34px; padding: 3px; cursor: p
1714
2157
  }
1715
2158
 
1716
2159
  @media (prefers-reduced-motion: reduce) {
1717
- .kywi-editor-root *, .kywi-overlay-shell * { transition-duration: 0.01ms !important; }
2160
+ .kywi-editor-root *, .kywi-overlay-chrome * { transition-duration: 0.01ms !important; }
1718
2161
  }
@@ -1,5 +1,5 @@
1
1
  export { LayoutEditor } from './layout-editor.js';
2
- export type { EditorState, EditorAction } from './editor-state.js';
2
+ export type { EditorState, EditorAction, VariantArmPatch, VariantContainerPatch, } from './editor-state.js';
3
3
  export { ModeToggle } from './mode-toggle.js';
4
4
  export { PagePropertiesPanel } from './page-properties.js';
5
5
  export { buildConnectedModuleNode, buildConnectedSectionNode, detachModuleFromComponent, detachSectionFromComponent, resolveSectionComponentDefinition, saveAsComponent, saveAsTemplate, cloneSection, cloneModule, copyToClipboard, pasteFromClipboard, type ComponentDefinitionInput, type SectionTemplateInput, type ClipboardPayload, } from './component-actions.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,iCAAiC,EACjC,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC1F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EACV,WAAW,EACX,YAAY,EACZ,eAAe,EACf,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,iCAAiC,EACjC,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC1F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,iCAAiC,EACjC,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,GAInB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAOjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,iCAAiC,EACjC,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,GAInB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * inline-text.ts
3
+ * Which modules can be text-edited directly on the page, and how to read the
4
+ * edited value back out of the DOM (kywi-cms#94).
5
+ *
6
+ * The front-of-site editor makes a text-bearing module's own rendered element
7
+ * `contenteditable` — the real `<h2>`, `<p>` or rich-text `<div>` the visitor
8
+ * sees, with the site's real styling — instead of swapping it for an input.
9
+ * Committing writes the SAME prop the props panel writes, through the SAME
10
+ * `UPDATE_PROPS` action, so there is one save path and undo/redo just work.
11
+ */
12
+ import type { ModuleConfig } from '../../config/types.js';
13
+ /** The prop a module's inline text edit writes. */
14
+ export interface InlineTextProp {
15
+ /** Prop name on the module node (e.g. `text` for heading, `content` for richText). */
16
+ name: string;
17
+ /** True when the prop holds HTML (rich text) rather than a plain string. */
18
+ html: boolean;
19
+ }
20
+ /**
21
+ * The module's *primary* text prop, or null when it has none.
22
+ *
23
+ * Deliberately only the FIRST declared prop: that is the module's headline
24
+ * content by convention across the registry (heading → `text`, richText →
25
+ * `content`, button → `label`), and it keeps a module like `image` — whose
26
+ * first prop is the image itself and whose `alt` is a text prop further down —
27
+ * out of inline editing, where typing over the picture would be nonsense.
28
+ */
29
+ export declare function resolveInlineTextProp(config: ModuleConfig | null | undefined): InlineTextProp | null;
30
+ /**
31
+ * Read the edited value back out of an editing host.
32
+ *
33
+ * The host is the module's wrapper element, which also carries the editor's own
34
+ * floating chrome (type chip, action cluster). Chrome is marked with
35
+ * `data-kywi-editor-chrome` and skipped, so the value comes from the module's
36
+ * own rendered element — `innerHTML` for a rich-text prop, `textContent` for a
37
+ * plain one.
38
+ *
39
+ * HTML values are put through the same `sanitizeHtml` the renderer applies, so
40
+ * what is stored is exactly what will render — contenteditable cannot smuggle a
41
+ * shape the public page would strip anyway.
42
+ */
43
+ export declare function readInlineTextValue(host: HTMLElement, html: boolean): string;
44
+ //# sourceMappingURL=inline-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-text.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/inline-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,uBAAuB,CAAA;AAG/E,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,IAAI,EAAE,OAAO,CAAA;CACd;AAaD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CASpG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAS5E"}