Skip to content

Foundation: Mac OS 1984 desktop — maximize, windows, shell, menus, context menus - #2

Merged
radroid merged 8 commits into
feat/mac-os-1984-desktopfrom
feat/pre-app-foundation
Apr 15, 2026
Merged

Foundation: Mac OS 1984 desktop — maximize, windows, shell, menus, context menus#2
radroid merged 8 commits into
feat/mac-os-1984-desktopfrom
feat/pre-app-foundation

Conversation

@radroid

@radroid radroid commented Apr 15, 2026

Copy link
Copy Markdown
Owner

Summary

First PR of the Mac OS 1984 desktop rebuild. This is the full foundation — after this merges, each app (Calculator, Note Pad, etc.) can branch off in parallel from feat/mac-os-1984-desktop per the strategy in DESKTOP-PLAN.md.

Brings the site from a static "Welcome to Macintosh" screen to a working interactive desktop with draggable windows, a dynamic menu bar, right-click menus, and maximize-to-viewport mode. All 8 app icons are registered with "Coming Soon" placeholders ready to be replaced per-app.

Also included: research notes on PostHog.com's desktop-OS architecture and the resulting architectural decisions that shaped this implementation.

What's in it

Foundation (Phase 1)8a1c10e

  • ScreenPhase gains 'desktop'; welcome → desktop auto-advance on desktop viewports; mobile stays on welcome permanently
  • isMaximized state lifted to page.tsx, passed as props
  • CRT screen gains container-type: size for cqw/cqh window sizing
  • Chin button on the iMac toggles maximize; floating Restore button in maximize mode; smooth transitions respect prefers-reduced-motion
  • Containing-block fix: intro scale animation is released after 700ms so the persistent transform doesn't trap position: fixed descendants

Window system + Desktop shell (Phase 2+3)d63b92b

  • WindowManagerProvider context — derived focus (top of z-stack), contiguous zIndex reshuffle on focus, close normalizes zIndex, cascade-on-open
  • app-registry.tsx — typed registry with all 8 apps using user-provided 1-bit SVG icons and clamp() + cqw/cqh sizes
  • Generic Window component — title bar with striped-active pattern, SVG X close box, 5px click-vs-drag threshold, direct DOM transform: translate3d during drag (rAF-throttled, no re-renders per frame), zoom-from-origin open animation, lazy content mount after entry animation
  • DesktopIcon — single-click select, double-click open with icon rect captured for the zoom animation
  • Desktop shell — 2-column icon grid top-right, clickable Trash bottom-right, windows layer
  • MenuBar refactor — Apple menu + Finder defaults, per-app overrides via registry, reads derived activeWindowId from context, z-index above windows
  • MaximizeNudge — one-shot tip dialog after first app open

Clickable Trashd4d5d86

  • Trash is a real registered app (no longer decorative), placeholder window, Finder-style status bar; plan's Design Decision #11 updated

Right-click context menusde0414d

  • Installed `shadcn/ui` context-menu (@radix-ui/react-context-menu)
  • Rewrote the file with the same API surface but inline Mac OS 1 styling (no lucide-react, no Tailwind v3 theme tokens, glyphs instead of icons)
  • DesktopIcon right-click: Open, Get Info, Duplicate, Move to Trash
  • Empty desktop right-click: Go Full Screen / Restore, Clean Up, Change Wallpaper, About This Macintosh
  • Two-word labels now wrap cleanly; icon selection highlights the label only

Docs66ff622

  • POSTHOG-RESEARCH.md — architectural reference for PostHog.com's desktop-OS implementation (state model, window chrome, shell, styling)
  • DESKTOP-PLAN.md updates incorporating learnings (derived focus, contiguous zIndex, 5px drag threshold, lazy content mount, zoom-from-origin, cascade spec, close normalization, menu bar z-index, readiness checklist, inspiration & anti-patterns section)
  • CLAUDE.md — note that the dev server is always running

Test plan

  • Fresh session → boot → welcome (no menu bar) → desktop auto-advances on desktop viewports after ~2.6s
  • Mobile viewport (<768px) stays on welcome permanently; no desktop, no chin button
  • Chin button toggles maximize; iMac body/chin/stand hide; floating Restore appears; windows scale via container query units
  • Double-click an icon → window zooms in from the icon rect to its cascaded position; placeholder content appears after entry animation
  • Drag a window by the title bar — tracks cursor 1:1, bounded to the CRT screen, no lag, <5px movement is treated as a click not a drag
  • Click a background window → brings to front (stripe pattern flips)
  • Close box X closes the window; stacking reshuffles contiguously
  • Open 2+ apps → cascaded positions
  • First open → "Use Full Screen" nudge dialog shows once per session
  • Menu bar Apple/File/Edit/View/Special dropdowns work (all disabled stubs for now)
  • Right-click an icon → App context menu; `Open` works, rest disabled
  • Right-click empty desktop → `Go Full Screen` / `Restore Screen` toggles live
  • Trash opens (no longer decorative)
  • `prefers-reduced-motion: reduce` → all transitions instant
  • `bunx tsc --noEmit` passes

🤖 Generated with Claude Code

