Polyform is a desktop app for Windows, macOS and Linux.
On a phone we can email you the link.
0.9.0-beta.26Beta
Released
Added
The Scale tool (K). Press K and drag a handle of the selection: it scales with everything inside it, strokes, corner radii, text, shadows and blurs included, about the opposite corner or edge. Alt scales about the centre and Shift steps by 5 %; a label by the pointer reads "×0.50 · 300 × 200", and a ring marks the point that stays put. In Move, hold K and drag a handle to scale that once, and Move comes back when you let go of K. While the tool is on, the Inspector has a Scale section: a factor with presets from 25 % to 400 %, W and H that keep the ratio, the anchor (top left by default, remembered), Scale each for several layers, and Keep or Detach and scale for values linked to a variable or a style. Each scale is one undo step, and it gives exactly the result an agent's scale gets. A drag of a 2,000-layer scene stays smooth (59 fps measured, against 7 fps before the drag was drawn under one transform). Ctrl-dragging a frame's handle still stretches its contents, and W and H still resize. (#322)
The Changes tab shows a scale as one row. An agent's scale of a 150-layer scene reads "Scale ×1 → ×0.25 (−75 %)" on the layer it scaled, "and its 151 layers" opens the list, and only what the factor doesn't explain, such as a text that stopped at 1 px or a radius kept by its variable, gets rows of its own, saying why. (#322)
Agents can scale artwork with everything in it.edit_document has a scale op, and scripts polyform.scale(): a layer is scaled proportionally with everything inside it, so its strokes, corner radii, text sizes, shadows and blurs, auto-layout spacing, guides, tiled images and material lengths shrink or grow with it, as Figma's Scale tool does. A plain resize still changes only the box. A smaller copy of a scene is one call (duplicate it, then scale the copy), and it looks like the original at that size. It takes a factor from 1 % to 10,000 %, or a width or height, an anchor (top-left by default), and can scale several layers as one or each on its own. Values linked to a variable or a shared style are kept unless the agent asks to detach them, and anything that stopped at a limit is listed. One scale is one undo step. (#314)
Instances and tilemaps keep a scale. A scaled instance draws its main component at that scale, strokes, text and effects included, and keeps following the main: a change to the main shows on it, scaled. Nested instances multiply their scales, and Detach turns the scale into real values so nothing moves. A tiled image fill can be scaled too. A file that uses either needs this version of Polyform to open it; older versions refuse it and change nothing on disk, and every other file is saved exactly as before (ADR-052). (#314)
Scripts find layers in one call.run_script's polyform.descendants(rootId) lists every layer under one, and polyform.find(rootId, {name, type, match}) the ones that match by name (exact, contains or starts with, optionally ignoring case) and type, instead of reading layers one at a time. (#316)
Fixed
Agents can send images as base64url.import_image now reads base64url and a data: URL as well as standard base64, and when the bytes aren't an image it says how many arrived, how they start and why: cut off, or not an image at all. (F-370, #320)
Stripes look the same at every scale. A Stripes material showed more stripes in a 2x export or when zoomed in; it now shows the same stripes at every scale and zoom, exactly as before at 1x. (F-369, #319)
An agent's radial gradient starts centred. Written without handles, it is centred and reaches the edge, as a new radial in the Inspector is; it used to sit on the top edge, so a soft glow drew as a hard half. Existing gradients don't change. (#311)
An agent's outline has no fill. A shape an agent creates with a stroke and no fill is now just the outline, instead of getting the default light-grey fill. Your own tools are unchanged. (#312)
An agent's path lands where it says. A vector an agent creates from a path with a position now puts the path's top-left at that position, and reads it back as written; it used to land offset by the position again. (#313)