mirror of
https://github.com/excalidraw/excalidraw.git
synced 2026-10-20 08:17:56 +00:00
master
80
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d3a9c2a195 |
fix(packages/excalidraw): complete cross-document runtime ownership (#11997)
* fix: complete cross-document runtime ownership * fix: schedule cross-document animations in owner window * fix: Prewarm the right font even if it's not Excalifont Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Hyperlink and tooltip host staleness issues Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Various hyperlink fixes * feat: Host abstraction * fix: exportToSvg support for document override Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Remove caches Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Refactor tests (cut non-critical) Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Break out host from App Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: hold after release fixes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: repaint when link image decode is complete per app Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Font loading per-doc test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: draw SVG frame labels without text measurement Frame labels were baked into exports as measured, ellipsis-truncated text elements, which made `exportToSvg` depend on a canvas (through the render environment) for the only text it ever measures. - add `getFrameLabel` (element package): a measurement-free label spec -- position, font, color, arithmetic height, the frame's width as max width - SVG: draw labels from the spec in the frame branch, full title clipped to the frame's width; extract `appendSvgTextLines` so text elements and labels produce identical markup - canvas: unchanged output (measured, truncated text elements), now built from the same spec - fit label boxes into the SVG export bounds explicitly (`extraBounds`) - test: SVG export of a named frame succeeds with a render environment whose `createCanvas` throws and creates no canvas through `document` Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: measurement-free embed placeholders; no render environment in SVG export The placeholder drawn for an embed that isn't rendered live was the last text `exportToSvg` measured, which kept a canvas (via the render environment) in the SVG path and made headless SVG export throw on any scene with an embed unless the process had been polyfilled. - show the placeholder as a single line capped at 30 characters (`getPlaceholderLinkText`: scheme and `www.` dropped, ellipsis) instead of wrapping the whole link: at or below that length the existing `width / length` font size fits the box by construction, so nothing is measured, wrapped or clipped, and output is identical on every host. The full link stays on the element's `<a href>` in SVG - `newTextElement` accepts pre-computed `metrics` to skip measuring - drop `renderEnvironment` from `SVGRenderConfig`, `exportToSvg` and the `@excalidraw/utils` signature: SVG export's only host dependency is its owner document - throw a guided error from the default canvas/image factories when there is no `document`/`Image`, instead of a bare ReferenceError - tests: placeholder text capping and layout; SVG export of an iframe and a long-link embed with a render environment whose `createCanvas` throws Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: scope top picks drag to source window * feat: StickyNotes support Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Revert "fix: measurement-free embed placeholders; no render environment in SVG export" This reverts commit |
||
|
|
3d056dbc8a |
feat(editor): wrap text at 800 and keep it clear of the side panels (#12192)
* feat(editor): wrap typed & pasted text at 800, clear of the side panels A free text growing as it's typed stopped only at the view's width (#12175), so on a large screen it ran to 2000+ px before wrapping, and a pasted text wrapped at half the view. Both now follow one rule, in `getMaxTextWidth`: - no wider than TEXT_MAX_WRAP_WIDTH (800, in @excalidraw/common) in scene units, at any zoom: the paste's former cap, now shared. A paste no longer wraps at half the view. - nor wider than the canvas between the side panels, less TEXT_VIEWPORT_PADDING at each side: the docked sidebar, as before; the full styles panel (desktop), which shows next to a text while it's edited, and once it's pasted and selected (pasting with nothing selected, its room is reserved: as last measured, else approximately); and the stats panel, only for a text beside it, i.e. with rows within that padding of it, as it covers only the top of the canvas' side. A text that starts wrapping is brought in between the same panels, as before. A paste is laid out as if the stats panel weren't there, and again, clear of it, only if it lands beside it. As the stats panel grows to show a selection's properties, with it open the paste is rendered selected first (not captured yet, via `flushSync`), so the panels are measured as they'll be; laid out again, it stays the same elements, and one undo step. A wrapped paste is then moved into view (as far as the view would have scrolled), instead of scrolling the view. The stats panel is marked as side viewport UI (`data-viewport-ui="side"`, named "stats"), so the viewport offsets' other users (zoom to fit, scroll to content, search, a host's `setViewport` with UI offsets) keep clear of it too while it's open. `AppViewport` gains `getSideInsets(names, { reserve })`, generalizing `getSidebarInsets`, and `getSideUIRect(name)`; `getOffsets` shares the reserve fallback through `getReservedSide`. Tests: the cap at 50% and 200%; a typed text clear of the styles and the stats panel, and of the stats panel only beside it; a paste wrapping as a typed one, with the styles panel's room reserved; a paste clear of the stats panel only where it lands beside it, and of the stats panel grown to show it, as one undo step; a wrapped paste moved into view, the view not scrolled. Claude Opus 5.5 (1M), xhigh effort * feat(editor): pan the canvas when the caret goes under the stats or styles panel The canvas follows the caret past the view's edge, and behind the docked sidebar, through the editor's box, which the browser scrolls to reveal a caret (#12175). The stats and the full styles panel don't span the box's height, so a caret typed in under them stayed out of sight. After each input, once the update (and any scroll it made) is in the app's state, the editor works out where the caret is: its line from `getWrappedTextLines`, its x from the width of the line before it, rotated with the text. In a line with RTL characters (`hasRTLChars`, new in @excalidraw/common) the x comes from the browser's bidirectional layout of the line instead, in its paragraph's direction (the editor's `dir="auto"` makes it `unicode-bidi: plaintext`: each paragraph goes its first strong character's way): with Hebrew then English, the caret after the English is mid-line, not at the line's edge. That's measured through a range in an off-screen mirror of the line, shared with the click-to-caret lookup. If the caret's under one of those panels, the canvas pans to bring it out on the canvas' side, with CARET_FOLLOW_PADDING to spare. Only then: a text beside or below a panel stays where it is. Tests: typing past the stats panel's left edge pans the canvas under it, but not below it; the caret in a bidirectional line, a paragraph after one going the other way, is found where the browser lays it out. Claude Opus 5.5 (1M), xhigh effort |
||
|
|
f3d99c42e9 |
fix(editor): pan the canvas to follow the caret instead of shifting the UI (#12175)
* fix(editor): keep the text editor flush with the canvas when a click places the caret in a tall text Editing a text that reaches past the viewport with the text tool (a click places the caret) scrolled the editor's content: the textarea is clamped to the viewport, focus() reveals the caret — which sits at the end of the value until the initial selection is applied — and the deferred refocus after the pointerdown's blur did that on an unfocused textarea, so the text moved up by the overflow while the box stayed put. The selection tool's single-click edit starts on pointerup, with no blur, so it never refocused and looked fine. After placing the initial caret — inside the visible box by construction — the textarea's own scroll goes back to zero. Verified in the browser on a 49-line text hanging 250px below the viewport at 90% zoom: internal scroll was 278px, now 0, for the text tool with the mousedown blur; the selection-tool path stays at 0. jsdom textareas don't scroll, so there is no unit test for it. Claude Fable 5.1, high effort * fix(editor): place the editor of a rotated text where the text is The editor's textarea was scaled and rotated about its own box's center, while the canvas rotates a text about the text's center. The two only agree when the box is the text's size — and it isn't once the box is clamped to the viewport (a tall text reaching past it) or, slightly, through the 5% height buffer. A rotated text scrolled partly off the viewport got its editor displaced by the rotated pivot offset (36px for a 49-line text at -18° and 130% zoom), and the clamp — a cut along the box's own edges — removed lines that were still on screen on the side the rotation lifted. Now the transform origin is the text's center whatever the box's size, with the translate derived from the text's dimensions, so the editor lands on the text in every case; the viewport clamps (right and bottom) apply only to an unrotated box, whose edges are the viewport's, and a rotated box is left whole. Since an unclamped box can reach past the viewport, both focus() calls pass preventScroll so revealing the caret can never scroll the container — the app owns scrolling. Verified in the browser: all four corners of the rotated editor coincide with the text's (they were off by up to 36px), typing at the off-screen end leaves the container unscrolled, and the unrotated case is unchanged — still clamped to the viewport bottom, top-left in place, no internal scroll on a click caret, select-all on Enter. Also updates the linear-editor style snapshot for the editor's explicit transform origin. Claude Fable 5.1, high effort Claude Opus 5.5 (1M), xhigh effort (moved the snapshot update in) * fix(editor): never let the browser scroll the editor container to reveal a caret The root container is `overflow: hidden`, which still makes it a scroll container the browser scrolls programmatically. Typing in a text editor whose box reaches past the viewport — a rotated text's editor cannot be clamped to the viewport without losing lines still on screen — made the browser reveal the caret by scrolling the container, pushing the whole UI out: two new lines below the viewport moved the toolbar up by 416px. `overflow: clip` clips exactly like `hidden` but is not a scroll container, so there is nothing for the caret reveal (or a focus) to scroll; `hidden` stays as the fallback where `clip` is unknown. The editor never scrolled this box itself. Verified in the browser with real key input on a rotated 49-line text hanging below the viewport at 130% zoom: the container stays at 0 and the toolbar in place through new lines and characters typed past the viewport. Claude Fable 5.1, high effort * fix(editor): the canvas follows the caret instead of the page scrolling to reveal it With the editor root at `overflow: clip` (previous commit), the browser's caret reveal skips the root, since it is no longer a scroll container. Full page, nothing moves, but the caret stays out of view. Embedded in a page that scrolls (directly, or in a same-origin iframe), the reveal scrolls that host page instead: 434px for a rotated text reaching past the editor's bottom edge, 345px for a text scrolled below it while edited. Only a cross-origin iframe stops it. The root goes back to `overflow: hidden`, so it absorbs the reveal again. While a text is being edited, the text editor hands any scroll of the root to the canvas (`viewport.translate` by the same screen distance) and resets the root. Scroll events fire before the frame is painted, so the root's shift is never seen. Nothing moves, host page included, and the canvas pans just far enough to bring the caret into view. The reveal is the browser's, so this covers everything that moves the caret: typing, Enter, arrow keys, paste, IME, and drag-selecting past the edge. Measured with real key input in Chromium, full page and in a same-origin iframe embed: host page and root unmoved, canvas panned 444px / 330-355px, caret visible, the editor still on its text. Outside text editing, a root scroll is left as it was. Test: a root scroll while editing is reset and pans the canvas (at 200% zoom, both axes) with the editor on it; with no text being edited, the root is left alone. Claude Opus 5.5 (1M), xhigh effort * fix(editor): never cut the text editor to the viewport; the canvas follows the caret for every text The editor of an unrotated text was cut to the viewport's right and bottom edges (the width of unbound text, plus a max-height), so a caret past an edge was revealed by scrolling the textarea's content inside the cut box. The canvas stood still, and the editor's text left its place on the canvas while typing. Rotated texts were already left whole, and their caret reveal panned the canvas (previous commit). Now no editor box is cut: rotated or not, it is the text's own box, and a caret past the viewport's edge is revealed by panning the canvas. Typing past the bottom edge, or a long line past the right edge, moves the view along; the editor stays on its text. What only existed for the cut goes with it: the `maxWidth` bookkeeping, and the reset of the textarea's internal scroll after placing a click's caret (a whole box never scrolls inside). Measured with real input in Chromium: 455px vertical pan for a 30-line text crossing the bottom edge, 454px horizontal pan for a line crossing the right edge, root and textarea unscrolled, editor on its text. A text-tool click on line 3 of a text taller than the viewport places the caret there without any pan or internal scroll. Tests: the max-width test now checks the editor isn't cut at either edge (it fails with the cut in place); the linear-editor style snapshot loses its max-height. Claude Opus 5.5 (1M), xhigh effort * feat(editor): leave room past the caret when the canvas follows it The browser reveals a caret only as far as it has to, so text typed past the viewport's edge came into view flush against the edge. The canvas now pans a further CARET_FOLLOW_PADDING (5 screen px) along the axis it follows, so the caret keeps a little room from the edge while the view follows it. Not done with the root's `scroll-padding`: the browser honors it only as far as it can scroll, and the editor box ends at the text (5% below it, nothing past a line's end). Measured in Chromium: `scroll-padding: 80px` left 39px below the caret and none to its right. Measured with real input: once the caret reaches the edge, every new line or character pans the canvas and the caret stays 4-5px from the edge, both typing down and along a long line. Test: the pan includes the padding, and a reveal along one axis pans along that axis only. Claude Opus 5.5 (1M), xhigh effort * feat(editor): the canvas follows a caret behind a docked sidebar; the reveal stays in the editor's box A caret that went behind a docked sidebar stayed hidden: it was still inside the editor root, so the browser had nothing to reveal and the canvas never followed. The text editor's container (`.excalidraw-textEditorContainer`) is now the box a caret is revealed into: the canvas area, clipped, and inset off a docked sidebar (measured from `.sidebar--docked` on every editor update, on the right or the left for RTL; the editor's position is relative to the box). A caret past its edge, or behind the sidebar, is revealed by scrolling the box, which the text editor turns into a canvas pan (+ padding) and resets, as it did for the root before. The reveal now never gets past the box, so the editor root is back to master's CSS, and what a host does to the root's overflow no longer matters: with the root forced to `overflow: visible` (as Excalidraw+ does, inside a wrapper that resets its own scroll), the canvas follows the same way and the wrapper never scrolls. Measured with real input in Chromium, full page, with the root forced to `visible`, in the Excalidraw+ arrangement, and in a same-origin iframe embed: nothing scrolled (host page, root, box), the canvas panned 449 / 360 / 580px for texts past the bottom edge, and for a line typed in behind the docked sidebar it panned 120px, leaving the caret 5px left of the sidebar. Clicks inside the editor still place the caret; clicks on the canvas still end editing. Tests: the pan test scrolls the editor's box; a new test covers the insets for a sidebar docked on either side, and their removal with the editor. Claude Opus 5.5 (1M), xhigh effort * refactor(editor): measure the sidebar through the viewport UI markers The text editor's box was kept off a docked sidebar by querying the `.sidebar--docked` class and measuring it inline in textWysiwyg. How far viewport UI reaches into the canvas is AppViewport's concern: `getOffsets` measures every UI marked `data-viewport-ui`, and the sidebar is marked (`side` / `sidebar`, except on phones, where it's an overlay). AppViewport gains `getSidebarInsets()`, which measures just the sidebar through those markers, with a `measureSide()` that `getOffsets` now shares. The text editor asks the viewport for the insets. An open sidebar that isn't docked covers the canvas the same way and now counts too; it closes when the canvas is clicked to start a text, so this rarely matters. `getOffsets` isn't used for the box: it gives one band per side for all marked UI, so the top toolbar and the styles panel (open while a text is edited) would pan the canvas as soon as one typed below them. Test: the docked-sidebar test's fake sidebar carries the markers. Claude Opus 5.5 (1M), xhigh effort * feat(editor): a text stops growing at the view's width, and is brought into view when it wraps Closes #8855. Typing a long line grew a free text past the view's edge. The canvas followed the caret, so the start of the text went out of view on the other side, and the box had to be resized by hand afterwards. A free text that grows with its content (`autoResize`) now stops at the view's width: the canvas minus the sidebar, less TEXT_VIEWPORT_PADDING (20px, in @excalidraw/common) at each side, in scene units, so it follows the zoom. On the keystroke that would cross it, the text becomes fixed width at exactly that width and wraps from then on (`autoResize: false`), anchored the way it grew, so a right-aligned or centered text keeps its edge or center. The editor switches to wrapping with it. A text that is already wider keeps growing; nothing shrinks. The rule is an optional `maxWidth` on `refreshTextDimensions`, which the editor's per-keystroke update passes. When a text starts wrapping in the editor, while typed or pasted into it, it is scrolled into view with the same room at the edges. Being exactly the padded view's width, it then fills it. That happens horizontally always, and vertically only if all of it fits; a taller text leaves the vertical to the caret-follow, so the view doesn't jump away from the caret. Pasting onto the canvas keeps its wrap width (half the view, 200-800), capped by the same limit, so that the 200 minimum can't outgrow a narrow or zoomed-in view. A paste that wrapped is scrolled fully into view; if it is taller than the view, its top is brought in. Scrolling into view is computed by a pure `scrollBoundsIntoView` in viewport.ts, next to `centerScrollOn` and `constrainScrollState`. It moves by the least amount, keeps the zoom, takes the usable area as `Offsets`, and returns null when nothing needs to move. The move itself goes through `viewport.translate`, as a user pan does: it respects scroll locks, yields to a host's pending locked navigation, and stops following a collaborator. No move, no translate. Measured with real input in Chromium on a 2560px view: typing character by character at 400% from 100px in, and a sentence inserted at once, both end as a wrapped text from 20 to 2540; with the docked sidebar the limit is 2226 (2560 - 294 - 40) and the text sits 20px left of it; a paste near the bottom-right corner ends 20px inside the right and bottom edges. Nothing scrolled the page or the editor box. Tests: the text stops and wraps at the view's width, and keeps that width; the limit follows the zoom and a docked sidebar; a right-aligned text keeps its right edge; a text already wider keeps growing (the not-cut-off test, reworked); starting to wrap scrolls it into view with room at its right edge, but not vertically when it's taller than the view; a paste isn't wider than the view at 800% and, wrapped, ends fully in view; `scrollBoundsIntoView` unit tests. Claude Opus 5.5 (1M), xhigh effort |
||
|
|
5db42c3ddb |
feat(editor): zen mode polish, hide toolbar hints preference & delayed canvas tooltips (#12165)
* feat(editor): preference to hide toolbar hints Add a "Show hints" checkbox to the Preferences menu, backed by a new browser-persisted `appState.showHints` (default true). When off, HintViewer renders nothing. Also exposed as `Preferences.ToggleShowHints` for hosts composing their own preferences list. Opus 5.5 (1M context), low * fix(editor): keep main menu open when toggling view mode Entering view mode ran terminateActiveInteraction, which reset `openMenu` and closed the main menu the user had just toggled view mode from (Preferences submenu). The main menu stays available in view mode, so only close it when interaction gets disabled. Opus 5.5 (1M context), low * feat(editor): chrome-less canvas controls & in-place fades in zen mode - UI hidden in zen mode fades out in place (opacity + visibility) instead of sliding off the viewport, and is non-interactive from the start of the fade - visible canvas controls (toolbar, main menu trigger, zoom, scroll back to content, exit zen mode) drop their background/border/shadow; hover/active states still apply, popups & dialogs are unaffected - only the zoom level stays visible out of the zoom controls - chrome fades are applied only while toggling (data-zen-mode-transition on the container, set before the DOM update) so hover transitions stay instant - single ZEN_MODE_TRANSITION_DURATION, exposed to CSS as --zen-mode-transition-duration - zoom & undo/redo button groups paint a single background (on the group) instead of one per button Opus 5.5 (1M context), low * feat(editor): shift zoom level into the zoom-out slot in zen mode With the zoom +/- buttons hidden in zen mode, slide the zoom level over to the left edge of the group. The shift is applied to the Tooltip wrapper (new `className` prop) rather than the button, so the tooltip (positioned off the wrapper) moves along. Also give the reset-zoom button its own hover background: unlike the +/- buttons it has no .ToolIcon__icon, and lost its (incidental) hover once the per-button backgrounds were removed. Opus 5.5 (1M context), low * feat(editor): delayed tooltips on canvas buttons - Tooltip gets a `delay` prop: shows after 500ms, or right away if a tooltip was visible within the last 300ms (moving across adjacent buttons); passing over a button quickly doesn't flash one - enable it for the zoom, undo/redo & help buttons - zoom +/- use our Tooltip instead of the native `title`, and the reset zoom button drops its native `title` (it was showing both) - zen mode now hides the zoom +/- tooltip wrappers rather than the buttons, so the invisible wrappers don't show tooltips Opus 5.5 (1M context), low Co-authored-by: GitUserVivek <jagtapvs9832@gmail.com> |
||
|
|
1118751f3e |
feat(editor): customizable font picker top picks (#12160)
* feat(editor): customizable font picker top picks Font families can be dragged from the font picker list onto the top-picks strip to replace a pick, the strip can be reordered by dragging, and right-clicking it resets to the default picks — same as the color picker top picks. The drag ghost is only the font's icon (on a strip-button tile). The color picker's drag & drop is generalized into components/TopPicksDnD (value-agnostic hook, reorder preview, drop outline, reset context menu, shared styles), with thin per-picker adapters (colorTopPicksDnD / fontTopPicksDnD) supplying value equality and the ghost visual. Custom picks are kept in `appState.fontTopPicks` (browser-persisted only, kept on canvas clear); restore drops unknown/internal/fallback families, dedupes, and caps at the strip's 3 slots. Claude Opus 5.5 (1M context), reasoning effort: 40 * feat(editor): reset link in top picks tips The "drag to pin" tip in the font picker and color picker popups now ends with a plain-text "Reset" link while that picker's top picks are customized, restoring the defaults without leaving the popup. Shared as TopPicksTip (tip text styles merged into it). The link never takes focus on click and hands it back to the picker when activated by keyboard, since it unmounts on reset; Enter/Space are kept from the pickers' key handlers. Claude Opus 5.5 (1M context), reasoning effort: 15 * fix(editor): don't clip or overlap font list item focus/pressed rings The font list scrolls (clipping outer box-shadows at its edges) and each item paints over the previous one's bottom edge, so the focus-visible and :active rings are drawn inset there. Pressing and holding items became common with dragging fonts onto the top picks. Claude Opus 5.5 (1M context), reasoning effort: 15 * fix(editor): cancelled top picks drag flying to viewport top-left The ghost (and its home rect) was built on drag activation, by which time the source could have been re-rendered away — the font list re-renders on hover — and a detached element measures as a zero rect. Build and measure it on pointerdown instead, while the source is mounted. Claude Opus 5.5 (1M context), reasoning effort: 15 * fix: Always show 3 slots to reorder Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Cleanup Inconsistent state updates. Font picks go through useExcalidrawSetAppState directly, while color picks go through the action's updateData. Duplicate icons in the strip. Virgil and Excalifont both use FreedrawIcon, so pinning both shows two identical tiles that can only be told apart by their tooltips. Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: remove useMemo Signed-off-by: Mark Tolmacs <mark@lazycat.hu> --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: Mark Tolmacs <mark@lazycat.hu> |
||
|
|
dc2c16d9e2 |
fix(editor): Binding follow-ups to #10753 (#12156)
* fix: Midpoint highlight color in dark mode
The highlighted binding midpoint hardcoded rgba(3, 93, 161) for dark mode,
the pre-filter color from when the interactive canvas was inverted by a
CSS filter. The canvas now draws post-filter colors directly, so it
rendered dark blue. Use BINDING_HIGHLIGHT_RGB like the rest of the binding
highlight.
Claude Opus 5.5 (1M context), high reasoning effort
* fix: Show binding midpoints like before the hit test rework
The binding highlight drew all four midpoints for any suggested binding,
including simple arrows with the cursor deep inside the element. Restore
the previous visibility: simple arrows show no midpoints from inside, and
from outside only the one nearest the cursor once it gets near; elbow
arrows show all of them. The highlighted midpoint still comes from the
suggested binding, so it sits where the arrow actually snaps.
Claude Opus 5.5 (1M context), high reasoning effort
* fix: Images and locked opaque elements occlude binding
Only filled shapes hid the elements behind them from binding. Images now
count as opaque, and locked opaque elements hide what is behind them too
(they still can't be bound to). Frames are future-proofed: once they get
a background, an opaque frame hides the non-children behind it without
ending the search, as its children sit just below it in z-order.
Claude Opus 5.5 (1M context), high reasoning effort
* refactor: Add DEFAULT_ZOOM for computations without an editor zoom
Replace the scattered { value: 1 } as Zoom / AppState["zoom"] casts used
as a zoom fallback (elbow arrow routing, transform, element rendering,
binding repair on restore, tests) with a shared frozen constant.
Claude Opus 5.5 (1M context), high reasoning effort
|
||
|
|
afa3a653fc |
feat(editor): sticky notes (#12064)
* feat(editor): sticky notes * clamp sticky note font size * [dragInput] reject non-finite values * chore: prettier formatting after rebase Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(editor): sticky notes cleanup (plan §7.1) - hide `transparent` in the sticky stroke picker through master's `excludedColors` (#11872) instead of the branch's own `hiddenPaletteColorNames`; the swatch becomes a grid-preserving placeholder, its hotkey stays dead, and arrow-key navigation steps over it in the arrow's direction (unit + integration tests) - move the text pixel-snap change out to `dwelle/text-pixel-snap` (705e497e2): it affects every text element at every zoom and is not zoom-aware, so it lands separately - rename `STICKY_NOTE_DEFAULT_FONT_SIZE` → `STICKY_NOTE_FALLBACK_FONT_SIZE`: it is only the non-finite fallback; new sticky text seeds its ceiling from `currentItemFontSize` (decision D3) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): one sticky note layout core + resize intents (plan §7.2, 1/2) - `getStickyNoteLayout` is the single fit/grow/position calculation (pure; ceiling-anchored size grid, warm-started binary search instead of the linear descent); `updateStickyNoteLayout` applies it to a scene and runs the bound-arrow pass by default (callers forward `simultaneouslyUpdated` or opt out when they own a pass); `relayoutStickyNotes` applies it inside immutable element arrays, identity- and version-preserving - resize intents are absolute values recomputed from the gesture-start snapshot on every pointer-move (`getStickyNoteResizeIntent`): width-only gestures keep the base height, height gestures set it, proportional gestures (Shift on any handle, aspect-locked multi-select, Stats group) also scale the font ceiling, flips preserve everything; the content correction anchors at the held edge (n* → bottom, Alt → center, Shift+E/W → center). The corner pre-fit clamp and the multi-resize sentinel/veto are gone: one fit per pointer-move - arrows bound to a note follow text-driven growth, and multi-resize / Stats group update them after the content correction, not before - `fontSizeMax` is `number | null`; every read/write goes through the container-aware `getUserFontSize` / `getUserFontSizeUpdate` (a detached label with a stale ceiling neither shows nor keeps writing it); unbind clears it and returns an empty note to its base height; restore seeds or clears it after binding repair and normalizes a sticky label's stroke - paste styles lays out a restyled note and its label together in a post-pass (no phantom-container mismatch) and reads the source ceiling from the copied snapshot rather than the fitted size - `redrawTextBoundingBox` / `handleBindTextResize` delegate sticky notes up front (no wasted generic wrap); the `refreshTextDimensions` sticky arm and the wysiwyg duplicate arm are gone; `clampStickyNoteProps` becomes the identity-preserving `normalizeStickyNote` Tests: content-pinned corner drag keeps the requested base, Shift+E scales note/base/ceiling, width-only drag preserves the base of a grown note, Shift released mid-gesture restores gesture-start values, flipping a rotated grown note keeps base/ceiling/label angle, multi-resize syncs an empty note's base. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): sticky note invariants in changeProperty + regression tests (plan §7.2, 2/2) - `changeProperty` normalizes sticky notes after any property write (identity-preserving `normalizeStickyNote`), replacing the three per-action wraps in the background, sloppiness and roundness actions - tests (new `tests/stickyNotes.test.tsx` + restore): arrows bound to a note follow text-driven growth while typing; binding free text seeds the ceiling and unbinding clears it (`null`) and returns the note to its base height; pasting styles copies the user's ceiling and lays out the note together with its label; unselected notes keep object identity through property actions; a transparent background write is normalized; restore clears a stale ceiling on unbound text and seeds ceiling + visible stroke on a sticky label Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): sticky note creation feel + sticky color domain (plan §7.3) Creation - a note starts as a 0×0 draft inserted like every other tool and is previewed at its true dragged size (no jump to 40px on the first move); a gesture under the drag threshold is a click → the default 250×250 square centered on the pointer; a longer drag snaps to the 40×40 minimum only on release, growing away from the origin corner. The preview suppression flag and the pointer-move sticky arm are gone - `normalizeStickyNote` splits into style (constructor + every pass) and geometry (finalize, restore, action passes) so a draft is legal Colors — sticky notes are their own color domain (decision D2) - new `currentItemStickynoteBackgroundColor` default (yellow) next to the stroke one; both persisted/restored non-transparent; new notes and their labels seed from the sticky defaults (a label takes the note's stroke) - own top-pick slots (`colorTopPicks.stickyNoteStroke/Background`) and classic note picks; transparent excluded like bucket fill - one `resolveColorTarget` (regular / sticky / mixed, incl. the text being edited) resolved at execution time in the stroke and background actions' `perform`; the panel only sends `{ color }`, so the memoized picker's stale closure can no longer write the wrong tool's default (A1) - one `getColorUpdate` policy used by the actions, both eyedroppers (which now include bound labels, so a stroke pick colors a note's visible text — 1.12), paste styles and bind-existing-text (a transparent label bound to a note takes the note's text color — A3); the per-surface forks and `isStickyNoteStrokeColorElement` are gone Tests: click under the threshold, 20px drag previews at 20 and snaps to 40 with the origin corner fixed (both directions); closed-popup top pick after a tool switch writes the sticky default; mixed selection writes both domains; notes are created from the sticky defaults; bind normalizes a transparent label; the keyboard eyedropper colors a note's label. App-state snapshots gain the three new keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): font-aware minimum sticky note size The 40×40 floor let a freshly placed note grow on the very first keystroke: one line at the default 20px ceiling plus the 16px padding needs 57px. - `STICKY_NOTE_MIN_SIZE = 75` replaces the width/height pair as the data floor (normalization, layout, restore) - `getStickyNoteMinSize({ fontSize, fontFamily })` = max(75, round(ceiling × lineHeight) + 2 × padding) is the UI floor: the creation snap on release uses the current font size/family, the single resize floor uses the label's ceiling (constant floor for an empty note). Excalifont: 75 up to the L ceiling, 77 at 36px, 92 at 48px Tests: the 20px drag now snaps to 75; a 48px ceiling yields a 92px note that keeps its height on the first character. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(editor): sticky note regression matrix (plan §7.4) + two Stats fixes Tests (27 new): Alt/center resize keeps the center of a content-pinned note; proportional multi-select scales the label's ceiling, a free multi-select of an empty note uses the requested height; a proportional gesture returning to scale 1 restores everything; rotated growth keeps the top edge; the fit takes ≤ 2 measurements on a warm keystroke and ≤ 12 on a cold search from a 512 ceiling; a lowered ceiling wins over a still-fitting old size; odd/fractional ceilings stay reachable on the ceiling-anchored grid; the font-aware minimum size. Bound arrows follow the note on unbind, after a content-constrained resize and after a font-size action, and are not moved twice when resized as part of the same selection. A top pick while editing a note's label targets the label and the sticky default; the preview eyedropper colors a selected note's label; paste styles resolves a since-deleted source's ceiling from the copied snapshot; a detached label with a stale ceiling neither shows nor writes it; duplication keeps the ceiling and base height; an empty note escaped from editing stays and is removed by undo; Stats W keeps the base height and a group W edit scales the ceiling; restore normalizes both sticky defaults, keeps the new top-pick slots and refits note + label under `refreshDimensions`; unit tests for `resolveColorTarget` (regular / sticky / mixed / editing) and the default normalization. Source fixes the tests surfaced: - Stats `DragInput`: the typed-value path handed callbacks the live elements map as "original", so gesture-start values (a note's width, its label's ceiling) read as the already-applied values; it now snapshots the scene like the pointer-drag path does - Stats group resize (`MultiDimension`): a group unit lists the note's label too and scaled it as a standalone element after the note, wiping the fitted size; the note's layout owns its label Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): sticky note skeleton API, tool hint, help row, docs (plan §7.5) - `convertToExcalidrawElements` accepts `{ type: "stickynote", x, y, label?, ... }` (new `ValidStickyNote` skeleton member): the note is constructed through `newStickyNoteElement` + `normalizeStickyNoteGeometry` (250×250 default, `baseHeight` finalized, sticky color invariants), and a label is bound through the sticky fit so its `fontSize` becomes the ceiling and the note grows only if the text overflows at the minimum size (decision D7) - tool hint for the sticky note tool ("Click to place a note, or drag to size it") and the `N` shortcut row in the help dialog - dev-docs: "Sticky Notes" section in the element-skeleton page; CHANGELOG entry (PR number placeholder) Tests: skeleton defaults, invariant enforcement on given props, label binding with the ceiling and the note's text color, growth for an overflowing label. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor: move the seeded PRNG (mulberry32) to @excalidraw/common Pure utility next to `randomInteger`/`reseed`; the sticky note corner jitter imports it from there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(editor): drop the dead roughjs Drawable for sticky notes Nothing ever generated or drew it: both renderers paint a note directly from `getStickyNoteRenderPoints`, and every `ShapeCache` consumer is type-gated to the rectangle family or linear elements. It also disagreed with the rendered outline (generic corner radius, no jitter). Sticky notes now sit with text/image/frame in the `null`-shape group, which the cache treats as a valid value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): sticky note creation-date footer Every note reserves a 32px band under its label body and paints the note's `created` date there, on canvas and in SVG export: "7 Sep" while the year is the current one, "7 Sep 2025" otherwise, nothing for an unknown timestamp (the band stays reserved so geometry never depends on data availability). The label form is chosen by width bucket, never measured, so painting stays measurement-free — server-side SVG export has no text measurer — and an absolute date means exports never go stale. - `STICKY_NOTE_FOOTER` and `STICKY_NOTE_BODY_INSET_Y` in @excalidraw/common; `getStickyNoteDateLabel` / `getStickyNoteFooter` in stickyNote.ts - the layout, `getBoundTextMaxHeight`, the text editor and both renderers share the one inset; a rotated label rotates about the note center, not the now-asymmetric body center - `getStickyNoteMinSize` returns `{ width, height }`; creation and resize clamp each axis to its own floor (a Shift drag stays square) - tests: date label, width buckets, body height, resize floor, SVG + canvas export, creation minimums Plan: sticky_notes_review_final.md §9. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): image tool into "More tools", letter shortcut badges With the sticky tool on N between text (8) and eraser (0), the numbered badge row no longer reads as a sequence. On desktop and tablet, move the image tool into the "More tools" menu (first entry, still `9`, `UIOptions.tools.image` keeps hiding it) and badge the toolbar tools with their letter shortcuts (V R D O A L P T N E) instead of numbers. The number keys keep working; tooltips and the help dialog are unchanged. The mobile toolbar keeps its image slot. - Toolbar.tsx: image item in `ExtraToolsDropdown` (trigger highlight and icon while active), `UIOptions` passed through for the gate - Tools.tsx: `keyBindingLabel` prefers the letter - tests: `UI.clickExtraTool`, image test uses it, toolbar describe in tool.test.tsx (menu placement, UIOptions gate, letter badges) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(editor): a sticky note and its label share one ink The creation-date footer paints from the note's `strokeColor`, the label from its own, and three writes let them drift: coloring the label while editing it (the wysiwyg clears the selection, so `changeProperty` targets only the edited text), binding an already-colored text (only transparent was rewritten), and paste styles from a source whose container and label carry two colors. Red text under a black date. The note's `strokeColor` is its ink — the seed for a new label and what the footer paints with — and the label always mirrors it: - `syncStickyNoteInk(elements, prevElementsMap)`: after a write, copies the side that changed onto the other; when both changed or the data drifted, the label wins (it is the text the user styled); a transparent label takes the note's color; identity-preserving when nothing changes - entrances: the stroke action (after `changeProperty`, which covers edit mode), paste styles (before the relayout), bind text (the text's color wins, transparent falls back), the skeleton path (a label that sets its own color gives it to the note), restore (label wins over a drifted note) - the stroke picker reads "Text color" for notes (`labels.textColor`) - tests for the helper and for every entrance Plan: sticky_notes_review_final.md §12. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * make footer opacity=1 * chore: fix sticky note test import order * feat(editor): drag a tool out of the toolbar to drop a default-sized element Press a tool button and drag onto the canvas: a default-sized element follows the pointer as a translucent preview and is placed where the pointer is released. Sticky notes opt in; the mechanism is generic. `App.toolDrag.ts` owns the gesture. `DRAGGABLE_TOOLS` is the registry — a tool provides `createElement(app, center)` (the element it drops, centered on the pointer, from the same `currentItem*` defaults a click uses) and an optional `onDrop` (a sticky note starts editing its label). Adding a tool is one entry. The preview exists only on the unit until the drop: not in the scene, the store or the history, so collaborators never see it and Escape, pointercancel, window blur or a release outside the canvas leave nothing behind. It is painted by the existing `NewElementCanvas`, drawn exactly as it will land, with the canvas itself made translucent (a CSS opacity on the canvas — per-draw-call alpha would darken where a shadow and a fill overlap). The button arms on pointerdown (a new `onPointerDown` hook on the toggle `IconButton`) and the drag starts once the pointer has moved `DRAGGING_THRESHOLD`, so a plain click still selects the tool. Listeners live on the owner window like the other pointer gestures. The drop is `insertNewElement` + one store capture (one undo entry), selects the element and switches to the preferred selection tool — whatever tool was active (an arrow, say) must not stay armed under the dropped element or its editor. The hit test prefers `elementFromPoint` (sees through a touch's implicit capture on the button) and falls back to the event target where it is missing (jsdom). Made public on App for the unit: `store`, `startTextEditing`, `getCurrentItemRoundness`, `getCurrentItemStrokeWidth`. Tests: drag → preview with an untouched scene and history → drop centered at the pointer with one history entry, the editor open and the selection tool active, undo removes it; Escape mid-drag leaves nothing and a late pointerup is inert; a release outside the canvas drops nothing; a plain click still selects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(editor): default a text element's fontSizeMax to null `newTextElement` left `fontSizeMax` undefined while restore normalizes it to `null`. `Delta.distinctKeysIterator` compares with `!==`, so a restored copy of a locally created text element reported a phantom `fontSizeMax` change — today only in collab reconciliation deltas, which are not undoable, but a latent source of spurious version bumps. Normalize to `null` in the constructor, like `labelPosition`. Snapshots change accordingly (`undefined` → `null`), including the embedded scene payload in the SVG export snapshot. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(editor): drop a duplicated scheduleCapture in the sticky note finalize A paste error: the two calls in one tick set the same flag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(editor): decide history visibility from the applied result Creating a sticky note and abandoning its editor by clicking out left two history entries: the note's creation, and one that only deleted the empty label and dropped the note's reference to it — nothing a user can see, so the first undo appeared to do nothing. (Escape left the same deletion pending instead, folded into the next entry.) The text tool's own click-create-and-abandon had the same invisible entry. History already skips entries with no visible change, but visibility was decided per property patch, before binding repair and layout ran: an `isDeleted` flip or a `boundElements` edit always counted, and anything layout moved afterwards never did. `ElementsDelta.applyTo` now decides it from the applied result. After the deltas, reordering, binding repair and text/arrow layout, the "before" and "after" of every affected element are compared: - `idsToCheck` = the changed elements plus what layout can move for them: a text's container and everything in `boundElements` (a container's label and arrows); the set is walked as it grows, so an arrow brings in its own label. - Unchanged elements are shared between the incoming map and the result and layout mutates them in place, so their "before" is a shallow copy; changed elements already have a fresh instance. The whole scene is never copied. - An element counts as visible only if it is not deleted and, for text, not empty — except an empty label carrying arrow bindings or sitting on an arrow, where it cuts the gap. `version`, `versionNonce`, `updated`, `created` are metadata and `index` is covered by the z-order check; `boundElements` is compared as `type:id` strings with references to empty labels dropped, so binding repair that rebuilds equal objects reads as unchanged. Everything else counts. A dev/test-only assertion records every element's version before layout and throws if layout bumped one outside `idsToCheck`, so a new layout dependency fails a test instead of letting undo silently skip a real change. For that to work the `return` had to leave the `finally` block: a return in `finally` overrides a thrown exception, so the existing dev/test rethrow for failures in reordering and layout never actually propagated. Production behavior is unchanged — the catch still returns with the flag forced to visible. Cost, production build, median per `applyTo`: sparse changes at parity through 50k elements; 10k visible bulk updates +13%; 10k metadata-only updates 44 → 95 ms, because proving "nothing visible" cannot stop at the first element — the case the old code answered wrongly. `applyTo` runs on undo/redo and the version slider, both user-paced. Creation and typing still undo separately, and a drop from the toolbar still records at the drop. Tests: delta-level (empty-label deletion and restoration on a rectangle and a sticky note ignored; a non-empty label, an arrow's label gap, a container resized by a restored label, arrow bindings on empty text, a label and an arrow repositioned by a container redraw all visible; excluded or already-applied properties and a metadata-only update invisible; snapshot restoration visible) and the sticky note flows (Escape, click-out, font change on an empty label, typing). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(editor): snap sticky note placement to the grid * perf(editor): ignore arrow bindings in sticky layout input check * feat(editor): center a sticky note's label in the whole note, tighten the footer A middle-aligned label was centered in the body above the creation-date footer, so it sat visibly high — half a footer above the note's center. It is now centered in the whole padded note (`height − 2·padding`), the footer ignored, and only pushed up against the body's bottom once centering would run it into the footer. The fit and the growth rule are unchanged: the note still grows at the same label height, no vertical budget is lost, and top and bottom alignment are as before. Rotation already turned the label about the note's center. The footer takes less room: its band shrinks from 32px to 20px and the date's baseline is defined from the note's bottom edge (14px) rather than from the padding, so the glyphs sit ~11px from the edge (was 17) with ~13px to the label body above (was 19). The vertical inset follows (64 → 52), and with it the font-aware minimum note sizes. Tests: a middle-aligned label is centered on the note's center at three angles; centering is clamped only once it would overlap the footer; existing sizes derive from the inset constant. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(editor): resize sticky notes proportionally by default * fix(editor): preserve resize flips and text container anchors Apply minimum sizes and proportional font scaling to dimension magnitudes, then restore their signs for shared flip geometry. Share rotation-aware positioning for text-driven height changes and pass vertical flip state through single and multiple-element resizing. Add regression coverage for mirrored resizing, proportional minimum sizes, and text growth after crossing the resize anchor. * feat(editor): lift a seeded sticky note corner at high roughness Curve one corner at roughness 2, chosen from the note seed so it stays consistent through redraws and resizes. Pull its shadow inward by half the bend while retaining the existing down-right offset. Share the generated paper and shadow paths between Canvas and SVG rendering. * fix(editor): share the default sticky note color with its quick pick * refactor(editor): rename a label's `fontSizeMax` to `baseFontSize` The field holds the font size the user picked, from which the sticky note layout derives the fitted `fontSize`. "Max" described today's fitter (it only shrinks); "base" names the intent and pairs with the note's `baseHeight`, the other user-set value the layout derives from. Renamed before the field ships, since it is persisted in scene files. The type is now required (`number | null`): the constructor and restore already always write it, and history deltas compare with strict inequality, so an omitted key against `null` would record a spurious change. Making it required lets tsc catch producers that omit it (one test fixture did). Helpers follow: `getBaseFontSize`, `getBaseFontSizeUpdate`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(editor): slightly strengthen sticky note edges * feat(editor): color a sticky note's background while editing its label While editing a note's label the styles panel targets the label, which has no fill, so the background picker was gone and the note's color could only be changed after leaving the editor. The picker now stays available while editing, shows the note's fill, and a pick colors the note — text color and background in one editing pass. One helper, `getColorTargetElement`, decides where a color pick on an element lands (a background pick on a note's label goes to the note); the target resolver, the panel predicate, the panel value and the action all use it, so the picker also gets the sticky domain (sticky picks, no transparent, the sticky default written). Also fixes a latent bug in the background action: with nothing selected, `selectedElements.every(isLine…)` was vacuously true, so the action took the polygon branch and did nothing. Unreachable before, since no background picker was available without a selection. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix changelog * fix snaps * more snap --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
f1a79b73df |
perf(editor): stop inverting the interactive canvas via CSS filter in dark mode (#12050)
The interactive canvas (selection outlines, transform handles, binding highlights, collaborator cursors, snap lines, scrollbars) was still composited through `filter: invert(93%) hue-rotate(180deg)` in dark mode (#10578 only de-filtered the static canvas). The canvas covers the whole viewport and repaints on nearly every pointer move, so browsers that composite in software (Firefox/Zen on software WebRender or with hardware acceleration disabled) paid for a full-viewport color-matrix pass per frame and dropped from 120fps to ~40fps during marquee/drag. Light mode was unaffected. Colors are now mapped in JS via `applyDarkModeFilter`, like the static canvas. Hardcoded dark-mode values are the exact post-filter equivalents of the previous ones, so rendering is unchanged, with two intentional exceptions: - collaborator cursors and remote selection outlines render in the collaborators' actual colors (matching their avatars) instead of inverted ones (per the existing TODO in clients.ts) - the lasso trail now uses the theme's `--color-selection` like the marquee selection, instead of a hardcoded light-theme purple that never matched in dark mode `--color-selection` for the dark theme now holds the actual rendered color (#b4b0ff); hosts overriding it no longer need to pre-invert. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
e1bb9ff8f8 |
feat(packages/excalidraw): support rendering into another document (#11974)
* feat: support rendering into another document * add missed places * tweak jsdoc --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
abeeaeba21 | feat(editor): customizing color top picks (#11872) | ||
|
|
cf212b2f33 |
feat(editor): eyedropper fixes and improvements (#11859)
Co-authored-by: rafa <rafaondjango@gmail.com> |
||
|
|
792ea3a06c |
feat(editor): bucket fill (#11799)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Mark Tolmacs <mark@lazycat.hu> Signed-off-by: Mark Tolmacs <mark@lazycat.hu> |
||
|
|
f179f7ffd2 |
feat(editor): draw to shape (auto-detection) (#9313)
* Initial commit * Segments detection * Remove aspect ratio * Refactor * Fix tests * Toggle shapeSnap * Fix linting * debug: live preview * Rebase fixes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Bind to elements (no hover background) * Shape closed * Fix rad calc + rename segments * Comments * Fix binding highlighting + lints * Rename to convert to shape * fix: Binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Refactor spline simplification Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove unused doBoundsIntersect Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Restore type signature Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Propose new button shapes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove unused types Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Icon shapes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Element cloning Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Element cloning Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Remove unused Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Shape settings in recognition mode Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove debug Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Actions Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrowheads for freedraw Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Draw shape tool instead of freedraw Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Keyboard shortcut visibility Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Unique cursor for draw shape Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Remove selection Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Shape selection Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Improve shape recognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Further refine shape recognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Preview Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Add hint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Refined shape recognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Less ellipse more rectanguloid Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Refined arrow recognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Refinerecognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Lower linear threshold Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Rectangle head arrow Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: More arrowhead sizes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow remains an arrow Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Draw line between elements converts to arrow Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * remove pendingDrawShapeElement * fix: Arrow start point binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Different arrow endpoint heuristic Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Point type Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Various small fixes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Avoid recompute Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Type issues Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * test(editor): cover the drawShape (snap-to-shape) tool The drawShape tool had no dedicated tests, which let master's toolbar / active-tool rewrite silently break it during the merge: actionFinalize reverted to the selection tool after every sketch, because the drawShape flow finalizes without a `newElement` and so failed master's new `&& element` guard. Covers the behavior the merge had to preserve: - sketch recognition (rectangle, ellipse) and the too-small no-op - the tool stays active after finalizing, so shapes can be sketched consecutively (regression guard for the above) - activation via the S shortcut and the extra-tools dropdown - the tool's own cursor Points are fed into the trail directly rather than through pointermove, which is rAF-throttled and would drop all but one point per frame — the same approach lasso.test.tsx uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: Add test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Breakout from App.tsx Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Shortcut is now Shift+X Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: No selecting arrows for shape recog Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Double history Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: PCA + feature extraction recognition Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * reduce and normalize min-size gates * never select newElement when shape recog tool active * simplify lifecycle & remove double finalize * make arrows bindings orbit * funnel though actionFinalize & fix two unrelated cleanup cases * use crosshair cursor * show strokeWidth * switch icons for drawShape & extraTools * dedupe getBoundsFromPoints * remove unused helpers * jsdoc cleanup * dynamic trail size * improve rectangle recognition * disallow linears to deviate too much from a straight line * put under freedraw tool on tablet/mobile * tweak label * rename `drawShape` -> `autoshape` --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> Co-authored-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
339d3c3733 | feat(editor): toolbar / active tool rewrites (#11649) | ||
|
|
3b9e1c07f5 |
fix(editor): Dragged arrow endpoint ignore grid and angle locks (#10972)
--------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
e4c70cb6c6 |
feat(editor): AnimationController for scrollToContent (#11553)
--------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
2a82821ec5 |
fix(editor): tweak freedraw settings and tablet UI/UX (#11551)
* fix(editor): align constant freedraw stroke width more with generic shapes * reduce streamline for non-mouse * do not change `currentItemStrokeVariability` unintentionally * render penMode button under compact styles panel on tablets * show stroke variability as standalone button in compact actions menu * improve toolbar clicking UX with pen * change streamline defaults * change to `variable` stroke if toggling penMode for the first time |
||
|
|
070df27e4d |
fix(editor): Modern TS require imports from rootDir (#11552)
--------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
cd514d72d6 |
feat(editor): LaserPointer based freedraw (#11507)
Introduces constant width freedraw mode, keeping the original variable mode as default. --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
0cf56b19c7 | test(editor): add unit tests for BinaryHeap (#11419) | ||
|
|
3372149277 | feat(packages/excalidraw): export applyDarkModeFilter and simplify (#11429) | ||
|
|
f6d85bc80f | feat(packages/excalidraw): expose image size config and optimize resizing (#11332) | ||
|
|
21dd1cfacc | feat(packages/excalidraw): state tracking, api hook, and others (#10870) | ||
|
|
fa1f7d9f22 | feat(packages/excalidraw): export throttleRAF (#10912) | ||
|
|
757dfeb6ad |
fix(editor): call throttleRAF with lastArgs and remove trailing (#10905)
Co-authored-by: Varun Chawla <varun_6april@hotmail.com> Co-authored-by: aziamimoh <aziamimoh@users.noreply.github.com> Co-authored-by: pgzcoa <pgzcoa@users.noreply.github.com> Co-authored-by: TinaZhang24 <TinaZhang24@users.noreply.github.com> |
||
|
|
c1dbbdf678 | feat(editor): mermaid code editor & improve parsing (#10897) | ||
|
|
60b275880d | feat(editor): support radar chart and multiple series for other chart types (#10824) | ||
|
|
b0404b10b6 | chore(debug): add debug.logChanged() and make easy to import (#10828) | ||
|
|
eb959128ac |
feat(editor): allow laser-pointing in view mode (#10802)
* feat(editor): allow laser pointing in view mode * feat: allow switching between laser/hand in view mode * fix lint * factor out to utils * fix: only handle primary clicks with the selection/laser tools |
||
|
|
8b3e149db6 | fix: hex8 regression #10578 (#10773) | ||
|
|
063533aede |
feat(packages/excalidraw): support nested dropdown menu (#10749)
Co-authored-by: Barnabás Molnár <38168628+barnabasmolnar@users.noreply.github.com> |
||
|
|
f5cf81ce42 |
feat: prevent pasting excalidraw into textarea & paste element text if avail (#10710)
* feat: prevent pasting excalidraw json into textarea & paste element text if avail Co-authored-by: Ashutosh Kumar <130897584+codeaashu@users.noreply.github.com> * fix FF --------- Co-authored-by: Ashutosh Kumar <130897584+codeaashu@users.noreply.github.com> |
||
|
|
60759d314d |
fix: Regression - invert SVGs in Dark Mode (#10695)
* initial implementation * lint * removed separate getThemeFilterValue function from renderElement * removed BinaryFileData changes * filter instead of css filter |
||
|
|
9d760336d1 |
feat: reduce max tablet MQ size (#10669)
* feat: reduce max tablet MQ size * replace UIOptions.formFactor with getFormFactor |
||
|
|
3b97f5a10c |
feat: move visualdebug to utils and introduce volume bindable visualization (#10617)
* feat: Move visualdebug to utils and introduce volume bindable volume visualization Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Move visualdebug to elements Due to dep circles Signed-off-by: Mark Tolmacs <mark@lazycat.hu> --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> |
||
|
|
b9a255407f | refactor: update SCSS syntax & remove open-color dep (#10633) | ||
|
|
63e1148280 |
feat: stop using CSS filters for dark mode (static canvas) (#10578)
* feat: stop using CSS filters for dark mode (static canvas) * fix comment * remove conditional dark mode export * make shape cache theme-aware * refactor * refactor * fixes and notes |
||
|
|
6c908553a9 | fix: reconciliation of server updates & refactor restore (#10597) | ||
|
|
e95222ed32 |
fix: add constants and side methods to packages (#10418)
* fix: add constants and side methods to packages * add transform to the element package * lint * remove dead code * put transform types back to transform.ts * fix imports * fix imports in test --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
d87620b239 |
fix: Circular reference (#10544)
* fix: Circular reference Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Trigger CI --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> |
||
|
|
c141960ada |
feat: Non-elbow arrow snapping and behavior changes (#9670)
* Fixed point binding for simple arrows Tests added Fix binding Remove unneeded params Unfinished simple arrow avoidance Fix newly created jumping arrow when gets outside Do not apply the jumping logic to elbow arrows for new elements Existing arrows now jump out Type updates to support fixed binding for simple arrows Fix crash for elbow arrws in mutateElement() Refactored simple arrow creation Updating tests No confirm threshold when inside biding range Fix multi-point arrow grid off Make elbow arrows respect grids Unbind arrow if bound and moved at shaft of arrow key Fix binding test Fix drag unbind when the bound element is in the selection Do not move mid point for simple arrows bound on both ends Add test for mobing mid points for simple arrows when bound on the same element on both ends Fix linear editor bug when both midpoint and endpoint is moved Fix all point multipoint arrow highlight and binding Arrow dragging gets a little drag to avoid accidental unbinding Fixed point binding for simple arrows when the arrow doesn't point to the element Fix binding disabled use-case triggering arrow editor Timed binding mode change for simple arrows Apply fixes Remove code to unbind on drag Update simple arrow fixed point when arrow is dragged or moved by arrow keys Binding highlight fixes Change bind mode timeout logic Fix tests Add Alt bindMode switch No dragging of arrows when bound, similar to elbow Fix timeout not taking effect immediately Bumop z-index for arrows when dragged Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Only transparent bindables allow binding fallthrough Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix point click array creation interaction with fixed point binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Restrict new behavior to arrows only Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Allow binding inside images Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix already existing fixed binding retention Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Refactor and implement fixed point binding for unfilled elements Restore drag Removed point binding Binding code refactor Added centered focus point Binding & focus point debug Add invariants to check binding integrity in elements Binding fixes Small refactors Completely rewritten binding Include point updates after binding update Fix point updates when endpoint dragged and opposite endpoint orbits centered focus point only for new arrows Make z-index arrow reorder on bind Turn off inside binding mode after leaving a shape Remove invariants from debug feat: expose `applyTo` options, don't commit empty text element (#9744) * Expose applyTo options, skip re-draw for empty text * Don't commit empty text elements test: added test file for distribute (#9754) z-index update Bind mode on precise binding Fix binding to inside element Fix initial arrow not following cursor (white dot) Fix elbow arrow Fix z-index so it works on hover Fix fixed angle orbiting Move point click arrow creation over to common strategy Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Add binding strategy for drag arrow creation Fix elbow arrow Fix point handles Snap to center Fix transparent shape binding Internal arrow creation fix Fix point binding Fix selection bug Fix new arrow focus point Images now always bind inside Flashing arrow creation on binding band Add watchState debug method to window.h Fix debug canvas crash Remove non-needed bind mode Fix restore No keyboard movement when bound Add actionFinalize when arrow in edit mode Add drag to the Stats panel when bound arrow is moved Further simplify curve tracking Add typing to action register() Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix point at finalize Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix type errors Signed-off-by: Mark Tolmacs <mark@lazycat.hu> New arrow binding rules Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix cyclical dep Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix jiggly arrows Fix jiggly arrow x2 Long inside-other binding Click-click binding Fix arrows Performance [PERF] Replace in-place Jacobian derivation with analytical version Different approach to inside binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fixes Fix inconsistent arrow start jump out Change how images are bound to on new arrow creation Lower timeout Small insurance fix Fix curve test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> No center focus point 90% inside center binding Fixing tests fix: Elbow arrow fixes fix: More arrow fixes Do not trigger arrow binding for linear elements fix: Linear elements fix: Refactor actionFinalize for linear Binding tests updated fix: Jump when cursor not moved fix: history tests Fix history snapshot Fix undo issue fix(eraser): Remove binding from the other element fix(tests): Update tests chore: Attempt filtering new set state Fix excessive history recording Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix all tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> fix(transform): Fix group resize and rotate fix(binding): Harmonize binding param usage fix: Center focus point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> chore: Trigger build Remove binding gap Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Binding highlight refactor fix: Refactored timeout bind mode handling fix: Center when orbiting feat: Color change on highlight Fix orbit binding highlight fix: hiding arrow Fix arrow binding Fix arrow drag selection logic Binding highlight is now hot pink Change inside binding logic for start point Render focus point in debug mode Fix snap to center Fix actionFinalize for new arrow creation fix: snapToCenter() 80% by length fix: attempt at fixing the dancing arrows feat: No center snap when start is not bound Fix centering for existing arrows tweak binding highlight color change `appState.suggestedBindings` -> `suggestedBinding` & remove unused code Refactor delayed bind mode change Binding highlight rotation support + image support fix(highlight): Overdraw fixes feat: Do not allow drag bound arrow closer to the shape than dragging distance feat: Stroke width adaptive fixed binding distance chore: More point dragging centralization New element behavior Refactor dragging Fix incorrect highlight sizing Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix delayed bind mode for multiElement arrows Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix multi-point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fix elbow arrows Simplify state Small positional fixes Fix jiggly arrows Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Fixes for arrow dragging Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Elbow arrow fixes Highlight fixes Fix elbow arrow binding Frame highlight Fix elbow mid-point binding Fix binding suggestion for disabled binding state Implement Alt Remove debug * CHange new arrow creation * fix: allow inside binding via timeout if arrow has no startBinding * fix: Delete invariant violation with arrows * fix: Deleted arrow causes problems * fix: Dragging issues * fix: Dragging fix 2 * fix: Disable drag drag when arrow is bound * fix: Multipoint arrow opposite point movement * fix: Ctrl+Alt precedence * feat: Alt inside start binding mode change * Fix multipoint arrow orbit Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow start inside binding switch Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: New arrow never binds inside Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Small refactor Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Multi-point arrows and linears Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Nested shapes handling Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Overlap behavior * Alt unbind fix Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Existing arrow nested bindable Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Binding suggestions Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Circular dep Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: snapshots Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt immediate update Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Laxing on invariants Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: New highlight overdraws arrow Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Image binding rule changed * Trigger Rebuild * fix:Highlight flicker Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Fully nested shapes * fix: Tune nested shape binding * fix: Size-based orbit jump-in * fix: Binding highlight stroke on sharp bindables * fix: Nested shape binding * fix: history * fix:More precise element nesting check * feat:Add tolerance to shape nesting detection * fix: Reverse * fix:Change center binding to circular * ignore invisible elements when binding * feat: Center point with more precise highlight outlines * fix:Arrow tool hover stuck highlight * fix:More stroke width for highlight * POC: highlight center on hover * tweak binding highlight width * render highlight on the outside * fix: Locked elbow arrow creation * update hints * reduce timeout * handle overlap when both elements the same size * tweak highlight stroke width some more * fix:Add intersection padding * fix: New arrow start orbit when nested binds on the end * fix: Update history snapshot * feat: Allow inside binding for new arrows in nested cases * chore: Logic for measurement * fix: Locked tool + arrow * feat: Remove center binding * fix: Jump arrow inside it gets visially too short Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore:Basic interactive canvas animation re-render trigger for highlights Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat:Highlight animations Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix:Refactored and fixed highlight animation * fix:Poisoned arrow * fix Arrow edit mode selection * fix:Tool lock binding behavior restored * fix:Overlap inside binding * fix:Animated binding highlight * alt anims + increase timeout to 700 * tweak animation some more + remove countdown * fix: False bind timeout indicator Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: better file normalization (#10024) * feat: better file normalization * fix lint * fix png detection * optimize * fix type * fix: increase rejection delay for opening files with legacy api (#8961) * Increased input change interval to 1000 ms to fix IOS 18 file opening issue * increase more --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * feat: library search (#9903) * feat(utils): add support for search input type in isWritableElement * feat(i18n): add search text * feat(cmdp+lib): add search functionality for command pallete and lib menu items * chore: fix formats, and whitespaces * fix: opt to optimal code changes * chore: fix for linting * focus input on mount * tweak placeholder * design and UX changes * tweak item hover/active/seletected states * unrelated: move publish button above delete/clear to keep it more stable * esc to clear search input / close sidebar * refactor command pallete library stuff * make library commands bigger --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * fix: Allow already inside bound arrows to continue inside binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: No angle lock over bindable elements Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Center binding on SHIFT key Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Fix ghost start binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * FEAT: No binding to frame cutout * feat: Bind to frame when frame-bound object hidden part is approached * fix: revert preferred selection to box once you switch to `full` UI (#10160) * fix: mobile UI and other fixes (#10177) * remove legacy openMenu=shape state and unused actions * close menus/popups in applicable cases when opening a different one * split ui z-indexes to account prefer different overlap * make top canvas area clickable on mobile * make mobile main menu closable by clicking outside and reduce width * offset picker popups from viewport border on mobile * reduce items gap in mobile main menu * show top picks for canvas bg colors in all ui modes * fix menu separator visibility on mobile * fix command palette items not being filtered * fix: Increase transform handle offset (#10180) * fix: Increase transform handle offset Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Temporarily disable transform handles for linear elements on mobile and tablets Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Linear hidden resize Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * disable mobielOrTablet linear element bbox completely * fix: Test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * fix: context menu getting covered (#10199) * do not show z-index actions on mobile or tablet * fix: context menu getting covered * fix lint * fix style popup getting covered * put contextmenu z-index above sidebar --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * feat: More prominent keyboard shortcuts in hints (#10057) * Initial * Memoize * Styling * Use double angle brackets for keyboard shortcuts * Use rem in gap * Use an existing function for substituting tags in a string * Revert styling * Avoid unique key warnings * Styling * getTransChildren -> nodesFromTextWithTags * Use height and padding instead of padding only * Initial new idea * WIP shortcut substitutions * Use simple regex for parsing shortcuts * Use single shortcut for combos * Use kbd instead of span * shortcutFromKeyString -> getTaggedShortcutKey * Bug fix * FlowChart -> Flowchart * memo is useless here * Trigger CI * Translate in getShortcutKey * More normalized shortcuts * improve shortcut normalization and replacement & support multi-key tagged shortcuts * fix regex * tweak css --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * fix: small tweaks to shortcut hints (#10214) * fix: Test Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Bind mode * feat: Support special key shortcut highlight * fix: Lint * fix: Remove non-needed function * fix: Skip frame cutout for hover, but keep shape for binding * fix: Lint * fix: Restore removal of deleted elements on restore * fix: Inside-inside during drag Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow vanishes when orbit binding to the same element * feat: Feature flag support * Simplified binding * fix: Diamond corner binding * feat: Binding highlight band re-added * feat: Settings menu * fix: Same shape binding * fix: set radix PropertiesPopover collision boundary (#10221) * Set collision boundary * Calculate collisionPadding dynamically based on container * Add appState offsetTop and offsetLeft to padding calculation. Refactor collisionPadding calculation to use app state offsets. * Update PropertiesPopover.tsx * popover positioning relative to container * fix: prevent wrap text in a container to only text that are not bound to a container (#10250) * fix: only enable wrap text in a container when at least one text element selected is unbound * Trigger Rebuild --------- Co-authored-by: Mark Tolmacs <mark@lazycat.hu> * chore: Uncap the nodejs version requirement (#10238) Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * refactor: single source of truths with editor interface (#10178) * 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> * chore: Update translations from Crowdin (#7429) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Russian) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Traditional) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Persian) * New translations en.json (Khmer) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Marathi) * New translations en.json (Thai) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Azerbaijani) * New translations en.json (Hindi) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Uzbek) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (German, Switzerland) * New translations en.json (Bengali, India) * New translations en.json (Kabyle) * New translations en.json (Karakalpak) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Marathi) * New translations en.json (Hindi) * New translations en.json (German) * New translations en.json (Chinese Simplified) * New translations en.json (Polish) * New translations en.json (Romanian) * New translations en.json (Korean) * New translations en.json (Chinese Traditional) * New translations en.json (Hebrew) * New translations en.json (Hebrew) * New translations en.json (Slovak) * New translations en.json (Slovak) * New translations en.json (Hungarian) * New translations en.json (Hungarian) * New translations en.json (Slovak) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Korean) * New translations en.json (Chinese Traditional) * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Bulgarian) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Basque) * New translations en.json (Finnish) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Khmer) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Marathi) * New translations en.json (Thai) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Azerbaijani) * New translations en.json (Hindi) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Uzbek) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (German, Switzerland) * New translations en.json (Bengali, India) * New translations en.json (Kabyle) * New translations en.json (Karakalpak) * New translations en.json (Romanian) * New translations en.json (German) * New translations en.json (Slovenian) * New translations en.json (Chinese Simplified) * New translations en.json (Spanish) * New translations en.json (Russian) * New translations en.json (Chinese Traditional) * New translations en.json (Turkish) * New translations en.json (Slovak) * New translations en.json (Slovak) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Korean) * New translations en.json (Chinese Traditional) * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Bulgarian) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Basque) * New translations en.json (Finnish) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Khmer) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Marathi) * New translations en.json (Thai) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Azerbaijani) * New translations en.json (Hindi) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Uzbek) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (German, Switzerland) * New translations en.json (Bengali, India) * New translations en.json (Kabyle) * New translations en.json (Karakalpak) * New translations en.json (German) * New translations en.json (Russian) * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Chinese Simplified) * New translations en.json (Marathi) * New translations en.json (Hindi) * New translations en.json (Slovak) * New translations en.json (German) * New translations en.json (Portuguese) * New translations en.json (Romanian) * New translations en.json (French) * New translations en.json (Spanish) * New translations en.json (Arabic) * New translations en.json (Bulgarian) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Basque) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Slovak) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Khmer) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Marathi) * New translations en.json (Thai) * New translations en.json (Norwegian Nynorsk) * New translations en.json (Kazakh) * New translations en.json (Latvian) * New translations en.json (Azerbaijani) * New translations en.json (Hindi) * New translations en.json (Burmese) * New translations en.json (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Uzbek) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (German, Switzerland) * New translations en.json (Bengali, India) * New translations en.json (Kabyle) * New translations en.json (Karakalpak) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Simplified) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * Auto commit: Calculate translation coverage * New translations en.json (French) * Auto commit: Calculate translation coverage * New translations en.json (Polish) * Auto commit: Calculate translation coverage * New translations en.json (Polish) * Auto commit: Calculate translation coverage * New translations en.json (Turkish) * Auto commit: Calculate translation coverage * New translations en.json (Turkish) --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * fix: mobile view ui issues (#10284) * hide zen mode when formFactor = phone * tool bar fixes: icon and width * view mode * fix lint * add exit-view-mode button --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> * chore: Update snaps Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Blue highlight Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Diagonal binding point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove settings Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Jump other binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Hovered arrow mode highlight Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Alt does not snap Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Check debug Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt precise positioning * fix: Jump out to orbit for new arrows when dragged outside * fix: New arrow preserved projection * chore: Remove debug * chore: Introduce different debug color for orbit and other binding modes * fix: Restore arrow start point when self binding * fix: Turn of start jump-out * fix: Tests * fix: Select the first possible altBindPoint * fix: Random projection * fix: Use last point for existing arrows * fix: Preserve alternate orbit focus point during drag * fix: Lint * fix: Update tests * fix: Elbow arrow direction at binding * binding gap and distance and binding highlight tweaks * chore: Naming refactors Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt-duplication copied elements placement (#10152) * feat: Animation support (#10042) * fix: banner url (#10315) * feat: Animation support (#10042) * fix: Merge discrepancy Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove non-needed code Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Trigger build * chore: Remove hint for V1 Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * shorten focus point diagonal helpers to fix corner binding cases * fix: Tests * fix: Multi-point arrow closeness fallback * fix: Finalize multipoint arrow on binding area click * fix: Finalize arrow now truly finalzes Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Point click arrow creation jumping to orbit Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt+drag movement block Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Trigger build * feat: hide point highlight when dragging * feat: hide bbox when dragging points * revert binding gap increase for elbow arrows * reset selectionLinearElement on tool change * chore: Remove debug * feat: Better restore for bindings * use elementsMap instead of array when passing to restoreElement * fix: Arrow angle reset * fix: Arrow angle * Arrow angle support * fix trashing cached canvases in `LinearElementEditor.getElementAbsoluteCoords` --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
37882c66cb |
fix: canvas panning stops when hovering over frame title (#10340) (#10351)
* fix: canvas panning stops when hovering over frame title (#10340) * improve --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
8fb16669ab | feat: Add binding visual debug (#10222) | ||
|
|
47cbb5b6fb |
refactor: single source of truths with editor interface (#10178)
* 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> |
||
|
|
8013eb5e16 |
feat: More prominent keyboard shortcuts in hints (#10057)
* Initial * Memoize * Styling * Use double angle brackets for keyboard shortcuts * Use rem in gap * Use an existing function for substituting tags in a string * Revert styling * Avoid unique key warnings * Styling * getTransChildren -> nodesFromTextWithTags * Use height and padding instead of padding only * Initial new idea * WIP shortcut substitutions * Use simple regex for parsing shortcuts * Use single shortcut for combos * Use kbd instead of span * shortcutFromKeyString -> getTaggedShortcutKey * Bug fix * FlowChart -> Flowchart * memo is useless here * Trigger CI * Translate in getShortcutKey * More normalized shortcuts * improve shortcut normalization and replacement & support multi-key tagged shortcuts * fix regex * tweak css --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
416e8b3e42 |
feat: new mobile layout (#9996)
* compact bottom toolbar * put menu trigger to top left * add popup to switch between grouped tool types * add a dedicated mobile toolbar * update position for mobile * fix active tool type * add mobile mode as well * mobile actions * remove refactored popups * excali logo mobile * include mobile * update mobile menu layout * move selection and deletion back to right * do not fill eraser * fix styling * fix active styling * bigger buttons, smaller gaps * fix other tools not opened * fix: Style panel persistence and restore Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * move hidden action btns to extra popover * fix dropdown overlapping with welcome screen * replace custom popup with popover * improve button styles * swapping redo and delete * always show undo & redo and improve styling * change background * toolbar styles * no any * persist perferred selection tool and align tablet as well * add a renderTopLeftUI to props * tweak border and bg * show combined properties only when using suitable tools * fix preferred tool * new stroke icon * hide color picker hot keys * init preferred tool based on device * fix main menu sizing * fix welcome screen offset * put text before image * disable call highlight on buttons * fix renderTopLeftUI --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
7c41944856 | fix: small improvement on binary heap implementation (#9992) | ||
|
|
ec070911b8 |
feat: library search (#9903)
* feat(utils): add support for search input type in isWritableElement * feat(i18n): add search text * feat(cmdp+lib): add search functionality for command pallete and lib menu items * chore: fix formats, and whitespaces * fix: opt to optimal code changes * chore: fix for linting * focus input on mount * tweak placeholder * design and UX changes * tweak item hover/active/seletected states * unrelated: move publish button above delete/clear to keep it more stable * esc to clear search input / close sidebar * refactor command pallete library stuff * make library commands bigger --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |
||
|
|
f738b74791 | fix: reintroduce height-based mobile query detection (#10020) | ||
|
|
06c5ea94d3 |
fix: Race conditions when adding many library items (#10013)
* Fix for race condition when adding many library items * Remove unused import * Replace any with LibraryItem type * Fix comments on pr * Fix build errors * Fix hoisted variable * new mime type * duplicate before passing down to be sure * lint * fix tests * Remove unused import --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> |