radroid and others added 7 commits April 8, 2026 21:21
…lans

CLAUDE.md: correct boot description, BootSequence→BootScreen component name,
and .woff2→.woff font path. Add pointer to DESKTOP-PLAN.md.

DESKTOP-PLAN.md: fix container-type (inline-size→size for cqh support),
correct Phase 4 branching (apps branch from mac-os-1984-desktop, not
pre-app-foundation), fix branch tree diagram, drop redundant isSelected
from WindowState, collapse isMaximized two-step into direct context usage.

MOBILE-PLAN.md: initial planning document for iPhone OS 1 mobile experience.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mobile gate

- Window positions stored as container percentages (0–1) instead of pixels
  so they scale automatically on maximize toggle
- isMaximized state lifted from WindowManager context to page.tsx,
  passed as props to IMacG3Frame and Desktop
- Maximize trigger simplified to chin button only (removed menu bar option)
- Clarified mobile never advances past welcome phase
- Drag listeners attached to document to prevent fast-drag cursor loss

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POSTHOG-RESEARCH.md: architectural reference notes on PostHog.com's
desktop-OS implementation (state model, window chrome, desktop shell,
styling primitives). Compiled via parallel subagent exploration of
the posthog.com repo.

DESKTOP-PLAN.md updates inspired by the research:
- Derived focused window (no stored activeWindowId) — top of z-stack
  IS the focused window via useMemo
- Contiguous zIndex reshuffle on focusApp (no counter drift)
- 5px click-vs-drag threshold on draggable title bars
- Lazy content mount after window entry animation
- Zoom-from-origin open animation using clicked-icon Rect
- Cascade-on-open position spec and close-normalizes-zIndex rule
- Menu bar z-index sits above window stack
- Reduced-motion covers maximize + open + close animations
- Inspiration & Anti-patterns section (7 patterns adopted, 9 rejected,
  4 future enhancements)
- Readiness Checklist marking PR #1 ready and app content blockers

CLAUDE.md: note that dev server is always running and should not
be started by automation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces the `'desktop'` screen phase and maximize mode as the
foundation for the interactive Mac OS 1984 desktop experience.

Screen phases:
- ScreenPhase gains `'desktop'`
- Welcome screen renders without its menu bar (menu bar moves to the
  desktop phase in Phase 2+3)
- Desktop viewports auto-advance from `'welcome'` → `'desktop'` after
  a brief hold; mobile stays on welcome permanently

Maximize mode:
- `isMaximized` state lifted to `page.tsx`, passed to IMacG3Frame and
  CRTScreen as props (per DESKTOP-PLAN architecture)
- Chin button on the iMac frame toggles maximize; a floating Restore
  button appears when maximized so the control stays reachable after
  the chin hides
- IMacG3Frame hides body/chin/stand when maximized; CRTScreen expands
  to fill the viewport
- CRTScreen gains `container-type: size` to enable cqw/cqh-based
  window sizing in later phases
- All transitions respect prefers-reduced-motion

Containing-block fix:
- The initial intro scale animation is released after 700ms (new
  `introDone` state), so a persistent `transform: scale(1)` no longer
  creates a containing block that traps the maximized IMacG3Frame's
  `position: fixed, inset: 0`.

Assets (user-provided):
- public/app-icons/: 1-bit SVGs for calculator, control-panel, finder,
  journal, music, notepad, trash, world-map (browser icon pending)
- public/apple-icon.svg
- public/cv/Raj_Dholakia_Resume_FullStack.pdf

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces the full interactive desktop — draggable windows,
placeholder apps, icon grid, and a dynamic menu bar that adapts to
the focused app. This is the last piece of the foundation PR before
app PRs can branch off in parallel.

Window manager (app/components/desktop/window-manager.tsx)
- React Context holding a windows record, selectedIconId, and an
  hasOpenedAnyApp flag
- Derived activeWindowId via useMemo — top of z-stack is the focused
  window; no separate focus field to sync
- focusApp does a contiguous zIndex reshuffle (no counter drift)
- closeApp re-normalizes zIndex so gaps never appear
- Cascade-on-open: first window at {12%, 14%}, +3% per next, wraps
  back to base past {60%, 55%}
- moveWindow clears fromOrigin so the zoom animation can't replay

App registry (app/components/desktop/app-registry.tsx)
- Typed AppDefinition/MenuConfig/MenuItem/Rect
- All 8 apps registered with the user-provided 1-bit SVG icons
  (browser.svg now included)
- clamp() + cqw/cqh sizes per the DESKTOP-PLAN
- Shared ComingSoon placeholder component until Phase 4 replaces it
- FINDER_DEFAULT_MENUS exported for the fallback menu bar state

Window component (app/components/desktop/window.tsx)
- Title bar with striped-active pattern, close box with an inline-SVG
  X, focus-on-click
- Drag uses direct DOM transform: translate3d() during the move,
  rAF-throttled; only the final position is committed via moveWindow
  on mouseup. Avoids re-rendering every context consumer per frame.
- 5px click-vs-drag threshold so taps don't jitter the window
- One-frame `transition: none` skip on drag commit so the window
  snaps to the new position without a ghost animation
