Compare commits

..

8 Commits

Author SHA1 Message Date
tk338g
232412d7bc Test fixup 2021-02-19 21:17:58 +03:00
tk338g
6a8680f500 Moved minimap rendering to offscreen canvas 2021-02-19 21:07:09 +03:00
tk338g
3b0aff0ac6 Fix minimap rerendering on scroll 2021-02-19 20:12:48 +03:00
tk338g
93d0a56bdb Check for NaN before applying styles 2021-02-19 20:12:48 +03:00
tk338g
0a3675e1b9 Update test snapshots 2021-02-19 20:12:48 +03:00
tk338g
cf35caaf23 Toggle minimap with "M" key 2021-02-19 20:12:48 +03:00
tk338g
4e3bf7e8d2 Use one canvas for minimap and preserve viewport borders 2021-02-19 20:12:47 +03:00
tk338g
4c3544df4a Simple minimap implementation 2021-02-19 20:12:47 +03:00
74 changed files with 59915 additions and 20654 deletions

View File

@@ -1,10 +1,10 @@
*
!.env
!.eslintrc.json
!.npmrc
!.prettierrc
!package.json
!public/
!src/
!.npmrc
!.eslintrc.json
!.prettierrc
!package-lock.json
!package.json
!tsconfig.json
!yarn.lock
!.env

View File

@@ -1,33 +1,36 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
directory: "/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- lipis
assignees:
- lipis
- package-ecosystem: npm
directory: /src/packages/excalidraw/
directory: "/src/packages/excalidraw/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:
- ad1992
- package-ecosystem: npm
directory: /src/packages/utils/
directory: "/src/packages/utils/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:

View File

@@ -6,8 +6,9 @@ on:
- master
jobs:
build-docker:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
- run: docker build -t excalidraw .

View File

@@ -7,23 +7,27 @@ on:
pull_request:
jobs:
packages:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn --cwd src/packages/excalidraw
yarn --cwd src/packages/utils
npm ci
npm ci --prefix src/packages/excalidraw
npm ci --prefix src/packages/utils
- name: Build @excalidraw/excalidraw
run: |
yarn --cwd src/packages/excalidraw run pack
npm run pack --prefix src/packages/excalidraw
- name: Build @excalidraw/utils
run: |
yarn --cwd src/packages/utils run pack
npm run pack --prefix src/packages/utils

View File

@@ -9,6 +9,7 @@ on:
jobs:
cancel:
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: styfle/cancel-workflow-action@0.6.0

View File

@@ -7,16 +7,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install and lint
run: |
yarn --frozen-lockfile
yarn test:other
yarn test:code
yarn test:typecheck
npm ci
npm run test:other
npm run test:code
npm run test:typecheck

View File

@@ -3,7 +3,7 @@ name: Build locales coverage
on:
push:
branches:
- l10n_master
- "l10n_master"
jobs:
locales:
@@ -15,13 +15,13 @@ jobs:
token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }}
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Create report file
run: |
yarn locales-coverage
npm run locales-coverage
FILE_CHANGED=$(git diff src/locales/percentages.json)
if [ ! -z "${FILE_CHANGED}" ]; then
git config --global user.name 'Excalidraw Bot'
@@ -33,7 +33,7 @@ jobs:
- name: Construct comment body
id: getCommentBody
run: |
body=$(yarn locales-coverage:description | grep '^[^>]')
body=$(npm run locales-coverage:description | grep '^[^>]')
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"

View File

@@ -1,4 +1,4 @@
name: Semantic PR title
name: "Semantic PR title"
on:
pull_request_target:
@@ -8,8 +8,9 @@ on:
- synchronize
jobs:
semantic:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v3.0.0
env:

View File

@@ -1,4 +1,4 @@
name: New Sentry production release
name: New Sentry Production Release
on:
push:
@@ -6,23 +6,28 @@ on:
- master
jobs:
sentry:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1.0.0
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install and build
run: |
yarn --frozen-lockfile
yarn build:app
npm ci
npm run build:app
env:
CI: true
- name: Install Sentry
run: |
curl -sL https://sentry.io/get-cli/ | bash
- name: Create new Sentry release
run: |
export SENTRY_RELEASE=$(sentry-cli releases propose-version)

View File

@@ -5,13 +5,16 @@ on: pull_request
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install and test
run: |
yarn --frozen-lockfile
yarn test:app
npm ci
npm run test:app

2
.gitignore vendored
View File

@@ -16,7 +16,7 @@ firebase
logs
node_modules
npm-debug.log*
package-lock.json
static
yarn-debug.log*
yarn-error.log*
yarn.lock

4
.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"proseWrap": "never",
"trailingComma": "all"
}

View File

@@ -2,13 +2,13 @@ FROM node:14-alpine AS build
WORKDIR /opt/node_app
COPY package.json yarn.lock ./
RUN yarn --ignore-optional
COPY package.json package-lock.json ./
RUN npm i --no-optional
ARG NODE_ENV=production
COPY . .
RUN yarn build:app:docker
RUN npm run build:app:docker
FROM nginx:1.17-alpine

View File

