/* Shared portfolio components — Titlebar, Statusbar, Footer, ProjectThumb */
const { useState, useEffect, useRef } = React;
// Tweakable defaults
window.PORTFOLIO_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"theme": "dark",
"accent": "#ec5e94",
"density": "comfortable"
}/*EDITMODE-END*/;
function applyTweaks(t) {
document.documentElement.setAttribute('data-theme', t.theme);
document.documentElement.setAttribute('data-density', t.density);
document.documentElement.style.setProperty('--accent', t.accent);
}
// Apply on load — read persisted theme override if present
(function initTweaks(){
const saved = (typeof localStorage !== 'undefined') ? localStorage.getItem('gg-theme') : null;
const t = { ...window.PORTFOLIO_TWEAK_DEFAULTS };
if (saved === 'light' || saved === 'dark' || saved === 'dim') t.theme = saved;
applyTweaks(t);
window.PORTFOLIO_TWEAK_DEFAULTS = t;
})();
function ThemeToggle() {
const [theme, setTheme] = useState(() => document.documentElement.getAttribute('data-theme') || 'dark');
function toggle() {
const next = theme === 'dark' ? 'light' : 'dark';
setTheme(next);
document.documentElement.setAttribute('data-theme', next);
try { localStorage.setItem('gg-theme', next); } catch (e) {}
// notify any listening Tweaks panels
try { window.parent?.postMessage({ type: '__edit_mode_set_keys', edits: { theme: next } }, '*'); } catch (e) {}
}
return (
);
}
function Titlebar({ active, path }) {
const isInProjects = path?.startsWith('projects/');
const indexHref = isInProjects ? '../index.html' : 'index.html';
const aboutHref = isInProjects ? '../about.html' : 'about.html';
const contactHref = isInProjects ? '../contact.html' : 'contact.html';
const projectsHref = isInProjects ? '../projects.html' : 'projects.html';
const projectsActive = active === 'projects' || active === 'work' || active === 'school';
return (
);
}
function Statusbar({ extra }) {
const year = new Date().getFullYear();
return (
);
}
// Realistic miniature UI mockups for each project — small product previews.
// Each one mimics the actual app's layout at thumbnail scale.
function ProjectThumb({ vibe, accent, accentSoft, title }) {
const common = { width: '100%', height: '100%', display: 'block' };
const BG = "#0d0e10";
const PANEL = "#16181c";
const PANEL_2 = "#1c1e23";
const LINE = "#26282e";
const TEXT_DIM = "#3a3d45";
const TEXT_MUTE = "#5a5d66";
// EVA — chat-style agent surface with sidebar of agents and a trace rail
if (vibe === 'agents') {
return (
);
}
// TMT — Windows desktop app: ribbon, file browser, queue table
if (vibe === 'desktop') {
return (
);
}
// Scrum Memories — recall-style memory feed
if (vibe === 'memory') {
return (
);
}
// Translation Verifier — CLI / diff output style
if (vibe === 'verify') {
return (
);
}
// Translation Model Dashboard — stats panel with metrics, sparkline, model rows
if (vibe === 'dashboard') {
return (
);
}
// Notes Collector — pipeline / job runner
if (vibe === 'scrape') {
return (
);
}
// Gengo Timer — focus app, large timer + task list
if (vibe === 'timer') {
return (
);
}
if (vibe === 'stitch') {
// StitchKit — clothing pack IDE: sidebar with component slots,
// grid of drawables with texture variants, validation panel.
const slots = [
{ id: '03', code: 'uppr', n: 12, on: false },
{ id: '04', code: 'lowr', n: 8, on: false },
{ id: '06', code: 'feet', n: 6, on: false },
{ id: '11', code: 'jbib', n: 18, on: true },
{ id: '08', code: 'accs', n: 4, on: false },
];
return (
);
}
if (vibe === 'city') {
// Mantra RP — FiveM RP server status board: live player count,
// factions roster, recent activity feed, server rules nav.
const factions = [
{ code: 'PD', name: 'Police Dept', count: 14 },
{ code: 'EMS', name: 'Emergency Med', count: 6 },
{ code: 'DOJ', name: 'Justice Dept', count: 4 },
{ code: 'CIV', name: 'Civilian', count: 38 },
{ code: 'BIZ', name: 'Player Biz', count: 11 },
];
const events = [
{ t: 'just now', c: '#10b981', txt: 'Officer Reyes signed in to PD shift' },
{ t: '2m ago', c: '#10b981', txt: 'New civilian application — approved' },
{ t: '5m ago', c: '#f59e0b', txt: 'Bank robbery — Vinewood · resolved' },
{ t: '12m ago', c: '#10b981', txt: 'Mantra Coffee — opened for the day' },
];
return (
);
}
return null;
}
/* =====================================================================
ProjectStage — Rescale-style "stage" wrapper around ProjectThumb.
- Saturated pink gradient background
- Vertical project wordmark down the left edge
- Glassy 3D blob shapes (Rescale-style)
- Window chrome (macOS or Windows) around the actual UI mockup
- Optional 3D tilt for featured cards
- Featured cards include title overlay inside the stage
===================================================================== */
function GGMark() {
return (
);
}
function GlassBlob({ variant = 1 }) {
// Rescale-style 3D candy shape: gradient orb with soft highlight + shadow.
const id = `gb${variant}`;
return (
);
}
function ProjectStage({ p, featured = false, chrome = "mac" }) {
const tiltClass = featured ? "stage-tilt" : "";
const word = p.slug.split('-')[0];
return (
{/* glassy 3D blobs */}
{featured && }
{featured && }
{/* vertical wordmark down the left */}
{word}
{/* GG monogram top-left */}
{/* small index pill top-right */}
{p.num}
/
07
{/* the actual UI mockup, framed in window chrome */}
{/* Featured: full-bleed title overlay sits below the screen */}
{featured && (
★ Featured · {p.role} · {p.year}
{p.title.split(p.italWord)[0]}
{p.italWord}
{p.title.split(p.italWord)[1]}
{p.summary}
{p.tags.slice(0,3).join(' · ')}
Read case study →
)}
{/* Smaller cards: caption strip at the bottom */}
{!featured && (
{p.role} · {p.year}
{p.tags.slice(0,2).join(' · ')}
)}
);
}
function WindowChrome({ kind = "mac", title = "", children }) {
if (kind === "win") {
return (
);
}
return (
);
}
window.Titlebar = Titlebar;
window.Statusbar = Statusbar;
window.ThemeToggle = ThemeToggle;
window.ProjectThumb = ProjectThumb;
window.ProjectStage = ProjectStage;
window.WindowChrome = WindowChrome;
window.GGMark = GGMark;
window.applyTweaks = applyTweaks;