Skip to content

feat: dual-skin system — all home components XP ↔ Sci-Fi - #2

Open
bravohenry wants to merge 2 commits into
masterfrom
v0/bravohenry-9d70ad2c
Open

feat: dual-skin system — all home components XP ↔ Sci-Fi#2
bravohenry wants to merge 2 commits into
masterfrom
v0/bravohenry-9d70ad2c

Conversation

@bravohenry

@bravohenry bravohenry commented Apr 1, 2026

Copy link
Copy Markdown
Owner

Summary

Completes the dual-skin system so every homepage component renders faithfully in both Windows XP and Sci-Fi modes. Toggling the skin button in the header switches the entire UI instantly, persisted across sessions.

Changes

New files

  • src/components/home/HomeGrid.tsx — client layout bridge that swaps WidgetPanel (XP) vs ActiveModules (sci-fi) in the left column, and renders XPTaskbar only in XP mode

Updated components

File Change
src/app/page.tsx Uses HomeGrid to delegate skin-conditional layout
src/components/home/IdentityMatrix.tsx XP: classic list-view groupbox; Sci-Fi: glass panel
src/components/home/TypeWriter.tsx XP: Tahoma monospace; Sci-Fi: VT323 neon
src/components/home/ArcReactor.tsx XP: Media Player chrome + controls bar; Sci-Fi: original rings + crosshair
src/components/home/Diagnostics.tsx XP: CMD bar chart + list-view table; Sci-Fi: original glass bars
src/components/home/CoreDirectives.tsx XP: bordered cards + toolbar footer; Sci-Fi: original directive stagger
src/components/home/ActiveModules.tsx XP: detail-view table with XP titlebar; Sci-Fi: jittery progress bars
src/components/home/Terminal.tsx XP: CMD window (existing); Sci-Fi: original glass panel with VT323
src/components/home/XPTaskbar.tsx Returns null in sci-fi skin
src/components/ui/TechBorder.tsx Trimmed to shared primitive (glass-panel + corner accents)
src/components/home/CLAUDE.md Updated member docs

Result

  • data-skin="xp" → full Windows XP UI (all panels, taskbar, CMD terminal)
  • data-skin="scifi" → original dark neon sci-fi UI restored
  • Build: ✅ pnpm next build passes with no errors

Restyle global CSS tokens, components, and layout for XP theme.

Co-authored-by: Zihan Huang <84128702+bravohenry@users.noreply.github.com>
@vercel vercel Bot added the v0 label Apr 1, 2026
@vercel

vercel Bot commented Apr 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
friday-portfolio Ready Ready Preview, Comment Apr 1, 2026 8:47pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 733e976b22

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/styles/globals.css
Comment on lines +49 to 52
* CSS CUSTOM PROPERTIES — Windows XP Luna theme
* -------------------------------------------------------------------------- */

