Skip to content

Add a themeable XP Paint application#37

Merged
Cyanoxide merged 34 commits into
mainfrom
add-paint-app
Jun 15, 2026
Merged

Add a themeable XP Paint application#37
Cyanoxide merged 34 commits into
mainfrom
add-paint-app

Conversation

@Cyanoxide

Copy link
Copy Markdown
Owner

Adds Paint as a new in-repo application — a lightweight, theme-aware MS Paint recreation, wired into the Start menu.

What's included

  • Drawing (raster <canvas>): pencil, brush, line, rectangle, ellipse, flood-fill (bucket), eraser, eyedropper. Left button draws with the foreground colour, right button with the background colour. Classic 28-colour palette + fg/bg swatch + brush-size selector.
  • Actions via authentic shortcuts: Ctrl+Z undo, Ctrl+S save as PNG, Ctrl+N new/clear. The menu bar (File/Edit/View/Image/Colors/Help) is kept decorative, consistent with Notepad / IE / File Explorer.
  • Theming: the toolbox, palette and menu chrome restyle for the blue / olive-green / silver themes via the global theme-color mixin; the canvas stays white. (Screenshots below.)
  • Wiring: applications.json entry, pinned in the Start menu next to Solitaire, and added under All Programs ▸ Accessories (the accessories submenu was referenced but previously undefined — now added). SVG app icon so it's crisp at every size with no binary asset to vendor.

Why in-repo (not a separate package like Solitaire)

Per-theme styling needs a light-DOM React component so body[data-theme] CSS can cascade in — an iframe/Shadow-DOM boundary would block it. In-repo also means theming works with zero boilerplate (the mixin is globally injected) and no extra repo/CI overhead. Can be extracted later if a standalone Paint is ever wanted.

Credit

Special thanks to JS Paint by Isaiah Odhner (MIT) — reference for the look and tooling. Added to the README "Special Thanks".

Verification

  • npm run lint and npm run build pass.
  • Puppeteer smoke test (login → Start → Paint): brush, rectangle and fill all draw correctly (18k+ pixels changed); theme switch confirmed across blue/olive/silver.

🤖 Generated with Claude Code

Cyanoxide and others added 24 commits June 14, 2026 09:11
A lightweight, theme-aware MS Paint recreation as an in-repo application,
wired into the Start menu (pinned + All Programs ▸ Accessories).

- Raster <canvas> engine with pencil, brush, line, rectangle, ellipse,
  flood-fill, eraser and eyedropper; right-button draws with the background
  colour, the classic 28-colour palette, fg/bg swatch and a size selector.
- Keyboard actions: Ctrl+Z undo, Ctrl+S save PNG, Ctrl+N new (menu bar kept
  decorative for consistency with the other apps).
- Toolbox/palette/menu chrome restyles for the blue/olive/silver themes via
  the global theme-color mixin; the canvas stays white.
- SVG app icon (crisp at every size, no binary asset to vendor).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rework the Paint app to mirror the real Windows XP layout:
- Full 16-tool toolbox in the correct two-column order (Free-Form Select,
  Select, Eraser, Fill, Pick Color, Magnifier, Pencil, Brush, Airbrush, Text,
  Line, Curve, Rectangle, Polygon, Ellipse, Rounded Rectangle), flat buttons
  that raise on hover and sink when selected.
- A sunken tool-options box below the tools (line widths for the relevant
  tools; empty for pencil, matching the real app).
- Canvas as a fixed bitmap in a grey surround with right/bottom/corner resize
  handles.
- Bottom colour palette plus a status bar (help text, two sunken panels, corner
  grip) with live cursor coordinates.
- Airbrush and rounded-rectangle now draw; curve/polygon approximate a line and
  the selection/text/magnifier tools are present but not yet interactive.

Tool glyphs are placeholders to be swapped for the XP spritemap. Chrome restyles
across the blue/olive/silver themes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Shrink the canvas resize handles and wire them up: dragging the right/bottom/
  corner handle resizes the white bitmap (anchored top-left, drawing preserved).
- Replace the status-bar grip with the authentic six-square 3-2-1 staircase that
  points into the bottom-right corner.
- Scroll the canvas area with the shared XPScrollbars component instead of the
  browser's default scrollbars, so the bitmap pans with XP-styled bars.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the placeholder glyphs with the real Windows XP tool icons from
spritemap__paint-tools.png. The 16 icons sit in a single row with uniform gaps
and varying widths, so each tool maps to a measured [startX, width] cell (edges
taken at the gap midpoints to avoid neighbour bleed) and the sheet is scaled by
height so every icon shares one scale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Position each tool icon by its tight bounding box so the sprite-edge icons
  (free-form select, select, rounded rectangle) centre correctly in both axes.