@@ -86,12 +86,6 @@ Try out [`@excalidraw/excalidraw`](https://www.npmjs.com/package/@excalidraw/exc
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
#### Requirements
- [Node.js](https://nodejs.org/en/)
- [Yarn](https://yarnpkg.com/getting-started/install)
- [Git](https://git-scm.com/downloads)
#### Clone the repo
```bash
@@ -100,14 +94,14 @@ git clone https://github.com/excalidraw/excalidraw.git
#### Commands
| Command | Description |
| ------------------ | --------------------------------- |
| `yarn` | Install the dependencies |
| `yarn start` | Run the project |
| `yarn fix` | Reformat all files with Prettier |
| `yarn test` | Run tests |
| `yarn test:update` | Update test snapshots |
| `yarn test:code` | Test for formatting with Prettier |
| Command | Description |
| --------------------- | --------------------------------- |
| `npm install` | Install the dependencies |
| `npm start` | Run the project |
| `npm run fix` | Reformat all files with Prettier |
| `npm test` | Run tests |
| `npm run test:update` | Update test snapshots |
| `npm run test:code` | Test for formatting with Prettier |
#### Docker Compose

View File

@@ -18,7 +18,7 @@ services:
volumes:
- ./:/opt/node_app/app:delegated
- ./package.json:/opt/node_app/package.json
- ./yarn.lock:/opt/node_app/yarn.lock
- ./package-lock.json:/opt/node_app/package-lock.json
- notused:/opt/node_app/app/node_modules
volumes:

51482
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,20 +19,21 @@
]
},
"dependencies": {
"@sentry/browser": "6.2.0",
"@sentry/integrations": "6.2.0",
"@sentry/browser": "6.1.0",
"@sentry/integrations": "6.1.0",
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
"@types/jest": "26.0.20",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.1",
"@types/socket.io-client": "1.4.35",
"browser-fs-access": "0.14.0",
"browser-fs-access": "0.13.1",
"clsx": "1.1.1",
"firebase": "8.2.9",
"firebase": "8.2.7",
"i18next-browser-languagedetector": "6.0.1",
"lodash.throttle": "4.1.1",
"nanoid": "3.1.20",
"node-sass": "4.14.1",
"open-color": "1.8.0",
"pako": "1.0.11",
"png-chunk-text": "1.0.0",
@@ -42,29 +43,28 @@
"pwacompat": "2.0.17",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-scripts": "4.0.3",
"react-scripts": "4.0.2",
"roughjs": "4.3.1",
"sass": "1.32.8",
"socket.io-client": "2.3.1",
"typescript": "4.1.5"
},
"devDependencies": {
"@excalidraw/prettier-config": "1.0.2",
"@types/lodash.throttle": "4.1.6",
"@types/pako": "1.0.1",
"@types/resize-observer-browser": "0.1.5",
"eslint-config-prettier": "8.1.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-prettier": "3.3.1",
"firebase-tools": "9.5.0",
"firebase-tools": "9.3.0",
"husky": "4.3.8",
"jest-canvas-mock": "2.3.1",
"lint-staged": "10.5.4",
"pepjs": "0.5.3",
"prettier": "2.2.1",
"rewire": "5.0.0"
"rewire": "5.0.0",
"worker-loader": "3.0.8"
},
"engines": {
"node": ">=14.0.0"
"node": ">=12.0.0"
},
"homepage": ".",
"husky": {
@@ -76,32 +76,34 @@
"transformIgnorePatterns": [
"node_modules/(?!(roughjs|points-on-curve|path-data-parser|points-on-path|browser-fs-access)/)"
],
"moduleNameMapper": {
"^worker-loader!.+": "<rootDir>/src/__mocks__/worker-mock.js"
},
"resetMocks": false
},
"name": "excalidraw",
"prettier": "@excalidraw/prettier-config",
"private": true,
"scripts": {
"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",
"build:version": "node ./scripts/build-version.js",
"build": "yarn build:app && yarn build:version",
"build": "npm run build:app && npm run build:version",
"eject": "react-scripts eject",
"fix:code": "yarn test:code --fix",
"fix:other": "yarn prettier --write",
"fix": "yarn fix:other && yarn fix:code",
"fix:code": "npm run test:code -- --fix",
"fix:other": "npm run prettier -- --write",
"fix": "npm run fix:other && npm run fix:code",
"locales-coverage": "node scripts/build-locales-coverage.js",
"locales-coverage:description": "node scripts/locales-coverage-description.js",
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
"start": "react-scripts start",
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watchAll=false",
"test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false",
"test:app": "react-scripts test --passWithNoTests",
"test:code": "eslint --max-warnings=0 --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test:debug": "react-scripts --inspect-brk test --runInBand --no-cache",
"test:other": "yarn prettier --list-different",
"test:other": "npm run prettier -- --list-different",
"test:typecheck": "tsc",
"test:update": "yarn test:app --updateSnapshot --watchAll=false",
"test": "yarn test:app"
"test:update": "npm run test:app -- --updateSnapshot --watchAll=false",
"test": "npm run test:app"
}
}

BIN
public/Cascadia.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
public/FG_Virgil.otf Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +1,7 @@
/* http://www.eaglefonts.com/fg-virgil-ttf-131249.htm */
@font-face {
font-family: "Virgil";
src: url("Virgil.woff2");
src: url("FG_Virgil.woff2");
font-display: swap;
}

View File

@@ -60,7 +60,7 @@
<link
rel="preload"
href="Virgil.woff2"
href="FG_Virgil.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
@@ -86,9 +86,7 @@
/>
<link rel="stylesheet" href="fonts.css" type="text/css" />
<script>
window.EXCALIDRAW_ASSET_PATH = "/";
</script>
<% if (process.env.REACT_APP_GOOGLE_ANALYTICS_ID) { %>
<script
async

View File

@@ -5,7 +5,7 @@
// In order to run:
// npm install canvas # please do not check it in
// yarn build-node
// npm run build-node
// node build/static/js/build-node.js
// open test.png

View File

@@ -0,0 +1,4 @@
module.exports = class {
postMessage() {}
terminate() {}
};

View File

@@ -5,7 +5,6 @@ import { ProjectName } from "../components/ProjectName";
import { ToolButton } from "../components/ToolButton";
import "../components/ToolIcon.scss";
import { Tooltip } from "../components/Tooltip";
import { DarkModeToggle, Appearence } from "../components/DarkModeToggle";
import { loadFromJSON, saveAsJSON } from "../data";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
@@ -205,31 +204,3 @@ export const actionLoadScene = register({
/>
),
});
export const actionExportWithDarkMode = register({
name: "exportWithDarkMode",
perform: (_elements, appState, value) => {
return {
appState: { ...appState, exportWithDarkMode: value },
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData }) => (
<div
style={{
display: "flex",
justifyContent: "flex-end",
marginTop: "-45px",
marginBottom: "10px",
}}
>
<DarkModeToggle
value={appState.exportWithDarkMode ? "dark" : "light"}
onChange={(appearance: Appearence) => {
updateData(appearance === "dark");
}}
title={t("labels.toggleExportColorScheme")}
/>
</div>
),
});

View File

@@ -7,6 +7,7 @@ import { register } from "./register";
import { allowFullScreen, exitFullScreen, isFullScreen } from "../utils";
import { CODES, KEYS } from "../keys";
import { HelpIcon } from "../components/HelpIcon";
import { MiniMap } from "../components/MiniMap";
export const actionToggleCanvasMenu = register({
name: "toggleCanvasMenu",
@@ -84,3 +85,21 @@ export const actionShortcuts = register({
),
keyTest: (event) => event.key === KEYS.QUESTION_MARK,
});
export const actionMinimap = register({
name: "toggleMinimap",
perform: (_elements, appState) => {
return {
appState: {
...appState,
isMinimapEnabled: !appState.isMinimapEnabled,
},
commitToHistory: false,
};
},
PanelComponent: ({ appState, elements }) =>
appState.isMinimapEnabled ? (
<MiniMap appState={appState} elements={elements} />
) : null,
keyTest: (event) => event.key === KEYS.M,
});

View File

@@ -44,6 +44,7 @@ export {
actionToggleEditMenu,
actionFullScreen,
actionShortcuts,
actionMinimap,
} from "./actionMenu";
export { actionGroup, actionUngroup } from "./actionGroup";

View File

@@ -86,7 +86,7 @@ export type ActionName =
| "distributeHorizontally"
| "distributeVertically"
| "viewMode"
| "exportWithDarkMode";
| "toggleMinimap";
export interface Action {
name: ActionName;

View File

@@ -40,10 +40,9 @@ export const getDefaultAppState = (): Omit<
errorMessage: null,
exportBackground: true,
exportEmbedScene: false,
exportWithDarkMode: false,
fileHandle: null,
gridSize: null,
height: window.innerHeight,
height: globalThis.innerHeight,
isBindingEnabled: true,
isLibraryOpen: false,
isLoading: false,
@@ -70,10 +69,11 @@ export const getDefaultAppState = (): Omit<
suggestedBindings: [],
toastMessage: null,
viewBackgroundColor: oc.white,
width: window.innerWidth,
width: globalThis.innerWidth,
zenModeEnabled: false,
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
viewModeEnabled: false,
isMinimapEnabled: false,
};
};
@@ -119,7 +119,6 @@ const APP_STATE_STORAGE_CONF = (<
errorMessage: { browser: false, export: false },
exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false },
exportWithDarkMode: { browser: true, export: false },
fileHandle: { browser: false, export: false },
gridSize: { browser: true, export: true },
height: { browser: false, export: false },
@@ -155,6 +154,7 @@ const APP_STATE_STORAGE_CONF = (<
zenModeEnabled: { browser: true, export: false },
zoom: { browser: true, export: false },
viewModeEnabled: { browser: false, export: false },
isMinimapEnabled: { browser: true, export: false },
});
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(

View File

@@ -672,24 +672,19 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scene.appState = {
...scene.appState,
...calculateScrollCenter(
scene.elements,
{
...scene.appState,
width: this.state.width,
height: this.state.height,
offsetTop: this.state.offsetTop,
offsetLeft: this.state.offsetLeft,
},
null,
),
isLoading: false,
};
if (initialData?.scrollToCenter) {
scene.appState = {
...scene.appState,
...calculateScrollCenter(
scene.elements,
{
...scene.appState,
width: this.state.width,
height: this.state.height,
offsetTop: this.state.offsetTop,
offsetLeft: this.state.offsetLeft,
},
null,
),
};
}
this.resetHistory();
this.syncActionResult({

View File

@@ -10,18 +10,13 @@ export type Appearence = "light" | "dark";
export const DarkModeToggle = (props: {
value: Appearence;
onChange: (value: Appearence) => void;
title?: string;
}) => {
const title = props.title
? props.title
: props.value === "dark"
? t("buttons.lightMode")
: t("buttons.darkMode");
return (
<label
className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`}
title={title}
title={
props.value === "dark" ? t("buttons.lightMode") : t("buttons.darkMode")
}
>
<input
className="ToolIcon_type_checkbox ToolIcon_toggle_opaque"
@@ -30,7 +25,11 @@ export const DarkModeToggle = (props: {
props.onChange(event.target.checked ? "dark" : "light")
}
checked={props.value === "dark"}
aria-label={title}
aria-label={
props.value === "dark"
? t("buttons.lightMode")
: t("buttons.darkMode")
}
/>
<div className="ToolIcon__icon">
{props.value === "light" ? ICONS.MOON : ICONS.SUN}

View File

@@ -19,9 +19,6 @@ import { ToolButton } from "./ToolButton";
const scales = [1, 2, 3];
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
const supportsContextFilters =
"filter" in document.createElement("canvas").getContext("2d")!;
export const ErrorCanvasPreview = () => {
return (
<div>
@@ -104,6 +101,10 @@ const ExportModal = ({
shouldAddWatermark,
});
if (canvas instanceof OffscreenCanvas) {
return;
}
// if converting to blob fails, there's some problem that will
// likely prevent preview and export (e.g. canvas too big)
canvasToBlob(canvas)
@@ -131,8 +132,6 @@ const ExportModal = ({
return (
<div className="ExportDialog">
<div className="ExportDialog__preview" ref={previewRef} />
{supportsContextFilters &&
actionManager.renderAction("exportWithDarkMode")}
<Stack.Col gap={2} align="center">
<div className="ExportDialog__actions">
<Stack.Row gap={2}>

View File

@@ -602,6 +602,7 @@ const LayerUI = ({
>
{renderCustomFooter?.(false)}
{actionManager.renderAction("toggleShortcuts")}
{actionManager.renderAction("toggleMinimap")}
</div>
<button
className={clsx("disable-zen-mode", {

View File

@@ -0,0 +1,5 @@
.Island.MiniMap {
position: absolute;
bottom: 50px;
right: calc(var(--space-factor) * 4);
}

151
src/components/MiniMap.tsx Normal file
View File

@@ -0,0 +1,151 @@
import "./MiniMap.scss";
import React, { useEffect, useRef, useMemo, useState } from "react";
import { getCommonBounds, getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import { distance, viewportCoordsToSceneCoords } from "../utils";
import { Island } from "./Island";
// eslint-disable-next-line import/no-webpack-loader-syntax
import MinimapWorker from "worker-loader!../renderer/minimapWorker";
const RATIO = 1.2;
const MINIMAP_HEIGHT = 150;
const MINIMAP_WIDTH = MINIMAP_HEIGHT * RATIO;
const MinimapViewport = ({
elements,
appState,
}: {
elements: readonly ExcalidrawElement[];
appState: AppState;
}) => {
const [minX, minY, maxX, maxY] = useMemo(
() => getCommonBounds(getNonDeletedElements(elements)),
[elements],
);
const minimapScale = Math.min(
MINIMAP_WIDTH / distance(minX, maxX),
MINIMAP_HEIGHT / distance(minY, maxY),
);
const leftTop = viewportCoordsToSceneCoords(
{ clientX: 0, clientY: 0 },
appState,
);
const rightBot = viewportCoordsToSceneCoords(
{ clientX: appState.width, clientY: appState.height },
appState,
);
const top = (leftTop.y - minY) * minimapScale;
const left = (leftTop.x - minX) * minimapScale;
const width = (rightBot.x - leftTop.x) * minimapScale;
const height = (rightBot.y - leftTop.y) * minimapScale;
// Set viewport boundaries
const viewportTop = Math.min(Math.max(0, top), MINIMAP_HEIGHT);
const viewportLeft = Math.min(Math.max(0, left), MINIMAP_WIDTH);
const viewportWidth = Math.min(
MINIMAP_WIDTH - viewportLeft,
width,
width + left,
);
const viewportHeight = Math.min(
MINIMAP_HEIGHT - viewportTop,
height,
height + top,
);
if (
Number.isNaN(viewportTop) ||
Number.isNaN(viewportLeft) ||
Number.isNaN(viewportWidth) ||
Number.isNaN(viewportHeight)
) {
return null;
}
return (
<div
style={{
border: "2px solid orange",
boxSizing: "border-box",
position: "absolute",
pointerEvents: "none",
top: viewportTop,
left: viewportLeft,
width: viewportWidth,
height: viewportHeight,
}}
/>
);
};
export function MiniMap({
appState,
elements,
}: {
appState: AppState;
elements: readonly ExcalidrawElement[];
}) {
const [minimapWorker] = useState(() => new MinimapWorker());
const canvasRef = useRef<HTMLCanvasElement>(null);
const elementsRef = useRef(elements);
elementsRef.current = elements;
const appStateRef = useRef(appState);
appStateRef.current = appState;
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) {
return;
}
const offscreenCanvas = canvas.transferControlToOffscreen();
minimapWorker.postMessage({ type: "INIT", canvas: offscreenCanvas }, [
offscreenCanvas,
]);
minimapWorker.postMessage({
type: "DRAW",
elements: elementsRef.current,
appState: appStateRef.current,
width: MINIMAP_WIDTH,
height: MINIMAP_HEIGHT,
});
setInterval(() => {
minimapWorker.postMessage({
type: "DRAW",
elements: elementsRef.current,
appState: appStateRef.current,
width: MINIMAP_WIDTH,
height: MINIMAP_HEIGHT,
});
}, 1000);
return () => {
minimapWorker.terminate();
};
}, [minimapWorker]);
return (
<Island padding={1} className="MiniMap">
<div
style={{
width: MINIMAP_WIDTH,
height: MINIMAP_HEIGHT,
position: "relative",
overflow: "hidden",
backgroundColor: appState.viewBackgroundColor,
}}
>
<canvas ref={canvasRef} />
<MinimapViewport elements={elements} appState={appState} />
</div>
</Island>
);
}

View File

@@ -1,5 +1,4 @@
import { FontFamily } from "./element/types";
import cssVariables from "./css/variables.module.scss";
export const APP_NAME = "Excalidraw";
@@ -108,5 +107,3 @@ export const MODES = {
ZEN: "zenMode",
GRID: "gridMode",
};
export const APPEARANCE_FILTER = cssVariables.appearanceFilter;

View File

@@ -1,5 +1,4 @@
@import "open-color/open-color.scss";
@import "./variables.module.scss";
:root {
--appearance-filter: none;
@@ -46,7 +45,7 @@
}
&.Appearance_dark {
--appearance-filter: #{$appearance-filter};
--appearance-filter: invert(93%) hue-rotate(180deg);
--button-destructive-bg-color: #5a0000;
--button-destructive-color: #{$oc-red-3};
--button-gray-1: #363636;

View File

@@ -2,9 +2,7 @@
// keep up to date with is-mobile.tsx
$is-mobile-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)";
$appearance-filter: "invert(93%) hue-rotate(180deg)";
:export {
isMobileQuery: unquote($is-mobile-query);
appearanceFilter: unquote($appearance-filter);
}

View File

@@ -41,7 +41,6 @@ export const exportCanvas = async (
if (type === "svg" || type === "clipboard-svg") {
const tempSvg = exportToSvg(elements, {
exportBackground,
exportWithDarkMode: appState.exportWithDarkMode,
viewBackgroundColor,
exportPadding,
scale,
@@ -74,6 +73,11 @@ export const exportCanvas = async (
scale,
shouldAddWatermark,
});
if (tempCanvas instanceof OffscreenCanvas) {
return;
}
tempCanvas.style.display = "none";
document.body.appendChild(tempCanvas);

View File

@@ -15,7 +15,6 @@ export interface ImportedDataState {
source?: string;
elements?: DataState["elements"] | null;
appState?: Partial<DataState["appState"]> | null;
scrollToCenter?: boolean;
}
export interface LibraryData {

View File

@@ -256,7 +256,6 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
if (elements) {
scenePromise.resolve({
elements,
scrollToCenter: true,
});
}
} catch (error) {
@@ -308,10 +307,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
init: true,
});
// noop if already resolved via init from firebase
scenePromise.resolve({
elements: reconciledElements,
scrollToCenter: true,
});
scenePromise.resolve({ elements: reconciledElements });
}
break;
}

View File

@@ -13,7 +13,7 @@ import { ExcalidrawImperativeAPI } from "../components/App";
import { ErrorDialog } from "../components/ErrorDialog";
import { TopErrorBoundary } from "../components/TopErrorBoundary";
import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
import { DataState, ImportedDataState } from "../data/types";
import { ImportedDataState } from "../data/types";
import {
ExcalidrawElement,
NonDeletedExcalidrawElement,
@@ -75,11 +75,7 @@ const initializeScene = async (opts: {
const initialData = importFromLocalStorage();
let scene: DataState & { scrollToCenter?: boolean } = await loadScene(
null,
null,
initialData,
);
let scene = await loadScene(null, null, initialData);
let roomLinkData = getCollaborationLinkData(window.location.href);
const isExternalScene = !!(id || jsonMatch || roomLinkData);
@@ -98,7 +94,6 @@ const initializeScene = async (opts: {
} else if (jsonMatch) {
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
}
scene.scrollToCenter = true;
if (!roomLinkData) {
window.history.replaceState({}, APP_NAME, window.location.origin);
}

12
src/global.d.ts vendored
View File

@@ -12,7 +12,6 @@ interface Document {
interface Window {
ClipboardItem: any;
__EXCALIDRAW_SHA__: string | undefined;
EXCALIDRAW_ASSET_PATH: string | undefined;
gtag: Function;
}
@@ -89,3 +88,14 @@ interface Blob {
handle?: import("browser-fs-acces").FileSystemHandle;
name?: string;
}
declare module "worker-loader!*" {
// You need to change `Worker`, if you specified a different value for the `workerType` option
class WebpackWorker extends Worker {
constructor();
}
// Uncomment this if you set the `esModule` option to `false`
// export = WebpackWorker;
export default WebpackWorker;
}

View File

@@ -54,8 +54,8 @@ const elements = [
},
];
registerFont("./public/Virgil.woff2", { family: "Virgil" });
registerFont("./public/Cascadia.woff2", { family: "Cascadia" });
registerFont("./public/FG_Virgil.ttf", { family: "Virgil" });
registerFont("./public/Cascadia.ttf", { family: "Cascadia" });
const canvas = exportToCanvas(
elements as any,

View File

@@ -4,6 +4,7 @@ import ExcalidrawApp from "./excalidraw-app";
import "./excalidraw-app/pwa";
import "./excalidraw-app/sentry";
window.__EXCALIDRAW_SHA__ = process.env.REACT_APP_GIT_SHA;
ReactDOM.render(<ExcalidrawApp />, document.getElementById("root"));

View File

@@ -1,5 +1,7 @@
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
export const isWindows = /^Win/.test(window.navigator.platform);
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(
globalThis.navigator.platform,
);
export const isWindows = /^Win/.test(globalThis.navigator.platform);
export const CODES = {
EQUAL: "Equal",
@@ -43,6 +45,7 @@ export const KEYS = {
D: "d",
E: "e",
L: "l",
M: "m",
O: "o",
P: "p",
Q: "q",

View File

@@ -92,8 +92,7 @@
"centerHorizontally": "Center horizontally",
"distributeHorizontally": "Distribute horizontally",
"distributeVertically": "Distribute vertically",
"viewMode": "View mode",
"toggleExportColorScheme": "Toggle export color scheme"
"viewMode": "View mode"
},
"buttons": {
"clearReset": "Reset the canvas",

View File

@@ -12,34 +12,18 @@ 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.4.0
## [Unreleased]
## Excalidraw API
### Features
- Expose `window.EXCALIDRAW_ASSET_PATH` which host can use to load assets from a different URL. By default it will be loaded from `https://unpkg.com/@excalidraw/excalidraw{currentVersion}/dist/`[#3068](https://github.com/excalidraw/excalidraw/pull/3068).
Also now the assets will have a hash in filename so cache bursting can easily happen with version bump.
- Add support for `scrollToCenter` in [initialData](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L18) so host can control whether to scroll to center on mount [#3070](https://github.com/excalidraw/excalidraw/pull/3070).
- Export [`restore`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L182), [`restoreAppState`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L144) and [`restoreElements`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L128) to host [#3049](https://github.com/excalidraw/excalidraw/pull/3049)
- Export [`restore`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L182), [`restoreAppState`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L144) and [`restoreElements`](https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L128) to host
### Fixes
- Show user state only when [userState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L35) is passed on remote pointers during collaboration [#3050](https://github.com/excalidraw/excalidraw/pull/3050)
## Excalidraw Library
### Features
- Adjust line-confirm-threshold based on zoom [#2884](https://github.com/excalidraw/excalidraw/pull/2884)
### Fixes
- Hide scrollbars on mobile [#3044](https://github.com/excalidraw/excalidraw/pull/3044)
## 0.3.1
## Excalidraw API

View File

@@ -20,9 +20,13 @@ After installation you will see a folder `excalidraw-assets` in `dist` directory
Move the folder `excalidraw-assets` to the path where your assets are served.
By default it will try to load the files from `https://unpkg.com/@excalidraw/excalidraw/{currentVersion}/dist/`
By default it will try to load the files from `{rootUrl}/excalidraw-assets/`
If you want to load assets from a different path you can set a variable `window.EXCALIDRAW_ASSET_PATH` to the url from where you want to load the assets.
With **Webpack**, if you want to load the files from different path you can use <pre><a href="https://webpack.js.org/guides/public-path/#on-the-fly">`__webpack_public_path__`</a></pre>.
With **create-react-app**, the assets can be served from `public/static/js/excalidraw-assets`since CRA tries to load the assets from `{rootUrl}/static/js` path by default.
You can update the value of `PUBLIC_URL` if you want to serve it from a different URL.
### Demo
@@ -282,11 +286,10 @@ Here you can try saving the data to your backend or local storage for example.
This helps to load Excalidraw with `initialData`. It must be an object or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to an object containing the below optional fields.
| Name | Type | Descrption |
| --- | --- | --- |
| `elements` | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) | The elements with which Excalidraw should be mounted. |
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) | The App state with which Excalidraw should be mounted. |
| `scrollToCenter` | boolean | This attribute implies whether to scroll to the nearest element to center once Excalidraw is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass `initialData.appState.scrollX` and `initialData.appState.scrollY` when `scrollToCenter` is false so that scroll positions are retained |
| name | type |
| --- | --- |
| elements | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) |
| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) |
```json
{

View File

@@ -1,5 +1,4 @@
import React, { useEffect, forwardRef } from "react";
import "./publicPath";
import { InitializeApp } from "../../components/InitializeApp";
import App from "../../components/App";

4451
src/packages/excalidraw/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@excalidraw/excalidraw",
"version": "0.4.0",
"version": "0.3.1",
"main": "dist/excalidraw.min.js",
"files": [
"dist/*"
@@ -41,24 +41,24 @@
"react-dom": "^17.0.1"
},
"devDependencies": {
"@babel/core": "7.13.8",
"@babel/plugin-transform-arrow-functions": "7.13.0",
"@babel/plugin-transform-async-to-generator": "7.13.0",
"@babel/plugin-transform-runtime": "7.13.8",
"@babel/plugin-transform-typescript": "7.13.0",
"@babel/preset-env": "7.13.8",
"@babel/core": "7.12.16",
"@babel/plugin-transform-arrow-functions": "7.12.13",
"@babel/plugin-transform-async-to-generator": "7.12.13",
"@babel/plugin-transform-runtime": "7.12.15",
"@babel/plugin-transform-typescript": "7.12.16",
"@babel/preset-env": "7.12.16",
"@babel/preset-react": "7.12.13",
"@babel/preset-typescript": "7.13.0",
"@babel/preset-typescript": "7.12.16",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.3",
"css-loader": "5.1.0",
"css-loader": "5.0.2",
"file-loader": "6.2.0",
"mini-css-extract-plugin": "1.3.9",
"mini-css-extract-plugin": "1.3.6",
"sass-loader": "11.0.1",
"terser-webpack-plugin": "5.1.1",
"ts-loader": "8.0.17",
"webpack": "5.24.2",
"webpack": "5.21.2",
"webpack-bundle-analyzer": "4.4.0",
"webpack-cli": "4.5.0"
},
@@ -68,6 +68,6 @@
"scripts": {
"build:umd": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js",
"build:umd:withAnalyzer": "cross-env NODE_ENV=production ANALYZER=true webpack --config webpack.prod.config.js",
"pack": "yarn build:umd && yarn pack"
"pack": "npm run build:umd && npm pack"
}
}

View File

@@ -1,9 +0,0 @@
import { ENV } from "../../constants";
import pkg from "./package.json";
if (process.env.NODE_ENV !== ENV.TEST) {
/* eslint-disable */
/* global __webpack_public_path__:writable */
__webpack_public_path__ =
window.EXCALIDRAW_ASSET_PATH ||
`https://unpkg.com/${pkg.name}@${pkg.version}/dist/`;
}

View File

@@ -13,8 +13,7 @@ module.exports = {
library: "Excalidraw",
libraryTarget: "umd",
filename: "[name].js",
chunkFilename: "excalidraw-assets/[name]-[contenthash].js",
publicPath: "",
chunkFilename: "excalidraw-assets/[name].js",
},
resolve: {
extensions: [".js", ".ts", ".tsx", ".css", ".scss"],

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,9 @@ export const exportToBlob = (
},
): Promise<Blob | null> => {
const canvas = exportToCanvas(opts);
if (canvas instanceof OffscreenCanvas) {
return Promise.resolve(null);
}
let { mimeType = "image/png", quality } = opts;

3446
src/packages/utils/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -34,21 +34,19 @@
]
},
"devDependencies": {
"@babel/core": "7.13.8",
"@babel/plugin-transform-arrow-functions": "7.13.0",
"@babel/plugin-transform-async-to-generator": "7.13.0",
"@babel/core": "7.12.16",
"@babel/plugin-transform-arrow-functions": "7.12.13",
"@babel/plugin-transform-async-to-generator": "7.12.13",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/plugin-transform-typescript": "7.13.0",
"@babel/preset-env": "7.13.8",
"@babel/preset-typescript": "7.13.0",
"@babel/plugin-transform-typescript": "7.12.16",
"@babel/preset-env": "7.12.16",
"@babel/preset-typescript": "7.12.16",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.3",
"css-loader": "5.1.0",
"file-loader": "6.2.0",
"sass-loader": "11.0.1",
"ts-loader": "8.0.17",
"webpack": "5.24.2",
"webpack": "5.21.2",
"webpack-bundle-analyzer": "4.4.0",
"webpack-cli": "4.5.0"
},
@@ -57,6 +55,6 @@
"scripts": {
"build:umd": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js",
"build:umd:withAnalyzer": "cross-env NODE_ENV=production ANALYZER=true webpack --config webpack.prod.config.js",
"pack": "yarn build:umd && npm pack"
"pack": "npm run build:umd && npm pack"
}
}

View File

@@ -13,18 +13,13 @@ module.exports = {
libraryTarget: "umd",
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".css", ".scss"],
extensions: [".tsx", ".ts", ".js"],
},
optimization: {
runtimeChunk: false,
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
exclude: /node_modules/,
use: ["style-loader", { loader: "css-loader" }, "sass-loader"],
},
{
test: /\.(ts|tsx|js)$/,
use: [

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
/* eslint-disable no-restricted-globals */
import { getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { exportToCanvas } from "../scene/export";
import { AppState } from "../types";
const ctx: Worker = self as any;
let canvas: OffscreenCanvas;
ctx.addEventListener("message", (ev) => {
if (ev.data.type === "INIT") {
canvas = ev.data.canvas;
}
if (ev.data.type === "DRAW") {
const { elements, appState, width, height } = ev.data;
drawScene(canvas, elements, appState, width, height);
}
});
function drawScene(
canvas: OffscreenCanvas,
elements: readonly ExcalidrawElement[],
appState: AppState,
minimapWidth: number,
minimapHeight: number,
) {
exportToCanvas(
getNonDeletedElements(elements),
appState,
{
exportBackground: true,
viewBackgroundColor: appState.viewBackgroundColor,
shouldAddWatermark: false,
},
(width, height) => {
const scale = Math.min(minimapWidth / width, minimapHeight / height);
canvas.width = width * scale;
canvas.height = height * scale;
return {
canvas,
scale,
};
},
);
}

View File

@@ -115,7 +115,7 @@ const generateElementCanvas = (
const drawElementOnCanvas = (
element: NonDeletedExcalidrawElement,
rc: RoughCanvas,
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
) => {
context.globalAlpha = element.opacity / 100;
switch (element.type) {
@@ -136,13 +136,16 @@ const drawElementOnCanvas = (
default: {
if (isTextElement(element)) {
const rtl = isRTL(element.text);
const shouldTemporarilyAttach = rtl && !context.canvas.isConnected;
if (shouldTemporarilyAttach) {
// to correctly render RTL text mixed with LTR, we have to append it
// to the DOM
document.body.appendChild(context.canvas);
let shouldTemporarilyAttach = false;
if (!(context instanceof OffscreenCanvasRenderingContext2D)) {
shouldTemporarilyAttach = rtl && !context.canvas.isConnected;
if (shouldTemporarilyAttach) {
// to correctly render RTL text mixed with LTR, we have to append it
// to the DOM
document.body.appendChild(context.canvas);
}
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
}
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
const font = context.font;
context.font = getFontString(element);
const fillStyle = context.fillStyle;
@@ -170,7 +173,10 @@ const drawElementOnCanvas = (
context.fillStyle = fillStyle;
context.font = font;
context.textAlign = textAlign;
if (shouldTemporarilyAttach) {
if (
shouldTemporarilyAttach &&
!(context instanceof OffscreenCanvasRenderingContext2D)
) {
context.canvas.remove();
}
} else {
@@ -451,7 +457,7 @@ const generateElementWithCanvas = (
const drawElementFromCanvas = (
elementWithCanvas: ExcalidrawElementWithCanvas,
rc: RoughCanvas,
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
sceneState: SceneState,
) => {
const element = elementWithCanvas.element;
@@ -482,7 +488,7 @@ const drawElementFromCanvas = (
export const renderElement = (
element: NonDeletedExcalidrawElement,
rc: RoughCanvas,
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
renderOptimizations: boolean,
sceneState: SceneState,
) => {

View File

@@ -49,12 +49,11 @@ import {
} from "../element/transformHandles";
import { viewportCoordsToSceneCoords, supportsEmoji } from "../utils";
import { UserIdleState } from "../excalidraw-app/collab/types";
import { APPEARANCE_FILTER } from "../constants";
const hasEmojiSupport = supportsEmoji();
const strokeRectWithRotation = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
x: number,
y: number,
width: number,
@@ -75,7 +74,7 @@ const strokeRectWithRotation = (
};
const strokeDiamondWithRotation = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
width: number,
height: number,
cx: number,
@@ -96,7 +95,7 @@ const strokeDiamondWithRotation = (
};
const strokeEllipseWithRotation = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
width: number,
height: number,
cx: number,
@@ -109,7 +108,7 @@ const strokeEllipseWithRotation = (
};
const fillCircle = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
cx: number,
cy: number,
radius: number,
@@ -121,7 +120,7 @@ const fillCircle = (
};
const strokeGrid = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
gridSize: number,
offsetX: number,
offsetY: number,
@@ -144,7 +143,7 @@ const strokeGrid = (
};
const renderLinearPointHandles = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
appState: AppState,
sceneState: SceneState,
element: NonDeleted<ExcalidrawLinearElement>,
@@ -183,7 +182,7 @@ export const renderScene = (
selectionElement: NonDeletedExcalidrawElement | null,
scale: number,
rc: RoughCanvas,
canvas: HTMLCanvasElement,
canvas: HTMLCanvasElement | OffscreenCanvas,
sceneState: SceneState,
// extra options, currently passed by export helper
{
@@ -212,10 +211,6 @@ export const renderScene = (
const normalizedCanvasWidth = canvas.width / scale;
const normalizedCanvasHeight = canvas.height / scale;
if (sceneState.exportWithDarkMode) {
context.filter = APPEARANCE_FILTER;
}
// Paint background
if (typeof sceneState.viewBackgroundColor === "string") {
const hasTransparence =
@@ -578,7 +573,7 @@ export const renderScene = (
};
const renderTransformHandles = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
sceneState: SceneState,
transformHandles: TransformHandles,
angle: number,
@@ -614,7 +609,7 @@ const renderTransformHandles = (
};
const renderSelectionBorder = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
sceneState: SceneState,
elementProperties: {
angle: number;
@@ -676,7 +671,7 @@ const renderSelectionBorder = (
};
const renderBindingHighlight = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
sceneState: SceneState,
suggestedBinding: SuggestedBinding,
) => {
@@ -698,7 +693,7 @@ const renderBindingHighlight = (
};
const renderBindingHighlightForBindableElement = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
element: ExcalidrawBindableElement,
) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
@@ -753,7 +748,7 @@ const renderBindingHighlightForBindableElement = (
};
const renderBindingHighlightForSuggestedPointBinding = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
suggestedBinding: SuggestedPointBinding,
) => {
const [element, startOrEnd, bindableElement] = suggestedBinding;

View File

@@ -9,7 +9,7 @@
* @param {Number} radius The corner radius
*/
export const roundRect = (
context: CanvasRenderingContext2D,
context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
x: number,
y: number,
width: number,

View File

@@ -7,11 +7,7 @@ import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
import { distance, SVG_NS } from "../utils";
import { AppState } from "../types";
import { t } from "../i18n";
import {
DEFAULT_FONT_FAMILY,
DEFAULT_VERTICAL_ALIGN,
APPEARANCE_FILTER,
} from "../constants";
import { DEFAULT_FONT_FAMILY, DEFAULT_VERTICAL_ALIGN } from "../constants";
import { getDefaultAppState } from "../appState";
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
@@ -36,7 +32,10 @@ export const exportToCanvas = (
createCanvas: (
width: number,
height: number,
) => { canvas: HTMLCanvasElement; scale: number } = (width, height) => {
) => { canvas: HTMLCanvasElement | OffscreenCanvas; scale: number } = (
width,
height,
) => {
const tempCanvas = document.createElement("canvas");
tempCanvas.width = width * scale;
tempCanvas.height = height * scale;
@@ -61,11 +60,10 @@ export const exportToCanvas = (
appState,
null,
newScale,
rough.canvas(tempCanvas),
rough.canvas((tempCanvas as unknown) as HTMLCanvasElement),
tempCanvas,
{
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
exportWithDarkMode: appState.exportWithDarkMode,
scrollX: -minX + exportPadding,
scrollY: -minY + exportPadding,
zoom: getDefaultAppState().zoom,
@@ -92,7 +90,6 @@ export const exportToSvg = (
exportBackground,
exportPadding = 10,
viewBackgroundColor,
exportWithDarkMode,
scale = 1,
shouldAddWatermark,
metadata = "",
@@ -101,7 +98,6 @@ export const exportToSvg = (
exportPadding?: number;
scale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
shouldAddWatermark: boolean;
metadata?: string;
},
@@ -121,9 +117,6 @@ export const exportToSvg = (
svgRoot.setAttribute("viewBox", `0 0 ${width} ${height}`);
svgRoot.setAttribute("width", `${width * scale}`);
svgRoot.setAttribute("height", `${height * scale}`);
if (exportWithDarkMode) {
svgRoot.setAttribute("filter", APPEARANCE_FILTER);
}
svgRoot.innerHTML = `
${SVG_EXPORT_TAG}
@@ -132,7 +125,7 @@ export const exportToSvg = (
<style>
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
src: url("https://excalidraw.com/FG_Virgil.woff2");
}
@font-face {
font-family: "Cascadia";

View File

@@ -6,7 +6,6 @@ export type SceneState = {
scrollY: number;
// null indicates transparent bg
viewBackgroundColor: string | null;
exportWithDarkMode?: boolean;
zoom: Zoom;
shouldCacheIgnoreZoom: boolean;
remotePointerViewportCoords: { [id: string]: { x: number; y: number } };

View File

@@ -29,13 +29,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -490,13 +490,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -957,13 +957,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": false,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -1733,13 +1733,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -1937,13 +1937,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -2395,13 +2395,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -2648,13 +2648,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -2812,13 +2812,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -3289,13 +3289,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -3597,13 +3597,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -3801,13 +3801,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -4045,13 +4045,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -4297,13 +4297,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -4680,13 +4680,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -4975,13 +4975,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -5282,13 +5282,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -5490,13 +5490,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -5654,13 +5654,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -6107,13 +6107,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -6425,13 +6425,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -8459,13 +8459,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -8821,13 +8821,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -9076,13 +9076,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -9329,13 +9329,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -9644,13 +9644,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -9808,13 +9808,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -9972,13 +9972,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -10136,13 +10136,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -10330,13 +10330,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -10524,13 +10524,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -10718,13 +10718,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -10912,13 +10912,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -11076,13 +11076,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -11240,13 +11240,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -11434,13 +11434,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -11598,13 +11598,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -11792,13 +11792,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -12508,13 +12508,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -12761,13 +12761,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "touch",
@@ -12863,13 +12863,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -12963,13 +12963,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -13127,13 +13127,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -13435,13 +13435,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -13743,13 +13743,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -13907,13 +13907,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -14103,13 +14103,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -14352,13 +14352,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -14676,13 +14676,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -15515,13 +15515,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -15823,13 +15823,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -16131,13 +16131,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -16510,13 +16510,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -16677,13 +16677,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -16998,13 +16998,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -17237,13 +17237,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -17492,13 +17492,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -17819,13 +17819,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -17919,13 +17919,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -18083,13 +18083,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -18904,13 +18904,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -19004,13 +19004,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -19758,13 +19758,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -20163,13 +20163,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -20436,13 +20436,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "touch",
@@ -20538,13 +20538,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -21036,13 +21036,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
@@ -21136,13 +21136,13 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isMinimapEnabled": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",

View File

@@ -5,7 +5,7 @@
<style>
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
src: url("https://excalidraw.com/FG_Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
@@ -13,4 +13,4 @@
}
</style>
</defs>
<rect x="0" y="0" width="56" height="77" fill="#ffffff"></rect><g transform="translate(10 10) rotate(0 18 28.5)"><text x="0" y="41" font-family="Virgil, Segoe UI Emoji" font-size="36px" fill="#c92a2a" text-anchor="start" style="white-space: pre;" direction="ltr">😀</text></g></svg>
<rect x="0" y="0" width="56" height="77" fill="#ffffff"></rect><g transform="translate(10 10) rotate(0 18 28.5)"><text x="0" y="41" font-family="Virgil, Segoe UI Emoji" font-size="36px" fill="#c92a2a" text-anchor="start" style="white-space: pre;" direction="ltr">😀</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -5,7 +5,7 @@
<style>
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
src: url("https://excalidraw.com/FG_Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
@@ -13,4 +13,4 @@
}
</style>
</defs>
<rect x="0" y="0" width="97" height="77" fill="#ffffff"></rect><g transform="translate(10 10) rotate(0 38.5 28.5)"><text x="0" y="41" font-family="Virgil, Segoe UI Emoji" font-size="36px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr">test</text></g></svg>
<rect x="0" y="0" width="97" height="77" fill="#ffffff"></rect><g transform="translate(10 10) rotate(0 38.5 28.5)"><text x="0" y="41" font-family="Virgil, Segoe UI Emoji" font-size="36px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr">test</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -30,7 +30,6 @@ describe("appState", () => {
height: ELEM_HEIGHT,
}),
],
scrollToCenter: true,
}}
/>,
);

View File

@@ -57,7 +57,6 @@ export type AppState = {
elementLocked: boolean;
exportBackground: boolean;
exportEmbedScene: boolean;
exportWithDarkMode: boolean;
shouldAddWatermark: boolean;
currentItemStrokeColor: string;
currentItemBackgroundColor: string;
@@ -118,6 +117,7 @@ export type AppState = {
shown: true;
data: Spreadsheet;
};
isMinimapEnabled: boolean;
};
export type NormalizedZoomValue = number & { _brand: "normalizedZoom" };

View File

@@ -372,6 +372,9 @@ export const getVersion = () => {
// Adapted from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/emoji.js
export const supportsEmoji = () => {
if (typeof document === "undefined") {
return;
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {

14933
yarn.lock

File diff suppressed because it is too large Load Diff