* [export] rewrite - init
* restore elements + filter deleted in export API (restores #11470 behavior)
* fix snaps
* port export callers added on master to the new API + snaps
- renderOverrides (#12100), sticky notes (#12064) and arrow-label hole
(#10947) tests called the old positional exportToSvg/exportToCanvas
- embedded-scene snapshots gain master's new element fields (created,
baseFontSize, labelPosition); decoded payloads match master's
Claude Opus 5.5 (xhigh)
* restore elements in embedded scene + json clipboard export (restores #12060 behavior)
- master's #12060 made every public export entry point restore its input
(legacy elements reach lower-level code with `created: null`), incl. the
PNG metadata embed and the json clipboard copy in the removed
@excalidraw/utils wrappers
- SVG embed now serializes the restored, non-deleted elements (same as
master's utils path), PNG embed serializes restoreElements(data.elements)
and json clipboard copies the non-deleted restored elements
- port #12060's utils tests to the new API, observing renderers/serializer
instead of the (now identical) scene export functions
Claude Opus 5.5 (xhigh)
* make config.canvasBackgroundColor the single export background knob
- background resolved once in configExportDimension; exportToSvg no longer
reads appState.exportBackground (now omitted from ExportSceneData's
appState type), and honors `canvasBackgroundColor: false`
- outline arrowheads (circle/triangle/diamond_outline, cardinality_zero_*)
are filled with the resolved background color, also when the background
isn't painted (as on master) — the canvas path filled them transparent,
showing the arrow's shaft through them; SVG ignored the config color
- JPEG: `canvasBackgroundColor: false` falls back to the default background
(+ warning) instead of rendering black; no more warning for the default
- callers (exportCanvas, ImageExportDialog, library/chart/publish previews,
AI) pass the background via config only
Claude Opus 5.5 (xhigh)
* resolve export theme before creating frame labels
- frame labels were colored by appState.exportWithDarkMode, ignoring
config.theme (export dialog's dark toggle), and in dark exports the
dark-theme label color got dark-filtered again by the renderer
(#7a7a7a -> #848484)
- theme is now resolved once in configExportDimension; dark labels are
pre-reverted so they render with the editor's frame name color
Claude Opus 5.5 (xhigh)
* drop appState.exportWithDarkMode theme fallback in export
- `config.theme` is the only theme input (defaults to light); the type
already omitted `exportWithDarkMode` from `data.appState`
- callers that relied on the fallback (PublishLibrary item preview, AI
frame export) pass `theme` explicitly, keeping their behavior
Claude Opus 5.5 (xhigh)
* exportToClipboard: don't restore appState before exportToSvg (already restored there)
Claude Opus 5.5 (xhigh)
* remove unused getExportSize, type getCanvasSize's [x, y, width, height] (not Bounds)
Claude Opus 5.5 (xhigh)
* fix typos in export comments/JSDoc
Claude Opus 5.5 (xhigh)
* port with-script-in-browser example to the new export API
- exportToSvg/Blob/Canvas/Clipboard take `{ data, config }`; theme via
`config.theme`, padding 10 kept explicit (new default is 0)
- dropped the no-op `width`/`height` appState overrides from the SVG export
Claude Opus 5.5 (xhigh)
* call getDimensions once, ignore it when widthOrHeight is set
- with an explicit `fit: "contain"`, getDimensions ran twice (the second
time on its own output); it now supplies width/height (+ scale) once,
and `contain` scales the content into that frame
- getDimensions + widthOrHeight both applied; widthOrHeight now wins (with
a warning), like width/height/maxWidthOrHeight already did
- `fit: "none"` (the E+ export server path) renders identically
Claude Opus 5.5 (xhigh)
* type the export size options as mutually exclusive
- `width`/`height`, `maxWidthOrHeight`, `widthOrHeight` and `getDimensions`
are now a union where the unused options are `?: never`, so combining
them is a type error (runtime still ignores/warns for JS callers)
- the `never` keys are remapped over `keyof`, so every option keeps its
JSDoc in completions & hovers (verified via the TS language service)
Claude Opus 5.5 (xhigh)
* replace getDimensions with a `scale` function, round the canvas size
- `config.scale` also accepts `(width, height) => number`, called with the
export dimensions at scale 1 (padding included) — covers content-dependent
scaling (e.g. E+'s pixel budgets) without the getDimensions round trip
- remove `getDimensions`: its returned `scale` replaced `config.scale` and
multiplied the returned size, so callers thinking in output pixels had
to divide it back out
- exportToCanvas rounds the canvas size instead of letting the canvas
truncate it (float noise like 28.999999999999996 lost a pixel); half
pixels now round up (frame label area 20.5px, mermaid preview 89.5px)
Claude Opus 5.5 (xhigh)
* align fit/padding/position export docs with the code
- `fit` defaults to "contain" when `width`/`height` is set, otherwise
"none" (the doc said the opposite); dropped the unimplemented
"x/y force none" claim
- padding is always subtracted for `maxWidthOrHeight`/`widthOrHeight`
(they ignore `fit`), not added
- `position` values are "center" | "topLeft" (doc listed "none")
Claude Opus 5.5 (xhigh)
* derive a missing export width/height from the aspect ratio
- with `fit: "contain"` (the default when a size is given), passing only
`width` or `height` now derives the other from the content's aspect
ratio, as documented — it defaulted to the content's unscaled size,
e.g. `{ height: 50 }` of 200x100 content gave a 200x50 canvas with
60x30 content in wide empty bands (now 80x50 with padding 10)
- with `fit: "none"` the missing side stays the content extent
Claude Opus 5.5 (xhigh)
* default export `fit` to "none" when x/y is set
- `{ x, y, width, height }` was scaled to fit with `x`/`y` silently
ignored (`fit` defaulted to "contain" whenever a size was given); an
explicit region now crops by default, `fit: "contain"` still opts in
(and ignores `x`/`y`, as now documented)
Claude Opus 5.5 (xhigh)
* ignore export x/y when the content is scaled to fit
- with `fit: "contain"`, `maxWidthOrHeight` or `widthOrHeight`, `x`/`y`
shifted the fitted content by a mix of scene units and pixels; they're
now dropped with a warning, which the previous commit already
documented (its "ignored" claim was based on a coincidental probe)
Claude Opus 5.5 (xhigh)
* pad an export x/y region on all sides
- with `position: "center"` (the default), the centering adjustment also
ran on axes pinned by `x`/`y`, shifting the region by the padding: the
canvas started exactly at `x`/`y` with all padding on the right/bottom
(`position: "topLeft"` padded all sides); `position` now only applies to
unpinned axes, as documented
Claude Opus 5.5 (xhigh)
* consolidate exportToCanvas/Blob/Svg into exportScene({ type, data, config })
- `type: "canvas" | "png" | "jpeg" | "svg"`, return type per `type`
(overloads, plus a union overload for dynamic callers); per-type options
are type-checked: `quality` only for jpeg, `embedScene` for png/svg,
`renderEmbeddables`/`skipInliningFonts`/`reuseImages` for svg
- the format replaces `mimeType` (and its "image/jpg" typo handling)
- `appState.exportEmbedScene` -> `config.embedScene` (no longer read
from `data.appState`); `data.files` is optional
- `config.createCanvas` -> `config.environment.createCanvas` (where
#11997's render environment and richtext's text metrics will go)
- exportToCanvas/Blob/Svg are no longer exported (no aliases);
exportToClipboard stays, routed through exportScene
- callers, the with-script-in-browser example and tests ported
Claude Opus 5.5 (xhigh)
* export: take only scene settings from data.appState, frameRendering -> config.frames
- `data.appState` is typed as (and stripped at runtime to) the scene
settings a .excalidraw file stores; the renderer state is built from
those + defaults, so editor state passed in (a live AppState: open
dialog, cropping, frame highlight…) can't affect the output. It only
supplies the default background and what `embedScene` embeds
- `appState.frameRendering` -> `config.frames: { name, outline, clip }`
- editor callers translate their export settings via one internal
`getExportConfigFromAppState`; `exportCanvas` drops its redundant
exportBackground/viewBackgroundColor options
- fixes the editor's SVG export ignoring `exportScale` (the branch's
exportCanvas SVG path had dropped it; master passed it)
Claude Opus 5.5 (xhigh)
* type export positioning options as width/height-only
- `fit`, `position`, `x`, `y`, `origin` move into the size-option union's
`width`/`height` member, so combining them with `maxWidthOrHeight` /
`widthOrHeight` (which ignore them) is a type error; runtime unchanged
(still ignores/warns for JS callers)
- docs stay visible in completions (same `keyof` remap as the size options)
Claude Opus 5.5 (xhigh)
* move export `loadFonts` into `config.environment`
- font loading is a host capability, next to `createCanvas` (svg gets
`environment: { loadFonts }` only, as it creates no canvas)
Claude Opus 5.5 (xhigh)
* changelog: export API (exportScene, config, migration table)
Claude Opus 5.5 (xhigh)
* export: default padding back to 10, canvas at least 1px
- export bounds are geometric (no stroke width), so padding 0 cut half
of the strokes on the content's edge and made flat content (a line)
zero-height — the library/chart/publish previews, which inherited
master's default of 10, rendered blank thumbnails for lines
- canvas dimensions never go below 1px (a 0-sized canvas can't be
encoded); SVG snapshot sizes are back to master's
Claude Opus 5.5 (xhigh)
* export: clamp padding against the fitted size, before deriving from it
- the padding clamp used the content's unpadded size, so flat content (a
horizontal line) lost its padding with maxWidthOrHeight/widthOrHeight
(`{ padding: 10, maxWidthOrHeight: 400 }` → 200x0); it now only
considers the target being fitted into
- a missing width/height was derived before clamping
(`{ height: 10, padding: 10 }` → 0x10, SVG viewBox NaN/Infinity)
- a zero-extent content axis no longer yields a NaN scale under contain
Claude Opus 5.5 (xhigh)
* export: clip frame exports to the frame
- frame clipping was off for exportingFrame (to avoid the rounded
per-element clip), relying on the canvas/SVG edge — which only crops
when the export is exactly the frame; a larger export (size options)
painted children and overlapping elements into the margins, and SVG
frame exports had stopped clipping (master clipped them)
- everything drawn is now clipped to the frame with square corners: a
canvas clip after the background (`renderConfig.clipToFrame`), and an
SVG group with the frame's clip path; the background isn't clipped
Claude Opus 5.5 (xhigh)
* changelog: fill in PR numbers for the placeholder links
Claude Opus 5.5 (xhigh)
* feat(packages/excalidraw): add `props.interaction` and `props.ui`
* fix: prevent browser zoom when interaction disabled
* changelog
* clean up interactive state on disable and consolidate `viewMode` and interaction state updates
* render UI rendered to `children` even if editor-exported
* support `allowed.navigation` and `allowed.embeds/interactiveContent`
* change naming `allowed` -> `enabled`
* changelog: add PR links
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* clear flowchart sessions via consolidated `app.flowchart` after master merge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor getters -> methods
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* refactor device to editor interface and derive styles panel
* allow host app to control form factor and ui mode
* add editor interface event listener
* put new props inside UIOptions
* refactor: move related apis into one file
* expose getFormFactor
* privatize the setting of desktop mode and fix snapshots
* refactor and fix test
* remove unimplemented code
* export getFormFactor()
* replace `getFormFactor` with `getEditorInterface`
* remove dead & useless
* comment
* fix ts
---------
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
* Expose renderScrollbars to AppState
* fix: scrollbar rendering should use al renderable elements
* remove `appState.renderScrollbars`
* clean unused
---------
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
* lasso without 'real' shape detection
* select a single linear el
* improve ux
* feed segments to worker
* simplify path threshold adaptive to zoom
* add a tiny threshold for checks
* refactor code
* lasso tests
* fix: ts
* do not capture lasso tool
* try worker-loader in next config
* update config
* refactor
* lint
* feat: show active tool when using "more tools"
* keep lasso if selected from toolbar
* fix incorrect checks for resetting to selection
* shift for additive selection
* bound text related fixes
* lint
* keep alt toggled lasso selection if shift pressed
* fix regression
* fix 'dead' lassos
* lint
* use workerpool and polyfill
* fix worker bundled with window related code
* refactor
* add file extension for worker constructor error
* another attempt at constructor error
* attempt at build issue
* attempt with dynamic import
* test not importing from math
* narrow down imports
* Reusing existing workers infrastructure (fallback to the main thread, type-safety)
* Points on curve inside the shared chunk
* Give up on experimental code splitting
* Remove potentially unnecessary optimisation
* Removing workers as the complexit is much worse, while perf. does not seem to be much better
* fix selecting text containers and containing frames together
* render fill directly from animated trail
* do not re-render static when setting selected element ids in lasso
* remove unnecessary property
* tweak trail animation
* slice points to remove notch
* always start alt-lasso from initial point
* revert build & worker changes (unused)
* remove `lasso` from `hasStrokeColor`
* label change
* remove unused props
* remove unsafe optimization
* snaps
---------
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
Co-authored-by: Marcel Mraz <marcel@excalidraw.com>
* Upgrade @babel/* versions to 7.24 to ensure non-vulnerable Babel versions
* Pinning React version to 18.2.0 exactly, avoiding test-utils type version clashes
* Fix warning message on yarn start
* Moving react to peer dependencies
* Moving app dependencies from workspace into app
* Bump vitest to 1.6.0 to fix history.test.tsx breaking
---------
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
* build: enable consistent type imports eslint rule
* change to warn
* fix the warning in example and excalidraw-app
* fix packages
* enable type annotations and throw error for the rule
* move the existing example to with-script-in-browser
* Add example with next js app router
* disable ssr for excalidraw client comp
* typo
* update output dir
* don't include nextjs example in tsconfig
* remove meta.json
* lint
* remove example.ts
* port
* move the examples outside packages and use the deps as workspaces in examples
* update gitignore
* fix example
* update path of build dir
* fix
* fix scripts
* try local path
* fix
* update commands
* fix
* fix
* fix script
* skip ts
* disable ts
* add vercel.json
* install
* update tsconfig
* fix lint
* remove console.log
* lets see if this works
* revert
* remove ts nocheck
* add types and some utils in nextjs example
* fix types
* updatw example and remove nextjs dynamic syntax so we don't import excal twice
* move both examples to workspaces and create generic example to be used by browser and next js both
* copy the static assets to nextjs
* fix ts config
* render custom menu items
* fix custom footer
* fix types in browser example
* use regular imports for importing excal and import it using dynamic next js in app router instead
* Add example for pages router
* fix css discrepancies
* fix css
* configure output dir
* fix
* fix css
* rename to with-nextjs
* move components to examples/excalidraw/components