Changelog

What's new and what's next for Scamp.

v0.3.0

Box shadow shipped

  • New Shadow section in the WYSIWYG panel for every selected element, with a "+ Add shadow" button for stacking multiple shadows
  • Each row has X offset, Y offset, Blur, Spread, a color picker with hex and opacity, and an Inset toggle
  • Per-row visibility toggle and remove button; inset shadows get a subtle inset icon
  • Multiple shadows emit as a comma-separated box-shadow value and round-trip through the parser, single or stacked
  • Promotes box-shadow from a customProperties warning entry to a first-class control

CSS blend modes shipped

  • New "Blend mode" dropdown in the appearance section emits mix-blend-mode for every value other than Normal
  • Full value set grouped by Darken, Lighten, Contrast, Inversion, and Component
  • Separate "Background blend" dropdown maps to background-blend-mode, shown only when both a background color and a background image are set
  • Rendered natively on the DOM-based canvas; mix-blend-mode and background-blend-mode added to the parser property map
  • Related isolation: isolate round-trips cleanly through customProperties

Export pages and elements shipped

  • Export the full canvas viewport from the toolbar, or a selected element and its children from the right-click menu; Cmd+Shift+E exports the current selection
  • Export panel with format (PNG / SVG / PDF), scale, and a live size preview that remembers the last used settings per session
  • PNG export at 1x / 2x / 3x via html-to-image, capturing design content only with transparent backgrounds preserved
  • SVG export with a best-for-simple-layouts hint; PDF export via Electron's printToPDF() with Canvas / A4 / Letter / A3 page sizes and a portrait / landscape toggle
  • Read-only operation that reflects current canvas state and never touches project files

CSS filters shipped

  • New Filters section with a "+ Add filter" button; filters apply in order and reorder via drag and drop
  • Supports blur, brightness, contrast, grayscale, hue-rotate, invert, opacity, saturate, and sepia, combined into a single filter property
  • Per-row visibility toggle and remove button
  • Separate "Backdrop filter" toggle emits backdrop-filter with a hint about needing a transparent background
  • filter and backdrop-filter added to the parser, decomposed back into individual rows; unknown filter functions preserved in customProperties

Visual history panel shipped

  • History panel from the toolbar or Cmd+Shift+H listing up to 100 changes for the current session, most recent first
  • Human-readable entry labels (Drew rectangle, Changed [property] — [name], Pasted [name], etc.) with relative timestamps
  • Click any entry to jump backward or forward; the canvas, properties panel, and file on disk update immediately
  • Shares the same underlying undo stack as Cmd+Z / Cmd+Shift+Z; consecutive same-property changes within 500ms collapse into one entry
  • Per-page, in-memory history; element names update retroactively on rename, and external agent edits appear as a single "External edit detected" entry

