@bendyline/squisq-editor-react 1.5.3 → 1.6.1

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 (186) hide show
  1. package/README.md +57 -10
  2. package/dist/index.d.ts +1114 -52
  3. package/dist/index.js +18446 -8050
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
  6. package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
  7. package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
  8. package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
  9. package/dist/styles/index.css +13867 -0
  10. package/package.json +15 -7
  11. package/src/BlockCardView.tsx +121 -0
  12. package/src/BlockPreviewPanel.tsx +69 -0
  13. package/src/BlockPropertiesPopover.tsx +191 -0
  14. package/src/EditorContext.tsx +165 -16
  15. package/src/EditorShell.tsx +265 -144
  16. package/src/FolderView.tsx +131 -0
  17. package/src/Icon.tsx +26 -0
  18. package/src/ImageEditor.tsx +69 -22
  19. package/src/OutlinePanel.tsx +64 -7
  20. package/src/PlainHtmlPreview.tsx +30 -3
  21. package/src/PreviewControls.tsx +491 -122
  22. package/src/PreviewPanel.tsx +15 -9
  23. package/src/RawEditor.tsx +179 -17
  24. package/src/RecorderEntry.tsx +9 -16
  25. package/src/TemplateAnnotation.ts +44 -0
  26. package/src/TemplatePicker.tsx +329 -54
  27. package/src/ThemeCustomizerPanel.tsx +30 -336
  28. package/src/ThemePicker.tsx +112 -3
  29. package/src/TimelineBlockPreview.tsx +37 -0
  30. package/src/TimelineTrack.tsx +671 -0
  31. package/src/Toolbar.tsx +544 -180
  32. package/src/Tooltip.tsx +22 -4
  33. package/src/TransitionPicker.tsx +351 -0
  34. package/src/VersionHistoryPanel.tsx +4 -16
  35. package/src/ViewMenuPanel.tsx +17 -14
  36. package/src/WysiwygEditor.tsx +161 -65
  37. package/src/__tests__/blockProperties.test.ts +92 -0
  38. package/src/__tests__/blockRange.test.ts +105 -0
  39. package/src/__tests__/buildPreviewDocTransition.test.ts +73 -0
  40. package/src/__tests__/codeContextSectionView.test.tsx +95 -0
  41. package/src/__tests__/codeContextZoneManager.test.ts +127 -0
  42. package/src/__tests__/createShapeLayer.test.ts +46 -0
  43. package/src/__tests__/diffContextSections.test.ts +39 -0
  44. package/src/__tests__/drawingShapeRoundTrip.test.ts +49 -0
  45. package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
  46. package/src/__tests__/editorShellProps.test.tsx +96 -0
  47. package/src/__tests__/embeddedMedia.test.ts +48 -0
  48. package/src/__tests__/headingTransition.test.ts +138 -0
  49. package/src/__tests__/layoutChildRoundTrip.test.ts +71 -0
  50. package/src/__tests__/plainHtmlPreview.test.tsx +10 -8
  51. package/src/__tests__/previewControls.test.tsx +70 -0
  52. package/src/__tests__/recorderMediaInsert.test.ts +86 -0
  53. package/src/__tests__/templateAnnotationRoundTrip.test.ts +18 -0
  54. package/src/__tests__/templatePickerMetadata.test.ts +32 -0
  55. package/src/__tests__/timelineSource.test.ts +134 -0
  56. package/src/__tests__/tiptapBridge.test.ts +92 -0
  57. package/src/__tests__/tiptapBridgeConformance.test.ts +47 -0
  58. package/src/__tests__/tooltip.test.tsx +72 -0
  59. package/src/__tests__/transitionCatalog.test.ts +64 -0
  60. package/src/__tests__/useBlockNavigator.test.tsx +67 -0
  61. package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
  62. package/src/__tests__/useMediaRecorder.test.ts +41 -0
  63. package/src/__tests__/useTimelineClock.test.ts +21 -0
  64. package/src/blockProperties.ts +88 -0
  65. package/src/blockRange.ts +132 -0
  66. package/src/buildPreviewDoc.ts +98 -9
  67. package/src/codeContext/CodeContextSectionView.tsx +124 -0
  68. package/src/codeContext/CodeContextZoneManager.ts +149 -0
  69. package/src/codeContext/CodeContextZones.tsx +121 -0
  70. package/src/codeContext/diffContextSections.ts +38 -0
  71. package/src/codeContext/types.ts +75 -0
  72. package/src/customTemplates/AddBin.tsx +126 -0
  73. package/src/customTemplates/CustomLayoutManager.tsx +233 -0
  74. package/src/customTemplates/CustomTemplateContext.tsx +182 -0
  75. package/src/customTemplates/LayerToolbar.tsx +580 -0
  76. package/src/customTemplates/ShapeGlyph.tsx +47 -0
  77. package/src/customTemplates/TemplateDesigner.tsx +430 -0
  78. package/src/customTemplates/__tests__/library.test.ts +88 -0
  79. package/src/customTemplates/__tests__/normalizePositions.test.ts +109 -0
  80. package/src/customTemplates/__tests__/shapeDefs.test.ts +49 -0
  81. package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +95 -0
  82. package/src/customTemplates/designer.css +673 -0
  83. package/src/customTemplates/index.ts +31 -0
  84. package/src/customTemplates/library.ts +97 -0
  85. package/src/customTemplates/normalizePositions.ts +75 -0
  86. package/src/customTemplates/shapeDefs.ts +131 -0
  87. package/src/customTemplates/thumbnail.tsx +63 -0
  88. package/src/customTemplates/tokenDefs.ts +60 -0
  89. package/src/customTemplates/useDocCustomTemplates.ts +52 -0
  90. package/src/customTemplates/useMemoryLayerAdapter.ts +123 -0
  91. package/src/customThemes/CustomThemeContext.tsx +179 -0
  92. package/src/customThemes/CustomThemeDialog.tsx +286 -0
  93. package/src/customThemes/__tests__/CustomThemeContext.test.tsx +64 -0
  94. package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +47 -0
  95. package/src/customThemes/__tests__/customThemeLibrary.test.ts +51 -0
  96. package/src/customThemes/customThemeLibrary.ts +97 -0
  97. package/src/customThemes/index.ts +31 -0
  98. package/src/customThemes/themeControls.tsx +229 -0
  99. package/src/customThemes/themeDraft.ts +272 -0
  100. package/src/customThemes/useDocCustomThemes.ts +49 -0
  101. package/src/diagram/DiagramCanvas.tsx +240 -0
  102. package/src/diagram/DiagramExtension.ts +209 -0
  103. package/src/diagram/DiagramMaximizedOverlay.tsx +46 -0
  104. package/src/diagram/DiagramWidget.tsx +270 -0
  105. package/src/diagram/diagramCommands.ts +604 -0
  106. package/src/diagram/diagramConstants.ts +17 -0
  107. package/src/diagram/useDiagramData.ts +126 -0
  108. package/src/embeddedMedia.ts +78 -0
  109. package/src/frontmatter.ts +29 -0
  110. package/src/headingTransition.ts +231 -0
  111. package/src/imageEditor/CanvasSurface.tsx +383 -88
  112. package/src/imageEditor/PropertiesPanel.tsx +47 -1
  113. package/src/imageEditor/Toolbar.tsx +229 -16
  114. package/src/imageEditor/createShapeLayer.ts +280 -0
  115. package/src/imageEditor/icons.tsx +34 -114
  116. package/src/imageEditor/image-editor.css +54 -5
  117. package/src/imageEditor/state.ts +23 -3
  118. package/src/index.ts +109 -1
  119. package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
  120. package/src/recorder/RecorderModal.tsx +120 -53
  121. package/src/recorder/RecorderPanel.tsx +2 -26
  122. package/src/recorder/hooks/useMediaRecorder.ts +17 -11
  123. package/src/recorder/insertMediaBlock.ts +30 -0
  124. package/src/resolveBlockVisual.ts +33 -0
  125. package/src/scene/Scene.tsx +540 -0
  126. package/src/scene/SceneBlockExtension.ts +198 -0
  127. package/src/scene/SceneBlockToolbar.tsx +201 -0
  128. package/src/scene/SceneBlockWidget.tsx +434 -0
  129. package/src/scene/ScenePropsBar.tsx +85 -0
  130. package/src/scene/SceneSelection.tsx +107 -0
  131. package/src/scene/SceneViewport.tsx +102 -0
  132. package/src/scene/ShapePalette.tsx +181 -0
  133. package/src/scene/__tests__/DiagramAdapter.test.ts +56 -0
  134. package/src/scene/__tests__/bezierEdit.test.ts +85 -0
  135. package/src/scene/__tests__/blockLayers.test.ts +57 -0
  136. package/src/scene/__tests__/shapeLayers.test.ts +106 -0
  137. package/src/scene/__tests__/useSceneHitTest.test.ts +90 -0
  138. package/src/scene/__tests__/useScenePanZoom.test.ts +103 -0
  139. package/src/scene/adapters/DiagramAdapter.ts +168 -0
  140. package/src/scene/adapters/DrawingAdapter.ts +415 -0
  141. package/src/scene/adapters/LayoutAdapter.ts +310 -0
  142. package/src/scene/adapters/blockLayers.ts +159 -0
  143. package/src/scene/commands/SceneCommand.ts +70 -0
  144. package/src/scene/commands/drawingCommands.ts +318 -0
  145. package/src/scene/commands/layoutCommands.ts +301 -0
  146. package/src/scene/hooks/useSceneHitTest.ts +105 -0
  147. package/src/scene/hooks/useScenePanZoom.ts +147 -0
  148. package/src/scene/hooks/useSceneSelection.ts +62 -0
  149. package/src/scene/index.ts +95 -0
  150. package/src/scene/layers/DiagramEdges.tsx +127 -0
  151. package/src/scene/layers/edgeGeometry.ts +77 -0
  152. package/src/scene/layers/nodeCard.tsx +145 -0
  153. package/src/scene/layers/renderLayer.tsx +70 -0
  154. package/src/scene/layers/shapeLayers.ts +201 -0
  155. package/src/scene/paths/bezierEdit.ts +208 -0
  156. package/src/scene/scene.css +649 -0
  157. package/src/scene/text/SceneTextOverlay.tsx +161 -0
  158. package/src/scene/text/sceneTextChannel.ts +40 -0
  159. package/src/scene/text/sceneTextConfig.ts +27 -0
  160. package/src/scene/text/sceneTiptap.ts +36 -0
  161. package/src/scene/text/useSceneTextEditing.ts +39 -0
  162. package/src/scene/tools/ConnectTool.ts +111 -0
  163. package/src/scene/tools/DrawingConnectTool.ts +161 -0
  164. package/src/scene/tools/PathTool.ts +158 -0
  165. package/src/scene/tools/PlaceTool.ts +47 -0
  166. package/src/scene/tools/SceneTool.ts +75 -0
  167. package/src/scene/tools/SelectTool.ts +284 -0
  168. package/src/scene/tools/ShapeTool.ts +144 -0
  169. package/src/scene/tools/TextTool.ts +72 -0
  170. package/src/scene/tools/TokenTool.ts +95 -0
  171. package/src/scene/tools/createDrawShapeTool.ts +82 -0
  172. package/src/styles/code-context.css +155 -0
  173. package/src/styles/diagram.css +183 -0
  174. package/src/styles/editor.css +1708 -150
  175. package/src/styles/folder-view.css +210 -0
  176. package/src/styles/image-edit-affordance.css +2 -2
  177. package/src/styles/index.css +5 -0
  178. package/src/timelineSource.ts +244 -0
  179. package/src/tiptapBridge.ts +115 -34
  180. package/src/tooltipPlacement.ts +13 -0
  181. package/src/transitionCatalog.ts +159 -0
  182. package/src/types/monaco-shims.d.ts +10 -0
  183. package/src/useBlockNavigator.ts +153 -0
  184. package/src/useMonacoLoader.ts +23 -1
  185. package/src/useTimelineClock.ts +76 -0
  186. package/src/utils/dropUtils.ts +1 -1
