@morphika/andami 0.5.1 → 0.5.3

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 (147) hide show
  1. package/README.md +27 -2
  2. package/app/admin/assets/page.tsx +6 -6
  3. package/app/admin/database/page.tsx +302 -302
  4. package/app/admin/error.tsx +53 -53
  5. package/app/admin/layout.tsx +332 -320
  6. package/app/admin/navigation/page.tsx +255 -255
  7. package/app/admin/pages/[slug]/page.tsx +44 -27
  8. package/app/admin/pages/page.tsx +24 -19
  9. package/app/admin/projects/page.tsx +30 -21
  10. package/app/admin/setup/page.tsx +1 -1
  11. package/app/admin/styles/page.tsx +1 -1
  12. package/app/api/admin/assets/register/route.ts +51 -14
  13. package/app/api/admin/assets/registry/route.ts +4 -1
  14. package/app/api/admin/assets/relink/confirm/route.ts +4 -1
  15. package/app/api/admin/assets/relink/route.ts +4 -1
  16. package/app/api/admin/assets/scan/route.ts +4 -1
  17. package/app/api/admin/backups/restore-data/route.ts +4 -1
  18. package/app/api/admin/r2/connect/route.ts +4 -1
  19. package/app/api/admin/r2/delete/route.ts +4 -1
  20. package/app/api/admin/r2/rename/route.ts +4 -1
  21. package/app/api/admin/r2/upload-url/route.ts +4 -1
  22. package/app/api/admin/revalidate/route.ts +4 -1
  23. package/app/api/admin/storage/switch/route.ts +4 -1
  24. package/app/api/custom-sections/[id]/route.ts +5 -6
  25. package/components/admin/MetadataEditor.tsx +6 -6
  26. package/components/admin/PublishToggle.tsx +2 -2
  27. package/components/admin/nav-builder/NavBuilder.tsx +1 -1
  28. package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
  29. package/components/admin/nav-builder/NavGridCell.tsx +48 -48
  30. package/components/admin/nav-builder/NavGridItem.tsx +8 -6
  31. package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
  32. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
  33. package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
  34. package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
  35. package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
  36. package/components/admin/nav-builder/NavSettingsFields.tsx +518 -514
  37. package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
  38. package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
  39. package/components/admin/setup-wizard/DoneStep.tsx +1 -1
  40. package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
  41. package/components/admin/setup-wizard/StorageStep.tsx +2 -2
  42. package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
  43. package/components/admin/styles/ColorsEditor.tsx +9 -8
  44. package/components/admin/styles/FontsEditor.tsx +9 -7
  45. package/components/admin/styles/GridLayoutEditor.tsx +9 -9
  46. package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
  47. package/components/admin/styles/TypographyEditor.tsx +6 -6
  48. package/components/admin/styles/shared.tsx +68 -68
  49. package/components/blocks/AudioBlockRenderer.tsx +286 -286
  50. package/components/blocks/CoverSectionRenderer.tsx +7 -1
  51. package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
  52. package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
  53. package/components/blocks/SectionV2Renderer.tsx +8 -1
  54. package/components/builder/BlockCardIcons.tsx +316 -316
  55. package/components/builder/BlockTypePicker.tsx +1 -1
  56. package/components/builder/BubbleIcons.tsx +104 -0
  57. package/components/builder/BuilderCanvas.tsx +2 -0
  58. package/components/builder/CanvasMinimap.tsx +66 -49
  59. package/components/builder/CanvasToolbar.tsx +31 -41
  60. package/components/builder/CoverSectionCanvas.tsx +363 -363
  61. package/components/builder/DeviceFrame.tsx +1 -1
  62. package/components/builder/DndWrapper.tsx +3 -3
  63. package/components/builder/InsertionLines.tsx +1 -1
  64. package/components/builder/SectionCardIcons.tsx +421 -320
  65. package/components/builder/SectionEditorBar.tsx +5 -3
  66. package/components/builder/SectionTypePicker.tsx +7 -5
  67. package/components/builder/SectionV2Canvas.tsx +1 -1
  68. package/components/builder/SectionV2Column.tsx +82 -68
  69. package/components/builder/SettingsPanel.tsx +21 -17
  70. package/components/builder/SortableBlock.tsx +93 -73
  71. package/components/builder/SortableRow.tsx +33 -35
  72. package/components/builder/VirtualAssetGrid.tsx +10 -4
  73. package/components/builder/asset-browser/R2BrowserContent.tsx +18 -14
  74. package/components/builder/blockStyles.tsx +192 -185
  75. package/components/builder/color-picker/AlphaSlider.tsx +141 -141
  76. package/components/builder/color-picker/ColorInputs.tsx +105 -105
  77. package/components/builder/color-picker/EyedropperButton.tsx +75 -74
  78. package/components/builder/color-picker/HueSlider.tsx +124 -124
  79. package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
  80. package/components/builder/color-picker/SwatchBar.tsx +98 -93
  81. package/components/builder/color-picker/UnifiedColorPicker.tsx +11 -6
  82. package/components/builder/editors/AudioBlockEditor.tsx +242 -242
  83. package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -360
  84. package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
  85. package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
  86. package/components/builder/editors/HoverEffectPicker.tsx +2 -2
  87. package/components/builder/editors/ImageBlockEditor.tsx +2 -2
  88. package/components/builder/editors/ImageGridBlockEditor.tsx +8 -6
  89. package/components/builder/editors/MarqueeBlockEditor.tsx +622 -0
  90. package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
  91. package/components/builder/editors/ProjectGridEditor.tsx +21 -16
  92. package/components/builder/editors/SpacerBlockEditor.tsx +29 -27
  93. package/components/builder/editors/StaggerSettings.tsx +109 -109
  94. package/components/builder/editors/TextBlockEditor.tsx +22 -17
  95. package/components/builder/editors/TextStylePicker.tsx +1 -1
  96. package/components/builder/editors/VideoBlockEditor.tsx +2 -2
  97. package/components/builder/editors/index.ts +11 -10
  98. package/components/builder/editors/shared.tsx +10 -8
  99. package/components/builder/live-preview/LiveAudioPreview.tsx +120 -120
  100. package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +1 -1
  101. package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
  102. package/components/builder/live-preview/LiveImagePreview.tsx +4 -2
  103. package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
  104. package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
  105. package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
  106. package/components/builder/live-preview/ProjectCardWrapper.tsx +293 -291
  107. package/components/builder/live-preview/RichTextBubbleMenu.tsx +10 -6
  108. package/components/builder/live-preview/shared.tsx +5 -2
  109. package/components/builder/settings-panel/AnimationTab.tsx +138 -138
  110. package/components/builder/settings-panel/BlockLayoutTab.tsx +11 -9
  111. package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
  112. package/components/builder/settings-panel/ColumnV2LayoutTab.tsx +242 -0
  113. package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
  114. package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
  115. package/components/builder/settings-panel/CoverSectionSettings.tsx +337 -335
  116. package/components/builder/settings-panel/PageSettings.tsx +3 -3
  117. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
  118. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
  119. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
  120. package/components/builder/settings-panel/SectionV2Settings.tsx +25 -20
  121. package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
  122. package/components/builder/settings-panel/index.ts +1 -0
  123. package/lib/animation/enter-types.ts +1 -0
  124. package/lib/animation/hover-effect-presets.ts +210 -210
  125. package/lib/animation/hover-effect-types.ts +1 -0
  126. package/lib/builder/block-registrations.ts +468 -417
  127. package/lib/builder/constants.ts +111 -111
  128. package/lib/builder/serializer/normalizers.ts +14 -0
  129. package/lib/builder/serializer/serializers.ts +27 -0
  130. package/lib/builder/store-sections.ts +23 -2
  131. package/lib/builder/types-slices.ts +428 -414
  132. package/lib/builder/types.ts +4 -1
  133. package/lib/config/index.ts +27 -27
  134. package/lib/sanity/queries.ts +48 -0
  135. package/lib/sanity/types.ts +112 -1
  136. package/lib/version.ts +1 -1
  137. package/package.json +7 -5
  138. package/sanity/schemas/blocks/audioBlock.ts +69 -69
  139. package/sanity/schemas/blocks/index.ts +12 -11
  140. package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
  141. package/sanity/schemas/index.ts +120 -117
  142. package/sanity/schemas/objects/coverSection.ts +32 -0
  143. package/sanity/schemas/objects/parallaxSlide.ts +32 -0
  144. package/sanity/schemas/pageSectionV2.ts +32 -0
  145. package/styles/admin.css +85 -85
  146. package/styles/animations.css +237 -237
  147. package/styles/base.css +114 -114
