@morphika/andami 0.5.0 → 0.5.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.
Files changed (122) hide show
  1. package/README.md +151 -36
  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 +320 -327
  6. package/app/admin/navigation/page.tsx +255 -255
  7. package/app/admin/pages/[slug]/page.tsx +6 -6
  8. package/app/admin/pages/page.tsx +11 -11
  9. package/app/admin/projects/page.tsx +14 -14
  10. package/app/admin/setup/page.tsx +1 -1
  11. package/app/admin/styles/page.tsx +1 -1
  12. package/components/admin/MetadataEditor.tsx +6 -6
  13. package/components/admin/nav-builder/NavBuilder.tsx +1 -1
  14. package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
  15. package/components/admin/nav-builder/NavGridCell.tsx +48 -48
  16. package/components/admin/nav-builder/NavGridItem.tsx +4 -4
  17. package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
  18. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
  19. package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
  20. package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
  21. package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
  22. package/components/admin/nav-builder/NavSettingsFields.tsx +514 -514
  23. package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
  24. package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
  25. package/components/admin/setup-wizard/DoneStep.tsx +1 -1
  26. package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
  27. package/components/admin/setup-wizard/StorageStep.tsx +2 -2
  28. package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
  29. package/components/admin/styles/ColorsEditor.tsx +2 -2
  30. package/components/admin/styles/FontsEditor.tsx +6 -6
  31. package/components/admin/styles/GridLayoutEditor.tsx +9 -9
  32. package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
  33. package/components/admin/styles/TypographyEditor.tsx +6 -6
  34. package/components/admin/styles/shared.tsx +68 -68
  35. package/components/blocks/AudioBlockRenderer.tsx +286 -0
  36. package/components/blocks/BeforeAfterBlockRenderer.tsx +274 -0
  37. package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
  38. package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
  39. package/components/builder/BlockCardIcons.tsx +316 -227
  40. package/components/builder/BlockTypePicker.tsx +3 -1
  41. package/components/builder/BubbleIcons.tsx +90 -0
  42. package/components/builder/BuilderCanvas.tsx +2 -0
  43. package/components/builder/CanvasMinimap.tsx +2 -2
  44. package/components/builder/CoverSectionCanvas.tsx +363 -275
  45. package/components/builder/DeviceFrame.tsx +1 -1
  46. package/components/builder/DndWrapper.tsx +3 -3
  47. package/components/builder/InsertionLines.tsx +1 -1
  48. package/components/builder/SectionCardIcons.tsx +421 -320
  49. package/components/builder/SectionEditorBar.tsx +1 -1
  50. package/components/builder/SectionTypePicker.tsx +4 -4
  51. package/components/builder/SectionV2Canvas.tsx +20 -4
  52. package/components/builder/SectionV2Column.tsx +74 -68
  53. package/components/builder/SortableBlock.tsx +93 -73
  54. package/components/builder/SortableRow.tsx +27 -26
  55. package/components/builder/VirtualAssetGrid.tsx +2 -2
  56. package/components/builder/asset-browser/R2BrowserContent.tsx +34 -17
  57. package/components/builder/asset-browser/helpers.ts +4 -0
  58. package/components/builder/asset-browser/types.ts +2 -1
  59. package/components/builder/blockStyles.tsx +192 -173
  60. package/components/builder/color-picker/AlphaSlider.tsx +141 -141
  61. package/components/builder/color-picker/ColorInputs.tsx +105 -105
  62. package/components/builder/color-picker/EyedropperButton.tsx +74 -74
  63. package/components/builder/color-picker/HueSlider.tsx +124 -124
  64. package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
  65. package/components/builder/color-picker/SwatchBar.tsx +93 -93
  66. package/components/builder/editors/AudioBlockEditor.tsx +242 -0
  67. package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -0
  68. package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
  69. package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
  70. package/components/builder/editors/HoverEffectPicker.tsx +2 -2
  71. package/components/builder/editors/ImageBlockEditor.tsx +2 -2
  72. package/components/builder/editors/ImageGridBlockEditor.tsx +4 -4
  73. package/components/builder/editors/MarqueeBlockEditor.tsx +621 -0
  74. package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
  75. package/components/builder/editors/ProjectGridEditor.tsx +9 -9
  76. package/components/builder/editors/SpacerBlockEditor.tsx +5 -5
  77. package/components/builder/editors/StaggerSettings.tsx +109 -109
  78. package/components/builder/editors/TextBlockEditor.tsx +3 -3
  79. package/components/builder/editors/TextStylePicker.tsx +1 -1
  80. package/components/builder/editors/VideoBlockEditor.tsx +2 -2
  81. package/components/builder/editors/index.ts +11 -10
  82. package/components/builder/editors/shared.tsx +7 -7
  83. package/components/builder/live-preview/LiveAudioPreview.tsx +120 -0
  84. package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +176 -0
  85. package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
  86. package/components/builder/live-preview/LiveImagePreview.tsx +1 -1
  87. package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
  88. package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
  89. package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
  90. package/components/builder/live-preview/ProjectCardWrapper.tsx +291 -291
  91. package/components/builder/settings-panel/AnimationTab.tsx +138 -138
  92. package/components/builder/settings-panel/BlockLayoutTab.tsx +7 -7
  93. package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
  94. package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
  95. package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
  96. package/components/builder/settings-panel/CoverSectionSettings.tsx +335 -335
  97. package/components/builder/settings-panel/PageSettings.tsx +3 -3
  98. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
  99. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
  100. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
  101. package/components/builder/settings-panel/SectionV2Settings.tsx +14 -14
  102. package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
  103. package/lib/animation/enter-types.ts +3 -0
  104. package/lib/animation/hover-effect-presets.ts +210 -210
  105. package/lib/animation/hover-effect-types.ts +3 -0
  106. package/lib/builder/block-registrations.ts +468 -335
  107. package/lib/builder/constants.ts +111 -111
  108. package/lib/builder/store-sections.ts +2 -2
  109. package/lib/builder/types-slices.ts +414 -414
  110. package/lib/builder/types.ts +6 -1
  111. package/lib/config/index.ts +27 -27
  112. package/lib/sanity/types.ts +156 -1
  113. package/lib/version.ts +1 -1
  114. package/package.json +1 -1
  115. package/sanity/schemas/blocks/audioBlock.ts +69 -0
  116. package/sanity/schemas/blocks/beforeAfterBlock.ts +121 -0
  117. package/sanity/schemas/blocks/index.ts +12 -9
  118. package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
  119. package/sanity/schemas/index.ts +120 -111
  120. package/styles/admin.css +85 -85
  121. package/styles/animations.css +237 -237
  122. package/styles/base.css +114 -114