@@ -107,6 +107,11 @@
107
107
  background: rgba(0, 0, 0, 0.07);
108
108
  container-type: inline-size;
109
109
  container-name: squisq-toolbar;
110
+ /* Icon glyphs render a touch lighter than the dark text labels (#374151)
111
+ so the solid Font Awesome icons read less heavy. Hosts can override.
112
+ Only the Free solid family ships these glyphs (no light/thin in Free),
113
+ so we soften the color rather than the stroke weight. */
114
+ --squisq-toolbar-icon: #6b7280;
110
115
  }
111
116
 
112
117
  /* ─── View Tabs (inside toolbar) ─────────────────────── */
@@ -251,6 +256,22 @@
251
256
  color: #1d4ed8;
252
257
  }
253
258
 
259
+ /* Font Awesome glyphs sit a touch larger than the 13px text labels so
260
+ icon buttons read at a comfortable weight against H1–H6 etc. They
261
+ render in a lighter color than the text labels by default. */
262
+ .squisq-toolbar-button > i {
263
+ font-size: 15px;
264
+ line-height: 1;
265
+ color: var(--squisq-toolbar-icon);
266
+ }
267
+
268
+ /* On hover / active, glyphs follow the button's color so the existing
269
+ hover and toggled-on feedback still applies. */
270
+ .squisq-toolbar-button:hover > i,
271
+ .squisq-toolbar-button--active > i {
272
+ color: inherit;
273
+ }
274
+
254
275
  /* ─── Toolbar: narrow screen adjustments ───────────────── */
255
276
 
256
277
  @media (max-width: 768px) {
@@ -360,6 +381,47 @@
360
381
  max-width: 100%;
361
382
  }
362
383
 
384
+ /* ─── Insert Menu Dropdown ──────────────────────────── */
385
+
386
+ .squisq-insert-menu {
387
+ z-index: 100;
388
+ min-width: 200px;
389
+ padding: 4px 0;
390
+ background: #fff;
391
+ border: 1px solid #e5e7eb;
392
+ border-radius: 6px;
393
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
394
+ }
395
+
396
+ .squisq-insert-menu-header {
397
+ padding: 6px 12px 4px;
398
+ font-size: 11px;
399
+ font-weight: 600;
400
+ letter-spacing: 0.04em;
401
+ text-transform: uppercase;
402
+ color: #9ca3af;
403
+ user-select: none;
404
+ }
405
+
406
+ /* Portaled outside squisq-editor-shell, so dark mode is applied via data-theme. */
407
+ .squisq-insert-menu[data-theme='dark'] {
408
+ background: #1f2937;
409
+ border-color: #374151;
410
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
411
+ }
412
+
413
+ .squisq-insert-menu[data-theme='dark'] .squisq-toolbar-overflow-item {
414
+ color: #d1d5db;
415
+ }
416
+
417
+ .squisq-insert-menu[data-theme='dark'] .squisq-toolbar-overflow-item:hover {
418
+ background: #374151;
419
+ }
420
+
421
+ .squisq-insert-menu[data-theme='dark'] .squisq-insert-menu-header {
422
+ color: #6b7280;
423
+ }
424
+
363
425
  /* ─── Template Picker (toolbar) ──────────────────────── */
364
426
 
365
427
  .squisq-template-picker {
@@ -480,7 +542,11 @@
480
542
  .squisq-template-gallery {
481
543
  width: 420px;
482
544
  max-width: calc(100vw - 24px);
483
- max-height: 480px;
545
+ /* Cap to the viewport (less a top+bottom margin) rather than a fixed
546
+ pixel height, so the panel never grows past the bottom of the page.
547
+ Its own overflow-y gives it a self-contained scrollbar instead of
548
+ stretching alongside the page scrollbar. */
549
+ max-height: calc(100vh - 72px);
484
550
  overflow-y: auto;
485
551
  background: #fff;
486
552
  border: 1px solid #e5e7eb;
@@ -503,6 +569,55 @@
503
569
  }
504
570
  }
505
571
 
572
+ /* Search input at the top of the gallery — sticky so it stays visible while
573
+ the user scrolls a long template list. */
574
+ .squisq-template-gallery-search {
575
+ position: sticky;
576
+ top: 0;
577
+ z-index: 1;
578
+ display: flex;
579
+ align-items: center;
580
+ gap: 6px;
581
+ padding: 4px 8px;
582
+ margin: -4px -4px 8px;
583
+ background: #fff;
584
+ border-bottom: 1px solid #f3f4f6;
585
+ }
586
+
587
+ .squisq-template-gallery-search-icon {
588
+ color: #9ca3af;
589
+ flex-shrink: 0;
590
+ }
591
+
592
+ .squisq-template-gallery-search-input {
593
+ flex: 1;
594
+ min-width: 0;
595
+ padding: 6px 4px;
596
+ border: none;
597
+ outline: none;
598
+ font: inherit;
599
+ font-size: 13px;
600
+ color: #111827;
601
+ background: transparent;
602
+ }
603
+
604
+ .squisq-template-gallery-search-input::placeholder {
605
+ color: #9ca3af;
606
+ }
607
+
608
+ /* Hide the native clear button on type="search" — the popover handles
609
+ clearing implicitly when it closes. */
610
+ .squisq-template-gallery-search-input::-webkit-search-cancel-button {
611
+ appearance: none;
612
+ }
613
+
614
+ .squisq-template-gallery-empty {
615
+ padding: 24px 12px;
616
+ text-align: center;
617
+ font-size: 12px;
618
+ color: #6b7280;
619
+ }
620
+
506
621
  /* "— none —" row at the top */