:root {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reintroduce light-theme overrides for data-theme toggling

The global theme refactor now defines only :root variables, but ThemeToggle still flips data-theme between dark and light (src/components/ui/ThemeToggle.tsx). Because the [data-theme="light"] overrides were removed, toggling persists state and changes the icon/label but no visual theme tokens actually change, so the theme switcher is effectively broken for users. Please restore a light-theme override block (or remove/disable the toggle behavior) so the control has a real effect.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restyles the FRI dashboard UI from the current neon/glass aesthetic to a Windows XP “Luna” look-and-feel, introducing XP window chrome (titlebars, beveled borders), XP typography, and an XP taskbar.

Changes:

  • Replaces global design tokens and core UI styling in globals.css to implement XP Luna colors, fonts, chrome, scrollbars, and controls.
  • Updates multiple home dashboard components to render XP-like titlebars/menu bars and CMD-style terminal presentation.
  • Adds a new XPTaskbar component and mounts it on the homepage.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/styles/globals.css Rebuilds global theme variables and adds XP chrome/control styles.
src/components/ui/ThemeToggle.tsx Restyles the toggle as an XP window-control button with glyph icons.
src/components/ui/TechBorder.tsx Changes TechBorder wrapper styling to XP window chrome and adds optional title.
src/components/home/XPTaskbar.tsx Introduces a new XP taskbar + Start menu component.
src/components/home/WidgetPanel.tsx Reworks widget panel UI into an XP window with tabs/status bar styling.
src/components/home/TypeWriter.tsx Updates typography/styling to match XP console-like text.
src/components/home/Terminal.tsx Converts terminal panel to CMD-style titlebar/body and XP input/button styling.
src/components/home/SystemHeader.tsx Converts header to XP titlebar + menubar layout with window controls.
src/components/home/IdentityMatrix.tsx Converts panel to XP titlebar and XP list/status-bar styling.
src/components/home/Diagnostics.tsx Converts diagnostics to XP titlebar + groupbox/list-view styling.
src/components/home/CoreDirectives.tsx Converts directives panel to XP titlebar + toolbar/footer styling.
src/components/home/ArcReactor.tsx Wraps center display in an XP window layout and adds XP-like bottom controls bar.
src/app/page.tsx Integrates XP taskbar and tightens grid spacing/padding.
src/app/layout.tsx Updates metadata title and forces XP font styling on <body>.
node_modules/.package-lock.json Updates a tracked lockfile under node_modules/ (should not be committed).
Comments suppressed due to low confidence (1)

src/components/home/SystemHeader.tsx:46

  • The clock format now only shows hours/minutes (no seconds), but the component still re-renders every 1s (setInterval(tick, 1000)). Consider updating less frequently (e.g., every 10–60s) to reduce unnecessary renders.
const clockFmt = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  hour12: true,
  hour: "numeric",
  minute: "2-digit",
});

/* ------------------------------------------------------------------ */
/*  Component                                                          */
/* ------------------------------------------------------------------ */

