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-shadowvalue and round-trip through the parser, single or stacked - Promotes
box-shadowfrom acustomPropertieswarning entry to a first-class control
CSS blend modes shipped
- New "Blend mode" dropdown in the appearance section emits
mix-blend-modefor 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-modeandbackground-blend-modeadded to the parser property map - Related
isolation: isolateround-trips cleanly throughcustomProperties
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+Eexports 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
filterproperty - Per-row visibility toggle and remove button
- Separate "Backdrop filter" toggle emits
backdrop-filterwith a hint about needing a transparent background filterandbackdrop-filteradded to the parser, decomposed back into individual rows; unknown filter functions preserved incustomProperties
Visual history panel shipped
- History panel from the toolbar or
Cmd+Shift+Hlisting 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
EyeDropperAPI, 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@keyframesin place
UA margin defaults round-trip on text tags shipped
margin: 0on<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@importforms pointing atuse.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:focusback to state overrides; unrecognised pseudo-classes (:focus-visible,:checked,:disabled) preserved verbatim viacustomProperties
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
animationshorthand to the element and appends the@keyframesblock 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
@keyframesblocks — recognised preset names map back to the picker, custom keyframes preserved verbatim - Custom
@keyframeswritten by agents pass throughcustomPropertiesuntouched - 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, andmain.tsxalongside design files; infrastructure files documented inagent.mdas off-limits to agents - First open runs
npm installautomatically with a progress indicator; subsequent opens reuse the existingnode_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.jsonfiles inject mock data into the auto-generatedmain.tsxso 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 devfrom 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
hrefmatching the page file name (e.g../dashboardfordashboard.tsx) - If the element isn't already an
<a>tag, Scamp wraps its content in one and writes both.tsxand.module.cssatomically via theelement:renameIPC 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
hrefand 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
opacityproperty - Visibility — segmented control with Visible (default), Hidden (
visibility: hidden), and None (display: none) - Elements with
display: nonestay 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.csswatched 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:
h1–h6,span,label,blockquote,pre,code,strong,em,small,time,figcaption,legend,li - Media variants:
video,iframe, andsvgalongside existingimg - New input element type with its own toolbar button (
F):input,textarea,select - Tag-specific attribute fields appear inline —
href/targetfora,typeforbutton,forforlabel,rows/placeholderfortextarea, etc. - Class name prefixes stay as
rect_,text_, andimg_— 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
@mediablocks 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
transitionshorthand 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 2or explicit1 / 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+Cto copy a selected element and all its childrenCmd+Vto paste as a sibling of the current selection, or onto the page root- Pasted elements get new IDs — deep copy, not a reference
Cmd+Dto 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 inagent.md
Add new page and duplicate page shipped
- Add page: click "+ Add Page" in the sidebar, name it inline, get a blank
.tsxand.module.csscreated 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+Zto undoCmd+Shift+Z/Ctrl+Shift+Zto redo- History covers canvas changes and properties panel edits
- 50 step limit
- External file edits clear the undo history for that page
- Implemented via
zundoZustand 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.cssfile 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.csswatched 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_ortext_ - 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
.tsxand.module.cssatomically via a dedicatedelement:renameIPC 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.mdon project creation with code conventions for coding agents
Pages
- Default
homepage 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
.tsxand.module.cssfile - Atomic file writes (write to
.tmp, then rename) to prevent partial reads data-scamp-idattribute 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