Compare commits

...

66 Commits

Author SHA1 Message Date
Panayiotis Lipiridis c12dab7a60 refactor: Remove the grid size and have a boolean instead 2020-12-24 20:15:12 +02:00
dependabot[bot] 39e7b8cf4f chore(deps-dev): bump firebase-tools from 8.19.0 to 9.0.1 (#2629)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 8.19.0 to 9.0.1.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Changelog](https://github.com/firebase/firebase-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-tools/compare/v8.19.0...v9.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-22 11:20:57 +01:00
dependabot[bot] e0ece680a6 chore(deps): bump firebase from 8.2.0 to 8.2.1 (#2631)
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 8.2.0 to 8.2.1.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-js-sdk/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-22 11:01:03 +01:00
David Luzar 8dfea49ec1 improvement: change hint for 2-point lines on resize (#2655) 2020-12-22 11:00:51 +01:00
Lipis d7f314cda8 refactor: Remove duplicate entry from en.json (#2654) 2020-12-22 11:42:01 +02:00
Lipis 6adb45ef5a feat: Change title to Excalidraw after a timeout (#2656)
* feat: Change title to Excalidraw after a timeout

* clear timeout
2020-12-22 10:34:06 +01:00
Lipis b0eeb8e6e6 refactor: Remove the word toggle from labels (#2648) 2020-12-21 14:58:43 +01:00
dependabot[bot] c3c20b6087 chore(deps): bump browser-nativefs from 0.11.2 to 0.12.0 (#2634)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-21 12:35:22 +02:00
Rene 0faec7efb6 feat: Checkmark to toggle context-menu-items (#2645) 2020-12-21 00:20:03 +02:00
Rene aff817c667 align items in context menu (#2640) 2020-12-20 12:15:40 -08:00
Christopher Chedeau 9a3a3ecb44 fix: Center zoom on iPhone and iPad (#2642)
My last attempt removed the gesture handler altogether but broke macbooks. This one keeps it working on macbook but makes sure it zooms properly on iPhone and iPad.
2020-12-20 12:13:15 -08:00
Christopher Chedeau 81f8039ec7 fix: Don't break zoom when zooming in on UI (#2638)
If you start the gesture on the chrome, gesture is not defined and the zoom will be set to NaN and you will have to refresh.

Typescript was actually right and we shouldn't have overridden the bang ;)

Repro:
- On iPhone touch down on a shape, touch down on the chrome, start moving around
- See that the selection is off, but the zoom is not being modified like crazy.

https://user-images.githubusercontent.com/197597/102722206-6c182400-42b4-11eb-9865-6ae1cd0af9be.MP4
2020-12-20 12:07:58 -08:00
Lipis 14759d5b72 chore: Remove changelog check and graphql (#2639) 2020-12-20 21:52:21 +02:00
David Luzar b997e69ebc improvement: Tweak error message on image import (#2619)
* improvement: tweak error message on image import

* tweak copy
2020-12-20 16:11:44 +01:00
dependabot[bot] 4a89aba682 chore(deps-dev): bump webpack in /src/packages/excalidraw (#2625)
Bumps [webpack](https://github.com/webpack/webpack) from 5.10.1 to 5.11.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.10.1...v5.11.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 20:35:49 +05:30
David Luzar 34dcf998bd docs: excalidraw package usage example tweaks (#2608)
Co-authored-by: Aakansha Doshi <monstershome@gmail.com>
2020-12-20 16:04:12 +01:00
Aakansha Doshi 325d1bec91 feat: Add onExportToBackend prop so host can handle it (#2612)
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-20 15:14:04 +01:00
Lipis b917e42694 fix: Consistent case for export locale strings (#2622) 2020-12-20 14:40:11 +02:00
Christopher Chedeau eb9e67e36a improvement: Support numbers with commas in them (#2636) 2020-12-20 14:08:22 +02:00
dependabot[bot] f14ae52e94 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#2624)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.10 to 7.12.11.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.11/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:49:20 +05:30
dependabot[bot] f93eb658d6 chore(deps-dev): bump webpack-bundle-analyzer (#2623)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.2.0...v4.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:46:37 +05:30
Aakansha Doshi 9bac44ee75 fix(appstate.ts): Remove unnecessary console.error as it was polluting Sentry (#2637) 2020-12-20 16:28:46 +05:30
dependabot[bot] 91b4109f67 chore(deps-dev): bump webpack-bundle-analyzer in /src/packages/utils (#2626)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.2.0...v4.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 16:21:17 +05:30
dependabot[bot] 6e45cb95db chore(deps-dev): bump webpack in /src/packages/utils (#2627)
Bumps [webpack](https://github.com/webpack/webpack) from 5.10.1 to 5.11.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.10.1...v5.11.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 15:20:35 +05:30
dependabot[bot] 5edf82898b chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#2628)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.10 to 7.12.11.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.11/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 15:19:20 +05:30
dependabot[bot] 60b82e3055 chore(deps-dev): bump eslint-config-prettier from 7.0.0 to 7.1.0 (#2632)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v7.0.0...v7.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-20 03:16:05 +02:00
Lipis 5d6590c200 ci: Update the coverage report for i18n PRs (#2592)
Co-authored-by: Kostas Bariotis <konmpar@gmail.com>
2020-12-19 22:44:01 +02:00
Lipis d3bebbc68d ci: Better locale coverage comment (#2616)
Co-authored-by: kbariotis <konmpar@gmail.com>
2020-12-19 20:35:03 +02:00
Kostas Bariotis 4ff8f3b006 add commenting on translations PRs, resolves #2587 (#2615) 2020-12-19 13:52:50 +00:00
Aakansha Doshi abbc756887 ci: Add semantic pr title action (#2610) 2020-12-18 21:32:37 +05:30
Lipis 10e07e434c chore: Remove support for deprecated Excalidraw for Desktop (#2465) 2020-12-18 14:02:29 +02:00
Andrés Rivera fb582b45db fix: Fix centering element on init (#2445)
Co-authored-by: Andrés Rivera <andres@MBP.local>
Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-16 18:17:39 +01:00
Aakansha Doshi 23f21434ff Fix changelog workflow (#2604)
Fix the workflow to make sure it runs on pull requests against forked branches and also don't run on l10_master and dependabot branches
2020-12-16 20:19:05 +05:30
Jaiwanth 7e9fdf85a0 fix: Dropdown for Arrowheads overflow (#2596)
Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-16 13:06:55 +01:00
David Luzar 98c26642d0 fix: hide collab button when onCollabButtonClick not supplied (#2598) 2020-12-15 16:21:14 +01:00
Lipis 2b434db062 chore: Don't run docker build on PRs (#2601) 2020-12-15 14:24:00 +01:00
Kartik Prajapati f919907855 Enhance delete button in context menu (#2591) 2020-12-15 00:59:00 +02:00
Aakansha Doshi bfeb3c7dfd fix(changelog-check.yml): ignore l10n_master and dependabot branches in changelog workflow (#2594) 2020-12-15 02:25:45 +05:30
Aakansha Doshi 8729ab3c54 chore(package/utils): update deps (#2593) 2020-12-15 02:13:19 +05:30
Aakansha Doshi 37f53bccbf Revert the changelog check of excluding the branches (#2590)
* temp commit

* fix

* fix

* Update actionAlign.tsx
2020-12-14 22:41:00 +02:00
Aakansha Doshi c783763307 chore(package/excalidraw): update deps (#2589) 2020-12-14 19:21:22 +01:00
dependabot[bot] f151f45df8 Bump webpack from 4.42.0 to 4.44.2 in /src/packages/utils (#2554)
Bumps [webpack](https://github.com/webpack/webpack) from 4.42.0 to 4.44.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.42.0...v4.44.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 21:08:06 +05:30
dependabot[bot] f33880e005 Bump @babel/plugin-transform-arrow-functions in /src/packages/utils (#2558)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.8.3 to 7.12.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.1/packages/babel-plugin-transform-arrow-functions)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 21:02:52 +05:30
dependabot[bot] 29ed50f6ea Bump webpack-cli from 3.3.11 to 4.2.0 in /src/packages/excalidraw (#2571)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.11 to 4.2.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.11...webpack-cli@4.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2020-12-14 21:00:06 +05:30
Lipis ce52c18382 fix: Add width/height for the lines in charts (#2586)
Co-authored-by: David Luzar <luzar.david@gmail.com>
2020-12-14 16:29:39 +01:00
Thomas Steiner 7c3e1d8d1b Add declarative link capturing
See https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#user-content-proposal:~:text=new_client,-%E2%80%9D%20%E2%80%94 for context.
2020-12-14 16:25:48 +01:00
dependabot[bot] 0d15934a96 Bump firebase-tools from 8.17.0 to 8.19.0 (#2574)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 8.17.0 to 8.19.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v8.17.0...v8.19.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 17:15:38 +02:00
dependabot[bot] a0069d04f0 Bump firebase from 8.1.2 to 8.2.0 (#2580)
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 8.1.2 to 8.2.0.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-js-sdk/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 17:15:16 +02:00
Lipis 3b86944365 chore: New Crowdin updates (#2480) 2020-12-14 16:49:01 +02:00
João Forja 4c7b1a2269 fix: Visibility and zooming when canvas offset is not zero (#2534) 2020-12-14 15:14:56 +01:00
Kartik Prajapati 60864ace54 feat: Add tooltip with icon for embedding scenes (#2532)
Co-authored-by: dwelle <luzar.david@gmail.com>
2020-12-14 14:24:54 +01:00
Daishi Kato 4b32c03994 feat: export exportToCanvas in utils (#2583) 2020-12-14 14:11:48 +01:00
dependabot[bot] 222dbdcc00 Bump @types/jest from 26.0.16 to 26.0.19 (#2578)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.16 to 26.0.19.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 07:59:56 +02:00
dependabot[bot] 1346227d30 Bump @sentry/browser from 5.28.0 to 5.29.0 (#2561)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 07:59:48 +02:00
dependabot[bot] ecbddd214c Bump pepjs from 0.5.2 to 0.5.3 (#2550)
Bumps [pepjs](https://github.com/jquery/PEP) from 0.5.2 to 0.5.3.
- [Release notes](https://github.com/jquery/PEP/releases)
- [Commits](https://github.com/jquery/PEP/commits/0.5.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:29:51 +02:00
dependabot[bot] 4999ca5c82 Bump husky from 4.3.0 to 4.3.6 (#2575)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:29:16 +02:00
dependabot[bot] 174638889d Bump typescript from 4.0.5 to 4.1.3 (#2569)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.0.5 to 4.1.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:55 +02:00
dependabot[bot] 480998582e Bump eslint-plugin-prettier from 3.1.4 to 3.3.0 (#2573)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 3.1.4 to 3.3.0.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v3.1.4...v3.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:40 +02:00
dependabot[bot] 94544e458c Bump browser-nativefs from 0.11.1 to 0.11.2 (#2577)
Bumps [browser-nativefs](https://github.com/GoogleChromeLabs/browser-nativefs) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/GoogleChromeLabs/browser-nativefs/releases)
- [Commits](https://github.com/GoogleChromeLabs/browser-nativefs/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:28:16 +02:00
dependabot[bot] f664ba9e1e Bump @sentry/integrations from 5.28.0 to 5.29.0 (#2579)
Bumps [@sentry/integrations](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 05:27:54 +02:00
Panayiotis Lipiridis 015a60638e chore: Update user for packages 2020-12-14 03:49:43 +02:00
Lipis 7abb80530f chore: Add dependabot configuration (#2535) 2020-12-14 03:47:13 +02:00
Zen Tang 5abe9b93e8 feat: Add zoom to fit for selected elements (#2522) 2020-12-13 22:54:35 +02:00
Aakansha Doshi 59cff0f219 ci: Add github action to make sure changelog for @excalidraw/excalidraw is updated (#2518)
Add guidelines for changelog and group the commits
update the changelog with the latest commits since the last release
Co-authored-by: Lipis <lipiridis@gmail.com>
2020-12-13 18:53:14 +05:30
Lipis 81c17a56fb RTL support for the stats dialog (#2530) 2020-12-13 13:39:45 +01:00
Zen Tang 802b8c50d5 Insert Library items in the middle of the screen (#2527)
Co-authored-by: Zen Tang <zen@wayve.ai>
2020-12-13 13:46:42 +02:00
97 changed files with 4499 additions and 10355 deletions
+37
View File
@@ -0,0 +1,37 @@
version: 2
updates:
- package-ecosystem: npm
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/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:
- ad1992
- package-ecosystem: npm
directory: "/src/packages/utils/"
schedule:
interval: weekly
day: sunday
time: "01:00"
open-pull-requests-limit: 99
reviewers:
- ad1992
assignees:
- ad1992
-1
View File
@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- master - master
pull_request:
jobs: jobs:
build-docker: build-docker:
-33
View File
@@ -1,33 +0,0 @@
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: "18 7 * * 0"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ["typescript"]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+15
View File
@@ -30,3 +30,18 @@ jobs:
git commit -am "Auto commit: Calculate translation coverage" git commit -am "Auto commit: Calculate translation coverage"
git push git push
fi fi
- name: Construct comment body
id: getCommentBody
run: |
body=$(npm run locales-coverage:description | grep '^[^>]')
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"
echo ::set-output name=body::$body
- name: Update description with coverage
uses: kt3k/update-pr-description@v1.0.1
with:
pr_body: ${{ steps.getCommentBody.outputs.body }}
pr_title: "chore: New Crowdin updates"
github_token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }}
+16
View File
@@ -0,0 +1,16 @@
name: "Semantic PR title"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v2.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+383 -586
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -19,18 +19,18 @@
] ]
}, },
"dependencies": { "dependencies": {
"@sentry/browser": "5.28.0", "@sentry/browser": "5.29.0",
"@sentry/integrations": "5.28.0", "@sentry/integrations": "5.29.0",
"@testing-library/jest-dom": "5.11.6", "@testing-library/jest-dom": "5.11.6",
"@testing-library/react": "11.2.2", "@testing-library/react": "11.2.2",
"@types/jest": "26.0.16", "@types/jest": "26.0.19",
"@types/nanoid": "2.1.0", "@types/nanoid": "2.1.0",
"@types/react": "17.0.0", "@types/react": "17.0.0",
"@types/react-dom": "17.0.0", "@types/react-dom": "17.0.0",
"@types/socket.io-client": "1.4.34", "@types/socket.io-client": "1.4.34",
"browser-nativefs": "0.11.1", "browser-nativefs": "0.12.0",
"clsx": "1.1.1", "clsx": "1.1.1",
"firebase": "8.1.2", "firebase": "8.2.1",
"i18next-browser-languagedetector": "6.0.1", "i18next-browser-languagedetector": "6.0.1",
"lodash.throttle": "4.1.1", "lodash.throttle": "4.1.1",
"nanoid": "2.1.11", "nanoid": "2.1.11",
@@ -47,19 +47,18 @@
"react-scripts": "4.0.1", "react-scripts": "4.0.1",
"roughjs": "4.3.1", "roughjs": "4.3.1",
"socket.io-client": "2.3.1", "socket.io-client": "2.3.1",
"typescript": "4.0.5" "typescript": "4.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.throttle": "4.1.6", "@types/lodash.throttle": "4.1.6",
"@types/pako": "1.0.1", "@types/pako": "1.0.1",
"asar": "3.0.3", "eslint-config-prettier": "7.1.0",
"eslint-config-prettier": "7.0.0", "eslint-plugin-prettier": "3.3.0",
"eslint-plugin-prettier": "3.1.4", "firebase-tools": "9.0.1",
"firebase-tools": "8.17.0", "husky": "4.3.6",
"husky": "4.3.0",
"jest-canvas-mock": "2.3.0", "jest-canvas-mock": "2.3.0",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"pepjs": "0.5.2", "pepjs": "0.5.3",
"prettier": "2.2.1", "prettier": "2.2.1",
"rewire": "5.0.0" "rewire": "5.0.0"
}, },
@@ -84,13 +83,14 @@
"build-node": "node ./scripts/build-node.js", "build-node": "node ./scripts/build-node.js",
"build:app:docker": "REACT_APP_INCLUDE_GTAG=false REACT_APP_DISABLE_SENTRY=true react-scripts build", "build:app:docker": "REACT_APP_INCLUDE_GTAG=false REACT_APP_DISABLE_SENTRY=true react-scripts build",
"build:app": "REACT_APP_INCLUDE_GTAG=true REACT_APP_GIT_SHA=$NOW_GITHUB_COMMIT_SHA react-scripts build", "build:app": "REACT_APP_INCLUDE_GTAG=true REACT_APP_GIT_SHA=$NOW_GITHUB_COMMIT_SHA react-scripts build",
"build:zip": "node ./scripts/build-version.js", "build:version": "node ./scripts/build-version.js",
"build": "npm run build:app && npm run build:zip", "build": "npm run build:app && npm run build:version",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"fix:code": "npm run test:code -- --fix", "fix:code": "npm run test:code -- --fix",
"fix:other": "npm run prettier -- --write", "fix:other": "npm run prettier -- --write",
"fix": "npm run fix:other && npm run fix:code", "fix": "npm run fix:other && npm run fix:code",
"locales-coverage": "node scripts/build-locales-coverage.js", "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", "prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
"start": "react-scripts start", "start": "react-scripts start",
"test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false", "test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false",
+2
View File
@@ -55,6 +55,8 @@
<meta name="twitter:image" content="https://excalidraw.com/og-image.png" /> <meta name="twitter:image" content="https://excalidraw.com/og-image.png" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<!-- Excalidraw version -->
<meta name="version" content="{version}" />
<link <link
rel="preload" rel="preload"
href="FG_Virgil.woff2" href="FG_Virgil.woff2"
+2 -1
View File
@@ -25,5 +25,6 @@
"application/vnd.excalidraw+json": [".excalidraw"] "application/vnd.excalidraw+json": [".excalidraw"]
} }
} }
] ],
"capture_links": "new_client"
} }
+15 -8
View File
@@ -2,7 +2,8 @@
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const asar = require("asar"); const versionFile = path.join("build", "version.json");
const indexFile = path.join("build", "index.html");
const zero = (digit) => `0${digit}`.slice(-2); const zero = (digit) => `0${digit}`.slice(-2);
@@ -20,18 +21,24 @@ const now = new Date();
const data = JSON.stringify( const data = JSON.stringify(
{ {
asar: "excalidraw.asar",
version: versionDate(now), version: versionDate(now),
}, },
undefined, undefined,
2, 2,
); );
fs.writeFileSync(path.join("build", "version.json"), data); fs.writeFileSync(versionFile, data);
(async () => { // https://stackoverflow.com/a/14181136/8418
const src = "build/"; fs.readFile(indexFile, "utf8", (error, data) => {
const dest = path.join("build", `excalidraw.asar`); if (error) {
return console.error(error);
}
const result = data.replace(/{version}/g, versionDate(now));
await asar.createPackage(src, dest); fs.writeFile(indexFile, result, "utf8", (error) => {
})(); if (error) {
return console.error(error);
}
});
});
+156
View File
@@ -0,0 +1,156 @@
const fs = require("fs");
const THRESSHOLD = 85;
const crowdinMap = {
"ar-SA": "en-ar",
"el-GR": "en-el",
"fi-FI": "en-fi",
"ja-JP": "en-ja",
"bg-BG": "en-bg",
"ca-ES": "en-ca",
"de-DE": "en-de",
"es-ES": "en-es",
"fa-IR": "en-fa",
"fr-FR": "en-fr",
"he-IL": "en-he",
"hi-IN": "en-hi",
"hu-HU": "en-hu",
"id-ID": "en-id",
"it-IT": "en-it",
"ko-KR": "en-ko",
"my-MM": "en-my",
"nb-NO": "en-nb",
"nl-NL": "en-nl",
"nn-NO": "en-nnno",
"pl-PL": "en-pl",
"pt-PT": "en-pt",
"ro-RO": "en-ro",
"ru-RU": "en-ru",
"sk-SK": "en-sk",
"sv-SE": "en-sv",
"tr-TR": "en-tr",
"uk-UA": "en-uk",
"zh-CN": "en-zhcn",
"zh-TW": "en-zhtw",
};
const flags = {
"ar-SA": "🇸🇦",
"bg-BG": "🇧🇬",
"ca-ES": "🇪🇸",
"de-DE": "🇩🇪",
"el-GR": "🇬🇷",
"es-ES": "🇪🇸",
"fa-IR": "🇮🇷",
"fi-FI": "🇫🇮",
"fr-FR": "🇫🇷",
"he-IL": "🇮🇱",
"hi-IN": "🇮🇳",
"hu-HU": "🇭🇺",
"id-ID": "🇮🇩",
"it-IT": "🇮🇹",
"ja-JP": "🇯🇵",
"ko-KR": "🇰🇷",
"my-MM": "🇲🇲",
"nb-NO": "🇳🇴",
"nl-NL": "🇳🇱",
"nn-NO": "🇳🇴",
"pl-PL": "🇵🇱",
"pt-PT": "🇵🇹",
"ro-RO": "🇷🇴",
"ru-RU": "🇷🇺",
"sk-SK": "🇸🇰",
"sv-SE": "🇸🇪",
"tr-TR": "🇹🇷",
"uk-UA": "🇺🇦",
"zh-CN": "🇨🇳",
"zh-TW": "🇹🇼",
};
const languages = {
"ar-SA": "العربية",
"bg-BG": "Български",
"ca-ES": "Catalan",
"de-DE": "Deutsch",
"el-GR": "Ελληνικά",
"es-ES": "Español",
"fa-IR": "فارسی",
"fi-FI": "Suomi",
"fr-FR": "Français",
"he-IL": "עברית",
"hi-IN": "हिन्दी",
"hu-HU": "Magyar",
"id-ID": "Bahasa Indonesia",
"it-IT": "Italiano",
"ja-JP": "日本語",
"ko-KR": "한국어",
"my-MM": "Burmese",
"nb-NO": "Norsk bokmål",
"nl-NL": "Nederlands",
"nn-NO": "Norsk nynorsk",
"pl-PL": "Polski",
"pt-PT": "Português",
"ro-RO": "Română",
"ru-RU": "Русский",
"sk-SK": "Slovenčina",
"sv-SE": "Svenska",
"tr-TR": "Türkçe",
"uk-UA": "Українська",
"zh-CN": "简体中文",
"zh-TW": "繁體中文",
};
const percentages = fs.readFileSync(
`${__dirname}/../src/locales/percentages.json`,
);
const rowData = JSON.parse(percentages);
const coverages = Object.entries(rowData)
.sort(([, a], [, b]) => b - a)
.reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
const boldIf = (text, condition) => (condition ? `**${text}**` : text);
const printHeader = () => {
let result = "| | Flag | Locale | % |\n";
result += "| --: | :--: | -- | --: |";
return result;
};
const printRow = (id, locale, coverage) => {
const isOver = coverage > THRESSHOLD;
let result = `| ${boldIf(id, isOver)} | `;
result += `${locale in flags ? flags[locale] : ""} | `;
const language = locale in languages ? languages[locale] : locale;
if (locale in crowdinMap && crowdinMap[locale]) {
result += `[${boldIf(
language,
isOver,
)}](https://crowdin.com/translate/excalidraw/10/${crowdinMap[locale]}) | `;
} else {
result += `${boldIf(language, isOver)} | `;
}
result += `${boldIf(coverage, isOver)} |`;
return result;
};
console.info("## Languages check");
console.info("\n\r");
console.info(
`Our translations for every languages should be at least **${THRESSHOLD}%** to appear on Excalidraw. Join our project in [Crowdin](https://crowdin.com/project/excalidraw) and help us translate it in your language. **Can't find your own?** Open an [issue](https://github.com/excalidraw/excalidraw/issues/new) and we'll add it to the list.`,
);
console.info("\n\r");
console.info(printHeader());
let index = 1;
for (const coverage in coverages) {
if (coverage === "en") {
continue;
}
console.info(printRow(index, coverage, coverages[coverage]));
index++;
}
console.info("\n\r");
console.info("\\* Languages in **bold** are going to appear on production.");
+70 -36
View File
@@ -4,12 +4,14 @@ import { getDefaultAppState } from "../appState";
import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons"; import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
import { ToolButton } from "../components/ToolButton"; import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n"; import { t } from "../i18n";
import { getNormalizedZoom } from "../scene"; import { getNormalizedZoom, getSelectedElements } from "../scene";
import { getNonDeletedElements } from "../element";
import { CODES, KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { getShortcutKey } from "../utils"; import { getShortcutKey } from "../utils";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { register } from "./register"; import { register } from "./register";
import { newElementWith } from "../element/mutateElement"; import { newElementWith } from "../element/mutateElement";
import { ExcalidrawElement } from "../element/types";
import { AppState, NormalizedZoomValue } from "../types"; import { AppState, NormalizedZoomValue } from "../types";
import { getCommonBounds } from "../element"; import { getCommonBounds } from "../element";
import { getNewZoom } from "../scene/zoom"; import { getNewZoom } from "../scene/zoom";
@@ -62,7 +64,7 @@ export const actionClearCanvas = register({
elementLocked: appState.elementLocked, elementLocked: appState.elementLocked,
exportBackground: appState.exportBackground, exportBackground: appState.exportBackground,
exportEmbedScene: appState.exportEmbedScene, exportEmbedScene: appState.exportEmbedScene,
gridSize: appState.gridSize, showGrid: appState.showGrid,
shouldAddWatermark: appState.shouldAddWatermark, shouldAddWatermark: appState.shouldAddWatermark,
showStats: appState.showStats, showStats: appState.showStats,
}, },
@@ -93,6 +95,7 @@ export const actionZoomIn = register({
const zoom = getNewZoom( const zoom = getNewZoom(
getNormalizedZoom(appState.zoom.value + ZOOM_STEP), getNormalizedZoom(appState.zoom.value + ZOOM_STEP),
appState.zoom, appState.zoom,
{ left: appState.offsetLeft, top: appState.offsetTop },
{ x: appState.width / 2, y: appState.height / 2 }, { x: appState.width / 2, y: appState.height / 2 },
); );
trackEvent(EVENT_ACTION, "zoom", "in", zoom.value * 100); trackEvent(EVENT_ACTION, "zoom", "in", zoom.value * 100);
@@ -126,6 +129,7 @@ export const actionZoomOut = register({
const zoom = getNewZoom( const zoom = getNewZoom(
getNormalizedZoom(appState.zoom.value - ZOOM_STEP), getNormalizedZoom(appState.zoom.value - ZOOM_STEP),
appState.zoom, appState.zoom,
{ left: appState.offsetLeft, top: appState.offsetTop },
{ x: appState.width / 2, y: appState.height / 2 }, { x: appState.width / 2, y: appState.height / 2 },
); );
@@ -161,10 +165,15 @@ export const actionResetZoom = register({
return { return {
appState: { appState: {
...appState, ...appState,
zoom: getNewZoom(1 as NormalizedZoomValue, appState.zoom, { zoom: getNewZoom(
x: appState.width / 2, 1 as NormalizedZoomValue,
y: appState.height / 2, appState.zoom,
}), { left: appState.offsetLeft, top: appState.offsetTop },
{
x: appState.width / 2,
y: appState.height / 2,
},
),
}, },
commitToHistory: false, commitToHistory: false,
}; };
@@ -204,38 +213,63 @@ const zoomValueToFitBoundsOnViewport = (
return clampedZoomValueToFitElements as NormalizedZoomValue; return clampedZoomValueToFitElements as NormalizedZoomValue;
}; };
const zoomToFitElements = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
zoomToSelection: boolean,
) => {
const nonDeletedElements = getNonDeletedElements(elements);
const selectedElements = getSelectedElements(nonDeletedElements, appState);
const commonBounds =
zoomToSelection && selectedElements.length > 0
? getCommonBounds(selectedElements)
: getCommonBounds(nonDeletedElements);
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
width: appState.width,
height: appState.height,
});
const newZoom = getNewZoom(zoomValue, appState.zoom, {
left: appState.offsetLeft,
top: appState.offsetTop,
});
const action = zoomToSelection ? "selection" : "fit";
const [x1, y1, x2, y2] = commonBounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
trackEvent(EVENT_ACTION, "zoom", action, newZoom.value * 100);
return {
appState: {
...appState,
...centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: newZoom,
}),
zoom: newZoom,
},
commitToHistory: false,
};
};
export const actionZoomToSelected = register({
name: "zoomToSelection",
perform: (elements, appState) => zoomToFitElements(elements, appState, true),
keyTest: (event) =>
event.code === CODES.TWO &&
event.shiftKey &&
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionZoomToFit = register({ export const actionZoomToFit = register({
name: "zoomToFit", name: "zoomToFit",
perform: (elements, appState) => { perform: (elements, appState) => zoomToFitElements(elements, appState, false),
const nonDeletedElements = elements.filter((element) => !element.isDeleted);
const commonBounds = getCommonBounds(nonDeletedElements);
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
width: appState.width,
height: appState.height,
});
const newZoom = getNewZoom(zoomValue, appState.zoom);
const [x1, y1, x2, y2] = commonBounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
trackEvent(EVENT_ACTION, "zoom", "fit", newZoom.value * 100);
return {
appState: {
...appState,
...centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: newZoom,
}),
zoom: newZoom,
},
commitToHistory: false,
};
},
keyTest: (event) => keyTest: (event) =>
event.code === CODES.ONE && event.code === CODES.ONE &&
event.shiftKey && event.shiftKey &&
+1 -1
View File
@@ -136,7 +136,7 @@ export const actionDeleteSelected = register({
}; };
}, },
contextItemLabel: "labels.delete", contextItemLabel: "labels.delete",
contextMenuOrder: 3, contextMenuOrder: 999999,
keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE, keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE,
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
+11 -1
View File
@@ -3,12 +3,15 @@ import { EVENT_CHANGE, EVENT_IO, trackEvent } from "../analytics";
import { load, save, saveAs } from "../components/icons"; import { load, save, saveAs } from "../components/icons";
import { ProjectName } from "../components/ProjectName"; import { ProjectName } from "../components/ProjectName";
import { ToolButton } from "../components/ToolButton"; import { ToolButton } from "../components/ToolButton";
import { Tooltip } from "../components/Tooltip";
import { questionCircle } from "../components/icons";
import { loadFromJSON, saveAsJSON } from "../data"; import { loadFromJSON, saveAsJSON } from "../data";
import { t } from "../i18n"; import { t } from "../i18n";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { KEYS } from "../keys"; import { KEYS } from "../keys";
import { muteFSAbortError } from "../utils"; import { muteFSAbortError } from "../utils";
import { register } from "./register"; import { register } from "./register";
import "../components/ToolIcon.scss";
export const actionChangeProjectName = register({ export const actionChangeProjectName = register({
name: "changeProjectName", name: "changeProjectName",
@@ -54,13 +57,20 @@ export const actionChangeExportEmbedScene = register({
}; };
}, },
PanelComponent: ({ appState, updateData }) => ( PanelComponent: ({ appState, updateData }) => (
<label title={t("labels.exportEmbedScene_details")}> <label style={{ display: "flex" }}>
<input <input
type="checkbox" type="checkbox"
checked={appState.exportEmbedScene} checked={appState.exportEmbedScene}
onChange={(event) => updateData(event.target.checked)} onChange={(event) => updateData(event.target.checked)}
/>{" "} />{" "}
{t("labels.exportEmbedScene")} {t("labels.exportEmbedScene")}
<Tooltip
label={t("labels.exportEmbedScene_details")}
position="above"
long={true}
>
<div className="TooltipIcon">{questionCircle}</div>
</Tooltip>
</label> </label>
), ),
}); });
+4 -4
View File
@@ -19,8 +19,8 @@ export type ShortcutName =
| "copyAsSvg" | "copyAsSvg"
| "group" | "group"
| "ungroup" | "ungroup"
| "toggleGridMode" | "gridMode"
| "toggleStats" | "stats"
| "addToLibrary"; | "addToLibrary";
const shortcutMap: Record<ShortcutName, string[]> = { const shortcutMap: Record<ShortcutName, string[]> = {
@@ -51,8 +51,8 @@ const shortcutMap: Record<ShortcutName, string[]> = {
copyAsSvg: [], copyAsSvg: [],
group: [getShortcutKey("CtrlOrCmd+G")], group: [getShortcutKey("CtrlOrCmd+G")],
ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")], ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")],
toggleGridMode: [getShortcutKey("CtrlOrCmd+'")], gridMode: [getShortcutKey("CtrlOrCmd+'")],
toggleStats: [], stats: [],
addToLibrary: [], addToLibrary: [],
}; };
+1
View File
@@ -58,6 +58,7 @@ export type ActionName =
| "zoomOut" | "zoomOut"
| "resetZoom" | "resetZoom"
| "zoomToFit" | "zoomToFit"
| "zoomToSelection"
| "changeFontFamily" | "changeFontFamily"
| "changeTextAlign" | "changeTextAlign"
| "toggleFullScreen" | "toggleFullScreen"
+14 -11
View File
@@ -11,14 +11,17 @@ export const EVENT_SHAPE = "shape";
export const EVENT_SHARE = "share"; export const EVENT_SHARE = "share";
export const EVENT_MAGIC = "magic"; export const EVENT_MAGIC = "magic";
export const trackEvent = window.gtag export const trackEvent =
? (category: string, name: string, label?: string, value?: number) => { typeof window !== "undefined" && window.gtag
window.gtag("event", name, { ? (category: string, name: string, label?: string, value?: number) => {
event_category: category, window.gtag("event", name, {
event_label: label, event_category: category,
value, event_label: label,
}); value,
} });
: (category: string, name: string, label?: string, value?: number) => { }
console.info("Track Event", category, name, label, value); : typeof process !== "undefined" && process?.env?.JEST_WORKER_ID
}; ? (category: string, name: string, label?: string, value?: number) => {}
: (category: string, name: string, label?: string, value?: number) => {
console.info("Track Event", category, name, label, value);
};
+2 -7
View File
@@ -65,7 +65,7 @@ export const getDefaultAppState = (): Omit<
showShortcutsDialog: false, showShortcutsDialog: false,
suggestedBindings: [], suggestedBindings: [],
zenModeEnabled: false, zenModeEnabled: false,
gridSize: null, showGrid: false,
editingGroupId: null, editingGroupId: null,
selectedGroupIds: {}, selectedGroupIds: {},
width: window.innerWidth, width: window.innerWidth,
@@ -120,7 +120,7 @@ const APP_STATE_STORAGE_CONF = (<
errorMessage: { browser: false, export: false }, errorMessage: { browser: false, export: false },
exportBackground: { browser: true, export: false }, exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false }, exportEmbedScene: { browser: true, export: false },
gridSize: { browser: true, export: true }, showGrid: { browser: true, export: false },
height: { browser: false, export: false }, height: { browser: false, export: false },
isBindingEnabled: { browser: false, export: false }, isBindingEnabled: { browser: false, export: false },
isLibraryOpen: { browser: false, export: false }, isLibraryOpen: { browser: false, export: false },
@@ -166,11 +166,6 @@ const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] }; const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] };
for (const key of Object.keys(appState) as (keyof typeof appState)[]) { for (const key of Object.keys(appState) as (keyof typeof appState)[]) {
const propConfig = APP_STATE_STORAGE_CONF[key]; const propConfig = APP_STATE_STORAGE_CONF[key];
if (!propConfig) {
console.error(
`_clearAppStateForStorage: appState key "${key}" config doesn't exist for "${exportType}" export type`,
);
}
if (propConfig?.[exportType]) { if (propConfig?.[exportType]) {
// @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445 // @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445
stateForExport[key] = appState[key]; stateForExport[key] = appState[key];
+18 -12
View File
@@ -19,15 +19,15 @@ export const NOT_SPREADSHEET = "NOT_SPREADSHEET";
export const VALID_SPREADSHEET = "VALID_SPREADSHEET"; export const VALID_SPREADSHEET = "VALID_SPREADSHEET";
type ParseSpreadsheetResult = type ParseSpreadsheetResult =
| { type: typeof NOT_SPREADSHEET } | { type: typeof NOT_SPREADSHEET; reason: string }
| { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet }; | { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet };
const tryParseNumber = (s: string): number | null => { const tryParseNumber = (s: string): number | null => {
const match = /^[$€£¥₩]?([0-9]+(\.[0-9]+)?)$/.exec(s); const match = /^[$€£¥₩]?([0-9,]+(\.[0-9]+)?)$/.exec(s);
if (!match) { if (!match) {
return null; return null;
} }
return parseFloat(match[1]); return parseFloat(match[1].replace(/,/g, ""));
}; };
const isNumericColumn = (lines: string[][], columnIndex: number) => const isNumericColumn = (lines: string[][], columnIndex: number) =>
@@ -37,12 +37,12 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const numCols = cells[0].length; const numCols = cells[0].length;
if (numCols > 2) { if (numCols > 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "More than 2 columns" };
} }
if (numCols === 1) { if (numCols === 1) {
if (!isNumericColumn(cells, 0)) { if (!isNumericColumn(cells, 0)) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
} }
const hasHeader = tryParseNumber(cells[0][0]) === null; const hasHeader = tryParseNumber(cells[0][0]) === null;
@@ -51,7 +51,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
); );
if (values.length < 2) { if (values.length < 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Less than two rows" };
} }
return { return {
@@ -67,7 +67,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const valueColumnIndex = isNumericColumn(cells, 0) ? 0 : 1; const valueColumnIndex = isNumericColumn(cells, 0) ? 0 : 1;
if (!isNumericColumn(cells, valueColumnIndex)) { if (!isNumericColumn(cells, valueColumnIndex)) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
} }
const labelColumnIndex = (valueColumnIndex + 1) % 2; const labelColumnIndex = (valueColumnIndex + 1) % 2;
@@ -75,7 +75,7 @@ const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const rows = hasHeader ? cells.slice(1) : cells; const rows = hasHeader ? cells.slice(1) : cells;
if (rows.length < 2) { if (rows.length < 2) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "Less than 2 rows" };
} }
return { return {
@@ -104,13 +104,13 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
// Copy/paste from excel, spreadhseets, tsv, csv. // Copy/paste from excel, spreadhseets, tsv, csv.
// For now we only accept 2 columns with an optional header // For now we only accept 2 columns with an optional header
// Check for tab separeted values // Check for tab separated values
let lines = text let lines = text
.trim() .trim()
.split("\n") .split("\n")
.map((line) => line.trim().split("\t")); .map((line) => line.trim().split("\t"));
// Check for comma separeted files // Check for comma separated files
if (lines.length && lines[0].length !== 2) { if (lines.length && lines[0].length !== 2) {
lines = text lines = text
.trim() .trim()
@@ -119,14 +119,17 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
} }
if (lines.length === 0) { if (lines.length === 0) {
return { type: NOT_SPREADSHEET }; return { type: NOT_SPREADSHEET, reason: "No values" };
} }
const numColsFirstLine = lines[0].length; const numColsFirstLine = lines[0].length;
const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine); const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine);
if (!isSpreadsheet) { if (!isSpreadsheet) {
return { type: NOT_SPREADSHEET }; return {
type: NOT_SPREADSHEET,
reason: "All rows don't have same number of columns",
};
} }
const result = tryParseCells(lines); const result = tryParseCells(lines);
@@ -192,6 +195,7 @@ export const renderSpreadsheet = (
y, y,
startArrowhead: null, startArrowhead: null,
endArrowhead: null, endArrowhead: null,
width: chartWidth,
points: [ points: [
[0, 0], [0, 0],
[chartWidth, 0], [chartWidth, 0],
@@ -205,6 +209,7 @@ export const renderSpreadsheet = (
y, y,
startArrowhead: null, startArrowhead: null,
endArrowhead: null, endArrowhead: null,
height: chartHeight,
points: [ points: [
[0, 0], [0, 0],
[0, -chartHeight], [0, -chartHeight],
@@ -220,6 +225,7 @@ export const renderSpreadsheet = (
endArrowhead: null, endArrowhead: null,
...commonProps, ...commonProps,
strokeStyle: "dotted", strokeStyle: "dotted",
width: chartWidth,
points: [ points: [
[0, 0], [0, 0],
[chartWidth, 0], [chartWidth, 0],
+80 -41
View File
@@ -124,6 +124,7 @@ import {
MIME_TYPES, MIME_TYPES,
TAP_TWICE_TIMEOUT, TAP_TWICE_TIMEOUT,
TOUCH_CTX_MENU_TIMEOUT, TOUCH_CTX_MENU_TIMEOUT,
APP_NAME,
} from "../constants"; } from "../constants";
import LayerUI from "./LayerUI"; import LayerUI from "./LayerUI";
@@ -345,12 +346,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
offsetLeft, offsetLeft,
} = this.state; } = this.state;
const { onCollabButtonClick } = this.props; const { onCollabButtonClick, onExportToBackend } = this.props;
const canvasScale = window.devicePixelRatio; const canvasScale = window.devicePixelRatio;
const canvasWidth = canvasDOMWidth * canvasScale; const canvasWidth = canvasDOMWidth * canvasScale;
const canvasHeight = canvasDOMHeight * canvasScale; const canvasHeight = canvasDOMHeight * canvasScale;
const DEFAULT_PASTE_X = canvasDOMWidth / 2;
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
return ( return (
<div <div
className="excalidraw" className="excalidraw"
@@ -371,12 +375,17 @@ class App extends React.Component<ExcalidrawProps, AppState> {
onCollabButtonClick={onCollabButtonClick} onCollabButtonClick={onCollabButtonClick}
onLockToggle={this.toggleLock} onLockToggle={this.toggleLock}
onInsertShape={(elements) => onInsertShape={(elements) =>
this.addElementsFromPasteOrLibrary(elements) this.addElementsFromPasteOrLibrary(
elements,
DEFAULT_PASTE_X,
DEFAULT_PASTE_Y,
)
} }
zenModeEnabled={zenModeEnabled} zenModeEnabled={zenModeEnabled}
toggleZenMode={this.toggleZenMode} toggleZenMode={this.toggleZenMode}
lng={getLanguage().lng} lng={getLanguage().lng}
isCollaborating={this.props.isCollaborating || false} isCollaborating={this.props.isCollaborating || false}
onExportToBackend={onExportToBackend}
/> />
{this.state.showStats && ( {this.state.showStats && (
<Stats <Stats
@@ -494,7 +503,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}; };
private importLibraryFromUrl = async (url: string) => { private importLibraryFromUrl = async (url: string) => {
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
try { try {
const request = await fetch(url); const request = await fetch(url);
const blob = await request.blob(); const blob = await request.blob();
@@ -586,6 +595,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scene.elements, scene.elements,
{ {
...scene.appState, ...scene.appState,
width: this.state.width,
height: this.state.height,
offsetTop: this.state.offsetTop, offsetTop: this.state.offsetTop,
offsetLeft: this.state.offsetLeft, offsetLeft: this.state.offsetLeft,
}, },
@@ -1025,7 +1036,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const dy = y - elementsCenterY; const dy = y - elementsCenterY;
const groupIdMap = new Map(); const groupIdMap = new Map();
const [gridX, gridY] = getGridPoint(dx, dy, this.state.gridSize); const [gridX, gridY] = getGridPoint(dx, dy, this.state.showGrid);
const oldIdToDuplicatedId = new Map(); const oldIdToDuplicatedId = new Map();
const newElements = clipboardElements.map((element) => { const newElements = clipboardElements.map((element) => {
@@ -1138,7 +1149,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
toggleGridMode = () => { toggleGridMode = () => {
this.setState({ this.setState({
gridSize: this.state.gridSize ? null : GRID_SIZE, showGrid: !this.state.showGrid,
}); });
}; };
@@ -1264,8 +1275,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (isArrowKey(event.key)) { if (isArrowKey(event.key)) {
const step = const step =
(this.state.gridSize && (this.state.showGrid &&
(event.shiftKey ? ELEMENT_TRANSLATE_AMOUNT : this.state.gridSize)) || (event.shiftKey ? ELEMENT_TRANSLATE_AMOUNT : GRID_SIZE)) ||
(event.shiftKey (event.shiftKey
? ELEMENT_SHIFT_TRANSLATE_AMOUNT ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
: ELEMENT_TRANSLATE_AMOUNT); : ELEMENT_TRANSLATE_AMOUNT);
@@ -1416,13 +1427,27 @@ class App extends React.Component<ExcalidrawProps, AppState> {
private onGestureChange = withBatchedUpdates((event: GestureEvent) => { private onGestureChange = withBatchedUpdates((event: GestureEvent) => {
event.preventDefault(); event.preventDefault();
this.setState(({ zoom }) => ({
zoom: getNewZoom( // onGestureChange only has zoom factor but not the center.
getNormalizedZoom(gesture.initialScale! * event.scale), // If we're on iPad or iPhone, then we recognize multi-touch and will
zoom, // zoom in at the right location on the touchMove handler already.
{ x: cursorX, y: cursorY }, // On Macbook, we don't have those events so will zoom in at the
), // current location instead.
})); if (gesture.pointers.size === 2) {
return;
}
const initialScale = gesture.initialScale;
if (initialScale) {
this.setState(({ zoom, offsetLeft, offsetTop }) => ({
zoom: getNewZoom(
getNormalizedZoom(initialScale * event.scale),
zoom,
{ left: offsetLeft, top: offsetTop },
{ x: cursorX, y: cursorY },
),
}));
}
}); });
private onGestureEnd = withBatchedUpdates((event: GestureEvent) => { private onGestureEnd = withBatchedUpdates((event: GestureEvent) => {
@@ -1734,21 +1759,28 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}); });
} }
if (gesture.pointers.size === 2) { const initialScale = gesture.initialScale;
if (
gesture.pointers.size === 2 &&
gesture.lastCenter &&
initialScale &&
gesture.initialDistance
) {
const center = getCenter(gesture.pointers); const center = getCenter(gesture.pointers);
const deltaX = center.x - gesture.lastCenter!.x; const deltaX = center.x - gesture.lastCenter.x;
const deltaY = center.y - gesture.lastCenter!.y; const deltaY = center.y - gesture.lastCenter.y;
gesture.lastCenter = center; gesture.lastCenter = center;
const distance = getDistance(Array.from(gesture.pointers.values())); const distance = getDistance(Array.from(gesture.pointers.values()));
const scaleFactor = distance / gesture.initialDistance!; const scaleFactor = distance / gesture.initialDistance;
this.setState(({ zoom, scrollX, scrollY }) => ({ this.setState(({ zoom, scrollX, scrollY, offsetLeft, offsetTop }) => ({
scrollX: normalizeScroll(scrollX + deltaX / zoom.value), scrollX: normalizeScroll(scrollX + deltaX / zoom.value),
scrollY: normalizeScroll(scrollY + deltaY / zoom.value), scrollY: normalizeScroll(scrollY + deltaY / zoom.value),
zoom: getNewZoom( zoom: getNewZoom(
getNormalizedZoom(gesture.initialScale! * scaleFactor), getNormalizedZoom(initialScale * scaleFactor),
zoom, zoom,
{ left: offsetLeft, top: offsetTop },
center, center,
), ),
shouldCacheIgnoreZoom: true, shouldCacheIgnoreZoom: true,
@@ -1787,7 +1819,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scenePointerX, scenePointerX,
scenePointerY, scenePointerY,
this.state.editingLinearElement, this.state.editingLinearElement,
this.state.gridSize, this.state.showGrid,
); );
if (editingLinearElement !== this.state.editingLinearElement) { if (editingLinearElement !== this.state.editingLinearElement) {
this.setState({ editingLinearElement }); this.setState({ editingLinearElement });
@@ -2217,7 +2249,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return { return {
origin, origin,
originInGrid: tupleToCoors( originInGrid: tupleToCoors(
getGridPoint(origin.x, origin.y, this.state.gridSize), getGridPoint(origin.x, origin.y, this.state.showGrid),
), ),
scrollbars: isOverScrollBars( scrollbars: isOverScrollBars(
currentScrollBars, currentScrollBars,
@@ -2575,13 +2607,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerDownState.origin.x, pointerDownState.origin.x,
pointerDownState.origin.y, pointerDownState.origin.y,
elementType === "draw" ? null : this.state.gridSize, elementType === "draw" ? false : this.state.showGrid,
); );
/* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads. /* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads.
If so, we want it to be null for start and "arrow" for end. If the linear item is not If so, we want it to be null for start and "arrow" for end. If the linear item is not
an arrow, we want it to be null for both. Otherwise, we want it to use the an arrow, we want it to be null for both. Otherwise, we want it to use the
values from appState. */ values from appState. */
const { currentItemStartArrowhead, currentItemEndArrowhead } = this.state; const { currentItemStartArrowhead, currentItemEndArrowhead } = this.state;
const [startArrowhead, endArrowhead] = const [startArrowhead, endArrowhead] =
@@ -2637,7 +2669,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerDownState.origin.x, pointerDownState.origin.x,
pointerDownState.origin.y, pointerDownState.origin.y,
this.state.gridSize, this.state.showGrid,
); );
const element = newElement({ const element = newElement({
type: elementType, type: elementType,
@@ -2726,7 +2758,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerCoords.x, pointerCoords.x,
pointerCoords.y, pointerCoords.y,
this.state.gridSize, this.state.showGrid,
); );
// for arrows/lines, don't start dragging until a given threshold // for arrows/lines, don't start dragging until a given threshold
@@ -2798,7 +2830,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [dragX, dragY] = getGridPoint( const [dragX, dragY] = getGridPoint(
pointerCoords.x - pointerDownState.drag.offset.x, pointerCoords.x - pointerDownState.drag.offset.x,
pointerCoords.y - pointerDownState.drag.offset.y, pointerCoords.y - pointerDownState.drag.offset.y,
this.state.gridSize, this.state.showGrid,
); );
const [dragDistanceX, dragDistanceY] = [ const [dragDistanceX, dragDistanceY] = [
@@ -2850,7 +2882,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [originDragX, originDragY] = getGridPoint( const [originDragX, originDragY] = getGridPoint(
pointerDownState.origin.x - pointerDownState.drag.offset.x, pointerDownState.origin.x - pointerDownState.drag.offset.x,
pointerDownState.origin.y - pointerDownState.drag.offset.y, pointerDownState.origin.y - pointerDownState.drag.offset.y,
this.state.gridSize, this.state.showGrid,
); );
mutateElement(duplicatedElement, { mutateElement(duplicatedElement, {
x: duplicatedElement.x + (originDragX - dragX), x: duplicatedElement.x + (originDragX - dragX),
@@ -3510,7 +3542,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerCoords.x, pointerCoords.x,
pointerCoords.y, pointerCoords.y,
this.state.gridSize, this.state.showGrid,
); );
dragNewElement( dragNewElement(
draggingElement, draggingElement,
@@ -3548,7 +3580,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [resizeX, resizeY] = getGridPoint( const [resizeX, resizeY] = getGridPoint(
pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.x - pointerDownState.resize.offset.x,
pointerCoords.y - pointerDownState.resize.offset.y, pointerCoords.y - pointerDownState.resize.offset.y,
this.state.gridSize, this.state.showGrid,
); );
if ( if (
transformElements( transformElements(
@@ -3612,13 +3644,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
CANVAS_ONLY_ACTIONS.includes(action.name), CANVAS_ONLY_ACTIONS.includes(action.name),
), ),
{ {
shortcutName: "toggleGridMode", checked: this.state.showGrid,
label: t("labels.toggleGridMode"), shortcutName: "gridMode",
label: t("labels.gridMode"),
action: this.toggleGridMode, action: this.toggleGridMode,
}, },
{ {
shortcutName: "toggleStats", checked: this.state.showStats,
label: t("labels.toggleStats"), shortcutName: "stats",
label: t("stats.title"),
action: this.toggleStats, action: this.toggleStats,
}, },
], ],
@@ -3695,11 +3729,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}, 1000); }, 1000);
} }
this.setState(({ zoom }) => ({ this.setState(({ zoom, offsetLeft, offsetTop }) => ({
zoom: getNewZoom(getNormalizedZoom(zoom.value - delta / 100), zoom, { zoom: getNewZoom(
x: cursorX, getNormalizedZoom(zoom.value - delta / 100),
y: cursorY, zoom,
}), { left: offsetLeft, top: offsetTop },
{
x: cursorX,
y: cursorY,
},
),
selectedElementIds: {}, selectedElementIds: {},
previousSelectedElementIds: previousSelectedElementIds:
Object.keys(selectedElementIds).length !== 0 Object.keys(selectedElementIds).length !== 0
+22
View File
@@ -32,6 +32,21 @@
display: grid; display: grid;
grid-template-columns: 1fr 0.2fr; grid-template-columns: 1fr 0.2fr;
align-items: center;
&.checkmark::before {
position: absolute;
left: 6px;
margin-bottom: 1px;
content: "\2713";
}
&.dangerous {
div:nth-child(1) {
color: $oc-red-7;
}
}
div:nth-child(1) { div:nth-child(1) {
justify-self: start; justify-self: start;
margin-inline-end: 20px; margin-inline-end: 20px;
@@ -46,6 +61,13 @@
.context-menu-option:hover { .context-menu-option:hover {
color: var(--popup-background-color); color: var(--popup-background-color);
background-color: var(--select-highlight-color); background-color: var(--select-highlight-color);
&.dangerous {
div:nth-child(1) {
color: var(--popup-background-color);
}
background-color: $oc-red-6;
}
} }
.context-menu-option:focus { .context-menu-option:focus {
+8 -3
View File
@@ -10,6 +10,7 @@ import {
} from "../actions/shortcuts"; } from "../actions/shortcuts";
type ContextMenuOption = { type ContextMenuOption = {
checked?: boolean;
shortcutName: ShortcutName; shortcutName: ShortcutName;
label: string; label: string;
action(): void; action(): void;
@@ -26,7 +27,6 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
const isDarkTheme = !!document const isDarkTheme = !!document
.querySelector(".excalidraw") .querySelector(".excalidraw")
?.classList.contains("Appearance_dark"); ?.classList.contains("Appearance_dark");
return ( return (
<div <div
className={clsx("excalidraw", { className={clsx("excalidraw", {
@@ -43,9 +43,14 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
className="context-menu" className="context-menu"
onContextMenu={(event) => event.preventDefault()} onContextMenu={(event) => event.preventDefault()}
> >
{options.map(({ action, shortcutName, label }, idx) => ( {options.map(({ action, checked, shortcutName, label }, idx) => (
<li data-testid={shortcutName} key={idx} onClick={onCloseRequest}> <li data-testid={shortcutName} key={idx} onClick={onCloseRequest}>
<button className="context-menu-option" onClick={action}> <button
className={`context-menu-option
${shortcutName === "delete" ? "dangerous" : ""}
${checked ? "checkmark" : ""}`}
onClick={action}
>
<div>{label}</div> <div>{label}</div>
<div> <div>
{shortcutName {shortcutName
+8 -2
View File
@@ -14,7 +14,9 @@ export const DarkModeToggle = (props: {
return ( return (
<label <label
className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`} className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`}
title={t("buttons.toggleDarkMode")} title={
props.value === "dark" ? t("buttons.lightMode") : t("buttons.darkMode")
}
> >
<input <input
className="ToolIcon_type_checkbox ToolIcon_toggle_opaque" className="ToolIcon_type_checkbox ToolIcon_toggle_opaque"
@@ -23,7 +25,11 @@ export const DarkModeToggle = (props: {
props.onChange(event.target.checked ? "dark" : "light") props.onChange(event.target.checked ? "dark" : "light")
} }
checked={props.value === "dark"} checked={props.value === "dark"}
aria-label={t("buttons.toggleDarkMode")} aria-label={
props.value === "dark"
? t("buttons.lightMode")
: t("buttons.darkMode")
}
/> />
<div className="ToolIcon__icon"> <div className="ToolIcon__icon">
{props.value === "light" ? ICONS.MOON : ICONS.SUN} {props.value === "light" ? ICONS.MOON : ICONS.SUN}
+8 -1
View File
@@ -28,7 +28,14 @@ export const ErrorDialog = ({
onCloseRequest={handleClose} onCloseRequest={handleClose}
title={t("errorDialog.title")} title={t("errorDialog.title")}
> >
<div>{message}</div> <div>
{message.split("\n").map((line) => (
<>
{line}
<br />
</>
))}
</div>
</Dialog> </Dialog>
)} )}
</> </>
+11 -9
View File
@@ -67,7 +67,7 @@ const ExportModal = ({
onExportToPng: ExportCB; onExportToPng: ExportCB;
onExportToSvg: ExportCB; onExportToSvg: ExportCB;
onExportToClipboard: ExportCB; onExportToClipboard: ExportCB;
onExportToBackend: ExportCB; onExportToBackend?: ExportCB;
onCloseRequest: () => void; onCloseRequest: () => void;
}) => { }) => {
const someElementIsSelected = isSomeElementSelected(elements, appState); const someElementIsSelected = isSomeElementSelected(elements, appState);
@@ -155,13 +155,15 @@ const ExportModal = ({
onClick={() => onExportToClipboard(exportedElements, scale)} onClick={() => onExportToClipboard(exportedElements, scale)}
/> />
)} )}
<ToolButton {onExportToBackend && (
type="button" <ToolButton
icon={link} type="button"
title={t("buttons.getShareableLink")} icon={link}
aria-label={t("buttons.getShareableLink")} title={t("buttons.getShareableLink")}
onClick={() => onExportToBackend(exportedElements)} aria-label={t("buttons.getShareableLink")}
/> onClick={() => onExportToBackend(exportedElements)}
/>
)}
</Stack.Row> </Stack.Row>
<div className="ExportDialog__name"> <div className="ExportDialog__name">
{actionManager.renderAction("changeProjectName")} {actionManager.renderAction("changeProjectName")}
@@ -235,7 +237,7 @@ export const ExportDialog = ({
onExportToPng: ExportCB; onExportToPng: ExportCB;
onExportToSvg: ExportCB; onExportToSvg: ExportCB;
onExportToClipboard: ExportCB; onExportToClipboard: ExportCB;
onExportToBackend: ExportCB; onExportToBackend?: ExportCB;
}) => { }) => {
const [modalIsShown, setModalIsShown] = useState(false); const [modalIsShown, setModalIsShown] = useState(false);
const triggerButton = useRef<HTMLButtonElement>(null); const triggerButton = useRef<HTMLButtonElement>(null);
+2 -2
View File
@@ -38,8 +38,8 @@ const getHints = ({ appState, elements }: Hint) => {
selectedElements.length === 1 selectedElements.length === 1
) { ) {
const targetElement = selectedElements[0]; const targetElement = selectedElements[0];
if (isLinearElement(targetElement) && targetElement.points.length > 2) { if (isLinearElement(targetElement) && targetElement.points.length === 2) {
return null; return t("hints.lockAngle");
} }
return t("hints.resize"); return t("hints.resize");
} }
+3
View File
@@ -93,6 +93,9 @@
grid-auto-flow: column; grid-auto-flow: column;
grid-gap: 0.5rem; grid-gap: 0.5rem;
border-radius: 4px; border-radius: 4px;
:root[dir="rtl"] & {
padding: 0.4rem;
}
} }
.picker-keybinding { .picker-keybinding {
-58
View File
@@ -39,64 +39,6 @@
width: 1.2rem; width: 1.2rem;
height: 1.2rem; height: 1.2rem;
} }
&.tooltip .tooltip-text {
visibility: hidden;
width: 20rem;
bottom: calc(50% + 0.8rem + 6px);
:root[dir="ltr"] & {
left: -5px;
}
:root[dir="rtl"] & {
right: -5px;
}
background-color: $oc-black;
color: $oc-white;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 10;
font-size: 13px;
line-height: 1.5;
white-space: pre-wrap;
&::after {
--size: 6px;
content: "";
border: var(--size) solid transparent;
border-top-color: $oc-black;
position: absolute;
bottom: calc(-2 * var(--size));
:root[dir="ltr"] & {
left: calc(5px + var(--size) / 2);
}
:root[dir="rtl"] & {
right: calc(5px + var(--size) / 2);
}
}
}
// the following 3 rules ensure that the tooltip doesn't show (nor affect
// the cursor) when you drag over when you draw on canvas, but at the same
// time it still works when clicking on the link/shield
body:active &.tooltip:not(:hover) {
pointer-events: none;
}
body:not(:active) &.tooltip:hover .tooltip-text {
visibility: visible;
}
.tooltip-text:hover {
visibility: visible;
}
} }
&__github-corner { &__github-corner {
+17 -26
View File
@@ -65,6 +65,11 @@ interface LayerUIProps {
toggleZenMode: () => void; toggleZenMode: () => void;
lng: string; lng: string;
isCollaborating: boolean; isCollaborating: boolean;
onExportToBackend?: (
exportedElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
canvas: HTMLCanvasElement | null,
) => void;
} }
const useOnClickOutside = ( const useOnClickOutside = (
@@ -317,10 +322,10 @@ const LayerUI = ({
zenModeEnabled, zenModeEnabled,
toggleZenMode, toggleZenMode,
isCollaborating, isCollaborating,
onExportToBackend,
}: LayerUIProps) => { }: LayerUIProps) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
// TODO: Extend tooltip component and use here.
const renderEncryptedIcon = () => ( const renderEncryptedIcon = () => (
<a <a
className={clsx("encrypted-icon tooltip zen-mode-visibility", { className={clsx("encrypted-icon tooltip zen-mode-visibility", {
@@ -333,10 +338,9 @@ const LayerUI = ({
trackEvent(EVENT_EXIT, "e2ee shield"); trackEvent(EVENT_EXIT, "e2ee shield");
}} }}
> >
<span className="tooltip-text" dir="auto"> <Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
{t("encrypted.tooltip")} {shield}
</span> </Tooltip>
{shield}
</a> </a>
); );
@@ -360,6 +364,7 @@ const LayerUI = ({
}); });
} }
}; };
return ( return (
<ExportDialog <ExportDialog
elements={elements} elements={elements}
@@ -368,28 +373,14 @@ const LayerUI = ({
onExportToPng={createExporter("png")} onExportToPng={createExporter("png")}
onExportToSvg={createExporter("svg")} onExportToSvg={createExporter("svg")}
onExportToClipboard={createExporter("clipboard")} onExportToClipboard={createExporter("clipboard")}
onExportToBackend={async (exportedElements) => { onExportToBackend={
if (canvas) { onExportToBackend
try { ? (elements) => {
await exportCanvas( onExportToBackend &&
"backend", onExportToBackend(elements, appState, canvas);
exportedElements,
{
...appState,
selectedElementIds: {},
},
canvas,
appState,
);
} catch (error) {
if (error.name !== "AbortError") {
const { width, height } = canvas;
console.error(error, { width, height });
setAppState({ errorMessage: error.message });
} }
} : undefined
} }
}}
/> />
); );
}; };
+5 -4
View File
@@ -207,15 +207,16 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
shortcuts={["Shift+1"]} shortcuts={["Shift+1"]}
/> />
<Shortcut <Shortcut
label={t("buttons.toggleFullScreen")} label={t("shortcutsDialog.zoomToSelection")}
shortcuts={["F"]} shortcuts={["Shift+2"]}
/> />
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
<Shortcut <Shortcut
label={t("buttons.toggleZenMode")} label={t("buttons.zenMode")}
shortcuts={[getShortcutKey("Alt+Z")]} shortcuts={[getShortcutKey("Alt+Z")]}
/> />
<Shortcut <Shortcut
label={t("labels.toggleGridMode")} label={t("labels.gridMode")}
shortcuts={[getShortcutKey("CtrlOrCmd+'")]} shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
/> />
</ShortcutIsland> </ShortcutIsland>
+15 -1
View File
@@ -6,8 +6,10 @@
right: 12px; right: 12px;
font-size: 12px; font-size: 12px;
z-index: 999; z-index: 999;
h3 { h3 {
margin: 0 24px 8px 0; margin: 0 24px 8px 0;
white-space: nowrap;
} }
.close { .close {
@@ -29,9 +31,21 @@
} }
tr { tr {
td:nth-child(2) { td:nth-child(2) {
min-width: 48px; min-width: 24px;
text-align: right; text-align: right;
} }
} }
} }
:root[dir="rtl"] & {
left: 12px;
right: initial;
h3 {
margin: 0 0 8px 24px;
}
.close {
float: left;
}
}
} }
+13
View File
@@ -1,4 +1,6 @@
@import "open-color/open-color.scss"; @import "open-color/open-color.scss";
@import "../css/variables";
.excalidraw { .excalidraw {
.ToolIcon { .ToolIcon {
display: inline-flex; display: inline-flex;
@@ -181,6 +183,17 @@
} }
} }
.TooltipIcon {
width: 0.9em;
height: 0.9em;
margin-left: 5px;
margin-top: 1px;
@media #{$media-query} {
display: none;
}
}
.unlocked-icon { .unlocked-icon {
:root[dir="ltr"] & { :root[dir="ltr"] & {
left: 2px; left: 2px;
+27 -10
View File
@@ -7,39 +7,56 @@
.Tooltip__label { .Tooltip__label {
--arrow-size: 4px; --arrow-size: 4px;
visibility: hidden; visibility: hidden;
width: 10ch;
background: $oc-black; background: $oc-black;
color: $oc-white; color: $oc-white;
text-align: center; text-align: center;
border-radius: 4px; border-radius: 6px;
padding: 4px; padding: 8px;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
font-size: 0.7rem; font-size: 13px;
line-height: 1.5; line-height: 1.5;
top: calc(100% + var(--arrow-size) + 3px); font-weight: 500;
// extra pixel offset for unknown reasons // extra pixel offset for unknown reasons
left: calc(-50% + var(--arrow-size) / 2 - 1px); left: calc(50% + var(--arrow-size) / 2 - 1px);
transform: translateX(-50%);
word-wrap: break-word; word-wrap: break-word;
&::after { &::after {
content: ""; content: "";
border: var(--arrow-size) solid transparent; border: var(--arrow-size) solid transparent;
border-bottom-color: $oc-black;
position: absolute; position: absolute;
bottom: 100%;
left: calc(50% - var(--arrow-size)); left: calc(50% - var(--arrow-size));
} }
&--above {
bottom: calc(100% + var(--arrow-size) + 3px);
&::after {
border-top-color: $oc-black;
top: 100%;
}
}
&--below {
top: calc(100% + var(--arrow-size) + 3px);
&::after {
border-bottom-color: $oc-black;
bottom: 100%;
}
}
} }
// the following 3 rules ensure that the tooltip doesn't show (nor affect // the following 3 rules ensure that the tooltip doesn't show (nor affect
// the cursor) when you drag over when you draw on canvas, but at the same // the cursor) when you drag over when you draw on canvas, but at the same
// time it still works when clicking on the link/shield // time it still works when clicking on the link/shield
body:active .Tooltip:not(:hover) {
body:active & .Tooltip:not(:hover) {
pointer-events: none; pointer-events: none;
} }
body:not(:active) .Tooltip:hover .Tooltip__label { body:not(:active) & .Tooltip:hover .Tooltip__label {
visibility: visible; visibility: visible;
} }
+18 -2
View File
@@ -5,11 +5,27 @@ import React from "react";
type TooltipProps = { type TooltipProps = {
children: React.ReactNode; children: React.ReactNode;
label: string; label: string;
position?: "above" | "below";
long?: boolean;
}; };
export const Tooltip = ({ children, label }: TooltipProps) => ( export const Tooltip = ({
children,
label,
position = "below",
long = false,
}: TooltipProps) => (
<div className="Tooltip"> <div className="Tooltip">
<span className="Tooltip__label">{label}</span> <span
className={
position === "above"
? "Tooltip__label Tooltip__label--above"
: "Tooltip__label Tooltip__label--below"
}
style={{ width: long ? "50ch" : "10ch" }}
>
{label}
</span>
{children} {children}
</div> </div>
); );
+5
View File
@@ -108,6 +108,11 @@ export const redo = createIcon(
{ mirror: true }, { mirror: true },
); );
export const questionCircle = createIcon(
"M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z",
{ mirror: true },
);
// Icon imported form Storybook // Icon imported form Storybook
// Storybook is licensed under MIT https://github.com/storybookjs/storybook/blob/next/LICENSE // Storybook is licensed under MIT https://github.com/storybookjs/storybook/blob/next/LICENSE
export const resetZoom = createIcon( export const resetZoom = createIcon(
+4 -1
View File
@@ -1,5 +1,7 @@
import { FontFamily } from "./element/types"; import { FontFamily } from "./element/types";
export const APP_NAME = "Excalidraw";
export const DRAGGING_THRESHOLD = 10; // 10px export const DRAGGING_THRESHOLD = 10; // 10px
export const LINE_CONFIRM_THRESHOLD = 10; // 10px export const LINE_CONFIRM_THRESHOLD = 10; // 10px
export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5; export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
@@ -71,7 +73,7 @@ export const DEFAULT_VERTICAL_ALIGN = "top";
export const CANVAS_ONLY_ACTIONS = ["selectAll"]; export const CANVAS_ONLY_ACTIONS = ["selectAll"];
export const GRID_SIZE = 20; // TODO make it configurable? export const GRID_SIZE = 20;
export const MIME_TYPES = { export const MIME_TYPES = {
excalidraw: "application/vnd.excalidraw+json", excalidraw: "application/vnd.excalidraw+json",
@@ -85,3 +87,4 @@ export const STORAGE_KEYS = {
// time in milliseconds // time in milliseconds
export const TAP_TWICE_TIMEOUT = 300; export const TAP_TWICE_TIMEOUT = 300;
export const TOUCH_CTX_MENU_TIMEOUT = 500; export const TOUCH_CTX_MENU_TIMEOUT = 500;
export const TITLE_TIMEOUT = 10000;
+1 -1
View File
@@ -9,7 +9,7 @@ import { AppState } from "../types";
import { restore } from "./restore"; import { restore } from "./restore";
import { ImportedDataState, LibraryData } from "./types"; import { ImportedDataState, LibraryData } from "./types";
export const parseFileContents = async (blob: Blob | File) => { const parseFileContents = async (blob: Blob | File) => {
let contents: string; let contents: string;
if (blob.type === "image/png") { if (blob.type === "image/png") {
+1 -71
View File
@@ -1,14 +1,10 @@
import { fileSave } from "browser-nativefs"; import { fileSave } from "browser-nativefs";
import { EVENT_IO, trackEvent } from "../analytics"; import { EVENT_IO, trackEvent } from "../analytics";
import { getDefaultAppState } from "../appState";
import { import {
copyCanvasToClipboardAsPng, copyCanvasToClipboardAsPng,
copyTextToSystemClipboard, copyTextToSystemClipboard,
} from "../clipboard"; } from "../clipboard";
import { import { NonDeletedExcalidrawElement } from "../element/types";
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "../element/types";
import { t } from "../i18n"; import { t } from "../i18n";
import { exportToCanvas, exportToSvg } from "../scene/export"; import { exportToCanvas, exportToSvg } from "../scene/export";
import { ExportType } from "../scene/types"; import { ExportType } from "../scene/types";
@@ -19,65 +15,6 @@ import { serializeAsJSON } from "./json";
export { loadFromBlob } from "./blob"; export { loadFromBlob } from "./blob";
export { loadFromJSON, saveAsJSON } from "./json"; export { loadFromJSON, saveAsJSON } from "./json";
const BACKEND_V2_POST = process.env.REACT_APP_BACKEND_V2_POST_URL;
export const exportToBackend = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const json = serializeAsJSON(elements, appState);
const encoded = new TextEncoder().encode(json);
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128,
},
true, // extractable
["encrypt", "decrypt"],
);
// The iv is set to 0. We are never going to reuse the same key so we don't
// need to have an iv. (I hope that's correct...)
const iv = new Uint8Array(12);
// We use symmetric encryption. AES-GCM is the recommended algorithm and
// includes checks that the ciphertext has not been modified by an attacker.
const encrypted = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
key,
encoded,
);
// We use jwk encoding to be able to extract just the base64 encoded key.
// We will hardcode the rest of the attributes when importing back the key.
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
try {
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: encrypted,
});
const json = await response.json();
if (json.id) {
const url = new URL(window.location.href);
// We need to store the key (and less importantly the id) as hash instead
// of queryParam in order to never send it to the server
url.hash = `json=${json.id},${exportedKey.k!}`;
const urlString = url.toString();
window.prompt(`🔒${t("alerts.uploadedSecurly")}`, urlString);
trackEvent(EVENT_IO, "export", "backend");
} else if (json.error_class === "RequestTooLargeError") {
window.alert(t("alerts.couldNotCreateShareableLinkTooBig"));
} else {
window.alert(t("alerts.couldNotCreateShareableLink"));
}
} catch (error) {
console.error(error);
window.alert(t("alerts.couldNotCreateShareableLink"));
}
};
export const exportCanvas = async ( export const exportCanvas = async (
type: ExportType, type: ExportType,
elements: readonly NonDeletedExcalidrawElement[], elements: readonly NonDeletedExcalidrawElement[],
@@ -169,13 +106,6 @@ export const exportCanvas = async (
} }
throw new Error(t("alerts.couldNotCopyToClipboard")); throw new Error(t("alerts.couldNotCopyToClipboard"));
} }
} else if (type === "backend") {
exportToBackend(elements, {
...appState,
viewBackgroundColor: exportBackground
? appState.viewBackgroundColor
: getDefaultAppState().viewBackgroundColor,
});
} }
// clean up the DOM // clean up the DOM
+6 -6
View File
@@ -102,7 +102,7 @@ export class LinearElementEditor {
element, element,
scenePointerX - editingLinearElement.pointerOffset.x, scenePointerX - editingLinearElement.pointerOffset.x,
scenePointerY - editingLinearElement.pointerOffset.y, scenePointerY - editingLinearElement.pointerOffset.y,
appState.gridSize, appState.showGrid,
); );
LinearElementEditor.movePoint(element, activePointIndex, newPoint); LinearElementEditor.movePoint(element, activePointIndex, newPoint);
if (isBindingElement(element)) { if (isBindingElement(element)) {
@@ -198,7 +198,7 @@ export class LinearElementEditor {
element, element,
scenePointer.x, scenePointer.x,
scenePointer.y, scenePointer.y,
appState.gridSize, appState.showGrid,
), ),
], ],
}); });
@@ -282,7 +282,7 @@ export class LinearElementEditor {
scenePointerX: number, scenePointerX: number,
scenePointerY: number, scenePointerY: number,
editingLinearElement: LinearElementEditor, editingLinearElement: LinearElementEditor,
gridSize: number | null, isGridOn: boolean,
): LinearElementEditor { ): LinearElementEditor {
const { elementId, lastUncommittedPoint } = editingLinearElement; const { elementId, lastUncommittedPoint } = editingLinearElement;
const element = LinearElementEditor.getElement(elementId); const element = LinearElementEditor.getElement(elementId);
@@ -304,7 +304,7 @@ export class LinearElementEditor {
element, element,
scenePointerX - editingLinearElement.pointerOffset.x, scenePointerX - editingLinearElement.pointerOffset.x,
scenePointerY - editingLinearElement.pointerOffset.y, scenePointerY - editingLinearElement.pointerOffset.y,
gridSize, isGridOn,
); );
if (lastPoint === lastUncommittedPoint) { if (lastPoint === lastUncommittedPoint) {
@@ -398,9 +398,9 @@ export class LinearElementEditor {
element: NonDeleted<ExcalidrawLinearElement>, element: NonDeleted<ExcalidrawLinearElement>,
scenePointerX: number, scenePointerX: number,
scenePointerY: number, scenePointerY: number,
gridSize: number | null, isGridOn: boolean,
): Point { ): Point {
const pointerOnGrid = getGridPoint(scenePointerX, scenePointerY, gridSize); const pointerOnGrid = getGridPoint(scenePointerX, scenePointerY, isGridOn);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const cx = (x1 + x2) / 2; const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2; const cy = (y1 + y2) / 2;
+3 -7
View File
@@ -1,7 +1,7 @@
import React, { PureComponent } from "react"; import React, { PureComponent } from "react";
import throttle from "lodash.throttle"; import throttle from "lodash.throttle";
import { ENV, EVENT } from "../../constants"; import { APP_NAME, ENV, EVENT } from "../../constants";
import { import {
decryptAESGEM, decryptAESGEM,
@@ -157,11 +157,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
}; };
openPortal = async () => { openPortal = async () => {
window.history.pushState( window.history.pushState({}, APP_NAME, await generateCollaborationLink());
{},
"Excalidraw",
await generateCollaborationLink(),
);
const elements = this.excalidrawRef.current!.getSceneElements(); const elements = this.excalidrawRef.current!.getSceneElements();
// remove deleted elements from elements array & history to ensure we don't // remove deleted elements from elements array & history to ensure we don't
// expose potentially sensitive user data in case user manually deletes // expose potentially sensitive user data in case user manually deletes
@@ -178,7 +174,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
closePortal = () => { closePortal = () => {
this.saveCollabRoomToFirebase(); this.saveCollabRoomToFirebase();
window.history.pushState({}, "Excalidraw", window.location.origin); window.history.pushState({}, APP_NAME, window.location.origin);
this.destroySocketClient(); this.destroySocketClient();
trackEvent(EVENT_SHARE, "session end"); trackEvent(EVENT_SHARE, "session end");
}; };
+60 -1
View File
@@ -3,12 +3,14 @@ import { ExcalidrawElement } from "../../element/types";
import { AppState } from "../../types"; import { AppState } from "../../types";
import { ImportedDataState } from "../../data/types"; import { ImportedDataState } from "../../data/types";
import { restore } from "../../data/restore"; import { restore } from "../../data/restore";
import { EVENT_ACTION, trackEvent } from "../../analytics"; import { EVENT_ACTION, EVENT_IO, trackEvent } from "../../analytics";
import { serializeAsJSON } from "../../data/json";
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2); const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
const BACKEND_GET = process.env.REACT_APP_BACKEND_V1_GET_URL; const BACKEND_GET = process.env.REACT_APP_BACKEND_V1_GET_URL;
const BACKEND_V2_GET = process.env.REACT_APP_BACKEND_V2_GET_URL; const BACKEND_V2_GET = process.env.REACT_APP_BACKEND_V2_GET_URL;
const BACKEND_V2_POST = process.env.REACT_APP_BACKEND_V2_POST_URL;
const generateRandomID = async () => { const generateRandomID = async () => {
const arr = new Uint8Array(10); const arr = new Uint8Array(10);
@@ -228,3 +230,60 @@ export const loadScene = async (
commitToHistory: false, commitToHistory: false,
}; };
}; };
export const exportToBackend = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const json = serializeAsJSON(elements, appState);
const encoded = new TextEncoder().encode(json);
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128,
},
true, // extractable
["encrypt", "decrypt"],
);
// The iv is set to 0. We are never going to reuse the same key so we don't
// need to have an iv. (I hope that's correct...)
const iv = new Uint8Array(12);
// We use symmetric encryption. AES-GCM is the recommended algorithm and
// includes checks that the ciphertext has not been modified by an attacker.
const encrypted = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
key,
encoded,
);
// We use jwk encoding to be able to extract just the base64 encoded key.
// We will hardcode the rest of the attributes when importing back the key.
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
try {
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: encrypted,
});
const json = await response.json();
if (json.id) {
const url = new URL(window.location.href);
// We need to store the key (and less importantly the id) as hash instead
// of queryParam in order to never send it to the server
url.hash = `json=${json.id},${exportedKey.k!}`;
const urlString = url.toString();
window.prompt(`🔒${t("alerts.uploadedSecurly")}`, urlString);
trackEvent(EVENT_IO, "export", "backend");
} else if (json.error_class === "RequestTooLargeError") {
window.alert(t("alerts.couldNotCreateShareableLinkTooBig"));
} else {
window.alert(t("alerts.couldNotCreateShareableLink"));
}
} catch (error) {
console.error(error);
window.alert(t("alerts.couldNotCreateShareableLink"));
}
};
+61 -15
View File
@@ -13,16 +13,22 @@ import { ImportedDataState } from "../data/types";
import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper"; import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper";
import { TopErrorBoundary } from "../components/TopErrorBoundary"; import { TopErrorBoundary } from "../components/TopErrorBoundary";
import { t } from "../i18n"; import { t } from "../i18n";
import { loadScene } from "./data"; import { exportToBackend, loadScene } from "./data";
import { getCollaborationLinkData } from "./data"; import { getCollaborationLinkData } from "./data";
import { EVENT } from "../constants"; import { EVENT } from "../constants";
import { loadFromFirebase } from "./data/firebase"; import { loadFromFirebase } from "./data/firebase";
import { ExcalidrawImperativeAPI } from "../components/App"; import { ExcalidrawImperativeAPI } from "../components/App";
import { debounce, ResolvablePromise, resolvablePromise } from "../utils"; import { debounce, ResolvablePromise, resolvablePromise } from "../utils";
import { AppState, ExcalidrawAPIRefValue } from "../types"; import { AppState, ExcalidrawAPIRefValue } from "../types";
import { ExcalidrawElement } from "../element/types"; import {
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "../element/types";
import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./app_constants"; import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./app_constants";
import { EVENT_LOAD, EVENT_SHARE, trackEvent } from "../analytics"; import { EVENT_LOAD, EVENT_SHARE, trackEvent } from "../analytics";
import { ErrorDialog } from "../components/ErrorDialog";
import { getDefaultAppState } from "../appState";
import { APP_NAME, TITLE_TIMEOUT } from "../constants";
const excalidrawRef: React.MutableRefObject< const excalidrawRef: React.MutableRefObject<
MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise"> MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise">
@@ -113,7 +119,7 @@ const initializeScene = async (opts: {
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData); scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
} }
if (!isCollabScene) { if (!isCollabScene) {
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
} }
} else { } else {
// https://github.com/excalidraw/excalidraw/issues/1919 // https://github.com/excalidraw/excalidraw/issues/1919
@@ -132,7 +138,7 @@ const initializeScene = async (opts: {
} }
isCollabScene = false; isCollabScene = false;
window.history.replaceState({}, "Excalidraw", window.location.origin); window.history.replaceState({}, APP_NAME, window.location.origin);
} }
} }
if (isCollabScene) { if (isCollabScene) {
@@ -178,6 +184,7 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight, height: window.innerHeight,
}); });
const [errorMessage, setErrorMessage] = useState("");
useLayoutEffect(() => { useLayoutEffect(() => {
const onResize = () => { const onResize = () => {
@@ -240,6 +247,10 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
} }
}; };
const titleTimeout = setTimeout(
() => (document.title = APP_NAME),
TITLE_TIMEOUT,
);
window.addEventListener(EVENT.HASHCHANGE, onHashChange, false); window.addEventListener(EVENT.HASHCHANGE, onHashChange, false);
window.addEventListener(EVENT.UNLOAD, onBlur, false); window.addEventListener(EVENT.UNLOAD, onBlur, false);
window.addEventListener(EVENT.BLUR, onBlur, false); window.addEventListener(EVENT.BLUR, onBlur, false);
@@ -247,6 +258,7 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
window.removeEventListener(EVENT.HASHCHANGE, onHashChange, false); window.removeEventListener(EVENT.HASHCHANGE, onHashChange, false);
window.removeEventListener(EVENT.UNLOAD, onBlur, false); window.removeEventListener(EVENT.UNLOAD, onBlur, false);
window.removeEventListener(EVENT.BLUR, onBlur, false); window.removeEventListener(EVENT.BLUR, onBlur, false);
clearTimeout(titleTimeout);
}; };
}, [collab.initializeSocketClient]); }, [collab.initializeSocketClient]);
@@ -260,18 +272,52 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
} }
}; };
const onExportToBackend = async (
exportedElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
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,
});
} catch (error) {
if (error.name !== "AbortError") {
const { width, height } = canvas;
console.error(error, { width, height });
setErrorMessage(error.message);
}
}
}
};
return ( return (
<Excalidraw <>
ref={excalidrawRef} <Excalidraw
onChange={onChange} ref={excalidrawRef}
width={dimensions.width} onChange={onChange}
height={dimensions.height} width={dimensions.width}
initialData={initialStatePromiseRef.current.promise} height={dimensions.height}
user={{ name: collab.username }} initialData={initialStatePromiseRef.current.promise}
onCollabButtonClick={collab.onCollabButtonClick} user={{ name: collab.username }}
isCollaborating={collab.isCollaborating} onCollabButtonClick={collab.onCollabButtonClick}
onPointerUpdate={collab.onPointerUpdate} isCollaborating={collab.isCollaborating}
/> onPointerUpdate={collab.onPointerUpdate}
onExportToBackend={onExportToBackend}
/>
{errorMessage && (
<ErrorDialog
message={errorMessage}
onClose={() => setErrorMessage("")}
/>
)}
</>
); );
} }
+1
View File
@@ -9,6 +9,7 @@ export const CODES = {
BRACKET_RIGHT: "BracketRight", BRACKET_RIGHT: "BracketRight",
BRACKET_LEFT: "BracketLeft", BRACKET_LEFT: "BracketLeft",
ONE: "Digit1", ONE: "Digit1",
TWO: "Digit2",
NINE: "Digit9", NINE: "Digit9",
QUOTE: "Quote", QUOTE: "Quote",
ZERO: "Digit0", ZERO: "Digit0",
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "تحديد الكل", "selectAll": "تحديد الكل",
"multiSelect": "إضافة عنصر للتحديد", "multiSelect": "إضافة عنصر للتحديد",
"moveCanvas": "نقل لوح رسم", "moveCanvas": "نقل لوح رسم",
"cut": "",
"copy": "نسخ", "copy": "نسخ",
"copyAsPng": "نسخ إلى الحافظة بصيغة PNG", "copyAsPng": "نسخ إلى الحافظة بصيغة PNG",
"copyAsSvg": "نسخ بصيغة SVG", "copyAsSvg": "نسخ بصيغة SVG",
@@ -28,6 +29,11 @@
"edges": "الحواف", "edges": "الحواف",
"sharp": "حادة", "sharp": "حادة",
"round": "دائرية", "round": "دائرية",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "حجم الخط", "fontSize": "حجم الخط",
"fontFamily": "نوع الخط", "fontFamily": "نوع الخط",
"onlySelected": "المحدد فقط", "onlySelected": "المحدد فقط",
@@ -71,9 +77,11 @@
"ungroup": "إلغاء تحديد مجموعة", "ungroup": "إلغاء تحديد مجموعة",
"collaborators": "المتعاونون", "collaborators": "المتعاونون",
"toggleGridMode": "التبديل إلى وضع الشبكة", "toggleGridMode": "التبديل إلى وضع الشبكة",
"toggleStats": "",
"addToLibrary": "أضف إلى المكتبة", "addToLibrary": "أضف إلى المكتبة",
"removeFromLibrary": "حذف من المكتبة", "removeFromLibrary": "حذف من المكتبة",
"libraryLoadingMessage": "جارٍ تحميل المكتبة...", "libraryLoadingMessage": "جارٍ تحميل المكتبة...",
"libraries": "",
"loadingScene": "جاري تحميل المشهد...", "loadingScene": "جاري تحميل المشهد...",
"align": "محاذاة", "align": "محاذاة",
"alignTop": "محاذاة إلى اﻷعلى", "alignTop": "محاذاة إلى اﻷعلى",
@@ -205,13 +213,22 @@
"textNewLine": "إضافة سطر جديد (نص)", "textNewLine": "إضافة سطر جديد (نص)",
"textFinish": "الانتهاء من تحرير (النص)", "textFinish": "الانتهاء من تحرير (النص)",
"zoomToFit": "تكبير لتلائم جميع العناصر", "zoomToFit": "تكبير لتلائم جميع العناصر",
"zoomToSelection": "",
"preventBinding": "منع ربط السهم" "preventBinding": "منع ربط السهم"
}, },
"encrypted": { "encrypted": {
"tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا." "tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا."
}, },
"charts": { "stats": {
"noNumericColumn": "قمت بلصق جدول بيانات دون عمود رقمي.", "angle": "",
"tooManyColumns": "قمت بلصق جدول بيانات دون عمود رقمي." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Маркирай всичко", "selectAll": "Маркирай всичко",
"multiSelect": "", "multiSelect": "",
"moveCanvas": "", "moveCanvas": "",
"cut": "",
"copy": "Копирай", "copy": "Копирай",
"copyAsPng": "Копиране в клипборда", "copyAsPng": "Копиране в клипборда",
"copyAsSvg": "Копиране в клипборда", "copyAsSvg": "Копиране в клипборда",
@@ -28,6 +29,11 @@
"edges": "", "edges": "",
"sharp": "", "sharp": "",
"round": "", "round": "",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Размер на шрифта", "fontSize": "Размер на шрифта",
"fontFamily": "Семейство шрифтове", "fontFamily": "Семейство шрифтове",
"onlySelected": "Само избраното", "onlySelected": "Само избраното",
@@ -71,9 +77,11 @@
"ungroup": "", "ungroup": "",
"collaborators": "", "collaborators": "",
"toggleGridMode": "", "toggleGridMode": "",
"toggleStats": "",
"addToLibrary": "", "addToLibrary": "",
"removeFromLibrary": "", "removeFromLibrary": "",
"libraryLoadingMessage": "", "libraryLoadingMessage": "",
"libraries": "",
"loadingScene": "", "loadingScene": "",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Добавяне на нов ред (текст)", "textNewLine": "Добавяне на нов ред (текст)",
"textFinish": "Завършете редактиране (текст)", "textFinish": "Завършете редактиране (текст)",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleccionar tot", "selectAll": "Seleccionar tot",
"multiSelect": "Afegir element a la selecció", "multiSelect": "Afegir element a la selecció",
"moveCanvas": "Moure el llenç", "moveCanvas": "Moure el llenç",
"cut": "",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar al porta-retalls com a PNG", "copyAsPng": "Copiar al porta-retalls com a PNG",
"copyAsSvg": "Copiar al porta-retalls com a SVG", "copyAsSvg": "Copiar al porta-retalls com a SVG",
@@ -28,6 +29,11 @@
"edges": "Vores", "edges": "Vores",
"sharp": "Agut", "sharp": "Agut",
"round": "Arrodonit", "round": "Arrodonit",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Mida de lletra", "fontSize": "Mida de lletra",
"fontFamily": "Tipus de lletra", "fontFamily": "Tipus de lletra",
"onlySelected": "Només seleccionats", "onlySelected": "Només seleccionats",
@@ -71,9 +77,11 @@
"ungroup": "Desagrupar la selecció", "ungroup": "Desagrupar la selecció",
"collaborators": "Col·laboradors", "collaborators": "Col·laboradors",
"toggleGridMode": "Commutar línies de graella", "toggleGridMode": "Commutar línies de graella",
"toggleStats": "",
"addToLibrary": "Afegir a la biblioteca", "addToLibrary": "Afegir a la biblioteca",
"removeFromLibrary": "Eliminar de la biblioteca", "removeFromLibrary": "Eliminar de la biblioteca",
"libraryLoadingMessage": "Carregant la biblioteca...", "libraryLoadingMessage": "Carregant la biblioteca...",
"libraries": "",
"loadingScene": "Carregant escena ...", "loadingScene": "Carregant escena ...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Afegir línea nova (text)", "textNewLine": "Afegir línea nova (text)",
"textFinish": "Acabar d'editar (text)", "textFinish": "Acabar d'editar (text)",
"zoomToFit": "Zoom per veure tots els elements", "zoomToFit": "Zoom per veure tots els elements",
"zoomToSelection": "",
"preventBinding": "Prevenir vinculació de la fletxa" "preventBinding": "Prevenir vinculació de la fletxa"
}, },
"encrypted": { "encrypted": {
"tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors dExcalidraw no els veuran mai." "tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors dExcalidraw no els veuran mai."
}, },
"charts": { "stats": {
"noNumericColumn": "Has enganxat un full de càlcul sense columna numèrica.", "angle": "",
"tooManyColumns": "Has enganxat un full de càlcul amb més de dues columnes." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Alle auswählen", "selectAll": "Alle auswählen",
"multiSelect": "Element zur Auswahl hinzufügen", "multiSelect": "Element zur Auswahl hinzufügen",
"moveCanvas": "Leinwand verschieben", "moveCanvas": "Leinwand verschieben",
"cut": "Ausschneiden",
"copy": "Kopieren", "copy": "Kopieren",
"copyAsPng": "In Zwischenablage kopieren (PNG)", "copyAsPng": "In Zwischenablage kopieren (PNG)",
"copyAsSvg": "In Zwischenablage kopieren (SVG)", "copyAsSvg": "In Zwischenablage kopieren (SVG)",
@@ -28,6 +29,11 @@
"edges": "Kanten", "edges": "Kanten",
"sharp": "Eckig", "sharp": "Eckig",
"round": "Rund", "round": "Rund",
"arrowheads": "Pfeilspitzen",
"arrowhead_none": "Keine",
"arrowhead_arrow": "Pfeil",
"arrowhead_bar": "Balken",
"arrowhead_dot": "Punkt",
"fontSize": "Schriftgröße", "fontSize": "Schriftgröße",
"fontFamily": "Schriftfamilie", "fontFamily": "Schriftfamilie",
"onlySelected": "Nur ausgewählte", "onlySelected": "Nur ausgewählte",
@@ -71,9 +77,11 @@
"ungroup": "Gruppierung aufheben", "ungroup": "Gruppierung aufheben",
"collaborators": "Mitarbeitende", "collaborators": "Mitarbeitende",
"toggleGridMode": "Gitterlinien ein-/ausschalten", "toggleGridMode": "Gitterlinien ein-/ausschalten",
"toggleStats": "Statistiken für Nerds ein-/ausschalten",
"addToLibrary": "Zur Bibliothek hinzufügen", "addToLibrary": "Zur Bibliothek hinzufügen",
"removeFromLibrary": "Aus Bibliothek entfernen", "removeFromLibrary": "Aus Bibliothek entfernen",
"libraryLoadingMessage": "Lade Bibliothek...", "libraryLoadingMessage": "Lade Bibliothek...",
"libraries": "Bibliotheken durchsuchen",
"loadingScene": "Lade Zeichnung...", "loadingScene": "Lade Zeichnung...",
"align": "Ausrichten", "align": "Ausrichten",
"alignTop": "Obere Kanten", "alignTop": "Obere Kanten",
@@ -205,13 +213,22 @@
"textNewLine": "Neue Zeile hinzufügen (Text)", "textNewLine": "Neue Zeile hinzufügen (Text)",
"textFinish": "Bearbeiten beenden (Text)", "textFinish": "Bearbeiten beenden (Text)",
"zoomToFit": "Zoomen um alle Elemente einzupassen", "zoomToFit": "Zoomen um alle Elemente einzupassen",
"zoomToSelection": "",
"preventBinding": "Pfeil-Bindung verhindern" "preventBinding": "Pfeil-Bindung verhindern"
}, },
"encrypted": { "encrypted": {
"tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals." "tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals."
}, },
"charts": { "stats": {
"noNumericColumn": "Du hast eine Tabelle ohne numerische Spalte eingefügt.", "angle": "Winkel",
"tooManyColumns": "Du hast eine Tabelle mit mehr als zwei Spalten eingefügt." "element": "Element",
"elements": "Elemente",
"height": "Höhe",
"scene": "Zeichnung",
"selected": "Ausgewählt",
"storage": "Speicher",
"title": "Statistiken für Nerds",
"total": "Gesamt",
"width": "Breite"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Επιλογή όλων", "selectAll": "Επιλογή όλων",
"multiSelect": "Προσθέστε το στοιχείο στην επιλογή", "multiSelect": "Προσθέστε το στοιχείο στην επιλογή",
"moveCanvas": "Μετακίνηση καμβά", "moveCanvas": "Μετακίνηση καμβά",
"cut": "Αποκοπή",
"copy": "Αντιγραφή", "copy": "Αντιγραφή",
"copyAsPng": "Αντιγραφή στο πρόχειρο ως PNG", "copyAsPng": "Αντιγραφή στο πρόχειρο ως PNG",
"copyAsSvg": "Αντιγραφή στο πρόχειρο ως SVG", "copyAsSvg": "Αντιγραφή στο πρόχειρο ως SVG",
@@ -28,6 +29,11 @@
"edges": "Άκρες", "edges": "Άκρες",
"sharp": "Οξύ", "sharp": "Οξύ",
"round": "Στρογγυλό", "round": "Στρογγυλό",
"arrowheads": "Σύμβολα βελών",
"arrowhead_none": "Κανένα",
"arrowhead_arrow": "Βέλος",
"arrowhead_bar": "Μπάρα",
"arrowhead_dot": "Τελεία",
"fontSize": "Μέγεθος γραμματοσειράς", "fontSize": "Μέγεθος γραμματοσειράς",
"fontFamily": "Γραμματοσειρά", "fontFamily": "Γραμματοσειρά",
"onlySelected": "Μόνο τα Επιλεγμένα", "onlySelected": "Μόνο τα Επιλεγμένα",
@@ -71,9 +77,11 @@
"ungroup": "Κατάργηση ομάδας από επιλογή", "ungroup": "Κατάργηση ομάδας από επιλογή",
"collaborators": "Συνεργάτες", "collaborators": "Συνεργάτες",
"toggleGridMode": "Εναλλαγή λειτουργίας πλέγματος", "toggleGridMode": "Εναλλαγή λειτουργίας πλέγματος",
"toggleStats": "",
"addToLibrary": "Προσθήκη στη βιβλιοθήκη", "addToLibrary": "Προσθήκη στη βιβλιοθήκη",
"removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη", "removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη",
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...", "libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...",
"libraries": "Άλλες βιβλιοθήκες",
"loadingScene": "Φόρτωση σκηνής...", "loadingScene": "Φόρτωση σκηνής...",
"align": "Στοίχιση", "align": "Στοίχιση",
"alignTop": "Στοίχιση πάνω", "alignTop": "Στοίχιση πάνω",
@@ -205,13 +213,22 @@
"textNewLine": "Προσθήκη νέας γραμμής (κείμενο)", "textNewLine": "Προσθήκη νέας γραμμής (κείμενο)",
"textFinish": "Ολοκλήρωση επεξεργασίας (κείμενο)", "textFinish": "Ολοκλήρωση επεξεργασίας (κείμενο)",
"zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία", "zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία",
"zoomToSelection": "",
"preventBinding": "Αποτροπή δέσμευσης βέλων" "preventBinding": "Αποτροπή δέσμευσης βέλων"
}, },
"encrypted": { "encrypted": {
"tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα έιναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw." "tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα έιναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw."
}, },
"charts": { "stats": {
"noNumericColumn": "Επικόλλησες ένα υπολογιστικό φύλλο χωρίς αριθμητική στήλη.", "angle": "Γωνία",
"tooManyColumns": "Επικόλλησες ένα υπολογιστικό φύλλο με περισσότερες από δύο στήλες." "element": "Στοιχείο",
"elements": "Στοιχεία",
"height": "Ύψος",
"scene": "",
"selected": "Επιλεγμένα",
"storage": "Χώρος",
"title": "",
"total": "Σύνολο ",
"width": "Πλάτος"
} }
} }
+9 -7
View File
@@ -37,7 +37,7 @@
"fontSize": "Font size", "fontSize": "Font size",
"fontFamily": "Font family", "fontFamily": "Font family",
"onlySelected": "Only selected", "onlySelected": "Only selected",
"withBackground": "With Background", "withBackground": "With background",
"exportEmbedScene": "Embed scene into exported file", "exportEmbedScene": "Embed scene into exported file",
"exportEmbedScene_details": "Scene data will be saved into the exported PNG/SVG file so that the scene can be restored from it.\nWill increase exported file size.", "exportEmbedScene_details": "Scene data will be saved into the exported PNG/SVG file so that the scene can be restored from it.\nWill increase exported file size.",
"addWatermark": "Add \"Made with Excalidraw\"", "addWatermark": "Add \"Made with Excalidraw\"",
@@ -76,8 +76,7 @@
"group": "Group selection", "group": "Group selection",
"ungroup": "Ungroup selection", "ungroup": "Ungroup selection",
"collaborators": "Collaborators", "collaborators": "Collaborators",
"toggleGridMode": "Toggle grid mode", "gridMode": "Grid mode",
"toggleStats": "Toggle stats for nerds",
"addToLibrary": "Add to library", "addToLibrary": "Add to library",
"removeFromLibrary": "Remove from library", "removeFromLibrary": "Remove from library",
"libraryLoadingMessage": "Loading library...", "libraryLoadingMessage": "Loading library...",
@@ -118,9 +117,10 @@
"redo": "Redo", "redo": "Redo",
"roomDialog": "Start live collaboration", "roomDialog": "Start live collaboration",
"createNewRoom": "Create new room", "createNewRoom": "Create new room",
"toggleFullScreen": "Toggle full screen", "fullScreen": "Full screen",
"toggleDarkMode": "Toggle dark mode", "darkMode": "Dark mode",
"toggleZenMode": "Toggle zen mode", "lightMode": "Light mode",
"zenMode": "Zen mode",
"exitZenMode": "Exit zen mode" "exitZenMode": "Exit zen mode"
}, },
"alerts": { "alerts": {
@@ -136,7 +136,7 @@
"loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?", "loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?",
"errorLoadingLibrary": "There was an error loading the third party library.", "errorLoadingLibrary": "There was an error loading the third party library.",
"confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?", "confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
"imageDoesNotContainScene": "Image file doesn't contain scene data. Have you enabled this during export?", "imageDoesNotContainScene": "Importing images isn't supported at the moment.\n\nDid you want to import a scene? This image does not seem to contain any scene data. Have you enabled this during export?",
"cannotRestoreFromImage": "Scene couldn't be restored from this image file" "cannotRestoreFromImage": "Scene couldn't be restored from this image file"
}, },
"toolBar": { "toolBar": {
@@ -161,6 +161,7 @@
"freeDraw": "Click and drag, release when you're finished", "freeDraw": "Click and drag, release when you're finished",
"text": "Tip: you can also add text by double-clicking anywhere with the selection tool", "text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
"linearElementMulti": "Click on last point or press Escape or Enter to finish", "linearElementMulti": "Click on last point or press Escape or Enter to finish",
"lockAngle": "You can constrain angle by holding SHIFT",
"resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center", "resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
"rotate": "You can constrain angles by holding SHIFT while rotating", "rotate": "You can constrain angles by holding SHIFT while rotating",
"lineEditor_info": "Double-click or press Enter to edit points", "lineEditor_info": "Double-click or press Enter to edit points",
@@ -213,6 +214,7 @@
"textNewLine": "Add new line (text)", "textNewLine": "Add new line (text)",
"textFinish": "Finish editing (text)", "textFinish": "Finish editing (text)",
"zoomToFit": "Zoom to fit all elements", "zoomToFit": "Zoom to fit all elements",
"zoomToSelection": "Zoom to selection",
"preventBinding": "Prevent arrow binding" "preventBinding": "Prevent arrow binding"
}, },
"encrypted": { "encrypted": {
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleccionar todo", "selectAll": "Seleccionar todo",
"multiSelect": "Añadir elemento a la selección", "multiSelect": "Añadir elemento a la selección",
"moveCanvas": "Mover el lienzo", "moveCanvas": "Mover el lienzo",
"cut": "",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar al portapapeles como PNG", "copyAsPng": "Copiar al portapapeles como PNG",
"copyAsSvg": "Copiar al portapapeles como SVG", "copyAsSvg": "Copiar al portapapeles como SVG",
@@ -28,6 +29,11 @@
"edges": "Bordes", "edges": "Bordes",
"sharp": "Afilado", "sharp": "Afilado",
"round": "Redondo", "round": "Redondo",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Tamaño de la fuente", "fontSize": "Tamaño de la fuente",
"fontFamily": "Tipo de fuente", "fontFamily": "Tipo de fuente",
"onlySelected": "Sólo seleccionados", "onlySelected": "Sólo seleccionados",
@@ -71,9 +77,11 @@
"ungroup": "Desagrupar", "ungroup": "Desagrupar",
"collaborators": "Colaboradores", "collaborators": "Colaboradores",
"toggleGridMode": "Alternar modo cuadrícula", "toggleGridMode": "Alternar modo cuadrícula",
"toggleStats": "",
"addToLibrary": "Añadir a la biblioteca", "addToLibrary": "Añadir a la biblioteca",
"removeFromLibrary": "Eliminar de la biblioteca", "removeFromLibrary": "Eliminar de la biblioteca",
"libraryLoadingMessage": "Cargando biblioteca...", "libraryLoadingMessage": "Cargando biblioteca...",
"libraries": "",
"loadingScene": "Cargando escena...", "loadingScene": "Cargando escena...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Añadir nueva línea (texto)", "textNewLine": "Añadir nueva línea (texto)",
"textFinish": "Finalizar edición (texto)", "textFinish": "Finalizar edición (texto)",
"zoomToFit": "Ajustar para mostrar todos los elementos", "zoomToFit": "Ajustar para mostrar todos los elementos",
"zoomToSelection": "",
"preventBinding": "Evitar enlace de flecha" "preventBinding": "Evitar enlace de flecha"
}, },
"encrypted": { "encrypted": {
"tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán." "tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán."
}, },
"charts": { "stats": {
"noNumericColumn": "Pegaste una hoja de cálculo sin una columna numérica.", "angle": "",
"tooManyColumns": "Pegaste una hoja de cálculo con más de dos columnas." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "انتخاب همه", "selectAll": "انتخاب همه",
"multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید.", "multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید.",
"moveCanvas": "بوم را حرکت بدهید", "moveCanvas": "بوم را حرکت بدهید",
"cut": "",
"copy": "کپی", "copy": "کپی",
"copyAsPng": "کپی در حافطه موقت به صورت PNG", "copyAsPng": "کپی در حافطه موقت به صورت PNG",
"copyAsSvg": "کپی در حافطه موقت به صورت SVG", "copyAsSvg": "کپی در حافطه موقت به صورت SVG",
@@ -28,6 +29,11 @@
"edges": "لبه ها", "edges": "لبه ها",
"sharp": "تیز", "sharp": "تیز",
"round": "دور", "round": "دور",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "اندازه قلم", "fontSize": "اندازه قلم",
"fontFamily": "نوع قلم", "fontFamily": "نوع قلم",
"onlySelected": "فقط انتخاب شده ها", "onlySelected": "فقط انتخاب شده ها",
@@ -71,9 +77,11 @@
"ungroup": "حذف گروهبندی انتخابها", "ungroup": "حذف گروهبندی انتخابها",
"collaborators": "همکاران", "collaborators": "همکاران",
"toggleGridMode": "سويچ خطوط راهنما", "toggleGridMode": "سويچ خطوط راهنما",
"toggleStats": "",
"addToLibrary": "افزودن به کتابخانه", "addToLibrary": "افزودن به کتابخانه",
"removeFromLibrary": "حذف از کتابخانه", "removeFromLibrary": "حذف از کتابخانه",
"libraryLoadingMessage": "بارگذاری کتابخانه...", "libraryLoadingMessage": "بارگذاری کتابخانه...",
"libraries": "",
"loadingScene": "باگذاری صحنه...", "loadingScene": "باگذاری صحنه...",
"align": "تراز", "align": "تراز",
"alignTop": "تراز به بالا", "alignTop": "تراز به بالا",
@@ -205,13 +213,22 @@
"textNewLine": "یک خط جدید اضافه کنید (متن)", "textNewLine": "یک خط جدید اضافه کنید (متن)",
"textFinish": "پایان ویرایش (متن)", "textFinish": "پایان ویرایش (متن)",
"zoomToFit": "بزرگنمایی برای دیدن تمام آیتم ها", "zoomToFit": "بزرگنمایی برای دیدن تمام آیتم ها",
"zoomToSelection": "",
"preventBinding": "مانع شدن از چسبیدن فلش ها" "preventBinding": "مانع شدن از چسبیدن فلش ها"
}, },
"encrypted": { "encrypted": {
"tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند." "tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند."
}, },
"charts": { "stats": {
"noNumericColumn": "شما یک صفحه گسترده را بدون ستون عددی کپی کرده اید.", "angle": "",
"tooManyColumns": "شما یک صفحه گسترده را با بیش از دو ستون کپی کرده اید." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Valitse kaikki", "selectAll": "Valitse kaikki",
"multiSelect": "Lisää kohde valintaan", "multiSelect": "Lisää kohde valintaan",
"moveCanvas": "Siirrä piirtoaluetta", "moveCanvas": "Siirrä piirtoaluetta",
"cut": "Leikkaa",
"copy": "Kopioi", "copy": "Kopioi",
"copyAsPng": "Kopioi leikepöydälle PNG-tiedostona", "copyAsPng": "Kopioi leikepöydälle PNG-tiedostona",
"copyAsSvg": "Kopioi leikepöydälle SVG-tiedostona", "copyAsSvg": "Kopioi leikepöydälle SVG-tiedostona",
@@ -28,6 +29,11 @@
"edges": "Reunat", "edges": "Reunat",
"sharp": "Terävä", "sharp": "Terävä",
"round": "Pyöreä", "round": "Pyöreä",
"arrowheads": "Nuolenkärjet",
"arrowhead_none": "Ei mitään",
"arrowhead_arrow": "Nuoli",
"arrowhead_bar": "Tasapää",
"arrowhead_dot": "Piste",
"fontSize": "Kirjasinkoko", "fontSize": "Kirjasinkoko",
"fontFamily": "Kirjasintyyppi", "fontFamily": "Kirjasintyyppi",
"onlySelected": "Vain valitut", "onlySelected": "Vain valitut",
@@ -71,9 +77,11 @@
"ungroup": "Pura valittu ryhmä", "ungroup": "Pura valittu ryhmä",
"collaborators": "Yhteistyökumppanit", "collaborators": "Yhteistyökumppanit",
"toggleGridMode": "Ruudukko päälle/pois", "toggleGridMode": "Ruudukko päälle/pois",
"toggleStats": "Nörttien tilastot päälle/pois",
"addToLibrary": "Lisää kirjastoon", "addToLibrary": "Lisää kirjastoon",
"removeFromLibrary": "Poista kirjastosta", "removeFromLibrary": "Poista kirjastosta",
"libraryLoadingMessage": "Ladataan kirjastoa...", "libraryLoadingMessage": "Ladataan kirjastoa...",
"libraries": "Selaa kirjastoja",
"loadingScene": "Ladataan työtä...", "loadingScene": "Ladataan työtä...",
"align": "Tasaa", "align": "Tasaa",
"alignTop": "Tasaa ylös", "alignTop": "Tasaa ylös",
@@ -205,13 +213,22 @@
"textNewLine": "Lisää uusi rivi (teksti)", "textNewLine": "Lisää uusi rivi (teksti)",
"textFinish": "Lopeta muokkaus (teksti)", "textFinish": "Lopeta muokkaus (teksti)",
"zoomToFit": "Zoomaa kaikki elementit näkyviin", "zoomToFit": "Zoomaa kaikki elementit näkyviin",
"zoomToSelection": "Zoomaa valintaan",
"preventBinding": "Estä nuolten sitominen" "preventBinding": "Estä nuolten sitominen"
}, },
"encrypted": { "encrypted": {
"tooltip": "Piirroksesi ovat päästä päähän salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä." "tooltip": "Piirroksesi ovat päästä päähän salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä."
}, },
"charts": { "stats": {
"noNumericColumn": "Liitit taulukon ilman lukuja sisältävää saraketta.", "angle": "Kulma",
"tooManyColumns": "Liitit taulukon, jossa on enemmän kuin kaksi saraketta." "element": "Elementti",
"elements": "Elementit",
"height": "Korkeus",
"scene": "Teos",
"selected": "Valitut",
"storage": "Tallennustila",
"title": "Nörttien tilastot",
"total": "Yhteensä",
"width": "Leveys"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Tout sélectionner", "selectAll": "Tout sélectionner",
"multiSelect": "Ajouter l'élément à la sélection", "multiSelect": "Ajouter l'élément à la sélection",
"moveCanvas": "Déplacer le canvas", "moveCanvas": "Déplacer le canvas",
"cut": "Couper",
"copy": "Copier", "copy": "Copier",
"copyAsPng": "Copier dans le presse-papier en PNG", "copyAsPng": "Copier dans le presse-papier en PNG",
"copyAsSvg": "Copier dans le presse-papier en SVG", "copyAsSvg": "Copier dans le presse-papier en SVG",
@@ -28,6 +29,11 @@
"edges": "Angles", "edges": "Angles",
"sharp": "Aigu", "sharp": "Aigu",
"round": "Rond", "round": "Rond",
"arrowheads": "Extrémités de ligne",
"arrowhead_none": "Aucun",
"arrowhead_arrow": "Flèche",
"arrowhead_bar": "Barre",
"arrowhead_dot": "Point",
"fontSize": "Taille de la police", "fontSize": "Taille de la police",
"fontFamily": "Police", "fontFamily": "Police",
"onlySelected": "Uniquement la sélection", "onlySelected": "Uniquement la sélection",
@@ -71,9 +77,11 @@
"ungroup": "Dégrouper la sélection", "ungroup": "Dégrouper la sélection",
"collaborators": "Collaborateurs", "collaborators": "Collaborateurs",
"toggleGridMode": "Basculer le mode grille", "toggleGridMode": "Basculer le mode grille",
"toggleStats": "Activer/désactiver les stats pour les nerds",
"addToLibrary": "Ajouter à la bibliothèque", "addToLibrary": "Ajouter à la bibliothèque",
"removeFromLibrary": "Supprimer de la bibliothèque", "removeFromLibrary": "Supprimer de la bibliothèque",
"libraryLoadingMessage": "Chargement de la bibliothèque...", "libraryLoadingMessage": "Chargement de la bibliothèque...",
"libraries": "Explorer les bibliothèques",
"loadingScene": "Chargement de la scène...", "loadingScene": "Chargement de la scène...",
"align": "Aligner", "align": "Aligner",
"alignTop": "Aligner en haut", "alignTop": "Aligner en haut",
@@ -205,13 +213,22 @@
"textNewLine": "Ajouter une nouvelle ligne (texte)", "textNewLine": "Ajouter une nouvelle ligne (texte)",
"textFinish": "Terminer l'édition (texte)", "textFinish": "Terminer l'édition (texte)",
"zoomToFit": "Zoomer pour visualiser tous les éléments", "zoomToFit": "Zoomer pour visualiser tous les éléments",
"zoomToSelection": "Zoom sur la sélection",
"preventBinding": "Empêcher la liaison de la flèche" "preventBinding": "Empêcher la liaison de la flèche"
}, },
"encrypted": { "encrypted": {
"tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais." "tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais."
}, },
"charts": { "stats": {
"noNumericColumn": "Vous avez collé une feuille de calcul sans données numérique.", "angle": "Angle",
"tooManyColumns": "Vous avez collé une feuille de calcul avec plus de deux colonnes." "element": "Élément",
"elements": "Éléments",
"height": "Hauteur",
"scene": "Scène",
"selected": "Sélectionné",
"storage": "Stockage",
"title": "Stats pour nerds",
"total": "Total",
"width": "Largeur"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "בחר הכל", "selectAll": "בחר הכל",
"multiSelect": "", "multiSelect": "",
"moveCanvas": "", "moveCanvas": "",
"cut": "",
"copy": "העתק", "copy": "העתק",
"copyAsPng": "העתק ללוח כ PNG", "copyAsPng": "העתק ללוח כ PNG",
"copyAsSvg": "העתק ללוח כ SVG", "copyAsSvg": "העתק ללוח כ SVG",
@@ -28,6 +29,11 @@
"edges": "", "edges": "",
"sharp": "", "sharp": "",
"round": "", "round": "",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "גודל גופן", "fontSize": "גודל גופן",
"fontFamily": "סוג הגופן", "fontFamily": "סוג הגופן",
"onlySelected": "רק מה שנבחר", "onlySelected": "רק מה שנבחר",
@@ -71,9 +77,11 @@
"ungroup": "פרק קבוצה", "ungroup": "פרק קבוצה",
"collaborators": "", "collaborators": "",
"toggleGridMode": "", "toggleGridMode": "",
"toggleStats": "",
"addToLibrary": "", "addToLibrary": "",
"removeFromLibrary": "", "removeFromLibrary": "",
"libraryLoadingMessage": "", "libraryLoadingMessage": "",
"libraries": "",
"loadingScene": "", "loadingScene": "",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "הוסף שורה חדשה (טקסט)", "textNewLine": "הוסף שורה חדשה (טקסט)",
"textFinish": "סיים עריכה (טקסט)", "textFinish": "סיים עריכה (טקסט)",
"zoomToFit": "זום להתאמת כל האלמנטים למסך", "zoomToFit": "זום להתאמת כל האלמנטים למסך",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם." "tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם."
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+21 -4
View File
@@ -4,6 +4,7 @@
"selectAll": "सभी चुनें", "selectAll": "सभी चुनें",
"multiSelect": "आकार को चयन में जोड़ें", "multiSelect": "आकार को चयन में जोड़ें",
"moveCanvas": "कैनवास को स्थानांतरित करें", "moveCanvas": "कैनवास को स्थानांतरित करें",
"cut": "काटें",
"copy": "प्रतिलिपि", "copy": "प्रतिलिपि",
"copyAsPng": "क्लिपबोर्ड पर कॉपी करें ,पीएनजी के रूप में", "copyAsPng": "क्लिपबोर्ड पर कॉपी करें ,पीएनजी के रूप में",
"copyAsSvg": "क्लिपबोर्ड पर कॉपी करें,एसवीजी के रूप में", "copyAsSvg": "क्लिपबोर्ड पर कॉपी करें,एसवीजी के रूप में",
@@ -28,6 +29,11 @@
"edges": "किनारा", "edges": "किनारा",
"sharp": "नुकीला", "sharp": "नुकीला",
"round": "गोल", "round": "गोल",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "तीर",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "फ़ॉन्ट का आकार", "fontSize": "फ़ॉन्ट का आकार",
"fontFamily": "फ़ॉन्ट का परिवार", "fontFamily": "फ़ॉन्ट का परिवार",
"onlySelected": "केवल चयनित", "onlySelected": "केवल चयनित",
@@ -63,7 +69,7 @@
"language": "भाषा", "language": "भाषा",
"createRoom": "अधिवेशन", "createRoom": "अधिवेशन",
"duplicateSelection": "डुप्लिकेट", "duplicateSelection": "डुप्लिकेट",
"untitled": "", "untitled": "अशीर्षित",
"name": "नाम", "name": "नाम",
"yourName": "आपका नाम", "yourName": "आपका नाम",
"madeWithExcalidraw": "मेड विथ एक्सकैलिडराव", "madeWithExcalidraw": "मेड विथ एक्सकैलिडराव",
@@ -71,9 +77,11 @@
"ungroup": "समूह चयन असमूहीकृत करें", "ungroup": "समूह चयन असमूहीकृत करें",
"collaborators": "सहयोगी", "collaborators": "सहयोगी",
"toggleGridMode": "टॉगल ग्रिड मोड", "toggleGridMode": "टॉगल ग्रिड मोड",
"toggleStats": "",
"addToLibrary": "लाइब्रेरी से जोड़ें", "addToLibrary": "लाइब्रेरी से जोड़ें",
"removeFromLibrary": "लाइब्रेरी से निकालें", "removeFromLibrary": "लाइब्रेरी से निकालें",
"libraryLoadingMessage": "लाइब्रेरी खुल रही है", "libraryLoadingMessage": "लाइब्रेरी खुल रही है",
"libraries": "",
"loadingScene": "दृश्य खुल रहा है", "loadingScene": "दृश्य खुल रहा है",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "नई पंक्ति (पाठ) जोड़ें", "textNewLine": "नई पंक्ति (पाठ) जोड़ें",
"textFinish": "संपादन समाप्त करें (पाठ)", "textFinish": "संपादन समाप्त करें (पाठ)",
"zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें", "zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।" "tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
}, },
"charts": { "stats": {
"noNumericColumn": "आपने एक संख्यात्मक कॉलम के बिना एक स्प्रेडशीट चिपकाई।", "angle": "कोण",
"tooManyColumns": "आपने दो से अधिक कॉलम के साथ एक स्प्रेडशीट चिपकाई।" "element": "",
"elements": "",
"height": "ऊंचाई",
"scene": "दृश्य",
"selected": "चयनित",
"storage": "संग्रह",
"title": "",
"total": "कुल",
"width": "चौड़ाई"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Összes kijelölése", "selectAll": "Összes kijelölése",
"multiSelect": "", "multiSelect": "",
"moveCanvas": "", "moveCanvas": "",
"cut": "",
"copy": "Másolás", "copy": "Másolás",
"copyAsPng": "Vágólapra másolás mint PNG", "copyAsPng": "Vágólapra másolás mint PNG",
"copyAsSvg": "Vágólapra másolás mint SVG", "copyAsSvg": "Vágólapra másolás mint SVG",
@@ -28,6 +29,11 @@
"edges": "", "edges": "",
"sharp": "", "sharp": "",
"round": "", "round": "",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "", "fontSize": "",
"fontFamily": "", "fontFamily": "",
"onlySelected": "Csak a kiválasztott", "onlySelected": "Csak a kiválasztott",
@@ -71,9 +77,11 @@
"ungroup": "", "ungroup": "",
"collaborators": "", "collaborators": "",
"toggleGridMode": "", "toggleGridMode": "",
"toggleStats": "",
"addToLibrary": "", "addToLibrary": "",
"removeFromLibrary": "", "removeFromLibrary": "",
"libraryLoadingMessage": "", "libraryLoadingMessage": "",
"libraries": "",
"loadingScene": "", "loadingScene": "",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "", "textNewLine": "",
"textFinish": "", "textFinish": "",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Pilih semua", "selectAll": "Pilih semua",
"multiSelect": "Tambahkan elemen ke pilihan", "multiSelect": "Tambahkan elemen ke pilihan",
"moveCanvas": "Pindahkan kanvas", "moveCanvas": "Pindahkan kanvas",
"cut": "Potong",
"copy": "Salin", "copy": "Salin",
"copyAsPng": "Salin ke papan klip sebagai PNG", "copyAsPng": "Salin ke papan klip sebagai PNG",
"copyAsSvg": "Salin ke papan klip sebagai SVG", "copyAsSvg": "Salin ke papan klip sebagai SVG",
@@ -28,6 +29,11 @@
"edges": "Tepi", "edges": "Tepi",
"sharp": "Tajam", "sharp": "Tajam",
"round": "Bulat", "round": "Bulat",
"arrowheads": "Mata panah",
"arrowhead_none": "Tidak ada",
"arrowhead_arrow": "Panah",
"arrowhead_bar": "Batang",
"arrowhead_dot": "Titik",
"fontSize": "Ukuran font", "fontSize": "Ukuran font",
"fontFamily": "Jenis font", "fontFamily": "Jenis font",
"onlySelected": "Hanya yang Dipilih", "onlySelected": "Hanya yang Dipilih",
@@ -71,9 +77,11 @@
"ungroup": "Pisahkan pilihan", "ungroup": "Pisahkan pilihan",
"collaborators": "Kolaborator", "collaborators": "Kolaborator",
"toggleGridMode": "Aktifkan/Matikan mode kisi", "toggleGridMode": "Aktifkan/Matikan mode kisi",
"toggleStats": "Aktifkan statistik untuk nerd",
"addToLibrary": "Tambahkan ke pustaka", "addToLibrary": "Tambahkan ke pustaka",
"removeFromLibrary": "Hapus dari pustaka", "removeFromLibrary": "Hapus dari pustaka",
"libraryLoadingMessage": "Memuat pustaka...", "libraryLoadingMessage": "Memuat pustaka...",
"libraries": "Telusur pustaka",
"loadingScene": "Memuat pemandangan...", "loadingScene": "Memuat pemandangan...",
"align": "Perataan", "align": "Perataan",
"alignTop": "Rata atas", "alignTop": "Rata atas",
@@ -205,13 +213,22 @@
"textNewLine": "Tambahkan baris baru (teks)", "textNewLine": "Tambahkan baris baru (teks)",
"textFinish": "Selesai mengedit (teks)", "textFinish": "Selesai mengedit (teks)",
"zoomToFit": "Perbesar agar sesuai dengan semua elemen", "zoomToFit": "Perbesar agar sesuai dengan semua elemen",
"zoomToSelection": "",
"preventBinding": "Cegah pengikatan panah" "preventBinding": "Cegah pengikatan panah"
}, },
"encrypted": { "encrypted": {
"tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya." "tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya."
}, },
"charts": { "stats": {
"noNumericColumn": "Anda menempelkan sebuah lembar bentang tanpa sebuah kolom numerik.", "angle": "Sudut",
"tooManyColumns": "Anda menempelkan sebuah lembar bentang dengan lebih dari dua kolom." "element": "Elemen",
"elements": "Elemen",
"height": "Tinggi",
"scene": "Pemandangan",
"selected": "Terpilih",
"storage": "Penyimpanan",
"title": "Statistik untuk nerd",
"total": "Total",
"width": "Lebar"
} }
} }
+22 -5
View File
@@ -4,6 +4,7 @@
"selectAll": "Seleziona tutto", "selectAll": "Seleziona tutto",
"multiSelect": "Aggiungi elemento alla selezione", "multiSelect": "Aggiungi elemento alla selezione",
"moveCanvas": "Sposta tela", "moveCanvas": "Sposta tela",
"cut": "Taglia",
"copy": "Copia", "copy": "Copia",
"copyAsPng": "Copia negli appunti come PNG", "copyAsPng": "Copia negli appunti come PNG",
"copyAsSvg": "Copia negli appunti come SVG", "copyAsSvg": "Copia negli appunti come SVG",
@@ -28,6 +29,11 @@
"edges": "Bordi", "edges": "Bordi",
"sharp": "Acuto", "sharp": "Acuto",
"round": "Rotondo", "round": "Rotondo",
"arrowheads": "Punta della freccia",
"arrowhead_none": "Nessuno",
"arrowhead_arrow": "Freccia",
"arrowhead_bar": "Barra",
"arrowhead_dot": "Punto",
"fontSize": "Dimensione carattere", "fontSize": "Dimensione carattere",
"fontFamily": "Carattere", "fontFamily": "Carattere",
"onlySelected": "Solo selezionati", "onlySelected": "Solo selezionati",
@@ -70,10 +76,12 @@
"group": "Crea gruppo da selezione", "group": "Crea gruppo da selezione",
"ungroup": "Dividi gruppo da selezione", "ungroup": "Dividi gruppo da selezione",
"collaborators": "Collaboratori", "collaborators": "Collaboratori",
"toggleGridMode": "Attiva/disattiva modalità quadrícula", "toggleGridMode": "Attiva/disattiva modalità griglia",
"toggleStats": "Attiva/disattiva statistiche per nerd",
"addToLibrary": "Aggiungi alla biblioteca", "addToLibrary": "Aggiungi alla biblioteca",
"removeFromLibrary": "Rimuovi dalla biblioteca", "removeFromLibrary": "Rimuovi dalla biblioteca",
"libraryLoadingMessage": "Caricamento della biblioteca...", "libraryLoadingMessage": "Caricamento della biblioteca...",
"libraries": "Sfoglia librerie",
"loadingScene": "Caricamento della scena...", "loadingScene": "Caricamento della scena...",
"align": "Allinea", "align": "Allinea",
"alignTop": "Allinea in alto", "alignTop": "Allinea in alto",
@@ -116,7 +124,7 @@
"exitZenMode": "Uscire dalla modalità zen" "exitZenMode": "Uscire dalla modalità zen"
}, },
"alerts": { "alerts": {
"clearReset": "Questo cancellerà l'intera tela. Sei sicuro?", "clearReset": "Questa azione cancellerà l'intera tela. Sei sicuro?",
"couldNotCreateShareableLink": "Non riesco a creare un link condivisibile.", "couldNotCreateShareableLink": "Non riesco a creare un link condivisibile.",
"couldNotCreateShareableLinkTooBig": "Impossibile creare il link condivisibile: la scena è troppo grande", "couldNotCreateShareableLinkTooBig": "Impossibile creare il link condivisibile: la scena è troppo grande",
"couldNotLoadInvalidFile": "Impossibile caricare un file no valido", "couldNotLoadInvalidFile": "Impossibile caricare un file no valido",
@@ -205,13 +213,22 @@
"textNewLine": "Aggiungi nuova riga (testo)", "textNewLine": "Aggiungi nuova riga (testo)",
"textFinish": "Completa la modifica (testo)", "textFinish": "Completa la modifica (testo)",
"zoomToFit": "Adatta zoom per mostrare tutti gli elementi", "zoomToFit": "Adatta zoom per mostrare tutti gli elementi",
"zoomToSelection": "Zoom alla selezione",
"preventBinding": "Prevenire l'associazione freccia" "preventBinding": "Prevenire l'associazione freccia"
}, },
"encrypted": { "encrypted": {
"tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere." "tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere."
}, },
"charts": { "stats": {
"noNumericColumn": "Hai incollato un foglio di calcolo senza una colonna numerica.", "angle": "Angolo",
"tooManyColumns": "Hai incollato un foglio di calcolo con più di due colonne." "element": "Elemento",
"elements": "Elementi",
"height": "Altezza",
"scene": "Scena",
"selected": "Selezionato",
"storage": "Memoria",
"title": "Statistiche per nerd",
"total": "Totale",
"width": "Larghezza"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "すべて選択", "selectAll": "すべて選択",
"multiSelect": "複数選択", "multiSelect": "複数選択",
"moveCanvas": "キャンバスを移動", "moveCanvas": "キャンバスを移動",
"cut": "",
"copy": "コピー", "copy": "コピー",
"copyAsPng": "PNGとしてクリップボードへコピー", "copyAsPng": "PNGとしてクリップボードへコピー",
"copyAsSvg": "SVGとしてクリップボードへコピー", "copyAsSvg": "SVGとしてクリップボードへコピー",
@@ -28,6 +29,11 @@
"edges": "角", "edges": "角",
"sharp": "四角", "sharp": "四角",
"round": "丸", "round": "丸",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "フォントの大きさ", "fontSize": "フォントの大きさ",
"fontFamily": "フォントの種類", "fontFamily": "フォントの種類",
"onlySelected": "選択中のみ", "onlySelected": "選択中のみ",
@@ -71,9 +77,11 @@
"ungroup": "グループ化を解除", "ungroup": "グループ化を解除",
"collaborators": "共同編集者", "collaborators": "共同編集者",
"toggleGridMode": "グリッドモードに切り替える", "toggleGridMode": "グリッドモードに切り替える",
"toggleStats": "",
"addToLibrary": "ライブラリに追加", "addToLibrary": "ライブラリに追加",
"removeFromLibrary": "ライブラリから削除", "removeFromLibrary": "ライブラリから削除",
"libraryLoadingMessage": "ライブラリを読み込み中...", "libraryLoadingMessage": "ライブラリを読み込み中...",
"libraries": "",
"loadingScene": "シーンを読み込み中...", "loadingScene": "シーンを読み込み中...",
"align": "整列", "align": "整列",
"alignTop": "上揃え", "alignTop": "上揃え",
@@ -205,13 +213,22 @@
"textNewLine": "テキストの改行", "textNewLine": "テキストの改行",
"textFinish": "テキストの編集を終える", "textFinish": "テキストの編集を終える",
"zoomToFit": "すべての図形が収まるよう拡大/縮小", "zoomToFit": "すべての図形が収まるよう拡大/縮小",
"zoomToSelection": "",
"preventBinding": "矢印を結合しない" "preventBinding": "矢印を結合しない"
}, },
"encrypted": { "encrypted": {
"tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。" "tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。"
}, },
"charts": { "stats": {
"noNumericColumn": "数値の列が存在しないスプレッドシートをペーストしました。", "angle": "",
"tooManyColumns": "2列以上のスプレッドシートを貼り付けました." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "전체 선택", "selectAll": "전체 선택",
"multiSelect": "선택 영역에 추가하기", "multiSelect": "선택 영역에 추가하기",
"moveCanvas": "캔버스 이동", "moveCanvas": "캔버스 이동",
"cut": "",
"copy": "복사하기", "copy": "복사하기",
"copyAsPng": "클립보드로 PNG 이미지 복사", "copyAsPng": "클립보드로 PNG 이미지 복사",
"copyAsSvg": "클립보드로 SVG 이미지 복사", "copyAsSvg": "클립보드로 SVG 이미지 복사",
@@ -28,6 +29,11 @@
"edges": "가장자리", "edges": "가장자리",
"sharp": "선명하게", "sharp": "선명하게",
"round": "둥글게", "round": "둥글게",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "폰트 크기", "fontSize": "폰트 크기",
"fontFamily": "폰트 스타일", "fontFamily": "폰트 스타일",
"onlySelected": "선택한 항목만", "onlySelected": "선택한 항목만",
@@ -71,9 +77,11 @@
"ungroup": "그룹 해제", "ungroup": "그룹 해제",
"collaborators": "공동 작업자", "collaborators": "공동 작업자",
"toggleGridMode": "격자 모드 켜기/끄기", "toggleGridMode": "격자 모드 켜기/끄기",
"toggleStats": "",
"addToLibrary": "라이브러리에 추가", "addToLibrary": "라이브러리에 추가",
"removeFromLibrary": "라이브러리에서 제거", "removeFromLibrary": "라이브러리에서 제거",
"libraryLoadingMessage": "라이브러리 불러오는 중...", "libraryLoadingMessage": "라이브러리 불러오는 중...",
"libraries": "",
"loadingScene": "화면 불러오는 중...", "loadingScene": "화면 불러오는 중...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "줄바꾸기", "textNewLine": "줄바꾸기",
"textFinish": "편집 완료", "textFinish": "편집 완료",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "",
"tooManyColumns": "" "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+24 -7
View File
@@ -4,6 +4,7 @@
"selectAll": "အကုန်ရွေး", "selectAll": "အကုန်ရွေး",
"multiSelect": "ရွေးထားသည့်ထဲပုံထည့်", "multiSelect": "ရွေးထားသည့်ထဲပုံထည့်",
"moveCanvas": "ကားချပ်ရွှေ့", "moveCanvas": "ကားချပ်ရွှေ့",
"cut": "",
"copy": "ကူး", "copy": "ကူး",
"copyAsPng": "PNG အနေဖြင့်ကူး", "copyAsPng": "PNG အနေဖြင့်ကူး",
"copyAsSvg": "SVG အနေဖြင့်ကူး", "copyAsSvg": "SVG အနေဖြင့်ကူး",
@@ -28,6 +29,11 @@
"edges": "အစွန်း", "edges": "အစွန်း",
"sharp": "ထောင့်ချွန်", "sharp": "ထောင့်ချွန်",
"round": "ထောင့်ဝိုင်း", "round": "ထောင့်ဝိုင်း",
"arrowheads": "မြှားခေါင်း",
"arrowhead_none": "ဘာမျှမရှိ",
"arrowhead_arrow": "မြှား",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "စာလုံးအရွယ်", "fontSize": "စာလုံးအရွယ်",
"fontFamily": "စာလုံးပုံစံ", "fontFamily": "စာလုံးပုံစံ",
"onlySelected": "ရွေးထားသလောက်", "onlySelected": "ရွေးထားသလောက်",
@@ -54,7 +60,7 @@
"architect": "ဗိသုကာ", "architect": "ဗိသုကာ",
"artist": "ပန်းချီ", "artist": "ပန်းချီ",
"cartoonist": "ကာတွန်း", "cartoonist": "ကာတွန်း",
"fileTitle": "", "fileTitle": "ခေါင်းစဉ်",
"colorPicker": "အရောင်ရွေး", "colorPicker": "အရောင်ရွေး",
"canvasBackground": "ကားချပ်နောက်ခံ", "canvasBackground": "ကားချပ်နောက်ခံ",
"drawingCanvas": "ပုံဆွဲကားချပ်", "drawingCanvas": "ပုံဆွဲကားချပ်",
@@ -63,7 +69,7 @@
"language": "ဘာသာစကား", "language": "ဘာသာစကား",
"createRoom": "တိုက်ရိုက်ပူးပေါင်းဆောင်ရွက်ရန်အဖွဲ့ဖွဲ့", "createRoom": "တိုက်ရိုက်ပူးပေါင်းဆောင်ရွက်ရန်အဖွဲ့ဖွဲ့",
"duplicateSelection": "ပွား", "duplicateSelection": "ပွား",
"untitled": "", "untitled": "အမည်မရှိ",
"name": "အမည်", "name": "အမည်",
"yourName": "သင့်အမည်", "yourName": "သင့်အမည်",
"madeWithExcalidraw": "Excalidraw ဖြင့်ဖန်တီးသည်။", "madeWithExcalidraw": "Excalidraw ဖြင့်ဖန်တီးသည်။",
@@ -71,9 +77,11 @@
"ungroup": "အုပ်စုဖျက်သိမ်း", "ungroup": "အုပ်စုဖျက်သိမ်း",
"collaborators": "ပူးပေါင်းပါဝင်သူများ", "collaborators": "ပူးပေါင်းပါဝင်သူများ",
"toggleGridMode": "ဇယားကွက်ဖော်/ဖျောက်", "toggleGridMode": "ဇယားကွက်ဖော်/ဖျောက်",
"toggleStats": "",
"addToLibrary": "မှတ်တမ်းတင်", "addToLibrary": "မှတ်တမ်းတင်",
"removeFromLibrary": "မှတ်တမ်းမှထုတ်", "removeFromLibrary": "မှတ်တမ်းမှထုတ်",
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...", "libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...",
"libraries": "စာကြည့်တိုက်တွင်ရှာဖွေပါ",
"loadingScene": "မြင်ကွင်းဖော်နေသည်...", "loadingScene": "မြင်ကွင်းဖော်နေသည်...",
"align": "ချိန်ညှိ", "align": "ချိန်ညှိ",
"alignTop": "ထိပ်ညှိ", "alignTop": "ထိပ်ညှိ",
@@ -82,8 +90,8 @@
"alignRight": "ညာညှိ", "alignRight": "ညာညှိ",
"centerVertically": "ဒေါင်လိုက်အလယ်ညှိ", "centerVertically": "ဒေါင်လိုက်အလယ်ညှိ",
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ", "centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
"distributeHorizontally": "", "distributeHorizontally": "အလျားလိုက်",
"distributeVertically": "" "distributeVertically": "ထောင်လိုက်"
}, },
"buttons": { "buttons": {
"clearReset": "ကားချပ်ရှင်းလင်း", "clearReset": "ကားချပ်ရှင်းလင်း",
@@ -205,13 +213,22 @@
"textNewLine": "စာသားဖြည့်သွင်း", "textNewLine": "စာသားဖြည့်သွင်း",
"textFinish": "စာသားဖြည့်သွင်းပြီး", "textFinish": "စာသားဖြည့်သွင်းပြီး",
"zoomToFit": "ကားချပ်အပြည့်ဖေါ်", "zoomToFit": "ကားချပ်အပြည့်ဖေါ်",
"zoomToSelection": "",
"preventBinding": "မြှားများမပေါင်းစေရန်" "preventBinding": "မြှားများမပေါင်းစေရန်"
}, },
"encrypted": { "encrypted": {
"tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။" "tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။"
}, },
"charts": { "stats": {
"noNumericColumn": "အမှတ်စဉ်ကော်လံမပါဝင်သောဇယားအားထည့်သွင်းလိုက်သည်။", "angle": "ထောင့်",
"tooManyColumns": "ကော်လံနှစ်ခုထက်ပိုပါနေသောဇယားအားထည့်သွင်းလိုက်သည်။" "element": "",
"elements": "",
"height": "အမြင့်",
"scene": "မြင်ကွင်း",
"selected": "ရွေးချယ်သည်",
"storage": "သိုလှောင်ခန်း",
"title": "အက္ခရာများအတွက်အချက်အလက်များ",
"total": "စုစုပေါင်း",
"width": "အကျယ်"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Velg alt", "selectAll": "Velg alt",
"multiSelect": "Legg til element i utvalg", "multiSelect": "Legg til element i utvalg",
"moveCanvas": "Flytt lerretet", "moveCanvas": "Flytt lerretet",
"cut": "Klipp ut",
"copy": "Kopier", "copy": "Kopier",
"copyAsPng": "Kopier til PNG", "copyAsPng": "Kopier til PNG",
"copyAsSvg": "Kopier til utklippstavlen som SVG", "copyAsSvg": "Kopier til utklippstavlen som SVG",
@@ -28,6 +29,11 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "Pilspisser",
"arrowhead_none": "Ingen",
"arrowhead_arrow": "Pil",
"arrowhead_bar": "Søyle",
"arrowhead_dot": "Prikk",
"fontSize": "Skriftstørrelse", "fontSize": "Skriftstørrelse",
"fontFamily": "Fontfamilie", "fontFamily": "Fontfamilie",
"onlySelected": "Kun valgte", "onlySelected": "Kun valgte",
@@ -71,9 +77,11 @@
"ungroup": "Avgruppér utvalg", "ungroup": "Avgruppér utvalg",
"collaborators": "Samarbeidspartnere", "collaborators": "Samarbeidspartnere",
"toggleGridMode": "Slå av/på rutenett", "toggleGridMode": "Slå av/på rutenett",
"toggleStats": "Skru av/på statistikk for nerder",
"addToLibrary": "Legg til i bibliotek", "addToLibrary": "Legg til i bibliotek",
"removeFromLibrary": "Fjern fra bibliotek", "removeFromLibrary": "Fjern fra bibliotek",
"libraryLoadingMessage": "Laster bibliotek...", "libraryLoadingMessage": "Laster bibliotek...",
"libraries": "Bla gjennom biblioteker",
"loadingScene": "Laster inn scene...", "loadingScene": "Laster inn scene...",
"align": "Juster", "align": "Juster",
"alignTop": "Juster øverst", "alignTop": "Juster øverst",
@@ -205,13 +213,22 @@
"textNewLine": "Legg til ny linje (tekst)", "textNewLine": "Legg til ny linje (tekst)",
"textFinish": "Fullfør redigering (tekst)", "textFinish": "Fullfør redigering (tekst)",
"zoomToFit": "Zoom for å passe alle elementene", "zoomToFit": "Zoom for å passe alle elementene",
"zoomToSelection": "Zoom til utvalg",
"preventBinding": "Forhindre pilbinding" "preventBinding": "Forhindre pilbinding"
}, },
"encrypted": { "encrypted": {
"tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem." "tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem."
}, },
"charts": { "stats": {
"noNumericColumn": "Du limte inn et regneark uten en numerisk kolonne.", "angle": "Vinkel",
"tooManyColumns": "Du limte inn et regneark med mer enn to kolonner." "element": "Element",
"elements": "Elementer",
"height": "Høyde",
"scene": "Scene",
"selected": "Valgt",
"storage": "Lagring",
"title": "Statistikk for nerder",
"total": "Totalt",
"width": "Bredde"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Alles selecteren", "selectAll": "Alles selecteren",
"multiSelect": "Voeg element toe aan selectie", "multiSelect": "Voeg element toe aan selectie",
"moveCanvas": "Canvas verplaatsen", "moveCanvas": "Canvas verplaatsen",
"cut": "",
"copy": "Kopiëren", "copy": "Kopiëren",
"copyAsPng": "Kopieer als PNG", "copyAsPng": "Kopieer als PNG",
"copyAsSvg": "Kopieer als SVG", "copyAsSvg": "Kopieer als SVG",
@@ -28,6 +29,11 @@
"edges": "Randen", "edges": "Randen",
"sharp": "Hoekig", "sharp": "Hoekig",
"round": "Rond", "round": "Rond",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Tekstgrootte", "fontSize": "Tekstgrootte",
"fontFamily": "Lettertype", "fontFamily": "Lettertype",
"onlySelected": "Enkel geselecteerde", "onlySelected": "Enkel geselecteerde",
@@ -71,9 +77,11 @@
"ungroup": "Groep opheffen", "ungroup": "Groep opheffen",
"collaborators": "Deelnemers", "collaborators": "Deelnemers",
"toggleGridMode": "Rasterlijnen in-/uitschakelen", "toggleGridMode": "Rasterlijnen in-/uitschakelen",
"toggleStats": "",
"addToLibrary": "Voeg toe aan bibliotheek", "addToLibrary": "Voeg toe aan bibliotheek",
"removeFromLibrary": "Verwijder uit bibliotheek", "removeFromLibrary": "Verwijder uit bibliotheek",
"libraryLoadingMessage": "Bibliotheek laden...", "libraryLoadingMessage": "Bibliotheek laden...",
"libraries": "",
"loadingScene": "Scène laden...", "loadingScene": "Scène laden...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Nieuwe regel toevoegen (tekst)", "textNewLine": "Nieuwe regel toevoegen (tekst)",
"textFinish": "Voltooi bewerken (tekst)", "textFinish": "Voltooi bewerken (tekst)",
"zoomToFit": "Zoom in op alle elementen", "zoomToFit": "Zoom in op alle elementen",
"zoomToSelection": "",
"preventBinding": "" "preventBinding": ""
}, },
"encrypted": { "encrypted": {
"tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent." "tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent."
}, },
"charts": { "stats": {
"noNumericColumn": "Je hebt een werkblad geplakt zonder een numerieke kolom.", "angle": "",
"tooManyColumns": "Je hebt een werkblad geplakt met meer dan twee kolommen." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Vel alt", "selectAll": "Vel alt",
"multiSelect": "Legg til element i utval", "multiSelect": "Legg til element i utval",
"moveCanvas": "Flytt lerretet", "moveCanvas": "Flytt lerretet",
"cut": "",
"copy": "Kopier", "copy": "Kopier",
"copyAsPng": "Kopier til utklippstavla som PNG", "copyAsPng": "Kopier til utklippstavla som PNG",
"copyAsSvg": "Kopier til utklippstavla som SVG", "copyAsSvg": "Kopier til utklippstavla som SVG",
@@ -28,6 +29,11 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Skriftstorleik", "fontSize": "Skriftstorleik",
"fontFamily": "Skrifttype", "fontFamily": "Skrifttype",
"onlySelected": "Kun valde", "onlySelected": "Kun valde",
@@ -71,9 +77,11 @@
"ungroup": "Avgrupper utval", "ungroup": "Avgrupper utval",
"collaborators": "Samarbeidarar", "collaborators": "Samarbeidarar",
"toggleGridMode": "Sla på/av rutenett", "toggleGridMode": "Sla på/av rutenett",
"toggleStats": "",
"addToLibrary": "Legg til i bibliotek", "addToLibrary": "Legg til i bibliotek",
"removeFromLibrary": "Fjern frå bibliotek", "removeFromLibrary": "Fjern frå bibliotek",
"libraryLoadingMessage": "Laster bibliotek...", "libraryLoadingMessage": "Laster bibliotek...",
"libraries": "",
"loadingScene": "Laster scene...", "loadingScene": "Laster scene...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Legg til ny linje (tekst)", "textNewLine": "Legg til ny linje (tekst)",
"textFinish": "Fullfør redigering (tekst)", "textFinish": "Fullfør redigering (tekst)",
"zoomToFit": "Zoom for å sjå alle elementa", "zoomToFit": "Zoom for å sjå alle elementa",
"zoomToSelection": "",
"preventBinding": "Hindre pilkobling" "preventBinding": "Hindre pilkobling"
}, },
"encrypted": { "encrypted": {
"tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei." "tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei."
}, },
"charts": { "stats": {
"noNumericColumn": "Du limte inn eit rekneark utan ei numerisk kolonne.", "angle": "",
"tooManyColumns": "Du limte inn eit rekneark med meir enn to kolonnar." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+23 -23
View File
@@ -1,33 +1,33 @@
{ {
"ar-SA": 98, "ar-SA": 89,
"bg-BG": 67, "bg-BG": 61,
"ca-ES": 89, "ca-ES": 81,
"de-DE": 100, "de-DE": 99,
"el-GR": 96, "el-GR": 95,
"en": 100, "en": 100,
"es-ES": 89, "es-ES": 81,
"fa-IR": 98, "fa-IR": 89,
"fi-FI": 100, "fi-FI": 100,
"fr-FR": 100, "fr-FR": 100,
"he-IL": 76, "he-IL": 69,
"hi-IN": 85, "hi-IN": 82,
"hu-HU": 48, "hu-HU": 44,
"id-ID": 100, "id-ID": 99,
"it-IT": 100, "it-IT": 100,
"ja-JP": 98, "ja-JP": 89,
"ko-KR": 75, "ko-KR": 68,
"my-MM": 97, "my-MM": 96,
"nb-NO": 100, "nb-NO": 100,
"nl-NL": 88, "nl-NL": 80,
"nn-NO": 89, "nn-NO": 80,
"pl-PL": 88, "pl-PL": 79,
"pt-PT": 92, "pt-PT": 83,
"ro-RO": 100, "ro-RO": 100,
"ru-RU": 85, "ru-RU": 81,
"sk-SK": 100, "sk-SK": 100,
"sv-SE": 100, "sv-SE": 100,
"tr-TR": 90, "tr-TR": 81,
"uk-UA": 100, "uk-UA": 98,
"zh-CN": 100, "zh-CN": 95,
"zh-TW": 100 "zh-TW": 99
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Zaznacz wszystko", "selectAll": "Zaznacz wszystko",
"multiSelect": "Dodaj element do zaznaczenia", "multiSelect": "Dodaj element do zaznaczenia",
"moveCanvas": "Przesuń obszar roboczy", "moveCanvas": "Przesuń obszar roboczy",
"cut": "",
"copy": "Kopiuj", "copy": "Kopiuj",
"copyAsPng": "Skopiuj do schowka jako plik PNG", "copyAsPng": "Skopiuj do schowka jako plik PNG",
"copyAsSvg": "Skopiuj do schowka jako plik SVG", "copyAsSvg": "Skopiuj do schowka jako plik SVG",
@@ -28,6 +29,11 @@
"edges": "Krawędzie", "edges": "Krawędzie",
"sharp": "Ostry", "sharp": "Ostry",
"round": "Zaokrąglij", "round": "Zaokrąglij",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Rozmiar tekstu", "fontSize": "Rozmiar tekstu",
"fontFamily": "Krój pisma", "fontFamily": "Krój pisma",
"onlySelected": "Tylko wybrane", "onlySelected": "Tylko wybrane",
@@ -71,9 +77,11 @@
"ungroup": "Rozgrupuj wybrane", "ungroup": "Rozgrupuj wybrane",
"collaborators": "Współtwórcy", "collaborators": "Współtwórcy",
"toggleGridMode": "Włącz siatkę", "toggleGridMode": "Włącz siatkę",
"toggleStats": "",
"addToLibrary": "Dodaj do biblioteki", "addToLibrary": "Dodaj do biblioteki",
"removeFromLibrary": "Usuń z biblioteki", "removeFromLibrary": "Usuń z biblioteki",
"libraryLoadingMessage": "Wczytywanie biblioteki...", "libraryLoadingMessage": "Wczytywanie biblioteki...",
"libraries": "",
"loadingScene": "Ładowanie sceny...", "loadingScene": "Ładowanie sceny...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Dodaj nową linię (tekst)", "textNewLine": "Dodaj nową linię (tekst)",
"textFinish": "Zakończ edycję (tekst)", "textFinish": "Zakończ edycję (tekst)",
"zoomToFit": "Powiększ, aby wyświetlić wszystkie elementy", "zoomToFit": "Powiększ, aby wyświetlić wszystkie elementy",
"zoomToSelection": "",
"preventBinding": "Zablokuj przywiązanie strzałek do obiektu" "preventBinding": "Zablokuj przywiązanie strzałek do obiektu"
}, },
"encrypted": { "encrypted": {
"tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz." "tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz."
}, },
"charts": { "stats": {
"noNumericColumn": "Wklejono arkusz kalkulacyjny bez kolumny numerycznej.", "angle": "",
"tooManyColumns": "Wklejono arkusz kalkulacyjny z więcej niż dwoma kolumnami." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Selecionar tudo", "selectAll": "Selecionar tudo",
"multiSelect": "Adicionar elemento à seleção", "multiSelect": "Adicionar elemento à seleção",
"moveCanvas": "Mover tela", "moveCanvas": "Mover tela",
"cut": "",
"copy": "Copiar", "copy": "Copiar",
"copyAsPng": "Copiar para a área de transferência como PNG", "copyAsPng": "Copiar para a área de transferência como PNG",
"copyAsSvg": "Copiar para a área de transferência como SVG", "copyAsSvg": "Copiar para a área de transferência como SVG",
@@ -28,6 +29,11 @@
"edges": "Arestas", "edges": "Arestas",
"sharp": "Aguçado", "sharp": "Aguçado",
"round": "Redondo", "round": "Redondo",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Tamanho da fonte", "fontSize": "Tamanho da fonte",
"fontFamily": "Família da fontes", "fontFamily": "Família da fontes",
"onlySelected": "Somente a seleção", "onlySelected": "Somente a seleção",
@@ -71,9 +77,11 @@
"ungroup": "Desagrupar seleção", "ungroup": "Desagrupar seleção",
"collaborators": "Colaboradores", "collaborators": "Colaboradores",
"toggleGridMode": "Alternar modo de grade", "toggleGridMode": "Alternar modo de grade",
"toggleStats": "",
"addToLibrary": "Adicionar à biblioteca", "addToLibrary": "Adicionar à biblioteca",
"removeFromLibrary": "Remover da biblioteca", "removeFromLibrary": "Remover da biblioteca",
"libraryLoadingMessage": "Carregando biblioteca...", "libraryLoadingMessage": "Carregando biblioteca...",
"libraries": "",
"loadingScene": "Carregando cena...", "loadingScene": "Carregando cena...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Adicionar nova linha (texto)", "textNewLine": "Adicionar nova linha (texto)",
"textFinish": "Finalizar edição (texto)", "textFinish": "Finalizar edição (texto)",
"zoomToFit": "Ajustar para caber todos os elementos", "zoomToFit": "Ajustar para caber todos os elementos",
"zoomToSelection": "",
"preventBinding": "Prevenir fixação de seta" "preventBinding": "Prevenir fixação de seta"
}, },
"encrypted": { "encrypted": {
"tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão." "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão."
}, },
"charts": { "stats": {
"noNumericColumn": "Você colou uma planilha sem uma coluna numérica.", "angle": "",
"tooManyColumns": "Você colou uma planilha com mais de duas colunas." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Selectare totală", "selectAll": "Selectare totală",
"multiSelect": "Adaugă element la selecție", "multiSelect": "Adaugă element la selecție",
"moveCanvas": "Mutare pânză", "moveCanvas": "Mutare pânză",
"cut": "Decupare",
"copy": "Copiere", "copy": "Copiere",
"copyAsPng": "Copiere în memoria temporară ca PNG", "copyAsPng": "Copiere în memoria temporară ca PNG",
"copyAsSvg": "Copiere în memoria temporară ca SVG", "copyAsSvg": "Copiere în memoria temporară ca SVG",
@@ -28,6 +29,11 @@
"edges": "Margini", "edges": "Margini",
"sharp": "Ascuțite", "sharp": "Ascuțite",
"round": "Rotunde", "round": "Rotunde",
"arrowheads": "Vârfuri de săgeată",
"arrowhead_none": "Niciunul",
"arrowhead_arrow": "Săgeată",
"arrowhead_bar": "Bară",
"arrowhead_dot": "Bulină",
"fontSize": "Dimensiune font", "fontSize": "Dimensiune font",
"fontFamily": "Familia de fonturi", "fontFamily": "Familia de fonturi",
"onlySelected": "Numai selecția", "onlySelected": "Numai selecția",
@@ -71,9 +77,11 @@
"ungroup": "Degrupare selecție", "ungroup": "Degrupare selecție",
"collaborators": "Colaboratori", "collaborators": "Colaboratori",
"toggleGridMode": "Comută modul grilă", "toggleGridMode": "Comută modul grilă",
"toggleStats": "Comută statisticile pentru pasionați",
"addToLibrary": "Adăugare la bibliotecă", "addToLibrary": "Adăugare la bibliotecă",
"removeFromLibrary": "Eliminare din bibliotecă", "removeFromLibrary": "Eliminare din bibliotecă",
"libraryLoadingMessage": "Se încarcă biblioteca...", "libraryLoadingMessage": "Se încarcă biblioteca...",
"libraries": "Răsfoiește bibliotecile",
"loadingScene": "Se încarcă scena...", "loadingScene": "Se încarcă scena...",
"align": "Aliniere", "align": "Aliniere",
"alignTop": "Aliniere sus", "alignTop": "Aliniere sus",
@@ -205,13 +213,22 @@
"textNewLine": "Adaugă o linie nouă (text)", "textNewLine": "Adaugă o linie nouă (text)",
"textFinish": "Finalizează editarea (text)", "textFinish": "Finalizează editarea (text)",
"zoomToFit": "Apropiere/depărtare pentru a cuprinde totul", "zoomToFit": "Apropiere/depărtare pentru a cuprinde totul",
"zoomToSelection": "Panoramare la selecție",
"preventBinding": "Împiedică legarea săgeții" "preventBinding": "Împiedică legarea săgeții"
}, },
"encrypted": { "encrypted": {
"tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată." "tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată."
}, },
"charts": { "stats": {
"noNumericColumn": "Ai inserat o foaie de calcul fără o coloană numerică.", "angle": "Unghi",
"tooManyColumns": "Ai inserat o foaie de calcul cu mai mult de două coloane." "element": "Element",
"elements": "Elemente",
"height": "Înălțime",
"scene": "Scenă",
"selected": "Selectate",
"storage": "Stocare",
"title": "Statistici pentru pasionați",
"total": "Total",
"width": "Lățime"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Выбрать все", "selectAll": "Выбрать все",
"multiSelect": "Добавить элемент к выбору", "multiSelect": "Добавить элемент к выбору",
"moveCanvas": "Переместить холст", "moveCanvas": "Переместить холст",
"cut": "",
"copy": "Копировать", "copy": "Копировать",
"copyAsPng": "Скопировать в буфер обмена как PNG", "copyAsPng": "Скопировать в буфер обмена как PNG",
"copyAsSvg": "Скопировать в буфер обмена как SVG", "copyAsSvg": "Скопировать в буфер обмена как SVG",
@@ -28,6 +29,11 @@
"edges": "Края", "edges": "Края",
"sharp": "Острые", "sharp": "Острые",
"round": "Скругленные", "round": "Скругленные",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "Cтрелка",
"arrowhead_bar": "",
"arrowhead_dot": "Точка",
"fontSize": "Размер шрифта", "fontSize": "Размер шрифта",
"fontFamily": "Семейство шрифтов", "fontFamily": "Семейство шрифтов",
"onlySelected": "Только выбранные", "onlySelected": "Только выбранные",
@@ -71,9 +77,11 @@
"ungroup": "Разделить выделение", "ungroup": "Разделить выделение",
"collaborators": "Сотрудники", "collaborators": "Сотрудники",
"toggleGridMode": "Переключить режим сетки", "toggleGridMode": "Переключить режим сетки",
"toggleStats": "",
"addToLibrary": "Добавить в библиотеку", "addToLibrary": "Добавить в библиотеку",
"removeFromLibrary": "Удалить из библиотеки", "removeFromLibrary": "Удалить из библиотеки",
"libraryLoadingMessage": "Загрузка библиотеки...", "libraryLoadingMessage": "Загрузка библиотеки...",
"libraries": "",
"loadingScene": "Загрузка сцены...", "loadingScene": "Загрузка сцены...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Добавить новую строку (текст)", "textNewLine": "Добавить новую строку (текст)",
"textFinish": "Закончить редактирование (текст)", "textFinish": "Закончить редактирование (текст)",
"zoomToFit": "", "zoomToFit": "",
"zoomToSelection": "",
"preventBinding": "Предотвратить привязку стрелок" "preventBinding": "Предотвратить привязку стрелок"
}, },
"encrypted": { "encrypted": {
"tooltip": "" "tooltip": ""
}, },
"charts": { "stats": {
"noNumericColumn": "", "angle": "Угол",
"tooManyColumns": "Вы вставили таблицу с более чем двумя столбцами." "element": "Элемент",
"elements": "Элементы",
"height": "Высота",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": "Ширина"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Vybrať všetko", "selectAll": "Vybrať všetko",
"multiSelect": "Pridať prvok do výberu", "multiSelect": "Pridať prvok do výberu",
"moveCanvas": "Pohyb plátna", "moveCanvas": "Pohyb plátna",
"cut": "Vystrihnúť",
"copy": "Kopírovať", "copy": "Kopírovať",
"copyAsPng": "Kopírovať do schránky ako PNG", "copyAsPng": "Kopírovať do schránky ako PNG",
"copyAsSvg": "Kopírovať do schránky ako SVG", "copyAsSvg": "Kopírovať do schránky ako SVG",
@@ -28,6 +29,11 @@
"edges": "Okraje", "edges": "Okraje",
"sharp": "Ostré", "sharp": "Ostré",
"round": "Zaokrúhlené", "round": "Zaokrúhlené",
"arrowheads": "Zakončenie šípky",
"arrowhead_none": "Žiadne",
"arrowhead_arrow": "Šípka",
"arrowhead_bar": "Čiara",
"arrowhead_dot": "Bod",
"fontSize": "Veľkosť písma", "fontSize": "Veľkosť písma",
"fontFamily": "Písmo", "fontFamily": "Písmo",
"onlySelected": "Iba vybrané", "onlySelected": "Iba vybrané",
@@ -71,9 +77,11 @@
"ungroup": "Zrušiť zoskupenie", "ungroup": "Zrušiť zoskupenie",
"collaborators": "Spolupracovníci", "collaborators": "Spolupracovníci",
"toggleGridMode": "Prepnúť mriežku", "toggleGridMode": "Prepnúť mriežku",
"toggleStats": "Prepnúť štatistiky",
"addToLibrary": "Pridať do knižnice", "addToLibrary": "Pridať do knižnice",
"removeFromLibrary": "Odstrániť z knižnice", "removeFromLibrary": "Odstrániť z knižnice",
"libraryLoadingMessage": "Načítavanie knižnice...", "libraryLoadingMessage": "Načítavanie knižnice...",
"libraries": "Prehliadať knižnice",
"loadingScene": "Načítavanie scény...", "loadingScene": "Načítavanie scény...",
"align": "Zarovnanie", "align": "Zarovnanie",
"alignTop": "Zarovnať nahor", "alignTop": "Zarovnať nahor",
@@ -205,13 +213,22 @@
"textNewLine": "Vložiť nový riadok (text)", "textNewLine": "Vložiť nový riadok (text)",
"textFinish": "Ukončenie editovania (text)", "textFinish": "Ukončenie editovania (text)",
"zoomToFit": "Priblížiť aby boli zahrnuté všetky prvky", "zoomToFit": "Priblížiť aby boli zahrnuté všetky prvky",
"zoomToSelection": "Priblížiť na výber",
"preventBinding": "Zakázať pripájanie šípky" "preventBinding": "Zakázať pripájanie šípky"
}, },
"encrypted": { "encrypted": {
"tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať." "tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať."
}, },
"charts": { "stats": {
"noNumericColumn": "Prilepili ste tabuľku bez číselného stĺpca.", "angle": "Uhol",
"tooManyColumns": "Prilepili ste tabuľku s viac ako dvoma stĺpcami." "element": "Prvok",
"elements": "Prvky",
"height": "Výška",
"scene": "Scéna",
"selected": "Vybrané",
"storage": "Úložisko",
"title": "Štatistiky",
"total": "Celkom",
"width": "Šírka"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Markera alla", "selectAll": "Markera alla",
"multiSelect": "Lägg till element till markering", "multiSelect": "Lägg till element till markering",
"moveCanvas": "Flytta canvas", "moveCanvas": "Flytta canvas",
"cut": "Klipp ut",
"copy": "Kopiera", "copy": "Kopiera",
"copyAsPng": "Kopiera till urklipp som PNG", "copyAsPng": "Kopiera till urklipp som PNG",
"copyAsSvg": "Kopiera till urklipp som SVG", "copyAsSvg": "Kopiera till urklipp som SVG",
@@ -28,6 +29,11 @@
"edges": "Kanter", "edges": "Kanter",
"sharp": "Skarp", "sharp": "Skarp",
"round": "Rund", "round": "Rund",
"arrowheads": "Pilhuvuden",
"arrowhead_none": "Inga",
"arrowhead_arrow": "Pil",
"arrowhead_bar": "Stolpe",
"arrowhead_dot": "Punkt",
"fontSize": "Teckenstorlek", "fontSize": "Teckenstorlek",
"fontFamily": "Teckensnitt", "fontFamily": "Teckensnitt",
"onlySelected": "Endast markering", "onlySelected": "Endast markering",
@@ -71,9 +77,11 @@
"ungroup": "Avgruppera markering", "ungroup": "Avgruppera markering",
"collaborators": "Medarbetare", "collaborators": "Medarbetare",
"toggleGridMode": "Växla rutnätsläge", "toggleGridMode": "Växla rutnätsläge",
"toggleStats": "Visa/dölj statistik för nördar",
"addToLibrary": "Lägg till i biblioteket", "addToLibrary": "Lägg till i biblioteket",
"removeFromLibrary": "Ta bort från bibliotek", "removeFromLibrary": "Ta bort från bibliotek",
"libraryLoadingMessage": "Laddar bibliotek...", "libraryLoadingMessage": "Laddar bibliotek...",
"libraries": "Bläddra i bibliotek",
"loadingScene": "Laddar scen...", "loadingScene": "Laddar scen...",
"align": "Justera", "align": "Justera",
"alignTop": "Justera överkant", "alignTop": "Justera överkant",
@@ -205,13 +213,22 @@
"textNewLine": "Lägg till ny rad (text)", "textNewLine": "Lägg till ny rad (text)",
"textFinish": "Slutför redigering (text)", "textFinish": "Slutför redigering (text)",
"zoomToFit": "Zooma för att rymma alla element", "zoomToFit": "Zooma för att rymma alla element",
"zoomToSelection": "Zooma till markering",
"preventBinding": "Förhindra pilbindning" "preventBinding": "Förhindra pilbindning"
}, },
"encrypted": { "encrypted": {
"tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem." "tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem."
}, },
"charts": { "stats": {
"noNumericColumn": "Du klistrade in ett kalkylblad utan en numerisk kolumn.", "angle": "Vinkel",
"tooManyColumns": "Du klistrade in ett kalkylblad med mer än två kolumner." "element": "Element",
"elements": "Element",
"height": "Höjd",
"scene": "Skiss",
"selected": "Valda",
"storage": "Lagring",
"title": "Statistik för nördar",
"total": "Totalt",
"width": "Bredd"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Tümünü seç", "selectAll": "Tümünü seç",
"multiSelect": "Seçime öge ekle", "multiSelect": "Seçime öge ekle",
"moveCanvas": "Tuvali taşı", "moveCanvas": "Tuvali taşı",
"cut": "",
"copy": "Kopyala", "copy": "Kopyala",
"copyAsPng": "Panoya PNG olarak kopyala", "copyAsPng": "Panoya PNG olarak kopyala",
"copyAsSvg": "Panoya SVG olarak kopyala", "copyAsSvg": "Panoya SVG olarak kopyala",
@@ -28,6 +29,11 @@
"edges": "Kenarlar", "edges": "Kenarlar",
"sharp": "Keskin", "sharp": "Keskin",
"round": "Yuvarlak", "round": "Yuvarlak",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "Yazı tipi boyutu", "fontSize": "Yazı tipi boyutu",
"fontFamily": "Yazı tipi ailesi", "fontFamily": "Yazı tipi ailesi",
"onlySelected": "Sadece seçilen", "onlySelected": "Sadece seçilen",
@@ -71,9 +77,11 @@
"ungroup": "Seçilen grubu dağıt", "ungroup": "Seçilen grubu dağıt",
"collaborators": "Ortaklar", "collaborators": "Ortaklar",
"toggleGridMode": "Izgara modunu aç", "toggleGridMode": "Izgara modunu aç",
"toggleStats": "",
"addToLibrary": "Kütüphaneye ekle", "addToLibrary": "Kütüphaneye ekle",
"removeFromLibrary": "Kütüphaneden kaldır", "removeFromLibrary": "Kütüphaneden kaldır",
"libraryLoadingMessage": "Kütüphane yükleniyor...", "libraryLoadingMessage": "Kütüphane yükleniyor...",
"libraries": "",
"loadingScene": "Çalışma alanı yükleniyor...", "loadingScene": "Çalışma alanı yükleniyor...",
"align": "", "align": "",
"alignTop": "", "alignTop": "",
@@ -205,13 +213,22 @@
"textNewLine": "Yeni satır ekle (yazı)", "textNewLine": "Yeni satır ekle (yazı)",
"textFinish": "(Yazıyı) düzenlemeyi bitir", "textFinish": "(Yazıyı) düzenlemeyi bitir",
"zoomToFit": "Tüm öğeleri sığdırmak için yakınlaştır", "zoomToFit": "Tüm öğeleri sığdırmak için yakınlaştır",
"zoomToSelection": "",
"preventBinding": "Ok bağlamayı önleyin" "preventBinding": "Ok bağlamayı önleyin"
}, },
"encrypted": { "encrypted": {
"tooltip": "Çizimleriniz uçtan-uca şifrelenmiştir, Excalidraw'ın sunucuları bile onları göremez." "tooltip": "Çizimleriniz uçtan-uca şifrelenmiştir, Excalidraw'ın sunucuları bile onları göremez."
}, },
"charts": { "stats": {
"noNumericColumn": "Sayısal sütunu olmayan bir tablo yapıştırdın.", "angle": "",
"tooManyColumns": "İkiden daha fazla sütuna sahip bir tablo yapıştırdın." "element": "",
"elements": "",
"height": "",
"scene": "",
"selected": "",
"storage": "",
"title": "",
"total": "",
"width": ""
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "Вибрати все", "selectAll": "Вибрати все",
"multiSelect": "Додати елемент до вибраного", "multiSelect": "Додати елемент до вибраного",
"moveCanvas": "Перемістити полотно", "moveCanvas": "Перемістити полотно",
"cut": "Вирізати",
"copy": "Копіювати", "copy": "Копіювати",
"copyAsPng": "Копіювати як PNG", "copyAsPng": "Копіювати як PNG",
"copyAsSvg": "Копіювати як SVG", "copyAsSvg": "Копіювати як SVG",
@@ -28,6 +29,11 @@
"edges": "Краї", "edges": "Краї",
"sharp": "Гострі", "sharp": "Гострі",
"round": "Круглі", "round": "Круглі",
"arrowheads": "",
"arrowhead_none": "Жоден",
"arrowhead_arrow": "Стрілка",
"arrowhead_bar": "Колона",
"arrowhead_dot": "Точка",
"fontSize": "Розмір шрифту", "fontSize": "Розмір шрифту",
"fontFamily": "Шрифт", "fontFamily": "Шрифт",
"onlySelected": "Тільки вибране", "onlySelected": "Тільки вибране",
@@ -71,9 +77,11 @@
"ungroup": "Розгрупувати виділене", "ungroup": "Розгрупувати виділене",
"collaborators": "Співавтори", "collaborators": "Співавтори",
"toggleGridMode": "Режим сітки", "toggleGridMode": "Режим сітки",
"toggleStats": "",
"addToLibrary": "Додати до бібліотеки", "addToLibrary": "Додати до бібліотеки",
"removeFromLibrary": "Видалити з бібліотеки", "removeFromLibrary": "Видалити з бібліотеки",
"libraryLoadingMessage": "Завантажити бібліотеку...", "libraryLoadingMessage": "Завантажити бібліотеку...",
"libraries": "Огляд бібліотек",
"loadingScene": "Завантаження сцени...", "loadingScene": "Завантаження сцени...",
"align": "Вирівнювання", "align": "Вирівнювання",
"alignTop": "Вирівняти по верхньому краю", "alignTop": "Вирівняти по верхньому краю",
@@ -205,13 +213,22 @@
"textNewLine": "Додати новий рядок (текст)", "textNewLine": "Додати новий рядок (текст)",
"textFinish": "Завершити редагування (текст)", "textFinish": "Завершити редагування (текст)",
"zoomToFit": "Збільшити щоб умістити все", "zoomToFit": "Збільшити щоб умістити все",
"zoomToSelection": "Перейти до виділеного",
"preventBinding": "Запобігти зв'язування зі стрілками" "preventBinding": "Запобігти зв'язування зі стрілками"
}, },
"encrypted": { "encrypted": {
"tooltip": "Ваші креслення захищені наскрізним шифруванням — сервери Excalidraw ніколи їх не побачать." "tooltip": "Ваші креслення захищені наскрізним шифруванням — сервери Excalidraw ніколи їх не побачать."
}, },
"charts": { "stats": {
"noNumericColumn": "Ви вставили таблицю без числової колонки.", "angle": "Кут",
"tooManyColumns": "Ви вставляли таблицю з більш ніж двома колонками." "element": "Елемент",
"elements": "Елементи",
"height": "Висота",
"scene": "Сцена",
"selected": "Вибраний",
"storage": "Сховище",
"title": "",
"total": "Всього",
"width": "Ширина"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "全部选中", "selectAll": "全部选中",
"multiSelect": "添加元素到选区", "multiSelect": "添加元素到选区",
"moveCanvas": "移动画布", "moveCanvas": "移动画布",
"cut": "",
"copy": "复制", "copy": "复制",
"copyAsPng": "复制为 PNG 到剪贴板", "copyAsPng": "复制为 PNG 到剪贴板",
"copyAsSvg": "复制为 SVG 到剪贴板", "copyAsSvg": "复制为 SVG 到剪贴板",
@@ -28,6 +29,11 @@
"edges": "边角", "edges": "边角",
"sharp": "尖锐", "sharp": "尖锐",
"round": "圆润", "round": "圆润",
"arrowheads": "",
"arrowhead_none": "",
"arrowhead_arrow": "",
"arrowhead_bar": "",
"arrowhead_dot": "",
"fontSize": "字体大小", "fontSize": "字体大小",
"fontFamily": "字体", "fontFamily": "字体",
"onlySelected": "仅被选中", "onlySelected": "仅被选中",
@@ -71,9 +77,11 @@
"ungroup": "取消组选", "ungroup": "取消组选",
"collaborators": "协作者", "collaborators": "协作者",
"toggleGridMode": "切换网格模式", "toggleGridMode": "切换网格模式",
"toggleStats": "",
"addToLibrary": "添加到库中", "addToLibrary": "添加到库中",
"removeFromLibrary": "从库中移除", "removeFromLibrary": "从库中移除",
"libraryLoadingMessage": "正在加载库...", "libraryLoadingMessage": "正在加载库...",
"libraries": "",
"loadingScene": "正在加载绘图...", "loadingScene": "正在加载绘图...",
"align": "对齐", "align": "对齐",
"alignTop": "顶部对齐", "alignTop": "顶部对齐",
@@ -205,13 +213,22 @@
"textNewLine": "文本换行", "textNewLine": "文本换行",
"textFinish": "完成编辑文本", "textFinish": "完成编辑文本",
"zoomToFit": "缩放以适应所有元素", "zoomToFit": "缩放以适应所有元素",
"zoomToSelection": "缩放至选择部分",
"preventBinding": "防止箭头吸附" "preventBinding": "防止箭头吸附"
}, },
"encrypted": { "encrypted": {
"tooltip": "您的绘图采用的端到端加密,因此Excalidraw服务器永远不会收集。" "tooltip": "您的绘图采用的端到端加密,因此Excalidraw服务器永远不会收集。"
}, },
"charts": { "stats": {
"noNumericColumn": "您粘贴了一个没有数字列的表格。", "angle": "角度",
"tooManyColumns": "您粘贴了两列以上的表格。" "element": "元素",
"elements": "元素",
"height": "高度",
"scene": "场景",
"selected": "选中",
"storage": "存储",
"title": "",
"total": "总计",
"width": "宽度"
} }
} }
+20 -3
View File
@@ -4,6 +4,7 @@
"selectAll": "全選", "selectAll": "全選",
"multiSelect": "將物件加入選取範圍", "multiSelect": "將物件加入選取範圍",
"moveCanvas": "移動畫布", "moveCanvas": "移動畫布",
"cut": "剪下",
"copy": "複製", "copy": "複製",
"copyAsPng": "複製 PNG 至剪貼簿", "copyAsPng": "複製 PNG 至剪貼簿",
"copyAsSvg": "複製 SVG 至剪貼簿", "copyAsSvg": "複製 SVG 至剪貼簿",
@@ -28,6 +29,11 @@
"edges": "邊緣", "edges": "邊緣",
"sharp": "尖銳", "sharp": "尖銳",
"round": "平滑", "round": "平滑",
"arrowheads": "箭頭",
"arrowhead_none": "無",
"arrowhead_arrow": "箭頭",
"arrowhead_bar": "塊",
"arrowhead_dot": "點",
"fontSize": "字型大小", "fontSize": "字型大小",
"fontFamily": "字體", "fontFamily": "字體",
"onlySelected": "僅選取物件", "onlySelected": "僅選取物件",
@@ -71,9 +77,11 @@
"ungroup": "取消群組", "ungroup": "取消群組",
"collaborators": "協作者", "collaborators": "協作者",
"toggleGridMode": "切換格線模式", "toggleGridMode": "切換格線模式",
"toggleStats": "切換詳細統計",
"addToLibrary": "加入資料庫", "addToLibrary": "加入資料庫",
"removeFromLibrary": "從資料庫中移除", "removeFromLibrary": "從資料庫中移除",
"libraryLoadingMessage": "資料庫讀取中…", "libraryLoadingMessage": "資料庫讀取中…",
"libraries": "",
"loadingScene": "場景讀取中…", "loadingScene": "場景讀取中…",
"align": "對齊", "align": "對齊",
"alignTop": "對齊頂部", "alignTop": "對齊頂部",
@@ -205,13 +213,22 @@
"textNewLine": "換行(文字)", "textNewLine": "換行(文字)",
"textFinish": "完成編輯(文字)", "textFinish": "完成編輯(文字)",
"zoomToFit": "放大至填滿畫面", "zoomToFit": "放大至填滿畫面",
"zoomToSelection": "縮放至選取區",
"preventBinding": "防止箭頭綁定" "preventBinding": "防止箭頭綁定"
}, },
"encrypted": { "encrypted": {
"tooltip": "你的作品已使用 end-to-end 方式加密,Excalidraw 的伺服器也無法取得其內容。" "tooltip": "你的作品已使用 end-to-end 方式加密,Excalidraw 的伺服器也無法取得其內容。"
}, },
"charts": { "stats": {
"noNumericColumn": "你貼上的 spreadsheet 沒有數字欄。", "angle": "角度",
"tooManyColumns": "你貼上的 spreadsheet 超過兩欄。" "element": "元素",
"elements": "元素",
"height": "高度",
"scene": "場景",
"selected": "已選",
"storage": "儲存",
"title": "詳細統計",
"total": "合計",
"width": "寬度"
} }
} }
+5 -5
View File
@@ -1,5 +1,5 @@
import { Point } from "./types"; import { Point } from "./types";
import { LINE_CONFIRM_THRESHOLD } from "./constants"; import { GRID_SIZE, LINE_CONFIRM_THRESHOLD } from "./constants";
import { ExcalidrawLinearElement } from "./element/types"; import { ExcalidrawLinearElement } from "./element/types";
export const rotate = ( export const rotate = (
@@ -307,12 +307,12 @@ const doSegmentsIntersect = (p1: Point, q1: Point, p2: Point, q2: Point) => {
export const getGridPoint = ( export const getGridPoint = (
x: number, x: number,
y: number, y: number,
gridSize: number | null, isGridOn: boolean,
): [number, number] => { ): [number, number] => {
if (gridSize) { if (isGridOn) {
return [ return [
Math.round(x / gridSize) * gridSize, Math.round(x / GRID_SIZE) * GRID_SIZE,
Math.round(y / gridSize) * gridSize, Math.round(y / GRID_SIZE) * GRID_SIZE,
]; ];
} }
return [x, y]; return [x, y];
-10
View File
@@ -1,10 +0,0 @@
# Changelog
## 0.1.0
First release of `@excalidraw/excalidraw`
## 0.1.1
#### Fix
- Update the homepage URL so it redirects to correct readme [#2498](https://github.com/excalidraw/excalidraw/pull/2498)
+62
View File
@@ -0,0 +1,62 @@
# Changelog
<!--
Guidelines for changelog:
The change should be grouped under one of the below section and must contain PR link.
- Features: For new features.
- Fixes: For bug fixes.
- Chore: Changes for non src files example package.json.
- Improvements: For any improvements.
- Refactor: For any refactoring.
Please add the latest change on the top under the correct section.
-->
## [Unreleased]
### Features
- Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
- Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
- Aligns arrowhead schemas [#2517](https://github.com/excalidraw/excalidraw/pull/2517)
- Add Cut to menus [#2511](https://github.com/excalidraw/excalidraw/pull/2511)
- More Arrowheads: dot, bar [#2486](https://github.com/excalidraw/excalidraw/pull/2486)
- Support CSV graphs and improve the look and feel [#2495](https://github.com/excalidraw/excalidraw/pull/2495)
### Fixes
- Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
- Remove unnecessary console.error as it was polluting Sentry [#2637](https://github.com/excalidraw/excalidraw/pull/2637)
- Fix scroll-to-center on init for non-zero canvas offsets [#2445](https://github.com/excalidraw/excalidraw/pull/2445)
- Hide collab button when onCollabButtonClick not supplied [#2598](https://github.com/excalidraw/excalidraw/pull/2598)
- Fix resizing the pasted charts [#2586](https://github.com/excalidraw/excalidraw/pull/2586)
- Fix element visibility and zoom on cursor when canvas offset isn't 0. [#2534](https://github.com/excalidraw/excalidraw/pull/2534)
- Fix Library Menu Layout [#2502](https://github.com/excalidraw/excalidraw/pull/2502)
- Support number with commas in charts [#2636](https://github.com/excalidraw/excalidraw/pull/2636)
- Don't break zoom when zooming in on UI [#2638](https://github.com/excalidraw/excalidraw/pull/2638)
### Improvements
- Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
- Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619)
- Add tooltip with icon for embedding scenes [#2532](https://github.com/excalidraw/excalidraw/pull/2532)
- RTL support for the stats dialog [#2530](https://github.com/excalidraw/excalidraw/pull/2530)
- Expand canvas padding based on zoom. [#2515](https://github.com/excalidraw/excalidraw/pull/2515)
- Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508)
- Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509)
### Chore
- Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500)
## 0.1.1
#### Fix
- Update the homepage URL so it redirects to correct readme [#2498](https://github.com/excalidraw/excalidraw/pull/2498)
## 0.1.0
First release of `@excalidraw/excalidraw`
+33 -23
View File
@@ -39,26 +39,19 @@ import "./styles.css";
export default function App() { export default function App() {
const excalidrawRef = createRef(); const excalidrawRef = createRef();
const onChange = (elements, state) => {
console.log(excalidrawRef.current);
console.log("Elements :", elements, "State : ", state);
};
const [dimensions, setDimensions] = useState({ const [dimensions, setDimensions] = useState({
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight, height: window.innerHeight,
}); });
const onResize = () => {
setDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
};
useEffect(() => { useEffect(() => {
const onResize = () => {
setDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", onResize); window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize);
}, []); }, []);
@@ -94,7 +87,6 @@ export default function App() {
excalidrawRef.current.updateScene(sceneData); excalidrawRef.current.updateScene(sceneData);
}; };
const { width, height } = dimensions;
return ( return (
<div className="App"> <div className="App">
<button className="update-scene" onClick={updateScene}> <button className="update-scene" onClick={updateScene}>
@@ -111,12 +103,17 @@ export default function App() {
<div className="excalidraw-wrapper"> <div className="excalidraw-wrapper">
<Excalidraw <Excalidraw
ref={excalidrawRef} ref={excalidrawRef}
width={width} width={dimensions.width}
height={height} height={dimensions.height}
initialData={InitialData} initialData={InitialData}
onChange={onChange} onChange={(elements, state) => {
console.log("Latest elements:", elements, "Latest state:", state);
}}
user={{ name: "Excalidraw User" }} user={{ name: "Excalidraw User" }}
onPointerUpdate={(payload) => console.log(payload)} onPointerUpdate={(pointerData) => console.log(pointerData)}
onCollabButtonClick={() => {
window.alert("You clicked on collab button");
}}
/> />
</div> </div>
</div> </div>
@@ -141,6 +138,7 @@ export default function App() {
| [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked | | [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked |
| [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode | | [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode |
| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. | | [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
#### `width` #### `width`
@@ -219,8 +217,8 @@ This is the user name which shows during collaboration. Defaults to `{name: ''}`
#### `excalidrawRef` #### `excalidrawRef`
You can pass a ref when you want to access some excalidraw API's. You can pass a `ref` when you want to access some excalidraw APIs.
We expose the below API's We expose the below APIs:
| API | signature | Usage | | API | signature | Usage |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -241,15 +239,15 @@ const excalidrawRef = { current: { readyPromise: <a href="https://github.com/exc
#### `onCollabButtonClick` #### `onCollabButtonClick`
This callback is triggered when clicked on the collab button in excalidraw This callback is triggered when clicked on the collab button in excalidraw. If not supplied, the collab dialog button is not rendered.
#### `isCollaborating` #### `isCollaborating`
This props implies if the app is in collaboration mode This prop indicates if the app is in collaboration mode.
#### `onPointerUpdate` #### `onPointerUpdate`
This callback is triggered when mouse pointer is updated This callback is triggered when mouse pointer is updated.
```js ```js
({ x, y }, button, pointersMap}) => void; ({ x, y }, button, pointersMap}) => void;
@@ -260,3 +258,15 @@ This callback is triggered when mouse pointer is updated
2.`button`: The position of the button. This will be one of `["down", "up"]` 2.`button`: The position of the button. This will be one of `["down", "up"]`
3.`pointersMap`: [`pointers map`](https://github.com/excalidraw/excalidraw/blob/182a3e39e1362d73d2a565c870eb2fb72071fdcc/src/types.ts#L122) of the scene 3.`pointersMap`: [`pointers map`](https://github.com/excalidraw/excalidraw/blob/182a3e39e1362d73d2a565c870eb2fb72071fdcc/src/types.ts#L122) of the scene
#### `onExportToBackend`
This callback is triggered when the shareable-link button is clicked in the export dialog. The link button will only be shown if this callback is passed.
```js
(exportedElements, appState, canvas) => void
```
1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/6e45cb95dbd7a8be1859c7055b06957298e3097c/src/element/types.ts#L76) which needs to be exported.
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/4c90ea5667d29effe8ec4a115e49efc7c340cdb3/src/types.ts#L33) of the scene.
3. `canvas`: The `HTMLCanvasElement` of the scene.
+3 -2
View File
@@ -8,7 +8,6 @@ import "../../css/styles.scss";
import { ExcalidrawAPIRefValue, ExcalidrawProps } from "../../types"; import { ExcalidrawAPIRefValue, ExcalidrawProps } from "../../types";
import { IsMobileProvider } from "../../is-mobile"; import { IsMobileProvider } from "../../is-mobile";
import { noop } from "../../utils";
const Excalidraw = (props: ExcalidrawProps) => { const Excalidraw = (props: ExcalidrawProps) => {
const { const {
@@ -20,9 +19,10 @@ const Excalidraw = (props: ExcalidrawProps) => {
initialData, initialData,
user, user,
excalidrawRef, excalidrawRef,
onCollabButtonClick = noop, onCollabButtonClick,
isCollaborating, isCollaborating,
onPointerUpdate, onPointerUpdate,
onExportToBackend,
} = props; } = props;
useEffect(() => { useEffect(() => {
@@ -58,6 +58,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
onCollabButtonClick={onCollabButtonClick} onCollabButtonClick={onCollabButtonClick}
isCollaborating={isCollaborating} isCollaborating={isCollaborating}
onPointerUpdate={onPointerUpdate} onPointerUpdate={onPointerUpdate}
onExportToBackend={onExportToBackend}
/> />
</IsMobileProvider> </IsMobileProvider>
</InitializeApp> </InitializeApp>
+1414 -4809
View File
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -37,30 +37,30 @@
] ]
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.13.1", "react": "^17.0.1",
"react-dom": "^16.13.1" "react-dom": "^17.0.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.9.0", "@babel/core": "7.12.10",
"@babel/plugin-transform-arrow-functions": "7.8.3", "@babel/plugin-transform-arrow-functions": "7.12.1",
"@babel/plugin-transform-async-to-generator": "7.8.3", "@babel/plugin-transform-async-to-generator": "7.12.1",
"@babel/plugin-transform-runtime": "7.12.1", "@babel/plugin-transform-runtime": "7.12.10",
"@babel/plugin-transform-typescript": "7.9.4", "@babel/plugin-transform-typescript": "7.12.1",
"@babel/preset-env": "7.9.5", "@babel/preset-env": "7.12.11",
"@babel/preset-react": "7.9.4", "@babel/preset-react": "7.12.10",
"@babel/preset-typescript": "7.9.0", "@babel/preset-typescript": "7.12.7",
"babel-loader": "8.1.0", "babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1", "babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.2", "cross-env": "7.0.3",
"css-loader": "3.5.2", "css-loader": "5.0.1",
"file-loader": "6.0.0", "file-loader": "6.2.0",
"mini-css-extract-plugin": "0.8.0", "mini-css-extract-plugin": "1.3.3",
"sass-loader": "8.0.2", "sass-loader": "10.1.0",
"terser-webpack-plugin": "2.3.5", "terser-webpack-plugin": "5.0.3",
"ts-loader": "7.0.0", "ts-loader": "8.0.12",
"webpack": "4.42.0", "webpack": "5.11.0",
"webpack-bundle-analyzer": "3.9.0", "webpack-bundle-analyzer": "4.3.0",
"webpack-cli": "3.3.11" "webpack-cli": "4.2.0"
}, },
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
+1 -1
View File
@@ -16,7 +16,7 @@ type ExportOpts = {
) => { width: number; height: number; scale: number }; ) => { width: number; height: number; scale: number };
}; };
const exportToCanvas = ({ export const exportToCanvas = ({
elements, elements,
appState = getDefaultAppState(), appState = getDefaultAppState(),
getDimensions = (width, height) => ({ width, height, scale: 1 }), getDimensions = (width, height) => ({ width, height, scale: 1 }),
-1
View File
@@ -75,7 +75,6 @@ const excalidrawDiagram = {
], ],
appState: { appState: {
viewBackgroundColor: "#ffffff", viewBackgroundColor: "#ffffff",
gridSize: null,
}, },
}; };
+1 -1
View File
@@ -1 +1 @@
export { exportToBlob, exportToSvg } from "../utils.ts"; export { exportToBlob, exportToSvg, exportToCanvas } from "../utils.ts";
+968 -4264
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -34,21 +34,21 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.9.0", "@babel/core": "7.12.10",
"@babel/plugin-transform-arrow-functions": "7.8.3", "@babel/plugin-transform-arrow-functions": "7.12.1",
"@babel/plugin-transform-async-to-generator": "7.8.3", "@babel/plugin-transform-async-to-generator": "7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.10",
"@babel/plugin-transform-typescript": "7.9.4", "@babel/plugin-transform-typescript": "7.12.1",
"@babel/preset-env": "7.9.5", "@babel/preset-env": "7.12.11",
"@babel/preset-typescript": "7.9.0", "@babel/preset-typescript": "7.12.7",
"babel-loader": "8.1.0", "babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1", "babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.2", "cross-env": "7.0.3",
"file-loader": "6.0.0", "file-loader": "6.2.0",
"ts-loader": "7.0.0", "ts-loader": "8.0.12",
"webpack": "4.42.0", "webpack": "5.11.0",
"webpack-bundle-analyzer": "3.9.0", "webpack-bundle-analyzer": "4.3.0",
"webpack-cli": "3.3.11" "webpack-cli": "4.2.0"
}, },
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
+23 -17
View File
@@ -48,6 +48,7 @@ import {
TransformHandleType, TransformHandleType,
} from "../element/transformHandles"; } from "../element/transformHandles";
import { viewportCoordsToSceneCoords } from "../utils"; import { viewportCoordsToSceneCoords } from "../utils";
import { GRID_SIZE } from "../constants";
const strokeRectWithRotation = ( const strokeRectWithRotation = (
context: CanvasRenderingContext2D, context: CanvasRenderingContext2D,
@@ -118,7 +119,6 @@ const fillCircle = (
const strokeGrid = ( const strokeGrid = (
context: CanvasRenderingContext2D, context: CanvasRenderingContext2D,
gridSize: number,
offsetX: number, offsetX: number,
offsetY: number, offsetY: number,
width: number, width: number,
@@ -127,13 +127,13 @@ const strokeGrid = (
const origStrokeStyle = context.strokeStyle; const origStrokeStyle = context.strokeStyle;
context.strokeStyle = "rgba(0,0,0,0.1)"; context.strokeStyle = "rgba(0,0,0,0.1)";
context.beginPath(); context.beginPath();
for (let x = offsetX; x < offsetX + width + gridSize * 2; x += gridSize) { for (let x = offsetX; x < offsetX + width + GRID_SIZE * 2; x += GRID_SIZE) {
context.moveTo(x, offsetY - gridSize); context.moveTo(x, offsetY - GRID_SIZE);
context.lineTo(x, offsetY + height + gridSize * 2); context.lineTo(x, offsetY + height + GRID_SIZE * 2);
} }
for (let y = offsetY; y < offsetY + height + gridSize * 2; y += gridSize) { for (let y = offsetY; y < offsetY + height + GRID_SIZE * 2; y += GRID_SIZE) {
context.moveTo(offsetX - gridSize, y); context.moveTo(offsetX - GRID_SIZE, y);
context.lineTo(offsetX + width + gridSize * 2, y); context.lineTo(offsetX + width + GRID_SIZE * 2, y);
} }
context.stroke(); context.stroke();
context.strokeStyle = origStrokeStyle; context.strokeStyle = origStrokeStyle;
@@ -233,16 +233,15 @@ export const renderScene = (
context.scale(sceneState.zoom.value, sceneState.zoom.value); context.scale(sceneState.zoom.value, sceneState.zoom.value);
// Grid // Grid
if (renderGrid && appState.gridSize) { if (renderGrid && appState.showGrid) {
strokeGrid( strokeGrid(
context, context,
appState.gridSize, -Math.ceil(zoomTranslationX / sceneState.zoom.value / GRID_SIZE) *
-Math.ceil(zoomTranslationX / sceneState.zoom.value / appState.gridSize) * GRID_SIZE +
appState.gridSize + (sceneState.scrollX % GRID_SIZE),
(sceneState.scrollX % appState.gridSize), -Math.ceil(zoomTranslationY / sceneState.zoom.value / GRID_SIZE) *
-Math.ceil(zoomTranslationY / sceneState.zoom.value / appState.gridSize) * GRID_SIZE +
appState.gridSize + (sceneState.scrollY % GRID_SIZE),
(sceneState.scrollY % appState.gridSize),
normalizedCanvasWidth / sceneState.zoom.value, normalizedCanvasWidth / sceneState.zoom.value,
normalizedCanvasHeight / sceneState.zoom.value, normalizedCanvasHeight / sceneState.zoom.value,
); );
@@ -763,13 +762,20 @@ const isVisibleElement = (
) => { ) => {
const [x1, y1, x2, y2] = getElementBounds(element); // scene coordinates const [x1, y1, x2, y2] = getElementBounds(element); // scene coordinates
const topLeftSceneCoords = viewportCoordsToSceneCoords( const topLeftSceneCoords = viewportCoordsToSceneCoords(
{ clientX: 0, clientY: 0 }, {
clientX: viewTransformations.offsetLeft,
clientY: viewTransformations.offsetTop,
},
viewTransformations, viewTransformations,
); );
const bottomRightSceneCoords = viewportCoordsToSceneCoords( const bottomRightSceneCoords = viewportCoordsToSceneCoords(
{ clientX: canvasWidth, clientY: canvasHeight }, {
clientX: viewTransformations.offsetLeft + canvasWidth,
clientY: viewTransformations.offsetTop + canvasHeight,
},
viewTransformations, viewTransformations,
); );
return ( return (
topLeftSceneCoords.x <= x2 && topLeftSceneCoords.x <= x2 &&
topLeftSceneCoords.y <= y2 && topLeftSceneCoords.y <= y2 &&
+5 -2
View File
@@ -3,6 +3,7 @@ import { NormalizedZoomValue, PointerCoords, Zoom } from "../types";
export const getNewZoom = ( export const getNewZoom = (
newZoomValue: NormalizedZoomValue, newZoomValue: NormalizedZoomValue,
prevZoom: Zoom, prevZoom: Zoom,
canvasOffset: { left: number; top: number },
zoomOnViewportPoint: PointerCoords = { x: 0, y: 0 }, zoomOnViewportPoint: PointerCoords = { x: 0, y: 0 },
): Zoom => { ): Zoom => {
return { return {
@@ -10,11 +11,13 @@ export const getNewZoom = (
translation: { translation: {
x: x:
zoomOnViewportPoint.x - zoomOnViewportPoint.x -
(zoomOnViewportPoint.x - prevZoom.translation.x) * canvasOffset.left -
(zoomOnViewportPoint.x - canvasOffset.left - prevZoom.translation.x) *
(newZoomValue / prevZoom.value), (newZoomValue / prevZoom.value),
y: y:
zoomOnViewportPoint.y - zoomOnViewportPoint.y -
(zoomOnViewportPoint.y - prevZoom.translation.y) * canvasOffset.top -
(zoomOnViewportPoint.y - canvasOffset.top - prevZoom.translation.y) *
(newZoomValue / prevZoom.value), (newZoomValue / prevZoom.value),
}, },
}; };
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`tryParseSpreadsheet works for numbers with comma in them 1`] = `
Object {
"spreadsheet": Object {
"labels": Array [
"Week 1",
"Week 2",
"Week 3",
],
"title": "Users",
"values": Array [
814,
10301,
4264,
],
},
"type": "VALID_SPREADSHEET",
}
`;
@@ -31,7 +31,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -67,6 +66,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -486,7 +486,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -529,6 +528,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -947,7 +947,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": false, "isBindingEnabled": false,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -973,6 +972,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -1717,7 +1717,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -1745,6 +1744,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -1915,7 +1915,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -1948,6 +1947,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -2367,7 +2367,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -2395,6 +2394,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -2614,7 +2614,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -2639,6 +2638,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -2772,7 +2772,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -2800,6 +2799,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -3243,7 +3243,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -3268,6 +3267,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -3545,7 +3545,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -3573,6 +3572,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -3743,7 +3743,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -3773,6 +3772,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -3981,7 +3981,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -4009,6 +4008,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -4227,7 +4227,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -4256,6 +4255,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -4604,7 +4604,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -4653,6 +4652,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -4893,7 +4893,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -4920,6 +4919,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -5194,7 +5194,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -5241,6 +5240,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -5396,7 +5396,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -5421,6 +5420,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -5554,7 +5554,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -5579,6 +5578,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -6001,7 +6001,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -6033,6 +6032,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -6313,7 +6313,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -6338,6 +6337,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -8341,7 +8341,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -8371,6 +8370,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -8697,7 +8697,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -8728,6 +8727,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -8946,7 +8946,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -8975,6 +8974,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -9193,7 +9193,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -9223,6 +9222,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -9502,7 +9502,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -9527,6 +9526,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -9660,7 +9660,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -9685,6 +9684,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -9818,7 +9818,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -9843,6 +9842,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -9976,7 +9976,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10001,6 +10000,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -10164,7 +10164,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10189,6 +10188,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -10352,7 +10352,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10377,6 +10376,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -10540,7 +10540,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10565,6 +10564,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -10728,7 +10728,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10753,6 +10752,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -10886,7 +10886,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -10911,6 +10910,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -11044,7 +11044,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -11069,6 +11068,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -11232,7 +11232,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -11257,6 +11256,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -11390,7 +11390,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -11415,6 +11414,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -11578,7 +11578,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -11614,6 +11613,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -12288,7 +12288,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -12317,6 +12316,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -12535,7 +12535,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -12560,6 +12559,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": true, "shouldCacheIgnoreZoom": true,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -12631,7 +12631,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -12654,6 +12653,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -12725,7 +12725,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -12750,6 +12749,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -12883,7 +12883,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -12908,6 +12907,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -13185,7 +13185,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -13210,6 +13209,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -13487,7 +13487,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -13512,6 +13511,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -13645,7 +13645,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -13668,6 +13667,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -13835,7 +13835,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -13860,6 +13859,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -14078,7 +14078,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -14109,6 +14108,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -14396,7 +14396,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -14421,6 +14420,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -15229,7 +15229,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -15254,6 +15253,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -15531,7 +15531,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -15556,6 +15555,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -15833,7 +15833,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -15862,6 +15861,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -16206,7 +16206,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -16234,6 +16233,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -16367,7 +16367,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -16401,6 +16400,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -16682,7 +16682,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -16710,6 +16709,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -16915,7 +16915,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -16946,6 +16945,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -17164,7 +17164,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -17197,6 +17196,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -17485,7 +17485,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -17508,6 +17507,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -17579,7 +17579,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -17604,6 +17603,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -17737,7 +17737,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -17773,6 +17772,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -18552,7 +18552,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -18575,6 +18574,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -18646,7 +18646,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -18671,6 +18670,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -19392,7 +19392,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -19443,6 +19442,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -19791,7 +19791,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -19840,6 +19839,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -20058,7 +20058,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -20083,6 +20082,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": true, "shouldCacheIgnoreZoom": true,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -20154,7 +20154,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -20179,6 +20178,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -20646,7 +20646,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -20669,6 +20668,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
@@ -20740,7 +20740,6 @@ Object {
"exportBackground": true, "exportBackground": true,
"exportEmbedScene": false, "exportEmbedScene": false,
"fileHandle": null, "fileHandle": null,
"gridSize": null,
"height": 768, "height": 768,
"isBindingEnabled": true, "isBindingEnabled": true,
"isLibraryOpen": false, "isLibraryOpen": false,
@@ -20763,6 +20762,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showGrid": false,
"showShortcutsDialog": false, "showShortcutsDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
+13
View File
@@ -0,0 +1,13 @@
import { tryParseSpreadsheet } from "../charts";
describe("tryParseSpreadsheet", () => {
it("works for numbers with comma in them", () => {
const result = tryParseSpreadsheet(
`Week Index${"\t"}Users
Week 1${"\t"}814
Week 2${"\t"}10,301
Week 3${"\t"}4,264`,
);
expect(result).toMatchSnapshot();
});
});
+4 -4
View File
@@ -1,4 +1,4 @@
import { queryByText } from "@testing-library/react"; import { queryAllByText, queryByText } from "@testing-library/react";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { copiedStyles } from "../actions/actionStyles"; import { copiedStyles } from "../actions/actionStyles";
@@ -635,8 +635,8 @@ describe("regression tests", () => {
const contextMenu = document.querySelector(".context-menu"); const contextMenu = document.querySelector(".context-menu");
const expectedShortcutNames: ShortcutName[] = [ const expectedShortcutNames: ShortcutName[] = [
"selectAll", "selectAll",
"toggleGridMode", "gridMode",
"toggleStats", "stats",
]; ];
expect(contextMenu).not.toBeNull(); expect(contextMenu).not.toBeNull();
@@ -864,7 +864,7 @@ describe("regression tests", () => {
clientY: 1, clientY: 1,
}); });
const contextMenu = document.querySelector(".context-menu"); const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Delete")!); fireEvent.click(queryAllByText(contextMenu as HTMLElement, "Delete")[0]);
expect(API.getSelectedElements()).toHaveLength(0); expect(API.getSelectedElements()).toHaveLength(0);
expect(h.elements[0].isDeleted).toBe(true); expect(h.elements[0].isDeleted).toBe(true);
}); });
+48
View File
@@ -0,0 +1,48 @@
import React from "react";
import { render, waitFor } from "./test-utils";
import Excalidraw from "../packages/excalidraw/index";
import { API } from "./helpers/api";
const { h } = window;
describe("appState", () => {
it("scroll-to-center on init works with non-zero offsets", async () => {
const WIDTH = 600;
const HEIGHT = 700;
const OFFSET_LEFT = 200;
const OFFSET_TOP = 100;
const ELEM_WIDTH = 100;
const ELEM_HEIGHT = 60;
await render(
<Excalidraw
width={WIDTH}
height={HEIGHT}
offsetLeft={OFFSET_LEFT}
offsetTop={OFFSET_TOP}
initialData={{
elements: [
API.createElement({
type: "rectangle",
id: "A",
width: ELEM_WIDTH,
height: ELEM_HEIGHT,
}),
],
}}
/>,
);
await waitFor(() => {
expect(h.state.width).toBe(WIDTH);
expect(h.state.height).toBe(HEIGHT);
expect(h.state.offsetLeft).toBe(OFFSET_LEFT);
expect(h.state.offsetTop).toBe(OFFSET_TOP);
// assert scroll is in center
expect(h.state.scrollX).toBe(WIDTH / 2 - ELEM_WIDTH / 2);
expect(h.state.scrollY).toBe(HEIGHT / 2 - ELEM_HEIGHT / 2);
});
});
});
+6 -1
View File
@@ -83,7 +83,7 @@ export type AppState = {
showShortcutsDialog: boolean; showShortcutsDialog: boolean;
zenModeEnabled: boolean; zenModeEnabled: boolean;
appearance: "light" | "dark"; appearance: "light" | "dark";
gridSize: number | null; showGrid: boolean;
/** top-most selected groups (i.e. does not include nested groups) */ /** top-most selected groups (i.e. does not include nested groups) */
selectedGroupIds: { [groupId: string]: boolean }; selectedGroupIds: { [groupId: string]: boolean };
@@ -166,6 +166,11 @@ export interface ExcalidrawProps {
button: "down" | "up"; button: "down" | "up";
pointersMap: Gesture["pointers"]; pointersMap: Gesture["pointers"];
}) => void; }) => void;
onExportToBackend?: (
exportedElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
canvas: HTMLCanvasElement | null,
) => void;
} }
export type SceneData = { export type SceneData = {
+11 -9
View File
@@ -7,6 +7,7 @@ import {
import { FontFamily, FontString } from "./element/types"; import { FontFamily, FontString } from "./element/types";
import { Zoom } from "./types"; import { Zoom } from "./types";
import { unstable_batchedUpdates } from "react-dom"; import { unstable_batchedUpdates } from "react-dom";
import { isDarwin } from "./keys";
export const SVG_NS = "http://www.w3.org/2000/svg"; export const SVG_NS = "http://www.w3.org/2000/svg";
@@ -179,15 +180,18 @@ export const allowFullScreen = () =>
export const exitFullScreen = () => document.exitFullscreen(); export const exitFullScreen = () => document.exitFullscreen();
export const getShortcutKey = (shortcut: string): string => { export const getShortcutKey = (shortcut: string): string => {
const isMac = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform); shortcut = shortcut
if (isMac) { .replace(/\bAlt\b/i, "Alt")
return `${shortcut .replace(/\bShift\b/i, "Shift")
.replace(/\b(Enter|Return)\b/i, "Enter")
.replace(/\bDel\b/i, "Delete");
if (isDarwin) {
return shortcut
.replace(/\bCtrlOrCmd\b/i, "Cmd") .replace(/\bCtrlOrCmd\b/i, "Cmd")
.replace(/\bAlt\b/i, "Option") .replace(/\bAlt\b/i, "Option");
.replace(/\bDel\b/i, "Delete")
.replace(/\b(Enter|Return)\b/i, "Enter")}`;
} }
return `${shortcut.replace(/\bCtrlOrCmd\b/i, "Ctrl")}`; return shortcut.replace(/\bCtrlOrCmd\b/i, "Ctrl");
}; };
export const viewportCoordsToSceneCoords = ( export const viewportCoordsToSceneCoords = (
@@ -310,8 +314,6 @@ export const isTransparent = (color: string) => {
); );
}; };
export const noop = () => ({});
export type ResolvablePromise<T> = Promise<T> & { export type ResolvablePromise<T> = Promise<T> & {
resolve: [T] extends [undefined] ? (value?: T) => void : (value: T) => void; resolve: [T] extends [undefined] ? (value?: T) => void : (value: T) => void;
reject: (error: Error) => void; reject: (error: Error) => void;
+1 -1
View File
@@ -14,7 +14,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react" "jsx": "react-jsx"
}, },
"include": ["src"] "include": ["src"]
} }