@@ -1,93 +1,93 @@
1
- "use client";
2
-
3
- /**
4
- * SwatchBar — User palette swatches + common neutral colors.
5
- *
6
- * Integrated inside the color picker modal. Shows:
7
- * - "Your Palette" row with user swatches + an "add to palette" button
8
- * - "Common" row with standard neutrals (white → black)
9
- */
10
-
11
- import { useCallback } from "react";
12
- import type { SwatchBarProps } from "./types";
13
-
14
- // Common neutral colors always available
15
- const COMMON_COLORS = [
16
- "#ffffff",
17
- "#f5f5f5",
18
- "#e5e5e5",
19
- "#a3a3a3",
20
- "#525252",
21
- "#262626",
22
- "#171717",
23
- "#000000",
24
- ];
25
-
26
- export default function SwatchBar({
27
- value,
28
- onSelect,
29
- swatches = [],
30
- }: SwatchBarProps) {
31
- const handleSwatchClick = useCallback(
32
- (hex: string) => {
33
- onSelect(hex);
34
- },
35
- [onSelect]
36
- );
37
-
38
- return (
39
- <div className="border-t border-neutral-200 pt-4">
40
- {/* User palette */}
41
- {swatches.length > 0 && (
42
- <div className="mb-3">
43
- <div className="flex items-center justify-between mb-2">
44
- <span className="text-[10px] text-neutral-400 uppercase tracking-widest">
45
- Your Palette
46
- </span>
47
- </div>
48
- <div className="flex flex-wrap gap-1.5">
49
- {swatches.map((s, i) => (
50
- <button
51
- key={s._key || `swatch-${i}`}
52
- type="button"
53
- onClick={() => handleSwatchClick(s.hex)}
54
- title={`${s.name}: ${s.hex}`}
55
- className={`w-8 h-8 rounded-lg cursor-pointer transition-all ${
56
- value.toLowerCase() === s.hex.toLowerCase()
57
- ? "ring-2 ring-[#076bff] ring-offset-1 ring-offset-white"
58
- : "border border-neutral-200 hover:border-neutral-400 hover:scale-110"
59
- }`}
60
- style={{ background: s.hex }}
61
- />
62
- ))}
63
- </div>
64
- </div>
65
- )}
66
-
67
- {/* Common colors */}
68
- <div>
69
- {swatches.length > 0 && (
70
- <span className="text-[10px] text-neutral-400 uppercase tracking-widest block mb-2">
71
- Common
72
- </span>
73
- )}
74
- <div className="flex gap-1">
75
- {COMMON_COLORS.map((c) => (
76
- <button
77
- key={c}
78
- type="button"
79
- onClick={() => handleSwatchClick(c)}
80
- title={c.toUpperCase()}
81
- className={`w-6 h-6 rounded-md cursor-pointer transition-all ${
82
- value.toLowerCase() === c
83
- ? "ring-2 ring-[#076bff] ring-offset-1 ring-offset-white"
84
- : "border border-neutral-200 hover:border-neutral-400 hover:scale-110"
85
- }`}
86
- style={{ background: c }}
87
- />
88
- ))}
89
- </div>
90
- </div>
91
- </div>
92
- );
93
- }
1
+ "use client";
2
+
3
+ /**
4
+ * SwatchBar — User palette swatches + common neutral colors.
5
+ *
6
+ * Integrated inside the color picker modal. Shows:
7
+ * - "Your Palette" row with user swatches + an "add to palette" button
8
+ * - "Common" row with standard neutrals (white → black)
9
+ */
10
+
11
+ import { useCallback } from "react";
12
+ import type { SwatchBarProps } from "./types";
13
+
14
+ // Common neutral colors always available
15
+ const COMMON_COLORS = [
16
+ "#ffffff",
17
+ "#f5f5f5",
18
+ "#e5e5e5",
19
+ "#a3a3a3",
20
+ "#525252",
21
+ "#262626",
22
+ "#171717",
23
+ "#000000",
24
+ ];
25
+
26
+ export default function SwatchBar({
27
+ value,
28
+ onSelect,
29
+ swatches = [],
30
+ }: SwatchBarProps) {
31
+ const handleSwatchClick = useCallback(
32
+ (hex: string) => {
33
+ onSelect(hex);
34
+ },
35
+ [onSelect]
36
+ );
37
+
38
+ return (
39
+ <div className="border-t border-neutral-200 pt-4">
40
+ {/* User palette */}
41
+ {swatches.length > 0 && (
42
+ <div className="mb-3">
43
+ <div className="flex items-center justify-between mb-2">
44
+ <span className="text-[10px] text-neutral-400 uppercase tracking-widest">
45
+ Your Palette
46
+ </span>
47
+ </div>
48
+ <div className="flex flex-wrap gap-1.5">
49
+ {swatches.map((s, i) => (
50
+ <button
51
+ key={s._key || `swatch-${i}`}
52
+ type="button"
53
+ onClick={() => handleSwatchClick(s.hex)}
54
+ title={`${s.name}: ${s.hex}`}
55
+ className={`w-8 h-8 rounded-lg cursor-pointer transition-all ${
56
+ value.toLowerCase() === s.hex.toLowerCase()
57
+ ? "ring-2 ring-[#3580f9] ring-offset-1 ring-offset-white"
58
+ : "border border-neutral-200 hover:border-neutral-400 hover:scale-110"
59
+ }`}
60
+ style={{ background: s.hex }}
61
+ />
62
+ ))}
63
+ </div>
64
+ </div>
65
+ )}
66
+
67
+ {/* Common colors */}
68
+ <div>
69
+ {swatches.length > 0 && (
70
+ <span className="text-[10px] text-neutral-400 uppercase tracking-widest block mb-2">
71
+ Common
72
+ </span>
73
+ )}
74
+ <div className="flex gap-1">
75
+ {COMMON_COLORS.map((c) => (
76
+ <button
77
+ key={c}
78
+ type="button"
79
+ onClick={() => handleSwatchClick(c)}
80
+ title={c.toUpperCase()}
81
+ className={`w-6 h-6 rounded-md cursor-pointer transition-all ${
82
+ value.toLowerCase() === c
83
+ ? "ring-2 ring-[#3580f9] ring-offset-1 ring-offset-white"
84
+ : "border border-neutral-200 hover:border-neutral-400 hover:scale-110"
85
+ }`}
86
+ style={{ background: c }}
87
+ />
88
+ ))}
89
+ </div>
90
+ </div>
91
+ </div>
92
+ );
93
+ }
@@ -0,0 +1,242 @@
1
+ "use client";
2
+
3
+ import { useBuilderStore } from "../../../lib/builder/store";
4
+ import { getEffectiveValue, setResponsiveOverride } from "../../../lib/builder/responsive";
5
+ import type { AudioBlock, ContentBlock } from "../../../lib/sanity/types";
6
+ import ColorSwatchPicker, { usePaletteSwatches } from "../ColorSwatchPicker";
7
+ import { resolveColorHex } from "../../../lib/color-utils";
8
+ import {
9
+ SourceIcon,
10
+ LayoutIcon,
11
+ AppearanceIcon,
12
+ PlaybackIcon,
13
+ OptionsIcon,
14
+ } from "./section-icons";
15
+ import {
16
+ SettingsField,
17
+ SettingsSection,
18
+ StyledCheckbox,
19
+ AssetPathInput,
20
+ ViewportBadge,
21
+ ResponsiveField,
22
+ useActiveViewport,
23
+ INPUT_CLASS,
24
+ } from "./shared";
25
+
26
+ interface Props {
27
+ block: AudioBlock;
28
+ }
29
+
30
+ export default function AudioBlockEditor({ block }: Props) {
31
+ const store = useBuilderStore();
32
+ const viewport = useActiveViewport();
33
+ const paletteSwatches = usePaletteSwatches();
34
+
35
+ const snapshotOnFocus = () => store._pushSnapshot();
36
+
37
+ const updateResponsive = (property: string, value: unknown) => {
38
+ if (viewport === "desktop") {
39
+ store.updateBlock(block._key, { [property]: value } as Partial<ContentBlock>);
40
+ } else {
41
+ const overrides = setResponsiveOverride(block as ContentBlock, viewport, property, value);
42
+ store.updateBlock(block._key, overrides as Partial<ContentBlock>);
43
+ }
44
+ };
45
+
46
+ const resetOverride = (property: string) => {
47
+ const overrides = setResponsiveOverride(block as ContentBlock, viewport, property, undefined);
48
+ store.updateBlock(block._key, overrides as Partial<ContentBlock>);
49
+ };
50
+
51
+ const update = (updates: Partial<AudioBlock>) => {
52
+ store.updateBlock(block._key, updates as Partial<ContentBlock>);
53
+ };
54
+
55
+ const updateDebounced = (updates: Partial<AudioBlock>) => {
56
+ store.updateBlockDebounced(block._key, updates as Partial<ContentBlock>);
57
+ };
58
+
59
+ const effectiveWidth = getEffectiveValue<string>(
60
+ block as ContentBlock, viewport, "width", block.width || "contained"
61
+ );
62
+
63
+ return (
64
+ <>
65
+ <ViewportBadge />
66
+
67
+ {/* ── Source ── */}
68
+ <SettingsSection title="Audio" defaultOpen icon={<SourceIcon />}>
69
+ <SettingsField label="Audio File" hint="mp3, wav, ogg, m4a, aac, flac">
70
+ <AssetPathInput
71
+ value={block.asset_path || ""}
72
+ onFocus={snapshotOnFocus}
73
+ onChange={(v) => updateDebounced({ asset_path: v })}
74
+ placeholder="projects/slug/track.mp3"
75
+ filterType="audio"
76
+ />
77
+ </SettingsField>
78
+
79
+ <SettingsField label="Alt Text">
80
+ <input
81
+ type="text"
82
+ value={block.alt || ""}
83
+ onFocus={snapshotOnFocus}
84
+ onChange={(e) => updateDebounced({ alt: e.target.value })}
85
+ className={INPUT_CLASS}
86
+ placeholder="Describe the audio for accessibility"
87
+ />
88
+ </SettingsField>
89
+ </SettingsSection>
90
+
91
+ {/* ── Metadata ── */}
92
+ <SettingsSection title="Metadata" icon={<OptionsIcon />}>
93
+ <SettingsField label="Title">
94
+ <input
95
+ type="text"
96
+ value={block.title || ""}
97
+ onFocus={snapshotOnFocus}
98
+ onChange={(e) => updateDebounced({ title: e.target.value })}
99
+ className={INPUT_CLASS}
100
+ placeholder="Track title"
101
+ />
102
+ </SettingsField>
103
+
104
+ <SettingsField label="Artist">
105
+ <input
106
+ type="text"
107
+ value={block.artist || ""}
108
+ onFocus={snapshotOnFocus}
109
+ onChange={(e) => updateDebounced({ artist: e.target.value })}
110
+ className={INPUT_CLASS}
111
+ placeholder="Artist name"
112
+ />
113
+ </SettingsField>
114
+
115
+ <SettingsField label="Cover Art" hint="Optional relative path to an image">
116
+ <AssetPathInput
117
+ value={block.cover_path || ""}
118
+ onFocus={snapshotOnFocus}
119
+ onChange={(v) => updateDebounced({ cover_path: v })}
120
+ placeholder="projects/slug/cover.jpg"
121
+ filterType="image"
122
+ />
123
+ </SettingsField>
124
+ </SettingsSection>
125
+
126
+ {/* ── Layout ── */}
127
+ <SettingsSection title="Layout" icon={<LayoutIcon />}>
128
+ <ResponsiveField
129
+ label="Width"
130
+ block={block as ContentBlock}
131
+ property="width"
132
+ onReset={() => resetOverride("width")}
133
+ >
134
+ <div className="flex gap-1">
135
+ {(
136
+ [
137
+ { value: "full", label: "Full" },
138
+ { value: "contained", label: "Contained" },
139
+ { value: "small", label: "Small" },
140
+ { value: "fill", label: "Fill" },
141
+ ] as const
142
+ ).map((opt) => (
143
+ <button
144
+ key={opt.value}
145
+ onClick={() => updateResponsive("width", opt.value)}
146
+ className={`flex-1 rounded border py-1 text-xs transition-colors ${
147
+ effectiveWidth === opt.value
148
+ ? "border-[#3580f9] bg-[#3580f9]/20 text-neutral-900"
149
+ : "border-neutral-200 bg-white text-neutral-500 hover:border-neutral-600"
150
+ }`}
151
+ >
152
+ {opt.label}
153
+ </button>
154
+ ))}
155
+ </div>
156
+ </ResponsiveField>
157
+ </SettingsSection>
158
+
159
+ {/* ── Appearance ── */}
160
+ <SettingsSection title="Appearance" icon={<AppearanceIcon />}>
161
+ <SettingsField label="Accent Color" hint="Play button + progress fill">
162
+ <ColorSwatchPicker
163
+ value={block.accent_color || "#3580f9"}
164
+ onChange={(value) => {
165
+ const hex = resolveColorHex(value) || "#3580f9";
166
+ update({ accent_color: hex });
167
+ }}
168
+ swatches={paletteSwatches}
169
+ />
170
+ </SettingsField>
171
+
172
+ <ResponsiveField
173
+ label="Border Radius"
174
+ block={block as ContentBlock}
175
+ property="border_radius"
176
+ onReset={() => resetOverride("border_radius")}
177
+ >
178
+ <div className="flex items-center gap-1.5">
179
+ <input
180
+ type="number"
181
+ value={String(getEffectiveValue<string>(block as ContentBlock, viewport, "border_radius", block.border_radius || "")).replace(/px$/i, "")}
182
+ onFocus={snapshotOnFocus}
183
+ onChange={(e) => {
184
+ store._pushSnapshot();
185
+ updateResponsive("border_radius", e.target.value.replace(/[^0-9]/g, ""));
186
+ }}
187
+ className={INPUT_CLASS}
188
+ placeholder="12"
189
+ min={0}
190
+ />
191
+ <span className="text-[10px] text-neutral-400 shrink-0">px</span>
192
+ </div>
193
+ </ResponsiveField>
194
+
195
+ <ResponsiveField
196
+ label="Shadow"
197
+ block={block as ContentBlock}
198
+ property="shadow"
199
+ onReset={() => resetOverride("shadow")}
200
+ >
201
+ <button
202
+ type="button"
203
+ onClick={() => {
204
+ const effectiveShadow = getEffectiveValue<boolean>(block as ContentBlock, viewport, "shadow", block.shadow || false);
205
+ updateResponsive("shadow", !effectiveShadow);
206
+ }}
207
+ className={`relative w-8 h-[18px] rounded-full transition-colors ${
208
+ getEffectiveValue<boolean>(block as ContentBlock, viewport, "shadow", block.shadow || false) ? "bg-[#3580f9]" : "bg-neutral-200 hover:bg-neutral-300"
209
+ }`}
210
+ >
211
+ <span
212
+ className={`absolute top-[2px] w-[14px] h-[14px] rounded-full bg-white shadow-sm transition-transform ${
213
+ getEffectiveValue<boolean>(block as ContentBlock, viewport, "shadow", block.shadow || false) ? "left-[16px]" : "left-[2px]"
214
+ }`}
215
+ />
216
+ </button>
217
+ </ResponsiveField>
218
+ </SettingsSection>
219
+
220
+ {/* ── Playback ── */}
221
+ <SettingsSection title="Playback" icon={<PlaybackIcon />}>
222
+ <div className="space-y-1.5">
223
+ <StyledCheckbox
224
+ label="Autoplay"
225
+ checked={block.autoplay === true}
226
+ onChange={(checked) => update({ autoplay: checked })}
227
+ />
228
+ <StyledCheckbox
229
+ label="Loop"
230
+ checked={block.loop === true}
231
+ onChange={(checked) => update({ loop: checked })}
232
+ />
233
+ <StyledCheckbox
234
+ label="Muted"
235
+ checked={block.muted === true}
236
+ onChange={(checked) => update({ muted: checked })}
237
+ />
238
+ </div>
239
+ </SettingsSection>
240
+ </>
241
+ );
242
+ }