Phase 3: Aura Agentic AI public site — rebrand, services, bilingual, n8n contact form, Dockerfile

This commit is contained in:
khondokartowsif171
2026-04-27 18:02:25 +06:00
commit 27cdba1cac
16 changed files with 3266 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
// Command palette — ⌘K
const CommandPalette = ({ open, onClose }) => {
const D = PORTFOLIO_DATA;
const [q, setQ] = React.useState("");
const [active, setActive] = React.useState(0);
const inputRef = React.useRef(null);
React.useEffect(() => {
if (open) {
setQ("");
setActive(0);
setTimeout(() => inputRef.current?.focus(), 50);
}
}, [open]);
const items = React.useMemo(() => {
const base = [
{ group: "Navigate", label: "Go to Work", icon: Icons.Cube, action: () => location.hash = "#work" },
{ group: "Navigate", label: "Go to Stack", icon: Icons.Code, action: () => location.hash = "#stack" },
{ group: "Navigate", label: "Go to Agent showcase", icon: Icons.Sparkles, action: () => location.hash = "#agents" },
{ group: "Navigate", label: "Go to Timeline", icon: Icons.Bolt, action: () => location.hash = "#timeline" },
{ group: "Navigate", label: "Go to Contact", icon: Icons.Mail, action: () => location.hash = "#contact" },
...D.projects.map(p => ({
group: "Projects",
label: p.name,
sub: p.kind,
icon: Icons.Cube,
action: () => location.hash = "#work",
})),
{ group: "Action", label: "Email me directly", icon: Icons.Mail, action: () => window.location.href = `mailto:${D.brand.email}` },
{ group: "Action", label: "Open GitHub", icon: Icons.Github, action: () => window.open(D.brand.socials.github) },
{ group: "Action", label: "Open LinkedIn", icon: Icons.LinkedIn, action: () => window.open(D.brand.socials.linkedin) },
{ group: "Action", label: "Toggle theme", icon: Icons.Sun, action: () => window.dispatchEvent(new CustomEvent("aura:toggle-theme")) },
];
if (!q) return base;
const Q = q.toLowerCase();
return base.filter(i => i.label.toLowerCase().includes(Q) || (i.sub && i.sub.toLowerCase().includes(Q)));
}, [q]);
React.useEffect(() => { setActive(0); }, [q]);
const onKey = (e) => {
if (e.key === "ArrowDown") { e.preventDefault(); setActive(a => Math.min(a + 1, items.length - 1)); }
else if (e.key === "ArrowUp") { e.preventDefault(); setActive(a => Math.max(a - 1, 0)); }
else if (e.key === "Enter") { e.preventDefault(); if (items[active]) { items[active].action(); onClose(); } }
else if (e.key === "Escape") { onClose(); }
};
if (!open) return null;
// group items
const groups = items.reduce((acc, it, idx) => {
(acc[it.group] = acc[it.group] || []).push({ ...it, idx });
return acc;
}, {});
return (
<div onClick={onClose} style={{
position: "fixed", inset: 0, zIndex: 100,
background: "color-mix(in srgb, #000 60%, transparent)",
backdropFilter: "blur(6px)",
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
paddingTop: "12vh",
animation: "float-up 0.2s",
}}>
<div onClick={e => e.stopPropagation()} style={{
width: "min(640px, 92vw)",
background: "var(--bg-card)",
border: "1px solid var(--line-strong)",
borderRadius: 14,
boxShadow: "var(--shadow-lg)",
overflow: "hidden",
}}>
<div style={{
display: "flex",
alignItems: "center",
gap: 10,
padding: "14px 18px",
borderBottom: "1px solid var(--line)",
}}>
<Icons.Search size={15} style={{ color: "var(--text-faint)" }} />
<input
ref={inputRef}
value={q}
onChange={e => setQ(e.target.value)}
onKeyDown={onKey}
placeholder="Search projects, sections, actions…"
style={{
flex: 1,
background: "transparent",
border: "none",
outline: "none",
color: "var(--text)",
fontSize: 15,
fontFamily: "inherit",
}}
/>
<kbd style={{
fontFamily: "var(--font-mono)",
fontSize: 11,
padding: "2px 6px",
border: "1px solid var(--line)",
borderRadius: 4,
color: "var(--text-faint)",
}}>esc</kbd>
</div>
<div style={{ maxHeight: 420, overflowY: "auto", padding: 8 }}>
{items.length === 0 && (
<div style={{ padding: 24, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
No matches for "{q}"
</div>
)}
{Object.entries(groups).map(([group, gitems]) => (
<div key={group} style={{ marginBottom: 8 }}>
<div style={{
padding: "10px 12px 6px",
fontFamily: "var(--font-mono)",
fontSize: 10.5,
color: "var(--text-faint)",
textTransform: "uppercase",
letterSpacing: "0.08em",
}}>{group}</div>
{gitems.map(it => {
const I = it.icon;
const on = it.idx === active;
return (
<button
key={it.label}
onMouseEnter={() => setActive(it.idx)}
onClick={() => { it.action(); onClose(); }}
style={{
width: "100%",
display: "flex",
alignItems: "center",
gap: 12,
padding: "10px 12px",
background: on ? "var(--bg-elev)" : "transparent",
border: "none",
borderRadius: 8,
color: "var(--text)",
fontSize: 13.5,
textAlign: "left",
}}>
<I size={14} style={{ color: on ? "var(--accent)" : "var(--text-faint)" }} />
<span style={{ flex: 1 }}>{it.label}</span>
{it.sub && <span style={{ fontSize: 11, color: "var(--text-faint)" }}>{it.sub}</span>}
{on && <Icons.Arrow size={12} style={{ color: "var(--text-faint)" }} />}
</button>
);
})}
</div>
))}
</div>
<div style={{
display: "flex",
gap: 16,
padding: "10px 16px",
borderTop: "1px solid var(--line)",
background: "var(--bg-elev)",
fontSize: 11,
fontFamily: "var(--font-mono)",
color: "var(--text-faint)",
}}>
<span>↑↓ navigate</span>
<span>↵ select</span>
<span>esc dismiss</span>
</div>
</div>
</div>
);
};
window.CommandPalette = CommandPalette;