- Shrink the canvas resize handles and drop the white halo.
- Make the horizontal scrollbar engage: the wide bitmap was inflating the
  window's intrinsic width (Window has min-width: fit-content), so the canvas
  area now hosts an absolutely-positioned scroller, keeping the viewport
  constrained so both XP scrollbars appear.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Select draws a rectangular marquee; Free-Form Select traces a freehand lasso
  and lifts only the pixels inside it (marquee is the bounding box once drawn).
  Drag inside a selection to move it (leaving white behind); Delete clears it.
- Move the canvas resize handles fully outside the bitmap (in the grey margin).
- Keep the menu bar's bottom border painting above the canvas scrollbar.
- Fix maximized windows hiding their footer under the taskbar: the taskbar
  height was read at module load (before the taskbar existed, so 0); read it
  lazily so a maximised window stops at the taskbar (Window.tsx — benefits all
  apps, surfaced by Paint's colour palette + status bar).
- Use the supplied PNG Paint icon; drop the placeholder SVG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Polygon: click to drop vertices with a rubber-band preview, double-click to
  close — produces a real multi-sided polygon instead of a rectangle. Switching
  tools mid-draw abandons the in-progress polygon.
- Magnifier: the options box offers 1x/2x/4x/8x zoom (clicking the canvas cycles
  in, right-click zooms out). Zoom scales the bitmap via CSS zoom so the XP
  scrollbars pan the enlarged view; pointer mapping and the resize handles
  account for the zoom, so drawing still lands on the right pixel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Polygon: segments now only commit on click (no rubber-band line trailing the
  cursor), and a manual double-click detector closes the shape reliably so it
  actually ends.
- Magnifier: clicking the canvas cycles through every level 1→2→4→8→1 (the cap
  was stopping at 4x).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commit a vertex on pointer-up, so a segment can be drawn either by dragging
(the edge previews like the Line tool while the button is held, releasing locks
it in) or by clicking point to point. Plain moves still show no trailing line;
double-click still closes the shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Text: drag out a box (like the selection tool) and type plain text into an
  overlaid textarea; the text word-wraps to the box and is stamped onto the
  canvas (in the foreground colour) when it loses focus or the tool changes.
- Curve: drag a straight line (phase 1), then drag to bend it into a quadratic
  curve with the endpoints fixed (phase 2) — matching the Line-then-bend flow.

Every toolbox tool except none is now interactive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Leaving the polygon tool now commits the segments already on the canvas rather
than restoring the pre-polygon snapshot, so a polygon no longer disappears
unless you explicitly close it. Double-click still closes the shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Eraser: four square sizes (black, selected white + blue border); the eraser
  now stamps a square nib of the chosen size.
- Magnifier: 1x/2x/6x/8x rows with a black square per level; selected level
  shows a blue overlay with white text; clicking the canvas cycles those levels.
- Brush: nib picker — large/medium/small circle, large/medium/small square, and
  right/left diagonal lines; the brush stamps the chosen nib along the stroke.
- Airbrush: small + medium (one row) and large (beneath); spray radius follows
  the choice.
- Line/Curve: keep the thickness options, now without the rounded ends.
- Rectangle/Polygon/Ellipse/Rounded Rectangle: fill style — border-only,
  border+fill, fill-only (fill uses the background colour, fill-only the
  foreground); applied to live shapes and closed polygons.
- Selection/Fill/Pick Color/Pencil/Text: no options, as in the real app.

All option icons are inline SVG/CSS — no sprites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The options box now grows to fit and centres its content, so nothing
  overflows past the sunken frame.
- Airbrush options are centred (small + medium on a row, large beneath).
- The selected magnifier level shows a white square (and white text) on the
  blue overlay, matching the label.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The options box is a consistent fixed size for all tools; content is sized to
  fit inside it.
- Eraser: one square per row (four, centred).
- Magnifier: compact rows so all four zoom levels fit.
- Brush: narrower 3-per-row grid; diagonals are thinner and now include a
  medium one (right / medium / left).
- Airbrush: small + medium on the first row, large centred on the second.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The eraser squares now fill the options box height and distribute evenly
(space-evenly), centred horizontally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Size each eraser button to its square and use a fixed gap, so the spacing
between the squares stays constant instead of shrinking as the squares grow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compact the magnifier zoom rows so all four levels fit, and make each row span
the full width so the blue selected highlight fills the container.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The zoom indicator squares now grow with the level (1x smallest, 8x largest),
  sitting in a fixed-width slot so the labels stay aligned.
- Compact rows so all four levels fit inside the box (measured 42px in a 54px
  inner box).
- Drop the box's horizontal padding so the full-width selected row's blue
  highlight reaches the container edges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The four zoom rows now flex to fill the box top-to-bottom (the selected row's
highlight is flush with the container border, no whitespace), each row is
centred with the size square to the right of the label, and the squares grow
with the level (1x smallest, 8x largest). Verified by measurement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add small/medium/large for both right- and left-leaning diagonal nibs, for 12
brush shapes total (3 circles, 3 squares, 6 diagonals) in a 3x4 grid that fits
the box (measured 50px in a 54px box).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Size each thickness row to its line plus constant padding with a fixed gap, so
the space between the lines stays equal instead of closing up as they thicken
(measured 6px between every line). Applies to Line and Curve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The selected option is now a solid blue fill (no button bevel) and the icon,
  drawn in currentColor, turns white; fixed greys (shape fill) stay grey.
- Shape fill options are wide, full-width rows so the blue selection spans the
  container and the rect previews nearly fill its width.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Selected options now use the app's menu selection colour per theme
  (#4069bf / #95a075 / #8788a1) instead of a fixed navy.
- Shape fill rows flex to fill the box so the selection runs edge to edge with
  no whitespace above/below.
- Remove the hover bevel from the option buttons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add an optional `name` field used by the Start menu, submenus and desktop
  icons (falls back to title), so Paint shows as "Paint" while the window title
  stays "untitled - Paint".
- Point the Startup menu entry at its own (now empty) submenu instead of
  Accessories, so Paint no longer appears under Startup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cyanoxide and others added 5 commits June 15, 2026 10:07
Port the data-driven dropdown support from react-solitaire's WindowMenu into the
shared react-xp WindowMenu via a new `menus` prop (the legacy `menuItems` path is
untouched, so Notepad/IE/File Explorer are unaffected). Paint now uses it for the
full File/Edit/View/Image/Colors/Help menu with every option disabled; the
dropdowns open to show the greyed items, ready to wire up later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Exit closes the Paint window (windows can now be closed from their content via
  a threaded window id + a closeWindow helper).
- Save to Computer downloads the bitmap as a PNG to the real machine.
- Save drops a re-openable icon on the XP desktop holding the image; the icon is
  a thumbnail of the saved bitmap, and double-clicking it reopens Paint with the
  image loaded.

Supporting changes: per-window `content` (carried through openApplication →
WindowManagement → WindowContent), a `savedImages` context slice, and DesktopIcon
gains optional label/content/iconSrc overrides for saved files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pencil, Fill, Pick Color, Magnifier and Airbrush show a custom monochrome
(black-with-white-outline) cursor matching the tool, with a sensible hotspot;
other tools keep the crosshair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The marquee selection kept icon ids as numbers, dropping the saved images'
  string ids; preserve string ids so they can be box-selected.
- Dragging a saved image to the recycle bin now removes it from savedImages
  (other icons still recycle by appId).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oomed

- The five tool cursors (pencil, fill, pick colour, magnifier, airbrush) are now
  generated at runtime as monochrome (greyscale) renders of their actual toolbox
  spritemap icons, instead of hand-drawn SVG approximations.
- Canvas magnification switched from CSS `zoom` (which also magnified and blurred
  the cursor) to CSS width/height scaling with image-rendering: pixelated, so the
  cursor stays crisp and the bitmap shows clean pixels at 6x/8x. Selection/text
  overlays are scaled to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cyanoxide and others added 5 commits June 15, 2026 15:08
Binning a saved image now flags it recycled (kept in state, hidden from the
desktop) rather than removing it. The recycle bin lists recycled images with
their thumbnail; double-clicking one restores it to the desktop, and Empty the
Recycle Bin brings them all back — matching how recycledItems already behaves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The shared desktop-icon img rule forces a fixed 5rem x 3rem box with 1rem side
padding and contain-fit, which letterboxed the opaque thumbnail with white on
all sides. Override it for thumbnails: drop the padding and size to the image's
aspect (fixed height, auto width) so the border wraps it tightly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 completed on the first pointer-up, so clicking two points (rather than
click-dragging) set the line's end equal to its start on the first click — a
zero-length line that jumped straight to the bend stage. Rewrote the tool as a
point-based state machine like Polygon: the start anchors on first press, the
end locks in on a release away from it (a drag or the second click), and the
preview follows the cursor whether or not the button is held, so click-to-point
and click-drag both work for laying the line and bending it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Cyanoxide Cyanoxide merged commit bbda829 into main Jun 15, 2026
1 check passed
@Cyanoxide Cyanoxide deleted the add-paint-app branch June 15, 2026 20:24
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