Color picker rework shipped

  • Drag interaction now updates at 60fps by keeping picker state local during the drag and committing to the store only on mouseup
  • Eyedropper button samples any pixel on screen via the native EyeDropper API, with Escape to cancel
  • Recent colors row of the last 8 swatches; hex input expands shorthand (#fff#ffffff) and click-to-copy with a "Copied" confirmation
  • Separate numeric opacity input alongside the hex field
  • Same smooth local-then-commit pattern applied to the hue and opacity sliders

Crash reporting and feedback shipped

  • One-time opt-in prompt on first launch; Sentry is never initialised unless the user opts in
  • Privacy-safe configuration strips file paths, project data, and request data, sending only the error, OS, and app version
  • "Privacy" toggle in Settings enables or disables crash reporting immediately and persists the choice
  • "Report a bug" in Help opens a pre-filled GitHub issue with app version and OS

Toggle CSS property groups shipped

  • Section header toggle comments out an entire group of properties at once as a labelled block (e.g. /* layout off */)
  • Covers Layout, Sizing, Background, Border, Shadow, Typography, Filters, Visibility, Blend, Transitions, and Animation groups
  • Toggled-off groups persist in the file, survive reloads, and show as inactive in the panel
  • Sizing toggle warns before collapsing an element; stretch-mode 100% width and height cannot be toggled off
  • Parser maps labelled comment blocks back to toggledOffGroups; the animation toggle leaves @keyframes in place

UA margin defaults round-trip on text tags shipped

  • margin: 0 on <p>, <h1>, <ul>, <blockquote>, and other text tags now survives generate → parse → generate cycles
  • Text tags with no margin declaration still render the browser's UA margin on the canvas
  • Per-element font-size changes no longer desync the canvas from what the browser would render
  • Extends the existing UA-aware tag-default model (previously padding-only on lists) to cover em-based text-tag margins

Adobe Fonts (Typekit) support shipped

  • Paste an Adobe Fonts embed link into the same Project Settings → Fonts box already used for Google Fonts
  • Accepts <link> and @import forms pointing at use.typekit.net
  • Each font family is listed below the paste box, matching the Google Fonts flow

Agent coexistence shipped

  • Makes Scamp safe to use while an external AI agent edits the same project files
  • External edits always win at the file level: if disk changed underneath, Scamp never overwrites
  • Sync engine backs off from active edit zones instead of racing bursts of external writes
  • Clear UI surfaces whether canvas changes are in-sync, paused, or diverged from disk

Components shipped

  • Convert any element and its children into a reusable component, or create a blank one from the components list
  • Each component lives in its own components/[Name]/ folder as PascalCase TSX and a CSS module
  • Dedicated component editor with the same canvas, properties panel, and CSS editor, plus a breadcrumb and an "editing affects all instances" banner
  • Drag a component onto any page to place an instance; edits propagate to every instance automatically via chokidar
  • New Data tab exposes each text element as a dynamic prop or a locked static string

v0.2.4

Per-element states (hover, active, focus) shipped

  • State switcher at the top of the properties panel: Default / Hover / Active / Focus
  • Edits made in a non-default state write to a pseudo-class block in the CSS module (e.g. .rect_a1b2:hover { ... })
  • Properties with overrides for the active state are highlighted; inherited properties show at reduced opacity with a "same as default" label
  • Only changed properties are written to the pseudo-class block — no redundant declarations
  • Dot indicator on each state button signals when overrides exist; clearing all overrides removes the pseudo-class block entirely
  • Pseudo-class blocks written directly after the base class block, grouped by element
  • Parser maps :hover, :active, and :focus back to state overrides; unrecognised pseudo-classes (:focus-visible, :checked, :disabled) preserved verbatim via customProperties

CSS animations (preset keyframes) shipped

  • New Animation section in the WYSIWYG panel with a searchable preset picker grouped by category
  • Property controls: Duration, Easing, Delay, Iteration (number or ∞), Direction, Fill mode, Play state
  • Selecting a preset writes the animation shorthand to the element and appends the @keyframes block to the bottom of the CSS module — one copy per file regardless of how many elements use it
  • Preset library covers entrances (fade-in, fade-in-up, slide-in-left, scale-in, bounce-in, etc.), exits (fade-out, slide-out-right, scale-out, etc.), attention (pulse, shake, bounce, spin, ping), and subtle loops (float, wiggle)
  • Play button in the panel triggers the animation once on the canvas; animations don't loop on the canvas during editing
  • Parser handles @keyframes blocks — recognised preset names map back to the picker, custom keyframes preserved verbatim
  • Custom @keyframes written by agents pass through customProperties untouched
  • Animations written inside @media (prefers-reduced-motion: no-preference) for accessible defaults

Preview mode shipped

  • "Preview" button in the toolbar (Cmd+P) opens a new window running a real Vite dev server pointing at the project folder
  • Real React, real CSS Modules, real HMR — transitions, animations, hover states, and links all work as they would in a true browser
  • Project folder auto-scaffolded with package.json, vite.config.ts, index.html, and main.tsx alongside design files; infrastructure files documented in agent.md as off-limits to agents
  • First open runs npm install automatically with a progress indicator; subsequent opens reuse the existing node_modules
  • Preview window toolbar: back, forward, refresh, read-only URL bar showing the current page, viewport width selector (Mobile 390 / Tablet 768 / Desktop 1440 / Custom)
  • Vite picks a random available port; only one server per project runs at a time and is stopped when the window or project closes
  • Optional [page-name].data.json files inject mock data into the auto-generated main.tsx so React expressions like {user.name} resolve to real values in preview — establishes the convention for full data binding later
  • Vite's error overlay surfaces TSX syntax errors with file and line number
  • Generated project is fully standalone — npm run dev from a terminal produces the same preview outside Scamp

Linking between pages shipped

  • "Link to" dropdown in the Element section of the WYSIWYG panel lists every page in the project plus an External URL option, with an "Open in new tab" toggle
  • Selecting a page writes a relative href matching the page file name (e.g. ./dashboard for dashboard.tsx)
  • If the element isn't already an <a> tag, Scamp wraps its content in one and writes both .tsx and .module.css atomically via the element:rename IPC pattern
  • External URL option accepts a free-text URL and opens in the system browser from preview mode, not the preview window
  • Removing a link clears the href and removes any auto-added <a> wrapper
  • Links navigate naturally inside the preview window via React Router; back/forward and the URL bar update to follow page history
  • Linked pages that are later deleted surface a "Linked page not found" warning on the canvas instead of silently breaking

v0.2.2

Visibility and opacity controls shipped

  • New Visibility section in the WYSIWYG properties panel for every element
  • Opacity — number input (0–100) and range slider, synced — maps to the CSS opacity property
  • Visibility — segmented control with Visible (default), Hidden (visibility: hidden), and None (display: none)
  • Elements with display: none stay visible on the canvas with a faded checkerboard overlay so you can still select them
  • Flex layout toggle auto-disables with a tooltip when an element is set to display: none
  • Round-trips through the parser and shows up in the raw CSS editor like any other property
  • Default values (opacity: 1) omitted from output to keep CSS clean

Typography tokens in theme.css shipped

  • New Typography section in the theme panel alongside Colors
  • Font sizes, font families, and line heights stored as CSS custom properties in theme.css
  • Token type inferred from the value — rem/px/em = size, quoted string = font family, unitless number = line height
  • Token picker next to font-size, line-height, and font-family inputs in the WYSIWYG panel
  • Autocomplete for typography tokens in the raw CSS editor
  • theme.css watched by chokidar — new tokens appear in the picker immediately
  • Add, rename, and delete typography tokens with the same flow as color tokens

Save status indicator shipped

  • Persistent indicator in the toolbar with four states: ✓ Saved, ↑ Saving…, ● Unsaved, ⚠ Save failed
  • Only shows Saved when canvas state, the file write IPC, and chokidar confirmation all agree
  • Save failed state shows a retry button and persists until the next successful write
  • Failed writes also logged to the terminal panel when it is open

HTML element types shipped

  • Change any rectangle or text element's HTML tag from a new Element section at the top of the properties panel
  • Rectangle variants: section, article, aside, main, header, footer, nav, figure, form, fieldset, ul, ol, li, details, summary, dialog, button, a
  • Text variants: h1h6, span, label, blockquote, pre, code, strong, em, small, time, figcaption, legend, li
  • Media variants: video, iframe, and svg alongside existing img
  • New input element type with its own toolbar button (F): input, textarea, select
  • Tag-specific attribute fields appear inline — href/target for a, type for button, for for label, rows/placeholder for textarea, etc.
  • Class name prefixes stay as rect_, text_, and img_ — only the tag changes
  • Unknown attributes round-trip through the parser untouched

Canvas size rework shipped

  • Root element defaults to width: 100%; height: auto — no more hardcoded pixel dimensions bleeding into exported CSS
  • Canvas viewport size (the white rectangle your design sits inside) is separate from root element CSS and saved in project metadata
  • Canvas height grows with its content, the way a real browser page does
  • Width presets in the toolbar: Mobile 390, Tablet 768, Desktop 1440, Wide 1920, or a custom value
  • Overflow hidden toggle clips content that extends past the viewport width
  • One-time migration notice on first open for existing projects

Mobile and tablet breakpoint toggles shipped

  • Toolbar viewport toggle: Desktop, Tablet, Mobile
  • Switching breakpoints resizes the canvas and scopes every edit to that breakpoint's @media (max-width: …) block
  • WYSIWYG panel shows the current value for the active breakpoint, with a small indicator when a property overrides the desktop base
  • Default breakpoints 1440 / 768 / 390, configurable per-project
  • @media blocks appended at the bottom of each CSS module, grouped by breakpoint
  • Parser reads and writes media query blocks; custom breakpoints preserved verbatim
  • Depends on the canvas size rework shipping first

Transitions shipped

  • New Transitions section in the WYSIWYG properties panel for every element
  • Per-row controls: Property (all, opacity, transform, background, color, border, width, height), Duration (ms/s), Easing (ease, linear, ease-in, ease-out, ease-in-out, custom cubic-bezier), Delay (ms/s)
  • Defaults: all, 200ms, ease, 0ms
  • "+ Add transition" appends another row; each row has a remove button
  • Output is a single transition shorthand per element, e.g. transition: opacity 200ms ease, transform 300ms ease-in-out
  • Custom cubic-bezier opens a four-point input — cubic-bezier(0.4, 0, 0.2, 1)
  • Round-trips through the parser — both shorthand and longhand forms read back correctly
  • Animations play in preview mode and when paired with hover/active states; static canvas viewport does not animate

CSS Grid layout shipped

  • Layout section gains a display toggle: None / Flex / Grid
  • Grid container controls replace flex controls when Grid is active: Columns and Rows (free-text, accept 1fr 1fr, repeat(3, 1fr), minmax, auto-fill, auto-fit, etc.), Column gap, Row gap, Align items, Justify items
  • Grid child controls appear in the sizing section for direct children of a grid container: Column span, Row span, Align self, Justify self — accept span 2 or explicit 1 / 3
  • Canvas renders grids using the browser's native grid engine, same as flex
  • Selecting a grid container shows a dashed overlay marking column and row lines
  • Parser handles display: grid, grid-template-columns, grid-template-rows, column-gap, row-gap, grid-column, grid-row, align-items, justify-items, align-self, justify-self
  • Implicit/auto-placement grids work out of the box — no extra handling required

v0.2.1

Copy and paste elements shipped

  • Cmd+C to copy a selected element and all its children
  • Cmd+V to paste as a sibling of the current selection, or onto the page root
  • Pasted elements get new IDs — deep copy, not a reference
  • Cmd+D to duplicate in place
  • CSS for new classes written to the module file on paste

Images shipped

  • As an img element: click the image button in the toolbar (I) or drag an image file onto the canvas to place an <img> element
  • As a background: select a rectangle and click "Set background image" in the background section of the properties panel to set background-image
  • Background controls expand on image set: background-size, background-position (9-point grid), background-repeat
  • All image files copied into an assets/ folder in the project directory
  • assets/ documented in agent.md

Add new page and duplicate page shipped

  • Add page: click "+ Add Page" in the sidebar, name it inline, get a blank .tsx and .module.css created immediately
  • Duplicate page: right-click a page → Duplicate, new name defaults to [name]-copy, editable inline
  • Both flows use the same inline naming input component
  • Name validation: lowercase, alphanumeric and hyphens only, unique within the project

Nudge with arrow keys shipped

  • Canvas: arrow keys move a selected element 1px; Shift + arrow moves 10px
  • Properties panel: up/down arrow in any number field increments/decrements by 1; Shift + up/down by 10
  • Canvas nudge triggers the normal debounced file write
  • Panel nudge commits on blur or Enter — not on every keydown
  • Values clamp at 0 for properties where negative is not meaningful

v0.2.0

WYSIWYG properties panel shipped

  • Full visual properties panel alongside the raw CSS editor — users can switch between the two at any time using the CSS / Visual toggle at the top of the panel
  • Changes in either panel stay in sync — edits in the visual panel update the CSS editor and vice versa

Remove layout section from text elements shipped

  • The flex/layout section is hidden in the properties panel when a text element is selected
  • Padding controls remain visible for text elements

Shorthand box property inputs shipped

  • Padding, margin, border, and border-radius inputs accept CSS shorthand format
  • Accepted formats: x, x x, x x x x — spaces or commas both work
  • Parsed values shown as a resolved preview on blur
  • Invalid input reverts to the previous value without crashing

Undo / redo shipped

  • Cmd+Z / Ctrl+Z to undo
  • Cmd+Shift+Z / Ctrl+Shift+Z to redo
  • History covers canvas changes and properties panel edits
  • 50 step limit
  • External file edits clear the undo history for that page
  • Implemented via zundo Zustand middleware

Project color swatches shipped

  • Color picker swatches default to a built-in neutral palette
  • Once the project has any colors in its CSS files, swatches switch to colors used in the project
  • Colors extracted from CSS modules at load time and on every file change
  • Swatches ordered by frequency of use, duplicates removed

Project themes and CSS variable tokens shipped

  • Each project gets a theme.css file with CSS custom properties
  • Token picker in the WYSIWYG panel lets users insert var(--token-name) as a value
  • Token swatches show the resolved color, not just the variable name
  • CodeMirror CSS editor shows token autocomplete suggestions
  • Theme panel for adding, renaming, and deleting tokens
  • theme.css watched by chokidar — changes hot-reload into the token picker
  • Deleting a token that is in use shows a warning

Element naming and layers panel shipped

  • Elements can be renamed — name becomes the CSS class prefix (e.g. "Hero Card" → hero-card_a1b2)
  • Unnamed elements default to rect_ or text_
  • Layers panel in the left sidebar shows the full element tree for the active page, indented to reflect nesting
  • Click a layer to select the element on the canvas
  • Selected element highlighted in the layers list
  • Renaming writes both the .tsx and .module.css atomically via a dedicated element:rename IPC channel — never one without the other

v0.1.0 — Initial Release (POC)

The first working version of Scamp. A proof of concept covering the full end-to-end loop: draw visually, get real code, edit with an agent, canvas reloads automatically.

Project management

  • Create a new project by choosing any folder on your machine
  • Open an existing project folder
  • Recent projects list on the start screen (last 5 projects)
  • Missing project folders shown as greyed out with a "Folder not found" label
  • Auto-generated agent.md on project creation with code conventions for coding agents

Pages

  • Default home page created with every new project
  • Pages listed in the left sidebar
  • Switch between pages by clicking in the sidebar

Canvas

  • DOM-based canvas viewport (1440×900, scales to fit the window)
  • Rectangle tool — click and drag to draw a rectangle (R)
  • Select tool — click to select, drag to move, handles to resize (S)
  • Text tool — click to place a text element, double-click to edit (T)
  • Nested rectangles — draw inside any selected rectangle
  • Selected element shown with a blue outline and 8 resize handles
  • Checkerboard background outside the viewport frame

Properties panel — raw CSS editor

  • CodeMirror editor loads the selected element's CSS class body on selection
  • Edit any CSS property in any format — shorthand, longhand, anything valid
  • Commit on blur or Cmd+S — triggers a targeted class patch and file write
  • Unknown properties preserved in output and shown with a ⚠ warning label
  • Placeholder shown when no element is selected

Code output

  • Every canvas change auto-saves to a .tsx and .module.css file
  • Atomic file writes (write to .tmp, then rename) to prevent partial reads
  • data-scamp-id attribute on every element as a stable identity anchor
  • Class names follow the [type]_[4-char-id] pattern (e.g. rect_a1b2, text_c3d4)
  • Only non-default properties emitted — output stays clean and readable
  • Read-only code panel at the bottom shows live TSX and CSS with syntax highlighting

Bidirectional sync

  • chokidar watches project files for external changes
  • External edits (agent, editor, terminal) parsed and reflected on the canvas automatically
  • parseCode() diffs against current state before updating — no unnecessary re-renders
  • Unknown CSS properties round-trip through the file untouched

Built-in terminal

  • Full terminal panel (`Ctrl+``) powered by node-pty and xterm.js
  • Opens in the project folder automatically
  • Up to 3 terminal tabs per session