Fazekit

Code

"use client";
import * as React from "react";
import { AnimatePresence, MotionConfig, motion } from "motion/react";
import { cn } from "@/lib/utils";
import { defaultOsData, type AppId, type OsData, type WallpaperId } from "./data";
import { BootScreen, Spotlight } from "./sections/chrome";
import { Desktop, type WindowActions } from "./sections/desktop";
import { Phone } from "./sections/phone";
import { OsContext, Wallpaper, useIsNarrow, type OsApi } from "./sections/ui";
import { MIN_H, MIN_W, type Bounds, type WinState } from "./sections/window";

export type { OsData, AppId, WallpaperId };
export { defaultOsData };

export interface DesktopOsPortfolioTemplateProps {
  /** Override any content group (person, apps, projects, tracks…). Missing groups use the demo content. */
  data?: Partial<OsData>;
  /** Apps opened right after boot on desktop. Default: About. */
  initialApps?: AppId[];
  initialWallpaper?: WallpaperId;
  /** Skip the boot animation (it is always skipped with reduced motion). */
  skipBoot?: boolean;
  /** Called when the Contact app's form is submitted. */
  onContact?: (msg: { name: string; email: string; message: string }) => Promise<void> | void;
  /** Sizing is up to you — by default the OS fills the viewport. */
  className?: string;
}

type Action =
  | { type: "open"; id: AppId; bounds: Bounds; size: [number, number] }
  | { type: "close"; id: AppId }
  | { type: "focus"; id: AppId }
  | { type: "minimize"; id: AppId }
  | { type: "toggleMax"; id: AppId }
  | { type: "commit"; id: AppId; g: { x: number; y: number; w: number; h: number } }
  | { type: "fit"; bounds: Bounds }
  | { type: "reset" };

type WmState = { wins: WinState[]; z: number; opened: number };

function wm(s: WmState, a: Action): WmState {
  switch (a.type) {
    case "open": {
      const found = s.wins.find((w) => w.id === a.id);
      if (found) return { ...s, z: s.z + 1, wins: s.wins.map((w) => (w.id === a.id ? { ...w, minimized: false, z: s.z + 1 } : w)) };
      const w = Math.max(MIN_W, Math.min(a.size[0], a.bounds.w - 24));
      const h = Math.max(MIN_H, Math.min(a.size[1], a.bounds.h - 16));
      const off = (s.opened % 6) * 28;
      const x = Math.max(8, Math.min(a.bounds.w - w - 8, Math.round((a.bounds.w - w) / 2) - 80 + off));
      const y = Math.max(8, Math.min(a.bounds.h - h - 8, Math.round((a.bounds.h - h) / 2) - 30 + off));
      return { z: s.z + 1, opened: s.opened + 1, wins: [...s.wins, { id: a.id, x, y, w, h, z: s.z + 1, minimized: false, maximized: false }] };
    }
    case "close":
      return { ...s, wins: s.wins.filter((w) => w.id !== a.id) };
    case "focus": {
      const top = s.wins.reduce((m, w) => Math.max(m, w.z), 0);
      const cur = s.wins.find((w) => w.id === a.id);
      if (!cur || cur.z === top) return s;
      return { ...s, z: s.z + 1, wins: s.wins.map((w) => (w.id === a.id ? { ...w, z: s.z + 1 } : w)) };
    }
    case "minimize":
      return { ...s, wins: s.wins.map((w) => (w.id === a.id ? { ...w, minimized: true } : w)) };
    case "toggleMax":
      return { ...s, z: s.z + 1, wins: s.wins.map((w) => (w.id === a.id ? { ...w, maximized: !w.maximized, minimized: false, z: s.z + 1 } : w)) };
    case "commit":
      return { ...s, wins: s.wins.map((w) => (w.id === a.id ? { ...w, ...a.g } : w)) };
    case "fit":
      // Keep windows reachable when the desktop shrinks.
      return {
        ...s,
        wins: s.wins.map((w) => {
          const nw = Math.min(w.w, Math.max(MIN_W, a.bounds.w - 16));
          const nh = Math.min(w.h, Math.max(MIN_H, a.bounds.h - 8));
          return { ...w, w: nw, h: nh, x: Math.max(0, Math.min(w.x, a.bounds.w - nw)), y: Math.max(0, Math.min(w.y, a.bounds.h - nh)) };
        }),
      };
    case "reset":
      return { wins: [], z: 0, opened: 0 };
  }
}

