From 741d5f1a187438b46d4482cdc3ca0f8fde4f4498 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 21 Sep 2023 09:28:48 +0530 Subject: [PATCH 001/144] refactor: move excalidraw-app outside src (#6987) * refactor: move excalidraw-app outside src * move some tests to excal app and fix some * fix tests * fix * port remaining tests * fix * update snap * move tests inside test folder * fix * fix --- .../CustomStats.tsx | 12 +- .../app-jotai.ts | 0 .../app_constants.ts | 0 .../collab/Collab.tsx | 30 +- .../collab/Portal.tsx | 12 +- .../collab/RoomDialog.scss | 2 +- .../collab/RoomDialog.tsx | 20 +- .../collab/reconciliation.ts | 8 +- .../components/AppFooter.tsx | 2 +- .../components/AppMainMenu.tsx | 4 +- .../components/AppWelcomeScreen.tsx | 8 +- .../components/EncryptedIcon.tsx | 6 +- .../components/ExcalidrawPlusAppLink.tsx | 0 .../components/ExportToExcalidrawPlus.tsx | 24 +- .../components/GitHubCorner.tsx | 4 +- .../components/LanguageList.tsx | 4 +- .../data/FileManager.ts | 12 +- .../data/LocalData.ts | 10 +- .../data/Locker.ts | 0 .../data/firebase.ts | 16 +- .../data/index.ts | 22 +- .../data/localStorage.ts | 10 +- .../data/tabSync.ts | 0 .../debug.ts | 0 .../index.scss | 0 .../index.tsx | 53 ++-- .../sentry.ts | 0 excalidraw-app/tests/LanguageList.test.tsx | 29 ++ .../tests/MobileMenu.test.tsx | 6 +- .../__snapshots__/MobileMenu.test.tsx.snap | 257 ++++++++++++++++++ {src => excalidraw-app}/tests/collab.test.tsx | 12 +- .../tests/reconciliation.test.ts | 10 +- src/element/newElement.test.ts | 1 - src/element/textWysiwyg.test.tsx | 10 +- src/index.tsx | 4 +- src/{components => tests}/App.test.tsx | 4 +- src/tests/__snapshots__/App.test.tsx.snap | 50 ++++ .../regressionTests.test.tsx.snap | 120 -------- src/{actions => tests}/actionStyles.test.tsx | 6 +- src/tests/align.test.tsx | 4 +- src/tests/appState.test.tsx | 37 +-- src/tests/binding.test.tsx | 4 +- src/tests/clipboard.test.tsx | 12 +- src/tests/contextmenu.test.tsx | 13 +- src/tests/dragCreate.test.tsx | 26 +- src/tests/elementLocking.test.tsx | 4 +- src/tests/export.test.tsx | 4 +- src/tests/fitToContent.test.tsx | 12 +- src/tests/flip.test.tsx | 4 +- src/tests/history.test.tsx | 40 +-- src/tests/library.test.tsx | 6 +- src/tests/linearElementEditor.test.tsx | 4 +- src/tests/move.test.tsx | 8 +- src/tests/multiPointCreate.test.tsx | 23 +- src/tests/regressionTests.test.tsx | 26 +- src/tests/resize.test.tsx | 4 +- src/tests/scroll.test.tsx | 3 +- src/tests/selection.test.tsx | 36 ++- src/tests/test-utils.ts | 2 +- src/tests/viewMode.test.tsx | 4 +- src/tests/zindex.test.tsx | 5 +- src/zindex.ts | 2 +- tsconfig.json | 2 +- 63 files changed, 638 insertions(+), 415 deletions(-) rename {src/excalidraw-app => excalidraw-app}/CustomStats.tsx (86%) rename {src/excalidraw-app => excalidraw-app}/app-jotai.ts (100%) rename {src/excalidraw-app => excalidraw-app}/app_constants.ts (100%) rename {src/excalidraw-app => excalidraw-app}/collab/Collab.tsx (97%) rename {src/excalidraw-app => excalidraw-app}/collab/Portal.tsx (94%) rename {src/excalidraw-app => excalidraw-app}/collab/RoomDialog.scss (98%) rename {src/excalidraw-app => excalidraw-app}/collab/RoomDialog.tsx (90%) rename {src/excalidraw-app => excalidraw-app}/collab/reconciliation.ts (95%) rename {src/excalidraw-app => excalidraw-app}/components/AppFooter.tsx (90%) rename {src/excalidraw-app => excalidraw-app}/components/AppMainMenu.tsx (91%) rename {src/excalidraw-app => excalidraw-app}/components/AppWelcomeScreen.tsx (90%) rename {src/excalidraw-app => excalidraw-app}/components/EncryptedIcon.tsx (72%) rename {src/excalidraw-app => excalidraw-app}/components/ExcalidrawPlusAppLink.tsx (100%) rename {src/excalidraw-app => excalidraw-app}/components/ExportToExcalidrawPlus.tsx (80%) rename {src/excalidraw-app => excalidraw-app}/components/GitHubCorner.tsx (94%) rename {src/excalidraw-app => excalidraw-app}/components/LanguageList.tsx (88%) rename {src/excalidraw-app => excalidraw-app}/data/FileManager.ts (95%) rename {src/excalidraw-app => excalidraw-app}/data/LocalData.ts (94%) rename {src/excalidraw-app => excalidraw-app}/data/Locker.ts (100%) rename {src/excalidraw-app => excalidraw-app}/data/firebase.ts (95%) rename {src/excalidraw-app => excalidraw-app}/data/index.ts (93%) rename {src/excalidraw-app => excalidraw-app}/data/localStorage.ts (91%) rename {src/excalidraw-app => excalidraw-app}/data/tabSync.ts (100%) rename {src/excalidraw-app => excalidraw-app}/debug.ts (100%) rename {src/excalidraw-app => excalidraw-app}/index.scss (100%) rename {src/excalidraw-app => excalidraw-app}/index.tsx (94%) rename {src/excalidraw-app => excalidraw-app}/sentry.ts (100%) create mode 100644 excalidraw-app/tests/LanguageList.test.tsx rename {src => excalidraw-app}/tests/MobileMenu.test.tsx (88%) create mode 100644 excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap rename {src => excalidraw-app}/tests/collab.test.tsx (86%) rename {src => excalidraw-app}/tests/reconciliation.test.ts (97%) rename src/{components => tests}/App.test.tsx (92%) create mode 100644 src/tests/__snapshots__/App.test.tsx.snap rename src/{actions => tests}/actionStyles.test.tsx (92%) diff --git a/src/excalidraw-app/CustomStats.tsx b/excalidraw-app/CustomStats.tsx similarity index 86% rename from src/excalidraw-app/CustomStats.tsx rename to excalidraw-app/CustomStats.tsx index addea4cd9d..b34c1f7d7c 100644 --- a/src/excalidraw-app/CustomStats.tsx +++ b/excalidraw-app/CustomStats.tsx @@ -1,14 +1,14 @@ import { useEffect, useState } from "react"; -import { debounce, getVersion, nFormatter } from "../utils"; +import { debounce, getVersion, nFormatter } from "../src/utils"; import { getElementsStorageSize, getTotalStorageSize, } from "./data/localStorage"; -import { DEFAULT_VERSION } from "../constants"; -import { t } from "../i18n"; -import { copyTextToSystemClipboard } from "../clipboard"; -import { NonDeletedExcalidrawElement } from "../element/types"; -import { UIAppState } from "../types"; +import { DEFAULT_VERSION } from "../src/constants"; +import { t } from "../src/i18n"; +import { copyTextToSystemClipboard } from "../src/clipboard"; +import { NonDeletedExcalidrawElement } from "../src/element/types"; +import { UIAppState } from "../src/types"; type StorageSizes = { scene: number; total: number }; diff --git a/src/excalidraw-app/app-jotai.ts b/excalidraw-app/app-jotai.ts similarity index 100% rename from src/excalidraw-app/app-jotai.ts rename to excalidraw-app/app-jotai.ts diff --git a/src/excalidraw-app/app_constants.ts b/excalidraw-app/app_constants.ts similarity index 100% rename from src/excalidraw-app/app_constants.ts rename to excalidraw-app/app_constants.ts diff --git a/src/excalidraw-app/collab/Collab.tsx b/excalidraw-app/collab/Collab.tsx similarity index 97% rename from src/excalidraw-app/collab/Collab.tsx rename to excalidraw-app/collab/Collab.tsx index 673d89e8b9..0d57a89063 100644 --- a/src/excalidraw-app/collab/Collab.tsx +++ b/excalidraw-app/collab/Collab.tsx @@ -1,23 +1,23 @@ import throttle from "lodash.throttle"; import { PureComponent } from "react"; -import { ExcalidrawImperativeAPI } from "../../types"; -import { ErrorDialog } from "../../components/ErrorDialog"; -import { APP_NAME, ENV, EVENT } from "../../constants"; -import { ImportedDataState } from "../../data/types"; +import { ExcalidrawImperativeAPI } from "../../src/types"; +import { ErrorDialog } from "../../src/components/ErrorDialog"; +import { APP_NAME, ENV, EVENT } from "../../src/constants"; +import { ImportedDataState } from "../../src/data/types"; import { ExcalidrawElement, InitializedExcalidrawImageElement, -} from "../../element/types"; +} from "../../src/element/types"; import { getSceneVersion, restoreElements, -} from "../../packages/excalidraw/index"; -import { Collaborator, Gesture } from "../../types"; +} from "../../src/packages/excalidraw/index"; +import { Collaborator, Gesture } from "../../src/types"; import { preventUnload, resolvablePromise, withBatchedUpdates, -} from "../../utils"; +} from "../../src/utils"; import { CURSOR_SYNC_TIMEOUT, FILE_UPLOAD_MAX_BYTES, @@ -48,25 +48,25 @@ import { } from "../data/localStorage"; import Portal from "./Portal"; import RoomDialog from "./RoomDialog"; -import { t } from "../../i18n"; -import { UserIdleState } from "../../types"; -import { IDLE_THRESHOLD, ACTIVE_THRESHOLD } from "../../constants"; +import { t } from "../../src/i18n"; +import { UserIdleState } from "../../src/types"; +import { IDLE_THRESHOLD, ACTIVE_THRESHOLD } from "../../src/constants"; import { encodeFilesForUpload, FileManager, updateStaleImageStatuses, } from "../data/FileManager"; -import { AbortError } from "../../errors"; +import { AbortError } from "../../src/errors"; import { isImageElement, isInitializedImageElement, -} from "../../element/typeChecks"; -import { newElementWith } from "../../element/mutateElement"; +} from "../../src/element/typeChecks"; +import { newElementWith } from "../../src/element/mutateElement"; import { ReconciledElements, reconcileElements as _reconcileElements, } from "./reconciliation"; -import { decryptData } from "../../data/encryption"; +import { decryptData } from "../../src/data/encryption"; import { resetBrowserStateVersions } from "../data/tabSync"; import { LocalData } from "../data/LocalData"; import { atom, useAtom } from "jotai"; diff --git a/src/excalidraw-app/collab/Portal.tsx b/excalidraw-app/collab/Portal.tsx similarity index 94% rename from src/excalidraw-app/collab/Portal.tsx rename to excalidraw-app/collab/Portal.tsx index 1d4db3c0cf..401b83ec51 100644 --- a/src/excalidraw-app/collab/Portal.tsx +++ b/excalidraw-app/collab/Portal.tsx @@ -6,19 +6,19 @@ import { import { TCollabClass } from "./Collab"; -import { ExcalidrawElement } from "../../element/types"; +import { ExcalidrawElement } from "../../src/element/types"; import { WS_EVENTS, FILE_UPLOAD_TIMEOUT, WS_SCENE_EVENT_TYPES, } from "../app_constants"; -import { UserIdleState } from "../../types"; -import { trackEvent } from "../../analytics"; +import { UserIdleState } from "../../src/types"; +import { trackEvent } from "../../src/analytics"; import throttle from "lodash.throttle"; -import { newElementWith } from "../../element/mutateElement"; +import { newElementWith } from "../../src/element/mutateElement"; import { BroadcastedExcalidrawElement } from "./reconciliation"; -import { encryptData } from "../../data/encryption"; -import { PRECEDING_ELEMENT_KEY } from "../../constants"; +import { encryptData } from "../../src/data/encryption"; +import { PRECEDING_ELEMENT_KEY } from "../../src/constants"; class Portal { collab: TCollabClass; diff --git a/src/excalidraw-app/collab/RoomDialog.scss b/excalidraw-app/collab/RoomDialog.scss similarity index 98% rename from src/excalidraw-app/collab/RoomDialog.scss rename to excalidraw-app/collab/RoomDialog.scss index 0d1bcad6cc..b5f62330ec 100644 --- a/src/excalidraw-app/collab/RoomDialog.scss +++ b/excalidraw-app/collab/RoomDialog.scss @@ -1,4 +1,4 @@ -@import "../../css/variables.module"; +@import "../../src/css/variables.module"; .excalidraw { .RoomDialog { diff --git a/src/excalidraw-app/collab/RoomDialog.tsx b/excalidraw-app/collab/RoomDialog.tsx similarity index 90% rename from src/excalidraw-app/collab/RoomDialog.tsx rename to excalidraw-app/collab/RoomDialog.tsx index 6b07062707..3f2a9e7bc2 100644 --- a/src/excalidraw-app/collab/RoomDialog.tsx +++ b/excalidraw-app/collab/RoomDialog.tsx @@ -1,13 +1,13 @@ import { useRef, useState } from "react"; import * as Popover from "@radix-ui/react-popover"; -import { copyTextToSystemClipboard } from "../../clipboard"; -import { trackEvent } from "../../analytics"; -import { getFrame } from "../../utils"; -import { useI18n } from "../../i18n"; -import { KEYS } from "../../keys"; +import { copyTextToSystemClipboard } from "../../src/clipboard"; +import { trackEvent } from "../../src/analytics"; +import { getFrame } from "../../src/utils"; +import { useI18n } from "../../src/i18n"; +import { KEYS } from "../../src/keys"; -import { Dialog } from "../../components/Dialog"; +import { Dialog } from "../../src/components/Dialog"; import { copyIcon, playerPlayIcon, @@ -16,11 +16,11 @@ import { shareIOS, shareWindows, tablerCheckIcon, -} from "../../components/icons"; -import { TextField } from "../../components/TextField"; -import { FilledButton } from "../../components/FilledButton"; +} from "../../src/components/icons"; +import { TextField } from "../../src/components/TextField"; +import { FilledButton } from "../../src/components/FilledButton"; -import { ReactComponent as CollabImage } from "../../assets/lock.svg"; +import { ReactComponent as CollabImage } from "../../src/assets/lock.svg"; import "./RoomDialog.scss"; const getShareIcon = () => { diff --git a/src/excalidraw-app/collab/reconciliation.ts b/excalidraw-app/collab/reconciliation.ts similarity index 95% rename from src/excalidraw-app/collab/reconciliation.ts rename to excalidraw-app/collab/reconciliation.ts index 3f50bc3587..1efc5db460 100644 --- a/src/excalidraw-app/collab/reconciliation.ts +++ b/excalidraw-app/collab/reconciliation.ts @@ -1,7 +1,7 @@ -import { PRECEDING_ELEMENT_KEY } from "../../constants"; -import { ExcalidrawElement } from "../../element/types"; -import { AppState } from "../../types"; -import { arrayToMapWithIndex } from "../../utils"; +import { PRECEDING_ELEMENT_KEY } from "../../src/constants"; +import { ExcalidrawElement } from "../../src/element/types"; +import { AppState } from "../../src/types"; +import { arrayToMapWithIndex } from "../../src/utils"; export type ReconciledElements = readonly ExcalidrawElement[] & { _brand: "reconciledElements"; diff --git a/src/excalidraw-app/components/AppFooter.tsx b/excalidraw-app/components/AppFooter.tsx similarity index 90% rename from src/excalidraw-app/components/AppFooter.tsx rename to excalidraw-app/components/AppFooter.tsx index 6248732183..81846f0d31 100644 --- a/src/excalidraw-app/components/AppFooter.tsx +++ b/excalidraw-app/components/AppFooter.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Footer } from "../../packages/excalidraw/index"; +import { Footer } from "../../src/packages/excalidraw/index"; import { EncryptedIcon } from "./EncryptedIcon"; import { ExcalidrawPlusAppLink } from "./ExcalidrawPlusAppLink"; import { isExcalidrawPlusSignedUser } from "../app_constants"; diff --git a/src/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx similarity index 91% rename from src/excalidraw-app/components/AppMainMenu.tsx rename to excalidraw-app/components/AppMainMenu.tsx index 6e12d78119..7a984a8f5d 100644 --- a/src/excalidraw-app/components/AppMainMenu.tsx +++ b/excalidraw-app/components/AppMainMenu.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { PlusPromoIcon } from "../../components/icons"; -import { MainMenu } from "../../packages/excalidraw/index"; +import { PlusPromoIcon } from "../../src/components/icons"; +import { MainMenu } from "../../src/packages/excalidraw/index"; import { LanguageList } from "./LanguageList"; export const AppMainMenu: React.FC<{ diff --git a/src/excalidraw-app/components/AppWelcomeScreen.tsx b/excalidraw-app/components/AppWelcomeScreen.tsx similarity index 90% rename from src/excalidraw-app/components/AppWelcomeScreen.tsx rename to excalidraw-app/components/AppWelcomeScreen.tsx index 72b688d9bd..cdefd1fe4b 100644 --- a/src/excalidraw-app/components/AppWelcomeScreen.tsx +++ b/excalidraw-app/components/AppWelcomeScreen.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { PlusPromoIcon } from "../../components/icons"; -import { useI18n } from "../../i18n"; -import { WelcomeScreen } from "../../packages/excalidraw/index"; +import { PlusPromoIcon } from "../../src/components/icons"; +import { useI18n } from "../../src/i18n"; +import { WelcomeScreen } from "../../src/packages/excalidraw/index"; import { isExcalidrawPlusSignedUser } from "../app_constants"; -import { POINTER_EVENTS } from "../../constants"; +import { POINTER_EVENTS } from "../../src/constants"; export const AppWelcomeScreen: React.FC<{ setCollabDialogShown: (toggle: boolean) => any; diff --git a/src/excalidraw-app/components/EncryptedIcon.tsx b/excalidraw-app/components/EncryptedIcon.tsx similarity index 72% rename from src/excalidraw-app/components/EncryptedIcon.tsx rename to excalidraw-app/components/EncryptedIcon.tsx index a917689176..84b2f1a0cd 100644 --- a/src/excalidraw-app/components/EncryptedIcon.tsx +++ b/excalidraw-app/components/EncryptedIcon.tsx @@ -1,6 +1,6 @@ -import { shield } from "../../components/icons"; -import { Tooltip } from "../../components/Tooltip"; -import { useI18n } from "../../i18n"; +import { shield } from "../../src/components/icons"; +import { Tooltip } from "../../src/components/Tooltip"; +import { useI18n } from "../../src/i18n"; export const EncryptedIcon = () => { const { t } = useI18n(); diff --git a/src/excalidraw-app/components/ExcalidrawPlusAppLink.tsx b/excalidraw-app/components/ExcalidrawPlusAppLink.tsx similarity index 100% rename from src/excalidraw-app/components/ExcalidrawPlusAppLink.tsx rename to excalidraw-app/components/ExcalidrawPlusAppLink.tsx diff --git a/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx b/excalidraw-app/components/ExportToExcalidrawPlus.tsx similarity index 80% rename from src/excalidraw-app/components/ExportToExcalidrawPlus.tsx rename to excalidraw-app/components/ExportToExcalidrawPlus.tsx index 42b7a7d4f7..0b577ad7de 100644 --- a/src/excalidraw-app/components/ExportToExcalidrawPlus.tsx +++ b/excalidraw-app/components/ExportToExcalidrawPlus.tsx @@ -1,20 +1,20 @@ import React from "react"; -import { Card } from "../../components/Card"; -import { ToolButton } from "../../components/ToolButton"; -import { serializeAsJSON } from "../../data/json"; +import { Card } from "../../src/components/Card"; +import { ToolButton } from "../../src/components/ToolButton"; +import { serializeAsJSON } from "../../src/data/json"; import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase"; -import { FileId, NonDeletedExcalidrawElement } from "../../element/types"; -import { AppState, BinaryFileData, BinaryFiles } from "../../types"; +import { FileId, NonDeletedExcalidrawElement } from "../../src/element/types"; +import { AppState, BinaryFileData, BinaryFiles } from "../../src/types"; import { nanoid } from "nanoid"; -import { useI18n } from "../../i18n"; -import { encryptData, generateEncryptionKey } from "../../data/encryption"; -import { isInitializedImageElement } from "../../element/typeChecks"; +import { useI18n } from "../../src/i18n"; +import { encryptData, generateEncryptionKey } from "../../src/data/encryption"; +import { isInitializedImageElement } from "../../src/element/typeChecks"; import { FILE_UPLOAD_MAX_BYTES } from "../app_constants"; import { encodeFilesForUpload } from "../data/FileManager"; -import { MIME_TYPES } from "../../constants"; -import { trackEvent } from "../../analytics"; -import { getFrame } from "../../utils"; -import { ExcalidrawLogo } from "../../components/ExcalidrawLogo"; +import { MIME_TYPES } from "../../src/constants"; +import { trackEvent } from "../../src/analytics"; +import { getFrame } from "../../src/utils"; +import { ExcalidrawLogo } from "../../src/components/ExcalidrawLogo"; export const exportToExcalidrawPlus = async ( elements: readonly NonDeletedExcalidrawElement[], diff --git a/src/excalidraw-app/components/GitHubCorner.tsx b/excalidraw-app/components/GitHubCorner.tsx similarity index 94% rename from src/excalidraw-app/components/GitHubCorner.tsx rename to excalidraw-app/components/GitHubCorner.tsx index 34e8d29ab0..e0575433d1 100644 --- a/src/excalidraw-app/components/GitHubCorner.tsx +++ b/excalidraw-app/components/GitHubCorner.tsx @@ -1,7 +1,7 @@ import oc from "open-color"; import React from "react"; -import { THEME } from "../../constants"; -import { Theme } from "../../element/types"; +import { THEME } from "../../src/constants"; +import { Theme } from "../../src/element/types"; // https://github.com/tholman/github-corners export const GitHubCorner = React.memo( diff --git a/src/excalidraw-app/components/LanguageList.tsx b/excalidraw-app/components/LanguageList.tsx similarity index 88% rename from src/excalidraw-app/components/LanguageList.tsx rename to excalidraw-app/components/LanguageList.tsx index c80acc9b60..11d4b6d001 100644 --- a/src/excalidraw-app/components/LanguageList.tsx +++ b/excalidraw-app/components/LanguageList.tsx @@ -1,8 +1,8 @@ import { useSetAtom } from "jotai"; import React from "react"; import { appLangCodeAtom } from ".."; -import { useI18n } from "../../i18n"; -import { languages } from "../../i18n"; +import { useI18n } from "../../src/i18n"; +import { languages } from "../../src/i18n"; export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { const { t, langCode } = useI18n(); diff --git a/src/excalidraw-app/data/FileManager.ts b/excalidraw-app/data/FileManager.ts similarity index 95% rename from src/excalidraw-app/data/FileManager.ts rename to excalidraw-app/data/FileManager.ts index 419407a6bb..426afc4d37 100644 --- a/src/excalidraw-app/data/FileManager.ts +++ b/excalidraw-app/data/FileManager.ts @@ -1,19 +1,19 @@ -import { compressData } from "../../data/encode"; -import { newElementWith } from "../../element/mutateElement"; -import { isInitializedImageElement } from "../../element/typeChecks"; +import { compressData } from "../../src/data/encode"; +import { newElementWith } from "../../src/element/mutateElement"; +import { isInitializedImageElement } from "../../src/element/typeChecks"; import { ExcalidrawElement, ExcalidrawImageElement, FileId, InitializedExcalidrawImageElement, -} from "../../element/types"; -import { t } from "../../i18n"; +} from "../../src/element/types"; +import { t } from "../../src/i18n"; import { BinaryFileData, BinaryFileMetadata, ExcalidrawImperativeAPI, BinaryFiles, -} from "../../types"; +} from "../../src/types"; export class FileManager { /** files being fetched */ diff --git a/src/excalidraw-app/data/LocalData.ts b/excalidraw-app/data/LocalData.ts similarity index 94% rename from src/excalidraw-app/data/LocalData.ts rename to excalidraw-app/data/LocalData.ts index 08f91d8d6b..5d9ba0e144 100644 --- a/src/excalidraw-app/data/LocalData.ts +++ b/excalidraw-app/data/LocalData.ts @@ -11,11 +11,11 @@ */ import { createStore, entries, del, getMany, set, setMany } from "idb-keyval"; -import { clearAppStateForLocalStorage } from "../../appState"; -import { clearElementsForLocalStorage } from "../../element"; -import { ExcalidrawElement, FileId } from "../../element/types"; -import { AppState, BinaryFileData, BinaryFiles } from "../../types"; -import { debounce } from "../../utils"; +import { clearAppStateForLocalStorage } from "../../src/appState"; +import { clearElementsForLocalStorage } from "../../src/element"; +import { ExcalidrawElement, FileId } from "../../src/element/types"; +import { AppState, BinaryFileData, BinaryFiles } from "../../src/types"; +import { debounce } from "../../src/utils"; import { SAVE_TO_LOCAL_STORAGE_TIMEOUT, STORAGE_KEYS } from "../app_constants"; import { FileManager } from "./FileManager"; import { Locker } from "./Locker"; diff --git a/src/excalidraw-app/data/Locker.ts b/excalidraw-app/data/Locker.ts similarity index 100% rename from src/excalidraw-app/data/Locker.ts rename to excalidraw-app/data/Locker.ts diff --git a/src/excalidraw-app/data/firebase.ts b/excalidraw-app/data/firebase.ts similarity index 95% rename from src/excalidraw-app/data/firebase.ts rename to excalidraw-app/data/firebase.ts index bb914de12f..831213d603 100644 --- a/src/excalidraw-app/data/firebase.ts +++ b/excalidraw-app/data/firebase.ts @@ -1,20 +1,20 @@ -import { ExcalidrawElement, FileId } from "../../element/types"; -import { getSceneVersion } from "../../element"; +import { ExcalidrawElement, FileId } from "../../src/element/types"; +import { getSceneVersion } from "../../src/element"; import Portal from "../collab/Portal"; -import { restoreElements } from "../../data/restore"; +import { restoreElements } from "../../src/data/restore"; import { AppState, BinaryFileData, BinaryFileMetadata, DataURL, -} from "../../types"; +} from "../../src/types"; import { FILE_CACHE_MAX_AGE_SEC } from "../app_constants"; -import { decompressData } from "../../data/encode"; -import { encryptData, decryptData } from "../../data/encryption"; -import { MIME_TYPES } from "../../constants"; +import { decompressData } from "../../src/data/encode"; +import { encryptData, decryptData } from "../../src/data/encryption"; +import { MIME_TYPES } from "../../src/constants"; import { reconcileElements } from "../collab/reconciliation"; import { getSyncableElements, SyncableExcalidrawElement } from "."; -import { ResolutionType } from "../../utility-types"; +import { ResolutionType } from "../../src/utility-types"; // private // ----------------------------------------------------------------------------- diff --git a/src/excalidraw-app/data/index.ts b/excalidraw-app/data/index.ts similarity index 93% rename from src/excalidraw-app/data/index.ts rename to excalidraw-app/data/index.ts index d3108c7617..3870ca37c5 100644 --- a/src/excalidraw-app/data/index.ts +++ b/excalidraw-app/data/index.ts @@ -1,23 +1,23 @@ -import { compressData, decompressData } from "../../data/encode"; +import { compressData, decompressData } from "../../src/data/encode"; import { decryptData, generateEncryptionKey, IV_LENGTH_BYTES, -} from "../../data/encryption"; -import { serializeAsJSON } from "../../data/json"; -import { restore } from "../../data/restore"; -import { ImportedDataState } from "../../data/types"; -import { isInvisiblySmallElement } from "../../element/sizeHelpers"; -import { isInitializedImageElement } from "../../element/typeChecks"; -import { ExcalidrawElement, FileId } from "../../element/types"; -import { t } from "../../i18n"; +} from "../../src/data/encryption"; +import { serializeAsJSON } from "../../src/data/json"; +import { restore } from "../../src/data/restore"; +import { ImportedDataState } from "../../src/data/types"; +import { isInvisiblySmallElement } from "../../src/element/sizeHelpers"; +import { isInitializedImageElement } from "../../src/element/typeChecks"; +import { ExcalidrawElement, FileId } from "../../src/element/types"; +import { t } from "../../src/i18n"; import { AppState, BinaryFileData, BinaryFiles, UserIdleState, -} from "../../types"; -import { bytesToHexString } from "../../utils"; +} from "../../src/types"; +import { bytesToHexString } from "../../src/utils"; import { DELETED_ELEMENT_TIMEOUT, FILE_UPLOAD_MAX_BYTES, diff --git a/src/excalidraw-app/data/localStorage.ts b/excalidraw-app/data/localStorage.ts similarity index 91% rename from src/excalidraw-app/data/localStorage.ts rename to excalidraw-app/data/localStorage.ts index 6902052bdc..2a1b93ebf2 100644 --- a/src/excalidraw-app/data/localStorage.ts +++ b/excalidraw-app/data/localStorage.ts @@ -1,12 +1,12 @@ -import { ExcalidrawElement } from "../../element/types"; -import { AppState } from "../../types"; +import { ExcalidrawElement } from "../../src/element/types"; +import { AppState } from "../../src/types"; import { clearAppStateForLocalStorage, getDefaultAppState, -} from "../../appState"; -import { clearElementsForLocalStorage } from "../../element"; +} from "../../src/appState"; +import { clearElementsForLocalStorage } from "../../src/element"; import { STORAGE_KEYS } from "../app_constants"; -import { ImportedDataState } from "../../data/types"; +import { ImportedDataState } from "../../src/data/types"; export const saveUsernameToLocalStorage = (username: string) => { try { diff --git a/src/excalidraw-app/data/tabSync.ts b/excalidraw-app/data/tabSync.ts similarity index 100% rename from src/excalidraw-app/data/tabSync.ts rename to excalidraw-app/data/tabSync.ts diff --git a/src/excalidraw-app/debug.ts b/excalidraw-app/debug.ts similarity index 100% rename from src/excalidraw-app/debug.ts rename to excalidraw-app/debug.ts diff --git a/src/excalidraw-app/index.scss b/excalidraw-app/index.scss similarity index 100% rename from src/excalidraw-app/index.scss rename to excalidraw-app/index.scss diff --git a/src/excalidraw-app/index.tsx b/excalidraw-app/index.tsx similarity index 94% rename from src/excalidraw-app/index.tsx rename to excalidraw-app/index.tsx index 00bcd0cb39..9156d5010a 100644 --- a/src/excalidraw-app/index.tsx +++ b/excalidraw-app/index.tsx @@ -1,31 +1,31 @@ -import polyfill from "../polyfill"; +import polyfill from "../src/polyfill"; import LanguageDetector from "i18next-browser-languagedetector"; import { useEffect, useRef, useState } from "react"; -import { trackEvent } from "../analytics"; -import { getDefaultAppState } from "../appState"; -import { ErrorDialog } from "../components/ErrorDialog"; -import { TopErrorBoundary } from "../components/TopErrorBoundary"; +import { trackEvent } from "../src/analytics"; +import { getDefaultAppState } from "../src/appState"; +import { ErrorDialog } from "../src/components/ErrorDialog"; +import { TopErrorBoundary } from "../src/components/TopErrorBoundary"; import { APP_NAME, EVENT, THEME, TITLE_TIMEOUT, VERSION_TIMEOUT, -} from "../constants"; -import { loadFromBlob } from "../data/blob"; +} from "../src/constants"; +import { loadFromBlob } from "../src/data/blob"; import { ExcalidrawElement, FileId, NonDeletedExcalidrawElement, Theme, -} from "../element/types"; -import { useCallbackRefState } from "../hooks/useCallbackRefState"; -import { t } from "../i18n"; +} from "../src/element/types"; +import { useCallbackRefState } from "../src/hooks/useCallbackRefState"; +import { t } from "../src/i18n"; import { Excalidraw, defaultLang, LiveCollaborationTrigger, -} from "../packages/excalidraw/index"; +} from "../src/packages/excalidraw/index"; import { AppState, LibraryItems, @@ -33,7 +33,7 @@ import { BinaryFiles, ExcalidrawInitialDataState, UIAppState, -} from "../types"; +} from "../src/types"; import { debounce, getVersion, @@ -43,7 +43,7 @@ import { ResolvablePromise, resolvablePromise, isRunningInIframe, -} from "../utils"; +} from "../src/utils"; import { FIREBASE_STORAGE_PREFIXES, STORAGE_KEYS, @@ -68,33 +68,40 @@ import { importUsernameFromLocalStorage, } from "./data/localStorage"; import CustomStats from "./CustomStats"; -import { restore, restoreAppState, RestoredDataState } from "../data/restore"; +import { + restore, + restoreAppState, + RestoredDataState, +} from "../src/data/restore"; import { ExportToExcalidrawPlus, exportToExcalidrawPlus, } from "./components/ExportToExcalidrawPlus"; import { updateStaleImageStatuses } from "./data/FileManager"; -import { newElementWith } from "../element/mutateElement"; -import { isInitializedImageElement } from "../element/typeChecks"; +import { newElementWith } from "../src/element/mutateElement"; +import { isInitializedImageElement } from "../src/element/typeChecks"; import { loadFilesFromFirebase } from "./data/firebase"; import { LocalData } from "./data/LocalData"; import { isBrowserStorageStateNewer } from "./data/tabSync"; import clsx from "clsx"; import { reconcileElements } from "./collab/reconciliation"; -import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library"; +import { + parseLibraryTokensFromUrl, + useHandleLibrary, +} from "../src/data/library"; import { AppMainMenu } from "./components/AppMainMenu"; import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; import { AppFooter } from "./components/AppFooter"; import { atom, Provider, useAtom, useAtomValue } from "jotai"; -import { useAtomWithInitialValue } from "../jotai"; +import { useAtomWithInitialValue } from "../src/jotai"; import { appJotaiStore } from "./app-jotai"; import "./index.scss"; -import { ResolutionType } from "../utility-types"; -import { ShareableLinkDialog } from "../components/ShareableLinkDialog"; -import { openConfirmModal } from "../components/OverwriteConfirm/OverwriteConfirmState"; -import { OverwriteConfirmDialog } from "../components/OverwriteConfirm/OverwriteConfirm"; -import Trans from "../components/Trans"; +import { ResolutionType } from "../src/utility-types"; +import { ShareableLinkDialog } from "../src/components/ShareableLinkDialog"; +import { openConfirmModal } from "../src/components/OverwriteConfirm/OverwriteConfirmState"; +import { OverwriteConfirmDialog } from "../src/components/OverwriteConfirm/OverwriteConfirm"; +import Trans from "../src/components/Trans"; polyfill(); diff --git a/src/excalidraw-app/sentry.ts b/excalidraw-app/sentry.ts similarity index 100% rename from src/excalidraw-app/sentry.ts rename to excalidraw-app/sentry.ts diff --git a/excalidraw-app/tests/LanguageList.test.tsx b/excalidraw-app/tests/LanguageList.test.tsx new file mode 100644 index 0000000000..49a70a14c0 --- /dev/null +++ b/excalidraw-app/tests/LanguageList.test.tsx @@ -0,0 +1,29 @@ +import { defaultLang } from "../../src/i18n"; +import { UI } from "../../src/tests/helpers/ui"; +import { screen, fireEvent, waitFor, render } from "../../src/tests/test-utils"; + +import ExcalidrawApp from "../../excalidraw-app"; + +describe("Test LanguageList", () => { + it("rerenders UI on language change", async () => { + await render(); + + // select rectangle tool to show properties menu + UI.clickTool("rectangle"); + // english lang should display `thin` label + expect(screen.queryByTitle(/thin/i)).not.toBeNull(); + fireEvent.click(document.querySelector(".dropdown-menu-button")!); + + fireEvent.change(document.querySelector(".dropdown-select__language")!, { + target: { value: "de-DE" }, + }); + // switching to german, `thin` label should no longer exist + await waitFor(() => expect(screen.queryByTitle(/thin/i)).toBeNull()); + // reset language + fireEvent.change(document.querySelector(".dropdown-select__language")!, { + target: { value: defaultLang.code }, + }); + // switching back to English + await waitFor(() => expect(screen.queryByTitle(/thin/i)).not.toBeNull()); + }); +}); diff --git a/src/tests/MobileMenu.test.tsx b/excalidraw-app/tests/MobileMenu.test.tsx similarity index 88% rename from src/tests/MobileMenu.test.tsx rename to excalidraw-app/tests/MobileMenu.test.tsx index a5014062db..2aec60b17a 100644 --- a/src/tests/MobileMenu.test.tsx +++ b/excalidraw-app/tests/MobileMenu.test.tsx @@ -1,11 +1,11 @@ -import ExcalidrawApp from "../excalidraw-app"; +import ExcalidrawApp from "../../excalidraw-app"; import { mockBoundingClientRect, render, restoreOriginalGetBoundingClientRect, -} from "./test-utils"; +} from "../../src/tests/test-utils"; -import { UI } from "./helpers/ui"; +import { UI } from "../../src/tests/helpers/ui"; describe("Test MobileMenu", () => { const { h } = window; diff --git a/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap new file mode 100644 index 0000000000..ad0c9f0f1d --- /dev/null +++ b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap @@ -0,0 +1,257 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Test MobileMenu > should initialize with welcome screen and hide once user interacts 1`] = ` +
+ +
+ All your data is saved locally in your browser. +
+
+ + + + +
+ +
+
+ Try Excalidraw Plus! +
+
+
+
+`; diff --git a/src/tests/collab.test.tsx b/excalidraw-app/tests/collab.test.tsx similarity index 86% rename from src/tests/collab.test.tsx rename to excalidraw-app/tests/collab.test.tsx index c5d4f6c443..343a67ac46 100644 --- a/src/tests/collab.test.tsx +++ b/excalidraw-app/tests/collab.test.tsx @@ -1,8 +1,8 @@ import { vi } from "vitest"; -import { render, updateSceneData, waitFor } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; -import { API } from "./helpers/api"; -import { createUndoAction } from "../actions/actionHistory"; +import { render, updateSceneData, waitFor } from "../../src/tests/test-utils"; +import ExcalidrawApp from "../../excalidraw-app"; +import { API } from "../../src/tests/helpers/api"; +import { createUndoAction } from "../../src/actions/actionHistory"; const { h } = window; Object.defineProperty(window, "crypto", { @@ -16,7 +16,7 @@ Object.defineProperty(window, "crypto", { }, }); -vi.mock("../excalidraw-app/data/index.ts", async (importActual) => { +vi.mock("../../excalidraw-app/data/index.ts", async (importActual) => { const module = (await importActual()) as any; return { __esmodule: true, @@ -27,7 +27,7 @@ vi.mock("../excalidraw-app/data/index.ts", async (importActual) => { }; }); -vi.mock("../excalidraw-app/data/firebase.ts", () => { +vi.mock("../../excalidraw-app/data/firebase.ts", () => { const loadFromFirebase = async () => null; const saveToFirebase = () => {}; const isSavedToFirebase = () => true; diff --git a/src/tests/reconciliation.test.ts b/excalidraw-app/tests/reconciliation.test.ts similarity index 97% rename from src/tests/reconciliation.test.ts rename to excalidraw-app/tests/reconciliation.test.ts index f050ed6597..c3e247406c 100644 --- a/src/tests/reconciliation.test.ts +++ b/excalidraw-app/tests/reconciliation.test.ts @@ -1,13 +1,13 @@ import { expect } from "chai"; -import { PRECEDING_ELEMENT_KEY } from "../constants"; -import { ExcalidrawElement } from "../element/types"; +import { PRECEDING_ELEMENT_KEY } from "../../src/constants"; +import { ExcalidrawElement } from "../../src/element/types"; import { BroadcastedExcalidrawElement, ReconciledElements, reconcileElements, -} from "../excalidraw-app/collab/reconciliation"; -import { randomInteger } from "../random"; -import { AppState } from "../types"; +} from "../../excalidraw-app/collab/reconciliation"; +import { randomInteger } from "../../src/random"; +import { AppState } from "../../src/types"; type Id = string; type ElementLike = { diff --git a/src/element/newElement.test.ts b/src/element/newElement.test.ts index ba7c63ee2d..19761231bd 100644 --- a/src/element/newElement.test.ts +++ b/src/element/newElement.test.ts @@ -203,7 +203,6 @@ describe("duplicating multiple elements", () => { ); clonedArrows.forEach((arrow) => { - // console.log(arrow); expect( clonedRectangle.boundElements!.find((e) => e.id === arrow.id), ).toEqual( diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index c855de3573..fc1e8cf2e4 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -1,5 +1,5 @@ import ReactDOM from "react-dom"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { GlobalTestState, render, screen } from "../tests/test-utils"; import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; import { CODES, KEYS } from "../keys"; @@ -41,7 +41,7 @@ describe("textWysiwyg", () => { describe("start text editing", () => { const { h } = window; beforeEach(async () => { - await render(); + await render(); h.elements = []; }); @@ -243,7 +243,7 @@ describe("textWysiwyg", () => { }); beforeEach(async () => { - await render(); + await render(); //@ts-ignore h.app.refreshDeviceState(h.app.excalidrawContainerRef.current!); @@ -477,7 +477,7 @@ describe("textWysiwyg", () => { const { h } = window; beforeEach(async () => { - await render(); + await render(); h.elements = []; rectangle = UI.createElement("rectangle", { @@ -1511,7 +1511,7 @@ describe("textWysiwyg", () => { }); it("should bump the version of labelled arrow when label updated", async () => { - await render(); + await render(); const arrow = UI.createElement("arrow", { width: 300, height: 0, diff --git a/src/index.tsx b/src/index.tsx index ea8c39cb1b..2165ded778 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,9 +1,9 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import ExcalidrawApp from "./excalidraw-app"; +import ExcalidrawApp from "../excalidraw-app"; import { registerSW } from "virtual:pwa-register"; -import "./excalidraw-app/sentry"; +import "../excalidraw-app/sentry"; window.__EXCALIDRAW_SHA__ = import.meta.env.VITE_APP_GIT_SHA; const rootElement = document.getElementById("root")!; const root = createRoot(rootElement); diff --git a/src/components/App.test.tsx b/src/tests/App.test.tsx similarity index 92% rename from src/components/App.test.tsx rename to src/tests/App.test.tsx index 19f987308d..af576dcb01 100644 --- a/src/components/App.test.tsx +++ b/src/tests/App.test.tsx @@ -3,7 +3,7 @@ import * as Renderer from "../renderer/renderScene"; import { reseed } from "../random"; import { render, queryByTestId } from "../tests/test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { vi } from "vitest"; const renderStaticScene = vi.spyOn(Renderer, "renderStaticScene"); @@ -35,7 +35,7 @@ describe("Test ", () => { }; }; - await render(); + await render(); expect( queryByTestId( document.querySelector(".excalidraw-modal-container")!, diff --git a/src/tests/__snapshots__/App.test.tsx.snap b/src/tests/__snapshots__/App.test.tsx.snap new file mode 100644 index 0000000000..49c2fd0fcc --- /dev/null +++ b/src/tests/__snapshots__/App.test.tsx.snap @@ -0,0 +1,50 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Test > should show error modal when using brave and measureText API is not working 1`] = ` +
+

+ Looks like you are using Brave browser with the + + Aggressively Block Fingerprinting + + setting enabled. +

+

+ This could result in breaking the + + Text Elements + + in your drawings. +

+

+ We strongly recommend disabling this setting. You can follow + + these steps + + on how to do so. +

+

+ If disabling this setting doesn't fix the display of text elements, please open an + + issue + + on our GitHub, or write us on + + Discord + . + +

+
+`; diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 832f1652a3..98e461a66a 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -13089,126 +13089,6 @@ exports[`regression tests > pinch-to-zoom works > [end of test] number of elemen exports[`regression tests > pinch-to-zoom works > [end of test] number of renders 1`] = `7`; -exports[`regression tests > rerenders UI on language change > [end of test] appState 1`] = ` -{ - "activeEmbeddable": null, - "activeTool": { - "customType": null, - "lastActiveTool": null, - "locked": false, - "type": "rectangle", - }, - "collaborators": Map {}, - "contextMenu": null, - "currentChartType": "bar", - "currentItemBackgroundColor": "transparent", - "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", - "currentItemFontFamily": 1, - "currentItemFontSize": 20, - "currentItemOpacity": 100, - "currentItemRoughness": 1, - "currentItemRoundness": "round", - "currentItemStartArrowhead": null, - "currentItemStrokeColor": "#1e1e1e", - "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, - "currentItemTextAlign": "left", - "cursorButton": "up", - "defaultSidebarDockedPreference": false, - "draggingElement": null, - "editingElement": null, - "editingFrame": null, - "editingGroupId": null, - "editingLinearElement": null, - "elementsToHighlight": null, - "errorMessage": null, - "exportBackground": true, - "exportEmbedScene": false, - "exportScale": 1, - "exportWithDarkMode": false, - "fileHandle": null, - "frameRendering": { - "clip": true, - "enabled": true, - "name": true, - "outline": true, - }, - "frameToHighlight": null, - "gridSize": null, - "height": 768, - "isBindingEnabled": true, - "isLoading": false, - "isResizing": false, - "isRotating": false, - "lastPointerDownWith": "mouse", - "multiElement": null, - "name": "Untitled-201933152653", - "offsetLeft": 0, - "offsetTop": 0, - "openDialog": null, - "openMenu": "canvas", - "openPopup": null, - "openSidebar": null, - "pasteDialog": { - "data": null, - "shown": false, - }, - "penDetected": false, - "penMode": false, - "pendingImageElementId": null, - "previousSelectedElementIds": {}, - "resizingElement": null, - "scrollX": 0, - "scrollY": 0, - "scrolledOutside": false, - "selectedElementIds": {}, - "selectedElementsAreBeingDragged": false, - "selectedGroupIds": {}, - "selectedLinearElement": null, - "selectionElement": null, - "shouldCacheIgnoreZoom": false, - "showHyperlinkPopup": false, - "showStats": false, - "showWelcomeScreen": true, - "startBoundElement": null, - "suggestedBindings": [], - "theme": "light", - "toast": null, - "viewBackgroundColor": "#ffffff", - "viewModeEnabled": false, - "width": 1024, - "zenModeEnabled": false, - "zoom": { - "value": 1, - }, -} -`; - -exports[`regression tests > rerenders UI on language change > [end of test] history 1`] = ` -{ - "recording": false, - "redoStack": [], - "stateHistory": [ - { - "appState": { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": {}, - "selectedGroupIds": {}, - "viewBackgroundColor": "#ffffff", - }, - "elements": [], - }, - ], -} -`; - -exports[`regression tests > rerenders UI on language change > [end of test] number of elements 1`] = `0`; - -exports[`regression tests > rerenders UI on language change > [end of test] number of renders 1`] = `5`; - exports[`regression tests > shift click on selected element should deselect it on pointer up > [end of test] appState 1`] = ` { "activeEmbeddable": null, diff --git a/src/actions/actionStyles.test.tsx b/src/tests/actionStyles.test.tsx similarity index 92% rename from src/actions/actionStyles.test.tsx rename to src/tests/actionStyles.test.tsx index 238196dfc9..832650427c 100644 --- a/src/actions/actionStyles.test.tsx +++ b/src/tests/actionStyles.test.tsx @@ -1,4 +1,4 @@ -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { CODES } from "../keys"; import { API } from "../tests/helpers/api"; import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; @@ -9,7 +9,7 @@ import { screen, togglePopover, } from "../tests/test-utils"; -import { copiedStyles } from "./actionStyles"; +import { copiedStyles } from "../actions/actionStyles"; const { h } = window; @@ -17,7 +17,7 @@ const mouse = new Pointer("mouse"); describe("actionStyles", () => { beforeEach(async () => { - await render(); + await render(); }); afterEach(async () => { diff --git a/src/tests/align.test.tsx b/src/tests/align.test.tsx index 33e3e33fce..ade5510b30 100644 --- a/src/tests/align.test.tsx +++ b/src/tests/align.test.tsx @@ -1,6 +1,6 @@ import ReactDOM from "react-dom"; import { render } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../../src/packages/excalidraw/index"; import { defaultLang, setLanguage } from "../i18n"; import { UI, Pointer, Keyboard } from "./helpers/ui"; import { API } from "./helpers/api"; @@ -60,7 +60,7 @@ describe("aligning", () => { mouse.reset(); await setLanguage(defaultLang); - await render(); + await render(); }); it("aligns two objects correctly to the top", () => { diff --git a/src/tests/appState.test.tsx b/src/tests/appState.test.tsx index 2572d25456..1b3077d3e2 100644 --- a/src/tests/appState.test.tsx +++ b/src/tests/appState.test.tsx @@ -1,6 +1,6 @@ import { queryByTestId, render, waitFor } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { getDefaultAppState } from "../appState"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants"; @@ -14,14 +14,17 @@ describe("appState", () => { const defaultAppState = getDefaultAppState(); const exportBackground = !defaultAppState.exportBackground; - await render(, { - localStorageData: { - appState: { - exportBackground, - viewBackgroundColor: "#F00", - }, - }, - }); + await render( + , + {}, + ); await waitFor(() => { expect(h.state.exportBackground).toBe(exportBackground); @@ -53,13 +56,15 @@ describe("appState", () => { }); it("changing fontSize with text tool selected (no element created yet)", async () => { - const { container } = await render(, { - localStorageData: { - appState: { - currentItemFontSize: 30, - }, - }, - }); + const { container } = await render( + , + ); UI.clickTool("text"); diff --git a/src/tests/binding.test.tsx b/src/tests/binding.test.tsx index 5a5663286f..e801361274 100644 --- a/src/tests/binding.test.tsx +++ b/src/tests/binding.test.tsx @@ -1,5 +1,5 @@ import { fireEvent, render } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../../src/packages/excalidraw/index"; import { UI, Pointer, Keyboard } from "./helpers/ui"; import { getTransformHandles } from "../element/transformHandles"; import { API } from "./helpers/api"; @@ -12,7 +12,7 @@ const mouse = new Pointer("mouse"); describe("element binding", () => { beforeEach(async () => { - await render(); + await render(); }); it("should create valid binding if duplicate start/end points", async () => { diff --git a/src/tests/clipboard.test.tsx b/src/tests/clipboard.test.tsx index 67a081013c..4a55e0689f 100644 --- a/src/tests/clipboard.test.tsx +++ b/src/tests/clipboard.test.tsx @@ -7,7 +7,7 @@ import { createPasteEvent, } from "./test-utils"; import { Pointer, Keyboard } from "./helpers/ui"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { KEYS } from "../keys"; import { getDefaultLineHeight, @@ -79,8 +79,13 @@ beforeEach(async () => { mouse.reset(); - await render(); - h.app.setAppState({ zoom: { value: 1 as NormalizedZoomValue } }); + await render( + , + ); setClipboardText(""); Object.assign(document, { elementFromPoint: () => GlobalTestState.canvas, @@ -91,7 +96,6 @@ describe("general paste behavior", () => { it("should randomize seed on paste", async () => { const rectangle = API.createElement({ type: "rectangle" }); const clipboardJSON = (await copyToClipboard([rectangle], null))!; - pasteWithCtrlCmdV(clipboardJSON); await waitFor(() => { diff --git a/src/tests/contextmenu.test.tsx b/src/tests/contextmenu.test.tsx index 1474c52247..92969faafa 100644 --- a/src/tests/contextmenu.test.tsx +++ b/src/tests/contextmenu.test.tsx @@ -11,7 +11,7 @@ import { waitFor, togglePopover, } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import * as Renderer from "../renderer/renderScene"; import { reseed } from "../random"; import { UI, Pointer, Keyboard } from "./helpers/ui"; @@ -20,7 +20,6 @@ import { ShortcutName } from "../actions/shortcuts"; import { copiedStyles } from "../actions/actionStyles"; import { API } from "./helpers/api"; import { setDateTimeForTests } from "../utils"; -import { LibraryItem } from "../types"; import { vi } from "vitest"; const checkpoint = (name: string) => { @@ -56,7 +55,7 @@ describe("contextMenu element", () => { reseed(7); setDateTimeForTests("201933152653"); - await render(); + await render(); }); beforeAll(() => { @@ -394,11 +393,9 @@ describe("contextMenu element", () => { const contextMenu = UI.queryContextMenu(); fireEvent.click(queryByText(contextMenu!, "Add to library")!); - await waitFor(() => { - const library = localStorage.getItem("excalidraw-library"); - expect(library).not.toBeNull(); - const addedElement = JSON.parse(library!)[0] as LibraryItem; - expect(addedElement.elements[0]).toEqual(h.elements[0]); + await waitFor(async () => { + const libraryItems = await h.app.library.getLatestLibrary(); + expect(libraryItems[0].elements[0]).toEqual(h.elements[0]); }); }); diff --git a/src/tests/dragCreate.test.tsx b/src/tests/dragCreate.test.tsx index 71dc63eb52..6e6c1dbe41 100644 --- a/src/tests/dragCreate.test.tsx +++ b/src/tests/dragCreate.test.tsx @@ -1,5 +1,5 @@ import ReactDOM from "react-dom"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import * as Renderer from "../renderer/renderScene"; import { KEYS } from "../keys"; import { @@ -30,7 +30,7 @@ const { h } = window; describe("Test dragCreate", () => { describe("add element to the scene when pointer dragging long enough", () => { it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("rectangle"); fireEvent.click(tool); @@ -62,7 +62,7 @@ describe("Test dragCreate", () => { }); it("ellipse", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("ellipse"); fireEvent.click(tool); @@ -95,7 +95,7 @@ describe("Test dragCreate", () => { }); it("diamond", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("diamond"); fireEvent.click(tool); @@ -127,7 +127,7 @@ describe("Test dragCreate", () => { }); it("arrow", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("arrow"); fireEvent.click(tool); @@ -163,7 +163,7 @@ describe("Test dragCreate", () => { }); it("line", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("line"); fireEvent.click(tool); @@ -207,7 +207,7 @@ describe("Test dragCreate", () => { }); it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("rectangle"); fireEvent.click(tool); @@ -227,7 +227,7 @@ describe("Test dragCreate", () => { }); it("ellipse", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("ellipse"); fireEvent.click(tool); @@ -247,7 +247,7 @@ describe("Test dragCreate", () => { }); it("diamond", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("diamond"); fireEvent.click(tool); @@ -267,7 +267,9 @@ describe("Test dragCreate", () => { }); it("arrow", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); // select tool const tool = getByToolName("arrow"); fireEvent.click(tool); @@ -292,7 +294,9 @@ describe("Test dragCreate", () => { }); it("line", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); // select tool const tool = getByToolName("line"); fireEvent.click(tool); diff --git a/src/tests/elementLocking.test.tsx b/src/tests/elementLocking.test.tsx index a2f6eb11dc..e3a2fb4455 100644 --- a/src/tests/elementLocking.test.tsx +++ b/src/tests/elementLocking.test.tsx @@ -1,5 +1,5 @@ import ReactDOM from "react-dom"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { render } from "../tests/test-utils"; import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; import { KEYS } from "../keys"; @@ -15,7 +15,7 @@ const h = window.h; describe("element locking", () => { beforeEach(async () => { - await render(); + await render(); h.elements = []; }); diff --git a/src/tests/export.test.tsx b/src/tests/export.test.tsx index da496dea37..5ce16abe5b 100644 --- a/src/tests/export.test.tsx +++ b/src/tests/export.test.tsx @@ -1,5 +1,5 @@ import { render, waitFor } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { encodePngMetadata, @@ -42,7 +42,7 @@ Object.defineProperty(window, "TextDecoder", { describe("export", () => { beforeEach(async () => { - await render(); + await render(); }); it("export embedded png and reimport", async () => { diff --git a/src/tests/fitToContent.test.tsx b/src/tests/fitToContent.test.tsx index 580467ddee..386883b66c 100644 --- a/src/tests/fitToContent.test.tsx +++ b/src/tests/fitToContent.test.tsx @@ -1,14 +1,14 @@ import { render } from "./test-utils"; import { API } from "./helpers/api"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { vi } from "vitest"; const { h } = window; describe("fitToContent", () => { it("should zoom to fit the selected element", async () => { - await render(); + await render(); h.state.width = 10; h.state.height = 10; @@ -30,7 +30,7 @@ describe("fitToContent", () => { }); it("should zoom to fit multiple elements", async () => { - await render(); + await render(); const topLeft = API.createElement({ width: 20, @@ -61,7 +61,7 @@ describe("fitToContent", () => { }); it("should scroll the viewport to the selected element", async () => { - await render(); + await render(); h.state.width = 10; h.state.height = 10; @@ -106,7 +106,7 @@ describe("fitToContent animated", () => { }); it("should ease scroll the viewport to the selected element", async () => { - await render(); + await render(); h.state.width = 10; h.state.height = 10; @@ -142,7 +142,7 @@ describe("fitToContent animated", () => { }); it("should animate the scroll but not the zoom", async () => { - await render(); + await render(); h.state.width = 50; h.state.height = 50; diff --git a/src/tests/flip.test.tsx b/src/tests/flip.test.tsx index 04f3e88b02..b80b25f2d4 100644 --- a/src/tests/flip.test.tsx +++ b/src/tests/flip.test.tsx @@ -19,7 +19,7 @@ import { FileId, } from "../element/types"; import { newLinearElement } from "../element"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { mutateElement } from "../element/mutateElement"; import { NormalizedZoomValue } from "../types"; import { ROUNDNESS } from "../constants"; @@ -52,7 +52,7 @@ beforeEach(async () => { Object.assign(document, { elementFromPoint: () => GlobalTestState.canvas, }); - await render(); + await render(); h.setState({ zoom: { value: 1 as NormalizedZoomValue, diff --git a/src/tests/history.test.tsx b/src/tests/history.test.tsx index 4cf5f8440d..aabfc7a772 100644 --- a/src/tests/history.test.tsx +++ b/src/tests/history.test.tsx @@ -1,5 +1,5 @@ import { assertSelectedElements, render } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { Keyboard, Pointer, UI } from "./helpers/ui"; import { API } from "./helpers/api"; import { getDefaultAppState } from "../appState"; @@ -13,14 +13,16 @@ const mouse = new Pointer("mouse"); describe("history", () => { it("initializing scene should end up with single history entry", async () => { - await render(, { - localStorageData: { - elements: [API.createElement({ type: "rectangle", id: "A" })], - appState: { - zenModeEnabled: true, - }, - }, - }); + await render( + , + ); await waitFor(() => expect(h.state.zenModeEnabled).toBe(true)); await waitFor(() => @@ -60,14 +62,16 @@ describe("history", () => { }); it("scene import via drag&drop should create new history entry", async () => { - await render(, { - localStorageData: { - elements: [API.createElement({ type: "rectangle", id: "A" })], - appState: { - viewBackgroundColor: "#FFF", - }, - }, - }); + await render( + , + ); await waitFor(() => expect(h.state.viewBackgroundColor).toBe("#FFF")); await waitFor(() => @@ -113,7 +117,7 @@ describe("history", () => { }); it("undo/redo works properly with groups", async () => { - await render(); + await render(); const rect1 = API.createElement({ type: "rectangle", groupIds: ["A"] }); const rect2 = API.createElement({ type: "rectangle", groupIds: ["A"] }); diff --git a/src/tests/library.test.tsx b/src/tests/library.test.tsx index 512d1b5642..a87c87f7bd 100644 --- a/src/tests/library.test.tsx +++ b/src/tests/library.test.tsx @@ -2,7 +2,7 @@ import { vi } from "vitest"; import { fireEvent, render, waitFor } from "./test-utils"; import { queryByTestId } from "@testing-library/react"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { MIME_TYPES } from "../constants"; import { LibraryItem, LibraryItems } from "../types"; @@ -42,7 +42,7 @@ vi.mock("../data/filesystem.ts", async (importOriginal) => { describe("library", () => { beforeEach(async () => { - await render(); + await render(); h.app.library.resetLibrary(); }); @@ -189,7 +189,7 @@ describe("library", () => { describe("library menu", () => { it("should load library from file picker", async () => { - const { container } = await render(); + const { container } = await render(); const latestLibrary = await h.app.library.getLatestLibrary(); expect(latestLibrary.length).toBe(0); diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index 7c14b6ef71..93ee8758f5 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -5,7 +5,7 @@ import { ExcalidrawTextElementWithContainer, FontString, } from "../element/types"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { centerPoint } from "../math"; import { reseed } from "../random"; import * as Renderer from "../renderer/renderScene"; @@ -43,7 +43,7 @@ describe("Test Linear Elements", () => { renderInteractiveScene.mockClear(); renderStaticScene.mockClear(); reseed(7); - const comp = await render(); + const comp = await render(); h.state.width = 1000; h.state.height = 1000; container = comp.container; diff --git a/src/tests/move.test.tsx b/src/tests/move.test.tsx index 4fe7ab0a6a..5435d7927b 100644 --- a/src/tests/move.test.tsx +++ b/src/tests/move.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { render, fireEvent } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import * as Renderer from "../renderer/renderScene"; import { reseed } from "../random"; import { bindOrUnbindLinearElement } from "../element/binding"; @@ -31,7 +31,7 @@ const { h } = window; describe("move element", () => { it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); const canvas = container.querySelector("canvas.interactive")!; { @@ -67,7 +67,7 @@ describe("move element", () => { }); it("rectangles with binding arrow", async () => { - await render(); + await render(); // create elements const rectA = UI.createElement("rectangle", { size: 100 }); @@ -119,7 +119,7 @@ describe("move element", () => { describe("duplicate element on move when ALT is clicked", () => { it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); const canvas = container.querySelector("canvas.interactive")!; { diff --git a/src/tests/multiPointCreate.test.tsx b/src/tests/multiPointCreate.test.tsx index d26207eea5..e198b25889 100644 --- a/src/tests/multiPointCreate.test.tsx +++ b/src/tests/multiPointCreate.test.tsx @@ -5,7 +5,7 @@ import { mockBoundingClientRect, restoreOriginalGetBoundingClientRect, } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import * as Renderer from "../renderer/renderScene"; import { KEYS } from "../keys"; import { ExcalidrawLinearElement } from "../element/types"; @@ -29,7 +29,7 @@ const { h } = window; describe("remove shape in non linear elements", () => { beforeAll(() => { - mockBoundingClientRect(); + mockBoundingClientRect({ width: 1000, height: 1000 }); }); afterAll(() => { @@ -37,12 +37,13 @@ describe("remove shape in non linear elements", () => { }); it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("rectangle"); fireEvent.click(tool); const canvas = container.querySelector("canvas.interactive")!; + fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 }); fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 }); @@ -52,7 +53,7 @@ describe("remove shape in non linear elements", () => { }); it("ellipse", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("ellipse"); fireEvent.click(tool); @@ -67,7 +68,7 @@ describe("remove shape in non linear elements", () => { }); it("diamond", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("diamond"); fireEvent.click(tool); @@ -84,7 +85,7 @@ describe("remove shape in non linear elements", () => { describe("multi point mode in linear elements", () => { it("arrow", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("arrow"); fireEvent.click(tool); @@ -109,8 +110,8 @@ describe("multi point mode in linear elements", () => { key: KEYS.ENTER, }); - expect(renderInteractiveScene).toHaveBeenCalledTimes(10); - expect(renderStaticScene).toHaveBeenCalledTimes(11); + expect(renderInteractiveScene).toHaveBeenCalledTimes(9); + expect(renderStaticScene).toHaveBeenCalledTimes(10); expect(h.elements.length).toEqual(1); const element = h.elements[0] as ExcalidrawLinearElement; @@ -128,7 +129,7 @@ describe("multi point mode in linear elements", () => { }); it("line", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("line"); fireEvent.click(tool); @@ -153,8 +154,8 @@ describe("multi point mode in linear elements", () => { key: KEYS.ENTER, }); - expect(renderInteractiveScene).toHaveBeenCalledTimes(10); - expect(renderStaticScene).toHaveBeenCalledTimes(11); + expect(renderInteractiveScene).toHaveBeenCalledTimes(9); + expect(renderStaticScene).toHaveBeenCalledTimes(10); expect(h.elements.length).toEqual(1); const element = h.elements[0] as ExcalidrawLinearElement; diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx index 47fa9208b2..8389dd42e1 100644 --- a/src/tests/regressionTests.test.tsx +++ b/src/tests/regressionTests.test.tsx @@ -1,7 +1,7 @@ import ReactDOM from "react-dom"; import { ExcalidrawElement } from "../element/types"; import { CODES, KEYS } from "../keys"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { reseed } from "../random"; import * as Renderer from "../renderer/renderScene"; import { setDateTimeForTests } from "../utils"; @@ -13,9 +13,7 @@ import { render, screen, togglePopover, - waitFor, } from "./test-utils"; -import { defaultLang } from "../i18n"; import { FONT_FAMILY } from "../constants"; import { vi } from "vitest"; @@ -56,7 +54,7 @@ beforeEach(async () => { finger1.reset(); finger2.reset(); - await render(); + await render(); h.setState({ height: 768, width: 1024 }); }); @@ -443,26 +441,6 @@ describe("regression tests", () => { expect(h.state.zoom.value).toBe(1); }); - it("rerenders UI on language change", async () => { - // select rectangle tool to show properties menu - UI.clickTool("rectangle"); - // english lang should display `thin` label - expect(screen.queryByTitle(/thin/i)).not.toBeNull(); - fireEvent.click(document.querySelector(".dropdown-menu-button")!); - - fireEvent.change(document.querySelector(".dropdown-select__language")!, { - target: { value: "de-DE" }, - }); - // switching to german, `thin` label should no longer exist - await waitFor(() => expect(screen.queryByTitle(/thin/i)).toBeNull()); - // reset language - fireEvent.change(document.querySelector(".dropdown-select__language")!, { - target: { value: defaultLang.code }, - }); - // switching back to English - await waitFor(() => expect(screen.queryByTitle(/thin/i)).not.toBeNull()); - }); - it("make a group and duplicate it", () => { UI.clickTool("rectangle"); mouse.down(10, 10); diff --git a/src/tests/resize.test.tsx b/src/tests/resize.test.tsx index fdecf8fb6c..60ce3765df 100644 --- a/src/tests/resize.test.tsx +++ b/src/tests/resize.test.tsx @@ -6,7 +6,7 @@ import { reseed } from "../random"; import { UI, Keyboard } from "./helpers/ui"; import { resize } from "./utils"; import { ExcalidrawTextElement } from "../element/types"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { KEYS } from "../keys"; import { vi } from "vitest"; @@ -126,7 +126,7 @@ describe("resize rectangle ellipses and diamond elements", () => { describe("Test text element", () => { it("should update font size via keyboard", async () => { - await render(); + await render(); const textElement = API.createElement({ type: "text", diff --git a/src/tests/scroll.test.tsx b/src/tests/scroll.test.tsx index e8aa074e0f..f2e812ca8b 100644 --- a/src/tests/scroll.test.tsx +++ b/src/tests/scroll.test.tsx @@ -8,7 +8,6 @@ import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { Keyboard } from "./helpers/ui"; import { KEYS } from "../keys"; -import ExcalidrawApp from "../excalidraw-app"; const { h } = window; @@ -56,7 +55,7 @@ describe("appState", () => { it("moving by page up/down/left/right", async () => { mockBoundingClientRect(); - await render(, {}); + await render(, {}); const scrollTest = () => { const initialScrollY = h.state.scrollY; diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx index acae9dd8cd..f6c83fc7a1 100644 --- a/src/tests/selection.test.tsx +++ b/src/tests/selection.test.tsx @@ -6,7 +6,7 @@ import { restoreOriginalGetBoundingClientRect, assertSelectedElements, } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import * as Renderer from "../renderer/renderScene"; import { KEYS } from "../keys"; import { reseed } from "../random"; @@ -34,7 +34,7 @@ const mouse = new Pointer("mouse"); describe("box-selection", () => { beforeEach(async () => { - await render(); + await render(); }); it("should allow adding to selection via box-select when holding shift", async () => { @@ -102,7 +102,7 @@ describe("box-selection", () => { describe("inner box-selection", () => { beforeEach(async () => { - await render(); + await render(); }); it("selecting elements visually nested inside another", async () => { const rect1 = API.createElement({ @@ -218,7 +218,7 @@ describe("inner box-selection", () => { describe("selection element", () => { it("create selection element on pointer down", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("selection"); fireEvent.click(tool); @@ -239,7 +239,7 @@ describe("selection element", () => { }); it("resize selection element on pointer move", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("selection"); fireEvent.click(tool); @@ -261,7 +261,7 @@ describe("selection element", () => { }); it("remove selection element on pointer up", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render(); // select tool const tool = getByToolName("selection"); fireEvent.click(tool); @@ -287,7 +287,9 @@ describe("select single element on the scene", () => { }); it("rectangle", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); const canvas = container.querySelector("canvas.interactive")!; { // create element @@ -317,7 +319,9 @@ describe("select single element on the scene", () => { }); it("diamond", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); const canvas = container.querySelector("canvas.interactive")!; { // create element @@ -347,7 +351,9 @@ describe("select single element on the scene", () => { }); it("ellipse", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); const canvas = container.querySelector("canvas.interactive")!; { // create element @@ -377,7 +383,9 @@ describe("select single element on the scene", () => { }); it("arrow", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); const canvas = container.querySelector("canvas.interactive")!; { // create element @@ -419,7 +427,9 @@ describe("select single element on the scene", () => { }); it("arrow escape", async () => { - const { getByToolName, container } = await render(); + const { getByToolName, container } = await render( + , + ); const canvas = container.querySelector("canvas.interactive")!; { // create element @@ -464,7 +474,7 @@ describe("select single element on the scene", () => { describe("tool locking & selection", () => { it("should not select newly created element while tool is locked", async () => { - await render(); + await render(); UI.clickTool("lock"); expect(h.state.activeTool.locked).toBe(true); @@ -480,7 +490,7 @@ describe("tool locking & selection", () => { describe("selectedElementIds stability", () => { beforeEach(async () => { - await render(); + await render(); }); it("box-selection should be stable when not changing selection", () => { diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts index 4a8a9f1f11..fed6b4897d 100644 --- a/src/tests/test-utils.ts +++ b/src/tests/test-utils.ts @@ -11,7 +11,7 @@ import { import * as toolQueries from "./queries/toolQueries"; import { ImportedDataState } from "../data/types"; -import { STORAGE_KEYS } from "../excalidraw-app/app_constants"; +import { STORAGE_KEYS } from "../../excalidraw-app/app_constants"; import { SceneData } from "../types"; import { getSelectedElements } from "../scene/selection"; diff --git a/src/tests/viewMode.test.tsx b/src/tests/viewMode.test.tsx index bc29f81c3e..1ba0c2caf9 100644 --- a/src/tests/viewMode.test.tsx +++ b/src/tests/viewMode.test.tsx @@ -1,5 +1,5 @@ import { render, GlobalTestState } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { KEYS } from "../keys"; import { Keyboard, Pointer, UI } from "./helpers/ui"; import { CURSOR_TYPE } from "../constants"; @@ -12,7 +12,7 @@ const pointerTypes = [mouse, touch, pen]; describe("view mode", () => { beforeEach(async () => { - await render(); + await render(); }); it("after switching to view mode – cursor type should be pointer", async () => { diff --git a/src/tests/zindex.test.tsx b/src/tests/zindex.test.tsx index 2047c032e2..543b2931b1 100644 --- a/src/tests/zindex.test.tsx +++ b/src/tests/zindex.test.tsx @@ -1,6 +1,6 @@ import ReactDOM from "react-dom"; import { render } from "./test-utils"; -import ExcalidrawApp from "../excalidraw-app"; +import { Excalidraw } from "../packages/excalidraw/index"; import { reseed } from "../random"; import { actionSendBackward, @@ -121,7 +121,6 @@ const assertZindex = ({ operations: [Actions, string[]][]; }) => { const selectedElementIds = populateElements(elements, appState); - operations.forEach(([action, expected]) => { h.app.actionManager.executeAction(action); expect(h.elements.map((element) => element.id)).toEqual(expected); @@ -131,7 +130,7 @@ const assertZindex = ({ describe("z-index manipulation", () => { beforeEach(async () => { - await render(); + await render(); }); it("send back", () => { diff --git a/src/zindex.ts b/src/zindex.ts index a1f051c54f..ed9c7a2e98 100644 --- a/src/zindex.ts +++ b/src/zindex.ts @@ -96,9 +96,9 @@ const getTargetIndexAccountingForBinding = ( if (direction === "left") { return elements.indexOf(nextElement); } + const boundTextElement = Scene.getScene(nextElement)!.getElement(boundElementId); - if (boundTextElement) { return elements.indexOf(boundTextElement); } diff --git a/tsconfig.json b/tsconfig.json index d75d3656aa..28c79d8b0e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,6 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": ["src"], + "include": ["src", "excalidraw-app"], "exclude": ["src/packages/excalidraw/types"] } From f8b3692262e81a33cf16b727ea770b1a5ae0dd38 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Thu, 21 Sep 2023 06:24:03 +0200 Subject: [PATCH 002/144] fix: more eye-droper fixes (#7019) --- src/components/App.tsx | 13 ++-- src/components/ColorPicker/ColorInput.tsx | 11 ++- src/components/ColorPicker/ColorPicker.tsx | 17 ++--- src/components/EyeDropper.tsx | 86 +++++++++++++--------- src/components/LayerUI.tsx | 40 +++++++++- src/hooks/useStable.ts | 7 ++ 6 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 src/hooks/useStable.ts diff --git a/src/components/App.tsx b/src/components/App.tsx index 367c8f7f67..4dff0025eb 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1330,7 +1330,8 @@ class App extends React.Component { private openEyeDropper = ({ type }: { type: "stroke" | "background" }) => { jotaiStore.set(activeEyeDropperAtom, { swapPreviewOnAlt: true, - previewType: type === "stroke" ? "strokeColor" : "backgroundColor", + colorPickerType: + type === "stroke" ? "elementStroke" : "elementBackground", onSelect: (color, event) => { const shouldUpdateStrokeColor = (type === "background" && event.altKey) || @@ -1341,12 +1342,14 @@ class App extends React.Component { this.state.activeTool.type !== "selection" ) { if (shouldUpdateStrokeColor) { - this.setState({ - currentItemStrokeColor: color, + this.syncActionResult({ + appState: { ...this.state, currentItemStrokeColor: color }, + commitToHistory: true, }); } else { - this.setState({ - currentItemBackgroundColor: color, + this.syncActionResult({ + appState: { ...this.state, currentItemBackgroundColor: color }, + commitToHistory: true, }); } } else { diff --git a/src/components/ColorPicker/ColorInput.tsx b/src/components/ColorPicker/ColorInput.tsx index 968729cc70..f10a174d1f 100644 --- a/src/components/ColorPicker/ColorInput.tsx +++ b/src/components/ColorPicker/ColorInput.tsx @@ -1,7 +1,10 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { getColor } from "./ColorPicker"; import { useAtom } from "jotai"; -import { activeColorPickerSectionAtom } from "./colorPickerUtils"; +import { + ColorPickerType, + activeColorPickerSectionAtom, +} from "./colorPickerUtils"; import { eyeDropperIcon } from "../icons"; import { jotaiScope } from "../../jotai"; import { KEYS } from "../../keys"; @@ -15,14 +18,14 @@ interface ColorInputProps { color: string; onChange: (color: string) => void; label: string; - eyeDropperType: "strokeColor" | "backgroundColor"; + colorPickerType: ColorPickerType; } export const ColorInput = ({ color, onChange, label, - eyeDropperType, + colorPickerType, }: ColorInputProps) => { const device = useDevice(); const [innerValue, setInnerValue] = useState(color); @@ -116,7 +119,7 @@ export const ColorInput = ({ : { keepOpenOnAlt: false, onSelect: (color) => onChange(color), - previewType: eyeDropperType, + colorPickerType, }, ) } diff --git a/src/components/ColorPicker/ColorPicker.tsx b/src/components/ColorPicker/ColorPicker.tsx index 4a378950cc..ff0b6f416b 100644 --- a/src/components/ColorPicker/ColorPicker.tsx +++ b/src/components/ColorPicker/ColorPicker.tsx @@ -82,14 +82,7 @@ const ColorPickerPopupContent = ({ const { container } = useExcalidrawContainer(); const { isMobile, isLandscape } = useDevice(); - const eyeDropperType = - type === "canvasBackground" - ? undefined - : type === "elementBackground" - ? "backgroundColor" - : "strokeColor"; - - const colorInputJSX = eyeDropperType && ( + const colorInputJSX = (
{t("colorPicker.hexCode")} { onChange(color); }} - eyeDropperType={eyeDropperType} + colorPickerType={type} />
); @@ -160,7 +153,7 @@ const ColorPickerPopupContent = ({ "0px 7px 14px rgba(0, 0, 0, 0.05), 0px 0px 3.12708px rgba(0, 0, 0, 0.0798), 0px 0px 0.931014px rgba(0, 0, 0, 0.1702)", }} > - {palette && eyeDropperType ? ( + {palette ? ( void; - previewType: "strokeColor" | "backgroundColor"; + /** + * property of selected elements to update live when alt-dragging. + * Supply `null` if not applicable (e.g. updating the canvas bg instead of + * elements) + **/ + colorPickerType: ColorPickerType; }; export const activeEyeDropperAtom = atom(null); export const EyeDropper: React.FC<{ onCancel: () => void; - onSelect: Required["onSelect"]; - swapPreviewOnAlt?: EyeDropperProperties["swapPreviewOnAlt"]; - previewType: EyeDropperProperties["previewType"]; -}> = ({ onCancel, onSelect, swapPreviewOnAlt, previewType }) => { + onSelect: EyeDropperProperties["onSelect"]; + /** called when color changes, on pointerdown for preview */ + onChange: ( + type: ColorPickerType, + color: string, + selectedElements: ExcalidrawElement[], + event: { altKey: boolean }, + ) => void; + colorPickerType: EyeDropperProperties["colorPickerType"]; +}> = ({ onCancel, onChange, onSelect, colorPickerType }) => { const eyeDropperContainer = useCreatePortalContainer({ className: "excalidraw-eye-dropper-backdrop", parentSelector: ".excalidraw-eye-dropper-container", @@ -40,9 +52,13 @@ export const EyeDropper: React.FC<{ const selectedElements = getSelectedElements(elements, appState); - const metaStuffRef = useRef({ selectedElements, app }); - metaStuffRef.current.selectedElements = selectedElements; - metaStuffRef.current.app = app; + const stableProps = useStable({ + app, + onCancel, + onChange, + onSelect, + selectedElements, + }); const { container: excalidrawContainer } = useExcalidrawContainer(); @@ -90,28 +106,28 @@ export const EyeDropper: React.FC<{ const currentColor = getCurrentColor({ clientX, clientY }); if (isHoldingPointerDown) { - for (const element of metaStuffRef.current.selectedElements) { - mutateElement( - element, - { - [altKey && swapPreviewOnAlt - ? previewType === "strokeColor" - ? "backgroundColor" - : "strokeColor" - : previewType]: currentColor, - }, - false, - ); - ShapeCache.delete(element); - } - Scene.getScene( - metaStuffRef.current.selectedElements[0], - )?.informMutation(); + stableProps.onChange( + colorPickerType, + currentColor, + stableProps.selectedElements, + { altKey }, + ); } colorPreviewDiv.style.background = currentColor; }; + const onCancel = () => { + stableProps.onCancel(); + }; + + const onSelect: Required["onSelect"] = ( + color, + event, + ) => { + stableProps.onSelect(color, event); + }; + const pointerDownListener = (event: PointerEvent) => { isHoldingPointerDown = true; // NOTE we can't event.preventDefault() as that would stop @@ -148,8 +164,8 @@ export const EyeDropper: React.FC<{ // init color preview else it would show only after the first mouse move mouseMoveListener({ - clientX: metaStuffRef.current.app.lastViewportPosition.x, - clientY: metaStuffRef.current.app.lastViewportPosition.y, + clientX: stableProps.app.lastViewportPosition.x, + clientY: stableProps.app.lastViewportPosition.y, altKey: false, }); @@ -179,12 +195,10 @@ export const EyeDropper: React.FC<{ window.removeEventListener(EVENT.BLUR, onCancel); }; }, [ + stableProps, app.canvas, eyeDropperContainer, - onCancel, - onSelect, - swapPreviewOnAlt, - previewType, + colorPickerType, excalidrawContainer, appState.offsetLeft, appState.offsetTop, diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 02f360e255..3bc8436cc1 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -52,6 +52,9 @@ import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper"; import "./LayerUI.scss"; import "./Toolbar.scss"; +import { mutateElement } from "../element/mutateElement"; +import { ShapeCache } from "../scene/ShapeCache"; +import Scene from "../scene/Scene"; interface LayerUIProps { actionManager: ActionManager; @@ -368,11 +371,44 @@ const LayerUI = ({ )} {eyeDropperState && !device.isMobile && ( { setEyeDropperState(null); }} + onChange={(colorPickerType, color, selectedElements, { altKey }) => { + if ( + colorPickerType !== "elementBackground" && + colorPickerType !== "elementStroke" + ) { + return; + } + + if (selectedElements.length) { + for (const element of selectedElements) { + mutateElement( + element, + { + [altKey && eyeDropperState.swapPreviewOnAlt + ? colorPickerType === "elementBackground" + ? "strokeColor" + : "backgroundColor" + : colorPickerType === "elementBackground" + ? "backgroundColor" + : "strokeColor"]: color, + }, + false, + ); + ShapeCache.delete(element); + } + Scene.getScene(selectedElements[0])?.informMutation(); + } else if (colorPickerType === "elementBackground") { + setAppState({ + currentItemBackgroundColor: color, + }); + } else { + setAppState({ currentItemStrokeColor: color }); + } + }} onSelect={(color, event) => { setEyeDropperState((state) => { return state?.keepOpenOnAlt && event.altKey ? state : null; diff --git a/src/hooks/useStable.ts b/src/hooks/useStable.ts new file mode 100644 index 0000000000..56608489db --- /dev/null +++ b/src/hooks/useStable.ts @@ -0,0 +1,7 @@ +import { useRef } from "react"; + +export const useStable = >(value: T) => { + const ref = useRef(value); + Object.assign(ref.current, value); + return ref.current; +}; From 4db73a7f955d757b9a46223a08328416e35c51bb Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 21 Sep 2023 10:28:21 +0530 Subject: [PATCH 003/144] =?UTF-8?q?docs:=20release=20@excalidraw/excalidra?= =?UTF-8?q?w@0.16.1=20=20=F0=9F=8E=89=20(#7020)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 16 ++++++++++++++++ src/packages/excalidraw/package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 6b87f83359..c3e8be9a02 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,22 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## 0.16.1 (2023-09-21) + +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +### Fixes + +- More eye-droper fixes [#7019](https://github.com/excalidraw/excalidraw/pull/7019) + +### Refactor + +- Move excalidraw-app outside src [#6987](https://github.com/excalidraw/excalidraw/pull/6987) + +--- + ## 0.16.0 (2023-09-19) - Support creating containers, linear elements, text containers, labelled arrows and arrow bindings programatically [#6546](https://github.com/excalidraw/excalidraw/pull/6546) diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index 28a39088b3..2e52e156f0 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.16.0", + "version": "0.16.1", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From 556175558a7f62b42e992f9b3b07f3b553167203 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 24 Sep 2023 19:07:35 +0200 Subject: [PATCH 004/144] fix: polyfill `Element.replaceChildren` (#7034) --- src/polyfill.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/polyfill.ts b/src/polyfill.ts index cc4891b4de..b8b080f754 100644 --- a/src/polyfill.ts +++ b/src/polyfill.ts @@ -22,5 +22,12 @@ const polyfill = () => { configurable: true, }); } + + if (!Element.prototype.replaceChildren) { + Element.prototype.replaceChildren = function (...nodes) { + this.innerHTML = ""; + this.append(...nodes); + }; + } }; export default polyfill; From 4765f5536e8913407bf44858ae56f208d39e530b Mon Sep 17 00:00:00 2001 From: David Luzar Date: Mon, 25 Sep 2023 16:54:23 +0200 Subject: [PATCH 005/144] fix: frame name not editable on dbl-click (#7037) --- src/components/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 4dff0025eb..7736c34971 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1086,7 +1086,7 @@ class App extends React.Component { cursor: CURSOR_TYPE.MOVE, pointerEvents: this.state.viewModeEnabled ? POINTER_EVENTS.disabled - : POINTER_EVENTS.inheritFromUI, + : POINTER_EVENTS.enabled, }} onPointerDown={(event) => this.handleCanvasPointerDown(event)} onWheel={(event) => this.handleWheel(event)} From 4c35eba72d13e8fab748d14c243c7c351c27357f Mon Sep 17 00:00:00 2001 From: hugofqt <12221094+hugo082@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:28:08 +0200 Subject: [PATCH 006/144] feat: element alignments - snapping (#6256) Co-authored-by: Ryan Di Co-authored-by: dwelle --- src/actions/actionToggleGridMode.tsx | 1 + src/actions/actionToggleObjectsSnapMode.tsx | 28 + src/actions/index.ts | 1 + src/actions/shortcuts.ts | 2 + src/actions/types.ts | 1 + src/appState.ts | 9 + src/components/App.tsx | 255 ++- src/components/HelpDialog.tsx | 4 + src/components/canvases/InteractiveCanvas.tsx | 2 + src/element/bounds.ts | 15 +- src/element/dragElements.ts | 76 +- src/element/resizeElements.ts | 40 +- src/element/textWysiwyg.test.tsx | 15 +- src/keys.ts | 1 + src/locales/en.json | 1 + src/math.test.ts | 42 +- src/math.ts | 33 + src/renderer/renderScene.ts | 3 + src/renderer/renderSnaps.ts | 189 +++ src/scene/selection.ts | 21 + src/snapping.ts | 1361 +++++++++++++++++ .../__snapshots__/contextmenu.test.tsx.snap | 73 + .../regressionTests.test.tsx.snap | 168 ++ src/tests/contextmenu.test.tsx | 1 + src/tests/linearElementEditor.test.tsx | 8 +- src/tests/move.test.tsx | 2 +- src/tests/multiPointCreate.test.tsx | 5 +- .../packages/__snapshots__/utils.test.ts.snap | 6 + src/types.ts | 19 + 29 files changed, 2295 insertions(+), 87 deletions(-) create mode 100644 src/actions/actionToggleObjectsSnapMode.tsx create mode 100644 src/renderer/renderSnaps.ts create mode 100644 src/snapping.ts diff --git a/src/actions/actionToggleGridMode.tsx b/src/actions/actionToggleGridMode.tsx index 8841cad5a8..e4f930bff1 100644 --- a/src/actions/actionToggleGridMode.tsx +++ b/src/actions/actionToggleGridMode.tsx @@ -15,6 +15,7 @@ export const actionToggleGridMode = register({ appState: { ...appState, gridSize: this.checked!(appState) ? null : GRID_SIZE, + objectsSnapModeEnabled: false, }, commitToHistory: false, }; diff --git a/src/actions/actionToggleObjectsSnapMode.tsx b/src/actions/actionToggleObjectsSnapMode.tsx new file mode 100644 index 0000000000..60986137b2 --- /dev/null +++ b/src/actions/actionToggleObjectsSnapMode.tsx @@ -0,0 +1,28 @@ +import { CODES, KEYS } from "../keys"; +import { register } from "./register"; + +export const actionToggleObjectsSnapMode = register({ + name: "objectsSnapMode", + viewMode: true, + trackEvent: { + category: "canvas", + predicate: (appState) => !appState.objectsSnapModeEnabled, + }, + perform(elements, appState) { + return { + appState: { + ...appState, + objectsSnapModeEnabled: !this.checked!(appState), + gridSize: null, + }, + commitToHistory: false, + }; + }, + checked: (appState) => appState.objectsSnapModeEnabled, + predicate: (elements, appState, appProps) => { + return typeof appProps.objectsSnapModeEnabled === "undefined"; + }, + contextItemLabel: "buttons.objectsSnapMode", + keyTest: (event) => + !event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.S, +}); diff --git a/src/actions/index.ts b/src/actions/index.ts index 9b53f81731..1e72aa48ab 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -80,6 +80,7 @@ export { export { actionToggleGridMode } from "./actionToggleGridMode"; export { actionToggleZenMode } from "./actionToggleZenMode"; +export { actionToggleObjectsSnapMode } from "./actionToggleObjectsSnapMode"; export { actionToggleStats } from "./actionToggleStats"; export { actionUnbindText, actionBindText } from "./actionBoundText"; diff --git a/src/actions/shortcuts.ts b/src/actions/shortcuts.ts index b9c24a7572..20ab9f7b44 100644 --- a/src/actions/shortcuts.ts +++ b/src/actions/shortcuts.ts @@ -28,6 +28,7 @@ export type ShortcutName = | "ungroup" | "gridMode" | "zenMode" + | "objectsSnapMode" | "stats" | "addToLibrary" | "viewMode" @@ -74,6 +75,7 @@ const shortcutMap: Record = { ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")], gridMode: [getShortcutKey("CtrlOrCmd+'")], zenMode: [getShortcutKey("Alt+Z")], + objectsSnapMode: [getShortcutKey("Alt+S")], stats: [getShortcutKey("Alt+/")], addToLibrary: [], flipHorizontal: [getShortcutKey("Shift+H")], diff --git a/src/actions/types.ts b/src/actions/types.ts index 9dea7974ac..c74e19552c 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -51,6 +51,7 @@ export type ActionName = | "pasteStyles" | "gridMode" | "zenMode" + | "objectsSnapMode" | "stats" | "changeStrokeColor" | "changeBackgroundColor" diff --git a/src/appState.ts b/src/appState.ts index c0c23a9c28..0089f57e98 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -99,6 +99,12 @@ export const getDefaultAppState = (): Omit< pendingImageElementId: null, showHyperlinkPopup: false, selectedLinearElement: null, + snapLines: [], + originSnapOffset: { + x: 0, + y: 0, + }, + objectsSnapModeEnabled: false, }; }; @@ -206,6 +212,9 @@ const APP_STATE_STORAGE_CONF = (< pendingImageElementId: { browser: false, export: false, server: false }, showHyperlinkPopup: { browser: false, export: false, server: false }, selectedLinearElement: { browser: true, export: false, server: false }, + snapLines: { browser: false, export: false, server: false }, + originSnapOffset: { browser: false, export: false, server: false }, + objectsSnapModeEnabled: { browser: true, export: false, server: false }, }); const _clearAppStateForStorage = < diff --git a/src/components/App.tsx b/src/components/App.tsx index 7736c34971..3fb4e5e739 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -35,6 +35,7 @@ import { actionLink, actionToggleElementLock, actionToggleLinearEditor, + actionToggleObjectsSnapMode, } from "../actions"; import { createRedoAction, createUndoAction } from "../actions/actionHistory"; import { ActionManager } from "../actions/manager"; @@ -228,6 +229,7 @@ import { FrameNameBoundsCache, SidebarName, SidebarTabName, + KeyboardModifiersObject, } from "../types"; import { debounce, @@ -342,6 +344,17 @@ import { import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas"; import { jotaiStore } from "../jotai"; import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; +import { + getSnapLinesAtPointer, + snapDraggedElements, + isActiveToolNonLinearSnappable, + snapNewElement, + snapResizingElements, + isSnappingEnabled, + getVisibleGaps, + getReferenceSnapPoints, + SnapCache, +} from "../snapping"; import { actionWrapTextInContainer } from "../actions/actionBoundText"; import BraveMeasureTextError from "./BraveMeasureTextError"; import { activeEyeDropperAtom } from "./EyeDropper"; @@ -490,6 +503,7 @@ class App extends React.Component { viewModeEnabled = false, zenModeEnabled = false, gridModeEnabled = false, + objectsSnapModeEnabled = false, theme = defaultAppState.theme, name = defaultAppState.name, } = props; @@ -500,6 +514,7 @@ class App extends React.Component { ...this.getCanvasOffsets(), viewModeEnabled, zenModeEnabled, + objectsSnapModeEnabled, gridSize: gridModeEnabled ? GRID_SIZE : null, name, width: window.innerWidth, @@ -1722,6 +1737,7 @@ class App extends React.Component { this.scene.destroy(); this.library.destroy(); ShapeCache.destroy(); + SnapCache.destroy(); clearTimeout(touchTimeout); isSomeElementSelected.clearCache(); selectGroupsForSelectedElements.clearCache(); @@ -3120,15 +3136,21 @@ class App extends React.Component { this.onImageAction(); } if (nextActiveTool.type !== "selection") { - this.setState({ + this.setState((prevState) => ({ activeTool: nextActiveTool, selectedElementIds: makeNextSelectedElementIds({}, this.state), selectedGroupIds: {}, editingGroupId: null, + snapLines: [], + originSnapOffset: null, + })); + } else { + this.setState({ + activeTool: nextActiveTool, + snapLines: [], + originSnapOffset: null, activeEmbeddable: null, }); - } else { - this.setState({ activeTool: nextActiveTool, activeEmbeddable: null }); } }; @@ -3865,6 +3887,30 @@ class App extends React.Component { const scenePointer = viewportCoordsToSceneCoords(event, this.state); const { x: scenePointerX, y: scenePointerY } = scenePointer; + if ( + !this.state.draggingElement && + isActiveToolNonLinearSnappable(this.state.activeTool.type) + ) { + const { originOffset, snapLines } = getSnapLinesAtPointer( + this.scene.getNonDeletedElements(), + this.state, + { + x: scenePointerX, + y: scenePointerY, + }, + event, + ); + + this.setState({ + snapLines, + originSnapOffset: originOffset, + }); + } else if (!this.state.draggingElement) { + this.setState({ + snapLines: [], + }); + } + if ( this.state.editingLinearElement && !this.state.editingLinearElement.isDragging @@ -4335,6 +4381,10 @@ class App extends React.Component { this.setState({ contextMenu: null }); } + if (this.state.snapLines) { + this.setAppState({ snapLines: [] }); + } + this.updateGestureOnPointerDown(event); // if dragging element is freedraw and another pointerdown event occurs @@ -5616,6 +5666,52 @@ class App extends React.Component { }); }; + private maybeCacheReferenceSnapPoints( + event: KeyboardModifiersObject, + selectedElements: ExcalidrawElement[], + recomputeAnyways: boolean = false, + ) { + if ( + isSnappingEnabled({ + event, + appState: this.state, + selectedElements, + }) && + (recomputeAnyways || !SnapCache.getReferenceSnapPoints()) + ) { + SnapCache.setReferenceSnapPoints( + getReferenceSnapPoints( + this.scene.getNonDeletedElements(), + selectedElements, + this.state, + ), + ); + } + } + + private maybeCacheVisibleGaps( + event: KeyboardModifiersObject, + selectedElements: ExcalidrawElement[], + recomputeAnyways: boolean = false, + ) { + if ( + isSnappingEnabled({ + event, + appState: this.state, + selectedElements, + }) && + (recomputeAnyways || !SnapCache.getVisibleGaps()) + ) { + SnapCache.setVisibleGaps( + getVisibleGaps( + this.scene.getNonDeletedElements(), + selectedElements, + this.state, + ), + ); + } + } + private onKeyDownFromPointerDownHandler( pointerDownState: PointerDownState, ): (event: KeyboardEvent) => void { @@ -5845,33 +5941,62 @@ class App extends React.Component { !this.state.editingElement && this.state.activeEmbeddable?.state !== "active" ) { - const [dragX, dragY] = getGridPoint( - pointerCoords.x - pointerDownState.drag.offset.x, - pointerCoords.y - pointerDownState.drag.offset.y, - event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, - ); + const dragOffset = { + x: pointerCoords.x - pointerDownState.origin.x, + y: pointerCoords.y - pointerDownState.origin.y, + }; - const [dragDistanceX, dragDistanceY] = [ - Math.abs(pointerCoords.x - pointerDownState.origin.x), - Math.abs(pointerCoords.y - pointerDownState.origin.y), + const originalElements = [ + ...pointerDownState.originalElements.values(), ]; // We only drag in one direction if shift is pressed const lockDirection = event.shiftKey; + + if (lockDirection) { + const distanceX = Math.abs(dragOffset.x); + const distanceY = Math.abs(dragOffset.y); + + const lockX = lockDirection && distanceX < distanceY; + const lockY = lockDirection && distanceX > distanceY; + + if (lockX) { + dragOffset.x = 0; + } + + if (lockY) { + dragOffset.y = 0; + } + } + + // Snap cache *must* be synchronously popuplated before initial drag, + // otherwise the first drag even will not snap, causing a jump before + // it snaps to its position if previously snapped already. + this.maybeCacheVisibleGaps(event, selectedElements); + this.maybeCacheReferenceSnapPoints(event, selectedElements); + + const { snapOffset, snapLines } = snapDraggedElements( + getSelectedElements(originalElements, this.state), + dragOffset, + this.state, + event, + ); + + this.setState({ snapLines }); + // when we're editing the name of a frame, we want the user to be // able to select and interact with the text input !this.state.editingFrame && dragSelectedElements( pointerDownState, selectedElements, - dragX, - dragY, - lockDirection, - dragDistanceX, - dragDistanceY, + dragOffset, this.state, this.scene, + snapOffset, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); + this.maybeSuggestBindingForAll(selectedElements); // We duplicate the selected element if alt is pressed on pointer move @@ -5912,15 +6037,21 @@ class App extends React.Component { groupIdMap, element, ); - const [originDragX, originDragY] = getGridPoint( - pointerDownState.origin.x - pointerDownState.drag.offset.x, - pointerDownState.origin.y - pointerDownState.drag.offset.y, - event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, - ); + const origElement = pointerDownState.originalElements.get( + element.id, + )!; mutateElement(duplicatedElement, { - x: duplicatedElement.x + (originDragX - dragX), - y: duplicatedElement.y + (originDragY - dragY), + x: origElement.x, + y: origElement.y, }); + + // put duplicated element to pointerDownState.originalElements + // so that we can snap to the duplicated element without releasing + pointerDownState.originalElements.set( + duplicatedElement.id, + duplicatedElement, + ); + nextElements.push(duplicatedElement); elementsToAppend.push(element); oldIdToDuplicatedId.set(element.id, duplicatedElement.id); @@ -5946,6 +6077,8 @@ class App extends React.Component { oldIdToDuplicatedId, ); this.scene.replaceAllElements(nextSceneElements); + this.maybeCacheVisibleGaps(event, selectedElements, true); + this.maybeCacheReferenceSnapPoints(event, selectedElements, true); } return; } @@ -6162,6 +6295,7 @@ class App extends React.Component { isResizing, isRotating, } = this.state; + this.setState({ isResizing: false, isRotating: false, @@ -6176,8 +6310,14 @@ class App extends React.Component { multiElement || isTextElement(this.state.editingElement) ? this.state.editingElement : null, + snapLines: [], + + originSnapOffset: null, }); + SnapCache.setReferenceSnapPoints(null); + SnapCache.setVisibleGaps(null); + this.savePointer(childEvent.clientX, childEvent.clientY, "up"); this.setState({ @@ -7705,7 +7845,7 @@ class App extends React.Component { shouldResizeFromCenter(event), ); } else { - const [gridX, gridY] = getGridPoint( + let [gridX, gridY] = getGridPoint( pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, @@ -7719,6 +7859,33 @@ class App extends React.Component { ? image.width / image.height : null; + this.maybeCacheReferenceSnapPoints(event, [draggingElement]); + + const { snapOffset, snapLines } = snapNewElement( + draggingElement, + this.state, + event, + { + x: + pointerDownState.originInGrid.x + + (this.state.originSnapOffset?.x ?? 0), + y: + pointerDownState.originInGrid.y + + (this.state.originSnapOffset?.y ?? 0), + }, + { + x: gridX - pointerDownState.originInGrid.x, + y: gridY - pointerDownState.originInGrid.y, + }, + ); + + gridX += snapOffset.x; + gridY += snapOffset.y; + + this.setState({ + snapLines, + }); + dragNewElement( draggingElement, this.state.activeTool.type, @@ -7733,6 +7900,7 @@ class App extends React.Component { : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), aspectRatio, + this.state.originSnapOffset, ); this.maybeSuggestBindingForAll([draggingElement]); @@ -7774,7 +7942,7 @@ class App extends React.Component { activeEmbeddable: null, }); const pointerCoords = pointerDownState.lastCoords; - const [resizeX, resizeY] = getGridPoint( + let [resizeX, resizeY] = getGridPoint( pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.y - pointerDownState.resize.offset.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, @@ -7802,6 +7970,41 @@ class App extends React.Component { }); }); + // check needed for avoiding flickering when a key gets pressed + // during dragging + if (!this.state.selectedElementsAreBeingDragged) { + const [gridX, gridY] = getGridPoint( + pointerCoords.x, + pointerCoords.y, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + ); + + const dragOffset = { + x: gridX - pointerDownState.originInGrid.x, + y: gridY - pointerDownState.originInGrid.y, + }; + + const originalElements = [...pointerDownState.originalElements.values()]; + + this.maybeCacheReferenceSnapPoints(event, selectedElements); + + const { snapOffset, snapLines } = snapResizingElements( + selectedElements, + getSelectedElements(originalElements, this.state), + this.state, + event, + dragOffset, + transformHandleType, + ); + + resizeX += snapOffset.x; + resizeY += snapOffset.y; + + this.setState({ + snapLines, + }); + } + if ( transformElements( pointerDownState, @@ -7817,6 +8020,7 @@ class App extends React.Component { resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y, + this.state, ) ) { this.maybeSuggestBindingForAll(selectedElements); @@ -7904,6 +8108,7 @@ class App extends React.Component { actionUnlockAllElements, CONTEXT_MENU_SEPARATOR, actionToggleGridMode, + actionToggleObjectsSnapMode, actionToggleZenMode, actionToggleViewMode, actionToggleStats, diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index b51fadfca6..3954839ea5 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -258,6 +258,10 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { label={t("buttons.zenMode")} shortcuts={[getShortcutKey("Alt+Z")]} /> + { + const [minX, minY, maxX, maxY] = getCommonBounds(elements); + return [ + minX + dragOffset.x, + minY + dragOffset.y, + maxX + dragOffset.x, + maxY + dragOffset.y, + ]; +}; + export const getResizedElementAbsoluteCoords = ( element: ExcalidrawElement, nextWidth: number, diff --git a/src/element/dragElements.ts b/src/element/dragElements.ts index 4dc5322ba8..866a56c543 100644 --- a/src/element/dragElements.ts +++ b/src/element/dragElements.ts @@ -6,23 +6,22 @@ import { NonDeletedExcalidrawElement } from "./types"; import { AppState, PointerDownState } from "../types"; import { getBoundTextElement } from "./textElement"; import { isSelectedViaGroup } from "../groups"; +import { getGridPoint } from "../math"; import Scene from "../scene/Scene"; import { isFrameElement } from "./typeChecks"; export const dragSelectedElements = ( pointerDownState: PointerDownState, selectedElements: NonDeletedExcalidrawElement[], - pointerX: number, - pointerY: number, - lockDirection: boolean = false, - distanceX: number = 0, - distanceY: number = 0, + offset: { x: number; y: number }, appState: AppState, scene: Scene, + snapOffset: { + x: number; + y: number; + }, + gridSize: AppState["gridSize"], ) => { - const [x1, y1] = getCommonBounds(selectedElements); - const offset = { x: pointerX - x1, y: pointerY - y1 }; - // we do not want a frame and its elements to be selected at the same time // but when it happens (due to some bug), we want to avoid updating element // in the frame twice, hence the use of set @@ -44,12 +43,11 @@ export const dragSelectedElements = ( elementsToUpdate.forEach((element) => { updateElementCoords( - lockDirection, - distanceX, - distanceY, pointerDownState, element, offset, + snapOffset, + gridSize, ); // update coords of bound text only if we're dragging the container directly // (we don't drag the group that it's part of) @@ -69,12 +67,11 @@ export const dragSelectedElements = ( (!textElement.frameId || !frames.includes(textElement.frameId)) ) { updateElementCoords( - lockDirection, - distanceX, - distanceY, pointerDownState, textElement, offset, + snapOffset, + gridSize, ); } } @@ -85,31 +82,40 @@ export const dragSelectedElements = ( }; const updateElementCoords = ( - lockDirection: boolean, - distanceX: number, - distanceY: number, pointerDownState: PointerDownState, element: NonDeletedExcalidrawElement, - offset: { x: number; y: number }, + dragOffset: { x: number; y: number }, + snapOffset: { x: number; y: number }, + gridSize: AppState["gridSize"], ) => { - let x: number; - let y: number; - if (lockDirection) { - const lockX = lockDirection && distanceX < distanceY; - const lockY = lockDirection && distanceX > distanceY; - const original = pointerDownState.originalElements.get(element.id); - x = lockX && original ? original.x : element.x + offset.x; - y = lockY && original ? original.y : element.y + offset.y; - } else { - x = element.x + offset.x; - y = element.y + offset.y; + const originalElement = + pointerDownState.originalElements.get(element.id) ?? element; + + let nextX = originalElement.x + dragOffset.x + snapOffset.x; + let nextY = originalElement.y + dragOffset.y + snapOffset.y; + + if (snapOffset.x === 0 || snapOffset.y === 0) { + const [nextGridX, nextGridY] = getGridPoint( + originalElement.x + dragOffset.x, + originalElement.y + dragOffset.y, + gridSize, + ); + + if (snapOffset.x === 0) { + nextX = nextGridX; + } + + if (snapOffset.y === 0) { + nextY = nextGridY; + } } mutateElement(element, { - x, - y, + x: nextX, + y: nextY, }); }; + export const getDragOffsetXY = ( selectedElements: NonDeletedExcalidrawElement[], x: number, @@ -133,6 +139,10 @@ export const dragNewElement = ( /** whether to keep given aspect ratio when `isResizeWithSidesSameLength` is true */ widthAspectRatio?: number | null, + originOffset: { + x: number; + y: number; + } | null = null, ) => { if (shouldMaintainAspectRatio && draggingElement.type !== "selection") { if (widthAspectRatio) { @@ -173,8 +183,8 @@ export const dragNewElement = ( if (width !== 0 && height !== 0) { mutateElement(draggingElement, { - x: newX, - y: newY, + x: newX + (originOffset?.x ?? 0), + y: newY + (originOffset?.y ?? 0), width, height, }); diff --git a/src/element/resizeElements.ts b/src/element/resizeElements.ts index 136c70fb8c..b4974848e9 100644 --- a/src/element/resizeElements.ts +++ b/src/element/resizeElements.ts @@ -41,7 +41,7 @@ import { MaybeTransformHandleType, TransformHandleDirection, } from "./transformHandles"; -import { Point, PointerDownState } from "../types"; +import { AppState, Point, PointerDownState } from "../types"; import Scene from "../scene/Scene"; import { getApproxMinLineWidth, @@ -79,6 +79,7 @@ export const transformElements = ( pointerY: number, centerX: number, centerY: number, + appState: AppState, ) => { if (selectedElements.length === 1) { const [element] = selectedElements; @@ -466,8 +467,8 @@ export const resizeSingleElement = ( boundTextElement.fontSize, boundTextElement.lineHeight, ); - eleNewWidth = Math.ceil(Math.max(eleNewWidth, minWidth)); - eleNewHeight = Math.ceil(Math.max(eleNewHeight, minHeight)); + eleNewWidth = Math.max(eleNewWidth, minWidth); + eleNewHeight = Math.max(eleNewHeight, minHeight); } } @@ -508,8 +509,11 @@ export const resizeSingleElement = ( } } + const flipX = eleNewWidth < 0; + const flipY = eleNewHeight < 0; + // Flip horizontally - if (eleNewWidth < 0) { + if (flipX) { if (transformHandleDirection.includes("e")) { newTopLeft[0] -= Math.abs(newBoundsWidth); } @@ -517,8 +521,9 @@ export const resizeSingleElement = ( newTopLeft[0] += Math.abs(newBoundsWidth); } } + // Flip vertically - if (eleNewHeight < 0) { + if (flipY) { if (transformHandleDirection.includes("s")) { newTopLeft[1] -= Math.abs(newBoundsHeight); } @@ -542,10 +547,20 @@ export const resizeSingleElement = ( const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle); newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle); + // For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner + // So we need to readjust (x,y) to be where the first point should be + const newOrigin = [...newTopLeft]; + const linearElementXOffset = stateAtResizeStart.x - newBoundsX1; + const linearElementYOffset = stateAtResizeStart.y - newBoundsY1; + newOrigin[0] += linearElementXOffset; + newOrigin[1] += linearElementYOffset; + + const nextX = newOrigin[0]; + const nextY = newOrigin[1]; + // Readjust points for linear elements let rescaledElementPointsY; let rescaledPoints; - if (isLinearElement(element) || isFreeDrawElement(element)) { rescaledElementPointsY = rescalePoints( 1, @@ -562,16 +577,11 @@ export const resizeSingleElement = ( ); } - // For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner - // So we need to readjust (x,y) to be where the first point should be - const newOrigin = [...newTopLeft]; - newOrigin[0] += stateAtResizeStart.x - newBoundsX1; - newOrigin[1] += stateAtResizeStart.y - newBoundsY1; const resizedElement = { width: Math.abs(eleNewWidth), height: Math.abs(eleNewHeight), - x: newOrigin[0], - y: newOrigin[1], + x: nextX, + y: nextY, points: rescaledPoints, }; @@ -680,6 +690,10 @@ export const resizeMultipleElements = ( const { minX, minY, maxX, maxY, midX, midY } = getCommonBoundingBox( targetElements.map(({ orig }) => orig).concat(boundTextElements), ); + + // const originalHeight = maxY - minY; + // const originalWidth = maxX - minX; + const direction = transformHandleType; const mapDirectionsToAnchors: Record = { diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index fc1e8cf2e4..9115d0e599 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -957,7 +957,7 @@ describe("textWysiwyg", () => { expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` [ 85, - 4.5, + 4.999999999999986, ] `); @@ -1002,8 +1002,8 @@ describe("textWysiwyg", () => { resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` [ - 375, - -539, + 374.99999999999994, + -535.0000000000001, ] `); }); @@ -1190,7 +1190,7 @@ describe("textWysiwyg", () => { editor.blur(); resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); - expect(rectangle.height).toBe(156); + expect(rectangle.height).toBeCloseTo(155, 8); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(null); mouse.select(rectangle); @@ -1200,9 +1200,12 @@ describe("textWysiwyg", () => { await new Promise((r) => setTimeout(r, 0)); editor.blur(); - expect(rectangle.height).toBe(156); + expect(rectangle.height).toBeCloseTo(155, 8); // cache updated again - expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(156); + expect(getOriginalContainerHeightFromCache(rectangle.id)).toBeCloseTo( + 155, + 8, + ); }); it("should reset the container height cache when font properties updated", async () => { diff --git a/src/keys.ts b/src/keys.ts index da3e6b49da..f7bf54db5e 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -21,6 +21,7 @@ export const CODES = { V: "KeyV", Z: "KeyZ", R: "KeyR", + S: "KeyS", } as const; export const KEYS = { diff --git a/src/locales/en.json b/src/locales/en.json index 6799d0649a..c3a041f579 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -164,6 +164,7 @@ "darkMode": "Dark mode", "lightMode": "Light mode", "zenMode": "Zen mode", + "objectsSnapMode": "Snap to objects", "exitZenMode": "Exit zen mode", "cancel": "Cancel", "clear": "Clear", diff --git a/src/math.test.ts b/src/math.test.ts index 60ab56184e..eb5392eed2 100644 --- a/src/math.test.ts +++ b/src/math.test.ts @@ -1,4 +1,4 @@ -import { rotate } from "./math"; +import { rangeIntersection, rangesOverlap, rotate } from "./math"; describe("rotate", () => { it("should rotate over (x2, y2) and return the rotated coordinates for (x1, y1)", () => { @@ -13,3 +13,43 @@ describe("rotate", () => { expect(res2).toEqual([x1, x2]); }); }); + +describe("range overlap", () => { + it("should overlap when range a contains range b", () => { + expect(rangesOverlap([1, 4], [2, 3])).toBe(true); + expect(rangesOverlap([1, 4], [1, 4])).toBe(true); + expect(rangesOverlap([1, 4], [1, 3])).toBe(true); + expect(rangesOverlap([1, 4], [2, 4])).toBe(true); + }); + + it("should overlap when range b contains range a", () => { + expect(rangesOverlap([2, 3], [1, 4])).toBe(true); + expect(rangesOverlap([1, 3], [1, 4])).toBe(true); + expect(rangesOverlap([2, 4], [1, 4])).toBe(true); + }); + + it("should overlap when range a and b intersect", () => { + expect(rangesOverlap([1, 4], [2, 5])).toBe(true); + }); +}); + +describe("range intersection", () => { + it("should intersect completely with itself", () => { + expect(rangeIntersection([1, 4], [1, 4])).toEqual([1, 4]); + }); + + it("should intersect irrespective of order", () => { + expect(rangeIntersection([1, 4], [2, 3])).toEqual([2, 3]); + expect(rangeIntersection([2, 3], [1, 4])).toEqual([2, 3]); + expect(rangeIntersection([1, 4], [3, 5])).toEqual([3, 4]); + expect(rangeIntersection([3, 5], [1, 4])).toEqual([3, 4]); + }); + + it("should intersect at the edge", () => { + expect(rangeIntersection([1, 4], [4, 5])).toEqual([4, 4]); + }); + + it("should not intersect", () => { + expect(rangeIntersection([1, 4], [5, 7])).toEqual(null); + }); +}); diff --git a/src/math.ts b/src/math.ts index f549a6af16..376052095b 100644 --- a/src/math.ts +++ b/src/math.ts @@ -472,3 +472,36 @@ export const isRightAngle = (angle: number) => { // angle, which we can check with modulo after rounding. return Math.round((angle / Math.PI) * 10000) % 5000 === 0; }; + +// Given two ranges, return if the two ranges overlap with each other +// e.g. [1, 3] overlaps with [2, 4] while [1, 3] does not overlap with [4, 5] +export const rangesOverlap = ( + [a0, a1]: [number, number], + [b0, b1]: [number, number], +) => { + if (a0 <= b0) { + return a1 >= b0; + } + + if (a0 >= b0) { + return b1 >= a0; + } + + return false; +}; + +// Given two ranges,return ther intersection of the two ranges if any +// e.g. the intersection of [1, 3] and [2, 4] is [2, 3] +export const rangeIntersection = ( + rangeA: [number, number], + rangeB: [number, number], +): [number, number] | null => { + const rangeStart = Math.max(rangeA[0], rangeB[0]); + const rangeEnd = Math.min(rangeA[1], rangeB[1]); + + if (rangeStart <= rangeEnd) { + return [rangeStart, rangeEnd]; + } + + return null; +}; diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 0af732ce6f..6395ed0e7a 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -67,6 +67,7 @@ import { EXTERNAL_LINK_IMG, getLinkHandleFromCoords, } from "../element/Hyperlink"; +import { renderSnaps } from "./renderSnaps"; import { isEmbeddableElement, isFrameElement, @@ -720,6 +721,8 @@ const _renderInteractiveScene = ({ context.restore(); } + renderSnaps(context, appState); + // Reset zoom context.restore(); diff --git a/src/renderer/renderSnaps.ts b/src/renderer/renderSnaps.ts new file mode 100644 index 0000000000..220c3e7db4 --- /dev/null +++ b/src/renderer/renderSnaps.ts @@ -0,0 +1,189 @@ +import { PointSnapLine, PointerSnapLine } from "../snapping"; +import { InteractiveCanvasAppState, Point } from "../types"; + +const SNAP_COLOR_LIGHT = "#ff6b6b"; +const SNAP_COLOR_DARK = "#ff0000"; +const SNAP_WIDTH = 1; +const SNAP_CROSS_SIZE = 2; + +export const renderSnaps = ( + context: CanvasRenderingContext2D, + appState: InteractiveCanvasAppState, +) => { + if (!appState.snapLines.length) { + return; + } + + // in dark mode, we need to adjust the color to account for color inversion. + // Don't change if zen mode, because we draw only crosses, we want the + // colors to be more visible + const snapColor = + appState.theme === "light" || appState.zenModeEnabled + ? SNAP_COLOR_LIGHT + : SNAP_COLOR_DARK; + // in zen mode make the cross more visible since we don't draw the lines + const snapWidth = + (appState.zenModeEnabled ? SNAP_WIDTH * 1.5 : SNAP_WIDTH) / + appState.zoom.value; + + context.save(); + context.translate(appState.scrollX, appState.scrollY); + + for (const snapLine of appState.snapLines) { + if (snapLine.type === "pointer") { + context.lineWidth = snapWidth; + context.strokeStyle = snapColor; + + drawPointerSnapLine(snapLine, context, appState); + } else if (snapLine.type === "gap") { + context.lineWidth = snapWidth; + context.strokeStyle = snapColor; + + drawGapLine( + snapLine.points[0], + snapLine.points[1], + snapLine.direction, + appState, + context, + ); + } else if (snapLine.type === "points") { + context.lineWidth = snapWidth; + context.strokeStyle = snapColor; + drawPointsSnapLine(snapLine, context, appState); + } + } + + context.restore(); +}; + +const drawPointsSnapLine = ( + pointSnapLine: PointSnapLine, + context: CanvasRenderingContext2D, + appState: InteractiveCanvasAppState, +) => { + if (!appState.zenModeEnabled) { + const firstPoint = pointSnapLine.points[0]; + const lastPoint = pointSnapLine.points[pointSnapLine.points.length - 1]; + + drawLine(firstPoint, lastPoint, context); + } + + for (const point of pointSnapLine.points) { + drawCross(point, appState, context); + } +}; + +const drawPointerSnapLine = ( + pointerSnapLine: PointerSnapLine, + context: CanvasRenderingContext2D, + appState: InteractiveCanvasAppState, +) => { + drawCross(pointerSnapLine.points[0], appState, context); + if (!appState.zenModeEnabled) { + drawLine(pointerSnapLine.points[0], pointerSnapLine.points[1], context); + } +}; + +const drawCross = ( + [x, y]: Point, + appState: InteractiveCanvasAppState, + context: CanvasRenderingContext2D, +) => { + context.save(); + const size = + (appState.zenModeEnabled ? SNAP_CROSS_SIZE * 1.5 : SNAP_CROSS_SIZE) / + appState.zoom.value; + context.beginPath(); + + context.moveTo(x - size, y - size); + context.lineTo(x + size, y + size); + + context.moveTo(x + size, y - size); + context.lineTo(x - size, y + size); + + context.stroke(); + context.restore(); +}; + +const drawLine = ( + from: Point, + to: Point, + context: CanvasRenderingContext2D, +) => { + context.beginPath(); + context.lineTo(...from); + context.lineTo(...to); + context.stroke(); +}; + +const drawGapLine = ( + from: Point, + to: Point, + direction: "horizontal" | "vertical", + appState: InteractiveCanvasAppState, + context: CanvasRenderingContext2D, +) => { + // a horizontal gap snap line + // |–––––––||–––––––| + // ^ ^ ^ ^ + // \ \ \ \ + // (1) (2) (3) (4) + + const FULL = 8 / appState.zoom.value; + const HALF = FULL / 2; + const QUARTER = FULL / 4; + + if (direction === "horizontal") { + const halfPoint = [(from[0] + to[0]) / 2, from[1]]; + // (1) + if (!appState.zenModeEnabled) { + drawLine([from[0], from[1] - FULL], [from[0], from[1] + FULL], context); + } + + // (3) + drawLine( + [halfPoint[0] - QUARTER, halfPoint[1] - HALF], + [halfPoint[0] - QUARTER, halfPoint[1] + HALF], + context, + ); + drawLine( + [halfPoint[0] + QUARTER, halfPoint[1] - HALF], + [halfPoint[0] + QUARTER, halfPoint[1] + HALF], + context, + ); + + if (!appState.zenModeEnabled) { + // (4) + drawLine([to[0], to[1] - FULL], [to[0], to[1] + FULL], context); + + // (2) + drawLine(from, to, context); + } + } else { + const halfPoint = [from[0], (from[1] + to[1]) / 2]; + // (1) + if (!appState.zenModeEnabled) { + drawLine([from[0] - FULL, from[1]], [from[0] + FULL, from[1]], context); + } + + // (3) + drawLine( + [halfPoint[0] - HALF, halfPoint[1] - QUARTER], + [halfPoint[0] + HALF, halfPoint[1] - QUARTER], + context, + ); + drawLine( + [halfPoint[0] - HALF, halfPoint[1] + QUARTER], + [halfPoint[0] + HALF, halfPoint[1] + QUARTER], + context, + ); + + if (!appState.zenModeEnabled) { + // (4) + drawLine([to[0] - FULL, to[1]], [to[0] + FULL, to[1]], context); + + // (2) + drawLine(from, to, context); + } + } +}; diff --git a/src/scene/selection.ts b/src/scene/selection.ts index e678894ab8..dce7c9cd54 100644 --- a/src/scene/selection.ts +++ b/src/scene/selection.ts @@ -11,6 +11,7 @@ import { getFrameElements, } from "../frame"; import { isShallowEqual } from "../utils"; +import { isElementInViewport } from "../element/sizeHelpers"; /** * Frames and their containing elements are not to be selected at the same time. @@ -89,6 +90,26 @@ export const getElementsWithinSelection = ( return elementsInSelection; }; +export const getVisibleAndNonSelectedElements = ( + elements: readonly NonDeletedExcalidrawElement[], + selectedElements: readonly NonDeletedExcalidrawElement[], + appState: AppState, +) => { + const selectedElementsSet = new Set( + selectedElements.map((element) => element.id), + ); + return elements.filter((element) => { + const isVisible = isElementInViewport( + element, + appState.width, + appState.height, + appState, + ); + + return !selectedElementsSet.has(element.id) && isVisible; + }); +}; + // FIXME move this into the editor instance to keep utility methods stateless export const isSomeElementSelected = (function () { let lastElements: readonly NonDeletedExcalidrawElement[] | null = null; diff --git a/src/snapping.ts b/src/snapping.ts new file mode 100644 index 0000000000..d3617e8024 --- /dev/null +++ b/src/snapping.ts @@ -0,0 +1,1361 @@ +import { + Bounds, + getCommonBounds, + getDraggedElementsBounds, + getElementAbsoluteCoords, +} from "./element/bounds"; +import { MaybeTransformHandleType } from "./element/transformHandles"; +import { isBoundToContainer, isFrameElement } from "./element/typeChecks"; +import { + ExcalidrawElement, + NonDeletedExcalidrawElement, +} from "./element/types"; +import { getMaximumGroups } from "./groups"; +import { KEYS } from "./keys"; +import { rangeIntersection, rangesOverlap, rotatePoint } from "./math"; +import { getVisibleAndNonSelectedElements } from "./scene/selection"; +import { AppState, KeyboardModifiersObject, Point } from "./types"; + +const SNAP_DISTANCE = 8; + +// do not comput more gaps per axis than this limit +// TODO increase or remove once we optimize +const VISIBLE_GAPS_LIMIT_PER_AXIS = 99999; + +// snap distance with zoom value taken into consideration +export const getSnapDistance = (zoomValue: number) => { + return SNAP_DISTANCE / zoomValue; +}; + +type Vector2D = { + x: number; + y: number; +}; + +type PointPair = [Point, Point]; + +export type PointSnap = { + type: "point"; + points: PointPair; + offset: number; +}; + +export type Gap = { + // start side ↓ length + // ┌───────────┐◄───────────────► + // │ │-----------------┌───────────┐ + // │ start │ ↑ │ │ + // │ element │ overlap │ end │ + // │ │ ↓ │ element │ + // └───────────┘-----------------│ │ + // └───────────┘ + // ↑ end side + startBounds: Bounds; + endBounds: Bounds; + startSide: [Point, Point]; + endSide: [Point, Point]; + overlap: [number, number]; + length: number; +}; + +export type GapSnap = { + type: "gap"; + direction: + | "center_horizontal" + | "center_vertical" + | "side_left" + | "side_right" + | "side_top" + | "side_bottom"; + gap: Gap; + offset: number; +}; + +export type GapSnaps = GapSnap[]; + +export type Snap = GapSnap | PointSnap; +export type Snaps = Snap[]; + +export type PointSnapLine = { + type: "points"; + points: Point[]; +}; + +export type PointerSnapLine = { + type: "pointer"; + points: PointPair; + direction: "horizontal" | "vertical"; +}; + +export type GapSnapLine = { + type: "gap"; + direction: "horizontal" | "vertical"; + points: PointPair; +}; + +export type SnapLine = PointSnapLine | GapSnapLine | PointerSnapLine; + +// ----------------------------------------------------------------------------- + +export class SnapCache { + private static referenceSnapPoints: Point[] | null = null; + + private static visibleGaps: { + verticalGaps: Gap[]; + horizontalGaps: Gap[]; + } | null = null; + + public static setReferenceSnapPoints = (snapPoints: Point[] | null) => { + SnapCache.referenceSnapPoints = snapPoints; + }; + + public static getReferenceSnapPoints = () => { + return SnapCache.referenceSnapPoints; + }; + + public static setVisibleGaps = ( + gaps: { + verticalGaps: Gap[]; + horizontalGaps: Gap[]; + } | null, + ) => { + SnapCache.visibleGaps = gaps; + }; + + public static getVisibleGaps = () => { + return SnapCache.visibleGaps; + }; + + public static destroy = () => { + SnapCache.referenceSnapPoints = null; + SnapCache.visibleGaps = null; + }; +} + +// ----------------------------------------------------------------------------- + +export const isSnappingEnabled = ({ + event, + appState, + selectedElements, +}: { + appState: AppState; + event: KeyboardModifiersObject; + selectedElements: NonDeletedExcalidrawElement[]; +}) => { + if (event) { + return ( + (appState.objectsSnapModeEnabled && !event[KEYS.CTRL_OR_CMD]) || + (!appState.objectsSnapModeEnabled && + event[KEYS.CTRL_OR_CMD] && + appState.gridSize === null) + ); + } + + // do not suggest snaps for an arrow to give way to binding + if (selectedElements.length === 1 && selectedElements[0].type === "arrow") { + return false; + } + return appState.objectsSnapModeEnabled; +}; + +export const areRoughlyEqual = (a: number, b: number, precision = 0.01) => { + return Math.abs(a - b) <= precision; +}; + +export const getElementsCorners = ( + elements: ExcalidrawElement[], + { + omitCenter, + boundingBoxCorners, + dragOffset, + }: { + omitCenter?: boolean; + boundingBoxCorners?: boolean; + dragOffset?: Vector2D; + } = { + omitCenter: false, + boundingBoxCorners: false, + }, +): Point[] => { + let result: Point[] = []; + + if (elements.length === 1) { + const element = elements[0]; + + let [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(element); + + if (dragOffset) { + x1 += dragOffset.x; + x2 += dragOffset.x; + cx += dragOffset.x; + + y1 += dragOffset.y; + y2 += dragOffset.y; + cy += dragOffset.y; + } + + const halfWidth = (x2 - x1) / 2; + const halfHeight = (y2 - y1) / 2; + + if ( + (element.type === "diamond" || element.type === "ellipse") && + !boundingBoxCorners + ) { + const leftMid = rotatePoint( + [x1, y1 + halfHeight], + [cx, cy], + element.angle, + ); + const topMid = rotatePoint([x1 + halfWidth, y1], [cx, cy], element.angle); + const rightMid = rotatePoint( + [x2, y1 + halfHeight], + [cx, cy], + element.angle, + ); + const bottomMid = rotatePoint( + [x1 + halfWidth, y2], + [cx, cy], + element.angle, + ); + const center: Point = [cx, cy]; + + result = omitCenter + ? [leftMid, topMid, rightMid, bottomMid] + : [leftMid, topMid, rightMid, bottomMid, center]; + } else { + const topLeft = rotatePoint([x1, y1], [cx, cy], element.angle); + const topRight = rotatePoint([x2, y1], [cx, cy], element.angle); + const bottomLeft = rotatePoint([x1, y2], [cx, cy], element.angle); + const bottomRight = rotatePoint([x2, y2], [cx, cy], element.angle); + const center: Point = [cx, cy]; + + result = omitCenter + ? [topLeft, topRight, bottomLeft, bottomRight] + : [topLeft, topRight, bottomLeft, bottomRight, center]; + } + } else if (elements.length > 1) { + const [minX, minY, maxX, maxY] = getDraggedElementsBounds( + elements, + dragOffset ?? { x: 0, y: 0 }, + ); + const width = maxX - minX; + const height = maxY - minY; + + const topLeft: Point = [minX, minY]; + const topRight: Point = [maxX, minY]; + const bottomLeft: Point = [minX, maxY]; + const bottomRight: Point = [maxX, maxY]; + const center: Point = [minX + width / 2, minY + height / 2]; + + result = omitCenter + ? [topLeft, topRight, bottomLeft, bottomRight] + : [topLeft, topRight, bottomLeft, bottomRight, center]; + } + + return result.map((point) => [round(point[0]), round(point[1])] as Point); +}; + +const getReferenceElements = ( + elements: readonly NonDeletedExcalidrawElement[], + selectedElements: NonDeletedExcalidrawElement[], + appState: AppState, +) => { + const selectedFrames = selectedElements + .filter((element) => isFrameElement(element)) + .map((frame) => frame.id); + + return getVisibleAndNonSelectedElements( + elements, + selectedElements, + appState, + ).filter( + (element) => !(element.frameId && selectedFrames.includes(element.frameId)), + ); +}; + +export const getVisibleGaps = ( + elements: readonly NonDeletedExcalidrawElement[], + selectedElements: ExcalidrawElement[], + appState: AppState, +) => { + const referenceElements: ExcalidrawElement[] = getReferenceElements( + elements, + selectedElements, + appState, + ); + + const referenceBounds = getMaximumGroups(referenceElements) + .filter( + (elementsGroup) => + !(elementsGroup.length === 1 && isBoundToContainer(elementsGroup[0])), + ) + .map( + (group) => + getCommonBounds(group).map((bound) => + round(bound), + ) as unknown as Bounds, + ); + + const horizontallySorted = referenceBounds.sort((a, b) => a[0] - b[0]); + + const horizontalGaps: Gap[] = []; + + let c = 0; + + horizontal: for (let i = 0; i < horizontallySorted.length; i++) { + const startBounds = horizontallySorted[i]; + + for (let j = i + 1; j < horizontallySorted.length; j++) { + if (++c > VISIBLE_GAPS_LIMIT_PER_AXIS) { + break horizontal; + } + + const endBounds = horizontallySorted[j]; + + const [, startMinY, startMaxX, startMaxY] = startBounds; + const [endMinX, endMinY, , endMaxY] = endBounds; + + if ( + startMaxX < endMinX && + rangesOverlap([startMinY, startMaxY], [endMinY, endMaxY]) + ) { + horizontalGaps.push({ + startBounds, + endBounds, + startSide: [ + [startMaxX, startMinY], + [startMaxX, startMaxY], + ], + endSide: [ + [endMinX, endMinY], + [endMinX, endMaxY], + ], + length: endMinX - startMaxX, + overlap: rangeIntersection( + [startMinY, startMaxY], + [endMinY, endMaxY], + )!, + }); + } + } + } + + const verticallySorted = referenceBounds.sort((a, b) => a[1] - b[1]); + + const verticalGaps: Gap[] = []; + + c = 0; + + vertical: for (let i = 0; i < verticallySorted.length; i++) { + const startBounds = verticallySorted[i]; + + for (let j = i + 1; j < verticallySorted.length; j++) { + if (++c > VISIBLE_GAPS_LIMIT_PER_AXIS) { + break vertical; + } + const endBounds = verticallySorted[j]; + + const [startMinX, , startMaxX, startMaxY] = startBounds; + const [endMinX, endMinY, endMaxX] = endBounds; + + if ( + startMaxY < endMinY && + rangesOverlap([startMinX, startMaxX], [endMinX, endMaxX]) + ) { + verticalGaps.push({ + startBounds, + endBounds, + startSide: [ + [startMinX, startMaxY], + [startMaxX, startMaxY], + ], + endSide: [ + [endMinX, endMinY], + [endMaxX, endMinY], + ], + length: endMinY - startMaxY, + overlap: rangeIntersection( + [startMinX, startMaxX], + [endMinX, endMaxX], + )!, + }); + } + } + } + + return { + horizontalGaps, + verticalGaps, + }; +}; + +const getGapSnaps = ( + selectedElements: ExcalidrawElement[], + dragOffset: Vector2D, + appState: AppState, + event: KeyboardModifiersObject, + nearestSnapsX: Snaps, + nearestSnapsY: Snaps, + minOffset: Vector2D, +) => { + if (!isSnappingEnabled({ appState, event, selectedElements })) { + return []; + } + + if (selectedElements.length === 0) { + return []; + } + + const visibleGaps = SnapCache.getVisibleGaps(); + + if (visibleGaps) { + const { horizontalGaps, verticalGaps } = visibleGaps; + + const [minX, minY, maxX, maxY] = getDraggedElementsBounds( + selectedElements, + dragOffset, + ).map((bound) => round(bound)); + const centerX = (minX + maxX) / 2; + const centerY = (minY + maxY) / 2; + + for (const gap of horizontalGaps) { + if (!rangesOverlap([minY, maxY], gap.overlap)) { + continue; + } + + // center gap + const gapMidX = gap.startSide[0][0] + gap.length / 2; + const centerOffset = round(gapMidX - centerX); + const gapIsLargerThanSelection = gap.length > maxX - minX; + + if (gapIsLargerThanSelection && Math.abs(centerOffset) <= minOffset.x) { + if (Math.abs(centerOffset) < minOffset.x) { + nearestSnapsX.length = 0; + } + minOffset.x = Math.abs(centerOffset); + + const snap: GapSnap = { + type: "gap", + direction: "center_horizontal", + gap, + offset: centerOffset, + }; + + nearestSnapsX.push(snap); + continue; + } + + // side gap, from the right + const [, , endMaxX] = gap.endBounds; + const distanceToEndElementX = minX - endMaxX; + const sideOffsetRight = round(gap.length - distanceToEndElementX); + + if (Math.abs(sideOffsetRight) <= minOffset.x) { + if (Math.abs(sideOffsetRight) < minOffset.x) { + nearestSnapsX.length = 0; + } + minOffset.x = Math.abs(sideOffsetRight); + + const snap: GapSnap = { + type: "gap", + direction: "side_right", + gap, + offset: sideOffsetRight, + }; + nearestSnapsX.push(snap); + continue; + } + + // side gap, from the left + const [startMinX, , ,] = gap.startBounds; + const distanceToStartElementX = startMinX - maxX; + const sideOffsetLeft = round(distanceToStartElementX - gap.length); + + if (Math.abs(sideOffsetLeft) <= minOffset.x) { + if (Math.abs(sideOffsetLeft) < minOffset.x) { + nearestSnapsX.length = 0; + } + minOffset.x = Math.abs(sideOffsetLeft); + + const snap: GapSnap = { + type: "gap", + direction: "side_left", + gap, + offset: sideOffsetLeft, + }; + nearestSnapsX.push(snap); + continue; + } + } + for (const gap of verticalGaps) { + if (!rangesOverlap([minX, maxX], gap.overlap)) { + continue; + } + + // center gap + const gapMidY = gap.startSide[0][1] + gap.length / 2; + const centerOffset = round(gapMidY - centerY); + const gapIsLargerThanSelection = gap.length > maxY - minY; + + if (gapIsLargerThanSelection && Math.abs(centerOffset) <= minOffset.y) { + if (Math.abs(centerOffset) < minOffset.y) { + nearestSnapsY.length = 0; + } + minOffset.y = Math.abs(centerOffset); + + const snap: GapSnap = { + type: "gap", + direction: "center_vertical", + gap, + offset: centerOffset, + }; + + nearestSnapsY.push(snap); + continue; + } + + // side gap, from the top + const [, startMinY, ,] = gap.startBounds; + const distanceToStartElementY = startMinY - maxY; + const sideOffsetTop = round(distanceToStartElementY - gap.length); + + if (Math.abs(sideOffsetTop) <= minOffset.y) { + if (Math.abs(sideOffsetTop) < minOffset.y) { + nearestSnapsY.length = 0; + } + minOffset.y = Math.abs(sideOffsetTop); + + const snap: GapSnap = { + type: "gap", + direction: "side_top", + gap, + offset: sideOffsetTop, + }; + nearestSnapsY.push(snap); + continue; + } + + // side gap, from the bottom + const [, , , endMaxY] = gap.endBounds; + const distanceToEndElementY = round(minY - endMaxY); + const sideOffsetBottom = gap.length - distanceToEndElementY; + + if (Math.abs(sideOffsetBottom) <= minOffset.y) { + if (Math.abs(sideOffsetBottom) < minOffset.y) { + nearestSnapsY.length = 0; + } + minOffset.y = Math.abs(sideOffsetBottom); + + const snap: GapSnap = { + type: "gap", + direction: "side_bottom", + gap, + offset: sideOffsetBottom, + }; + nearestSnapsY.push(snap); + continue; + } + } + } +}; + +export const getReferenceSnapPoints = ( + elements: readonly NonDeletedExcalidrawElement[], + selectedElements: ExcalidrawElement[], + appState: AppState, +) => { + const referenceElements = getReferenceElements( + elements, + selectedElements, + appState, + ); + + return getMaximumGroups(referenceElements) + .filter( + (elementsGroup) => + !(elementsGroup.length === 1 && isBoundToContainer(elementsGroup[0])), + ) + .flatMap((elementGroup) => getElementsCorners(elementGroup)); +}; + +const getPointSnaps = ( + selectedElements: ExcalidrawElement[], + selectionSnapPoints: Point[], + appState: AppState, + event: KeyboardModifiersObject, + nearestSnapsX: Snaps, + nearestSnapsY: Snaps, + minOffset: Vector2D, +) => { + if ( + !isSnappingEnabled({ appState, event, selectedElements }) || + (selectedElements.length === 0 && selectionSnapPoints.length === 0) + ) { + return []; + } + + const referenceSnapPoints = SnapCache.getReferenceSnapPoints(); + + if (referenceSnapPoints) { + for (const thisSnapPoint of selectionSnapPoints) { + for (const otherSnapPoint of referenceSnapPoints) { + const offsetX = otherSnapPoint[0] - thisSnapPoint[0]; + const offsetY = otherSnapPoint[1] - thisSnapPoint[1]; + + if (Math.abs(offsetX) <= minOffset.x) { + if (Math.abs(offsetX) < minOffset.x) { + nearestSnapsX.length = 0; + } + + nearestSnapsX.push({ + type: "point", + points: [thisSnapPoint, otherSnapPoint], + offset: offsetX, + }); + + minOffset.x = Math.abs(offsetX); + } + + if (Math.abs(offsetY) <= minOffset.y) { + if (Math.abs(offsetY) < minOffset.y) { + nearestSnapsY.length = 0; + } + + nearestSnapsY.push({ + type: "point", + points: [thisSnapPoint, otherSnapPoint], + offset: offsetY, + }); + + minOffset.y = Math.abs(offsetY); + } + } + } + } +}; + +export const snapDraggedElements = ( + selectedElements: ExcalidrawElement[], + dragOffset: Vector2D, + appState: AppState, + event: KeyboardModifiersObject, +) => { + if ( + !isSnappingEnabled({ appState, event, selectedElements }) || + selectedElements.length === 0 + ) { + return { + snapOffset: { + x: 0, + y: 0, + }, + snapLines: [], + }; + } + + dragOffset.x = round(dragOffset.x); + dragOffset.y = round(dragOffset.y); + const nearestSnapsX: Snaps = []; + const nearestSnapsY: Snaps = []; + const snapDistance = getSnapDistance(appState.zoom.value); + const minOffset = { + x: snapDistance, + y: snapDistance, + }; + + const selectionPoints = getElementsCorners(selectedElements, { + dragOffset, + }); + + // get the nearest horizontal and vertical point and gap snaps + getPointSnaps( + selectedElements, + selectionPoints, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + getGapSnaps( + selectedElements, + dragOffset, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + // using the nearest snaps to figure out how + // much the elements need to be offset to be snapped + // to some reference elements + const snapOffset = { + x: nearestSnapsX[0]?.offset ?? 0, + y: nearestSnapsY[0]?.offset ?? 0, + }; + + // once the elements are snapped + // and moved to the snapped position + // we want to use the element's snapped position + // to update nearest snaps so that we can create + // point and gap snap lines correctly without any shifting + + minOffset.x = 0; + minOffset.y = 0; + nearestSnapsX.length = 0; + nearestSnapsY.length = 0; + const newDragOffset = { + x: round(dragOffset.x + snapOffset.x), + y: round(dragOffset.y + snapOffset.y), + }; + + getPointSnaps( + selectedElements, + getElementsCorners(selectedElements, { + dragOffset: newDragOffset, + }), + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + getGapSnaps( + selectedElements, + newDragOffset, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + const pointSnapLines = createPointSnapLines(nearestSnapsX, nearestSnapsY); + + const gapSnapLines = createGapSnapLines( + selectedElements, + newDragOffset, + [...nearestSnapsX, ...nearestSnapsY].filter( + (snap) => snap.type === "gap", + ) as GapSnap[], + ); + + return { + snapOffset, + snapLines: [...pointSnapLines, ...gapSnapLines], + }; +}; + +const round = (x: number) => { + const decimalPlaces = 6; + return Math.round(x * 10 ** decimalPlaces) / 10 ** decimalPlaces; +}; + +const dedupePoints = (points: Point[]): Point[] => { + const map = new Map(); + + for (const point of points) { + const key = point.join(","); + + if (!map.has(key)) { + map.set(key, point); + } + } + + return Array.from(map.values()); +}; + +const createPointSnapLines = ( + nearestSnapsX: Snaps, + nearestSnapsY: Snaps, +): PointSnapLine[] => { + const snapsX = {} as { [key: string]: Point[] }; + const snapsY = {} as { [key: string]: Point[] }; + + if (nearestSnapsX.length > 0) { + for (const snap of nearestSnapsX) { + if (snap.type === "point") { + // key = thisPoint.x + const key = round(snap.points[0][0]); + if (!snapsX[key]) { + snapsX[key] = []; + } + snapsX[key].push( + ...snap.points.map( + (point) => [round(point[0]), round(point[1])] as Point, + ), + ); + } + } + } + + if (nearestSnapsY.length > 0) { + for (const snap of nearestSnapsY) { + if (snap.type === "point") { + // key = thisPoint.y + const key = round(snap.points[0][1]); + if (!snapsY[key]) { + snapsY[key] = []; + } + snapsY[key].push( + ...snap.points.map( + (point) => [round(point[0]), round(point[1])] as Point, + ), + ); + } + } + } + + return Object.entries(snapsX) + .map(([key, points]) => { + return { + type: "points", + points: dedupePoints( + points + .map((point) => { + return [Number(key), point[1]] as Point; + }) + .sort((a, b) => a[1] - b[1]), + ), + } as PointSnapLine; + }) + .concat( + Object.entries(snapsY).map(([key, points]) => { + return { + type: "points", + points: dedupePoints( + points + .map((point) => { + return [point[0], Number(key)] as Point; + }) + .sort((a, b) => a[0] - b[0]), + ), + } as PointSnapLine; + }), + ); +}; + +const dedupeGapSnapLines = (gapSnapLines: GapSnapLine[]) => { + const map = new Map(); + + for (const gapSnapLine of gapSnapLines) { + const key = gapSnapLine.points + .flat() + .map((point) => [round(point)]) + .join(","); + + if (!map.has(key)) { + map.set(key, gapSnapLine); + } + } + + return Array.from(map.values()); +}; + +const createGapSnapLines = ( + selectedElements: ExcalidrawElement[], + dragOffset: Vector2D, + gapSnaps: GapSnap[], +): GapSnapLine[] => { + const [minX, minY, maxX, maxY] = getDraggedElementsBounds( + selectedElements, + dragOffset, + ); + + const gapSnapLines: GapSnapLine[] = []; + + for (const gapSnap of gapSnaps) { + const [startMinX, startMinY, startMaxX, startMaxY] = + gapSnap.gap.startBounds; + const [endMinX, endMinY, endMaxX, endMaxY] = gapSnap.gap.endBounds; + + const verticalIntersection = rangeIntersection( + [minY, maxY], + gapSnap.gap.overlap, + ); + + const horizontalGapIntersection = rangeIntersection( + [minX, maxX], + gapSnap.gap.overlap, + ); + + switch (gapSnap.direction) { + case "center_horizontal": { + if (verticalIntersection) { + const gapLineY = + (verticalIntersection[0] + verticalIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "horizontal", + points: [ + [gapSnap.gap.startSide[0][0], gapLineY], + [minX, gapLineY], + ], + }, + { + type: "gap", + direction: "horizontal", + points: [ + [maxX, gapLineY], + [gapSnap.gap.endSide[0][0], gapLineY], + ], + }, + ); + } + break; + } + case "center_vertical": { + if (horizontalGapIntersection) { + const gapLineX = + (horizontalGapIntersection[0] + horizontalGapIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, gapSnap.gap.startSide[0][1]], + [gapLineX, minY], + ], + }, + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, maxY], + [gapLineX, gapSnap.gap.endSide[0][1]], + ], + }, + ); + } + break; + } + case "side_right": { + if (verticalIntersection) { + const gapLineY = + (verticalIntersection[0] + verticalIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "horizontal", + points: [ + [startMaxX, gapLineY], + [endMinX, gapLineY], + ], + }, + { + type: "gap", + direction: "horizontal", + points: [ + [endMaxX, gapLineY], + [minX, gapLineY], + ], + }, + ); + } + break; + } + case "side_left": { + if (verticalIntersection) { + const gapLineY = + (verticalIntersection[0] + verticalIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "horizontal", + points: [ + [maxX, gapLineY], + [startMinX, gapLineY], + ], + }, + { + type: "gap", + direction: "horizontal", + points: [ + [startMaxX, gapLineY], + [endMinX, gapLineY], + ], + }, + ); + } + break; + } + case "side_top": { + if (horizontalGapIntersection) { + const gapLineX = + (horizontalGapIntersection[0] + horizontalGapIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, maxY], + [gapLineX, startMinY], + ], + }, + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, startMaxY], + [gapLineX, endMinY], + ], + }, + ); + } + break; + } + case "side_bottom": { + if (horizontalGapIntersection) { + const gapLineX = + (horizontalGapIntersection[0] + horizontalGapIntersection[1]) / 2; + + gapSnapLines.push( + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, startMaxY], + [gapLineX, endMinY], + ], + }, + { + type: "gap", + direction: "vertical", + points: [ + [gapLineX, endMaxY], + [gapLineX, minY], + ], + }, + ); + } + break; + } + } + } + + return dedupeGapSnapLines( + gapSnapLines.map((gapSnapLine) => { + return { + ...gapSnapLine, + points: gapSnapLine.points.map( + (point) => [round(point[0]), round(point[1])] as Point, + ) as PointPair, + }; + }), + ); +}; + +export const snapResizingElements = ( + // use the latest elements to create snap lines + selectedElements: ExcalidrawElement[], + // while using the original elements to appy dragOffset to calculate snaps + selectedOriginalElements: ExcalidrawElement[], + appState: AppState, + event: KeyboardModifiersObject, + dragOffset: Vector2D, + transformHandle: MaybeTransformHandleType, +) => { + if ( + !isSnappingEnabled({ event, selectedElements, appState }) || + selectedElements.length === 0 || + (selectedElements.length === 1 && + !areRoughlyEqual(selectedElements[0].angle, 0)) + ) { + return { + snapOffset: { x: 0, y: 0 }, + snapLines: [], + }; + } + + let [minX, minY, maxX, maxY] = getCommonBounds(selectedOriginalElements); + + if (transformHandle) { + if (transformHandle.includes("e")) { + maxX += dragOffset.x; + } else if (transformHandle.includes("w")) { + minX += dragOffset.x; + } + + if (transformHandle.includes("n")) { + minY += dragOffset.y; + } else if (transformHandle.includes("s")) { + maxY += dragOffset.y; + } + } + + const selectionSnapPoints: Point[] = []; + + if (transformHandle) { + switch (transformHandle) { + case "e": { + selectionSnapPoints.push([maxX, minY], [maxX, maxY]); + break; + } + case "w": { + selectionSnapPoints.push([minX, minY], [minX, maxY]); + break; + } + case "n": { + selectionSnapPoints.push([minX, minY], [maxX, minY]); + break; + } + case "s": { + selectionSnapPoints.push([minX, maxY], [maxX, maxY]); + break; + } + case "ne": { + selectionSnapPoints.push([maxX, minY]); + break; + } + case "nw": { + selectionSnapPoints.push([minX, minY]); + break; + } + case "se": { + selectionSnapPoints.push([maxX, maxY]); + break; + } + case "sw": { + selectionSnapPoints.push([minX, maxY]); + break; + } + } + } + + const snapDistance = getSnapDistance(appState.zoom.value); + + const minOffset = { + x: snapDistance, + y: snapDistance, + }; + + const nearestSnapsX: Snaps = []; + const nearestSnapsY: Snaps = []; + + getPointSnaps( + selectedOriginalElements, + selectionSnapPoints, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + const snapOffset = { + x: nearestSnapsX[0]?.offset ?? 0, + y: nearestSnapsY[0]?.offset ?? 0, + }; + + // again, once snap offset is calculated + // reset to recompute for creating snap lines to be rendered + minOffset.x = 0; + minOffset.y = 0; + nearestSnapsX.length = 0; + nearestSnapsY.length = 0; + + const [x1, y1, x2, y2] = getCommonBounds(selectedElements).map((bound) => + round(bound), + ); + + const corners: Point[] = [ + [x1, y1], + [x1, y2], + [x2, y1], + [x2, y2], + ]; + + getPointSnaps( + selectedElements, + corners, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + const pointSnapLines = createPointSnapLines(nearestSnapsX, nearestSnapsY); + + return { + snapOffset, + snapLines: pointSnapLines, + }; +}; + +export const snapNewElement = ( + draggingElement: ExcalidrawElement, + appState: AppState, + event: KeyboardModifiersObject, + origin: Vector2D, + dragOffset: Vector2D, +) => { + if ( + !isSnappingEnabled({ event, selectedElements: [draggingElement], appState }) + ) { + return { + snapOffset: { x: 0, y: 0 }, + snapLines: [], + }; + } + + const selectionSnapPoints: Point[] = [ + [origin.x + dragOffset.x, origin.y + dragOffset.y], + ]; + + const snapDistance = getSnapDistance(appState.zoom.value); + + const minOffset = { + x: snapDistance, + y: snapDistance, + }; + + const nearestSnapsX: Snaps = []; + const nearestSnapsY: Snaps = []; + + getPointSnaps( + [draggingElement], + selectionSnapPoints, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + const snapOffset = { + x: nearestSnapsX[0]?.offset ?? 0, + y: nearestSnapsY[0]?.offset ?? 0, + }; + + minOffset.x = 0; + minOffset.y = 0; + nearestSnapsX.length = 0; + nearestSnapsY.length = 0; + + const corners = getElementsCorners([draggingElement], { + boundingBoxCorners: true, + omitCenter: true, + }); + + getPointSnaps( + [draggingElement], + corners, + appState, + event, + nearestSnapsX, + nearestSnapsY, + minOffset, + ); + + const pointSnapLines = createPointSnapLines(nearestSnapsX, nearestSnapsY); + + return { + snapOffset, + snapLines: pointSnapLines, + }; +}; + +export const getSnapLinesAtPointer = ( + elements: readonly ExcalidrawElement[], + appState: AppState, + pointer: Vector2D, + event: KeyboardModifiersObject, +) => { + if (!isSnappingEnabled({ event, selectedElements: [], appState })) { + return { + originOffset: { x: 0, y: 0 }, + snapLines: [], + }; + } + + const referenceElements = getVisibleAndNonSelectedElements( + elements, + [], + appState, + ); + + const snapDistance = getSnapDistance(appState.zoom.value); + + const minOffset = { + x: snapDistance, + y: snapDistance, + }; + + const horizontalSnapLines: PointerSnapLine[] = []; + const verticalSnapLines: PointerSnapLine[] = []; + + for (const referenceElement of referenceElements) { + const corners = getElementsCorners([referenceElement]); + + for (const corner of corners) { + const offsetX = corner[0] - pointer.x; + + if (Math.abs(offsetX) <= Math.abs(minOffset.x)) { + if (Math.abs(offsetX) < Math.abs(minOffset.x)) { + verticalSnapLines.length = 0; + } + + verticalSnapLines.push({ + type: "pointer", + points: [corner, [corner[0], pointer.y]], + direction: "vertical", + }); + + minOffset.x = offsetX; + } + + const offsetY = corner[1] - pointer.y; + + if (Math.abs(offsetY) <= Math.abs(minOffset.y)) { + if (Math.abs(offsetY) < Math.abs(minOffset.y)) { + horizontalSnapLines.length = 0; + } + + horizontalSnapLines.push({ + type: "pointer", + points: [corner, [pointer.x, corner[1]]], + direction: "horizontal", + }); + + minOffset.y = offsetY; + } + } + } + + return { + originOffset: { + x: + verticalSnapLines.length > 0 + ? verticalSnapLines[0].points[0][0] - pointer.x + : 0, + y: + horizontalSnapLines.length > 0 + ? horizontalSnapLines[0].points[0][1] - pointer.y + : 0, + }, + snapLines: [...verticalSnapLines, ...horizontalSnapLines], + }; +}; + +export const isActiveToolNonLinearSnappable = ( + activeToolType: AppState["activeTool"]["type"], +) => { + return ( + activeToolType === "rectangle" || + activeToolType === "ellipse" || + activeToolType === "diamond" || + activeToolType === "frame" || + activeToolType === "image" + ); +}; diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 488cd624af..5f94922255 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -331,12 +331,17 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -363,6 +368,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -524,12 +530,14 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -553,6 +561,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -723,12 +732,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -752,6 +763,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -1096,12 +1108,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -1125,6 +1139,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -1469,12 +1484,14 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -1498,6 +1515,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -1668,12 +1686,14 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -1695,6 +1715,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -1904,12 +1925,14 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -1933,6 +1956,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2205,12 +2229,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2239,6 +2265,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2594,12 +2621,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2623,6 +2652,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -3473,12 +3503,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -3502,6 +3534,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -3846,12 +3879,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -3875,6 +3910,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -4219,12 +4255,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -4251,6 +4289,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -4951,12 +4990,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -4983,6 +5024,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5531,12 +5573,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -5565,6 +5609,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5950,6 +5995,19 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app }, "viewMode": true, }, + { + "checked": [Function], + "contextItemLabel": "buttons.objectsSnapMode", + "keyTest": [Function], + "name": "objectsSnapMode", + "perform": [Function], + "predicate": [Function], + "trackEvent": { + "category": "canvas", + "predicate": [Function], + }, + "viewMode": true, + }, { "checked": [Function], "contextItemLabel": "buttons.zenMode", @@ -6035,12 +6093,17 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -6062,6 +6125,7 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -6431,12 +6495,14 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -6460,6 +6526,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -6805,12 +6872,17 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 20, "offsetTop": 10, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -6834,6 +6906,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 98e461a66a..32dd175454 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -55,12 +55,14 @@ exports[`given element A and group of elements B and given both are selected whe "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -91,6 +93,7 @@ exports[`given element A and group of elements B and given both are selected whe "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -508,12 +511,14 @@ exports[`given element A and group of elements B and given both are selected whe "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -546,6 +551,7 @@ exports[`given element A and group of elements B and given both are selected whe "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -963,12 +969,14 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -992,6 +1000,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -1791,12 +1800,14 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -1822,6 +1833,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2001,12 +2013,14 @@ exports[`regression tests > adjusts z order when grouping > [end of test] appSta "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2035,6 +2049,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] appSta "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2452,12 +2467,14 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] appSt "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2483,6 +2500,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] appSt "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2691,12 +2709,14 @@ exports[`regression tests > arrow keys > [end of test] appState 1`] = ` "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2720,6 +2740,7 @@ exports[`regression tests > arrow keys > [end of test] appState 1`] = ` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -2856,12 +2877,14 @@ exports[`regression tests > can drag element that covers another element, while "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -2887,6 +2910,7 @@ exports[`regression tests > can drag element that covers another element, while "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -3297,12 +3321,14 @@ exports[`regression tests > change the properties of a shape > [end of test] app "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": "elementStroke", "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -3326,6 +3352,7 @@ exports[`regression tests > change the properties of a shape > [end of test] app "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -3591,12 +3618,14 @@ exports[`regression tests > click on an element and drag it > [dragged] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -3622,6 +3651,7 @@ exports[`regression tests > click on an element and drag it > [dragged] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -3833,12 +3863,14 @@ exports[`regression tests > click on an element and drag it > [end of test] appS "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -3864,6 +3896,7 @@ exports[`regression tests > click on an element and drag it > [end of test] appS "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -4086,12 +4119,14 @@ exports[`regression tests > click to select a shape > [end of test] appState 1`] "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -4117,6 +4152,7 @@ exports[`regression tests > click to select a shape > [end of test] appState 1`] "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -4325,12 +4361,14 @@ exports[`regression tests > click-drag to select a group > [end of test] appStat "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -4357,6 +4395,7 @@ exports[`regression tests > click-drag to select a group > [end of test] appStat "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -4666,12 +4705,14 @@ exports[`regression tests > deleting last but one element in editing group shoul "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -4697,6 +4738,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5166,12 +5208,14 @@ exports[`regression tests > deselects group of selected elements on pointer down "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -5224,6 +5268,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5460,12 +5505,14 @@ exports[`regression tests > deselects group of selected elements on pointer up w "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -5490,6 +5537,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5726,12 +5774,14 @@ exports[`regression tests > deselects selected element on pointer down when poin "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -5783,6 +5833,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -5919,12 +5970,14 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -5948,6 +6001,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -6084,12 +6138,14 @@ exports[`regression tests > double click to edit a group > [end of test] appStat "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -6113,6 +6169,7 @@ exports[`regression tests > double click to edit a group > [end of test] appStat "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -6533,12 +6590,14 @@ exports[`regression tests > drags selected elements from point inside common bou "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -6566,6 +6625,7 @@ exports[`regression tests > drags selected elements from point inside common bou "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -6847,12 +6907,14 @@ exports[`regression tests > draw every type of shape > [end of test] appState 1` "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -6874,6 +6936,7 @@ exports[`regression tests > draw every type of shape > [end of test] appState 1` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -8911,12 +8974,14 @@ exports[`regression tests > given a group of selected elements with an element t "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -8943,6 +9008,7 @@ exports[`regression tests > given a group of selected elements with an element t "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -9252,12 +9318,14 @@ exports[`regression tests > given a selected element A and a not selected elemen "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": "elementBackground", "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -9284,6 +9352,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -9492,12 +9561,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -9523,6 +9594,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -9688,12 +9760,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -9719,6 +9793,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -9956,12 +10031,14 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] appStat "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -9985,6 +10062,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] appStat "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -10121,12 +10199,14 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -10150,6 +10230,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -10286,12 +10367,14 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -10315,6 +10398,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -10451,12 +10535,14 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -10503,6 +10589,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -10654,12 +10741,14 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -10706,6 +10795,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -10857,12 +10947,14 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -10884,6 +10976,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11040,12 +11133,14 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11092,6 +11187,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11243,12 +11339,14 @@ exports[`regression tests > key d selects diamond tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11272,6 +11370,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11408,12 +11507,14 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11460,6 +11561,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11611,12 +11713,14 @@ exports[`regression tests > key o selects ellipse tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11640,6 +11744,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11776,12 +11881,14 @@ exports[`regression tests > key p selects freedraw tool > [end of test] appState "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11803,6 +11910,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] appState "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -11959,12 +12067,14 @@ exports[`regression tests > key r selects rectangle tool > [end of test] appStat "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -11988,6 +12098,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] appStat "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -12124,12 +12235,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] appSta "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -12161,6 +12274,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] appSta "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -12785,12 +12899,14 @@ exports[`regression tests > noop interaction after undo shouldn't create history "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -12816,6 +12932,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -13024,12 +13141,14 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` "lastPointerDownWith": "touch", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -13051,6 +13170,7 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -13144,12 +13264,14 @@ exports[`regression tests > shift click on selected element should deselect it o "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -13173,6 +13295,7 @@ exports[`regression tests > shift click on selected element should deselect it o "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -13309,12 +13432,14 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -13342,6 +13467,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -13623,12 +13749,14 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -13658,6 +13786,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -14181,12 +14310,14 @@ exports[`regression tests > should show fill icons when element has non transpar "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": "elementBackground", "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -14210,6 +14341,7 @@ exports[`regression tests > should show fill icons when element has non transpar "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -14389,12 +14521,14 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -14428,6 +14562,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -15242,12 +15377,17 @@ exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] a "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -15269,6 +15409,7 @@ exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] a "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -15362,12 +15503,14 @@ exports[`regression tests > supports nested groups > [end of test] appState 1`] "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -15393,6 +15536,7 @@ exports[`regression tests > supports nested groups > [end of test] appState 1`] "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -16174,12 +16318,14 @@ exports[`regression tests > switches from group of selected elements to another "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -16234,6 +16380,7 @@ exports[`regression tests > switches from group of selected elements to another "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -16571,12 +16718,14 @@ exports[`regression tests > switches selected element on pointer down > [end of "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -16630,6 +16779,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -16838,12 +16988,14 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`] "lastPointerDownWith": "touch", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -16865,6 +17017,7 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`] "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -16958,12 +17111,14 @@ exports[`regression tests > undo/redo drawing an element > [end of test] appStat "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -16987,6 +17142,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] appStat "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -17438,12 +17594,17 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -17465,6 +17626,7 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", @@ -17558,12 +17720,17 @@ exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` "lastPointerDownWith": "mouse", "multiElement": null, "name": "Untitled-201933152653", + "objectsSnapModeEnabled": false, "offsetLeft": 0, "offsetTop": 0, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -17585,6 +17752,7 @@ exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": true, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", diff --git a/src/tests/contextmenu.test.tsx b/src/tests/contextmenu.test.tsx index 92969faafa..2d556d3ac9 100644 --- a/src/tests/contextmenu.test.tsx +++ b/src/tests/contextmenu.test.tsx @@ -87,6 +87,7 @@ describe("contextMenu element", () => { "gridMode", "zenMode", "viewMode", + "objectsSnapMode", "stats", ]; diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index 93ee8758f5..a31f6f046f 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -1048,14 +1048,14 @@ describe("Test Linear Elements", () => { .toMatchInlineSnapshot(` { "height": 130, - "width": 367, + "width": 366.11716195150507, } `); expect(getBoundTextElementPosition(container, textElement)) .toMatchInlineSnapshot(` { - "x": 272, + "x": 271.11716195150507, "y": 45, } `); @@ -1069,9 +1069,9 @@ describe("Test Linear Elements", () => { [ 20, 35, - 502, + 501.11716195150507, 95, - 205.9061448421403, + 205.4589377083102, 52.5, ] `); diff --git a/src/tests/move.test.tsx b/src/tests/move.test.tsx index 5435d7927b..6e2d600831 100644 --- a/src/tests/move.test.tsx +++ b/src/tests/move.test.tsx @@ -84,7 +84,7 @@ describe("move element", () => { // select the second rectangles new Pointer("mouse").clickOn(rectB); - expect(renderInteractiveScene).toHaveBeenCalledTimes(21); + expect(renderInteractiveScene).toHaveBeenCalledTimes(24); expect(renderStaticScene).toHaveBeenCalledTimes(20); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(3); diff --git a/src/tests/multiPointCreate.test.tsx b/src/tests/multiPointCreate.test.tsx index e198b25889..bf23955cce 100644 --- a/src/tests/multiPointCreate.test.tsx +++ b/src/tests/multiPointCreate.test.tsx @@ -110,7 +110,7 @@ describe("multi point mode in linear elements", () => { key: KEYS.ENTER, }); - expect(renderInteractiveScene).toHaveBeenCalledTimes(9); + expect(renderInteractiveScene).toHaveBeenCalledTimes(11); expect(renderStaticScene).toHaveBeenCalledTimes(10); expect(h.elements.length).toEqual(1); @@ -153,8 +153,7 @@ describe("multi point mode in linear elements", () => { fireEvent.keyDown(document, { key: KEYS.ENTER, }); - - expect(renderInteractiveScene).toHaveBeenCalledTimes(9); + expect(renderInteractiveScene).toHaveBeenCalledTimes(11); expect(renderStaticScene).toHaveBeenCalledTimes(10); expect(h.elements.length).toEqual(1); diff --git a/src/tests/packages/__snapshots__/utils.test.ts.snap b/src/tests/packages/__snapshots__/utils.test.ts.snap index 9c573a1a41..2da2f72d99 100644 --- a/src/tests/packages/__snapshots__/utils.test.ts.snap +++ b/src/tests/packages/__snapshots__/utils.test.ts.snap @@ -55,10 +55,15 @@ exports[`exportToSvg > with default arguments 1`] = ` "lastPointerDownWith": "mouse", "multiElement": null, "name": "name", + "objectsSnapModeEnabled": false, "openDialog": null, "openMenu": null, "openPopup": null, "openSidebar": null, + "originSnapOffset": { + "x": 0, + "y": 0, + }, "pasteDialog": { "data": null, "shown": false, @@ -80,6 +85,7 @@ exports[`exportToSvg > with default arguments 1`] = ` "showHyperlinkPopup": false, "showStats": false, "showWelcomeScreen": false, + "snapLines": [], "startBoundElement": null, "suggestedBindings": [], "theme": "light", diff --git a/src/types.ts b/src/types.ts index f07b134097..af82123559 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,6 +34,7 @@ import Library from "./data/library"; import type { FileSystemHandle } from "./data/filesystem"; import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; import { ContextMenuItems } from "./components/ContextMenu"; +import { SnapLine } from "./snapping"; import { Merge, ForwardRef, ValueOf } from "./utility-types"; export type Point = Readonly; @@ -150,6 +151,9 @@ export type InteractiveCanvasAppState = Readonly< showHyperlinkPopup: AppState["showHyperlinkPopup"]; // Collaborators collaborators: AppState["collaborators"]; + // SnapLines + snapLines: AppState["snapLines"]; + zenModeEnabled: AppState["zenModeEnabled"]; } >; @@ -287,6 +291,13 @@ export type AppState = { pendingImageElementId: ExcalidrawImageElement["id"] | null; showHyperlinkPopup: false | "info" | "editor"; selectedLinearElement: LinearElementEditor | null; + + snapLines: SnapLine[]; + originSnapOffset: { + x: number; + y: number; + } | null; + objectsSnapModeEnabled: boolean; }; export type UIAppState = Omit< @@ -400,6 +411,7 @@ export interface ExcalidrawProps { viewModeEnabled?: boolean; zenModeEnabled?: boolean; gridModeEnabled?: boolean; + objectsSnapModeEnabled?: boolean; libraryReturnUrl?: string; theme?: Theme; name?: string; @@ -653,3 +665,10 @@ export type FrameNameBoundsCache = { } >; }; + +export type KeyboardModifiersObject = { + ctrlKey: boolean; + shiftKey: boolean; + altKey: boolean; + metaKey: boolean; +}; From ceb637f5eaa7fb01c2303079880545bb8fa69370 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 29 Sep 2023 15:40:14 +0200 Subject: [PATCH 007/144] fix: elements being dropped/duplicated when added to frame (#7057) --- src/frame.test.tsx | 8 +-- src/frame.ts | 132 +++++++++++++++++++++++---------------------- 2 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/frame.test.tsx b/src/frame.test.tsx index 85417ddf32..75c515d9a4 100644 --- a/src/frame.test.tsx +++ b/src/frame.test.tsx @@ -177,7 +177,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect2, frame]); }); - it("should add elements", async () => { + it.skip("should add elements", async () => { h.elements = [rect2, rect3, frame]; func(frame, rect2); @@ -188,7 +188,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect3, rect2, frame]); }); - it("should add elements when there are other other elements in between", async () => { + it.skip("should add elements when there are other other elements in between", async () => { h.elements = [rect1, rect2, rect4, rect3, frame]; func(frame, rect2); @@ -199,7 +199,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect1, rect4, rect3, rect2, frame]); }); - it("should add elements when there are other elements in between and the order is reversed", async () => { + it.skip("should add elements when there are other elements in between and the order is reversed", async () => { h.elements = [rect3, rect4, rect2, rect1, frame]; func(frame, rect2); @@ -234,7 +234,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect1, rect2, rect3, frame, rect4]); }); - it("should add elements when there are other elements in between and the order is reversed", async () => { + it.skip("should add elements when there are other elements in between and the order is reversed", async () => { h.elements = [rect3, rect4, frame, rect2, rect1]; func(frame, rect2); diff --git a/src/frame.ts b/src/frame.ts index e366ff4304..9ec98b9e9c 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -14,7 +14,7 @@ import { getBoundTextElement, getContainerElement, } from "./element/textElement"; -import { arrayToMap, findIndex } from "./utils"; +import { arrayToMap } from "./utils"; import { mutateElement } from "./element/mutateElement"; import { AppClassProperties, AppState, StaticCanvasAppState } from "./types"; import { getElementsWithinSelection, getSelectedElements } from "./scene"; @@ -457,85 +457,87 @@ export const addElementsToFrame = ( elementsToAdd: NonDeletedExcalidrawElement[], frame: ExcalidrawFrameElement, ) => { - const _elementsToAdd: ExcalidrawElement[] = []; - - for (const element of elementsToAdd) { - _elementsToAdd.push(element); - - const boundTextElement = getBoundTextElement(element); - if (boundTextElement) { - _elementsToAdd.push(boundTextElement); - } - } - - const allElementsIndex = allElements.reduce( - (acc: Record, element, index) => { - acc[element.id] = index; - return acc; - }, - {}, + const currTargetFrameChildrenMap = new Map( + allElements.reduce( + (acc: [ExcalidrawElement["id"], ExcalidrawElement][], element) => { + if (element.frameId === frame.id) { + acc.push([element.id, element]); + } + return acc; + }, + [], + ), ); - const frameIndex = allElementsIndex[frame.id]; - // need to be calculated before the mutation below occurs - const leftFrameBoundaryIndex = findIndex( - allElements, - (e) => e.frameId === frame.id, - ); + const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id)); - const existingFrameChildren = allElements.filter( - (element) => element.frameId === frame.id, - ); - - const addedFrameChildren_left: ExcalidrawElement[] = []; - const addedFrameChildren_right: ExcalidrawElement[] = []; + const finalElementsToAdd: ExcalidrawElement[] = []; + // - add bound text elements if not already in the array + // - filter out elements that are already in the frame for (const element of omitGroupsContainingFrames( allElements, - _elementsToAdd, + elementsToAdd, )) { - if (element.frameId !== frame.id && !isFrameElement(element)) { - if (allElementsIndex[element.id] > frameIndex) { - addedFrameChildren_right.push(element); - } else { - addedFrameChildren_left.push(element); - } + if (!currTargetFrameChildrenMap.has(element.id)) { + finalElementsToAdd.push(element); + } - mutateElement( - element, - { - frameId: frame.id, - }, - false, - ); + const boundTextElement = getBoundTextElement(element); + if ( + boundTextElement && + !suppliedElementsToAddSet.has(boundTextElement.id) && + !currTargetFrameChildrenMap.has(boundTextElement.id) + ) { + finalElementsToAdd.push(boundTextElement); } } - const frameElement = allElements[frameIndex]; - const nextFrameChildren = addedFrameChildren_left - .concat(existingFrameChildren) - .concat(addedFrameChildren_right); + const finalElementsToAddSet = new Set(finalElementsToAdd.map((el) => el.id)); - const nextFrameChildrenMap = nextFrameChildren.reduce( - (acc: Record, element) => { - acc[element.id] = true; - return acc; - }, - {}, - ); + const nextElements: ExcalidrawElement[] = []; - const nextOtherElements_left = allElements - .slice(0, leftFrameBoundaryIndex >= 0 ? leftFrameBoundaryIndex : frameIndex) - .filter((element) => !nextFrameChildrenMap[element.id]); + const processedElements = new Set(); - const nextOtherElement_right = allElements - .slice(frameIndex + 1) - .filter((element) => !nextFrameChildrenMap[element.id]); + for (const element of allElements) { + if (processedElements.has(element.id)) { + continue; + } - const nextElements = nextOtherElements_left - .concat(nextFrameChildren) - .concat([frameElement]) - .concat(nextOtherElement_right); + processedElements.add(element.id); + + if ( + finalElementsToAddSet.has(element.id) || + (element.frameId && element.frameId === frame.id) + ) { + // will be added in bulk once we process target frame + continue; + } + + // target frame + if (element.id === frame.id) { + const currFrameChildren = getFrameElements(allElements, frame.id); + currFrameChildren.forEach((child) => { + processedElements.add(child.id); + }); + // console.log(currFrameChildren, finalElementsToAdd, element); + nextElements.push(...currFrameChildren, ...finalElementsToAdd, element); + continue; + } + + // console.log("(2)", element.frameId); + nextElements.push(element); + } + + for (const element of finalElementsToAdd) { + mutateElement( + element, + { + frameId: frame.id, + }, + false, + ); + } return nextElements; }; From 7d2b6f33742ab3c3028d6ea774f951478384035b Mon Sep 17 00:00:00 2001 From: Alberto Torrigiotti <95320421+atorcode@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:52:53 -0700 Subject: [PATCH 008/144] docs: fix typo on homepage of developer docs (#7047) --- dev-docs/src/components/Homepage/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-docs/src/components/Homepage/index.js b/dev-docs/src/components/Homepage/index.js index b92a294c5a..e350e03a28 100644 --- a/dev-docs/src/components/Homepage/index.js +++ b/dev-docs/src/components/Homepage/index.js @@ -15,7 +15,7 @@ const FeatureList = [ Svg: require("@site/static/img/undraw_blank_canvas.svg").default, description: ( <> - Want to build your own app powered by Excalidraw by don't know where to + Want to build your own app powered by Excalidraw but don't know where to start? ), From 84fd13e872147e9a7a7343adb29960e4239bc6fa Mon Sep 17 00:00:00 2001 From: Tanmoy <89120102+tempewda@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:41:02 +0530 Subject: [PATCH 009/144] docs: fix minor grammar and spellings (#7039) --- dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx | 2 +- dev-docs/docs/@excalidraw/excalidraw/integration.mdx | 2 +- dev-docs/docs/introduction/contributing.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx b/dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx index 7a1d423fc8..9e0f9fcdf9 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx @@ -1,6 +1,6 @@ # Customizing Styles -Excalidraw is using CSS variables to style certain components. To override them, you should set your own on the `.excalidraw` and `.excalidraw.theme--dark` (for dark mode variables) selectors. +Excalidraw uses CSS variables to style certain components. To override them, you should set your own on the `.excalidraw` and `.excalidraw.theme--dark` (for dark mode variables) selectors. Make sure the selector has higher specificity, e.g. by prefixing it with your app's selector: diff --git a/dev-docs/docs/@excalidraw/excalidraw/integration.mdx b/dev-docs/docs/@excalidraw/excalidraw/integration.mdx index 7080c32e95..888f9c5bf3 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/integration.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/integration.mdx @@ -34,7 +34,7 @@ function App() { Since _Excalidraw_ doesn't support server side rendering, you should render the component once the host is `mounted`. -The following worfklow shows one way how to render Excalidraw on Next.js. We'll add more detailed and alternative Next.js examples, soon. +The following workflow shows one way how to render Excalidraw on Next.js. We'll add more detailed and alternative Next.js examples, soon. ```jsx showLineNumbers import { useState, useEffect } from "react"; diff --git a/dev-docs/docs/introduction/contributing.mdx b/dev-docs/docs/introduction/contributing.mdx index 169aa53550..a33cb7a03f 100644 --- a/dev-docs/docs/introduction/contributing.mdx +++ b/dev-docs/docs/introduction/contributing.mdx @@ -15,7 +15,7 @@ In case you want to pick up something from the roadmap, comment on that issue an 1. Run `yarn` to install dependencies 1. Create a branch for your PR with `git checkout -b your-branch-name` -> To keep `master` branch pointing to remote repository and make pull requests from branches on your fork. To do this, run: +> To keep `master` branch pointing to remote repository and make pull requests from branches on your fork, run: > > ```bash > git remote add upstream https://github.com/excalidraw/excalidraw.git From 6a821f3b76cd9b3c5fb034e2224cfe44b2d90d9b Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Tue, 3 Oct 2023 11:07:02 +0200 Subject: [PATCH 010/144] fix: Icon size in manifest (#7073) --- public/manifest.json | 2 +- vite.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/manifest.json b/public/manifest.json index 64f7e5bace..374d426e6e 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -11,7 +11,7 @@ { "src": "apple-touch-icon.png", "type": "image/png", - "sizes": "256x256" + "sizes": "180x180" } ], "start_url": "/", diff --git a/vite.config.ts b/vite.config.ts index 870e21aeb6..8d4528886f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -111,7 +111,7 @@ export default defineConfig({ { src: "apple-touch-icon.png", type: "image/png", - sizes: "256x256", + sizes: "180x180", }, ], start_url: "/", From bfd318e76594a8b7f95891d1d029e08305f42d24 Mon Sep 17 00:00:00 2001 From: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com> Date: Tue, 3 Oct 2023 08:41:13 -0500 Subject: [PATCH 011/144] docs: Update the `excalidraw-app` source-code link in README.md (#7035) chore: Update the `excalidraw-app` source-code link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a3d1c4d61..a88ab9cc87 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ The Excalidraw editor (npm package) supports: ## Excalidraw.com -The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/master/src/excalidraw-app) is part of this repository as well, and the app features: +The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/master/excalidraw-app) is part of this repository as well, and the app features: - 📡 PWA support (works offline). - 🤼 Real-time collaboration. From 12420592efab734f81738d913683b4b5acaf8d45 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 3 Oct 2023 23:35:47 +0200 Subject: [PATCH 012/144] feat: support menu / dropdown items to have `selected` state (#7078) --- .../excalidraw/api/children-components/main-menu.mdx | 2 ++ src/components/Actions.tsx | 4 ++++ src/components/dropdownMenu/DropdownMenu.scss | 5 +++++ src/components/dropdownMenu/DropdownMenuItem.tsx | 4 +++- src/components/dropdownMenu/DropdownMenuItemCustom.tsx | 6 +++++- src/components/dropdownMenu/DropdownMenuItemLink.tsx | 4 +++- src/components/dropdownMenu/common.ts | 9 +++++++-- src/packages/excalidraw/CHANGELOG.md | 6 ++++++ 8 files changed, 35 insertions(+), 5 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx index 2494df108f..77c746b840 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx @@ -38,6 +38,7 @@ To render an item, its recommended to use `MainMenu.Item`. | Prop | Type | Required | Default | Description | | --- | --- | :-: | :-: | --- | | `onSelect` | `function` | Yes | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. | +| `selected` | `boolean` | No | `false` | Whether item is active | | `children` | `React.ReactNode` | Yes | - | The content of the menu item | | `icon` | `JSX.Element` | No | - | The icon used in the menu item | | `shortcut` | `string` | No | - | The shortcut to be shown for the menu item | @@ -70,6 +71,7 @@ function App() { | Prop | Type | Required | Default | Description | | --- | --- | :-: | :-: | --- | | `onSelect` | `function` | No | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. | +| `selected` | `boolean` | No | `false` | Whether item is active | | `href` | `string` | Yes | - | The `href` attribute to be added to the `anchor` element. | | `children` | `React.ReactNode` | Yes | - | The content of the menu item | | `icon` | `JSX.Element` | No | - | The icon used in the menu item | diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index e9483eac14..47c8df4658 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -318,6 +318,7 @@ export const ShapesSwitcher = ({ activeEmbeddable: null, }); }} + selected={activeTool.type === "frame"} /> ) : ( @@ -378,6 +380,7 @@ export const ShapesSwitcher = ({ icon={frameToolIcon} shortcut={KEYS.F.toLocaleUpperCase()} data-testid="toolbar-frame" + selected={activeTool.type === "frame"} > {t("toolBar.frame")} @@ -394,6 +397,7 @@ export const ShapesSwitcher = ({ }} icon={EmbedIcon} data-testid="toolbar-embeddable" + selected={activeTool.type === "embeddable"} > {t("toolBar.embeddable")} diff --git a/src/components/dropdownMenu/DropdownMenu.scss b/src/components/dropdownMenu/DropdownMenu.scss index 6e628736fd..1df90fd4a7 100644 --- a/src/components/dropdownMenu/DropdownMenu.scss +++ b/src/components/dropdownMenu/DropdownMenu.scss @@ -59,6 +59,11 @@ height: 2.25rem; } + &--selected { + background: var(--color-primary-light); + --icon-fill-color: var(--color-primary-darker); + } + &__text { text-overflow: ellipsis; overflow: hidden; diff --git a/src/components/dropdownMenu/DropdownMenuItem.tsx b/src/components/dropdownMenu/DropdownMenuItem.tsx index 5532dbaae2..93108a9f6c 100644 --- a/src/components/dropdownMenu/DropdownMenuItem.tsx +++ b/src/components/dropdownMenu/DropdownMenuItem.tsx @@ -11,12 +11,14 @@ const DropdownMenuItem = ({ children, shortcut, className, + selected, ...rest }: { icon?: JSX.Element; onSelect: (event: Event) => void; children: React.ReactNode; shortcut?: string; + selected?: boolean; className?: string; } & Omit, "onSelect">) => { const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect); @@ -26,7 +28,7 @@ const DropdownMenuItem = ({ {...rest} onClick={handleClick} type="button" - className={getDropdownMenuItemClassName(className)} + className={getDropdownMenuItemClassName(className, selected)} title={rest.title ?? rest["aria-label"]} > diff --git a/src/components/dropdownMenu/DropdownMenuItemCustom.tsx b/src/components/dropdownMenu/DropdownMenuItemCustom.tsx index 323f310925..795c5c7945 100644 --- a/src/components/dropdownMenu/DropdownMenuItemCustom.tsx +++ b/src/components/dropdownMenu/DropdownMenuItemCustom.tsx @@ -3,15 +3,19 @@ import React from "react"; const DropdownMenuItemCustom = ({ children, className = "", + selected, ...rest }: { children: React.ReactNode; className?: string; + selected?: boolean; } & React.HTMLAttributes) => { return (
{children}
diff --git a/src/components/dropdownMenu/DropdownMenuItemLink.tsx b/src/components/dropdownMenu/DropdownMenuItemLink.tsx index c93749ac28..0dacaebf98 100644 --- a/src/components/dropdownMenu/DropdownMenuItemLink.tsx +++ b/src/components/dropdownMenu/DropdownMenuItemLink.tsx @@ -12,6 +12,7 @@ const DropdownMenuItemLink = ({ children, onSelect, className = "", + selected, ...rest }: { href: string; @@ -19,6 +20,7 @@ const DropdownMenuItemLink = ({ children: React.ReactNode; shortcut?: string; className?: string; + selected?: boolean; onSelect?: (event: Event) => void; } & React.AnchorHTMLAttributes) => { const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect); @@ -29,7 +31,7 @@ const DropdownMenuItemLink = ({ href={href} target="_blank" rel="noreferrer" - className={getDropdownMenuItemClassName(className)} + className={getDropdownMenuItemClassName(className, selected)} title={rest.title ?? rest["aria-label"]} onClick={handleClick} > diff --git a/src/components/dropdownMenu/common.ts b/src/components/dropdownMenu/common.ts index 9b5e7bfe99..c59584584e 100644 --- a/src/components/dropdownMenu/common.ts +++ b/src/components/dropdownMenu/common.ts @@ -6,8 +6,13 @@ export const DropdownMenuContentPropsContext = React.createContext<{ onSelect?: (event: Event) => void; }>({}); -export const getDropdownMenuItemClassName = (className = "") => { - return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim(); +export const getDropdownMenuItemClassName = ( + className = "", + selected = false, +) => { + return `dropdown-menu-item dropdown-menu-item-base ${className} ${ + selected ? "dropdown-menu-item--selected" : "" + }`.trim(); }; export const useHandleDropdownMenuItemClick = ( diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index c3e8be9a02..b852757d39 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## Unreleased + +### Features + +- Add `selected` prop for `MainMenu.Item` and `MainMenu.ItemCustom` components to indicate active state. [7078](https://github.com/excalidraw/excalidraw/pull/7078) + ## 0.16.1 (2023-09-21) ## Excalidraw Library From 1f4f5e11aec9d8e1bceb3ece40c6c9d69a3f553e Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 4 Oct 2023 00:16:54 +0200 Subject: [PATCH 013/144] refactor: DRY out and simplify setting active tool from toolbar (#7079) --- src/components/Actions.tsx | 80 +++---------------- src/components/App.tsx | 44 +++++----- src/components/LayerUI.tsx | 2 +- src/components/MobileMenu.tsx | 2 +- .../__snapshots__/contextmenu.test.tsx.snap | 28 +++---- .../regressionTests.test.tsx.snap | 75 +++++++++-------- src/tests/dragCreate.test.tsx | 20 ++--- src/tests/move.test.tsx | 6 +- src/tests/multiPointCreate.test.tsx | 10 +-- src/tests/selection.test.tsx | 10 +-- src/types.ts | 4 +- 11 files changed, 116 insertions(+), 165 deletions(-) diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index 47c8df4658..a176ee5ab6 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -14,13 +14,8 @@ import { hasText, } from "../scene"; import { SHAPES } from "../shapes"; -import { UIAppState, Zoom } from "../types"; -import { - capitalizeString, - isTransparent, - updateActiveTool, - setCursorForShape, -} from "../utils"; +import { AppClassProperties, UIAppState, Zoom } from "../types"; +import { capitalizeString, isTransparent } from "../utils"; import Stack from "./Stack"; import { ToolButton } from "./ToolButton"; import { hasStrokeColor } from "../scene/comparisons"; @@ -215,15 +210,15 @@ export const SelectedShapeActions = ({ export const ShapesSwitcher = ({ interactiveCanvas, activeTool, - setAppState, onImageAction, appState, + app, }: { interactiveCanvas: HTMLCanvasElement | null; activeTool: UIAppState["activeTool"]; - setAppState: React.Component["setState"]; onImageAction: (data: { pointerType: PointerType | null }) => void; appState: UIAppState; + app: AppClassProperties; }) => { const [isExtraToolsMenuOpen, setIsExtraToolsMenuOpen] = useState(false); const device = useDevice(); @@ -251,29 +246,14 @@ export const ShapesSwitcher = ({ data-testid={`toolbar-${value}`} onPointerDown={({ pointerType }) => { if (!appState.penDetected && pointerType === "pen") { - setAppState({ - penDetected: true, - penMode: true, - }); + app.togglePenMode(true); } }} onChange={({ pointerType }) => { if (appState.activeTool.type !== value) { trackEvent("toolbar", value, "ui"); } - const nextActiveTool = updateActiveTool(appState, { - type: value, - }); - setAppState({ - activeTool: nextActiveTool, - activeEmbeddable: null, - multiElement: null, - selectedElementIds: {}, - }); - setCursorForShape(interactiveCanvas, { - ...appState, - activeTool: nextActiveTool, - }); + app.setActiveTool({ type: value }); if (value === "image") { onImageAction({ pointerType }); } @@ -300,23 +280,12 @@ export const ShapesSwitcher = ({ data-testid={`toolbar-frame`} onPointerDown={({ pointerType }) => { if (!appState.penDetected && pointerType === "pen") { - setAppState({ - penDetected: true, - penMode: true, - }); + app.togglePenMode(true); } }} onChange={({ pointerType }) => { trackEvent("toolbar", "frame", "ui"); - const nextActiveTool = updateActiveTool(appState, { - type: "frame", - }); - setAppState({ - activeTool: nextActiveTool, - multiElement: null, - selectedElementIds: {}, - activeEmbeddable: null, - }); + app.setActiveTool({ type: "frame" }); }} selected={activeTool.type === "frame"} /> @@ -331,23 +300,12 @@ export const ShapesSwitcher = ({ data-testid={`toolbar-embeddable`} onPointerDown={({ pointerType }) => { if (!appState.penDetected && pointerType === "pen") { - setAppState({ - penDetected: true, - penMode: true, - }); + app.togglePenMode(true); } }} onChange={({ pointerType }) => { trackEvent("toolbar", "embeddable", "ui"); - const nextActiveTool = updateActiveTool(appState, { - type: "embeddable", - }); - setAppState({ - activeTool: nextActiveTool, - multiElement: null, - selectedElementIds: {}, - activeEmbeddable: null, - }); + app.setActiveTool({ type: "embeddable" }); }} selected={activeTool.type === "embeddable"} /> @@ -368,14 +326,7 @@ export const ShapesSwitcher = ({ > { - const nextActiveTool = updateActiveTool(appState, { - type: "frame", - }); - setAppState({ - activeTool: nextActiveTool, - multiElement: null, - selectedElementIds: {}, - }); + app.setActiveTool({ type: "frame" }); }} icon={frameToolIcon} shortcut={KEYS.F.toLocaleUpperCase()} @@ -386,14 +337,7 @@ export const ShapesSwitcher = ({ { - const nextActiveTool = updateActiveTool(appState, { - type: "embeddable", - }); - setAppState({ - activeTool: nextActiveTool, - multiElement: null, - selectedElementIds: {}, - }); + app.setActiveTool({ type: "embeddable" }); }} icon={EmbedIcon} data-testid="toolbar-embeddable" diff --git a/src/components/App.tsx b/src/components/App.tsx index 3fb4e5e739..b91e0e4b57 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2552,10 +2552,11 @@ class App extends React.Component { }); }; - togglePenMode = () => { + togglePenMode = (force?: boolean) => { this.setState((prevState) => { return { - penMode: !prevState.penMode, + penMode: force ?? !prevState.penMode, + penDetected: true, }; }); }; @@ -3108,7 +3109,7 @@ class App extends React.Component { } }); - private setActiveTool = ( + setActiveTool = ( tool: | { type: @@ -3135,23 +3136,30 @@ class App extends React.Component { if (nextActiveTool.type === "image") { this.onImageAction(); } - if (nextActiveTool.type !== "selection") { - this.setState((prevState) => ({ - activeTool: nextActiveTool, - selectedElementIds: makeNextSelectedElementIds({}, this.state), - selectedGroupIds: {}, - editingGroupId: null, - snapLines: [], - originSnapOffset: null, - })); - } else { - this.setState({ - activeTool: nextActiveTool, - snapLines: [], + + this.setState((prevState) => { + const commonResets = { + snapLines: prevState.snapLines.length ? [] : prevState.snapLines, originSnapOffset: null, activeEmbeddable: null, - }); - } + } as const; + if (nextActiveTool.type !== "selection") { + return { + ...prevState, + activeTool: nextActiveTool, + selectedElementIds: makeNextSelectedElementIds({}, prevState), + selectedGroupIds: makeNextSelectedElementIds({}, prevState), + editingGroupId: null, + multiElement: null, + ...commonResets, + }; + } + return { + ...prevState, + activeTool: nextActiveTool, + ...commonResets, + }; + }); }; private setCursor = (cursor: string) => { diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 3bc8436cc1..d9d3d8ce45 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -279,7 +279,7 @@ const LayerUI = ({ appState={appState} interactiveCanvas={interactiveCanvas} activeTool={appState.activeTool} - setAppState={setAppState} + app={app} onImageAction={({ pointerType }) => { onImageAction({ insertOnCanvasDirectly: pointerType !== "mouse", diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index f08b999691..b7f0ba3338 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -87,7 +87,7 @@ export const MobileMenu = ({ appState={appState} interactiveCanvas={interactiveCanvas} activeTool={appState.activeTool} - setAppState={setAppState} + app={app} onImageAction={({ pointerType }) => { onImageAction({ insertOnCanvasDirectly: pointerType !== "mouse", diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 5f94922255..0cdd20b25e 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -675,7 +675,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e exports[`contextMenu element > selecting 'Add to library' in context menu adds element to library > [end of test] number of elements 1`] = `1`; -exports[`contextMenu element > selecting 'Add to library' in context menu adds element to library > [end of test] number of renders 1`] = `7`; +exports[`contextMenu element > selecting 'Add to library' in context menu adds element to library > [end of test] number of renders 1`] = `6`; exports[`contextMenu element > selecting 'Bring forward' in context menu brings element forward > [end of test] appState 1`] = ` { @@ -1051,7 +1051,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings exports[`contextMenu element > selecting 'Bring forward' in context menu brings element forward > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Bring forward' in context menu brings element forward > [end of test] number of renders 1`] = `13`; +exports[`contextMenu element > selecting 'Bring forward' in context menu brings element forward > [end of test] number of renders 1`] = `12`; exports[`contextMenu element > selecting 'Bring to front' in context menu brings element to front > [end of test] appState 1`] = ` { @@ -1427,7 +1427,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings exports[`contextMenu element > selecting 'Bring to front' in context menu brings element to front > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Bring to front' in context menu brings element to front > [end of test] number of renders 1`] = `13`; +exports[`contextMenu element > selecting 'Bring to front' in context menu brings element to front > [end of test] number of renders 1`] = `12`; exports[`contextMenu element > selecting 'Copy styles' in context menu copies styles > [end of test] appState 1`] = ` { @@ -1629,7 +1629,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st exports[`contextMenu element > selecting 'Copy styles' in context menu copies styles > [end of test] number of elements 1`] = `1`; -exports[`contextMenu element > selecting 'Copy styles' in context menu copies styles > [end of test] number of renders 1`] = `7`; +exports[`contextMenu element > selecting 'Copy styles' in context menu copies styles > [end of test] number of renders 1`] = `6`; exports[`contextMenu element > selecting 'Delete' in context menu deletes element > [end of test] appState 1`] = ` { @@ -1868,7 +1868,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen exports[`contextMenu element > selecting 'Delete' in context menu deletes element > [end of test] number of elements 1`] = `1`; -exports[`contextMenu element > selecting 'Delete' in context menu deletes element > [end of test] number of renders 1`] = `8`; +exports[`contextMenu element > selecting 'Delete' in context menu deletes element > [end of test] number of renders 1`] = `7`; exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates element > [end of test] appState 1`] = ` { @@ -2172,7 +2172,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates element > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates element > [end of test] number of renders 1`] = `8`; +exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates element > [end of test] number of renders 1`] = `7`; exports[`contextMenu element > selecting 'Group selection' in context menu groups selected elements > [end of test] appState 1`] = ` { @@ -2564,7 +2564,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group exports[`contextMenu element > selecting 'Group selection' in context menu groups selected elements > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Group selection' in context menu groups selected elements > [end of test] number of renders 1`] = `13`; +exports[`contextMenu element > selecting 'Group selection' in context menu groups selected elements > [end of test] number of renders 1`] = `12`; exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] appState 1`] = ` { @@ -3446,7 +3446,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of renders 1`] = `20`; +exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of renders 1`] = `19`; exports[`contextMenu element > selecting 'Send backward' in context menu sends element backward > [end of test] appState 1`] = ` { @@ -3822,7 +3822,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e exports[`contextMenu element > selecting 'Send backward' in context menu sends element backward > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Send backward' in context menu sends element backward > [end of test] number of renders 1`] = `12`; +exports[`contextMenu element > selecting 'Send backward' in context menu sends element backward > [end of test] number of renders 1`] = `11`; exports[`contextMenu element > selecting 'Send to back' in context menu sends element to back > [end of test] appState 1`] = ` { @@ -4198,7 +4198,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el exports[`contextMenu element > selecting 'Send to back' in context menu sends element to back > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Send to back' in context menu sends element to back > [end of test] number of renders 1`] = `12`; +exports[`contextMenu element > selecting 'Send to back' in context menu sends element to back > [end of test] number of renders 1`] = `11`; exports[`contextMenu element > selecting 'Ungroup selection' in context menu ungroups selected group > [end of test] appState 1`] = ` { @@ -4657,7 +4657,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung exports[`contextMenu element > selecting 'Ungroup selection' in context menu ungroups selected group > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Ungroup selection' in context menu ungroups selected group > [end of test] number of renders 1`] = `14`; +exports[`contextMenu element > selecting 'Ungroup selection' in context menu ungroups selected group > [end of test] number of renders 1`] = `13`; exports[`contextMenu element > shows 'Group selection' in context menu for multiple selected elements > [end of test] appState 1`] = ` { @@ -5240,7 +5240,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi exports[`contextMenu element > shows 'Group selection' in context menu for multiple selected elements > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > shows 'Group selection' in context menu for multiple selected elements > [end of test] number of renders 1`] = `13`; +exports[`contextMenu element > shows 'Group selection' in context menu for multiple selected elements > [end of test] number of renders 1`] = `12`; exports[`contextMenu element > shows 'Ungroup selection' in context menu for group inside selected elements > [end of test] appState 1`] = ` { @@ -5908,7 +5908,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro exports[`contextMenu element > shows 'Ungroup selection' in context menu for group inside selected elements > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > shows 'Ungroup selection' in context menu for group inside selected elements > [end of test] number of renders 1`] = `14`; +exports[`contextMenu element > shows 'Ungroup selection' in context menu for group inside selected elements > [end of test] number of renders 1`] = `13`; exports[`contextMenu element > shows context menu for canvas > [end of test] appState 1`] = ` { @@ -7104,6 +7104,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] nu exports[`contextMenu element > shows context menu for element > [end of test] number of elements 2`] = `2`; -exports[`contextMenu element > shows context menu for element > [end of test] number of renders 1`] = `7`; +exports[`contextMenu element > shows context menu for element > [end of test] number of renders 1`] = `6`; exports[`contextMenu element > shows context menu for element > [end of test] number of renders 2`] = `6`; diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 32dd175454..c990a6e038 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -454,7 +454,7 @@ exports[`given element A and group of elements B and given both are selected whe exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected > [end of test] number of elements 1`] = `0`; -exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected > [end of test] number of renders 1`] = `22`; +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected > [end of test] number of renders 1`] = `21`; exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected > [end of test] appState 1`] = ` { @@ -912,7 +912,7 @@ exports[`given element A and group of elements B and given both are selected whe exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected > [end of test] number of elements 1`] = `0`; -exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected > [end of test] number of renders 1`] = `20`; +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected > [end of test] number of renders 1`] = `19`; exports[`regression tests > Cmd/Ctrl-click exclusively select element under pointer > [end of test] appState 1`] = ` { @@ -1743,7 +1743,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin exports[`regression tests > Cmd/Ctrl-click exclusively select element under pointer > [end of test] number of elements 1`] = `0`; -exports[`regression tests > Cmd/Ctrl-click exclusively select element under pointer > [end of test] number of renders 1`] = `30`; +exports[`regression tests > Cmd/Ctrl-click exclusively select element under pointer > [end of test] number of renders 1`] = `29`; exports[`regression tests > Drags selected element when hitting only bounding box and keeps element selected > [end of test] appState 1`] = ` { @@ -1956,7 +1956,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo exports[`regression tests > Drags selected element when hitting only bounding box and keeps element selected > [end of test] number of elements 1`] = `0`; -exports[`regression tests > Drags selected element when hitting only bounding box and keeps element selected > [end of test] number of renders 1`] = `10`; +exports[`regression tests > Drags selected element when hitting only bounding box and keeps element selected > [end of test] number of renders 1`] = `9`; exports[`regression tests > adjusts z order when grouping > [end of test] appState 1`] = ` { @@ -2410,7 +2410,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor exports[`regression tests > adjusts z order when grouping > [end of test] number of elements 1`] = `0`; -exports[`regression tests > adjusts z order when grouping > [end of test] number of renders 1`] = `19`; +exports[`regression tests > adjusts z order when grouping > [end of test] number of renders 1`] = `18`; exports[`regression tests > alt-drag duplicates an element > [end of test] appState 1`] = ` { @@ -2652,7 +2652,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo exports[`regression tests > alt-drag duplicates an element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > alt-drag duplicates an element > [end of test] number of renders 1`] = `10`; +exports[`regression tests > alt-drag duplicates an element > [end of test] number of renders 1`] = `9`; exports[`regression tests > arrow keys > [end of test] appState 1`] = ` { @@ -2820,7 +2820,7 @@ exports[`regression tests > arrow keys > [end of test] history 1`] = ` exports[`regression tests > arrow keys > [end of test] number of elements 1`] = `0`; -exports[`regression tests > arrow keys > [end of test] number of renders 1`] = `14`; +exports[`regression tests > arrow keys > [end of test] number of renders 1`] = `13`; exports[`regression tests > can drag element that covers another element, while another elem is selected > [end of test] appState 1`] = ` { @@ -3264,7 +3264,7 @@ exports[`regression tests > can drag element that covers another element, while exports[`regression tests > can drag element that covers another element, while another elem is selected > [end of test] number of elements 1`] = `0`; -exports[`regression tests > can drag element that covers another element, while another elem is selected > [end of test] number of renders 1`] = `19`; +exports[`regression tests > can drag element that covers another element, while another elem is selected > [end of test] number of renders 1`] = `18`; exports[`regression tests > change the properties of a shape > [end of test] appState 1`] = ` { @@ -3561,7 +3561,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his exports[`regression tests > change the properties of a shape > [end of test] number of elements 1`] = `0`; -exports[`regression tests > change the properties of a shape > [end of test] number of renders 1`] = `11`; +exports[`regression tests > change the properties of a shape > [end of test] number of renders 1`] = `10`; exports[`regression tests > click on an element and drag it > [dragged] appState 1`] = ` { @@ -3806,7 +3806,7 @@ exports[`regression tests > click on an element and drag it > [dragged] history exports[`regression tests > click on an element and drag it > [dragged] number of elements 1`] = `1`; -exports[`regression tests > click on an element and drag it > [dragged] number of renders 1`] = `10`; +exports[`regression tests > click on an element and drag it > [dragged] number of renders 1`] = `9`; exports[`regression tests > click on an element and drag it > [end of test] appState 1`] = ` { @@ -4062,7 +4062,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist exports[`regression tests > click on an element and drag it > [end of test] number of elements 1`] = `0`; -exports[`regression tests > click on an element and drag it > [end of test] number of renders 1`] = `12`; +exports[`regression tests > click on an element and drag it > [end of test] number of renders 1`] = `11`; exports[`regression tests > click to select a shape > [end of test] appState 1`] = ` { @@ -4304,7 +4304,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] exports[`regression tests > click to select a shape > [end of test] number of elements 1`] = `0`; -exports[`regression tests > click to select a shape > [end of test] number of renders 1`] = `13`; +exports[`regression tests > click to select a shape > [end of test] number of renders 1`] = `12`; exports[`regression tests > click-drag to select a group > [end of test] appState 1`] = ` { @@ -4648,7 +4648,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history exports[`regression tests > click-drag to select a group > [end of test] number of elements 1`] = `0`; -exports[`regression tests > click-drag to select a group > [end of test] number of renders 1`] = `18`; +exports[`regression tests > click-drag to select a group > [end of test] number of renders 1`] = `17`; exports[`regression tests > deleting last but one element in editing group should unselect the group > [end of test] appState 1`] = ` { @@ -5123,7 +5123,7 @@ exports[`regression tests > deleting last but one element in editing group shoul exports[`regression tests > deleting last but one element in editing group should unselect the group > [end of test] number of elements 1`] = `0`; -exports[`regression tests > deleting last but one element in editing group should unselect the group > [end of test] number of renders 1`] = `20`; +exports[`regression tests > deleting last but one element in editing group should unselect the group > [end of test] number of renders 1`] = `19`; exports[`regression tests > deselects group of selected elements on pointer down when pointer doesn't hit any element > [end of test] appState 1`] = ` { @@ -5420,7 +5420,7 @@ exports[`regression tests > deselects group of selected elements on pointer down exports[`regression tests > deselects group of selected elements on pointer down when pointer doesn't hit any element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > deselects group of selected elements on pointer down when pointer doesn't hit any element > [end of test] number of renders 1`] = `14`; +exports[`regression tests > deselects group of selected elements on pointer down when pointer doesn't hit any element > [end of test] number of renders 1`] = `13`; exports[`regression tests > deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element > [end of test] appState 1`] = ` { @@ -5689,7 +5689,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w exports[`regression tests > deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element > [end of test] number of renders 1`] = `14`; +exports[`regression tests > deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element > [end of test] number of renders 1`] = `13`; exports[`regression tests > deselects selected element on pointer down when pointer doesn't hit any element > [end of test] appState 1`] = ` { @@ -5913,7 +5913,7 @@ exports[`regression tests > deselects selected element on pointer down when poin exports[`regression tests > deselects selected element on pointer down when pointer doesn't hit any element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > deselects selected element on pointer down when pointer doesn't hit any element > [end of test] number of renders 1`] = `9`; +exports[`regression tests > deselects selected element on pointer down when pointer doesn't hit any element > [end of test] number of renders 1`] = `8`; exports[`regression tests > deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element > [end of test] appState 1`] = ` { @@ -6081,7 +6081,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic exports[`regression tests > deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element > [end of test] number of renders 1`] = `9`; +exports[`regression tests > deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element > [end of test] number of renders 1`] = `8`; exports[`regression tests > double click to edit a group > [end of test] appState 1`] = ` { @@ -6533,7 +6533,7 @@ exports[`regression tests > double click to edit a group > [end of test] history exports[`regression tests > double click to edit a group > [end of test] number of elements 1`] = `0`; -exports[`regression tests > double click to edit a group > [end of test] number of renders 1`] = `19`; +exports[`regression tests > double click to edit a group > [end of test] number of renders 1`] = `18`; exports[`regression tests > drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging > [end of test] appState 1`] = ` { @@ -6850,7 +6850,7 @@ exports[`regression tests > drags selected elements from point inside common bou exports[`regression tests > drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging > [end of test] number of elements 1`] = `0`; -exports[`regression tests > drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging > [end of test] number of renders 1`] = `15`; +exports[`regression tests > drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging > [end of test] number of renders 1`] = `14`; exports[`regression tests > draw every type of shape > [end of test] appState 1`] = ` { @@ -8917,7 +8917,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] exports[`regression tests > draw every type of shape > [end of test] number of elements 1`] = `0`; -exports[`regression tests > draw every type of shape > [end of test] number of renders 1`] = `44`; +exports[`regression tests > draw every type of shape > [end of test] number of renders 1`] = `43`; exports[`regression tests > given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up > [end of test] appState 1`] = ` { @@ -9261,7 +9261,7 @@ exports[`regression tests > given a group of selected elements with an element t exports[`regression tests > given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up > [end of test] number of elements 1`] = `0`; -exports[`regression tests > given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up > [end of test] number of renders 1`] = `18`; +exports[`regression tests > given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up > [end of test] number of renders 1`] = `17`; exports[`regression tests > given a selected element A and a not selected element B with higher z-index than A and given B partially overlaps A when there's a shift-click on the overlapped section B is added to the selection > [end of test] appState 1`] = ` { @@ -9504,7 +9504,7 @@ exports[`regression tests > given a selected element A and a not selected elemen exports[`regression tests > given a selected element A and a not selected element B with higher z-index than A and given B partially overlaps A when there's a shift-click on the overlapped section B is added to the selection > [end of test] number of elements 1`] = `0`; -exports[`regression tests > given a selected element A and a not selected element B with higher z-index than A and given B partially overlaps A when there's a shift-click on the overlapped section B is added to the selection > [end of test] number of renders 1`] = `15`; +exports[`regression tests > given a selected element A and a not selected element B with higher z-index than A and given B partially overlaps A when there's a shift-click on the overlapped section B is added to the selection > [end of test] number of renders 1`] = `14`; exports[`regression tests > given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up > [end of test] appState 1`] = ` { @@ -12842,7 +12842,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor exports[`regression tests > make a group and duplicate it > [end of test] number of elements 1`] = `0`; -exports[`regression tests > make a group and duplicate it > [end of test] number of renders 1`] = `21`; +exports[`regression tests > make a group and duplicate it > [end of test] number of renders 1`] = `20`; exports[`regression tests > noop interaction after undo shouldn't create history entry > [end of test] appState 1`] = ` { @@ -13084,7 +13084,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history exports[`regression tests > noop interaction after undo shouldn't create history entry > [end of test] number of elements 1`] = `0`; -exports[`regression tests > noop interaction after undo shouldn't create history entry > [end of test] number of renders 1`] = `17`; +exports[`regression tests > noop interaction after undo shouldn't create history entry > [end of test] number of renders 1`] = `16`; exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` { @@ -13375,7 +13375,7 @@ exports[`regression tests > shift click on selected element should deselect it o exports[`regression tests > shift click on selected element should deselect it on pointer up > [end of test] number of elements 1`] = `0`; -exports[`regression tests > shift click on selected element should deselect it on pointer up > [end of test] number of renders 1`] = `9`; +exports[`regression tests > shift click on selected element should deselect it on pointer up > [end of test] number of renders 1`] = `8`; exports[`regression tests > shift-click to multiselect, then drag > [end of test] appState 1`] = ` { @@ -13692,7 +13692,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test exports[`regression tests > shift-click to multiselect, then drag > [end of test] number of elements 1`] = `0`; -exports[`regression tests > shift-click to multiselect, then drag > [end of test] number of renders 1`] = `16`; +exports[`regression tests > shift-click to multiselect, then drag > [end of test] number of renders 1`] = `15`; exports[`regression tests > should group elements and ungroup them > [end of test] appState 1`] = ` { @@ -14253,7 +14253,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes exports[`regression tests > should group elements and ungroup them > [end of test] number of elements 1`] = `0`; -exports[`regression tests > should group elements and ungroup them > [end of test] number of renders 1`] = `22`; +exports[`regression tests > should group elements and ungroup them > [end of test] number of renders 1`] = `21`; exports[`regression tests > should show fill icons when element has non transparent background > [end of test] appState 1`] = ` { @@ -14464,7 +14464,7 @@ exports[`regression tests > should show fill icons when element has non transpar exports[`regression tests > should show fill icons when element has non transparent background > [end of test] number of elements 1`] = `0`; -exports[`regression tests > should show fill icons when element has non transparent background > [end of test] number of renders 1`] = `10`; +exports[`regression tests > should show fill icons when element has non transparent background > [end of test] number of renders 1`] = `9`; exports[`regression tests > single-clicking on a subgroup of a selected group should not alter selection > [end of test] appState 1`] = ` { @@ -15320,7 +15320,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh exports[`regression tests > single-clicking on a subgroup of a selected group should not alter selection > [end of test] number of elements 1`] = `0`; -exports[`regression tests > single-clicking on a subgroup of a selected group should not alter selection > [end of test] number of renders 1`] = `30`; +exports[`regression tests > single-clicking on a subgroup of a selected group should not alter selection > [end of test] number of renders 1`] = `29`; exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] appState 1`] = ` { @@ -16233,7 +16233,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = exports[`regression tests > supports nested groups > [end of test] number of elements 1`] = `0`; -exports[`regression tests > supports nested groups > [end of test] number of renders 1`] = `27`; +exports[`regression tests > supports nested groups > [end of test] number of renders 1`] = `26`; exports[`regression tests > switches from group of selected elements to another element on pointer down > [end of test] appState 1`] = ` { @@ -16633,7 +16633,7 @@ exports[`regression tests > switches from group of selected elements to another exports[`regression tests > switches from group of selected elements to another element on pointer down > [end of test] number of elements 1`] = `0`; -exports[`regression tests > switches from group of selected elements to another element on pointer down > [end of test] number of renders 1`] = `18`; +exports[`regression tests > switches from group of selected elements to another element on pointer down > [end of test] number of renders 1`] = `17`; exports[`regression tests > switches selected element on pointer down > [end of test] appState 1`] = ` { @@ -16931,7 +16931,7 @@ exports[`regression tests > switches selected element on pointer down > [end of exports[`regression tests > switches selected element on pointer down > [end of test] number of elements 1`] = `0`; -exports[`regression tests > switches selected element on pointer down > [end of test] number of renders 1`] = `13`; +exports[`regression tests > switches selected element on pointer down > [end of test] number of renders 1`] = `12`; exports[`regression tests > two-finger scroll works > [end of test] appState 1`] = ` { @@ -17537,7 +17537,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history exports[`regression tests > undo/redo drawing an element > [end of test] number of elements 1`] = `0`; -exports[`regression tests > undo/redo drawing an element > [end of test] number of renders 1`] = `24`; +exports[`regression tests > undo/redo drawing an element > [end of test] number of renders 1`] = `23`; exports[`regression tests > updates fontSize & fontFamily appState > [end of test] appState 1`] = ` { @@ -17601,10 +17601,7 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "openMenu": null, "openPopup": null, "openSidebar": null, - "originSnapOffset": { - "x": 0, - "y": 0, - }, + "originSnapOffset": null, "pasteDialog": { "data": null, "shown": false, @@ -17663,7 +17660,7 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes exports[`regression tests > updates fontSize & fontFamily appState > [end of test] number of elements 1`] = `0`; -exports[`regression tests > updates fontSize & fontFamily appState > [end of test] number of renders 1`] = `6`; +exports[`regression tests > updates fontSize & fontFamily appState > [end of test] number of renders 1`] = `5`; exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` { diff --git a/src/tests/dragCreate.test.tsx b/src/tests/dragCreate.test.tsx index 6e6c1dbe41..f1808f8738 100644 --- a/src/tests/dragCreate.test.tsx +++ b/src/tests/dragCreate.test.tsx @@ -47,7 +47,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); @@ -79,7 +79,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); @@ -112,7 +112,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); @@ -144,7 +144,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); @@ -180,7 +180,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); @@ -221,7 +221,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(0); }); @@ -241,7 +241,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(0); }); @@ -261,7 +261,7 @@ describe("Test dragCreate", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(0); }); @@ -288,7 +288,7 @@ describe("Test dragCreate", () => { }); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(0); }); @@ -315,7 +315,7 @@ describe("Test dragCreate", () => { }); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(0); }); diff --git a/src/tests/move.test.tsx b/src/tests/move.test.tsx index 6e2d600831..eab21bfba3 100644 --- a/src/tests/move.test.tsx +++ b/src/tests/move.test.tsx @@ -43,7 +43,7 @@ describe("move element", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); @@ -85,7 +85,7 @@ describe("move element", () => { new Pointer("mouse").clickOn(rectB); expect(renderInteractiveScene).toHaveBeenCalledTimes(24); - expect(renderStaticScene).toHaveBeenCalledTimes(20); + expect(renderStaticScene).toHaveBeenCalledTimes(19); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(3); expect(h.state.selectedElementIds[rectB.id]).toBeTruthy(); @@ -131,7 +131,7 @@ describe("duplicate element on move when ALT is clicked", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(6); - expect(renderStaticScene).toHaveBeenCalledTimes(7); + expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); diff --git a/src/tests/multiPointCreate.test.tsx b/src/tests/multiPointCreate.test.tsx index bf23955cce..53da14b12c 100644 --- a/src/tests/multiPointCreate.test.tsx +++ b/src/tests/multiPointCreate.test.tsx @@ -48,7 +48,7 @@ describe("remove shape in non linear elements", () => { fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 }); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.elements.length).toEqual(0); }); @@ -63,7 +63,7 @@ describe("remove shape in non linear elements", () => { fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 }); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.elements.length).toEqual(0); }); @@ -78,7 +78,7 @@ describe("remove shape in non linear elements", () => { fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 }); expect(renderInteractiveScene).toHaveBeenCalledTimes(5); - expect(renderStaticScene).toHaveBeenCalledTimes(6); + expect(renderStaticScene).toHaveBeenCalledTimes(5); expect(h.elements.length).toEqual(0); }); }); @@ -111,7 +111,7 @@ describe("multi point mode in linear elements", () => { }); expect(renderInteractiveScene).toHaveBeenCalledTimes(11); - expect(renderStaticScene).toHaveBeenCalledTimes(10); + expect(renderStaticScene).toHaveBeenCalledTimes(9); expect(h.elements.length).toEqual(1); const element = h.elements[0] as ExcalidrawLinearElement; @@ -154,7 +154,7 @@ describe("multi point mode in linear elements", () => { key: KEYS.ENTER, }); expect(renderInteractiveScene).toHaveBeenCalledTimes(11); - expect(renderStaticScene).toHaveBeenCalledTimes(10); + expect(renderStaticScene).toHaveBeenCalledTimes(9); expect(h.elements.length).toEqual(1); const element = h.elements[0] as ExcalidrawLinearElement; diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx index f6c83fc7a1..4f1c27015a 100644 --- a/src/tests/selection.test.tsx +++ b/src/tests/selection.test.tsx @@ -310,7 +310,7 @@ describe("select single element on the scene", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(9); - expect(renderStaticScene).toHaveBeenCalledTimes(8); + expect(renderStaticScene).toHaveBeenCalledTimes(7); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); @@ -342,7 +342,7 @@ describe("select single element on the scene", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(9); - expect(renderStaticScene).toHaveBeenCalledTimes(8); + expect(renderStaticScene).toHaveBeenCalledTimes(7); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); @@ -374,7 +374,7 @@ describe("select single element on the scene", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(9); - expect(renderStaticScene).toHaveBeenCalledTimes(8); + expect(renderStaticScene).toHaveBeenCalledTimes(7); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); @@ -419,7 +419,7 @@ describe("select single element on the scene", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(9); - expect(renderStaticScene).toHaveBeenCalledTimes(8); + expect(renderStaticScene).toHaveBeenCalledTimes(7); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); @@ -463,7 +463,7 @@ describe("select single element on the scene", () => { fireEvent.pointerUp(canvas); expect(renderInteractiveScene).toHaveBeenCalledTimes(9); - expect(renderStaticScene).toHaveBeenCalledTimes(8); + expect(renderStaticScene).toHaveBeenCalledTimes(7); expect(h.state.selectionElement).toBeNull(); expect(h.elements.length).toEqual(1); expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy(); diff --git a/src/types.ts b/src/types.ts index af82123559..991de9f19d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -292,7 +292,7 @@ export type AppState = { showHyperlinkPopup: false | "info" | "editor"; selectedLinearElement: LinearElementEditor | null; - snapLines: SnapLine[]; + snapLines: readonly SnapLine[]; originSnapOffset: { x: number; y: number; @@ -539,6 +539,8 @@ export type AppClassProperties = { onInsertElements: App["onInsertElements"]; onExportImage: App["onExportImage"]; lastViewportPosition: App["lastViewportPosition"]; + togglePenMode: App["togglePenMode"]; + setActiveTool: App["setActiveTool"]; }; export type PointerDownState = Readonly<{ From 8b838049dfbf6f0aef3e61e51b52e648aa1eba69 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 4 Oct 2023 16:09:59 +0200 Subject: [PATCH 014/144] fix: remove invisible elements safely (#7083) --- src/actions/actionFinalize.tsx | 4 +++- src/components/App.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/actions/actionFinalize.tsx b/src/actions/actionFinalize.tsx index c25e2ef4d0..4d422994c2 100644 --- a/src/actions/actionFinalize.tsx +++ b/src/actions/actionFinalize.tsx @@ -90,7 +90,9 @@ export const actionFinalize = register({ } } if (isInvisiblySmallElement(multiPointElement)) { - newElements = newElements.slice(0, -1); + newElements = newElements.filter( + (el) => el.id !== multiPointElement.id, + ); } // If the multi point line closes the loop, diff --git a/src/components/App.tsx b/src/components/App.tsx index b91e0e4b57..2e7c468eb0 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -6549,7 +6549,9 @@ class App extends React.Component { ) { // remove invisible element which was added in onPointerDown this.scene.replaceAllElements( - this.scene.getElementsIncludingDeleted().slice(0, -1), + this.scene + .getElementsIncludingDeleted() + .filter((el) => el.id !== draggingElement.id), ); this.setState({ draggingElement: null, From fa33aa08abcc929aa1ba52ad01728ea0d1f0a568 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 4 Oct 2023 16:18:22 +0200 Subject: [PATCH 015/144] refactor: refactor event globals to differentiate from `lastPointerUp` (#7084) --- src/components/App.tsx | 62 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 2e7c468eb0..2372f0a229 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -491,8 +491,9 @@ class App extends React.Component { private iFrameRefs = new Map(); hitLinkElement?: NonDeletedExcalidrawElement; - lastPointerDown: React.PointerEvent | null = null; - lastPointerUp: React.PointerEvent | PointerEvent | null = null; + lastPointerDownEvent: React.PointerEvent | null = null; + lastPointerUpEvent: React.PointerEvent | PointerEvent | null = + null; lastViewportPosition = { x: 0, y: 0 }; constructor(props: AppProps) { @@ -3736,10 +3737,10 @@ class App extends React.Component { isTouchScreen: boolean, ) => { const draggedDistance = distance2d( - this.lastPointerDown!.clientX, - this.lastPointerDown!.clientY, - this.lastPointerUp!.clientX, - this.lastPointerUp!.clientY, + this.lastPointerDownEvent!.clientX, + this.lastPointerDownEvent!.clientY, + this.lastPointerUpEvent!.clientX, + this.lastPointerUpEvent!.clientY, ); if ( !this.hitLinkElement || @@ -3750,7 +3751,7 @@ class App extends React.Component { return; } const lastPointerDownCoords = viewportCoordsToSceneCoords( - this.lastPointerDown!, + this.lastPointerDownEvent!, this.state, ); const lastPointerDownHittingLinkIcon = isPointHittingLink( @@ -3760,7 +3761,7 @@ class App extends React.Component { this.device.isMobile, ); const lastPointerUpCoords = viewportCoordsToSceneCoords( - this.lastPointerUp!, + this.lastPointerUpEvent!, this.state, ); const lastPointerUpHittingLinkIcon = isPointHittingLink( @@ -4465,7 +4466,8 @@ class App extends React.Component { return; } - this.lastPointerDown = event; + this.lastPointerDownEvent = event; + this.setState({ lastPointerDownWith: event.pointerType, cursorButton: "down", @@ -4605,14 +4607,14 @@ class App extends React.Component { event: React.PointerEvent, ) => { this.removePointer(event); - this.lastPointerUp = event; + this.lastPointerUpEvent = event; const scenePointer = viewportCoordsToSceneCoords( { clientX: event.clientX, clientY: event.clientY }, this.state, ); const clicklength = - event.timeStamp - (this.lastPointerDown?.timeStamp ?? 0); + event.timeStamp - (this.lastPointerDownEvent?.timeStamp ?? 0); if (this.device.isMobile && clicklength < 300) { const hitElement = this.getElementAtPosition( scenePointer.x, @@ -5366,7 +5368,9 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( sceneX, sceneY, - this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + this.lastPointerDownEvent?.[KEYS.CTRL_OR_CMD] + ? null + : this.state.gridSize, ); const embedLink = getEmbedLink(link); @@ -5416,7 +5420,9 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( sceneX, sceneY, - this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + this.lastPointerDownEvent?.[KEYS.CTRL_OR_CMD] + ? null + : this.state.gridSize, ); const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ @@ -5593,7 +5599,9 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerDownState.origin.x, pointerDownState.origin.y, - this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + this.lastPointerDownEvent?.[KEYS.CTRL_OR_CMD] + ? null + : this.state.gridSize, ); const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ @@ -5651,7 +5659,9 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerDownState.origin.x, pointerDownState.origin.y, - this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + this.lastPointerDownEvent?.[KEYS.CTRL_OR_CMD] + ? null + : this.state.gridSize, ); const frame = newFrameElement({ @@ -6773,17 +6783,17 @@ class App extends React.Component { } if (isEraserActive(this.state)) { const draggedDistance = distance2d( - this.lastPointerDown!.clientX, - this.lastPointerDown!.clientY, - this.lastPointerUp!.clientX, - this.lastPointerUp!.clientY, + this.lastPointerDownEvent!.clientX, + this.lastPointerDownEvent!.clientY, + this.lastPointerUpEvent!.clientX, + this.lastPointerUpEvent!.clientY, ); if (draggedDistance === 0) { const scenePointer = viewportCoordsToSceneCoords( { - clientX: this.lastPointerUp!.clientX, - clientY: this.lastPointerUp!.clientY, + clientX: this.lastPointerUpEvent!.clientX, + clientY: this.lastPointerUpEvent!.clientY, }, this.state, ); @@ -7039,14 +7049,16 @@ class App extends React.Component { if ( hitElement && - this.lastPointerUp && - this.lastPointerDown && - this.lastPointerUp.timeStamp - this.lastPointerDown.timeStamp < 300 && + this.lastPointerUpEvent && + this.lastPointerDownEvent && + this.lastPointerUpEvent.timeStamp - + this.lastPointerDownEvent.timeStamp < + 300 && gesture.pointers.size <= 1 && isEmbeddableElement(hitElement) && this.isEmbeddableCenter( hitElement, - this.lastPointerUp, + this.lastPointerUpEvent, pointerDownState.origin.x, pointerDownState.origin.y, ) From e6f74350ac7d561646475642597b8a27e103bb98 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 4 Oct 2023 23:39:00 +0200 Subject: [PATCH 016/144] refactor: DRY out tool typing (#7086) --- src/components/App.tsx | 10 +++------ src/types.ts | 46 ++++++++++++++++++------------------------ src/utils.ts | 12 +++-------- 3 files changed, 26 insertions(+), 42 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 2372f0a229..8a90595579 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -211,7 +211,7 @@ import { import Scene from "../scene/Scene"; import { RenderInteractiveSceneCallback, ScrollBars } from "../scene/types"; import { getStateForZoom } from "../scene/zoom"; -import { findShapeByKey, SHAPES } from "../shapes"; +import { findShapeByKey } from "../shapes"; import { AppClassProperties, AppProps, @@ -230,6 +230,7 @@ import { SidebarName, SidebarTabName, KeyboardModifiersObject, + ToolType, } from "../types"; import { debounce, @@ -3113,12 +3114,7 @@ class App extends React.Component { setActiveTool = ( tool: | { - type: - | typeof SHAPES[number]["value"] - | "eraser" - | "hand" - | "frame" - | "embeddable"; + type: ToolType; } | { type: "custom"; customType: string }, ) => { diff --git a/src/types.ts b/src/types.ts index 991de9f19d..c3b0252e23 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,7 +18,6 @@ import { ExcalidrawFrameElement, ExcalidrawEmbeddableElement, } from "./element/types"; -import { SHAPES } from "./shapes"; import { Point as RoughPoint } from "roughjs/bin/geometry"; import { LinearElementEditor } from "./element/linearElementEditor"; import { SuggestedBinding } from "./element/binding"; @@ -86,21 +85,30 @@ export type BinaryFileMetadata = Omit; export type BinaryFiles = Record; -export type LastActiveTool = +export type ToolType = + | "selection" + | "rectangle" + | "diamond" + | "ellipse" + | "arrow" + | "line" + | "freedraw" + | "text" + | "image" + | "eraser" + | "hand" + | "frame" + | "embeddable"; + +export type ActiveTool = | { - type: - | typeof SHAPES[number]["value"] - | "eraser" - | "hand" - | "frame" - | "embeddable"; + type: ToolType; customType: null; } | { type: "custom"; customType: string; - } - | null; + }; export type SidebarName = string; export type SidebarTabName = string; @@ -195,23 +203,9 @@ export type AppState = { * indicates a previous tool we should revert back to if we deselect the * currently active tool. At the moment applies to `eraser` and `hand` tool. */ - lastActiveTool: LastActiveTool; + lastActiveTool: ActiveTool | null; locked: boolean; - } & ( - | { - type: - | typeof SHAPES[number]["value"] - | "eraser" - | "hand" - | "frame" - | "embeddable"; - customType: null; - } - | { - type: "custom"; - customType: string; - } - ); + } & ActiveTool; penMode: boolean; penDetected: boolean; exportBackground: boolean; diff --git a/src/utils.ts b/src/utils.ts index 8b142744ab..5acd1b6648 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,9 +15,8 @@ import { FontString, NonDeletedExcalidrawElement, } from "./element/types"; -import { AppState, DataURL, LastActiveTool, Zoom } from "./types"; +import { ActiveTool, AppState, DataURL, ToolType, Zoom } from "./types"; import { unstable_batchedUpdates } from "react-dom"; -import { SHAPES } from "./shapes"; import { isEraserActive, isHandToolActive } from "./appState"; import { ResolutionType } from "./utility-types"; import React from "react"; @@ -371,15 +370,10 @@ export const updateActiveTool = ( appState: Pick, data: ( | { - type: - | typeof SHAPES[number]["value"] - | "eraser" - | "hand" - | "frame" - | "embeddable"; + type: ToolType; } | { type: "custom"; customType: string } - ) & { lastActiveToolBeforeEraser?: LastActiveTool }, + ) & { lastActiveToolBeforeEraser?: ActiveTool | null }, ): AppState["activeTool"] => { if (data.type === "custom") { return { From e921bfb1aef6d3435fa8b209e371e1a399c220df Mon Sep 17 00:00:00 2001 From: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com> Date: Wed, 4 Oct 2023 18:17:22 -0500 Subject: [PATCH 017/144] feat: Export `iconFillColor()` (#6996) --- src/components/icons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 97fc253011..2d06d1073e 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -13,7 +13,7 @@ import clsx from "clsx"; import { Theme } from "../element/types"; import { THEME } from "../constants"; -const iconFillColor = (theme: Theme) => "var(--icon-fill-color)"; +export const iconFillColor = (theme: Theme) => "var(--icon-fill-color)"; const handlerColor = (theme: Theme) => theme === THEME.LIGHT ? oc.white : "#1e1e1e"; From 2e61926a6b0bf01a49db167979ea53a618b0ef2a Mon Sep 17 00:00:00 2001 From: Are Date: Thu, 5 Oct 2023 17:05:16 +0200 Subject: [PATCH 018/144] feat: initial Laser Pointer MVP (#6739) * feat: initial Laser pointer mvp * feat: add laser-pointer package and integrate it with collab * chore: fix yarn.lock * feat: update laser-pointer package, prevent panning from showing * feat: add laser pointer tool button when collaborating, migrate to official package * feat: reduce laser tool button size * update icon * fix icon & rotate * fix: lock zoom level * fix icon * add `selected` state, simplify and reduce api * set up pointer callbacks in viewMode if laser tool active * highlight extra-tools button if one of the nested tools active * add shortcut to laser pointer * feat: don't update paths if nothing changed * ensure we reset flag if no rAF scheduled * move `lastUpdate` to instance to optimize * return early * factor out into constants and add doc * skip iteration instead of exit * fix naming * feat: remove testing variable on window * destroy on editor unmount * fix incorrectly resetting `lastUpdate` in `stop()` --------- Co-authored-by: dwelle --- excalidraw-app/data/index.ts | 2 +- package.json | 1 + src/components/Actions.tsx | 37 ++- src/components/App.tsx | 51 ++- src/components/HelpDialog.tsx | 1 + src/components/LaserTool/LaserPathManager.ts | 293 ++++++++++++++++++ .../LaserTool/LaserPointerButton.tsx | 41 +++ src/components/LaserTool/LaserTool.tsx | 27 ++ .../LaserTool/LaserToolOverlay.scss | 20 ++ src/components/LayerUI.tsx | 21 ++ src/components/ToolIcon.scss | 5 + src/components/Toolbar.scss | 6 + src/components/icons.tsx | 19 ++ src/data/restore.ts | 1 + src/element/showSelectedShapeActions.ts | 3 +- src/locales/en.json | 1 + src/types.ts | 16 +- yarn.lock | 5 + 18 files changed, 531 insertions(+), 19 deletions(-) create mode 100644 src/components/LaserTool/LaserPathManager.ts create mode 100644 src/components/LaserTool/LaserPointerButton.tsx create mode 100644 src/components/LaserTool/LaserTool.tsx create mode 100644 src/components/LaserTool/LaserToolOverlay.scss diff --git a/excalidraw-app/data/index.ts b/excalidraw-app/data/index.ts index 3870ca37c5..4dfb780179 100644 --- a/excalidraw-app/data/index.ts +++ b/excalidraw-app/data/index.ts @@ -107,7 +107,7 @@ export type SocketUpdateDataSource = { type: "MOUSE_LOCATION"; payload: { socketId: string; - pointer: { x: number; y: number }; + pointer: { x: number; y: number; tool: "pointer" | "laser" }; button: "down" | "up"; selectedElementIds: AppState["selectedElementIds"]; username: string; diff --git a/package.json b/package.json index a2a66b5c1a..5ae2d1ff6c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@braintree/sanitize-url": "6.0.2", + "@excalidraw/laser-pointer": "1.2.0", "@excalidraw/random-username": "1.0.0", "@radix-ui/react-popover": "1.0.3", "@radix-ui/react-tabs": "1.0.2", diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index a176ee5ab6..cd5993097e 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -31,7 +31,12 @@ import { import "./Actions.scss"; import DropdownMenu from "./dropdownMenu/DropdownMenu"; -import { EmbedIcon, extraToolsIcon, frameToolIcon } from "./icons"; +import { + EmbedIcon, + extraToolsIcon, + frameToolIcon, + laserPointerToolIcon, +} from "./icons"; import { KEYS } from "../keys"; export const SelectedShapeActions = ({ @@ -222,6 +227,11 @@ export const ShapesSwitcher = ({ }) => { const [isExtraToolsMenuOpen, setIsExtraToolsMenuOpen] = useState(false); const device = useDevice(); + + const frameToolSelected = activeTool.type === "frame"; + const laserToolSelected = activeTool.type === "laser"; + const embeddableToolSelected = activeTool.type === "embeddable"; + return ( <> {SHAPES.map(({ value, icon, key, numericKey, fillable }, index) => { @@ -313,7 +323,15 @@ export const ShapesSwitcher = ({ ) : ( setIsExtraToolsMenuOpen(!isExtraToolsMenuOpen)} title={t("toolBar.extraTools")} > @@ -331,7 +349,7 @@ export const ShapesSwitcher = ({ icon={frameToolIcon} shortcut={KEYS.F.toLocaleUpperCase()} data-testid="toolbar-frame" - selected={activeTool.type === "frame"} + selected={frameToolSelected} > {t("toolBar.frame")} @@ -341,10 +359,21 @@ export const ShapesSwitcher = ({ }} icon={EmbedIcon} data-testid="toolbar-embeddable" - selected={activeTool.type === "embeddable"} + selected={embeddableToolSelected} > {t("toolBar.embeddable")} + { + app.setActiveTool({ type: "laser" }); + }} + icon={laserPointerToolIcon} + data-testid="toolbar-laser" + selected={laserToolSelected} + shortcut={KEYS.K.toLocaleUpperCase()} + > + {t("toolBar.laser")} + )} diff --git a/src/components/App.tsx b/src/components/App.tsx index 8a90595579..737d2bed7f 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -230,6 +230,7 @@ import { SidebarName, SidebarTabName, KeyboardModifiersObject, + CollaboratorPointer, ToolType, } from "../types"; import { @@ -368,6 +369,8 @@ import { isSidebarDockedAtom } from "./Sidebar/Sidebar"; import { StaticCanvas, InteractiveCanvas } from "./canvases"; import { Renderer } from "../scene/Renderer"; import { ShapeCache } from "../scene/ShapeCache"; +import { LaserToolOverlay } from "./LaserTool/LaserTool"; +import { LaserPathManager } from "./LaserTool/LaserPathManager"; const AppContext = React.createContext(null!); const AppPropsContext = React.createContext(null!); @@ -497,6 +500,8 @@ class App extends React.Component { null; lastViewportPosition = { x: 0, y: 0 }; + laserPathManager: LaserPathManager = new LaserPathManager(this); + constructor(props: AppProps) { super(props); const defaultAppState = getDefaultAppState(); @@ -1205,12 +1210,14 @@ class App extends React.Component { !this.scene.getElementsIncludingDeleted().length } app={this} + isCollaborating={this.props.isCollaborating} > {this.props.children}
+ {selectedElements.length === 1 && !this.state.contextMenu && this.state.showHyperlinkPopup && ( @@ -1738,6 +1745,7 @@ class App extends React.Component { this.removeEventListeners(); this.scene.destroy(); this.library.destroy(); + this.laserPathManager.destroy(); ShapeCache.destroy(); SnapCache.destroy(); clearTimeout(touchTimeout); @@ -3052,6 +3060,15 @@ class App extends React.Component { } } + if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) { + if (this.state.activeTool.type === "laser") { + this.setActiveTool({ type: "selection" }); + } else { + this.setActiveTool({ type: "laser" }); + } + return; + } + if ( event[KEYS.CTRL_OR_CMD] && (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) @@ -4462,6 +4479,10 @@ class App extends React.Component { return; } + if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) { + return; + } + this.lastPointerDownEvent = event; this.setState({ @@ -4470,10 +4491,6 @@ class App extends React.Component { }); this.savePointer(event.clientX, event.clientY, "down"); - if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) { - return; - } - // only handle left mouse button or touch if ( event.button !== POINTER_BUTTON.MAIN && @@ -4564,6 +4581,11 @@ class App extends React.Component { setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO); } else if (this.state.activeTool.type === "frame") { this.createFrameElementOnPointerDown(pointerDownState); + } else if (this.state.activeTool.type === "laser") { + this.laserPathManager.startPath( + pointerDownState.lastCoords.x, + pointerDownState.lastCoords.y, + ); } else if ( this.state.activeTool.type !== "eraser" && this.state.activeTool.type !== "hand" @@ -4587,7 +4609,7 @@ class App extends React.Component { lastPointerUp = onPointerUp; - if (!this.state.viewModeEnabled) { + if (!this.state.viewModeEnabled || this.state.activeTool.type === "laser") { window.addEventListener(EVENT.POINTER_MOVE, onPointerMove); window.addEventListener(EVENT.POINTER_UP, onPointerUp); window.addEventListener(EVENT.KEYDOWN, onKeyDown); @@ -5783,6 +5805,10 @@ class App extends React.Component { return; } + if (this.state.activeTool.type === "laser") { + this.laserPathManager.addPointToPath(pointerCoords.x, pointerCoords.y); + } + const [gridX, gridY] = getGridPoint( pointerCoords.x, pointerCoords.y, @@ -7029,6 +7055,11 @@ class App extends React.Component { : unbindLinearElements)(this.scene.getSelectedElements(this.state)); } + if (activeTool.type === "laser") { + this.laserPathManager.endPath(); + return; + } + if (!activeTool.locked && activeTool.type !== "freedraw") { resetCursor(this.interactiveCanvas); this.setState({ @@ -8273,15 +8304,21 @@ class App extends React.Component { if (!x || !y) { return; } - const pointer = viewportCoordsToSceneCoords( + const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords( { clientX: x, clientY: y }, this.state, ); - if (isNaN(pointer.x) || isNaN(pointer.y)) { + if (isNaN(sceneX) || isNaN(sceneY)) { // sometimes the pointer goes off screen } + const pointer: CollaboratorPointer = { + x: sceneX, + y: sceneY, + tool: this.state.activeTool.type === "laser" ? "laser" : "pointer", + }; + this.props.onPointerUpdate?.({ pointer, button, diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 3954839ea5..b27823fc55 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -165,6 +165,7 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { shortcuts={[KEYS.E, KEYS["0"]]} /> + (a + b) / 2; +function getSvgPathFromStroke(points: number[][], closed = true) { + const len = points.length; + + if (len < 4) { + return ``; + } + + let a = points[0]; + let b = points[1]; + const c = points[2]; + + let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed( + 2, + )},${b[1].toFixed(2)} ${average(b[0], c[0]).toFixed(2)},${average( + b[1], + c[1], + ).toFixed(2)} T`; + + for (let i = 2, max = len - 1; i < max; i++) { + a = points[i]; + b = points[i + 1]; + result += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed( + 2, + )} `; + } + + if (closed) { + result += "Z"; + } + + return result; +} + +declare global { + interface Window { + LPM: LaserPathManager; + } +} + +function easeOutCubic(t: number) { + return 1 - Math.pow(1 - t, 3); +} + +function instantiateCollabolatorState(): CollabolatorState { + return { + currentPath: undefined, + finishedPaths: [], + lastPoint: [-10000, -10000], + svg: document.createElementNS("http://www.w3.org/2000/svg", "path"), + }; +} + +function instantiatePath() { + LaserPointer.constants.cornerDetectionMaxAngle = 70; + + return new LaserPointer({ + simplify: 0, + streamline: 0.4, + sizeMapping: (c) => { + const pt = DECAY_TIME; + const pl = DECAY_LENGTH; + const t = Math.max(0, 1 - (performance.now() - c.pressure) / pt); + const l = (pl - Math.min(pl, c.totalLength - c.currentIndex)) / pl; + + return Math.min(easeOutCubic(l), easeOutCubic(t)); + }, + }); +} + +type CollabolatorState = { + currentPath: LaserPointer | undefined; + finishedPaths: LaserPointer[]; + lastPoint: [number, number]; + svg: SVGPathElement; +}; + +export class LaserPathManager { + private ownState: CollabolatorState; + private collaboratorsState: Map = new Map(); + + private rafId: number | undefined; + private lastUpdate = 0; + private container: SVGSVGElement | undefined; + + constructor(private app: App) { + this.ownState = instantiateCollabolatorState(); + } + + destroy() { + this.stop(); + this.lastUpdate = 0; + this.ownState = instantiateCollabolatorState(); + this.collaboratorsState = new Map(); + } + + startPath(x: number, y: number) { + this.ownState.currentPath = instantiatePath(); + this.ownState.currentPath.addPoint([x, y, performance.now()]); + this.updatePath(this.ownState); + } + + addPointToPath(x: number, y: number) { + if (this.ownState.currentPath) { + this.ownState.currentPath?.addPoint([x, y, performance.now()]); + this.updatePath(this.ownState); + } + } + + endPath() { + if (this.ownState.currentPath) { + this.ownState.currentPath.close(); + this.ownState.finishedPaths.push(this.ownState.currentPath); + this.updatePath(this.ownState); + } + } + + private updatePath(state: CollabolatorState) { + this.lastUpdate = performance.now(); + + if (!this.isRunning) { + this.start(); + } + } + + private isRunning = false; + + start(svg?: SVGSVGElement) { + if (svg) { + this.container = svg; + this.container.appendChild(this.ownState.svg); + } + + this.stop(); + this.isRunning = true; + this.loop(); + } + + stop() { + this.isRunning = false; + if (this.rafId) { + cancelAnimationFrame(this.rafId); + } + this.rafId = undefined; + } + + loop() { + this.rafId = requestAnimationFrame(this.loop.bind(this)); + + this.updateCollabolatorsState(); + + if (performance.now() - this.lastUpdate < DECAY_TIME * 2) { + this.update(); + } else { + this.isRunning = false; + } + } + + draw(path: LaserPointer) { + const stroke = path + .getStrokeOutline(path.options.size / this.app.state.zoom.value) + .map(([x, y]) => { + const result = sceneCoordsToViewportCoords( + { sceneX: x, sceneY: y }, + this.app.state, + ); + + return [result.x, result.y]; + }); + + return getSvgPathFromStroke(stroke, true); + } + + updateCollabolatorsState() { + if (!this.container || !this.app.state.collaborators.size) { + return; + } + + for (const [key, collabolator] of this.app.state.collaborators.entries()) { + if (!this.collaboratorsState.has(key)) { + const state = instantiateCollabolatorState(); + this.container.appendChild(state.svg); + this.collaboratorsState.set(key, state); + + this.updatePath(state); + } + + const state = this.collaboratorsState.get(key)!; + + if (collabolator.pointer && collabolator.pointer.tool === "laser") { + if (collabolator.button === "down" && state.currentPath === undefined) { + state.lastPoint = [collabolator.pointer.x, collabolator.pointer.y]; + state.currentPath = instantiatePath(); + state.currentPath.addPoint([ + collabolator.pointer.x, + collabolator.pointer.y, + performance.now(), + ]); + + this.updatePath(state); + } + + if (collabolator.button === "down" && state.currentPath !== undefined) { + if ( + collabolator.pointer.x !== state.lastPoint[0] || + collabolator.pointer.y !== state.lastPoint[1] + ) { + state.lastPoint = [collabolator.pointer.x, collabolator.pointer.y]; + state.currentPath.addPoint([ + collabolator.pointer.x, + collabolator.pointer.y, + performance.now(), + ]); + + this.updatePath(state); + } + } + + if (collabolator.button === "up" && state.currentPath !== undefined) { + state.lastPoint = [collabolator.pointer.x, collabolator.pointer.y]; + state.currentPath.addPoint([ + collabolator.pointer.x, + collabolator.pointer.y, + performance.now(), + ]); + state.currentPath.close(); + + state.finishedPaths.push(state.currentPath); + state.currentPath = undefined; + + this.updatePath(state); + } + } + } + } + + update() { + if (!this.container) { + return; + } + + for (const [key, state] of this.collaboratorsState.entries()) { + if (!this.app.state.collaborators.has(key)) { + state.svg.remove(); + this.collaboratorsState.delete(key); + continue; + } + + state.finishedPaths = state.finishedPaths.filter((path) => { + const lastPoint = path.originalPoints[path.originalPoints.length - 1]; + + return !(lastPoint && lastPoint[2] < performance.now() - DECAY_TIME); + }); + + let paths = state.finishedPaths.map((path) => this.draw(path)).join(" "); + + if (state.currentPath) { + paths += ` ${this.draw(state.currentPath)}`; + } + + state.svg.setAttribute("d", paths); + state.svg.setAttribute("fill", getClientColor(key)); + } + + this.ownState.finishedPaths = this.ownState.finishedPaths.filter((path) => { + const lastPoint = path.originalPoints[path.originalPoints.length - 1]; + + return !(lastPoint && lastPoint[2] < performance.now() - DECAY_TIME); + }); + + let paths = this.ownState.finishedPaths + .map((path) => this.draw(path)) + .join(" "); + + if (this.ownState.currentPath) { + paths += ` ${this.draw(this.ownState.currentPath)}`; + } + + this.ownState.svg.setAttribute("d", paths); + this.ownState.svg.setAttribute("fill", "red"); + } +} diff --git a/src/components/LaserTool/LaserPointerButton.tsx b/src/components/LaserTool/LaserPointerButton.tsx new file mode 100644 index 0000000000..dbb8432938 --- /dev/null +++ b/src/components/LaserTool/LaserPointerButton.tsx @@ -0,0 +1,41 @@ +import "../ToolIcon.scss"; + +import clsx from "clsx"; +import { ToolButtonSize } from "../ToolButton"; +import { laserPointerToolIcon } from "../icons"; + +type LaserPointerIconProps = { + title?: string; + name?: string; + checked: boolean; + onChange?(): void; + isMobile?: boolean; +}; + +const DEFAULT_SIZE: ToolButtonSize = "small"; + +export const LaserPointerButton = (props: LaserPointerIconProps) => { + return ( + + ); +}; diff --git a/src/components/LaserTool/LaserTool.tsx b/src/components/LaserTool/LaserTool.tsx new file mode 100644 index 0000000000..e93d72dfc9 --- /dev/null +++ b/src/components/LaserTool/LaserTool.tsx @@ -0,0 +1,27 @@ +import { useEffect, useRef } from "react"; +import { LaserPathManager } from "./LaserPathManager"; +import "./LaserToolOverlay.scss"; + +type LaserToolOverlayProps = { + manager: LaserPathManager; +}; + +export const LaserToolOverlay = ({ manager }: LaserToolOverlayProps) => { + const svgRef = useRef(null); + + useEffect(() => { + if (svgRef.current) { + manager.start(svgRef.current); + } + + return () => { + manager.stop(); + }; + }, [manager]); + + return ( +
+ +
+ ); +}; diff --git a/src/components/LaserTool/LaserToolOverlay.scss b/src/components/LaserTool/LaserToolOverlay.scss new file mode 100644 index 0000000000..da874b452f --- /dev/null +++ b/src/components/LaserTool/LaserToolOverlay.scss @@ -0,0 +1,20 @@ +.excalidraw { + .LaserToolOverlay { + pointer-events: none; + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + + z-index: 2; + + .LaserToolOverlayCanvas { + image-rendering: auto; + overflow: visible; + position: absolute; + top: 0; + left: 0; + } + } +} diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index d9d3d8ce45..59ac60a763 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -55,6 +55,7 @@ import "./Toolbar.scss"; import { mutateElement } from "../element/mutateElement"; import { ShapeCache } from "../scene/ShapeCache"; import Scene from "../scene/Scene"; +import { LaserPointerButton } from "./LaserTool/LaserPointerButton"; interface LayerUIProps { actionManager: ActionManager; @@ -77,6 +78,7 @@ interface LayerUIProps { renderWelcomeScreen: boolean; children?: React.ReactNode; app: AppClassProperties; + isCollaborating: boolean; } const DefaultMainMenu: React.FC<{ @@ -134,6 +136,7 @@ const LayerUI = ({ renderWelcomeScreen, children, app, + isCollaborating, }: LayerUIProps) => { const device = useDevice(); const tunnels = useInitializeTunnels(); @@ -288,6 +291,24 @@ const LayerUI = ({ /> + {isCollaborating && ( + + + app.setActiveTool({ type: "laser" }) + } + isMobile + /> + + )}
diff --git a/src/components/ToolIcon.scss b/src/components/ToolIcon.scss index 994ee6ba5f..066f26d61d 100644 --- a/src/components/ToolIcon.scss +++ b/src/components/ToolIcon.scss @@ -170,5 +170,10 @@ height: var(--lg-icon-size); } } + + .ToolIcon__LaserPointer .ToolIcon__icon { + width: var(--default-button-size); + height: var(--default-button-size); + } } } diff --git a/src/components/Toolbar.scss b/src/components/Toolbar.scss index 4bd20f7b3e..aee50a1446 100644 --- a/src/components/Toolbar.scss +++ b/src/components/Toolbar.scss @@ -28,6 +28,12 @@ box-shadow: 0 0 0 1px var(--button-active-border, var(--color-primary-darkest)) inset; } + + &--selected, + &--selected:hover { + background: var(--color-primary-light); + color: var(--color-primary); + } } .App-toolbar__extra-tools-dropdown { diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 2d06d1073e..87059ce617 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -1653,3 +1653,22 @@ export const frameToolIcon = createIcon( , tablerIconProps, ); + +export const laserPointerToolIcon = createIcon( + + + + , + + 20, +); diff --git a/src/data/restore.ts b/src/data/restore.ts index 9316cfe49c..bda6818f6f 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -67,6 +67,7 @@ export const AllowedExcalidrawActiveTools: Record< frame: true, embeddable: true, hand: true, + laser: false, }; export type RestoredDataState = { diff --git a/src/element/showSelectedShapeActions.ts b/src/element/showSelectedShapeActions.ts index cc42ec8d01..1fd47f6834 100644 --- a/src/element/showSelectedShapeActions.ts +++ b/src/element/showSelectedShapeActions.ts @@ -12,6 +12,7 @@ export const showSelectedShapeActions = ( (appState.editingElement || (appState.activeTool.type !== "selection" && appState.activeTool.type !== "eraser" && - appState.activeTool.type !== "hand"))) || + appState.activeTool.type !== "hand" && + appState.activeTool.type !== "laser"))) || getSelectedElements(elements, appState).length), ); diff --git a/src/locales/en.json b/src/locales/en.json index c3a041f579..f2e6b601a0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -236,6 +236,7 @@ "eraser": "Eraser", "frame": "Frame tool", "embeddable": "Web Embed", + "laser": "Laser pointer", "hand": "Hand (panning tool)", "extraTools": "More tools" }, diff --git a/src/types.ts b/src/types.ts index c3b0252e23..8b05ba40a7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,10 +39,7 @@ import { Merge, ForwardRef, ValueOf } from "./utility-types"; export type Point = Readonly; export type Collaborator = { - pointer?: { - x: number; - y: number; - }; + pointer?: CollaboratorPointer; button?: "up" | "down"; selectedElementIds?: AppState["selectedElementIds"]; username?: string | null; @@ -58,6 +55,12 @@ export type Collaborator = { id?: string; }; +export type CollaboratorPointer = { + x: number; + y: number; + tool: "pointer" | "laser"; +}; + export type DataURL = string & { _brand: "DataURL" }; export type BinaryFileData = { @@ -98,7 +101,8 @@ export type ToolType = | "eraser" | "hand" | "frame" - | "embeddable"; + | "embeddable" + | "laser"; export type ActiveTool = | { @@ -389,7 +393,7 @@ export interface ExcalidrawProps { excalidrawRef?: ForwardRef; isCollaborating?: boolean; onPointerUpdate?: (payload: { - pointer: { x: number; y: number }; + pointer: { x: number; y: number; tool: "pointer" | "laser" }; button: "down" | "up"; pointersMap: Gesture["pointers"]; }) => void; diff --git a/yarn.lock b/yarn.lock index 166532f4e2..8022634e61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1522,6 +1522,11 @@ resolved "https://registry.yarnpkg.com/@excalidraw/eslint-config/-/eslint-config-1.0.3.tgz#2122ef7413ae77874ae9848ce0f1c6b3f0d8bbbd" integrity sha512-GemHNF5Z6ga0BWBSX7GJaNBUchLu6RwTcAB84eX1MeckRNhNasAsPCdelDlFalz27iS4RuYEQh0bPE8SRxJgbQ== +"@excalidraw/laser-pointer@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@excalidraw/laser-pointer/-/laser-pointer-1.2.0.tgz#cd34ea7d24b11743c726488cc1fcb28c161cacba" + integrity sha512-WjFFwLk9ahmKRKku7U0jqYpeM3fe9ZS1K43pfwPREHk4/FYU3iKDKVeS8m4tEAASnRlBt3hhLCBQLBF2uvgOnw== + "@excalidraw/prettier-config@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@excalidraw/prettier-config/-/prettier-config-1.0.2.tgz#b7c061c99cee2f78b9ca470ea1fbd602683bba65" From a249f332a23aa4e902e06a9e41202b70588e9b6f Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 6 Oct 2023 12:00:35 +0200 Subject: [PATCH 019/144] fix: ensure we do not stop laser update prematurely (#7100) --- src/components/LaserTool/LaserPathManager.ts | 24 ++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/LaserTool/LaserPathManager.ts b/src/components/LaserTool/LaserPathManager.ts index b36d7816c8..2f0c63955f 100644 --- a/src/components/LaserTool/LaserPathManager.ts +++ b/src/components/LaserTool/LaserPathManager.ts @@ -91,7 +91,7 @@ export class LaserPathManager { private collaboratorsState: Map = new Map(); private rafId: number | undefined; - private lastUpdate = 0; + private isDrawing = false; private container: SVGSVGElement | undefined; constructor(private app: App) { @@ -100,7 +100,7 @@ export class LaserPathManager { destroy() { this.stop(); - this.lastUpdate = 0; + this.isDrawing = false; this.ownState = instantiateCollabolatorState(); this.collaboratorsState = new Map(); } @@ -127,7 +127,7 @@ export class LaserPathManager { } private updatePath(state: CollabolatorState) { - this.lastUpdate = performance.now(); + this.isDrawing = true; if (!this.isRunning) { this.start(); @@ -160,7 +160,7 @@ export class LaserPathManager { this.updateCollabolatorsState(); - if (performance.now() - this.lastUpdate < DECAY_TIME * 2) { + if (this.isDrawing) { this.update(); } else { this.isRunning = false; @@ -250,6 +250,8 @@ export class LaserPathManager { return; } + let somePathsExist = false; + for (const [key, state] of this.collaboratorsState.entries()) { if (!this.app.state.collaborators.has(key)) { state.svg.remove(); @@ -269,6 +271,10 @@ export class LaserPathManager { paths += ` ${this.draw(state.currentPath)}`; } + if (paths.trim()) { + somePathsExist = true; + } + state.svg.setAttribute("d", paths); state.svg.setAttribute("fill", getClientColor(key)); } @@ -287,7 +293,17 @@ export class LaserPathManager { paths += ` ${this.draw(this.ownState.currentPath)}`; } + paths = paths.trim(); + + if (paths) { + somePathsExist = true; + } + this.ownState.svg.setAttribute("d", paths); this.ownState.svg.setAttribute("fill", "red"); + + if (!somePathsExist) { + this.isDrawing = false; + } } } From 03da9112cfaa08c2ad7616558355cfd538f940f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Ferdman Date: Mon, 9 Oct 2023 03:37:17 +0300 Subject: [PATCH 020/144] fix: update links to excalidraw-app (#7072) --- dev-docs/docs/@excalidraw/excalidraw/faq.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/faq.mdx b/dev-docs/docs/@excalidraw/excalidraw/faq.mdx index 4684d6c791..4274972ab2 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/faq.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/faq.mdx @@ -2,7 +2,7 @@ ### Does this package support collaboration ? -No, Excalidraw package doesn't come with collaboration built in, since the implementation is specific to each host app. We expose APIs which you can use to communicate with Excalidraw which you can use to implement it. You can check our own implementation [here](https://github.com/excalidraw/excalidraw/blob/master/src/excalidraw-app/index.tsx). Here is a [detailed answer](https://github.com/excalidraw/excalidraw/discussions/3879#discussioncomment-1110524) on how you can achieve the same. +No, Excalidraw package doesn't come with collaboration built in, since the implementation is specific to each host app. We expose APIs which you can use to communicate with Excalidraw which you can use to implement it. You can check our own implementation [here](https://github.com/excalidraw/excalidraw/blob/master/excalidraw-app/index.tsx). Here is a [detailed answer](https://github.com/excalidraw/excalidraw/discussions/3879#discussioncomment-1110524) on how you can achieve the same. ### Turning off Aggressive Anti-Fingerprinting in Brave browser @@ -18,7 +18,7 @@ We strongly recommend turning it off. You can follow the steps below on how to d 2. Once opened, look for **Aggressively Block Fingerprinting** -![Aggresive block fingerprinting](../../assets/aggressive-block-fingerprint.png) +![Aggressive block fingerprinting](../../assets/aggressive-block-fingerprint.png) 3. Switch to **Block Fingerprinting** From f20ba90ffa2b3cf6c38844dd4bc6c8d816927f3b Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Mon, 9 Oct 2023 16:32:27 +0800 Subject: [PATCH 021/144] perf: improve element in frame check (#7124) --- src/frame.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/frame.ts b/src/frame.ts index 9ec98b9e9c..1da9cfa102 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -707,6 +707,17 @@ export const isElementInFrame = ( : element; if (frame) { + // Perf improvement: + // For an element that's already in a frame, if it's not being dragged + // then there is no need to refer to geometry (which, yes, is slow) to check if it's in a frame. + // It has to be in its containing frame. + if ( + !appState.selectedElementIds[element.id] || + !appState.selectedElementsAreBeingDragged + ) { + return true; + } + if (_element.groupIds.length === 0) { return elementOverlapsWithFrame(_element, frame); } From 4ea079eb85a65ab06baac6b4a69be69cce98fdb0 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Mon, 9 Oct 2023 12:26:49 +0200 Subject: [PATCH 022/144] fix: regression from #6739 preventing redirect link in view mode (#7120) Co-authored-by: dwelle --- src/components/App.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 737d2bed7f..d38fa630a8 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -4479,12 +4479,15 @@ class App extends React.Component { return; } + this.lastPointerDownEvent = event; + + // we must exit before we set `cursorButton` state and `savePointer` + // else it will send pointer state & laser pointer events in collab when + // panning if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) { return; } - this.lastPointerDownEvent = event; - this.setState({ lastPointerDownWith: event.pointerType, cursorButton: "down", From 2523fe82e3df7c7267c6026d971e6972055f8734 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 10 Oct 2023 13:55:55 +0200 Subject: [PATCH 023/144] feat: laser pointer improvements (#7128) --- src/components/App.tsx | 3 +++ src/utils.ts | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/components/App.tsx b/src/components/App.tsx index d38fa630a8..f2c58165ba 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1155,6 +1155,9 @@ class App extends React.Component { this.state.selectionElement || this.state.draggingElement || this.state.resizingElement || + (this.state.activeTool.type === "laser" && + // technically we can just test on this once we make it more safe + this.state.cursorButton === "down") || (this.state.editingElement && !isTextElement(this.state.editingElement)) ? POINTER_EVENTS.disabled diff --git a/src/utils.ts b/src/utils.ts index 5acd1b6648..65dfe1402c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -21,6 +21,14 @@ import { isEraserActive, isHandToolActive } from "./appState"; import { ResolutionType } from "./utility-types"; import React from "react"; +const laserPointerCursorSVG = ` + +`; + +const laserPointerCursorDataURL = `data:${MIME_TYPES.svg},${encodeURIComponent( + `${laserPointerCursorSVG}`, +)}`; + let mockDateTime: string | null = null; export const setDateTimeForTests = (dateTime: string) => { @@ -467,6 +475,9 @@ export const setCursorForShape = ( // do nothing if image tool is selected which suggests there's // a image-preview set as the cursor // Ignore custom type as well and let host decide + } else if (appState.activeTool.type === "laser") { + const url = laserPointerCursorDataURL; + interactiveCanvas.style.cursor = `url(${url}), auto`; } else if (!["image", "custom"].includes(appState.activeTool.type)) { interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR; } From 7ad02c359ae64bae28eb5b59b783c5d858cd12bc Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 10 Oct 2023 23:31:23 +0200 Subject: [PATCH 024/144] fix: memoize static canvas on `props.renderConfig` (#7131) --- src/components/canvases/StaticCanvas.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/canvases/StaticCanvas.tsx b/src/components/canvases/StaticCanvas.tsx index dfdf8b51e1..38b9baade5 100644 --- a/src/components/canvases/StaticCanvas.tsx +++ b/src/components/canvases/StaticCanvas.tsx @@ -114,11 +114,13 @@ const areEqual = ( return false; } - return isShallowEqual( - // asserting AppState because we're being passed the whole AppState - // but resolve to only the StaticCanvas-relevant props - getRelevantAppStateProps(prevProps.appState as AppState), - getRelevantAppStateProps(nextProps.appState as AppState), + return ( + isShallowEqual( + // asserting AppState because we're being passed the whole AppState + // but resolve to only the StaticCanvas-relevant props + getRelevantAppStateProps(prevProps.appState as AppState), + getRelevantAppStateProps(nextProps.appState as AppState), + ) && isShallowEqual(prevProps.renderConfig, nextProps.renderConfig) ); }; From 26ff3993bb0600cc3dbd77a8468820394a2204fc Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 11 Oct 2023 11:17:27 +0200 Subject: [PATCH 025/144] feat: better laser cursor for dark mode (#7132) --- src/actions/actionCanvas.tsx | 3 +- src/actions/actionFinalize.tsx | 3 +- src/actions/actionFrame.ts | 3 +- src/components/App.tsx | 10 ++-- src/cursor.ts | 103 +++++++++++++++++++++++++++++++++ src/element/embeddable.ts | 3 +- src/utils.ts | 96 +----------------------------- 7 files changed, 118 insertions(+), 103 deletions(-) create mode 100644 src/cursor.ts diff --git a/src/actions/actionCanvas.tsx b/src/actions/actionCanvas.tsx index 6531203ee6..2ba584bc1d 100644 --- a/src/actions/actionCanvas.tsx +++ b/src/actions/actionCanvas.tsx @@ -10,7 +10,7 @@ import { getNormalizedZoom } from "../scene"; import { centerScrollOn } from "../scene/scroll"; import { getStateForZoom } from "../scene/zoom"; import { AppState, NormalizedZoomValue } from "../types"; -import { getShortcutKey, setCursor, updateActiveTool } from "../utils"; +import { getShortcutKey, updateActiveTool } from "../utils"; import { register } from "./register"; import { Tooltip } from "../components/Tooltip"; import { newElementWith } from "../element/mutateElement"; @@ -21,6 +21,7 @@ import { } from "../appState"; import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; import { Bounds } from "../element/bounds"; +import { setCursor } from "../cursor"; export const actionChangeViewBackgroundColor = register({ name: "changeViewBackgroundColor", diff --git a/src/actions/actionFinalize.tsx b/src/actions/actionFinalize.tsx index 4d422994c2..a7c34c5ac5 100644 --- a/src/actions/actionFinalize.tsx +++ b/src/actions/actionFinalize.tsx @@ -1,6 +1,6 @@ import { KEYS } from "../keys"; import { isInvisiblySmallElement } from "../element"; -import { updateActiveTool, resetCursor } from "../utils"; +import { updateActiveTool } from "../utils"; import { ToolButton } from "../components/ToolButton"; import { done } from "../components/icons"; import { t } from "../i18n"; @@ -15,6 +15,7 @@ import { } from "../element/binding"; import { isBindingElement, isLinearElement } from "../element/typeChecks"; import { AppState } from "../types"; +import { resetCursor } from "../cursor"; export const actionFinalize = register({ name: "finalize", diff --git a/src/actions/actionFrame.ts b/src/actions/actionFrame.ts index 339545f877..1266920ea7 100644 --- a/src/actions/actionFrame.ts +++ b/src/actions/actionFrame.ts @@ -4,7 +4,8 @@ import { removeAllElementsFromFrame } from "../frame"; import { getFrameElements } from "../frame"; import { KEYS } from "../keys"; import { AppClassProperties, AppState } from "../types"; -import { setCursorForShape, updateActiveTool } from "../utils"; +import { updateActiveTool } from "../utils"; +import { setCursorForShape } from "../cursor"; import { register } from "./register"; const isSingleFrameSelected = (appState: AppState, app: AppClassProperties) => { diff --git a/src/components/App.tsx b/src/components/App.tsx index f2c58165ba..4ad7b889e9 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -241,18 +241,14 @@ import { isInputLike, isToolIcon, isWritableElement, - resetCursor, resolvablePromise, sceneCoordsToViewportCoords, - setCursor, - setCursorForShape, tupleToCoors, viewportCoordsToSceneCoords, withBatchedUpdates, wrapEvent, withBatchedUpdatesThrottled, updateObject, - setEraserCursor, updateActiveTool, getShortcutKey, isTransparent, @@ -371,6 +367,12 @@ import { Renderer } from "../scene/Renderer"; import { ShapeCache } from "../scene/ShapeCache"; import { LaserToolOverlay } from "./LaserTool/LaserTool"; import { LaserPathManager } from "./LaserTool/LaserPathManager"; +import { + setEraserCursor, + setCursor, + resetCursor, + setCursorForShape, +} from "../cursor"; const AppContext = React.createContext(null!); const AppPropsContext = React.createContext(null!); diff --git a/src/cursor.ts b/src/cursor.ts new file mode 100644 index 0000000000..364ce155c8 --- /dev/null +++ b/src/cursor.ts @@ -0,0 +1,103 @@ +import { CURSOR_TYPE, MIME_TYPES, THEME } from "./constants"; +import OpenColor from "open-color"; +import { AppState, DataURL } from "./types"; +import { isHandToolActive, isEraserActive } from "./appState"; + +const laserPointerCursorSVG_tag = ``; +const laserPointerCursorBackgroundSVG = ``; +const laserPointerCursorIconSVG = ``; + +const laserPointerCursorDataURL_lightMode = `data:${ + MIME_TYPES.svg +},${encodeURIComponent( + `${laserPointerCursorSVG_tag}${laserPointerCursorIconSVG}`, +)}`; +const laserPointerCursorDataURL_darkMode = `data:${ + MIME_TYPES.svg +},${encodeURIComponent( + `${laserPointerCursorSVG_tag}${laserPointerCursorBackgroundSVG}${laserPointerCursorIconSVG}`, +)}`; + +export const resetCursor = (interactiveCanvas: HTMLCanvasElement | null) => { + if (interactiveCanvas) { + interactiveCanvas.style.cursor = ""; + } +}; + +export const setCursor = ( + interactiveCanvas: HTMLCanvasElement | null, + cursor: string, +) => { + if (interactiveCanvas) { + interactiveCanvas.style.cursor = cursor; + } +}; + +let eraserCanvasCache: any; +let previewDataURL: string; +export const setEraserCursor = ( + interactiveCanvas: HTMLCanvasElement | null, + theme: AppState["theme"], +) => { + const cursorImageSizePx = 20; + + const drawCanvas = () => { + const isDarkTheme = theme === THEME.DARK; + eraserCanvasCache = document.createElement("canvas"); + eraserCanvasCache.theme = theme; + eraserCanvasCache.height = cursorImageSizePx; + eraserCanvasCache.width = cursorImageSizePx; + const context = eraserCanvasCache.getContext("2d")!; + context.lineWidth = 1; + context.beginPath(); + context.arc( + eraserCanvasCache.width / 2, + eraserCanvasCache.height / 2, + 5, + 0, + 2 * Math.PI, + ); + context.fillStyle = isDarkTheme ? OpenColor.black : OpenColor.white; + context.fill(); + context.strokeStyle = isDarkTheme ? OpenColor.white : OpenColor.black; + context.stroke(); + previewDataURL = eraserCanvasCache.toDataURL(MIME_TYPES.svg) as DataURL; + }; + if (!eraserCanvasCache || eraserCanvasCache.theme !== theme) { + drawCanvas(); + } + + setCursor( + interactiveCanvas, + `url(${previewDataURL}) ${cursorImageSizePx / 2} ${ + cursorImageSizePx / 2 + }, auto`, + ); +}; + +export const setCursorForShape = ( + interactiveCanvas: HTMLCanvasElement | null, + appState: Pick, +) => { + if (!interactiveCanvas) { + return; + } + if (appState.activeTool.type === "selection") { + resetCursor(interactiveCanvas); + } else if (isHandToolActive(appState)) { + interactiveCanvas.style.cursor = CURSOR_TYPE.GRAB; + } else if (isEraserActive(appState)) { + setEraserCursor(interactiveCanvas, appState.theme); + // do nothing if image tool is selected which suggests there's + // a image-preview set as the cursor + // Ignore custom type as well and let host decide + } else if (appState.activeTool.type === "laser") { + const url = + appState.theme === THEME.LIGHT + ? laserPointerCursorDataURL_lightMode + : laserPointerCursorDataURL_darkMode; + interactiveCanvas.style.cursor = `url(${url}), auto`; + } else if (!["image", "custom"].includes(appState.activeTool.type)) { + interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR; + } +}; diff --git a/src/element/embeddable.ts b/src/element/embeddable.ts index 80585bc72c..4aa6f0fdc0 100644 --- a/src/element/embeddable.ts +++ b/src/element/embeddable.ts @@ -2,7 +2,8 @@ import { register } from "../actions/register"; import { FONT_FAMILY, VERTICAL_ALIGN } from "../constants"; import { t } from "../i18n"; import { ExcalidrawProps } from "../types"; -import { getFontString, setCursorForShape, updateActiveTool } from "../utils"; +import { getFontString, updateActiveTool } from "../utils"; +import { setCursorForShape } from "../cursor"; import { newTextElement } from "./newElement"; import { getContainerElement, wrapText } from "./textElement"; import { isEmbeddableElement } from "./typeChecks"; diff --git a/src/utils.ts b/src/utils.ts index 65dfe1402c..f95139200f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,13 +1,9 @@ -import oc from "open-color"; import { COLOR_PALETTE } from "./colors"; import { - CURSOR_TYPE, DEFAULT_VERSION, EVENT, FONT_FAMILY, isDarwin, - MIME_TYPES, - THEME, WINDOWS_EMOJI_FALLBACK_FONT, } from "./constants"; import { @@ -15,20 +11,11 @@ import { FontString, NonDeletedExcalidrawElement, } from "./element/types"; -import { ActiveTool, AppState, DataURL, ToolType, Zoom } from "./types"; +import { ActiveTool, AppState, ToolType, Zoom } from "./types"; import { unstable_batchedUpdates } from "react-dom"; -import { isEraserActive, isHandToolActive } from "./appState"; import { ResolutionType } from "./utility-types"; import React from "react"; -const laserPointerCursorSVG = ` - -`; - -const laserPointerCursorDataURL = `data:${MIME_TYPES.svg},${encodeURIComponent( - `${laserPointerCursorSVG}`, -)}`; - let mockDateTime: string | null = null; export const setDateTimeForTests = (dateTime: string) => { @@ -402,87 +389,6 @@ export const updateActiveTool = ( }; }; -export const resetCursor = (interactiveCanvas: HTMLCanvasElement | null) => { - if (interactiveCanvas) { - interactiveCanvas.style.cursor = ""; - } -}; - -export const setCursor = ( - interactiveCanvas: HTMLCanvasElement | null, - cursor: string, -) => { - if (interactiveCanvas) { - interactiveCanvas.style.cursor = cursor; - } -}; - -let eraserCanvasCache: any; -let previewDataURL: string; -export const setEraserCursor = ( - interactiveCanvas: HTMLCanvasElement | null, - theme: AppState["theme"], -) => { - const cursorImageSizePx = 20; - - const drawCanvas = () => { - const isDarkTheme = theme === THEME.DARK; - eraserCanvasCache = document.createElement("canvas"); - eraserCanvasCache.theme = theme; - eraserCanvasCache.height = cursorImageSizePx; - eraserCanvasCache.width = cursorImageSizePx; - const context = eraserCanvasCache.getContext("2d")!; - context.lineWidth = 1; - context.beginPath(); - context.arc( - eraserCanvasCache.width / 2, - eraserCanvasCache.height / 2, - 5, - 0, - 2 * Math.PI, - ); - context.fillStyle = isDarkTheme ? oc.black : oc.white; - context.fill(); - context.strokeStyle = isDarkTheme ? oc.white : oc.black; - context.stroke(); - previewDataURL = eraserCanvasCache.toDataURL(MIME_TYPES.svg) as DataURL; - }; - if (!eraserCanvasCache || eraserCanvasCache.theme !== theme) { - drawCanvas(); - } - - setCursor( - interactiveCanvas, - `url(${previewDataURL}) ${cursorImageSizePx / 2} ${ - cursorImageSizePx / 2 - }, auto`, - ); -}; - -export const setCursorForShape = ( - interactiveCanvas: HTMLCanvasElement | null, - appState: Pick, -) => { - if (!interactiveCanvas) { - return; - } - if (appState.activeTool.type === "selection") { - resetCursor(interactiveCanvas); - } else if (isHandToolActive(appState)) { - interactiveCanvas.style.cursor = CURSOR_TYPE.GRAB; - } else if (isEraserActive(appState)) { - setEraserCursor(interactiveCanvas, appState.theme); - // do nothing if image tool is selected which suggests there's - // a image-preview set as the cursor - // Ignore custom type as well and let host decide - } else if (appState.activeTool.type === "laser") { - const url = laserPointerCursorDataURL; - interactiveCanvas.style.cursor = `url(${url}), auto`; - } else if (!["image", "custom"].includes(appState.activeTool.type)) { - interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR; - } -}; - export const isFullScreen = () => document.fullscreenElement?.nodeName === "HTML"; From b55216692476132cb1556af7be269eb76048ad96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20Moln=C3=A1r?= <38168628+barnabasmolnar@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:58:33 +0200 Subject: [PATCH 026/144] feat: new dark mode theme & light theme tweaks (#7104) Co-authored-by: dwelle --- src/components/Actions.scss | 4 +- src/components/FilledButton.scss | 18 ++--- src/components/HelpDialog.scss | 17 ++++- src/components/LibraryMenu.scss | 4 +- src/components/RadioGroup.scss | 25 +++---- src/components/Sidebar/SidebarTrigger.scss | 3 +- src/components/Switch.scss | 30 ++++---- src/components/TextField.scss | 33 ++++----- src/components/ToolIcon.scss | 5 -- src/components/Toolbar.scss | 1 + src/components/dropdownMenu/DropdownMenu.scss | 44 ++++++++---- .../LiveCollaborationTrigger.scss | 2 + src/components/main-menu/MainMenu.tsx | 1 + .../welcome-screen/WelcomeScreen.scss | 11 ++- src/css/styles.scss | 19 +++++- src/css/theme.scss | 68 ++++++++++++------- src/css/variables.module.scss | 40 ++++++++--- 17 files changed, 197 insertions(+), 128 deletions(-) diff --git a/src/components/Actions.scss b/src/components/Actions.scss index 925e918d28..df0d73755d 100644 --- a/src/components/Actions.scss +++ b/src/components/Actions.scss @@ -2,13 +2,13 @@ .undo-redo-buttons { background-color: var(--island-bg-color); border-radius: var(--border-radius-lg); + box-shadow: 0 0 0 1px var(--color-surface-lowest); } .zoom-button, .undo-redo-buttons button { - border: 1px solid var(--default-border-color) !important; border-radius: 0 !important; - background-color: transparent !important; + background-color: var(--color-surface-low) !important; font-size: 0.875rem !important; width: var(--lg-button-size); height: var(--lg-button-size); diff --git a/src/components/FilledButton.scss b/src/components/FilledButton.scss index e171ff8d0b..c0a57f2729 100644 --- a/src/components/FilledButton.scss +++ b/src/components/FilledButton.scss @@ -12,32 +12,32 @@ &--color-primary { &.ExcButton--variant-filled { - --text-color: var(--input-bg-color); + --text-color: var(--color-surface-lowest); --back-color: var(--color-primary); &:hover { - --back-color: var(--color-primary-darker); + --back-color: var(--color-brand-hover); } &:active { - --back-color: var(--color-primary-darkest); + --back-color: var(--color-brand-active); } } &.ExcButton--variant-outlined, &.ExcButton--variant-icon { --text-color: var(--color-primary); - --border-color: var(--color-primary); - --back-color: var(--input-bg-color); + --border-color: var(--color-border-outline); + --back-color: transparent; &:hover { - --text-color: var(--color-primary-darker); - --border-color: var(--color-primary-darker); + --text-color: var(--color-brand-hover); + --border-color: var(--color-brand-hover); } &:active { - --text-color: var(--color-primary-darkest); - --border-color: var(--color-primary-darkest); + --text-color: var(--color-brand-active); + --border-color: var(--color-brand-active); } } } diff --git a/src/components/HelpDialog.scss b/src/components/HelpDialog.scss index 8a0a08f3c4..0722c97239 100644 --- a/src/components/HelpDialog.scss +++ b/src/components/HelpDialog.scss @@ -19,20 +19,35 @@ } &__btn { + --background: var(--color-surface-mid); + display: flex; column-gap: 0.5rem; align-items: center; - border: 1px solid var(--default-border-color); + background-color: var(--background); padding: 0.625rem 1rem; + border: 1px solid var(--background); border-radius: var(--border-radius-lg); color: var(--text-primary-color); font-weight: 600; font-size: 0.75rem; letter-spacing: 0.4px; + @at-root .excalidraw.theme--dark#{&} { + --background: var(--color-surface-high); + &:hover { + --background: #363541; + } + } + &:hover { + --background: var(--color-surface-high); text-decoration: none; } + + &:active { + border-color: var(--color-primary); + } } &__link-icon { diff --git a/src/components/LibraryMenu.scss b/src/components/LibraryMenu.scss index 16715614ab..1de8aee183 100644 --- a/src/components/LibraryMenu.scss +++ b/src/components/LibraryMenu.scss @@ -99,10 +99,10 @@ font-size: 0.75rem; &:hover { - background-color: var(--color-primary-darker); + background-color: var(--color-brand-hover); } &:active { - background-color: var(--color-primary-darkest); + background-color: var(--color-brand-active); } } diff --git a/src/components/RadioGroup.scss b/src/components/RadioGroup.scss index 8606482693..eb70f4a82a 100644 --- a/src/components/RadioGroup.scss +++ b/src/components/RadioGroup.scss @@ -1,27 +1,18 @@ @import "../css/variables.module"; .excalidraw { - --RadioGroup-background: #ffffff; - --RadioGroup-border: var(--color-gray-30); + --RadioGroup-background: var(--island-bg-color); + --RadioGroup-border: var(--color-surface-high); --RadioGroup-choice-color-off: var(--color-primary); - --RadioGroup-choice-color-off-hover: var(--color-primary-darkest); - --RadioGroup-choice-background-off: white; - --RadioGroup-choice-background-off-active: var(--color-gray-20); + --RadioGroup-choice-color-off-hover: var(--color-brand-hover); + --RadioGroup-choice-background-off: var(--island-bg-color); + --RadioGroup-choice-background-off-active: var(--color-surface-high); - --RadioGroup-choice-color-on: white; + --RadioGroup-choice-color-on: var(--color-surface-lowest); --RadioGroup-choice-background-on: var(--color-primary); - --RadioGroup-choice-background-on-hover: var(--color-primary-darker); - --RadioGroup-choice-background-on-active: var(--color-primary-darkest); - - &.theme--dark { - --RadioGroup-background: var(--color-gray-85); - --RadioGroup-border: var(--color-gray-70); - - --RadioGroup-choice-background-off: var(--color-gray-85); - --RadioGroup-choice-background-off-active: var(--color-gray-70); - --RadioGroup-choice-color-on: var(--color-gray-85); - } + --RadioGroup-choice-background-on-hover: var(--color-brand-hover); + --RadioGroup-choice-background-on-active: var(--color-brand-active); .RadioGroup { box-sizing: border-box; diff --git a/src/components/Sidebar/SidebarTrigger.scss b/src/components/Sidebar/SidebarTrigger.scss index 7c0669c4d5..834df65635 100644 --- a/src/components/Sidebar/SidebarTrigger.scss +++ b/src/components/Sidebar/SidebarTrigger.scss @@ -3,8 +3,7 @@ .excalidraw { .sidebar-trigger { @include outlineButtonStyles; - - background-color: var(--island-bg-color); + @include filledButtonOnCanvas; width: auto; height: var(--lg-button-size); diff --git a/src/components/Switch.scss b/src/components/Switch.scss index ab98bad639..ca3c03718d 100644 --- a/src/components/Switch.scss +++ b/src/components/Switch.scss @@ -1,15 +1,13 @@ @import "../css/variables.module"; .excalidraw { - --Switch-disabled-color: #d6d6d6; - --Switch-track-background: white; - --Switch-thumb-background: #3d3d3d; - - &.theme--dark { - --Switch-disabled-color: #5c5c5c; - --Switch-track-background: #242424; - --Switch-thumb-background: #b8b8b8; - } + --Switch-disabled-color: var(--color-border-outline); + --Switch-disabled-toggled-background: var(--color-border-outline-variant); + --Switch-disabled-border: var(--color-border-outline-variant); + --Switch-track-background: var(--island-bg-color); + --Switch-thumb-background: var(--color-on-surface); + --Switch-hover-background: var(--color-brand-hover); + --Switch-active-background: var(--color-brand-active); .Switch { position: relative; @@ -28,7 +26,11 @@ &:hover { background: var(--Switch-track-background); - border: 1px solid #999999; + border: 1px solid var(--Switch-hover-background); + } + + &:active { + border: 1px solid var(--Switch-active-background); } &.toggled { @@ -43,11 +45,11 @@ &.disabled { background: var(--Switch-track-background); - border: 1px solid var(--Switch-disabled-color); + border: 1px solid var(--Switch-disabled-border); &.toggled { - background: var(--Switch-disabled-color); - border: 1px solid var(--Switch-disabled-color); + background: var(--Switch-disabled-toggled-background); + border: 1px solid var(--Switch-disabled-toggled-background); } } @@ -92,7 +94,7 @@ } &.disabled.toggled:before { - background: var(--color-gray-50); + background: var(--Switch-disabled-color); } & input { diff --git a/src/components/TextField.scss b/src/components/TextField.scss index 57093996ca..74518d218f 100644 --- a/src/components/TextField.scss +++ b/src/components/TextField.scss @@ -1,25 +1,16 @@ @import "../css/variables.module"; .excalidraw { - --ExcTextField--color: var(--color-gray-80); - --ExcTextField--label-color: var(--color-gray-80); - --ExcTextField--background: white; - --ExcTextField--readonly--background: var(--color-gray-10); - --ExcTextField--readonly--color: var(--color-gray-80); - --ExcTextField--border: var(--color-gray-40); - --ExcTextField--border-hover: var(--color-gray-50); - --ExcTextField--placeholder: var(--color-gray-40); - - &.theme--dark { - --ExcTextField--color: var(--color-gray-10); - --ExcTextField--label-color: var(--color-gray-20); - --ExcTextField--background: var(--color-gray-85); - --ExcTextField--readonly--background: var(--color-gray-80); - --ExcTextField--readonly--color: var(--color-gray-40); - --ExcTextField--border: var(--color-gray-70); - --ExcTextField--border-hover: var(--color-gray-60); - --ExcTextField--placeholder: var(--color-gray-80); - } + --ExcTextField--color: var(--color-on-surface); + --ExcTextField--label-color: var(--color-on-surface); + --ExcTextField--background: transparent; + --ExcTextField--readonly--background: var(--color-surface-high); + --ExcTextField--readonly--color: var(--color-on-surface); + --ExcTextField--border: var(--color-border-outline); + --ExcTextField--readonly--border: var(--color-border-outline-variant); + --ExcTextField--border-hover: var(--color-brand-hover); + --ExcTextField--border-active: var(--color-brand-active); + --ExcTextField--placeholder: var(--color-border-outline-variant); .ExcTextField { &--fullWidth { @@ -61,7 +52,7 @@ &:active, &:focus-within { - border-color: var(--color-primary); + border-color: var(--ExcTextField--border-active); } } @@ -107,7 +98,7 @@ &--readonly { background: var(--ExcTextField--readonly--background); - border-color: transparent; + border-color: var(--ExcTextField--readonly--border); & input { color: var(--ExcTextField--readonly--color); diff --git a/src/components/ToolIcon.scss b/src/components/ToolIcon.scss index 066f26d61d..6ebd31cb04 100644 --- a/src/components/ToolIcon.scss +++ b/src/components/ToolIcon.scss @@ -97,10 +97,6 @@ } } - // &:hover { - // background-color: var(--button-gray-2); - // } - &:active { background-color: var(--button-gray-3); } @@ -110,7 +106,6 @@ } &--hide { - // visibility: hidden; display: none !important; } } diff --git a/src/components/Toolbar.scss b/src/components/Toolbar.scss index aee50a1446..fa123248dd 100644 --- a/src/components/Toolbar.scss +++ b/src/components/Toolbar.scss @@ -22,6 +22,7 @@ .App-toolbar__extra-tools-trigger { box-shadow: none; border: 0; + background-color: transparent; &:active { background-color: var(--button-hover-bg); diff --git a/src/components/dropdownMenu/DropdownMenu.scss b/src/components/dropdownMenu/DropdownMenu.scss index 1df90fd4a7..0ab1eb0465 100644 --- a/src/components/dropdownMenu/DropdownMenu.scss +++ b/src/components/dropdownMenu/DropdownMenu.scss @@ -16,7 +16,7 @@ .dropdown-menu-container { padding: 8px 8px; box-sizing: border-box; - background-color: var(--island-bg-color); + // background-color: var(--island-bg-color); box-shadow: var(--shadow-island); border-radius: var(--border-radius-lg); position: relative; @@ -29,7 +29,7 @@ } .dropdown-menu-container { - background-color: #fff !important; + background-color: var(--island-bg-color); max-height: calc(100vh - 150px); overflow-y: auto; --gap: 2; @@ -40,7 +40,7 @@ padding: 0 0.625rem; column-gap: 0.625rem; font-size: 0.875rem; - color: var(--color-gray-100); + color: var(--color-on-surface); width: 100%; box-sizing: border-box; font-weight: normal; @@ -49,7 +49,7 @@ .dropdown-menu-item { background-color: transparent; - border: 0; + border: 1px solid transparent; align-items: center; height: 2rem; cursor: pointer; @@ -80,6 +80,11 @@ text-decoration: none; } + &:active { + background-color: var(--button-hover-bg); + border-color: var(--color-brand-active); + } + svg { width: 1rem; height: 1rem; @@ -98,22 +103,33 @@ font-weight: 500; } } - &.theme--dark { - .dropdown-menu-item { - color: var(--color-gray-40); - } - - .dropdown-menu-container { - background-color: var(--color-gray-90) !important; - } - } .dropdown-menu-button { @include outlineButtonStyles; - background-color: var(--island-bg-color); width: var(--lg-button-size); height: var(--lg-button-size); + --background: var(--color-surface-mid); + + background-color: var(--background); + + @at-root .excalidraw.theme--dark#{&} { + --background: var(--color-surface-high); + &:hover { + --background: #363541; + } + } + + &:hover { + --background: var(--color-surface-high); + background-color: var(--background); + text-decoration: none; + } + + &:active { + border-color: var(--color-primary); + } + svg { width: var(--lg-icon-size); height: var(--lg-icon-size); diff --git a/src/components/live-collaboration/LiveCollaborationTrigger.scss b/src/components/live-collaboration/LiveCollaborationTrigger.scss index 138d6459b2..763193341a 100644 --- a/src/components/live-collaboration/LiveCollaborationTrigger.scss +++ b/src/components/live-collaboration/LiveCollaborationTrigger.scss @@ -14,6 +14,8 @@ --button-active-bg: var(--color-primary-darker); + box-shadow: 0 0 0 1px var(--color-surface-lowest); + flex-shrink: 0; // double .active to force specificity diff --git a/src/components/main-menu/MainMenu.tsx b/src/components/main-menu/MainMenu.tsx index aa2454083f..6d6ba5ef14 100644 --- a/src/components/main-menu/MainMenu.tsx +++ b/src/components/main-menu/MainMenu.tsx @@ -43,6 +43,7 @@ const MainMenu = Object.assign( }); }} data-testid="main-menu-trigger" + className="main-menu-trigger" > {HamburgerMenuIcon} diff --git a/src/components/welcome-screen/WelcomeScreen.scss b/src/components/welcome-screen/WelcomeScreen.scss index 8f1a09bf31..ef526aba41 100644 --- a/src/components/welcome-screen/WelcomeScreen.scss +++ b/src/components/welcome-screen/WelcomeScreen.scss @@ -174,7 +174,7 @@ justify-content: space-between; background: none; - border: none; + border: 1px solid transparent; padding: 0.75rem; @@ -204,7 +204,7 @@ .welcome-screen-menu-item:hover { text-decoration: none; - background: var(--color-gray-10); + background: var(--button-hover-bg); .welcome-screen-menu-item__shortcut { color: var(--color-gray-50); @@ -216,7 +216,8 @@ } .welcome-screen-menu-item:active { - background: var(--color-gray-20); + background: var(--button-hover-bg); + border-color: var(--color-brand-active); .welcome-screen-menu-item__shortcut { color: var(--color-gray-50); @@ -247,8 +248,7 @@ } .welcome-screen-menu-item:hover { - background: var(--color-gray-85); - + background-color: var(--color-surface-low); .welcome-screen-menu-item__shortcut { color: var(--color-gray-50); } @@ -259,7 +259,6 @@ } .welcome-screen-menu-item:active { - background-color: var(--color-gray-90); .welcome-screen-menu-item__text { color: var(--color-gray-10); } diff --git a/src/css/styles.scss b/src/css/styles.scss index 4ca58af4f7..d355387173 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -444,13 +444,14 @@ } &:active { - border: 1px solid var(--color-primary-darkest); + border: 1px solid var(--button-active-border); } } .help-icon { @include outlineButtonStyles; - background-color: var(--island-bg-color); + @include filledButtonOnCanvas; + width: var(--lg-button-size); height: var(--lg-button-size); @@ -621,6 +622,20 @@ padding: 0; } } + + .main-menu-trigger { + @include filledButtonOnCanvas; + } + + .App-menu__left { + --button-border: transparent; + --button-bg: var(--color-surface-mid); + + @at-root .excalidraw.theme--dark#{&} { + --button-hover-bg: #363541; + --button-bg: var(--color-surface-high); + } + } } .ErrorSplash.excalidraw { diff --git a/src/css/theme.scss b/src/css/theme.scss index 1db22313ae..4fb8bf81f7 100644 --- a/src/css/theme.scss +++ b/src/css/theme.scss @@ -12,27 +12,30 @@ --dialog-border-color: var(--color-gray-20); --dropdown-icon: url('data:image/svg+xml,'); --focus-highlight-color: #{$oc-blue-2}; - --icon-fill-color: var(--color-gray-80); + --icon-fill-color: var(--color-on-surface); --icon-green-fill-color: #{$oc-green-9}; --default-bg-color: #{$oc-white}; --input-bg-color: #{$oc-white}; --input-border-color: #{$oc-gray-4}; --input-hover-bg-color: #{$oc-gray-1}; --input-label-color: #{$oc-gray-7}; - --island-bg-color: rgba(255, 255, 255, 0.96); + --island-bg-color: #ffffff; --keybinding-color: var(--color-gray-40); --link-color: #{$oc-blue-7}; --overlay-bg-color: #{transparentize($oc-white, 0.12)}; - --popup-bg-color: #{$oc-white}; + --popup-bg-color: var(--island-bg-color); --popup-secondary-bg-color: #{$oc-gray-1}; --popup-text-color: #{$oc-black}; --popup-text-inverted-color: #{$oc-white}; --select-highlight-color: #{$oc-blue-5}; - --shadow-island: 0px 7px 14px rgba(0, 0, 0, 0.05), - 0px 0px 3.12708px rgba(0, 0, 0, 0.0798), - 0px 0px 0.931014px rgba(0, 0, 0, 0.1702); - --button-hover-bg: var(--color-gray-10); - --default-border-color: var(--color-gray-30); + --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), + 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), + 0px 7px 14px 0px rgba(0, 0, 0, 0.05); + + --button-hover-bg: var(--color-surface-high); + --button-active-bg: var(--color-surface-high); + --button-active-border: var(--color-brand-active); + --default-border-color: var(--color-surface-high); --default-button-size: 2rem; --default-icon-size: 1rem; @@ -63,14 +66,14 @@ 0px 12.5216px 10.0172px rgba(0, 0, 0, 0.035), 0px 6.6501px 5.32008px rgba(0, 0, 0, 0.0282725), 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802); - --sidebar-border-color: var(--color-gray-20); - --sidebar-bg-color: #fff; + --sidebar-border-color: var(--color-surface-high); + --sidebar-bg-color: var(--island-bg-color); --library-dropdown-shadow: 0px 15px 6px rgba(0, 0, 0, 0.01), 0px 8px 5px rgba(0, 0, 0, 0.05), 0px 4px 4px rgba(0, 0, 0, 0.09), 0px 1px 2px rgba(0, 0, 0, 0.1), 0px 0px 0px rgba(0, 0, 0, 0.1); --space-factor: 0.25rem; - --text-primary-color: var(--color-gray-80); + --text-primary-color: var(--color-on-surface); --color-selection: #6965db; @@ -132,6 +135,19 @@ --border-radius-md: 0.375rem; --border-radius-lg: 0.5rem; + --color-surface-high: hsl(244, 100%, 97%); + --color-surface-mid: hsl(240 25% 96%); + --color-surface-low: hsl(240 25% 94%); + --color-surface-lowest: #ffffff; + --color-on-surface: #1b1b1f; + --color-brand-hover: #5753d0; + --color-on-primary-container: #030064; + --color-surface-primary-container: #e0dfff; + --color-brand-active: #4440bf; + --color-border-outline: #767680; + --color-border-outline-variant: #c5c5d0; + --color-surface-primary-container: #e0dfff; + &.theme--dark { &.theme--dark-background-none { background: none; @@ -150,29 +166,24 @@ --dialog-border-color: var(--color-gray-80); --dropdown-icon: url('data:image/svg+xml,'); --focus-highlight-color: #{$oc-blue-6}; - --icon-fill-color: var(--color-gray-40); --icon-green-fill-color: #{$oc-green-4}; --default-bg-color: #121212; --input-bg-color: #121212; --input-border-color: #2e2e2e; --input-hover-bg-color: #181818; --input-label-color: #{$oc-gray-2}; - --island-bg-color: #262627; + --island-bg-color: #232329; --keybinding-color: var(--color-gray-60); --link-color: #{$oc-blue-4}; --overlay-bg-color: #{transparentize($oc-gray-8, 0.88)}; - --popup-bg-color: #2c2c2c; --popup-secondary-bg-color: #222; --popup-text-color: #{$oc-gray-4}; --popup-text-inverted-color: #2c2c2c; --select-highlight-color: #{$oc-blue-4}; - --text-primary-color: var(--color-gray-40); - --button-hover-bg: var(--color-gray-80); - --default-border-color: var(--color-gray-80); - --shadow-island: 0px 13px 33px rgba(0, 0, 0, 0.07), - 0px 4.13px 9.94853px rgba(0, 0, 0, 0.0456112), - 0px 1.13px 4.13211px rgba(0, 0, 0, 0.035), - 0px 0.769896px 1.4945px rgba(0, 0, 0, 0.0243888); + --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), + 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), + 0px 7px 14px 0px rgba(0, 0, 0, 0.05); + --modal-shadow: 0px 100px 80px rgba(0, 0, 0, 0.07), 0px 41.7776px 33.4221px rgba(0, 0, 0, 0.0503198), 0px 22.3363px 17.869px rgba(0, 0, 0, 0.0417275), @@ -180,8 +191,6 @@ 0px 6.6501px 5.32008px rgba(0, 0, 0, 0.0282725), 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802); --avatar-border-color: var(--color-gray-85); - --sidebar-border-color: var(--color-gray-85); - --sidebar-bg-color: #191919; --scrollbar-thumb: #{$oc-gray-8}; --scrollbar-thumb-hover: #{$oc-gray-7}; @@ -224,5 +233,18 @@ --color-promo: #d297ff; --color-logo-text: #e2dfff; + + --color-surface-high: hsl(245, 10%, 21%); + --color-surface-low: hsl(240, 8%, 15%); + --color-surface-mid: hsl(240 6% 10%); + --color-surface-lowest: hsl(0, 0%, 7%); + --color-on-surface: #e3e3e8; + --color-brand-hover: #bbb8ff; + --color-on-primary-container: #e0dfff; + --color-surface-primary-container: #403e6a; + --color-brand-active: #d0ccff; + --color-border-outline: #8e8d9c; + --color-border-outline-variant: #46464f; + --color-surface-primary-container: #403e6a; } } diff --git a/src/css/variables.module.scss b/src/css/variables.module.scss index 10e42dc726..634752dfa0 100644 --- a/src/css/variables.module.scss +++ b/src/css/variables.module.scss @@ -11,7 +11,7 @@ .ToolIcon_type_radio, .ToolIcon_type_checkbox { &:checked + .ToolIcon__icon { - --icon-fill-color: var(--color-primary-darker); + --icon-fill-color: var(--color-on-primary-container); svg { fill: var(--icon-fill-color); @@ -23,11 +23,11 @@ .ToolIcon_type_radio, .ToolIcon_type_checkbox { &:checked + .ToolIcon__icon { - background: var(--color-primary-light); - --keybinding-color: var(--color-gray-60); + background: var(--color-surface-primary-container); + --keybinding-color: var(--color-on-primary-container); svg { - color: var(--color-primary-darker); + color: var(--color-on-primary-container); } } } @@ -44,7 +44,11 @@ &:active { background: var(--button-hover-bg); - border: 1px solid var(--color-primary-darkest); + border: 1px solid var(--button-active-border); + + svg { + color: var(--color-on-primary-container); + } } } } @@ -63,7 +67,7 @@ border-radius: var(--border-radius-lg); cursor: pointer; background-color: var(--button-bg, var(--island-bg-color)); - color: var(--button-color, var(--text-primary-color)); + color: var(--button-color, var(--color-on-surface)); svg { width: var(--button-width, var(--lg-icon-size)); @@ -88,22 +92,38 @@ } &.active { - background-color: var(--button-selected-bg, var(--color-primary-light)); - border-color: var(--button-selected-border, var(--color-primary-light)); + background-color: var( + --button-selected-bg, + var(--color-surface-primary-container) + ); + border-color: var( + --button-selected-border, + var(--color-surface-primary-container) + ); &:hover { background-color: var( --button-selected-hover-bg, - var(--color-primary-light) + var(--color-surface-primary-container) ); } svg { - color: var(--button-color, var(--color-primary-darker)); + color: var(--button-color, var(--color-on-primary-container)); } } } +@mixin filledButtonOnCanvas { + border: none; + box-shadow: 0 0 0 1px var(--color-surface-lowest); + background-color: var(--color-surface-low); + + &:active { + box-shadow: 0 0 0 1px var(--color-brand-active); + } +} + $theme-filter: "invert(93%) hue-rotate(180deg)"; $right-sidebar-width: "302px"; From b86184a84944e8650167f422880d67b1a29bb11e Mon Sep 17 00:00:00 2001 From: David Luzar Date: Thu, 12 Oct 2023 15:00:23 +0200 Subject: [PATCH 027/144] fix: ensure relative z-index of elements added to frame is retained (#7134) --- dev-docs/docs/codebase/frames.mdx | 22 ++++++ dev-docs/sidebars.js | 6 +- src/frame.test.tsx | 124 +++++++++++++++++++++++++++++- src/frame.ts | 51 +++++++++--- src/tests/helpers/api.ts | 3 +- 5 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 dev-docs/docs/codebase/frames.mdx diff --git a/dev-docs/docs/codebase/frames.mdx b/dev-docs/docs/codebase/frames.mdx new file mode 100644 index 0000000000..45a551f230 --- /dev/null +++ b/dev-docs/docs/codebase/frames.mdx @@ -0,0 +1,22 @@ +# Frames + +## Ordering + +Frames should be ordered where frame children come first, followed by the frame element itself: + +``` +[ + other_element, + frame1_child1, + frame1_child2, + frame1, + other_element, + frame2_child1, + frame2_child2, + frame2, + other_element, + ... +] +``` + +If not oredered correctly, the editor will still function, but the elements may not be rendered and clipped correctly. Further, the renderer relies on this ordering for performance optimizations. diff --git a/dev-docs/sidebars.js b/dev-docs/sidebars.js index 257d16b540..2b4ab8d860 100644 --- a/dev-docs/sidebars.js +++ b/dev-docs/sidebars.js @@ -23,7 +23,11 @@ const sidebars = { }, items: ["introduction/development", "introduction/contributing"], }, - { type: "category", label: "Codebase", items: ["codebase/json-schema"] }, + { + type: "category", + label: "Codebase", + items: ["codebase/json-schema", "codebase/frames"], + }, { type: "category", label: "@excalidraw/excalidraw", diff --git a/src/frame.test.tsx b/src/frame.test.tsx index 75c515d9a4..5dbfdb05c9 100644 --- a/src/frame.test.tsx +++ b/src/frame.test.tsx @@ -177,7 +177,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect2, frame]); }); - it.skip("should add elements", async () => { + it("should add elements", async () => { h.elements = [rect2, rect3, frame]; func(frame, rect2); @@ -188,7 +188,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect3, rect2, frame]); }); - it.skip("should add elements when there are other other elements in between", async () => { + it("should add elements when there are other other elements in between", async () => { h.elements = [rect1, rect2, rect4, rect3, frame]; func(frame, rect2); @@ -199,7 +199,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect1, rect4, rect3, rect2, frame]); }); - it.skip("should add elements when there are other elements in between and the order is reversed", async () => { + it("should add elements when there are other elements in between and the order is reversed", async () => { h.elements = [rect3, rect4, rect2, rect1, frame]; func(frame, rect2); @@ -234,7 +234,7 @@ describe("adding elements to frames", () => { expectEqualIds([rect1, rect2, rect3, frame, rect4]); }); - it.skip("should add elements when there are other elements in between and the order is reversed", async () => { + it("should add elements when there are other elements in between and the order is reversed", async () => { h.elements = [rect3, rect4, frame, rect2, rect1]; func(frame, rect2); @@ -436,5 +436,121 @@ describe("adding elements to frames", () => { expect(rect2.frameId).toBe(null); expectEqualIds([rect2_copy, frame, rect2]); }); + + it("random order 01", () => { + const frame1 = API.createElement({ + type: "frame", + x: 0, + y: 0, + width: 100, + height: 100, + }); + const frame2 = API.createElement({ + type: "frame", + x: 200, + y: 0, + width: 100, + height: 100, + }); + const frame3 = API.createElement({ + type: "frame", + x: 300, + y: 0, + width: 100, + height: 100, + }); + + const rectangle1 = API.createElement({ + type: "rectangle", + x: 25, + y: 25, + width: 50, + height: 50, + frameId: frame1.id, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 225, + y: 25, + width: 50, + height: 50, + frameId: frame2.id, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 325, + y: 25, + width: 50, + height: 50, + frameId: frame3.id, + }); + const rectangle4 = API.createElement({ + type: "rectangle", + x: 350, + y: 25, + width: 50, + height: 50, + frameId: frame3.id, + }); + + h.elements = [ + frame1, + rectangle4, + rectangle1, + rectangle3, + frame3, + rectangle2, + frame2, + ]; + + API.setSelectedElements([rectangle2]); + + const origSize = h.elements.length; + + expect(h.elements.length).toBe(origSize); + dragElementIntoFrame(frame3, rectangle2); + expect(h.elements.length).toBe(origSize); + }); + + it("random order 02", () => { + const frame1 = API.createElement({ + type: "frame", + x: 0, + y: 0, + width: 100, + height: 100, + }); + const frame2 = API.createElement({ + type: "frame", + x: 200, + y: 0, + width: 100, + height: 100, + }); + const rectangle1 = API.createElement({ + type: "rectangle", + x: 25, + y: 25, + width: 50, + height: 50, + frameId: frame1.id, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 225, + y: 25, + width: 50, + height: 50, + frameId: frame2.id, + }); + + h.elements = [rectangle1, rectangle2, frame1, frame2]; + + API.setSelectedElements([rectangle2]); + + expect(h.elements.length).toBe(4); + dragElementIntoFrame(frame2, rectangle1); + expect(h.elements.length).toBe(4); + }); }); }); diff --git a/src/frame.ts b/src/frame.ts index 1da9cfa102..6dccedd2b1 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -452,22 +452,31 @@ export const getContainingFrame = ( }; // --------------------------- Frame Operations ------------------------------- + +/** + * Retains (or repairs for target frame) the ordering invriant where children + * elements come right before the parent frame: + * [el, el, child, child, frame, el] + */ export const addElementsToFrame = ( allElements: ExcalidrawElementsIncludingDeleted, elementsToAdd: NonDeletedExcalidrawElement[], frame: ExcalidrawFrameElement, ) => { - const currTargetFrameChildrenMap = new Map( + const { allElementsIndexMap, currTargetFrameChildrenMap } = allElements.reduce( - (acc: [ExcalidrawElement["id"], ExcalidrawElement][], element) => { + (acc, element, index) => { + acc.allElementsIndexMap.set(element.id, index); if (element.frameId === frame.id) { - acc.push([element.id, element]); + acc.currTargetFrameChildrenMap.set(element.id, true); } return acc; }, - [], - ), - ); + { + allElementsIndexMap: new Map(), + currTargetFrameChildrenMap: new Map(), + }, + ); const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id)); @@ -520,12 +529,36 @@ export const addElementsToFrame = ( currFrameChildren.forEach((child) => { processedElements.add(child.id); }); - // console.log(currFrameChildren, finalElementsToAdd, element); - nextElements.push(...currFrameChildren, ...finalElementsToAdd, element); + + // if not found, add all children on top by assigning the lowest index + const targetFrameIndex = allElementsIndexMap.get(frame.id) ?? -1; + + const { newChildren_left, newChildren_right } = finalElementsToAdd.reduce( + (acc, element) => { + // if index not found, add on top of current frame children + const elementIndex = allElementsIndexMap.get(element.id) ?? Infinity; + if (elementIndex < targetFrameIndex) { + acc.newChildren_left.push(element); + } else { + acc.newChildren_right.push(element); + } + return acc; + }, + { + newChildren_left: [] as ExcalidrawElement[], + newChildren_right: [] as ExcalidrawElement[], + }, + ); + + nextElements.push( + ...newChildren_left, + ...currFrameChildren, + ...newChildren_right, + element, + ); continue; } - // console.log("(2)", element.frameId); nextElements.push(element); } diff --git a/src/tests/helpers/api.ts b/src/tests/helpers/api.ts index 1b1943de08..1fb95804ed 100644 --- a/src/tests/helpers/api.ts +++ b/src/tests/helpers/api.ts @@ -94,6 +94,7 @@ export class API { angle?: number; id?: string; isDeleted?: boolean; + frameId?: ExcalidrawElement["id"]; groupIds?: string[]; // generic element props strokeColor?: ExcalidrawGenericElement["strokeColor"]; @@ -151,12 +152,12 @@ export class API { | "versionNonce" | "isDeleted" | "groupIds" - | "frameId" | "link" | "updated" > = { x, y, + frameId: rest.frameId ?? null, angle: rest.angle ?? 0, strokeColor: rest.strokeColor ?? appState.currentItemStrokeColor, backgroundColor: From 89a3bbddb72fba6a3ceae91541afd4d9d6a702b0 Mon Sep 17 00:00:00 2001 From: Alex Kim <45559664+alex-kim-dev@users.noreply.github.com> Date: Thu, 12 Oct 2023 23:59:02 +0500 Subject: [PATCH 028/144] test: add more resizing tests (#7028) Co-authored-by: dwelle --- src/element/textWysiwyg.test.tsx | 29 +- src/tests/helpers/ui.ts | 279 +++++- src/tests/linearElementEditor.test.tsx | 72 +- src/tests/resize.test.tsx | 1096 +++++++++++++++++++++--- src/tests/rotate.test.tsx | 81 ++ src/tests/selection.test.tsx | 2 +- src/tests/utils.ts | 48 -- 7 files changed, 1305 insertions(+), 302 deletions(-) create mode 100644 src/tests/rotate.test.tsx delete mode 100644 src/tests/utils.ts diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 9115d0e599..b4f5db191f 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -17,7 +17,6 @@ import { } from "./types"; import { API } from "../tests/helpers/api"; import { mutateElement } from "./mutateElement"; -import { resize } from "../tests/utils"; import { getOriginalContainerHeightFromCache } from "./textWysiwyg"; // Unmount ReactDOM from root @@ -953,7 +952,7 @@ describe("textWysiwyg", () => { editor.blur(); // should center align horizontally and vertically by default - resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); + UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` [ 85, @@ -977,7 +976,7 @@ describe("textWysiwyg", () => { editor.blur(); // should left align horizontally and bottom vertically after resize - resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); + UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` [ 15, @@ -999,7 +998,7 @@ describe("textWysiwyg", () => { editor.blur(); // should right align horizontally and top vertically after resize - resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); + UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(` [ 374.99999999999994, @@ -1049,7 +1048,7 @@ describe("textWysiwyg", () => { expect(rectangle.height).toBe(75); expect(textElement.fontSize).toBe(20); - resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 50], { + UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 50], { shift: true, }); expect(rectangle.width).toBe(200); @@ -1189,7 +1188,7 @@ describe("textWysiwyg", () => { updateTextEditor(editor, "Hello"); editor.blur(); - resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); + UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]); expect(rectangle.height).toBeCloseTo(155, 8); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(null); @@ -1513,28 +1512,16 @@ describe("textWysiwyg", () => { }); }); - it("should bump the version of labelled arrow when label updated", async () => { + it("should bump the version of a labeled arrow when the label is updated", async () => { await render(); const arrow = UI.createElement("arrow", { width: 300, height: 0, }); - - mouse.select(arrow); - Keyboard.keyPress(KEYS.ENTER); - let editor = getTextEditor(); - await new Promise((r) => setTimeout(r, 0)); - updateTextEditor(editor, "Hello"); - editor.blur(); - + await UI.editText(arrow, "Hello"); const { version } = arrow; - mouse.select(arrow); - Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); - await new Promise((r) => setTimeout(r, 0)); - updateTextEditor(editor, "Hello\nworld!"); - editor.blur(); + await UI.editText(arrow, "Hello\nworld!"); expect(arrow.version).toEqual(version + 1); }); diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts index f815b07dc8..62290fe236 100644 --- a/src/tests/helpers/ui.ts +++ b/src/tests/helpers/ui.ts @@ -1,13 +1,37 @@ -import { +import type { Point } from "../../types"; +import type { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, + ExcalidrawArrowElement, + ExcalidrawRectangleElement, + ExcalidrawEllipseElement, + ExcalidrawDiamondElement, + ExcalidrawTextContainer, + ExcalidrawTextElementWithContainer, } from "../../element/types"; +import { + getTransformHandles, + getTransformHandlesFromCoords, + OMIT_SIDES_FOR_FRAME, + OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, + TransformHandleType, + type TransformHandle, + type TransformHandleDirection, +} from "../../element/transformHandles"; import { KEYS } from "../../keys"; -import { ToolName } from "../queries/toolQueries"; -import { fireEvent, GlobalTestState } from "../test-utils"; +import { type ToolName } from "../queries/toolQueries"; +import { fireEvent, GlobalTestState, screen } from "../test-utils"; import { mutateElement } from "../../element/mutateElement"; import { API } from "./api"; +import { + isFrameElement, + isLinearElement, + isFreeDrawElement, + isTextElement, +} from "../../element/typeChecks"; +import { getCommonBounds, getElementPointsCoords } from "../../element/bounds"; +import { rotatePoint } from "../../math"; const { h } = window; @@ -86,6 +110,29 @@ export class Keyboard { }; } +const getElementPointForSelection = (element: ExcalidrawElement): Point => { + const { x, y, width, height, angle } = element; + const target: Point = [ + x + + (isLinearElement(element) || isFreeDrawElement(element) ? 0 : width / 2), + y, + ]; + let center: Point; + + if (isLinearElement(element)) { + const bounds = getElementPointsCoords(element, element.points); + center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]; + } else { + center = [x + width / 2, y + height / 2]; + } + + if (isTextElement(element)) { + return center; + } + + return rotatePoint(target, center, angle); +}; + export class Pointer { public clientX = 0; public clientY = 0; @@ -199,31 +246,120 @@ export class Pointer { elements: ExcalidrawElement | ExcalidrawElement[], ) { API.clearSelection(); + Keyboard.withModifierKeys({ shift: true }, () => { elements = Array.isArray(elements) ? elements : [elements]; elements.forEach((element) => { this.reset(); - this.click(element.x, element.y); + this.click(...getElementPointForSelection(element)); }); }); + this.reset(); } clickOn(element: ExcalidrawElement) { this.reset(); - this.click(element.x, element.y); + this.click(...getElementPointForSelection(element)); this.reset(); } doubleClickOn(element: ExcalidrawElement) { this.reset(); - this.doubleClick(element.x, element.y); + this.doubleClick(...getElementPointForSelection(element)); this.reset(); } } const mouse = new Pointer("mouse"); +const transform = ( + element: ExcalidrawElement | ExcalidrawElement[], + handle: TransformHandleType, + mouseMove: [deltaX: number, deltaY: number], + keyboardModifiers: KeyboardModifiers = {}, +) => { + const elements = Array.isArray(element) ? element : [element]; + mouse.select(elements); + let handleCoords: TransformHandle | undefined; + + if (elements.length === 1) { + handleCoords = getTransformHandles(elements[0], h.state.zoom, "mouse")[ + handle + ]; + } else { + const [x1, y1, x2, y2] = getCommonBounds(elements); + const isFrameSelected = elements.some(isFrameElement); + const transformHandles = getTransformHandlesFromCoords( + [x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2], + 0, + h.state.zoom, + "mouse", + isFrameSelected ? OMIT_SIDES_FOR_FRAME : OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, + ); + handleCoords = transformHandles[handle]; + } + + if (!handleCoords) { + throw new Error(`There is no "${handle}" handle for this selection`); + } + + const clientX = handleCoords[0] + handleCoords[2] / 2; + const clientY = handleCoords[1] + handleCoords[3] / 2; + + Keyboard.withModifierKeys(keyboardModifiers, () => { + mouse.reset(); + mouse.down(clientX, clientY); + mouse.move(mouseMove[0], mouseMove[1]); + mouse.up(); + }); +}; + +const proxy = ( + element: T, +): typeof element & { + /** Returns the actual, current element from the elements array, instead of + the proxy */ + get(): typeof element; +} => { + return new Proxy( + {}, + { + get(target, prop) { + const currentElement = h.elements.find( + ({ id }) => id === element.id, + ) as any; + if (prop === "get") { + if (currentElement.hasOwnProperty("get")) { + throw new Error( + "trying to get `get` test property, but ExcalidrawElement seems to define its own", + ); + } + return () => currentElement; + } + return currentElement[prop]; + }, + }, + ) as any; +}; + +/** Tools that can be used to draw shapes */ +type DrawingToolName = Exclude; + +type Element = T extends "line" | "freedraw" + ? ExcalidrawLinearElement + : T extends "arrow" + ? ExcalidrawArrowElement + : T extends "text" + ? ExcalidrawTextElement + : T extends "rectangle" + ? ExcalidrawRectangleElement + : T extends "ellipse" + ? ExcalidrawEllipseElement + : T extends "diamond" + ? ExcalidrawDiamondElement + : ExcalidrawElement; + export class UI { static clickTool = (toolName: ToolName) => { fireEvent.click(GlobalTestState.renderResult.getByToolName(toolName)); @@ -246,6 +382,10 @@ export class UI { fireEvent.click(element); }; + static clickByTitle = (title: string) => { + fireEvent.click(screen.getByTitle(title)); + }; + /** * Creates an Excalidraw element, and returns a proxy that wraps it so that * accessing props will return the latest ones from the object existing in @@ -255,16 +395,17 @@ export class UI { * If you need to get the actual element, not the proxy, call `get()` method * on the proxy object. */ - static createElement( + static createElement( type: T, { position = 0, x = position, y = position, size = 10, - width = size, - height = width, + width: initialWidth = size, + height: initialHeight = initialWidth, angle = 0, + points: initialPoints, }: { position?: number; x?: number; @@ -273,25 +414,46 @@ export class UI { width?: number; height?: number; angle?: number; + points?: T extends "line" | "arrow" | "freedraw" ? Point[] : never; } = {}, - ): (T extends "arrow" | "line" | "freedraw" - ? ExcalidrawLinearElement - : T extends "text" - ? ExcalidrawTextElement - : ExcalidrawElement) & { + ): Element & { /** Returns the actual, current element from the elements array, instead of the proxy */ - get(): T extends "arrow" | "line" | "freedraw" - ? ExcalidrawLinearElement - : T extends "text" - ? ExcalidrawTextElement - : ExcalidrawElement; + get(): Element; } { + const width = initialWidth ?? initialHeight ?? size; + const height = initialHeight ?? size; + const points: Point[] = initialPoints ?? [ + [0, 0], + [width, height], + ]; + UI.clickTool(type); - mouse.reset(); - mouse.down(x, y); - mouse.reset(); - mouse.up(x + (width ?? height ?? size), y + (height ?? size)); + + if (type === "text") { + mouse.reset(); + mouse.click(x, y); + } else if ((type === "line" || type === "arrow") && points.length > 2) { + points.forEach((point) => { + mouse.reset(); + mouse.click(x + point[0], y + point[1]); + }); + Keyboard.keyPress(KEYS.ESCAPE); + } else if (type === "freedraw" && points.length > 2) { + const firstPoint = points[0]; + mouse.reset(); + mouse.down(x + firstPoint[0], y + firstPoint[1]); + points + .slice(1) + .forEach((point) => mouse.moveTo(x + point[0], y + point[1])); + mouse.upAt(); + Keyboard.keyPress(KEYS.ESCAPE); + } else { + mouse.reset(); + mouse.down(x, y); + mouse.reset(); + mouse.up(x + width, y + height); + } const origElement = h.elements[h.elements.length - 1] as any; @@ -299,25 +461,58 @@ export class UI { mutateElement(origElement, { angle }); } - return new Proxy( - {}, - { - get(target, prop) { - const currentElement = h.elements.find( - (element) => element.id === origElement.id, - ) as any; - if (prop === "get") { - if (currentElement.hasOwnProperty("get")) { - throw new Error( - "trying to get `get` test property, but ExcalidrawElement seems to define its own", - ); - } - return () => currentElement; - } - return currentElement[prop]; - }, - }, - ) as any; + return proxy(origElement); + } + + static async editText< + T extends ExcalidrawTextElement | ExcalidrawTextContainer, + >(element: T, text: string) { + const openedEditor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ); + + if (!openedEditor) { + mouse.select(element); + Keyboard.keyPress(KEYS.ENTER); + } + + const editor = + openedEditor ?? + document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ); + if (!editor) { + throw new Error("Can't find wysiwyg text editor in the dom"); + } + + fireEvent.input(editor, { target: { value: text } }); + await new Promise((resolve) => setTimeout(resolve, 0)); + editor.blur(); + + return isTextElement(element) + ? element + : proxy( + h.elements[ + h.elements.length - 1 + ] as ExcalidrawTextElementWithContainer, + ); + } + + static resize( + element: ExcalidrawElement | ExcalidrawElement[], + handle: TransformHandleDirection, + mouseMove: [deltaX: number, deltaY: number], + keyboardModifiers: KeyboardModifiers = {}, + ) { + return transform(element, handle, mouseMove, keyboardModifiers); + } + + static rotate( + element: ExcalidrawElement | ExcalidrawElement[], + mouseMove: [deltaX: number, deltaY: number], + keyboardModifiers: KeyboardModifiers = {}, + ) { + return transform(element, "rotation", mouseMove, keyboardModifiers); } static group(elements: ExcalidrawElement[]) { diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index a31f6f046f..920ed884d7 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -16,7 +16,6 @@ import { Point } from "../types"; import { KEYS } from "../keys"; import { LinearElementEditor } from "../element/linearElementEditor"; import { queryByTestId, queryByText } from "@testing-library/react"; -import { resize, rotate } from "./utils"; import { getBoundTextElementPosition, wrapText, @@ -939,71 +938,10 @@ describe("Test Linear Elements", () => { expect(line.boundElements).toBeNull(); }); - it("should not rotate the bound text and update position of bound text and bounding box correctly when arrow rotated", () => { - createThreePointerLinearElement("arrow", { - type: ROUNDNESS.PROPORTIONAL_RADIUS, - }); - - const arrow = h.elements[0] as ExcalidrawLinearElement; - - const { textElement, container } = createBoundTextElement( - DEFAULT_TEXT, - arrow, - ); - - expect(container.angle).toBe(0); - expect(textElement.angle).toBe(0); - expect(getBoundTextElementPosition(arrow, textElement)) - .toMatchInlineSnapshot(` - { - "x": 75, - "y": 60, - } - `); - expect(textElement.text).toMatchInlineSnapshot(` - "Online whiteboard - collaboration made - easy" - `); - expect(LinearElementEditor.getElementAbsoluteCoords(container, true)) - .toMatchInlineSnapshot(` - [ - 20, - 20, - 105, - 80, - 55.45893770831013, - 45, - ] - `); - - rotate(container, -35, 55); - expect(container.angle).toMatchInlineSnapshot(`1.3988061968364685`); - expect(textElement.angle).toBe(0); - expect(getBoundTextElementPosition(container, textElement)) - .toMatchInlineSnapshot(` - { - "x": 21.73926141863671, - "y": 73.31003398390868, - } - `); - expect(textElement.text).toMatchInlineSnapshot(` - "Online whiteboard - collaboration made - easy" - `); - expect(LinearElementEditor.getElementAbsoluteCoords(container, true)) - .toMatchInlineSnapshot(` - [ - 20, - 20, - 102.41961302274555, - 86.49012635273976, - 55.45893770831013, - 45, - ] - `); - }); + // TODO fix #7029 and rewrite this test + it.todo( + "should not rotate the bound text and update position of bound text and bounding box correctly when arrow rotated", + ); it("should resize and position the bound text and bounding box correctly when 3 pointer arrow element resized", () => { createThreePointerLinearElement("arrow", { @@ -1042,7 +980,7 @@ describe("Test Linear Elements", () => { ] `); - resize(container, "ne", [300, 200]); + UI.resize(container, "ne", [300, 200]); expect({ width: container.width, height: container.height }) .toMatchInlineSnapshot(` diff --git a/src/tests/resize.test.tsx b/src/tests/resize.test.tsx index 60ce3765df..be5c21143e 100644 --- a/src/tests/resize.test.tsx +++ b/src/tests/resize.test.tsx @@ -1,153 +1,1003 @@ import ReactDOM from "react-dom"; import { render } from "./test-utils"; -import App from "../components/App"; -import * as Renderer from "../renderer/renderScene"; import { reseed } from "../random"; -import { UI, Keyboard } from "./helpers/ui"; -import { resize } from "./utils"; -import { ExcalidrawTextElement } from "../element/types"; +import { UI, Keyboard, Pointer } from "./helpers/ui"; +import type { + ExcalidrawFreeDrawElement, + ExcalidrawLinearElement, +} from "../element/types"; +import type { Point } from "../types"; +import { Bounds, getElementPointsCoords } from "../element/bounds"; import { Excalidraw } from "../packages/excalidraw/index"; import { API } from "./helpers/api"; import { KEYS } from "../keys"; -import { vi } from "vitest"; +import { isLinearElement } from "../element/typeChecks"; +import { LinearElementEditor } from "../element/linearElementEditor"; -// Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); -const renderStaticScene = vi.spyOn(Renderer, "renderStaticScene"); - -beforeEach(() => { - localStorage.clear(); - renderStaticScene.mockClear(); - reseed(7); -}); - const { h } = window; +const mouse = new Pointer("mouse"); -describe("resize rectangle ellipses and diamond elements", () => { - const elemData = { - x: 0, - y: 0, - width: 100, - height: 100, - }; - // Value for irrelevant cursor movements - const _ = 234; +const getBoundsFromPoints = ( + element: ExcalidrawLinearElement | ExcalidrawFreeDrawElement, +): Bounds => { + if (isLinearElement(element)) { + return getElementPointsCoords(element, element.points); + } - it.each` - handle | move | dimensions | topLeft - ${"n"} | ${[_, -100]} | ${[100, 200]} | ${[elemData.x, -100]} - ${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]} - ${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]} - ${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]} - ${"ne"} | ${[5, 55]} | ${[105, 45]} | ${[elemData.x, 55]} - ${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]} - ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]} - ${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]} - `( - "resizes with handle $handle", - async ({ handle, move, dimensions, topLeft }) => { - await render(); - const rectangle = UI.createElement("rectangle", elemData); - resize(rectangle, handle, move); - const element = h.elements[0]; - expect([element.width, element.height]).toEqual(dimensions); - expect([element.x, element.y]).toEqual(topLeft); - }, - ); + const { x, y, points } = element; + const pointsX = points.map(([x]) => x); + const pointsY = points.map(([, y]) => y); - it.each` - handle | move | dimensions | topLeft - ${"n"} | ${[_, -100]} | ${[200, 200]} | ${[-50, -100]} - ${"nw"} | ${[-300, -200]} | ${[400, 400]} | ${[-300, -300]} - ${"sw"} | ${[40, -20]} | ${[80, 80]} | ${[20, 0]} - `( - "resizes with fixed side ratios on handle $handle", - async ({ handle, move, dimensions, topLeft }) => { - await render(); - const rectangle = UI.createElement("rectangle", elemData); - resize(rectangle, handle, move, { shift: true }); - const element = h.elements[0]; - expect([element.width, element.height]).toEqual(dimensions); - expect([element.x, element.y]).toEqual(topLeft); - }, - ); + return [ + Math.min(...pointsX) + x, + Math.min(...pointsY) + y, + Math.max(...pointsX) + x, + Math.max(...pointsY) + y, + ]; +}; - it.each` - handle | move | dimensions | topLeft - ${"nw"} | ${[0, 120]} | ${[100, 100]} | ${[0, 100]} - ${"ne"} | ${[-120, 0]} | ${[100, 100]} | ${[-100, 0]} - ${"sw"} | ${[200, -200]} | ${[100, 100]} | ${[100, -100]} - ${"n"} | ${[_, 150]} | ${[50, 50]} | ${[25, 100]} - `( - "Flips while resizing and keeping side ratios on handle $handle", - async ({ handle, move, dimensions, topLeft }) => { - await render(); - const rectangle = UI.createElement("rectangle", elemData); - resize(rectangle, handle, move, { shift: true }); - const element = h.elements[0]; - expect([element.width, element.height]).toEqual(dimensions); - expect([element.x, element.y]).toEqual(topLeft); - }, - ); +beforeEach(async () => { + localStorage.clear(); + reseed(7); + mouse.reset(); - it.each` - handle | move | dimensions | topLeft - ${"ne"} | ${[50, -100]} | ${[200, 300]} | ${[-50, -100]} - ${"s"} | ${[_, -20]} | ${[100, 60]} | ${[0, 20]} - `( - "Resizes from center on handle $handle", - async ({ handle, move, dimensions, topLeft }) => { - await render(); - const rectangle = UI.createElement("rectangle", elemData); - resize(rectangle, handle, move, { alt: true }); - const element = h.elements[0]; - expect([element.width, element.height]).toEqual(dimensions); - expect([element.x, element.y]).toEqual(topLeft); - }, - ); + await render(); + h.state.width = 1000; + h.state.height = 1000; - it.each` - handle | move | dimensions | topLeft - ${"nw"} | ${[100, 120]} | ${[140, 140]} | ${[-20, -20]} - ${"e"} | ${[-130, _]} | ${[160, 160]} | ${[-30, -30]} - `( - "Resizes from center, flips and keeps side rations on handle $handle", - async ({ handle, move, dimensions, topLeft }) => { - await render(); - const rectangle = UI.createElement("rectangle", elemData); - resize(rectangle, handle, move, { alt: true, shift: true }); - const element = h.elements[0]; - expect([element.width, element.height]).toEqual(dimensions); - expect([element.x, element.y]).toEqual(topLeft); - }, - ); + // The bounds of hand-drawn linear elements may change after flipping, so + // removing this style for testing + UI.clickTool("arrow"); + UI.clickByTitle("Architect"); + UI.clickTool("selection"); }); -describe("Test text element", () => { - it("should update font size via keyboard", async () => { - await render(); +describe("generic element", () => { + // = rectangle/diamond/ellipse - const textElement = API.createElement({ - type: "text", - text: "abc", + describe("resizes", () => { + it.each` + handle | move | size | xy + ${"n"} | ${[10, -27]} | ${[200, 127]} | ${[0, -27]} + ${"e"} | ${[67, -45]} | ${[267, 100]} | ${[0, 0]} + ${"s"} | ${[-50, -39]} | ${[200, 61]} | ${[0, 0]} + ${"w"} | ${[20, 90]} | ${[180, 100]} | ${[20, 0]} + ${"ne"} | ${[5, -33]} | ${[205, 133]} | ${[0, -33]} + ${"se"} | ${[-30, -81]} | ${[170, 19]} | ${[0, 0]} + ${"sw"} | ${[37, 25]} | ${[163, 125]} | ${[37, 0]} + ${"nw"} | ${[-34, 42]} | ${[234, 58]} | ${[-34, 42]} + `( + "with handle $handle", + async ({ handle, move, size: [width, height], xy: [x, y] }) => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + UI.resize(rectangle, handle, move); + + expect(rectangle.x).toBeCloseTo(x); + expect(rectangle.y).toBeCloseTo(y); + expect(rectangle.width).toBeCloseTo(width); + expect(rectangle.height).toBeCloseTo(height); + expect(rectangle.angle).toBeCloseTo(0); + }, + ); + }); + + describe("flips while resizing", () => { + it.each` + handle | move | size | xy + ${"n"} | ${[15, 139]} | ${[200, 39]} | ${[0, 100]} + ${"e"} | ${[-245, 67]} | ${[45, 100]} | ${[-45, 0]} + ${"s"} | ${[-26, -210]} | ${[200, 110]} | ${[0, -110]} + ${"w"} | ${[241, 0]} | ${[41, 100]} | ${[200, 0]} + ${"ne"} | ${[-250, 125]} | ${[50, 25]} | ${[-50, 100]} + ${"se"} | ${[-283, -58]} | ${[83, 42]} | ${[-83, 0]} + ${"sw"} | ${[40, -123]} | ${[160, 23]} | ${[40, -23]} + ${"nw"} | ${[270, 133]} | ${[70, 33]} | ${[200, 100]} + `( + "with handle $handle", + async ({ handle, move, size: [width, height], xy: [x, y] }) => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + UI.resize(rectangle, handle, move); + + expect(rectangle.x).toBeCloseTo(x); + expect(rectangle.y).toBeCloseTo(y); + expect(rectangle.width).toBeCloseTo(width); + expect(rectangle.height).toBeCloseTo(height); + expect(rectangle.angle).toBeCloseTo(0); + }, + ); + }); + + it("resizes with locked aspect ratio", async () => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + UI.resize(rectangle, "se", [100, 10], { shift: true }); + + expect(rectangle.x).toBeCloseTo(0); + expect(rectangle.y).toBeCloseTo(0); + expect(rectangle.width).toBeCloseTo(300); + expect(rectangle.height).toBeCloseTo(150); + expect(rectangle.angle).toBeCloseTo(0); + + UI.resize(rectangle, "n", [30, 50], { shift: true }); + + expect(rectangle.x).toBeCloseTo(50); + expect(rectangle.y).toBeCloseTo(50); + expect(rectangle.width).toBeCloseTo(200); + expect(rectangle.height).toBeCloseTo(100); + expect(rectangle.angle).toBeCloseTo(0); + }); + + it("resizes from center", async () => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + UI.resize(rectangle, "nw", [20, 10], { alt: true }); + + expect(rectangle.x).toBeCloseTo(20); + expect(rectangle.y).toBeCloseTo(10); + expect(rectangle.width).toBeCloseTo(160); + expect(rectangle.height).toBeCloseTo(80); + expect(rectangle.angle).toBeCloseTo(0); + + UI.resize(rectangle, "e", [15, 43], { alt: true }); + + expect(rectangle.x).toBeCloseTo(5); + expect(rectangle.y).toBeCloseTo(10); + expect(rectangle.width).toBeCloseTo(190); + expect(rectangle.height).toBeCloseTo(80); + expect(rectangle.angle).toBeCloseTo(0); + }); + + it("resizes with bound arrow", async () => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + const arrow = UI.createElement("arrow", { + x: -30, + y: 50, + width: 28, + height: 5, }); - window.h.elements = [textElement]; + expect(arrow.endBinding?.elementId).toEqual(rectangle.id); - API.setSelectedElements([textElement]); + UI.resize(rectangle, "e", [40, 0]); - const origFontSize = textElement.fontSize; + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30); + + UI.resize(rectangle, "w", [50, 0]); + + expect(arrow.endBinding?.elementId).toEqual(rectangle.id); + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80); + }); + + it("resizes with a label", async () => { + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + const label = await UI.editText(rectangle, "Hello world"); + UI.resize(rectangle, "se", [50, 50]); + + expect(label.x + label.width / 2).toBeCloseTo( + rectangle.x + rectangle.width / 2, + ); + expect(label.y + label.height / 2).toBeCloseTo( + rectangle.y + rectangle.height / 2, + ); + expect(label.angle).toBeCloseTo(rectangle.angle); + expect(label.fontSize).toEqual(20); + + UI.resize(rectangle, "w", [190, 0]); + + expect(label.x + label.width / 2).toBeCloseTo( + rectangle.x + rectangle.width / 2, + ); + expect(label.y + label.height / 2).toBeCloseTo( + rectangle.y + rectangle.height / 2, + ); + expect(label.angle).toBeCloseTo(rectangle.angle); + expect(label.fontSize).toEqual(20); + }); +}); + +describe.each(["line", "freedraw"] as const)("%s element", (type) => { + const points: Record = { + line: [ + [0, 0], + [60, -20], + [20, 40], + [-40, 0], + ], + freedraw: [ + [0, 0], + [-2.474600807561444, 41.021700699972], + [3.6627956000014024, 47.84174560617245], + [40.495224145598115, 47.15909710753482], + ], + }; + + it("resizes", async () => { + const element = UI.createElement(type, { points: points[type] }); + const bounds = getBoundsFromPoints(element); + + UI.resize(element, "ne", [30, -60]); + const newBounds = getBoundsFromPoints(element); + + expect(newBounds[0]).toBeCloseTo(bounds[0]); + expect(newBounds[1]).toBeCloseTo(bounds[1] - 60); + expect(newBounds[2]).toBeCloseTo(bounds[2] + 30); + expect(newBounds[3]).toBeCloseTo(bounds[3]); + expect(element.angle).toBeCloseTo(0); + }); + + it("flips while resizing", async () => { + const element = UI.createElement(type, { points: points[type] }); + const bounds = getBoundsFromPoints(element); + + UI.resize(element, "sw", [140, -80]); + const newBounds = getBoundsFromPoints(element); + + expect(newBounds[0]).toBeCloseTo(bounds[2]); + expect(newBounds[1]).toBeCloseTo(bounds[3] - 80); + expect(newBounds[2]).toBeCloseTo(bounds[0] + 140); + expect(newBounds[3]).toBeCloseTo(bounds[1]); + expect(element.angle).toBeCloseTo(0); + }); + + it("resizes with locked aspect ratio", async () => { + const element = UI.createElement(type, { points: points[type] }); + const bounds = getBoundsFromPoints(element); + + UI.resize(element, "ne", [30, -60], { shift: true }); + const newBounds = getBoundsFromPoints(element); + const scale = 1 + 60 / (bounds[3] - bounds[1]); + + expect(newBounds[0]).toBeCloseTo(bounds[0]); + expect(newBounds[1]).toBeCloseTo(bounds[1] - 60); + expect(newBounds[2]).toBeCloseTo( + bounds[0] + (bounds[2] - bounds[0]) * scale, + ); + expect(newBounds[3]).toBeCloseTo(bounds[3]); + expect(element.angle).toBeCloseTo(0); + }); + + it("resizes from center", async () => { + const element = UI.createElement(type, { points: points[type] }); + const bounds = getBoundsFromPoints(element); + + UI.resize(element, "nw", [-20, -30], { alt: true }); + const newBounds = getBoundsFromPoints(element); + + expect(newBounds[0]).toBeCloseTo(bounds[0] - 20); + expect(newBounds[1]).toBeCloseTo(bounds[1] - 30); + expect(newBounds[2]).toBeCloseTo(bounds[2] + 20); + expect(newBounds[3]).toBeCloseTo(bounds[3] + 30); + expect(element.angle).toBeCloseTo(0); + }); +}); + +describe("arrow element", () => { + it("resizes with a label", async () => { + const arrow = UI.createElement("arrow", { + points: [ + [0, 0], + [40, 140], + [80, 60], // label's anchor + [180, 20], + [200, 120], + ], + }); + const label = await UI.editText(arrow, "Hello"); + UI.resize(arrow, "se", [50, 30]); + let labelPos = LinearElementEditor.getBoundTextElementPosition( + arrow, + label, + ); + + expect(labelPos.x + label.width / 2).toBeCloseTo( + arrow.x + arrow.points[2][0], + ); + expect(labelPos.y + label.height / 2).toBeCloseTo( + arrow.y + arrow.points[2][1], + ); + expect(label.angle).toBeCloseTo(0); + expect(label.fontSize).toEqual(20); + + UI.resize(arrow, "w", [20, 0]); + labelPos = LinearElementEditor.getBoundTextElementPosition(arrow, label); + + expect(labelPos.x + label.width / 2).toBeCloseTo( + arrow.x + arrow.points[2][0], + ); + expect(labelPos.y + label.height / 2).toBeCloseTo( + arrow.y + arrow.points[2][1], + ); + expect(label.angle).toBeCloseTo(0); + expect(label.fontSize).toEqual(20); + }); +}); + +describe("text element", () => { + it("resizes", async () => { + const text = UI.createElement("text"); + await UI.editText(text, "hello\nworld"); + const { width, height, fontSize } = text; + const scale = 40 / height + 1; + UI.resize(text, "se", [30, 40]); + + expect(text.x).toBeCloseTo(0); + expect(text.y).toBeCloseTo(0); + expect(text.width).toBeCloseTo(width * scale); + expect(text.height).toBeCloseTo(height * scale); + expect(text.angle).toBeCloseTo(0); + expect(text.fontSize).toBeCloseTo(fontSize * scale); + }); + + // TODO enable this test after adding single text element flipping + it.skip("flips while resizing", async () => { + const text = UI.createElement("text"); + await UI.editText(text, "hello\nworld"); + const { width, height, fontSize } = text; + const scale = 100 / width - 1; + UI.resize(text, "nw", [100, 80]); + + expect(text.x).toBeCloseTo(width); + expect(text.y).toBeCloseTo(height); + expect(text.width).toBeCloseTo(width * scale); + expect(text.height).toBeCloseTo(height * scale); + expect(text.angle).toBeCloseTo(0); + expect(text.fontSize).toBeCloseTo(fontSize * scale); + }); + + // TODO enable this test after fixing text resizing from center + it.skip("resizes from center", async () => { + const text = UI.createElement("text"); + await UI.editText(text, "hello\nworld"); + const { x, y, width, height, fontSize } = text; + const scale = 80 / height + 1; + UI.resize(text, "nw", [-25, -40], { alt: true }); + + expect(text.x).toBeCloseTo(x - ((scale - 1) * width) / 2); + expect(text.y).toBeCloseTo(y - 40); + expect(text.width).toBeCloseTo(width * scale); + expect(text.height).toBeCloseTo(height * scale); + expect(text.angle).toBeCloseTo(0); + expect(text.fontSize).toBeCloseTo(fontSize * scale); + }); + + it("resizes with bound arrow", async () => { + const text = UI.createElement("text"); + await UI.editText(text, "hello\nworld"); + const boundArrow = UI.createElement("arrow", { + x: -30, + y: 25, + width: 28, + height: 5, + }); + + expect(boundArrow.endBinding?.elementId).toEqual(text.id); + + UI.resize(text, "ne", [40, 0]); + + expect(boundArrow.width + boundArrow.endBinding!.gap).toBeCloseTo(30); + + const textWidth = text.width; + const scale = 20 / text.height; + UI.resize(text, "nw", [50, 20]); + + expect(boundArrow.endBinding?.elementId).toEqual(text.id); + expect(boundArrow.width + boundArrow.endBinding!.gap).toBeCloseTo( + 30 + textWidth * scale, + ); + }); + + it("updates font size via keyboard", async () => { + const text = UI.createElement("text"); + await UI.editText(text, "abc"); + const { fontSize } = text; + mouse.select(text); Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => { Keyboard.keyDown(KEYS.CHEVRON_RIGHT); - expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe( - origFontSize * 1.1, - ); + expect(text.fontSize).toBe(fontSize * 1.1); + Keyboard.keyDown(KEYS.CHEVRON_LEFT); - expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe( - origFontSize, - ); + expect(text.fontSize).toBe(fontSize); }); }); }); + +describe("image element", () => { + it("resizes", async () => { + const image = API.createElement({ type: "image", width: 100, height: 100 }); + h.elements = [image]; + UI.resize(image, "ne", [-20, -30]); + + expect(image.x).toBeCloseTo(0); + expect(image.y).toBeCloseTo(-30); + expect(image.width).toBeCloseTo(130); + expect(image.height).toBeCloseTo(130); + expect(image.angle).toBeCloseTo(0); + expect(image.scale).toEqual([1, 1]); + }); + + it("flips while resizing", async () => { + const image = API.createElement({ type: "image", width: 100, height: 100 }); + h.elements = [image]; + UI.resize(image, "sw", [150, -150]); + + expect(image.x).toBeCloseTo(100); + expect(image.y).toBeCloseTo(-50); + expect(image.width).toBeCloseTo(50); + expect(image.height).toBeCloseTo(50); + expect(image.angle).toBeCloseTo(0); + expect(image.scale).toEqual([-1, -1]); + }); + + it("resizes with locked/unlocked aspect ratio", async () => { + const image = API.createElement({ type: "image", width: 100, height: 100 }); + h.elements = [image]; + UI.resize(image, "ne", [30, -20]); + + expect(image.x).toBeCloseTo(0); + expect(image.y).toBeCloseTo(-30); + expect(image.width).toBeCloseTo(130); + expect(image.height).toBeCloseTo(130); + + UI.resize(image, "ne", [-30, 50], { shift: true }); + + expect(image.x).toBeCloseTo(0); + expect(image.y).toBeCloseTo(20); + expect(image.width).toBeCloseTo(100); + expect(image.height).toBeCloseTo(80); + }); + + it("resizes from center", async () => { + const image = API.createElement({ type: "image", width: 100, height: 100 }); + h.elements = [image]; + UI.resize(image, "nw", [25, 15], { alt: true }); + + expect(image.x).toBeCloseTo(15); + expect(image.y).toBeCloseTo(15); + expect(image.width).toBeCloseTo(70); + expect(image.height).toBeCloseTo(70); + expect(image.angle).toBeCloseTo(0); + expect(image.scale).toEqual([1, 1]); + }); + + it("resizes with bound arrow", async () => { + const image = API.createElement({ + type: "image", + width: 100, + height: 100, + }); + h.elements = [image]; + const arrow = UI.createElement("arrow", { + x: -30, + y: 50, + width: 28, + height: 5, + }); + + expect(arrow.endBinding?.elementId).toEqual(image.id); + + UI.resize(image, "ne", [40, 0]); + + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30); + + const imageWidth = image.width; + const scale = 20 / image.height; + UI.resize(image, "nw", [50, 20]); + + expect(arrow.endBinding?.elementId).toEqual(image.id); + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo( + 30 + imageWidth * scale, + ); + }); +}); + +describe("multiple selection", () => { + it("resizes with generic elements", async () => { + const rectangle = UI.createElement("rectangle", { + position: 0, + width: 100, + height: 80, + }); + const rectLabel = await UI.editText(rectangle, "hello\nworld"); + const diamond = UI.createElement("diamond", { + x: 140, + y: 40, + size: 80, + }); + const ellipse = UI.createElement("ellipse", { + x: 40, + y: 100, + width: 80, + height: 60, + }); + + const selectionWidth = 220; + const selectionHeight = 160; + const move = [50, 30] as [number, number]; + const scale = Math.max( + 1 + move[0] / selectionWidth, + 1 + move[1] / selectionHeight, + ); + + UI.resize([rectangle, diamond, ellipse], "se", move); + + expect(rectangle.x).toBeCloseTo(0); + expect(rectangle.y).toBeCloseTo(0); + expect(rectangle.width).toBeCloseTo(100 * scale); + expect(rectangle.height).toBeCloseTo(80 * scale); + expect(rectangle.angle).toEqual(0); + + expect(rectLabel.type).toEqual("text"); + expect(rectLabel.containerId).toEqual(rectangle.id); + expect(rectLabel.x + rectLabel.width / 2).toBeCloseTo( + rectangle.x + rectangle.width / 2, + ); + expect(rectLabel.y + rectLabel.height / 2).toBeCloseTo( + rectangle.y + rectangle.height / 2, + ); + expect(rectLabel.angle).toEqual(0); + expect(rectLabel.fontSize).toBeCloseTo(20 * scale, -1); + + expect(diamond.x).toBeCloseTo(140 * scale); + expect(diamond.y).toBeCloseTo(40 * scale); + expect(diamond.width).toBeCloseTo(80 * scale); + expect(diamond.height).toBeCloseTo(80 * scale); + expect(diamond.angle).toEqual(0); + + expect(ellipse.x).toBeCloseTo(40 * scale); + expect(ellipse.y).toBeCloseTo(100 * scale); + expect(ellipse.width).toBeCloseTo(80 * scale); + expect(ellipse.height).toBeCloseTo(60 * scale); + expect(ellipse.angle).toEqual(0); + }); + + it("resizes with linear elements > 2 points", async () => { + UI.clickTool("line"); + UI.clickByTitle("Sharp"); + + const line = UI.createElement("line", { + x: 60, + y: 40, + points: [ + [0, 0], + [-40, 40], + [-60, 0], + [0, -40], + [40, 20], + [0, 40], + ], + }); + const freedraw = UI.createElement("freedraw", { + x: 63.56072661326618, + y: 100, + points: [ + [0, 0], + [-43.56072661326618, 18.15048126846341], + [-43.56072661326618, 29.041198460587566], + [-38.115368017204105, 42.652452795512204], + [-19.964886748740696, 66.24829266003775], + [19.056612930986716, 77.1390098521619], + ], + }); + + const selectionWidth = 100; + const selectionHeight = 177.1390098521619; + const move = [-25, -25] as [number, number]; + const scale = Math.max( + 1 + move[0] / selectionWidth, + 1 + move[1] / selectionHeight, + ); + + UI.resize([line, freedraw], "se", move); + + expect(line.x).toBeCloseTo(60 * scale); + expect(line.y).toBeCloseTo(40 * scale); + expect(line.width).toBeCloseTo(100 * scale); + expect(line.height).toBeCloseTo(80 * scale); + expect(line.angle).toEqual(0); + + expect(freedraw.x).toBeCloseTo(63.56072661326618 * scale); + expect(freedraw.y).toBeCloseTo(100 * scale); + expect(freedraw.width).toBeCloseTo(62.6173395442529 * scale); + expect(freedraw.height).toBeCloseTo(77.1390098521619 * scale); + expect(freedraw.angle).toEqual(0); + }); + + it("resizes with 2-point lines", async () => { + const horizLine = UI.createElement("line", { + position: 0, + width: 120, + height: 0, + }); + const vertLine = UI.createElement("line", { + x: 0, + y: 20, + width: 0, + height: 80, + }); + const diagLine = UI.createElement("line", { + position: 40, + size: 60, + }); + + const selectionWidth = 120; + const selectionHeight = 100; + const move = [40, 40] as [number, number]; + const scale = Math.max( + 1 - move[0] / selectionWidth, + 1 - move[1] / selectionHeight, + ); + + UI.resize([horizLine, vertLine, diagLine], "nw", move); + + expect(horizLine.x).toBeCloseTo(selectionWidth * (1 - scale)); + expect(horizLine.y).toBeCloseTo(selectionHeight * (1 - scale)); + expect(horizLine.width).toBeCloseTo(120 * scale); + expect(horizLine.height).toBeCloseTo(0); + expect(horizLine.angle).toEqual(0); + + expect(vertLine.x).toBeCloseTo(selectionWidth * (1 - scale)); + expect(vertLine.y).toBeCloseTo((selectionHeight - 20) * (1 - scale) + 20); + expect(vertLine.width).toBeCloseTo(0); + expect(vertLine.height).toBeCloseTo(80 * scale); + expect(vertLine.angle).toEqual(0); + + expect(diagLine.x).toBeCloseTo((selectionWidth - 40) * (1 - scale) + 40); + expect(diagLine.y).toBeCloseTo((selectionHeight - 40) * (1 - scale) + 40); + expect(diagLine.width).toBeCloseTo(60 * scale); + expect(diagLine.height).toBeCloseTo(60 * scale); + expect(diagLine.angle).toEqual(0); + }); + + it("resizes with bound arrows", async () => { + const rectangle = UI.createElement("rectangle", { + position: 0, + size: 100, + }); + const leftBoundArrow = UI.createElement("arrow", { + x: -110, + y: 50, + width: 100, + height: 0, + }); + const rightBoundArrow = UI.createElement("arrow", { + x: 210, + y: 50, + width: -100, + height: 0, + }); + + const selectionWidth = 210; + const selectionHeight = 100; + const move = [40, 40] as [number, number]; + const scale = Math.max( + 1 - move[0] / selectionWidth, + 1 - move[1] / selectionHeight, + ); + const leftArrowBinding = { ...leftBoundArrow.endBinding }; + const rightArrowBinding = { ...rightBoundArrow.endBinding }; + delete rightArrowBinding.gap; + + UI.resize([rectangle, rightBoundArrow], "nw", move); + + expect(leftBoundArrow.x).toBeCloseTo(-110); + expect(leftBoundArrow.y).toBeCloseTo(50); + expect(leftBoundArrow.width).toBeCloseTo(140, 0); + expect(leftBoundArrow.height).toBeCloseTo(7, 0); + expect(leftBoundArrow.angle).toEqual(0); + expect(leftBoundArrow.startBinding).toBeNull(); + expect(leftBoundArrow.endBinding).toMatchObject(leftArrowBinding); + + expect(rightBoundArrow.x).toBeCloseTo(210); + expect(rightBoundArrow.y).toBeCloseTo( + (selectionHeight - 50) * (1 - scale) + 50, + ); + expect(rightBoundArrow.width).toBeCloseTo(100 * scale); + expect(rightBoundArrow.height).toBeCloseTo(0); + expect(rightBoundArrow.angle).toEqual(0); + expect(rightBoundArrow.startBinding).toBeNull(); + expect(rightBoundArrow.endBinding).toMatchObject(rightArrowBinding); + }); + + it("resizes with labeled arrows", async () => { + const topArrow = UI.createElement("arrow", { + x: 0, + y: 20, + width: 220, + height: 0, + }); + const topArrowLabel = await UI.editText(topArrow.get(), "lorem ipsum"); + + UI.clickTool("text"); + UI.clickByTitle("Large"); + const bottomArrow = UI.createElement("arrow", { + x: 0, + y: 80, + width: 220, + height: 0, + }); + const bottomArrowLabel = await UI.editText( + bottomArrow.get(), + "dolor\nsit amet", + ); + + const selectionWidth = 220; + const selectionTop = 20 - topArrowLabel.height / 2; + const move = [80, 0] as [number, number]; + const scale = move[0] / selectionWidth + 1; + + UI.resize([topArrow.get(), bottomArrow.get()], "se", move); + const topArrowLabelPos = LinearElementEditor.getBoundTextElementPosition( + topArrow, + topArrowLabel, + ); + const bottomArrowLabelPos = LinearElementEditor.getBoundTextElementPosition( + bottomArrow, + bottomArrowLabel, + ); + + expect(topArrow.x).toBeCloseTo(0); + expect(topArrow.y).toBeCloseTo(selectionTop + (20 - selectionTop) * scale); + expect(topArrow.width).toBeCloseTo(300); + expect(topArrow.points).toEqual([ + [0, 0], + [300, 0], + ]); + + expect(topArrowLabelPos.x + topArrowLabel.width / 2).toBeCloseTo( + topArrow.width / 2, + ); + expect(topArrowLabelPos.y + topArrowLabel.height / 2).toBeCloseTo( + topArrow.y, + ); + expect(topArrowLabel.fontSize).toBeCloseTo(20 * scale); + + expect(bottomArrow.x).toBeCloseTo(0); + expect(bottomArrow.y).toBeCloseTo( + selectionTop + (80 - selectionTop) * scale, + ); + expect(bottomArrow.width).toBeCloseTo(300); + expect(topArrow.points).toEqual([ + [0, 0], + [300, 0], + ]); + + expect(bottomArrowLabelPos.x + bottomArrowLabel.width / 2).toBeCloseTo( + bottomArrow.width / 2, + ); + expect(bottomArrowLabelPos.y + bottomArrowLabel.height / 2).toBeCloseTo( + bottomArrow.y, + ); + expect(bottomArrowLabel.fontSize).toBeCloseTo(28 * scale); + }); + + it("resizes with text elements", async () => { + const topText = UI.createElement("text", { position: 0 }); + await UI.editText(topText, "lorem ipsum"); + + UI.clickTool("text"); + UI.clickByTitle("Large"); + const bottomText = UI.createElement("text", { position: 40 }); + await UI.editText(bottomText, "dolor\nsit amet"); + + const selectionWidth = 40 + bottomText.width; + const selectionHeight = 40 + bottomText.height; + const move = [30, -40] as [number, number]; + const scale = Math.max( + 1 + move[0] / selectionWidth, + 1 - move[1] / selectionHeight, + ); + + UI.resize([topText, bottomText], "ne", move); + + expect(topText.x).toBeCloseTo(0); + expect(topText.y).toBeCloseTo(-selectionHeight * (scale - 1)); + expect(topText.fontSize).toBeCloseTo(20 * scale); + expect(topText.angle).toEqual(0); + + expect(bottomText.x).toBeCloseTo(40 * scale); + expect(bottomText.y).toBeCloseTo(40 - (selectionHeight - 40) * (scale - 1)); + expect(bottomText.fontSize).toBeCloseTo(28 * scale); + expect(bottomText.angle).toEqual(0); + }); + + it("resizes with images", () => { + const topImage = API.createElement({ + type: "image", + x: 0, + y: 0, + width: 200, + height: 100, + }); + const bottomImage = API.createElement({ + type: "image", + x: 30, + y: 150, + width: 120, + height: 80, + }); + h.elements = [topImage, bottomImage]; + + const selectionWidth = 200; + const selectionHeight = 230; + const move = [-50, -50] as [number, number]; + const scale = Math.max( + 1 + move[0] / selectionWidth, + 1 + move[1] / selectionHeight, + ); + + UI.resize([topImage, bottomImage], "se", move); + + expect(topImage.x).toBeCloseTo(0); + expect(topImage.y).toBeCloseTo(0); + expect(topImage.width).toBeCloseTo(200 * scale); + expect(topImage.height).toBeCloseTo(100 * scale); + expect(topImage.angle).toEqual(0); + expect(topImage.scale).toEqual([1, 1]); + + expect(bottomImage.x).toBeCloseTo(30 * scale); + expect(bottomImage.y).toBeCloseTo(150 * scale); + expect(bottomImage.width).toBeCloseTo(120 * scale); + expect(bottomImage.height).toBeCloseTo(80 * scale); + expect(bottomImage.angle).toEqual(0); + expect(bottomImage.scale).toEqual([1, 1]); + }); + + it("resizes from center", () => { + const rectangle = UI.createElement("rectangle", { + x: -200, + y: -140, + width: 120, + height: 100, + }); + const ellipse = UI.createElement("ellipse", { + position: 60, + width: 140, + height: 80, + }); + + const selectionWidth = 400; + const selectionHeight = 280; + const move = [-80, -80] as [number, number]; + const scale = Math.max( + 1 + (2 * move[0]) / selectionWidth, + 1 + (2 * move[1]) / selectionHeight, + ); + + UI.resize([rectangle, ellipse], "se", move, { alt: true }); + + expect(rectangle.x).toBeCloseTo(-200 * scale); + expect(rectangle.y).toBeCloseTo(-140 * scale); + expect(rectangle.width).toBeCloseTo(120 * scale); + expect(rectangle.height).toBeCloseTo(100 * scale); + expect(rectangle.angle).toEqual(0); + + expect(ellipse.x).toBeCloseTo(60 * scale); + expect(ellipse.y).toBeCloseTo(60 * scale); + expect(ellipse.width).toBeCloseTo(140 * scale); + expect(ellipse.height).toBeCloseTo(80 * scale); + expect(ellipse.angle).toEqual(0); + }); + + it("flips while resizing", async () => { + const image = API.createElement({ + type: "image", + x: 60, + y: 100, + width: 100, + height: 100, + angle: (Math.PI * 7) / 6, + }); + h.elements = [image]; + + const line = UI.createElement("line", { + x: 60, + y: 0, + points: [ + [0, 0], + [-40, 40], + [-20, 60], + [20, 20], + [40, 40], + [-20, 100], + [-60, 60], + ], + }); + + const rectangle = UI.createElement("rectangle", { + x: 180, + y: 60, + width: 160, + height: 80, + angle: Math.PI / 6, + }); + const rectLabel = await UI.editText(rectangle, "hello\nworld"); + + const boundArrow = UI.createElement("arrow", { + x: 380, + y: 240, + width: -60, + height: -80, + }); + const arrowLabel = await UI.editText(boundArrow, "test"); + + const selectionWidth = 380; + const move = [-800, 0] as [number, number]; + const scaleX = move[0] / selectionWidth + 1; + const scaleY = -scaleX; + const lineOrigBounds = getBoundsFromPoints(line); + + UI.resize([line, image, rectangle, boundArrow], "se", move); + const lineNewBounds = getBoundsFromPoints(line); + const arrowLabelPos = LinearElementEditor.getBoundTextElementPosition( + boundArrow, + arrowLabel, + ); + + expect(line.x).toBeCloseTo(60 * scaleX); + expect(line.y).toBeCloseTo(0); + expect(lineNewBounds[0]).toBeCloseTo( + (lineOrigBounds[2] - lineOrigBounds[0]) * scaleX, + ); + expect(lineNewBounds[1]).toBeCloseTo(0); + expect(lineNewBounds[3]).toBeCloseTo( + (lineOrigBounds[3] - lineOrigBounds[1]) * scaleY, + ); + expect(lineNewBounds[2]).toBeCloseTo(0); + expect(line.angle).toEqual(0); + + expect(image.x).toBeCloseTo((60 + 100) * scaleX); + expect(image.y).toBeCloseTo(100 * scaleY); + expect(image.width).toBeCloseTo(100 * -scaleX); + expect(image.height).toBeCloseTo(100 * scaleY); + expect(image.angle).toBeCloseTo((Math.PI * 5) / 6); + expect(image.scale).toEqual([1, 1]); + + expect(rectangle.x).toBeCloseTo((180 + 160) * scaleX); + expect(rectangle.y).toBeCloseTo(60 * scaleY); + expect(rectangle.width).toBeCloseTo(160 * -scaleX); + expect(rectangle.height).toBeCloseTo(80 * scaleY); + expect(rectangle.angle).toEqual((Math.PI * 11) / 6); + + expect(rectLabel.x + rectLabel.width / 2).toBeCloseTo( + rectangle.x + rectangle.width / 2, + ); + expect(rectLabel.y + rectLabel.height / 2).toBeCloseTo( + rectangle.y + rectangle.height / 2, + ); + expect(rectLabel.angle).toBeCloseTo(rectangle.angle); + expect(rectLabel.fontSize).toBeCloseTo(20 * scaleY); + + expect(boundArrow.x).toBeCloseTo(380 * scaleX); + expect(boundArrow.y).toBeCloseTo(240 * scaleY); + expect(boundArrow.points[1][0]).toBeCloseTo(-60 * scaleX); + expect(boundArrow.points[1][1]).toBeCloseTo(-80 * scaleY); + + expect(arrowLabelPos.x + arrowLabel.width / 2).toBeCloseTo( + boundArrow.x + boundArrow.points[1][0] / 2, + ); + expect(arrowLabelPos.y + arrowLabel.height / 2).toBeCloseTo( + boundArrow.y + boundArrow.points[1][1] / 2, + ); + expect(arrowLabel.angle).toEqual(0); + expect(arrowLabel.fontSize).toBeCloseTo(20 * scaleY); + }); +}); diff --git a/src/tests/rotate.test.tsx b/src/tests/rotate.test.tsx new file mode 100644 index 0000000000..0c598e7cc8 --- /dev/null +++ b/src/tests/rotate.test.tsx @@ -0,0 +1,81 @@ +import ReactDOM from "react-dom"; +import { render } from "./test-utils"; +import { reseed } from "../random"; +import { UI } from "./helpers/ui"; +import { Excalidraw } from "../packages/excalidraw/index"; +import { expect } from "vitest"; + +ReactDOM.unmountComponentAtNode(document.getElementById("root")!); + +beforeEach(() => { + localStorage.clear(); + reseed(7); +}); + +test("unselected bound arrow updates when rotating its target element", async () => { + await render(); + const rectangle = UI.createElement("rectangle", { + width: 200, + height: 100, + }); + const arrow = UI.createElement("arrow", { + x: -80, + y: 50, + width: 70, + height: 0, + }); + + expect(arrow.endBinding?.elementId).toEqual(rectangle.id); + + UI.rotate(rectangle, [60, 36], { shift: true }); + + expect(arrow.endBinding?.elementId).toEqual(rectangle.id); + expect(arrow.x).toBeCloseTo(-80); + expect(arrow.y).toBeCloseTo(50); + expect(arrow.width).toBeCloseTo(110.7, 1); + expect(arrow.height).toBeCloseTo(0); +}); + +test("unselected bound arrows update when rotating their target elements", async () => { + await render(); + const ellipse = UI.createElement("ellipse", { + x: 0, + y: 80, + width: 300, + height: 120, + }); + const ellipseArrow = UI.createElement("arrow", { + position: 0, + width: 40, + height: 80, + }); + const text = UI.createElement("text", { + position: 220, + }); + await UI.editText(text, "test"); + const textArrow = UI.createElement("arrow", { + x: 360, + y: 300, + width: -100, + height: -40, + }); + + expect(ellipseArrow.endBinding?.elementId).toEqual(ellipse.id); + expect(textArrow.endBinding?.elementId).toEqual(text.id); + + UI.rotate([ellipse, text], [-82, 23], { shift: true }); + + expect(ellipseArrow.endBinding?.elementId).toEqual(ellipse.id); + expect(ellipseArrow.x).toEqual(0); + expect(ellipseArrow.y).toEqual(0); + expect(ellipseArrow.points[0]).toEqual([0, 0]); + expect(ellipseArrow.points[1][0]).toBeCloseTo(48.5, 1); + expect(ellipseArrow.points[1][1]).toBeCloseTo(126.5, 1); + + expect(textArrow.endBinding?.elementId).toEqual(text.id); + expect(textArrow.x).toEqual(360); + expect(textArrow.y).toEqual(300); + expect(textArrow.points[0]).toEqual([0, 0]); + expect(textArrow.points[1][0]).toBeCloseTo(-94, 1); + expect(textArrow.points[1][1]).toBeCloseTo(-116.1, 1); +}); diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx index 4f1c27015a..f251126f81 100644 --- a/src/tests/selection.test.tsx +++ b/src/tests/selection.test.tsx @@ -480,7 +480,7 @@ describe("tool locking & selection", () => { expect(h.state.activeTool.locked).toBe(true); for (const { value } of Object.values(SHAPES)) { - if (value !== "image" && value !== "selection") { + if (value !== "image" && value !== "selection" && value !== "eraser") { const element = UI.createElement(value); expect(h.state.selectedElementIds[element.id]).not.toBe(true); } diff --git a/src/tests/utils.ts b/src/tests/utils.ts deleted file mode 100644 index 2c91c3fce7..0000000000 --- a/src/tests/utils.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - getTransformHandles, - TransformHandleDirection, -} from "../element/transformHandles"; -import { ExcalidrawElement } from "../element/types"; -import { Keyboard, KeyboardModifiers, Pointer } from "./helpers/ui"; - -const mouse = new Pointer("mouse"); -const { h } = window; - -export const resize = ( - element: ExcalidrawElement, - handleDir: TransformHandleDirection, - mouseMove: [number, number], - keyboardModifiers: KeyboardModifiers = {}, -) => { - mouse.select(element); - const handle = getTransformHandles(element, h.state.zoom, "mouse")[ - handleDir - ]!; - const clientX = handle[0] + handle[2] / 2; - const clientY = handle[1] + handle[3] / 2; - Keyboard.withModifierKeys(keyboardModifiers, () => { - mouse.reset(); - mouse.down(clientX, clientY); - mouse.move(mouseMove[0], mouseMove[1]); - mouse.up(); - }); -}; - -export const rotate = ( - element: ExcalidrawElement, - deltaX: number, - deltaY: number, - keyboardModifiers: KeyboardModifiers = {}, -) => { - mouse.select(element); - const handle = getTransformHandles(element, h.state.zoom, "mouse").rotation!; - const clientX = handle[0] + handle[2] / 2; - const clientY = handle[1] + handle[3] / 2; - - Keyboard.withModifierKeys(keyboardModifiers, () => { - mouse.reset(); - mouse.down(clientX, clientY); - mouse.move(clientX + deltaX, clientY + deltaY); - mouse.up(); - }); -}; From 44d9d5fcacfdb72da32cd7a8f0d711fece33e1c8 Mon Sep 17 00:00:00 2001 From: mazijian-pp <69623008+mazijian-pp@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:29:54 +0800 Subject: [PATCH 029/144] fix: wysiwyg left in undefined state on reload (#7123) --- src/data/restore.ts | 11 ++++++++++- src/element/mutateElement.ts | 4 ++-- src/element/textWysiwyg.tsx | 3 ++- src/tests/data/__snapshots__/restore.test.ts.snap | 8 ++++---- src/tests/data/restore.test.ts | 3 +++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/data/restore.ts b/src/data/restore.ts index bda6818f6f..d9ea999ffc 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -189,7 +189,7 @@ const restoreElement = ( fontSize = parseFloat(fontPx); fontFamily = getFontFamilyByName(_fontFamily); } - const text = element.text ?? ""; + const text = (typeof element.text === "string" && element.text) || ""; // line-height might not be specified either when creating elements // programmatically, or when importing old diagrams. @@ -222,9 +222,17 @@ const restoreElement = ( baseline, }); + // if empty text, mark as deleted. We keep in array + // for data integrity purposes (collab etc.) + if (!text && !element.isDeleted) { + element = { ...element, originalText: text, isDeleted: true }; + element = bumpVersion(element); + } + if (refreshDimensions) { element = { ...element, ...refreshTextDimensions(element) }; } + return element; case "freedraw": { return restoreElementWithProperties(element, { @@ -299,6 +307,7 @@ const restoreElement = ( // We also don't want to throw, but instead return void so we filter // out these unsupported elements from the restored array. } + return null; }; /** diff --git a/src/element/mutateElement.ts b/src/element/mutateElement.ts index 0e01a080ae..d4dbd8cd25 100644 --- a/src/element/mutateElement.ts +++ b/src/element/mutateElement.ts @@ -140,8 +140,8 @@ export const newElementWith = ( * * NOTE: does not trigger re-render. */ -export const bumpVersion = ( - element: Mutable, +export const bumpVersion = >( + element: T, version?: ExcalidrawElement["version"], ) => { element.version = (version ?? element.version) + 1; diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index d7fed6981d..52f89e0b91 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -584,7 +584,7 @@ export const textWysiwyg = ({ window.removeEventListener("pointerdown", onPointerDown); window.removeEventListener("pointerup", bindBlurEvent); window.removeEventListener("blur", handleSubmit); - + window.removeEventListener("beforeunload", handleSubmit); unbindUpdate(); editable.remove(); @@ -701,6 +701,7 @@ export const textWysiwyg = ({ passive: false, capture: true, }); + window.addEventListener("beforeunload", handleSubmit); excalidrawContainer ?.querySelector(".excalidraw-textEditorContainer")! .appendChild(editable); diff --git a/src/tests/data/__snapshots__/restore.test.ts.snap b/src/tests/data/__snapshots__/restore.test.ts.snap index 881940210c..c5482c2157 100644 --- a/src/tests/data/__snapshots__/restore.test.ts.snap +++ b/src/tests/data/__snapshots__/restore.test.ts.snap @@ -340,12 +340,12 @@ exports[`restoreElements > should restore text element correctly with unknown fo "groupIds": [], "height": 100, "id": "id-text01", - "isDeleted": false, + "isDeleted": true, "lineHeight": 1.25, "link": null, "locked": false, "opacity": 100, - "originalText": "test", + "originalText": "", "roughness": 1, "roundness": { "type": 3, @@ -358,8 +358,8 @@ exports[`restoreElements > should restore text element correctly with unknown fo "textAlign": "left", "type": "text", "updated": 1, - "version": 1, - "versionNonce": 0, + "version": 2, + "versionNonce": Any, "verticalAlign": "top", "width": 100, "x": 0, diff --git a/src/tests/data/restore.test.ts b/src/tests/data/restore.test.ts index 0019b0e8bc..0a0d9b1518 100644 --- a/src/tests/data/restore.test.ts +++ b/src/tests/data/restore.test.ts @@ -86,12 +86,15 @@ describe("restoreElements", () => { textElement.text = null; textElement.font = "10 unknown"; + expect(textElement.isDeleted).toBe(false); const restoredText = restore.restoreElements( [textElement], null, )[0] as ExcalidrawTextElement; + expect(restoredText.isDeleted).toBe(true); expect(restoredText).toMatchSnapshot({ seed: expect.any(Number), + versionNonce: expect.any(Number), }); }); From aaf73c8ff31f58cfd8274e8463e9c960d218c8b4 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Mon, 16 Oct 2023 00:19:46 +0200 Subject: [PATCH 030/144] fix: double image dialog shown on insert (#7152) --- src/components/Actions.tsx | 14 +++++++------- src/components/App.tsx | 25 ++++++++++++++++--------- src/components/LayerUI.tsx | 26 ++------------------------ src/components/MobileMenu.tsx | 11 +---------- 4 files changed, 26 insertions(+), 50 deletions(-) diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index cd5993097e..4010f98ab4 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { ActionManager } from "../actions/manager"; import { getNonDeletedElements } from "../element"; -import { ExcalidrawElement, PointerType } from "../element/types"; +import { ExcalidrawElement } from "../element/types"; import { t } from "../i18n"; import { useDevice } from "../components/App"; import { @@ -213,15 +213,11 @@ export const SelectedShapeActions = ({ }; export const ShapesSwitcher = ({ - interactiveCanvas, activeTool, - onImageAction, appState, app, }: { - interactiveCanvas: HTMLCanvasElement | null; activeTool: UIAppState["activeTool"]; - onImageAction: (data: { pointerType: PointerType | null }) => void; appState: UIAppState; app: AppClassProperties; }) => { @@ -263,9 +259,13 @@ export const ShapesSwitcher = ({ if (appState.activeTool.type !== value) { trackEvent("toolbar", value, "ui"); } - app.setActiveTool({ type: value }); if (value === "image") { - onImageAction({ pointerType }); + app.setActiveTool({ + type: value, + insertOnCanvasDirectly: pointerType !== "mouse", + }); + } else { + app.setActiveTool({ type: value }); } }} /> diff --git a/src/components/App.tsx b/src/components/App.tsx index 4ad7b889e9..d0f618d894 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1188,7 +1188,6 @@ class App extends React.Component { > { this.state.zenModeEnabled } UIOptions={this.props.UIOptions} - onImageAction={this.onImageAction} onExportImage={this.onExportImage} renderWelcomeScreen={ !this.state.isLoading && @@ -3135,9 +3133,13 @@ class App extends React.Component { setActiveTool = ( tool: - | { - type: ToolType; - } + | ( + | { type: Exclude } + | { + type: Extract; + insertOnCanvasDirectly?: boolean; + } + ) | { type: "custom"; customType: string }, ) => { const nextActiveTool = updateActiveTool(this.state, tool); @@ -3153,7 +3155,10 @@ class App extends React.Component { this.setState({ suggestedBindings: [] }); } if (nextActiveTool.type === "image") { - this.onImageAction(); + this.onImageAction({ + insertOnCanvasDirectly: + (tool.type === "image" && tool.insertOnCanvasDirectly) ?? false, + }); } this.setState((prevState) => { @@ -7353,9 +7358,11 @@ class App extends React.Component { } }; - private onImageAction = async ( - { insertOnCanvasDirectly } = { insertOnCanvasDirectly: false }, - ) => { + private onImageAction = async ({ + insertOnCanvasDirectly, + }: { + insertOnCanvasDirectly: boolean; + }) => { try { const clientX = this.state.width / 2 + this.state.offsetLeft; const clientY = this.state.height / 2 + this.state.offsetTop; diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 59ac60a763..580d243754 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -62,7 +62,6 @@ interface LayerUIProps { appState: UIAppState; files: BinaryFiles; canvas: HTMLCanvasElement; - interactiveCanvas: HTMLCanvasElement | null; setAppState: React.Component["setState"]; elements: readonly NonDeletedExcalidrawElement[]; onLockToggle: () => void; @@ -73,7 +72,6 @@ interface LayerUIProps { renderTopRightUI?: ExcalidrawProps["renderTopRightUI"]; renderCustomStats?: ExcalidrawProps["renderCustomStats"]; UIOptions: AppProps["UIOptions"]; - onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void; onExportImage: AppClassProperties["onExportImage"]; renderWelcomeScreen: boolean; children?: React.ReactNode; @@ -123,7 +121,6 @@ const LayerUI = ({ setAppState, elements, canvas, - interactiveCanvas, onLockToggle, onHandToolToggle, onPenModeToggle, @@ -131,7 +128,6 @@ const LayerUI = ({ renderTopRightUI, renderCustomStats, UIOptions, - onImageAction, onExportImage, renderWelcomeScreen, children, @@ -280,14 +276,8 @@ const LayerUI = ({ { - onImageAction({ - insertOnCanvasDirectly: pointerType !== "mouse", - }); - }} /> @@ -472,8 +462,6 @@ const LayerUI = ({ onLockToggle={onLockToggle} onHandToolToggle={onHandToolToggle} onPenModeToggle={onPenModeToggle} - interactiveCanvas={interactiveCanvas} - onImageAction={onImageAction} renderTopRightUI={renderTopRightUI} renderCustomStats={renderCustomStats} renderSidebars={renderSidebars} @@ -560,18 +548,8 @@ const areEqual = (prevProps: LayerUIProps, nextProps: LayerUIProps) => { return false; } - const { - canvas: _pC, - interactiveCanvas: _pIC, - appState: prevAppState, - ...prev - } = prevProps; - const { - canvas: _nC, - interactiveCanvas: _nIC, - appState: nextAppState, - ...next - } = nextProps; + const { canvas: _pC, appState: prevAppState, ...prev } = prevProps; + const { canvas: _nC, appState: nextAppState, ...next } = nextProps; return ( isShallowEqual( diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index b7f0ba3338..bb26fe7136 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -36,9 +36,7 @@ type MobileMenuProps = { onLockToggle: () => void; onHandToolToggle: () => void; onPenModeToggle: () => void; - interactiveCanvas: HTMLCanvasElement | null; - onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void; renderTopRightUI?: ( isMobile: boolean, appState: UIAppState, @@ -58,8 +56,7 @@ export const MobileMenu = ({ onLockToggle, onHandToolToggle, onPenModeToggle, - interactiveCanvas, - onImageAction, + renderTopRightUI, renderCustomStats, renderSidebars, @@ -85,14 +82,8 @@ export const MobileMenu = ({ { - onImageAction({ - insertOnCanvasDirectly: pointerType !== "mouse", - }); - }} /> From 5b94cffc74668c59dbc00c11b780774c6b97e7a4 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Mon, 16 Oct 2023 11:38:57 +0200 Subject: [PATCH 031/144] fix: ensure `ClipboardItem` created in the same tick to fix safari (#7066) --- src/data/blob.ts | 9 ++++++--- src/data/index.ts | 8 +------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/data/blob.ts b/src/data/blob.ts index 922400303f..cb2c051972 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -8,7 +8,7 @@ import { t } from "../i18n"; import { calculateScrollCenter } from "../scene"; import { AppState, DataURL, LibraryItem } from "../types"; import { ValueOf } from "../utility-types"; -import { bytesToHexString } from "../utils"; +import { bytesToHexString, isPromiseLike } from "../utils"; import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem"; import { isValidExcalidrawData, isValidLibrary } from "./json"; import { restore, restoreLibraryItems } from "./restore"; @@ -207,10 +207,13 @@ export const loadLibraryFromBlob = async ( }; export const canvasToBlob = async ( - canvas: HTMLCanvasElement, + canvas: HTMLCanvasElement | Promise, ): Promise => { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { try { + if (isPromiseLike(canvas)) { + canvas = await canvas; + } canvas.toBlob((blob) => { if (!blob) { return reject( diff --git a/src/data/index.ts b/src/data/index.ts index 20ba75ebe5..29eedb2a7f 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -66,17 +66,14 @@ export const exportCanvas = async ( } } - const tempCanvas = await exportToCanvas(elements, appState, files, { + const tempCanvas = exportToCanvas(elements, appState, files, { exportBackground, viewBackgroundColor, exportPadding, }); - tempCanvas.style.display = "none"; - document.body.appendChild(tempCanvas); if (type === "png") { let blob = await canvasToBlob(tempCanvas); - tempCanvas.remove(); if (appState.exportEmbedScene) { blob = await ( await import(/* webpackChunkName: "image" */ "./image") @@ -114,11 +111,8 @@ export const exportCanvas = async ( } else { throw new Error(t("alerts.couldNotCopyToClipboard")); } - } finally { - tempCanvas.remove(); } } else { - tempCanvas.remove(); // shouldn't happen throw new Error("Unsupported export type"); } From dde3dac931cd0d1db4c27f2809261f4e0e3843ca Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 17 Oct 2023 18:18:20 +0200 Subject: [PATCH 032/144] feat: remove bound-arrows from frames (#7157) --- dev-docs/docs/codebase/frames.mdx | 10 ++++++++ src/actions/actionDuplicateSelection.tsx | 11 +++++++-- src/components/App.tsx | 11 ++++----- src/data/restore.ts | 19 ++++++++++----- src/element/binding.ts | 10 ++++++++ src/frame.ts | 30 +++++++++++++++++++++++- src/renderer/renderScene.ts | 6 ++++- 7 files changed, 81 insertions(+), 16 deletions(-) diff --git a/dev-docs/docs/codebase/frames.mdx b/dev-docs/docs/codebase/frames.mdx index 45a551f230..0dfd99ea2a 100644 --- a/dev-docs/docs/codebase/frames.mdx +++ b/dev-docs/docs/codebase/frames.mdx @@ -20,3 +20,13 @@ Frames should be ordered where frame children come first, followed by the frame ``` If not oredered correctly, the editor will still function, but the elements may not be rendered and clipped correctly. Further, the renderer relies on this ordering for performance optimizations. + +# Arrows + +An arrow can be a child of a frame only if it has no binding (either start or end) to any other element, regardless of whether the bound element is inside the frame or not. + +This ensures that when an arrow is bound to an element outside the frame, it's rendered and behaves correctly. + +Therefore, when an arrow (that's a child of a frame) gets bound to an element, it's automatically removed from the frame. + +Bound-arrow is duplicated alongside a frame only if the arrow start is bound to an element within that frame. diff --git a/src/actions/actionDuplicateSelection.tsx b/src/actions/actionDuplicateSelection.tsx index a21260d5bb..fec91c4a59 100644 --- a/src/actions/actionDuplicateSelection.tsx +++ b/src/actions/actionDuplicateSelection.tsx @@ -155,7 +155,12 @@ const duplicateElements = ( groupId, ).flatMap((element) => isFrameElement(element) - ? [...getFrameElements(elements, element.id), element] + ? [ + ...getFrameElements(elements, element.id, { + includeBoundArrows: true, + }), + element, + ] : [element], ); @@ -181,7 +186,9 @@ const duplicateElements = ( continue; } if (isElementAFrame) { - const elementsInFrame = getFrameElements(sortedElements, element.id); + const elementsInFrame = getFrameElements(sortedElements, element.id, { + includeBoundArrows: true, + }); elementsWithClones.push( ...markAsProcessed([ diff --git a/src/components/App.tsx b/src/components/App.tsx index d0f618d894..c9d58b35f1 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2431,18 +2431,12 @@ class App extends React.Component { const lineHeight = getDefaultLineHeight(textElementProps.fontFamily); if (text.length) { - const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ - x, - y: currentY, - }); - const element = newTextElement({ ...textElementProps, x, y: currentY, text, lineHeight, - frameId: topLayerFrame ? topLayerFrame.id : null, }); acc.push(element); currentY += element.height + LINE_GAP; @@ -3456,6 +3450,11 @@ class App extends React.Component { return getElementsAtPosition(elements, (element) => hitTest(element, this.state, this.frameNameBoundsCache, x, y), ).filter((element) => { + // arrows don't clip even if they're children of frames, + // so always allow hitbox regardless of beinging contained in frame + if (isArrowElement(element)) { + return true; + } // hitting a frame's element from outside the frame is not considered a hit const containingFrame = getContainingFrame(element); return containingFrame && diff --git a/src/data/restore.ts b/src/data/restore.ts index d9ea999ffc..e951dbf5ea 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -43,6 +43,7 @@ import { measureBaseline, } from "../element/textElement"; import { normalizeLink } from "./url"; +import { isValidFrameChild } from "../frame"; type RestoredAppState = Omit< AppState, @@ -396,7 +397,7 @@ const repairBoundElement = ( }; /** - * Remove an element's frameId if its containing frame is non-existent + * resets `frameId` if no longer applicable. * * NOTE mutates elements. */ @@ -404,12 +405,16 @@ const repairFrameMembership = ( element: Mutable, elementsMap: Map>, ) => { - if (element.frameId) { - const containingFrame = elementsMap.get(element.frameId); + if (!element.frameId) { + return; + } - if (!containingFrame) { - element.frameId = null; - } + if ( + !isValidFrameChild(element) || + // target frame not exists + !elementsMap.get(element.frameId) + ) { + element.frameId = null; } }; @@ -453,6 +458,8 @@ export const restoreElements = ( // repair binding. Mutates elements. const restoredElementsMap = arrayToMap(restoredElements); for (const element of restoredElements) { + // repair frame membership *after* bindings we do in restoreElement() + // since we rely on bindings to be correct if (element.frameId) { repairFrameMembership(element, restoredElementsMap); } diff --git a/src/element/binding.ts b/src/element/binding.ts index 3f6cf0022d..6b39e1afba 100644 --- a/src/element/binding.ts +++ b/src/element/binding.ts @@ -27,6 +27,7 @@ import { LinearElementEditor } from "./linearElementEditor"; import { arrayToMap, tupleToCoors } from "../utils"; import { KEYS } from "../keys"; import { getBoundTextElement, handleBindTextResize } from "./textElement"; +import { isValidFrameChild } from "../frame"; export type SuggestedBinding = | NonDeleted @@ -211,6 +212,15 @@ export const bindLinearElement = ( }), }); } + if (linearElement.frameId && !isValidFrameChild(linearElement)) { + mutateElement( + linearElement, + { + frameId: null, + }, + false, + ); + } }; // Don't bind both ends of a simple segment diff --git a/src/frame.ts b/src/frame.ts index 6dccedd2b1..40b08896c7 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -323,7 +323,24 @@ export const groupByFrames = (elements: readonly ExcalidrawElement[]) => { export const getFrameElements = ( allElements: ExcalidrawElementsIncludingDeleted, frameId: string, -) => allElements.filter((element) => element.frameId === frameId); + opts?: { includeBoundArrows?: boolean }, +) => { + return allElements.filter((element) => { + if (element.frameId === frameId) { + return true; + } + if (opts?.includeBoundArrows && element.type === "arrow") { + const bindingId = element.startBinding?.elementId; + if (bindingId) { + const boundElement = Scene.getScene(element)?.getElement(bindingId); + if (boundElement?.frameId === frameId) { + return true; + } + } + } + return false; + }); +}; export const getElementsInResizingFrame = ( allElements: ExcalidrawElementsIncludingDeleted, @@ -451,6 +468,14 @@ export const getContainingFrame = ( return null; }; +export const isValidFrameChild = (element: ExcalidrawElement) => { + return ( + element.type !== "frame" && + // arrows that are bound to elements cannot be frame children + (element.type !== "arrow" || (!element.startBinding && !element.endBinding)) + ); +}; + // --------------------------- Frame Operations ------------------------------- /** @@ -489,6 +514,9 @@ export const addElementsToFrame = ( elementsToAdd, )) { if (!currTargetFrameChildrenMap.has(element.id)) { + if (!isValidFrameChild(element)) { + continue; + } finalElementsToAdd.push(element); } diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 6395ed0e7a..f7fb35cc57 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -69,6 +69,7 @@ import { } from "../element/Hyperlink"; import { renderSnaps } from "./renderSnaps"; import { + isArrowElement, isEmbeddableElement, isFrameElement, isLinearElement, @@ -984,7 +985,10 @@ const _renderStaticScene = ({ // TODO do we need to check isElementInFrame here? if (frame && isElementInFrame(element, elements, appState)) { - frameClip(frame, context, renderConfig, appState); + // do not clip arrows + if (!isArrowElement(element)) { + frameClip(frame, context, renderConfig, appState); + } } renderElement(element, rc, context, renderConfig, appState); context.restore(); From 63650f82d134bbccfe3848ff2a42d69da4d9d0cd Mon Sep 17 00:00:00 2001 From: Lakshya Satpal <81241551+LakshyaSatpal@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:44:23 +0530 Subject: [PATCH 033/144] feat: Added Copy/Paste from Google Docs (#7136) Co-authored-by: dwelle --- src/clipboard.test.ts | 19 ++---- src/clipboard.ts | 79 +++++++++++++++++++--- src/components/App.tsx | 125 ++++++++++++++++++++++++++++++----- src/data/blob.ts | 25 +++++++ src/element/embeddable.ts | 1 + src/locales/en.json | 1 + src/tests/clipboard.test.tsx | 46 ++++--------- src/tests/flip.test.tsx | 2 +- src/tests/test-utils.ts | 11 ++- 9 files changed, 232 insertions(+), 77 deletions(-) diff --git a/src/clipboard.test.ts b/src/clipboard.test.ts index 142038e8e8..f1717464b5 100644 --- a/src/clipboard.test.ts +++ b/src/clipboard.test.ts @@ -1,26 +1,21 @@ import { parseClipboard } from "./clipboard"; +import { createPasteEvent } from "./tests/test-utils"; describe("Test parseClipboard", () => { it("should parse valid json correctly", async () => { let text = "123"; - let clipboardData = await parseClipboard({ - //@ts-ignore - clipboardData: { - getData: () => text, - }, - }); + let clipboardData = await parseClipboard( + createPasteEvent({ "text/plain": text }), + ); expect(clipboardData.text).toBe(text); text = "[123]"; - clipboardData = await parseClipboard({ - //@ts-ignore - clipboardData: { - getData: () => text, - }, - }); + clipboardData = await parseClipboard( + createPasteEvent({ "text/plain": text }), + ); expect(clipboardData.text).toBe(text); }); diff --git a/src/clipboard.ts b/src/clipboard.ts index c0deb93af2..bc2167a094 100644 --- a/src/clipboard.ts +++ b/src/clipboard.ts @@ -18,11 +18,14 @@ type ElementsClipboard = { files: BinaryFiles | undefined; }; +export type PastedMixedContent = { type: "text" | "imageUrl"; value: string }[]; + export interface ClipboardData { spreadsheet?: Spreadsheet; elements?: readonly ExcalidrawElement[]; files?: BinaryFiles; text?: string; + mixedContent?: PastedMixedContent; errorMessage?: string; programmaticAPI?: boolean; } @@ -142,22 +145,74 @@ const parsePotentialSpreadsheet = ( return null; }; +/** internal, specific to parsing paste events. Do not reuse. */ +function parseHTMLTree(el: ChildNode) { + let result: PastedMixedContent = []; + for (const node of el.childNodes) { + if (node.nodeType === 3) { + const text = node.textContent?.trim(); + if (text) { + result.push({ type: "text", value: text }); + } + } else if (node instanceof HTMLImageElement) { + const url = node.getAttribute("src"); + if (url && url.startsWith("http")) { + result.push({ type: "imageUrl", value: url }); + } + } else { + result = result.concat(parseHTMLTree(node)); + } + } + return result; +} + +const maybeParseHTMLPaste = (event: ClipboardEvent) => { + const html = event.clipboardData?.getData("text/html"); + + if (!html) { + return null; + } + + try { + const doc = new DOMParser().parseFromString(html, "text/html"); + + const content = parseHTMLTree(doc.body); + + if (content.length) { + return content; + } + } catch (error: any) { + console.error(`error in parseHTMLFromPaste: ${error.message}`); + } + + return null; +}; + /** * Retrieves content from system clipboard (either from ClipboardEvent or * via async clipboard API if supported) */ -export const getSystemClipboard = async ( +const getSystemClipboard = async ( event: ClipboardEvent | null, -): Promise => { + isPlainPaste = false, +): Promise< + | { type: "text"; value: string } + | { type: "mixedContent"; value: PastedMixedContent } +> => { try { + const mixedContent = !isPlainPaste && event && maybeParseHTMLPaste(event); + if (mixedContent) { + return { type: "mixedContent", value: mixedContent }; + } + const text = event ? event.clipboardData?.getData("text/plain") : probablySupportsClipboardReadText && (await navigator.clipboard.readText()); - return (text || "").trim(); + return { type: "text", value: (text || "").trim() }; } catch { - return ""; + return { type: "text", value: "" }; } }; @@ -168,14 +223,20 @@ export const parseClipboard = async ( event: ClipboardEvent | null, isPlainPaste = false, ): Promise => { - const systemClipboard = await getSystemClipboard(event); + const systemClipboard = await getSystemClipboard(event, isPlainPaste); + + if (systemClipboard.type === "mixedContent") { + return { + mixedContent: systemClipboard.value, + }; + } // if system clipboard empty, couldn't be resolved, or contains previously // copied excalidraw scene as SVG, fall back to previously copied excalidraw // elements if ( !systemClipboard || - (!isPlainPaste && systemClipboard.includes(SVG_EXPORT_TAG)) + (!isPlainPaste && systemClipboard.value.includes(SVG_EXPORT_TAG)) ) { return getAppClipboard(); } @@ -183,7 +244,7 @@ export const parseClipboard = async ( // if system clipboard contains spreadsheet, use it even though it's // technically possible it's staler than in-app clipboard const spreadsheetResult = - !isPlainPaste && parsePotentialSpreadsheet(systemClipboard); + !isPlainPaste && parsePotentialSpreadsheet(systemClipboard.value); if (spreadsheetResult) { return spreadsheetResult; @@ -192,7 +253,7 @@ export const parseClipboard = async ( const appClipboardData = getAppClipboard(); try { - const systemClipboardData = JSON.parse(systemClipboard); + const systemClipboardData = JSON.parse(systemClipboard.value); const programmaticAPI = systemClipboardData.type === EXPORT_DATA_TYPES.excalidrawClipboardWithAPI; if (clipboardContainsElements(systemClipboardData)) { @@ -216,7 +277,7 @@ export const parseClipboard = async ( ? JSON.stringify(appClipboardData.elements, null, 2) : undefined, } - : { text: systemClipboard }; + : { text: systemClipboard.value }; }; export const copyBlobToClipboardAsPng = async (blob: Blob | Promise) => { diff --git a/src/components/App.tsx b/src/components/App.tsx index c9d58b35f1..9aa23d62b3 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -47,7 +47,7 @@ import { isEraserActive, isHandToolActive, } from "../appState"; -import { parseClipboard } from "../clipboard"; +import { PastedMixedContent, parseClipboard } from "../clipboard"; import { APP_NAME, CURSOR_TYPE, @@ -275,6 +275,7 @@ import { generateIdFromFile, getDataURL, getFileFromEvent, + ImageURLToFile, isImageFileHandle, isSupportedImageFile, loadSceneOrLibraryFromBlob, @@ -2183,21 +2184,6 @@ class App extends React.Component { return; } - // must be called in the same frame (thus before any awaits) as the paste - // event else some browsers (FF...) will clear the clipboardData - // (something something security) - let file = event?.clipboardData?.files[0]; - - const data = await parseClipboard(event, isPlainPaste); - if (!file && data.text && !isPlainPaste) { - const string = data.text.trim(); - if (string.startsWith("")) { - // ignore SVG validation/normalization which will be done during image - // initialization - file = SVGStringToFile(string); - } - } - const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords( { clientX: this.lastViewportPosition.x, @@ -2206,6 +2192,29 @@ class App extends React.Component { this.state, ); + // must be called in the same frame (thus before any awaits) as the paste + // event else some browsers (FF...) will clear the clipboardData + // (something something security) + let file = event?.clipboardData?.files[0]; + + const data = await parseClipboard(event, isPlainPaste); + if (!file && !isPlainPaste) { + if (data.mixedContent) { + return this.addElementsFromMixedContentPaste(data.mixedContent, { + isPlainPaste, + sceneX, + sceneY, + }); + } else if (data.text) { + const string = data.text.trim(); + if (string.startsWith("")) { + // ignore SVG validation/normalization which will be done during image + // initialization + file = SVGStringToFile(string); + } + } + } + // prefer spreadsheet data over image file (MS Office/Libre Office) if (isSupportedImageFile(file) && !data.spreadsheet) { const imageElement = this.createImageElement({ sceneX, sceneY }); @@ -2259,6 +2268,7 @@ class App extends React.Component { }); } else if (data.text) { const maybeUrl = extractSrc(data.text); + if ( !isPlainPaste && embeddableURLValidator(maybeUrl, this.props.validateEmbeddable) && @@ -2393,6 +2403,85 @@ class App extends React.Component { this.setActiveTool({ type: "selection" }); }; + // TODO rewrite this to paste both text & images at the same time if + // pasted data contains both + private async addElementsFromMixedContentPaste( + mixedContent: PastedMixedContent, + { + isPlainPaste, + sceneX, + sceneY, + }: { isPlainPaste: boolean; sceneX: number; sceneY: number }, + ) { + if ( + !isPlainPaste && + mixedContent.some((node) => node.type === "imageUrl") + ) { + const imageURLs = mixedContent + .filter((node) => node.type === "imageUrl") + .map((node) => node.value); + const responses = await Promise.all( + imageURLs.map(async (url) => { + try { + return { file: await ImageURLToFile(url) }; + } catch (error: any) { + return { errorMessage: error.message as string }; + } + }), + ); + let y = sceneY; + let firstImageYOffsetDone = false; + const nextSelectedIds: Record = {}; + for (const response of responses) { + if (response.file) { + const imageElement = this.createImageElement({ + sceneX, + sceneY: y, + }); + + const initializedImageElement = await this.insertImageElement( + imageElement, + response.file, + ); + if (initializedImageElement) { + // vertically center first image in the batch + if (!firstImageYOffsetDone) { + firstImageYOffsetDone = true; + y -= initializedImageElement.height / 2; + } + // hack to reset the `y` coord because we vertically center during + // insertImageElement + mutateElement(initializedImageElement, { y }, false); + + y = imageElement.y + imageElement.height + 25; + + nextSelectedIds[imageElement.id] = true; + } + } + } + + this.setState({ + selectedElementIds: makeNextSelectedElementIds( + nextSelectedIds, + this.state, + ), + }); + + const error = responses.find((response) => !!response.errorMessage); + if (error && error.errorMessage) { + this.setState({ errorMessage: error.errorMessage }); + } + } else { + const textNodes = mixedContent.filter((node) => node.type === "text"); + if (textNodes.length) { + this.addTextFromPaste( + textNodes.map((node) => node.value).join("\n\n"), + isPlainPaste, + ); + } + } + } + private addTextFromPaste(text: string, isPlainPaste = false) { const { x, y } = viewportCoordsToSceneCoords( { @@ -4401,6 +4490,7 @@ class App extends React.Component { setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO); } } + private handleCanvasPointerDown = ( event: React.PointerEvent, ) => { @@ -7302,7 +7392,7 @@ class App extends React.Component { this.scene.addNewElement(imageElement); try { - await this.initializeImage({ + return await this.initializeImage({ imageFile, imageElement, showCursorImagePreview, @@ -7315,6 +7405,7 @@ class App extends React.Component { this.setState({ errorMessage: error.message || t("errors.imageInsertError"), }); + return null; } }; diff --git a/src/data/blob.ts b/src/data/blob.ts index cb2c051972..81ce340fe1 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -327,6 +327,31 @@ export const SVGStringToFile = (SVGString: string, filename: string = "") => { }) as File & { type: typeof MIME_TYPES.svg }; }; +export const ImageURLToFile = async ( + imageUrl: string, + filename: string = "", +): Promise => { + let response; + try { + response = await fetch(imageUrl); + } catch (error: any) { + throw new Error(t("errors.failedToFetchImage")); + } + + if (!response.ok) { + throw new Error(t("errors.failedToFetchImage")); + } + + const blob = await response.blob(); + + if (blob.type && isSupportedImageFile(blob)) { + const name = filename || blob.name || ""; + return new File([blob], name, { type: blob.type }); + } + + throw new Error(t("errors.unsupportedFileType")); +}; + export const getFileFromEvent = async ( event: React.DragEvent, ) => { diff --git a/src/element/embeddable.ts b/src/element/embeddable.ts index 4aa6f0fdc0..615fb99a3e 100644 --- a/src/element/embeddable.ts +++ b/src/element/embeddable.ts @@ -28,6 +28,7 @@ const embeddedLinkCache = new Map(); const RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/; + const RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/; const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/; diff --git a/src/locales/en.json b/src/locales/en.json index f2e6b601a0..19dbf81f03 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -203,6 +203,7 @@ "imageInsertError": "Couldn't insert image. Try again later...", "fileTooBig": "File is too big. Maximum allowed size is {{maxSize}}.", "svgImageInsertError": "Couldn't insert SVG image. The SVG markup looks invalid.", + "failedToFetchImage": "Failed to fetch image.", "invalidSVGString": "Invalid SVG.", "cannotResolveCollabServer": "Couldn't connect to the collab server. Please reload the page and try again.", "importLibraryError": "Couldn't load library", diff --git a/src/tests/clipboard.test.tsx b/src/tests/clipboard.test.tsx index 4a55e0689f..0ea480ad64 100644 --- a/src/tests/clipboard.test.tsx +++ b/src/tests/clipboard.test.tsx @@ -35,22 +35,14 @@ vi.mock("../keys.ts", async (importOriginal) => { }; }); -const setClipboardText = (text: string) => { - Object.assign(navigator, { - clipboard: { - readText: () => text, - }, +const sendPasteEvent = (text: string) => { + const clipboardEvent = createPasteEvent({ + "text/plain": text, }); -}; - -const sendPasteEvent = (text?: string) => { - const clipboardEvent = createPasteEvent( - text || (() => window.navigator.clipboard.readText()), - ); document.dispatchEvent(clipboardEvent); }; -const pasteWithCtrlCmdShiftV = (text?: string) => { +const pasteWithCtrlCmdShiftV = (text: string) => { Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { //triggering keydown with an empty clipboard Keyboard.keyPress(KEYS.V); @@ -59,7 +51,7 @@ const pasteWithCtrlCmdShiftV = (text?: string) => { }); }; -const pasteWithCtrlCmdV = (text?: string) => { +const pasteWithCtrlCmdV = (text: string) => { Keyboard.withModifierKeys({ ctrl: true }, () => { //triggering keydown with an empty clipboard Keyboard.keyPress(KEYS.V); @@ -86,7 +78,6 @@ beforeEach(async () => { initialData={{ appState: { zoom: { value: 1 as NormalizedZoomValue } } }} />, ); - setClipboardText(""); Object.assign(document, { elementFromPoint: () => GlobalTestState.canvas, }); @@ -120,8 +111,7 @@ describe("general paste behavior", () => { describe("paste text as single lines", () => { it("should create an element for each line when copying with Ctrl/Cmd+V", async () => { const text = "sajgfakfn\naaksfnknas\nakefnkasf"; - setClipboardText(text); - pasteWithCtrlCmdV(); + pasteWithCtrlCmdV(text); await waitFor(() => { expect(h.elements.length).toEqual(text.split("\n").length); }); @@ -129,8 +119,7 @@ describe("paste text as single lines", () => { it("should ignore empty lines when creating an element for each line", async () => { const text = "\n\nsajgfakfn\n\n\naaksfnknas\n\nakefnkasf\n\n\n"; - setClipboardText(text); - pasteWithCtrlCmdV(); + pasteWithCtrlCmdV(text); await waitFor(() => { expect(h.elements.length).toEqual(3); }); @@ -138,8 +127,7 @@ describe("paste text as single lines", () => { it("should not create any element if clipboard has only new lines", async () => { const text = "\n\n\n\n\n"; - setClipboardText(text); - pasteWithCtrlCmdV(); + pasteWithCtrlCmdV(text); await waitFor(async () => { await sleep(50); // elements lenght will always be zero if we don't wait, since paste is async expect(h.elements.length).toEqual(0); @@ -155,8 +143,7 @@ describe("paste text as single lines", () => { ) + 10 / h.app.state.zoom.value; mouse.moveTo(100, 100); - setClipboardText(text); - pasteWithCtrlCmdV(); + pasteWithCtrlCmdV(text); await waitFor(async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [fx, firstElY] = getElementBounds(h.elements[0]); @@ -177,8 +164,7 @@ describe("paste text as single lines", () => { ) + 10 / h.app.state.zoom.value; mouse.moveTo(100, 100); - setClipboardText(text); - pasteWithCtrlCmdV(); + pasteWithCtrlCmdV(text); await waitFor(async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [fx, firstElY] = getElementBounds(h.elements[0]); @@ -192,16 +178,14 @@ describe("paste text as single lines", () => { describe("paste text as a single element", () => { it("should create single text element when copying text with Ctrl/Cmd+Shift+V", async () => { const text = "sajgfakfn\naaksfnknas\nakefnkasf"; - setClipboardText(text); - pasteWithCtrlCmdShiftV(); + pasteWithCtrlCmdShiftV(text); await waitFor(() => { expect(h.elements.length).toEqual(1); }); }); it("should not create any element when only new lines in clipboard", async () => { const text = "\n\n\n\n"; - setClipboardText(text); - pasteWithCtrlCmdShiftV(); + pasteWithCtrlCmdShiftV(text); await waitFor(async () => { await sleep(50); expect(h.elements.length).toEqual(0); @@ -243,8 +227,7 @@ describe("Paste bound text container", () => { type: "excalidraw/clipboard", elements: [container, textElement], }); - setClipboardText(data); - pasteWithCtrlCmdShiftV(); + pasteWithCtrlCmdShiftV(data); await waitFor(async () => { await sleep(1); @@ -266,8 +249,7 @@ describe("Paste bound text container", () => { textElement, ], }); - setClipboardText(data); - pasteWithCtrlCmdShiftV(); + pasteWithCtrlCmdShiftV(data); await waitFor(async () => { await sleep(1); diff --git a/src/tests/flip.test.tsx b/src/tests/flip.test.tsx index b80b25f2d4..982105e979 100644 --- a/src/tests/flip.test.tsx +++ b/src/tests/flip.test.tsx @@ -727,7 +727,7 @@ describe("freedraw", () => { describe("image", () => { const createImage = async () => { const sendPasteEvent = (file?: File) => { - const clipboardEvent = createPasteEvent("", file ? [file] : []); + const clipboardEvent = createPasteEvent({}, file ? [file] : []); document.dispatchEvent(clipboardEvent); }; diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts index fed6b4897d..0f80182a94 100644 --- a/src/tests/test-utils.ts +++ b/src/tests/test-utils.ts @@ -208,10 +208,8 @@ export const assertSelectedElements = ( expect(selectedElementIds).toEqual(expect.arrayContaining(ids)); }; -export const createPasteEvent = ( - text: - | string - | /* getData function */ ((type: string) => string | Promise), +export const createPasteEvent = ( + items: Record, files?: File[], ) => { return Object.assign( @@ -222,11 +220,12 @@ export const createPasteEvent = ( }), { clipboardData: { - getData: typeof text === "string" ? () => text : text, + getData: (type: string) => + (items as Record)[type] || "", files: files || [], }, }, - ); + ) as any as ClipboardEvent; }; export const toggleMenu = (container: HTMLElement) => { From 2cabb1f1f43d28aef7b1100ed5cb1736e9d6c9bd Mon Sep 17 00:00:00 2001 From: David Luzar Date: Thu, 19 Oct 2023 12:32:31 +0200 Subject: [PATCH 034/144] fix: attempt to fix flake in wysiwyg tests (#7173) --- src/element/textWysiwyg.test.tsx | 94 +++++++++++++++++--------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index b4f5db191f..842d75af06 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -8,7 +8,7 @@ import { mockBoundingClientRect, restoreOriginalGetBoundingClientRect, } from "../tests/test-utils"; -import { queryByText } from "@testing-library/react"; +import { queryByText, waitFor } from "@testing-library/react"; import { FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; import { @@ -25,10 +25,16 @@ ReactDOM.unmountComponentAtNode(document.getElementById("root")!); const tab = " "; const mouse = new Pointer("mouse"); -const getTextEditor = () => { - return document.querySelector( - ".excalidraw-textEditorContainer > textarea", - ) as HTMLTextAreaElement; +const getTextEditor = async (waitForEditor = false) => { + const query = () => + document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + if (waitForEditor) { + waitFor(() => expect(query()).not.toBe(null)); + return query(); + } + return query(); }; const updateTextEditor = (editor: HTMLTextAreaElement, value: string) => { @@ -185,7 +191,7 @@ describe("textWysiwyg", () => { expect(h.state.editingElement?.id).toBe(boundText.id); }); - it("should edit text under cursor when clicked with text tool", () => { + it("should edit text under cursor when clicked with text tool", async () => { const text = API.createElement({ type: "text", text: "ola", @@ -200,14 +206,14 @@ describe("textWysiwyg", () => { mouse.clickAt(text.x + 50, text.y + 50); - const editor = getTextEditor(); + const editor = await getTextEditor(); expect(editor).not.toBe(null); expect(h.state.editingElement?.id).toBe(text.id); expect(h.elements.length).toBe(1); }); - it("should edit text under cursor when double-clicked with selection tool", () => { + it("should edit text under cursor when double-clicked with selection tool", async () => { const text = API.createElement({ type: "text", text: "ola", @@ -222,7 +228,7 @@ describe("textWysiwyg", () => { mouse.doubleClickAt(text.x + 50, text.y + 50); - const editor = getTextEditor(); + const editor = await getTextEditor(); expect(editor).not.toBe(null); expect(h.state.editingElement?.id).toBe(text.id); @@ -249,7 +255,7 @@ describe("textWysiwyg", () => { textElement = UI.createElement("text"); mouse.clickOn(textElement); - textarea = getTextEditor(); + textarea = await getTextEditor(true); }); afterAll(() => { @@ -459,7 +465,7 @@ describe("textWysiwyg", () => { UI.clickTool("text"); mouse.clickAt(750, 300); - textarea = getTextEditor(); + textarea = await getTextEditor(true); updateTextEditor( textarea, "Excalidraw is an opensource virtual collaborative whiteboard for sketching hand-drawn like diagrams!", @@ -511,7 +517,7 @@ describe("textWysiwyg", () => { { id: text.id, type: "text" }, ]); mouse.down(); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); @@ -539,7 +545,7 @@ describe("textWysiwyg", () => { ]); expect(text.angle).toBe(rectangle.angle); mouse.down(); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); @@ -566,7 +572,7 @@ describe("textWysiwyg", () => { API.setSelectedElements([diamond]); Keyboard.keyPress(KEYS.ENTER); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); const value = new Array(1000).fill("1").join("\n"); @@ -601,7 +607,7 @@ describe("textWysiwyg", () => { expect(text.type).toBe("text"); expect(text.containerId).toBe(null); mouse.down(); - let editor = getTextEditor(); + let editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); editor.blur(); @@ -616,7 +622,7 @@ describe("textWysiwyg", () => { expect(text.containerId).toBe(rectangle.id); mouse.down(); - editor = getTextEditor(); + editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); await new Promise((r) => setTimeout(r, 0)); @@ -638,7 +644,7 @@ describe("textWysiwyg", () => { const text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.type).toBe("text"); expect(text.containerId).toBe(rectangle.id); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); @@ -673,7 +679,7 @@ describe("textWysiwyg", () => { { id: text.id, type: "text" }, ]); mouse.down(); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); await new Promise((r) => setTimeout(r, 0)); @@ -698,7 +704,7 @@ describe("textWysiwyg", () => { freedraw.y + freedraw.height / 2, ); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); fireEvent.keyDown(editor, { key: KEYS.ESCAPE }); @@ -732,7 +738,7 @@ describe("textWysiwyg", () => { expect(text.type).toBe("text"); expect(text.containerId).toBe(null); mouse.down(); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); @@ -747,7 +753,7 @@ describe("textWysiwyg", () => { UI.clickTool("text"); mouse.clickAt(20, 30); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor( editor, @@ -792,7 +798,7 @@ describe("textWysiwyg", () => { mouse.down(); const text = h.elements[1] as ExcalidrawTextElementWithContainer; - let editor = getTextEditor(); + let editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello World!"); @@ -805,7 +811,7 @@ describe("textWysiwyg", () => { rectangle.y + rectangle.height / 2, ); mouse.down(); - editor = getTextEditor(); + editor = await getTextEditor(true); editor.select(); fireEvent.click(screen.getByTitle(/code/i)); @@ -838,7 +844,7 @@ describe("textWysiwyg", () => { Keyboard.keyDown(KEYS.ENTER); let text = h.elements[1] as ExcalidrawTextElementWithContainer; - let editor = getTextEditor(); + let editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); @@ -859,7 +865,7 @@ describe("textWysiwyg", () => { mouse.select(rectangle); Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); updateTextEditor(editor, "Hello"); await new Promise((r) => setTimeout(r, 0)); @@ -888,7 +894,7 @@ describe("textWysiwyg", () => { const text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.containerId).toBe(rectangle.id); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); @@ -925,7 +931,7 @@ describe("textWysiwyg", () => { // Bind first text const text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.containerId).toBe(rectangle.id); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello World!"); editor.blur(); @@ -946,7 +952,7 @@ describe("textWysiwyg", () => { it("should respect text alignment when resizing", async () => { Keyboard.keyPress(KEYS.ENTER); - let editor = getTextEditor(); + let editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello"); editor.blur(); @@ -963,7 +969,7 @@ describe("textWysiwyg", () => { mouse.select(rectangle); Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); editor.select(); @@ -986,7 +992,7 @@ describe("textWysiwyg", () => { mouse.select(rectangle); Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); editor.select(); @@ -1024,7 +1030,7 @@ describe("textWysiwyg", () => { expect(text.type).toBe("text"); expect(text.containerId).toBe(rectangle.id); mouse.down(); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); @@ -1039,7 +1045,7 @@ describe("textWysiwyg", () => { it("should scale font size correctly when resizing using shift", async () => { Keyboard.keyPress(KEYS.ENTER); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello"); editor.blur(); @@ -1059,7 +1065,7 @@ describe("textWysiwyg", () => { it("should bind text correctly when container duplicated with alt-drag", async () => { Keyboard.keyPress(KEYS.ENTER); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello"); editor.blur(); @@ -1091,7 +1097,7 @@ describe("textWysiwyg", () => { it("undo should work", async () => { Keyboard.keyPress(KEYS.ENTER); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello"); editor.blur(); @@ -1128,7 +1134,7 @@ describe("textWysiwyg", () => { it("should not allow bound text with only whitespaces", async () => { Keyboard.keyPress(KEYS.ENTER); - const editor = getTextEditor(); + const editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, " "); @@ -1183,7 +1189,7 @@ describe("textWysiwyg", () => { it("should reset the container height cache when resizing", async () => { Keyboard.keyPress(KEYS.ENTER); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(75); - let editor = getTextEditor(); + let editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello"); editor.blur(); @@ -1195,7 +1201,7 @@ describe("textWysiwyg", () => { mouse.select(rectangle); Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); editor.blur(); @@ -1211,7 +1217,7 @@ describe("textWysiwyg", () => { Keyboard.keyPress(KEYS.ENTER); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(75); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); editor.blur(); @@ -1236,7 +1242,7 @@ describe("textWysiwyg", () => { Keyboard.keyPress(KEYS.ENTER); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(75); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor(editor, "Hello World!"); editor.blur(); expect( @@ -1268,12 +1274,12 @@ describe("textWysiwyg", () => { beforeEach(async () => { Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); updateTextEditor(editor, "Hello"); editor.blur(); mouse.select(rectangle); Keyboard.keyPress(KEYS.ENTER); - editor = getTextEditor(); + editor = await getTextEditor(true); editor.select(); }); @@ -1384,7 +1390,7 @@ describe("textWysiwyg", () => { it("should wrap text in a container when wrap text in container triggered from context menu", async () => { UI.clickTool("text"); mouse.clickAt(20, 30); - const editor = getTextEditor(); + const editor = await getTextEditor(true); updateTextEditor( editor, @@ -1472,7 +1478,7 @@ describe("textWysiwyg", () => { // Bind first text let text = h.elements[1] as ExcalidrawTextElementWithContainer; expect(text.containerId).toBe(rectangle.id); - let editor = getTextEditor(); + let editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Hello!"); expect( @@ -1497,7 +1503,7 @@ describe("textWysiwyg", () => { rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2, ); - editor = getTextEditor(); + editor = await getTextEditor(true); await new Promise((r) => setTimeout(r, 0)); updateTextEditor(editor, "Excalidraw"); editor.blur(); From 7e38cab76eb41fc294dfaa8d2edd88235d6dd986 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:33:16 +0200 Subject: [PATCH 035/144] build(deps): bump @babel/traverse from 7.21.4 to 7.23.2 (#7171) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 119 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 34 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8022634e61..95a7ed8b21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,6 +33,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/code-frame@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" @@ -121,6 +129,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -197,6 +215,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" @@ -217,13 +240,13 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -362,6 +385,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" @@ -377,6 +407,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" @@ -429,6 +464,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" @@ -443,6 +487,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/parser@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" @@ -1201,6 +1250,15 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/template@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -1210,35 +1268,19 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== +"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.5": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" - integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -1251,6 +1293,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@babel/types@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -3355,7 +3406,7 @@ chai@^4.3.7: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== From 83f86e2b86623be8f0187ccff6cc50a381b80803 Mon Sep 17 00:00:00 2001 From: Vaibhav Shukla Date: Thu, 19 Oct 2023 16:29:39 +0530 Subject: [PATCH 036/144] fix: Fix for Strange Symbol Appearing on Canvas after Deleting Grouped Graphics (Issue #7116) (#7170) Co-authored-by: dwelle --- CHANGELOG.md | 3 --- src/actions/actionDeleteSelected.tsx | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a6506e9a0f..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 2020-10-13 - -- Added ability to embed scene source into exported PNG/SVG files so you can import the scene from them (open via `Load` button or drag & drop). #2219 diff --git a/src/actions/actionDeleteSelected.tsx b/src/actions/actionDeleteSelected.tsx index 2520ea83a4..4d7ec6a7c3 100644 --- a/src/actions/actionDeleteSelected.tsx +++ b/src/actions/actionDeleteSelected.tsx @@ -46,6 +46,7 @@ const deleteSelectedElements = ( appState: { ...appState, selectedElementIds: {}, + selectedGroupIds: {}, }, }; }; From b1cac35269ef127e4464fcabee77396520bf62a2 Mon Sep 17 00:00:00 2001 From: Vaibhav Shukla Date: Thu, 19 Oct 2023 23:21:50 +0530 Subject: [PATCH 037/144] feat: Closing of "Save to.." Dialog on Save To Disk (#7168) Co-authored-by: dwelle --- .../components/ExportToExcalidrawPlus.tsx | 4 +++- excalidraw-app/index.tsx | 11 ++++++++--- src/actions/actionExport.tsx | 10 +++++++++- src/components/JSONExportDialog.tsx | 15 ++++++++++++--- src/components/ToolButton.tsx | 10 +++++----- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/excalidraw-app/components/ExportToExcalidrawPlus.tsx b/excalidraw-app/components/ExportToExcalidrawPlus.tsx index 0b577ad7de..c0818c9664 100644 --- a/excalidraw-app/components/ExportToExcalidrawPlus.tsx +++ b/excalidraw-app/components/ExportToExcalidrawPlus.tsx @@ -80,7 +80,8 @@ export const ExportToExcalidrawPlus: React.FC<{ appState: Partial; files: BinaryFiles; onError: (error: Error) => void; -}> = ({ elements, appState, files, onError }) => { + onSuccess: () => void; +}> = ({ elements, appState, files, onError, onSuccess }) => { const { t } = useI18n(); return ( @@ -107,6 +108,7 @@ export const ExportToExcalidrawPlus: React.FC<{ try { trackEvent("export", "eplus", `ui (${getFrame()})`); await exportToExcalidrawPlus(elements, appState, files); + onSuccess(); } catch (error: any) { console.error(error); if (error.name !== "AbortError") { diff --git a/excalidraw-app/index.tsx b/excalidraw-app/index.tsx index 9156d5010a..5b2e29818c 100644 --- a/excalidraw-app/index.tsx +++ b/excalidraw-app/index.tsx @@ -608,7 +608,7 @@ const ExcalidrawWrapper = () => { canvas: HTMLCanvasElement, ) => { if (exportedElements.length === 0) { - return window.alert(t("alerts.cannotExportEmptyCanvas")); + throw new Error(t("alerts.cannotExportEmptyCanvas")); } if (canvas) { try { @@ -624,7 +624,7 @@ const ExcalidrawWrapper = () => { ); if (errorMessage) { - setErrorMessage(errorMessage); + throw new Error(errorMessage); } if (url) { @@ -634,7 +634,7 @@ const ExcalidrawWrapper = () => { if (error.name !== "AbortError") { const { width, height } = canvas; console.error(error, { width, height }); - setErrorMessage(error.message); + throw new Error(error.message); } } } @@ -714,6 +714,11 @@ const ExcalidrawWrapper = () => { }, }); }} + onSuccess={() => { + excalidrawAPI?.updateScene({ + appState: { openDialog: null }, + }); + }} /> ); }, diff --git a/src/actions/actionExport.tsx b/src/actions/actionExport.tsx index 2c94a98638..9622023f2e 100644 --- a/src/actions/actionExport.tsx +++ b/src/actions/actionExport.tsx @@ -191,7 +191,15 @@ export const actionSaveFileToDisk = register({ }, app.files, ); - return { commitToHistory: false, appState: { ...appState, fileHandle } }; + return { + commitToHistory: false, + appState: { + ...appState, + openDialog: null, + fileHandle, + toast: { message: t("toast.fileSaved") }, + }, + }; } catch (error: any) { if (error?.name !== "AbortError") { console.error(error); diff --git a/src/components/JSONExportDialog.tsx b/src/components/JSONExportDialog.tsx index e4d51da43f..59cffbbaef 100644 --- a/src/components/JSONExportDialog.tsx +++ b/src/components/JSONExportDialog.tsx @@ -23,12 +23,15 @@ export type ExportCB = ( const JSONExportModal = ({ elements, appState, + setAppState, files, actionManager, exportOpts, canvas, + onCloseRequest, }: { appState: UIAppState; + setAppState: React.Component["setState"]; files: BinaryFiles; elements: readonly NonDeletedExcalidrawElement[]; actionManager: ActionManager; @@ -72,9 +75,14 @@ const JSONExportModal = ({ title={t("exportDialog.link_button")} aria-label={t("exportDialog.link_button")} showAriaLabel={true} - onClick={() => { - onExportToBackend(elements, appState, files, canvas); - trackEvent("export", "link", `ui (${getFrame()})`); + onClick={async () => { + try { + trackEvent("export", "link", `ui (${getFrame()})`); + await onExportToBackend(elements, appState, files, canvas); + onCloseRequest(); + } catch (error: any) { + setAppState({ errorMessage: error.message }); + } }} /> @@ -114,6 +122,7 @@ export const JSONExportDialog = ({ { } }; - useEffect( - () => () => { + useEffect(() => { + isMountedRef.current = true; + return () => { isMountedRef.current = false; - }, - [], - ); + }; + }, []); const lastPointerTypeRef = useRef(null); From ab1bcc761565a4b82f72437931c6762d62577f78 Mon Sep 17 00:00:00 2001 From: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:29:28 +0200 Subject: [PATCH 038/144] chore: Update translations from Crowdin (#6695) --- src/locales/ar-SA.json | 209 ++++++++++++++++++------------ src/locales/az-AZ.json | 57 ++++++++- src/locales/bg-BG.json | 241 +++++++++++++++++++++-------------- src/locales/bn-BD.json | 57 ++++++++- src/locales/ca-ES.json | 57 ++++++++- src/locales/cs-CZ.json | 57 ++++++++- src/locales/da-DK.json | 65 ++++++++-- src/locales/de-DE.json | 57 ++++++++- src/locales/el-GR.json | 57 ++++++++- src/locales/es-ES.json | 131 +++++++++++++------ src/locales/eu-ES.json | 57 ++++++++- src/locales/fa-IR.json | 97 ++++++++++---- src/locales/fi-FI.json | 57 ++++++++- src/locales/fr-FR.json | 139 +++++++++++++------- src/locales/gl-ES.json | 105 +++++++++++---- src/locales/he-IL.json | 63 ++++++++- src/locales/hi-IN.json | 57 ++++++++- src/locales/hu-HU.json | 59 ++++++++- src/locales/id-ID.json | 95 ++++++++++---- src/locales/it-IT.json | 67 ++++++++-- src/locales/ja-JP.json | 137 +++++++++++++------- src/locales/kaa.json | 183 ++++++++++++++++---------- src/locales/kab-KAB.json | 57 ++++++++- src/locales/kk-KZ.json | 57 ++++++++- src/locales/km-KH.json | 57 ++++++++- src/locales/ko-KR.json | 59 ++++++++- src/locales/ku-TR.json | 57 ++++++++- src/locales/lt-LT.json | 57 ++++++++- src/locales/lv-LV.json | 57 ++++++++- src/locales/mr-IN.json | 57 ++++++++- src/locales/my-MM.json | 57 ++++++++- src/locales/nb-NO.json | 57 ++++++++- src/locales/nl-NL.json | 57 ++++++++- src/locales/nn-NO.json | 57 ++++++++- src/locales/oc-FR.json | 57 ++++++++- src/locales/pa-IN.json | 57 ++++++++- src/locales/percentages.json | 94 +++++++------- src/locales/pl-PL.json | 67 ++++++++-- src/locales/pt-BR.json | 67 ++++++++-- src/locales/pt-PT.json | 57 ++++++++- src/locales/ro-RO.json | 57 ++++++++- src/locales/ru-RU.json | 57 ++++++++- src/locales/si-LK.json | 57 ++++++++- src/locales/sk-SK.json | 65 ++++++++-- src/locales/sl-SI.json | 57 ++++++++- src/locales/sv-SE.json | 57 ++++++++- src/locales/ta-IN.json | 117 ++++++++++++----- src/locales/th-TH.json | 127 ++++++++++++------ src/locales/tr-TR.json | 133 +++++++++++++------ src/locales/uk-UA.json | 87 ++++++++++--- src/locales/vi-VN.json | 57 ++++++++- src/locales/zh-CN.json | 61 ++++++++- src/locales/zh-HK.json | 57 ++++++++- src/locales/zh-TW.json | 57 ++++++++- 54 files changed, 3416 insertions(+), 819 deletions(-) diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json index 6d94f0ad25..18742875f6 100644 --- a/src/locales/ar-SA.json +++ b/src/locales/ar-SA.json @@ -50,7 +50,7 @@ "veryLarge": "كبير جدا", "solid": "كامل", "hachure": "خطوط", - "zigzag": "", + "zigzag": "متعرج", "crossHatch": "خطوط متقطعة", "thin": "نحيف", "bold": "داكن", @@ -106,11 +106,15 @@ "increaseFontSize": "تكبير حجم الخط", "unbindText": "فك ربط النص", "bindText": "ربط النص بالحاوية", - "createContainerFromText": "", + "createContainerFromText": "نص مغلف في حاوية", "link": { "edit": "تعديل الرابط", + "editEmbed": "تحرير الرابط وإدراجه", "create": "إنشاء رابط", - "label": "رابط" + "createEmbed": "إنشاء رابط و إدراجه", + "label": "رابط", + "labelEmbed": "رابط و إدراج", + "empty": "لم يتم تعيين رابط" }, "lineEditor": { "edit": "تحرير السطر", @@ -124,9 +128,9 @@ }, "statusPublished": "نُشر", "sidebarLock": "إبقاء الشريط الجانبي مفتوح", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "تحديد جميع العناصر في الإطار", + "removeAllElementsFromFrame": "إزالة جميع العناصر من الإطار", + "eyeDropper": "اختيار اللون من القماش" }, "library": { "noItems": "لا توجد عناصر أضيفت بعد...", @@ -160,13 +164,16 @@ "darkMode": "الوضع المظلم", "lightMode": "الوضع المضيء", "zenMode": "وضع التأمل", + "objectsSnapMode": "التقط إلى العناصر", "exitZenMode": "إلغاء الوضع الليلى", "cancel": "إلغاء", "clear": "مسح", "remove": "إزالة", + "embed": "تبديل الإدراج", "publishLibrary": "انشر", "submit": "أرسل", - "confirm": "تأكيد" + "confirm": "تأكيد", + "embeddableInteractionButton": "اضغط للتفاعل" }, "alerts": { "clearReset": "هذا سيُزيل كامل اللوحة. هل أنت متأكد؟", @@ -189,23 +196,28 @@ "resetLibrary": "هذا سوف يمسح مكتبتك. هل أنت متأكد؟", "removeItemsFromsLibrary": "حذف {{count}} عنصر (عناصر) من المكتبة؟", "invalidEncryptionKey": "مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل.", - "collabOfflineWarning": "" + "collabOfflineWarning": "لا يوجد اتصال بالانترنت.\nلن يتم حفظ التغييرات التي قمت بها!" }, "errors": { "unsupportedFileType": "نوع الملف غير مدعوم.", "imageInsertError": "تعذر إدراج الصورة. حاول مرة أخرى لاحقاً...", "fileTooBig": "الملف كبير جداً. الحد الأقصى المسموح به للحجم هو {{maxSize}}.", "svgImageInsertError": "تعذر إدراج صورة SVG. يبدو أن ترميز SVG غير صحيح.", + "failedToFetchImage": "", "invalidSVGString": "SVG غير صالح.", "cannotResolveCollabServer": "تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.", "importLibraryError": "تعذر تحميل المكتبة", "collabSaveFailed": "تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.", "collabSaveFailed_sizeExceeded": "تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك.", "brave_measure_text_error": { - "line1": "", - "line2": "", - "line3": "", - "line4": "" + "line1": "يبدو أنك تستخدم متصفح Brave مع إعداد حظر صارم لتتبع البصمة.", + "line2": "قد يؤدي هذا إلى كسر عناصر النص في الرسومات الخاصة بك.", + "line3": "من المستحسن إلغاء تفعيل هذا الإعداد. يمكنك اتباع هذه الخطوات لفعل ذلك.", + "line4": "إذا لم يصلح تعطيل هذا الإعداد طريقة عرض النصوص، الرجاء كتابة بلاغ على حسابنا في GitHub، أو راسلنا على Discord" + }, + "libraryElementTypeError": { + "embeddable": "لا يمكن إضافة العناصر القابلة للتضمين في المكتبة.", + "image": "سوف يتم دعم إضافة صور إلى المكتبة قريباً!" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "وضع القلم - امنع اللمس", "link": "إضافة/تحديث الرابط للشكل المحدد", "eraser": "ممحاة", - "frame": "", - "hand": "", - "extraTools": "" + "frame": "أداة الإطار", + "embeddable": "تضمين ويب", + "laser": "مؤشر ليزر", + "hand": "يد (أداة الإزاحة)", + "extraTools": "المزيد من أﻷدوات" }, "headings": { "canvasActions": "إجراءات اللوحة", @@ -237,6 +251,7 @@ "linearElement": "انقر لبدء نقاط متعددة، اسحب لخط واحد", "freeDraw": "انقر واسحب، افرج عند الانتهاء", "text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار", + "embeddable": "اضغط مع السحب لإنشاء موقع ويب مضمّن", "text_selected": "انقر نقراً مزدوجاً أو اضغط ادخال لتعديل النص", "text_editing": "اضغط على Esc أو (Ctrl أو Cmd) + Enter لإنهاء التعديل", "linearElementMulti": "انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء", @@ -245,14 +260,15 @@ "resizeImage": "يمكنك تغيير الحجم بحرية بالضغط بأستمرار على SHIFT،\nاضغط بأستمرار على ALT أيضا لتغيير الحجم من المركز", "rotate": "يمكنك تقييد الزوايا من خلال الضغط على SHIFT أثناء الدوران", "lineEditor_info": "اضغط على مفتاح (Ctrl أو Cmd) و انقر بشكل مزدوج، أو اضغط على مفتاحي (Ctrl أو Cmd) و (Enter) لتعديل النقاط", - "lineEditor_pointSelected": "", - "lineEditor_nothingSelected": "", - "placeImage": "", + "lineEditor_pointSelected": "اضغط على حذف لإزالة النقطة (النِّقَاط)، Ctrl/Cmd+D للتكرار، أو اسحب للانتقال", + "lineEditor_nothingSelected": "اختر نقطة لتعديلها (اضغط على SHIFT لتحديد عدة نِقَاط),\nأو اضغط على ALT و انقر بالفأرة لإضافة نِقَاط جديدة", + "placeImage": "انقر لوضع الصورة، أو انقر واسحب لتعيين حجمها يدوياً", "publishLibrary": "نشر مكتبتك", - "bindTextToElement": "", - "deepBoxSelect": "", - "eraserRevert": "", - "firefox_clipboard_write": "" + "bindTextToElement": "اضغط على إدخال لإضافة نص", + "deepBoxSelect": "اضغط على Ctrl\\Cmd للاختيار العميق، ولمنع السحب", + "eraserRevert": "اضغط على Alt لاستعادة العناصر المعلَّمة للحذف", + "firefox_clipboard_write": "يمكن على الأرجح تمكين هذه الميزة عن طريق تعيين علم \"dom.events.asyncClipboard.clipboardItem\" إلى \"true\". لتغيير أعلام المتصفح في Firefox، قم بزيارة صفحة \"about:config\".", + "disableSnapping": "اضغط على Ctrl أو Cmd لتعطيل الالتقاط" }, "canvasError": { "cannotShowPreview": "تعذر عرض المعاينة", @@ -260,11 +276,11 @@ "canvasTooBigTip": "نصيحة: حاول تحريك العناصر البعيدة بشكل أقرب قليلاً." }, "errorSplash": { - "headingMain": "", + "headingMain": "حدث خطأ. حاول .", "clearCanvasMessage": "إذا لم تعمل إعادة التحميل، حاول مرة أخرى ", "clearCanvasCaveat": " هذا سيؤدي إلى فقدان العمل ", - "trackedToSentry": "", - "openIssueMessage": "", + "trackedToSentry": "تم تتبع الخطأ في المعرف {{eventId}} على نظامنا.", + "openIssueMessage": "حرصنا على عدم إضافة معلومات المشهد في بلاغ الخطأ. في حال كون مشهدك لا يحمل أي معلومات خاصة نرجو المتابعة على . نرجو إضافة المعلومات أدناه بنسخها ولصقها في محتوى البلاغ على GitHub.", "sceneContent": "محتوى المشهد:" }, "roomDialog": { @@ -294,16 +310,16 @@ "helpDialog": { "blog": "اقرأ مدونتنا", "click": "انقر", - "deepSelect": "", - "deepBoxSelect": "", + "deepSelect": "تحديد عميق", + "deepBoxSelect": "تحديد عميق داخل المربع، ومنع السحب", "curvedArrow": "سهم مائل", "curvedLine": "خط مائل", "documentation": "دليل الاستخدام", "doubleClick": "انقر مرتين", "drag": "اسحب", "editor": "المحرر", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "تحرير سطر/نقاط سهم", + "editText": "تعديل النص / إضافة تسمية", "github": "عثرت على مشكلة؟ إرسال", "howto": "اتبع التعليمات", "or": "أو", @@ -316,9 +332,9 @@ "view": "عرض", "zoomToFit": "تكبير للملائمة", "zoomToSelection": "تكبير للعنصر المحدد", - "toggleElementLock": "", - "movePageUpDown": "", - "movePageLeftRight": "" + "toggleElementLock": "إغلاق/فتح المحدد", + "movePageUpDown": "نقل الصفحة أعلى/أسفل", + "movePageLeftRight": "نقل الصفحة يسار/يمين" }, "clearCanvasDialog": { "title": "مسح اللوحة" @@ -336,20 +352,20 @@ "authorName": "اسمك أو اسم المستخدم", "libraryName": "اسم مكتبتك", "libraryDesc": "وصف مكتبتك لمساعدة الناس على فهم استخدامها", - "githubHandle": "", - "twitterHandle": "", - "website": "" + "githubHandle": "معالج GitHub (اختياري)، حتى تتمكن من تحرير المكتبة عند إرسالها للمراجعة", + "twitterHandle": "اسم مستخدم تويتر (اختياري)، حتى نعرف من الذي سيتم الإشارة إليه عند الترويج عبر تويتر", + "website": "رابط إلى موقعك الشخصي أو في مكان آخر (اختياري)" }, "errors": { "required": "مطلوب", "website": "أدخل عنوان URL صالح" }, - "noteDescription": "", - "noteGuidelines": "", - "noteLicense": "", + "noteDescription": "تقديم مكتبتك لتضمينها في مستودع المكتبة العامة لأشخاص آخرين لاستخدامها في رسومهم.", + "noteGuidelines": "تحتاج المكتبة إلى الموافقة أولا. يرجى قراءة المعايير قبل تقديمها. سوف تحتاج إلى حساب GitHub للتواصل وإجراء التغييرات عند الطلب، ولكن ليس مطلوبا بشكل صارم.", + "noteLicense": "تقديمك يعني موافقتك على نشر المكتبة المقدمة تحت MIT ترخيص، ما يعني أن لأي أحد الحق في استخدامها دون قيود.", "noteItems": "يجب أن يكون لكل عنصر مكتبة اسمه الخاص حتى يكون قابلاً للتصفية. سيتم تضمين عناصر المكتبة التالية:", "atleastOneLibItem": "يرجى تحديد عنصر مكتبة واحد على الأقل للبدء", - "republishWarning": "" + "republishWarning": "ملاحظة: بعض العناصر المحددة معينة على أنه نشرها أو تقديمها من قبل. يجب عليك فقط إعادة إرسال العناصر عند تحديث مكتبة موجودة أو إرسالها." }, "publishSuccessDialog": { "title": "تم إرسال المكتبة", @@ -360,27 +376,27 @@ "removeItemsFromLib": "إزالة العناصر المحددة من المكتبة" }, "imageExportDialog": { - "header": "", + "header": "تصدير الصورة", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", - "padding": "" + "withBackground": "الخلفية", + "onlySelected": "المحدد فقط", + "darkMode": "الوضع الداكن", + "embedScene": "تضمين المشهد", + "scale": "الحجم", + "padding": "الهوامش" }, "tooltip": { - "embedScene": "" + "embedScene": "سيتم حفظ بيانات المشهد في ملف PNG/SVG المصدّر بحيث يمكن استعادة المشهد منه.\nسيزيد حجم الملف المصدر." }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "تصدير بصيغة PNG", + "exportToSvg": "تصدير بصيغة SVG", + "copyPngToClipboard": "نسخ الـ PNG إلى الحافظة" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "نسخ إلى الحافظة" } }, "encrypted": { @@ -411,43 +427,76 @@ "fileSavedToFilename": "حفظ باسم {filename}", "canvas": "لوحة الرسم", "selection": "العنصر المحدد", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "استخدم {{shortcut}} للصق كعنصر واحد،\nأو لصق في محرر نص موجود", + "unableToEmbed": "تضمين هذا الرابط غير مسموح حاليًا. افتح بلاغاً على GitHub لطلب عنوان Url القائمة البيضاء", + "unrecognizedLinkFormat": "الرابط الذي ضمنته لا يتطابق مع التنسيق المتوقع. الرجاء محاولة لصق النص 'المضمن' المُزوَد من موقع المصدر" }, "colors": { "transparent": "شفاف", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "أسود", + "white": "أبيض", + "red": "أحمر", + "pink": "وردي", + "grape": "عنبي", + "violet": "بنفسجي", + "gray": "رمادي", + "blue": "أزرق", + "cyan": "سماوي", + "teal": "أزرق مخضر", + "green": "أخضر", + "yellow": "أصفر", + "orange": "برتقالي", + "bronze": "برونزي" }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "جميع بياناتك محفوظة محليا في المتصفح الخاص بك.", + "center_heading_plus": "هل تريد الذهاب إلى Excalidraw+ بدلاً من ذلك؟", + "menuHint": "التصدير والتفضيلات واللغات ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "التصدير والتفضيلات وغيرها...", + "center_heading": "الرسم البياني التصويري. بشكل مبسط.", + "toolbarHint": "اختر أداة و ابدأ الرسم!", + "helpHint": "الاختصارات و المساعدة" } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", - "shades": "", - "hexCode": "", - "noShades": "" + "mostUsedCustomColors": "الألوان المخصصة الأكثر استخداما", + "colors": "الألوان", + "shades": "الدرجات", + "hexCode": "رمز Hex", + "noShades": "لا تتوفر درجات لهذا اللون" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "تصدير كصورة", + "button": "تصدير كصورة", + "description": "تصدير بيانات المشهد إلى ملف يمكنك الاستيراد منه لاحقاً." + }, + "saveToDisk": { + "title": "حفظ الملف للجهاز", + "button": "حفظ الملف للجهاز", + "description": "تصدير بيانات المشهد إلى ملف يمكنك الاستيراد منه لاحقاً." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "تصدير إلى Excalidraw+", + "description": "حفظ المشهد إلى مساحة العمل +Excalidraw الخاصة بك." + } + }, + "modal": { + "loadFromFile": { + "title": "تحميل من ملف", + "button": "تحميل من ملف", + "description": "سيتم التحميل من الملف استبدال المحتوى الموجود.

يمكنك النسخ الاحتياطي لرسمك أولاً باستخدام أحد الخيارات أدناه." + }, + "shareableLink": { + "title": "تحميل من رابط", + "button": "استبدال محتواي", + "description": "سيتسبب تحميل رسمة خارجية باستبدال محتواك الموجود حالياً.

بإمكانك إجراء النسخ الاحتياطي لرسمتك الحالية باستخدام أحد الخيارات أدناه." + } + } } } diff --git a/src/locales/az-AZ.json b/src/locales/az-AZ.json index 2618ccbdc0..1ece3cc98d 100644 --- a/src/locales/az-AZ.json +++ b/src/locales/az-AZ.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "", "lightMode": "", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "", "cancel": "", "clear": "", "remove": "", + "embed": "", "publishLibrary": "", "submit": "", - "confirm": "" + "confirm": "", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json index 76bcef5745..716c73d540 100644 --- a/src/locales/bg-BG.json +++ b/src/locales/bg-BG.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Постави", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Постави като обикновен текст", "pasteCharts": "Постави графики", "selectAll": "Маркирай всичко", "multiSelect": "Добави елемент към селекция", @@ -50,7 +50,7 @@ "veryLarge": "Много голям", "solid": "Солиден", "hachure": "Хералдика", - "zigzag": "", + "zigzag": "Зигзаг", "crossHatch": "Двойно-пресечено", "thin": "Тънък", "bold": "Ясно очертан", @@ -63,7 +63,7 @@ "cartoonist": "Карикатурист", "fileTitle": "Име на файл", "colorPicker": "Избор на цвят", - "canvasColors": "", + "canvasColors": "Използван на платно", "canvasBackground": "Фон на платно", "drawingCanvas": "Платно за рисуване", "layers": "Слоеве", @@ -99,37 +99,41 @@ "share": "Сподели", "showStroke": "", "showBackground": "", - "toggleTheme": "", - "personalLib": "", - "excalidrawLib": "", - "decreaseFontSize": "", - "increaseFontSize": "", + "toggleTheme": "Включи тема", + "personalLib": "Лична Библиотека", + "excalidrawLib": "Excalidraw Библиотека", + "decreaseFontSize": "Намали размера на шрифта", + "increaseFontSize": "Увеличи размера на шрифта", "unbindText": "", "bindText": "", "createContainerFromText": "", "link": { - "edit": "", + "edit": "Редактирай линк", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "Линк", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", "exit": "" }, "elementLock": { - "lock": "", - "unlock": "", - "lockAll": "", - "unlockAll": "" + "lock": "Заключи", + "unlock": "Отключи", + "lockAll": "Заключи всички", + "unlockAll": "Отключи всички" }, - "statusPublished": "", + "statusPublished": "Публикувани", "sidebarLock": "", "selectAllElementsInFrame": "", "removeAllElementsFromFrame": "", - "eyeDropper": "" + "eyeDropper": "Избери цвят от платното" }, "library": { - "noItems": "", + "noItems": "Няма добавени неща все още...", "hint_emptyLibrary": "", "hint_emptyPrivateLibrary": "" }, @@ -137,11 +141,11 @@ "clearReset": "Нулиране на платно", "exportJSON": "", "exportImage": "", - "export": "", + "export": "Запази на...", "copyToClipboard": "Копиране в клипборда", - "save": "", + "save": "Запази към текущ файл", "saveAs": "Запиши като", - "load": "", + "load": "Отвори", "getShareableLink": "Получаване на връзка за споделяне", "close": "Затвори", "selectLanguage": "Избор на език", @@ -160,13 +164,16 @@ "darkMode": "Тъмен режим", "lightMode": "Светъл режим", "zenMode": "Режим Zen", + "objectsSnapMode": "", "exitZenMode": "Спиране на Zen режим", "cancel": "Отмени", "clear": "Изчисти", "remove": "Премахване", + "embed": "", "publishLibrary": "Публикувай", "submit": "Изпрати", - "confirm": "Потвърждаване" + "confirm": "Потвърждаване", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Това ще изчисти цялото платно. Сигурни ли сте?", @@ -175,37 +182,42 @@ "couldNotLoadInvalidFile": "Невалиден файл не може да се зареди", "importBackendFailed": "Импортирането от бекенд не беше успешно.", "cannotExportEmptyCanvas": "Не може да се експортира празно платно.", - "couldNotCopyToClipboard": "", + "couldNotCopyToClipboard": "Не можем да копираме в клипбоарда.", "decryptFailed": "Данните не можаха да се дешифрират.", "uploadedSecurly": "Качването е защитено с криптиране от край до край, което означава, че сървърът Excalidraw и трети страни не могат да четат съдържанието.", "loadSceneOverridePrompt": "Зареждането на външна рисунка ще презапише настоящото ви съдържание. Желаете ли да продължите?", "collabStopOverridePrompt": "Прекратяването на сесията ще презапише предишната, локално запазена, рисунка. Сигурни ли сте?\n\n(Ако искате да продължите с локалната рисунка, просто затворете таба на браузъра.)", - "errorAddingToLibrary": "", - "errorRemovingFromLibrary": "", + "errorAddingToLibrary": "Не можем да заредим от библиотеката", + "errorRemovingFromLibrary": "Не можем да премахнем елемент от библиотеката", "confirmAddLibrary": "Ще се добавят {{numShapes}} фигура(и) във вашата библиотека. Сигурни ли сте?", "imageDoesNotContainScene": "", "cannotRestoreFromImage": "Не може да бъде възстановена сцена от този файл", "invalidSceneUrl": "", "resetLibrary": "", - "removeItemsFromsLibrary": "", + "removeItemsFromsLibrary": "Изтрий {{count}} елемент(а) от библиотеката?", "invalidEncryptionKey": "", "collabOfflineWarning": "" }, "errors": { "unsupportedFileType": "Този файлов формат не се поддържа.", "imageInsertError": "", - "fileTooBig": "", + "fileTooBig": "Файлът е твърде голям. Максималния допустим размер е {{maxSize}}.", "svgImageInsertError": "", - "invalidSVGString": "", + "failedToFetchImage": "", + "invalidSVGString": "Невалиден SVG.", "cannotResolveCollabServer": "", - "importLibraryError": "", + "importLibraryError": "Не можем да заредим библиотеката", "collabSaveFailed": "", "collabSaveFailed_sizeExceeded": "", "brave_measure_text_error": { "line1": "", "line2": "", - "line3": "", + "line3": "Силно препоръчваме да изключите тази настройка. Можете да следвате тези стъпки за това как да го направите.", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -222,10 +234,12 @@ "lock": "Поддържайте избрания инструмент активен след рисуване", "penMode": "", "link": "", - "eraser": "", + "eraser": "Гума", "frame": "", + "embeddable": "", + "laser": "", "hand": "", - "extraTools": "" + "extraTools": "Още инструменти" }, "headings": { "canvasActions": "Действия по платното", @@ -237,6 +251,7 @@ "linearElement": "Кликнете, за да стартирате няколко точки, плъзнете за една линия", "freeDraw": "Натиснете и влачете, пуснете като сте готови", "text": "Подсказка: Можете също да добавите текст като натиснете някъде два път с инструмента за селекция", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "Кликнете върху последната точка или натиснете Escape или Enter, за да завършите", @@ -252,7 +267,8 @@ "bindTextToElement": "Натиснете Enter, за да добавите", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Невъзможност за показване на preview", @@ -288,7 +304,7 @@ "link_details": "", "link_button": "", "excalidrawplus_description": "", - "excalidrawplus_button": "", + "excalidrawplus_button": "Експорт", "excalidrawplus_exportError": "" }, "helpDialog": { @@ -299,7 +315,7 @@ "curvedArrow": "Извита стрелка", "curvedLine": "Извита линия", "documentation": "Документация", - "doubleClick": "", + "doubleClick": "двойно-щракване", "drag": "плъзнете", "editor": "Редактор", "editLineArrowPoints": "", @@ -308,41 +324,41 @@ "howto": "Следвайте нашите ръководства", "or": "или", "preventBinding": "Спри прилепяне на стрелките", - "tools": "", + "tools": "Инструменти", "shortcuts": "Клавиши за бърз достъп", - "textFinish": "", - "textNewLine": "", + "textFinish": "Завърши редактиране (текстов редактор)", + "textNewLine": "Добави нова линия (текстов редактор)", "title": "Помощ", "view": "Преглед", "zoomToFit": "Приближи докато се виждат всички елементи", "zoomToSelection": "Приближи селекцията", - "toggleElementLock": "", - "movePageUpDown": "", - "movePageLeftRight": "" + "toggleElementLock": "Заключи/Отключи селекция", + "movePageUpDown": "Премести страница нагоре/надолу", + "movePageLeftRight": "Премести страница наляво/надясно" }, "clearCanvasDialog": { - "title": "" + "title": "Изчисти платното" }, "publishDialog": { - "title": "", - "itemName": "", - "authorName": "", - "githubUsername": "", - "twitterUsername": "", - "libraryName": "", - "libraryDesc": "", - "website": "", + "title": "Публикувай библиотека", + "itemName": "Име", + "authorName": "Авторско име", + "githubUsername": "GitHub потребителско име", + "twitterUsername": "Twitter потребителско име", + "libraryName": "Име на библиотеката", + "libraryDesc": "Описание на библиотеката", + "website": "Уебсайт", "placeholder": { - "authorName": "", - "libraryName": "", - "libraryDesc": "", + "authorName": "Името или потребителското Ви име", + "libraryName": "Име на библиотеката Ви", + "libraryDesc": "Описание на библиотеката ви, за да помогнете на хората да разберат приложенията ѝ", "githubHandle": "", "twitterHandle": "", "website": "" }, "errors": { - "required": "", - "website": "" + "required": "Задължително", + "website": "Въведете валиден URL адрес" }, "noteDescription": "", "noteGuidelines": "", @@ -356,15 +372,15 @@ "content": "" }, "confirmDialog": { - "resetLibrary": "", + "resetLibrary": "Нулирай библиотека", "removeItemsFromLib": "" }, "imageExportDialog": { "header": "", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", + "withBackground": "Фон", + "onlySelected": "Само избраното", + "darkMode": "Тъмен режим", "embedScene": "", "scale": "", "padding": "" @@ -373,14 +389,14 @@ "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "Изнасяне в PNG", + "exportToSvg": "Изнасяне в SVG", + "copyPngToClipboard": "Копирай PNG в клипборда" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Копиране в клипборда" } }, "encrypted": { @@ -403,51 +419,84 @@ "width": "Широчина" }, "toast": { - "addedToLibrary": "", + "addedToLibrary": "Добавена към библиотеката", "copyStyles": "Копирани стилове.", "copyToClipboard": "Копирано в клипборда.", - "copyToClipboardAsPng": "", - "fileSaved": "", - "fileSavedToFilename": "", - "canvas": "", - "selection": "", - "pasteAsSingleElement": "" + "copyToClipboardAsPng": "Копира {{exportSelection}} в клипборда като PNG\n({{exportColorScheme}})", + "fileSaved": "Файлът е запазен.", + "fileSavedToFilename": "Запазен към {filename}", + "canvas": "платно", + "selection": "селекция", + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { - "transparent": "", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "transparent": "Прозрачен", + "black": "Черен", + "white": "Бял", + "red": "Червен", + "pink": "Розов", + "grape": "Грозде", + "violet": "Виолетово", + "gray": "Сив", + "blue": "Син", + "cyan": "Синьозелено", + "teal": "Тъмно синьо-зелено", + "green": "Зелено", + "yellow": "Жълто", + "orange": "Оранжево", + "bronze": "Бронзово" }, "welcomeScreen": { "app": { - "center_heading": "", + "center_heading": "Всичките Ви данни са запазени локално в браузъра Ви.", "center_heading_plus": "", - "menuHint": "" + "menuHint": "Експорт, предпочитания, езици, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Експорт, предпочитания, и още...", + "center_heading": "Диаграми. Направени. Просто.", + "toolbarHint": "Изберете инструмент & Започнете да рисувате!", + "helpHint": "Преки пътища & помощ" } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", - "shades": "", - "hexCode": "", + "mostUsedCustomColors": "Най-често използвани цветове", + "colors": "Цветове", + "shades": "Нюанси", + "hexCode": "Шестнадесетичен код", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Изнеси като изображение", + "button": "Изнеси като изображение", + "description": "" + }, + "saveToDisk": { + "title": "Запази към диск", + "button": "Запази към диск", + "description": "" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Експортирай към Excalidraw+", + "description": "Запази сцената към Excalidraw+ работното място." + } + }, + "modal": { + "loadFromFile": { + "title": "Зареди от файл", + "button": "Зареди от файл", + "description": "" + }, + "shareableLink": { + "title": "Зареди от линк", + "button": "Замени моето съдържание", + "description": "" + } + } } } diff --git a/src/locales/bn-BD.json b/src/locales/bn-BD.json index 2c5aa7277e..a2b9268908 100644 --- a/src/locales/bn-BD.json +++ b/src/locales/bn-BD.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "লিঙ্ক সংশোধন", + "editEmbed": "", "create": "লিঙ্ক তৈরী", - "label": "লিঙ্ক নামকরণ" + "createEmbed": "", + "label": "লিঙ্ক নামকরণ", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "ডার্ক মোড", "lightMode": "লাইট মোড", "zenMode": "জেন মোড", + "objectsSnapMode": "", "exitZenMode": "জেন মোড বন্ধ করুন", "cancel": "বাতিল", "clear": "সাফ", "remove": "বিয়োগ", + "embed": "", "publishLibrary": "সংগ্রহ প্রকাশ করুন", "submit": "জমা করুন", - "confirm": "নিশ্চিত করুন" + "confirm": "নিশ্চিত করুন", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "এটি পুরো ক্যানভাস সাফ করবে। আপনি কি নিশ্চিত?", @@ -196,6 +203,7 @@ "imageInsertError": "ছবি সন্নিবেশ করা যায়নি। পরে আবার চেষ্টা করুন...", "fileTooBig": "ফাইলটি খুব বড়। সর্বাধিক অনুমোদিত আকার হল {{maxSize}}৷", "svgImageInsertError": "এসভীজী ছবি সন্নিবেশ করা যায়নি। এসভীজী মার্কআপটি অবৈধ মনে হচ্ছে৷", + "failedToFetchImage": "", "invalidSVGString": "এসভীজী মার্কআপটি অবৈধ মনে হচ্ছে৷", "cannotResolveCollabServer": "কোল্যাব সার্ভারের সাথে সংযোগ করা যায়নি। পৃষ্ঠাটি পুনরায় লোড করে আবার চেষ্টা করুন।", "importLibraryError": "সংগ্রহ লোড করা যায়নি", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "একটি নির্বাচিত আকৃতির জন্য লিঙ্ক যোগ বা আপডেট করুন", "eraser": "ঝাড়ন", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "একাধিক বিন্দু শুরু করতে ক্লিক করুন, একক লাইনের জন্য টেনে আনুন", "freeDraw": "ক্লিক করুন এবং টেনে আনুন, আপনার কাজ শেষ হলে ছেড়ে দিন", "text": "বিশেষ্য: আপনি নির্বাচন টুলের সাথে যে কোনো জায়গায় ডাবল-ক্লিক করে পাঠ্য যোগ করতে পারেন", + "embeddable": "", "text_selected": "লেখা সম্পাদনা করতে ডাবল-ক্লিক করুন বা এন্টার টিপুন", "text_editing": "লেখা সম্পাদনা শেষ করতে এসকেপ বা কন্ট্রোল/কম্যান্ড যোগে এন্টার টিপুন", "linearElementMulti": "শেষ বিন্দুতে ক্লিক করুন অথবা শেষ করতে এসকেপ বা এন্টার টিপুন", @@ -252,7 +267,8 @@ "bindTextToElement": "লেখা যোগ করতে এন্টার টিপুন", "deepBoxSelect": "", "eraserRevert": "মুছে ফেলার জন্য চিহ্নিত উপাদানগুলিকে ফিরিয়ে আনতে অল্ট ধরে রাখুন", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "প্রিভিউ দেখাতে অপারগ", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "বাছাই", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json index daa9da8c47..1205838c44 100644 --- a/src/locales/ca-ES.json +++ b/src/locales/ca-ES.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Edita l'enllaç", + "editEmbed": "", "create": "Crea un enllaç", - "label": "Enllaç" + "createEmbed": "", + "label": "Enllaç", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Editar línia", @@ -160,13 +164,16 @@ "darkMode": "Mode fosc", "lightMode": "Mode clar", "zenMode": "Mode zen", + "objectsSnapMode": "", "exitZenMode": "Surt de mode zen", "cancel": "Cancel·la", "clear": "Neteja", "remove": "Suprimeix", + "embed": "", "publishLibrary": "Publica", "submit": "Envia", - "confirm": "Confirma" + "confirm": "Confirma", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "S'esborrarà tot el llenç. N'esteu segur?", @@ -196,6 +203,7 @@ "imageInsertError": "No s'ha pogut insertar la imatge, torneu-ho a provar més tard...", "fileTooBig": "El fitxer és massa gros. La mida màxima permesa és {{maxSize}}.", "svgImageInsertError": "No ha estat possible inserir la imatge SVG. Les marques SVG semblen invàlides.", + "failedToFetchImage": "", "invalidSVGString": "SVG no vàlid.", "cannotResolveCollabServer": "No ha estat possible connectar amb el servidor collab. Si us plau recarregueu la pàgina i torneu a provar.", "importLibraryError": "No s'ha pogut carregar la biblioteca", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Afegeix / actualitza l'enllaç per a la forma seleccionada", "eraser": "Esborrador", "frame": "", + "embeddable": "", + "laser": "", "hand": "Mà (eina de desplaçament)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Feu clic per a dibuixar múltiples punts; arrossegueu per a una sola línia", "freeDraw": "Feu clic i arrossegueu, deixeu anar per a finalitzar", "text": "Consell: també podeu afegir text fent doble clic en qualsevol lloc amb l'eina de selecció", + "embeddable": "", "text_selected": "Feu doble clic o premeu Retorn per a editar el text", "text_editing": "Premeu Escapada o Ctrl+Retorn (o Ordre+Retorn) per a finalitzar l'edició", "linearElementMulti": "Feu clic a l'ultim punt, o pitgeu Esc o Retorn per a finalitzar", @@ -252,7 +267,8 @@ "bindTextToElement": "Premeu enter per a afegir-hi text", "deepBoxSelect": "Manteniu CtrlOrCmd per a selecció profunda, i per a evitar l'arrossegament", "eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar", - "firefox_clipboard_write": "És probable que aquesta funció es pugui activar posant la marca \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Per canviar les marques del navegador al Firefox, visiteu la pàgina \"about:config\"." + "firefox_clipboard_write": "És probable que aquesta funció es pugui activar posant la marca \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Per canviar les marques del navegador al Firefox, visiteu la pàgina \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "No es pot mostrar la previsualització", @@ -411,7 +427,9 @@ "fileSavedToFilename": "S'ha desat a {filename}", "canvas": "el llenç", "selection": "la selecció", - "pasteAsSingleElement": "Fer servir {{shortcut}} per enganxar com un sol element,\no enganxeu-lo en un editor de text existent" + "pasteAsSingleElement": "Fer servir {{shortcut}} per enganxar com un sol element,\no enganxeu-lo en un editor de text existent", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparent", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/cs-CZ.json b/src/locales/cs-CZ.json index 528fb1b639..8d758e0dad 100644 --- a/src/locales/cs-CZ.json +++ b/src/locales/cs-CZ.json @@ -109,8 +109,12 @@ "createContainerFromText": "Zabalit text do kontejneru", "link": { "edit": "Upravit odkaz", + "editEmbed": "", "create": "Vytvořit odkaz", - "label": "Odkaz" + "createEmbed": "", + "label": "Odkaz", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Upravit čáru", @@ -160,13 +164,16 @@ "darkMode": "Tmavý režim", "lightMode": "Světlý režim", "zenMode": "Zen mód", + "objectsSnapMode": "", "exitZenMode": "Opustit zen mód", "cancel": "Zrušit", "clear": "Vyčistit", "remove": "Odstranit", + "embed": "", "publishLibrary": "Zveřejnit", "submit": "Odeslat", - "confirm": "Potvrdit" + "confirm": "Potvrdit", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Toto vymaže celé plátno. Jste si jisti?", @@ -196,6 +203,7 @@ "imageInsertError": "Nelze vložit obrázek. Zkuste to později...", "fileTooBig": "Soubor je příliš velký. Maximální povolená velikost je {{maxSize}}.", "svgImageInsertError": "Nelze vložit SVG obrázek. Značení SVG je neplatné.", + "failedToFetchImage": "", "invalidSVGString": "Neplatný SVG.", "cannotResolveCollabServer": "Nelze se připojit ke sdílenému serveru. Prosím obnovte stránku a zkuste to znovu.", "importLibraryError": "Nelze načíst knihovnu", @@ -206,6 +214,10 @@ "line2": "To by mohlo vést k narušení Textových elementů ve vašich výkresech.", "line3": "Důrazně doporučujeme zakázat toto nastavení. Můžete sledovat tyto kroky jak to udělat.", "line4": "Pokud vypnutí tohoto nastavení neopravuje zobrazení textových prvků, prosím, otevřete problém na našem GitHubu, nebo nám napište na Discord" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Přidat/aktualizovat odkaz pro vybraný tvar", "eraser": "Guma", "frame": "", + "embeddable": "", + "laser": "", "hand": "Ruka (nástroj pro posouvání)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Kliknutím pro více bodů, táhnutím pro jednu čáru", "freeDraw": "Klikněte a táhněte, pro ukončení pusťte", "text": "Tip: Text můžete také přidat dvojitým kliknutím kdekoli pomocí nástroje pro výběr", + "embeddable": "", "text_selected": "Dvojklikem nebo stisknutím klávesy ENTER upravíte text", "text_editing": "Stiskněte Escape nebo Ctrl/Cmd+ENTER pro dokončení úprav", "linearElementMulti": "Klikněte na poslední bod nebo stiskněte Escape anebo Enter pro dokončení", @@ -252,7 +267,8 @@ "bindTextToElement": "Stiskněte Enter pro přidání textu", "deepBoxSelect": "Podržte Ctrl/Cmd pro hluboký výběr a pro zabránění táhnutí", "eraserRevert": "Podržením klávesy Alt vrátíte prvky označené pro smazání", - "firefox_clipboard_write": "Tato funkce může být povolena nastavením vlajky \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Chcete-li změnit vlajky prohlížeče ve Firefoxu, navštivte stránku \"about:config\"." + "firefox_clipboard_write": "Tato funkce může být povolena nastavením vlajky \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Chcete-li změnit vlajky prohlížeče ve Firefoxu, navštivte stránku \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Náhled nelze zobrazit", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Uloženo do {filename}", "canvas": "plátno", "selection": "výběr", - "pasteAsSingleElement": "Pomocí {{shortcut}} vložte jako jeden prvek,\nnebo vložte do existujícího textového editoru" + "pasteAsSingleElement": "Pomocí {{shortcut}} vložte jako jeden prvek,\nnebo vložte do existujícího textového editoru", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Průhledná", @@ -449,5 +467,36 @@ "shades": "Stíny", "hexCode": "Hex kód", "noShades": "Pro tuto barvu nejsou k dispozici žádné odstíny" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/da-DK.json b/src/locales/da-DK.json index dd2b8c3fad..0cd9c8ce02 100644 --- a/src/locales/da-DK.json +++ b/src/locales/da-DK.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Indsæt", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Indsæt som klartekst", "pasteCharts": "Indsæt diagrammer", "selectAll": "Marker alle", "multiSelect": "Tilføj element til markering", @@ -50,7 +50,7 @@ "veryLarge": "Meget stor", "solid": "Solid", "hachure": "Skravering", - "zigzag": "", + "zigzag": "Zigzag", "crossHatch": "Krydsskravering", "thin": "Tynd", "bold": "Fed", @@ -69,8 +69,8 @@ "layers": "Lag", "actions": "Handlinger", "language": "Sprog", - "liveCollaboration": "", - "duplicateSelection": "", + "liveCollaboration": "Live samarbejde...", + "duplicateSelection": "Duplikér", "untitled": "Unavngivet", "name": "Navn", "yourName": "Dit navn", @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "Mørk tilstand", "lightMode": "Lys baggrund", "zenMode": "Zentilstand", + "objectsSnapMode": "", "exitZenMode": "Stop zentilstand", "cancel": "Annuller", "clear": "Ryd", "remove": "Fjern", + "embed": "", "publishLibrary": "Publicér", "submit": "Gem", - "confirm": "Bekræft" + "confirm": "Bekræft", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Dette vil rydde hele lærredet. Er du sikker?", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "Klik og træk, slip når du er færdig", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Gemt som {filename}", "canvas": "canvas", "selection": "markering", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index 81fe54dd1e..c41186ef1e 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -109,8 +109,12 @@ "createContainerFromText": "Text in Container einbetten", "link": { "edit": "Link bearbeiten", + "editEmbed": "Link bearbeiten & einbetten", "create": "Link erstellen", - "label": "Link" + "createEmbed": "Link erstellen & einbetten", + "label": "Link", + "labelEmbed": "Verlinken & einbetten", + "empty": "Kein Link festgelegt" }, "lineEditor": { "edit": "Linie bearbeiten", @@ -160,13 +164,16 @@ "darkMode": "Dunkles Design", "lightMode": "Helles Design", "zenMode": "Zen-Modus", + "objectsSnapMode": "Einrasten an Objekten", "exitZenMode": "Zen-Modus verlassen", "cancel": "Abbrechen", "clear": "Löschen", "remove": "Entfernen", + "embed": "Einbettung umschalten", "publishLibrary": "Veröffentlichen", "submit": "Absenden", - "confirm": "Bestätigen" + "confirm": "Bestätigen", + "embeddableInteractionButton": "Klicken, um zu interagieren" }, "alerts": { "clearReset": "Dies wird die ganze Zeichenfläche löschen. Bist du dir sicher?", @@ -196,6 +203,7 @@ "imageInsertError": "Das Bild konnte nicht eingefügt werden. Versuche es später erneut...", "fileTooBig": "Die Datei ist zu groß. Die maximal zulässige Größe ist {{maxSize}}.", "svgImageInsertError": "SVG-Bild konnte nicht eingefügt werden. Das SVG-Markup sieht ungültig aus.", + "failedToFetchImage": "Bild konnte nicht abgerufen werden.", "invalidSVGString": "Ungültige SVG.", "cannotResolveCollabServer": "Konnte keine Verbindung zum Collab-Server herstellen. Bitte lade die Seite neu und versuche es erneut.", "importLibraryError": "Bibliothek konnte nicht geladen werden", @@ -206,6 +214,10 @@ "line2": "Dies könnte dazu führen, dass die Textelemente in Ihren Zeichnungen zerstört werden.", "line3": "Wir empfehlen dringend, diese Einstellung zu deaktivieren. Dazu kannst Du diesen Schritten folgen.", "line4": "Wenn die Deaktivierung dieser Einstellung die fehlerhafte Anzeige von Textelementen nicht behebt, öffne bitte ein Ticket auf unserem GitHub oder schreibe uns auf Discord" + }, + "libraryElementTypeError": { + "embeddable": "Einbettbare Elemente können der Bibliothek nicht hinzugefügt werden.", + "image": "Unterstützung für das Hinzufügen von Bildern in die Bibliothek kommt bald!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Link für ausgewählte Form hinzufügen / aktualisieren", "eraser": "Radierer", "frame": "Rahmenwerkzeug", + "embeddable": "Web-Einbettung", + "laser": "Laserpointer", "hand": "Hand (Schwenkwerkzeug)", "extraTools": "Weitere Werkzeuge" }, @@ -237,6 +251,7 @@ "linearElement": "Klicken für Linie mit mehreren Punkten, Ziehen für einzelne Linie", "freeDraw": "Klicke und ziehe. Lass los, wenn du fertig bist", "text": "Tipp: Du kannst auch Text hinzufügen, indem du mit dem Auswahlwerkzeug auf eine beliebige Stelle doppelklickst", + "embeddable": "Klicken und ziehen, um eine Webseiten-Einbettung zu erstellen", "text_selected": "Doppelklicken oder Eingabetaste drücken, um Text zu bearbeiten", "text_editing": "Drücke Escape oder CtrlOrCmd+Eingabetaste, um die Bearbeitung abzuschließen", "linearElementMulti": "Zum Beenden auf den letzten Punkt klicken oder Escape oder Eingabe drücken", @@ -252,7 +267,8 @@ "bindTextToElement": "Zum Hinzufügen Eingabetaste drücken", "deepBoxSelect": "Halte CtrlOrCmd gedrückt, um innerhalb der Gruppe auszuwählen, und um Ziehen zu vermeiden", "eraserRevert": "Halte Alt gedrückt, um die zum Löschen markierten Elemente zurückzusetzen", - "firefox_clipboard_write": "Diese Funktion kann wahrscheinlich aktiviert werden, indem die Einstellung \"dom.events.asyncClipboard.clipboardItem\" auf \"true\" gesetzt wird. Um die Browsereinstellungen in Firefox zu ändern, besuche die Seite \"about:config\"." + "firefox_clipboard_write": "Diese Funktion kann wahrscheinlich aktiviert werden, indem die Einstellung \"dom.events.asyncClipboard.clipboardItem\" auf \"true\" gesetzt wird. Um die Browsereinstellungen in Firefox zu ändern, besuche die Seite \"about:config\".", + "disableSnapping": "Halte CtrlOrCmd gedrückt, um das Einrasten zu deaktivieren" }, "canvasError": { "cannotShowPreview": "Vorschau kann nicht angezeigt werden", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Als {filename} gespeichert", "canvas": "Zeichenfläche", "selection": "Auswahl", - "pasteAsSingleElement": "Verwende {{shortcut}} , um als einzelnes Element\neinzufügen oder in einen existierenden Texteditor einzufügen" + "pasteAsSingleElement": "Verwende {{shortcut}} , um als einzelnes Element\neinzufügen oder in einen existierenden Texteditor einzufügen", + "unableToEmbed": "Einbetten dieser URL ist derzeit nicht zulässig. Erstelle einen Issue auf GitHub, um die URL freigeben zu lassen", + "unrecognizedLinkFormat": "Der Link, den Du eingebettet hast, stimmt nicht mit dem erwarteten Format überein. Bitte versuche den 'embed' String einzufügen, der von der Quellseite zur Verfügung gestellt wird" }, "colors": { "transparent": "Transparent", @@ -449,5 +467,36 @@ "shades": "Schattierungen", "hexCode": "Hex-Code", "noShades": "Keine Schattierungen für diese Farbe verfügbar" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Als Bild exportieren", + "button": "Als Bild exportieren", + "description": "Exportiere die Zeichnungsdaten als ein Bild, von dem Du später importieren kannst." + }, + "saveToDisk": { + "title": "Auf Festplatte speichern", + "button": "Auf Festplatte speichern", + "description": "Exportiere die Zeichnungsdaten in eine Datei, von der Du später importieren kannst." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Export nach Excalidraw+", + "description": "Speichere die Szene in deinem Excalidraw+-Arbeitsbereich." + } + }, + "modal": { + "loadFromFile": { + "title": "Aus Datei laden", + "button": "Aus Datei laden", + "description": "Das Laden aus einer Datei wird Deinen vorhandenen Inhalt ersetzen.

Du kannst Deine Zeichnung zuerst mit einer der folgenden Optionen sichern." + }, + "shareableLink": { + "title": "Aus Link laden", + "button": "Meinen Inhalt ersetzen", + "description": "Das Laden einer externen Zeichnung wird Deinen vorhandenen Inhalt ersetzen.

Du kannst Deine Zeichnung zuerst mit einer der folgenden Optionen sichern." + } + } } } diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json index ce52d82811..5a60fbb8be 100644 --- a/src/locales/el-GR.json +++ b/src/locales/el-GR.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Επεξεργασία συνδέσμου", + "editEmbed": "", "create": "Δημιουργία συνδέσμου", - "label": "Σύνδεσμος" + "createEmbed": "", + "label": "Σύνδεσμος", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Επεξεργασία γραμμής", @@ -160,13 +164,16 @@ "darkMode": "Σκοτεινή λειτουργία", "lightMode": "Φωτεινή λειτουργία", "zenMode": "Λειτουργία Zεν", + "objectsSnapMode": "", "exitZenMode": "Έξοδος από την λειτουργία Zen", "cancel": "Ακύρωση", "clear": "Καθαρισμός", "remove": "Κατάργηση", + "embed": "", "publishLibrary": "Δημοσίευση", "submit": "Υποβολή", - "confirm": "Επιβεβαίωση" + "confirm": "Επιβεβαίωση", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Αυτό θα σβήσει ολόκληρο τον καμβά. Είσαι σίγουρος;", @@ -196,6 +203,7 @@ "imageInsertError": "Αδυναμία εισαγωγής εικόνας. Προσπαθήστε ξανά αργότερα...", "fileTooBig": "Το αρχείο είναι πολύ μεγάλο. Το μέγιστο επιτρεπόμενο μέγεθος είναι {{maxSize}}.", "svgImageInsertError": "Αδυναμία εισαγωγής εικόνας SVG. Η σήμανση της SVG δεν φαίνεται έγκυρη.", + "failedToFetchImage": "", "invalidSVGString": "Μη έγκυρο SVG.", "cannotResolveCollabServer": "Αδυναμία σύνδεσης με τον διακομιστή συνεργασίας. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά.", "importLibraryError": "Αδυναμία φόρτωσης βιβλιοθήκης", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Προσθήκη/ Ενημέρωση συνδέσμου για ένα επιλεγμένο σχήμα", "eraser": "Γόμα", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Κάνε κλικ για να ξεκινήσεις πολλαπλά σημεία, σύρε για μια γραμμή", "freeDraw": "Κάντε κλικ και σύρτε, απελευθερώσατε όταν έχετε τελειώσει", "text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών", + "embeddable": "", "text_selected": "Κάντε διπλό κλικ ή πατήστε ENTER για να επεξεργαστείτε το κείμενο", "text_editing": "Πατήστε Escape ή CtrlOrCmd+ENTER για να ολοκληρώσετε την επεξεργασία", "linearElementMulti": "Κάνε κλικ στο τελευταίο σημείο ή πάτησε Escape ή Enter για να τελειώσεις", @@ -252,7 +267,8 @@ "bindTextToElement": "Πατήστε Enter για προσθήκη κειμένου", "deepBoxSelect": "Κρατήστε πατημένο το CtrlOrCmd για να επιλέξετε βαθιά, και να αποτρέψετε τη μεταφορά", "eraserRevert": "Κρατήστε πατημένο το Alt για να επαναφέρετε τα στοιχεία που σημειώθηκαν για διαγραφή", - "firefox_clipboard_write": "Αυτή η επιλογή μπορεί πιθανώς να ενεργοποιηθεί αλλάζοντας την ρύθμιση \"dom.events.asyncClipboard.clipboardItem\" σε \"true\". Για να αλλάξετε τις ρυθμίσεις του προγράμματος περιήγησης στο Firefox, επισκεφθείτε τη σελίδα \"about:config\"." + "firefox_clipboard_write": "Αυτή η επιλογή μπορεί πιθανώς να ενεργοποιηθεί αλλάζοντας την ρύθμιση \"dom.events.asyncClipboard.clipboardItem\" σε \"true\". Για να αλλάξετε τις ρυθμίσεις του προγράμματος περιήγησης στο Firefox, επισκεφθείτε τη σελίδα \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Αδυναμία εμφάνισης προεπισκόπησης", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Αποθηκεύτηκε στο {filename}", "canvas": "καμβάς", "selection": "επιλογή", - "pasteAsSingleElement": "Χρησιμοποίησε το {{shortcut}} για να επικολλήσεις ως ένα μόνο στοιχείο,\nή να επικολλήσεις σε έναν υπάρχοντα επεξεργαστή κειμένου" + "pasteAsSingleElement": "Χρησιμοποίησε το {{shortcut}} για να επικολλήσεις ως ένα μόνο στοιχείο,\nή να επικολλήσεις σε έναν υπάρχοντα επεξεργαστή κειμένου", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Διαφανές", @@ -449,5 +467,36 @@ "shades": "Αποχρώσεις", "hexCode": "Κωδικός Hex", "noShades": "Δεν υπάρχουν διαθέσιμες αποχρώσεις για αυτό το χρώμα" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index ae8d30759b..d925ab7882 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -109,8 +109,12 @@ "createContainerFromText": "Envolver el texto en un contenedor", "link": { "edit": "Editar enlace", + "editEmbed": "Editar enlace e incrustar", "create": "Crear enlace", - "label": "Enlace" + "createEmbed": "Crear enlace e incrustar", + "label": "Enlace", + "labelEmbed": "Enlazar e incrustar", + "empty": "No se ha establecido un enlace" }, "lineEditor": { "edit": "Editar línea", @@ -124,9 +128,9 @@ }, "statusPublished": "Publicado", "sidebarLock": "Mantener barra lateral abierta", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Seleccionar todos los elementos en el marco", + "removeAllElementsFromFrame": "Eliminar todos los elementos del marco", + "eyeDropper": "Seleccionar un color del lienzo" }, "library": { "noItems": "No hay elementos añadidos todavía...", @@ -160,13 +164,16 @@ "darkMode": "Modo oscuro", "lightMode": "Modo claro", "zenMode": "Modo Zen", + "objectsSnapMode": "Ajustar a los objetos", "exitZenMode": "Salir del modo Zen", "cancel": "Cancelar", "clear": "Borrar", "remove": "Eliminar", + "embed": "", "publishLibrary": "Publicar", "submit": "Enviar", - "confirm": "Confirmar" + "confirm": "Confirmar", + "embeddableInteractionButton": "Pulsa para interactuar" }, "alerts": { "clearReset": "Esto limpiará todo el lienzo. Estás seguro?", @@ -196,16 +203,21 @@ "imageInsertError": "No se pudo insertar la imagen. Inténtelo de nuevo más tarde...", "fileTooBig": "Archivo demasiado grande. El tamaño máximo permitido es {{maxSize}}.", "svgImageInsertError": "No se pudo insertar la imagen SVG. El código SVG parece inválido.", + "failedToFetchImage": "", "invalidSVGString": "SVG no válido.", "cannotResolveCollabServer": "No se pudo conectar al servidor colaborador. Por favor, vuelva a cargar la página y vuelva a intentarlo.", "importLibraryError": "No se pudo cargar la librería", "collabSaveFailed": "No se pudo guardar en la base de datos del backend. Si los problemas persisten, debería guardar su archivo localmente para asegurarse de que no pierde su trabajo.", "collabSaveFailed_sizeExceeded": "No se pudo guardar en la base de datos del backend, el lienzo parece ser demasiado grande. Debería guardar el archivo localmente para asegurarse de que no pierde su trabajo.", "brave_measure_text_error": { - "line1": "", - "line2": "", - "line3": "", + "line1": "Parece que estás usando el navegador Brave con el ajuste Forzar el bloqueo de huellas digitales habilitado.", + "line2": "Esto podría resultar en errores en los Elementos de Texto en tus dibujos.", + "line3": "Recomendamos fuertemente deshabilitar esta configuración. Puedes seguir estos pasos sobre cómo hacerlo.", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,8 +236,10 @@ "link": "Añadir/Actualizar enlace para una forma seleccionada", "eraser": "Borrar", "frame": "", + "embeddable": "Incrustar Web", + "laser": "Puntero láser", "hand": "Mano (herramienta de panoramización)", - "extraTools": "" + "extraTools": "Más herramientas" }, "headings": { "canvasActions": "Acciones del lienzo", @@ -237,6 +251,7 @@ "linearElement": "Haz clic para dibujar múltiples puntos, arrastrar para solo una línea", "freeDraw": "Haz clic y arrastra, suelta al terminar", "text": "Consejo: también puedes añadir texto haciendo doble clic en cualquier lugar con la herramienta de selección", + "embeddable": "Haga clic y arrastre para crear un sitio web incrustado", "text_selected": "Doble clic o pulse ENTER para editar el texto", "text_editing": "Pulse Escape o Ctrl/Cmd + ENTER para terminar de editar", "linearElementMulti": "Haz clic en el último punto o presiona Escape o Enter para finalizar", @@ -252,7 +267,8 @@ "bindTextToElement": "Presione Entrar para agregar", "deepBoxSelect": "Mantén CtrlOrCmd para seleccionar en profundidad, y para evitar arrastrar", "eraserRevert": "Mantenga pulsado Alt para revertir los elementos marcados para su eliminación", - "firefox_clipboard_write": "Esta característica puede ser habilitada estableciendo la bandera \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar las banderas del navegador en Firefox, visite la página \"about:config\"." + "firefox_clipboard_write": "Esta característica puede ser habilitada estableciendo la bandera \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar las banderas del navegador en Firefox, visite la página \"about:config\".", + "disableSnapping": "Mantén pulsado CtrlOrCmd para desactivar el ajuste" }, "canvasError": { "cannotShowPreview": "No se puede mostrar la vista previa", @@ -360,27 +376,27 @@ "removeItemsFromLib": "Eliminar elementos seleccionados de la biblioteca" }, "imageExportDialog": { - "header": "", + "header": "Exportar imagen", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", - "padding": "" + "withBackground": "Fondo", + "onlySelected": "Sólo seleccionados", + "darkMode": "Modo oscuro", + "embedScene": "Incrustar escena", + "scale": "Escalar", + "padding": "Espaciado" }, "tooltip": { "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "Exportar a PNG", + "exportToSvg": "Exportar a SVG", + "copyPngToClipboard": "Copiar PNG al portapapeles" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Copiar al portapapeles" } }, "encrypted": { @@ -411,24 +427,26 @@ "fileSavedToFilename": "Guardado en {filename}", "canvas": "lienzo", "selection": "selección", - "pasteAsSingleElement": "Usa {{shortcut}} para pegar como un solo elemento,\no pegar en un editor de texto existente" + "pasteAsSingleElement": "Usa {{shortcut}} para pegar como un solo elemento,\no pegar en un editor de texto existente", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparente", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "Negro", + "white": "Blanco", + "red": "Rojo", + "pink": "Rosa", + "grape": "Uva", + "violet": "Violeta", + "gray": "Gris", + "blue": "Azul", + "cyan": "Cian", + "teal": "Turquesa", + "green": "Verde", + "yellow": "Amarillo", + "orange": "Naranja", + "bronze": "Bronce" }, "welcomeScreen": { "app": { @@ -444,10 +462,41 @@ } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", + "mostUsedCustomColors": "Colores personalizados más utilizados", + "colors": "Colores", "shades": "", - "hexCode": "", + "hexCode": "Código Hexadecimal", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportar como imagen", + "button": "Exportar como imagen", + "description": "" + }, + "saveToDisk": { + "title": "Guardar en el disco", + "button": "Guardar en el disco", + "description": "Exporta los datos de la escena a un archivo desde el cual podrás importar más tarde." + }, + "excalidrawPlus": { + "title": "", + "button": "Exportar a Excalidraw+", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "Cargar desde un archivo", + "button": "Cargar desde un archivo", + "description": "" + }, + "shareableLink": { + "title": "Cargar desde un enlace", + "button": "Reemplazar mi contenido", + "description": "Cargar un dibujo externo reemplazará tu contenido existente.

Puedes primero hacer una copia de seguridad de tu dibujo usando una de las opciones de abajo." + } + } } } diff --git a/src/locales/eu-ES.json b/src/locales/eu-ES.json index e66849dacc..5a257f05d8 100644 --- a/src/locales/eu-ES.json +++ b/src/locales/eu-ES.json @@ -109,8 +109,12 @@ "createContainerFromText": "Bilatu testua edukiontzi batean", "link": { "edit": "Editatu esteka", + "editEmbed": "Editatu esteka eta kapsulatu", "create": "Sortu esteka", - "label": "Esteka" + "createEmbed": "Sortu esteka eta kapsulatu", + "label": "Esteka", + "labelEmbed": "Esteka eta kapsula", + "empty": "Ez da estekarik ezarri" }, "lineEditor": { "edit": "Editatu lerroa", @@ -160,13 +164,16 @@ "darkMode": "Modu iluna", "lightMode": "Modu argia", "zenMode": "Zen modua", + "objectsSnapMode": "Atxiki objektuei", "exitZenMode": "Irten Zen modutik", "cancel": "Utzi", "clear": "Garbitu", "remove": "Kendu", + "embed": "Aldatu kapsulatzea", "publishLibrary": "Argitaratu", "submit": "Bidali", - "confirm": "Bai" + "confirm": "Bai", + "embeddableInteractionButton": "Egin klik elkar eragiteko" }, "alerts": { "clearReset": "Honek oihal osoa garbituko du. Ziur zaude?", @@ -196,6 +203,7 @@ "imageInsertError": "Ezin izan da irudia txertatu. Saiatu berriro geroago...", "fileTooBig": "Fitxategia handiegia da. Onartutako gehienezko tamaina {{maxSize}} da.", "svgImageInsertError": "Ezin izan da SVG irudia txertatu. SVG markak baliogabea dirudi.", + "failedToFetchImage": "", "invalidSVGString": "SVG baliogabea.", "cannotResolveCollabServer": "Ezin izan da elkarlaneko zerbitzarira konektatu. Mesedez, berriro kargatu orria eta saiatu berriro.", "importLibraryError": "Ezin izan da liburutegia kargatu", @@ -206,6 +214,10 @@ "line2": "Honek zure marrazkietako Testu-elementuak hautsi ditzake.", "line3": "Ezarpen hau desgaitzea gomendatzen dugu. urrats hauek jarrai ditzakezu hori nola egin jakiteko.", "line4": "Ezarpen hau desgaituz gero, testu-elementuen bistaratzea konpontzen ez bada, ireki arazo gure GitHub-en edo idatzi iezaguzu Discord helbidera" + }, + "libraryElementTypeError": { + "embeddable": "Kapsulatutako elementuak ezin dira liburutegira gehitu.", + "image": "Laster egongo da irudiak liburutegian gehitzeko laguntza!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Gehitu / Eguneratu esteka hautatutako forma baterako", "eraser": "Borragoma", "frame": "Marko tresna", + "embeddable": "Web kapsulatzea", + "laser": "Laser punteroa", "hand": "Eskua (panoratze tresna)", "extraTools": "Tresna gehiago" }, @@ -237,6 +251,7 @@ "linearElement": "Egin klik hainbat puntu hasteko, arrastatu lerro bakarrerako", "freeDraw": "Egin klik eta arrastatu, askatu amaitutakoan", "text": "Aholkua: testua gehitu dezakezu edozein lekutan klik bikoitza eginez hautapen tresnarekin", + "embeddable": "Egin klik eta arrastatu webgunea kapsulatzeko", "text_selected": "Egin klik bikoitza edo sakatu SARTU testua editatzeko", "text_editing": "Sakatu Esc edo Ctrl+SARTU editatzen amaitzeko", "linearElementMulti": "Egin klik azken puntuan edo sakatu Esc edo Sartu amaitzeko", @@ -252,7 +267,8 @@ "bindTextToElement": "Sakatu Sartu testua gehitzeko", "deepBoxSelect": "Eutsi Ctrl edo Cmd sakatuta aukeraketa sakona egiteko eta arrastatzea saihesteko", "eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko", - "firefox_clipboard_write": "Ezaugarri hau \"dom.events.asyncClipboard.clipboardItem\" marka \"true\" gisa ezarrita gaitu daiteke. Firefox-en arakatzailearen banderak aldatzeko, bisitatu \"about:config\" orrialdera." + "firefox_clipboard_write": "Ezaugarri hau \"dom.events.asyncClipboard.clipboardItem\" marka \"true\" gisa ezarrita gaitu daiteke. Firefox-en arakatzailearen banderak aldatzeko, bisitatu \"about:config\" orrialdera.", + "disableSnapping": "Eduki sakatuta Ctrl edo Cmd tekla atxikipena desgaitzeko" }, "canvasError": { "cannotShowPreview": "Ezin da oihala aurreikusi", @@ -411,7 +427,9 @@ "fileSavedToFilename": "{filename}-n gorde da", "canvas": "oihala", "selection": "hautapena", - "pasteAsSingleElement": "Erabili {{shortcut}} elementu bakar gisa itsasteko,\nedo itsatsi lehendik dagoen testu-editore batean" + "pasteAsSingleElement": "Erabili {{shortcut}} elementu bakar gisa itsasteko,\nedo itsatsi lehendik dagoen testu-editore batean", + "unableToEmbed": "Url hau txertatzea ez da une honetan onartzen. Sortu issue bat GitHub-en Urla zerrenda zurian sartzea eskatzeko", + "unrecognizedLinkFormat": "Kapsulatu duzun esteka ez dator bat espero den formatuarekin. Mesedez, saiatu iturburu-guneak emandako 'kapsulatu' katea itsasten" }, "colors": { "transparent": "Gardena", @@ -449,5 +467,36 @@ "shades": "Ñabardurak", "hexCode": "Hez kodea", "noShades": "Kolore honetarako ez dago ñabardurarik eskuragarri" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Esportatu irudi gisa", + "button": "Esportatu irudi gisa", + "description": "Esportatu eszenaren datuak geroago inportatu ahal izango duzun irudi gisa." + }, + "saveToDisk": { + "title": "Gorde diskoan", + "button": "Gorde diskoan", + "description": "Esportatu eszenaren datuak geroago inportatu ahal izango duzun fitxategi batan." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Esportatu Excalidraw+ra", + "description": "Gorde eszena zure Excalidraw+ laneko areara." + } + }, + "modal": { + "loadFromFile": { + "title": "Fitxategitik kargatu", + "button": "Fitxategitik kargatu", + "description": "Fitxategi batetik kargatzeak lehendik duzun edukia ordezkatuko du.

Lehenengo marrazkiaren babeskopia egin dezakezu beheko aukeretako bat erabiliz." + }, + "shareableLink": { + "title": "Estekatik kargatu", + "button": "Ordeztu nire edukia", + "description": "Kanpoko irudi bat kargatzeak lehendik duzun edukia ordezkatuko du.

. Zure marrazkiaren babeskopia egin dezakezu lehenik beheko aukeretako bat erabiliz." + } + } } } diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json index 1870a17308..1018032589 100644 --- a/src/locales/fa-IR.json +++ b/src/locales/fa-IR.json @@ -109,8 +109,12 @@ "createContainerFromText": "متن را در یک جایگاه بپیچید", "link": { "edit": "ویرایش لینک", + "editEmbed": "", "create": "ایجاد پیوند", - "label": "لینک" + "createEmbed": "", + "label": "لینک", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "ویرایش لینک", @@ -126,7 +130,7 @@ "sidebarLock": "باز نگه داشتن سایدبار", "selectAllElementsInFrame": "", "removeAllElementsFromFrame": "", - "eyeDropper": "" + "eyeDropper": "انتخاب رنگ از کرباس" }, "library": { "noItems": "آیتمی به اینجا اضافه نشده...", @@ -160,13 +164,16 @@ "darkMode": "حالت تیره", "lightMode": "حالت روشن", "zenMode": "حالت ذن", + "objectsSnapMode": "", "exitZenMode": "خروج از حالت تمرکز", "cancel": "لغو", "clear": "پاک کردن", "remove": "پاک کردن", + "embed": "", "publishLibrary": "انتشار", "submit": "ارسال", - "confirm": "تایید" + "confirm": "تایید", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "این کار کل صفحه را پاک میکند. آیا مطمئنید؟", @@ -196,6 +203,7 @@ "imageInsertError": "عکس ارسال نشد. بعداً دوباره تلاش کنید...", "fileTooBig": "فایل خیلی بزرگ است حداکثر اندازه مجاز {{maxSize}}.", "svgImageInsertError": "تصویر SVG وارد نشد. نشانه گذاری SVG نامعتبر به نظر می رسد.", + "failedToFetchImage": "", "invalidSVGString": "SVG نادرست.", "cannotResolveCollabServer": "به سرور collab متصل نشد. لطفا صفحه را مجددا بارگذاری کنید و دوباره تلاش کنید.", "importLibraryError": "داده‌ها بارگذاری نشدند", @@ -206,6 +214,10 @@ "line2": "این می تواند منجر به شکستن عناصر متن در نقاشی های شما شود.", "line3": "اکیداً توصیه می کنیم این تنظیم را غیرفعال کنید. برای نحوه انجام این کار می‌توانید این مراحل را دنبال کنید.", "line4": "اگر غیرفعال کردن این تنظیم نمایش عناصر متنی را برطرف نکرد، لطفاً یک مشکل را در GitHub ما باز کنید یا برای ما در Discord بنویسید." + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,8 +236,10 @@ "link": "افزودن/به‌روزرسانی پیوند برای شکل انتخابی", "eraser": "پاک کن", "frame": "", + "embeddable": "", + "laser": "", "hand": "دست (ابزار پانینگ)", - "extraTools": "" + "extraTools": "ابزارهای بیشتر" }, "headings": { "canvasActions": "عملیات روی بوم", @@ -237,6 +251,7 @@ "linearElement": "برای چند نقطه کلیک و برای یک خط بکشید", "freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید", "text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید", + "embeddable": "", "text_selected": "دوبار کلیک کنید یا Enter را فشار دهید تا نقاط را ویرایش کنید", "text_editing": "Escape یا CtrlOrCmd+ENTER را فشار دهید تا ویرایش تمام شود", "linearElementMulti": "روی آخرین نقطه کلیک کنید یا کلید ESC را بزنید یا کلید Enter را بزنید برای اتمام کار", @@ -252,7 +267,8 @@ "bindTextToElement": "برای افزودن اینتر را بزنید", "deepBoxSelect": "CtrlOrCmd را برای انتخاب عمیق و جلوگیری از کشیدن نگه دارید", "eraserRevert": "Alt را نگه دارید تا عناصر علامت گذاری شده برای حذف برگردند", - "firefox_clipboard_write": "احتمالاً می‌توان این ویژگی را با تنظیم پرچم «dom.events.asyncClipboard.clipboardItem» روی «true» فعال کرد. برای تغییر پرچم های مرورگر در فایرفاکس، از صفحه \"about:config\" دیدن کنید." + "firefox_clipboard_write": "احتمالاً می‌توان این ویژگی را با تنظیم پرچم «dom.events.asyncClipboard.clipboardItem» روی «true» فعال کرد. برای تغییر پرچم های مرورگر در فایرفاکس، از صفحه \"about:config\" دیدن کنید.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "پیش نمایش نشان داده نمی شود", @@ -362,7 +378,7 @@ "imageExportDialog": { "header": "", "label": { - "withBackground": "", + "withBackground": "پس زمینه", "onlySelected": "", "darkMode": "", "embedScene": "", @@ -411,24 +427,26 @@ "fileSavedToFilename": "ذخیره در {filename}", "canvas": "بوم", "selection": "انتخاب", - "pasteAsSingleElement": "از {{shortcut}} برای چسباندن به عنوان یک عنصر استفاده کنید،\nیا در یک ویرایشگر متن موجود جایگذاری کنید" + "pasteAsSingleElement": "از {{shortcut}} برای چسباندن به عنوان یک عنصر استفاده کنید،\nیا در یک ویرایشگر متن موجود جایگذاری کنید", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "شفاف", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "سیاه", + "white": "سفید", + "red": "قرمز", + "pink": "صورتی", + "grape": "یاقوتی", + "violet": "بنفش", + "gray": "خاکستری", + "blue": "آبی", + "cyan": "فیروزه‌ای", + "teal": "سبزآبی", + "green": "سبز", + "yellow": "زرد", + "orange": "نارنجی", + "bronze": "برنزی" }, "welcomeScreen": { "app": { @@ -445,9 +463,40 @@ }, "colorPicker": { "mostUsedCustomColors": "", - "colors": "", - "shades": "", - "hexCode": "", + "colors": "رنگ‌ها", + "shades": "جلوه‌ها", + "hexCode": "کدِ هگز", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "ذخیره در دیسک", + "button": "ذخیره در دیسک", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "بارگذاری از فایل", + "button": "بارگذاری از فایل", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json index d1c68b12a4..0a8963f9a1 100644 --- a/src/locales/fi-FI.json +++ b/src/locales/fi-FI.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Muokkaa linkkiä", + "editEmbed": "", "create": "Luo linkki", - "label": "Linkki" + "createEmbed": "", + "label": "Linkki", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Muokkaa riviä", @@ -160,13 +164,16 @@ "darkMode": "Tumma tila", "lightMode": "Vaalea tila", "zenMode": "Zen-tila", + "objectsSnapMode": "", "exitZenMode": "Poistu zen-tilasta", "cancel": "Peruuta", "clear": "Pyyhi", "remove": "Poista", + "embed": "", "publishLibrary": "Julkaise", "submit": "Lähetä", - "confirm": "Vahvista" + "confirm": "Vahvista", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Tämä tyhjentää koko piirtoalueen. Jatketaanko?", @@ -196,6 +203,7 @@ "imageInsertError": "Kuvan lisääminen epäonnistui. Yritä myöhemmin uudelleen...", "fileTooBig": "Tiedosto on liian suuri. Suurin sallittu koko on {{maxSize}}.", "svgImageInsertError": "SVG- kuvaa ei voitu lisätä. Tiedoston SVG-sisältö näyttää virheelliseltä.", + "failedToFetchImage": "", "invalidSVGString": "Virheellinen SVG.", "cannotResolveCollabServer": "Yhteyden muodostaminen collab-palvelimeen epäonnistui. Virkistä sivu ja yritä uudelleen.", "importLibraryError": "Kokoelman lataaminen epäonnistui", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Lisää/päivitä linkki valitulle muodolle", "eraser": "Poistotyökalu", "frame": "", + "embeddable": "", + "laser": "", "hand": "Käsi (panning-työkalu)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Klikkaa piirtääksesi useampi piste, raahaa piirtääksesi yksittäinen viiva", "freeDraw": "Paina ja raahaa, päästä irti kun olet valmis", "text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla", + "embeddable": "", "text_selected": "Kaksoisnapsauta tai paina ENTER muokataksesi tekstiä", "text_editing": "Paina Escape tai CtrlOrCmd+ENTER lopettaaksesi muokkaamisen", "linearElementMulti": "Lopeta klikkaamalla viimeistä pistettä, painamalla Escape- tai Enter-näppäintä", @@ -252,7 +267,8 @@ "bindTextToElement": "Lisää tekstiä painamalla enter", "deepBoxSelect": "Käytä syvävalintaa ja estä raahaus painamalla CtrlOrCmd", "eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen", - "firefox_clipboard_write": "Tämä ominaisuus voidaan todennäköisesti ottaa käyttöön asettamalla \"dom.events.asyncClipboard.clipboardItem\" kohta \"true\":ksi. Vaihtaaksesi selaimen kohdan Firefoxissa, käy \"about:config\" sivulla." + "firefox_clipboard_write": "Tämä ominaisuus voidaan todennäköisesti ottaa käyttöön asettamalla \"dom.events.asyncClipboard.clipboardItem\" kohta \"true\":ksi. Vaihtaaksesi selaimen kohdan Firefoxissa, käy \"about:config\" sivulla.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Esikatselua ei voitu näyttää", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Tallennettiin kohteeseen {filename}", "canvas": "piirtoalue", "selection": "valinta", - "pasteAsSingleElement": "Käytä {{shortcut}} liittääksesi yhtenä elementtinä,\ntai liittääksesi olemassa olevaan tekstieditoriin" + "pasteAsSingleElement": "Käytä {{shortcut}} liittääksesi yhtenä elementtinä,\ntai liittääksesi olemassa olevaan tekstieditoriin", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Läpinäkyvä", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index e48cf1a85c..8dfcc4c053 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -50,7 +50,7 @@ "veryLarge": "Très grande", "solid": "Solide", "hachure": "Hachures", - "zigzag": "", + "zigzag": "Zigzag", "crossHatch": "Hachures croisées", "thin": "Fine", "bold": "Épaisse", @@ -109,8 +109,12 @@ "createContainerFromText": "Encadrer le texte dans un conteneur", "link": { "edit": "Modifier le lien", + "editEmbed": "Éditer le lien & intégrer", "create": "Ajouter un lien", - "label": "Lien" + "createEmbed": "Créer un lien & intégrer", + "label": "Lien", + "labelEmbed": "", + "empty": "Aucun lien défini" }, "lineEditor": { "edit": "Modifier la ligne", @@ -124,8 +128,8 @@ }, "statusPublished": "Publié", "sidebarLock": "Maintenir la barre latérale ouverte", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", + "selectAllElementsInFrame": "Sélectionner tous les éléments du cadre", + "removeAllElementsFromFrame": "Supprimer tous les éléments du cadre", "eyeDropper": "" }, "library": { @@ -160,13 +164,16 @@ "darkMode": "Mode sombre", "lightMode": "Mode clair", "zenMode": "Mode zen", + "objectsSnapMode": "Aimanter aux objets", "exitZenMode": "Quitter le mode zen", "cancel": "Annuler", "clear": "Effacer", "remove": "Supprimer", + "embed": "Activer/Désactiver l'intégration", "publishLibrary": "Publier", "submit": "Envoyer", - "confirm": "Confirmer" + "confirm": "Confirmer", + "embeddableInteractionButton": "Cliquez pour interagir" }, "alerts": { "clearReset": "L'intégralité du canevas va être effacée. Êtes-vous sûr ?", @@ -196,16 +203,21 @@ "imageInsertError": "Impossible d'insérer l'image. Réessayez plus tard...", "fileTooBig": "Le fichier est trop volumineux. La taille maximale autorisée est de {{maxSize}}.", "svgImageInsertError": "Impossible d'insérer l'image SVG. Le balisage SVG semble invalide.", + "failedToFetchImage": "", "invalidSVGString": "SVG invalide.", "cannotResolveCollabServer": "Impossible de se connecter au serveur collaboratif. Veuillez recharger la page et réessayer.", "importLibraryError": "Impossible de charger la bibliothèque", "collabSaveFailed": "Impossible d'enregistrer dans la base de données en arrière-plan. Si des problèmes persistent, vous devriez enregistrer votre fichier localement pour vous assurer de ne pas perdre votre travail.", "collabSaveFailed_sizeExceeded": "Impossible d'enregistrer dans la base de données en arrière-plan, le tableau semble trop grand. Vous devriez enregistrer le fichier localement pour vous assurer de ne pas perdre votre travail.", "brave_measure_text_error": { - "line1": "", - "line2": "", - "line3": "", - "line4": "" + "line1": "On dirait que vous utilisez le navigateur Brave avec l'option Bloquer agressivement le fichage activée.", + "line2": "Cela pourrait entraîner des problèmes avec les Éléments Textuels dans vos dessins.", + "line3": "Nous recommandons fortement de désactiver cette option. Vous pouvez suivre ces instructions pour savoir comment faire.", + "line4": "Si désactiver cette option de résout pas le problème d'affichage des éléments textuels, veuillez ouvrir un ticket sur notre GitHub, ou écrivez-nous sur notre Discord" + }, + "libraryElementTypeError": { + "embeddable": "Les éléments intégrés ne peuvent pas être ajoutés à la librairie.", + "image": "Le support pour l'ajout d'images à la librairie arrive bientôt !" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Mode stylo - évite le toucher", "link": "Ajouter/mettre à jour le lien pour une forme sélectionnée", "eraser": "Gomme", - "frame": "", + "frame": "Outil de cadre", + "embeddable": "Intégration Web", + "laser": "", "hand": "Mains (outil de déplacement de la vue)", - "extraTools": "" + "extraTools": "Plus d'outils" }, "headings": { "canvasActions": "Actions du canevas", @@ -237,6 +251,7 @@ "linearElement": "Cliquez pour démarrer plusieurs points, faites glisser pour une seule ligne", "freeDraw": "Cliquez et faites glissez, relâchez quand vous avez terminé", "text": "Astuce : vous pouvez aussi ajouter du texte en double-cliquant n'importe où avec l'outil de sélection", + "embeddable": "Cliquez et glissez pour créer une intégration de site web", "text_selected": "Double-cliquez ou appuyez sur ENTRÉE pour modifier le texte", "text_editing": "Appuyez sur ÉCHAP ou Ctrl/Cmd+ENTRÉE pour terminer l'édition", "linearElementMulti": "Cliquez sur le dernier point ou appuyez sur Échap ou Entrée pour terminer", @@ -252,7 +267,8 @@ "bindTextToElement": "Appuyer sur Entrée pour ajouter du texte", "deepBoxSelect": "Maintenir Ctrl ou Cmd pour sélectionner dans les groupes et empêcher le déplacement", "eraserRevert": "Maintenez Alt enfoncé pour annuler les éléments marqués pour suppression", - "firefox_clipboard_write": "Cette fonctionnalité devrait pouvoir être activée en définissant l'option \"dom.events.asyncClipboard.clipboard.clipboardItem\" à \"true\". Pour modifier les paramètres du navigateur dans Firefox, visitez la page \"about:config\"." + "firefox_clipboard_write": "Cette fonctionnalité devrait pouvoir être activée en définissant l'option \"dom.events.asyncClipboard.clipboard.clipboardItem\" à \"true\". Pour modifier les paramètres du navigateur dans Firefox, visitez la page \"about:config\".", + "disableSnapping": "Maintenez CtrlOuCmd pour désactiver l'aimantation" }, "canvasError": { "cannotShowPreview": "Impossible d’afficher l’aperçu", @@ -360,27 +376,27 @@ "removeItemsFromLib": "Enlever les éléments sélectionnés de la bibliothèque" }, "imageExportDialog": { - "header": "", + "header": "Exporter l'image", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", + "withBackground": "Fond", + "onlySelected": "Uniquement la sélection", + "darkMode": "Mode sombre", + "embedScene": "Intégrer la scène", + "scale": "Échelle", "padding": "" }, "tooltip": { - "embedScene": "" + "embedScene": "Les données de la scène seront sauvegardées dans le fichier PNG/SVG exporté afin que la scène puisse être restaurée depuis celui-ci.\nCela augmentera la taille du fichier exporté." }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "Exporter en PNG", + "exportToSvg": "Exporter en SVG", + "copyPngToClipboard": "Copier le PNG dans le presse-papier" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Copier dans le presse-papier" } }, "encrypted": { @@ -411,24 +427,26 @@ "fileSavedToFilename": "Enregistré sous {filename}", "canvas": "canevas", "selection": "sélection", - "pasteAsSingleElement": "Utiliser {{shortcut}} pour coller comme un seul élément,\nou coller dans un éditeur de texte existant" + "pasteAsSingleElement": "Utiliser {{shortcut}} pour coller comme un seul élément,\nou coller dans un éditeur de texte existant", + "unableToEmbed": "Intégrer cet URL n'est actuellement pas autorisé. Ouvrez un ticket sur GitHub pour demander son ajout à la liste blanche", + "unrecognizedLinkFormat": "Le lien que vous avez intégré ne correspond pas au format attendu. Veuillez essayer de coller la chaîne d'intégration fournie par le site source" }, "colors": { "transparent": "Transparent", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "Noir", + "white": "Blanc", + "red": "Rouge", + "pink": "Rose", + "grape": "Mauve", + "violet": "Violet", + "gray": "Gris", + "blue": "Bleu", + "cyan": "Cyan", + "teal": "Turquoise", + "green": "Vert", + "yellow": "Jaune", + "orange": "Orange", + "bronze": "Bronze" }, "welcomeScreen": { "app": { @@ -444,10 +462,41 @@ } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", - "shades": "", - "hexCode": "", - "noShades": "" + "mostUsedCustomColors": "Couleurs personnalisées les plus fréquemment utilisées", + "colors": "Couleurs", + "shades": "Nuances", + "hexCode": "Code hex", + "noShades": "Aucune nuance disponible pour cette couleur" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exporter en image", + "button": "Exporter en image", + "description": "Exporter les données de la scène comme une image que vous pourrez importer ultérieurement." + }, + "saveToDisk": { + "title": "Sauvegarder sur le disque", + "button": "Sauvegarder sur le disque", + "description": "Exporter les données de la scène comme un fichier que vous pourrez importer ultérieurement." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exporter vers Excalidraw+", + "description": "Enregistrer la scène dans votre espace de travail Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Charger depuis un fichier", + "button": "Charger depuis un fichier", + "description": "Charger depuis un fichier va remplacer votre contenu existant.

Vous pouvez d'abord sauvegarder votre dessin en utilisant l'une des options ci-dessous." + }, + "shareableLink": { + "title": "Charger depuis un lien", + "button": "Remplacer mon contenu", + "description": "Charger un dessin externe va remplacer votre contenu existant.

Vous pouvez d'abord sauvegarder votre dessin en utilisant l'une des options ci-dessous." + } + } } } diff --git a/src/locales/gl-ES.json b/src/locales/gl-ES.json index 7522860a6c..6585635487 100644 --- a/src/locales/gl-ES.json +++ b/src/locales/gl-ES.json @@ -50,7 +50,7 @@ "veryLarge": "Moi grande", "solid": "Sólido", "hachure": "Folleto", - "zigzag": "", + "zigzag": "Zigzag", "crossHatch": "Raiado transversal", "thin": "Estreito", "bold": "Groso", @@ -109,8 +109,12 @@ "createContainerFromText": "Envolver o texto nun contedor", "link": { "edit": "Editar ligazón", + "editEmbed": "", "create": "Crear ligazón", - "label": "Ligazón" + "createEmbed": "", + "label": "Ligazón", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Editar liña", @@ -160,13 +164,16 @@ "darkMode": "Modo escuro", "lightMode": "Modo claro", "zenMode": "Modo zen", + "objectsSnapMode": "", "exitZenMode": "Saír do modo zen", "cancel": "Cancelar", "clear": "Limpar", "remove": "Eliminar", + "embed": "", "publishLibrary": "Publicar", "submit": "Enviar", - "confirm": "Confirmar" + "confirm": "Confirmar", + "embeddableInteractionButton": "Faga clic para interactuar" }, "alerts": { "clearReset": "Isto limpará todo o lenzo. Estás seguro?", @@ -196,6 +203,7 @@ "imageInsertError": "Non se puido inserir a imaxe. Probe de novo máis tarde...", "fileTooBig": "O ficheiro é demasiado grande. O tamaño máximo permitido é {{maxSize}}.", "svgImageInsertError": "Non se puido inserir como imaxe SVG. O marcado SVG semella inválido.", + "failedToFetchImage": "", "invalidSVGString": "SVG inválido.", "cannotResolveCollabServer": "Non se puido conectar ao servidor de colaboración. Por favor recargue a páxina e probe de novo.", "importLibraryError": "Non se puido cargar a biblioteca", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,8 +236,10 @@ "link": "Engadir/ Actualizar ligazón para a forma seleccionada", "eraser": "Goma de borrar", "frame": "", + "embeddable": "Inserir na web", + "laser": "Punteiro láser", "hand": "Man (ferramenta de desprazamento)", - "extraTools": "" + "extraTools": "Máis ferramentas" }, "headings": { "canvasActions": "Accións do lenzo", @@ -237,6 +251,7 @@ "linearElement": "Faga clic para iniciar varios puntos, arrastre para unha sola liña", "freeDraw": "Fai clic e arrastra, solta cando acabes", "text": "Consello: tamén podes engadir texto facendo dobre-clic en calquera lugar coa ferramenta de selección", + "embeddable": "Faga clic e arrastre para crear un sitio web embebido", "text_selected": "Dobre-clic ou prema ENTER para editar o texto", "text_editing": "Prema Escape ou CtrlOrCmd+ENTER para finalizar a edición", "linearElementMulti": "Faga clic no último punto ou prema Escape ou Enter para rematar", @@ -252,7 +267,8 @@ "bindTextToElement": "Prema a tecla enter para engadir texto", "deepBoxSelect": "Manteña pulsado CtrlOrCmd para seleccionar en profundidade e evitar o arrastre", "eraserRevert": "Manteña pulsado Alt para reverter os elementos marcados para a súa eliminación", - "firefox_clipboard_write": "Esta función pódese activar establecendo a opción \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar as opcións do navegador en Firefox, visita a páxina \"about:config\"." + "firefox_clipboard_write": "Esta función pódese activar establecendo a opción \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar as opcións do navegador en Firefox, visita a páxina \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Non se pode mostrar a vista previa", @@ -360,11 +376,11 @@ "removeItemsFromLib": "Eliminar os elementos seleccionados da biblioteca" }, "imageExportDialog": { - "header": "", + "header": "Exportar imaxe", "label": { - "withBackground": "", + "withBackground": "Fondo", "onlySelected": "", - "darkMode": "", + "darkMode": "Modo escuro", "embedScene": "", "scale": "", "padding": "" @@ -373,14 +389,14 @@ "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "Exportar a PNG", + "exportToSvg": "Exportar a SVG", + "copyPngToClipboard": "Copiar PNG ao portapapeis" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Copiar ao portapapeis" } }, "encrypted": { @@ -411,24 +427,26 @@ "fileSavedToFilename": "Gardado en {filename}", "canvas": "lenzo", "selection": "selección", - "pasteAsSingleElement": "Usa {{shortcut}} para pegar como un único elemento\nou pega nun editor de texto existente" + "pasteAsSingleElement": "Usa {{shortcut}} para pegar como un único elemento\nou pega nun editor de texto existente", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparente", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", + "black": "Negro", + "white": "Branco", + "red": "Vermello", + "pink": "Rosa", + "grape": "Uva", + "violet": "Violeta", + "gray": "Gris", + "blue": "Azul", "cyan": "", "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "green": "Verde", + "yellow": "Marelo", + "orange": "Laranxa", + "bronze": "Bronce" }, "welcomeScreen": { "app": { @@ -445,9 +463,40 @@ }, "colorPicker": { "mostUsedCustomColors": "", - "colors": "", + "colors": "Cores", "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportar como imaxe", + "button": "Exportar como imaxe", + "description": "" + }, + "saveToDisk": { + "title": "Gardar no disco", + "button": "Gardar no disco", + "description": "" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exportar a Excalidraw+", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "Cargar dende arquivo", + "button": "Cargar dende arquivo", + "description": "" + }, + "shareableLink": { + "title": "Cargar dende un enlace", + "button": "Substituír o meu contido", + "description": "" + } + } } } diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json index 8cdc242f0a..aec18f9558 100644 --- a/src/locales/he-IL.json +++ b/src/locales/he-IL.json @@ -50,7 +50,7 @@ "veryLarge": "גדול מאוד", "solid": "מוצק", "hachure": "קווים מקבילים קצרים להצגת כיוון וחדות שיפוע במפה", - "zigzag": "", + "zigzag": "זיגזג", "crossHatch": "קווים מוצלבים שתי וערב", "thin": "דק", "bold": "מודגש", @@ -109,8 +109,12 @@ "createContainerFromText": "ארוז טקסט במיכל", "link": { "edit": "עריכת קישור", + "editEmbed": "ערוך קישור ושבץ", "create": "יצירת קישור", - "label": "קישור" + "createEmbed": "צור קישור ושבץ", + "label": "קישור", + "labelEmbed": "קשר ושבץ", + "empty": "לא נקבע קישור" }, "lineEditor": { "edit": "ערוך קו", @@ -124,8 +128,8 @@ }, "statusPublished": "פורסם", "sidebarLock": "שמור את סרגל הצד פתוח", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", + "selectAllElementsInFrame": "בחר את כל האלמנטים במסגרת", + "removeAllElementsFromFrame": "הסר את כל האלמנטים שבמסגרת", "eyeDropper": "" }, "library": { @@ -160,13 +164,16 @@ "darkMode": "מצב כהה", "lightMode": "מצב בהיר", "zenMode": "מצב זן", + "objectsSnapMode": "", "exitZenMode": "צא ממצב זן", "cancel": "ביטול", "clear": "ניקוי", "remove": "הסר", + "embed": "", "publishLibrary": "פרסום", "submit": "שליחה", - "confirm": "אשר" + "confirm": "אשר", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "פעולה זו תנקה את כל הקנבס. אתה בטוח?", @@ -196,6 +203,7 @@ "imageInsertError": "לא ניתן היה להוסיף את התמונה. אנא נסה שוב מאוחר יותר...", "fileTooBig": "הקובץ גדול מדי. הגודל המירבי המותר הינו {{maxSize}}.", "svgImageInsertError": "לא ניתן היה להוסיף את תמונת ה-SVG. הסימונים בתוך קובץ ה-SVG עשויים להיות שגויים.", + "failedToFetchImage": "", "invalidSVGString": "SVG שגוי.", "cannotResolveCollabServer": "לא הצלחתי להתחבר לשרת השיתוף. אנא רענן את הדף ונסה שוב.", "importLibraryError": "לא ניתן היה לטעון את הספריה", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "הוספה/עדכון קישור של הצורה שנבחרה", "eraser": "מחק", "frame": "", + "embeddable": "", + "laser": "", "hand": "יד (כלי הזזה)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "לחץ להתחלת מספר נקודות, גרור לקו יחיד", "freeDraw": "לחץ וגרור, שחרר כשסיימת", "text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה", + "embeddable": "", "text_selected": "לחץ לחיצה כפולה או הקש על אנטר לעריכת הטקסט", "text_editing": "כדי לסיים את העריכה לחץ על מקש Escape או על Ctrl (Cmd במחשבי אפל) ומקש Enter", "linearElementMulti": "הקלק על הנקודה האחרונה או הקש Escape או Enter לסיום", @@ -252,7 +267,8 @@ "bindTextToElement": "הקש Enter כדי להוספת טקסט", "deepBoxSelect": "החזק Ctrl / Cmd לבחירה עמוקה ולמניעת גרירה", "eraserRevert": "החזק Alt להחזרת רכיבים מסומנים למחיקה", - "firefox_clipboard_write": "יכולות זה ניתנת להפעלה על ידי שינוי הדגל של \"dom.events.asyncClipboard.clipboardItem\" למצב \"true\". כדי לשנות את הדגל בדפדפן Firefox, בקר בעמוד ״about:config״." + "firefox_clipboard_write": "יכולות זה ניתנת להפעלה על ידי שינוי הדגל של \"dom.events.asyncClipboard.clipboardItem\" למצב \"true\". כדי לשנות את הדגל בדפדפן Firefox, בקר בעמוד ״about:config״.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "לא ניתן להראות תצוגה מקדימה", @@ -411,7 +427,9 @@ "fileSavedToFilename": "נשמר לקובץ {filename}", "canvas": "קנבאס", "selection": "בחירה", - "pasteAsSingleElement": "השתמש ב- {{shortcut}} כדי להדביק כפריט יחיד,\nאו הדבק לתוך עורך טקסט קיים" + "pasteAsSingleElement": "השתמש ב- {{shortcut}} כדי להדביק כפריט יחיד,\nאו הדבק לתוך עורך טקסט קיים", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "שקוף", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json index 66b517c0d9..0f3d135cba 100644 --- a/src/locales/hi-IN.json +++ b/src/locales/hi-IN.json @@ -109,8 +109,12 @@ "createContainerFromText": "मूलपाठ कंटेनर में मोड के दिखाए", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "रेखा संपादित करे", @@ -160,13 +164,16 @@ "darkMode": "डार्क मोड", "lightMode": "लाइट मोड", "zenMode": "ज़ेन मोड", + "objectsSnapMode": "वस्तुओं से पकड़े", "exitZenMode": "जेन मोड से बाहर निकलें", "cancel": "", "clear": "साफ़ करे", "remove": "हटाएं", + "embed": "", "publishLibrary": "प्रकाशित करें", "submit": "प्रस्तुत करे", - "confirm": "पुष्टि करें" + "confirm": "पुष्टि करें", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "इससे पूरा कैनवास साफ हो जाएगा। क्या आपको यकीन है?", @@ -196,6 +203,7 @@ "imageInsertError": "छवि सम्मिलित नहीं की जा सकी. पुनः प्रयत्न करे...", "fileTooBig": "फ़ाइल ज़रूरत से ज़्यादा बड़ी हैं. अधिकतम अनुमित परिमाण {{maxSize}} हैं", "svgImageInsertError": "एसवीजी छवि सम्मिलित नहीं कर सके, एसवीजी रचना अनुचित हैं", + "failedToFetchImage": "", "invalidSVGString": "अनुचित SVG", "cannotResolveCollabServer": "कॉलेब सर्वर से कनेक्शन नहीं हो पा रहा. कृपया पृष्ठ को पुनः लाने का प्रयास करे.", "importLibraryError": "संग्रह प्रतिष्ठापित नहीं किया जा सका", @@ -206,6 +214,10 @@ "line2": "यह आपके चित्रों के पाठ तत्वोंको खंडित कर सकता हैं", "line3": "हमें आपसे ठोस आग्रह है की आप सेट्टिंग में इस विकल्प का चयन ना करे. इस अनुक्रम का पालन करके इसका पता लगा सकते हैं", "line4": "यदि इस सेटिंग्स को अक्षम करने पर भी पृष्ठ ठीक नहीं दिखता हो तो, हमारे GitHub पर एक मुद्दा प्रस्तुत करे, या हमें डिस्कोर्ड पर लिखित सम्पर्क करें" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "रबड़", "frame": "", + "embeddable": "", + "laser": "लेसर टॉर्च", "hand": "हाथ ( खिसकाने का औज़ार)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "कई बिंदुओं को शुरू करने के लिए क्लिक करें, सिंगल लाइन के लिए खींचें", "freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो", "text": "आप चयन टूल से कहीं भी डबल-क्लिक करके टेक्स्ट जोड़ सकते हैं", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "अंतिम बिंदु पर क्लिक करें या समाप्त होने के लिए एस्केप या एंटर दबाएं", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "मिटाने के लिए चुने हुए चीजों को ना चुनने के लिए Alt साथ में दबाए", - "firefox_clipboard_write": "\"dom.events.asyncClipboard.clipboardItem\" फ़्लैग को \"true\" पर सेट करके इस सुविधा को संभवतः सक्षम किया जा सकता है। Firefox में ब्राउज़र फ़्लैग बदलने के लिए, \"about:config\" पृष्ठ पर जाएँ।" + "firefox_clipboard_write": "\"dom.events.asyncClipboard.clipboardItem\" फ़्लैग को \"true\" पर सेट करके इस सुविधा को संभवतः सक्षम किया जा सकता है। Firefox में ब्राउज़र फ़्लैग बदलने के लिए, \"about:config\" पृष्ठ पर जाएँ।", + "disableSnapping": "स्नैपिंग को निष्क्रिय करने के लिए CtrlOrCmd दबाए रखें" }, "canvasError": { "cannotShowPreview": "पूर्वावलोकन नहीं दिखा सकते हैं", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "", - "pasteAsSingleElement": "एक अवयव के रूप में चिपकाने के लिए {{shortcut}} का उपयोग करें,\nया किसी मौजूदा पाठ संपादक में चिपकायें" + "pasteAsSingleElement": "एक अवयव के रूप में चिपकाने के लिए {{shortcut}} का उपयोग करें,\nया किसी मौजूदा पाठ संपादक में चिपकायें", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "छाया", "hexCode": "हेक्स कोड", "noShades": "इस रंग की कोई छाया उपलब्ध नहीं हैं" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "छवि स्वरूप में निर्यात करे", + "button": "छवि स्वरूप निर्यात करे", + "description": "दृष्य डेटा छवि स्वरूप में निर्यात करे, उस स्वरूप से आप उसे पुनः आयात कर सकते हो" + }, + "saveToDisk": { + "title": "डिस्क में सम्हाले", + "button": "डिस्क में सम्हाले", + "description": "दृष्य डेटा बाहरी फ़ाइल में निर्यात करे, जहाँसे आप उसे पुनः आयात कर सकते हो" + }, + "excalidrawPlus": { + "title": "एक्षकालीड्रॉ+", + "button": "एक्षकालीड्रॉ+ में निर्यात करे", + "description": "दृष्य को आपके एक्षकालीड्रॉ+ के कर्यस्थल में सम्हाले" + } + }, + "modal": { + "loadFromFile": { + "title": "फ़ाइल से लोड करें:", + "button": "फ़ाइल से लोड करें:", + "description": "फ़ाइल से लोड करने पर यह आपके कार्य की जगह लेलेगा

आपकी ड्रॉइंग निम्न दर्शित विकल्पो में से एक चुनके और उपयोग करके सम्हाल सकते हैं" + }, + "shareableLink": { + "title": "लिंक से लोड करें:", + "button": "इस जगह प्रतिस्थापित करे", + "description": "बाहर का चित्र लोड करने पर यह आपके कार्य की जगह लेलेगा

आप आपकी ड्रॉइंग पहले निम्न दर्शित विकल्पो में से एक चुनके और उपयोग करके सम्हाल सकते हों." + } + } } } diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json index 2e22ee296f..130c388c84 100644 --- a/src/locales/hu-HU.json +++ b/src/locales/hu-HU.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Hivatkozás szerkesztése", + "editEmbed": "", "create": "Hivatkozás létrehozása", - "label": "Hivatkozás" + "createEmbed": "", + "label": "Hivatkozás", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "Sötét mód", "lightMode": "Világos mód", "zenMode": "Letisztult mód", + "objectsSnapMode": "", "exitZenMode": "Kilépés a letisztult módból", "cancel": "Mégsem", "clear": "Kiűrítés", "remove": "Eltávolítás", + "embed": "", "publishLibrary": "Közzététel", "submit": "Elküldés", - "confirm": "Megerősítés" + "confirm": "Megerősítés", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Ez a művelet törli a vászont. Biztos benne?", @@ -196,6 +203,7 @@ "imageInsertError": "Nem sikerült beszúrni a képet. Próbáld újra később...", "fileTooBig": "A fájl túl nagy. A megengedett maximális méret {{maxSize}}.", "svgImageInsertError": "Nem sikerült beszúrni az SVG-képet. Az SVG szintaktika érvénytelennek tűnik.", + "failedToFetchImage": "", "invalidSVGString": "Érvénytelen SVG.", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Hivatkozás hozzáadása/frissítése a kiválasztott alakzathoz", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Kattintással görbe, az eger húzásával pedig egyenes nyilat rajzolhatsz", "freeDraw": "Kattints és húzd, majd engedd el, amikor végeztél", "text": "Tipp: A kijelölés eszközzel a dupla kattintás új szöveget hoz létre", + "embeddable": "", "text_selected": "Kattints duplán, vagy nyomj entert a szöveg szerkesztéséhez", "text_editing": "Nyomjd meg az Escape vagy a Ctrl/Cmd+ENTER billentyűkombinációt a szerkesztés befejezéséhez", "linearElementMulti": "Kattints a következő ív pozíciójára, vagy fejezd be a nyilat az Escape vagy Enter megnyomásával", @@ -252,7 +267,8 @@ "bindTextToElement": "Nyomd meg az Entert szöveg hozzáadáshoz", "deepBoxSelect": "Tartsd lenyomva a Ctrl/Cmd billentyűt a mély kijelöléshez és a húzás megakadályozásához", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Előnézet nem jeleníthető meg", @@ -360,7 +376,7 @@ "removeItemsFromLib": "A kiválasztott elemek eltávolítása a könyvtárból" }, "imageExportDialog": { - "header": "", + "header": "Kép exportálása", "label": { "withBackground": "", "onlySelected": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Mentve mint {filename}", "canvas": "rajzvászon", "selection": "kijelölés", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Átlátszó", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json index 8ce039700a..bb0274cf26 100644 --- a/src/locales/id-ID.json +++ b/src/locales/id-ID.json @@ -109,8 +109,12 @@ "createContainerFromText": "Bungkus teks dalam kontainer", "link": { "edit": "Edit tautan", + "editEmbed": "", "create": "Buat tautan", - "label": "Tautan" + "createEmbed": "", + "label": "Tautan", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Edit tautan", @@ -124,9 +128,9 @@ }, "statusPublished": "Telah terbit", "sidebarLock": "Biarkan sidebar tetap terbuka", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Pilih semua elemen di bingkai", + "removeAllElementsFromFrame": "Hapus semua elemen dari bingkai", + "eyeDropper": "Ambil warna dari kanvas" }, "library": { "noItems": "Belum ada item yang ditambahkan...", @@ -160,13 +164,16 @@ "darkMode": "Mode gelap", "lightMode": "Mode terang", "zenMode": "Mode zen", + "objectsSnapMode": "", "exitZenMode": "Keluar dari mode zen", "cancel": "Batal", "clear": "Hapus", "remove": "Hapus", + "embed": "", "publishLibrary": "Terbitkan", "submit": "Kirimkan", - "confirm": "Konfirmasi" + "confirm": "Konfirmasi", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Ini akan menghapus semua yang ada dikanvas. Apakah kamu yakin ?", @@ -196,6 +203,7 @@ "imageInsertError": "Tidak dapat menyisipkan gambar. Coba lagi nanti...", "fileTooBig": "File terlalu besar. Ukuran maksimum yang dibolehkan {{maxSize}}.", "svgImageInsertError": "Tidak dapat menyisipkan gambar SVG. Markup SVG sepertinya tidak valid.", + "failedToFetchImage": "", "invalidSVGString": "SVG tidak valid.", "cannotResolveCollabServer": "Tidak dapat terhubung ke server kolab. Muat ulang laman dan coba lagi.", "importLibraryError": "Tidak dapat memuat pustaka", @@ -206,6 +214,10 @@ "line2": "Ini dapat membuat Elemen Teks dalam gambar mu.", "line3": "Kami sangat menyarankan mematikan pengaturan ini. Anda dapat mengikuti langkah-langkah ini untuk melakukannya.", "line4": "Jika mematikan pengaturan ini tidak membenarkan tampilan elemen teks, mohon buka\nisu di GitHub kami, atau chat kami di Discord" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Mode pena - mencegah sentuhan", "link": "Tambah/Perbarui tautan untuk bentuk yang dipilih", "eraser": "Penghapus", - "frame": "", + "frame": "Alat bingkai", + "embeddable": "", + "laser": "", "hand": "Tangan (alat panning)", - "extraTools": "" + "extraTools": "Alat-alat lain" }, "headings": { "canvasActions": "Opsi Kanvas", @@ -237,6 +251,7 @@ "linearElement": "Klik untuk memulai banyak poin, seret untuk satu baris", "freeDraw": "Klik dan seret, lepaskan jika Anda selesai", "text": "Tip: Anda juga dapat menambahkan teks dengan klik ganda di mana saja dengan alat pemilihan", + "embeddable": "", "text_selected": "Klik ganda atau tekan ENTER untuk edit teks", "text_editing": "Tekan Escape atau CtrlAtauCmd+ENTER untuk selesai mengedit", "linearElementMulti": "Klik pada titik akhir atau tekan Escape atau Enter untuk menyelesaikan", @@ -252,7 +267,8 @@ "bindTextToElement": "Tekan enter untuk tambahkan teks", "deepBoxSelect": "Tekan Ctrl atau Cmd untuk memilih yang di dalam, dan mencegah penggeseran", "eraserRevert": "Tahan Alt untuk mengembalikan elemen yang ditandai untuk dihapus", - "firefox_clipboard_write": "Fitur ini dapat diaktifkan melalui pengaturan flag \"dom.events.asyncClipboard.clipboardItem\" ke \"true\". Untuk mengganti flag di Firefox, pergi ke laman \"about:config\"." + "firefox_clipboard_write": "Fitur ini dapat diaktifkan melalui pengaturan flag \"dom.events.asyncClipboard.clipboardItem\" ke \"true\". Untuk mengganti flag di Firefox, pergi ke laman \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Tidak dapat menampilkan pratinjau", @@ -360,27 +376,27 @@ "removeItemsFromLib": "Hapus item yang dipilih dari pustaka" }, "imageExportDialog": { - "header": "", + "header": "Ekspor gambar", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", - "padding": "" + "withBackground": "Latar", + "onlySelected": "Hanya yang dipilih", + "darkMode": "Mode gelap", + "embedScene": "Sematkan pemandangan", + "scale": "Skala", + "padding": "Lapisan" }, "tooltip": { - "embedScene": "" + "embedScene": "Data pemandangan akan disimpan dalam file PNG/SVG yang diekspor sehingga pemandangan itu dapat dipulihkan darinya.\nAkan membesarkan ukuran file yang diekspor." }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "Ekspor ke PNG", + "exportToSvg": "Ekspor ke SVG", + "copyPngToClipboard": "Salin PNG ke papan klip" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Salin ke papan klip" } }, "encrypted": { @@ -411,7 +427,9 @@ "fileSavedToFilename": "Disimpan ke {filename}", "canvas": "kanvas", "selection": "pilihan", - "pasteAsSingleElement": "Gunakan {{shortcut}} untuk menempelkan sebagai satu elemen,\natau tempelkan ke teks editor yang ada" + "pasteAsSingleElement": "Gunakan {{shortcut}} untuk menempelkan sebagai satu elemen,\natau tempelkan ke teks editor yang ada", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparan", @@ -449,5 +467,36 @@ "shades": "Nuansa", "hexCode": "Kode hexa", "noShades": "Tidak ada nuansa untuk warna ini" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Ekspor sebagai gambar", + "button": "Ekspor sebagai gambar", + "description": "Ekspor data pemandangan sebagai gambar yang dapat anda impor nanti." + }, + "saveToDisk": { + "title": "Simpan ke disk", + "button": "Simpan ke disk", + "description": "Ekspor data pemandangan ke file yang dapat Anda dapat impor nanti." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Ekspor ke Excalidraw+", + "description": "Simpan pemandangan ke ruang kerja Excalidraw+ Anda." + } + }, + "modal": { + "loadFromFile": { + "title": "Muat dari file", + "button": "Muat dari file", + "description": "Memuat dari file yang akan menggantikan konten Anda sekarang.

Anda dapat mencadangkan gambar anda dulu menggunakan opsi-opsi ini." + }, + "shareableLink": { + "title": "Muat dari link", + "button": "Ganti konten saya", + "description": "Memuat dari file yang akan menggantikan konten Anda sekarang.

Anda dapat mencadangkan gambar anda dulu menggunakan opsi-opsi ini." + } + } } } diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json index b1ff02efe1..23490ed5e7 100644 --- a/src/locales/it-IT.json +++ b/src/locales/it-IT.json @@ -109,8 +109,12 @@ "createContainerFromText": "Avvolgi il testo in un container", "link": { "edit": "Modifica link", + "editEmbed": "Modifica collegamento e incorpora", "create": "Crea link", - "label": "Link" + "createEmbed": "Crea collegamento e incorpora", + "label": "Link", + "labelEmbed": "Collega & incorpora", + "empty": "Nessun collegamento impostato" }, "lineEditor": { "edit": "Modifica linea", @@ -124,9 +128,9 @@ }, "statusPublished": "Pubblicato", "sidebarLock": "Mantieni aperta la barra laterale", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Seleziona tutti gli elementi nel riquadro", + "removeAllElementsFromFrame": "Rimuovi tutti gli elementi dal riquadro", + "eyeDropper": "Scegli il colore della tela" }, "library": { "noItems": "Nessun elemento ancora aggiunto...", @@ -160,13 +164,16 @@ "darkMode": "Tema scuro", "lightMode": "Tema chiaro", "zenMode": "Modalità Zen", + "objectsSnapMode": "Aggancia agli oggetti", "exitZenMode": "Uscire dalla modalità zen", "cancel": "Annulla", "clear": "Cancella", "remove": "Rimuovi", + "embed": "Attiva/disattiva incorporamento", "publishLibrary": "Pubblica", "submit": "Invia", - "confirm": "Conferma" + "confirm": "Conferma", + "embeddableInteractionButton": "Clicca per interagire" }, "alerts": { "clearReset": "Questa azione cancellerà l'intera tela. Sei sicuro?", @@ -196,6 +203,7 @@ "imageInsertError": "Non è stato possibile inserire l'immagine. Riprova più tardi...", "fileTooBig": "Il file è troppo grande. La dimensione massima consentita è {{maxSize}}.", "svgImageInsertError": "Impossibile inserire l'immagine SVG. Il markup SVG non sembra corretto.", + "failedToFetchImage": "", "invalidSVGString": "SVG non valido.", "cannotResolveCollabServer": "Impossibile connettersi al server di collab. Ricarica la pagina e riprova.", "importLibraryError": "Impossibile caricare la libreria", @@ -206,6 +214,10 @@ "line2": "Ciò potrebbe causare la rottura degli Elementi di testo nei tuoi disegni.", "line3": "Consigliamo vivamente di disabilitare questa impostazione. Puoi seguire questi passaggi su come farlo.", "line4": "Se la disattivazione di questa impostazione non risolve la visualizzazione degli elementi di testo, apri un problema sul nostro GitHub o scrivici su Discord" + }, + "libraryElementTypeError": { + "embeddable": "Gli elementi incorporabili non possono essere aggiunti alla libreria.", + "image": "Il supporto per l'aggiunta d'immagini alla libreria verrà aggiunto a breve!" } }, "toolBar": { @@ -223,7 +235,9 @@ "penMode": "Modalità penna - previene il tocco", "link": "Aggiungi/ aggiorna il link per una forma selezionata", "eraser": "Gomma", - "frame": "", + "frame": "Strumento riquadro", + "embeddable": "Incorporamento Web", + "laser": "Puntatore laser", "hand": "Mano (strumento di panoramica)", "extraTools": "Altri strumenti" }, @@ -237,6 +251,7 @@ "linearElement": "Clicca per iniziare una linea in più punti, trascina per singola linea", "freeDraw": "Clicca e trascina, rilascia quando avrai finito", "text": "Suggerimento: puoi anche aggiungere del testo facendo doppio clic ovunque con lo strumento di selezione", + "embeddable": "Fare click e trascina per creare un incorporamento web", "text_selected": "Fai doppio click o premi INVIO per modificare il testo", "text_editing": "Premi ESC o CtrlOCmd+INVIO per completare le modifiche", "linearElementMulti": "Clicca sull'ultimo punto o premi Esc o Invio per finire", @@ -252,7 +267,8 @@ "bindTextToElement": "Premi invio per aggiungere il testo", "deepBoxSelect": "Tieni premuto CtrlOCmd per selezionare in profondità e per impedire il trascinamento", "eraserRevert": "Tieni premuto Alt per ripristinare gli elementi contrassegnati per l'eliminazione", - "firefox_clipboard_write": "Questa funzione può essere abilitata impostando il flag \"dom.events.asyncClipboard.clipboardItem\" su \"true\". Per modificare i flag del browser in Firefox, visitare la pagina \"about:config\"." + "firefox_clipboard_write": "Questa funzione può essere abilitata impostando il flag \"dom.events.asyncClipboard.clipboardItem\" su \"true\". Per modificare i flag del browser in Firefox, visitare la pagina \"about:config\".", + "disableSnapping": "Tieni premuto Ctrl o Cmd per disabilitare lo snap" }, "canvasError": { "cannotShowPreview": "Impossibile visualizzare l'anteprima", @@ -367,7 +383,7 @@ "darkMode": "Tema scuro", "embedScene": "Includi scena", "scale": "Scala", - "padding": "" + "padding": "Rientro" }, "tooltip": { "embedScene": "I dati della scena saranno salvati nel file PNG/SVG esportato in modo che la scena possa essere ripristinata da esso.\nQuesto aumenterà la dimensione del file esportato." @@ -411,7 +427,9 @@ "fileSavedToFilename": "Salvato in {filename}", "canvas": "tela", "selection": "selezione", - "pasteAsSingleElement": "Usa {{shortcut}} per incollare come un singolo elemento,\no incollare in un editor di testo esistente" + "pasteAsSingleElement": "Usa {{shortcut}} per incollare come un singolo elemento,\no incollare in un editor di testo esistente", + "unableToEmbed": "Incorporare questo url non è permesso. Crea una issue su GitHub per richiedere che l'url sia autorizzato", + "unrecognizedLinkFormat": "Il link che hai incorporato non corrisponde al formato previsto. Prova a incollare la stringa 'embed' fornita dal sito di origine" }, "colors": { "transparent": "Trasparente", @@ -449,5 +467,36 @@ "shades": "Sfumature", "hexCode": "Codice esadecimale", "noShades": "Nessuna sfumatura disponibile per questo colore" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Esporta come immagine", + "button": "Esporta come immagine", + "description": "Esporta i dati della scena come immagine, che potrai importare in seguito." + }, + "saveToDisk": { + "title": "Salva su disco", + "button": "Salva su disco", + "description": "Esporta i dati della scena su file, che potrai importare in seguito." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Esporta su Excalidraw+", + "description": "Salva la scena sul tuo spazio di lavoro Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Carica da file", + "button": "Carica da file", + "description": "Il caricamento da file sostituirà il contenuto esistente.

Puoi salvare il tuo disegno prima usando una delle opzioni qui sotto." + }, + "shareableLink": { + "title": "Carica da link", + "button": "Sostituisci il mio contenuto", + "description": "Il caricamento da file sostituirà il contenuto esistente.

Puoi salvare il tuo disegno prima usando una delle opzioni qui sotto." + } + } } } diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json index 018752c235..8951739a61 100644 --- a/src/locales/ja-JP.json +++ b/src/locales/ja-JP.json @@ -50,7 +50,7 @@ "veryLarge": "特大", "solid": "ベタ塗り", "hachure": "斜線", - "zigzag": "", + "zigzag": "ジグザグ", "crossHatch": "網掛け", "thin": "細", "bold": "太字", @@ -106,11 +106,15 @@ "increaseFontSize": "フォントサイズを拡大", "unbindText": "テキストのバインド解除", "bindText": "テキストをコンテナにバインド", - "createContainerFromText": "", + "createContainerFromText": "コンテナ内でテキストを折り返す", "link": { "edit": "リンクを編集", + "editEmbed": "リンクの編集と埋め込み", "create": "リンクを作成", - "label": "リンク" + "createEmbed": "リンクの作成と埋め込み", + "label": "リンク", + "labelEmbed": "リンクと埋め込み", + "empty": "リンクが設定されていません" }, "lineEditor": { "edit": "行を編集", @@ -124,9 +128,9 @@ }, "statusPublished": "公開済み", "sidebarLock": "サイドバーを開いたままにする", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "フレーム内のすべての要素を選択", + "removeAllElementsFromFrame": "フレーム内のすべての要素を削除", + "eyeDropper": "キャンバスから色を選択" }, "library": { "noItems": "まだアイテムが追加されていません…", @@ -160,13 +164,16 @@ "darkMode": "ダークモード", "lightMode": "ライトモード", "zenMode": "Zenモード", + "objectsSnapMode": "", "exitZenMode": "集中モードをやめる", "cancel": "キャンセル", "clear": "消去", "remove": "削除", + "embed": "埋め込みの切り替え", "publishLibrary": "公開", "submit": "送信", - "confirm": "確認" + "confirm": "確認", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "この操作によってキャンバス全体が消えます。よろしいですか?", @@ -196,16 +203,21 @@ "imageInsertError": "画像を挿入できませんでした。後でもう一度お試しください...", "fileTooBig": "ファイルが大きすぎます。許可される最大サイズは {{maxSize}} です。", "svgImageInsertError": "SVGイメージを挿入できませんでした。SVGマークアップは無効に見えます。", + "failedToFetchImage": "", "invalidSVGString": "無効なSVGです。", "cannotResolveCollabServer": "コラボレーションサーバに接続できませんでした。ページを再読み込みして、もう一度お試しください。", "importLibraryError": "ライブラリを読み込めませんでした。", "collabSaveFailed": "バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。", "collabSaveFailed_sizeExceeded": "キャンバスが大きすぎるため、バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。", "brave_measure_text_error": { - "line1": "", - "line2": "", - "line3": "", - "line4": "" + "line1": "Aggressly Block Fingerprinting の設定が有効なBraveブラウザを使用しているようです。", + "line2": "これにより、図面の テキスト要素 が壊れる可能性があります。", + "line3": "この設定を無効にすることを強く推奨します。 設定手順 をこちらから確認できます。", + "line4": "この設定を無効にすると、テキスト要素の表示が修正されません。 GitHub で Issue を開くか、 Discord にご記入ください" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "ペンモード - タッチ防止", "link": "選択した図形のリンクを追加/更新", "eraser": "消しゴム", - "frame": "", + "frame": "フレームツール", + "embeddable": "Web埋め込み", + "laser": "", "hand": "手 (パンニングツール)", - "extraTools": "" + "extraTools": "その他のツール" }, "headings": { "canvasActions": "キャンバス操作", @@ -237,6 +251,7 @@ "linearElement": "クリックすると複数の頂点からなる曲線を開始、ドラッグすると直線", "freeDraw": "クリックしてドラッグします。離すと終了します", "text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます", + "embeddable": "", "text_selected": "テキストを編集するには、ダブルクリックまたはEnterキーを押します", "text_editing": "Esc キーまたは CtrlOrCmd+ENTER キーを押して編集を終了します", "linearElementMulti": "最後のポイントをクリックするか、エスケープまたはEnterを押して終了します", @@ -252,7 +267,8 @@ "bindTextToElement": "Enterを押してテキストを追加", "deepBoxSelect": "CtrlOrCmd を押し続けることでドラッグを抑止し、深い選択を行います", "eraserRevert": "Alt を押し続けることで削除マークされた要素を元に戻す", - "firefox_clipboard_write": "この機能は、\"dom.events.asyncClipboard.clipboardItem\" フラグを \"true\" に設定することで有効になる可能性があります。Firefox でブラウザーの設定を変更するには、\"about:config\" ページを参照してください。" + "firefox_clipboard_write": "この機能は、\"dom.events.asyncClipboard.clipboardItem\" フラグを \"true\" に設定することで有効になる可能性があります。Firefox でブラウザーの設定を変更するには、\"about:config\" ページを参照してください。", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "プレビューを表示できません", @@ -360,27 +376,27 @@ "removeItemsFromLib": "選択したアイテムをライブラリから削除" }, "imageExportDialog": { - "header": "", + "header": "画像をエクスポート", "label": { - "withBackground": "", + "withBackground": "背景", "onlySelected": "", - "darkMode": "", + "darkMode": "ダークモード", "embedScene": "", - "scale": "", - "padding": "" + "scale": "スケール", + "padding": "余白" }, "tooltip": { "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG にエクスポート", + "exportToSvg": "SVG にエクスポート", + "copyPngToClipboard": "クリップボードにPNGをコピー" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "クリップボードにコピー" } }, "encrypted": { @@ -411,24 +427,26 @@ "fileSavedToFilename": "{filename} に保存しました", "canvas": "キャンバス", "selection": "選択", - "pasteAsSingleElement": "{{shortcut}} を使用して単一の要素として貼り付けるか、\n既存のテキストエディタに貼り付け" + "pasteAsSingleElement": "{{shortcut}} を使用して単一の要素として貼り付けるか、\n既存のテキストエディタに貼り付け", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "透明", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "黒", + "white": "白", + "red": "赤", + "pink": "ピンク", + "grape": "グレープ", + "violet": "バイオレット", + "gray": "灰色", + "blue": "青", + "cyan": "シアン", + "teal": "ティール", + "green": "緑", + "yellow": "黄", + "orange": "オレンジ", + "bronze": "ブロンズ" }, "welcomeScreen": { "app": { @@ -444,10 +462,41 @@ } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", - "shades": "", - "hexCode": "", + "mostUsedCustomColors": "最も使用されているカスタム色", + "colors": "色", + "shades": "影", + "hexCode": "Hexコード", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "画像としてエクスポート", + "button": "画像としてエクスポート", + "description": "" + }, + "saveToDisk": { + "title": "ディスクに保存", + "button": "ディスクに保存", + "description": "" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Excalidraw+にエクスポート", + "description": "Excalidraw+ ワークスペースにシーンを保存します。" + } + }, + "modal": { + "loadFromFile": { + "title": "ファイルからロード", + "button": "ファイルからロード", + "description": "" + }, + "shareableLink": { + "title": "リンクからロード", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/kaa.json b/src/locales/kaa.json index 5b83e7d2ea..c045890957 100644 --- a/src/locales/kaa.json +++ b/src/locales/kaa.json @@ -1,14 +1,14 @@ { "labels": { - "paste": "", - "pasteAsPlaintext": "", - "pasteCharts": "", + "paste": "Qoyıw", + "pasteAsPlaintext": "Ápiwayı tekst retinde qoyıw", + "pasteCharts": "Diagrammalardı qoyıw", "selectAll": "Barlıǵın tańlaw", "multiSelect": "", "moveCanvas": "", - "cut": "", + "cut": "Qıyıw", "copy": "Kóshirip alıw", - "copyAsPng": "", + "copyAsPng": "Almasıw buferine PNG retinde kóshirip alıw", "copyAsSvg": "", "copyText": "", "bringForward": "", @@ -18,8 +18,8 @@ "delete": "Óshiriw", "copyStyles": "", "pasteStyles": "", - "stroke": "", - "background": "", + "stroke": "Jiyek", + "background": "Fon", "fill": "", "strokeWidth": "", "strokeStyle": "", @@ -29,31 +29,31 @@ "sloppiness": "", "opacity": "", "textAlign": "", - "edges": "", + "edges": "Qırlar", "sharp": "", "round": "", "arrowheads": "", "arrowhead_none": "", - "arrowhead_arrow": "", + "arrowhead_arrow": "Jebe", "arrowhead_bar": "", - "arrowhead_dot": "", + "arrowhead_dot": "Noqat", "arrowhead_triangle": "", "fontSize": "Shrift ólshemi", "fontFamily": "", "addWatermark": "", "handDrawn": "", "normal": "", - "code": "", + "code": "Kod", "small": "", "medium": "", - "large": "", - "veryLarge": "", + "large": "Úlken", + "veryLarge": "Júdá úlken", "solid": "", "hachure": "", - "zigzag": "", + "zigzag": "Zigzag", "crossHatch": "", - "thin": "", - "bold": "", + "thin": "Jińishke", + "bold": "Qalıń", "left": "", "center": "", "right": "", @@ -71,19 +71,19 @@ "language": "Til", "liveCollaboration": "", "duplicateSelection": "Nusqa", - "untitled": "", + "untitled": "Atamasız", "name": "Ataması", "yourName": "Atıńız", - "madeWithExcalidraw": "", + "madeWithExcalidraw": "Excalidraw járdeminde islengen", "group": "", "ungroup": "", - "collaborators": "", + "collaborators": "Qatnasıwshılar", "showGrid": "", "addToLibrary": "Kitapxanaǵa qosıw", "removeFromLibrary": "Kitapxanadan alıp taslaw", "libraryLoadingMessage": "Kitapxana júklenbekte…", "libraries": "Kitapxanalardı kóriw", - "loadingScene": "", + "loadingScene": "Saxna júklenbekte…", "align": "", "alignTop": "", "alignBottom": "", @@ -99,22 +99,26 @@ "share": "Bólisiw", "showStroke": "", "showBackground": "", - "toggleTheme": "", - "personalLib": "", - "excalidrawLib": "", - "decreaseFontSize": "", - "increaseFontSize": "", + "toggleTheme": "Temanı ózgertiw", + "personalLib": "Jeke kitapxana", + "excalidrawLib": "Excalidraw kitapxanası", + "decreaseFontSize": "Shrift ólshemin kishireytiw", + "increaseFontSize": "Shrift ólshemin úlkeytiw", "unbindText": "", "bindText": "", "createContainerFromText": "", "link": { "edit": "Siltemeni ózgertiw", + "editEmbed": "", "create": "Siltemeni jaratıw", - "label": "Silteme" + "createEmbed": "", + "label": "Silteme", + "labelEmbed": "", + "empty": "" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "Qatardı ózgertiw", + "exit": "Qatardı ózgertiw redaktorınan shıǵıw" }, "elementLock": { "lock": "Qulıplaw", @@ -136,11 +140,11 @@ "buttons": { "clearReset": "", "exportJSON": "", - "exportImage": "", - "export": "", + "exportImage": "Súwretti eksportlaw...", + "export": "Retinde saqlaw...", "copyToClipboard": "Almasıw buferine kóshirip alındı", - "save": "", - "saveAs": "", + "save": "Ámeldegi faylǵa saqlaw", + "saveAs": "Retinde saqlaw", "load": "Ashıw", "getShareableLink": "", "close": "Jabıw", @@ -160,13 +164,16 @@ "darkMode": "Qarańǵı tema", "lightMode": "Jaqtı tema", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "", "cancel": "Biykarlaw", "clear": "Tazalaw", "remove": "Óshiriw", + "embed": "", "publishLibrary": "", "submit": "Jiberiw", - "confirm": "Tastıyıqlaw" + "confirm": "Tastıyıqlaw", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "Jaramsız SVG.", "cannotResolveCollabServer": "", "importLibraryError": "Kitapxananı júklew ámelge aspadı", @@ -206,12 +214,16 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { "selection": "", "image": "Súwret qoyıw", - "rectangle": "", + "rectangle": "Tórt múyeshlik", "diamond": "", "ellipse": "", "arrow": "", @@ -224,19 +236,22 @@ "link": "", "eraser": "Óshirgish", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, "headings": { "canvasActions": "", "selectedShapeActions": "", - "shapes": "" + "shapes": "Figuralar" }, "hints": { "canvasPanning": "", "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -249,10 +264,11 @@ "lineEditor_nothingSelected": "", "placeImage": "", "publishLibrary": "", - "bindTextToElement": "", + "bindTextToElement": "Tekst qosıw ushın Enter túymesin basıń", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -281,18 +297,18 @@ "title": "Qátelik" }, "exportDialog": { - "disk_title": "", + "disk_title": "Diskke saqlaw", "disk_details": "", - "disk_button": "", + "disk_button": "Faylǵa saqlaw", "link_title": "", "link_details": "", - "link_button": "", + "link_button": "Siltemege eksportlaw", "excalidrawplus_description": "", "excalidrawplus_button": "Eksportlaw", "excalidrawplus_exportError": "" }, "helpDialog": { - "blog": "", + "blog": "Biziń blogtı oqıń", "click": "basıw", "deepSelect": "", "deepBoxSelect": "", @@ -301,7 +317,7 @@ "documentation": "Hújjetshilik", "doubleClick": "", "drag": "", - "editor": "", + "editor": "Redaktor", "editLineArrowPoints": "", "editText": "", "github": "", @@ -333,7 +349,7 @@ "libraryDesc": "", "website": "Veb-sayt", "placeholder": { - "authorName": "", + "authorName": "Atıńız yamasa paydalanıwshı atı", "libraryName": "", "libraryDesc": "", "githubHandle": "", @@ -356,15 +372,15 @@ "content": "" }, "confirmDialog": { - "resetLibrary": "", + "resetLibrary": "Kitapxananı qayta ornatıw", "removeItemsFromLib": "" }, "imageExportDialog": { - "header": "", + "header": "Súwretti eksportlaw", "label": { - "withBackground": "", + "withBackground": "Fon", "onlySelected": "", - "darkMode": "", + "darkMode": "Qarańǵı tema", "embedScene": "", "scale": "", "padding": "" @@ -378,9 +394,9 @@ "copyPngToClipboard": "" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Almasıw buferine kóshirip alıw" } }, "encrypted": { @@ -389,8 +405,8 @@ }, "stats": { "angle": "", - "element": "", - "elements": "", + "element": "Element", + "elements": "Elementler", "height": "", "scene": "Saxna", "selected": "Tańlandı", @@ -403,31 +419,33 @@ "width": "" }, "toast": { - "addedToLibrary": "", + "addedToLibrary": "Kitapxanaǵa qosıldı", "copyStyles": "", - "copyToClipboard": "", + "copyToClipboard": "Almasıw buferine kóshirip alındı.", "copyToClipboardAsPng": "", "fileSaved": "Fayl saqlandı.", "fileSavedToFilename": "{filename} saqlandı", "canvas": "", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", - "black": "", - "white": "", - "red": "", - "pink": "", + "black": "Qara", + "white": "Aq", + "red": "Qızıl", + "pink": "Qızǵılt", "grape": "", - "violet": "", + "violet": "Qızǵılt kók", "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", + "blue": "Kók", + "cyan": "Kók aspan", + "teal": "Piruza", + "green": "Jasıl", + "yellow": "Sarı", + "orange": "Qızǵılt sarı", "bronze": "" }, "welcomeScreen": { @@ -445,9 +463,40 @@ }, "colorPicker": { "mostUsedCustomColors": "", - "colors": "", + "colors": "Reńler", "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Súwret retinde eksportlaw", + "button": "Súwret retinde eksportlaw", + "description": "" + }, + "saveToDisk": { + "title": "Diskke saqlaw", + "button": "Diskke saqlaw", + "description": "" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "Fayldan júklew", + "button": "Fayldan júklew", + "description": "" + }, + "shareableLink": { + "title": "Siltemeden júklew", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json index a0465c6546..ffd3aa079f 100644 --- a/src/locales/kab-KAB.json +++ b/src/locales/kab-KAB.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Ẓreg aseɣwen", + "editEmbed": "", "create": "Snulfu-d aseɣwen", - "label": "Aseɣwen" + "createEmbed": "", + "label": "Aseɣwen", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Ẓreg izirig", @@ -160,13 +164,16 @@ "darkMode": "Askar imsulles", "lightMode": "Askar afaw", "zenMode": "Askar Zen", + "objectsSnapMode": "", "exitZenMode": "Ffeɣ seg uskar Zen", "cancel": "Sefsex", "clear": "Sfeḍ", "remove": "Kkes", + "embed": "", "publishLibrary": "Ẓreg", "submit": "Azen", - "confirm": "Sentem" + "confirm": "Sentem", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Ayagi ad isfeḍ akk taɣzut n usuneɣ. Tetḥeqqeḍ?", @@ -196,6 +203,7 @@ "imageInsertError": "D awezɣi tugra n tugna. Eɛreḍ tikkelt-nniḍen ardeqqal...", "fileTooBig": "Afaylu meqqer aṭas. Tiddi tafellayt yurgen d {{maxSize}}.", "svgImageInsertError": "D awezɣi tugra n tugna SVG. Acraḍ SVG yettban-d d armeɣtu.", + "failedToFetchImage": "", "invalidSVGString": "SVG armeɣtu.", "cannotResolveCollabServer": "Ulamek tuqqna s aqeddac n umyalel. Ma ulac uɣilif ales asali n usebter sakin eɛreḍ tikkelt-nniḍen.", "importLibraryError": "Ur d-ssalay ara tamkarḍit", @@ -206,6 +214,10 @@ "line2": "Ayagi yezmer ad d-iglu s truẓi nIferdisen n uḍrisdeg wunuɣen-inek.", "line3": "Ad k-nsemter ad tsexsiḍ aɣewwar-agi. Tzemreḍ ad tḍefreḍisurifen-agi ɣef wamek ara txedmeḍ.", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Rnu/leqqem aseɣwen i talɣa yettwafernen", "eraser": "Sfeḍ", "frame": "", + "embeddable": "", + "laser": "", "hand": "Afus (afecku n usmutti n tmuɣli)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Ssit akken ad tebduḍ aṭas n tenqiḍin, zuɣer i yiwen n yizirig", "freeDraw": "Ssit yerna zuɣer, serreḥ ticki tfukeḍ", "text": "Tixidest: tzemreḍ daɣen ad ternuḍ aḍris s usiti snat n tikkal anida tebɣiḍ s ufecku n tefrayt", + "embeddable": "", "text_selected": "Ssit snat n tikkal neɣ ssed taqeffalt Kcem akken ad tẓergeḍ aḍris", "text_editing": "Ssit Escape neɣ CtrlOrCmd+ENTER akken ad tfakkeḍ asiẓreg", "linearElementMulti": "Ssit ɣef tenqiḍt taneggarut neɣ ssed taqeffalt Escape neɣ taqeffalt Kcem akken ad tfakkeḍ", @@ -252,7 +267,8 @@ "bindTextToElement": "Ssed ɣef kcem akken ad ternuḍ aḍris", "deepBoxSelect": "Ṭṭef CtrlOrCmd akken ad tferneḍ s telqey, yerna ad trewleḍ i uzuɣer", "eraserRevert": "Ssed Alt akken ad tsefsxeḍ iferdisen yettwacerḍen i tukksa", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Ulamek abeqqeḍ n teskant", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Yettwasekles di {filename}", "canvas": "taɣzut n usuneɣ", "selection": "tafrayt", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Afrawan", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/kk-KZ.json b/src/locales/kk-KZ.json index e960a13255..fd86a8ba77 100644 --- a/src/locales/kk-KZ.json +++ b/src/locales/kk-KZ.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "", "lightMode": "", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "", "cancel": "", "clear": "", "remove": "", + "embed": "", "publishLibrary": "", "submit": "", - "confirm": "" + "confirm": "", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "", @@ -196,6 +203,7 @@ "imageInsertError": "Суретті жүктеу мүмкін болмады. Кейінірек қайталап көріңіз...", "fileTooBig": "Файл өте үлкен. Максималды рұқсат етілген көлем {{maxSize}}.", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "{filename} сақталды", "canvas": "", "selection": "таңдау", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/km-KH.json b/src/locales/km-KH.json index ff51abd8d8..9ad1ac345a 100644 --- a/src/locales/km-KH.json +++ b/src/locales/km-KH.json @@ -109,8 +109,12 @@ "createContainerFromText": "រុំអត្ថបទក្នុងប្រអប់មួយ", "link": { "edit": "កែតំណភ្ជាប់", + "editEmbed": "", "create": "បង្កើតតំណភ្ជាប់", - "label": "តំណ" + "createEmbed": "", + "label": "តំណ", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "កែសម្រួលបន្ទាត់", @@ -160,13 +164,16 @@ "darkMode": "ម៉ូដងងឹត", "lightMode": "ម៉ូដភ្លឺ", "zenMode": "ម៉ូត Zen", + "objectsSnapMode": "", "exitZenMode": "ចេញពី zen​ ម៉ូត", "cancel": "បោះបង់", "clear": "សម្អាត", "remove": "ដកចេញ", + "embed": "", "publishLibrary": "បោះពុម្ពផ្សាយ", "submit": "ដាក់​ស្នើ", - "confirm": "បញ្ជាក់" + "confirm": "បញ្ជាក់", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "វានឹងសម្អាតបាវទាំងមូល។ តើ​អ្នក​ប្រាកដ​ឬ​អត់?", @@ -196,6 +203,7 @@ "imageInsertError": "មិនអាចបញ្ចូលរូបភាពបានទេ។ សូម​ព្យាយាម​ម្តង​ទៀត​នៅ​ពេល​ក្រោយ……", "fileTooBig": "ឯកសារធំពេក។ ទំហំអតិបរមាដែលអនុញ្ញាតគឺ {{maxSize}}។", "svgImageInsertError": "មិនអាចបញ្ចូលរូបភាព SVG បានទេ។ ស្លាក SVG ហាក់ដូចជាមិនត្រឹមត្រូវ។", + "failedToFetchImage": "", "invalidSVGString": "SVG មិន​ត្រឹមត្រូវ។", "cannotResolveCollabServer": "មិនអាចភ្ជាប់ទៅម៉ាស៊ីនមេសហការផ្ទាល់បានទេ។ សូមផ្ទុកទំព័រឡើងវិញ ហើយព្យាយាមម្តងទៀត។", "importLibraryError": "មិនអាចផ្ទុកបណ្ណាល័យបានទេ។", @@ -206,6 +214,10 @@ "line2": "វាអាចបណ្តាលឱ្យមានការបំបែក ធាតុអត្ថបទ នៅក្នុងគំនូររបស់អ្នក។", "line3": "យើងណែនាំយ៉ាងមុតមាំឱ្យបិទការកំណត់នេះ។ អ្នកអាចអនុវត្តតាម ជំហានទាំងនេះ ដើម្បីបិទការកំណត់នេះ។", "line4": "ប្រសិនបើការបិទការកំណត់នេះមិនបានជួសជុលការបង្ហាញធាតុអត្ថបទទេ សូមដាក់ issue នៅលើ GitHub ឬរាយការណ៍នៅលើ Discord របស់យើង" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "បន្ថែម/ធ្វើបច្ចុប្បន្នភាពតំណភ្ជាប់សម្រាប់រូបរាងដែលបានជ្រើសរើស", "eraser": "ជ័រលុប", "frame": "", + "embeddable": "", + "laser": "", "hand": "ដៃ (panning tool)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "ចុចដើម្បីបង្កើតចំណុចច្រើន អូសដើម្បីបង្កើតបន្ទាត់មួយ", "freeDraw": "ចុច​ហើយ​អូស លែង​ពេល​រួចរាល់", "text": "គន្លឹះ៖ អ្នកក៏អាចបន្ថែមអត្ថបទដោយចុចពីរដងនៅកន្លែងណាមួយដោយប្រើឧបករណ៍ជ្រើសរើស", + "embeddable": "", "text_selected": "ចុចពីរដង ឬចុច ENTER ដើម្បីកែសម្រួលអត្ថបទ", "text_editing": "ចុច Escape ឬ CtrlOrCmd +ENTER ដើម្បីបញ្ចប់ការកែសម្រួល", "linearElementMulti": "ចុចលើចំណុចចុងក្រោយ ឬចុច Esc/Enter ដើម្បីបញ្ចប់", @@ -252,7 +267,8 @@ "bindTextToElement": "ចុច Enter ដើម្បីបន្ថែមអត្ថបទ", "deepBoxSelect": "សង្កត់ CtrlOrCmd ដើម្បីជ្រើសរើសយ៉ាងជ្រៅ និងជៀសវាងការអូស", "eraserRevert": "សង្កត់ Alt ដើម្បីដកការជ្រើសរើសធាតុដែលត្រូវបានសម្គាល់សម្រាប់ការលុប", - "firefox_clipboard_write": "បើកមុខងារនេះដោយកំណត់ទង់ \"dom.events.asyncClipboard.clipboardItem\" ទៅ \"true\" \nដើម្បីផ្លាស់ប្តូរទង់កម្មវិធីរុករកនៅក្នុង Firefox សូមចូលទៅកាន់ទំព័រ \"about:config\"។" + "firefox_clipboard_write": "បើកមុខងារនេះដោយកំណត់ទង់ \"dom.events.asyncClipboard.clipboardItem\" ទៅ \"true\" \nដើម្បីផ្លាស់ប្តូរទង់កម្មវិធីរុករកនៅក្នុង Firefox សូមចូលទៅកាន់ទំព័រ \"about:config\"។", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "មិនអាចបង្ហាញការមើលជាមុនបាន", @@ -411,7 +427,9 @@ "fileSavedToFilename": "បានរក្សាទុកនៅក្នុង {filename}", "canvas": "តំបន់គំនូរ", "selection": "ការជ្រើសរើស", - "pasteAsSingleElement": "ប្រើ {{shortcut}} ដើម្បីបិទភ្ជាប់ជាធាតុតែមួយ,\nឬបិទភ្ជាប់ទៅក្នុងកម្មវិធីនិពន្ធអត្ថបទដែលមានស្រាប់" + "pasteAsSingleElement": "ប្រើ {{shortcut}} ដើម្បីបិទភ្ជាប់ជាធាតុតែមួយ,\nឬបិទភ្ជាប់ទៅក្នុងកម្មវិធីនិពន្ធអត្ថបទដែលមានស្រាប់", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "ថ្លាមើលធ្លុះ", @@ -449,5 +467,36 @@ "shades": "ស្រមោល", "hexCode": "លេខកូដ hex", "noShades": "មិនមានស្រមោលសម្រាប់ពណ៌នេះទេ" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json index c6337bc8dd..fb59bea0b7 100644 --- a/src/locales/ko-KR.json +++ b/src/locales/ko-KR.json @@ -109,8 +109,12 @@ "createContainerFromText": "텍스트를 컨테이너에 담기", "link": { "edit": "링크 수정하기", + "editEmbed": "링크 & 임베드 수정하기", "create": "링크 만들기", - "label": "링크" + "createEmbed": "링크 & 임베드 만들기", + "label": "링크", + "labelEmbed": "링크 & 임베드", + "empty": "링크를 지정하지 않았습니다" }, "lineEditor": { "edit": "선 수정하기", @@ -160,13 +164,16 @@ "darkMode": "다크 모드", "lightMode": "밝은 모드", "zenMode": "젠 모드", + "objectsSnapMode": "다른 요소들에 정렬시키기", "exitZenMode": "젠 모드 종료하기", "cancel": "취소", "clear": "지우기", "remove": "삭제", + "embed": "임베딩 토글", "publishLibrary": "게시하기", "submit": "제출", - "confirm": "확인" + "confirm": "확인", + "embeddableInteractionButton": "클릭하여 상호작용" }, "alerts": { "clearReset": "모든 작업 내용이 초기화됩니다. 계속하시겠습니까?", @@ -196,6 +203,7 @@ "imageInsertError": "이미지를 삽입할 수 없습니다. 나중에 다시 시도 하십시오", "fileTooBig": "파일이 너무 큽니다. 최대 크기는 {{maxSize}} 입니다.", "svgImageInsertError": "SVG 이미지를 삽입하지 못했습니다. SVG 문법이 유효하지 않은 것 같습니다.", + "failedToFetchImage": "이미지를 가져오는데 실패했습니다.", "invalidSVGString": "유효하지 않은 SVG입니다.", "cannotResolveCollabServer": "협업 서버에 접속하는데 실패했습니다. 페이지를 새로고침하고 다시 시도해보세요.", "importLibraryError": "라이브러리를 불러오지 못했습니다.", @@ -206,6 +214,10 @@ "line2": "이 기능으로 인해 화이트보드의 텍스트 요소들이 손상될 수 있습니다.", "line3": "저희는 해당 기능을 비활성화하는 것을 강력히 권장 드립니다. 비활성화 방법에 대해서는 이 게시글을 참고해주세요.", "line4": "만약 이 설정을 껐음에도 텍스트 요소들이 올바르게 표시되지 않는다면, 저희 Github에 이슈를 올려주시거나 Discord로 알려주세요." + }, + "libraryElementTypeError": { + "embeddable": "임베드 요소들은 라이브러리에 추가할 수 없습니다.", + "image": "라이브러리에 이미지 삽입 기능은 곧 지원될 예정입니다!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "선택한 도형에 대해서 링크를 추가/업데이트", "eraser": "지우개", "frame": "프레임 도구", + "embeddable": "웹 임베드", + "laser": "레이저 포인터", "hand": "손 (패닝 도구)", "extraTools": "다른 도구" }, @@ -237,6 +251,7 @@ "linearElement": "여러 점을 연결하려면 클릭하고, 직선을 그리려면 바로 드래그하세요.", "freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.", "text": "팁: 선택 툴로 아무 곳이나 더블 클릭해 텍스트를 추가할 수도 있습니다.", + "embeddable": "클릭 및 드래그하여 웹사이트 임베드 만들기", "text_selected": "더블 클릭 또는 ENTER를 눌러서 텍스트 수정", "text_editing": "ESC나 CtrlOrCmd+ENTER를 눌러서 수정을 종료하기", "linearElementMulti": "마지막 지점을 클릭하거나 Esc 또는 Enter 키를 눌러 완료하세요.", @@ -252,7 +267,8 @@ "bindTextToElement": "Enter 키를 눌러서 텍스트 추가하기", "deepBoxSelect": "CtrlOrCmd 키를 눌러서 깊게 선택하고, 드래그하지 않도록 하기", "eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기", - "firefox_clipboard_write": "이 기능은 설정에서 \"dom.events.asyncClipboard.clipboardItem\" 플래그를 \"true\"로 설정하여 활성화할 수 있습니다. Firefox에서 브라우저 플래그를 수정하려면, \"about:config\" 페이지에 접속하세요." + "firefox_clipboard_write": "이 기능은 설정에서 \"dom.events.asyncClipboard.clipboardItem\" 플래그를 \"true\"로 설정하여 활성화할 수 있습니다. Firefox에서 브라우저 플래그를 수정하려면, \"about:config\" 페이지에 접속하세요.", + "disableSnapping": "CtrlOrCmd 키를 눌러서 다른 요소와의 정렬 무시하기" }, "canvasError": { "cannotShowPreview": "미리보기를 볼 수 없습니다", @@ -411,7 +427,9 @@ "fileSavedToFilename": "{filename} 로 저장되었습니다", "canvas": "캔버스", "selection": "선택한 요소", - "pasteAsSingleElement": "단일 요소로 붙여넣거나, 기존 텍스트 에디터에 붙여넣으려면 {{shortcut}} 을 사용하세요." + "pasteAsSingleElement": "단일 요소로 붙여넣거나, 기존 텍스트 에디터에 붙여넣으려면 {{shortcut}} 을 사용하세요.", + "unableToEmbed": "이 URL의 임베딩이 허용되지 않았습니다. GitHub에 이슈를 남겨서 이 URL이 화이트리스트에 등재될 수 있도록 요청하세요", + "unrecognizedLinkFormat": "임베딩하려는 링크의 형식이 잘못된 것 같습니다. 원본 사이트에서 제공하는 \"임베딩\" 텍스트를 그대로 붙여 넣어 주세요" }, "colors": { "transparent": "투명", @@ -444,10 +462,41 @@ } }, "colorPicker": { - "mostUsedCustomColors": "많이 쓰이는 사용자 색상들", + "mostUsedCustomColors": "가장 많이 사용된 색상들", "colors": "색상", "shades": "색조", "hexCode": "Hex 코드", "noShades": "사용할 수 있는 색조가 없음" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "이미지로 내보내기", + "button": "이미지로 내보내기", + "description": "나중에 다시 불러올 수 있도록 화면 데이터를 이미지로 내보냅니다." + }, + "saveToDisk": { + "title": "디스크에 저장", + "button": "디스크에 저장", + "description": "나중에 다시 불러올 수 있도록 화면 데이터를 내보냅니다." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Excalidraw+로 내보내기", + "description": "화면을 당신의 Excalidraw+ 작업 공간으로 저장합니다." + } + }, + "modal": { + "loadFromFile": { + "title": "파일에서 불러오기", + "button": "파일에서 불러오기", + "description": "파일을 불러오면 현재 작성된 데이터를 덮어쓰게 됩니다.

다음 옵션 중 하나를 선택하여 작업물을 백업해 둘 수 있습니다." + }, + "shareableLink": { + "title": "주소에서 불러오기", + "button": "컨텐츠를 덮어쓰기", + "description": "외부 작업물을 불러오면 현재 작성된 데이터를 덮어쓰게 됩니다.

다음 옵션 중 하나를 선택하여 작업물을 백업해 둘 수 있습니다." + } + } } } diff --git a/src/locales/ku-TR.json b/src/locales/ku-TR.json index 7f83c67cb3..b968d01637 100644 --- a/src/locales/ku-TR.json +++ b/src/locales/ku-TR.json @@ -109,8 +109,12 @@ "createContainerFromText": "دەق لە چوارچێوەیەکدا بپێچە", "link": { "edit": "دەستکاریکردنی بەستەر", + "editEmbed": "", "create": "دروستکردنی بەستەر", - "label": "بەستەر" + "createEmbed": "", + "label": "بەستەر", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "دەستکاری کردنی دێڕ", @@ -160,13 +164,16 @@ "darkMode": "دۆخی تاریک", "lightMode": "دۆخی ڕووناک", "zenMode": "دۆخی زێن", + "objectsSnapMode": "", "exitZenMode": "بەجێهێشتنی دۆخی زێن", "cancel": "هەڵوەشاندنەوە", "clear": "خاوێنکردنەوە", "remove": "لابردن", + "embed": "", "publishLibrary": "بڵاوکردنەوە", "submit": "پێشکەشکردن", - "confirm": "دوپاتکردنەوە" + "confirm": "دوپاتکردنەوە", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "ئەمە هەموو تابلۆکە خاوێن دەکاتەوە، دڵنیایت؟", @@ -196,6 +203,7 @@ "imageInsertError": "نەیتوانی وێنە داخڵ بکات. دواتر هەوڵ بدە", "fileTooBig": "فایلەکە زۆر گەورەیە. زۆرترین قەبارەی ڕێگەپێدراو {{maxSize}}}.", "svgImageInsertError": "نەیتوانی وێنەی SVG داخڵ بکات. نیشانەی ئێس ڤی جی نادروست دیارە.", + "failedToFetchImage": "", "invalidSVGString": "ئێس ڤی جی نادروستە.", "cannotResolveCollabServer": "ناتوانێت پەیوەندی بکات بە سێرڤەری کۆلاب. تکایە لاپەڕەکە دووبارە باربکەوە و دووبارە هەوڵ بدەوە.", "importLibraryError": "نەیتوانی کتێبخانە بار بکات", @@ -206,6 +214,10 @@ "line2": "ئەمە ئەکرێ ببێتە هۆی تێکدانی دانە دەقییەکان لە وێنەکێشانەکانتدا.", "line3": "ئێمە بە توندی پێشنیاری لەکارخستنی ئەم ڕێکخستنە دەکەین. بۆ لە کارخستنی دەتوانیت بەم هەنگاوانەدا بڕۆیت.", "line4": "ئەگەر لەکارخستنی ئەم ڕێکخستنە نەبوە هۆی چاککردنەوەی پێشاندانی دانە دەقییەکان، تکایە کێشەیەک بکەرەوە لەسەر گیتهەبەکەمان، یان بۆمان بنوسە لەسەر دیسکۆرد" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "زیادکردن/ نوێکردنەوەی لینک بۆ شێوەی دیاریکراو", "eraser": "سڕەر", "frame": "ئامرازی چوارچێوە", + "embeddable": "", + "laser": "", "hand": "دەست (ئامرازی پانکردن)", "extraTools": "ئامرازی زیاتر" }, @@ -237,6 +251,7 @@ "linearElement": "کرتە بکە بۆ دەستپێکردنی چەند خاڵێک، ڕایبکێشە بۆ یەک هێڵ", "freeDraw": "کرتە بکە و ڕایبکێشە، کاتێک تەواو بوویت دەست هەڵگرە", "text": "زانیاری: هەروەها دەتوانیت دەق زیادبکەیت بە دوو کرتەکردن لە هەر شوێنێک لەگەڵ ئامڕازی دەستنیشانکردن", + "embeddable": "", "text_selected": "دووجار کلیک بکە یان ENTER بکە بۆ دەستکاریکردنی دەق", "text_editing": "بۆ تەواوکردنی دەستکاریکردنەکە Escape یان Ctrl/Cmd+ENTER بکە", "linearElementMulti": "کلیک لەسەر کۆتا خاڵ بکە یان Escape یان Enter بکە بۆ تەواوکردن", @@ -252,7 +267,8 @@ "bindTextToElement": "بۆ زیادکردنی دەق enter بکە", "deepBoxSelect": "CtrlOrCmd ڕابگرە بۆ هەڵبژاردنی قووڵ، و بۆ ڕێگریکردن لە ڕاکێشان", "eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە", - "firefox_clipboard_write": "ئەم تایبەتمەندییە بە ئەگەرێکی زۆرەوە دەتوانرێت چالاک بکرێت بە ڕێکخستنی ئاڵای \"dom.events.asyncClipboard.clipboardItem\" بۆ \"true\". بۆ گۆڕینی ئاڵاکانی وێبگەڕ لە فایەرفۆکسدا، سەردانی لاپەڕەی \"about:config\" بکە." + "firefox_clipboard_write": "ئەم تایبەتمەندییە بە ئەگەرێکی زۆرەوە دەتوانرێت چالاک بکرێت بە ڕێکخستنی ئاڵای \"dom.events.asyncClipboard.clipboardItem\" بۆ \"true\". بۆ گۆڕینی ئاڵاکانی وێبگەڕ لە فایەرفۆکسدا، سەردانی لاپەڕەی \"about:config\" بکە.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "ناتوانرێ پێشبینین پیشان بدرێت", @@ -411,7 +427,9 @@ "fileSavedToFilename": "هەڵگیراوە بۆ {filename}", "canvas": "تابلۆ", "selection": "دەستنیشانکراوەکان", - "pasteAsSingleElement": "بۆ دانانەوە وەکو یەک توخم یان دانانەوە بۆ نێو دەسکاریکەرێکی دەق کە بوونی هەیە {{shortcut}} بەکاربهێنە" + "pasteAsSingleElement": "بۆ دانانەوە وەکو یەک توخم یان دانانەوە بۆ نێو دەسکاریکەرێکی دەق کە بوونی هەیە {{shortcut}} بەکاربهێنە", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "ڕوون", @@ -449,5 +467,36 @@ "shades": "سێبەرەکان", "hexCode": "کۆدی هێکس", "noShades": "هیچ سێبەرێک بۆ ئەم ڕەنگە بەردەست نییە" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/lt-LT.json b/src/locales/lt-LT.json index ee2fc79bc1..5dc9b33283 100644 --- a/src/locales/lt-LT.json +++ b/src/locales/lt-LT.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Redeguoti nuorodą", + "editEmbed": "", "create": "Sukurti nuorodą", - "label": "Nuoroda" + "createEmbed": "", + "label": "Nuoroda", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "Tamsus režimas", "lightMode": "Šviesus režimas", "zenMode": "„Zen“ režimas", + "objectsSnapMode": "", "exitZenMode": "Išeiti iš „Zen“ režimo", "cancel": "Atšaukti", "clear": "Išvalyti", "remove": "Pašalinti", + "embed": "", "publishLibrary": "Paskelbti", "submit": "Pateikti", - "confirm": "Patvirtinti" + "confirm": "Patvirtinti", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "", @@ -196,6 +203,7 @@ "imageInsertError": "Nepyko įkelti paveiksliuko. Pabandyk vėliau...", "fileTooBig": "Per didelis failas. Didžiausias leidžiamas dydis yra {{maxSize}}.", "svgImageInsertError": "Nepavyko įtraukti SVG paveiksliuko. Panašu, jog SVG yra nevalidus.", + "failedToFetchImage": "", "invalidSVGString": "Nevalidus SVG.", "cannotResolveCollabServer": "Nepavyko prisijungti prie serverio bendradarbiavimui. Perkrauk puslapį ir pabandyk prisijungti dar kartą.", "importLibraryError": "Nepavyko įkelti bibliotekos", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Pridėti / Atnaujinti pasirinktos figūros nuorodą", "eraser": "Trintukas", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Paspaudimai sukurs papildomus taškus, nepertraukiamas tempimas sukurs liniją", "freeDraw": "Spausk ir tempk, paleisk kai norėsi pabaigti", "text": "Užuomina: tekstą taip pat galima pridėti bet kur su dvigubu pelės paspaudimu, kol parinkas žymėjimo įrankis", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Išsaugota į {filename}", "canvas": "drobė", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Permatoma", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/lv-LV.json b/src/locales/lv-LV.json index ec9a7e3925..df142b63b5 100644 --- a/src/locales/lv-LV.json +++ b/src/locales/lv-LV.json @@ -109,8 +109,12 @@ "createContainerFromText": "Ietilpināt tekstu figurā", "link": { "edit": "Rediģēt saiti", + "editEmbed": "", "create": "Izveidot saiti", - "label": "Saite" + "createEmbed": "", + "label": "Saite", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Rediģēt līniju", @@ -160,13 +164,16 @@ "darkMode": "Tumšais režīms", "lightMode": "Gaišais režīms", "zenMode": "Zen režīms", + "objectsSnapMode": "", "exitZenMode": "Pamest Zen režīmu", "cancel": "Atcelt", "clear": "Notīrīt", "remove": "Noņemt", + "embed": "", "publishLibrary": "Publicēt", "submit": "Iesniegt", - "confirm": "Apstiprināt" + "confirm": "Apstiprināt", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Šī funkcija notīrīs visu tāfeli. Vai turpināt?", @@ -196,6 +203,7 @@ "imageInsertError": "Nevarēja ievietot attēlu. Mēģiniet vēlāk...", "fileTooBig": "Datne ir par lielu. Lielākais atļautais izmērs ir {{maxSize}}.", "svgImageInsertError": "Nevarēja ievietot SVG attēlu. Šķiet, ka SVG marķējums nav derīgs.", + "failedToFetchImage": "", "invalidSVGString": "Nederīgs SVG.", "cannotResolveCollabServer": "Nevarēja savienoties ar sadarbošanās serveri. Lūdzu, pārlādējiet lapu un mēģiniet vēlreiz.", "importLibraryError": "Nevarēja ielādēt bibliotēku", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Pievienot/rediģēt atlasītās figūras saiti", "eraser": "Dzēšgumija", "frame": "", + "embeddable": "", + "laser": "", "hand": "Roka (panoramēšanas rīks)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Klikšķiniet, lai sāktu zīmēt vairākus punktus; velciet, lai zīmētu līniju", "freeDraw": "Spiediet un velciet; atlaidiet, kad pabeidzat", "text": "Ieteikums: lai pievienotu tekstu, varat arī jebkur dubultklikšķināt ar atlases rīku", + "embeddable": "", "text_selected": "Dubultklikšķiniet vai spiediet ievades taustiņu, lai rediģētu tekstu", "text_editing": "Spiediet iziešanas taustiņu vai CtrlOrCmd+ENTER, lai beigtu rediģēt", "linearElementMulti": "Klikšķiniet uz pēdējā punkta vai spiediet izejas vai ievades taustiņu, lai pabeigtu", @@ -252,7 +267,8 @@ "bindTextToElement": "Spiediet ievades taustiņu, lai pievienotu tekstu", "deepBoxSelect": "Turient nospiestu Ctrl vai Cmd, lai atlasītu dziļumā un lai nepieļautu objektu pavilkšanu", "eraserRevert": "Turiet Alt, lai noņemtu elementus no dzēsšanas atlases", - "firefox_clipboard_write": "Šis iestatījums var tikt ieslēgts ar \"dom.events.asyncClipboard.clipboardItem\" marķieri pārslēgtu uz \"true\". Lai mainītu pārlūka marķierus Firefox, apmeklē \"about:config\" lapu." + "firefox_clipboard_write": "Šis iestatījums var tikt ieslēgts ar \"dom.events.asyncClipboard.clipboardItem\" marķieri pārslēgtu uz \"true\". Lai mainītu pārlūka marķierus Firefox, apmeklē \"about:config\" lapu.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Nevar rādīt priekšskatījumu", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Saglabāts kā {filename}", "canvas": "tāfeli", "selection": "atlasi", - "pasteAsSingleElement": "Izmantojiet {{shortcut}}, lai ielīmētu kā jaunu elementu, vai ielīmētu esošā teksta lauciņā" + "pasteAsSingleElement": "Izmantojiet {{shortcut}}, lai ielīmētu kā jaunu elementu, vai ielīmētu esošā teksta lauciņā", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Caurspīdīgs", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/mr-IN.json b/src/locales/mr-IN.json index d98a1c7383..114bbe1f22 100644 --- a/src/locales/mr-IN.json +++ b/src/locales/mr-IN.json @@ -109,8 +109,12 @@ "createContainerFromText": "मजकूर कंटेनर मधे मोडून दाखवा", "link": { "edit": "दुवा संपादन", + "editEmbed": "", "create": "दुवा तयार करा", - "label": "दुवा" + "createEmbed": "", + "label": "दुवा", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "रेघ संपादन", @@ -160,13 +164,16 @@ "darkMode": "अंधार स्थिथि", "lightMode": "उजेड स्थिति", "zenMode": "ध्यानग्र स्थिति", + "objectsSnapMode": "वस्तूंपासून पकड़ा", "exitZenMode": "ध्यानग्र स्थितितून बाहेर", "cancel": "रद्द", "clear": "स्वछ", "remove": "हटवा", + "embed": "", "publishLibrary": "प्रकाशित करा", "submit": "जमा करा", - "confirm": "पुष्टि करा" + "confirm": "पुष्टि करा", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "पटल स्वच्छ होणार, तुम्हाला खात्री आहे का?", @@ -196,6 +203,7 @@ "imageInsertError": "प्रतिमा आत घालता येत नाही. नंतर पुन्हा प्रयत्न करा...", "fileTooBig": "फाइल फार मोठी आहे. आकाराची कमाल परवानगी {{maxSize}} आहे.", "svgImageInsertError": "एस-वी-जी प्रतिमा आत घालवू शकलो नाही. एस-वी-जी-मार्क-अप यंत्र अयोग्य आहे.", + "failedToFetchImage": "", "invalidSVGString": "अयोग्य एस-वी-जी.", "cannotResolveCollabServer": "कॉलेब-सर्वर हे पोहोचत नाही आहे. पान परत लोड करायचा प्रयत्न करावे.", "importLibraryError": "संग्रह प्रतिस्थापित नाही करता आला", @@ -206,6 +214,10 @@ "line2": "हे तुमच्या चित्रांच्या पाठ तत्वांनां खंडित करू शकतात.", "line3": "तुम्हाला आमच्या कड़ून खूप आग्रह आहे की हे सेटिंग्स मधले चयन नका करु. हे अनुक्रम हे कसे करावे हे दाखवु शकते.", "line4": "ही सेटिंग अक्षम करूनही पृष्ठ योग्यरित्या प्रदर्शित होत नसल्यास, आमच्या GitHub वर समस्या सबमिट करा, किव्हा डिस्कॉर्ड वर आम्हाला लिहा" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "निवडलेल्या आकारासाठी दुवा जोडा/बदल करा", "eraser": "खोड रबर", "frame": "", + "embeddable": "", + "laser": "लेसर टॉर्च", "hand": "हात ( सरकवण्या चे उपकरण)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "अनेक बिंदु साठी क्लिक करा, रेघे साठी ड्रैग करा", "freeDraw": "क्लिक आणि ड्रैग करा, झालं तेव्हा सोडा", "text": "टीप: तुम्हीं निवड यंत्रानी कोठेही दुहेरी क्लिक करून टेक्स्ट जोडू शकता", + "embeddable": "", "text_selected": "लेखन संपादन साठी दुहेरी क्लिक करा किव्हा एंटर दाबा", "text_editing": "संपादन संपवायचं असल्यास एस्केप दाबा किव्हा कंट्रोल या कम्मांड बरोबार एंटर दाबा", "linearElementMulti": "शेवटच्या बिंदु वर क्लिक करा किव्हा एस्केप या एंटर दाबा", @@ -252,7 +267,8 @@ "bindTextToElement": "मजकूर जोडण्यासाठी एंटर की दाबा", "deepBoxSelect": "खोल निवड ह्या साठी कंट्रोल किव्हा कमांड दाबून ठेवा, आणि बाहेर खेचणे वाचवण्या साठी पण", "eraserRevert": "खोडण्या साठी घेतलेल्या वस्तु ना घेण्या साठी Alt दाबून ठेवावे", - "firefox_clipboard_write": "हे वैशिष्ट्य \"dom.events.asyncClipboard.clipboardItem\" फ्लॅग \"सत्य\" वर सेट करून शक्यतो सक्षम केले जाऊ शकते. Firefox मध्ये ब्राउझर फ्लॅग बदलण्यासाठी, \"about:config\" पृष्ठावर जा." + "firefox_clipboard_write": "हे वैशिष्ट्य \"dom.events.asyncClipboard.clipboardItem\" फ्लॅग \"सत्य\" वर सेट करून शक्यतो सक्षम केले जाऊ शकते. Firefox मध्ये ब्राउझर फ्लॅग बदलण्यासाठी, \"about:config\" पृष्ठावर जा.", + "disableSnapping": "स्नैपिंग अक्षम करण्या साठी CtrlOrCmd दाबून ठेवा" }, "canvasError": { "cannotShowPreview": "पूर्वावलोकन दाखवू शकत नाही", @@ -411,7 +427,9 @@ "fileSavedToFilename": "{filename} मधे जतन झाली", "canvas": "पटल", "selection": "निवड", - "pasteAsSingleElement": "एक घटक म्हणून चिपकावण्या साठी {{shortcut}} वापरा,\nकिंवा विद्यमान मजकूर संपादकात चिपकवा" + "pasteAsSingleElement": "एक घटक म्हणून चिपकावण्या साठी {{shortcut}} वापरा,\nकिंवा विद्यमान मजकूर संपादकात चिपकवा", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "पारदर्शक", @@ -449,5 +467,36 @@ "shades": "रंगछटा", "hexCode": "हेक्स कोड", "noShades": "ह्या रंगाच्या छटा उपलब्ध नाहित" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "छवि स्वरूपे निर्यात करा", + "button": "छवि स्वरूपे निर्यात करा", + "description": "सीन डेटा बाहेर एक फ़ाइल मधे जतन करा, त्या फ़ाइल मधुन तो डेटा नंतर परत आणु शकता." + }, + "saveToDisk": { + "title": "डिस्क मधे जतन करा", + "button": "डिस्क मधे जतन करा", + "description": "सीन डेटा बाहेर एक फ़ाइल मधे जतन करा, त्या फ़ाइल मधुन तो डेटा नंतर परत आणु शकता." + }, + "excalidrawPlus": { + "title": "एक्षकालीड्रॉ +", + "button": "एक्षकाली ड्रॉ+ मधे निर्यात करा", + "description": "दृष्य तुमच्या एक्षकालीड्रॉ+ चा कार्यस्थल मधे जतन करा." + } + }, + "modal": { + "loadFromFile": { + "title": "फ़ाइल मधुन लोड करा", + "button": "फ़ाइल मधुन लोड करा", + "description": "फ़ाइल मधुन लोड केल्या वर ते तुमचा सध्याचा कामा ठिकाणि एईल

तुम्हीं तुमचं चित्र एकाधं खाली दिलेलं विकल्प निवडुन पहले सुरक्षीत करु शकता." + }, + "shareableLink": { + "title": "लिंक पासून लोड करा", + "button": "माझ सध्याचे कार्य बदला", + "description": "बाहरी चित्र लोड केल्या वर ते तुमचा सध्याचा कामा ठिकाणि एईल

तुम्हीं तुमचं चित्र एकाधं खाली दिलेलं विकल्प निवडुन पहले सुरक्षीत करु शकता." + } + } } } diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json index f5a69f8cad..2a54c0af2e 100644 --- a/src/locales/my-MM.json +++ b/src/locales/my-MM.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "", "lightMode": "", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "ဇင်မြင်ကွင်းမှထွက်", "cancel": "", "clear": "", "remove": "", + "embed": "", "publishLibrary": "", "submit": "", - "confirm": "" + "confirm": "", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "ကားချပ်တစ်ခုလုံးရှင်းလင်းပါတော့မည်။ အတည်ပြုပါ။", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "အမှတ်များချမှတ်ရေးဆွဲရန်ကလစ်နှိပ်ပါ၊ မျဉ်းတစ်ကြောင်းတည်းအတွက် တရွတ်ဆွဲပါ။", "freeDraw": "ကလစ်နှိပ်၍ တရွတ်ဆွဲပါ၊ ပြီးလျှင်လွှတ်ပါ။", "text": "မှတ်ချက်။ ။မည်သည့်ကိရိယာရွေးထားသည်ဖြစ်စေ ကလစ်နှစ်ချက်နှိပ်၍စာသားထည့်နိုင်သည်", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "နောက်ဆုံးအမှတ်ပေါ်တွင်ကလစ်နှိပ်ခြင်း၊ Escape (သို့) Enter နှိပ်ခြင်းတို့ဖြင့်အဆုံးသတ်နိုင်", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "နမူနာမပြသနိုင်ပါ", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json index 11962b7dc1..8ebc986e3d 100644 --- a/src/locales/nb-NO.json +++ b/src/locales/nb-NO.json @@ -109,8 +109,12 @@ "createContainerFromText": "La tekst flyte i en beholder", "link": { "edit": "Rediger lenke", + "editEmbed": "Rediger lenke og bygg inn", "create": "Opprett lenke", - "label": "Lenke" + "createEmbed": "Opprett lenke og bygg inn", + "label": "Lenke", + "labelEmbed": "Lenk & bygg inn", + "empty": "Ingen lenke er valgt" }, "lineEditor": { "edit": "Rediger linje", @@ -160,13 +164,16 @@ "darkMode": "Mørk modus", "lightMode": "Lys modus", "zenMode": "Zen-modus", + "objectsSnapMode": "", "exitZenMode": "Avslutt zen-modus", "cancel": "Avbryt", "clear": "Tøm", "remove": "Fjern", + "embed": "Slå av/på innebygging", "publishLibrary": "Publiser", "submit": "Send inn", - "confirm": "Bekreft" + "confirm": "Bekreft", + "embeddableInteractionButton": "Klikk for å samhandle" }, "alerts": { "clearReset": "Dette vil tømme lerretet. Er du sikker?", @@ -196,6 +203,7 @@ "imageInsertError": "Kunne ikke sette inn bildet. Prøv igjen senere...", "fileTooBig": "Filen er for stor. Maksimal tillatt størrelse er {{maxSize}}.", "svgImageInsertError": "Kunne ikke sette inn SVG-bilde. SVG-koden ser ugyldig ut.", + "failedToFetchImage": "", "invalidSVGString": "Ugyldig SVG.", "cannotResolveCollabServer": "Kunne ikke koble til samarbeidsserveren. Vennligst oppdater siden og prøv på nytt.", "importLibraryError": "Kunne ikke laste bibliotek", @@ -206,6 +214,10 @@ "line2": "Dette kan resultere i å bryte tekst-elementene i tegningene.", "line3": "Vi anbefaler på det sterkeste å deaktivere denne innstillingen. Du kan følge disse trinnene om hvordan du gjør det.", "line4": "Hvis deaktivering av denne innstillingen ikke fikser visningen av tekstelementer, vennligst åpne en sak på vår GitHub, eller skriv oss på Discord" + }, + "libraryElementTypeError": { + "embeddable": "Innebygde elementer kan ikke legges til i biblioteket.", + "image": "Støtte for å legge til bilder i biblioteket kommer snart!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Legg til / oppdater link for en valgt figur", "eraser": "Viskelær", "frame": "Rammeverktøy", + "embeddable": "Nettinnbygging", + "laser": "", "hand": "Hånd (panoreringsverktøy)", "extraTools": "Flere verktøy" }, @@ -237,6 +251,7 @@ "linearElement": "Klikk for å starte linje med flere punkter, eller dra for en enkel linje", "freeDraw": "Klikk og dra, slipp når du er ferdig", "text": "Tips: du kan også legge til tekst ved å dobbeltklikke hvor som helst med utvalgsverktøyet", + "embeddable": "Klikk og dra for å opprette en nettside innebygd", "text_selected": "Dobbeltklikk eller trykk ENTER for å redigere tekst", "text_editing": "Trykk Escape eller Ctrl/Cmd+Enter for å fullføre redigering", "linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre", @@ -252,7 +267,8 @@ "bindTextToElement": "Trykk Enter for å legge til tekst", "deepBoxSelect": "Hold CTRL/CMD for å markere dypt og forhindre flytting", "eraserRevert": "Hold Alt for å reversere elementene merket for sletting", - "firefox_clipboard_write": "Denne funksjonen kan sannsynligvis aktiveres ved å sette \"dom.events.asyncClipboard.clipboardItem\" flagget til \"true\". For å endre nettleserens flagg i Firefox, besøk \"about:config\"-siden." + "firefox_clipboard_write": "Denne funksjonen kan sannsynligvis aktiveres ved å sette \"dom.events.asyncClipboard.clipboardItem\" flagget til \"true\". For å endre nettleserens flagg i Firefox, besøk \"about:config\"-siden.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Kan ikke vise forhåndsvisning", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Lagret til {filename}", "canvas": "lerret", "selection": "utvalg", - "pasteAsSingleElement": "Bruk {{shortcut}} for å lime inn som ett enkelt element,\neller lim inn i en eksisterende tekstbehandler" + "pasteAsSingleElement": "Bruk {{shortcut}} for å lime inn som ett enkelt element,\neller lim inn i en eksisterende tekstbehandler", + "unableToEmbed": "Innbygging av denne nettadressen er ikke tillatt. Oppret en sak på GitHub for å be om url-hvitelisting", + "unrecognizedLinkFormat": "Linken du bygget inn samsvarer ikke med det forventede formatet. Prøv å lime inn \"bygg inn\"-strengen fra kildesiden" }, "colors": { "transparent": "Gjennomsiktig", @@ -449,5 +467,36 @@ "shades": "Toner", "hexCode": "Heksadesimal kode", "noShades": "Ingen toner tilgjengelig for denne fargen" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Eksporter som bilde", + "button": "Eksporter som bilde", + "description": "Eksporter scene-dataene til en fil som du kan importere fra senere." + }, + "saveToDisk": { + "title": "Lagre til disk", + "button": "Lagre til disk", + "description": "Eksporter scene-dataene til en fil som du kan importere fra senere." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Eksporter til Excalidraw+", + "description": "Lagre scenen til ditt Excalidraw+-arbeidsområde." + } + }, + "modal": { + "loadFromFile": { + "title": "Last inn fra fil", + "button": "Last inn fra fil", + "description": "Å laste fra en fil vil erstatte ditt eksisterende innhold.

Du kan sikkerhetskopiere tegningen din først ved å bruke en av valgene under." + }, + "shareableLink": { + "title": "Last inn fra lenke", + "button": "Erstatt innholdet mitt", + "description": "Lasting av ekstern tegning vil erstatte ditt eksisterende innhold.

Du kan sikkerhetskopiere tegningen din først ved å bruke en av valgene nedenfor." + } + } } } diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json index 87529aac04..84471044ba 100644 --- a/src/locales/nl-NL.json +++ b/src/locales/nl-NL.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Wijzig link", + "editEmbed": "Link bewerken & insluiten", "create": "Maak link", - "label": "Link" + "createEmbed": "Link maken en insluiten", + "label": "Link", + "labelEmbed": "Link toevoegen & insluiten", + "empty": "Er is geen link ingesteld" }, "lineEditor": { "edit": "Bewerk regel", @@ -160,13 +164,16 @@ "darkMode": "Donkere modus", "lightMode": "Lichte modus", "zenMode": "Zen modus", + "objectsSnapMode": "", "exitZenMode": "Verlaat zen modus", "cancel": "Annuleren", "clear": "Wissen", "remove": "Verwijderen", + "embed": "Insluiten in-/uitschakelen", "publishLibrary": "Publiceren", "submit": "Versturen", - "confirm": "Bevestigen" + "confirm": "Bevestigen", + "embeddableInteractionButton": "Klik voor interactie" }, "alerts": { "clearReset": "Dit zal het hele canvas verwijderen. Weet je het zeker?", @@ -196,6 +203,7 @@ "imageInsertError": "Afbeelding invoegen mislukt. Probeer het later opnieuw...", "fileTooBig": "Bestand is te groot. Maximale grootte is {{maxSize}}.", "svgImageInsertError": "Kon geen SVG-afbeelding invoegen. De SVG-opmaak ziet er niet geldig uit.", + "failedToFetchImage": "", "invalidSVGString": "Ongeldige SVG.", "cannotResolveCollabServer": "Kan geen verbinding maken met de collab server. Herlaad de pagina en probeer het opnieuw.", "importLibraryError": "Kon bibliotheek niet laden", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "Ingesloten elementen kunnen niet worden toegevoegd aan de bibliotheek.", + "image": "Ondersteuning voor het toevoegen van afbeeldingen aan de bibliotheek komt binnenkort!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Link toevoegen / bijwerken voor een geselecteerde vorm", "eraser": "Gum", "frame": "", + "embeddable": "Web insluiten", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Klik om meerdere punten te starten, sleep voor één lijn", "freeDraw": "Klik en sleep, laat los als je klaar bent", "text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool", + "embeddable": "Klink-sleep om een website-insluiting te maken", "text_selected": "Dubbelklik of druk op ENTER om tekst te bewerken", "text_editing": "Druk op Escape of CtrlOrCmd+ENTER om het bewerken te voltooien", "linearElementMulti": "Klik op het laatste punt of druk op Escape of Enter om te stoppen", @@ -252,7 +267,8 @@ "bindTextToElement": "Druk op enter om tekst toe te voegen", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Kan voorbeeld niet tonen", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Opgeslagen als {filename}", "canvas": "canvas", "selection": "selectie", - "pasteAsSingleElement": "Gebruik {{shortcut}} om te plakken als een enkel element,\nof plak in een bestaande teksteditor" + "pasteAsSingleElement": "Gebruik {{shortcut}} om te plakken als een enkel element,\nof plak in een bestaande teksteditor", + "unableToEmbed": "Het insluiten van deze url is momenteel niet toegestaan. Zet een probleem op GitHub om de URL op de whitelist te zetten", + "unrecognizedLinkFormat": "De link die u hebt ingesloten komt niet overeen met het verwachte formaat. Probeer de 'embed' string van de bronsite te plakken" }, "colors": { "transparent": "Transparant", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json index 21ed75d9c1..8d110945bf 100644 --- a/src/locales/nn-NO.json +++ b/src/locales/nn-NO.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Rediger lenke", + "editEmbed": "", "create": "Lag lenke", - "label": "Lenke" + "createEmbed": "", + "label": "Lenke", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "Mørk modus", "lightMode": "Lys modus", "zenMode": "Zen-modus", + "objectsSnapMode": "", "exitZenMode": "Avslutt zen-modus", "cancel": "Avbryt", "clear": "Tøm", "remove": "Fjern", + "embed": "", "publishLibrary": "Publiser", "submit": "Send inn", - "confirm": "Stadfest" + "confirm": "Stadfest", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Dette vil tømme lerretet. Er du sikker?", @@ -196,6 +203,7 @@ "imageInsertError": "Kunne ikkje sette inn biletet. Prøv igjen seinare...", "fileTooBig": "Fila er for stor. Maksimal tillate storleik er {{maxSize}}.", "svgImageInsertError": "Kunne ikkje sette inn SVG-biletet. SVG-koden ser ugyldig ut.", + "failedToFetchImage": "", "invalidSVGString": "Ugyldig SVG.", "cannotResolveCollabServer": "Kunne ikkje kople til samarbeidsserveren. Ver vennleg å oppdatere inn sida og prøv på nytt.", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Legg til/ oppdater lenke til valt figur", "eraser": "Viskelêr", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Klikk for å starte linje med fleire punkt, eller drag for ei enkel linje", "freeDraw": "Klikk og drag, slepp når du er ferdig", "text": "Tips: du kan òg leggje til tekst ved å dobbeltklikke kor som helst med utvalgsverktyet", + "embeddable": "", "text_selected": "Dobbelklikk eller trykk ENTER for å redigere teksta", "text_editing": "Trykk Escape eller CtrlOrCmd+ENTER for å fullføre redigeringa", "linearElementMulti": "Klikk på siste punkt eller trykk Escape eller Enter for å fullføre", @@ -252,7 +267,8 @@ "bindTextToElement": "Trykk på enter for å legge til tekst", "deepBoxSelect": "Hald inne Ctrl / Cmd for å velje djupt, og forhindre flytting", "eraserRevert": "Hald inne Alt for å reversere markering av element for sletting", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Kan ikkje vise førehandsvising", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Lagra som {filename}", "canvas": "lerret", "selection": "val", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Gjennomsiktig", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json index d549e3a1b1..37b9e91d4a 100644 --- a/src/locales/oc-FR.json +++ b/src/locales/oc-FR.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Modificar lo ligam", + "editEmbed": "", "create": "Crear un ligam", - "label": "Ligam" + "createEmbed": "", + "label": "Ligam", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Modificar la linha", @@ -160,13 +164,16 @@ "darkMode": "Mòde escur", "lightMode": "Mòde clar", "zenMode": "Mòde escur", + "objectsSnapMode": "", "exitZenMode": "Sortir del mòde zen", "cancel": "Anullar", "clear": "Escafar", "remove": "Tirar", + "embed": "", "publishLibrary": "Publicar", "submit": "Enviar", - "confirm": "Confirmar" + "confirm": "Confirmar", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Aquò suprimirà lo canabàs complèt. O volètz vertadièrament ?", @@ -196,6 +203,7 @@ "imageInsertError": "Insercion d’imatge impossibla. Tornatz ensajar mai tard...", "fileTooBig": "Fichièr tròp pesuc. La talha maximala autorizada es {{maxSize}}.", "svgImageInsertError": "Insercion d’imatge SVG impossibla. Las balisas SVG semblan invalidas.", + "failedToFetchImage": "", "invalidSVGString": "SVG invalid.", "cannotResolveCollabServer": "Connexion impossibla al servidor collab. Mercés de recargar la pagina e tornar ensajar.", "importLibraryError": "Impossible de cargar la bibliotèca", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Apondre/Actualizar lo ligam per una fòrma seleccionada", "eraser": "Goma", "frame": "", + "embeddable": "", + "laser": "", "hand": "Man (aisina de desplaçament de la vista)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Clicatz per començar mantun punt, lisatz per una sola linha", "freeDraw": "Clicatz e lisatz, relargatz un còp acabat", "text": "Astúcia : podètz tanben apondre de tèxt en doble clicant ont que siá amb l’aisina de seleccion", + "embeddable": "", "text_selected": "Clicatz dos còps o quichatz ENTRADA per modificar lo tèxt", "text_editing": "Quichatz ESCAPAR o CtrlOrCmd+ENTRADA per acabar la modificacion", "linearElementMulti": "Clicatz sul darrièr punt o quichatz Ecap o Entrada per acabar", @@ -252,7 +267,8 @@ "bindTextToElement": "Quichatz Entrada per apondre de tèxte", "deepBoxSelect": "Gardar CtrlOCmd per una seleccion gropada e empachar lo desplaçament", "eraserRevert": "Tenètz quichat Alt per anullar los elements marcats per supression", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Afichatge impossible de l’apercebut", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Enregistrat jos {filename}", "canvas": "canabàs", "selection": "seleccion", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparéncia", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json index 18fa57b63d..5f51d1a2cd 100644 --- a/src/locales/pa-IN.json +++ b/src/locales/pa-IN.json @@ -109,8 +109,12 @@ "createContainerFromText": "ਪਾਠ ਨੂੰ ਕੰਟੇਨਰ ਵਿੱਚ ਇਕੱਠਾ ਕਰੋ", "link": { "edit": "ਕੜੀ ਸੋਧੋ", + "editEmbed": "", "create": "ਕੜੀ ਬਣਾਓ", - "label": "ਕੜੀ" + "createEmbed": "", + "label": "ਕੜੀ", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "ਪੰਕਤੀ ਸੋਧੋ", @@ -160,13 +164,16 @@ "darkMode": "ਡਾਰਕ ਮੋਡ", "lightMode": "ਲਾਇਟ ਮੋਡ", "zenMode": "ਜ਼ੈੱਨ ਮੋਡ", + "objectsSnapMode": "", "exitZenMode": "ਜ਼ੈੱਨ ਮੋਡ 'ਚੋਂ ਬਾਹਰ ਨਿਕਲੋ", "cancel": "ਰੱਦ ਕਰੋ", "clear": "ਸਾਫ਼ ਕਰੋ", "remove": "ਹਟਾਓ", + "embed": "", "publishLibrary": "ਪ੍ਰਕਾਸ਼ਤ ਕਰੋ", "submit": "ਜਮ੍ਹਾ ਕਰਵਾਓ", - "confirm": "ਪੁਸ਼ਟੀ ਕਰੋ" + "confirm": "ਪੁਸ਼ਟੀ ਕਰੋ", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "ਇਹ ਸਾਰਾ ਕੈਨਵਸ ਸਾਫ ਕਰ ਦੇਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?", @@ -196,6 +203,7 @@ "imageInsertError": "ਚਿੱਤਰ ਸ਼ਾਮਲ ਨਹੀਂ ਜਾ ਸਕਿਆ, ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ...", "fileTooBig": "ਫਾਈਲ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵੱਡੀ ਹੈ। ਵੱਧ-ਤੋਂ-ਵੱਧ ਪ੍ਰਵਾਨਤ ਅਕਾਰ {{maxSize}} ਹੈ।", "svgImageInsertError": "SVG ਤਸਵੀਰ ਸ਼ਾਮਲ ਨਹੀਂ ਕਰ ਸਕੇ। SVG ਮਾਰਕ-ਅੱਪ ਨਜਾਇਜ਼ ਲੱਗ ਰਿਹਾ ਹੈ।", + "failedToFetchImage": "", "invalidSVGString": "SVG ਨਜਾਇਜ਼ ਹੈ।", "cannotResolveCollabServer": "", "importLibraryError": "ਲਾਇਬ੍ਰੇਰੀ ਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "ਰਬੜ", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "ਇੱਕ ਤੋਂ ਜ਼ਿਆਦਾ ਬਿੰਦੂਆਂ ਲਈ ਕਲਿੱਕ ਕਰਕੇ ਸ਼ੁਰੂਆਤ ਕਰੋ, ਇਕਹਿਰੀ ਲਕੀਰ ਲਈ ਘਸੀਟੋ", "freeDraw": "ਕਲਿੱਕ ਕਰਕੇ ਘਸੀਟੋ, ਪੂਰਾ ਹੋਣ 'ਤੇ ਛੱਡ ਦਿਉ", "text": "ਨੁਸਖਾ: ਤੁਸੀਂ ਚੋਣਕਾਰ ਸੰਦ ਰਾਹੀਂ ਕਿਤੇ ਵੀ ਡਬਲ-ਕਲਿੱਕ ਕਰਕੇ ਵੀ ਪਾਠ ਜੋੜ ਸਕਦੇ ਹੋ", + "embeddable": "", "text_selected": "ਪਾਠ ਨੂੰ ਸੋਧਣ ਲਈ ਡਬਲ-ਕਲਿੱਕ ਕਰੋ ਜਾਂ ਐਂਟਰ ਦਬਾਓ", "text_editing": "ਸੋਧ ਮੁਕੰਮਲ ਕਰਨ ਲਈ ਐਸਕੇਪ (Esc) ਜਾਂ Ctrl-ਜਾਂ-Cmd+ਐਂਟਰ (enter) ਦਬਾਓ", "linearElementMulti": "ਮੁਕੰਮਲ ਕਰਨ ਲਈ ਆਖਰੀ ਬਿੰਦੂ 'ਤੇ ਕਲਿੱਕ ਕਰੋ ਜਾਂ ਇਸਕੇਪ ਜਾਂ ਐਂਟਰ ਦਬਾਓ", @@ -252,7 +267,8 @@ "bindTextToElement": "ਪਾਠ ਜੋੜਨ ਲਈ ਐੰਟਰ ਦਬਾਓ", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "ਝਲਕ ਨਹੀਂ ਦਿਖਾ ਸਕਦੇ", @@ -411,7 +427,9 @@ "fileSavedToFilename": "{filename} ਵਿੱਚ ਸਾਂਭੀ", "canvas": "ਕੈਨਵਸ", "selection": "ਚੋਣ", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "ਪਾਰਦਰਸ਼ੀ", @@ -449,5 +467,36 @@ "shades": "ਸ਼ੇਡਾਂ", "hexCode": "ਹੈਕਸ ਕੋਡ", "noShades": "ਇਸ ਰੰਗ ਦੀ ਕੋਈ ਸ਼ੇਡ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/percentages.json b/src/locales/percentages.json index b6b13a8638..b73e02126f 100644 --- a/src/locales/percentages.json +++ b/src/locales/percentages.json @@ -1,56 +1,56 @@ { - "ar-SA": 79, - "az-AZ": 20, - "bg-BG": 54, - "bn-BD": 59, - "ca-ES": 87, - "cs-CZ": 98, - "da-DK": 33, + "ar-SA": 99, + "az-AZ": 18, + "bg-BG": 75, + "bn-BD": 55, + "ca-ES": 81, + "cs-CZ": 91, + "da-DK": 31, "de-DE": 100, - "el-GR": 92, + "el-GR": 85, "en": 100, - "es-ES": 88, - "eu-ES": 100, - "fa-IR": 88, - "fi-FI": 87, - "fr-FR": 88, - "gl-ES": 88, - "he-IL": 86, - "hi-IN": 77, - "hu-HU": 76, - "id-ID": 94, - "it-IT": 98, - "ja-JP": 88, - "kaa": 18, - "kab-KAB": 87, - "kk-KZ": 21, - "km-KH": 94, + "es-ES": 96, + "eu-ES": 99, + "fa-IR": 87, + "fi-FI": 81, + "fr-FR": 98, + "gl-ES": 90, + "he-IL": 81, + "hi-IN": 75, + "hu-HU": 70, + "id-ID": 96, + "it-IT": 99, + "ja-JP": 95, + "kaa": 34, + "kab-KAB": 80, + "kk-KZ": 20, + "km-KH": 87, "ko-KR": 100, - "ku-TR": 100, - "lt-LT": 55, - "lv-LV": 88, - "mr-IN": 95, - "my-MM": 40, - "nb-NO": 100, - "nl-NL": 83, - "nn-NO": 76, - "oc-FR": 86, - "pa-IN": 89, - "pl-PL": 98, - "pt-BR": 98, - "pt-PT": 94, + "ku-TR": 92, + "lt-LT": 51, + "lv-LV": 82, + "mr-IN": 92, + "my-MM": 37, + "nb-NO": 99, + "nl-NL": 79, + "nn-NO": 71, + "oc-FR": 79, + "pa-IN": 82, + "pl-PL": 100, + "pt-BR": 96, + "pt-PT": 88, "ro-RO": 98, - "ru-RU": 98, - "si-LK": 9, - "sk-SK": 98, + "ru-RU": 94, + "si-LK": 8, + "sk-SK": 99, "sl-SI": 100, "sv-SE": 100, - "ta-IN": 84, - "th-TH": 38, - "tr-TR": 86, - "uk-UA": 96, - "vi-VN": 56, - "zh-CN": 100, - "zh-HK": 25, + "ta-IN": 85, + "th-TH": 46, + "tr-TR": 91, + "uk-UA": 99, + "vi-VN": 51, + "zh-CN": 99, + "zh-HK": 24, "zh-TW": 100 } diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json index 8cdd547363..ffe5247ea2 100644 --- a/src/locales/pl-PL.json +++ b/src/locales/pl-PL.json @@ -104,13 +104,17 @@ "excalidrawLib": "Biblioteka Excalidraw", "decreaseFontSize": "Zmniejsz rozmiar czcionki", "increaseFontSize": "Zwiększ rozmiar czcionki", - "unbindText": "Rozłącz tekst", + "unbindText": "Odłącz tekst od kontenera", "bindText": "Połącz tekst z kontenerem", "createContainerFromText": "Zawijaj tekst w kontenerze", "link": { "edit": "Edytuj łącze", + "editEmbed": "Edytuj i osadź link", "create": "Utwórz łącze", - "label": "Łącze" + "createEmbed": "Stwórz i osadź link", + "label": "Łącze", + "labelEmbed": "Podlinkuj i osadź", + "empty": "Brakujący link" }, "lineEditor": { "edit": "Edytuj linię", @@ -124,8 +128,8 @@ }, "statusPublished": "Opublikowano", "sidebarLock": "Panel boczny zawsze otwarty", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", + "selectAllElementsInFrame": "Zaznacz wszystkie elementy w ramce", + "removeAllElementsFromFrame": "Usuń wszystkie elementy z ramki", "eyeDropper": "Wybierz kolor z płótna" }, "library": { @@ -160,13 +164,16 @@ "darkMode": "Ciemny motyw", "lightMode": "Jasny motyw", "zenMode": "Tryb Zen", + "objectsSnapMode": "Przyciąganie do obiektów", "exitZenMode": "Wyjdź z trybu Zen", "cancel": "Anuluj", "clear": "Wyczyść", "remove": "Usuń", + "embed": "Przełącz osadzenie", "publishLibrary": "Opublikuj", "submit": "Prześlij", - "confirm": "Zatwierdź" + "confirm": "Zatwierdź", + "embeddableInteractionButton": "Kliknij, aby wejść w interakcję" }, "alerts": { "clearReset": "To spowoduje usunięcie wszystkiego z dokumentu. Czy chcesz kontynuować?", @@ -196,6 +203,7 @@ "imageInsertError": "Nie udało się wstawić obrazu. Spróbuj ponownie później...", "fileTooBig": "Plik jest zbyt duży. Maksymalny dozwolony rozmiar to {{maxSize}}.", "svgImageInsertError": "Nie udało się wstawić obrazu SVG. Znacznik SVG wygląda na nieprawidłowy.", + "failedToFetchImage": "Nie udało się załadować obrazu.", "invalidSVGString": "Nieprawidłowy SVG.", "cannotResolveCollabServer": "Nie można połączyć się z serwerem współpracy w czasie rzeczywistym. Proszę odświeżyć stronę i spróbować ponownie.", "importLibraryError": "Wystąpił błąd w trakcie ładowania biblioteki", @@ -206,6 +214,10 @@ "line2": "Może to doprowadzić do złamania elementów tekstu na rysunkach.", "line3": "Zdecydowanie zalecamy wyłączenie tego ustawienia. Możesz wykonać te kroki, aby to zrobić.", "line4": "Jeśli wyłączenie tego ustawienia nie naprawia wyświetlania elementów tekstowych, zgłoś problem na naszym GitHubie lub napisz do nas na Discordzie" + }, + "libraryElementTypeError": { + "embeddable": "Elementy osadzone nie mogą zostać dodane do biblioteki.", + "image": "Dodawania obrazów do biblioteki nadejdzie wkrótce!" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Tryb pióra — zapobiegaj dotknięciom", "link": "Dodaj/aktualizuj link dla wybranego kształtu", "eraser": "Gumka", - "frame": "", + "frame": "Ramka", + "embeddable": "Osadzenie z internetu", + "laser": "Wskaźnik laserowy", "hand": "Ręka (narzędzie do przesuwania)", - "extraTools": "" + "extraTools": "Więcej narzędzi" }, "headings": { "canvasActions": "Narzędzia", @@ -237,6 +251,7 @@ "linearElement": "Naciśnij, aby zrobić punkt, przeciągnij, aby narysować linię", "freeDraw": "Naciśnij i przeciągnij by rysować, puść kiedy skończysz", "text": "Wskazówka: możesz również dodać tekst klikając dwukrotnie gdziekolwiek za pomocą narzędzia zaznaczania", + "embeddable": "Kliknij i przeciągnij, aby stworzyć osadzenie strony", "text_selected": "Kliknij dwukrotnie lub naciśnij ENTER, aby edytować tekst", "text_editing": "Naciśnij Escape lub Ctrl (Cmd w macOS) + ENTER, aby zakończyć edycję", "linearElementMulti": "Aby zakończyć krzywą, ponownie kliknij w ostatni punkt, bądź naciśnij Esc albo Enter", @@ -252,7 +267,8 @@ "bindTextToElement": "Wciśnij enter, aby dodać tekst", "deepBoxSelect": "Przytrzymaj CtrlOrCmd, aby wybrać w obrębie grupy i uniknąć przeciągania", "eraserRevert": "Przytrzymaj Alt, aby przywrócić elementy oznaczone do usunięcia", - "firefox_clipboard_write": "Ta funkcja może być włączona poprzez ustawienie flagi \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Aby zmienić flagi przeglądarki w Firefox, odwiedź stronę \"about:config\"." + "firefox_clipboard_write": "Ta funkcja może być włączona poprzez ustawienie flagi \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Aby zmienić flagi przeglądarki w Firefox, odwiedź stronę \"about:config\".", + "disableSnapping": "Przytrzymaj Ctrl lub Cmd, aby wyłączyć przyciąganie" }, "canvasError": { "cannotShowPreview": "Nie można wyświetlić podglądu", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Zapisano jako {filename}", "canvas": "płótno", "selection": "zaznaczenie", - "pasteAsSingleElement": "Użyj {{shortcut}}, aby wkleić jako pojedynczy element,\nlub wklej do istniejącego edytora tekstu" + "pasteAsSingleElement": "Użyj {{shortcut}}, aby wkleić jako pojedynczy element,\nlub wklej do istniejącego edytora tekstu", + "unableToEmbed": "Osadzenie tego linku jest obecnie niedozwolone. Zgłoś propozycję na portalu GitHub, aby dodać go do listy dozwolonych wyjątków", + "unrecognizedLinkFormat": "Osadzony link ma niewłaściwy format. Spróbuj wkleić całą zawartość pola \"embed\" z oryginalnej strony." }, "colors": { "transparent": "Przezroczysty", @@ -449,5 +467,36 @@ "shades": "Odcienie", "hexCode": "Kod HEX", "noShades": "Brak dostępnych odcieni dla tego koloru" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Eksportuj jako obraz", + "button": "Eksportuj jako obraz", + "description": "Eksportuj zawartość sceny jako obraz z możliwością importowania." + }, + "saveToDisk": { + "title": "Zapisz na dysku", + "button": "Zapisz na dysku", + "description": "Eksportuj zawartość sceny jako plik z możliwością importowania." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Eksportuj do Excalidraw+", + "description": "Zapisz scenę do swojego obszaru roboczego Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Wczytaj z pliku", + "button": "Wczytaj z pliku", + "description": "Wczytanie z pliku nadpisze istniejącą zawartość.

Możesz najpierw utworzyć kopię zapasową swojego rysunku, używając jednej z poniższych opcji." + }, + "shareableLink": { + "title": "Wczytaj z linku", + "button": "Nadpisz moją zawartość", + "description": "Wczytanie zewnętrznego pliku nadpisze istniejącą zawartość.

Możesz najpierw utworzyć kopię zapasową swojego rysunku, używając jednej z poniższych opcji." + } + } } } diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json index 55fdb5396c..68c0059ed5 100644 --- a/src/locales/pt-BR.json +++ b/src/locales/pt-BR.json @@ -109,8 +109,12 @@ "createContainerFromText": "Envolver texto em um contêiner", "link": { "edit": "Editar link", + "editEmbed": "", "create": "Criar link", - "label": "Link" + "createEmbed": "", + "label": "Link", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Editar linha", @@ -124,9 +128,9 @@ }, "statusPublished": "Publicado", "sidebarLock": "Manter barra lateral aberta", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Selecionar todos os elementos no quadro", + "removeAllElementsFromFrame": "Remover todos os elementos do quadro", + "eyeDropper": "Escolher cor da tela" }, "library": { "noItems": "Nenhum item adicionado ainda...", @@ -160,13 +164,16 @@ "darkMode": "Modo escuro", "lightMode": "Modo claro", "zenMode": "Modo Zen", + "objectsSnapMode": "", "exitZenMode": "Sair do modo zen", "cancel": "Cancelar", "clear": "Limpar", "remove": "Remover", + "embed": "", "publishLibrary": "Publicar", "submit": "Enviar", - "confirm": "Confirmar" + "confirm": "Confirmar", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Isto irá limpar toda a tela. Você tem certeza?", @@ -196,6 +203,7 @@ "imageInsertError": "Não foi possível inserir imagem. Tente novamente mais tarde...", "fileTooBig": "O arquivo é muito grande. O tamanho máximo permitido é {{maxSize}}.", "svgImageInsertError": "Não foi possível inserir a imagem SVG. A marcação SVG parece inválida.", + "failedToFetchImage": "", "invalidSVGString": "SVG Inválido.", "cannotResolveCollabServer": "Não foi possível conectar-se ao servidor colaborativo. Por favor, recarregue a página e tente novamente.", "importLibraryError": "Não foi possível carregar a biblioteca", @@ -206,6 +214,10 @@ "line2": "Isso pode acabar quebrando Elementos de Texto em seus desenhos.", "line3": "Recomendamos fortemente desativar essa configuração. Você pode acessar o passo a passo sobre como fazer isso.", "line4": "Se desativar essa configuração não corrigir a exibição de elementos de texto, por favor abra uma issue em nosso GitHub, ou mande uma mensagem em nosso Discord" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Modo caneta — impede o toque", "link": "Adicionar/Atualizar link para uma forma selecionada", "eraser": "Borracha", - "frame": "", + "frame": "Ferramenta de quadro", + "embeddable": "", + "laser": "", "hand": "Mão (ferramenta de rolagem)", - "extraTools": "" + "extraTools": "Mais ferramentas" }, "headings": { "canvasActions": "Ações da tela", @@ -237,6 +251,7 @@ "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Toque e arraste, solte quando terminar", "text": "Dica: você também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", + "embeddable": "", "text_selected": "Clique duplo ou tecle ENTER para editar o texto", "text_editing": "Pressione Esc ou Ctrl/Cmd+ENTER para encerrar a edição", "linearElementMulti": "Clique no último ponto ou pressione Escape ou Enter para terminar", @@ -252,7 +267,8 @@ "bindTextToElement": "Pressione Enter para adicionar o texto", "deepBoxSelect": "Segure Ctrl/Cmd para seleção profunda e para evitar arrastar", "eraserRevert": "Segure a tecla Alt para inverter os elementos marcados para exclusão", - "firefox_clipboard_write": "Esse recurso pode ser ativado configurando a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\"." + "firefox_clipboard_write": "Esse recurso pode ser ativado configurando a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Não é possível mostrar pré-visualização", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Salvo em {filename}", "canvas": "tela", "selection": "seleção", - "pasteAsSingleElement": "Use {{shortcut}} para colar como um único elemento,\nou cole em um editor de texto já existente" + "pasteAsSingleElement": "Use {{shortcut}} para colar como um único elemento,\nou cole em um editor de texto já existente", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparente", @@ -449,5 +467,36 @@ "shades": "Tons", "hexCode": "Código hexadecimal", "noShades": "Sem tons disponíveis para essa cor" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportar como imagem", + "button": "Exportar como imagem", + "description": "Exportar os dados da cena para um arquivo que você poderá importar mais tarde." + }, + "saveToDisk": { + "title": "Salvar no computador", + "button": "Salvar no computador", + "description": "Exportar os dados da cena para um arquivo que você poderá importar mais tarde." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exportar para Excalidraw+", + "description": "Salvar a cena na sua área de trabalho Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Carregar de arquivo", + "button": "Carregar de arquivo", + "description": "Carregar de um arquivo irá substituir o conteúdo existente.

Você pode salvar seu desenho primeiro usando uma das opções abaixo." + }, + "shareableLink": { + "title": "Carregar de um link", + "button": "Substituir meu conteúdo", + "description": "Carregar um desenho externo irá substituir seu conteúdo existente.

Você pode salvar seu desenho antes utilizando uma das opções abaixo." + } + } } } diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json index 13e6c27b2c..401a45ac52 100644 --- a/src/locales/pt-PT.json +++ b/src/locales/pt-PT.json @@ -109,8 +109,12 @@ "createContainerFromText": "Envolver texto num recipiente", "link": { "edit": "Editar ligação", + "editEmbed": "", "create": "Criar ligação", - "label": "Ligação" + "createEmbed": "", + "label": "Ligação", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Editar linha", @@ -160,13 +164,16 @@ "darkMode": "Modo escuro", "lightMode": "Modo claro", "zenMode": "Modo zen", + "objectsSnapMode": "", "exitZenMode": "Sair do modo zen", "cancel": "Cancelar", "clear": "Limpar", "remove": "Remover", + "embed": "", "publishLibrary": "Publicar", "submit": "Enviar", - "confirm": "Confirmar" + "confirm": "Confirmar", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Isto irá limpar toda a área de desenho. Tem a certeza?", @@ -196,6 +203,7 @@ "imageInsertError": "Não foi possível inserir a imagem, tente novamente mais tarde...", "fileTooBig": "O ficheiro é muito grande. O tamanho máximo permitido é {{maxSize}}.", "svgImageInsertError": "Não foi possível inserir a imagem SVG. A marcação SVG parece inválida.", + "failedToFetchImage": "", "invalidSVGString": "SVG inválido.", "cannotResolveCollabServer": "Não foi possível fazer a ligação ao servidor colaborativo. Por favor, volte a carregar a página e tente novamente.", "importLibraryError": "Não foi possível carregar a biblioteca", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Acrescentar/ Adicionar ligação para uma forma seleccionada", "eraser": "Borracha", "frame": "", + "embeddable": "", + "laser": "", "hand": "Mão (ferramenta de movimento da tela)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Clique e arraste, large quando terminar", "text": "Dica: também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", + "embeddable": "", "text_selected": "Clique duas vezes ou pressione a tecla Enter para editar o texto", "text_editing": "Pressione a tecla Escape ou CtrlOrCmd+ENTER para terminar a edição", "linearElementMulti": "Clique no último ponto ou pressione Escape ou Enter para terminar", @@ -252,7 +267,8 @@ "bindTextToElement": "Carregue Enter para acrescentar texto", "deepBoxSelect": "Mantenha a tecla CtrlOrCmd carregada para selecção profunda, impedindo o arrastamento", "eraserRevert": "Carregue também em Alt para reverter os elementos marcados para serem apagados", - "firefox_clipboard_write": "Esta função pode provavelmente ser ativada definindo a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\"." + "firefox_clipboard_write": "Esta função pode provavelmente ser ativada definindo a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Não é possível mostrar uma pré-visualização", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Guardado como {filename}", "canvas": "área de desenho", "selection": "seleção", - "pasteAsSingleElement": "Usar {{shortcut}} para colar como um único elemento,\nou colar num editor de texto existente" + "pasteAsSingleElement": "Usar {{shortcut}} para colar como um único elemento,\nou colar num editor de texto existente", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Transparente", @@ -449,5 +467,36 @@ "shades": "Tons", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "Guardar no disco", + "button": "Guardar no disco", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "Carregar a partir de ficheiro", + "button": "Carregar a partir de ficheiro", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json index cc51f74b58..125c286636 100644 --- a/src/locales/ro-RO.json +++ b/src/locales/ro-RO.json @@ -109,8 +109,12 @@ "createContainerFromText": "Încadrare text într-un container", "link": { "edit": "Editare URL", + "editEmbed": "Editare URL și încorporare", "create": "Creare URL", - "label": "URL" + "createEmbed": "Creare URL și încorporare", + "label": "URL", + "labelEmbed": "URL și încorporare", + "empty": "Nu este setat niciun URL" }, "lineEditor": { "edit": "Editare linie", @@ -160,13 +164,16 @@ "darkMode": "Mod întunecat", "lightMode": "Mod luminos", "zenMode": "Mod zen", + "objectsSnapMode": "Ancorare la obiecte", "exitZenMode": "Ieșire din modul zen", "cancel": "Anulare", "clear": "Ștergere", "remove": "Eliminare", + "embed": "Comutare încorporare", "publishLibrary": "Publicare", "submit": "Trimitere", - "confirm": "Confirmare" + "confirm": "Confirmare", + "embeddableInteractionButton": "Clic pentru interacționare" }, "alerts": { "clearReset": "Această opțiune va șterge întreaga pânză. Confirmi?", @@ -196,6 +203,7 @@ "imageInsertError": "Imaginea nu a putut fi introdusă. Reîncearcă mai târziu...", "fileTooBig": "Fișierul este prea mare. Dimensiunea maximă permisă este de {{maxSize}}.", "svgImageInsertError": "Imaginea SVG nu a putut fi introdus. Marcajul SVG pare invalid.", + "failedToFetchImage": "", "invalidSVGString": "SVG invalid.", "cannotResolveCollabServer": "Nu a putut fi realizată conexiunea la serverul de colaborare. Reîncarcă pagina și încearcă din nou.", "importLibraryError": "Biblioteca nu a putut fi încărcată", @@ -206,6 +214,10 @@ "line2": "Acest lucru poate duce la întreruperea elementelor text din desene.", "line3": "Îți recomandăm ferm să dezactivezi această setare. Poți urma acești pași pentru a face acest lucru.", "line4": "Dacă dezactivarea acestei setări nu duce la remedierea afișării elementelor text, deschide un tichet de problemă pe pagina noastră de GitHub sau scrie-ne pe Discord" + }, + "libraryElementTypeError": { + "embeddable": "Elementele încorporabile nu pot fi adăugate la bibliotecă.", + "image": "În curând vor putea fi adăugate imagini în bibliotecă!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Adăugare/actualizare URL pentru forma selectată", "eraser": "Radieră", "frame": "", + "embeddable": "Încorporare web", + "laser": "Indicator laser", "hand": "Mână (instrument de panoramare)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Dă clic pentru a crea mai multe puncte, glisează pentru a forma o singură linie", "freeDraw": "Dă clic pe pânză și glisează cursorul, apoi eliberează-l când ai terminat", "text": "Sfat: poți adăuga text și dând dublu clic oriunde cu instrumentul de selecție", + "embeddable": "Dă clic și trage pentru a crea un cod de încorporare de pagină web", "text_selected": "Dă dublu clic sau apasă tasta Enter pentru a edita textul", "text_editing": "Apasă tasta Escape sau Ctrl sau Cmd + Enter pentru a finaliza editarea", "linearElementMulti": "Dă clic pe ultimul punct sau apasă tasta Escape sau tasta Enter pentru a termina", @@ -252,7 +267,8 @@ "bindTextToElement": "Apasă tasta Enter pentru a adăuga text", "deepBoxSelect": "Ține apăsată tasta Ctrl sau Cmd pentru a efectua selectarea de adâncime și pentru a preveni glisarea", "eraserRevert": "Ține apăsată tasta Alt pentru a anula elementele marcate pentru ștergere", - "firefox_clipboard_write": "Această caracteristică poate fi probabil activată prin setarea preferinței „dom.events.asyncClipboard.clipboardItem” ca „true”. Pentru a schimba preferințele navigatorului în Firefox, accesează pagina „about:config”." + "firefox_clipboard_write": "Această caracteristică poate fi probabil activată prin setarea preferinței „dom.events.asyncClipboard.clipboardItem” ca „true”. Pentru a schimba preferințele navigatorului în Firefox, accesează pagina „about:config”.", + "disableSnapping": "Ține apăsat CtrlOrCmd pentru a dezactiva ancorarea" }, "canvasError": { "cannotShowPreview": "Nu se poate afișa previzualizarea", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Salvat în {filename}", "canvas": "pânza", "selection": "selecția", - "pasteAsSingleElement": "Folosește {{shortcut}} pentru a insera ca un singur element\nsau insera într-un editor de text existent" + "pasteAsSingleElement": "Folosește {{shortcut}} pentru a insera ca un singur element\nsau insera într-un editor de text existent", + "unableToEmbed": "Încorporarea acestui URL nu este permisă momentan. Deschideți un tichet cu probleme pe GitHub pentru a solicita adăugarea acestui URL în lista albă", + "unrecognizedLinkFormat": "URL-ul pe care l-ai încorporat nu coincide cu formatul așteptat. Încearcă să lipești șirul „de încorporat” furnizat de pagina sursă" }, "colors": { "transparent": "Transparent", @@ -449,5 +467,36 @@ "shades": "Nuanțe", "hexCode": "Cod hexa", "noShades": "Nu este disponibilă nicio nuanță pentru această culoare" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportare ca imagine", + "button": "Exportare ca imagine", + "description": "Exportă datele scenei ca fișier din care poți importa mai târziu." + }, + "saveToDisk": { + "title": "Salvare pe disc", + "button": "Salvare pe disc", + "description": "Exportă datele scenei pe un fișier din care poți importa mai târziu." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exportare în Excalidraw+", + "description": "Salvează scena în spațiul de lucru Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Încărcare din fișier", + "button": "Încărcare din fișier", + "description": "Încărcarea dintr-un fișier va înlocui conținutul existent.

Poți face mai întâi o copie de rezervă a desenului folosind una dintre opțiunile de mai jos." + }, + "shareableLink": { + "title": "Încărcare din lnk", + "button": "Înlocuiește conținutul meu", + "description": "Încărcarea unui desen extern va înlocui conținutul existent.

Poți face mai întâi o copie de rezervă a desenului folosind una dintre opțiunile de mai jos." + } + } } } diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 00be90c515..7c489022c9 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -109,8 +109,12 @@ "createContainerFromText": "Поместить текст в контейнер", "link": { "edit": "Редактировать ссылку", + "editEmbed": "", "create": "Создать ссылку", - "label": "Ссылка" + "createEmbed": "", + "label": "Ссылка", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Редактирование строки", @@ -160,13 +164,16 @@ "darkMode": "Темная тема", "lightMode": "Светлая тема", "zenMode": "Режим Дзен", + "objectsSnapMode": "Привязка к объектам", "exitZenMode": "Выключить режим концентрации внимания", "cancel": "Отменить", "clear": "Очистить", "remove": "Удалить", + "embed": "", "publishLibrary": "Опубликовать", "submit": "Отправить", - "confirm": "Подтвердить" + "confirm": "Подтвердить", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Это очистит весь холст. Вы уверены?", @@ -196,6 +203,7 @@ "imageInsertError": "Не удалось вставить изображение. Попробуйте позже...", "fileTooBig": "Очень большой файл. Максимально разрешенный размер {{maxSize}}.", "svgImageInsertError": "Не удалось вставить изображение SVG. Разметка SVG выглядит недействительной.", + "failedToFetchImage": "Не удалось получить изображение.", "invalidSVGString": "Некорректный SVG.", "cannotResolveCollabServer": "Не удалось подключиться к серверу совместного редактирования. Перезагрузите страницу и повторите попытку.", "importLibraryError": "Не удалось загрузить библиотеку", @@ -206,6 +214,10 @@ "line2": "Это может привести к поломке Текстовых объектов на рисунке.", "line3": "Мы настоятельно рекомендуем отключить эту настройку. Для этого нужно выполнить эти шаги.", "line4": "Если отключение этой настройки не исправит отображение текстовых объектов, создайте issue на нашем GitHub или напишите нам в Discord" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Добавить/обновить ссылку для выбранной фигуры", "eraser": "Ластик", "frame": "", + "embeddable": "", + "laser": "Лазерная указка", "hand": "Рука (перемещение холста)", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Нажмите, чтобы начать несколько точек, перетащите для одной линии", "freeDraw": "Нажмите и перетаскивайте, отпустите по завершении", "text": "Совет: при выбранном инструменте выделения дважды щёлкните в любом месте, чтобы добавить текст", + "embeddable": "", "text_selected": "Дважды щелкните мышью или нажмите ENTER, чтобы редактировать текст", "text_editing": "Нажмите Escape либо Ctrl или Cmd + ENTER для завершения редактирования", "linearElementMulti": "Кликните на последней точке или нажмите Escape или Enter чтобы закончить", @@ -252,7 +267,8 @@ "bindTextToElement": "Нажмите Enter для добавления текста", "deepBoxSelect": "Удерживайте Ctrl или Cmd для глубокого выделения, чтобы предотвратить перетаскивание", "eraserRevert": "Удерживайте Alt, чтобы вернуть элементы, отмеченные для удаления", - "firefox_clipboard_write": "Эта функция может быть включена при изменении значения флага \"dom.events.asyncClipboard.clipboardItem\" на \"true\". Чтобы изменить флаги браузера в Firefox, посетите страницу \"about:config\"." + "firefox_clipboard_write": "Эта функция может быть включена при изменении значения флага \"dom.events.asyncClipboard.clipboardItem\" на \"true\". Чтобы изменить флаги браузера в Firefox, посетите страницу \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Не удается отобразить предпросмотр", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Сохранено в {filename}", "canvas": "холст", "selection": "выделение", - "pasteAsSingleElement": "Используйте {{shortcut}}, чтобы вставить один объект,\nили вставьте в существующий текстовый редактор" + "pasteAsSingleElement": "Используйте {{shortcut}}, чтобы вставить один объект,\nили вставьте в существующий текстовый редактор", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Прозрачный", @@ -449,5 +467,36 @@ "shades": "Оттенки", "hexCode": "Шестнадцатеричный код", "noShades": "Нет доступных оттенков для этого цвета" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Экспортировать как изображение", + "button": "Экспортировать как изображение", + "description": "" + }, + "saveToDisk": { + "title": "Сохранить на диск", + "button": "Сохранить на диск", + "description": "" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Экспорт в Excalidraw+", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "Загрузить из файла", + "button": "Загрузить из файла", + "description": "" + }, + "shareableLink": { + "title": "Загрузить по ссылке", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/si-LK.json b/src/locales/si-LK.json index 468beaf47f..8cc93b03a7 100644 --- a/src/locales/si-LK.json +++ b/src/locales/si-LK.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "", "lightMode": "", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "", "cancel": "", "clear": "", "remove": "", + "embed": "", "publishLibrary": "", "submit": "", - "confirm": "" + "confirm": "", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json index cfda4f16b9..b0d9e4b841 100644 --- a/src/locales/sk-SK.json +++ b/src/locales/sk-SK.json @@ -109,8 +109,12 @@ "createContainerFromText": "Zabaliť text do kontajneru", "link": { "edit": "Upraviť odkaz", + "editEmbed": "Editovať a zapustiť odkaz", "create": "Vytvoriť odkaz", - "label": "Odkaz" + "createEmbed": "Vytvoriť a zapustiť odkaz", + "label": "Odkaz", + "labelEmbed": "Zapustiť odkaz", + "empty": "Nie je nastavený žiaden odkaz" }, "lineEditor": { "edit": "Upraviť čiaru", @@ -124,8 +128,8 @@ }, "statusPublished": "Zverejnené", "sidebarLock": "Nechať bočný panel otvorený", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", + "selectAllElementsInFrame": "Vybrať všetky prvky v ráme", + "removeAllElementsFromFrame": "Odstrániť všetky prvky z rámu", "eyeDropper": "Vybrať farbu z plátna" }, "library": { @@ -160,13 +164,16 @@ "darkMode": "Tmavý režim", "lightMode": "Svetlý režim", "zenMode": "Režim zen", + "objectsSnapMode": "Prichytiť k objektom", "exitZenMode": "Zrušiť režim zen", "cancel": "Zrušiť", "clear": "Vymazať", "remove": "Odstrániť", + "embed": "Prepnúť zapustenie", "publishLibrary": "Uverejniť", "submit": "Potvrdiť", - "confirm": "Potvrdiť" + "confirm": "Potvrdiť", + "embeddableInteractionButton": "Kliknite pre interakciu" }, "alerts": { "clearReset": "Týmto sa vyčistí celé plátno. Ste si istí?", @@ -196,6 +203,7 @@ "imageInsertError": "Nepodarilo sa vložiť obrázok. Skúste to znova neskôr...", "fileTooBig": "Súbor je príliš veľký. Maximálna povolená veľkosť je {{maxSize}}.", "svgImageInsertError": "Nepodarilo sa vložiť SVG obrázok. SVG formát je pravdepodobne nevalidný.", + "failedToFetchImage": "", "invalidSVGString": "Nevalidné SVG.", "cannotResolveCollabServer": "Nepodarilo sa pripojiť ku kolaboračnému serveru. Prosím obnovte stránku a skúste to znovu.", "importLibraryError": "Nepodarilo sa načítať knižnicu", @@ -206,6 +214,10 @@ "line2": "To môže spôsobiť nesprávne zobrazenie textových prvkov vo vašej kresbe.", "line3": "Dôrazne odporúčame vypnutie toho nastavenia. Môžete tak spraviť vykonaním týchto krokov.", "line4": "Ak vypnutie toho nastavenia nevyrieši problém so zobrazením textových prvkov, prosím ohláste problém na našom GitHub-e alebo nám napíšte na náš Discord" + }, + "libraryElementTypeError": { + "embeddable": "Zapustené prvky nie je možné pridať do knižnice.", + "image": "Podpora pre pridávanie obrázkov do knižnice bude dostupná už čoskoro!" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Režim pera – zabrániť dotyku", "link": "Pridať/ Upraviť odkaz pre vybraný tvar", "eraser": "Guma", - "frame": "", + "frame": "Nástroj rám", + "embeddable": "Web Embed", + "laser": "", "hand": "Ruka (nástroj pre pohyb plátna)", - "extraTools": "" + "extraTools": "Ďalšie nástroje" }, "headings": { "canvasActions": "Akcie plátna", @@ -237,6 +251,7 @@ "linearElement": "Kliknite na vloženie viacerých bodov, potiahnite na vytvorenie jednej priamky", "freeDraw": "Kliknite a ťahajte, pustite na ukončenie", "text": "Tip: text môžete pridať aj dvojklikom kdekoľvek, ak je zvolený nástroj výber", + "embeddable": "Kliknite a ťahajte pre zapustenie webovej stránky", "text_selected": "Použite dvojklik alebo stlačte Enter na editáciu textu", "text_editing": "Stlačte Escape alebo CtrlOrCmd+ENTER na ukončenie editovania", "linearElementMulti": "Kliknite na počiatočný bod alebo stlačte Escape alebo Enter na ukončenie", @@ -252,7 +267,8 @@ "bindTextToElement": "Stlačte enter na pridanie textu", "deepBoxSelect": "Podržte CtrlOrCmd na výber v skupine alebo zamedzeniu poťiahnutia", "eraserRevert": "Podržte Alt pre prehodenie položiek určených na vymazanie", - "firefox_clipboard_write": "Táto sa funkcionalita sa dá zapnúť nastavením \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Pre zmenu nastavení vo Firefox-e otvorte stránku \"about:config\"." + "firefox_clipboard_write": "Táto sa funkcionalita sa dá zapnúť nastavením \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Pre zmenu nastavení vo Firefox-e otvorte stránku \"about:config\".", + "disableSnapping": "Podržte CtrlOrCmd pre vypnutie prichytávania" }, "canvasError": { "cannotShowPreview": "Nie je možné zobraziť náhľad plátna", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Uložený ako {filename}", "canvas": "plátna", "selection": "výberu", - "pasteAsSingleElement": "Použitím {{shortcut}} vložte ako samostatný prvok alebo vložte do existujúceho editovaného textu" + "pasteAsSingleElement": "Použitím {{shortcut}} vložte ako samostatný prvok alebo vložte do existujúceho editovaného textu", + "unableToEmbed": "Zapustenie tejto URL nie je povolené. Vytvorte issue na GitHub-e a požiadajte povolenie tejto URL", + "unrecognizedLinkFormat": "Odkaz, ktorý sa snažíte zapustiť nie je v očakávanom formáte. Prosím skúste vložiť 'odkaz na zdieľanie' poskytnutý zdrojovou webovou stránkou" }, "colors": { "transparent": "Priehľadná", @@ -449,5 +467,36 @@ "shades": "Odtiene", "hexCode": "Hex kód", "noShades": "Pre túto farbu nie sú dostupné žiadne odtiene" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportovať ako obrázok", + "button": "Exportovať ako obrázok", + "description": "Exportovať údaje scény ako obrázok, z ktorého môžu byť neskôr importované." + }, + "saveToDisk": { + "title": "Uložiť na disk", + "button": "Uložiť na disk", + "description": "Exportovať údaje scény do súboru, z ktorého môžu byť neskôr importované." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exportovať ako Excalidraw+", + "description": "Uložiť scénu do vášho Excalidraw+ pracovného priestoru." + } + }, + "modal": { + "loadFromFile": { + "title": "Načítať zo súboru", + "button": "Načítať zo súboru", + "description": "Načítanie zo súboru nahradí váš existujúci obsah.

Vašu kresbu môžete zálohovať jednou z nižšie uvedených možností." + }, + "shareableLink": { + "title": "Načítať z odkazu", + "button": "Nahradiť môj obsah", + "description": "Načítanie externej kresby nahradí váš existujúci obsah.

Vašu kresbu môžete zálohovať jednou z nižšie uvedených možností." + } + } } } diff --git a/src/locales/sl-SI.json b/src/locales/sl-SI.json index 70945a1f01..5a6ea8c6f7 100644 --- a/src/locales/sl-SI.json +++ b/src/locales/sl-SI.json @@ -109,8 +109,12 @@ "createContainerFromText": "Zavij besedilo v vsebnik", "link": { "edit": "Uredi povezavo", + "editEmbed": "Uredi povezavo in vdelaj", "create": "Ustvari povezavo", - "label": "Povezava" + "createEmbed": "Ustvari povezavo in vdelaj", + "label": "Povezava", + "labelEmbed": "Povezava in vdelovanje", + "empty": "Povezava ni nastavljena" }, "lineEditor": { "edit": "Uredi črto", @@ -160,13 +164,16 @@ "darkMode": "Temni način", "lightMode": "Svetli način", "zenMode": "Način Zen", + "objectsSnapMode": "Pripenjanje na predmete", "exitZenMode": "Zapri način Zen", "cancel": "Prekliči", "clear": "Počisti", "remove": "Odstrani", + "embed": "Preklopi vdelavo", "publishLibrary": "Objavi", "submit": "Pošlji", - "confirm": "Potrdi" + "confirm": "Potrdi", + "embeddableInteractionButton": "Kliknite za interakcijo" }, "alerts": { "clearReset": "To bo počistilo celotno platno. Ali ste prepričani?", @@ -196,6 +203,7 @@ "imageInsertError": "Vstavljanje slike ni bilo uspešno. Poskusite ponovno kasneje...", "fileTooBig": "Datoteka je prevelika. Največja dovoljena velikost je {{maxSize}}.", "svgImageInsertError": "Vstavljanje slike SVG ni uspelo. Oznake SVG so videti neveljavne.", + "failedToFetchImage": "Pridobivanje slike ni uspelo.", "invalidSVGString": "Neveljaven SVG.", "cannotResolveCollabServer": "Povezave s strežnikom za sodelovanje ni bilo mogoče vzpostaviti. Ponovno naložite stran in poskusite znova.", "importLibraryError": "Nalaganje knjižnice ni uspelo", @@ -206,6 +214,10 @@ "line2": "To bi lahko povzročilo motnje v obnašanju besedilnih elementov v vaših risbah.", "line3": "Močno priporočamo, da onemogočite to nastavitev. Sledite tem korakom, kako to storiti.", "line4": "Če onemogočanje te nastavitve ne popravi prikaza besedilnih elementov, odprite vprašanje na našem GitHubu ali nam pišite na Discord" + }, + "libraryElementTypeError": { + "embeddable": "Vdelani elementi ne morejo biti dodani v knjižnico.", + "image": "Podpora za dodajanje slik v knjižnico prihaja kmalu!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Dodaj/posodobi povezavo za izbrano obliko", "eraser": "Radirka", "frame": "Okvir", + "embeddable": "Spletna vdelava", + "laser": "Laserski kazalec", "hand": "Roka (orodje za premikanje)", "extraTools": "Več orodij" }, @@ -237,6 +251,7 @@ "linearElement": "Kliknite za začetek več točk, povlecite za posamezno črto", "freeDraw": "Kliknite in povlecite, spustite, ko končate", "text": "Namig: besedilo lahko dodate tudi z dvoklikom kjer koli z orodjem za izbiro", + "embeddable": "Kliknite in povlecite, da ustvarite spletno vdelavo", "text_selected": "Dvokliknite ali pritisnite tipko Enter, da uredite besedilo", "text_editing": "Pritisnite tipko Escape ali CtrlOrCmd+Enter za zaključek urejanja", "linearElementMulti": "Kliknite zadnjo točko ali pritisnite Escape ali Enter, da končate", @@ -252,7 +267,8 @@ "bindTextToElement": "Pritisnite tipko Enter za dodajanje besedila", "deepBoxSelect": "Držite tipko CtrlOrCmd za globoko izbiro in preprečitev vlečenja", "eraserRevert": "Pridržite tipko Alt, da razveljavite elemente, označene za brisanje", - "firefox_clipboard_write": "To funkcijo lahko verjetno omogočite z nastavitvijo zastavice \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Če želite spremeniti zastavice brskalnika v Firefoxu, obiščite stran \"about:config\"." + "firefox_clipboard_write": "To funkcijo lahko verjetno omogočite z nastavitvijo zastavice \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Če želite spremeniti zastavice brskalnika v Firefoxu, obiščite stran \"about:config\".", + "disableSnapping": "Držite CtrlOrCmd, da onemogočite pripenjanje" }, "canvasError": { "cannotShowPreview": "Predogleda ni bilo mogoče prikazati", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Shranjeno v {filename}", "canvas": "platno", "selection": "izbor", - "pasteAsSingleElement": "Uporabite {{shortcut}}, da prilepite kot en element,\n ali prilepite v obstoječ urejevalnik besedil" + "pasteAsSingleElement": "Uporabite {{shortcut}}, da prilepite kot en element,\n ali prilepite v obstoječ urejevalnik besedil", + "unableToEmbed": "Vdelava tega URL-ja trenutno ni dovoljena. Ustvarite vprašanje na GitHub-u in prosite za vmestitev URL-ja na seznam dovoljenih", + "unrecognizedLinkFormat": "Povezava, ki ste jo vdelali, se ne ujema s pričakovano obliko. Poskusite prilepiti niz za vdelavo, ki ste ga prejeli na izvorni strani" }, "colors": { "transparent": "Prosojno", @@ -449,5 +467,36 @@ "shades": "Odtenki", "hexCode": "Hex koda", "noShades": "Odtenki za to barvo niso na voljo" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Izvozi kot sliko", + "button": "Izvozi kot sliko", + "description": "Izvozite podatke scene kot sliko, iz katere jo lahko pozneje uvozite." + }, + "saveToDisk": { + "title": "Shrani na disk", + "button": "Shrani na disk", + "description": "Izvozite podatke scene v datoteko, iz katere jo lahko pozneje uvozite." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Izvozi v Excalidraw+", + "description": "Shrani sceno v svoj delovni prostor Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Naloži iz datoteke", + "button": "Naloži iz datoteke", + "description": "Nalaganje iz datoteke bo prepisalo vašo obstoječo vsebino.

Svojo risbo lahko najprej varnostno kopirate z eno od spodnjih možnosti." + }, + "shareableLink": { + "title": "Naloži iz povezave", + "button": "Zamenjaj mojo vsebino", + "description": "Nalaganje zunanje risbe bo prepisalo vašo obstoječo vsebino.

Svojo risbo lahko najprej varnostno kopirate z eno od spodnjih možnosti." + } + } } } diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json index e437e39243..94a09610c0 100644 --- a/src/locales/sv-SE.json +++ b/src/locales/sv-SE.json @@ -109,8 +109,12 @@ "createContainerFromText": "Radbryt text i en avgränsad yta", "link": { "edit": "Redigera länk", + "editEmbed": "Redigera länk & bädda in", "create": "Skapa länk", - "label": "Länk" + "createEmbed": "Skapa länk & bädda in", + "label": "Länk", + "labelEmbed": "Länka & bädda in", + "empty": "Ingen länk är angiven" }, "lineEditor": { "edit": "Redigera linje", @@ -160,13 +164,16 @@ "darkMode": "Mörkt läge", "lightMode": "Ljust läge", "zenMode": "Zen-läge", + "objectsSnapMode": "Fäst mot objekt", "exitZenMode": "Gå ur zen-läge", "cancel": "Avbryt", "clear": "Rensa", "remove": "Ta bort", + "embed": "Växla inbäddning", "publishLibrary": "Publicera", "submit": "Skicka", - "confirm": "Bekräfta" + "confirm": "Bekräfta", + "embeddableInteractionButton": "Klicka för att interagera" }, "alerts": { "clearReset": "Detta rensar hela canvasen. Är du säker?", @@ -196,6 +203,7 @@ "imageInsertError": "Kunde inte infoga bild. Försök igen senare...", "fileTooBig": "Filen är för stor. Maximal tillåten storlek är {{maxSize}}.", "svgImageInsertError": "Kunde inte infoga SVG-bild. SVG-koden ser ogiltig ut.", + "failedToFetchImage": "Kunde inte hämta bilden.", "invalidSVGString": "Ogiltig SVG.", "cannotResolveCollabServer": "Det gick inte att ansluta till samarbets-servern. Ladda om sidan och försök igen.", "importLibraryError": "Kunde inte ladda bibliotek", @@ -206,6 +214,10 @@ "line2": "Detta kan resultera i trasiga Textelement i dina ritningar.", "line3": "Vi rekommenderar starkt att du inaktiverar den här inställningen. Du kan följa dessa steg för att inaktivera den.", "line4": "Om inaktivering av denna inställning inte åtgärdar visningen av textelement, öppna ett ärende på vår GitHub, eller skriv till oss på Discord" + }, + "libraryElementTypeError": { + "embeddable": "Inbäddbara element kan inte läggas till i biblioteket.", + "image": "Stöd för att lägga till bilder till biblioteket kommer snart!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Lägg till / Uppdatera länk för en vald form", "eraser": "Radergummi", "frame": "Rutverktyg", + "embeddable": "Bädda in (web)", + "laser": "Laserpekare", "hand": "Hand (panoreringsverktyg)", "extraTools": "Fler verktyg" }, @@ -237,6 +251,7 @@ "linearElement": "Klicka för att starta flera punkter, dra för en linje", "freeDraw": "Klicka och dra, släpp när du är klar", "text": "Tips: du kan också lägga till text genom att dubbelklicka var som helst med markeringsverktyget", + "embeddable": "Klicka-dra för att skapa en webbplats-inbäddning", "text_selected": "Dubbelklicka eller tryck ENTER för att redigera text", "text_editing": "Tryck Escape eller CtrlOrCmd + ENTER för att slutföra redigeringen", "linearElementMulti": "Klicka på sista punkten eller tryck Escape eller Enter för att avsluta", @@ -252,7 +267,8 @@ "bindTextToElement": "Tryck på Enter för att lägga till text", "deepBoxSelect": "Håll Ctrl eller Cmd för att djupvälja, och för att förhindra att dra", "eraserRevert": "Håll Alt för att återställa de element som är markerade för borttagning", - "firefox_clipboard_write": "Denna funktion kan sannolikt aktiveras genom att ställa in \"dom.events.asyncClipboard.clipboardItem\" flaggan till \"true\". För att ändra webbläsarens flaggor i Firefox, besök \"about:config\" sidan." + "firefox_clipboard_write": "Denna funktion kan sannolikt aktiveras genom att ställa in \"dom.events.asyncClipboard.clipboardItem\" flaggan till \"true\". För att ändra webbläsarens flaggor i Firefox, besök \"about:config\" sidan.", + "disableSnapping": "Håll Ctrl eller Cmd för att inaktivera fästning" }, "canvasError": { "cannotShowPreview": "Kan inte visa förhandsgranskning", @@ -411,7 +427,9 @@ "fileSavedToFilename": "Sparad till {filename}", "canvas": "canvas", "selection": "markering", - "pasteAsSingleElement": "Använd {{shortcut}} för att klistra in som ett enda element,\neller klistra in i en befintlig textredigerare" + "pasteAsSingleElement": "Använd {{shortcut}} för att klistra in som ett enda element,\neller klistra in i en befintlig textredigerare", + "unableToEmbed": "Att bädda in denna webbadress är för närvarande inte tillåtet. Skapa en problemrapport på GitHub för att begära att webbadressen vitlistas.", + "unrecognizedLinkFormat": "Länken du bäddade in matchar inte det förväntade formatet. Försök klistra in 'embed'-strängen som tillhandahålls av källwebbplatsen" }, "colors": { "transparent": "Genomskinlig", @@ -449,5 +467,36 @@ "shades": "Nyanser", "hexCode": "Hex-kod", "noShades": "Inga nyanser tillgängliga för denna färg" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Exportera som bild", + "button": "Exportera som bild", + "description": "Exportera scendata som en bild från vilken du kan importera senare." + }, + "saveToDisk": { + "title": "Spara till disk", + "button": "Spara till disk", + "description": "Exportera scendata till en fil från vilken du kan importera senare." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Exportera till Excalidraw+", + "description": "Spara skissen till din Excalidraw+ arbetsyta." + } + }, + "modal": { + "loadFromFile": { + "title": "Läs in från fil", + "button": "Läs in från fil", + "description": "Laddar från en fil kommer ersätta ditt befintliga innehåll.

Du kan säkerhetskopiera din ritning först med hjälp av ett av alternativen nedan." + }, + "shareableLink": { + "title": "Läs in från länk", + "button": "Ersätt mitt innehåll", + "description": "Inläsning av en extern ritning kommer ersätta ditt befintliga innehåll.

Du kan säkerhetskopiera din ritning först genom att använda ett av alternativen nedan." + } + } } } diff --git a/src/locales/ta-IN.json b/src/locales/ta-IN.json index f7022391f5..05d8c3daef 100644 --- a/src/locales/ta-IN.json +++ b/src/locales/ta-IN.json @@ -109,8 +109,12 @@ "createContainerFromText": "உரையைக் கொள்கலனுள் சுருட்டு", "link": { "edit": "தொடுப்பைத் திருத்து", + "editEmbed": "", "create": "தொடுப்பைப் படை", - "label": "தொடுப்பு" + "createEmbed": "", + "label": "தொடுப்பு", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "தொடுப்பைத் திருத்து", @@ -160,13 +164,16 @@ "darkMode": "கருமை பயன்முறை", "lightMode": "வெளிர்ந்த பயன்முறை", "zenMode": "ஜென் பயன்முறை", + "objectsSnapMode": "", "exitZenMode": "ஜென் பயன்முறையை விலகு", "cancel": "ரத்துசெய்", "clear": "துடை", "remove": "நீக்கு", + "embed": "", "publishLibrary": "பிரசுரி", "submit": "சமர்ப்பி", - "confirm": "உறுதிசெய்" + "confirm": "உறுதிசெய்", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "இது முழு கித்தானையும் துடைக்கும். நீங்கள் உறுதியா?", @@ -196,6 +203,7 @@ "imageInsertError": "படத்தைப் புகுத்தவியலா. பிறகு மீண்டும் முயலவும்...", "fileTooBig": "கோப்பு மிகப்பெரிது. அனுமதிக்கப்பட்ட அதிகபட்ச அளவு {{maxSize}}.", "svgImageInsertError": "எஸ்விஜி படத்தைப் புகுத்தவியலா. எஸ்விஜியின் மார்க்அப் செல்லாததாக தெரிகிறது.", + "failedToFetchImage": "", "invalidSVGString": "செல்லாத SVG.", "cannotResolveCollabServer": "கூட்டுப்பணிச் சேவையகத்துடன் இணைக்க முடியவில்லை. பக்கத்தை மீளேற்றி மீண்டும் முயலவும்.", "importLibraryError": "நூலகத்தை ஏற்ற முடியவில்லை", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -220,12 +232,14 @@ "text": "உரை", "library": "நூலகம்", "lock": "தேர்ந்த கருவியை வரைந்த பின்பும் வைத்திரு", - "penMode": "", + "penMode": "பேனா - தடுப்பு தொடுதல்", "link": "தேர்தெடுத்த வடிவத்திற்குத் தொடுப்பைச் சேர்/ புதுப்பி", "eraser": "அழிப்பி", - "frame": "", - "hand": "", - "extraTools": "" + "frame": "சட்டகம் கருவி", + "embeddable": "", + "laser": "", + "hand": "கை (பார்வை நகர்கும் கருவி)", + "extraTools": "மற்ற கருவிகள்" }, "headings": { "canvasActions": "கித்தான் செயல்கள்", @@ -237,6 +251,7 @@ "linearElement": "பல புள்ளிகளைத் துவக்க சொடுக்கு, ஒற்றை வரிக்கு பிடித்திழு", "freeDraw": "சொடுக்கி பிடித்திழு, முடித்ததும் விடுவி", "text": "துணுக்குதவி: தெரிவு கருவி கொண்டு எங்காவது இரு-சொடுக்கி உரையைச் சேர்க்கலாம்", + "embeddable": "", "text_selected": "உரையைத் திருத்த இரு-சொடுக்கு அ ENTERஐ அழுத்து", "text_editing": "திருத்துவதை முடிக்க Escape அ CtrlOrCmd+ENTERஐ அழுத்து", "linearElementMulti": "கடைசி புள்ளியில் சொடுக்கு அ முடிக்க Escape அ Enter அழுத்து", @@ -252,7 +267,8 @@ "bindTextToElement": "உரையைச் சேர்க்க enterஐ அழுத்து", "deepBoxSelect": "ஆழ்ந்துத் தேரவும் பிடித்திழுத்தலைத் தவிர்க்கவும் CtrlOrCmdஐ அழுத்திப்பிடி", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "முன்னோட்டம் காட்ட இயலவில்லை", @@ -317,8 +333,8 @@ "zoomToFit": "அனைத்துறுப்புகளும் பொருந்தும்படி விரிவாக்கு", "zoomToSelection": "தெரிவுக்கு விரிவாக்கு", "toggleElementLock": "தேர்ந்தெடுப்பைப் பூட்டு/பூட்டவிழ்", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "மேலே/கீழே நகர்த்தவும்", + "movePageLeftRight": "இடது/வலது பக்கம் நகர்த்தவும்" }, "clearCanvasDialog": { "title": "கித்தானைத் துடை" @@ -360,26 +376,26 @@ "removeItemsFromLib": "நூலகத்திலிருந்து தேர்ந்தெடுத்த உருப்படிகளை நீக்கு" }, "imageExportDialog": { - "header": "", + "header": "படத்தை ஏற்றுமதிதல", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", + "withBackground": "பின்னணி", + "onlySelected": "தேர்ந்ததை மட்டும்", + "darkMode": "இருள் பயன்முறை", + "embedScene": "காட்சியை உட்பொதி", + "scale": "அளவுகோல்", "padding": "" }, "tooltip": { "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", + "exportToPng": "PNGக்கு ஏற்றுமதிசெய்", + "exportToSvg": "SVGக்கு ஏற்றுமதிசெய்", "copyPngToClipboard": "" }, "button": { - "exportToPng": "", - "exportToSvg": "", + "exportToPng": "PNG", + "exportToSvg": "SVG", "copyPngToClipboard": "" } }, @@ -411,24 +427,26 @@ "fileSavedToFilename": "{filename}-க்கு சேமிக்கப்பட்டது", "canvas": "கித்தான்", "selection": "தெரிவு", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "ஒளிபுகுத்தன்மை", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "black": "கருப்பு", + "white": "வெள்ளை", + "red": "சிவப்பு", + "pink": "இளஞ்சிவப்பு", + "grape": "திராட்சை", + "violet": "ஊதா", + "gray": "சாம்பல்", + "blue": "நீலம்", + "cyan": "மயில்நிறம்", + "teal": "டீல்", + "green": "பச்சை", + "yellow": "மஞ்சள்", + "orange": "ஆரஞ்சு", + "bronze": "வெண்கலம்" }, "welcomeScreen": { "app": { @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/th-TH.json b/src/locales/th-TH.json index 9d06b8cbb3..57258e2fa7 100644 --- a/src/locales/th-TH.json +++ b/src/locales/th-TH.json @@ -22,22 +22,22 @@ "background": "พื้นหลัง", "fill": "เติมสี", "strokeWidth": "น้ำหนักเส้นขอบ", - "strokeStyle": "", - "strokeStyle_solid": "", - "strokeStyle_dashed": "", - "strokeStyle_dotted": "", + "strokeStyle": "รูปแบบเส้น", + "strokeStyle_solid": "เส้นทึบ", + "strokeStyle_dashed": "เส้นประ", + "strokeStyle_dotted": "จุด", "sloppiness": "ความเลอะเทอะ", "opacity": "ความทึบแสง", "textAlign": "จัดข้อความ", "edges": "ขอบ", "sharp": "", "round": "", - "arrowheads": "", - "arrowhead_none": "", - "arrowhead_arrow": "", - "arrowhead_bar": "", - "arrowhead_dot": "", - "arrowhead_triangle": "", + "arrowheads": "หัวลูกศร", + "arrowhead_none": "ไม่มี", + "arrowhead_arrow": "ลูกศร", + "arrowhead_bar": "แถบ", + "arrowhead_dot": "จุด", + "arrowhead_triangle": "สามเหลี่ยม", "fontSize": "ขนาดตัวอักษร", "fontFamily": "แบบตัวอักษร", "addWatermark": "เพิ่มลายน้ำ \"สร้างด้วย Excalidraw\"", @@ -59,7 +59,7 @@ "right": "ขวา", "extraBold": "หนาพิเศษ", "architect": "", - "artist": "", + "artist": "ศิลปิน", "cartoonist": "", "fileTitle": "ชื่อไฟล์", "colorPicker": "เลือกสีที่กำหนดเอง", @@ -104,13 +104,17 @@ "excalidrawLib": "คลังของ Excalidraw", "decreaseFontSize": "ลดขนาดตัวอักษร", "increaseFontSize": "เพิ่มขนาดตัวอักษร", - "unbindText": "", + "unbindText": "ยกเลิกการผูกติด", "bindText": "", "createContainerFromText": "", "link": { "edit": "แก้ไขลิงก์", + "editEmbed": "แก้ไขลิงค์และการฝัง", "create": "สร้างลิงค์", - "label": "ลิงค์" + "createEmbed": "สร้างลิงค์และการฝัง", + "label": "ลิงค์", + "labelEmbed": "ลิงค์และการฝัง", + "empty": "ไม่ได้ใส่ลิงค์" }, "lineEditor": { "edit": "แก้ไขเส้น", @@ -129,18 +133,18 @@ "eyeDropper": "" }, "library": { - "noItems": "", + "noItems": "ยังไม่มีรายการที่เพิ่มเข้าไปได้", "hint_emptyLibrary": "", "hint_emptyPrivateLibrary": "" }, "buttons": { - "clearReset": "", + "clearReset": "รีเซ็ทผืนผ้าใบ", "exportJSON": "ส่งออกไปยังไฟล์", "exportImage": "ส่งออกเป็นรูปภาพ", "export": "บันทึกไปยัง", "copyToClipboard": "คัดลอกไปยังคลิปบอร์ด", - "save": "", - "saveAs": "", + "save": "บันทึกเป็นไฟล์ปัจจุบัน", + "saveAs": "บันทึกเป็น", "load": "เปิด", "getShareableLink": "สร้างลิงค์ที่แชร์ได้", "close": "ปิด", @@ -160,21 +164,24 @@ "darkMode": "โหมดกลางคืน", "lightMode": "โหมดกลางวัน", "zenMode": "โหมด Zen", + "objectsSnapMode": "", "exitZenMode": "ออกจากโหมด Zen", "cancel": "ยกเลิก", "clear": "เคลียร์", "remove": "ลบ", + "embed": "สลับการฝัง", "publishLibrary": "เผยแพร่", "submit": "ตกลง", - "confirm": "ยืนยัน" + "confirm": "ยืนยัน", + "embeddableInteractionButton": "คลิกเพื่อปฏิสัมพันธ์" }, "alerts": { "clearReset": "", - "couldNotCreateShareableLink": "", + "couldNotCreateShareableLink": "ไม่สามารถสร้างลิงค์ได้", "couldNotCreateShareableLinkTooBig": "", "couldNotLoadInvalidFile": "ไม่สามารถโหลดไฟล์ที่ผิดพลาดได้", - "importBackendFailed": "", - "cannotExportEmptyCanvas": "", + "importBackendFailed": "เกิดข้อผิดพลาดจากการนำเข้าจากระบบหลังบ้าน", + "cannotExportEmptyCanvas": "ไม่สามารถนำออกจากผืนผ้าใบที่ว่างเปล่าได้", "couldNotCopyToClipboard": "ไม่สามารถคัดลอกไปยังคลิปบอร์ดได้", "decryptFailed": "ไม่สามารถถอดรหัสข้อมูลได้", "uploadedSecurly": "การอัพโหลดได้ถูกเข้ารหัสแบบ end-to-end หมายความว่าเซิร์ฟเวอร์ของ Excalidraw และบุคคลอื่นไม่สามารถอ่านข้อมูลได้", @@ -196,6 +203,7 @@ "imageInsertError": "ไม่สามารถเพิ่มรูปภาพได้ ลองอีกครั้งในภายหลัง", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "ไฟล์ SVG ผิดพลาด", "cannotResolveCollabServer": "ไม่สามารถเชื่อต่อกับ collab เซิร์ฟเวอร์ได้ โปรดลองโหลดหน้านี้ใหม่และลองอีกครั้ง", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "การเพิ่มองค์ประกอบที่ฝังยังไม่สามารถเพิ่มเข้าไปในไลบลารีได้", + "image": "การสนับสนุนสำหรับเพิ่มรูปภาพลงในไลบลารีจะมาในเร็ว ๆ นี้" } }, "toolBar": { @@ -213,8 +225,8 @@ "image": "", "rectangle": "สี่เหลี่ยมผืนผ้า", "diamond": "", - "ellipse": "", - "arrow": "", + "ellipse": "วงรี", + "arrow": "ลูกศร", "line": "", "freedraw": "", "text": "ข้อความ", @@ -224,8 +236,10 @@ "link": "", "eraser": "ยางลบ", "frame": "", + "embeddable": "ฝังเว็บ", + "laser": "", "hand": "", - "extraTools": "" + "extraTools": "เครื่องมืออื่นๆ" }, "headings": { "canvasActions": "", @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "คลิกและลากเพื่อสร้างการฝังสำหรับเว็บไซต์", "text_selected": "คลิกสองครั้งหรือกด ENTER เพื่อแก้ไขข้อความ", "text_editing": "กดปุ่ม Esc หรือกด Ctrl, Cmd + Enter เพื่อเสร็จการแก้ไข", "linearElementMulti": "คลิกที่จุดสุดท้ายหรือกด Escape หรือ Enter เพื่อเสร็จสิ้น", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "", @@ -303,7 +319,7 @@ "drag": "ลาก", "editor": "", "editLineArrowPoints": "", - "editText": "", + "editText": "แก้ไขข้อความ / เพิ่มข้อความ", "github": "", "howto": "", "or": "", @@ -364,7 +380,7 @@ "label": { "withBackground": "", "onlySelected": "", - "darkMode": "", + "darkMode": "โหมดกลางคืน", "embedScene": "", "scale": "", "padding": "" @@ -373,14 +389,14 @@ "embedScene": "" }, "title": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "ส่งออกไปเป็น SVG", + "exportToSvg": "ส่งออกไปเป็น SVG", + "copyPngToClipboard": "คัดลอก PNG ไปยังคลิปบอร์ด" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "คัดลอกไปยังคลิปบอร์ด" } }, "encrypted": { @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -424,11 +442,11 @@ "gray": "", "blue": "", "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", - "bronze": "" + "teal": "ฟ้าน้ำทะเล", + "green": "เขียว", + "yellow": "เหลือง", + "orange": "ส้ม", + "bronze": "ทองแดง" }, "welcomeScreen": { "app": { @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json index 8130134ef7..e9cc463e9a 100644 --- a/src/locales/tr-TR.json +++ b/src/locales/tr-TR.json @@ -50,7 +50,7 @@ "veryLarge": "Çok geniş", "solid": "Dolu", "hachure": "Taralı", - "zigzag": "", + "zigzag": "Zikzak", "crossHatch": "Çapraz-taralı", "thin": "İnce", "bold": "Kalın", @@ -106,11 +106,15 @@ "increaseFontSize": "Yazı Tipi Boyutunu Büyült", "unbindText": "Metni çöz", "bindText": "Metni taşıyıcıya bağla", - "createContainerFromText": "", + "createContainerFromText": "Metni bileşen içinde sar", "link": { "edit": "Bağlantıyı düzenle", + "editEmbed": "Bağlantıyı düzenle & yerleştir", "create": "Bağlantı oluştur", - "label": "Bağlantı" + "createEmbed": "Bağlantı oluştur & yerleştir", + "label": "Bağlantı", + "labelEmbed": "Bağlantı & yerleştirme", + "empty": "Herhangi bir bağlantı oluşturulmadı" }, "lineEditor": { "edit": "Çizgiyi düzenle", @@ -124,9 +128,9 @@ }, "statusPublished": "Yayınlandı", "sidebarLock": "Kenar çubuğu açık kalsın", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Çerçevedeki tüm bileşenleri seç", + "removeAllElementsFromFrame": "Çerçevedeki tüm bileşenleri sil", + "eyeDropper": "Tuvalden renk seç" }, "library": { "noItems": "Öğe eklenmedi...", @@ -160,13 +164,16 @@ "darkMode": "Koyu tema", "lightMode": "Açık tema", "zenMode": "Zen modu", + "objectsSnapMode": "Nesnelere hizala", "exitZenMode": "Zen modundan çık", "cancel": "İptal", "clear": "Temizle", "remove": "Kaldır", + "embed": "", "publishLibrary": "Yayınla", "submit": "Gönder", - "confirm": "Onayla" + "confirm": "Onayla", + "embeddableInteractionButton": "Etkileşime girmek için tıkla" }, "alerts": { "clearReset": "Tuvalin tamamı temizlenecek. Emin misiniz?", @@ -189,13 +196,14 @@ "resetLibrary": "Bu işlem kütüphanenizi sıfırlayacak. Emin misiniz?", "removeItemsFromsLibrary": "{{count}} öğe(ler) kitaplıktan kaldırılsın mı?", "invalidEncryptionKey": "Şifreleme anahtarı 22 karakter olmalı. Canlı işbirliği devre dışı bırakıldı.", - "collabOfflineWarning": "" + "collabOfflineWarning": "İnternet bağlantısı bulunamadı. Değişiklikleriniz kaydedilmeyecek!" }, "errors": { "unsupportedFileType": "Desteklenmeyen dosya türü.", "imageInsertError": "Görsel eklenemedi. Daha sonra tekrar deneyin...", "fileTooBig": "Dosya çok büyük. İzin verilen maksimum boyut {{maxSize}}.", "svgImageInsertError": "SVG resmi eklenemedi. SVG işaretlemesi geçersiz görünüyor.", + "failedToFetchImage": "", "invalidSVGString": "Geçersiz SVG.", "cannotResolveCollabServer": "İş birliği sunucusuna bağlanılamıyor. Lütfen sayfayı yenileyip tekrar deneyin.", "importLibraryError": "Kütüphane yüklenemedi", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "Resimleri kütüphaneye ekleme desteği yakında geliyor!" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Kalem modu - dokunmayı engelle", "link": "Seçilen şekil için bağlantı Ekle/Güncelle", "eraser": "Silgi", - "frame": "", + "frame": "Çerçeve aracı", + "embeddable": "Web Yerleştirme", + "laser": "Lazer işaretçisi", "hand": "", - "extraTools": "" + "extraTools": "Daha fazla araç" }, "headings": { "canvasActions": "Tuval eylemleri", @@ -237,6 +251,7 @@ "linearElement": "Birden fazla nokta için tıklayın, tek çizgi için sürükleyin", "freeDraw": "Tıkla ve sürükle, bitirdiğinde serbest bırak", "text": "İpucu: seçme aracıyla herhangi bir yere çift tıklayarak da yazı ekleyebilirsin", + "embeddable": "Web sitesi yerleştirmek için sürükle bırak", "text_selected": "Metni düzenlemek için çift tıklayın veya ENTER'a basın", "text_editing": "Düzenlemeyi bitirmek için ESC veya Ctrl/Cmd+ENTER tuşlarına basın", "linearElementMulti": "Bitirmek için son noktaya tıklayın ya da Escape veya Enter tuşuna basın", @@ -252,7 +267,8 @@ "bindTextToElement": "Enter tuşuna basarak metin ekleyin", "deepBoxSelect": "Ctrl/Cmd tuşuna basılı tutarak derin seçim yapın ya da sürüklemeyi engelleyin", "eraserRevert": "Alt tuşuna basılı tutarak silinme için işaretlenmiş ögeleri tersine çevirin", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Önizleme gösterilemiyor", @@ -360,27 +376,27 @@ "removeItemsFromLib": "Seçilen ögeleri kütüphaneden kaldır" }, "imageExportDialog": { - "header": "", + "header": "Resmi dışa aktar", "label": { - "withBackground": "", - "onlySelected": "", - "darkMode": "", - "embedScene": "", - "scale": "", - "padding": "" + "withBackground": "Arka plan", + "onlySelected": "Sadece seçilen", + "darkMode": "Karanlık mod", + "embedScene": "Sahne yerleştir", + "scale": "Ölçeklendir", + "padding": "Dış boşluk" }, "tooltip": { - "embedScene": "" + "embedScene": "Sahne verisi, sahnenin geri yüklenebilmesi için dışarı aktarılan PNG/SVG dosyasına kaydedilecektir. Bu, dışa aktarılan dosya boyutunu arttıracaktır." }, "title": { - "exportToPng": "", - "exportToSvg": "", + "exportToPng": "PNG olarak dışa aktar", + "exportToSvg": "SVG olarak dışa aktar", "copyPngToClipboard": "" }, "button": { - "exportToPng": "", - "exportToSvg": "", - "copyPngToClipboard": "" + "exportToPng": "PNG", + "exportToSvg": "SVG", + "copyPngToClipboard": "Panoya kopyala" } }, "encrypted": { @@ -411,29 +427,31 @@ "fileSavedToFilename": "{filename} kaydedildi", "canvas": "tuval", "selection": "seçim", - "pasteAsSingleElement": "Tekil obje olarak yapıştırmak için veya var olan bir metin editörüne yapıştırmak için {{shortcut}} kullanın" + "pasteAsSingleElement": "Tekil obje olarak yapıştırmak için veya var olan bir metin editörüne yapıştırmak için {{shortcut}} kullanın", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "Şeffaf", - "black": "", - "white": "", - "red": "", - "pink": "", - "grape": "", - "violet": "", - "gray": "", - "blue": "", - "cyan": "", - "teal": "", - "green": "", - "yellow": "", - "orange": "", + "black": "Siyah", + "white": "Beyaz", + "red": "Kırmızı", + "pink": "Pembe", + "grape": "Koyu Mor", + "violet": "Menekşe rengi", + "gray": "Gri", + "blue": "Mavi", + "cyan": "Camgöbeği", + "teal": "Deniz mavisi", + "green": "Yeşil", + "yellow": "Sarı", + "orange": "Turuncu", "bronze": "" }, "welcomeScreen": { "app": { "center_heading": "", - "center_heading_plus": "", + "center_heading_plus": "Ecalidraw+'a mı gitmek istediniz?", "menuHint": "Dışa aktar, seçenekler, diller, ..." }, "defaults": { @@ -444,10 +462,41 @@ } }, "colorPicker": { - "mostUsedCustomColors": "", - "colors": "", + "mostUsedCustomColors": "En çok kullanılan özel renkler", + "colors": "Renkler", "shades": "", - "hexCode": "", + "hexCode": "Hex kodu", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "Diske Kaydet", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json index 36d9fc7af0..5e3c4add9d 100644 --- a/src/locales/uk-UA.json +++ b/src/locales/uk-UA.json @@ -109,8 +109,12 @@ "createContainerFromText": "Огорнути текст у контейнер", "link": { "edit": "Редагування посилання", + "editEmbed": "Редагування посилання і вкладення", "create": "Створити посилання", - "label": "Посилання" + "createEmbed": "Створити посилання і вкладення", + "label": "Посилання", + "labelEmbed": "Посилання і вкладення", + "empty": "Посилання відсутнє" }, "lineEditor": { "edit": "Редагувати лінію", @@ -124,9 +128,9 @@ }, "statusPublished": "Опубліковано", "sidebarLock": "Не закривати бокове меню", - "selectAllElementsInFrame": "", - "removeAllElementsFromFrame": "", - "eyeDropper": "" + "selectAllElementsInFrame": "Обрати всі елементи у фреймі", + "removeAllElementsFromFrame": "Видалити всі елементи з фрейму", + "eyeDropper": "Вибрати колір з полотна" }, "library": { "noItems": "Тут поки пусто...", @@ -160,13 +164,16 @@ "darkMode": "Темна тема", "lightMode": "Світла тема", "zenMode": "Режим Дзен", + "objectsSnapMode": "", "exitZenMode": "Вийти з дзен-режиму", "cancel": "Скасувати", "clear": "Очистити", "remove": "Видалити", + "embed": "Перемкнути вкладення", "publishLibrary": "Опублікувати", "submit": "Надіслати", - "confirm": "Підтвердити" + "confirm": "Підтвердити", + "embeddableInteractionButton": "Натисніть для взаємодії" }, "alerts": { "clearReset": "Це очистить все полотно. Впевнені?", @@ -196,6 +203,7 @@ "imageInsertError": "Не вдалося вставити зображення. Повторіть спробу пізніше...", "fileTooBig": "Занадто великий розмір файлу, максимальний розмір файлу {{maxSize}}.", "svgImageInsertError": "Не вдалося вставити SVG-зображення. Помилка розмітки SVG.", + "failedToFetchImage": "", "invalidSVGString": "Недійсний SVG.", "cannotResolveCollabServer": "Не вдалося приєднатися до сервера. Перезавантажте сторінку та повторіть спробу.", "importLibraryError": "Не вдалося завантажити бібліотеку", @@ -206,6 +214,10 @@ "line2": "Це може нашкодити текстовим елементам у ваших малюнках.", "line3": "Ми наполегливо рекомендуємо вимкнути це налаштування. Виконайте наступні кроки, щоб виправити це.", "line4": "Якщо вимкнення цього параметра не вирішує показ текстових елементів, тоді створіть, будь ласка, запит на розв'язання проблеми на нашому GitHub або напишіть нам у Discord" + }, + "libraryElementTypeError": { + "embeddable": "Вбудовані елементи не можна додати в бібліотеку.", + "image": "Підтримка додавання зображень в бібліотеку найближчим часом!" } }, "toolBar": { @@ -223,9 +235,11 @@ "penMode": "Режим пера - запобігання дотику", "link": "Додати/Оновити посилання для вибраної форми", "eraser": "Очищувач", - "frame": "", + "frame": "Інструмент фрейму", + "embeddable": "Веб вкладення", + "laser": "", "hand": "Рука (інструмент для панорамування)", - "extraTools": "" + "extraTools": "Інші інструменти" }, "headings": { "canvasActions": "Дії з полотном", @@ -237,6 +251,7 @@ "linearElement": "Натисніть щоб додати кілька точок. Перетягніть щоб намалювати одну лінію", "freeDraw": "Натисніть і потягніть, відпустіть коли завершите", "text": "Порада: можна також додати текст, двічі клацнувши по будь-якому місці інструментом вибору", + "embeddable": "Клікніть та перетягніть для створення вбудованого вебсайту", "text_selected": "Подвійний клік або натисніть клавішу ENTER, щоб редагувати текст", "text_editing": "Натисніть клавішу Escape або Ctrl/Cmd+ENTER, щоб завершити редагування", "linearElementMulti": "Натисніть на останню точку, клацніть Esc або Enter щоб завершити", @@ -252,7 +267,8 @@ "bindTextToElement": "Натисніть Enter, щоб додати текст", "deepBoxSelect": "Втримуйте Ctrl/Cmd для глибокого виділення та щоб попередити перетягування", "eraserRevert": "Втримуйте клавішу Alt, щоб повернути елементи позначені для видалення", - "firefox_clipboard_write": "Цю функцію можна ввімкнути, встановивши значення \"true\" для налаштування \"dom.events.asyncClipboard.clipboardItem\". Перейдіть на сторінку «about:config», щоб змінити налаштування браузера у Firefox." + "firefox_clipboard_write": "Цю функцію можна ввімкнути, встановивши значення \"true\" для налаштування \"dom.events.asyncClipboard.clipboardItem\". Перейдіть на сторінку «about:config», щоб змінити налаштування браузера у Firefox.", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Не вдається показати попередній перегляд", @@ -362,25 +378,25 @@ "imageExportDialog": { "header": "Експортувати зображення", "label": { - "withBackground": "", + "withBackground": "Тло", "onlySelected": "Тільки вибране", "darkMode": "Темний режим", "embedScene": "Вбудована сцена", - "scale": "", - "padding": "" + "scale": "Масштабування", + "padding": "Відступ" }, "tooltip": { - "embedScene": "" + "embedScene": "Дані сцени будуть збережені в експортований файл PNG/SVG. Ця сцена може бути відновлена з нього, однак це збільшить розмір експортованого файлу." }, "title": { "exportToPng": "Експортувати в PNG", "exportToSvg": "Експортувати у SVG", - "copyPngToClipboard": "" + "copyPngToClipboard": "Скопіювати PNG в буфер обміну" }, "button": { "exportToPng": "PNG", "exportToSvg": "SVG", - "copyPngToClipboard": "" + "copyPngToClipboard": "Копіювати в буфер обміну" } }, "encrypted": { @@ -411,7 +427,9 @@ "fileSavedToFilename": "Збережено в {filename}", "canvas": "полотно", "selection": "виділення", - "pasteAsSingleElement": "Використайте {{shortcut}} для вставки самостійного зразка або використайте в текстовому редакторі" + "pasteAsSingleElement": "Використайте {{shortcut}} для вставки самостійного зразка або використайте в текстовому редакторі", + "unableToEmbed": "Вбудування цієї url на даний час не допускається. Підніміть питання на GitHub, щоб попросити внести URL-адресу до білого списку", + "unrecognizedLinkFormat": "Посилання, яке ви вставили, не відповідає очікуваному формату. Будь ласка, спробуйте вставити рядок \"embed\", наданий сайтом-джерелом" }, "colors": { "transparent": "Прозорий", @@ -419,12 +437,12 @@ "white": "Білий", "red": "Червоний", "pink": "Рожевий", - "grape": "", + "grape": "Виноградний", "violet": "Фіолетовий", "gray": "Сірий", "blue": "Синій", "cyan": "Ціан", - "teal": "", + "teal": "Бірюзовий", "green": "Зелений", "yellow": "Жовтий", "orange": "Помаранчевий", @@ -446,8 +464,39 @@ "colorPicker": { "mostUsedCustomColors": "Найбільш використовувані користувацькі кольори", "colors": "Кольори", - "shades": "", + "shades": "Тіні", "hexCode": "Hex-код", - "noShades": "" + "noShades": "Немає доступних відтінків цього кольору" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "Експортувати як зображення", + "button": "Експортувати як зображення", + "description": "Експорт даних сцени у вигляді зображення, з якого можна імпортувати пізніше." + }, + "saveToDisk": { + "title": "Зберегти на диск", + "button": "Зберегти на диск", + "description": "Експорт даних сцени в файл, з якого можна імпортувати пізніше." + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "Експортувати до Excalidraw+", + "description": "Зберегти сцену до робочого простору Excalidraw+." + } + }, + "modal": { + "loadFromFile": { + "title": "Завантажити з файлу", + "button": "Завантажити з файлу", + "description": "Завантаження з файлу замінить наявний вміст.

Ви можете спочатку створити резервну копію малюнка, скориставшись одним із наведених нижче способів." + }, + "shareableLink": { + "title": "Завантажити з посилання", + "button": "Замінити мій контент", + "description": "Завантаження зовнішнього малюнка замінить ваш наявний вміст.

Ви можете спочатку створити резервну копію малюнка, скориставшись одним із наведених нижче способів." + } + } } } diff --git a/src/locales/vi-VN.json b/src/locales/vi-VN.json index e692854a81..2f8381b55a 100644 --- a/src/locales/vi-VN.json +++ b/src/locales/vi-VN.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "Sửa liên kết", + "editEmbed": "", "create": "Tạo liên kết", - "label": "Liên kết" + "createEmbed": "", + "label": "Liên kết", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "Điều chỉnh nét", @@ -160,13 +164,16 @@ "darkMode": "Chế độ tối", "lightMode": "Chế độ sáng", "zenMode": "Chế độ zen", + "objectsSnapMode": "", "exitZenMode": "Thoát chể độ zen", "cancel": "Hủy", "clear": "Làm sạch", "remove": "Xóa", + "embed": "", "publishLibrary": "Đăng tải", "submit": "Gửi", - "confirm": "Xác nhận" + "confirm": "Xác nhận", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "Điều này sẽ dọn hết canvas. Bạn có chắc không?", @@ -196,6 +203,7 @@ "imageInsertError": "Không thể thêm ảnh. Hãy thử lại sau...", "fileTooBig": "Tệp tin quá lớn. Dung lượng tối đa cho phép là {{maxSize}}.", "svgImageInsertError": "Không thể thêm ảnh SVG. Mã SVG có vẻ sai.", + "failedToFetchImage": "", "invalidSVGString": "SVG không hợp lệ.", "cannotResolveCollabServer": "Không thể kết nối với máy chủ hợp tác. Hãy tải lại trang và thử lại.", "importLibraryError": "Không thể tải thư viện", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "Thêm/ Chỉnh sửa liên kết cho hình được chọn", "eraser": "Xóa", "frame": "", + "embeddable": "", + "laser": "", "hand": "Tay kéo", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "Ấn để bắt đầu nhiểm điểm vẽ, kéo để vẽ một đường thẳng", "freeDraw": "Ấn bà kéo, thả khi bạn xong", "text": "Mẹo: bạn có thể thêm văn bản tại bất cứ đâu bằng cách ấn hai lần bằng tool lựa chọn", + "embeddable": "", "text_selected": "Ấn 2 lần hoặc nhấn ENTER để chỉnh văn bản", "text_editing": "Nhấn Escape hoặc Ctrl/Cmd+ENTER để hoàn thành chỉnh sửa", "linearElementMulti": "Nhấn vào điểm cuối hoặc nhấn Escape hoặc Enter để kết thúc", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "Tính năng này có thể được bật bằng cách đặt cờ \"dom.events.asyncClipboard.clipboardItem\" thành \"true\". Để thay đổi cờ trình duyệt trong Firefox, hãy truy cập trang \"about:config\"." + "firefox_clipboard_write": "Tính năng này có thể được bật bằng cách đặt cờ \"dom.events.asyncClipboard.clipboardItem\" thành \"true\". Để thay đổi cờ trình duyệt trong Firefox, hãy truy cập trang \"about:config\".", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "Không thể xem trước", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "canvas", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 25d43a46f6..04c5b4d408 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -109,8 +109,12 @@ "createContainerFromText": "将文本包围在容器中", "link": { "edit": "编辑链接", + "editEmbed": "编辑链接与嵌入", "create": "新建链接", - "label": "链接" + "createEmbed": "创建链接与嵌入", + "label": "链接", + "labelEmbed": "链接与嵌入", + "empty": "未设定链接" }, "lineEditor": { "edit": "编辑线条", @@ -125,7 +129,7 @@ "statusPublished": "已发布", "sidebarLock": "侧边栏常驻", "selectAllElementsInFrame": "选择画框中的所有元素", - "removeAllElementsFromFrame": "删除画框中的所有元素", + "removeAllElementsFromFrame": "分离出画框中的所有元素", "eyeDropper": "从画布上取色" }, "library": { @@ -160,13 +164,16 @@ "darkMode": "深色模式", "lightMode": "浅色模式", "zenMode": "禅模式", + "objectsSnapMode": "吸附至对象", "exitZenMode": "退出禅模式", "cancel": "取消", "clear": "清除", "remove": "删除", + "embed": "切换嵌入", "publishLibrary": "发布", "submit": "提交", - "confirm": "确定" + "confirm": "确定", + "embeddableInteractionButton": "点击以开始交互" }, "alerts": { "clearReset": "这将会清除整个画布。您是否要继续?", @@ -196,6 +203,7 @@ "imageInsertError": "无法插入图像。请稍后再试……", "fileTooBig": "文件过大。最大允许的大小为 {{maxSize}}。", "svgImageInsertError": "无法插入 SVG 图像。该 SVG 标记似乎是无效的。", + "failedToFetchImage": "", "invalidSVGString": "无效的 SVG。", "cannotResolveCollabServer": "无法连接到实时协作服务器。请重新加载页面并重试。", "importLibraryError": "无法加载素材库", @@ -206,6 +214,10 @@ "line2": "这可能会破坏绘图中的 文本元素。", "line3": "我们强烈建议禁用此设置。您可以按照这些步骤来设置。", "line4": "如果禁用此设置无法修复文本元素的显示,请在 GitHub 上提交一个 issue ,或者在 Discord 上反馈" + }, + "libraryElementTypeError": { + "embeddable": "嵌入元素不能添加到素材库。", + "image": "我们不久将支持添加图片到素材库" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "为选中的形状添加/更新链接", "eraser": "橡皮", "frame": "画框工具", + "embeddable": "嵌入网页", + "laser": "激光笔", "hand": "抓手(平移工具)", "extraTools": "更多工具" }, @@ -237,6 +251,7 @@ "linearElement": "点击创建多个点 拖动创建直线", "freeDraw": "点击并拖动,完成时松开", "text": "提示:您也可以使用选择工具双击任意位置来添加文字", + "embeddable": "点击并拖动以创建嵌入网页", "text_selected": "双击或按回车键以编辑文本", "text_editing": "按下 Escape 或 CtrlOrCmd+ENTER 完成编辑", "linearElementMulti": "点击最后一个点或按下 Esc/Enter 来完成", @@ -245,14 +260,15 @@ "resizeImage": "按住SHIFT可以自由缩放,\n按住ALT可以从中间缩放", "rotate": "旋转时可以按住 Shift 来约束角度", "lineEditor_info": "按住 CtrlOrCmd 并双击或按 CtrlOrCmd + Enter 来编辑点", - "lineEditor_pointSelected": "按下 Delete 移除点,Ctrl 或 Cmd+D 以复制,拖动以移动", + "lineEditor_pointSelected": "按下 Delete 移除点,CtrlOrCmd+D 以复制,拖动以移动", "lineEditor_nothingSelected": "选择要编辑的点 (按住 SHIFT 选择多个),\n或按住 Alt 并点击以添加新点", "placeImage": "点击放置图像,或者点击并拖动以手动设置图像大小", "publishLibrary": "发布您自己的素材库", "bindTextToElement": "按下 Enter 以添加文本", "deepBoxSelect": "按住 CtrlOrCmd 以深度选择,并避免拖拽", "eraserRevert": "按住 Alt 以反选被标记删除的元素", - "firefox_clipboard_write": "将高级配置首选项“dom.events.asyncClipboard.clipboardItem”设置为“true”可以启用此功能。要更改 Firefox 的高级配置首选项,请前往“about:config”页面。" + "firefox_clipboard_write": "将高级配置首选项“dom.events.asyncClipboard.clipboardItem”设置为“true”可以启用此功能。要更改 Firefox 的高级配置首选项,请前往“about:config”页面。", + "disableSnapping": "按住 CtrlOrCmd 以禁用吸附" }, "canvasError": { "cannotShowPreview": "无法显示预览", @@ -411,7 +427,9 @@ "fileSavedToFilename": "保存到 {filename}", "canvas": "画布", "selection": "所选项", - "pasteAsSingleElement": "使用 {{shortcut}} 粘贴为单个元素,\n或粘贴到现有的文本编辑器里" + "pasteAsSingleElement": "使用 {{shortcut}} 粘贴为单个元素,\n或粘贴到现有的文本编辑器里", + "unableToEmbed": "目前不允许嵌入此网址。请在 GitHub 上提 issue 请求将此网址加入白名单", + "unrecognizedLinkFormat": "您嵌入的链接不符合格式要求。请尝试粘贴源网站提供的“嵌入 (embed)”字符串" }, "colors": { "transparent": "透明", @@ -449,5 +467,36 @@ "shades": "色调明暗", "hexCode": "十六进制值", "noShades": "此颜色没有可用的明暗变化" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "导出为图片", + "button": "导出为图片", + "description": "将画布数据导出为图片,以便以后导入。" + }, + "saveToDisk": { + "title": "保存到本地", + "button": "保存到本地", + "description": "将画布数据导出为文件,以便以后导入。" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "导出到 Excalidraw+", + "description": "将画布保存到您的 Excalidraw+ 工作区。" + } + }, + "modal": { + "loadFromFile": { + "title": "从文件加载", + "button": "从文件加载", + "description": "从文件加载将替换您现有的内容

您可以先使用下列方式备份您的绘图。" + }, + "shareableLink": { + "title": "从链接加载", + "button": "替换我的内容", + "description": "加载外部绘图将替换您现有的内容

您可以先使用下列方式备份您的绘图。" + } + } } } diff --git a/src/locales/zh-HK.json b/src/locales/zh-HK.json index 54406cb23c..1ecdb0ce37 100644 --- a/src/locales/zh-HK.json +++ b/src/locales/zh-HK.json @@ -109,8 +109,12 @@ "createContainerFromText": "", "link": { "edit": "", + "editEmbed": "", "create": "", - "label": "" + "createEmbed": "", + "label": "", + "labelEmbed": "", + "empty": "" }, "lineEditor": { "edit": "", @@ -160,13 +164,16 @@ "darkMode": "", "lightMode": "", "zenMode": "", + "objectsSnapMode": "", "exitZenMode": "", "cancel": "", "clear": "", "remove": "", + "embed": "", "publishLibrary": "", "submit": "", - "confirm": "" + "confirm": "", + "embeddableInteractionButton": "" }, "alerts": { "clearReset": "【‼️ 警告 ‼️ 無法復原的動作】你確定要清空呢塊畫布嗎?", @@ -196,6 +203,7 @@ "imageInsertError": "", "fileTooBig": "", "svgImageInsertError": "", + "failedToFetchImage": "", "invalidSVGString": "", "cannotResolveCollabServer": "", "importLibraryError": "", @@ -206,6 +214,10 @@ "line2": "", "line3": "", "line4": "" + }, + "libraryElementTypeError": { + "embeddable": "", + "image": "" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "", "eraser": "", "frame": "", + "embeddable": "", + "laser": "", "hand": "", "extraTools": "" }, @@ -237,6 +251,7 @@ "linearElement": "", "freeDraw": "", "text": "", + "embeddable": "", "text_selected": "", "text_editing": "", "linearElementMulti": "", @@ -252,7 +267,8 @@ "bindTextToElement": "", "deepBoxSelect": "", "eraserRevert": "", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "", + "disableSnapping": "" }, "canvasError": { "cannotShowPreview": "無法顯示預覽", @@ -411,7 +427,9 @@ "fileSavedToFilename": "", "canvas": "畫布", "selection": "", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "", + "unableToEmbed": "", + "unrecognizedLinkFormat": "" }, "colors": { "transparent": "", @@ -449,5 +467,36 @@ "shades": "", "hexCode": "", "noShades": "" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "", + "button": "", + "description": "" + }, + "saveToDisk": { + "title": "", + "button": "", + "description": "" + }, + "excalidrawPlus": { + "title": "", + "button": "", + "description": "" + } + }, + "modal": { + "loadFromFile": { + "title": "", + "button": "", + "description": "" + }, + "shareableLink": { + "title": "", + "button": "", + "description": "" + } + } } } diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 9c706dc281..64826dc60a 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -109,8 +109,12 @@ "createContainerFromText": "將文字包於容器中", "link": { "edit": "編輯連結", + "editEmbed": "編輯連結&嵌入", "create": "建立連結", - "label": "連結" + "createEmbed": "建立連結&嵌入", + "label": "連結", + "labelEmbed": "連結&嵌入", + "empty": "未設定連結" }, "lineEditor": { "edit": "編輯線條", @@ -160,13 +164,16 @@ "darkMode": "深色模式", "lightMode": "淺色模式", "zenMode": "專注模式", + "objectsSnapMode": "吸附至物件", "exitZenMode": "離開專注模式", "cancel": "取消", "clear": "清除", "remove": "刪除", + "embed": "切換嵌入", "publishLibrary": "發布", "submit": "送出", - "confirm": "確認" + "confirm": "確認", + "embeddableInteractionButton": "點擊以互動" }, "alerts": { "clearReset": "這將會清除整個 canvas。你確定嗎?", @@ -196,6 +203,7 @@ "imageInsertError": "無法插入圖片。請稍後再試…", "fileTooBig": "檔案過大。可接受的最大尺寸為 {{maxSize}} 。", "svgImageInsertError": "無法插入 SVG 圖片。此 SVG 檔案有問題。", + "failedToFetchImage": "無法獲取圖片。", "invalidSVGString": "無效的 SVG。", "cannotResolveCollabServer": "無法連結至 collab 伺服器。請重新整理後再試一次。", "importLibraryError": "無法載入資料庫", @@ -206,6 +214,10 @@ "line2": "這可能造成您畫布中 文字元素 的異常。", "line3": "我們強烈建議您關閉此設定。您可以依照 這些步驟 來進行。", "line4": "若關閉此設定並未修復文字元素的顯示問題,請回報於我們 GitHub 上的 issue,或在 Discord 上告訴我們。" + }, + "libraryElementTypeError": { + "embeddable": "可嵌入元素無法加入資料庫", + "image": "即將支援加入圖片至資料庫!" } }, "toolBar": { @@ -224,6 +236,8 @@ "link": "為所選的形狀增加\b/更新連結", "eraser": "橡皮擦", "frame": "框架工具", + "embeddable": "嵌入網站", + "laser": "雷射筆", "hand": "手形(平移工具)", "extraTools": "更多工具" }, @@ -237,6 +251,7 @@ "linearElement": "點擊以繪製多點曲線;或拖曳以繪製直線", "freeDraw": "點擊並拖曳來繪圖,放開即結束", "text": "提示:亦可使用選取工具在任何地方雙擊來加入文字", + "embeddable": "點擊並拖移以建立嵌入網站", "text_selected": "雙擊滑鼠或按 Enter 以編輯文字", "text_editing": "按跳脫鍵或 Ctrl 或 Cmd + Enter 以結束編輯", "linearElementMulti": "按下 Escape 或 Enter 以結束繪製", @@ -252,7 +267,8 @@ "bindTextToElement": "按下 Enter 以加入文字。", "deepBoxSelect": "按住 Ctrl 或 Cmd 以深度選取並避免拖曳", "eraserRevert": "按住 Alt 以反選取已標記待刪除的元素", - "firefox_clipboard_write": "此功能有機會透過將 \"dom.events.asyncClipboard.clipboardItem\" 設定為 \"true\" 來開啟。\n若要變更 Firefox 瀏覽器的此設定值,請至 \"about:config\" 頁面。" + "firefox_clipboard_write": "此功能有機會透過將 \"dom.events.asyncClipboard.clipboardItem\" 設定為 \"true\" 來開啟。\n若要變更 Firefox 瀏覽器的此設定值,請至 \"about:config\" 頁面。", + "disableSnapping": "按住 Ctrl 或 Cmd 以禁用吸附" }, "canvasError": { "cannotShowPreview": "無法顯示預覽", @@ -411,7 +427,9 @@ "fileSavedToFilename": "儲存為 {filename}", "canvas": "畫布", "selection": "已選項目", - "pasteAsSingleElement": "使用 {{shortcut}} 以做為單一物件貼上,\n或貼上至現有的文字編輯器" + "pasteAsSingleElement": "使用 {{shortcut}} 以做為單一物件貼上,\n或貼上至現有的文字編輯器", + "unableToEmbed": "目前不允許嵌入此網址。您可至 GitHub 提出 issue 以要求將此網址加入合格名單。", + "unrecognizedLinkFormat": "您嵌入的連結格式不符。請嘗試貼入原網站所提供的「嵌入」字串。" }, "colors": { "transparent": "透明", @@ -449,5 +467,36 @@ "shades": "漸變色", "hexCode": "Hex 碼", "noShades": "沒有此顏色的漸變色" + }, + "overwriteConfirm": { + "action": { + "exportToImage": { + "title": "匯出為圖片", + "button": "匯出為圖片", + "description": "將場景匯出為可供匯入的圖片檔案" + }, + "saveToDisk": { + "title": "儲存至硬碟", + "button": "儲存至硬碟", + "description": "將場景匯出為可供匯入的檔案" + }, + "excalidrawPlus": { + "title": "Excalidraw+", + "button": "匯出至 Excalidraw+", + "description": "將此場景儲存至您的 Excalidraw+ 工作區" + } + }, + "modal": { + "loadFromFile": { + "title": "從檔案載入", + "button": "從檔案載入", + "description": "從檔案載入將取代您目前的內容

可先使用下方的選項備份您的繪圖。" + }, + "shareableLink": { + "title": "從連結載入", + "button": "取代我的內容", + "description": "載入外部繪圖將取代您目前的內容

可先使用下方的選項備份您的繪圖。" + } + } } } From 9eb89f9960ef549f659da1bc27052fe4b9d9e8a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:31:27 +0200 Subject: [PATCH 039/144] build(deps): bump @babel/traverse from 7.18.9 to 7.23.2 in /dev-docs (#7165) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dev-docs/yarn.lock | 129 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 12 deletions(-) diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index f06b9fb245..b22621f317 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -145,6 +145,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" @@ -202,6 +210,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -265,6 +283,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -280,6 +303,14 @@ "@babel/template" "^7.18.6" "@babel/types" "^7.18.9" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -287,6 +318,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" @@ -374,11 +412,28 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -412,11 +467,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.12.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.8", "@babel/parser@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1147,19 +1216,28 @@ "@babel/parser" "^7.18.6" "@babel/types" "^7.18.6" -"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" - integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.18.9": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -1171,6 +1249,15 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -1670,6 +1757,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -1688,6 +1780,19 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": version "0.3.14" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" From e7cc2337eac70278ebc4aee830cf3cac91a75731 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 20 Oct 2023 13:08:22 +0200 Subject: [PATCH 040/144] feat: add `onChange`, `onPointerDown`, `onPointerUp` api subs (#7154) --- src/components/App.tsx | 45 +++++++++++++++++++++++ src/emitter.ts | 47 ++++++++++++++++++++++++ src/tests/packages/events.test.tsx | 58 ++++++++++++++++++++++++++++++ src/types.ts | 23 ++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 src/emitter.ts create mode 100644 src/tests/packages/events.test.tsx diff --git a/src/components/App.tsx b/src/components/App.tsx index 9aa23d62b3..44ecdb4dbf 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -374,6 +374,7 @@ import { resetCursor, setCursorForShape, } from "../cursor"; +import { Emitter } from "../emitter"; const AppContext = React.createContext(null!); const AppPropsContext = React.createContext(null!); @@ -505,6 +506,30 @@ class App extends React.Component { laserPathManager: LaserPathManager = new LaserPathManager(this); + onChangeEmitter = new Emitter< + [ + elements: readonly ExcalidrawElement[], + appState: AppState, + files: BinaryFiles, + ] + >(); + + onPointerDownEmitter = new Emitter< + [ + activeTool: AppState["activeTool"], + pointerDownState: PointerDownState, + event: React.PointerEvent, + ] + >(); + + onPointerUpEmitter = new Emitter< + [ + activeTool: AppState["activeTool"], + pointerDownState: PointerDownState, + event: PointerEvent, + ] + >(); + constructor(props: AppProps) { super(props); const defaultAppState = getDefaultAppState(); @@ -568,6 +593,9 @@ class App extends React.Component { resetCursor: this.resetCursor, updateFrameRendering: this.updateFrameRendering, toggleSidebar: this.toggleSidebar, + onChange: (cb) => this.onChangeEmitter.on(cb), + onPointerDown: (cb) => this.onPointerDownEmitter.on(cb), + onPointerUp: (cb) => this.onPointerUpEmitter.on(cb), } as const; if (typeof excalidrawRef === "function") { excalidrawRef(api); @@ -1750,6 +1778,7 @@ class App extends React.Component { this.scene.destroy(); this.library.destroy(); this.laserPathManager.destroy(); + this.onChangeEmitter.destroy(); ShapeCache.destroy(); SnapCache.destroy(); clearTimeout(touchTimeout); @@ -2034,6 +2063,11 @@ class App extends React.Component { this.state, this.files, ); + this.onChangeEmitter.trigger( + this.scene.getElementsIncludingDeleted(), + this.state, + this.files, + ); } } @@ -4699,6 +4733,11 @@ class App extends React.Component { } this.props?.onPointerDown?.(this.state.activeTool, pointerDownState); + this.onPointerDownEmitter.trigger( + this.state.activeTool, + pointerDownState, + event, + ); const onPointerMove = this.onPointerMoveFromPointerDownHandler(pointerDownState); @@ -6551,6 +6590,12 @@ class App extends React.Component { this.setState({ pendingImageElementId: null }); } + this.onPointerUpEmitter.trigger( + this.state.activeTool, + pointerDownState, + childEvent, + ); + if (draggingElement?.type === "freedraw") { const pointerCoords = viewportCoordsToSceneCoords( childEvent, diff --git a/src/emitter.ts b/src/emitter.ts new file mode 100644 index 0000000000..5b1cdd0a78 --- /dev/null +++ b/src/emitter.ts @@ -0,0 +1,47 @@ +type Subscriber = (...payload: T) => void; + +export class Emitter { + public subscribers: Subscriber[] = []; + public value: T | undefined; + private updateOnChangeOnly: boolean; + + constructor(opts?: { initialState?: T; updateOnChangeOnly?: boolean }) { + this.updateOnChangeOnly = opts?.updateOnChangeOnly ?? false; + this.value = opts?.initialState; + } + + /** + * Attaches subscriber + * + * @returns unsubscribe function + */ + on(...handlers: Subscriber[] | Subscriber[][]) { + const _handlers = handlers + .flat() + .filter((item) => typeof item === "function"); + + this.subscribers.push(..._handlers); + + return () => this.off(_handlers); + } + + off(...handlers: Subscriber[] | Subscriber[][]) { + const _handlers = handlers.flat(); + this.subscribers = this.subscribers.filter( + (handler) => !_handlers.includes(handler), + ); + } + + trigger(...payload: T): any[] { + if (this.updateOnChangeOnly && this.value === payload) { + return []; + } + this.value = payload; + return this.subscribers.map((handler) => handler(...payload)); + } + + destroy() { + this.subscribers = []; + this.value = undefined; + } +} diff --git a/src/tests/packages/events.test.tsx b/src/tests/packages/events.test.tsx new file mode 100644 index 0000000000..231af57999 --- /dev/null +++ b/src/tests/packages/events.test.tsx @@ -0,0 +1,58 @@ +import { vi } from "vitest"; +import { Excalidraw } from "../../packages/excalidraw/index"; +import { ExcalidrawImperativeAPI } from "../../types"; +import { resolvablePromise } from "../../utils"; +import { render } from "../test-utils"; +import { Pointer } from "../helpers/ui"; + +describe("event callbacks", () => { + const h = window.h; + + let excalidrawAPI: ExcalidrawImperativeAPI; + + const mouse = new Pointer("mouse"); + + beforeEach(async () => { + const excalidrawAPIPromise = resolvablePromise(); + await render( + excalidrawAPIPromise.resolve(api as any)} />, + ); + excalidrawAPI = await excalidrawAPIPromise; + }); + + it("should trigger onChange on render", async () => { + const onChange = vi.fn(); + + const origBackgroundColor = h.state.viewBackgroundColor; + excalidrawAPI.onChange(onChange); + excalidrawAPI.updateScene({ appState: { viewBackgroundColor: "red" } }); + expect(onChange).toHaveBeenCalledWith( + // elements + [], + // appState + expect.objectContaining({ + viewBackgroundColor: "red", + }), + // files + {}, + ); + expect(onChange.mock.lastCall[1].viewBackgroundColor).not.toBe( + origBackgroundColor, + ); + }); + + it("should trigger onPointerDown/onPointerUp on canvas pointerDown/pointerUp", async () => { + const onPointerDown = vi.fn(); + const onPointerUp = vi.fn(); + + excalidrawAPI.onPointerDown(onPointerDown); + excalidrawAPI.onPointerUp(onPointerUp); + + mouse.downAt(100); + expect(onPointerDown).toHaveBeenCalledTimes(1); + expect(onPointerUp).not.toHaveBeenCalled(); + mouse.up(); + expect(onPointerDown).toHaveBeenCalledTimes(1); + expect(onPointerUp).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/types.ts b/src/types.ts index 8b05ba40a7..c6fe765cae 100644 --- a/src/types.ts +++ b/src/types.ts @@ -607,6 +607,8 @@ export type PointerDownState = Readonly<{ }; }>; +type UnsubscribeCallback = () => void; + export type ExcalidrawImperativeAPI = { updateScene: InstanceType["updateScene"]; updateLibrary: InstanceType["updateLibrary"]; @@ -637,6 +639,27 @@ export type ExcalidrawImperativeAPI = { * used in conjunction with view mode (props.viewModeEnabled). */ updateFrameRendering: InstanceType["updateFrameRendering"]; + onChange: ( + callback: ( + elements: readonly ExcalidrawElement[], + appState: AppState, + files: BinaryFiles, + ) => void, + ) => UnsubscribeCallback; + onPointerDown: ( + callback: ( + activeTool: AppState["activeTool"], + pointerDownState: PointerDownState, + event: React.PointerEvent, + ) => void, + ) => UnsubscribeCallback; + onPointerUp: ( + callback: ( + activeTool: AppState["activeTool"], + pointerDownState: PointerDownState, + event: PointerEvent, + ) => void, + ) => UnsubscribeCallback; }; export type Device = Readonly<{ From 3697618266a6410da27717230b15f5c7b9938cee Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 20 Oct 2023 13:16:23 +0200 Subject: [PATCH 041/144] feat: support `props.locked` for `setActiveTool` (#7153) Co-authored-by: Aakansha Doshi --- src/components/App.tsx | 7 +++--- src/cursor.ts | 2 ++ src/tests/tool.test.tsx | 55 +++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 8 ++++-- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/tests/tool.test.tsx diff --git a/src/components/App.tsx b/src/components/App.tsx index 44ecdb4dbf..880462bbca 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3249,7 +3249,7 @@ class App extends React.Component { }); setActiveTool = ( - tool: + tool: ( | ( | { type: Exclude } | { @@ -3257,7 +3257,8 @@ class App extends React.Component { insertOnCanvasDirectly?: boolean; } ) - | { type: "custom"; customType: string }, + | { type: "custom"; customType: string } + ) & { locked?: boolean }, ) => { const nextActiveTool = updateActiveTool(this.state, tool); if (nextActiveTool.type === "hand") { @@ -4714,7 +4715,7 @@ class App extends React.Component { pointerDownState, ); } else if (this.state.activeTool.type === "custom") { - setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO); + setCursorForShape(this.interactiveCanvas, this.state); } else if (this.state.activeTool.type === "frame") { this.createFrameElementOnPointerDown(pointerDownState); } else if (this.state.activeTool.type === "laser") { diff --git a/src/cursor.ts b/src/cursor.ts index 364ce155c8..c39c2efcff 100644 --- a/src/cursor.ts +++ b/src/cursor.ts @@ -99,5 +99,7 @@ export const setCursorForShape = ( interactiveCanvas.style.cursor = `url(${url}), auto`; } else if (!["image", "custom"].includes(appState.activeTool.type)) { interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR; + } else { + interactiveCanvas.style.cursor = CURSOR_TYPE.AUTO; } }; diff --git a/src/tests/tool.test.tsx b/src/tests/tool.test.tsx new file mode 100644 index 0000000000..84a43c6b5e --- /dev/null +++ b/src/tests/tool.test.tsx @@ -0,0 +1,55 @@ +import { Excalidraw } from "../packages/excalidraw/index"; +import { ExcalidrawImperativeAPI } from "../types"; +import { resolvablePromise } from "../utils"; +import { render } from "./test-utils"; +import { Pointer } from "./helpers/ui"; + +describe("setActiveTool()", () => { + const h = window.h; + + let excalidrawAPI: ExcalidrawImperativeAPI; + + const mouse = new Pointer("mouse"); + + beforeEach(async () => { + const excalidrawAPIPromise = resolvablePromise(); + await render( + excalidrawAPIPromise.resolve(api as any)} />, + ); + excalidrawAPI = await excalidrawAPIPromise; + }); + + it("should expose setActiveTool on package API", () => { + expect(excalidrawAPI.setActiveTool).toBeDefined(); + expect(excalidrawAPI.setActiveTool).toBe(h.app.setActiveTool); + }); + + it("should set the active tool type", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "rectangle" }); + expect(h.state.activeTool.type).toBe("rectangle"); + + mouse.down(10, 10); + mouse.up(20, 20); + + expect(h.state.activeTool.type).toBe("selection"); + }); + + it("should support tool locking", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "rectangle", locked: true }); + expect(h.state.activeTool.type).toBe("rectangle"); + + mouse.down(10, 10); + mouse.up(20, 20); + + expect(h.state.activeTool.type).toBe("rectangle"); + }); + + it("should set custom tool", async () => { + expect(h.state.activeTool.type).toBe("selection"); + excalidrawAPI.setActiveTool({ type: "custom", customType: "comment" }); + expect(h.state.activeTool.type).toBe("custom"); + expect(h.state.activeTool.customType).toBe("comment"); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index f95139200f..d5f952fa02 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -363,18 +363,21 @@ export const distance = (x: number, y: number) => Math.abs(x - y); export const updateActiveTool = ( appState: Pick, - data: ( + data: (( | { type: ToolType; } | { type: "custom"; customType: string } - ) & { lastActiveToolBeforeEraser?: ActiveTool | null }, + ) & { locked?: boolean }) & { + lastActiveToolBeforeEraser?: ActiveTool | null; + }, ): AppState["activeTool"] => { if (data.type === "custom") { return { ...appState.activeTool, type: "custom", customType: data.customType, + locked: data.locked ?? appState.activeTool.locked, }; } @@ -386,6 +389,7 @@ export const updateActiveTool = ( : data.lastActiveToolBeforeEraser, type: data.type, customType: null, + locked: data.locked ?? appState.activeTool.locked, }; }; From d2a508104e19446e9cc4b95f47e74f3c3775b08d Mon Sep 17 00:00:00 2001 From: Preet <833927+pshihn@users.noreply.github.com> Date: Fri, 20 Oct 2023 06:08:24 -0700 Subject: [PATCH 042/144] fix: Better fill rendering with latest RoughJS (#7031) Co-authored-by: dwelle --- package.json | 4 ++-- src/element/collision.ts | 2 +- src/element/textWysiwyg.test.tsx | 19 ++++--------------- src/scene/Shape.ts | 4 +++- src/tests/helpers/ui.ts | 7 ++----- src/tests/queries/dom.ts | 13 +++++++++++++ .../scene/__snapshots__/export.test.ts.snap | 8 ++++---- yarn.lock | 19 +++++++++++++++---- 8 files changed, 44 insertions(+), 32 deletions(-) create mode 100644 src/tests/queries/dom.ts diff --git a/package.json b/package.json index 5ae2d1ff6c..5e297fdd6b 100644 --- a/package.json +++ b/package.json @@ -49,11 +49,11 @@ "png-chunk-text": "1.0.0", "png-chunks-encode": "1.0.0", "png-chunks-extract": "1.0.0", - "points-on-curve": "0.2.0", + "points-on-curve": "1.0.1", "pwacompat": "2.0.17", "react": "18.2.0", "react-dom": "18.2.0", - "roughjs": "4.5.2", + "roughjs": "4.6.4", "sass": "1.51.0", "socket.io-client": "2.3.1", "tunnel-rat": "0.1.2" diff --git a/src/element/collision.ts b/src/element/collision.ts index d04f1a0ea7..103d3fd991 100644 --- a/src/element/collision.ts +++ b/src/element/collision.ts @@ -494,7 +494,7 @@ const hitTestFreeDrawElement = ( // for filled freedraw shapes, support // selecting from inside if (shape && shape.sets.length) { - return hitTestRoughShape(shape, x, y, threshold); + return hitTestCurveInside(shape, x, y, "round"); } return false; diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 842d75af06..e02fa9937c 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -8,7 +8,7 @@ import { mockBoundingClientRect, restoreOriginalGetBoundingClientRect, } from "../tests/test-utils"; -import { queryByText, waitFor } from "@testing-library/react"; +import { queryByText } from "@testing-library/react"; import { FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; import { @@ -18,6 +18,7 @@ import { import { API } from "../tests/helpers/api"; import { mutateElement } from "./mutateElement"; import { getOriginalContainerHeightFromCache } from "./textWysiwyg"; +import { getTextEditor } from "../tests/queries/dom"; // Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); @@ -25,18 +26,6 @@ ReactDOM.unmountComponentAtNode(document.getElementById("root")!); const tab = " "; const mouse = new Pointer("mouse"); -const getTextEditor = async (waitForEditor = false) => { - const query = () => - document.querySelector( - ".excalidraw-textEditorContainer > textarea", - ) as HTMLTextAreaElement; - if (waitForEditor) { - waitFor(() => expect(query()).not.toBe(null)); - return query(); - } - return query(); -}; - const updateTextEditor = (editor: HTMLTextAreaElement, value: string) => { fireEvent.change(editor, { target: { value } }); editor.dispatchEvent(new Event("input")); @@ -206,7 +195,7 @@ describe("textWysiwyg", () => { mouse.clickAt(text.x + 50, text.y + 50); - const editor = await getTextEditor(); + const editor = await getTextEditor(false); expect(editor).not.toBe(null); expect(h.state.editingElement?.id).toBe(text.id); @@ -228,7 +217,7 @@ describe("textWysiwyg", () => { mouse.doubleClickAt(text.x + 50, text.y + 50); - const editor = await getTextEditor(); + const editor = await getTextEditor(false); expect(editor).not.toBe(null); expect(h.state.editingElement?.id).toBe(text.id); diff --git a/src/scene/Shape.ts b/src/scene/Shape.ts index f43a1260d4..5a2f68bbf0 100644 --- a/src/scene/Shape.ts +++ b/src/scene/Shape.ts @@ -12,6 +12,7 @@ import type { import { isPathALoop, getCornerRadius } from "../math"; import { generateFreeDrawShape } from "../renderer/renderElement"; import { isTransparent, assertNever } from "../utils"; +import { simplify } from "points-on-curve"; const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth]; @@ -334,7 +335,8 @@ export const _generateElementShape = ( if (isPathALoop(element.points)) { // generate rough polygon to fill freedraw shape - shape = generator.polygon(element.points as [number, number][], { + const simplifiedPoints = simplify(element.points, 0.75); + shape = generator.curve(simplifiedPoints as [number, number][], { ...generateRoughOptions(element), stroke: "none", }); diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts index 62290fe236..105d7a3949 100644 --- a/src/tests/helpers/ui.ts +++ b/src/tests/helpers/ui.ts @@ -32,6 +32,7 @@ import { } from "../../element/typeChecks"; import { getCommonBounds, getElementPointsCoords } from "../../element/bounds"; import { rotatePoint } from "../../math"; +import { getTextEditor } from "../queries/dom"; const { h } = window; @@ -476,11 +477,7 @@ export class UI { Keyboard.keyPress(KEYS.ENTER); } - const editor = - openedEditor ?? - document.querySelector( - ".excalidraw-textEditorContainer > textarea", - ); + const editor = await getTextEditor(); if (!editor) { throw new Error("Can't find wysiwyg text editor in the dom"); } diff --git a/src/tests/queries/dom.ts b/src/tests/queries/dom.ts new file mode 100644 index 0000000000..62364e4c45 --- /dev/null +++ b/src/tests/queries/dom.ts @@ -0,0 +1,13 @@ +import { waitFor } from "@testing-library/dom"; + +export const getTextEditor = async (waitForEditor = true) => { + const query = () => + document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + if (waitForEditor) { + waitFor(() => expect(query()).not.toBe(null)); + return query(); + } + return query(); +}; diff --git a/src/tests/scene/__snapshots__/export.test.ts.snap b/src/tests/scene/__snapshots__/export.test.ts.snap index 2b959db254..8d000f849b 100644 --- a/src/tests/scene/__snapshots__/export.test.ts.snap +++ b/src/tests/scene/__snapshots__/export.test.ts.snap @@ -42,7 +42,7 @@ exports[`exportToSvg > with default arguments 1`] = ` transform="translate(10 10) rotate(0 50 50)" > with default arguments 1`] = ` transform="translate(10 10) rotate(0 50 50)" > with elements that have a link 1`] = ` - " + " `; exports[`exportToSvg > with exportEmbedScene 1`] = ` @@ -111,5 +111,5 @@ exports[`exportToSvg > with exportEmbedScene 1`] = ` - " + " `; diff --git a/yarn.lock b/yarn.lock index 95a7ed8b21..8b34b8de85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4684,6 +4684,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +hachure-fill@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/hachure-fill/-/hachure-fill-0.5.2.tgz#d19bc4cc8750a5962b47fb1300557a85fcf934cc" + integrity sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg== + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -6028,6 +6033,11 @@ points-on-curve@0.2.0, points-on-curve@^0.2.0: resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-0.2.0.tgz#7dbb98c43791859434284761330fa893cb81b4d1" integrity sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A== +points-on-curve@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-1.0.1.tgz#03fcdc08e48e3bfdc7e8bd1d7ccd4d5f11e132c6" + integrity sha512-3nmX4/LIiyuwGLwuUrfhTlDeQFlAhi7lyK/zcRNGhalwapDWgAGR82bUpmn2mA03vII3fvNCG8jAONzKXwpxAg== + points-on-path@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/points-on-path/-/points-on-path-0.2.1.tgz#553202b5424c53bed37135b318858eacff85dd52" @@ -6449,11 +6459,12 @@ rollup@^3.25.2: optionalDependencies: fsevents "~2.3.2" -roughjs@4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.5.2.tgz#aab644dcb41e9a75826c8bd5a5b0a859095f2f10" - integrity sha512-2xSlLDKdsWyFxrveYWk9YQ/Y9UfK38EAMRNkYkMqYBJvPX8abCa9PN0x3w02H8Oa6/0bcZICJU+U95VumPqseg== +roughjs@4.6.4: + version "4.6.4" + resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.4.tgz#b6f39b44645854a6e0a4a28b078368701eb7f939" + integrity sha512-s6EZ0BntezkFYMf/9mGn7M8XGIoaav9QQBCnJROWB3brUWQ683Q2LbRD/hq0Z3bAJ/9NVpU/5LpiTWvQMyLDhw== dependencies: + hachure-fill "^0.5.2" path-data-parser "^0.1.0" points-on-curve "^0.2.0" points-on-path "^0.2.1" From afea0df1417b38030d1276a092f3908bfaeb896c Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 20 Oct 2023 17:45:37 +0200 Subject: [PATCH 043/144] feat: renderer tweaks (#6698) --- src/constants.ts | 12 +- src/data/__snapshots__/transform.test.ts.snap | 180 +-- src/element/textWysiwyg.test.tsx | 4 +- src/scene/Shape.ts | 18 +- .../__snapshots__/contextmenu.test.tsx.snap | 504 +++--- .../__snapshots__/dragCreate.test.tsx.snap | 20 +- src/tests/__snapshots__/move.test.tsx.snap | 24 +- .../multiPointCreate.test.tsx.snap | 8 +- .../regressionTests.test.tsx.snap | 1372 ++++++++--------- .../__snapshots__/selection.test.tsx.snap | 20 +- .../data/__snapshots__/restore.test.ts.snap | 24 +- .../packages/__snapshots__/utils.test.ts.snap | 4 +- src/tests/queries/dom.ts | 2 +- .../scene/__snapshots__/export.test.ts.snap | 10 +- 14 files changed, 1075 insertions(+), 1127 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 90fc667581..91c59615e1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -296,6 +296,12 @@ export const ROUNDNESS = { * collaboration */ export const PRECEDING_ELEMENT_KEY = "__precedingElement__"; +export const ROUGHNESS = { + architect: 0, + artist: 1, + cartoonist: 2, +} as const; + export const DEFAULT_ELEMENT_PROPS: { strokeColor: ExcalidrawElement["strokeColor"]; backgroundColor: ExcalidrawElement["backgroundColor"]; @@ -308,10 +314,10 @@ export const DEFAULT_ELEMENT_PROPS: { } = { strokeColor: COLOR_PALETTE.black, backgroundColor: COLOR_PALETTE.transparent, - fillStyle: "hachure", - strokeWidth: 1, + fillStyle: "solid", + strokeWidth: 2, strokeStyle: "solid", - roughness: 1, + roughness: ROUGHNESS.artist, opacity: 100, locked: false, }; diff --git a/src/data/__snapshots__/transform.test.ts.snap b/src/data/__snapshots__/transform.test.ts.snap index 24503a9484..0b3a33cdbb 100644 --- a/src/data/__snapshots__/transform.test.ts.snap +++ b/src/data/__snapshots__/transform.test.ts.snap @@ -14,7 +14,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 300, @@ -28,7 +28,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "seed": Any, "strokeColor": "#66a80f", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 3, @@ -49,7 +49,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -63,7 +63,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "seed": Any, "strokeColor": "#9c36b5", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -85,7 +85,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "focus": -0.008153707962747813, "gap": 1, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 35, @@ -116,7 +116,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s }, "strokeColor": "#1864ab", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -138,7 +138,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "focus": 0.10666666666666667, "gap": 3.834326468444573, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -169,7 +169,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s }, "strokeColor": "#e67700", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -190,7 +190,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 300, @@ -204,7 +204,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -227,7 +227,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t }, ], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -245,7 +245,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t "seed": Any, "strokeColor": "#c2255c", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HEYYYYY", "textAlign": "left", "type": "text", @@ -271,7 +271,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t }, ], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -289,7 +289,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "Whats up ?", "textAlign": "left", "type": "text", @@ -319,7 +319,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t "focus": 0, "gap": 5, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -350,7 +350,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -368,7 +368,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t "baseline": 0, "boundElements": null, "containerId": "id43", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -386,7 +386,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HELLO WORLD!!", "textAlign": "center", "type": "text", @@ -416,7 +416,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "focus": 0, "gap": 1, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -447,7 +447,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -465,7 +465,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "baseline": 0, "boundElements": null, "containerId": "id32", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -483,7 +483,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HELLO WORLD!!", "textAlign": "center", "type": "text", @@ -507,7 +507,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -521,7 +521,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -542,7 +542,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -556,7 +556,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -583,7 +583,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when "focus": 0, "gap": 1, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -614,7 +614,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -632,7 +632,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when "baseline": 0, "boundElements": null, "containerId": "id36", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -650,7 +650,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HELLO WORLD!!", "textAlign": "center", "type": "text", @@ -676,7 +676,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when }, ], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -694,7 +694,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HEYYYYY", "textAlign": "left", "type": "text", @@ -720,7 +720,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when }, ], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -738,7 +738,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "WHATS UP ?", "textAlign": "left", "type": "text", @@ -757,7 +757,7 @@ exports[`Test Transform > should not allow duplicate ids 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -771,7 +771,7 @@ exports[`Test Transform > should not allow duplicate ids 1`] = ` "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -789,7 +789,7 @@ exports[`Test Transform > should transform linear elements 1`] = ` "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -816,7 +816,7 @@ exports[`Test Transform > should transform linear elements 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 1, @@ -834,7 +834,7 @@ exports[`Test Transform > should transform linear elements 2`] = ` "boundElements": null, "endArrowhead": "triangle", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -879,7 +879,7 @@ exports[`Test Transform > should transform linear elements 3`] = ` "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -906,7 +906,7 @@ exports[`Test Transform > should transform linear elements 3`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 1, @@ -924,7 +924,7 @@ exports[`Test Transform > should transform linear elements 4`] = ` "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -967,7 +967,7 @@ exports[`Test Transform > should transform regular shapes 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -981,7 +981,7 @@ exports[`Test Transform > should transform regular shapes 1`] = ` "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -997,7 +997,7 @@ exports[`Test Transform > should transform regular shapes 2`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -1011,7 +1011,7 @@ exports[`Test Transform > should transform regular shapes 2`] = ` "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 1, @@ -1027,7 +1027,7 @@ exports[`Test Transform > should transform regular shapes 3`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -1041,7 +1041,7 @@ exports[`Test Transform > should transform regular shapes 3`] = ` "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 1, @@ -1057,7 +1057,7 @@ exports[`Test Transform > should transform regular shapes 4`] = ` "angle": 0, "backgroundColor": "#c0eb75", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -1149,7 +1149,7 @@ exports[`Test Transform > should transform text element 1`] = ` "baseline": 0, "boundElements": null, "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1167,7 +1167,7 @@ exports[`Test Transform > should transform text element 1`] = ` "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "HELLO WORLD!", "textAlign": "left", "type": "text", @@ -1188,7 +1188,7 @@ exports[`Test Transform > should transform text element 2`] = ` "baseline": 0, "boundElements": null, "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1206,7 +1206,7 @@ exports[`Test Transform > should transform text element 2`] = ` "seed": Any, "strokeColor": "#5f3dc4", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "STYLED HELLO WORLD!", "textAlign": "left", "type": "text", @@ -1232,7 +1232,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide ], "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -1259,7 +1259,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 1, @@ -1282,7 +1282,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide ], "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -1309,7 +1309,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 1, @@ -1332,7 +1332,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide ], "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 130, @@ -1382,7 +1382,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide ], "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 130, @@ -1427,7 +1427,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "baseline": 0, "boundElements": null, "containerId": "id24", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1445,7 +1445,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "LABELED ARROW", "textAlign": "center", "type": "text", @@ -1466,7 +1466,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "baseline": 0, "boundElements": null, "containerId": "id25", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1484,7 +1484,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "seed": Any, "strokeColor": "#099268", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "STYLED LABELED ARROW", "textAlign": "center", "type": "text", @@ -1505,7 +1505,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "baseline": 0, "boundElements": null, "containerId": "id26", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1523,7 +1523,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "seed": Any, "strokeColor": "#1098ad", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "ANOTHER STYLED LABELLED ARROW", "textAlign": "center", @@ -1545,7 +1545,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "baseline": 0, "boundElements": null, "containerId": "id27", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1563,7 +1563,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide "seed": Any, "strokeColor": "#099268", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "ANOTHER STYLED LABELLED ARROW", "textAlign": "center", @@ -1588,7 +1588,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 35, @@ -1602,7 +1602,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1623,7 +1623,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 85, @@ -1637,7 +1637,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -1658,7 +1658,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 170, @@ -1672,7 +1672,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -1693,7 +1693,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 120, @@ -1728,7 +1728,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 85, @@ -1742,7 +1742,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#c2255c", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1763,7 +1763,7 @@ exports[`Test Transform > should transform to text containers when label provide "type": "text", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 120, @@ -1777,7 +1777,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#f08c00", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -1795,7 +1795,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id12", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1813,7 +1813,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "RECTANGLE TEXT CONTAINER", "textAlign": "center", "type": "text", @@ -1834,7 +1834,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id13", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1852,7 +1852,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "ELLIPSE TEXT CONTAINER", "textAlign": "center", @@ -1874,7 +1874,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id14", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1893,7 +1893,7 @@ TEXT CONTAINER", "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "DIAMOND TEXT CONTAINER", @@ -1916,7 +1916,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id15", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1934,7 +1934,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#099268", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "STYLED DIAMOND TEXT CONTAINER", "textAlign": "center", @@ -1956,7 +1956,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id16", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -1974,7 +1974,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#c2255c", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "TOP LEFT ALIGNED RECTANGLE TEXT CONTAINER", @@ -1997,7 +1997,7 @@ exports[`Test Transform > should transform to text containers when label provide "baseline": 0, "boundElements": null, "containerId": "id17", - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 20, "frameId": null, @@ -2015,7 +2015,7 @@ exports[`Test Transform > should transform to text containers when label provide "seed": Any, "strokeColor": "#c2255c", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "STYLED ELLIPSE TEXT CONTAINER", diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index e02fa9937c..45d4b83d81 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -1425,7 +1425,7 @@ describe("textWysiwyg", () => { type: "text", }, ], - fillStyle: "hachure", + fillStyle: "solid", groupIds: [], height: 35, isDeleted: false, @@ -1438,7 +1438,7 @@ describe("textWysiwyg", () => { }, strokeColor: "#1e1e1e", strokeStyle: "solid", - strokeWidth: 1, + strokeWidth: 2, type: "rectangle", updated: 1, version: 1, diff --git a/src/scene/Shape.ts b/src/scene/Shape.ts index 5a2f68bbf0..16b6518f8e 100644 --- a/src/scene/Shape.ts +++ b/src/scene/Shape.ts @@ -13,11 +13,21 @@ import { isPathALoop, getCornerRadius } from "../math"; import { generateFreeDrawShape } from "../renderer/renderElement"; import { isTransparent, assertNever } from "../utils"; import { simplify } from "points-on-curve"; +import { ROUGHNESS } from "../constants"; const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth]; const getDashArrayDotted = (strokeWidth: number) => [1.5, 6 + strokeWidth]; +function adjustRoughness(size: number, roughness: number): number { + if (size >= 50) { + return roughness; + } + const factor = 2 + (50 - size) / 10; + + return roughness / factor; +} + export const generateRoughOptions = ( element: ExcalidrawElement, continuousPath = false, @@ -44,9 +54,13 @@ export const generateRoughOptions = ( // calculate them (and we don't want the fills to be modified) fillWeight: element.strokeWidth / 2, hachureGap: element.strokeWidth * 4, - roughness: element.roughness, + roughness: adjustRoughness( + Math.min(element.width, element.height), + element.roughness, + ), stroke: element.strokeColor, - preserveVertices: continuousPath, + preserveVertices: + continuousPath || element.roughness < ROUGHNESS.cartoonist, }; switch (element.type) { diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 0cdd20b25e..e141c01124 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -290,7 +290,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -299,7 +299,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -406,7 +406,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -440,7 +440,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -489,7 +489,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -498,7 +498,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -583,7 +583,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -599,7 +599,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -642,7 +642,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -658,7 +658,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -691,7 +691,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -700,7 +700,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -783,7 +783,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -799,7 +799,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -815,7 +815,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -831,7 +831,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -874,7 +874,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -890,7 +890,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -917,7 +917,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -933,7 +933,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -946,7 +946,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -962,7 +962,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -989,7 +989,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1005,7 +1005,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1018,7 +1018,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1034,7 +1034,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1067,7 +1067,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1076,7 +1076,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1159,7 +1159,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1175,7 +1175,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1191,7 +1191,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1207,7 +1207,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1250,7 +1250,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1266,7 +1266,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1293,7 +1293,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1309,7 +1309,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1322,7 +1322,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1338,7 +1338,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1365,7 +1365,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1381,7 +1381,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1394,7 +1394,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1410,7 +1410,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1443,7 +1443,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1452,7 +1452,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1537,7 +1537,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1553,7 +1553,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1596,7 +1596,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1612,7 +1612,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1645,7 +1645,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1654,7 +1654,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1735,7 +1735,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1751,7 +1751,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1794,7 +1794,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1810,7 +1810,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1835,7 +1835,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1851,7 +1851,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1884,7 +1884,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1893,7 +1893,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1976,7 +1976,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -1992,7 +1992,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2008,7 +2008,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2024,7 +2024,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2067,7 +2067,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2083,7 +2083,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2110,7 +2110,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2126,7 +2126,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2139,7 +2139,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2155,7 +2155,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2188,7 +2188,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -2197,7 +2197,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -2285,7 +2285,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -2303,7 +2303,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2319,7 +2319,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -2337,7 +2337,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2380,7 +2380,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2396,7 +2396,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2423,7 +2423,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2439,7 +2439,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2452,7 +2452,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2468,7 +2468,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2498,7 +2498,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -2516,7 +2516,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2529,7 +2529,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -2547,7 +2547,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2694,7 +2694,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "type": "rectangle", "updated": 1, "version": 3, - "versionNonce": 406373543, + "versionNonce": 1898319239, "width": 20, "x": -10, "y": 0, @@ -2719,14 +2719,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roundness": { "type": 3, }, - "seed": 1006504105, + "seed": 1723083209, "strokeColor": "#e03131", "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 9, - "versionNonce": 1898319239, + "version": 8, + "versionNonce": 289600103, "width": 20, "x": 20, "y": 30, @@ -2765,7 +2765,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2781,7 +2781,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2808,7 +2808,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2824,7 +2824,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2837,7 +2837,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2853,7 +2853,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2880,7 +2880,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2896,7 +2896,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2909,7 +2909,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2925,7 +2925,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 1150084233, "strokeColor": "#e03131", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2952,7 +2952,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2968,7 +2968,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2981,7 +2981,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "#a5d8ff", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -2997,7 +2997,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 1150084233, "strokeColor": "#e03131", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -3024,7 +3024,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3040,7 +3040,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3069,7 +3069,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 1150084233, "strokeColor": "#e03131", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, @@ -3096,7 +3096,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3112,7 +3112,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3140,7 +3140,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s }, "seed": 1150084233, "strokeColor": "#e03131", - "strokeStyle": "solid", + "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, @@ -3168,7 +3168,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3184,81 +3184,9 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, - "type": "rectangle", - "updated": 1, - "version": 2, - "versionNonce": 401146281, - "width": 20, - "x": -10, - "y": 0, - }, - { - "angle": 0, - "backgroundColor": "#a5d8ff", - "boundElements": null, - "fillStyle": "cross-hatch", - "frameId": null, - "groupIds": [], - "height": 20, - "id": "id1", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": { - "type": 3, - }, - "seed": 1150084233, - "strokeColor": "#e03131", - "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 7, - "versionNonce": 1723083209, - "width": 20, - "x": 20, - "y": 30, - }, - ], - }, - { - "appState": { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": { - "id1": true, - }, - "selectedGroupIds": {}, - "viewBackgroundColor": "#ffffff", - }, - "elements": [ - { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "fillStyle": "hachure", - "frameId": null, - "groupIds": [], - "height": 20, - "id": "id0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": { - "type": 3, - }, - "seed": 449462985, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 1, - "type": "rectangle", - "updated": 1, "version": 2, "versionNonce": 401146281, "width": 20, @@ -3282,14 +3210,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roundness": { "type": 3, }, - "seed": 1006504105, + "seed": 1723083209, "strokeColor": "#e03131", "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 8, - "versionNonce": 289600103, + "version": 7, + "versionNonce": 760410951, "width": 20, "x": 20, "y": 30, @@ -3312,7 +3240,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3328,7 +3256,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3354,14 +3282,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roundness": { "type": 3, }, - "seed": 1006504105, + "seed": 1723083209, "strokeColor": "#e03131", "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 9, - "versionNonce": 1898319239, + "version": 8, + "versionNonce": 289600103, "width": 20, "x": 20, "y": 30, @@ -3404,7 +3332,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "type": "rectangle", "updated": 1, "version": 3, - "versionNonce": 406373543, + "versionNonce": 1898319239, "width": 20, "x": -10, "y": 0, @@ -3426,14 +3354,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roundness": { "type": 3, }, - "seed": 1006504105, + "seed": 1723083209, "strokeColor": "#e03131", "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 9, - "versionNonce": 1898319239, + "version": 8, + "versionNonce": 289600103, "width": 20, "x": 20, "y": 30, @@ -3446,7 +3374,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of elements 1`] = `2`; -exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of renders 1`] = `19`; +exports[`contextMenu element > selecting 'Paste styles' in context menu pastes styles > [end of test] number of renders 1`] = `18`; exports[`contextMenu element > selecting 'Send backward' in context menu sends element backward > [end of test] appState 1`] = ` { @@ -3462,7 +3390,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -3471,7 +3399,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -3554,7 +3482,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3570,7 +3498,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3586,7 +3514,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3602,7 +3530,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3645,7 +3573,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3661,7 +3589,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3688,7 +3616,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3704,7 +3632,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3717,7 +3645,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3733,7 +3661,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3760,7 +3688,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3776,7 +3704,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3789,7 +3717,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3805,7 +3733,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3838,7 +3766,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -3847,7 +3775,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -3930,7 +3858,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3946,7 +3874,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3962,7 +3890,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -3978,7 +3906,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4021,7 +3949,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4037,7 +3965,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4064,7 +3992,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4080,7 +4008,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4093,7 +4021,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4109,7 +4037,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4136,7 +4064,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4152,7 +4080,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4165,7 +4093,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4181,7 +4109,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4214,7 +4142,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -4223,7 +4151,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -4309,7 +4237,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4325,7 +4253,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -4341,7 +4269,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4357,7 +4285,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -4400,7 +4328,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4416,7 +4344,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4443,7 +4371,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4459,7 +4387,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4472,7 +4400,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4488,7 +4416,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4518,7 +4446,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -4536,7 +4464,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4549,7 +4477,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -4567,7 +4495,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4595,7 +4523,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4611,7 +4539,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -4624,7 +4552,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -4640,7 +4568,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -4949,7 +4877,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -4958,7 +4886,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -5044,7 +4972,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5060,7 +4988,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5076,7 +5004,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5092,7 +5020,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5135,7 +5063,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5151,7 +5079,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5178,7 +5106,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5194,7 +5122,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5207,7 +5135,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5223,7 +5151,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5532,7 +5460,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -5541,7 +5469,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -5629,7 +5557,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5647,7 +5575,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -5663,7 +5591,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5681,7 +5609,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -5724,7 +5652,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5740,7 +5668,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5767,7 +5695,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5783,7 +5711,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5796,7 +5724,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5812,7 +5740,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5842,7 +5770,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5860,7 +5788,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 453191, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -5873,7 +5801,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5891,7 +5819,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -6052,7 +5980,7 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6061,7 +5989,7 @@ exports[`contextMenu element > shows context menu for canvas > [end of test] app "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6454,7 +6382,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6463,7 +6391,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6831,7 +6759,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6840,7 +6768,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6926,7 +6854,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -6942,7 +6870,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6958,7 +6886,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -6974,7 +6902,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -6990,7 +6918,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -7006,7 +6934,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -7049,7 +6977,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] hi "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -7065,7 +6993,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] hi "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, diff --git a/src/tests/__snapshots__/dragCreate.test.tsx.snap b/src/tests/__snapshots__/dragCreate.test.tsx.snap index 34c994da51..5c986f44b8 100644 --- a/src/tests/__snapshots__/dragCreate.test.tsx.snap +++ b/src/tests/__snapshots__/dragCreate.test.tsx.snap @@ -9,7 +9,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -38,7 +38,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -56,7 +56,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -72,7 +72,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -90,7 +90,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -106,7 +106,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -124,7 +124,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -153,7 +153,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -171,7 +171,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -187,7 +187,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, diff --git a/src/tests/__snapshots__/move.test.tsx.snap b/src/tests/__snapshots__/move.test.tsx.snap index 092074ba35..015ff7c4c9 100644 --- a/src/tests/__snapshots__/move.test.tsx.snap +++ b/src/tests/__snapshots__/move.test.tsx.snap @@ -5,7 +5,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -21,7 +21,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 1`] = ` "seed": 1014066025, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -37,7 +37,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 2`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -53,7 +53,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 2`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -69,7 +69,7 @@ exports[`move element > rectangle 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -85,7 +85,7 @@ exports[`move element > rectangle 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -106,7 +106,7 @@ exports[`move element > rectangles with binding arrow 1`] = ` "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -122,7 +122,7 @@ exports[`move element > rectangles with binding arrow 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -143,7 +143,7 @@ exports[`move element > rectangles with binding arrow 2`] = ` "type": "arrow", }, ], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 300, @@ -159,7 +159,7 @@ exports[`move element > rectangles with binding arrow 2`] = ` "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 6, @@ -181,7 +181,7 @@ exports[`move element > rectangles with binding arrow 3`] = ` "focus": -0.46666666666666673, "gap": 10, }, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 81.48231043525051, @@ -214,7 +214,7 @@ exports[`move element > rectangles with binding arrow 3`] = ` }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 11, diff --git a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap index 1f488b4d19..48ae1f6401 100644 --- a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap +++ b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap @@ -7,7 +7,7 @@ exports[`multi point mode in linear elements > arrow 1`] = ` "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 110, @@ -43,7 +43,7 @@ exports[`multi point mode in linear elements > arrow 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -61,7 +61,7 @@ exports[`multi point mode in linear elements > line 1`] = ` "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 110, @@ -97,7 +97,7 @@ exports[`multi point mode in linear elements > line 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 7, diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index c990a6e038..4d99470d3c 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -14,7 +14,7 @@ exports[`given element A and group of elements B and given both are selected whe "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -23,7 +23,7 @@ exports[`given element A and group of elements B and given both are selected whe "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -140,7 +140,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -156,7 +156,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -183,7 +183,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -199,7 +199,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -212,7 +212,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -228,7 +228,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -255,7 +255,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -271,7 +271,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -284,7 +284,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -300,7 +300,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -313,7 +313,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -329,7 +329,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -359,7 +359,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -375,7 +375,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -388,7 +388,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -406,7 +406,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -419,7 +419,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -437,7 +437,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -470,7 +470,7 @@ exports[`given element A and group of elements B and given both are selected whe "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -479,7 +479,7 @@ exports[`given element A and group of elements B and given both are selected whe "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -598,7 +598,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -614,7 +614,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -641,7 +641,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -657,7 +657,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -670,7 +670,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -686,7 +686,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -713,7 +713,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -729,7 +729,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -742,7 +742,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -758,7 +758,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -771,7 +771,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -787,7 +787,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -817,7 +817,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -833,7 +833,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -846,7 +846,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -864,7 +864,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -877,7 +877,7 @@ exports[`given element A and group of elements B and given both are selected whe "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -895,7 +895,7 @@ exports[`given element A and group of elements B and given both are selected whe "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -928,7 +928,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -937,7 +937,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1047,7 +1047,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1063,7 +1063,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1090,7 +1090,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1106,7 +1106,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1119,7 +1119,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1135,7 +1135,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1165,7 +1165,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1183,7 +1183,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1196,7 +1196,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1214,7 +1214,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1241,7 +1241,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1259,7 +1259,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1272,7 +1272,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1290,7 +1290,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1317,7 +1317,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1335,7 +1335,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1348,7 +1348,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1366,7 +1366,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1379,7 +1379,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1395,7 +1395,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 81784553, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -1426,7 +1426,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1445,7 +1445,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1458,7 +1458,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1477,7 +1477,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1490,7 +1490,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id10", @@ -1508,7 +1508,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 81784553, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1535,7 +1535,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1554,7 +1554,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1567,7 +1567,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1586,7 +1586,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1599,7 +1599,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id10", @@ -1617,7 +1617,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 81784553, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1644,7 +1644,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1663,7 +1663,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1676,7 +1676,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -1695,7 +1695,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -1708,7 +1708,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id10", @@ -1726,7 +1726,7 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "seed": 81784553, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -1759,7 +1759,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1768,7 +1768,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -1880,7 +1880,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1896,7 +1896,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -1923,7 +1923,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -1939,7 +1939,7 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 3, @@ -1972,7 +1972,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] appSta "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -1981,7 +1981,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] appSta "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -2096,7 +2096,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2112,7 +2112,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2139,7 +2139,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2155,7 +2155,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2168,7 +2168,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2184,7 +2184,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2211,7 +2211,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2227,7 +2227,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2240,7 +2240,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2256,7 +2256,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2269,7 +2269,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2285,7 +2285,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2315,7 +2315,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2331,7 +2331,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2344,7 +2344,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -2362,7 +2362,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2375,7 +2375,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -2393,7 +2393,7 @@ exports[`regression tests > adjusts z order when grouping > [end of test] histor "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2426,7 +2426,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] appSt "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -2435,7 +2435,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] appSt "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -2547,7 +2547,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2563,7 +2563,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2590,7 +2590,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2606,7 +2606,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "seed": 1014066025, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -2619,7 +2619,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2635,7 +2635,7 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] histo "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -2668,7 +2668,7 @@ exports[`regression tests > arrow keys > [end of test] appState 1`] = ` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -2677,7 +2677,7 @@ exports[`regression tests > arrow keys > [end of test] appState 1`] = ` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -2787,7 +2787,7 @@ exports[`regression tests > arrow keys > [end of test] history 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -2803,7 +2803,7 @@ exports[`regression tests > arrow keys > [end of test] history 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -2836,7 +2836,7 @@ exports[`regression tests > can drag element that covers another element, while "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -2845,7 +2845,7 @@ exports[`regression tests > can drag element that covers another element, while "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -2957,7 +2957,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -2973,7 +2973,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3000,7 +3000,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3016,7 +3016,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3029,7 +3029,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3045,7 +3045,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3072,7 +3072,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3088,7 +3088,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3101,7 +3101,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3117,7 +3117,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3130,7 +3130,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 350, @@ -3146,7 +3146,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -3173,7 +3173,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3189,7 +3189,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3202,7 +3202,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 200, @@ -3218,7 +3218,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3231,7 +3231,7 @@ exports[`regression tests > can drag element that covers another element, while "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 350, @@ -3247,7 +3247,7 @@ exports[`regression tests > can drag element that covers another element, while "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -3280,7 +3280,7 @@ exports[`regression tests > change the properties of a shape > [end of test] app "currentChartType": "bar", "currentItemBackgroundColor": "#ffc9c9", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -3289,7 +3289,7 @@ exports[`regression tests > change the properties of a shape > [end of test] app "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1971c2", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -3399,7 +3399,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3415,7 +3415,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3442,7 +3442,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "angle": 0, "backgroundColor": "#ffec99", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3458,7 +3458,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3485,7 +3485,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3501,7 +3501,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -3528,7 +3528,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3544,7 +3544,7 @@ exports[`regression tests > change the properties of a shape > [end of test] his "seed": 1278240551, "strokeColor": "#1971c2", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, @@ -3577,7 +3577,7 @@ exports[`regression tests > click on an element and drag it > [dragged] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -3586,7 +3586,7 @@ exports[`regression tests > click on an element and drag it > [dragged] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -3671,7 +3671,7 @@ exports[`regression tests > click on an element and drag it > [dragged] element "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3687,7 +3687,7 @@ exports[`regression tests > click on an element and drag it > [dragged] element "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3730,7 +3730,7 @@ exports[`regression tests > click on an element and drag it > [dragged] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3746,7 +3746,7 @@ exports[`regression tests > click on an element and drag it > [dragged] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3773,7 +3773,7 @@ exports[`regression tests > click on an element and drag it > [dragged] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3789,7 +3789,7 @@ exports[`regression tests > click on an element and drag it > [dragged] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -3822,7 +3822,7 @@ exports[`regression tests > click on an element and drag it > [end of test] appS "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -3831,7 +3831,7 @@ exports[`regression tests > click on an element and drag it > [end of test] appS "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -3943,7 +3943,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -3959,7 +3959,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -3986,7 +3986,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4002,7 +4002,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4029,7 +4029,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4045,7 +4045,7 @@ exports[`regression tests > click on an element and drag it > [end of test] hist "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -4078,7 +4078,7 @@ exports[`regression tests > click to select a shape > [end of test] appState 1`] "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -4087,7 +4087,7 @@ exports[`regression tests > click to select a shape > [end of test] appState 1`] "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -4199,7 +4199,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4215,7 +4215,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4242,7 +4242,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4258,7 +4258,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4271,7 +4271,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4287,7 +4287,7 @@ exports[`regression tests > click to select a shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4320,7 +4320,7 @@ exports[`regression tests > click-drag to select a group > [end of test] appStat "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -4329,7 +4329,7 @@ exports[`regression tests > click-drag to select a group > [end of test] appStat "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -4442,7 +4442,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4458,7 +4458,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4485,7 +4485,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4501,7 +4501,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4514,7 +4514,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4530,7 +4530,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4557,7 +4557,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4573,7 +4573,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4586,7 +4586,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4602,7 +4602,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4615,7 +4615,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4631,7 +4631,7 @@ exports[`regression tests > click-drag to select a group > [end of test] history "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4664,7 +4664,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -4673,7 +4673,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -4785,7 +4785,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4801,7 +4801,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4828,7 +4828,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4844,7 +4844,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4857,7 +4857,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -4873,7 +4873,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -4903,7 +4903,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -4921,7 +4921,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4934,7 +4934,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -4952,7 +4952,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -4979,7 +4979,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -4997,7 +4997,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -5010,7 +5010,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5028,7 +5028,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -5057,7 +5057,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5075,7 +5075,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -5088,7 +5088,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -5106,7 +5106,7 @@ exports[`regression tests > deleting last but one element in editing group shoul "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -5139,7 +5139,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -5148,7 +5148,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -5156,7 +5156,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -5172,7 +5172,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "seed": 400692809, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -5239,7 +5239,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -5255,7 +5255,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "seed": 400692809, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -5315,7 +5315,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5331,7 +5331,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5358,7 +5358,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5374,7 +5374,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5387,7 +5387,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5403,7 +5403,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -5436,7 +5436,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -5445,7 +5445,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -5453,7 +5453,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -5469,7 +5469,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "seed": 400692809, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -5584,7 +5584,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5600,7 +5600,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5627,7 +5627,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5643,7 +5643,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5656,7 +5656,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5672,7 +5672,7 @@ exports[`regression tests > deselects group of selected elements on pointer up w "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -5705,7 +5705,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -5714,7 +5714,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -5722,7 +5722,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -5738,7 +5738,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -5804,7 +5804,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -5820,7 +5820,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -5880,7 +5880,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -5896,7 +5896,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -5929,7 +5929,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -5938,7 +5938,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6048,7 +6048,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -6064,7 +6064,7 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -6097,7 +6097,7 @@ exports[`regression tests > double click to edit a group > [end of test] appStat "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6106,7 +6106,7 @@ exports[`regression tests > double click to edit a group > [end of test] appStat "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6216,7 +6216,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6232,7 +6232,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6259,7 +6259,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6275,7 +6275,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6288,7 +6288,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6304,7 +6304,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6331,7 +6331,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6347,7 +6347,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6360,7 +6360,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6376,7 +6376,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6389,7 +6389,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6405,7 +6405,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6436,7 +6436,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -6454,7 +6454,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -6467,7 +6467,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -6485,7 +6485,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -6498,7 +6498,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -6516,7 +6516,7 @@ exports[`regression tests > double click to edit a group > [end of test] history "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -6549,7 +6549,7 @@ exports[`regression tests > drags selected elements from point inside common bou "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6558,7 +6558,7 @@ exports[`regression tests > drags selected elements from point inside common bou "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6672,7 +6672,7 @@ exports[`regression tests > drags selected elements from point inside common bou "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6688,7 +6688,7 @@ exports[`regression tests > drags selected elements from point inside common bou "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6715,7 +6715,7 @@ exports[`regression tests > drags selected elements from point inside common bou "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6731,7 +6731,7 @@ exports[`regression tests > drags selected elements from point inside common bou "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -6744,7 +6744,7 @@ exports[`regression tests > drags selected elements from point inside common bou "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6760,7 +6760,7 @@ exports[`regression tests > drags selected elements from point inside common bou "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -6788,7 +6788,7 @@ exports[`regression tests > drags selected elements from point inside common bou "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6804,7 +6804,7 @@ exports[`regression tests > drags selected elements from point inside common bou "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -6817,7 +6817,7 @@ exports[`regression tests > drags selected elements from point inside common bou "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6833,7 +6833,7 @@ exports[`regression tests > drags selected elements from point inside common bou "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 3, @@ -6866,7 +6866,7 @@ exports[`regression tests > draw every type of shape > [end of test] appState 1` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -6875,7 +6875,7 @@ exports[`regression tests > draw every type of shape > [end of test] appState 1` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -6983,7 +6983,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -6999,7 +6999,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7026,7 +7026,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7042,7 +7042,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7055,7 +7055,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7071,7 +7071,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7098,7 +7098,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7114,7 +7114,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7127,7 +7127,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7143,7 +7143,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7156,7 +7156,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7172,7 +7172,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -7199,7 +7199,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7215,7 +7215,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7228,7 +7228,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7244,7 +7244,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7257,7 +7257,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7273,7 +7273,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -7288,7 +7288,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7317,7 +7317,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -7344,7 +7344,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7360,7 +7360,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7373,7 +7373,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7389,7 +7389,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7402,7 +7402,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7418,7 +7418,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -7433,7 +7433,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7462,7 +7462,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -7477,7 +7477,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7506,7 +7506,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -7533,7 +7533,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7549,7 +7549,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7562,7 +7562,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7578,7 +7578,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7591,7 +7591,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7607,7 +7607,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -7622,7 +7622,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7651,7 +7651,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -7666,7 +7666,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7695,7 +7695,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -7710,7 +7710,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7742,7 +7742,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 5, @@ -7769,7 +7769,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7785,7 +7785,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -7798,7 +7798,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7814,7 +7814,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -7827,7 +7827,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7843,7 +7843,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -7858,7 +7858,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7887,7 +7887,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -7902,7 +7902,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -7931,7 +7931,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -7946,7 +7946,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -7982,7 +7982,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -8009,7 +8009,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8025,7 +8025,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -8038,7 +8038,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8054,7 +8054,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -8067,7 +8067,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8083,7 +8083,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -8098,7 +8098,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8127,7 +8127,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -8142,7 +8142,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8171,7 +8171,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -8186,7 +8186,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -8222,7 +8222,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -8237,7 +8237,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8269,7 +8269,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 5, @@ -8296,7 +8296,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8312,7 +8312,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -8325,7 +8325,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8341,7 +8341,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -8354,7 +8354,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8370,7 +8370,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -8385,7 +8385,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8414,7 +8414,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -8429,7 +8429,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8458,7 +8458,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -8473,7 +8473,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -8509,7 +8509,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -8524,7 +8524,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -8560,7 +8560,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 7, @@ -8585,7 +8585,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8601,7 +8601,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -8614,7 +8614,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8630,7 +8630,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -8643,7 +8643,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8659,7 +8659,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -8674,7 +8674,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8703,7 +8703,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -8718,7 +8718,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8747,7 +8747,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -8762,7 +8762,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -8798,7 +8798,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -8813,7 +8813,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -8849,7 +8849,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 7, @@ -8862,7 +8862,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -8900,7 +8900,7 @@ exports[`regression tests > draw every type of shape > [end of test] history 1`] "simulatePressure": false, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "freedraw", "updated": 1, "version": 4, @@ -8933,7 +8933,7 @@ exports[`regression tests > given a group of selected elements with an element t "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -8942,7 +8942,7 @@ exports[`regression tests > given a group of selected elements with an element t "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -9055,7 +9055,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -9071,7 +9071,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9098,7 +9098,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -9114,7 +9114,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9127,7 +9127,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -9143,7 +9143,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -9170,7 +9170,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -9186,7 +9186,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9199,7 +9199,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -9215,7 +9215,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -9228,7 +9228,7 @@ exports[`regression tests > given a group of selected elements with an element t "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -9244,7 +9244,7 @@ exports[`regression tests > given a group of selected elements with an element t "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -9277,7 +9277,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "currentChartType": "bar", "currentItemBackgroundColor": "#ffc9c9", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -9286,7 +9286,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -9399,7 +9399,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9415,7 +9415,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9442,7 +9442,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9458,7 +9458,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9471,7 +9471,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9487,7 +9487,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "seed": 1150084233, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -9520,7 +9520,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -9529,7 +9529,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -9641,7 +9641,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9657,7 +9657,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -9670,7 +9670,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 500, @@ -9686,7 +9686,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -9719,7 +9719,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -9728,7 +9728,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -9840,7 +9840,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9856,7 +9856,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -9869,7 +9869,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 500, @@ -9885,7 +9885,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -9912,7 +9912,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 1000, @@ -9928,7 +9928,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -9941,7 +9941,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "angle": 0, "backgroundColor": "red", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 500, @@ -9957,7 +9957,7 @@ exports[`regression tests > given selected element A with lower z-index than uns "seed": 449462985, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 1, @@ -9990,7 +9990,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] appStat "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -9999,7 +9999,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] appStat "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -10109,7 +10109,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -10125,7 +10125,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -10158,7 +10158,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -10167,7 +10167,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -10277,7 +10277,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] history 1 "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -10293,7 +10293,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] history 1 "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -10326,7 +10326,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -10335,7 +10335,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -10445,7 +10445,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] history 1 "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -10461,7 +10461,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] history 1 "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -10494,7 +10494,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -10503,7 +10503,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -10638,7 +10638,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -10667,7 +10667,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -10700,7 +10700,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -10709,7 +10709,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`] "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -10844,7 +10844,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -10873,7 +10873,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -10906,7 +10906,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -10915,7 +10915,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11021,7 +11021,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11059,7 +11059,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] history "simulatePressure": false, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "freedraw", "updated": 1, "version": 4, @@ -11092,7 +11092,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -11101,7 +11101,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11236,7 +11236,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] history 1`] "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11265,7 +11265,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -11298,7 +11298,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -11307,7 +11307,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11417,7 +11417,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] history 1 "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11433,7 +11433,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] history 1 "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -11466,7 +11466,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -11475,7 +11475,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`] "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11610,7 +11610,7 @@ exports[`regression tests > key l selects line tool > [end of test] history 1`] "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11639,7 +11639,7 @@ exports[`regression tests > key l selects line tool > [end of test] history 1`] "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -11672,7 +11672,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -11681,7 +11681,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11791,7 +11791,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] history 1 "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11807,7 +11807,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] history 1 "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -11840,7 +11840,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] appState "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -11849,7 +11849,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] appState "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -11955,7 +11955,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -11993,7 +11993,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] history "simulatePressure": false, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "freedraw", "updated": 1, "version": 4, @@ -12026,7 +12026,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] appStat "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -12035,7 +12035,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] appStat "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -12145,7 +12145,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12161,7 +12161,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12194,7 +12194,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] appSta "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -12203,7 +12203,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] appSta "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -12321,7 +12321,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12337,7 +12337,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12364,7 +12364,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12380,7 +12380,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12393,7 +12393,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12409,7 +12409,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12436,7 +12436,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12452,7 +12452,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12465,7 +12465,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12481,7 +12481,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12494,7 +12494,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12510,7 +12510,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -12541,7 +12541,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12559,7 +12559,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -12572,7 +12572,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12590,7 +12590,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -12603,7 +12603,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12621,7 +12621,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -12652,7 +12652,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4_copy", @@ -12670,7 +12670,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 941653321, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, @@ -12683,7 +12683,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4_copy", @@ -12701,7 +12701,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1402203177, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, @@ -12714,7 +12714,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4_copy", @@ -12732,7 +12732,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1349943049, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, @@ -12745,7 +12745,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12763,7 +12763,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -12776,7 +12776,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12794,7 +12794,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -12807,7 +12807,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -12825,7 +12825,7 @@ exports[`regression tests > make a group and duplicate it > [end of test] histor "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -12858,7 +12858,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -12867,7 +12867,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -12979,7 +12979,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -12995,7 +12995,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13022,7 +13022,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13038,7 +13038,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13051,7 +13051,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13067,7 +13067,7 @@ exports[`regression tests > noop interaction after undo shouldn't create history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13100,7 +13100,7 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -13109,7 +13109,7 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = ` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -13223,7 +13223,7 @@ exports[`regression tests > shift click on selected element should deselect it o "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -13232,7 +13232,7 @@ exports[`regression tests > shift click on selected element should deselect it o "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -13342,7 +13342,7 @@ exports[`regression tests > shift click on selected element should deselect it o "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13358,7 +13358,7 @@ exports[`regression tests > shift click on selected element should deselect it o "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13391,7 +13391,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -13400,7 +13400,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -13514,7 +13514,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13530,7 +13530,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13557,7 +13557,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13573,7 +13573,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13586,7 +13586,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13602,7 +13602,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13630,7 +13630,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13646,7 +13646,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -13659,7 +13659,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13675,7 +13675,7 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -13708,7 +13708,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -13717,7 +13717,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -13833,7 +13833,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13849,7 +13849,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13876,7 +13876,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13892,7 +13892,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13905,7 +13905,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13921,7 +13921,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13948,7 +13948,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13964,7 +13964,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -13977,7 +13977,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -13993,7 +13993,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14006,7 +14006,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14022,7 +14022,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14053,7 +14053,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14071,7 +14071,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14084,7 +14084,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14102,7 +14102,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14115,7 +14115,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14133,7 +14133,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14162,7 +14162,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14178,7 +14178,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -14191,7 +14191,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14207,7 +14207,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -14220,7 +14220,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14236,7 +14236,7 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -14269,7 +14269,7 @@ exports[`regression tests > should show fill icons when element has non transpar "currentChartType": "bar", "currentItemBackgroundColor": "#ffc9c9", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -14278,7 +14278,7 @@ exports[`regression tests > should show fill icons when element has non transpar "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -14388,7 +14388,7 @@ exports[`regression tests > should show fill icons when element has non transpar "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14404,7 +14404,7 @@ exports[`regression tests > should show fill icons when element has non transpar "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14431,7 +14431,7 @@ exports[`regression tests > should show fill icons when element has non transpar "angle": 0, "backgroundColor": "#ffc9c9", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14447,7 +14447,7 @@ exports[`regression tests > should show fill icons when element has non transpar "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14480,7 +14480,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -14489,7 +14489,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -14609,7 +14609,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14625,7 +14625,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14652,7 +14652,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14668,7 +14668,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14681,7 +14681,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14697,7 +14697,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14727,7 +14727,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14745,7 +14745,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14758,7 +14758,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14776,7 +14776,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14803,7 +14803,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14821,7 +14821,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14834,7 +14834,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14852,7 +14852,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14865,7 +14865,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14881,7 +14881,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14908,7 +14908,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14926,7 +14926,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14939,7 +14939,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -14957,7 +14957,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -14970,7 +14970,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -14986,7 +14986,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -14999,7 +14999,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -15015,7 +15015,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1723083209, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15045,7 +15045,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -15063,7 +15063,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15076,7 +15076,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -15094,7 +15094,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15107,7 +15107,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id9", @@ -15125,7 +15125,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15138,7 +15138,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id9", @@ -15156,7 +15156,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1723083209, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15188,7 +15188,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -15207,7 +15207,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -15220,7 +15220,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id4", @@ -15239,7 +15239,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -15252,7 +15252,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id9", @@ -15271,7 +15271,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -15284,7 +15284,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id9", @@ -15303,7 +15303,7 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "seed": 1723083209, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -15336,7 +15336,7 @@ exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] a "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -15345,7 +15345,7 @@ exports[`regression tests > spacebar + drag scrolls the canvas > [end of test] a "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -15462,7 +15462,7 @@ exports[`regression tests > supports nested groups > [end of test] appState 1`] "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -15471,7 +15471,7 @@ exports[`regression tests > supports nested groups > [end of test] appState 1`] "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -15583,7 +15583,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15599,7 +15599,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15626,7 +15626,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15642,7 +15642,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15655,7 +15655,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15671,7 +15671,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15698,7 +15698,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15714,7 +15714,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15727,7 +15727,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15743,7 +15743,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15756,7 +15756,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -15772,7 +15772,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -15803,7 +15803,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15821,7 +15821,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15834,7 +15834,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15852,7 +15852,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15865,7 +15865,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15883,7 +15883,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15911,7 +15911,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15929,7 +15929,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15942,7 +15942,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15960,7 +15960,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -15973,7 +15973,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -15991,7 +15991,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -16021,7 +16021,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -16039,7 +16039,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -16052,7 +16052,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -16071,7 +16071,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -16084,7 +16084,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -16103,7 +16103,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -16134,7 +16134,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id3", @@ -16152,7 +16152,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, @@ -16165,7 +16165,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -16184,7 +16184,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -16197,7 +16197,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [ "id5", @@ -16216,7 +16216,7 @@ exports[`regression tests > supports nested groups > [end of test] history 1`] = "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, @@ -16249,7 +16249,7 @@ exports[`regression tests > switches from group of selected elements to another "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -16258,7 +16258,7 @@ exports[`regression tests > switches from group of selected elements to another "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -16266,7 +16266,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -16282,7 +16282,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -16351,7 +16351,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -16367,7 +16367,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -16427,7 +16427,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16443,7 +16443,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -16470,7 +16470,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16486,7 +16486,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -16499,7 +16499,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -16515,7 +16515,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -16542,7 +16542,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16558,7 +16558,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -16571,7 +16571,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -16587,7 +16587,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -16600,7 +16600,7 @@ exports[`regression tests > switches from group of selected elements to another "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -16616,7 +16616,7 @@ exports[`regression tests > switches from group of selected elements to another "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -16649,7 +16649,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -16658,7 +16658,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -16666,7 +16666,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -16682,7 +16682,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -16750,7 +16750,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -16766,7 +16766,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "selection", "updated": 1, "version": 1, @@ -16826,7 +16826,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16842,7 +16842,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -16869,7 +16869,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16885,7 +16885,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -16898,7 +16898,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -16914,7 +16914,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -16947,7 +16947,7 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`] "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -16956,7 +16956,7 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`] "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "down", "defaultSidebarDockedPreference": false, @@ -17070,7 +17070,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] appStat "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -17079,7 +17079,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] appStat "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -17177,7 +17177,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -17193,7 +17193,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17206,7 +17206,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -17222,7 +17222,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17237,7 +17237,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -17273,7 +17273,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 7, @@ -17300,7 +17300,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -17316,7 +17316,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17329,7 +17329,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -17345,7 +17345,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17360,7 +17360,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -17392,7 +17392,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 5, @@ -17432,7 +17432,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -17448,7 +17448,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17475,7 +17475,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 10, @@ -17491,7 +17491,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17504,7 +17504,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 20, @@ -17520,7 +17520,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] history "seed": 2019559783, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, @@ -17553,7 +17553,7 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 3, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -17562,7 +17562,7 @@ exports[`regression tests > updates fontSize & fontFamily appState > [end of tes "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, @@ -17676,7 +17676,7 @@ exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -17685,7 +17685,7 @@ exports[`regression tests > zoom hotkeys > [end of test] appState 1`] = ` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, diff --git a/src/tests/__snapshots__/selection.test.tsx.snap b/src/tests/__snapshots__/selection.test.tsx.snap index 33635bca54..92ebee6313 100644 --- a/src/tests/__snapshots__/selection.test.tsx.snap +++ b/src/tests/__snapshots__/selection.test.tsx.snap @@ -7,7 +7,7 @@ exports[`select single element on the scene > arrow 1`] = ` "boundElements": null, "endArrowhead": "arrow", "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -36,7 +36,7 @@ exports[`select single element on the scene > arrow 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 3, @@ -54,7 +54,7 @@ exports[`select single element on the scene > arrow escape 1`] = ` "boundElements": null, "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -83,7 +83,7 @@ exports[`select single element on the scene > arrow escape 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 3, @@ -99,7 +99,7 @@ exports[`select single element on the scene > diamond 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -115,7 +115,7 @@ exports[`select single element on the scene > diamond 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "diamond", "updated": 1, "version": 2, @@ -131,7 +131,7 @@ exports[`select single element on the scene > ellipse 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -147,7 +147,7 @@ exports[`select single element on the scene > ellipse 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "ellipse", "updated": 1, "version": 2, @@ -163,7 +163,7 @@ exports[`select single element on the scene > rectangle 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 50, @@ -179,7 +179,7 @@ exports[`select single element on the scene > rectangle 1`] = ` "seed": 1278240551, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 2, diff --git a/src/tests/data/__snapshots__/restore.test.ts.snap b/src/tests/data/__snapshots__/restore.test.ts.snap index c5482c2157..0c06b65f10 100644 --- a/src/tests/data/__snapshots__/restore.test.ts.snap +++ b/src/tests/data/__snapshots__/restore.test.ts.snap @@ -7,7 +7,7 @@ exports[`restoreElements > should restore arrow element correctly 1`] = ` "boundElements": [], "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -36,7 +36,7 @@ exports[`restoreElements > should restore arrow element correctly 1`] = ` "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "arrow", "updated": 1, "version": 1, @@ -160,7 +160,7 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = ` "angle": 0, "backgroundColor": "transparent", "boundElements": [], - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 0, @@ -180,7 +180,7 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = ` "simulatePressure": true, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "freedraw", "updated": 1, "version": 1, @@ -198,7 +198,7 @@ exports[`restoreElements > should restore line and draw elements correctly 1`] = "boundElements": [], "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -227,7 +227,7 @@ exports[`restoreElements > should restore line and draw elements correctly 1`] = "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 1, @@ -245,7 +245,7 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] = "boundElements": [], "endArrowhead": null, "endBinding": null, - "fillStyle": "hachure", + "fillStyle": "solid", "frameId": null, "groupIds": [], "height": 100, @@ -274,7 +274,7 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] = "startBinding": null, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "type": "line", "updated": 1, "version": 1, @@ -292,7 +292,7 @@ exports[`restoreElements > should restore text element correctly passing value f "baseline": 0, "boundElements": [], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 14, "frameId": null, @@ -312,7 +312,7 @@ exports[`restoreElements > should restore text element correctly passing value f "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "text", "textAlign": "center", "type": "text", @@ -333,7 +333,7 @@ exports[`restoreElements > should restore text element correctly with unknown fo "baseline": 0, "boundElements": [], "containerId": null, - "fillStyle": "hachure", + "fillStyle": "solid", "fontFamily": 1, "fontSize": 10, "frameId": null, @@ -353,7 +353,7 @@ exports[`restoreElements > should restore text element correctly with unknown fo "seed": Any, "strokeColor": "#1e1e1e", "strokeStyle": "solid", - "strokeWidth": 1, + "strokeWidth": 2, "text": "", "textAlign": "left", "type": "text", diff --git a/src/tests/packages/__snapshots__/utils.test.ts.snap b/src/tests/packages/__snapshots__/utils.test.ts.snap index 2da2f72d99..254d4163b6 100644 --- a/src/tests/packages/__snapshots__/utils.test.ts.snap +++ b/src/tests/packages/__snapshots__/utils.test.ts.snap @@ -14,7 +14,7 @@ exports[`exportToSvg > with default arguments 1`] = ` "currentChartType": "bar", "currentItemBackgroundColor": "transparent", "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "hachure", + "currentItemFillStyle": "solid", "currentItemFontFamily": 1, "currentItemFontSize": 20, "currentItemOpacity": 100, @@ -23,7 +23,7 @@ exports[`exportToSvg > with default arguments 1`] = ` "currentItemStartArrowhead": null, "currentItemStrokeColor": "#1e1e1e", "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, + "currentItemStrokeWidth": 2, "currentItemTextAlign": "left", "cursorButton": "up", "defaultSidebarDockedPreference": false, diff --git a/src/tests/queries/dom.ts b/src/tests/queries/dom.ts index 62364e4c45..82edc39492 100644 --- a/src/tests/queries/dom.ts +++ b/src/tests/queries/dom.ts @@ -6,7 +6,7 @@ export const getTextEditor = async (waitForEditor = true) => { ".excalidraw-textEditorContainer > textarea", ) as HTMLTextAreaElement; if (waitForEditor) { - waitFor(() => expect(query()).not.toBe(null)); + await waitFor(() => expect(query()).not.toBe(null)); return query(); } return query(); diff --git a/src/tests/scene/__snapshots__/export.test.ts.snap b/src/tests/scene/__snapshots__/export.test.ts.snap index 8d000f849b..33a1f9ca9c 100644 --- a/src/tests/scene/__snapshots__/export.test.ts.snap +++ b/src/tests/scene/__snapshots__/export.test.ts.snap @@ -42,13 +42,13 @@ exports[`exportToSvg > with default arguments 1`] = ` transform="translate(10 10) rotate(0 50 50)" > with default arguments 1`] = ` transform="translate(10 10) rotate(0 50 50)" > with elements that have a link 1`] = ` - " + " `; exports[`exportToSvg > with exportEmbedScene 1`] = ` @@ -111,5 +111,5 @@ exports[`exportToSvg > with exportEmbedScene 1`] = ` - " + " `; From 71ad3c53563fb6ffcc0e18a5157ca81e8c5698af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viczi=C3=A1n=20Andr=C3=A1s?= Date: Tue, 24 Oct 2023 20:36:13 +0200 Subject: [PATCH 044/144] fix: Actions panel ux improvement (#6850) Co-authored-by: dwelle --- src/actions/actionProperties.test.tsx | 167 ++++++++++++++ src/actions/actionProperties.tsx | 111 ++++++--- src/components/Actions.tsx | 13 +- src/components/ColorPicker/TopPicks.tsx | 1 + src/constants.ts | 6 + src/scene/comparisons.ts | 2 - src/scene/index.ts | 1 - .../regressionTests.test.tsx.snap | 211 ------------------ .../__snapshots__/excalidraw.test.tsx.snap | 5 + src/tests/regressionTests.test.tsx | 14 -- src/types.ts | 9 + 11 files changed, 279 insertions(+), 261 deletions(-) create mode 100644 src/actions/actionProperties.test.tsx diff --git a/src/actions/actionProperties.test.tsx b/src/actions/actionProperties.test.tsx new file mode 100644 index 0000000000..3122cd1080 --- /dev/null +++ b/src/actions/actionProperties.test.tsx @@ -0,0 +1,167 @@ +import { Excalidraw } from "../packages/excalidraw/index"; +import { queryByTestId } from "@testing-library/react"; +import { render } from "../tests/test-utils"; +import { UI } from "../tests/helpers/ui"; +import { API } from "../tests/helpers/api"; +import { COLOR_PALETTE, DEFAULT_ELEMENT_BACKGROUND_PICKS } from "../colors"; +import { FONT_FAMILY, STROKE_WIDTH } from "../constants"; + +const { h } = window; + +describe("element locking", () => { + beforeEach(async () => { + await render(); + }); + + describe("properties when tool selected", () => { + it("should show active background top picks", () => { + UI.clickTool("rectangle"); + + const color = DEFAULT_ELEMENT_BACKGROUND_PICKS[1]; + + // just in case we change it in the future + expect(color).not.toBe(COLOR_PALETTE.transparent); + + h.setState({ + currentItemBackgroundColor: color, + }); + const activeColor = queryByTestId( + document.body, + `color-top-pick-${color}`, + ); + expect(activeColor).toHaveClass("active"); + }); + + it("should show fill style when background non-transparent", () => { + UI.clickTool("rectangle"); + + const color = DEFAULT_ELEMENT_BACKGROUND_PICKS[1]; + + // just in case we change it in the future + expect(color).not.toBe(COLOR_PALETTE.transparent); + + h.setState({ + currentItemBackgroundColor: color, + currentItemFillStyle: "hachure", + }); + const hachureFillButton = queryByTestId(document.body, `fill-hachure`); + + expect(hachureFillButton).toHaveClass("active"); + h.setState({ + currentItemFillStyle: "solid", + }); + const solidFillStyle = queryByTestId(document.body, `fill-solid`); + expect(solidFillStyle).toHaveClass("active"); + }); + + it("should not show fill style when background transparent", () => { + UI.clickTool("rectangle"); + + h.setState({ + currentItemBackgroundColor: COLOR_PALETTE.transparent, + currentItemFillStyle: "hachure", + }); + const hachureFillButton = queryByTestId(document.body, `fill-hachure`); + + expect(hachureFillButton).toBe(null); + }); + + it("should show horizontal text align for text tool", () => { + UI.clickTool("text"); + + h.setState({ + currentItemTextAlign: "right", + }); + + const centerTextAlign = queryByTestId(document.body, `align-right`); + expect(centerTextAlign).toBeChecked(); + }); + }); + + describe("properties when elements selected", () => { + it("should show active styles when single element selected", () => { + const rect = API.createElement({ + type: "rectangle", + backgroundColor: "red", + fillStyle: "cross-hatch", + }); + h.elements = [rect]; + API.setSelectedElements([rect]); + + const crossHatchButton = queryByTestId(document.body, `fill-cross-hatch`); + expect(crossHatchButton).toHaveClass("active"); + }); + + it("should not show fill style selected element's background is transparent", () => { + const rect = API.createElement({ + type: "rectangle", + backgroundColor: COLOR_PALETTE.transparent, + fillStyle: "cross-hatch", + }); + h.elements = [rect]; + API.setSelectedElements([rect]); + + const crossHatchButton = queryByTestId(document.body, `fill-cross-hatch`); + expect(crossHatchButton).toBe(null); + }); + + it("should highlight common stroke width of selected elements", () => { + const rect1 = API.createElement({ + type: "rectangle", + strokeWidth: STROKE_WIDTH.thin, + }); + const rect2 = API.createElement({ + type: "rectangle", + strokeWidth: STROKE_WIDTH.thin, + }); + h.elements = [rect1, rect2]; + API.setSelectedElements([rect1, rect2]); + + const thinStrokeWidthButton = queryByTestId( + document.body, + `strokeWidth-thin`, + ); + expect(thinStrokeWidthButton).toBeChecked(); + }); + + it("should not highlight any stroke width button if no common style", () => { + const rect1 = API.createElement({ + type: "rectangle", + strokeWidth: STROKE_WIDTH.thin, + }); + const rect2 = API.createElement({ + type: "rectangle", + strokeWidth: STROKE_WIDTH.bold, + }); + h.elements = [rect1, rect2]; + API.setSelectedElements([rect1, rect2]); + + expect(queryByTestId(document.body, `strokeWidth-thin`)).not.toBe(null); + expect( + queryByTestId(document.body, `strokeWidth-thin`), + ).not.toBeChecked(); + expect( + queryByTestId(document.body, `strokeWidth-bold`), + ).not.toBeChecked(); + expect( + queryByTestId(document.body, `strokeWidth-extraBold`), + ).not.toBeChecked(); + }); + + it("should show properties of different element types when selected", () => { + const rect = API.createElement({ + type: "rectangle", + strokeWidth: STROKE_WIDTH.bold, + }); + const text = API.createElement({ + type: "text", + fontFamily: FONT_FAMILY.Cascadia, + }); + h.elements = [rect, text]; + API.setSelectedElements([rect, text]); + + expect(queryByTestId(document.body, `strokeWidth-bold`)).toBeChecked(); + expect(queryByTestId(document.body, `font-family-code`)).toBeChecked(); + }); + }); +}); diff --git a/src/actions/actionProperties.tsx b/src/actions/actionProperties.tsx index 1a05bc7cbf..c362b37757 100644 --- a/src/actions/actionProperties.tsx +++ b/src/actions/actionProperties.tsx @@ -1,4 +1,4 @@ -import { AppState } from "../../src/types"; +import { AppState, Primitive } from "../../src/types"; import { DEFAULT_ELEMENT_BACKGROUND_COLOR_PALETTE, DEFAULT_ELEMENT_BACKGROUND_PICKS, @@ -51,6 +51,7 @@ import { DEFAULT_FONT_SIZE, FONT_FAMILY, ROUNDNESS, + STROKE_WIDTH, VERTICAL_ALIGN, } from "../constants"; import { @@ -82,7 +83,6 @@ import { getLanguage, t } from "../i18n"; import { KEYS } from "../keys"; import { randomInteger } from "../random"; import { - canChangeRoundness, canHaveArrowheads, getCommonAttributeOfSelectedElements, getSelectedElements, @@ -118,25 +118,44 @@ export const changeProperty = ( }); }; -export const getFormValue = function ( +export const getFormValue = function ( elements: readonly ExcalidrawElement[], appState: AppState, getAttribute: (element: ExcalidrawElement) => T, - defaultValue: T, + isRelevantElement: true | ((element: ExcalidrawElement) => boolean), + defaultValue: T | ((isSomeElementSelected: boolean) => T), ): T { const editingElement = appState.editingElement; const nonDeletedElements = getNonDeletedElements(elements); - return ( - (editingElement && getAttribute(editingElement)) ?? - (isSomeElementSelected(nonDeletedElements, appState) - ? getCommonAttributeOfSelectedElements( - nonDeletedElements, + + let ret: T | null = null; + + if (editingElement) { + ret = getAttribute(editingElement); + } + + if (!ret) { + const hasSelection = isSomeElementSelected(nonDeletedElements, appState); + + if (hasSelection) { + ret = + getCommonAttributeOfSelectedElements( + isRelevantElement === true + ? nonDeletedElements + : nonDeletedElements.filter((el) => isRelevantElement(el)), appState, getAttribute, - ) - : defaultValue) ?? - defaultValue - ); + ) ?? + (typeof defaultValue === "function" + ? defaultValue(true) + : defaultValue); + } else { + ret = + typeof defaultValue === "function" ? defaultValue(false) : defaultValue; + } + } + + return ret; }; const offsetElementAfterFontResize = ( @@ -247,6 +266,7 @@ export const actionChangeStrokeColor = register({ elements, appState, (element) => element.strokeColor, + true, appState.currentItemStrokeColor, )} onChange={(color) => updateData({ currentItemStrokeColor: color })} @@ -289,6 +309,7 @@ export const actionChangeBackgroundColor = register({ elements, appState, (element) => element.backgroundColor, + true, appState.currentItemBackgroundColor, )} onChange={(color) => updateData({ currentItemBackgroundColor: color })} @@ -338,23 +359,28 @@ export const actionChangeFillStyle = register({ } (${getShortcutKey("Alt-Click")})`, icon: allElementsZigZag ? FillZigZagIcon : FillHachureIcon, active: allElementsZigZag ? true : undefined, + testId: `fill-hachure`, }, { value: "cross-hatch", text: t("labels.crossHatch"), icon: FillCrossHatchIcon, + testId: `fill-cross-hatch`, }, { value: "solid", text: t("labels.solid"), icon: FillSolidIcon, + testId: `fill-solid`, }, ]} value={getFormValue( elements, appState, (element) => element.fillStyle, - appState.currentItemFillStyle, + (element) => element.hasOwnProperty("fillStyle"), + (hasSelection) => + hasSelection ? null : appState.currentItemFillStyle, )} onClick={(value, event) => { const nextValue = @@ -393,26 +419,31 @@ export const actionChangeStrokeWidth = register({ group="stroke-width" options={[ { - value: 1, + value: STROKE_WIDTH.thin, text: t("labels.thin"), icon: StrokeWidthBaseIcon, + testId: "strokeWidth-thin", }, { - value: 2, + value: STROKE_WIDTH.bold, text: t("labels.bold"), icon: StrokeWidthBoldIcon, + testId: "strokeWidth-bold", }, { - value: 4, + value: STROKE_WIDTH.extraBold, text: t("labels.extraBold"), icon: StrokeWidthExtraBoldIcon, + testId: "strokeWidth-extraBold", }, ]} value={getFormValue( elements, appState, (element) => element.strokeWidth, - appState.currentItemStrokeWidth, + (element) => element.hasOwnProperty("strokeWidth"), + (hasSelection) => + hasSelection ? null : appState.currentItemStrokeWidth, )} onChange={(value) => updateData(value)} /> @@ -461,7 +492,9 @@ export const actionChangeSloppiness = register({ elements, appState, (element) => element.roughness, - appState.currentItemRoughness, + (element) => element.hasOwnProperty("roughness"), + (hasSelection) => + hasSelection ? null : appState.currentItemRoughness, )} onChange={(value) => updateData(value)} /> @@ -509,7 +542,9 @@ export const actionChangeStrokeStyle = register({ elements, appState, (element) => element.strokeStyle, - appState.currentItemStrokeStyle, + (element) => element.hasOwnProperty("strokeStyle"), + (hasSelection) => + hasSelection ? null : appState.currentItemStrokeStyle, )} onChange={(value) => updateData(value)} /> @@ -549,6 +584,7 @@ export const actionChangeOpacity = register({ elements, appState, (element) => element.opacity, + true, appState.currentItemOpacity, ) ?? undefined } @@ -607,7 +643,12 @@ export const actionChangeFontSize = register({ } return null; }, - appState.currentItemFontSize || DEFAULT_FONT_SIZE, + (element) => + isTextElement(element) || getBoundTextElement(element) !== null, + (hasSelection) => + hasSelection + ? null + : appState.currentItemFontSize || DEFAULT_FONT_SIZE, )} onChange={(value) => updateData(value)} /> @@ -692,21 +733,25 @@ export const actionChangeFontFamily = register({ value: FontFamilyValues; text: string; icon: JSX.Element; + testId: string; }[] = [ { value: FONT_FAMILY.Virgil, text: t("labels.handDrawn"), icon: FreedrawIcon, + testId: "font-family-virgil", }, { value: FONT_FAMILY.Helvetica, text: t("labels.normal"), icon: FontFamilyNormalIcon, + testId: "font-family-normal", }, { value: FONT_FAMILY.Cascadia, text: t("labels.code"), icon: FontFamilyCodeIcon, + testId: "font-family-code", }, ]; @@ -729,7 +774,12 @@ export const actionChangeFontFamily = register({ } return null; }, - appState.currentItemFontFamily || DEFAULT_FONT_FAMILY, + (element) => + isTextElement(element) || getBoundTextElement(element) !== null, + (hasSelection) => + hasSelection + ? null + : appState.currentItemFontFamily || DEFAULT_FONT_FAMILY, )} onChange={(value) => updateData(value)} /> @@ -806,7 +856,10 @@ export const actionChangeTextAlign = register({ } return null; }, - appState.currentItemTextAlign, + (element) => + isTextElement(element) || getBoundTextElement(element) !== null, + (hasSelection) => + hasSelection ? null : appState.currentItemTextAlign, )} onChange={(value) => updateData(value)} /> @@ -882,7 +935,9 @@ export const actionChangeVerticalAlign = register({ } return null; }, - VERTICAL_ALIGN.MIDDLE, + (element) => + isTextElement(element) || getBoundTextElement(element) !== null, + (hasSelection) => (hasSelection ? null : VERTICAL_ALIGN.MIDDLE), )} onChange={(value) => updateData(value)} /> @@ -947,9 +1002,9 @@ export const actionChangeRoundness = register({ appState, (element) => hasLegacyRoundness ? null : element.roundness ? "round" : "sharp", - (canChangeRoundness(appState.activeTool.type) && - appState.currentItemRoundness) || - null, + (element) => element.hasOwnProperty("roundness"), + (hasSelection) => + hasSelection ? null : appState.currentItemRoundness, )} onChange={(value) => updateData(value)} /> @@ -1043,6 +1098,7 @@ export const actionChangeArrowhead = register({ isLinearElement(element) && canHaveArrowheads(element.type) ? element.startArrowhead : appState.currentItemStartArrowhead, + true, appState.currentItemStartArrowhead, )} onChange={(value) => updateData({ position: "start", type: value })} @@ -1089,6 +1145,7 @@ export const actionChangeArrowhead = register({ isLinearElement(element) && canHaveArrowheads(element.type) ? element.endArrowhead : appState.currentItemEndArrowhead, + true, appState.currentItemEndArrowhead, )} onChange={(value) => updateData({ position: "end", type: value })} diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index 4010f98ab4..8ba8830d11 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -11,7 +11,6 @@ import { hasBackground, hasStrokeStyle, hasStrokeWidth, - hasText, } from "../scene"; import { SHAPES } from "../shapes"; import { AppClassProperties, UIAppState, Zoom } from "../types"; @@ -20,7 +19,7 @@ import Stack from "./Stack"; import { ToolButton } from "./ToolButton"; import { hasStrokeColor } from "../scene/comparisons"; import { trackEvent } from "../analytics"; -import { hasBoundTextElement } from "../element/typeChecks"; +import { hasBoundTextElement, isTextElement } from "../element/typeChecks"; import clsx from "clsx"; import { actionToggleZenMode } from "../actions"; import { Tooltip } from "./Tooltip"; @@ -66,7 +65,8 @@ export const SelectedShapeActions = ({ const isRTL = document.documentElement.getAttribute("dir") === "rtl"; const showFillIcons = - hasBackground(appState.activeTool.type) || + (hasBackground(appState.activeTool.type) && + !isTransparent(appState.currentItemBackgroundColor)) || targetElements.some( (element) => hasBackground(element.type) && !isTransparent(element.backgroundColor), @@ -123,14 +123,15 @@ export const SelectedShapeActions = ({ <>{renderAction("changeRoundness")} )} - {(hasText(appState.activeTool.type) || - targetElements.some((element) => hasText(element.type))) && ( + {(appState.activeTool.type === "text" || + targetElements.some(isTextElement)) && ( <> {renderAction("changeFontSize")} {renderAction("changeFontFamily")} - {suppportsHorizontalAlign(targetElements) && + {(appState.activeTool.type === "text" || + suppportsHorizontalAlign(targetElements)) && renderAction("changeTextAlign")} )} diff --git a/src/components/ColorPicker/TopPicks.tsx b/src/components/ColorPicker/TopPicks.tsx index ae420c3fd0..34adbdf495 100644 --- a/src/components/ColorPicker/TopPicks.tsx +++ b/src/components/ColorPicker/TopPicks.tsx @@ -55,6 +55,7 @@ export const TopPicks = ({ type="button" title={color} onClick={() => onChange(color)} + data-testid={`color-top-pick-${color}`} >
diff --git a/src/constants.ts b/src/constants.ts index 91c59615e1..07eb679566 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -302,6 +302,12 @@ export const ROUGHNESS = { cartoonist: 2, } as const; +export const STROKE_WIDTH = { + thin: 1, + bold: 2, + extraBold: 4, +} as const; + export const DEFAULT_ELEMENT_PROPS: { strokeColor: ExcalidrawElement["strokeColor"]; backgroundColor: ExcalidrawElement["backgroundColor"]; diff --git a/src/scene/comparisons.ts b/src/scene/comparisons.ts index b6bdf03b57..a6a2302438 100644 --- a/src/scene/comparisons.ts +++ b/src/scene/comparisons.ts @@ -39,8 +39,6 @@ export const canChangeRoundness = (type: string) => type === "line" || type === "diamond"; -export const hasText = (type: string) => type === "text"; - export const canHaveArrowheads = (type: string) => type === "arrow"; export const getElementAtPosition = ( diff --git a/src/scene/index.ts b/src/scene/index.ts index 08568aa41f..5a7b9028a3 100644 --- a/src/scene/index.ts +++ b/src/scene/index.ts @@ -14,7 +14,6 @@ export { canHaveArrowheads, canChangeRoundness, getElementAtPosition, - hasText, getElementsAtPosition, } from "./comparisons"; export { getNormalizedZoom } from "./zoom"; diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 4d99470d3c..9cf919a9ce 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -14255,217 +14255,6 @@ exports[`regression tests > should group elements and ungroup them > [end of tes exports[`regression tests > should group elements and ungroup them > [end of test] number of renders 1`] = `21`; -exports[`regression tests > should show fill icons when element has non transparent background > [end of test] appState 1`] = ` -{ - "activeEmbeddable": null, - "activeTool": { - "customType": null, - "lastActiveTool": null, - "locked": false, - "type": "selection", - }, - "collaborators": Map {}, - "contextMenu": null, - "currentChartType": "bar", - "currentItemBackgroundColor": "#ffc9c9", - "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "solid", - "currentItemFontFamily": 1, - "currentItemFontSize": 20, - "currentItemOpacity": 100, - "currentItemRoughness": 1, - "currentItemRoundness": "round", - "currentItemStartArrowhead": null, - "currentItemStrokeColor": "#1e1e1e", - "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 2, - "currentItemTextAlign": "left", - "cursorButton": "up", - "defaultSidebarDockedPreference": false, - "draggingElement": null, - "editingElement": null, - "editingFrame": null, - "editingGroupId": null, - "editingLinearElement": null, - "elementsToHighlight": null, - "errorMessage": null, - "exportBackground": true, - "exportEmbedScene": false, - "exportScale": 1, - "exportWithDarkMode": false, - "fileHandle": null, - "frameRendering": { - "clip": true, - "enabled": true, - "name": true, - "outline": true, - }, - "frameToHighlight": null, - "gridSize": null, - "height": 768, - "isBindingEnabled": true, - "isLoading": false, - "isResizing": false, - "isRotating": false, - "lastPointerDownWith": "mouse", - "multiElement": null, - "name": "Untitled-201933152653", - "objectsSnapModeEnabled": false, - "offsetLeft": 0, - "offsetTop": 0, - "openDialog": null, - "openMenu": null, - "openPopup": "elementBackground", - "openSidebar": null, - "originSnapOffset": null, - "pasteDialog": { - "data": null, - "shown": false, - }, - "penDetected": false, - "penMode": false, - "pendingImageElementId": null, - "previousSelectedElementIds": {}, - "resizingElement": null, - "scrollX": 0, - "scrollY": 0, - "scrolledOutside": false, - "selectedElementIds": { - "id0": true, - }, - "selectedElementsAreBeingDragged": false, - "selectedGroupIds": {}, - "selectedLinearElement": null, - "selectionElement": null, - "shouldCacheIgnoreZoom": false, - "showHyperlinkPopup": false, - "showStats": false, - "showWelcomeScreen": true, - "snapLines": [], - "startBoundElement": null, - "suggestedBindings": [], - "theme": "light", - "toast": null, - "viewBackgroundColor": "#ffffff", - "viewModeEnabled": false, - "width": 1024, - "zenModeEnabled": false, - "zoom": { - "value": 1, - }, -} -`; - -exports[`regression tests > should show fill icons when element has non transparent background > [end of test] history 1`] = ` -{ - "recording": false, - "redoStack": [], - "stateHistory": [ - { - "appState": { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": {}, - "selectedGroupIds": {}, - "viewBackgroundColor": "#ffffff", - }, - "elements": [], - }, - { - "appState": { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": { - "id0": true, - }, - "selectedGroupIds": {}, - "viewBackgroundColor": "#ffffff", - }, - "elements": [ - { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 10, - "id": "id0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": { - "type": 3, - }, - "seed": 1278240551, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 2, - "versionNonce": 453191, - "width": 10, - "x": 0, - "y": 0, - }, - ], - }, - { - "appState": { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": { - "id0": true, - }, - "selectedGroupIds": {}, - "viewBackgroundColor": "#ffffff", - }, - "elements": [ - { - "angle": 0, - "backgroundColor": "#ffc9c9", - "boundElements": null, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 10, - "id": "id0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": { - "type": 3, - }, - "seed": 1278240551, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 3, - "versionNonce": 2019559783, - "width": 10, - "x": 0, - "y": 0, - }, - ], - }, - ], -} -`; - -exports[`regression tests > should show fill icons when element has non transparent background > [end of test] number of elements 1`] = `0`; - -exports[`regression tests > should show fill icons when element has non transparent background > [end of test] number of renders 1`] = `9`; - exports[`regression tests > single-clicking on a subgroup of a selected group should not alter selection > [end of test] appState 1`] = ` { "activeEmbeddable": null, diff --git a/src/tests/packages/__snapshots__/excalidraw.test.tsx.snap b/src/tests/packages/__snapshots__/excalidraw.test.tsx.snap index a9f6d37a6d..39aed37458 100644 --- a/src/tests/packages/__snapshots__/excalidraw.test.tsx.snap +++ b/src/tests/packages/__snapshots__/excalidraw.test.tsx.snap @@ -535,6 +535,7 @@ exports[` > Test UIOptions prop > Test canvasActions > should rende >
" +`; diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts index 105d7a3949..5ccf05069b 100644 --- a/src/tests/helpers/ui.ts +++ b/src/tests/helpers/ui.ts @@ -468,16 +468,16 @@ export class UI { static async editText< T extends ExcalidrawTextElement | ExcalidrawTextContainer, >(element: T, text: string) { - const openedEditor = document.querySelector( - ".excalidraw-textEditorContainer > textarea", - ); + const textEditorSelector = ".excalidraw-textEditorContainer > textarea"; + const openedEditor = + document.querySelector(textEditorSelector); if (!openedEditor) { mouse.select(element); Keyboard.keyPress(KEYS.ENTER); } - const editor = await getTextEditor(); + const editor = await getTextEditor(textEditorSelector); if (!editor) { throw new Error("Can't find wysiwyg text editor in the dom"); } diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index 9b11bfbddc..1e7ebeb0e6 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -273,7 +273,7 @@ describe("Test Linear Elements", () => { // drag line from midpoint drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]); - expect(renderInteractiveScene).toHaveBeenCalledTimes(14); + expect(renderInteractiveScene).toHaveBeenCalledTimes(13); expect(renderStaticScene).toHaveBeenCalledTimes(6); expect(line.points.length).toEqual(3); @@ -416,7 +416,7 @@ describe("Test Linear Elements", () => { lastSegmentMidpoint[1] + delta, ]); - expect(renderInteractiveScene).toHaveBeenCalledTimes(21); + expect(renderInteractiveScene).toHaveBeenCalledTimes(19); expect(renderStaticScene).toHaveBeenCalledTimes(9); expect(line.points.length).toEqual(5); @@ -519,7 +519,7 @@ describe("Test Linear Elements", () => { // delete 3rd point deletePoint(points[2]); expect(line.points.length).toEqual(3); - expect(renderInteractiveScene).toHaveBeenCalledTimes(21); + expect(renderInteractiveScene).toHaveBeenCalledTimes(20); expect(renderStaticScene).toHaveBeenCalledTimes(9); const newMidPoints = LinearElementEditor.getEditorMidPoints( @@ -566,7 +566,7 @@ describe("Test Linear Elements", () => { lastSegmentMidpoint[0] + delta, lastSegmentMidpoint[1] + delta, ]); - expect(renderInteractiveScene).toHaveBeenCalledTimes(21); + expect(renderInteractiveScene).toHaveBeenCalledTimes(19); expect(renderStaticScene).toHaveBeenCalledTimes(9); expect(line.points.length).toEqual(5); diff --git a/src/tests/queries/dom.ts b/src/tests/queries/dom.ts index 82edc39492..e3494edfb8 100644 --- a/src/tests/queries/dom.ts +++ b/src/tests/queries/dom.ts @@ -1,13 +1,19 @@ import { waitFor } from "@testing-library/dom"; +import { fireEvent } from "@testing-library/react"; -export const getTextEditor = async (waitForEditor = true) => { - const query = () => - document.querySelector( - ".excalidraw-textEditorContainer > textarea", - ) as HTMLTextAreaElement; +export const getTextEditor = async (selector: string, waitForEditor = true) => { + const query = () => document.querySelector(selector) as HTMLTextAreaElement; if (waitForEditor) { await waitFor(() => expect(query()).not.toBe(null)); return query(); } return query(); }; + +export const updateTextEditor = ( + editor: HTMLTextAreaElement, + value: string, +) => { + fireEvent.change(editor, { target: { value } }); + editor.dispatchEvent(new Event("input")); +}; diff --git a/src/types.ts b/src/types.ts index 5cee083fcb..5e72b01623 100644 --- a/src/types.ts +++ b/src/types.ts @@ -241,7 +241,7 @@ export type AppState = { openMenu: "canvas" | "shape" | null; openPopup: "canvasBackground" | "elementBackground" | "elementStroke" | null; openSidebar: { name: SidebarName; tab?: SidebarTabName } | null; - openDialog: "imageExport" | "help" | "jsonExport" | null; + openDialog: "imageExport" | "help" | "jsonExport" | "mermaid" | null; /** * Reflects user preference for whether the default sidebar should be docked. * @@ -537,8 +537,12 @@ export type AppClassProperties = { onInsertElements: App["onInsertElements"]; onExportImage: App["onExportImage"]; lastViewportPosition: App["lastViewportPosition"]; + scrollToContent: App["scrollToContent"]; + addFiles: App["addFiles"]; + addElementsFromPasteOrLibrary: App["addElementsFromPasteOrLibrary"]; togglePenMode: App["togglePenMode"]; setActiveTool: App["setActiveTool"]; + setOpenDialog: App["setOpenDialog"]; }; export type PointerDownState = Readonly<{ diff --git a/vitest.config.ts b/vitest.config.ts index 51a4df95fb..f1484262fd 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ globals: true, environment: "jsdom", coverage: { - reporter: ["text", "json-summary", "json"], + reporter: ["text", "json-summary", "json", "html"], lines: 70, branches: 70, functions: 68, diff --git a/yarn.lock b/yarn.lock index 8b34b8de85..eacc147c7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1321,6 +1321,11 @@ resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f" integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg== +"@braintree/sanitize-url@^6.0.2": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783" + integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== + "@esbuild/android-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" @@ -1578,6 +1583,20 @@ resolved "https://registry.yarnpkg.com/@excalidraw/laser-pointer/-/laser-pointer-1.2.0.tgz#cd34ea7d24b11743c726488cc1fcb28c161cacba" integrity sha512-WjFFwLk9ahmKRKku7U0jqYpeM3fe9ZS1K43pfwPREHk4/FYU3iKDKVeS8m4tEAASnRlBt3hhLCBQLBF2uvgOnw== +"@excalidraw/markdown-to-text@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@excalidraw/markdown-to-text/-/markdown-to-text-0.1.2.tgz#1703705e7da608cf478f17bfe96fb295f55a23eb" + integrity sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg== + +"@excalidraw/mermaid-to-excalidraw@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-0.1.2.tgz#be7b412536fc00b7986ccdccba8e7c33592aa004" + integrity sha512-LFk+cLGhXlvRTaf0f6ClCFIZFRsbZPb1ke2cytr5/JlnOefnXQQHgWITafskjcIO2c34KXFGO0HjgYPNFLUknw== + dependencies: + "@excalidraw/markdown-to-text" "0.1.2" + mermaid "10.2.3" + nanoid "4.0.2" + "@excalidraw/prettier-config@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@excalidraw/prettier-config/-/prettier-config-1.0.2.tgz#b7c061c99cee2f78b9ca470ea1fbd602683bba65" @@ -2556,6 +2575,13 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== +"@types/debug@^4.0.0": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" + integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== + dependencies: + "@types/ms" "*" + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -2628,6 +2654,18 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/mdast@^3.0.0": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.12.tgz#beeb511b977c875a5b0cc92eab6fcac2f0895514" + integrity sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg== + dependencies: + "@types/unist" "^2" + +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "18.15.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" @@ -2738,6 +2776,11 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311" integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g== +"@types/unist@^2", "@types/unist@^2.0.0": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.7.tgz#5b06ad6894b236a1d2bd6b2f07850ca5c59cf4d6" + integrity sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g== + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -3431,6 +3474,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -3534,6 +3582,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@7: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3601,6 +3654,20 @@ corser@^2.0.1: resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ== +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + dependencies: + layout-base "^1.0.0" + +cose-base@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-2.2.0.tgz#1c395c35b6e10bb83f9769ca8b817d614add5c01" + integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== + dependencies: + layout-base "^2.0.0" + cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" @@ -3669,6 +3736,280 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape-fcose@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz#e4d6f6490df4fab58ae9cea9e5c3ab8d7472f471" + integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== + dependencies: + cose-base "^2.2.0" + +cytoscape@^3.23.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.26.0.tgz#b4c6961445fd51e1fd3cca83c3ffe924d9a8abc9" + integrity sha512-IV+crL+KBcrCnVVUCZW+zRRRFUZQcrtdOPXki+o4CFUWLdAEYvuZLcBSJC9EBK++suamERKzeY7roq2hdovV3w== + dependencies: + heap "^0.2.6" + lodash "^4.17.21" + +"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-contour@4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc" + integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + dependencies: + d3-array "^3.2.0" + +d3-delaunay@6: + version "6.0.4" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.0.tgz#74fd54e1f4cebd5185ac2039217a98d39b0a4c0e" + integrity sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" + integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-scale-chromatic@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a" + integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.4.0, d3@^7.8.2: + version "7.8.5" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.8.5.tgz#fde4b760d4486cdb6f0cc8e2cbff318af844635c" + integrity sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + +dagre-d3-es@7.0.10: + version "7.0.10" + resolved "https://registry.yarnpkg.com/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz#19800d4be674379a3cd8c86a8216a2ac6827cadc" + integrity sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A== + dependencies: + d3 "^7.8.2" + lodash-es "^4.17.21" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -3683,7 +4024,12 @@ data-urls@^4.0.0: whatwg-mimetype "^3.0.0" whatwg-url "^12.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: +dayjs@^1.11.7: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -3709,6 +4055,13 @@ decimal.js@^10.4.3: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +decode-named-character-reference@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== + dependencies: + character-entities "^2.0.0" + decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -3769,11 +4122,23 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delaunator@5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b" + integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw== + dependencies: + robust-predicates "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -3789,6 +4154,11 @@ diff-sequences@^29.4.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3834,6 +4204,11 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" +dompurify@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.3.tgz#4b115d15a091ddc96f232bcef668550a2f6f1430" + integrity sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ== + dotenv@16.0.1: version "16.0.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" @@ -3856,6 +4231,11 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz#5c4d13cb08032469fcd6bd36457915caa211356b" integrity sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw== +elkjs@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e" + integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -4752,6 +5132,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +heap@^0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -4831,7 +5216,7 @@ i18next-browser-languagedetector@6.1.4: dependencies: "@babel/runtime" "^7.14.6" -iconv-lite@0.6.3: +iconv-lite@0.6, iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -4927,6 +5312,11 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -5399,6 +5789,16 @@ jsonpointer@^5.0.0: array-includes "^3.1.5" object.assign "^4.1.3" +khroma@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/khroma/-/khroma-2.0.0.tgz#7577de98aed9f36c7a474c4d453d94c0d6c6588b" + integrity sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g== + +kleur@^4.0.3: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -5411,6 +5811,16 @@ language-tags@=1.0.5: dependencies: language-subtag-registry "~0.3.2" +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + +layout-base@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285" + integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -5487,6 +5897,11 @@ localforage@^1.8.1: dependencies: lie "3.1.1" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -5608,6 +6023,31 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" +mdast-util-from-markdown@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz#9421a5a247f10d31d2faed2a30df5ec89ceafcf0" + integrity sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + decode-named-character-reference "^1.0.0" + mdast-util-to-string "^3.1.0" + micromark "^3.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-decode-string "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + unist-util-stringify-position "^3.0.0" + uvu "^0.5.0" + +mdast-util-to-string@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz#66f7bb6324756741c5f47a53557f0cbf16b6f789" + integrity sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg== + dependencies: + "@types/mdast" "^3.0.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -5618,6 +6058,223 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +mermaid@10.2.3: + version "10.2.3" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.2.3.tgz#789d3b582c5da8c69aa4a7c0e2b826562c8c8b12" + integrity sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw== + dependencies: + "@braintree/sanitize-url" "^6.0.2" + cytoscape "^3.23.0" + cytoscape-cose-bilkent "^4.1.0" + cytoscape-fcose "^2.1.0" + d3 "^7.4.0" + dagre-d3-es "7.0.10" + dayjs "^1.11.7" + dompurify "3.0.3" + elkjs "^0.8.2" + khroma "^2.0.0" + lodash-es "^4.17.21" + mdast-util-from-markdown "^1.3.0" + non-layered-tidy-tree-layout "^2.0.2" + stylis "^4.1.3" + ts-dedent "^2.2.0" + uuid "^9.0.0" + web-worker "^1.2.0" + +micromark-core-commonmark@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz#1386628df59946b2d39fb2edfd10f3e8e0a75bb8" + integrity sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-factory-destination "^1.0.0" + micromark-factory-label "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-factory-title "^1.0.0" + micromark-factory-whitespace "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-html-tag-name "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + +micromark-factory-destination@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz#eb815957d83e6d44479b3df640f010edad667b9f" + integrity sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-label@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz#cc95d5478269085cfa2a7282b3de26eb2e2dec68" + integrity sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + +micromark-factory-space@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz#c8f40b0640a0150751d3345ed885a080b0d15faf" + integrity sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-title@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz#dd0fe951d7a0ac71bdc5ee13e5d1465ad7f50ea1" + integrity sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-whitespace@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz#798fb7489f4c8abafa7ca77eed6b5745853c9705" + integrity sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-character@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.2.0.tgz#4fedaa3646db249bc58caeb000eb3549a8ca5dcc" + integrity sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-chunked@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz#37a24d33333c8c69a74ba12a14651fd9ea8a368b" + integrity sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-classify-character@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz#6a7f8c8838e8a120c8e3c4f2ae97a2bff9190e9d" + integrity sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-combine-extensions@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz#192e2b3d6567660a85f735e54d8ea6e3952dbe84" + integrity sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-decode-numeric-character-reference@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz#b1e6e17009b1f20bc652a521309c5f22c85eb1c6" + integrity sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-decode-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz#dc12b078cba7a3ff690d0203f95b5d5537f2809c" + integrity sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-symbol "^1.0.0" + +micromark-util-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz#92e4f565fd4ccb19e0dcae1afab9a173bbeb19a5" + integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw== + +micromark-util-html-tag-name@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz#48fd7a25826f29d2f71479d3b4e83e94829b3588" + integrity sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q== + +micromark-util-normalize-identifier@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz#7a73f824eb9f10d442b4d7f120fecb9b38ebf8b7" + integrity sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-resolve-all@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz#4652a591ee8c8fa06714c9b54cd6c8e693671188" + integrity sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA== + dependencies: + micromark-util-types "^1.0.0" + +micromark-util-sanitize-uri@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz#613f738e4400c6eedbc53590c67b197e30d7f90d" + integrity sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-symbol "^1.0.0" + +micromark-util-subtokenize@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz#941c74f93a93eaf687b9054aeb94642b0e92edb1" + integrity sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + +micromark-util-symbol@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz#813cd17837bdb912d069a12ebe3a44b6f7063142" + integrity sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag== + +micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.1.0.tgz#e6676a8cae0bb86a2171c498167971886cb7e283" + integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg== + +micromark@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.2.0.tgz#1af9fef3f995ea1ea4ac9c7e2f19c48fd5c006e9" + integrity sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + micromark-core-commonmark "^1.0.1" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -5696,6 +6353,11 @@ moo-color@^1.0.2: dependencies: color-name "^1.1.4" +mri@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + mrmime@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" @@ -5729,6 +6391,11 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +nanoid@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" + integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== + nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" @@ -5754,6 +6421,11 @@ node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +non-layered-tidy-tree-layout@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804" + integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6428,6 +7100,11 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +robust-predicates@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + rollup-plugin-terser@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" @@ -6481,6 +7158,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + rxjs@^7.5.5: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" @@ -6488,6 +7170,13 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" +sade@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" + integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== + dependencies: + mri "^1.1.0" + safari-14-idb-fix@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz#450fc049b996ec7f3fd9ca2f89d32e0761583440" @@ -6866,6 +7555,11 @@ strip-literal@^1.0.1: dependencies: acorn "^8.8.2" +stylis@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.0.tgz#abe305a669fc3d8777e10eefcfc73ad861c5588c" + integrity sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -7028,6 +7722,11 @@ tr46@^4.1.1: dependencies: punycode "^2.3.0" +ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + tsconfig-paths@^3.14.1: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" @@ -7169,6 +7868,13 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +unist-util-stringify-position@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" + integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== + dependencies: + "@types/unist" "^2.0.0" + universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -7237,6 +7943,21 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +uvu@^0.5.0: + version "0.5.6" + resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" + integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== + dependencies: + dequal "^2.0.0" + diff "^5.0.0" + kleur "^4.0.3" + sade "^1.7.3" + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -7422,6 +8143,11 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" +web-worker@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da" + integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" From 18a7b975158117fbb8b9f77939496f6567c42528 Mon Sep 17 00:00:00 2001 From: Dante Calderon <18385321+dantehemerson@users.noreply.github.com> Date: Sat, 4 Nov 2023 12:15:09 -0500 Subject: [PATCH 060/144] chore: Fix typo in comment in LocalData file (#7235) --- excalidraw-app/data/LocalData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/excalidraw-app/data/LocalData.ts b/excalidraw-app/data/LocalData.ts index 5d9ba0e144..25f068aaac 100644 --- a/excalidraw-app/data/LocalData.ts +++ b/excalidraw-app/data/LocalData.ts @@ -6,7 +6,7 @@ * * - DataState refers to full state of the app: appState, elements, images, * though some state is saved separately (collab username, library) for one - * reason or another. We also save different data to different sotrage + * reason or another. We also save different data to different storage * (localStorage, indexedDB). */ From b1037b342dfe52ef73b559b8a835804aab8b7b79 Mon Sep 17 00:00:00 2001 From: David Luzar <5153846+dwelle@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:29:00 +0100 Subject: [PATCH 061/144] feat: make device breakpoints more specific (#7243) --- .../api/children-components/footer.mdx | 2 +- .../excalidraw/api/utils/utils-intro.md | 3 +- excalidraw-app/tests/MobileMenu.test.tsx | 18 ++- src/actions/actionExport.tsx | 2 +- src/actions/actionProperties.tsx | 2 +- src/actions/manager.tsx | 2 +- src/components/Actions.tsx | 4 +- src/components/App.tsx | 147 ++++++++++-------- src/components/ColorPicker/ColorInput.tsx | 2 +- src/components/ColorPicker/ColorPicker.tsx | 14 +- src/components/Dialog.tsx | 2 +- src/components/HintViewer.tsx | 2 +- src/components/LayerUI.tsx | 16 +- src/components/LibraryUnit.tsx | 2 +- src/components/Sidebar/Sidebar.tsx | 8 +- src/components/Sidebar/SidebarHeader.tsx | 2 +- .../dropdownMenu/DropdownMenuContent.tsx | 4 +- .../dropdownMenu/DropdownMenuItemContent.tsx | 2 +- .../dropdownMenu/DropdownMenuTrigger.tsx | 2 +- src/components/main-menu/MainMenu.tsx | 4 +- .../welcome-screen/WelcomeScreen.Center.tsx | 2 +- src/constants.ts | 2 - src/element/textWysiwyg.test.tsx | 6 +- src/hooks/useCreatePortalContainer.ts | 9 +- src/packages/excalidraw/CHANGELOG.md | 4 + .../excalidraw/example/MobileFooter.tsx | 2 +- src/tests/test-utils.ts | 8 +- src/types.ts | 12 +- 28 files changed, 164 insertions(+), 121 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx index cdd5ea5a49..3831268f0f 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx @@ -34,7 +34,7 @@ Open the `Menu` in the below playground and you will see the `custom footer` ren ```jsx live noInline const MobileFooter = ({}) => { const device = useDevice(); - if (device.isMobile) { + if (device.editor.isMobile) { return (