export function SystemHeader({ totalEntries, totalWords, daysSinceLaunch }: SystemHeaderProps) {
  const [clock, setClock] = useState("");

  /* --- NYC clock (1 s) --- */
  useEffect(() => {
    const tick = () => setClock(clockFmt.format(new Date()));
    tick();
    const id = setInterval(tick, 1000);
    return () => clearInterval(id);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 505 to 580
@@ -521,23 +553,29 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.2.4"
"@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
"node_modules/@img/sharp-linuxmusl-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
"arm64"
"x64"
],
"license": "LGPL-3.0-or-later",
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file lives under node_modules/ (which is gitignored) and shouldn’t be tracked/modified in the repo. Please remove node_modules/.package-lock.json from version control (and from this PR), and ensure dependency changes are reflected only in the root package-lock.json.

Copilot uses AI. Check for mistakes.
Comment on lines 299 to 313
return (
<div
id="session-panel"
className={`h-1/3 min-h-[200px] md:min-h-0 glass-panel tech-border rounded-t-lg p-4 md:p-6 flex flex-col justify-end mt-4 ${
className={`h-1/3 min-h-[200px] md:min-h-0 flex flex-col mt-4 ${
expanded ? "expanded" : ""
}`}
style={{
border: '1px solid #848484',
boxShadow: '1px 1px 0 #ffffff inset, -1px -1px 0 #808080 inset',
}}
>
{/* Corner decorations */}
<div className="corner-bl absolute bottom-0 left-0 w-3 h-3" />
<div className="corner-br absolute bottom-0 right-0 w-3 h-3" />

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corner-bl / corner-br are absolutely positioned, but the #session-panel container is no longer position: relative (previously provided by .glass-panel). This will cause the corner decorations to position relative to the page instead of the terminal panel. Add a relative class (or otherwise set position: relative) on the #session-panel container, or remove the absolute corner elements.

Copilot uses AI. Check for mistakes.
Comment thread src/components/home/SystemHeader.tsx Outdated
<div className="flex items-center gap-2">
<span className="status-dot w-2 h-2 animate-pulse shrink-0" style={{ background: 'var(--text-status)' }} />
<span className="hidden sm:inline">SYSTEM ONLINE</span>
<header className="flex-none z-50 xp-window" style={{ borderRadius: '0 !important' }}>

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React inline styles don’t support !important, so style={{ borderRadius: '0 !important' }} is invalid and will be ignored by the browser. If you need to enforce no rounding here, rely on the global border-radius: 0 !important rule or move this override into a CSS class.

Suggested change
<header className="flex-none z-50 xp-window" style={{ borderRadius: '0 !important' }}>
<header className="flex-none z-50 xp-window" style={{ borderRadius: 0 }}>

Copilot uses AI. Check for mistakes.
Comment on lines 17 to 40
interface SystemHeaderProps {
totalEntries: number;
totalWords: number;
daysSinceLaunch: number;
}

/* ------------------------------------------------------------------ */
/* Constants */
/* ------------------------------------------------------------------ */

const clockFmt = new Intl.DateTimeFormat("en-US", {
timeZone: "America/New_York",
hour12: false,
hour: "2-digit",
hour12: true,
hour: "numeric",
minute: "2-digit",
second: "2-digit",
});

/* ------------------------------------------------------------------ */
/* Component */
/* ------------------------------------------------------------------ */

export function SystemHeader({ totalEntries, totalWords }: SystemHeaderProps) {
export function SystemHeader({ totalEntries, totalWords, daysSinceLaunch }: SystemHeaderProps) {
const [clock, setClock] = useState("");

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

daysSinceLaunch is included in the component props/destructuring but isn’t used anywhere in the render. This will typically trigger an unused-var lint warning and makes the public props harder to reason about. Either use it in the UI or remove it from the destructured props (and possibly from the interface if no longer needed).

Copilot uses AI. Check for mistakes.
Comment on lines 19 to 28
return (
<div className={`glass-panel tech-border rounded-sm relative ${className}`}>
<div className={`xp-window relative ${className}`}>
{title && (
<div className="xp-panel-title">
{title}
</div>
)}
<div className="corner-bl absolute bottom-0 left-0 w-3 h-3" />
<div className="corner-br absolute bottom-0 right-0 w-3 h-3" />
{children}

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TechBorder no longer includes the tech-border class, but it still renders .corner-bl / .corner-br elements that are only styled under .tech-border > .corner-* in globals.css. As a result, these corner divs will have no visible styling. Either re-add the tech-border class to the wrapper or remove the corner elements (and/or the now-dead .tech-border CSS).

Copilot uses AI. Check for mistakes.
Comment on lines 12 to 39
export function ThemeToggle() {
const [theme, setTheme] = useState<"dark" | "light">("dark");

useEffect(() => {
const saved = localStorage.getItem("fri-theme") as "dark" | "light" | null;
const initial = saved || "dark";
setTheme(initial);
document.documentElement.setAttribute("data-theme", initial);
}, []);

const toggle = useCallback(() => {
const next = theme === "dark" ? "light" : "dark";
setTheme(next);
document.documentElement.setAttribute("data-theme", next);
localStorage.setItem("fri-theme", next);
}, [theme]);

return (
<button
type="button"
onClick={toggle}
className="p-1.5 transition-colors hover:opacity-80"
className="xp-btn-min"
style={{ width: '21px', height: '21px', fontSize: '10px' }}
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
title={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
>
<img
src={
theme === "dark"
? "https://unpkg.com/pixelarticons@1.8.1/svg/sun.svg"
: "https://unpkg.com/pixelarticons@1.8.1/svg/moon.svg"
}
className="pa-icon w-4 h-4 inline-block"
alt=""
aria-hidden="true"
/>
{theme === "dark" ? "☀" : "☾"}
</button>

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theme toggling still flips data-theme between dark and light, but globals.css no longer defines any [data-theme="light"] overrides. That makes the button state/ARIA label misleading and suggests a feature regression. Either reintroduce per-theme CSS variables, or update ThemeToggle to reflect what it actually toggles (or remove it if the XP theme is intended to be fixed).

Copilot uses AI. Check for mistakes.
Comment thread src/components/home/CoreDirectives.tsx Outdated
Comment on lines +19 to +34
color: "#0a246a",
},
{
title: "Honesty / 诚实",
body: "Truthful analysis, even when it stings a little.",
color: "#0a246a",
},
{
title: "Privacy / 隐私",
body: "Absolute data integrity. Trust is earned, not demanded.",
color: "#0a246a",
},
{
title: "Autonomy / 自主",
body: "Proactive when needed, reserved when not. Always present.",
color: "#0a246a",

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each directive now includes a color field, but the render path doesn’t use it (all colors are hard-coded in styles below). If the per-directive color is no longer needed, remove the field; otherwise, wire it into the styles so this data doesn’t become stale/dead.

Suggested change
color: "#0a246a",
},
{
title: "Honesty / 诚实",
body: "Truthful analysis, even when it stings a little.",
color: "#0a246a",
},
{
title: "Privacy / 隐私",
body: "Absolute data integrity. Trust is earned, not demanded.",
color: "#0a246a",
},
{
title: "Autonomy / 自主",
body: "Proactive when needed, reserved when not. Always present.",
color: "#0a246a",
},
{
title: "Honesty / 诚实",
body: "Truthful analysis, even when it stings a little.",
},
{
title: "Privacy / 隐私",
body: "Absolute data integrity. Trust is earned, not demanded.",
},
{
title: "Autonomy / 自主",
body: "Proactive when needed, reserved when not. Always present.",

Copilot uses AI. Check for mistakes.
<Link
key={entry.slug}
href={`/daily/${entry.slug}`}
className="group flex gap-2 p-2 transition-all block"

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Link has both flex and block display utilities in the same className (... flex ... block). These utilities conflict and whichever wins in Tailwind’s generated CSS will control layout, potentially breaking the row alignment. Remove the redundant/conflicting display class (likely drop block).

Suggested change
className="group flex gap-2 p-2 transition-all block"
className="group flex gap-2 p-2 transition-all"

Copilot uses AI. Check for mistakes.
Comment thread src/app/layout.tsx
Comment on lines 8 to 31
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import { GeistPixelSquare } from "geist/font/pixel";
import "@/styles/globals.css";

export const metadata: Metadata = {
title: "FRI Interface v3.28",
title: "FRI Interface v3.28 — Windows Edition",
description: "Intelligent Assistant — Portfolio Shell for Friday",
icons: { icon: "/favicon.png" },
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={`${GeistSans.variable} ${GeistMono.variable} ${GeistPixelSquare.variable}`} suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: `try{var t=localStorage.getItem('fri-theme');if(t)document.documentElement.setAttribute('data-theme',t)}catch(e){}` }} />
</head>
<body className="font-suse">{children}</body>
<body style={{ fontFamily: "'Tahoma', 'Trebuchet MS', sans-serif", fontSize: '11px' }}>{children}</body>
</html>

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layout.tsx still imports Geist fonts and applies their CSS variables to <html>, but the <body> now forces a different font family via inline style. This means the Geist font preload/code is likely unused, adding bundle/FOIT overhead. Consider either removing the Geist font imports/variables if XP fonts are the new global default, or applying the font choice via Tailwind/CSS so you can keep a consistent font strategy across the app.

Copilot uses AI. Check for mistakes.
Implement dual-skin theme with XP and sci-fi options.

Co-authored-by: Zihan Huang <84128702+bravohenry@users.noreply.github.com>
@vercel vercel Bot changed the title Windows XP Luna Theme for FRI Interface feat: dual-skin system — all home components XP ↔ Sci-Fi Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants