@morphika/andami 0.2.0 → 0.2.2

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.
@@ -19,7 +19,7 @@ import {
19
19
  verticalListSortingStrategy,
20
20
  } from "@dnd-kit/sortable";
21
21
  import type { Page, PageSectionV2, ParallaxGroup, SectionColumn, CustomSectionInstance, CustomSectionListItem } from "../../../../lib/sanity/types";
22
- import { isPageSectionV2, isCustomSectionInstance, isParallexGroup } from "../../../../lib/sanity/types";
22
+ import { isPageSectionV2, isCustomSectionInstance, isParallaxGroup } from "../../../../lib/sanity/types";
23
23
  import SectionEditorBar from "../../../../components/builder/SectionEditorBar";
24
24
  import CustomSectionInstanceCard from "../../../../components/builder/CustomSectionInstanceCard";
25
25
  import { ColumnDragProvider } from "../../../../components/builder/ColumnDragContext";
@@ -666,7 +666,7 @@ export default function PageEditorPage() {
666
666
  {store.rows.map((item, rowIndex) => {
667
667
  const isV2Section = isPageSectionV2(item);
668
668
  const isInstance = isCustomSectionInstance(item);
669
- const isParallax = isParallexGroup(item);
669
+ const isParallax = isParallaxGroup(item);
670
670
  const v2Section = isV2Section ? (item as PageSectionV2) : null;
671
671
 
672
672
  // Custom Section Instance — rendered directly without SortableRow chrome
@@ -783,7 +783,13 @@ export default function PageEditorPage() {
783
783
  e.stopPropagation();
784
784
  setShowSectionPicker(true);
785
785
  }}
786
- className="w-full rounded-xl py-3 text-xs font-medium text-white bg-[#93278f] hover:bg-[#7a1f76] transition-colors shadow-sm"
786
+ className="w-full rounded-xl py-3 text-xs font-medium transition-colors"
787
+ style={{
788
+ background: "linear-gradient(170deg, rgba(38,38,48,0.95) 0%, rgba(28,28,36,0.97) 100%)",
789
+ color: "rgba(200,160,220,0.8)",
790
+ boxShadow: "0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.04)",
791
+ border: "1px solid rgba(255,255,255,0.06)",
792
+ }}
787
793
  >
788
794
  + Add Section
789
795
  </button>
@@ -87,7 +87,7 @@ export default function TextBlockRenderer({ block }: { block: TextBlock }) {
87
87
  return (
88
88
  <div
89
89
  className={`${className} space-y-[0.75em]`}
90
- style={{ overflowWrap: "break-word", wordBreak: "normal", minWidth: 0, ...style }}
90
+ style={style}
91
91
  >
92
92
  <PortableText value={block.text} />
93
93
  </div>
@@ -20,11 +20,11 @@ import { memo, useMemo, useState, useEffect } from "react";
20
20
  import { useBuilderStore } from "../../lib/builder/store";
21
21
  import type { DeviceViewport } from "../../lib/builder/types";
22
22
  import type { ContentItem, PageSectionV2, CustomSectionInstance, ParallaxGroup, ParallaxSlideV2 } from "../../lib/sanity/types";
23
- import { isPageSectionV2, isCustomSectionInstance, isParallexGroup } from "../../lib/sanity/types";
23
+ import { isPageSectionV2, isCustomSectionInstance, isParallaxGroup } from "../../lib/sanity/types";
24
24
  import { DEVICE_HEIGHTS } from "../../lib/builder/types";
25
25
  import { getEffectiveColumnsV2, getSectionV2SettingValue } from "./settings-panel/responsive-helpers";
26
26
  import BlockLivePreview from "./BlockLivePreview";
27
- import { getColumnVerticalAlign } from "../../lib/builder/layout-styles";
27
+ import { getColumnVerticalAlign, getRowLayoutStyles } from "../../lib/builder/layout-styles";
28
28
 
29
29
  // Layout keys that support responsive overrides for V2 sections
30
30
  const OVERRIDABLE_KEYS = [
@@ -450,12 +450,23 @@ export default function SectionV2Column({
450
450
  aria-label="Add block to empty column"
451
451
  className={`w-full py-2 rounded-lg text-xs font-medium transition-all flex items-center justify-center ${
452
452
  showChrome
453
- ? "bg-[#0d9668] text-white hover:bg-[#0a7d56] shadow-sm opacity-100"
453
+ ? "opacity-100"
454
454
  : showFaintOutline
455
- ? "bg-[#0d9668]/30 text-white/50 opacity-40"
455
+ ? "opacity-40"
456
456
  : "bg-transparent text-transparent opacity-0 pointer-events-none"
457
457
  }`}
458
- style={{ pointerEvents: showChrome || showFaintOutline ? "auto" : "none" }}
458
+ style={{
459
+ pointerEvents: showChrome || showFaintOutline ? "auto" : "none",
460
+ ...(showChrome ? {
461
+ background: "linear-gradient(170deg, rgba(38,38,48,0.95) 0%, rgba(28,28,36,0.97) 100%)",
462
+ color: "rgba(100,220,170,0.8)",
463
+ boxShadow: "0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.04)",
464
+ border: "1px solid rgba(255,255,255,0.06)",
465
+ } : showFaintOutline ? {
466
+ background: "rgba(38,38,48,0.3)",
467
+ color: "rgba(100,220,170,0.4)",
468
+ } : {}),
469
+ }}
459
470
  >
460
471
  + Add Block
461
472
  </button>
@@ -476,8 +487,14 @@ export default function SectionV2Column({
476
487
  <button
477
488
  onClick={handleAddBlockBelow}
478
489
  aria-label="Add block below existing blocks"
479
- className="w-full py-1.5 text-[11px] font-medium rounded bg-[#0d9668] text-white hover:bg-[#0a7d56] transition-all shadow-sm"
480
- style={{ pointerEvents: showChrome ? "auto" : "none" }}
490
+ className="w-full py-1.5 text-[11px] font-medium rounded transition-all"
491
+ style={{
492
+ pointerEvents: showChrome ? "auto" : "none",
493
+ background: "linear-gradient(170deg, rgba(38,38,48,0.95) 0%, rgba(28,28,36,0.97) 100%)",
494
+ color: "rgba(100,220,170,0.8)",
495
+ boxShadow: "0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.04)",
496
+ border: "1px solid rgba(255,255,255,0.06)",
497
+ }}
481
498
  >
482
499
  + Add Block
483
500
  </button>
@@ -144,9 +144,6 @@ export default function SettingsPanel() {
144
144
  } else if (selectedSectionV2) {
145
145
  onDelete = () => store.deleteSection(selectedSectionV2._key);
146
146
  deleteTitle = "Delete Section";
147
- } else if (selectedSection) {
148
- onDelete = () => store.deleteSection(selectedSection._key);
149
- deleteTitle = "Delete Section";
150
147
  }
151
148
 
152
149
  if (!onDelete) return null;
@@ -149,14 +149,18 @@ export default function SortableBlock({
149
149
  className="flex items-center gap-1.5"
150
150
  style={{ transform: `scale(${Math.min(2, 1 / canvasZoom)})`, transformOrigin: "top center" }}
151
151
  >
152
- <div className="flex items-center bg-[#0d9668] rounded shadow-lg overflow-hidden">
152
+ <div className="flex items-center rounded-[5px] overflow-hidden" style={{
153
+ background: "linear-gradient(170deg, rgba(38,38,48,0.97) 0%, rgba(28,28,36,0.98) 100%)",
154
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.06)",
155
+ border: "1px solid rgba(255,255,255,0.06)",
156
+ }}>
153
157
  {/* Move up arrow */}
154
158
  <button
155
159
  onClick={() => canMoveUp && reorderBlocks(rowKey, colKey, blockIndex, blockIndex - 1)}
156
160
  className={`transition-colors px-1 py-0.5 text-[11px] ${
157
161
  canMoveUp
158
- ? "text-white/80 hover:text-white hover:bg-white/10 cursor-pointer"
159
- : "text-white/25 cursor-default"
162
+ ? "text-white/45 hover:text-white/80 hover:bg-white/10 cursor-pointer"
163
+ : "text-white/20 cursor-default"
160
164
  }`}
161
165
  title="Move block up"
162
166
  aria-label="Move block up"
@@ -169,10 +173,10 @@ export default function SortableBlock({
169
173
  {/* Move down arrow */}
170
174
  <button
171
175
  onClick={() => canMoveDown && reorderBlocks(rowKey, colKey, blockIndex, blockIndex + 1)}
172
- className={`transition-colors px-1 py-0.5 text-[11px] border-l border-white/20 ${
176
+ className={`transition-colors px-1 py-0.5 text-[11px] border-l border-white/10 ${
173
177
  canMoveDown
174
- ? "text-white/80 hover:text-white hover:bg-white/10 cursor-pointer"
175
- : "text-white/25 cursor-default"
178
+ ? "text-white/45 hover:text-white/80 hover:bg-white/10 cursor-pointer"
179
+ : "text-white/20 cursor-default"
176
180
  }`}
177
181
  title="Move block down"
178
182
  aria-label="Move block down"
@@ -183,12 +187,12 @@ export default function SortableBlock({
183
187
  </svg>
184
188
  </button>
185
189
  {/* Block type label */}
186
- <span className="text-[11px] text-white/70 px-1.5 py-0.5 border-l border-white/20">
190
+ <span className="text-[11px] px-1.5 py-0.5 border-l border-white/10 font-medium" style={{ color: "rgba(100,220,170,0.9)" }}>
187
191
  {info?.icon || "▪"} {info?.label || block._type}
188
192
  </span>
189
193
  {/* Enter animation badge */}
190
194
  {block.enter_animation?.preset && block.enter_animation.preset !== "none" && (
191
- <span className="text-[10px] text-white/50 px-1 py-0.5 border-l border-white/15" title={`Animation: ${block.enter_animation.preset}`}>
195
+ <span className="text-[10px] text-white/35 px-1 py-0.5 border-l border-white/10" title={`Animation: ${block.enter_animation.preset}`}>
192
196
 
193
197
  </span>
194
198
  )}
@@ -196,7 +200,7 @@ export default function SortableBlock({
196
200
  {onDuplicate && (
197
201
  <button
198
202
  onClick={onDuplicate}
199
- className="text-white/70 hover:text-white transition-colors px-1.5 py-0.5 text-[11px] border-l border-white/20 hover:bg-white/10"
203
+ className="text-white/45 hover:text-white/80 transition-colors px-1.5 py-0.5 text-[11px] border-l border-white/10 hover:bg-white/10"
200
204
  title="Duplicate block (Ctrl+D)"
201
205
  aria-label="Duplicate block"
202
206
  >
@@ -253,12 +253,18 @@ export default function SortableRow({
253
253
  >
254
254
  {/* Main toolbar — drag + actions */}
255
255
  <div
256
- className="flex flex-col items-stretch bg-[#93278f]/90 rounded-l-lg shadow-lg py-2 px-2.5 gap-1 cursor-grab active:cursor-grabbing"
256
+ className="flex flex-col items-stretch rounded-l-lg py-2 px-2.5 gap-1 cursor-grab active:cursor-grabbing"
257
+ style={{
258
+ background: "linear-gradient(170deg, rgba(38,38,48,0.97) 0%, rgba(28,28,36,0.98) 100%)",
259
+ boxShadow: "0 4px 16px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.06)",
260
+ border: "1px solid rgba(255,255,255,0.06)",
261
+ borderRight: "none",
262
+ }}
257
263
  {...attributes}
258
264
  {...listeners}
259
265
  >
260
266
  {/* Section label — shows specific type for page sections */}
261
- <span className="text-[11px] text-white select-none leading-tight pointer-events-none font-medium tracking-wide">
267
+ <span className="text-[11px] select-none leading-tight pointer-events-none font-medium tracking-wide" style={{ color: "rgba(200,160,220,0.9)" }}>
262
268
  {sectionLabel || "Section"}
263
269
  </span>
264
270
 
@@ -267,7 +273,7 @@ export default function SortableRow({
267
273
  <button
268
274
  onClick={(e) => { e.stopPropagation(); onDuplicate(); }}
269
275
  onPointerDown={(e) => e.stopPropagation()}
270
- className="flex items-center justify-center text-[12px] text-white/80 hover:text-white transition-colors"
276
+ className="flex items-center justify-center text-[12px] text-white/50 hover:text-white/85 transition-colors"
271
277
  title="Duplicate section"
272
278
  aria-label="Duplicate section"
273
279
  >
@@ -277,7 +283,7 @@ export default function SortableRow({
277
283
  onClick={(e) => { e.stopPropagation(); onMoveUp(); }}
278
284
  onPointerDown={(e) => e.stopPropagation()}
279
285
  disabled={isFirst}
280
- className="flex items-center justify-center text-[12px] text-white/80 hover:text-white transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
286
+ className="flex items-center justify-center text-[12px] text-white/50 hover:text-white/85 transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
281
287
  title="Move up"
282
288
  aria-label="Move section up"
283
289
  >
@@ -287,7 +293,7 @@ export default function SortableRow({
287
293
  onClick={(e) => { e.stopPropagation(); onMoveDown(); }}
288
294
  onPointerDown={(e) => e.stopPropagation()}
289
295
  disabled={isLast}
290
- className="flex items-center justify-center text-[12px] text-white/80 hover:text-white transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
296
+ className="flex items-center justify-center text-[12px] text-white/50 hover:text-white/85 transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
291
297
  title="Move down"
292
298
  aria-label="Move section down"
293
299
  >
@@ -300,11 +306,11 @@ export default function SortableRow({
300
306
  <button
301
307
  onClick={(e) => { e.stopPropagation(); onAddColumn(); }}
302
308
  onPointerDown={(e) => e.stopPropagation()}
303
- className="flex items-center gap-1 text-[11px] text-white/80 hover:text-white transition-colors py-0.5"
309
+ className="flex items-center gap-1 text-[11px] text-white/50 hover:text-white/85 transition-colors py-0.5"
304
310
  title="Add column"
305
311
  aria-label="Add column"
306
312
  >
307
- <span className="text-white/50">+</span> Col
313
+ <span className="text-white/30">+</span> Col
308
314
  </button>
309
315
  )}
310
316
 
@@ -312,11 +318,11 @@ export default function SortableRow({
312
318
  <button
313
319
  onClick={(e) => { e.stopPropagation(); onDelete(); }}
314
320
  onPointerDown={(e) => e.stopPropagation()}
315
- className="flex items-center gap-1 text-[11px] text-white/80 hover:text-red-300 transition-colors py-0.5"
321
+ className="flex items-center gap-1 text-[11px] text-white/50 hover:text-red-300 transition-colors py-0.5"
316
322
  title="Delete section"
317
323
  aria-label="Delete section"
318
324
  >
319
- <span className="text-white/50">-</span> Delete
325
+ <span className="text-white/30">-</span> Delete
320
326
  </button>
321
327
  </div>
322
328
  </div>
@@ -333,7 +339,7 @@ export default function SortableRow({
333
339
  )}
334
340
 
335
341
  {/* Content — same layout as Preview */}
336
- <div style={coverRow ? undefined : { maxWidth, margin: "0 auto", paddingLeft: maxWidth !== "100%" ? gridPadding : undefined, paddingRight: maxWidth !== "100%" ? gridPadding : undefined }} className="relative z-[2]">
342
+ <div style={coverRow ? undefined : { maxWidth, margin: "0 auto", paddingLeft: maxWidth !== "100%" ? gridPadding : undefined, paddingRight: maxWidth !== "100%" ? gridPadding : undefined }} className="relative">
337
343
  {children}
338
344
  </div>
339
345
  </div>
@@ -149,7 +149,7 @@ export default function LiveTextEditor({ block, editable = false }: { block: Tex
149
149
  fontFamily: "inherit",
150
150
  outline: "none",
151
151
  whiteSpace: "pre-wrap",
152
- wordBreak: "normal",
152
+ wordBreak: "break-word",
153
153
  minHeight: "1em",
154
154
  // Multi-column layout: gap inherits global grid gutter
155
155
  ...(cols ? {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morphika/andami",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Visual Page Builder — core library. A reusable website builder with visual editing, CMS integration, and asset management.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/styles/base.css CHANGED
@@ -57,21 +57,14 @@ body {
57
57
  overflow-x: clip;
58
58
  }
59
59
 
60
- /* Prevent text overflow in grid columns on narrow viewports.
61
- * overflow-wrap: break-word — breaks mid-word ONLY when the entire word
62
- * cannot fit on a fresh line. Does NOT affect min-content intrinsic sizing,
63
- * so CSS Grid columns keep their proper width (unlike 'anywhere' which
64
- * collapses min-content to a single character width).
65
- * word-break: normal — wraps at natural word boundaries first, preventing
66
- * ugly mid-word splits like "Collecti" + "on". */
60
+ /* Prevent text overflow in grid columns on narrow viewports */
67
61
  [data-site] p,
68
62
  [data-site] h1,
69
63
  [data-site] h2,
70
64
  [data-site] h3,
71
65
  [data-site] h4,
72
66
  [data-site] span {
73
- overflow-wrap: break-word;
74
- word-break: normal;
67
+ word-break: break-word;
75
68
  }
76
69
 
77
70
  [data-custom-cursor] {