507
622
  .squisq-template-gallery-none {
508
623
  display: flex;
@@ -550,7 +665,6 @@
550
665
  @media (min-width: 900px) {
551
666
  .squisq-template-gallery {
552
667
  width: 780px;
553
- max-height: 880px;
554
668
  }
555
669
 
556
670
  .squisq-template-gallery-grid {
@@ -620,6 +734,65 @@
620
734
  line-height: 1.4;
621
735
  }
622
736
 
737
+ /* "+ New custom template" card pinned at the top of the gallery */
738
+ .squisq-template-gallery-new {
739
+ display: flex;
740
+ align-items: center;
741
+ gap: 10px;
742
+ width: 100%;
743
+ padding: 8px 12px;
744
+ margin-bottom: 6px;
745
+ border: 1px dashed #a5b4fc;
746
+ border-radius: 7px;
747
+ background: #f5f3ff;
748
+ cursor: pointer;
749
+ text-align: left;
750
+ transition:
751
+ background 0.1s,
752
+ border-color 0.1s;
753
+ }
754
+
755
+ .squisq-template-gallery-new:hover {
756
+ background: #ede9fe;
757
+ border-color: #818cf8;
758
+ border-style: solid;
759
+ }
760
+
761
+ .squisq-template-gallery-new-plus {
762
+ display: inline-flex;
763
+ align-items: center;
764
+ justify-content: center;
765
+ width: 28px;
766
+ height: 28px;
767
+ flex-shrink: 0;
768
+ font-size: 20px;
769
+ font-weight: 600;
770
+ line-height: 1;
771
+ color: #6366f1;
772
+ background: #ffffff;
773
+ border: 1px solid #c4b5fd;
774
+ border-radius: 6px;
775
+ }
776
+
777
+ .squisq-template-gallery-new-body {
778
+ display: flex;
779
+ flex-direction: column;
780
+ gap: 2px;
781
+ min-width: 0;
782
+ }
783
+
784
+ .squisq-template-gallery-new-label {
785
+ font-size: 12px;
786
+ font-weight: 600;
787
+ color: #4338ca;
788
+ }
789
+
790
+ .squisq-template-gallery-new-desc {
791
+ font-size: 11px;
792
+ color: #6b7280;
793
+ line-height: 1.4;
794
+ }
795
+
623
796
  /* Recommended / All templates segmentation */
624
797
  .squisq-template-gallery-section + .squisq-template-gallery-section {
625
798
  margin-top: 14px;
@@ -649,7 +822,8 @@
649
822
  /* ─── Tooltip (portal) ────────────────────────────────── */
650
823
 
651
824
  .squisq-tooltip {
652
- transform: translateX(-50%);
825
+ width: max-content;
826
+ max-width: calc(100vw - 16px);
653
827
  padding: 4px 8px;
654
828
  font-size: 12px;
655
829
  font-weight: 500;
@@ -657,7 +831,9 @@
657
831
  background: #1f2937;
658
832
  border-radius: 4px;
659
833
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
660
- white-space: nowrap;
834
+ line-height: 1.3;
835
+ white-space: normal;
836
+ overflow-wrap: anywhere;
661
837
  pointer-events: none;
662
838
  z-index: 2000;
663
839
  animation: squisq-tooltip-fade 0.1s ease-out;
@@ -746,10 +922,171 @@
746
922
  /* Hide block-template chips when the View menu toggles them off. The
747
923
  * underlying `data-template` heading attribute is untouched — only the
748
924
  * inline visual affordance disappears. */
749
- .squisq-wysiwyg-container[data-block-tags='hidden'] .squisq-template-badge {
925
+ .squisq-wysiwyg-container[data-block-tags='hidden'] .squisq-template-badge,
926
+ .squisq-wysiwyg-container[data-block-tags='hidden'] .squisq-props-badge {
750
927
  display: none;
751
928
  }
752
929
 
930
+ /* ─── Block-properties badge (sibling of the template badge) ───────────
931
+ The on-canvas entry point to the block-properties palette: a small
932
+ sliders chip just after the template badge. Quiet by default like the
933
+ empty template badge, brightening on heading hover / direct hover. */
934
+ .squisq-props-badge {
935
+ display: inline-flex;
936
+ align-items: center;
937
+ margin-left: 4px;
938
+ padding: 1px 8px;
939
+ min-height: 19px;
940
+ font-family:
941
+ ui-sans-serif,
942
+ system-ui,
943
+ -apple-system,
944
+ sans-serif;
945
+ font-size: 11px;
946
+ font-weight: 600;
947
+ color: #94a3b8;
948
+ background: transparent;
949
+ border: 1px solid #d1d5db;
950
+ border-radius: 10px;
951
+ vertical-align: middle;
952
+ user-select: none;
953
+ cursor: pointer;
954
+ opacity: 0.5;
955
+ transition:
956
+ background 0.15s ease,
957
+ border-color 0.15s ease,
958
+ opacity 0.15s ease;
959
+ }
960
+
961
+ /* Sliders glyph painted via CSS so the badge span stays empty in the DOM
962
+ (see propsBadgeSpec — a real child could leak into serialized markdown).
963
+ `\f1de` is Font Awesome's "sliders"; the font is already loaded for the
964
+ editor's inline-icon glyph decorations. */
965
+ .squisq-props-badge::before {
966
+ content: '\f1de';
967
+ font-family: 'Font Awesome 6 Free';
968
+ font-weight: 900;
969
+ font-size: 10px;
970
+ line-height: 1;
971
+ }
972
+
973
+ /* When properties are set, the badge carries a `data-props-summary` and shows
974
+ it after the glyph — and reads as an active (not just affordance) chip. */
975
+ .squisq-props-badge[data-props-summary] {
976
+ opacity: 1;
977
+ color: #6d28d9;
978
+ background: #f5f3ff;
979
+ border-color: #c4b5fd;
980
+ }
981
+
982
+ .squisq-props-badge[data-props-summary]::after {
983
+ content: attr(data-props-summary);
984
+ margin-left: 5px;
985
+ font-weight: 500;
986
+ white-space: nowrap;
987
+ }
988
+
989
+ :is(h1, h2, h3, h4, h5, h6):hover > .squisq-props-badge {
990
+ opacity: 0.9;
991
+ }
992
+
993
+ .squisq-props-badge:hover,
994
+ .squisq-props-badge:focus-visible {
995
+ opacity: 1;
996
+ color: #6d28d9;
997
+ background: #ede9fe;
998
+ border-color: #c4b5fd;
999
+ outline: none;
1000
+ }
1001
+
1002
+ /* ─── Block-properties palette popover (portaled to <body>) ──────────── */
1003
+ .squisq-block-props-popover {
1004
+ width: 280px;
1005
+ max-width: calc(100vw - 24px);
1006
+ padding: 10px 12px;
1007
+ display: flex;
1008
+ flex-direction: column;
1009
+ gap: 10px;
1010
+ background: #fff;
1011
+ border: 1px solid #e5e7eb;
1012
+ border-radius: 10px;
1013
+ box-shadow:
1014
+ 0 8px 24px rgba(0, 0, 0, 0.12),
1015
+ 0 2px 6px rgba(0, 0, 0, 0.08);
1016
+ }
1017
+
1018
+ .squisq-block-props-row {
1019
+ display: flex;
1020
+ align-items: center;
1021
+ justify-content: space-between;
1022
+ gap: 10px;
1023
+ }
1024
+
1025
+ .squisq-block-props-label {
1026
+ font-size: 11px;
1027
+ font-weight: 600;
1028
+ color: #6b7280;
1029
+ flex-shrink: 0;
1030
+ }
1031
+
1032
+ .squisq-block-props-numfield {
1033
+ display: inline-flex;
1034
+ align-items: center;
1035
+ gap: 6px;
1036
+ }
1037
+
1038
+ .squisq-block-props-input {
1039
+ width: 72px;
1040
+ padding: 4px 6px;
1041
+ font-size: 12px;
1042
+ color: #111827;
1043
+ border: 1px solid #d1d5db;
1044
+ border-radius: 5px;
1045
+ outline: none;
1046
+ }
1047
+
1048
+ .squisq-block-props-input:focus {
1049
+ border-color: #6366f1;
1050
+ }
1051
+
1052
+ .squisq-block-props-unit {
1053
+ font-size: 11px;
1054
+ color: #9ca3af;
1055
+ }
1056
+
1057
+ /* Dark theme */
1058
+ .squisq-editor-shell[data-theme='dark'] .squisq-props-badge {
1059
+ border-color: #4b5563;
1060
+ color: #9ca3af;
1061
+ }
1062
+
1063
+ .squisq-editor-shell[data-theme='dark'] .squisq-props-badge[data-props-summary] {
1064
+ color: #ddd6fe;
1065
+ background: #3730a3;
1066
+ border-color: #6d5fd6;
1067
+ }
1068
+
1069
+ .squisq-editor-shell[data-theme='dark'] .squisq-props-badge:hover,
1070
+ .squisq-editor-shell[data-theme='dark'] .squisq-props-badge:focus-visible {
1071
+ color: #ede9fe;
1072
+ background: #4c1d95;
1073
+ border-color: #7c3aed;
1074
+ }
1075
+
1076
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-props-popover {
1077
+ background: #1f2937;
1078
+ border-color: #374151;
1079
+ box-shadow:
1080
+ 0 8px 24px rgba(0, 0, 0, 0.4),
1081
+ 0 2px 6px rgba(0, 0, 0, 0.2);
1082
+ }
1083
+
1084
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-props-input {
1085
+ background: #111827;
1086
+ color: #f9fafb;
1087
+ border-color: #4b5563;
1088
+ }
1089
+
753
1090
  /* ─── Status Bar ─────────────────────────────────────── */
754
1091
 
755
1092
  .squisq-status-bar {
@@ -853,29 +1190,40 @@
853
1190
  font-family: var(--squisq-theme-title-font, var(--squisq-theme-body-font, inherit));
854
1191
  }
855
1192
 
1193
+ /* Headings start blocks, so they get generous space ABOVE (a clear break from
1194
+ the previous block) and a tighter margin BELOW so the heading stays visually
1195
+ grouped with the content it introduces. */
856
1196
  .squisq-wysiwyg-editor h1 {
857
1197
  font-size: 2em;
858
- margin: 0.67em 0;
1198
+ margin: 1em 0 0.4em;
859
1199
  }
860
1200
  .squisq-wysiwyg-editor h2 {
861
1201
  font-size: 1.5em;
862
- margin: 0.75em 0;
1202
+ margin: 1.15em 0 0.35em;
863
1203
  }
864
1204
  .squisq-wysiwyg-editor h3 {
865
1205
  font-size: 1.17em;
866
- margin: 0.83em 0;
1206
+ margin: 1.3em 0 0.3em;
867
1207
  }
868
1208
  .squisq-wysiwyg-editor h4 {
869
1209
  font-size: 1em;
870
- margin: 1em 0;
1210
+ margin: 1.4em 0 0.3em;
871
1211
  }
872
1212
  .squisq-wysiwyg-editor h5 {
873
1213
  font-size: 0.83em;
874
- margin: 1.17em 0;
1214
+ margin: 1.5em 0 0.3em;
875
1215
  }
876
1216
  .squisq-wysiwyg-editor h6 {
877
1217
  font-size: 0.67em;
878
- margin: 1.33em 0;
1218
+ margin: 1.6em 0 0.3em;
1219
+ }
1220
+
1221
+ /* The first block in the document has nothing above it — don't push the top of
1222
+ the editor down with the section-break margin. (The editable ProseMirror node
1223
+ itself carries the `squisq-wysiwyg-editor` class, so its blocks are direct
1224
+ children.) */
1225
+ .squisq-wysiwyg-editor > :first-child {
1226
+ margin-top: 0;
879
1227
  }
880
1228
 
881
1229
  .squisq-wysiwyg-editor p {
@@ -930,6 +1278,43 @@
930
1278
  margin: 0.25em 0;
931
1279
  }
932
1280
 
1281
+ /* Task lists (GFM checkboxes). TaskItem renders
1282
+ `<li data-type="taskItem"><label><input type="checkbox"><span></span></label><div>…</div></li>`
1283
+ inside `<ul data-type="taskList">`. Drop the bullet/indent and lay each item
1284
+ out as a checkbox + content row. */
1285
+ .squisq-wysiwyg-editor ul[data-type='taskList'] {
1286
+ list-style: none;
1287
+ padding-left: 0;
1288
+ }
1289
+
1290
+ .squisq-wysiwyg-editor ul[data-type='taskList'] li {
1291
+ display: flex;
1292
+ align-items: flex-start;
1293
+ gap: 0.5em;
1294
+ }
1295
+
1296
+ .squisq-wysiwyg-editor ul[data-type='taskList'] li > label {
1297
+ flex: 0 0 auto;
1298
+ margin-top: 0.15em;
1299
+ user-select: none;
1300
+ }
1301
+
1302
+ .squisq-wysiwyg-editor ul[data-type='taskList'] li > div {
1303
+ flex: 1 1 auto;
1304
+ min-width: 0;
1305
+ }
1306
+
1307
+ .squisq-wysiwyg-editor ul[data-type='taskList'] li > div > p {
1308
+ margin: 0;
1309
+ }
1310
+
1311
+ .squisq-wysiwyg-editor ul[data-type='taskList'] input[type='checkbox'] {
1312
+ width: 1em;
1313
+ height: 1em;
1314
+ cursor: pointer;
1315
+ accent-color: #2563eb;
1316
+ }
1317
+
933
1318
  .squisq-wysiwyg-editor hr {
934
1319
  border: none;
935
1320
  border-top: 2px solid #e5e7eb;
@@ -1031,22 +1416,142 @@
1031
1416
 
1032
1417
  /* ─── Preview Controls ──────────────────────────────── */
1033
1418
 
1034
- .squisq-preview-controls-inline {
1419
+ /* Left-side preview switches: the segmented mode switch and the aspect-ratio
1420
+ switch, separated by a divider. Natural width; sits just after the view
1421
+ tabs and never shrinks. */
1422
+ .squisq-preview-left-controls {
1035
1423
  display: flex;
1036
1424
  align-items: center;
1037
- gap: 6px;
1038
- flex-wrap: wrap;
1039
- padding: 2px 0 2px 9px;
1040
- }
1425
+ gap: 8px;
1426
+ margin-left: 8px;
1427
+ flex: 0 0 auto;
1428
+ }
1429
+
1430
+ .squisq-preview-seg-divider {
1431
+ width: 1px;
1432
+ height: 20px;
1433
+ background: var(--squisq-border, #d1d5db);
1434
+ flex: 0 0 auto;
1435
+ }
1436
+
1437
+ /* Segmented switch — a row of connected buttons (display mode: Video /
1438
+ Slideshow / Document / Page; aspect ratio: 16:9 / 1:1 / 9:16 / 4:3),
1439
+ replacing the old "Mode:" / "Format:" dropdowns. */
1440
+ .squisq-preview-seg {
1441
+ display: inline-flex;
1442
+ align-items: center;
1443
+ flex: 0 0 auto;
1444
+ border: 1px solid var(--squisq-border, #d1d5db);
1445
+ border-radius: 6px;
1446
+ overflow: hidden;
1447
+ background: var(--squisq-input-bg, #fff);
1448
+ }
1449
+
1450
+ .squisq-preview-seg-btn {
1451
+ appearance: none;
1452
+ border: none;
1453
+ border-left: 1px solid var(--squisq-border, #d1d5db);
1454
+ background: transparent;
1455
+ color: #4b5563;
1456
+ font-size: 12px;
1457
+ font-weight: 500;
1458
+ line-height: 1;
1459
+ padding: 6px 11px;
1460
+ cursor: pointer;
1461
+ white-space: nowrap;
1462
+ transition:
1463
+ background 0.12s,
1464
+ color 0.12s;
1465
+ }
1466
+
1467
+ /* Icon segments (aspect ratio) center their glyph and use tighter, square-ish
1468
+ padding so the row of ratio icons reads as a compact cluster. */
1469
+ .squisq-preview-seg-btn--icon {
1470
+ display: flex;
1471
+ align-items: center;
1472
+ justify-content: center;
1473
+ padding: 4px 8px;
1474
+ }
1475
+
1476
+ .squisq-preview-seg-btn--icon svg {
1477
+ display: block;
1478
+ }
1479
+
1480
+ /* Font Awesome glyph segments (captions: cc / social) — size the webfont
1481
+ glyph to sit level with the SVG aspect-ratio icons. */
1482
+ .squisq-preview-seg-btn--icon > i {
1483
+ font-size: 15px;
1484
+ line-height: 1;
1485
+ }
1486
+
1487
+ .squisq-preview-seg-btn:first-child {
1488
+ border-left: none;
1489
+ }
1490
+
1491
+ .squisq-preview-seg-btn:hover {
1492
+ background: #f3f4f6;
1493
+ color: #111827;
1494
+ }
1495
+
1496
+ .squisq-preview-seg-btn--active,
1497
+ .squisq-preview-seg-btn--active:hover {
1498
+ background: #2563eb;
1499
+ color: #fff;
1500
+ }
1501
+
1502
+ /* Flex-filler root: spans the toolbar's leftover width, which is the budget
1503
+ PreviewToolbarControls measures to decide how many controls stay inline vs
1504
+ fold into the overflow menu. It also pushes the right-side toolbar buttons
1505
+ to the far edge (replacing the preview-mode spacer). `position: relative`
1506
+ anchors the hidden measurement probe; the 9px lead offsets the first
1507
+ control from the view-tabs divider. */
1508
+ .squisq-preview-controls {
1509
+ display: flex;
1510
+ align-items: center;
1511
+ gap: 6px;
1512
+ flex: 1 1 auto;
1513
+ min-width: 0;
1514
+ position: relative;
1515
+ padding-left: 9px;
1516
+ }
1517
+
1518
+ /* Hidden probe: renders every control at natural width so the fit measurement
1519
+ has per-control widths to work from. Absolutely positioned + hidden so it
1520
+ never affects layout or catches pointer events. */
1521
+ .squisq-preview-controls-probe {
1522
+ position: absolute;
1523
+ top: 0;
1524
+ left: 0;
1525
+ display: flex;
1526
+ align-items: center;
1527
+ gap: 6px;
1528
+ flex-wrap: nowrap;
1529
+ white-space: nowrap;
1530
+ visibility: hidden;
1531
+ pointer-events: none;
1532
+ }
1533
+
1534
+ /* Inline row sits at its natural width (the overflow gear, when present, sits
1535
+ just after it); it never wraps — controls that don't fit move to the menu. */
1536
+ .squisq-preview-controls-inline {
1537
+ display: flex;
1538
+ align-items: center;
1539
+ gap: 6px;
1540
+ flex-wrap: nowrap;
1541
+ flex: 0 0 auto;
1542
+ }
1041
1543
 
1042
- .squisq-preview-controls-inline .squisq-preview-control {
1544
+ .squisq-preview-controls-inline .squisq-preview-control,
1545
+ .squisq-preview-controls-probe .squisq-preview-control {
1043
1546
  display: flex;
1044
1547
  align-items: center;
1045
1548
  gap: 4px;
1549
+ flex-shrink: 0;
1046
1550
  }
1047
1551
 
1048
1552
  .squisq-preview-controls-compact {
1049
1553
  position: relative;
1554
+ flex: 0 0 auto;
1050
1555
  }
1051
1556
 
1052
1557
  .squisq-preview-controls-popover {
@@ -1187,6 +1692,8 @@
1187
1692
  /* Toolbar */
1188
1693
  .squisq-editor-shell[data-theme='dark'] .squisq-toolbar {
1189
1694
  background: rgba(255, 255, 255, 0.08);
1695
+ /* Muted, but lighter than the dark-theme label color (#d1d5db). */
1696
+ --squisq-toolbar-icon: #9ca3af;
1190
1697
  }
1191
1698
 
1192
1699
  .squisq-editor-shell[data-theme='dark'] .squisq-toolbar-view-tabs {
@@ -1235,6 +1742,32 @@
1235
1742
  background: #4b5563;
1236
1743
  }
1237
1744
 
1745
+ /* Segmented switches — mode + aspect ratio (dark) */
1746
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-divider {
1747
+ background: #4b5563;
1748
+ }
1749
+
1750
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg {
1751
+ border-color: #4b5563;
1752
+ background: #111827;
1753
+ }
1754
+
1755
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn {
1756
+ color: #d1d5db;
1757
+ border-left-color: #4b5563;
1758
+ }
1759
+
1760
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn:hover {
1761
+ background: #374151;
1762
+ color: #f9fafb;
1763
+ }
1764
+
1765
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn--active,
1766
+ .squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn--active:hover {
1767
+ background: #2563eb;
1768
+ color: #fff;
1769
+ }
1770
+
1238
1771
  /* Overflow menu (dark) */
1239
1772
  .squisq-editor-shell[data-theme='dark'] .squisq-toolbar-overflow-menu {
1240
1773
  background: #1f2937;
@@ -1308,6 +1841,23 @@
1308
1841
  0 2px 6px rgba(0, 0, 0, 0.2);
1309
1842
  }
1310
1843
 
1844
+ .squisq-editor-shell[data-theme='dark'] .squisq-template-gallery-search {
1845
+ background: #1f2937;
1846
+ border-bottom-color: #374151;
1847
+ }
1848
+
1849
+ .squisq-editor-shell[data-theme='dark'] .squisq-template-gallery-search-input {
1850
+ color: #f9fafb;
1851
+ }
1852
+
1853
+ .squisq-editor-shell[data-theme='dark'] .squisq-template-gallery-search-input::placeholder {
1854
+ color: #6b7280;
1855
+ }
1856
+
1857
+ .squisq-editor-shell[data-theme='dark'] .squisq-template-gallery-empty {
1858
+ color: #9ca3af;
1859
+ }
1860
+
1311
1861
  .squisq-editor-shell[data-theme='dark'] .squisq-template-gallery-none:hover {
1312
1862
  background: #374151;
1313
1863
  }
@@ -2806,141 +3356,643 @@
2806
3356
  min-height: 0;
2807
3357
  }
2808
3358
 
2809
- /* All gutter chrome is parameterized through CSS custom properties so
2810
- hosts can re-theme without overriding individual rules. Defaults
2811
- below match the original light-mode values; the dark-mode block
2812
- further down rebinds them. Hosts override at any selector level
2813
- (e.g. `.db-shell[data-theme='dark'] .squisq-editor-shell { ... }`). */
2814
- .squisq-editor-shell {
2815
- --squisq-preview-accent: #6366f1;
2816
- --squisq-preview-accent-muted: #94a3b8;
2817
- --squisq-preview-gutter-bg: #dcd8d0;
2818
- --squisq-preview-card-bg: #ffffff;
2819
- --squisq-preview-card-border: #e5e7eb;
2820
- --squisq-preview-card-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
2821
- --squisq-preview-card-title: #111827;
2822
- --squisq-preview-card-svg-bg: #f1f5f9;
2823
- --squisq-preview-dot-halo: #ffffff;
2824
- --squisq-preview-empty-bg: #ffffff;
2825
- --squisq-preview-empty-border: #d1d5db;
2826
- --squisq-preview-empty-text: #6b7280;
2827
- }
2828
-
2829
- .squisq-inline-preview-gutter {
2830
- /* Tinted to match the WYSIWYG container's "desk" color so this pane
2831
- reads as part of the same surrounding chrome the Monaco gutter
2832
- blends into on the other side of the canvas. */
2833
- padding: 0;
2834
- background: var(--squisq-preview-gutter-bg);
2835
- border: none;
2836
- box-sizing: border-box;
2837
- font-family: inherit;
2838
- }
2839
-
2840
- .squisq-inline-preview-empty {
2841
- position: absolute;
2842
- top: 12px;
2843
- left: 12px;
2844
- right: 12px;
2845
- color: var(--squisq-preview-empty-text);
2846
- font-size: 12px;
2847
- line-height: 1.4;
2848
- padding: 12px;
2849
- text-align: center;
2850
- border: 1px dashed var(--squisq-preview-empty-border);
2851
- border-radius: 8px;
2852
- background: var(--squisq-preview-empty-bg);
3359
+ /* ─── Block-at-a-time card ───────────────────────────────
3360
+ Wraps a single block's editor surface with Prev / Next / Add chrome.
3361
+ The card itself is the flex child that takes the remaining width; its
3362
+ body holds the editor (which fills the height) and the nav bar pins to
3363
+ the bottom. */
3364
+ .squisq-editor-with-gutter > .squisq-block-card {
3365
+ flex: 1 1 auto;
3366
+ min-width: 0;
3367
+ min-height: 0;
2853
3368
  }
2854
3369
 
2855
- .squisq-inline-preview-empty p {
2856
- margin: 0;
3370
+ /* ─── Block preview panel ─────────────────────────────────
3371
+ A large live preview of the active block, beside the card in
3372
+ block / timeline mode when "Show block previews" is on. Shares the
3373
+ editing area roughly 50/50 with the card; the framed slide inside
3374
+ fills the panel width at the active viewport's aspect ratio. */
3375
+ .squisq-editor-with-gutter > .squisq-block-preview-panel {
3376
+ flex: 1 1 0;
3377
+ min-width: 260px;
3378
+ min-height: 0;
2857
3379
  }
2858
3380
 
2859
- .squisq-inline-preview-card {
3381
+ .squisq-block-preview-panel {
2860
3382
  display: flex;
2861
- flex-direction: column;
2862
- gap: 6px;
2863
- padding: 8px;
2864
- background: var(--squisq-preview-card-bg);
2865
- border: 1px solid var(--squisq-preview-card-border);
2866
- border-radius: 8px;
2867
- box-shadow: var(--squisq-preview-card-shadow);
2868
- transition: top 120ms ease-out;
3383
+ align-items: center;
3384
+ justify-content: center;
3385
+ padding: 12px 16px;
3386
+ /* Same beige "desk" as the block card so the framed slide sits on it. */
3387
+ background: #dcd8d0;
3388
+ border-left: 1px solid #b0a99a;
3389
+ overflow: auto;
2869
3390
  }
2870
3391
 
2871
- .squisq-inline-preview-card-label {
2872
- display: flex;
2873
- flex-direction: row;
2874
- align-items: baseline;
2875
- gap: 6px;
2876
- font-size: 12px;
2877
- line-height: 1.3;
2878
- white-space: nowrap;
3392
+ .squisq-block-preview-frame {
3393
+ width: 100%;
3394
+ max-width: 760px;
3395
+ max-height: 100%;
2879
3396
  overflow: hidden;
3397
+ background: var(--squisq-preview-card-svg-bg, #f1f5f9);
3398
+ border: 1px solid #b0a99a;
3399
+ border-radius: 6px;
3400
+ box-shadow: 0 1px 3px rgba(15, 23, 42, 0.1);
2880
3401
  }
2881
3402
 
2882
- .squisq-inline-preview-card-template {
2883
- font-weight: 600;
2884
- color: var(--squisq-preview-accent);
2885
- flex-shrink: 0;
3403
+ .squisq-block-preview-frame svg {
3404
+ display: block;
3405
+ width: 100%;
3406
+ height: 100%;
2886
3407
  }
2887
3408
 
2888
- .squisq-inline-preview-card-sep {
2889
- color: var(--squisq-preview-accent-muted);
2890
- flex-shrink: 0;
3409
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-preview-panel {
3410
+ background: #0f1219;
3411
+ border-left-color: #2a3144;
2891
3412
  }
2892
3413
 
2893
- .squisq-inline-preview-card-title {
2894
- color: var(--squisq-preview-card-title);
2895
- font-weight: 500;
2896
- overflow: hidden;
2897
- text-overflow: ellipsis;
3414
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-preview-frame {
3415
+ border-color: #2a3144;
2898
3416
  }
2899
3417
 
2900
- .squisq-inline-preview-card-svg {
2901
- width: 100%;
2902
- overflow: hidden;
2903
- border-radius: 4px;
2904
- background: var(--squisq-preview-card-svg-bg);
3418
+ .squisq-block-card {
3419
+ display: flex;
3420
+ flex-direction: column;
3421
+ height: 100%;
3422
+ min-height: 0;
3423
+ /* Matches the editor "desk" so the card body's top/bottom margin reads as
3424
+ the same beige as the left/right sides rather than white. */
3425
+ background: #dcd8d0;
2905
3426
  }
2906
3427
 
2907
- /* Vertical bracket line showing each block's editor extent. Lives in the
2908
- connector strip, anchored to its right edge so the cards sit
2909
- immediately to its right. The diagonal connector bridges from the
2910
- vertical line to its card at the heading row. */
2911
- .squisq-inline-preview-extent {
2912
- width: 4px;
2913
- background: var(--squisq-preview-accent);
2914
- border-radius: 2px;
2915
- transition:
2916
- top 120ms ease-out,
2917
- height 120ms ease-out;
3428
+ .squisq-block-card-body {
3429
+ flex: 1 1 auto;
3430
+ min-height: 0;
3431
+ position: relative;
3432
+ display: flex;
3433
+ /* A few px of beige "desk" above and below the block, so the framed card
3434
+ sits off the toolbar and nav bar. The body background is transparent, so
3435
+ this reveals the card's desk color. */
3436
+ padding: 6px 0;
2918
3437
  }
2919
3438
 
2920
- /* Untagged blocks get a thinner, lower-contrast bar — they still surface
2921
- the block's structural reach but recede behind the strong accent bars
2922
- that pair with cards. */
2923
- .squisq-inline-preview-extent--untagged {
2924
- width: 3px;
2925
- background: var(--squisq-preview-accent-muted);
2926
- border-radius: 1.5px;
3439
+ .squisq-block-card-body > .squisq-raw-editor-container,
3440
+ .squisq-block-card-body > .squisq-wysiwyg-container {
3441
+ flex: 1 1 auto;
3442
+ min-width: 0;
3443
+ min-height: 0;
2927
3444
  }
2928
3445
 
2929
- /* Diagonal connector SVG colorsthe JSX uses inline `stroke`/`fill`
2930
- presentation attributes so the geometry stays self-contained, but
2931
- those attributes are weaker than CSS rules and are overridden here
2932
- so all preview accents flow through the same custom property. */
2933
- .squisq-inline-preview-connector-svg line {
2934
- stroke: var(--squisq-preview-accent);
3446
+ /* Frame just the editable surface top and bottom so a single block reads
3447
+ as a discrete card. Scoped to the surface (not the full-width body) so the
3448
+ line tracks the white page rather than stretching across the beige desk.
3449
+ Uses the same seam color as the Monaco frame so it reads against the desk. */
3450
+ .squisq-block-card-body .squisq-wysiwyg-editor {
3451
+ border-top: 1px solid #b0a99a;
3452
+ border-bottom: 1px solid #b0a99a;
2935
3453
  }
2936
- .squisq-inline-preview-connector-svg circle {
2937
- fill: var(--squisq-preview-accent);
2938
- stroke: var(--squisq-preview-dot-halo);
3454
+
3455
+ .squisq-block-card-body > .squisq-raw-editor-container {
3456
+ border-top: 1px solid #b0a99a;
3457
+ border-bottom: 1px solid #b0a99a;
2939
3458
  }
2940
3459
 
2941
- /* Diagonal connector SVG — drawn directly via <line>/<circle> elements
2942
- in InlinePreviewGutter; CSS is intentionally minimal so the JSX
2943
- coordinates remain the source of truth. */
3460
+ .squisq-block-card-nav {
3461
+ display: flex;
3462
+ align-items: center;
3463
+ gap: 10px;
3464
+ padding: 6px 12px;
3465
+ background: #f9fafb;
3466
+ flex-shrink: 0;
3467
+ }
3468
+
3469
+ .squisq-block-card-position {
3470
+ font-size: 12px;
3471
+ color: #6b7280;
3472
+ white-space: nowrap;
3473
+ margin: 0 auto;
3474
+ }
3475
+
3476
+ .squisq-block-card-button {
3477
+ display: inline-flex;
3478
+ align-items: center;
3479
+ gap: 5px;
3480
+ padding: 4px 10px;
3481
+ border: 1px solid #d1d5db;
3482
+ border-radius: 6px;
3483
+ background: #ffffff;
3484
+ color: #374151;
3485
+ font-size: 12px;
3486
+ cursor: pointer;
3487
+ }
3488
+
3489
+ .squisq-block-card-button:hover:not(:disabled) {
3490
+ background: #f3f4f6;
3491
+ }
3492
+
3493
+ .squisq-block-card-button:disabled {
3494
+ opacity: 0.45;
3495
+ cursor: default;
3496
+ }
3497
+
3498
+ .squisq-block-card-add {
3499
+ margin-left: 8px;
3500
+ }
3501
+
3502
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card {
3503
+ background: #0f1219;
3504
+ }
3505
+
3506
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-body .squisq-wysiwyg-editor,
3507
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-body > .squisq-raw-editor-container {
3508
+ border-top-color: #2a3144;
3509
+ border-bottom-color: #2a3144;
3510
+ }
3511
+
3512
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-nav {
3513
+ background: #1f2937;
3514
+ }
3515
+
3516
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-position {
3517
+ color: #9ca3af;
3518
+ }
3519
+
3520
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-button {
3521
+ border-color: #374151;
3522
+ background: #111827;
3523
+ color: #d1d5db;
3524
+ }
3525
+
3526
+ .squisq-editor-shell[data-theme='dark'] .squisq-block-card-button:hover:not(:disabled) {
3527
+ background: #1f2937;
3528
+ }
3529
+
3530
+ /* ─── Timeline track ─────────────────────────────────────
3531
+ Horizontal strip of block + media bars shown below the editor in Timeline
3532
+ view. Scrolls horizontally; bars are absolutely positioned by time. */
3533
+ .squisq-timeline {
3534
+ position: relative;
3535
+ flex-shrink: 0;
3536
+ display: flex;
3537
+ flex-direction: row;
3538
+ background: #efece6;
3539
+ border-top: 1px solid #b0a99a;
3540
+ user-select: none;
3541
+ }
3542
+
3543
+ .squisq-timeline-controls {
3544
+ display: flex;
3545
+ flex-direction: column;
3546
+ align-items: center;
3547
+ gap: 6px;
3548
+ padding: 8px 6px;
3549
+ flex-shrink: 0;
3550
+ border-right: 1px solid #b0a99a;
3551
+ }
3552
+
3553
+ .squisq-timeline-zoom-button {
3554
+ display: inline-flex;
3555
+ align-items: center;
3556
+ justify-content: center;
3557
+ width: 24px;
3558
+ height: 24px;
3559
+ padding: 0;
3560
+ border: 1px solid #d1d5db;
3561
+ border-radius: 6px;
3562
+ background: #ffffff;
3563
+ color: #374151;
3564
+ font-size: 16px;
3565
+ line-height: 1;
3566
+ cursor: pointer;
3567
+ }
3568
+
3569
+ .squisq-timeline-zoom-button:hover:not(:disabled) {
3570
+ background: #f3f4f6;
3571
+ }
3572
+
3573
+ .squisq-timeline-zoom-button:disabled {
3574
+ opacity: 0.45;
3575
+ cursor: default;
3576
+ }
3577
+
3578
+ .squisq-timeline-play-button {
3579
+ font-size: 12px;
3580
+ color: #2563eb;
3581
+ }
3582
+
3583
+ .squisq-timeline-time {
3584
+ font-size: 10px;
3585
+ color: #6b7280;
3586
+ text-align: center;
3587
+ white-space: nowrap;
3588
+ font-variant-numeric: tabular-nums;
3589
+ }
3590
+
3591
+ /* Off-screen host for the timed-media playback elements. */
3592
+ .squisq-timeline-media-host {
3593
+ position: absolute;
3594
+ width: 0;
3595
+ height: 0;
3596
+ overflow: hidden;
3597
+ }
3598
+
3599
+ .squisq-timeline-scroll {
3600
+ flex: 1;
3601
+ overflow-x: auto;
3602
+ overflow-y: hidden;
3603
+ padding: 8px 0 0;
3604
+ min-width: 0;
3605
+ min-height: 0;
3606
+ }
3607
+
3608
+ .squisq-timeline-inner {
3609
+ position: relative;
3610
+ display: flex;
3611
+ flex-direction: column;
3612
+ min-width: 100%;
3613
+ min-height: 100%;
3614
+ box-sizing: border-box;
3615
+ /* Leave room at the top for the playhead's knob/notch, which is anchored
3616
+ to the padding-box top (absolute `top: 0`) and so stays above the rows
3617
+ while this padding pushes the track down — the notch reads as a handle
3618
+ sitting above the timeline rather than overlapping the first block. */
3619
+ padding-top: 14px;
3620
+ }
3621
+
3622
+ /* Playhead — draggable scrubber. A wide transparent column is the hit area;
3623
+ the 2px red line is drawn down its center via ::before. */
3624
+ .squisq-timeline-playhead {
3625
+ position: absolute;
3626
+ top: 0;
3627
+ bottom: 0;
3628
+ width: 14px;
3629
+ margin-left: -7px;
3630
+ z-index: 20;
3631
+ cursor: ew-resize;
3632
+ touch-action: none;
3633
+ }
3634
+
3635
+ .squisq-timeline-playhead::before {
3636
+ content: '';
3637
+ position: absolute;
3638
+ top: 0;
3639
+ bottom: 0;
3640
+ left: 6px;
3641
+ width: 2px;
3642
+ background: #ef4444;
3643
+ pointer-events: none;
3644
+ }
3645
+
3646
+ .squisq-timeline-playhead-knob {
3647
+ position: absolute;
3648
+ top: 0;
3649
+ left: 2px;
3650
+ width: 10px;
3651
+ height: 9px;
3652
+ border-radius: 2px;
3653
+ background: #ef4444;
3654
+ pointer-events: none;
3655
+ }
3656
+
3657
+ .squisq-timeline-row--ruler {
3658
+ cursor: pointer;
3659
+ }
3660
+
3661
+ .squisq-timeline-row {
3662
+ position: relative;
3663
+ height: 40px;
3664
+ margin: 0 0 6px;
3665
+ }
3666
+
3667
+ .squisq-timeline-row--media {
3668
+ height: 26px;
3669
+ margin-bottom: 0;
3670
+ }
3671
+
3672
+ .squisq-timeline-block {
3673
+ position: absolute;
3674
+ top: 0;
3675
+ height: 100%;
3676
+ box-sizing: border-box;
3677
+ display: flex;
3678
+ align-items: center;
3679
+ padding: 0 8px;
3680
+ border: 1px solid #b0a99a;
3681
+ border-radius: 5px;
3682
+ background: #ffffff;
3683
+ color: #374151;
3684
+ font-size: 12px;
3685
+ cursor: pointer;
3686
+ overflow: hidden;
3687
+ }
3688
+
3689
+ .squisq-timeline-block--active {
3690
+ border-color: var(--squisq-outline-accent, #6c5ce7);
3691
+ box-shadow: 0 0 0 1px var(--squisq-outline-accent, #6c5ce7) inset;
3692
+ }
3693
+
3694
+ /* Per-bar slideshow thumbnail — right-aligned and inset with a small margin
3695
+ so it reads as a card tucked into the track. Width follows the 16:9 viewport
3696
+ from the fixed (margin-inset) height. */
3697
+ .squisq-timeline-block-thumb {
3698
+ position: absolute;
3699
+ top: 4px;
3700
+ bottom: 4px;
3701
+ right: 4px;
3702
+ aspect-ratio: 16 / 9;
3703
+ z-index: 0;
3704
+ overflow: hidden;
3705
+ border-radius: 3px;
3706
+ border: 1px solid rgba(0, 0, 0, 0.12);
3707
+ pointer-events: none;
3708
+ }
3709
+
3710
+ .squisq-timeline-block-thumb svg {
3711
+ display: block;
3712
+ width: 100%;
3713
+ height: 100%;
3714
+ }
3715
+
3716
+ .squisq-timeline-block-label {
3717
+ position: relative;
3718
+ z-index: 1;
3719
+ white-space: nowrap;
3720
+ overflow: hidden;
3721
+ text-overflow: ellipsis;
3722
+ pointer-events: none;
3723
+ /* Legible over the thumbnail. */
3724
+ background: rgba(255, 255, 255, 0.82);
3725
+ border-radius: 3px;
3726
+ padding: 0 4px;
3727
+ }
3728
+
3729
+ .squisq-timeline-clip {
3730
+ position: absolute;
3731
+ top: 0;
3732
+ height: 100%;
3733
+ box-sizing: border-box;
3734
+ display: flex;
3735
+ align-items: center;
3736
+ padding: 0 6px;
3737
+ border-radius: 4px;
3738
+ font-size: 11px;
3739
+ color: #ffffff;
3740
+ cursor: grab;
3741
+ overflow: hidden;
3742
+ }
3743
+
3744
+ .squisq-timeline-clip--audio {
3745
+ background: #2f855a;
3746
+ }
3747
+
3748
+ .squisq-timeline-clip--video {
3749
+ background: #2b6cb0;
3750
+ }
3751
+
3752
+ /* Body-embedded media (recordings, dropped files): snapped to its block,
3753
+ outlined to distinguish from authored `{[…]}` clips. Dragging re-times it
3754
+ (and converts it to a clip annotation). */
3755
+ .squisq-timeline-clip--embedded {
3756
+ opacity: 0.8;
3757
+ border: 1px dashed rgba(255, 255, 255, 0.7);
3758
+ }
3759
+
3760
+ .squisq-timeline-clip--document {
3761
+ opacity: 0.85;
3762
+ background-image: repeating-linear-gradient(
3763
+ 45deg,
3764
+ rgba(255, 255, 255, 0.12) 0,
3765
+ rgba(255, 255, 255, 0.12) 6px,
3766
+ transparent 6px,
3767
+ transparent 12px
3768
+ );
3769
+ }
3770
+
3771
+ .squisq-timeline-clip-label {
3772
+ white-space: nowrap;
3773
+ overflow: hidden;
3774
+ text-overflow: ellipsis;
3775
+ pointer-events: none;
3776
+ }
3777
+
3778
+ /* Drag handles on a bar's edges. */
3779
+ .squisq-timeline-edge {
3780
+ position: absolute;
3781
+ top: 0;
3782
+ width: 7px;
3783
+ height: 100%;
3784
+ cursor: ew-resize;
3785
+ }
3786
+
3787
+ .squisq-timeline-edge--left {
3788
+ left: -1px;
3789
+ }
3790
+
3791
+ .squisq-timeline-edge--right {
3792
+ right: -1px;
3793
+ }
3794
+
3795
+ /* Time ruler pinned to the bottom of the track — spans the full width. */
3796
+ .squisq-timeline-row--ruler {
3797
+ height: 18px;
3798
+ margin-top: auto;
3799
+ flex-shrink: 0;
3800
+ overflow: hidden;
3801
+ border-top: 1px solid #c9c2b4;
3802
+ }
3803
+
3804
+ .squisq-timeline-tick {
3805
+ position: absolute;
3806
+ top: 0;
3807
+ height: 100%;
3808
+ border-left: 1px solid #c9c2b4;
3809
+ padding-left: 3px;
3810
+ }
3811
+
3812
+ .squisq-timeline-tick-label {
3813
+ font-size: 10px;
3814
+ color: #6b7280;
3815
+ white-space: nowrap;
3816
+ pointer-events: none;
3817
+ }
3818
+
3819
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline {
3820
+ background: #0b0e14;
3821
+ border-top-color: #2a3144;
3822
+ }
3823
+
3824
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-block {
3825
+ background: #111827;
3826
+ border-color: #2a3144;
3827
+ color: #d1d5db;
3828
+ }
3829
+
3830
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-block-label {
3831
+ background: rgba(17, 24, 39, 0.82);
3832
+ }
3833
+
3834
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-zoom-button {
3835
+ border-color: #374151;
3836
+ background: #111827;
3837
+ color: #d1d5db;
3838
+ }
3839
+
3840
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-time {
3841
+ color: #9ca3af;
3842
+ }
3843
+
3844
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-play-button {
3845
+ color: #60a5fa;
3846
+ }
3847
+
3848
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-zoom-button:hover:not(:disabled) {
3849
+ background: #1f2937;
3850
+ }
3851
+
3852
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-row--ruler,
3853
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-tick {
3854
+ border-color: #2a3144;
3855
+ }
3856
+
3857
+ .squisq-editor-shell[data-theme='dark'] .squisq-timeline-tick-label {
3858
+ color: #9ca3af;
3859
+ }
3860
+
3861
+ /* All gutter chrome is parameterized through CSS custom properties so
3862
+ hosts can re-theme without overriding individual rules. Defaults
3863
+ below match the original light-mode values; the dark-mode block
3864
+ further down rebinds them. Hosts override at any selector level
3865
+ (e.g. `.db-shell[data-theme='dark'] .squisq-editor-shell { ... }`). */
3866
+ .squisq-editor-shell {
3867
+ --squisq-preview-accent: #6366f1;
3868
+ --squisq-preview-accent-muted: #94a3b8;
3869
+ --squisq-preview-gutter-bg: #dcd8d0;
3870
+ --squisq-preview-card-bg: #ffffff;
3871
+ --squisq-preview-card-border: #e5e7eb;
3872
+ --squisq-preview-card-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
3873
+ --squisq-preview-card-title: #111827;
3874
+ --squisq-preview-card-svg-bg: #f1f5f9;
3875
+ --squisq-preview-dot-halo: #ffffff;
3876
+ --squisq-preview-empty-bg: #ffffff;
3877
+ --squisq-preview-empty-border: #d1d5db;
3878
+ --squisq-preview-empty-text: #6b7280;
3879
+ }
3880
+
3881
+ .squisq-inline-preview-gutter {
3882
+ /* Tinted to match the WYSIWYG container's "desk" color so this pane
3883
+ reads as part of the same surrounding chrome the Monaco gutter
3884
+ blends into on the other side of the canvas. */
3885
+ padding: 0;
3886
+ background: var(--squisq-preview-gutter-bg);
3887
+ border: none;
3888
+ box-sizing: border-box;
3889
+ font-family: inherit;
3890
+ }
3891
+
3892
+ .squisq-inline-preview-empty {
3893
+ position: absolute;
3894
+ top: 12px;
3895
+ left: 12px;
3896
+ right: 12px;
3897
+ color: var(--squisq-preview-empty-text);
3898
+ font-size: 12px;
3899
+ line-height: 1.4;
3900
+ padding: 12px;
3901
+ text-align: center;
3902
+ border: 1px dashed var(--squisq-preview-empty-border);
3903
+ border-radius: 8px;
3904
+ background: var(--squisq-preview-empty-bg);
3905
+ }
3906
+
3907
+ .squisq-inline-preview-empty p {
3908
+ margin: 0;
3909
+ }
3910
+
3911
+ .squisq-inline-preview-card {
3912
+ display: flex;
3913
+ flex-direction: column;
3914
+ gap: 6px;
3915
+ padding: 8px;
3916
+ background: var(--squisq-preview-card-bg);
3917
+ border: 1px solid var(--squisq-preview-card-border);
3918
+ border-radius: 8px;
3919
+ box-shadow: var(--squisq-preview-card-shadow);
3920
+ transition: top 120ms ease-out;
3921
+ }
3922
+
3923
+ .squisq-inline-preview-card-label {
3924
+ display: flex;
3925
+ flex-direction: row;
3926
+ align-items: baseline;
3927
+ gap: 6px;
3928
+ font-size: 12px;
3929
+ line-height: 1.3;
3930
+ white-space: nowrap;
3931
+ overflow: hidden;
3932
+ }
3933
+
3934
+ .squisq-inline-preview-card-template {
3935
+ font-weight: 600;
3936
+ color: var(--squisq-preview-accent);
3937
+ flex-shrink: 0;
3938
+ }
3939
+
3940
+ .squisq-inline-preview-card-sep {
3941
+ color: var(--squisq-preview-accent-muted);
3942
+ flex-shrink: 0;
3943
+ }
3944
+
3945
+ .squisq-inline-preview-card-title {
3946
+ color: var(--squisq-preview-card-title);
3947
+ font-weight: 500;
3948
+ overflow: hidden;
3949
+ text-overflow: ellipsis;
3950
+ }
3951
+
3952
+ .squisq-inline-preview-card-svg {
3953
+ width: 100%;
3954
+ overflow: hidden;
3955
+ border-radius: 4px;
3956
+ background: var(--squisq-preview-card-svg-bg);
3957
+ }
3958
+
3959
+ /* Vertical bracket line showing each block's editor extent. Lives in the
3960
+ connector strip, anchored to its right edge so the cards sit
3961
+ immediately to its right. The diagonal connector bridges from the
3962
+ vertical line to its card at the heading row. */
3963
+ .squisq-inline-preview-extent {
3964
+ width: 4px;
3965
+ background: var(--squisq-preview-accent);
3966
+ border-radius: 2px;
3967
+ transition:
3968
+ top 120ms ease-out,
3969
+ height 120ms ease-out;
3970
+ }
3971
+
3972
+ /* Untagged blocks get a thinner, lower-contrast bar — they still surface
3973
+ the block's structural reach but recede behind the strong accent bars
3974
+ that pair with cards. */
3975
+ .squisq-inline-preview-extent--untagged {
3976
+ width: 3px;
3977
+ background: var(--squisq-preview-accent-muted);
3978
+ border-radius: 1.5px;
3979
+ }
3980
+
3981
+ /* Diagonal connector SVG colors — the JSX uses inline `stroke`/`fill`
3982
+ presentation attributes so the geometry stays self-contained, but
3983
+ those attributes are weaker than CSS rules and are overridden here
3984
+ so all preview accents flow through the same custom property. */
3985
+ .squisq-inline-preview-connector-svg line {
3986
+ stroke: var(--squisq-preview-accent);
3987
+ }
3988
+ .squisq-inline-preview-connector-svg circle {
3989
+ fill: var(--squisq-preview-accent);
3990
+ stroke: var(--squisq-preview-dot-halo);
3991
+ }
3992
+
3993
+ /* Diagonal connector SVG — drawn directly via <line>/<circle> elements
3994
+ in InlinePreviewGutter; CSS is intentionally minimal so the JSX
3995
+ coordinates remain the source of truth. */
2944
3996
  .squisq-inline-preview-connector-svg line,
2945
3997
  .squisq-inline-preview-connector-svg circle {
2946
3998
  transition:
@@ -3636,15 +4688,19 @@
3636
4688
  .squisq-theme-picker-popover {
3637
4689
  max-height: min(70vh, 520px);
3638
4690
  overflow-y: auto;
4691
+ box-sizing: border-box;
3639
4692
  background: var(--squisq-bg, #fff);
3640
4693
  color: var(--squisq-text, #1f2937);
3641
4694
  border: 1px solid var(--squisq-border, #e5e7eb);
3642
4695
  border-radius: 8px;
3643
4696
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
3644
4697
  padding: 6px;
3645
- display: flex;
3646
- flex-direction: column;
3647
- gap: 2px;
4698
+ /* Card grid: each theme is a compact card, packed into as many columns
4699
+ as the (viewport-clamped) popover width allows. Collapses to a single
4700
+ column on narrow windows so the popover never overruns the viewport. */
4701
+ display: grid;
4702
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
4703
+ gap: 4px;
3648
4704
  animation: squisq-theme-picker-appear 0.12s ease-out;
3649
4705
  }
3650
4706
 
@@ -3661,8 +4717,9 @@
3661
4717
 
3662
4718
  .squisq-theme-picker-row {
3663
4719
  display: flex;
3664
- align-items: center;
3665
- gap: 10px;
4720
+ flex-direction: column;
4721
+ align-items: stretch;
4722
+ gap: 6px;
3666
4723
  padding: 8px;
3667
4724
  border-radius: 6px;
3668
4725
  background: transparent;
@@ -3692,8 +4749,9 @@
3692
4749
  }
3693
4750
 
3694
4751
  .squisq-theme-picker-row .squisq-theme-picker-name-chip {
3695
- flex-shrink: 0;
3696
- min-width: 110px;
4752
+ width: 100%;
4753
+ min-width: 0;
4754
+ box-sizing: border-box;
3697
4755
  text-align: center;
3698
4756
  padding: 6px 10px;
3699
4757
  font-size: 13px;
@@ -3716,24 +4774,206 @@
3716
4774
  .squisq-theme-picker-row-desc {
3717
4775
  font-size: 11px;
3718
4776
  color: var(--squisq-text-muted, #6b7280);
3719
- line-height: 1.35;
3720
- display: -webkit-box;
3721
- -webkit-line-clamp: 2;
3722
- -webkit-box-orient: vertical;
3723
- overflow: hidden;
4777
+ line-height: 1.35;
4778
+ display: -webkit-box;
4779
+ -webkit-line-clamp: 2;
4780
+ -webkit-box-orient: vertical;
4781
+ overflow: hidden;
4782
+ }
4783
+
4784
+ /* Dark editor-shell variants for the trigger / popover */
4785
+ .squisq-editor-shell[data-theme='dark'] .squisq-theme-picker-trigger {
4786
+ background: #111827;
4787
+ border-color: #374151;
4788
+ color: #e5e7eb;
4789
+ }
4790
+
4791
+ .squisq-editor-shell[data-theme='dark'] .squisq-theme-picker-popover {
4792
+ background: #1f2937;
4793
+ border-color: #374151;
4794
+ color: #e5e7eb;
4795
+ }
4796
+
4797
+ /* ─── Theme "edit" pencil (beside the toolbar theme dropdown) ────── */
4798
+
4799
+ .squisq-theme-edit-btn {
4800
+ display: inline-flex;
4801
+ align-items: center;
4802
+ justify-content: center;
4803
+ width: 24px;
4804
+ height: 24px;
4805
+ padding: 0;
4806
+ flex-shrink: 0;
4807
+ border: 1px solid var(--squisq-border, #d1d5db);
4808
+ border-radius: 4px;
4809
+ background: var(--squisq-input-bg, #fff);
4810
+ color: var(--squisq-text-muted, #6b7280);
4811
+ cursor: pointer;
4812
+ }
4813
+ .squisq-theme-edit-btn:hover {
4814
+ color: var(--squisq-text, #1f2937);
4815
+ border-color: var(--squisq-text-muted, #9ca3af);
4816
+ }
4817
+ .squisq-editor-shell[data-theme='dark'] .squisq-theme-edit-btn {
4818
+ background: #111827;
4819
+ border-color: #374151;
4820
+ color: #9ca3af;
4821
+ }
4822
+ .squisq-editor-shell[data-theme='dark'] .squisq-theme-edit-btn:hover {
4823
+ color: #e5e7eb;
4824
+ border-color: #4b5563;
4825
+ }
4826
+
4827
+ /* ─── Theme picker: custom-theme group, cards, create row ───────── */
4828
+
4829
+ /* Group label + create row span the full grid width. */
4830
+ .squisq-theme-picker-group-label {
4831
+ grid-column: 1 / -1;
4832
+ padding: 8px 8px 2px;
4833
+ font-size: 11px;
4834
+ font-weight: 600;
4835
+ text-transform: uppercase;
4836
+ letter-spacing: 0.04em;
4837
+ color: var(--squisq-text-muted, #6b7280);
4838
+ }
4839
+
4840
+ /* A custom card is wrapped so its edit/delete actions can overlay the corner
4841
+ without nesting interactive elements inside the selectable button. */
4842
+ .squisq-theme-picker-custom-wrap {
4843
+ position: relative;
4844
+ display: flex;
4845
+ }
4846
+ .squisq-theme-picker-custom-wrap .squisq-theme-picker-row {
4847
+ flex: 1;
4848
+ }
4849
+ .squisq-theme-picker-card-actions {
4850
+ position: absolute;
4851
+ top: 4px;
4852
+ right: 4px;
4853
+ display: flex;
4854
+ gap: 2px;
4855
+ opacity: 0;
4856
+ transition: opacity 0.1s ease;
4857
+ }
4858
+ .squisq-theme-picker-custom-wrap:hover .squisq-theme-picker-card-actions {
4859
+ opacity: 1;
4860
+ }
4861
+ .squisq-theme-picker-card-action {
4862
+ width: 20px;
4863
+ height: 20px;
4864
+ display: inline-flex;
4865
+ align-items: center;
4866
+ justify-content: center;
4867
+ border: 1px solid var(--squisq-border, #e5e7eb);
4868
+ border-radius: 4px;
4869
+ background: var(--squisq-bg, #fff);
4870
+ color: var(--squisq-text-muted, #6b7280);
4871
+ font-size: 12px;
4872
+ cursor: pointer;
4873
+ padding: 0;
4874
+ }
4875
+ .squisq-theme-picker-card-action:hover {
4876
+ color: var(--squisq-text, #1f2937);
4877
+ border-color: var(--squisq-text-muted, #9ca3af);
4878
+ }
4879
+
4880
+ .squisq-theme-picker-create {
4881
+ grid-column: 1 / -1;
4882
+ justify-content: flex-start;
4883
+ gap: 10px;
4884
+ border-style: dashed;
4885
+ border-color: var(--squisq-border, #d1d5db);
4886
+ color: var(--squisq-text, #1f2937);
4887
+ }
4888
+ .squisq-theme-picker-create-plus {
4889
+ flex-shrink: 0;
4890
+ width: 28px;
4891
+ height: 28px;
4892
+ display: inline-flex;
4893
+ align-items: center;
4894
+ justify-content: center;
4895
+ border-radius: 6px;
4896
+ background: var(--squisq-row-hover, #f3f4f6);
4897
+ font-size: 18px;
4898
+ line-height: 1;
4899
+ }
4900
+
4901
+ /* ─── Custom theme designer: right-docked pane + accents ────────── */
4902
+
4903
+ /* Docked into the editor's content row as a fixed-width, full-height flex item
4904
+ (mirrors `.squisq-media-bin`), so the preview flexes narrower beside it — no
4905
+ overlay, no covering the toolbars. The WYSIWYG preview stays fully visible
4906
+ and interactive while the user edits the theme. */
4907
+ .squisq-custom-theme-pane {
4908
+ width: 380px;
4909
+ max-width: 100%;
4910
+ flex-shrink: 0;
4911
+ display: flex;
4912
+ flex-direction: column;
4913
+ background: var(--squisq-bg, #fff);
4914
+ color: var(--squisq-text, #1f2937);
4915
+ border-left: 1px solid var(--squisq-border, #e5e7eb);
4916
+ overflow: hidden;
4917
+ }
4918
+
4919
+ .squisq-custom-theme-pane .squisq-doc-settings-header,
4920
+ .squisq-custom-theme-pane .squisq-doc-settings-footer {
4921
+ flex-shrink: 0;
4922
+ }
4923
+
4924
+ /* Body flex-grows and scrolls; footer/header stay pinned. */
4925
+ .squisq-custom-theme-pane-body {
4926
+ flex: 1;
4927
+ overflow-y: auto;
4928
+ max-height: none;
4929
+ }
4930
+
4931
+ /* Three buttons in a narrow pane — wrap and right-align rather than overflow. */
4932
+ .squisq-custom-theme-pane-footer {
4933
+ flex-wrap: wrap;
4934
+ justify-content: flex-end;
4935
+ gap: 6px;
4936
+ }
4937
+
4938
+ .squisq-editor-shell[data-theme='dark'] .squisq-custom-theme-pane {
4939
+ background: #1f2937;
4940
+ border-color: #374151;
4941
+ color: #e5e7eb;
4942
+ }
4943
+
4944
+ .squisq-theme-customizer-accents {
4945
+ display: flex;
4946
+ flex-direction: column;
4947
+ gap: 6px;
4948
+ }
4949
+ .squisq-theme-customizer-row--accent {
4950
+ display: flex;
4951
+ align-items: center;
4952
+ gap: 8px;
4953
+ }
4954
+ .squisq-theme-customizer-accent-remove {
4955
+ width: 24px;
4956
+ height: 24px;
4957
+ flex-shrink: 0;
4958
+ display: inline-flex;
4959
+ align-items: center;
4960
+ justify-content: center;
4961
+ border: 1px solid var(--squisq-border, #e5e7eb);
4962
+ border-radius: 4px;
4963
+ background: transparent;
4964
+ color: var(--squisq-text-muted, #6b7280);
4965
+ font-size: 16px;
4966
+ line-height: 1;
4967
+ cursor: pointer;
4968
+ padding: 0;
3724
4969
  }
3725
-
3726
- /* Dark editor-shell variants for the trigger / popover */
3727
- .squisq-editor-shell[data-theme='dark'] .squisq-theme-picker-trigger {
3728
- background: #111827;
3729
- border-color: #374151;
3730
- color: #e5e7eb;
4970
+ .squisq-theme-customizer-accent-remove:hover {
4971
+ color: #dc2626;
4972
+ border-color: #fca5a5;
3731
4973
  }
3732
-
3733
- .squisq-editor-shell[data-theme='dark'] .squisq-theme-picker-popover {
3734
- background: #1f2937;
3735
- border-color: #374151;
3736
- color: #e5e7eb;
4974
+ .squisq-theme-customizer-accent-add {
4975
+ align-self: flex-start;
4976
+ margin-top: 2px;
3737
4977
  }
3738
4978
 
3739
4979
  /* ─── Document settings dialog ───────────────────────── */
@@ -3874,3 +5114,321 @@
3874
5114
  background: #2563eb;
3875
5115
  border-color: #2563eb;
3876
5116
  }
5117
+
5118
+ /* ── Transition picker ──────────────────────────────────────────────
5119
+ Toolbar control + portal flyout for choosing a block transition. The
5120
+ trigger reuses the template picker's visual language; the flyout is its
5121
+ own grouped grid with direction + duration sub-controls. */
5122
+ .squisq-transition-picker {
5123
+ display: inline-flex;
5124
+ }
5125
+
5126
+ .squisq-transition-picker-trigger {
5127
+ display: inline-flex;
5128
+ align-items: center;
5129
+ gap: 5px;
5130
+ padding: 3px 8px;
5131
+ font-size: 12px;
5132
+ font-weight: 500;
5133
+ color: #374151;
5134
+ background: #fff;
5135
+ border: 1px solid #d1d5db;
5136
+ border-radius: 5px;
5137
+ cursor: pointer;
5138
+ white-space: nowrap;
5139
+ transition:
5140
+ background 0.1s,
5141
+ border-color 0.1s;
5142
+ }
5143
+
5144
+ .squisq-transition-picker-trigger:hover {
5145
+ background: #f3f4f6;
5146
+ border-color: #9ca3af;
5147
+ }
5148
+
5149
+ .squisq-transition-picker-trigger--open {
5150
+ background: #f3f4f6;
5151
+ border-color: #6366f1;
5152
+ outline: 2px solid rgba(99, 102, 241, 0.25);
5153
+ outline-offset: -1px;
5154
+ }
5155
+
5156
+ .squisq-transition-picker-trigger-label {
5157
+ color: #6b7280;
5158
+ }
5159
+
5160
+ .squisq-transition-picker-trigger-value {
5161
+ color: #111827;
5162
+ font-weight: 400;
5163
+ max-width: 110px;
5164
+ overflow: hidden;
5165
+ text-overflow: ellipsis;
5166
+ }
5167
+
5168
+ .squisq-transition-picker-trigger svg {
5169
+ color: #9ca3af;
5170
+ flex-shrink: 0;
5171
+ }
5172
+
5173
+ @container squisq-toolbar (max-width: 900px) {
5174
+ .squisq-transition-picker-trigger-label {
5175
+ display: none;
5176
+ }
5177
+ }
5178
+
5179
+ /* Flyout panel — portaled with fixed positioning (component supplies top/left). */
5180
+ .squisq-transition-flyout {
5181
+ /* Three columns of options keep the panel short enough to avoid running off
5182
+ the viewport. `max-height` is set inline by the component to the actual
5183
+ space available at its anchored position; the list scrolls within it. */
5184
+ width: 460px;
5185
+ max-width: calc(100vw - 24px);
5186
+ display: flex;
5187
+ flex-direction: column;
5188
+ background: #fff;
5189
+ border: 1px solid #e5e7eb;
5190
+ border-radius: 10px;
5191
+ box-shadow:
5192
+ 0 8px 24px rgba(0, 0, 0, 0.12),
5193
+ 0 2px 6px rgba(0, 0, 0, 0.08);
5194
+ overflow: hidden;
5195
+ }
5196
+
5197
+ .squisq-transition-flyout-search {
5198
+ padding: 8px;
5199
+ border-bottom: 1px solid #f0f0f0;
5200
+ }
5201
+
5202
+ .squisq-transition-flyout-search-input {
5203
+ width: 100%;
5204
+ padding: 6px 8px;
5205
+ font-size: 13px;
5206
+ color: #111827;
5207
+ border: 1px solid #d1d5db;
5208
+ border-radius: 6px;
5209
+ outline: none;
5210
+ }
5211
+
5212
+ .squisq-transition-flyout-search-input:focus {
5213
+ border-color: #6366f1;
5214
+ }
5215
+
5216
+ .squisq-transition-flyout-list {
5217
+ /* `min-height: 0` lets this flex child shrink below its content so it scrolls
5218
+ within the panel's capped height instead of overflowing it. The search box
5219
+ above and the direction/duration controls below stay pinned. */
5220
+ min-height: 0;
5221
+ overflow-y: auto;
5222
+ padding: 6px 8px;
5223
+ }
5224
+
5225
+ .squisq-transition-flyout-group {
5226
+ margin-top: 6px;
5227
+ }
5228
+
5229
+ .squisq-transition-flyout-group-title {
5230
+ margin: 6px 2px 4px;
5231
+ font-size: 10px;
5232
+ font-weight: 600;
5233
+ letter-spacing: 0.04em;
5234
+ text-transform: uppercase;
5235
+ color: #9ca3af;
5236
+ }
5237
+
5238
+ .squisq-transition-flyout-grid {
5239
+ display: grid;
5240
+ grid-template-columns: repeat(3, 1fr);
5241
+ gap: 4px;
5242
+ }
5243
+
5244
+ .squisq-transition-option {
5245
+ display: block;
5246
+ width: 100%;
5247
+ text-align: left;
5248
+ padding: 6px 8px;
5249
+ font-size: 12px;
5250
+ color: #374151;
5251
+ background: #fff;
5252
+ border: 1px solid #e5e7eb;
5253
+ border-radius: 6px;
5254
+ cursor: pointer;
5255
+ white-space: nowrap;
5256
+ overflow: hidden;
5257
+ text-overflow: ellipsis;
5258
+ }
5259
+
5260
+ .squisq-transition-option:hover {
5261
+ background: #f3f4f6;
5262
+ border-color: #d1d5db;
5263
+ }
5264
+
5265
+ .squisq-transition-option--selected {
5266
+ background: #eef2ff;
5267
+ border-color: #6366f1;
5268
+ color: #4338ca;
5269
+ font-weight: 600;
5270
+ }
5271
+
5272
+ /* On hover, the label plays the real entering transition (the option button
5273
+ clips it with overflow:hidden; perspective gives the 3D transitions depth).
5274
+ The label is inline-block so transforms apply without disturbing layout, and
5275
+ we override `--transition-duration` to keep previews snappy. */
5276
+ .squisq-transition-option {
5277
+ perspective: 220px;
5278
+ }
5279
+
5280
+ .squisq-transition-option-label {
5281
+ display: inline-block;
5282
+ --transition-duration: 0.55s;
5283
+ transform-origin: center;
5284
+ backface-visibility: hidden;
5285
+ }
5286
+
5287
+ @media (prefers-reduced-motion: reduce) {
5288
+ .squisq-transition-option-label {
5289
+ /* Respect reduced-motion: show the label statically, no entrance preview. */
5290
+ animation: none !important;
5291
+ }
5292
+ }
5293
+
5294
+ .squisq-transition-flyout-empty {
5295
+ padding: 12px 8px;
5296
+ font-size: 12px;
5297
+ color: #9ca3af;
5298
+ text-align: center;
5299
+ }
5300
+
5301
+ /* Direction + duration controls, shown below the list once a type is set. */
5302
+ .squisq-transition-flyout-controls {
5303
+ border-top: 1px solid #f0f0f0;
5304
+ padding: 10px 12px;
5305
+ display: flex;
5306
+ flex-direction: column;
5307
+ gap: 10px;
5308
+ }
5309
+
5310
+ .squisq-transition-flyout-control {
5311
+ display: flex;
5312
+ align-items: center;
5313
+ justify-content: space-between;
5314
+ gap: 10px;
5315
+ }
5316
+
5317
+ .squisq-transition-flyout-control-label {
5318
+ font-size: 11px;
5319
+ font-weight: 600;
5320
+ color: #6b7280;
5321
+ }
5322
+
5323
+ .squisq-transition-direction-row {
5324
+ display: inline-flex;
5325
+ gap: 4px;
5326
+ }
5327
+
5328
+ .squisq-transition-direction-button {
5329
+ padding: 4px 8px;
5330
+ font-size: 11px;
5331
+ color: #374151;
5332
+ background: #fff;
5333
+ border: 1px solid #d1d5db;
5334
+ border-radius: 5px;
5335
+ cursor: pointer;
5336
+ }
5337
+
5338
+ .squisq-transition-direction-button:hover {
5339
+ background: #f3f4f6;
5340
+ }
5341
+
5342
+ .squisq-transition-direction-button--selected {
5343
+ background: #eef2ff;
5344
+ border-color: #6366f1;
5345
+ color: #4338ca;
5346
+ font-weight: 600;
5347
+ }
5348
+
5349
+ .squisq-transition-duration-row {
5350
+ display: inline-flex;
5351
+ align-items: center;
5352
+ gap: 6px;
5353
+ }
5354
+
5355
+ .squisq-transition-duration-input {
5356
+ width: 64px;
5357
+ padding: 4px 6px;
5358
+ font-size: 12px;
5359
+ color: #111827;
5360
+ border: 1px solid #d1d5db;
5361
+ border-radius: 5px;
5362
+ outline: none;
5363
+ }
5364
+
5365
+ .squisq-transition-duration-input:focus {
5366
+ border-color: #6366f1;
5367
+ }
5368
+
5369
+ .squisq-transition-duration-unit {
5370
+ font-size: 11px;
5371
+ color: #9ca3af;
5372
+ }
5373
+
5374
+ /* Dark theme */
5375
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-picker-trigger {
5376
+ background: #1f2937;
5377
+ color: #e5e7eb;
5378
+ border-color: #4b5563;
5379
+ }
5380
+
5381
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-picker-trigger:hover {
5382
+ background: #374151;
5383
+ border-color: #6b7280;
5384
+ }
5385
+
5386
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-picker-trigger--open {
5387
+ background: #374151;
5388
+ border-color: #818cf8;
5389
+ }
5390
+
5391
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-picker-trigger-value {
5392
+ color: #f9fafb;
5393
+ }
5394
+
5395
+ /* Dark styling for the portaled flyout — keyed off the editor shell the same
5396
+ way the template gallery popover is (see `.squisq-template-gallery` above). */
5397
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-flyout {
5398
+ background: #1f2937;
5399
+ border-color: #374151;
5400
+ box-shadow:
5401
+ 0 8px 24px rgba(0, 0, 0, 0.4),
5402
+ 0 2px 6px rgba(0, 0, 0, 0.2);
5403
+ }
5404
+
5405
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-flyout-search,
5406
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-flyout-controls {
5407
+ border-color: #374151;
5408
+ }
5409
+
5410
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-flyout-search-input,
5411
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-duration-input {
5412
+ background: #111827;
5413
+ color: #f9fafb;
5414
+ border-color: #4b5563;
5415
+ }
5416
+
5417
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-option,
5418
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-direction-button {
5419
+ background: #1f2937;
5420
+ color: #e5e7eb;
5421
+ border-color: #4b5563;
5422
+ }
5423
+
5424
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-option:hover,
5425
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-direction-button:hover {
5426
+ background: #374151;
5427
+ }
5428
+
5429
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-option--selected,
5430
+ .squisq-editor-shell[data-theme='dark'] .squisq-transition-direction-button--selected {
5431
+ background: #3730a3;
5432
+ border-color: #818cf8;
5433
+ color: #e0e7ff;
5434
+ }