export function DesktopOsPortfolioTemplate({ data, initialApps = ["about"], initialWallpaper = "dune", skipBoot = false, onContact, className }: DesktopOsPortfolioTemplateProps) {
  const d = React.useMemo(() => ({ ...defaultOsData, ...data }), [data]);
  const rootRef = React.useRef<HTMLDivElement>(null);
  const narrow = useIsNarrow(rootRef);
  const [booting, setBooting] = React.useState(!skipBoot);
  const [bootKey, setBootKey] = React.useState(0);
  const [wallpaper, setWallpaper] = React.useState<WallpaperId>(initialWallpaper);
  const [state, dispatch] = React.useReducer(wm, { wins: [], z: 0, opened: 0 });
  const [bounds, setBounds] = React.useState<Bounds>({ w: 1200, h: 700 });
  const [phoneApp, setPhoneApp] = React.useState<AppId | null>(null);
  const [spotlight, setSpotlight] = React.useState(false);
  const [payload, setPayload] = React.useState<OsApi["payload"]>({});
  const nonce = React.useRef(0);
  const boundsRef = React.useRef(bounds);
  const didInitial = React.useRef(false);
  const initialRef = React.useRef(initialApps);

  const onBounds = React.useCallback((b: Bounds) => {
    boundsRef.current = b;
    setBounds(b);
    dispatch({ type: "fit", bounds: b });
  }, []);

  const open = React.useCallback(
    (id: AppId, value?: string) => {
      if (value !== undefined) setPayload((p) => ({ ...p, [id]: { value, n: ++nonce.current } }));
      const app = d.apps.find((a) => a.id === id);
      if (!app) return;
      if (narrow) setPhoneApp(id);
      else dispatch({ type: "open", id, bounds: boundsRef.current, size: app.size });
    },
    [d.apps, narrow],
  );

  const close = React.useCallback(
    (id: AppId) => {
      setPayload((p) => ({ ...p, [id]: undefined }));
      if (narrow) setPhoneApp((cur) => (cur === id ? null : cur));
      else dispatch({ type: "close", id });
    },
    [narrow],
  );

  const restart = React.useCallback(() => {
    setSpotlight(false);
    dispatch({ type: "reset" });
    setPhoneApp(null);
    didInitial.current = false;
    setBootKey((k) => k + 1);
    setBooting(true);
  }, []);

  // Open the initial apps once the desktop is ready.
  React.useEffect(() => {
    if (booting || narrow !== false || didInitial.current) return;
    didInitial.current = true;
    window.setTimeout(() => initialRef.current.forEach((a) => open(a)), 250);
  }, [booting, narrow, open]);

  // Global shortcut: Ctrl/⌘ K toggles Spotlight.
  React.useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setSpotlight((s) => !s);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const closeSpotlight = React.useCallback(() => setSpotlight(false), []);

  const actions: WindowActions = React.useMemo(
    () => ({
      focus: (id) => dispatch({ type: "focus", id }),
      close,
      minimize: (id) => dispatch({ type: "minimize", id }),
      toggleMax: (id) => dispatch({ type: "toggleMax", id }),
      commit: (id, g) => dispatch({ type: "commit", id, g }),
      launch: (id) => open(id),
    }),
    [close, open],
  );

  const api: OsApi = React.useMemo(
    () => ({ data: d, mobile: !!narrow, wallpaper, setWallpaper, open, close, restart, openSpotlight: () => setSpotlight(true), payload, onContact }),
    [d, narrow, wallpaper, open, close, restart, payload, onContact],
  );

  return (
    <MotionConfig reducedMotion="user">
      <OsContext.Provider value={api}>
        <div
          ref={rootRef}
          data-os-root
          data-wallpaper={wallpaper}
          className={cn("relative isolate h-dvh min-h-[560px] w-full select-none overflow-hidden bg-black text-foreground antialiased [&_input]:select-text [&_textarea]:select-text", className)}
        >
          <AnimatePresence mode="wait">
            <motion.div key={wallpaper} className="absolute inset-0" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.6 }}>
              <Wallpaper id={wallpaper} />
            </motion.div>
          </AnimatePresence>

          {narrow !== null && !booting && (
            <motion.div className="absolute inset-0" initial={{ opacity: 0, scale: 1.02 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.6 }}>
              {narrow ? (
                <Phone app={phoneApp} onOpen={open} onHome={() => setPhoneApp(null)} onSpotlight={() => setSpotlight(true)} />
              ) : (
                <Desktop wins={state.wins} actions={actions} bounds={bounds} onBounds={onBounds} onSpotlight={() => setSpotlight(true)} />
              )}
            </motion.div>
          )}

          <Spotlight open={spotlight && !booting} onClose={closeSpotlight} />

          <AnimatePresence>{booting && <BootScreen key={bootKey} name={d.os.name} version={d.os.version} lines={d.os.bootLines} onDone={() => setBooting(false)} />}</AnimatePresence>
        </div>
      </OsContext.Provider>
    </MotionConfig>
  );
}

More in Portfolio

View all →