import React, { useState } from “react”;
const initialMicroApps = [
{
id: “1”,
author: “VibeBot”,
createdAt: new Date().toISOString(),
likes: 12,
liked: false,
saved: false,
shareSlug: “ocean-focus-mode”,
vibeCode: {
emoji: “🌊”,
title: “Ocean Focus Mode”,
message: “30 minutes of deep, distraction-free work. Phone on DND.”,
moodColor: “#2563eb”,
intensity: 0.8,
tag: “focus”
}
},
{
id: “2”,
author: “VibeBot”,
createdAt: new Date().toISOString(),
likes: 7,
liked: false,
saved: false,
shareSlug: “midnight-soft-reset”,
vibeCode: {
emoji: “🌙”,
title: “Midnight Soft Reset”,
message: “Short walk, stretch, and 10 minutes of journaling.”,
moodColor: “#4b5563”,
intensity: 0.5,
tag: “reset”
}
},
{
id: “3”,
author: “VibeBot”,
createdAt: new Date().toISOString(),
likes: 20,
liked: false,
saved: false,
shareSlug: “chaotic-good-energy”,
vibeCode: {
emoji: “⚡️”,
title: “Chaotic Good Energy”,
message: “Put on a hype playlist, ship something small, post it.”,
moodColor: “#f97316”,
intensity: 1,
tag: “energy”
}
}
];
const tabs = [
{ id: “feed”, label: “Feed” },
{ id: “saved”, label: “Saved” },
{ id: “mine”, label: “My Creations” }
];
function App() {
const [microApps, setMicroApps] = useState(initialMicroApps);
const [activeTab, setActiveTab] = useState(“feed”);
const [showCreator, setShowCreator] = useState(false);
const [filterText, setFilterText] = useState(“”);
const [newVibe, setNewVibe] = useState({
emoji: “✨”,
title: “”,
message: “”,
moodColor: “#6366f1”,
intensity: 0.7,
tag: “custom”
});
const myAuthorName = “You”; // in a real app this would be the logged in user
const handleToggleLike = (id) => {
setMicroApps((prev) =>
prev.map((m) => {
if (m.id !== id) return m;
const liked = !m.liked;
const likes = liked ? m.likes + 1 : m.likes – 1;
return { …m, liked, likes: Math.max(0, likes) };
})
);
};
const handleToggleSave = (id) => {
setMicroApps((prev) =>
prev.map((m) => (m.id === id ? { …m, saved: !m.saved } : m))
);
};
const handleShare = async (microApp) => {
const shareUrl = `${window.location.origin}/vibe/${microApp.shareSlug || microApp.id}`;
const shareText = `${microApp.vibeCode.title} – a micro vibe from ${microApp.author}`;
if (navigator.share) {
try {
await navigator.share({
title: microApp.vibeCode.title,
text: shareText,
url: shareUrl
});
} catch (err) {
console.log(“Share cancelled or failed”, err);
}
} else if (navigator.clipboard) {
try {
await navigator.clipboard.writeText(shareUrl);
alert(“Link copied to clipboard!”);
} catch (err) {
console.log(“Clipboard failed”, err);
}
} else {
alert(`Share this link:\n\n${shareUrl}`);
}
};
const handleNewVibeChange = (field, value) => {
setNewVibe((prev) => ({ …prev, [field]: value }));
};
const handleCreateMicroApp = (e) => {
e.preventDefault();
if (!newVibe.title.trim() || !newVibe.message.trim()) {
alert(“Title and message are required to set the vibe.”);
return;
}
const id = Date.now().toString();
const shareSlug = newVibe.title
.toLowerCase()
.replace(/[^\w]+/g, “-“)
.replace(/^-+|-+$/g, “”) || id;
const microApp = {
id,
author: myAuthorName,
createdAt: new Date().toISOString(),
likes: 0,
liked: false,
saved: true, // auto-save your own creation
shareSlug,
vibeCode: { …newVibe }
};
setMicroApps((prev) => [microApp, …prev]);
setShowCreator(false);
setNewVibe({
emoji: “✨”,
title: “”,
message: “”,
moodColor: “#6366f1”,
intensity: 0.7,
tag: “custom”
});
setActiveTab(“mine”);
};
const visibleMicroApps = microApps.filter((m) => {
if (activeTab === “saved” && !m.saved) return false;
if (activeTab === “mine” && m.author !== myAuthorName) return false;
if (!filterText.trim()) return true;
const q = filterText.toLowerCase();
const v = m.vibeCode;
return (
v.title.toLowerCase().includes(q) ||
v.message.toLowerCase().includes(q) ||
(v.tag && v.tag.toLowerCase().includes(q)) ||
m.author.toLowerCase().includes(q)
);
});
return (
))}
setFilterText(e.target.value)}
style={styles.searchInput}
/>
{visibleMicroApps.length === 0 ? (
) : (
visibleMicroApps.map((m) => (
))
)}
{showCreator && (
)}
);
}
function MicroAppCard({ microApp, onToggleLike, onToggleSave, onShare }) {
const { vibeCode } = microApp;
const borderColor = withAlpha(vibeCode.moodColor, 0.35);
const bgGradient = `linear-gradient(135deg, ${withAlpha(
vibeCode.moodColor,
0.12
)}, rgba(15,23,42,0.9))`;
return (
{vibeCode.title}
•
{vibeCode.tag}
intensity {Math.round(vibeCode.intensity * 100)}%
{vibeCode.message}
);
}
function Modal({ children, onClose }) {
return (
);
}
function VibeCreatorForm({ newVibe, onChange, onSubmit }) {
return (
);
}
// — styles —
const styles = {
appRoot: {
minHeight: “100vh”,
margin: 0,
padding: “24px”,
background: “radial-gradient(circle at top, #1f2937, #020617)”,
fontFamily:
‘-apple-system, BlinkMacSystemFont, system-ui, -system-ui, “Segoe UI”, sans-serif’,
color: “#e5e7eb”,
boxSizing: “border-box”
},
appShell: {
maxWidth: 1100,
margin: “0 auto”,
background: “rgba(15,23,42,0.92)”,
borderRadius: 18,
border: “1px solid rgba(148,163,184,0.35)”,
padding: 20,
boxShadow: “0 24px 80px rgba(0,0,0,0.6)”,
backdropFilter: “blur(16px)”
},
header: {
display: “flex”,
justifyContent: “space-between”,
alignItems: “center”,
gap: 16,
marginBottom: 16
},
brand: {
display: “flex”,
alignItems: “center”,
gap: 10
},
brandLogo: {
width: 36,
height: 36,
borderRadius: “999px”,
background:
“conic-gradient(from 180deg, #a855f7, #6366f1, #22c55e, #eab308, #a855f7)”,
display: “inline-flex”,
alignItems: “center”,
justifyContent: “center”,
color: “#020617”,
fontWeight: 700,
fontSize: 16,
boxShadow: “0 0 0 2px rgba(15,23,42,0.9)”
},
brandTitle: {
fontSize: 18,
fontWeight: 600
},
brandSubtitle: {
fontSize: 12,
color: “#9ca3af”
},
primaryButton: {
borderRadius: 999,
border: “none”,
padding: “10px 18px”,
fontSize: 14,
fontWeight: 600,
cursor: “pointer”,
background:
“linear-gradient(135deg, #6366f1, #a855f7)”,
color: “#f9fafb”,
boxShadow: “0 10px 30px rgba(88,28,135,0.6)”
},
primaryButtonWide: {
borderRadius: 999,
border: “none”,
padding: “10px 16px”,
fontSize: 15,
fontWeight: 600,
cursor: “pointer”,
background: “linear-gradient(135deg, #6366f1, #a855f7)”,
color: “#f9fafb”,
width: “100%”,
marginTop: 8
},
topBar: {
display: “flex”,
gap: 16,
alignItems: “center”,
marginBottom: 16,
flexWrap: “wrap”
},
tabs: {
display: “flex”,
gap: 4,
background: “rgba(15,23,42,0.9)”,
borderRadius: 999,
padding: 3,
border: “1px solid rgba(75,85,99,0.7)”
},
tabButton: {
borderRadius: 999,
border: “none”,
padding: “6px 14px”,
background: “transparent”,
color: “#9ca3af”,
fontSize: 13,
cursor: “pointer”,
fontWeight: 500
},
tabButtonActive: {
background: “rgba(55,65,81,0.9)”,
color: “#f9fafb”
},
searchInput: {
flex: 1,
minWidth: 200,
borderRadius: 999,
border: “1px solid rgba(75,85,99,0.8)”,
background: “rgba(15,23,42,0.9)”,
padding: “8px 12px”,
color: “#e5e7eb”,
fontSize: 13,
outline: “none”
},
grid: {
display: “grid”,
gridTemplateColumns: “repeat(auto-fill, minmax(260px, 1fr))”,
gap: 16
},
card: {
borderRadius: 16,
border: “1px solid rgba(148,163,184,0.4)”,
padding: 14,
display: “flex”,
flexDirection: “column”,
justifyContent: “space-between”,
backgroundColor: “rgba(15,23,42,0.9)”
},
cardHeader: {
display: “flex”,
justifyContent: “space-between”,
gap: 8,
marginBottom: 8
},
cardTitleRow: {
display: “flex”,
alignItems: “center”,
gap: 8
},
cardEmoji: {
fontSize: 26
},
cardTitle: {
fontSize: 15,
margin: 0,
fontWeight: 600
},
cardMeta: {
fontSize: 11,
color: “#9ca3af”,
display: “flex”,
alignItems: “center”,
gap: 4
},
dot: {
opacity: 0.7
},
cardMessage: {
fontSize: 13,
color: “#e5e7eb”,
marginBottom: 10,
marginTop: 2,
lineHeight: 1.4
},
cardFooter: {
display: “flex”,
justifyContent: “space-between”,
alignItems: “center”
},
actionsLeft: {
display: “flex”,
gap: 6
},
iconButton: {
borderRadius: 999,
border: “1px solid rgba(75,85,99,0.9)”,
padding: “4px 10px”,
fontSize: 11,
cursor: “pointer”,
background: “rgba(15,23,42,0.85)”,
color: “#e5e7eb”,
display: “inline-flex”,
alignItems: “center”,
gap: 4
},
iconButtonActive: {
borderColor: “#a855f7”,
background: “rgba(76,29,149,0.6)”
},
intensityPill: {
borderRadius: 999,
border: “1px solid rgba(148,163,184,0.8)”,
padding: “3px 8px”,
fontSize: 11,
color: “#e5e7eb”,
alignSelf: “flex-start”
},
emptyState: {
gridColumn: “1 / -1”,
textAlign: “center”,
padding: “40px 20px”,
color: “#9ca3af”
},
emptyEmoji: {
fontSize: 30,
marginBottom: 8
},
emptyTitle: {
fontSize: 16,
fontWeight: 600,
marginBottom: 4
},
emptySubtitle: {
fontSize: 13
},
modalBackdrop: {
position: “fixed”,
inset: 0,
background: “rgba(15,23,42,0.75)”,
display: “flex”,
alignItems: “center”,
justifyContent: “center”,
padding: 16,
zIndex: 50
},
modal: {
width: “100%”,
maxWidth: 480,
borderRadius: 18,
background: “rgba(15,23,42,0.97)”,
border: “1px solid rgba(148,163,184,0.5)”,
boxShadow: “0 24px 80px rgba(0,0,0,0.7)”,
padding: 16
},
modalHeader: {
display: “flex”,
justifyContent: “space-between”,
alignItems: “center”,
marginBottom: 10
},
modalTitle: {
fontSize: 16,
fontWeight: 600
},
closeButton: {
borderRadius: 999,
border: “none”,
background: “transparent”,
color: “#9ca3af”,
cursor: “pointer”,
fontSize: 16
},
form: {
display: “flex”,
flexDirection: “column”,
gap: 10
},
label: {
fontSize: 12,
color: “#d1d5db”,
display: “flex”,
flexDirection: “column”,
gap: 4
},
input: {
borderRadius: 10,
border: “1px solid rgba(75,85,99,0.8)”,
background: “rgba(15,23,42,0.95)”,
padding: “7px 10px”,
color: “#e5e7eb”,
fontSize: 13,
outline: “none”
},
range: {
width: “100%”
}
};
function withAlpha(hex, alpha) {
// crude hex -> rgba converter, handles #rrggbb
if (!hex || !hex.startsWith(“#”) || (hex.length !== 7 && hex.length !== 4)) {
return `rgba(148,163,184,${alpha})`;
}
let r, g, b;
if (hex.length === 7) {
r = parseInt(hex.slice(1, 3), 16);
g = parseInt(hex.slice(3, 5), 16);
b = parseInt(hex.slice(5, 7), 16);
} else {
r = parseInt(hex[1] + hex[1], 16);
g = parseInt(hex[2] + hex[2], 16);
b = parseInt(hex[3] + hex[3], 16);
}
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
export default App;