diff --git a/package.json b/package.json index e622c9386374..3540b39dbb2b 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "prettier": "@excalidraw/prettier-config", "private": true, "scripts": { + "setup": "yarn && cd src/packages/excalidraw && yarn", "build-node": "node ./scripts/build-node.js", "build:app:docker": "REACT_APP_DISABLE_SENTRY=true react-scripts build", "build:app": "REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build", @@ -115,6 +116,8 @@ "test": "yarn test:app", "autorelease": "node scripts/autorelease.js", "prerelease": "node scripts/prerelease.js", - "release": "node scripts/release.js" + "release": "node scripts/release.js", + "pull": "git pull upstream master --rebase", + "publish": "cd src/packages/excalidraw && npm run build:umd && yarn publish" } } diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index 246bfe7a6cd7..d8ce13cc4e6e 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -83,7 +83,7 @@ export class ActionManager { .sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0)) .filter( (action) => - (action.name in canvasActions + (canvasActions && action.name in canvasActions ? canvasActions[action.name as keyof typeof canvasActions] : true) && action.keyTest && @@ -141,7 +141,7 @@ export class ActionManager { if ( this.actions[name] && "PanelComponent" in this.actions[name] && - (name in canvasActions + (canvasActions && name in canvasActions ? canvasActions[name as keyof typeof canvasActions] : true) ) { diff --git a/src/components/App.tsx b/src/components/App.tsx index f88d069a5f79..369b7304c023 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -167,7 +167,10 @@ import { } from "../keys"; import { distance2d, getGridPoint, isPathALoop } from "../math"; import { renderSceneThrottled } from "../renderer/renderScene"; -import { invalidateShapeForElement } from "../renderer/renderElement"; +import { + clearRenderCache, + invalidateShapeForElement, +} from "../renderer/renderElement"; import { calculateScrollCenter, getTextBindableContainerAtPosition, @@ -270,7 +273,7 @@ const DeviceContext = React.createContext(deviceContextInitialValue); export const useDevice = () => useContext(DeviceContext); const ExcalidrawContainerContext = React.createContext<{ container: HTMLDivElement | null; - id: string | null; + id?: string | null; }>({ container: null, id: null }); export const useExcalidrawContainer = () => useContext(ExcalidrawContainerContext); @@ -385,6 +388,7 @@ class App extends React.Component { setActiveTool: this.setActiveTool, setCursor: this.setCursor, resetCursor: this.resetCursor, + app: this, } as const; if (typeof excalidrawRef === "function") { excalidrawRef(api); @@ -484,6 +488,7 @@ class App extends React.Component { return (
{ > { } showThemeBtn={ typeof this.props?.theme === "undefined" && + this.props.UIOptions.canvasActions && this.props.UIOptions.canvasActions.theme } libraryReturnUrl={this.props.libraryReturnUrl} @@ -787,6 +794,13 @@ class App extends React.Component { }; } + if (initialData?.scrollX != null) { + scene.appState.scrollX = initialData.scrollX; + } + if (initialData?.scrollY != null) { + scene.appState.scrollY = initialData.scrollY; + } + this.resetHistory(); this.syncActionResult({ ...scene, @@ -917,6 +931,25 @@ class App extends React.Component { this.unmounted = true; this.removeEventListeners(); this.scene.destroy(); + clearRenderCache(); + + this.scene = new Scene(); + this.history = new History(); + this.actionManager = new ActionManager( + this.syncActionResult, + () => this.state, + () => this.scene.getElementsIncludingDeleted(), + this, + ); + this.library = new Library(this); + this.canvas = null; + this.rc = null; + + // @ts-ignore + this.excalidrawContainerRef.current = undefined; + this.nearestScrollableContainer = undefined; + this.excalidrawContainerValue = { container: null, id: "unmounted" }; + clearTimeout(touchTimeout); touchTimeout = 0; } @@ -975,6 +1008,7 @@ class App extends React.Component { this.disableEvent, false, ); + document.fonts?.removeEventListener?.("loadingdone", this.onFontLoaded); document.removeEventListener( EVENT.GESTURE_START, @@ -1122,6 +1156,15 @@ class App extends React.Component { }); } + if ( + !this.props.UIOptions.canvasActions && + this.state.openMenu === "canvas" + ) { + this.setState({ + openMenu: null, + }); + } + if (this.props.name && prevProps.name !== this.props.name) { this.setState({ name: this.props.name, @@ -1269,6 +1312,7 @@ class App extends React.Component { this.scene.getElementsIncludingDeleted(), this.state, this.files, + this.props.id, ); } } diff --git a/src/components/Avatar.scss b/src/components/Avatar.scss index d3f8c8bd309a..0a27c45b4736 100644 --- a/src/components/Avatar.scss +++ b/src/components/Avatar.scss @@ -4,7 +4,7 @@ .Avatar { width: 2.5rem; height: 2.5rem; - border-radius: 1.25rem; + border-radius: 50%; display: flex; justify-content: center; align-items: center; diff --git a/src/components/Island.scss b/src/components/Island.scss index 499d13ad3d51..95e8e82b7006 100644 --- a/src/components/Island.scss +++ b/src/components/Island.scss @@ -6,10 +6,49 @@ border-radius: var(--border-radius-lg); padding: calc(var(--padding) * var(--space-factor)); position: relative; - transition: box-shadow 0.5s ease-in-out; &.zen-mode { box-shadow: none; } + + &::-webkit-scrollbar { + width: 10px; + } + + &::-webkit-scrollbar-track { + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--color-scrollbar-thumb); + } + &::-webkit-scrollbar-thumb:hover { + background-color: var(--color-scrollbar-thumb-hover); + } + + &::-webkit-scrollbar-thumb:active { + background-color: var(--color-scrollbar-thumb-active); + } + } + + .App-menu_top { + .Stack_vertical { + .Island { + min-width: 216px; + } + .Stack_horizontal { + justify-content: center !important; + } + } + } + + &.excalidraw--view-mode { + .App-menu_top { + .Stack_vertical { + .Island { + min-width: auto; + } + } + } } } diff --git a/src/components/JSONExportDialog.tsx b/src/components/JSONExportDialog.tsx index 98e0519f3786..f8d075048fb5 100644 --- a/src/components/JSONExportDialog.tsx +++ b/src/components/JSONExportDialog.tsx @@ -2,9 +2,9 @@ import React, { useState } from "react"; import { NonDeletedExcalidrawElement } from "../element/types"; import { t } from "../i18n"; import { useDevice } from "./App"; -import { AppState, ExportOpts, BinaryFiles } from "../types"; +import { AppState, CanvasActions, ExportOpts, BinaryFiles } from "../types"; import { Dialog } from "./Dialog"; -import { exportFile, exportToFileIcon, link } from "./icons"; +import { exportToFileIcon, link } from "./icons"; import { ToolButton } from "./ToolButton"; import { actionSaveFileToDisk } from "../actions/actionExport"; import { Card } from "./Card"; @@ -98,7 +98,7 @@ export const JSONExportDialog = ({ appState: AppState; files: BinaryFiles; actionManager: ActionManager; - exportOpts: ExportOpts; + exportOpts: CanvasActions["export"]; canvas: HTMLCanvasElement | null; }) => { const [modalIsShown, setModalIsShown] = useState(false); @@ -111,16 +111,20 @@ export const JSONExportDialog = ({ <> { - setModalIsShown(true); + if (typeof exportOpts === "function") { + actionManager.executeAction(actionSaveFileToDisk); + } else { + setModalIsShown(true); + } }} data-testid="json-export-button" - icon={exportFile} + icon={exportToFileIcon} type="button" aria-label={t("buttons.export")} showAriaLabel={useDevice().isMobile} title={t("buttons.export")} /> - {modalIsShown && ( + {modalIsShown && exportOpts && typeof exportOpts !== "function" && ( void; actionManager: ActionManager; appState: AppState; files: BinaryFiles; @@ -69,6 +70,7 @@ interface LayerUIProps { onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void; } const LayerUI = ({ + onHomeButtonClick, actionManager, appState, files, @@ -98,7 +100,7 @@ const LayerUI = ({ const device = useDevice(); const renderJSONExportDialog = () => { - if (!UIOptions.canvasActions.export) { + if (!UIOptions.canvasActions || !UIOptions.canvasActions.export) { return null; } @@ -115,7 +117,7 @@ const LayerUI = ({ }; const renderImageExportDialog = () => { - if (!UIOptions.canvasActions.saveAsImage) { + if (!UIOptions.canvasActions || !UIOptions.canvasActions.saveAsImage) { return null; } @@ -162,10 +164,6 @@ const LayerUI = ({ ); }; - const Separator = () => { - return
; - }; - const renderViewModeCanvasActions = () => { return (
- + {actionManager.renderAction("clearCanvas")} - {actionManager.renderAction("loadScene")} {renderJSONExportDialog()} {renderImageExportDialog()} - {onCollabButtonClick && ( - {viewModeEnabled - ? renderViewModeCanvasActions() - : renderCanvasActions()} + {UIOptions.canvasActions && + (viewModeEnabled + ? renderViewModeCanvasActions() + : renderCanvasActions())} {shouldRenderSelectedShapeActions && renderSelectedShapeActions()} {!viewModeEnabled && ( @@ -378,8 +375,12 @@ const LayerUI = ({ - {renderTopRightUI?.(device.isMobile, appState)} + {renderTopRightUI?.(device.isMobile, appState, canvas)}
@@ -530,6 +531,7 @@ const LayerUI = ({ <> {dialogs} ) : ( @@ -606,6 +609,7 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => { const keys = Object.keys(prevAppState) as (keyof Partial)[]; return ( + prev.renderTopRightUI === next.renderTopRightUI && prev.renderCustomFooter === next.renderCustomFooter && prev.langCode === next.langCode && prev.elements === next.elements && diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index ea45b393eaa2..7b82c250c6df 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { AppState } from "../types"; +import { AppProps, AppState } from "../types"; import { ActionManager } from "../actions/manager"; import { t } from "../i18n"; import Stack from "./Stack"; @@ -7,7 +7,6 @@ import { showSelectedShapeActions } from "../element"; import { NonDeletedExcalidrawElement } from "../element/types"; import { FixedSideContainer } from "./FixedSideContainer"; import { Island } from "./Island"; -import { HintViewer } from "./HintViewer"; import { calculateScrollCenter, getSelectedElements } from "../scene"; import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; import { Section } from "./Section"; @@ -18,8 +17,11 @@ import { UserList } from "./UserList"; import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle"; import { LibraryButton } from "./LibraryButton"; import { PenModeButton } from "./PenModeButton"; +import { ToolButton } from "./ToolButton"; +import { home } from "./icons"; type MobileMenuProps = { + onHomeButtonClick?: () => void; appState: AppState; actionManager: ActionManager; renderJSONExportDialog: () => React.ReactNode; @@ -42,11 +44,14 @@ type MobileMenuProps = { renderTopRightUI?: ( isMobile: boolean, appState: AppState, + canvas: HTMLCanvasElement | null, ) => JSX.Element | null; renderStats: () => JSX.Element | null; + UIOptions: AppProps["UIOptions"]; }; export const MobileMenu = ({ + onHomeButtonClick, appState, elements, libraryMenu, @@ -65,10 +70,13 @@ export const MobileMenu = ({ onImageAction, renderTopRightUI, renderStats, + UIOptions, }: MobileMenuProps) => { const renderToolbar = () => { return ( + {/* placeholder for grid 3-column template */} +
{(heading) => ( @@ -89,7 +97,15 @@ export const MobileMenu = ({ /> - {renderTopRightUI && renderTopRightUI(true, appState)} + )}
- +
+ {renderTopRightUI?.(true, appState, canvas)} +
); }; @@ -126,16 +144,19 @@ export const MobileMenu = ({ getSelectedElements(elements, appState).length === 0; if (viewModeEnabled) { - return ( + return UIOptions.canvasActions ? (
{actionManager.renderAction("toggleCanvasMenu")}
+ ) : ( +
Excalidraw
); } return (
- {actionManager.renderAction("toggleCanvasMenu")} + {UIOptions.canvasActions && + actionManager.renderAction("toggleCanvasMenu")} {actionManager.renderAction("toggleEditMenu")} {actionManager.renderAction("undo")} @@ -196,7 +217,7 @@ export const MobileMenu = ({ }} > - {appState.openMenu === "canvas" ? ( + {UIOptions.canvasActions && appState.openMenu === "canvas" ? (
@@ -207,6 +228,7 @@ export const MobileMenu = ({ {t("labels.collaborators")} diff --git a/src/components/PublishLibrary.tsx b/src/components/PublishLibrary.tsx index 37543b6b063a..5491db14d856 100644 --- a/src/components/PublishLibrary.tsx +++ b/src/components/PublishLibrary.tsx @@ -225,10 +225,13 @@ const PublishLibrary = ({ formData.append("twitterHandle", libraryData.twitterHandle); formData.append("website", libraryData.website); - fetch(`${process.env.REACT_APP_LIBRARY_BACKEND}/submit`, { - method: "post", - body: formData, - }) + fetch( + `https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries/submit`, + { + method: "post", + body: formData, + }, + ) .then( (response) => { if (response.ok) { diff --git a/src/components/ToolIcon.scss b/src/components/ToolIcon.scss index 7057110c9f33..0f40a3119de4 100644 --- a/src/components/ToolIcon.scss +++ b/src/components/ToolIcon.scss @@ -187,7 +187,7 @@ .ToolIcon.ToolIcon_type_floating { display: inline-block; position: absolute; - right: -8px; + right: calc(-2 * var(--space-factor)); margin-left: 0; border-radius: 20px 0 0 20px; diff --git a/src/components/UserList.scss b/src/components/UserList.scss index 139d1b2dc09b..feadde6f6b39 100644 --- a/src/components/UserList.scss +++ b/src/components/UserList.scss @@ -1,21 +1,50 @@ .excalidraw { + $marginTop: 60px; + // eye-balled + $bottomOffset: 60px; + .UserList { pointer-events: none; /*github corner*/ - padding: var(--space-factor) var(--space-factor) var(--space-factor) - var(--space-factor); + padding: var(--space-factor); display: flex; - flex-wrap: wrap; justify-content: flex-end; + overflow: hidden; + border-radius: 60px; + &:empty { display: none; } + + &.layout-vertical { + grid-column: 3; + flex-direction: column-reverse; + position: absolute; + top: $marginTop; + right: var(--space-factor); + max-height: calc( + 100vh - var(--space-factor) - #{$marginTop} - #{$bottomOffset} + + var(--itemOffset) + ); + + .Avatar { + width: 2.4rem; + height: 2.4rem; + } + + padding-bottom: max(calc(var(--itemOffset) * -1), 0px); + } } .UserList > * { pointer-events: all; - margin: 0 0 var(--space-factor) var(--space-factor); + } + .UserList.layout-vertical > * { + margin-bottom: var(--itemOffset); + } + .UserList.layout-horizontal > * { + margin-right: var(--itemOffset); } .UserList_mobile { diff --git a/src/components/UserList.tsx b/src/components/UserList.tsx index b9f7a36a08dc..3798f1132d78 100644 --- a/src/components/UserList.tsx +++ b/src/components/UserList.tsx @@ -11,7 +11,14 @@ export const UserList: React.FC<{ mobile?: boolean; collaborators: AppState["collaborators"]; actionManager: ActionManager; -}> = ({ className, mobile, collaborators, actionManager }) => { + layout: "vertical" | "horizontal"; +}> = ({ className, mobile, collaborators, actionManager, layout }) => { + const threshold = layout === "vertical" ? 6 : 3; + const offset = + collaborators.size > threshold + ? Math.min(collaborators.size - threshold, 15) * -2 + : 4; + const uniqueCollaborators = new Map(); collaborators.forEach((collaborator, socketId) => { @@ -45,7 +52,14 @@ export const UserList: React.FC<{ }); return ( -
+
{avatars}
); diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 9e0ce7e84309..41cc762293cc 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -938,3 +938,7 @@ export const editIcon = createIcon( export const eraser = createIcon( , ); + +export const home = createIcon( + "M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z", +); diff --git a/src/constants.ts b/src/constants.ts index b1638f39f1cb..bcbb94d52e30 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,5 @@ import cssVariables from "./css/variables.module.scss"; -import { AppProps } from "./types"; +import { AppProps, CanvasActions, ExportOpts } from "./types"; import { FontFamilyValues } from "./element/types"; export const APP_NAME = "Excalidraw"; @@ -142,7 +142,12 @@ export const URL_HASH_KEYS = { addLibrary: "addLibrary", } as const; -export const DEFAULT_UI_OPTIONS: AppProps["UIOptions"] = { +export const DEFAULT_UI_OPTIONS: Merge< + AppProps["UIOptions"], + { + canvasActions: Merge, { export: ExportOpts }>; + } +> = { canvasActions: { changeViewBackgroundColor: true, clearCanvas: true, @@ -159,7 +164,7 @@ export const DEFAULT_UI_OPTIONS: AppProps["UIOptions"] = { // sm screen export const MQ_SM_MAX_WIDTH = 640; // md screen -export const MQ_MAX_WIDTH_PORTRAIT = 730; +export const MQ_MAX_WIDTH_PORTRAIT = 800; export const MQ_MAX_WIDTH_LANDSCAPE = 1000; export const MQ_MAX_HEIGHT_LANDSCAPE = 500; // sidebar diff --git a/src/css/styles.scss b/src/css/styles.scss index 920f7e7aeacc..c63c010a34f5 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -249,9 +249,8 @@ .App-top-bar { z-index: var(--zIndex-layerUI); - display: flex; - flex-direction: column; - align-items: center; + display: grid; + grid-template-columns: 1fr max-content 1fr; } .App-bottom-bar { @@ -584,6 +583,30 @@ ::-webkit-scrollbar-thumb:active { background: var(--button-gray-2); } + + .HomeButton.ToolIcon.ToolIcon_type_floating { + display: inline-block; + position: absolute; + top: 60px; + left: calc(-2 * var(--space-factor)); + right: auto; + border-radius: 0 20px 20px 0; + + background-color: var(--button-gray-1); + &:hover { + background-color: var(--button-gray-1); + } + &:active { + background-color: var(--button-gray-2); + } + + .ToolIcon__icon { + border-radius: inherit; + } + svg { + position: static; + } + } } .ErrorSplash.excalidraw { diff --git a/src/css/theme.scss b/src/css/theme.scss index 1827a74bc2ad..b28e1fac84b6 100644 --- a/src/css/theme.scss +++ b/src/css/theme.scss @@ -78,7 +78,7 @@ --popup-bg-color: #2c2c2c; --popup-secondary-bg-color: #222; --popup-text-color: #{$oc-gray-4}; - --popup-text-inverted-color: #2c2c2c; + --popup-text-inverted-color: #635c5c; --select-highlight-color: #{$oc-blue-4}; --shadow-island: 1px 1px 5px #{transparentize($oc-black, 0.7)}; --text-primary-color: #{$oc-gray-4}; diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 8f35772e3629..2ab49905526a 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -1,29 +1,14 @@ import LanguageDetector from "i18next-browser-languagedetector"; import { useCallback, useEffect, useRef, useState } from "react"; import { trackEvent } from "../analytics"; -import { getDefaultAppState } from "../appState"; import { ErrorDialog } from "../components/ErrorDialog"; import { TopErrorBoundary } from "../components/TopErrorBoundary"; -import { - APP_NAME, - COOKIES, - EVENT, - TITLE_TIMEOUT, - VERSION_TIMEOUT, -} from "../constants"; +import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants"; import { loadFromBlob } from "../data/blob"; -import { - ExcalidrawElement, - FileId, - NonDeletedExcalidrawElement, -} from "../element/types"; +import { ExcalidrawElement, FileId } from "../element/types"; import { useCallbackRefState } from "../hooks/useCallbackRefState"; import { t } from "../i18n"; -import { - Excalidraw, - defaultLang, - languages, -} from "../packages/excalidraw/index"; +import { Excalidraw, defaultLang } from "../packages/excalidraw/index"; import { AppState, LibraryItems, @@ -51,9 +36,7 @@ import Collab, { collabDialogShownAtom, isCollaboratingAtom, } from "./collab/Collab"; -import { LanguageList } from "./components/LanguageList"; import { - exportToBackend, getCollaborationLinkData, isCollaborationLink, loadScene, @@ -65,11 +48,8 @@ import { } from "./data/localStorage"; import CustomStats from "./CustomStats"; import { restore, restoreAppState, RestoredDataState } from "../data/restore"; -import { Tooltip } from "../components/Tooltip"; -import { shield } from "../components/icons"; import "./index.scss"; -import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus"; import { updateStaleImageStatuses } from "./data/FileManager"; import { newElementWith } from "../element/mutateElement"; @@ -83,10 +63,6 @@ import { jotaiStore, useAtomWithInitialValue } from "../jotai"; import { reconcileElements } from "./collab/reconciliation"; import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library"; -const isExcalidrawPlusSignedUser = document.cookie.includes( - COOKIES.AUTH_STATE_COOKIE, -); - const languageDetector = new LanguageDetector(); languageDetector.init({ languageUtils: {}, @@ -197,38 +173,14 @@ const initializeScene = async (opts: { return { scene: null, isExternalScene: false }; }; -const PlusLPLinkJSX = ( -

- Introducing Excalidraw+ -
- - Try out now! - -

-); - -const PlusAppLinkJSX = ( - - Go to Excalidraw+ - -); - const ExcalidrawWrapper = () => { const [errorMessage, setErrorMessage] = useState(""); let currentLangCode = languageDetector.detect() || defaultLang.code; if (Array.isArray(currentLangCode)) { currentLangCode = currentLangCode[0]; } - const [langCode, setLangCode] = useState(currentLangCode); + const [langCode] = useState(currentLangCode); + // initial state // --------------------------------------------------------------------------- @@ -395,7 +347,6 @@ const ExcalidrawWrapper = () => { if (Array.isArray(langCode)) { langCode = langCode[0]; } - setLangCode(langCode); excalidrawAPI.updateScene({ ...localDataState, }); @@ -536,124 +487,16 @@ const ExcalidrawWrapper = () => { } }; - const onExportToBackend = async ( - exportedElements: readonly NonDeletedExcalidrawElement[], - appState: AppState, - files: BinaryFiles, - canvas: HTMLCanvasElement | null, - ) => { - if (exportedElements.length === 0) { - return window.alert(t("alerts.cannotExportEmptyCanvas")); - } - if (canvas) { - try { - await exportToBackend( - exportedElements, - { - ...appState, - viewBackgroundColor: appState.exportBackground - ? appState.viewBackgroundColor - : getDefaultAppState().viewBackgroundColor, - }, - files, - ); - } catch (error: any) { - if (error.name !== "AbortError") { - const { width, height } = canvas; - console.error(error, { width, height }); - setErrorMessage(error.message); - } - } - } - }; - const renderTopRightUI = useCallback( (isMobile: boolean, appState: AppState) => { - if (isMobile) { - return null; - } - - return ( -
- {isExcalidrawPlusSignedUser ? PlusAppLinkJSX : PlusLPLinkJSX} -
- ); + return
placeholder
; }, [], ); - const renderFooter = useCallback( - (isMobile: boolean) => { - const renderEncryptedIcon = () => ( - - - {shield} - - - ); - - const renderLanguageList = () => ( - setLangCode(langCode)} - languages={languages} - currentLangCode={langCode} - /> - ); - if (isMobile) { - const isTinyDevice = window.innerWidth < 362; - return ( -
-
- {t("labels.language")} - {renderLanguageList()} -
- {/* FIXME remove after 2021-05-20 */} -
- {isExcalidrawPlusSignedUser ? PlusAppLinkJSX : PlusLPLinkJSX} -
-
- ); - } - return ( - <> - {renderEncryptedIcon()} - {renderLanguageList()} - - ); - }, - [langCode], - ); + const renderFooter = useCallback((isMobile: boolean) => { + return <>; + }, []); const renderCustomStats = () => { return ( @@ -680,6 +523,7 @@ const ExcalidrawWrapper = () => { })} > { onPointerUpdate={collabAPI?.onPointerUpdate} UIOptions={{ canvasActions: { - export: { - onExportToBackend, - renderCustomUI: (elements, appState, files) => { - return ( - { - excalidrawAPI?.updateScene({ - appState: { - errorMessage: error.message, - }, - }); - }} - /> - ); - }, - }, + clearCanvas: false, + export: () => {}, }, }} renderTopRightUI={renderTopRightUI} @@ -717,6 +544,7 @@ const ExcalidrawWrapper = () => { handleKeyboardGlobally={true} onLibraryChange={onLibraryChange} autoFocus={true} + theme="light" /> {excalidrawAPI && } {errorMessage && ( diff --git a/src/global.d.ts b/src/global.d.ts index 3bff2b5daeb1..cddb1611c469 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -6,6 +6,10 @@ interface Document { type: "loading" | "loadingdone" | "loadingerror", listener: (this: Document, ev: Event) => any, ): void; + removeEventListener?( + type: "loading" | "loadingdone" | "loadingerror", + listener: (this: Document, ev: Event) => any, + ): void; }; } @@ -31,6 +35,8 @@ interface Clipboard extends EventTarget { write(data: any[]): Promise; } +type Merge = Omit & N; + type Mutable = { -readonly [P in keyof T]: T[P]; }; diff --git a/src/i18n.ts b/src/i18n.ts index cf6ab15065a0..88bc6bc2b7fd 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -87,7 +87,7 @@ export const setLanguage = async (lang: Language) => { currentLangData = {}; } else { currentLangData = await import( - /* webpackChunkName: "locales/[request]" */ `./locales/${currentLang.code}.json` + /* webpackChunkName: "locales/[request]" */ `./locales/en.json` ); } }; diff --git a/src/locales/en.json b/src/locales/en.json index 31570400b4d8..cf911e260b98 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -71,7 +71,7 @@ "layers": "Layers", "actions": "Actions", "language": "Language", - "liveCollaboration": "Live collaboration", + "liveCollaboration": "Sharing", "duplicateSelection": "Duplicate", "untitled": "Untitled", "name": "Name", @@ -138,7 +138,7 @@ "copyToClipboard": "Copy to clipboard", "copyPngToClipboard": "Copy PNG to clipboard", "scale": "Scale", - "save": "Save to current file", + "save": "Save (to active file)", "saveAs": "Save as", "load": "Load", "getShareableLink": "Get shareable link", @@ -154,6 +154,7 @@ "undo": "Undo", "redo": "Redo", "resetLibrary": "Reset library", + "roomDialog": "Sharing", "createNewRoom": "Create new room", "fullScreen": "Full screen", "darkMode": "Dark mode", diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index fc567a5cb26e..2bf42b7a2cff 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -14,6 +14,7 @@ import { jotaiScope, jotaiStore } from "../../jotai"; const ExcalidrawBase = (props: ExcalidrawProps) => { const { + onHomeButtonClick, onChange, initialData, excalidrawRef, @@ -39,6 +40,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => { onLinkOpen, onPointerDown, onScrollChange, + id, } = props; const canvasActions = props.UIOptions?.canvasActions; @@ -51,9 +53,9 @@ const ExcalidrawBase = (props: ExcalidrawProps) => { }, }; - if (canvasActions?.export) { - UIOptions.canvasActions.export.saveFileToDisk = - canvasActions.export?.saveFileToDisk ?? + if (canvasActions && typeof canvasActions.export === "object") { + canvasActions.export.saveFileToDisk = + canvasActions.export?.saveFileToDisk || DEFAULT_UI_OPTIONS.canvasActions.export.saveFileToDisk; } @@ -79,6 +81,8 @@ const ExcalidrawBase = (props: ExcalidrawProps) => { jotaiStore} scope={jotaiScope}> )[]; canvasOptionKeys.every((key) => { + if (!prevUIOptions?.canvasActions || !nextUIOptions?.canvasActions) { + return prevUIOptions?.canvasActions === nextUIOptions?.canvasActions; + } if ( key === "export" && prevUIOptions?.canvasActions?.export && nextUIOptions?.canvasActions?.export ) { + if ( + typeof prevUIOptions.canvasActions.export === "function" || + typeof nextUIOptions.canvasActions.export === "function" + ) { + return ( + prevUIOptions.canvasActions.export === + nextUIOptions.canvasActions.export + ); + } return ( prevUIOptions.canvasActions.export.saveFileToDisk === nextUIOptions.canvasActions.export.saveFileToDisk @@ -194,8 +210,8 @@ export { restoreLibraryItems, } from "../../data/restore"; export { - exportToCanvas, exportToBlob, + exportToCanvas, exportToSvg, serializeAsJSON, serializeLibraryAsJSON, @@ -225,3 +241,11 @@ export { sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, } from "../../utils"; +export { + getDefaultAppState, + cleanAppStateForExport, + clearAppStateForLocalStorage, +} from "../../appState"; + +export { jotaiScope, jotaiStore } from "../../jotai"; +export { libraryItemsAtom } from "../../data/library"; diff --git a/src/packages/excalidraw/main.js b/src/packages/excalidraw/main.js index b1668faf70a4..25c55a77b099 100644 --- a/src/packages/excalidraw/main.js +++ b/src/packages/excalidraw/main.js @@ -1,4 +1,7 @@ -if (process.env.NODE_ENV === "production") { +if ( + process.env.NODE_ENV === "production" || + window.__excalidraw_env__ === "production" +) { module.exports = require("./dist/excalidraw.production.min.js"); } else { module.exports = require("./dist/excalidraw.development.js"); diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index 4c3397a51547..f3c3acb21dd8 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { - "name": "@excalidraw/excalidraw", - "version": "0.12.0", + "name": "@dwelle/excalidraw", + "version": "0.3.73", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ @@ -11,15 +11,8 @@ "access": "public" }, "description": "Excalidraw as a React component", - "repository": "https://github.com/excalidraw/excalidraw", "license": "MIT", - "keywords": [ - "excalidraw", - "excalidraw-embed", - "react", - "npm", - "npm excalidraw" - ], + "keywords": [], "browserslist": { "production": [ ">0.2%", @@ -69,7 +62,7 @@ "webpack-dev-server": "4.9.3", "webpack-merge": "5.8.0" }, - "bugs": "https://github.com/excalidraw/excalidraw/issues", + "repository": "https://github.com/dwelle/excalidraw", "homepage": "https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw", "scripts": { "gen:types": "tsc --project ../../../tsconfig-types.json", diff --git a/src/packages/utils/package.json b/src/packages/utils/package.json index 500b52617cab..2969e8a417b4 100644 --- a/src/packages/utils/package.json +++ b/src/packages/utils/package.json @@ -34,7 +34,7 @@ ] }, "devDependencies": { - "@babel/core": "7.17.2", + "@babel/core": "7.18.6", "@babel/plugin-transform-arrow-functions": "7.16.0", "@babel/plugin-transform-async-to-generator": "7.16.5", "@babel/plugin-transform-runtime": "7.18.6", diff --git a/src/packages/utils/yarn.lock b/src/packages/utils/yarn.lock index bc56dbf34dda..23e3db0a9c07 100644 --- a/src/packages/utils/yarn.lock +++ b/src/packages/utils/yarn.lock @@ -2,54 +2,60 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.0.tgz#72becdf17ee44b2d1ac5651fb12f1952c336fe23" - integrity sha512-d5RysTlJ7hmw5Tw4UxgxcY3lkMe92n8sXCcuLPAyIAHK6j8DefDwtGnVVDgOnv+RnEosulDJ9NPKQL27bDId0g== +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== -"@babel/core@7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.2.tgz#2c77fc430e95139d816d39b113b31bf40fb22337" - integrity sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw== - dependencies: - "@ampproject/remapping" "^2.0.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.0" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.0" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" - "@babel/types" "^7.17.0" +"@babel/compat-data@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + +"@babel/core@7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.6.tgz#54a107a3c298aee3fe5e1947a6464b9b6faca03d" + integrity sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helpers" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" + json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.0.tgz#7bd890ba706cd86d3e2f727322346ffdbf98f65e" - integrity sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw== +"@babel/generator@^7.18.6", "@babel/generator@^7.18.7": + version "7.18.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd" + integrity sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.18.7" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" - source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.16.0": version "7.16.0" @@ -73,14 +79,14 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz#18d35bfb9f83b1293c22c55b3d576c1315b6ed96" + integrity sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg== dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + "@babel/compat-data" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.0": @@ -152,6 +158,11 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7" + integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q== + "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" @@ -177,6 +188,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-function-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83" + integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw== + dependencies: + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.6" + "@babel/helper-get-function-arity@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" @@ -198,6 +217,13 @@ dependencies: "@babel/types" "^7.16.7" +"@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" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-member-expression-to-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" @@ -212,19 +238,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" - integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== +"@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz#4f8408afead0188cfa48672f9d0e5787b61778c8" + integrity sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@babel/types" "^7.18.8" "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" @@ -274,6 +300,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" @@ -288,6 +321,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" @@ -303,6 +343,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@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" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helper-wrap-function@^7.16.5": version "7.16.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.5.tgz#0158fca6f6d0889c3fee8a6ed6e5e07b9b54e41f" @@ -323,28 +368,28 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helpers@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" - integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== +"@babel/helpers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.6.tgz#4c966140eaa1fcaa3d5a8c09d7db61077d4debfd" + integrity sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ== dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" - "@babel/types" "^7.17.0" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" -"@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.16.7", "@babel/parser@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.0.tgz#f0ac33eddbe214e4105363bb17c3341c5ffcc43c" - integrity sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw== +"@babel/parser@^7.18.6", "@babel/parser@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf" + integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" @@ -998,43 +1043,35 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.16.0", "@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.16.0", "@babel/template@^7.16.7", "@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.5", "@babel/traverse@^7.16.7", "@babel/traverse@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.0.tgz#3143e5066796408ccc880a33ecd3184f3e75cd30" - integrity sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.0" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.0" - "@babel/types" "^7.17.0" +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.5", "@babel/traverse@^7.16.7", "@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0" + integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.8" + "@babel/types" "^7.18.8" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.6": - version "7.18.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.7.tgz#a4a2c910c15040ea52cdd1ddb1614a65c8041726" - integrity sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ== +"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8", "@babel/types@^7.4.4": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f" + integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw== dependencies: "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" @@ -1044,20 +1081,42 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz#b876e3feefb9c8d3aa84014da28b5e52a0640d72" integrity sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg== +"@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" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.10" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.10.tgz#baf57b4e2a690d4f38560171f91783656b7f8186" integrity sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg== -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@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" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1465,7 +1524,7 @@ braces@^3.0.1: dependencies: fill-range "^7.0.1" -browserslist@^4.14.5, browserslist@^4.17.5, browserslist@^4.17.6, browserslist@^4.19.1: +browserslist@^4.14.5, browserslist@^4.17.6, browserslist@^4.19.1: version "4.19.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.3.tgz#29b7caad327ecf2859485f696f9604214bedd383" integrity sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg== @@ -1476,6 +1535,16 @@ browserslist@^4.14.5, browserslist@^4.17.5, browserslist@^4.17.6, browserslist@^ node-releases "^2.0.2" picocolors "^1.0.0" +browserslist@^4.20.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" + integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== + dependencies: + caniuse-lite "^1.0.30001366" + electron-to-chromium "^1.4.188" + node-releases "^2.0.6" + update-browserslist-db "^1.0.4" + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1494,6 +1563,11 @@ caniuse-lite@^1.0.30001312: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== +caniuse-lite@^1.0.30001366: + version "1.0.30001366" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz#c73352c83830a9eaf2dea0ff71fb4b9a4bbaa89c" + integrity sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA== + chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1671,6 +1745,11 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +electron-to-chromium@^1.4.188: + version "1.4.189" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.189.tgz#4e5b221dc44e09e9dddc9abbc6457857dee7ba25" + integrity sha512-dQ6Zn4ll2NofGtxPXaDfY2laIa6NyCQdqXYHdwH90GJQW0LpJJib0ZU/ERtbb0XkBEmUD2eJtagbOie3pdMiPg== + electron-to-chromium@^1.4.71: version "1.4.75" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz#d1ad9bb46f2f1bf432118c2be21d27ffeae82fdd" @@ -1994,12 +2073,10 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json5@^2.1.2, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== kind-of@^6.0.2: version "6.0.3" @@ -2098,11 +2175,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2128,6 +2200,11 @@ node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -2470,11 +2547,6 @@ source-map-support@~0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -2614,6 +2686,14 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= +update-browserslist-db@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" + integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 92f48a76b61e..3bd3dbcaad22 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -300,12 +300,12 @@ const drawElementOnCanvas = ( context.globalAlpha = 1; }; -const elementWithCanvasCache = new WeakMap< +let elementWithCanvasCache = new WeakMap< ExcalidrawElement, ExcalidrawElementWithCanvas >(); -const shapeCache = new WeakMap(); +let shapeCache = new WeakMap(); type ElementShape = Drawable | Drawable[] | null; @@ -332,6 +332,12 @@ export const setShapeForElement = ( export const invalidateShapeForElement = (element: ExcalidrawElement) => shapeCache.delete(element); +export const clearRenderCache = () => { + elementWithCanvasCache = new WeakMap(); + shapeCache = new WeakMap(); + pathsCache = new WeakMap(); +}; + export const generateRoughOptions = ( element: ExcalidrawElement, continuousPath = false, @@ -1044,7 +1050,7 @@ export const renderElementToSvg = ( } }; -export const pathsCache = new WeakMap([]); +export let pathsCache = new WeakMap([]); export function generateFreeDrawShape(element: ExcalidrawFreeDrawElement) { const svgPathData = getFreeDrawSvgPath(element); diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 9df354078a73..4eecf7acc8a5 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -322,7 +322,11 @@ export const renderScene = ( selectionColors.push( ...renderConfig.remoteSelectedElementIds[element.id].map( (socketId) => { - const { background } = getClientColors(socketId, appState); + const picture = appState.collaborators.get(socketId)?.avatarUrl; + const { background } = getClientColors( + picture || socketId, + appState, + ); return background; }, ), @@ -430,7 +434,6 @@ export const renderScene = ( // Paint remote pointers for (const clientId in renderConfig.remotePointerViewportCoords) { let { x, y } = renderConfig.remotePointerViewportCoords[clientId]; - x -= appState.offsetLeft; y -= appState.offsetTop; @@ -448,7 +451,11 @@ export const renderScene = ( y = Math.max(y, 0); y = Math.min(y, normalizedCanvasHeight - height); - const { background, stroke } = getClientColors(clientId, appState); + const picture = appState.collaborators.get(clientId)?.avatarUrl; + const { background, stroke } = getClientColors( + picture || clientId, + appState, + ); context.save(); context.strokeStyle = stroke; diff --git a/src/scene/export.ts b/src/scene/export.ts index 9dacc755b2d8..6b3ec048e9c3 100644 --- a/src/scene/export.ts +++ b/src/scene/export.ts @@ -135,11 +135,11 @@ export const exportToSvg = async ( diff --git a/src/types.ts b/src/types.ts index 1539d533c041..e44367bc8929 100644 --- a/src/types.ts +++ b/src/types.ts @@ -248,6 +248,8 @@ export type ExcalidrawAPIRefValue = export type ExcalidrawInitialDataState = Merge< ImportedDataState, { + scrollX?: number; + scrollY?: number; libraryItems?: | Required["libraryItems"] | Promise["libraryItems"]>; @@ -255,15 +257,21 @@ export type ExcalidrawInitialDataState = Merge< >; export interface ExcalidrawProps { + id?: string | null; onChange?: ( elements: readonly ExcalidrawElement[], appState: AppState, files: BinaryFiles, + id?: string | null, ) => void; initialData?: | ExcalidrawInitialDataState | null | Promise; + onHomeButtonClick?: () => void; + user?: { + name?: string | null; + }; excalidrawRef?: ForwardRef; onCollabButtonClick?: () => void; isCollaborating?: boolean; @@ -279,6 +287,7 @@ export interface ExcalidrawProps { renderTopRightUI?: ( isMobile: boolean, appState: AppState, + canvas: HTMLCanvasElement | null, ) => JSX.Element | null; renderFooter?: (isMobile: boolean, appState: AppState) => JSX.Element | null; langCode?: Language["code"]; @@ -343,28 +352,29 @@ export type ExportOpts = { ) => JSX.Element; }; -type CanvasActions = { +export type CanvasActions = { changeViewBackgroundColor?: boolean; clearCanvas?: boolean; - export?: false | ExportOpts; + export?: false | ExportOpts | (() => void); loadScene?: boolean; saveToActiveFile?: boolean; theme?: boolean; saveAsImage?: boolean; }; -export type AppProps = Merge< - ExcalidrawProps, - { - UIOptions: { - canvasActions: Required & { export: ExportOpts }; - dockedSidebarBreakpoint?: number; - }; - detectScroll: boolean; - handleKeyboardGlobally: boolean; - isCollaborating: boolean; - } ->; +export type UIOptions = { + canvasActions?: CanvasActions | false; +}; + +export type AppProps = ExcalidrawProps & { + UIOptions: { + canvasActions: Required | false; + dockedSidebarBreakpoint?: number; + }; + detectScroll: boolean; + handleKeyboardGlobally: boolean; + isCollaborating: boolean; +}; /** A subset of App class properties that we need to use elsewhere * in the app, eg Manager. Factored out into a separate type to keep DRY. */ @@ -475,6 +485,7 @@ export type ExcalidrawImperativeAPI = { setActiveTool: InstanceType["setActiveTool"]; setCursor: InstanceType["setCursor"]; resetCursor: InstanceType["resetCursor"]; + app: InstanceType; }; export type Device = Readonly<{