@morphika/andami 0.1.3 → 0.1.6
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.
- package/app/(site)/[slug]/page.tsx +7 -4
- package/app/(site)/layout.tsx +5 -2
- package/app/(site)/page.tsx +2 -2
- package/app/(site)/preview/page.tsx +4 -4
- package/app/(site)/work/[slug]/page.tsx +7 -4
- package/app/admin/layout.tsx +3 -2
- package/app/admin/login/page.tsx +5 -5
- package/app/admin/navigation/page.tsx +255 -157
- package/app/api/admin/assets/health/route.ts +1 -1
- package/app/api/admin/assets/register/route.ts +1 -1
- package/app/api/admin/assets/registry/route.ts +1 -1
- package/app/api/admin/assets/relink/confirm/route.ts +2 -2
- package/app/api/admin/assets/relink/route.ts +1 -1
- package/app/api/admin/assets/scan/route.ts +1 -1
- package/app/api/admin/custom-sections/[slug]/route.ts +1 -1
- package/app/api/admin/custom-sections/route.ts +1 -1
- package/app/api/admin/database/route.ts +1 -1
- package/app/api/admin/pages/[slug]/duplicate/route.ts +1 -1
- package/app/api/admin/pages/[slug]/route.ts +2 -2
- package/app/api/admin/pages/[slug]/set-home/route.ts +1 -1
- package/app/api/admin/pages/route.ts +1 -1
- package/app/api/admin/preview/route.ts +1 -1
- package/app/api/admin/r2/delete/route.ts +1 -1
- package/app/api/admin/r2/rename/route.ts +1 -1
- package/app/api/admin/r2/status/route.ts +1 -1
- package/app/api/admin/r2/upload-url/route.ts +1 -1
- package/app/api/admin/settings/route.ts +41 -16
- package/app/api/admin/setup/complete/route.ts +2 -2
- package/app/api/admin/setup/route.ts +7 -4
- package/app/api/admin/storage/switch/route.ts +1 -1
- package/app/api/admin/styles/route.ts +1 -1
- package/components/admin/index.ts +7 -0
- package/components/admin/nav-builder/NavGeneralSettings.tsx +11 -15
- package/components/admin/nav-builder/NavItemSettings.tsx +29 -5
- package/components/admin/nav-builder/NavLivePreview.tsx +4 -1
- package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -0
- package/components/admin/nav-builder/NavMobileSettings.tsx +223 -0
- package/components/admin/nav-builder/index.ts +2 -0
- package/components/blocks/BlockRenderer.tsx +65 -13
- package/components/blocks/ButtonBlockRenderer.tsx +29 -6
- package/components/blocks/CoverBlockRenderer.tsx +36 -14
- package/components/blocks/ImageBlockRenderer.tsx +5 -3
- package/components/blocks/ImageGridBlockRenderer.tsx +13 -6
- package/components/blocks/PageRenderer.tsx +4 -2
- package/components/blocks/ProjectGridBlockRenderer.tsx +18 -3
- package/components/blocks/SectionRenderer.tsx +9 -8
- package/components/blocks/SectionV2Renderer.tsx +8 -8
- package/components/blocks/SpacerBlockRenderer.tsx +4 -2
- package/components/blocks/TextBlockRenderer.tsx +9 -4
- package/components/builder/BuilderCanvas.tsx +10 -4
- package/components/builder/ColorPicker.tsx +51 -243
- package/components/builder/ColorSwatchPicker.tsx +214 -274
- package/components/builder/DndWrapper.tsx +5 -2
- package/components/builder/SectionV2Canvas.tsx +15 -4
- package/components/builder/asset-browser/useAssetBrowser.ts +9 -1
- package/components/builder/color-picker/AlphaSlider.tsx +141 -0
- package/components/builder/color-picker/AngleControl.tsx +138 -0
- package/components/builder/color-picker/ColorInputs.tsx +105 -0
- package/components/builder/color-picker/EyedropperButton.tsx +74 -0
- package/components/builder/color-picker/GradientBar.tsx +222 -0
- package/components/builder/color-picker/GradientPreview.tsx +53 -0
- package/components/builder/color-picker/HueSlider.tsx +124 -0
- package/components/builder/color-picker/MeshCanvas.tsx +172 -0
- package/components/builder/color-picker/MeshPointEditor.tsx +133 -0
- package/components/builder/color-picker/MeshPointList.tsx +200 -0
- package/components/builder/color-picker/PositionControl.tsx +158 -0
- package/components/builder/color-picker/SaturationCanvas.tsx +142 -0
- package/components/builder/color-picker/StopEditor.tsx +178 -0
- package/components/builder/color-picker/SwatchBar.tsx +93 -0
- package/components/builder/color-picker/UnifiedColorPicker.tsx +713 -0
- package/components/builder/color-picker/index.ts +62 -0
- package/components/builder/color-picker/types.ts +115 -0
- package/components/builder/color-picker/utils.ts +138 -0
- package/components/builder/editors/CoverBlockEditor.tsx +86 -32
- package/components/builder/editors/ProjectGridEditor.tsx +51 -4
- package/components/builder/hooks/useColumnDrag.ts +25 -27
- package/components/builder/settings-panel/BlockLayoutTab.tsx +29 -7
- package/components/builder/settings-panel/LayoutTab.tsx +382 -310
- package/components/builder/settings-panel/PageSettings.tsx +6 -4
- package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
- package/components/builder/settings-panel/SectionV2LayoutTab.tsx +392 -312
- package/components/builder/settings-panel/SectionV2Settings.tsx +65 -35
- package/components/ui/Navbar.tsx +97 -25
- package/components/ui/PortfolioTracker.tsx +3 -3
- package/lib/assets.ts +1 -1
- package/lib/auth.ts +1 -1
- package/lib/builder/gradient-presets.ts +128 -0
- package/lib/builder/layout-styles.ts +16 -10
- package/lib/builder/serializer.ts +1 -0
- package/lib/builder/store-blocks.ts +48 -61
- package/lib/builder/store-helpers.ts +31 -14
- package/lib/builder/store.ts +59 -41
- package/lib/builder/types.ts +14 -0
- package/lib/color-utils.ts +200 -0
- package/lib/revalidate.ts +2 -2
- package/lib/sanity/client.ts +16 -0
- package/lib/sanity/queries.ts +4 -3
- package/lib/sanity/types.ts +76 -1
- package/lib/setup/detect.ts +1 -1
- package/lib/storage/index.ts +22 -4
- package/lib/version.ts +6 -0
- package/package.json +8 -2
- package/sanity/schemas/siteSettings.ts +34 -0
- package/styles/base.css +3 -3
- package/app/globals.css +0 -7
|
@@ -1,243 +1,51 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
color
|
|
25
|
-
onChange
|
|
26
|
-
onClose
|
|
27
|
-
confirmLabel
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
|
|
53
|
-
const s = Math.round(x * 100);
|
|
54
|
-
const v = Math.round((1 - y) * 100);
|
|
55
|
-
setSat(s);
|
|
56
|
-
setVal(v);
|
|
57
|
-
updateHex(hue, s, v);
|
|
58
|
-
}, [hue, updateHex]);
|
|
59
|
-
|
|
60
|
-
// Hue slider interaction
|
|
61
|
-
const handleHueMove = useCallback((e: React.MouseEvent | MouseEvent) => {
|
|
62
|
-
const rect = hueRef.current?.getBoundingClientRect();
|
|
63
|
-
if (!rect) return;
|
|
64
|
-
const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
65
|
-
const h = Math.round(x * 360);
|
|
66
|
-
setHue(h);
|
|
67
|
-
updateHex(h, sat, val);
|
|
68
|
-
}, [sat, val, updateHex]);
|
|
69
|
-
|
|
70
|
-
// Global mouse up
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
const handleUp = () => {
|
|
73
|
-
draggingCanvas.current = false;
|
|
74
|
-
draggingHue.current = false;
|
|
75
|
-
};
|
|
76
|
-
const handleMove = (e: MouseEvent) => {
|
|
77
|
-
if (draggingCanvas.current) handleCanvasMove(e);
|
|
78
|
-
if (draggingHue.current) handleHueMove(e);
|
|
79
|
-
};
|
|
80
|
-
window.addEventListener("mouseup", handleUp);
|
|
81
|
-
window.addEventListener("mousemove", handleMove);
|
|
82
|
-
return () => {
|
|
83
|
-
window.removeEventListener("mouseup", handleUp);
|
|
84
|
-
window.removeEventListener("mousemove", handleMove);
|
|
85
|
-
};
|
|
86
|
-
}, [handleCanvasMove, handleHueMove]);
|
|
87
|
-
|
|
88
|
-
const handleHexInput = (v: string) => {
|
|
89
|
-
setHex(v);
|
|
90
|
-
if (isValidHex(v)) {
|
|
91
|
-
const hsv = hexToHSV(v);
|
|
92
|
-
setHue(hsv.h);
|
|
93
|
-
setSat(hsv.s);
|
|
94
|
-
setVal(hsv.v);
|
|
95
|
-
if (compact) onChange(v);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Keyboard handlers for canvas (saturation/value) and hue slider
|
|
100
|
-
const handleCanvasKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
101
|
-
const step = e.shiftKey ? 10 : 2;
|
|
102
|
-
let newSat = sat;
|
|
103
|
-
let newVal = val;
|
|
104
|
-
switch (e.key) {
|
|
105
|
-
case "ArrowRight": newSat = Math.min(100, sat + step); break;
|
|
106
|
-
case "ArrowLeft": newSat = Math.max(0, sat - step); break;
|
|
107
|
-
case "ArrowUp": newVal = Math.min(100, val + step); break;
|
|
108
|
-
case "ArrowDown": newVal = Math.max(0, val - step); break;
|
|
109
|
-
default: return;
|
|
110
|
-
}
|
|
111
|
-
e.preventDefault();
|
|
112
|
-
setSat(newSat);
|
|
113
|
-
setVal(newVal);
|
|
114
|
-
updateHex(hue, newSat, newVal);
|
|
115
|
-
}, [sat, val, hue, updateHex]);
|
|
116
|
-
|
|
117
|
-
const handleHueKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
118
|
-
const step = e.shiftKey ? 20 : 5;
|
|
119
|
-
let newHue = hue;
|
|
120
|
-
switch (e.key) {
|
|
121
|
-
case "ArrowRight": newHue = Math.min(360, hue + step); break;
|
|
122
|
-
case "ArrowLeft": newHue = Math.max(0, hue - step); break;
|
|
123
|
-
default: return;
|
|
124
|
-
}
|
|
125
|
-
e.preventDefault();
|
|
126
|
-
setHue(newHue);
|
|
127
|
-
updateHex(newHue, sat, val);
|
|
128
|
-
}, [hue, sat, val, updateHex]);
|
|
129
|
-
|
|
130
|
-
const r = parseInt(hex.slice(1, 3), 16) || 0;
|
|
131
|
-
const g = parseInt(hex.slice(3, 5), 16) || 0;
|
|
132
|
-
const b = parseInt(hex.slice(5, 7), 16) || 0;
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
<div className="bg-white rounded-2xl p-4 w-[260px] shadow-xl border border-neutral-200">
|
|
136
|
-
{/* Saturation/Value canvas */}
|
|
137
|
-
<div
|
|
138
|
-
ref={canvasRef}
|
|
139
|
-
role="slider"
|
|
140
|
-
tabIndex={0}
|
|
141
|
-
aria-label="Color saturation and brightness"
|
|
142
|
-
aria-valuetext={`Saturation ${sat}%, Brightness ${val}%`}
|
|
143
|
-
className="w-full h-[160px] rounded-xl cursor-crosshair relative select-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#076bff]"
|
|
144
|
-
style={{
|
|
145
|
-
background: `linear-gradient(to bottom, transparent, #000), linear-gradient(to right, #fff, hsl(${hue}, 100%, 50%))`,
|
|
146
|
-
}}
|
|
147
|
-
onMouseDown={(e) => { draggingCanvas.current = true; handleCanvasMove(e); }}
|
|
148
|
-
onKeyDown={handleCanvasKeyDown}
|
|
149
|
-
>
|
|
150
|
-
<div
|
|
151
|
-
className="absolute w-4 h-4 rounded-full border-2 border-white pointer-events-none"
|
|
152
|
-
style={{
|
|
153
|
-
left: `${sat}%`,
|
|
154
|
-
top: `${100 - val}%`,
|
|
155
|
-
transform: "translate(-50%, -50%)",
|
|
156
|
-
boxShadow: "0 0 0 1px rgba(0,0,0,0.3), 0 2px 6px rgba(0,0,0,0.3)",
|
|
157
|
-
}}
|
|
158
|
-
/>
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
{/* Hue slider */}
|
|
162
|
-
<div
|
|
163
|
-
ref={hueRef}
|
|
164
|
-
role="slider"
|
|
165
|
-
tabIndex={0}
|
|
166
|
-
aria-label="Color hue"
|
|
167
|
-
aria-valuemin={0}
|
|
168
|
-
aria-valuemax={360}
|
|
169
|
-
aria-valuenow={hue}
|
|
170
|
-
aria-valuetext={`Hue ${hue} degrees`}
|
|
171
|
-
className="w-full h-3.5 rounded-full mt-3 cursor-pointer relative select-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#076bff]"
|
|
172
|
-
style={{
|
|
173
|
-
background: "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
|
|
174
|
-
}}
|
|
175
|
-
onMouseDown={(e) => { draggingHue.current = true; handleHueMove(e); }}
|
|
176
|
-
onKeyDown={handleHueKeyDown}
|
|
177
|
-
>
|
|
178
|
-
<div
|
|
179
|
-
className="absolute w-4 h-4 rounded-full border-2 border-white pointer-events-none"
|
|
180
|
-
style={{
|
|
181
|
-
left: `${(hue / 360) * 100}%`,
|
|
182
|
-
top: "50%",
|
|
183
|
-
transform: "translate(-50%, -50%)",
|
|
184
|
-
background: `hsl(${hue}, 100%, 50%)`,
|
|
185
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.4)",
|
|
186
|
-
}}
|
|
187
|
-
/>
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
{/* Hex input */}
|
|
191
|
-
<div className="flex items-center gap-2.5 mt-3.5">
|
|
192
|
-
<div
|
|
193
|
-
className="w-8 h-8 rounded-lg border border-neutral-200 shrink-0"
|
|
194
|
-
style={{ background: hex }}
|
|
195
|
-
/>
|
|
196
|
-
<input
|
|
197
|
-
value={hex.toUpperCase()}
|
|
198
|
-
onChange={(e) => handleHexInput(e.target.value)}
|
|
199
|
-
className="flex-1 bg-neutral-50 border border-neutral-200 rounded-lg px-2.5 py-1.5 text-neutral-900 text-xs font-mono outline-none focus:border-[#076bff] focus:ring-2 focus:ring-[#076bff]/10 transition-colors"
|
|
200
|
-
/>
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
{/* RGB readout */}
|
|
204
|
-
<div className="flex gap-2 mt-2.5">
|
|
205
|
-
{[
|
|
206
|
-
{ label: "R", val: r },
|
|
207
|
-
{ label: "G", val: g },
|
|
208
|
-
{ label: "B", val: b },
|
|
209
|
-
].map(({ label, val: v }) => (
|
|
210
|
-
<div key={label} className="flex-1">
|
|
211
|
-
<div className="text-[9px] text-neutral-400 uppercase tracking-widest mb-0.5">{label}</div>
|
|
212
|
-
<div className="bg-neutral-50 border border-neutral-200 rounded-md px-2 py-1 text-neutral-600 text-[11px] text-center font-mono">
|
|
213
|
-
{v}
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
))}
|
|
217
|
-
</div>
|
|
218
|
-
|
|
219
|
-
{/* Buttons (non-compact mode) */}
|
|
220
|
-
{!compact && (
|
|
221
|
-
<div className="flex gap-2 mt-3.5">
|
|
222
|
-
{onClose && (
|
|
223
|
-
<button
|
|
224
|
-
onClick={onClose}
|
|
225
|
-
className="flex-1 py-2 rounded-lg border border-neutral-200 bg-transparent text-neutral-500 text-xs font-mono cursor-pointer hover:border-neutral-300 hover:text-neutral-700 transition-colors"
|
|
226
|
-
>
|
|
227
|
-
Cancel
|
|
228
|
-
</button>
|
|
229
|
-
)}
|
|
230
|
-
<button
|
|
231
|
-
onClick={() => { onChange(hex); onClose?.(); }}
|
|
232
|
-
className="flex-1 py-2 rounded-lg border-none bg-[#076bff] text-white text-xs font-mono font-semibold cursor-pointer hover:bg-[#0559d4] transition-colors"
|
|
233
|
-
>
|
|
234
|
-
{confirmLabel}
|
|
235
|
-
</button>
|
|
236
|
-
</div>
|
|
237
|
-
)}
|
|
238
|
-
</div>
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ─── Export helpers ───
|
|
243
|
-
export { isValidHex, hexToHSL, hexToHSV, hsvToHex };
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use `UnifiedColorPicker` from `./color-picker` instead.
|
|
5
|
+
*
|
|
6
|
+
* This file is kept for backward compatibility. It re-exports the new
|
|
7
|
+
* UnifiedColorPicker with an adapter that maps the old props to the new API.
|
|
8
|
+
*
|
|
9
|
+
* Old API: ColorPicker({ color, onChange, onClose, confirmLabel, compact })
|
|
10
|
+
* New API: UnifiedColorPicker({ value, onChange, onClose, confirmLabel, ... })
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { UnifiedColorPicker } from "./color-picker";
|
|
14
|
+
import { isValidHex } from "../../lib/color-utils";
|
|
15
|
+
|
|
16
|
+
// Re-export helpers for any consumers that imported from here
|
|
17
|
+
export { isValidHex } from "../../lib/color-utils";
|
|
18
|
+
export { hexToHSL, hexToHSV, hsvToHex } from "../../lib/color-utils";
|
|
19
|
+
|
|
20
|
+
// ─── Legacy ColorPicker Adapter ───
|
|
21
|
+
|
|
22
|
+
interface ColorPickerProps {
|
|
23
|
+
/** @deprecated Use `value` instead */
|
|
24
|
+
color: string;
|
|
25
|
+
onChange: (hex: string) => void;
|
|
26
|
+
onClose?: () => void;
|
|
27
|
+
confirmLabel?: string;
|
|
28
|
+
/** @deprecated Compact mode is no longer supported. Ignored. */
|
|
29
|
+
compact?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use `import { UnifiedColorPicker } from "./color-picker"` instead.
|
|
34
|
+
*/
|
|
35
|
+
export default function ColorPicker({
|
|
36
|
+
color,
|
|
37
|
+
onChange,
|
|
38
|
+
onClose,
|
|
39
|
+
confirmLabel = "Confirm",
|
|
40
|
+
}: ColorPickerProps) {
|
|
41
|
+
const handleClose = onClose ?? (() => {});
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<UnifiedColorPicker
|
|
45
|
+
value={isValidHex(color) ? color : "#ffffff"}
|
|
46
|
+
onChange={(val) => onChange(typeof val === "string" ? val : "#000000")}
|
|
47
|
+
onClose={handleClose}
|
|
48
|
+
confirmLabel={confirmLabel}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|