Add a themeable XP Paint application#37
Merged
Merged
Conversation
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>
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Paint as a new in-repo application — a lightweight, theme-aware MS Paint recreation, wired into the Start menu.
What's included
<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.Ctrl+Zundo,Ctrl+Ssave as PNG,Ctrl+Nnew/clear. The menu bar (File/Edit/View/Image/Colors/Help) is kept decorative, consistent with Notepad / IE / File Explorer.theme-colormixin; the canvas stays white. (Screenshots below.)applications.jsonentry, pinned in the Start menu next to Solitaire, and added under All Programs ▸ Accessories (theaccessoriessubmenu 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 lintandnpm run buildpass.🤖 Generated with Claude Code