@@ -75,7 +75,7 @@ function BlockCard({
75
75
  aria-hidden="true"
76
76
  className="absolute inset-0 rounded-2xl pointer-events-none"
77
77
  style={{
78
- boxShadow: isHovered ? "inset 0 0 0 2px #4794E2" : "inset 0 0 0 2px transparent",
78
+ boxShadow: isHovered ? "inset 0 0 0 2px #3580f9" : "inset 0 0 0 2px transparent",
79
79
  transition: "box-shadow 160ms ease",
80
80
  }}
81
81
  />
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ /**
4
+ * BubbleIcons — shared icon set + tooltip wrapper for the block & column bubbles
5
+ * floating at the top-outside of each entity in the visual builder.
6
+ *
7
+ * Icons come from the Tabler set, re-exported as filled <path> SVGs so they
8
+ * scale cleanly without stroke-width inconsistencies. Viewbox is 24×24; the
9
+ * default display size is 12px (pill-scale).
10
+ *
11
+ * Tooltip styling mirrors the admin sidebar's group-hover tooltip
12
+ * (app/admin/layout.tsx) — dark pill, 150ms fade + slide-in.
13
+ */
14
+
15
+ import type { ReactNode } from "react";
16
+
17
+ // ============================================
18
+ // Icons (filled, 24×24 viewBox)
19
+ // ============================================
20
+
21
+ interface IconProps {
22
+ size?: number;
23
+ color?: string;
24
+ }
25
+
26
+ export function DragDropIcon({ size = 12, color = "currentColor" }: IconProps) {
27
+ return (
28
+ <svg width={size} height={size} viewBox="0 0 24 24" fill={color} aria-hidden="true">
29
+ <path d="M11,18H9c-0.6,0-1-0.4-1-1V9c0-0.6,0.4-1,1-1h8c0.6,0,1,0.4,1,1v2c0,0.6,0.4,1,1,1s1-0.4,1-1V9c0-1.7-1.3-3-3-3H9 C7.3,6,6,7.3,6,9v8c0,1.7,1.3,3,3,3h2c0.6,0,1-0.4,1-1S11.6,18,11,18z" />
30
+ <path d="M22.3,15.1l-9-3c-0.4-0.1-0.8,0-1,0.2c-0.3,0.3-0.4,0.7-0.2,1l3,9c0.1,0.4,0.5,0.7,0.9,0.7c0,0,0,0,0.1,0 c0.4,0,0.7-0.2,0.9-0.6l1.9-3.7l3.7-1.9c0.4-0.2,0.6-0.6,0.5-1C23,15.5,22.7,15.2,22.3,15.1z M17.6,17.1c-0.2,0.1-0.4,0.3-0.4,0.4 l-0.9,1.8l-1.6-4.8l4.8,1.6L17.6,17.1z" />
31
+ <path d="M3,2C2.4,2,2,2.5,2,3s0.4,1,1,1s1-0.4,1-1v0C4,2.4,3.6,2,3,2z" />
32
+ <path d="M7,4c0.6,0,1-0.4,1-1v0c0-0.6-0.4-1-1-1S6,2.5,6,3S6.4,4,7,4z" />
33
+ <path d="M11,4c0.6,0,1-0.4,1-1v0c0-0.6-0.4-1-1-1s-1,0.5-1,1S10.4,4,11,4z" />
34
+ <path d="M15,4c0.6,0,1-0.4,1-1v0c0-0.6-0.4-1-1-1s-1,0.5-1,1S14.4,4,15,4z" />
35
+ <path d="M3,6C2.4,6,2,6.5,2,7s0.4,1,1,1s1-0.4,1-1v0C4,6.4,3.6,6,3,6z" />
36
+ <path d="M3,10c-0.6,0-1,0.5-1,1s0.4,1,1,1s1-0.4,1-1v0C4,10.4,3.6,10,3,10z" />
37
+ <path d="M3,14c-0.6,0-1,0.5-1,1s0.4,1,1,1s1-0.4,1-1v0C4,14.4,3.6,14,3,14z" />
38
+ </svg>
39
+ );
40
+ }
41
+
42
+ export function CopyIcon({ size = 12, color = "currentColor" }: IconProps) {
43
+ return (
44
+ <svg width={size} height={size} viewBox="0 0 24 24" fill={color} aria-hidden="true">
45
+ <path d="M18.3,22H9.7c-2,0-3.7-1.6-3.7-3.7V9.7C6,7.6,7.6,6,9.7,6h8.7c2,0,3.7,1.6,3.7,3.7v8.7C22,20.4,20.4,22,18.3,22z M9.7,8 C8.7,8,8,8.7,8,9.7v8.7C8,19.3,8.7,20,9.7,20h8.7c0.9,0,1.7-0.7,1.7-1.7V9.7C20,8.7,19.3,8,18.3,8H9.7z" />
46
+ <path d="M4,17.7c-0.2,0-0.3,0-0.5-0.1C2.6,17.1,2,16.1,2,15V5c0-1.7,1.3-3,3-3h10c1.5,0,2.2,1.1,2.4,1.5C17.6,4,17.5,4.6,17,4.9 c-0.5,0.3-1.1,0.1-1.4-0.4C15.4,4,15.2,4,15,4H5C4.4,4,4,4.4,4,5v10c0,0.4,0.2,0.7,0.5,0.9c0.5,0.3,0.6,0.9,0.4,1.4 C4.7,17.6,4.4,17.7,4,17.7z" />
47
+ </svg>
48
+ );
49
+ }
50
+
51
+ export function ArrowDownIcon({ size = 12, color = "currentColor" }: IconProps) {
52
+ return (
53
+ <svg width={size} height={size} viewBox="0 0 24 24" fill={color} aria-hidden="true">
54
+ <path d="M16.7,14.3c-0.4-0.4-1-0.4-1.4,0L13,16.6V5c0-0.6-0.4-1-1-1s-1,0.4-1,1v11.6l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4 l4,4c0.1,0.1,0.2,0.2,0.3,0.2C11.7,20,11.9,20,12,20s0.3,0,0.4-0.1c0.1-0.1,0.2-0.1,0.3-0.2l4-4C17.1,15.3,17.1,14.7,16.7,14.3z" />
55
+ </svg>
56
+ );
57
+ }
58
+
59
+ export function ArrowUpIcon({ size = 12, color = "currentColor" }: IconProps) {
60
+ return (
61
+ <svg width={size} height={size} viewBox="0 0 24 24" fill={color} aria-hidden="true" style={{ transform: "rotate(180deg)" }}>
62
+ <path d="M16.7,14.3c-0.4-0.4-1-0.4-1.4,0L13,16.6V5c0-0.6-0.4-1-1-1s-1,0.4-1,1v11.6l-2.3-2.3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4 l4,4c0.1,0.1,0.2,0.2,0.3,0.2C11.7,20,11.9,20,12,20s0.3,0,0.4-0.1c0.1-0.1,0.2-0.1,0.3-0.2l4-4C17.1,15.3,17.1,14.7,16.7,14.3z" />
63
+ </svg>
64
+ );
65
+ }
66
+
67
+ export function CloseIcon({ size = 12, color = "currentColor" }: IconProps) {
68
+ return (
69
+ <svg width={size} height={size} viewBox="0 0 24 24" fill={color} aria-hidden="true">
70
+ <path d="M16.2,4H7.6c-2,0-3.7,1.6-3.7,3.7v8.7c0,2,1.6,3.7,3.7,3.7h8.7c2,0,3.7-1.6,3.7-3.7V7.7C19.9,5.7,18.3,4,16.2,4z M15,13.8 c0.4,0.4,0.4,1,0,1.4l-0.1,0.1c-0.4,0.3-1,0.3-1.3-0.1l-1.8-1.8L10,15.2c-0.4,0.4-1,0.4-1.4,0l-0.1-0.1c-0.3-0.4-0.3-1,0.1-1.3 l1.8-1.8l-1.7-1.8c-0.4-0.4-0.4-1,0-1.4l0.1-0.1c0.4-0.3,1-0.3,1.3,0.1l1.7,1.8l1.8-1.8c0.4-0.4,1-0.4,1.4,0l0.1,0.1 c0.3,0.4,0.3,1-0.1,1.3L13.3,12L15,13.8z" />
71
+ </svg>
72
+ );
73
+ }
74
+
75
+ // ============================================
76
+ // Tooltip — mirrors the admin sidebar pattern (CSS-only, group-hover).
77
+ // Positioned below the trigger; parent needs `group relative`.
78
+ // ============================================
79
+
80
+ export function BubbleTooltip({ children }: { children: ReactNode }) {
81
+ return (
82
+ <span
83
+ role="tooltip"
84
+ className="pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 whitespace-nowrap rounded-md border border-white/10 bg-[#2a2d33] px-2.5 py-1.5 text-[11px] font-medium text-white shadow-lg opacity-0 transition-[opacity,margin] duration-150 ease-out group-hover/bb:opacity-100 group-hover/bb:mt-3"
85
+ style={{ transform: "translateX(-50%)" }}
86
+ >
87
+ {children}
88
+ </span>
89
+ );
90
+ }
91
+
92
+ /** Same styling as BubbleTooltip but positioned ABOVE the trigger.
93
+ * For bottom-anchored toolbars (e.g. the floating canvas toolbar). */
94
+ export function BubbleTooltipAbove({ children }: { children: ReactNode }) {
95
+ return (
96
+ <span
97
+ role="tooltip"
98
+ className="pointer-events-none absolute left-1/2 bottom-full z-50 mb-1.5 whitespace-nowrap rounded-md border border-white/10 bg-[#2a2d33] px-2.5 py-1.5 text-[11px] font-medium text-white shadow-lg opacity-0 transition-[opacity,margin] duration-150 ease-out group-hover/bb:opacity-100 group-hover/bb:mb-3"
99
+ style={{ transform: "translateX(-50%)" }}
100
+ >
101
+ {children}
102
+ </span>
103
+ );
104
+ }
@@ -69,6 +69,7 @@ export default function BuilderCanvas({ children }: BuilderCanvasProps) {
69
69
  const setActiveViewport = useBuilderStore((s) => s.setActiveViewport);
70
70
  const bgColor = useBuilderStore((s) => s.pageSettings.background_color);
71
71
  const pageSlug = useBuilderStore((s) => s.pageSlug);
72
+ const clearSelection = useBuilderStore((s) => s.clearSelection);
72
73
 
73
74
  // Refs for stable wheel/pan handlers — avoids re-creating callbacks on every zoom/pan change
74
75
  const zoomRef = useRef(zoom);
@@ -288,6 +289,7 @@ export default function BuilderCanvas({ children }: BuilderCanvasProps) {
288
289
  onMouseMove={handleMouseMove}
289
290
  onMouseUp={handleMouseUp}
290
291
  onMouseLeave={handleMouseUp}
292
+ onClick={clearSelection}
291
293
  >
292
294
  {/* Dot grid background */}
293
295
  <div
@@ -3,6 +3,7 @@
3
3
  import { useCallback, useMemo, useRef, useState } from "react";
4
4
  import { useBuilderStore } from "../../lib/builder/store";
5
5
  import { DEVICE_WIDTHS } from "../../lib/builder/types";
6
+ import { BubbleTooltipAbove } from "./BubbleIcons";
6
7
 
7
8
  // ============================================
8
9
  // CanvasMinimap — Shows viewport position in canvas
@@ -121,80 +122,96 @@ export default function CanvasMinimap({
121
122
  return (
122
123
  <button
123
124
  onClick={() => setIsCollapsed(false)}
124
- className="absolute bottom-6 right-4 z-40 w-8 h-8 flex items-center justify-center rounded-lg bg-[#1a1a1a]/80 text-white/60 hover:text-white hover:bg-[#1a1a1a] transition-colors shadow-lg backdrop-blur-sm"
125
- title="Show minimap"
125
+ className="group/bb absolute bottom-6 right-4 z-40 w-8 h-8 flex items-center justify-center rounded-lg bg-[#1a1a1a]/80 text-white/60 hover:text-white hover:bg-[#1a1a1a] transition-colors shadow-lg backdrop-blur-sm"
126
126
  aria-label="Show minimap"
127
127
  >
128
128
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
129
129
  <rect x="1" y="1" width="12" height="12" rx="1.5" stroke="currentColor" strokeWidth="1.2" />
130
130
  <rect x="3" y="3" width="4" height="3" rx="0.5" stroke="currentColor" strokeWidth="0.8" opacity="0.6" />
131
131
  </svg>
132
+ <BubbleTooltipAbove>Show minimap</BubbleTooltipAbove>
132
133
  </button>
133
134
  );
134
135
  }
135
136
 
136
137
  return (
137
138
  <div
138
- className="absolute bottom-6 right-4 z-40 rounded-lg overflow-hidden shadow-lg backdrop-blur-sm"
139
+ className="absolute bottom-6 right-4 z-40 rounded-lg overflow-hidden shadow-xl backdrop-blur-sm"
139
140
  style={{
140
141
  width: MINIMAP_WIDTH,
141
- height: MINIMAP_HEIGHT,
142
- backgroundColor: "rgba(26, 26, 26, 0.85)",
143
- border: "1px solid rgba(255, 255, 255, 0.1)",
142
+ backgroundColor: "rgba(26, 26, 26, 0.92)",
143
+ border: "1px solid rgba(255, 255, 255, 0.08)",
144
144
  userSelect: "none",
145
145
  }}
146
- onMouseDown={handleMinimapMouseDown}
147
- onMouseMove={handleMouseMove}
148
- onMouseUp={handleMouseUp}
149
- onMouseLeave={handleMouseUp}
150
146
  >
151
- {/* Close/collapse button */}
152
- <button
153
- onClick={(e) => {
154
- e.stopPropagation();
155
- setIsCollapsed(true);
147
+ {/* Header — label + close button. Matches the toolbar's dark pill aesthetic. */}
148
+ <div
149
+ className="flex items-center justify-between px-2 py-1"
150
+ style={{
151
+ borderBottom: "1px solid rgba(255, 255, 255, 0.06)",
152
+ background: "rgba(255, 255, 255, 0.02)",
156
153
  }}
157
- className="absolute top-1 right-1 z-50 w-4 h-4 flex items-center justify-center rounded text-white/40 hover:text-white/80 transition-colors"
158
- title="Hide minimap"
159
- aria-label="Hide minimap"
160
154
  >
161
- <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
162
- <path d="M1 1L7 7M7 1L1 7" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
163
- </svg>
164
- </button>
165
-
166
- {/* Device frame outlines */}
167
- {frames.map((f, i) => (
155
+ <span className="text-[9px] font-semibold tracking-[0.08em] uppercase text-white/45">
156
+ Overview
157
+ </span>
158
+ <button
159
+ onClick={() => setIsCollapsed(true)}
160
+ className="group/bb relative w-4 h-4 flex items-center justify-center rounded text-white/40 hover:text-white/90 hover:bg-white/10 transition-colors"
161
+ aria-label="Hide minimap"
162
+ >
163
+ <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
164
+ <path d="M1 1L7 7M7 1L1 7" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
165
+ </svg>
166
+ <BubbleTooltipAbove>Hide minimap</BubbleTooltipAbove>
167
+ </button>
168
+ </div>
169
+
170
+ {/* Content area — all pan/drag interactions live here (not the header). */}
171
+ <div
172
+ className="relative"
173
+ style={{
174
+ width: MINIMAP_WIDTH,
175
+ height: MINIMAP_HEIGHT,
176
+ }}
177
+ onMouseDown={handleMinimapMouseDown}
178
+ onMouseMove={handleMouseMove}
179
+ onMouseUp={handleMouseUp}
180
+ onMouseLeave={handleMouseUp}
181
+ >
182
+ {/* Device frame outlines */}
183
+ {frames.map((f, i) => (
184
+ <div
185
+ key={i}
186
+ className="absolute"
187
+ style={{
188
+ left: f.x,
189
+ top: 8 * scale,
190
+ width: f.w,
191
+ height: (contentEstimateHeight - 40) * scale,
192
+ border: `1px solid ${f.active ? "rgba(53, 128, 249, 0.6)" : "rgba(255, 255, 255, 0.15)"}`,
193
+ borderRadius: 2,
194
+ backgroundColor: f.active ? "rgba(53, 128, 249, 0.08)" : "rgba(255, 255, 255, 0.03)",
195
+ }}
196
+ />
197
+ ))}
198
+
199
+ {/* Viewport indicator */}
168
200
  <div
169
- key={i}
170
201
  className="absolute"
171
202
  style={{
172
- left: f.x,
173
- top: 8 * scale,
174
- width: f.w,
175
- height: (contentEstimateHeight - 40) * scale,
176
- border: `1px solid ${f.active ? "rgba(7, 107, 255, 0.6)" : "rgba(255, 255, 255, 0.15)"}`,
203
+ left: Math.max(0, viewX),
204
+ top: Math.max(0, viewY),
205
+ width: Math.min(viewW, MINIMAP_WIDTH),
206
+ height: Math.min(viewH, MINIMAP_HEIGHT),
207
+ border: "1.5px solid rgba(237, 56, 33, 0.7)",
177
208
  borderRadius: 2,
178
- backgroundColor: f.active ? "rgba(7, 107, 255, 0.08)" : "rgba(255, 255, 255, 0.03)",
209
+ backgroundColor: "rgba(237, 56, 33, 0.06)",
210
+ cursor: isDragging ? "grabbing" : "grab",
211
+ pointerEvents: "none",
179
212
  }}
180
213
  />
181
- ))}
182
-
183
- {/* Viewport indicator */}
184
- <div
185
- className="absolute"
186
- style={{
187
- left: Math.max(0, viewX),
188
- top: Math.max(0, viewY),
189
- width: Math.min(viewW, MINIMAP_WIDTH),
190
- height: Math.min(viewH, MINIMAP_HEIGHT),
191
- border: "1.5px solid rgba(237, 56, 33, 0.7)",
192
- borderRadius: 2,
193
- backgroundColor: "rgba(237, 56, 33, 0.06)",
194
- cursor: isDragging ? "grabbing" : "grab",
195
- pointerEvents: "none",
196
- }}
197
- />
214
+ </div>
198
215
  </div>
199
216
  );
200
217
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useRef, useState, useEffect, useCallback } from "react";
4
4
  import { useBuilderStore } from "../../lib/builder/store";
5
+ import { BubbleTooltipAbove } from "./BubbleIcons";
5
6
 
6
7
  // ============================================
7
8
  // CanvasToolbar — Floating mini toolbar
@@ -72,66 +73,68 @@ export default function CanvasToolbar({ viewportRef, onAnimatedAction }: CanvasT
72
73
  const zoomPercent = Math.round(zoom * 100);
73
74
 
74
75
  return (
75
- <div className="absolute bottom-6 left-1/2 -translate-x-1/2 z-40 flex items-center gap-px rounded-full bg-[#1a1a1a] shadow-xl px-1 py-1"
76
+ <div className="absolute bottom-6 left-1/2 -translate-x-1/2 z-40 flex items-center gap-0.5 rounded-full bg-[#1a1a1a] shadow-xl px-1.5 py-1.5"
76
77
  style={{ userSelect: "none" }}
77
78
  >
78
- {/* Select tool — Figma-style filled cursor */}
79
+ {/* Select tool — Figma-style filled cursor.
80
+ Active state uses the app's primary blue so selection signals
81
+ match the rest of the builder (block/column accents). */}
79
82
  <button
80
83
  onClick={() => setCanvasTool("select")}
81
- className={`flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
84
+ className={`group/bb relative flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
82
85
  tool === "select"
83
- ? "bg-white/15 text-white"
86
+ ? "bg-[#3580f9] text-white"
84
87
  : "text-neutral-400 hover:text-white hover:bg-white/10"
85
88
  }`}
86
- title="Select tool (V)"
87
89
  aria-label="Select tool"
88
90
  >
89
91
  <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round">
90
92
  <path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z" />
91
93
  </svg>
94
+ <BubbleTooltipAbove>Select <span className="ml-1 text-white/55">V</span></BubbleTooltipAbove>
92
95
  </button>
93
96
 
94
- {/* Hand tool — Figma-style */}
97
+ {/* Hand tool — tabler hand-stop (outline) */}
95
98
  <button
96
99
  onClick={() => setCanvasTool("hand")}
97
- className={`flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
100
+ className={`group/bb relative flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
98
101
  tool === "hand"
99
- ? "bg-white/15 text-white"
102
+ ? "bg-[#3580f9] text-white"
100
103
  : "text-neutral-400 hover:text-white hover:bg-white/10"
101
104
  }`}
102
- title="Hand tool (H)"
103
105
  aria-label="Hand tool"
104
106
  >
105
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
106
- <path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2" />
107
- <path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" />
108
- <path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8" />
109
- <path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" />
107
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
108
+ <path d="M8 13v-7.5a1.5 1.5 0 0 1 3 0v6.5" />
109
+ <path d="M11 5.5v-2a1.5 1.5 0 1 1 3 0v8.5" />
110
+ <path d="M14 5.5a1.5 1.5 0 0 1 3 0v6.5" />
111
+ <path d="M17 7.5a1.5 1.5 0 0 1 3 0v8.5a6 6 0 0 1 -6 6h-2h.208a6 6 0 0 1 -5.012 -2.7a69.74 69.74 0 0 1 -.196 -.3c-.312 -.479 -1.407 -2.388 -3.286 -5.728a1.5 1.5 0 0 1 .536 -2.022a1.867 1.867 0 0 1 2.28 .28l1.47 1.47" />
110
112
  </svg>
113
+ <BubbleTooltipAbove>Hand <span className="ml-1 text-white/55">H</span></BubbleTooltipAbove>
111
114
  </button>
112
115
 
113
116
  {/* Divider */}
114
- <div className="w-px h-5 bg-white/20 mx-1" />
117
+ <div className="w-px h-5 bg-white/15 mx-2" />
115
118
 
116
119
  {/* Zoom out */}
117
120
  <button
118
121
  onClick={handleZoomOut}
119
- className="flex items-center justify-center w-7 h-8 text-neutral-400 hover:text-white transition-colors text-sm"
120
- title="Zoom out (Ctrl + −)"
122
+ className="group/bb relative flex items-center justify-center w-7 h-8 text-neutral-400 hover:text-white transition-colors text-sm"
121
123
  aria-label="Zoom out"
122
124
  >
123
125
 
126
+ <BubbleTooltipAbove>Zoom out <span className="ml-1 text-white/55">Ctrl −</span></BubbleTooltipAbove>
124
127
  </button>
125
128
 
126
129
  {/* Zoom percentage (clickable dropdown) */}
127
130
  <div className="relative" ref={presetsRef}>
128
131
  <button
129
132
  onClick={() => setShowPresets((v) => !v)}
130
- className="flex items-center justify-center min-w-[48px] h-8 text-white text-[11px] font-medium hover:bg-white/10 rounded-md transition-colors px-1"
131
- title="Click to select zoom level"
133
+ className="group/bb relative flex items-center justify-center min-w-[48px] h-8 text-white text-[11px] font-medium hover:bg-white/10 rounded-md transition-colors px-1"
132
134
  aria-label="Select zoom level"
133
135
  >
134
136
  {zoomPercent}%
137
+ <BubbleTooltipAbove>Zoom level</BubbleTooltipAbove>
135
138
  </button>
136
139
 
137
140
  {showPresets && (
@@ -156,40 +159,27 @@ export default function CanvasToolbar({ viewportRef, onAnimatedAction }: CanvasT
156
159
  {/* Zoom in */}
157
160
  <button
158
161
  onClick={handleZoomIn}
159
- className="flex items-center justify-center w-7 h-8 text-neutral-400 hover:text-white transition-colors text-sm"
160
- title="Zoom in (Ctrl + +)"
162
+ className="group/bb relative flex items-center justify-center w-7 h-8 text-neutral-400 hover:text-white transition-colors text-sm"
161
163
  aria-label="Zoom in"
162
164
  >
163
165
  +
166
+ <BubbleTooltipAbove>Zoom in <span className="ml-1 text-white/55">Ctrl +</span></BubbleTooltipAbove>
164
167
  </button>
165
168
 
166
169
  {/* Divider */}
167
- <div className="w-px h-5 bg-white/20 mx-1" />
170
+ <div className="w-px h-5 bg-white/15 mx-2" />
168
171
 
169
- {/* Fit */}
172
+ {/* Fit — tabler zoom (magnifying glass, outline) */}
170
173
  <button
171
174
  onClick={handleFit}
172
- className="flex items-center justify-center w-8 h-8 rounded-full text-neutral-400 hover:text-white hover:bg-white/10 transition-colors"
173
- title="Zoom to fit (Ctrl + 0)"
175
+ className="group/bb relative flex items-center justify-center w-8 h-8 rounded-full text-neutral-400 hover:text-white hover:bg-white/10 transition-colors"
174
176
  aria-label="Zoom to fit"
175
177
  >
176
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
177
- <rect
178
- x="2"
179
- y="2"
180
- width="10"
181
- height="10"
182
- rx="1.5"
183
- stroke="currentColor"
184
- strokeWidth="1.5"
185
- />
186
- <path
187
- d="M5 2V0M9 2V0M5 14V12M9 14V12M0 5H2M0 9H2M12 5H14M12 9H14"
188
- stroke="currentColor"
189
- strokeWidth="1"
190
- opacity="0.5"
191
- />
178
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
179
+ <path d="M3 10a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" />
180
+ <path d="M21 21l-6 -6" />
192
181
  </svg>
182
+ <BubbleTooltipAbove>Zoom to fit <span className="ml-1 text-white/55">Ctrl 0</span></BubbleTooltipAbove>
193
183
  </button>
194
184
  </div>
195
185
  );