- Zoom-from-origin open animation: first render at the clicked icon's
  CRT-relative rect, then flipped to the cascaded position on the
  next frame so CSS interpolates the delta
- Lazy content mount — app component only renders after the entry
  animation completes (skipped under prefers-reduced-motion)
- Drag is constrained to the CRT screen bounds via container ref

DesktopIcon (app/components/desktop/desktop-icon.tsx)
- Single click selects (invert colors), double click opens and
  captures the icon's rect for the zoom-from-origin animation
- `large` prop scales the icon/label up when the desktop is maximized

Desktop shell (app/components/desktop/desktop.tsx)
- Wraps children in WindowManagerProvider
- 2-column icon grid in the top-right so all 8 apps fit in the small
  CRT; icons scale up in maximize mode
- Decorative Trash bottom-right with pointer-events: none
- Windows layer renders every open window; click on bare desktop
  deselects the active icon

MaximizeNudge (app/components/desktop/maximize-nudge.tsx)
- Small Mac-style tip dialog that appears once after the first app
  opens, suggesting Full Screen. Auto-dismisses when user maximizes
  or clicks Not Now. Persisted to sessionStorage.

Menu bar refactor (app/components/menu-bar.tsx)
- Now reads derived activeWindowId from WindowManagerContext
- Apple-glyph menu always leftmost (SVG apple)
- Falls back to Finder defaults when no window is focused
- Per-app menuItems (when provided) override the defaults
- z-index 9999 so dropdowns cover windows
- No longer mounted in welcome-screen.tsx

page.tsx
- Desktop phase now renders <Desktop /> (the real shell) instead of
  the Phase 1 placeholder text
- Under-construction indicator removed from the welcome screen

Assets
- public/app-icons/browser.svg added to round out all 8 app icons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Trash was previously a decorative element pinned bottom-right with
pointer-events: none. It's now a real entry in APP_REGISTRY with a
Coming Soon placeholder component and a Finder-style status bar.

Desktop shell renders Trash as a normal DesktopIcon at bottom-right
(filtered out of the top-right 2-column grid so layout is preserved),
so single-click selects and double-click opens with the usual
zoom-from-origin animation.

DESKTOP-PLAN.md Decision #11 updated to match — Trash is clickable
but not functional beyond opening.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scaffolds the shadcn/ui context-menu component (app/components/ui/
context-menu.tsx) and wires it into desktop icons and the bare
desktop surface.

shadcn component
- Installed via `shadcn add context-menu` which added
  @radix-ui/react-context-menu to package.json
- File rewritten to drop the lucide-react dependency and the
  Tailwind v3 theme tokens (bg-popover, rounded-md, animate-in, …)
  that don't exist in our Tailwind v4 alpha setup
- Same export surface as stock shadcn, restyled inline with Mac OS 1
  aesthetics: Chicago font, white bg, 1px black border, 2px black
  drop shadow, text glyphs (▶, ✓, ●) in place of lucide icons
- Hover/keyboard-focus highlighting driven by Radix's data-highlighted
  attribute via a small rule in global.css

Right-click handlers
- DesktopIcon: App name label → Open, Get Info, Duplicate, Move to
  Trash. Right-click also selects the icon so it shows the highlight
  state before the menu opens. Icon is split into DesktopIcon
  (handles context menu + data) and IconButton (forwardRef button
  used by ContextMenuTrigger asChild).
- Desktop surface: Curly OS label → Go Full Screen / Restore Screen
  (live-toggles based on isMaximized), Clean Up Desktop, Change
  Wallpaper, About This Macintosh. Wrapped the desktop container in
  ContextMenu/ContextMenuTrigger so empty-area right-clicks work.

Icon highlight fixes
- whiteSpace: normal on the label so two-word names (Curly Browser,
  Control Panel, Note Pad, World Map) wrap at the space and the
  black selection background covers the whole label instead of
  overflowing the button width
- outline: none on the icon button so the browser's default blue
  focus ring doesn't appear after a right-click
- Removed the filter: invert(1) on icon images when selected — the
  label highlight alone is enough; the icon art stays untouched

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 15, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
raj-portfolio f0d56cf Commit Preview URL

Branch Preview URL
Apr 15 2026, 10:54 PM

Boot icon
- SIZING.macIconSize reduced ~30% (100 → 70 desktop, 60 → 42 mobile)
  so the Happy Mac on the boot screen sits more comfortably in the
  CRT frame.

Chin toggle
- The chin used to be stacked (CD slot on top, separate circular
  button below) and the button was only rendered on welcome/desktop
  phases. This caused a visible chin-height shift when the screen
  transitioned out of boot. Now there is no separate button — the
  CD slot itself IS the maximize toggle. Always rendered (disabled
  during boot) so the chin height is constant across phases.
- Hover brightens the slot with a soft white glow to hint at
  interactivity without breaking the iMac hardware aesthetic.
- aria-label flips between "Maximize screen" and "Restore screen".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@radroid
radroid merged commit 72191ab into feat/mac-os-1984-desktop Apr 15, 2026
1 check passed
@radroid
radroid deleted the feat/pre-app-foundation branch April 27, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant