Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bc80ea3a3 | |||
| 7ead1848da | |||
| 7d00d2f9bb | |||
| 704986042d | |||
| da45a0ecbb | |||
| fde1579884 | |||
| e4a0254c47 | |||
| 5c7113cb72 | |||
| 1acd42a44c | |||
| bf2566d65e | |||
| 85e8e93d60 | |||
| 1e7dfa692b | |||
| 1eb8920bc3 | |||
| abacd22c3f | |||
| b05e0709b5 | |||
| 229aa84668 | |||
| 75148f6bac | |||
| 154654bb9f | |||
| dc2581a308 | |||
| 428752542d | |||
| 2712a06ab8 |
@@ -1,12 +0,0 @@
|
||||
# http://EditorConfig.org
|
||||
|
||||
# top-level EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,10 +1,6 @@
|
||||
name: Cancel previous runs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Lint
|
||||
|
||||
on: pull_request
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Tests
|
||||
|
||||
on: pull_request
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
||||
@@ -21,5 +21,12 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/([^.]+)",
|
||||
"destination": "/",
|
||||
"statusCode": 301
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+42
-58
@@ -2663,86 +2663,70 @@
|
||||
}
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.0.3.tgz",
|
||||
"integrity": "sha512-Ukxh83Twql4UmUgds9wPWllE62NG71cYvm5AM6daTojvM8wFR2jh7G6GiA0WYfgMb2fw6SlbevB2xb6RDG5DzQ==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.0.1.tgz",
|
||||
"integrity": "sha512-iP8Bqxj4Ye8CXA4ja77buPZfXsKiZYUgHFzBQxVMihTHA8ZZLgBMPLQI6uFfHuJJW+1/yLzOf8BhvF2zknAebg==",
|
||||
"requires": {
|
||||
"@sentry/core": "6.0.3",
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/utils": "6.0.3",
|
||||
"@sentry/core": "6.0.1",
|
||||
"@sentry/types": "6.0.1",
|
||||
"@sentry/utils": "6.0.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.0.3.tgz",
|
||||
"integrity": "sha512-UykB/4/98y2DkNvwTiL2ofFPuK3KDHc7rIRNsdj6dg6D+Cf7FRexgmWUUkZrpC/y+QBj0TPqkcFDcZAuQDa3Ag==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.0.1.tgz",
|
||||
"integrity": "sha512-EoxgodyClasI8PA4GyU8Cp88W3R5ebpiLsE7fCcBcOU0DOBRkO8GAZ5IzfCDtYDJ50c9npivum5Oyj2wf8CXYw==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.0.3",
|
||||
"@sentry/minimal": "6.0.3",
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/utils": "6.0.3",
|
||||
"@sentry/hub": "6.0.1",
|
||||
"@sentry/minimal": "6.0.1",
|
||||
"@sentry/types": "6.0.1",
|
||||
"@sentry/utils": "6.0.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.0.3.tgz",
|
||||
"integrity": "sha512-BfV32tE09rjTWM9W0kk8gzxUC2k1h57Z5dNWJ35na79+LguNNtCcI6fHlFQ3PkJca6ITYof9FI8iQHUfsHFZnw==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.0.1.tgz",
|
||||
"integrity": "sha512-pGckNdhKcr7qYVXgSgA/QVGArATcmQu54YFAR5xTnkWVHpAwNmh0fc4CJCc4JBwS/LXSU1Y0nYiLQduVfnv8Cg==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/utils": "6.0.3",
|
||||
"@sentry/types": "6.0.1",
|
||||
"@sentry/utils": "6.0.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/integrations": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.0.3.tgz",
|
||||
"integrity": "sha512-SE/rQ+ttfoC6FlHDibB4e9lV95j78YkjQ6PvYNUe+zGkGIretCJREqgaS+W3qTNYvOdbUViuiiqtdfyvW9nM2g==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.0.1.tgz",
|
||||
"integrity": "sha512-5HGwKW0otSVXSLAJ9ezqlux4AYdeX6ElzQgpm6roWEBXEWf/5OyD0n+M3+yHq4NdQXk2kkfL/0DCyNdy8zZX2Q==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/utils": "6.0.3",
|
||||
"localforage": "^1.8.1",
|
||||
"@sentry/types": "6.0.1",
|
||||
"@sentry/utils": "6.0.1",
|
||||
"localforage": "1.8.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/types": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.3.tgz",
|
||||
"integrity": "sha512-266aBQbk9AGedhG2dzXshWbn23LYLElXqlI74DLku48UrU2v7TGKdyik/8/nfOfquCoRSp0GFGYHbItwU124XQ=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.3.tgz",
|
||||
"integrity": "sha512-lvuBFvZHYs1zYwI8dkC8Z8ryb0aYnwPFUl1rbZiMwJpYI2Dgl1jpqqZWv9luux2rSRYOMid74uGedV708rvEgA==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.0.3",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.0.3.tgz",
|
||||
"integrity": "sha512-YsW+nw0SMyyb7UQdjZeKlZjxbGsJFpXNLh9iIp6fHKnoLTTv17YPm2ej9sOikDsQuVotaPg/xn/Qt5wySGHIxw==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.0.1.tgz",
|
||||
"integrity": "sha512-TQ/M5A+OsxtQJ8dzHwrclxKXpJNdQeM1PUoYhff4BvsOXJScvZb7+Yn0OUEQXEc9pSMNt62tnQy4ct80iAMTHw==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.0.3",
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/hub": "6.0.1",
|
||||
"@sentry/types": "6.0.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.3.tgz",
|
||||
"integrity": "sha512-266aBQbk9AGedhG2dzXshWbn23LYLElXqlI74DLku48UrU2v7TGKdyik/8/nfOfquCoRSp0GFGYHbItwU124XQ=="
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.1.tgz",
|
||||
"integrity": "sha512-cEoe19vtam75Tf6eWmaobfbeV8XwBdr5FJoSVTomzcSsEiP2FHGOEhlE7kVBigzeH5Lri0aibiW6BDi1hIqHdg=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.3.tgz",
|
||||
"integrity": "sha512-lvuBFvZHYs1zYwI8dkC8Z8ryb0aYnwPFUl1rbZiMwJpYI2Dgl1jpqqZWv9luux2rSRYOMid74uGedV708rvEgA==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.1.tgz",
|
||||
"integrity": "sha512-bjGuBYnG6fulZ8mLhPGBxttNu96DCN6d7Glw2sfLf4aurn1kjJ/58hP2c8dH0OqWO5e+rGYTsZ5Dr5kqVKNGTg==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.0.3",
|
||||
"@sentry/types": "6.0.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
@@ -11106,9 +11090,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.0.4",
|
||||
@@ -14262,9 +14246,9 @@
|
||||
}
|
||||
},
|
||||
"localforage": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz",
|
||||
"integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.8.1.tgz",
|
||||
"integrity": "sha512-azSSJJfc7h4bVpi0PGi+SmLQKJl2/8NErI+LhJsrORNikMZnhaQ7rv9fHj+ofwgSHrKRlsDCL/639a6nECIKuQ==",
|
||||
"requires": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"browserslist": {
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
],
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
@@ -11,16 +16,11 @@
|
||||
"not chrome < 70",
|
||||
"not and_uc < 13",
|
||||
"not samsung < 10"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/browser": "6.0.3",
|
||||
"@sentry/integrations": "6.0.3",
|
||||
"@sentry/browser": "6.0.1",
|
||||
"@sentry/integrations": "6.0.1",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@types/jest": "26.0.20",
|
||||
@@ -71,34 +71,34 @@
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"resetMocks": false,
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!(roughjs|points-on-curve|path-data-parser|points-on-path|browser-fs-access)/)"
|
||||
],
|
||||
"resetMocks": false
|
||||
]
|
||||
},
|
||||
"name": "excalidraw",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-node": "node ./scripts/build-node.js",
|
||||
"build:app:docker": "REACT_APP_DISABLE_SENTRY=true react-scripts build",
|
||||
"build:app": "REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build",
|
||||
"build:version": "node ./scripts/build-version.js",
|
||||
"build": "npm run build:app && npm run build:version",
|
||||
"build-node": "node ./scripts/build-node.js",
|
||||
"build:app": "REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build",
|
||||
"build:app:docker": "REACT_APP_DISABLE_SENTRY=true react-scripts build",
|
||||
"build:version": "node ./scripts/build-version.js",
|
||||
"eject": "react-scripts eject",
|
||||
"fix": "npm run fix:other && npm run fix:code",
|
||||
"fix:code": "npm run test:code -- --fix",
|
||||
"fix:other": "npm run prettier -- --write",
|
||||
"fix": "npm run fix:other && npm run fix:code",
|
||||
"locales-coverage": "node scripts/build-locales-coverage.js",
|
||||
"locales-coverage:description": "node scripts/locales-coverage-description.js",
|
||||
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
|
||||
"start": "react-scripts start",
|
||||
"test": "npm run test:app",
|
||||
"test:all": "npm run test:typecheck && npm run test:code && npm run test:other && npm run test:app -- --watchAll=false",
|
||||
"test:app": "react-scripts test --passWithNoTests",
|
||||
"test:code": "eslint --max-warnings=0 --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"test:debug": "react-scripts --inspect-brk test --runInBand --no-cache",
|
||||
"test:other": "npm run prettier -- --list-different",
|
||||
"test:typecheck": "tsc",
|
||||
"test:update": "npm run test:app -- --updateSnapshot --watchAll=false",
|
||||
"test": "npm run test:app"
|
||||
"test:update": "npm run test:app -- --updateSnapshot --watchAll=false"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
|
||||
<!-- Excalidraw version -->
|
||||
<meta name="version" content="{version}" />
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="FG_Virgil.woff2"
|
||||
|
||||
@@ -18,7 +18,6 @@ const crowdinMap = {
|
||||
"id-ID": "en-id",
|
||||
"it-IT": "en-it",
|
||||
"ja-JP": "en-ja",
|
||||
"kab-KAB": "en-kab",
|
||||
"ko-KR": "en-ko",
|
||||
"my-MM": "en-my",
|
||||
"nb-NO": "en-nb",
|
||||
@@ -41,7 +40,7 @@ const crowdinMap = {
|
||||
const flags = {
|
||||
"ar-SA": "🇸🇦",
|
||||
"bg-BG": "🇧🇬",
|
||||
"ca-ES": "🏳",
|
||||
"ca-ES": "🇪🇸",
|
||||
"de-DE": "🇩🇪",
|
||||
"el-GR": "🇬🇷",
|
||||
"es-ES": "🇪🇸",
|
||||
@@ -54,7 +53,6 @@ const flags = {
|
||||
"id-ID": "🇮🇩",
|
||||
"it-IT": "🇮🇹",
|
||||
"ja-JP": "🇯🇵",
|
||||
"kab-KAB": "🏳",
|
||||
"ko-KR": "🇰🇷",
|
||||
"my-MM": "🇲🇲",
|
||||
"nb-NO": "🇳🇴",
|
||||
@@ -90,7 +88,6 @@ const languages = {
|
||||
"id-ID": "Bahasa Indonesia",
|
||||
"it-IT": "Italiano",
|
||||
"ja-JP": "日本語",
|
||||
"kab-KAB": "Taqbaylit",
|
||||
"ko-KR": "한국어",
|
||||
"my-MM": "Burmese",
|
||||
"nb-NO": "Norsk bokmål",
|
||||
|
||||
@@ -2,12 +2,10 @@ import { CODES, KEYS } from "../keys";
|
||||
import { register } from "./register";
|
||||
import { GRID_SIZE } from "../constants";
|
||||
import { AppState } from "../types";
|
||||
import { trackEvent } from "../analytics";
|
||||
|
||||
export const actionToggleGridMode = register({
|
||||
name: "gridMode",
|
||||
perform(elements, appState) {
|
||||
trackEvent("view", "mode", "grid");
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { CODES, KEYS } from "../keys";
|
||||
import { register } from "./register";
|
||||
import { trackEvent } from "../analytics";
|
||||
|
||||
export const actionToggleViewMode = register({
|
||||
name: "viewMode",
|
||||
perform(elements, appState) {
|
||||
trackEvent("view", "mode", "view");
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
viewModeEnabled: !this.checked!(appState),
|
||||
selectedElementIds: {},
|
||||
},
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
checked: (appState) => appState.viewModeEnabled,
|
||||
contextItemLabel: "labels.viewMode",
|
||||
keyTest: (event) =>
|
||||
!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.R,
|
||||
});
|
||||
@@ -1,12 +1,9 @@
|
||||
import { CODES, KEYS } from "../keys";
|
||||
import { register } from "./register";
|
||||
import { trackEvent } from "../analytics";
|
||||
|
||||
export const actionToggleZenMode = register({
|
||||
name: "zenMode",
|
||||
perform(elements, appState) {
|
||||
trackEvent("view", "mode", "zen");
|
||||
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
ActionResult,
|
||||
} from "./types";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState, ExcalidrawProps } from "../types";
|
||||
import { AppState } from "../types";
|
||||
|
||||
// This is the <App> component, but for now we don't care about anything but its
|
||||
// `canvas` state.
|
||||
type App = { canvas: HTMLCanvasElement | null; props: ExcalidrawProps };
|
||||
type App = { canvas: HTMLCanvasElement | null };
|
||||
|
||||
export class ActionManager implements ActionsManagerInterface {
|
||||
actions = {} as ActionsManagerInterface["actions"];
|
||||
@@ -66,12 +66,6 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
if (data.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const { viewModeEnabled } = this.getAppState();
|
||||
if (viewModeEnabled) {
|
||||
if (data[0].name !== "viewMode") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
this.updater(
|
||||
|
||||
@@ -22,8 +22,7 @@ export type ShortcutName =
|
||||
| "gridMode"
|
||||
| "zenMode"
|
||||
| "stats"
|
||||
| "addToLibrary"
|
||||
| "viewMode";
|
||||
| "addToLibrary";
|
||||
|
||||
const shortcutMap: Record<ShortcutName, string[]> = {
|
||||
cut: [getShortcutKey("CtrlOrCmd+X")],
|
||||
@@ -57,7 +56,6 @@ const shortcutMap: Record<ShortcutName, string[]> = {
|
||||
zenMode: [getShortcutKey("Alt+Z")],
|
||||
stats: [],
|
||||
addToLibrary: [],
|
||||
viewMode: [getShortcutKey("Alt+R")],
|
||||
};
|
||||
|
||||
export const getShortcutFromShortcutName = (name: ShortcutName) => {
|
||||
|
||||
@@ -84,8 +84,7 @@ export type ActionName =
|
||||
| "alignVerticallyCentered"
|
||||
| "alignHorizontallyCentered"
|
||||
| "distributeHorizontally"
|
||||
| "distributeVertically"
|
||||
| "viewMode";
|
||||
| "distributeVertically";
|
||||
|
||||
export interface Action {
|
||||
name: ActionName;
|
||||
|
||||
@@ -72,7 +72,6 @@ export const getDefaultAppState = (): Omit<
|
||||
width: window.innerWidth,
|
||||
zenModeEnabled: false,
|
||||
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
||||
viewModeEnabled: false,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -152,7 +151,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
width: { browser: false, export: false },
|
||||
zenModeEnabled: { browser: true, export: false },
|
||||
zoom: { browser: true, export: false },
|
||||
viewModeEnabled: { browser: false, export: false },
|
||||
});
|
||||
|
||||
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
||||
|
||||
+62
-209
@@ -2,8 +2,6 @@ import { Point, simplify } from "points-on-curve";
|
||||
import React from "react";
|
||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
import rough from "roughjs/bin/rough";
|
||||
import clsx from "clsx";
|
||||
|
||||
import "../actions";
|
||||
import {
|
||||
actionAddToLibrary,
|
||||
@@ -177,11 +175,10 @@ import {
|
||||
withBatchedUpdates,
|
||||
} from "../utils";
|
||||
import { isMobile } from "../is-mobile";
|
||||
import ContextMenu, { ContextMenuOption } from "./ContextMenu";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
import LayerUI from "./LayerUI";
|
||||
import { Stats } from "./Stats";
|
||||
import { Toast } from "./Toast";
|
||||
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||
|
||||
const { history } = createHistory();
|
||||
|
||||
@@ -298,7 +295,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
offsetLeft,
|
||||
offsetTop,
|
||||
excalidrawRef,
|
||||
viewModeEnabled = false,
|
||||
} = props;
|
||||
this.state = {
|
||||
...defaultAppState,
|
||||
@@ -306,7 +302,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
width,
|
||||
height,
|
||||
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
||||
viewModeEnabled,
|
||||
};
|
||||
if (excalidrawRef) {
|
||||
const readyPromise =
|
||||
@@ -347,62 +342,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.actionManager.registerAction(createRedoAction(history));
|
||||
}
|
||||
|
||||
private renderCanvas() {
|
||||
const canvasScale = window.devicePixelRatio;
|
||||
const {
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
viewModeEnabled,
|
||||
} = this.state;
|
||||
const canvasWidth = canvasDOMWidth * canvasScale;
|
||||
const canvasHeight = canvasDOMHeight * canvasScale;
|
||||
if (viewModeEnabled) {
|
||||
return (
|
||||
<canvas
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
cursor: "grabbing",
|
||||
}}
|
||||
width={canvasWidth}
|
||||
height={canvasHeight}
|
||||
ref={this.handleCanvasRef}
|
||||
onContextMenu={this.handleCanvasContextMenu}
|
||||
onPointerMove={this.handleCanvasPointerMove}
|
||||
onPointerUp={this.removePointer}
|
||||
onPointerCancel={this.removePointer}
|
||||
onTouchMove={this.handleTouchMove}
|
||||
onPointerDown={this.handleCanvasPointerDown}
|
||||
>
|
||||
{t("labels.drawingCanvas")}
|
||||
</canvas>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<canvas
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
}}
|
||||
width={canvasWidth}
|
||||
height={canvasHeight}
|
||||
ref={this.handleCanvasRef}
|
||||
onContextMenu={this.handleCanvasContextMenu}
|
||||
onPointerDown={this.handleCanvasPointerDown}
|
||||
onDoubleClick={this.handleCanvasDoubleClick}
|
||||
onPointerMove={this.handleCanvasPointerMove}
|
||||
onPointerUp={this.removePointer}
|
||||
onPointerCancel={this.removePointer}
|
||||
onTouchMove={this.handleTouchMove}
|
||||
onDrop={this.handleCanvasOnDrop}
|
||||
>
|
||||
{t("labels.drawingCanvas")}
|
||||
</canvas>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
zenModeEnabled,
|
||||
@@ -410,19 +349,20 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
height: canvasDOMHeight,
|
||||
offsetTop,
|
||||
offsetLeft,
|
||||
viewModeEnabled,
|
||||
} = this.state;
|
||||
|
||||
const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
|
||||
const canvasScale = window.devicePixelRatio;
|
||||
|
||||
const canvasWidth = canvasDOMWidth * canvasScale;
|
||||
const canvasHeight = canvasDOMHeight * canvasScale;
|
||||
|
||||
const DEFAULT_PASTE_X = canvasDOMWidth / 2;
|
||||
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("excalidraw", {
|
||||
"excalidraw--view-mode": viewModeEnabled,
|
||||
})}
|
||||
className="excalidraw"
|
||||
ref={this.excalidrawContainerRef}
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
@@ -452,13 +392,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
isCollaborating={this.props.isCollaborating || false}
|
||||
onExportToBackend={onExportToBackend}
|
||||
renderCustomFooter={renderFooter}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
/>
|
||||
<div className="excalidraw-textEditorContainer" />
|
||||
{this.state.showStats && (
|
||||
<Stats
|
||||
appState={this.state}
|
||||
setAppState={this.setAppState}
|
||||
elements={this.scene.getElements()}
|
||||
onClose={this.toggleStats}
|
||||
/>
|
||||
@@ -469,7 +406,28 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
clearToast={this.clearToast}
|
||||
/>
|
||||
)}
|
||||
<main>{this.renderCanvas()}</main>
|
||||
<main>
|
||||
<canvas
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
}}
|
||||
width={canvasWidth}
|
||||
height={canvasHeight}
|
||||
ref={this.handleCanvasRef}
|
||||
onContextMenu={this.handleCanvasContextMenu}
|
||||
onPointerDown={this.handleCanvasPointerDown}
|
||||
onDoubleClick={this.handleCanvasDoubleClick}
|
||||
onPointerMove={this.handleCanvasPointerMove}
|
||||
onPointerUp={this.removePointer}
|
||||
onPointerCancel={this.removePointer}
|
||||
onTouchMove={this.handleTouchMove}
|
||||
onDrop={this.handleCanvasOnDrop}
|
||||
>
|
||||
{t("labels.drawingCanvas")}
|
||||
</canvas>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -509,13 +467,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
if (actionResult.commitToHistory) {
|
||||
history.resumeRecording();
|
||||
}
|
||||
|
||||
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||
|
||||
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||
viewModeEnabled = this.props.viewModeEnabled;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
(state) => ({
|
||||
...actionResult.appState,
|
||||
@@ -525,7 +476,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
height: state.height,
|
||||
offsetTop: state.offsetTop,
|
||||
offsetLeft: state.offsetLeft,
|
||||
viewModeEnabled,
|
||||
}),
|
||||
() => {
|
||||
if (actionResult.syncHistory) {
|
||||
@@ -708,6 +658,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
|
||||
this.scene.addCallback(this.onSceneUpdated);
|
||||
|
||||
this.addEventListeners();
|
||||
|
||||
// optim to avoid extra render on init
|
||||
@@ -774,16 +725,25 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
|
||||
private addEventListeners() {
|
||||
this.removeEventListeners();
|
||||
document.addEventListener(EVENT.COPY, this.onCopy);
|
||||
document.addEventListener(EVENT.PASTE, this.pasteFromClipboard);
|
||||
document.addEventListener(EVENT.CUT, this.onCut);
|
||||
|
||||
document.addEventListener(EVENT.KEYDOWN, this.onKeyDown, false);
|
||||
document.addEventListener(EVENT.KEYUP, this.onKeyUp, { passive: true });
|
||||
document.addEventListener(
|
||||
EVENT.MOUSE_MOVE,
|
||||
this.updateCurrentCursorPosition,
|
||||
);
|
||||
window.addEventListener(EVENT.RESIZE, this.onResize, false);
|
||||
window.addEventListener(EVENT.UNLOAD, this.onUnload, false);
|
||||
window.addEventListener(EVENT.BLUR, this.onBlur, false);
|
||||
window.addEventListener(EVENT.DRAG_OVER, this.disableEvent, false);
|
||||
window.addEventListener(EVENT.DROP, this.disableEvent, false);
|
||||
|
||||
// rerender text elements on font load to fix #637 && #1553
|
||||
document.fonts?.addEventListener?.("loadingdone", this.onFontLoaded);
|
||||
|
||||
// Safari-only desktop pinch zoom
|
||||
document.addEventListener(
|
||||
EVENT.GESTURE_START,
|
||||
@@ -800,18 +760,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.onGestureEnd as any,
|
||||
false,
|
||||
);
|
||||
if (this.state.viewModeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.addEventListener(EVENT.PASTE, this.pasteFromClipboard);
|
||||
document.addEventListener(EVENT.CUT, this.onCut);
|
||||
|
||||
window.addEventListener(EVENT.RESIZE, this.onResize, false);
|
||||
window.addEventListener(EVENT.UNLOAD, this.onUnload, false);
|
||||
window.addEventListener(EVENT.BLUR, this.onBlur, false);
|
||||
window.addEventListener(EVENT.DRAG_OVER, this.disableEvent, false);
|
||||
window.addEventListener(EVENT.DROP, this.disableEvent, false);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
||||
@@ -834,17 +782,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
});
|
||||
}
|
||||
|
||||
if (prevProps.viewModeEnabled !== this.props.viewModeEnabled) {
|
||||
this.setState(
|
||||
{ viewModeEnabled: !!this.props.viewModeEnabled },
|
||||
this.addEventListeners,
|
||||
);
|
||||
}
|
||||
|
||||
if (prevState.viewModeEnabled !== this.state.viewModeEnabled) {
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector(".excalidraw")
|
||||
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
||||
@@ -884,7 +821,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
const pointerViewportCoords: SceneState["remotePointerViewportCoords"] = {};
|
||||
const remoteSelectedElementIds: SceneState["remoteSelectedElementIds"] = {};
|
||||
const pointerUsernames: { [id: string]: string } = {};
|
||||
const pointerUserStates: { [id: string]: string } = {};
|
||||
this.state.collaborators.forEach((user, socketId) => {
|
||||
if (user.selectedElementIds) {
|
||||
for (const id of Object.keys(user.selectedElementIds)) {
|
||||
@@ -900,9 +836,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
if (user.username) {
|
||||
pointerUsernames[socketId] = user.username;
|
||||
}
|
||||
if (user.userState) {
|
||||
pointerUserStates[socketId] = user.userState;
|
||||
}
|
||||
pointerViewportCoords[socketId] = sceneCoordsToViewportCoords(
|
||||
{
|
||||
sceneX: user.pointer.x,
|
||||
@@ -937,7 +870,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
remotePointerButton: cursorButton,
|
||||
remoteSelectedElementIds,
|
||||
remotePointerUsernames: pointerUsernames,
|
||||
remotePointerUserStates: pointerUserStates,
|
||||
shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom,
|
||||
},
|
||||
{
|
||||
@@ -1202,6 +1134,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.actionManager.executeAction(actionToggleZenMode);
|
||||
};
|
||||
|
||||
toggleGridMode = () => {
|
||||
this.actionManager.executeAction(actionToggleGridMode);
|
||||
};
|
||||
|
||||
toggleStats = () => {
|
||||
if (!this.state.showStats) {
|
||||
trackEvent("dialog", "stats");
|
||||
@@ -1296,18 +1232,14 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.actionManager.handleKeyDown(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event[KEYS.CTRL_OR_CMD]) {
|
||||
this.setState({ isBindingEnabled: false });
|
||||
}
|
||||
|
||||
if (this.actionManager.handleKeyDown(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.code === CODES.NINE) {
|
||||
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
||||
}
|
||||
@@ -1830,11 +1762,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isPointerOverScrollBars = isOverScrollBars(
|
||||
currentScrollBars,
|
||||
event.clientX - this.state.offsetLeft,
|
||||
event.clientY - this.state.offsetTop,
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
);
|
||||
const isOverScrollBar = isPointerOverScrollBars.isOverEither;
|
||||
if (!this.state.draggingElement && !this.state.multiElement) {
|
||||
@@ -2115,16 +2046,14 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
|
||||
lastPointerUp = onPointerUp;
|
||||
|
||||
if (!this.state.viewModeEnabled) {
|
||||
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
||||
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
||||
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
||||
window.addEventListener(EVENT.KEYUP, onKeyUp);
|
||||
pointerDownState.eventListeners.onMove = onPointerMove;
|
||||
pointerDownState.eventListeners.onUp = onPointerUp;
|
||||
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
||||
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
||||
}
|
||||
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
||||
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
||||
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
||||
window.addEventListener(EVENT.KEYUP, onKeyUp);
|
||||
pointerDownState.eventListeners.onMove = onPointerMove;
|
||||
pointerDownState.eventListeners.onUp = onPointerUp;
|
||||
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
||||
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
||||
};
|
||||
|
||||
private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
|
||||
@@ -2174,8 +2103,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
!(
|
||||
gesture.pointers.size === 0 &&
|
||||
(event.button === POINTER_BUTTON.WHEEL ||
|
||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
|
||||
this.state.viewModeEnabled)
|
||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace))
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
@@ -2290,8 +2218,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
),
|
||||
scrollbars: isOverScrollBars(
|
||||
currentScrollBars,
|
||||
event.clientX - this.state.offsetLeft,
|
||||
event.clientY - this.state.offsetTop,
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
),
|
||||
// we need to duplicate because we'll be updating this state
|
||||
lastCoords: { ...origin },
|
||||
@@ -3453,40 +3381,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
};
|
||||
|
||||
private handleCanvasImageDrop = async (
|
||||
event: React.DragEvent<HTMLCanvasElement>,
|
||||
file: File,
|
||||
) => {
|
||||
try {
|
||||
const shapes = await (
|
||||
await import(
|
||||
/* webpackChunkName: "pixelated-image" */ "../data/pixelated-image"
|
||||
)
|
||||
).pixelateImage(file, 20, 1200, event.clientX, event.clientY);
|
||||
|
||||
const nextElements = [
|
||||
...this.scene.getElementsIncludingDeleted(),
|
||||
...shapes,
|
||||
];
|
||||
|
||||
this.scene.replaceAllElements(nextElements);
|
||||
} catch (error) {
|
||||
return this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private handleCanvasOnDrop = async (
|
||||
event: React.DragEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
let imageFile: File | null = null;
|
||||
try {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (file?.type.indexOf("image/") === 0) {
|
||||
imageFile = file;
|
||||
}
|
||||
if (file?.type === "image/png" || file?.type === "image/svg+xml") {
|
||||
const { elements, appState } = await loadFromBlob(file, this.state);
|
||||
this.syncActionResult({
|
||||
@@ -3498,13 +3397,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
commitToHistory: true,
|
||||
});
|
||||
return;
|
||||
} else if (imageFile) {
|
||||
return await this.handleCanvasImageDrop(event, imageFile);
|
||||
}
|
||||
} catch (error) {
|
||||
if (imageFile) {
|
||||
return await this.handleCanvasImageDrop(event, imageFile);
|
||||
}
|
||||
return this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: error.message,
|
||||
@@ -3696,36 +3590,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
|
||||
const elements = this.scene.getElements();
|
||||
const element = this.getElementAtPosition(x, y);
|
||||
const options: ContextMenuOption[] = [];
|
||||
if (probablySupportsClipboardBlob && elements.length > 0) {
|
||||
options.push(actionCopyAsPng);
|
||||
}
|
||||
|
||||
if (probablySupportsClipboardWriteText && elements.length > 0) {
|
||||
options.push(actionCopyAsSvg);
|
||||
}
|
||||
if (!element) {
|
||||
const viewModeOptions: ContextMenuOption[] = [
|
||||
...options,
|
||||
actionToggleStats,
|
||||
];
|
||||
|
||||
if (typeof this.props.viewModeEnabled === "undefined") {
|
||||
viewModeOptions.push(actionToggleViewMode);
|
||||
}
|
||||
|
||||
ContextMenu.push({
|
||||
options: viewModeOptions,
|
||||
top: clientY,
|
||||
left: clientX,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
});
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
_isMobile &&
|
||||
@@ -3753,8 +3618,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
separator,
|
||||
actionToggleGridMode,
|
||||
actionToggleZenMode,
|
||||
typeof this.props.viewModeEnabled === "undefined" &&
|
||||
actionToggleViewMode,
|
||||
actionToggleStats,
|
||||
],
|
||||
top: clientY,
|
||||
@@ -3769,17 +3632,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.setState({ selectedElementIds: { [element.id]: true } });
|
||||
}
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
ContextMenu.push({
|
||||
options: [navigator.clipboard && actionCopy, ...options],
|
||||
top: clientY,
|
||||
left: clientX,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
_isMobile && actionCut,
|
||||
@@ -3796,7 +3648,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
contextItemLabel: "labels.paste",
|
||||
},
|
||||
_isMobile && separator,
|
||||
...options,
|
||||
probablySupportsClipboardBlob && actionCopyAsPng,
|
||||
probablySupportsClipboardWriteText && actionCopyAsSvg,
|
||||
separator,
|
||||
actionCopyStyles,
|
||||
actionPasteStyles,
|
||||
|
||||
@@ -14,11 +14,11 @@ export const ButtonIconCycle = <T extends any>({
|
||||
}) => {
|
||||
const current = options.find((op) => op.value === value);
|
||||
|
||||
function cycle() {
|
||||
const cycle = () => {
|
||||
const index = options.indexOf(current!);
|
||||
const next = (index + 1) % options.length;
|
||||
onChange(options[next].value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<label key={group} className={clsx({ active: current!.value !== null })}>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.excalidraw {
|
||||
.CollabButton.is-collaborating {
|
||||
background-color: var(--button-special-active-background-color);
|
||||
background-color: var(--button-special-active-bg-color);
|
||||
|
||||
.ToolIcon__icon svg {
|
||||
color: var(--icon-green-fill-color);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
.excalidraw {
|
||||
.color-picker {
|
||||
background: var(--popup-background-color);
|
||||
border: 0px solid transparentize($oc-white, 0.75);
|
||||
box-shadow: transparentize($oc-black, 0.75) 0px 1px 4px;
|
||||
background: var(--popup-bg-color);
|
||||
border: 0 solid transparentize($oc-white, 0.75);
|
||||
box-shadow: transparentize($oc-black, 0.75) 0 1px 4px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
}
|
||||
|
||||
.color-picker-triangle {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 0px 9px 10px;
|
||||
border-color: transparent transparent var(--popup-background-color);
|
||||
border-width: 0 9px 10px;
|
||||
border-color: transparent transparent var(--popup-bg-color);
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
|
||||
@@ -84,12 +84,12 @@
|
||||
|
||||
.color-picker-transparent {
|
||||
border-radius: 4px;
|
||||
box-shadow: transparentize($oc-black, 0.9) 0px 0px 0px 1px inset;
|
||||
box-shadow: transparentize($oc-black, 0.9) 0 0 0 1px inset;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.color-picker-transparent,
|
||||
@@ -104,11 +104,11 @@
|
||||
width: 1.875rem;
|
||||
|
||||
:root[dir="ltr"] & {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
:root[dir="rtl"] & {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
color: var(--input-label-color);
|
||||
@@ -144,7 +144,7 @@
|
||||
}
|
||||
|
||||
.color-input-container:focus-within .color-picker-hash::after {
|
||||
background: var(--input-background-color);
|
||||
background: var(--input-bg-color);
|
||||
|
||||
:root[dir="ltr"] & {
|
||||
right: -2px;
|
||||
@@ -163,19 +163,19 @@
|
||||
width: 12ch; /* length of `transparent` + 1 */
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
background-color: var(--input-background-color);
|
||||
background-color: var(--input-bg-color);
|
||||
color: var(--text-color-primary);
|
||||
border: 0px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
height: 1.75em;
|
||||
box-shadow: var(--input-border-color) 0px 0px 0px 1px inset;
|
||||
box-shadow: var(--input-border-color) 0 0 0 1px inset;
|
||||
|
||||
:root[dir="ltr"] & {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
:root[dir="rtl"] & {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
float: left;
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
.context-menu {
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 3px 10px transparentize($oc-black, 0.8);
|
||||
box-shadow: 0 3px 10px transparentize($oc-black, 0.8);
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
margin: -0.25rem 0 0 0.125rem;
|
||||
padding: 0.5rem 0;
|
||||
background-color: var(--popup-secondary-background-color);
|
||||
background-color: var(--popup-secondary-bg-color);
|
||||
border: 1px solid var(--button-gray-3);
|
||||
cursor: default;
|
||||
}
|
||||
@@ -61,12 +61,12 @@
|
||||
}
|
||||
|
||||
.context-menu-option:hover {
|
||||
color: var(--popup-background-color);
|
||||
color: var(--popup-bg-color);
|
||||
background-color: var(--select-highlight-color);
|
||||
|
||||
&.dangerous {
|
||||
.context-menu-option__label {
|
||||
color: var(--popup-background-color);
|
||||
color: var(--popup-bg-color);
|
||||
}
|
||||
background-color: $oc-red-6;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Action } from "../actions/types";
|
||||
import { ActionManager } from "../actions/manager";
|
||||
import { AppState } from "../types";
|
||||
|
||||
export type ContextMenuOption = "separator" | Action;
|
||||
type ContextMenuOption = "separator" | Action;
|
||||
|
||||
type ContextMenuProps = {
|
||||
options: ContextMenuOption[];
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: calc(var(--space-factor) * 2);
|
||||
background: var(--bg-color-island);
|
||||
background: var(--island-bg-color);
|
||||
font-size: 1.25em;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -227,10 +227,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
label={t("labels.gridMode")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.viewMode")}
|
||||
shortcuts={[getShortcutKey("Alt+R")]}
|
||||
/>
|
||||
</ShortcutIsland>
|
||||
</Column>
|
||||
<Column>
|
||||
|
||||
@@ -26,7 +26,7 @@ $wide-viewport-width: 1000px;
|
||||
|
||||
> span {
|
||||
padding: 0.2rem 0.4rem;
|
||||
background-color: var(--overlay-background-color);
|
||||
background-color: var(--overlay-bg-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
}
|
||||
|
||||
.picker {
|
||||
background: var(--popup-background-color);
|
||||
border: 0px solid transparentize($oc-white, 0.75);
|
||||
box-shadow: transparentize($oc-black, 0.75) 0px 1px 4px;
|
||||
background: var(--popup-bg-color);
|
||||
border: 0 solid transparentize($oc-white, 0.75);
|
||||
box-shadow: transparentize($oc-black, 0.75) 0 1px 4px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
}
|
||||
@@ -56,8 +56,8 @@
|
||||
}
|
||||
|
||||
.picker-triangle {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
:root[dir="ltr"] & {
|
||||
@@ -73,7 +73,7 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 0px 9px 10px;
|
||||
border-width: 0 9px 10px;
|
||||
border-color: transparent transparent transparentize($oc-black, 0.9);
|
||||
top: -1px;
|
||||
}
|
||||
@@ -82,8 +82,8 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 0px 9px 10px;
|
||||
border-color: transparent transparent var(--popup-background-color);
|
||||
border-width: 0 9px 10px;
|
||||
border-color: transparent transparent var(--popup-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.excalidraw {
|
||||
.Island {
|
||||
--padding: 0;
|
||||
background-color: var(--bg-color-island);
|
||||
background-color: var(--island-bg-color);
|
||||
backdrop-filter: saturate(100%) blur(10px);
|
||||
box-shadow: var(--shadow-island);
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
}
|
||||
|
||||
.layer-ui__wrapper {
|
||||
z-index: var(--zIndex-layerUI);
|
||||
|
||||
.encrypted-icon {
|
||||
position: relative;
|
||||
margin-inline-start: 15px;
|
||||
@@ -73,7 +71,7 @@
|
||||
&__footer {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
bottom: 0px;
|
||||
bottom: 0;
|
||||
|
||||
:root[dir="ltr"] & {
|
||||
right: 0;
|
||||
@@ -94,7 +92,7 @@
|
||||
}
|
||||
|
||||
:root[dir="ltr"] &.transition-right {
|
||||
transform: translate(999px, 0px);
|
||||
transform: translate(999px, 0);
|
||||
}
|
||||
|
||||
:root[dir="rtl"] &.transition-left {
|
||||
|
||||
+44
-83
@@ -11,7 +11,7 @@ import { CLASSES } from "../constants";
|
||||
import { exportCanvas } from "../data";
|
||||
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
|
||||
import { Library } from "../data/library";
|
||||
import { isTextElement, showSelectedShapeActions } from "../element";
|
||||
import { showSelectedShapeActions } from "../element";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { Language, t } from "../i18n";
|
||||
import useIsMobile from "../is-mobile";
|
||||
@@ -61,7 +61,6 @@ interface LayerUIProps {
|
||||
canvas: HTMLCanvasElement | null,
|
||||
) => void;
|
||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||
viewModeEnabled: boolean;
|
||||
}
|
||||
|
||||
const useOnClickOutside = (
|
||||
@@ -300,7 +299,6 @@ const LayerUI = ({
|
||||
isCollaborating,
|
||||
onExportToBackend,
|
||||
renderCustomFooter,
|
||||
viewModeEnabled,
|
||||
}: LayerUIProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -360,28 +358,6 @@ const LayerUI = ({
|
||||
);
|
||||
};
|
||||
|
||||
const renderViewModeCanvasActions = () => {
|
||||
return (
|
||||
<Section
|
||||
heading="canvasActions"
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-left": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
{/* the zIndex ensures this menu has higher stacking order,
|
||||
see https://github.com/excalidraw/excalidraw/pull/1445 */}
|
||||
<Island padding={2} style={{ zIndex: 1 }}>
|
||||
<Stack.Col gap={4}>
|
||||
<Stack.Row gap={1} justifyContent="space-between">
|
||||
{actionManager.renderAction("saveScene")}
|
||||
{actionManager.renderAction("saveAsScene")}
|
||||
{renderExportDialog()}
|
||||
</Stack.Row>
|
||||
</Stack.Col>
|
||||
</Island>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
const renderCanvasActions = () => (
|
||||
<Section
|
||||
heading="canvasActions"
|
||||
@@ -472,42 +448,38 @@ const LayerUI = ({
|
||||
gap={4}
|
||||
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
||||
>
|
||||
{viewModeEnabled
|
||||
? renderViewModeCanvasActions()
|
||||
: renderCanvasActions()}
|
||||
{renderCanvasActions()}
|
||||
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
||||
</Stack.Col>
|
||||
{!viewModeEnabled && (
|
||||
<Section heading="shapes">
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="start">
|
||||
<Stack.Row gap={1}>
|
||||
<Island
|
||||
padding={1}
|
||||
className={clsx({ "zen-mode": zenModeEnabled })}
|
||||
>
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
{heading}
|
||||
<Stack.Row gap={1}>
|
||||
<ShapesSwitcher
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LockIcon
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
</Stack.Col>
|
||||
)}
|
||||
</Section>
|
||||
)}
|
||||
<Section heading="shapes">
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="start">
|
||||
<Stack.Row gap={1}>
|
||||
<Island
|
||||
padding={1}
|
||||
className={clsx({ "zen-mode": zenModeEnabled })}
|
||||
>
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
{heading}
|
||||
<Stack.Row gap={1}>
|
||||
<ShapesSwitcher
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LockIcon
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
</Stack.Col>
|
||||
)}
|
||||
</Section>
|
||||
<UserList
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-right": zenModeEnabled,
|
||||
@@ -552,20 +524,6 @@ const LayerUI = ({
|
||||
);
|
||||
};
|
||||
|
||||
const renderGitHubCorner = () => {
|
||||
return (
|
||||
<aside
|
||||
className={clsx(
|
||||
"layer-ui__wrapper__github-corner zen-mode-transition",
|
||||
{
|
||||
"transition-right": zenModeEnabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<GitHubCorner appearance={appState.appearance} />
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
const renderFooter = () => (
|
||||
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
||||
<div
|
||||
@@ -641,22 +599,25 @@ const LayerUI = ({
|
||||
canvas={canvas}
|
||||
isCollaborating={isCollaborating}
|
||||
renderCustomFooter={renderCustomFooter}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className={clsx("layer-ui__wrapper", {
|
||||
"disable-pointerEvents":
|
||||
appState.draggingElement ||
|
||||
appState.resizingElement ||
|
||||
(appState.editingElement && !isTextElement(appState.editingElement)),
|
||||
})}
|
||||
>
|
||||
<div className="layer-ui__wrapper">
|
||||
{dialogs}
|
||||
{renderFixedSideContainer()}
|
||||
{renderBottomAppMenu()}
|
||||
{renderGitHubCorner()}
|
||||
{
|
||||
<aside
|
||||
className={clsx(
|
||||
"layer-ui__wrapper__github-corner zen-mode-transition",
|
||||
{
|
||||
"transition-right": zenModeEnabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<GitHubCorner appearance={appState.appearance} />
|
||||
</aside>
|
||||
}
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
|
||||
+116
-160
@@ -29,7 +29,6 @@ type MobileMenuProps = {
|
||||
canvas: HTMLCanvasElement | null;
|
||||
isCollaborating: boolean;
|
||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||
viewModeEnabled: boolean;
|
||||
};
|
||||
|
||||
export const MobileMenu = ({
|
||||
@@ -44,164 +43,121 @@ export const MobileMenu = ({
|
||||
canvas,
|
||||
isCollaborating,
|
||||
renderCustomFooter,
|
||||
viewModeEnabled,
|
||||
}: MobileMenuProps) => {
|
||||
const renderToolbar = () => {
|
||||
return (
|
||||
<FixedSideContainer side="top" className="App-top-bar">
|
||||
<Section heading="shapes">
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="center">
|
||||
<Stack.Row gap={1}>
|
||||
<Island padding={1}>
|
||||
{heading}
|
||||
<Stack.Row gap={1}>
|
||||
<ShapesSwitcher
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LockIcon
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
</Stack.Col>
|
||||
)}
|
||||
</Section>
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
</FixedSideContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const renderAppToolbar = () => {
|
||||
if (viewModeEnabled) {
|
||||
return (
|
||||
<div className="App-toolbar-content">
|
||||
{actionManager.renderAction("toggleCanvasMenu")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="App-toolbar-content">
|
||||
{actionManager.renderAction("toggleCanvasMenu")}
|
||||
{actionManager.renderAction("toggleEditMenu")}
|
||||
{actionManager.renderAction("undo")}
|
||||
{actionManager.renderAction("redo")}
|
||||
{actionManager.renderAction(
|
||||
appState.multiElement ? "finalize" : "duplicateSelection",
|
||||
)}
|
||||
{actionManager.renderAction("deleteSelectedElements")}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderCanvasActions = () => {
|
||||
if (viewModeEnabled) {
|
||||
return (
|
||||
<>
|
||||
{actionManager.renderAction("saveScene")}
|
||||
{actionManager.renderAction("saveAsScene")}
|
||||
{exportButton}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{actionManager.renderAction("loadScene")}
|
||||
{actionManager.renderAction("saveScene")}
|
||||
{actionManager.renderAction("saveAsScene")}
|
||||
{exportButton}
|
||||
{actionManager.renderAction("clearCanvas")}
|
||||
{onCollabButtonClick && (
|
||||
<CollabButton
|
||||
isCollaborating={isCollaborating}
|
||||
collaboratorCount={appState.collaborators.size}
|
||||
onClick={onCollabButtonClick}
|
||||
/>
|
||||
)}
|
||||
{
|
||||
<BackgroundPickerAndDarkModeToggle
|
||||
actionManager={actionManager}
|
||||
appState={appState}
|
||||
setAppState={setAppState}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{!viewModeEnabled && renderToolbar()}
|
||||
<div
|
||||
className="App-bottom-bar"
|
||||
style={{
|
||||
marginBottom: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
marginLeft: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
marginRight: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
}}
|
||||
>
|
||||
<Island padding={0}>
|
||||
{appState.openMenu === "canvas" ? (
|
||||
<Section className="App-mobile-menu" heading="canvasActions">
|
||||
<div className="panelColumn">
|
||||
<Stack.Col gap={4}>
|
||||
{renderCanvasActions()}
|
||||
{renderCustomFooter?.(true)}
|
||||
<fieldset>
|
||||
<legend>{t("labels.collaborators")}</legend>
|
||||
<UserList mobile>
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(
|
||||
([_, client]) => Object.keys(client).length !== 0,
|
||||
)
|
||||
.map(([clientId, client]) => (
|
||||
<React.Fragment key={clientId}>
|
||||
{actionManager.renderAction(
|
||||
"goToCollaborator",
|
||||
clientId,
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</UserList>
|
||||
</fieldset>
|
||||
</Stack.Col>
|
||||
</div>
|
||||
</Section>
|
||||
) : appState.openMenu === "shape" &&
|
||||
!viewModeEnabled &&
|
||||
showSelectedShapeActions(appState, elements) ? (
|
||||
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
||||
<SelectedShapeActions
|
||||
appState={appState}
|
||||
elements={elements}
|
||||
renderAction={actionManager.renderAction}
|
||||
elementType={appState.elementType}
|
||||
}: MobileMenuProps) => (
|
||||
<>
|
||||
<FixedSideContainer side="top">
|
||||
<Section heading="shapes">
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="center">
|
||||
<Stack.Row gap={1}>
|
||||
<Island padding={1}>
|
||||
{heading}
|
||||
<Stack.Row gap={1}>
|
||||
<ShapesSwitcher
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LockIcon
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
</Section>
|
||||
) : null}
|
||||
<footer className="App-toolbar">
|
||||
{renderAppToolbar()}
|
||||
{appState.scrolledOutside && !appState.openMenu && (
|
||||
<button
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState({
|
||||
...calculateScrollCenter(elements, appState, canvas),
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("buttons.scrollBackToContent")}
|
||||
</button>
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
</Stack.Col>
|
||||
)}
|
||||
</Section>
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
</FixedSideContainer>
|
||||
<div
|
||||
className="App-bottom-bar"
|
||||
style={{
|
||||
marginBottom: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
marginLeft: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
marginRight: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
||||
}}
|
||||
>
|
||||
<Island padding={0}>
|
||||
{appState.openMenu === "canvas" ? (
|
||||
<Section className="App-mobile-menu" heading="canvasActions">
|
||||
<div className="panelColumn">
|
||||
<Stack.Col gap={4}>
|
||||
{actionManager.renderAction("loadScene")}
|
||||
{actionManager.renderAction("saveScene")}
|
||||
{actionManager.renderAction("saveAsScene")}
|
||||
{exportButton}
|
||||
{actionManager.renderAction("clearCanvas")}
|
||||
{onCollabButtonClick && (
|
||||
<CollabButton
|
||||
isCollaborating={isCollaborating}
|
||||
collaboratorCount={appState.collaborators.size}
|
||||
onClick={onCollabButtonClick}
|
||||
/>
|
||||
)}
|
||||
<BackgroundPickerAndDarkModeToggle
|
||||
actionManager={actionManager}
|
||||
appState={appState}
|
||||
setAppState={setAppState}
|
||||
/>
|
||||
{renderCustomFooter?.(true)}
|
||||
<fieldset>
|
||||
<legend>{t("labels.collaborators")}</legend>
|
||||
<UserList mobile>
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(([_, client]) => Object.keys(client).length !== 0)
|
||||
.map(([clientId, client]) => (
|
||||
<React.Fragment key={clientId}>
|
||||
{actionManager.renderAction(
|
||||
"goToCollaborator",
|
||||
clientId,
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</UserList>
|
||||
</fieldset>
|
||||
</Stack.Col>
|
||||
</div>
|
||||
</Section>
|
||||
) : appState.openMenu === "shape" &&
|
||||
showSelectedShapeActions(appState, elements) ? (
|
||||
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
||||
<SelectedShapeActions
|
||||
appState={appState}
|
||||
elements={elements}
|
||||
renderAction={actionManager.renderAction}
|
||||
elementType={appState.elementType}
|
||||
/>
|
||||
</Section>
|
||||
) : null}
|
||||
<footer className="App-toolbar">
|
||||
<div className="App-toolbar-content">
|
||||
{actionManager.renderAction("toggleCanvasMenu")}
|
||||
{actionManager.renderAction("toggleEditMenu")}
|
||||
{actionManager.renderAction("undo")}
|
||||
{actionManager.renderAction("redo")}
|
||||
{actionManager.renderAction(
|
||||
appState.multiElement ? "finalize" : "duplicateSelection",
|
||||
)}
|
||||
</footer>
|
||||
</Island>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
{actionManager.renderAction("deleteSelectedElements")}
|
||||
</div>
|
||||
{appState.scrolledOutside && !appState.openMenu && (
|
||||
<button
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState({
|
||||
...calculateScrollCenter(elements, appState, canvas),
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("buttons.scrollBackToContent")}
|
||||
</button>
|
||||
)}
|
||||
</footer>
|
||||
</Island>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
overflow-y: auto;
|
||||
|
||||
// for modals, reset blurry bg
|
||||
background: var(--bg-color-island);
|
||||
background: var(--island-bg-color);
|
||||
backdrop-filter: none;
|
||||
|
||||
border: 1px solid var(--dialog-border);
|
||||
|
||||
+42
-40
@@ -1,51 +1,53 @@
|
||||
@import "../css/variables.module";
|
||||
|
||||
.Stats {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
right: 12px;
|
||||
font-size: 12px;
|
||||
z-index: 999;
|
||||
.excalidraw {
|
||||
.Stats {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
right: 12px;
|
||||
font-size: 12px;
|
||||
z-index: 999;
|
||||
|
||||
h3 {
|
||||
margin: 0 24px 8px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
h3 {
|
||||
margin: 0 24px 8px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.close {
|
||||
float: right;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
.close {
|
||||
float: right;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
th {
|
||||
border-bottom: 1px solid var(--input-border-color);
|
||||
padding: 4px;
|
||||
}
|
||||
tr {
|
||||
td:nth-child(2) {
|
||||
min-width: 24px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
th {
|
||||
border-bottom: 1px solid var(--input-border-color);
|
||||
padding: 4px;
|
||||
}
|
||||
tr {
|
||||
td:nth-child(2) {
|
||||
min-width: 24px;
|
||||
text-align: right;
|
||||
:root[dir="rtl"] & {
|
||||
left: 12px;
|
||||
right: initial;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px 24px;
|
||||
}
|
||||
.close {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:root[dir="rtl"] & {
|
||||
left: 12px;
|
||||
right: initial;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px 24px;
|
||||
}
|
||||
.close {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { copyTextToSystemClipboard } from "../clipboard";
|
||||
import { DEFAULT_VERSION } from "../constants";
|
||||
import { getCommonBounds } from "../element/bounds";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import {
|
||||
@@ -11,7 +9,7 @@ import { t } from "../i18n";
|
||||
import useIsMobile from "../is-mobile";
|
||||
import { getTargetElements } from "../scene";
|
||||
import { AppState } from "../types";
|
||||
import { debounce, getVersion, nFormatter } from "../utils";
|
||||
import { debounce, nFormatter } from "../utils";
|
||||
import { close } from "./icons";
|
||||
import { Island } from "./Island";
|
||||
import "./Stats.scss";
|
||||
@@ -27,7 +25,6 @@ const getStorageSizes = debounce((cb: (sizes: StorageSizes) => void) => {
|
||||
|
||||
export const Stats = (props: {
|
||||
appState: AppState;
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
@@ -53,17 +50,6 @@ export const Stats = (props: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const version = getVersion();
|
||||
let hash;
|
||||
let timestamp;
|
||||
|
||||
if (version !== DEFAULT_VERSION) {
|
||||
timestamp = version.slice(0, 16).replace("T", " ");
|
||||
hash = version.slice(21);
|
||||
} else {
|
||||
timestamp = t("stats.versionNotAvailable");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Stats">
|
||||
<Island padding={2}>
|
||||
@@ -170,28 +156,6 @@ export const Stats = (props: {
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
<tr>
|
||||
<th colSpan={2}>{t("stats.version")}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
colSpan={2}
|
||||
style={{ textAlign: "center", cursor: "pointer" }}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await copyTextToSystemClipboard(getVersion());
|
||||
props.setAppState({
|
||||
toastMessage: t("toast.copyToClipboard"),
|
||||
});
|
||||
} catch {}
|
||||
}}
|
||||
title={t("stats.versionCopy")}
|
||||
>
|
||||
{timestamp}
|
||||
<br />
|
||||
{hash}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Island>
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
padding: 0.75rem;
|
||||
white-space: nowrap;
|
||||
border-radius: var(--space-factor);
|
||||
background-color: var(--input-background-color);
|
||||
background-color: var(--input-bg-color);
|
||||
|
||||
&:not(:focus) {
|
||||
&:hover {
|
||||
background-color: var(--input-hover-background-color);
|
||||
background-color: var(--input-hover-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.Tooltip:hover .Tooltip__label {
|
||||
// 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__label {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
+24
-30
@@ -2,51 +2,50 @@ import { FontFamily } from "./element/types";
|
||||
|
||||
export const APP_NAME = "Excalidraw";
|
||||
|
||||
export const DRAGGING_THRESHOLD = 10; // 10px
|
||||
export const LINE_CONFIRM_THRESHOLD = 10; // 10px
|
||||
export const DRAGGING_THRESHOLD = 10;
|
||||
export const LINE_CONFIRM_THRESHOLD = 10;
|
||||
export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
||||
export const ELEMENT_TRANSLATE_AMOUNT = 1;
|
||||
export const TEXT_TO_CENTER_SNAP_THRESHOLD = 30;
|
||||
export const SHIFT_LOCKING_ANGLE = Math.PI / 12;
|
||||
export const CURSOR_TYPE = {
|
||||
TEXT: "text",
|
||||
AUTO: "",
|
||||
CROSSHAIR: "crosshair",
|
||||
GRABBING: "grabbing",
|
||||
POINTER: "pointer",
|
||||
MOVE: "move",
|
||||
AUTO: "",
|
||||
POINTER: "pointer",
|
||||
TEXT: "text",
|
||||
};
|
||||
export const POINTER_BUTTON = {
|
||||
MAIN: 0,
|
||||
WHEEL: 1,
|
||||
SECONDARY: 2,
|
||||
TOUCH: -1,
|
||||
WHEEL: 1,
|
||||
};
|
||||
|
||||
export enum EVENT {
|
||||
BEFORE_UNLOAD = "beforeunload",
|
||||
BLUR = "blur",
|
||||
COPY = "copy",
|
||||
PASTE = "paste",
|
||||
CUT = "cut",
|
||||
DRAG_OVER = "dragover",
|
||||
DROP = "drop",
|
||||
GESTURE_CHANGE = "gesturechange",
|
||||
GESTURE_END = "gestureend",
|
||||
GESTURE_START = "gesturestart",
|
||||
HASHCHANGE = "hashchange",
|
||||
KEYDOWN = "keydown",
|
||||
KEYUP = "keyup",
|
||||
MOUSE_MOVE = "mousemove",
|
||||
RESIZE = "resize",
|
||||
UNLOAD = "unload",
|
||||
BLUR = "blur",
|
||||
DRAG_OVER = "dragover",
|
||||
DROP = "drop",
|
||||
GESTURE_END = "gestureend",
|
||||
BEFORE_UNLOAD = "beforeunload",
|
||||
GESTURE_START = "gesturestart",
|
||||
GESTURE_CHANGE = "gesturechange",
|
||||
PASTE = "paste",
|
||||
POINTER_MOVE = "pointermove",
|
||||
POINTER_UP = "pointerup",
|
||||
RESIZE = "resize",
|
||||
STATE_CHANGE = "statechange",
|
||||
WHEEL = "wheel",
|
||||
TOUCH_START = "touchstart",
|
||||
TOUCH_END = "touchend",
|
||||
HASHCHANGE = "hashchange",
|
||||
VISIBILITY_CHANGE = "visibilitychange",
|
||||
TOUCH_START = "touchstart",
|
||||
UNLOAD = "unload",
|
||||
WHEEL = "wheel",
|
||||
}
|
||||
|
||||
export const ENV = {
|
||||
@@ -67,11 +66,11 @@ export const FONT_FAMILY = {
|
||||
|
||||
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
|
||||
|
||||
export const DEFAULT_FONT_SIZE = 20;
|
||||
export const DEFAULT_FONT_FAMILY: FontFamily = 1;
|
||||
export const DEFAULT_FONT_SIZE = 20;
|
||||
export const DEFAULT_TEXT_ALIGN = "left";
|
||||
export const DEFAULT_VERTICAL_ALIGN = "top";
|
||||
export const DEFAULT_VERSION = "{version}";
|
||||
export const DEFAULT_VERTICAL_ALIGN = "top";
|
||||
|
||||
export const CANVAS_ONLY_ACTIONS = ["selectAll"];
|
||||
|
||||
@@ -86,16 +85,11 @@ export const STORAGE_KEYS = {
|
||||
LOCAL_STORAGE_LIBRARY: "excalidraw-library",
|
||||
};
|
||||
|
||||
// time in milliseconds
|
||||
// Time in milliseconds
|
||||
export const TAP_TWICE_TIMEOUT = 300;
|
||||
export const TOUCH_CTX_MENU_TIMEOUT = 500;
|
||||
export const TITLE_TIMEOUT = 10000;
|
||||
export const TOAST_TIMEOUT = 5000;
|
||||
export const VERSION_TIMEOUT = 30000;
|
||||
export const TOUCH_CTX_MENU_TIMEOUT = 500;
|
||||
export const VERSION_TIMEOUT = 15000;
|
||||
|
||||
export const ZOOM_STEP = 0.1;
|
||||
|
||||
// Report a user inactive after IDLE_THRESHOLD milliseconds
|
||||
export const IDLE_THRESHOLD = 60_000;
|
||||
// Report a user active each ACTIVE_THRESHOLD milliseconds
|
||||
export const ACTIVE_THRESHOLD = 3_000;
|
||||
|
||||
+12
-33
@@ -1,12 +1,6 @@
|
||||
@import "./variables.module";
|
||||
@import "./theme";
|
||||
|
||||
:root {
|
||||
--zIndex-canvas: 1;
|
||||
--zIndex-wysiwyg: 2;
|
||||
--zIndex-layerUI: 3;
|
||||
}
|
||||
|
||||
.excalidraw {
|
||||
color: var(--text-color-primary);
|
||||
display: flex;
|
||||
@@ -36,8 +30,6 @@
|
||||
image-rendering: pixelated; // chromium
|
||||
// NOTE: must be declared *after* the above
|
||||
image-rendering: -moz-crisp-edges; // FF
|
||||
|
||||
z-index: var(--zIndex-canvas);
|
||||
}
|
||||
|
||||
&.Appearance_dark {
|
||||
@@ -51,10 +43,10 @@
|
||||
}
|
||||
|
||||
.FixedSideContainer {
|
||||
padding-top: var(--sat, 0px);
|
||||
padding-right: var(--sar, 0px);
|
||||
padding-bottom: var(--sab, 0px);
|
||||
padding-left: var(--sal, 0px);
|
||||
padding-top: var(--sat, 0);
|
||||
padding-right: var(--sar, 0);
|
||||
padding-bottom: var(--sab, 0);
|
||||
padding-left: var(--sal, 0);
|
||||
}
|
||||
|
||||
.panelRow {
|
||||
@@ -224,12 +216,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.App-top-bar {
|
||||
z-index: var(--zIndex-layerUI);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.App-bottom-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -237,10 +223,10 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
--bar-padding: calc(4 * var(--space-factor));
|
||||
padding-top: #{"max(var(--bar-padding), var(--sat, 0px))"};
|
||||
padding-right: var(--sar, 0px);
|
||||
padding-bottom: var(--sab, 0px);
|
||||
padding-left: var(--sal, 0px);
|
||||
padding-top: #{"max(var(--bar-padding), var(--sat, 0))"};
|
||||
padding-right: var(--sar, 0);
|
||||
padding-bottom: var(--sab, 0);
|
||||
padding-left: var(--sal, 0);
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@@ -257,7 +243,7 @@
|
||||
pointer-events: initial;
|
||||
|
||||
.panelColumn {
|
||||
padding: 8px 8px 0px 8px;
|
||||
padding: 8px 8px 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +282,7 @@
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top > * {
|
||||
.App-menu_top > * {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@@ -337,7 +323,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_bottom > * {
|
||||
.App-menu_bottom > * {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@@ -461,7 +447,7 @@
|
||||
display: none;
|
||||
}
|
||||
.scroll-back-to-content {
|
||||
bottom: calc(80px + var(--sab, 0px));
|
||||
bottom: calc(80px + var(--sab, 0));
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
@@ -506,13 +492,6 @@
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
&.excalidraw--view-mode {
|
||||
.App-menu {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.App-bottom-bar,
|
||||
.FixedSideContainer,
|
||||
|
||||
+40
-42
@@ -1,43 +1,43 @@
|
||||
@import "open-color/open-color.scss";
|
||||
|
||||
:root {
|
||||
--bg-color-island: rgba(255, 255, 255, 0.9);
|
||||
--popup-background-color: #{$oc-white};
|
||||
--space-factor: 0.25rem;
|
||||
--appearance-filter: none;
|
||||
--button-destructive-bg-color: #{$oc-red-1};
|
||||
--button-destructive-color: #{$oc-red-9};
|
||||
--button-gray-1: #{$oc-gray-2};
|
||||
--button-gray-2: #{$oc-gray-4};
|
||||
--button-gray-3: #{$oc-gray-5};
|
||||
--input-border-color: #{$oc-gray-3};
|
||||
--input-background-color: #{$oc-white};
|
||||
--input-hover-background-color: #{$oc-gray-1};
|
||||
--input-label-color: #{$oc-gray-7};
|
||||
--button-special-active-bg-color: #{$oc-green-0};
|
||||
--dialog-border: #{$oc-gray-6};
|
||||
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
||||
--focus-highlight-color: #{$oc-blue-2};
|
||||
--icon-fill-color: #{$oc-black};
|
||||
--icon-green-fill-color: #{$oc-green-9};
|
||||
--input-bg-color: #{$oc-white};
|
||||
--input-border-color: #{$oc-gray-3};
|
||||
--input-hover-bg-color: #{$oc-gray-1};
|
||||
--input-label-color: #{$oc-gray-7};
|
||||
--island-bg-color: #{transparentize($oc-white, 0.12)};
|
||||
--keybinding-color: #{$oc-gray-5};
|
||||
--sat: env(safe-area-inset-top);
|
||||
--link-color: #{$oc-blue-7};
|
||||
--overlay-bg-color: #{transparentize($oc-white, 0.12)};
|
||||
--popup-bg-color: #{$oc-white};
|
||||
--popup-secondary-bg-color: #{$oc-gray-1};
|
||||
--popup-text-color: #{$oc-black};
|
||||
--popup-text-inverted-color: #{$oc-white};
|
||||
--sab: env(safe-area-inset-bottom);
|
||||
--sal: env(safe-area-inset-left);
|
||||
--sar: env(safe-area-inset-right);
|
||||
--text-color-primary: #{$oc-gray-8};
|
||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)};
|
||||
--overlay-background-color: #{transparentize($oc-white, 0.12)};
|
||||
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
||||
--focus-highlight-color: #{$oc-blue-2};
|
||||
--sat: env(safe-area-inset-top);
|
||||
--select-highlight-color: #{$oc-blue-5};
|
||||
--appearance-filter: none;
|
||||
--button-special-active-background-color: #{$oc-green-0};
|
||||
--button-destructive-color: #{$oc-red-9};
|
||||
--button-destructive-background-color: #{$oc-red-1};
|
||||
--popup-secondary-background-color: #{$oc-gray-1};
|
||||
--popup-text-color: #{$oc-black};
|
||||
--popup-text-inverted-color: #{$oc-white};
|
||||
--dialog-border: #{$oc-gray-6};
|
||||
--link-color: #{$oc-blue-7};
|
||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)};
|
||||
--space-factor: 0.25rem;
|
||||
--text-color-primary: #{$oc-gray-8};
|
||||
}
|
||||
|
||||
.excalidraw {
|
||||
&.Appearance_dark {
|
||||
background: #000;
|
||||
background: $oc-black;
|
||||
|
||||
&.Appearance_dark-background-none {
|
||||
background: none;
|
||||
@@ -45,31 +45,29 @@
|
||||
}
|
||||
|
||||
&.Appearance_dark {
|
||||
--text-color-primary: #{$oc-gray-4};
|
||||
--bg-color-island: #1e1e1e;
|
||||
--popup-background-color: #2c2c2c;
|
||||
--appearance-filter: invert(93%) hue-rotate(180deg);
|
||||
--button-destructive-bg-color: #5a0000;
|
||||
--button-destructive-color: #{$oc-red-3};
|
||||
--button-gray-1: #363636;
|
||||
--button-gray-2: #272727;
|
||||
--button-gray-3: #222;
|
||||
--input-border-color: #2e2e2e;
|
||||
--input-background-color: #121212;
|
||||
--input-hover-background-color: #181818;
|
||||
--input-label-color: #{$oc-gray-2};
|
||||
--icon-fill-color: #{$oc-gray-4};
|
||||
--icon-green-fill-color: #{$oc-green-4};
|
||||
--keybinding-color: #{$oc-gray-6};
|
||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)};
|
||||
--overlay-background-color: rgba(30, 30, 30, 0.88);
|
||||
--button-special-active-bg-color: #204624;
|
||||
--dialog-border: #{$oc-gray-9};
|
||||
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
||||
--focus-highlight-color: #{$oc-blue-6};
|
||||
--select-highlight-color: #{$oc-blue-4};
|
||||
--appearance-filter: invert(93%) hue-rotate(180deg);
|
||||
--button-special-active-background-color: #204624;
|
||||
--button-destructive-color: #{$oc-red-3};
|
||||
--button-destructive-background-color: #5a0000;
|
||||
--popup-secondary-background-color: #222;
|
||||
--icon-fill-color: #{$oc-gray-4};
|
||||
--icon-green-fill-color: #{$oc-green-4};
|
||||
--input-bg-color: #121212;
|
||||
--input-border-color: #2e2e2e;
|
||||
--input-hover-bg-color: #181818;
|
||||
--input-label-color: #{$oc-gray-2};
|
||||
--island-bg-color: #1e1e1e;
|
||||
--keybinding-color: #{$oc-gray-6};
|
||||
--overlay-bg-color: rgba(30, 30, 30, 0.88);
|
||||
--popup-secondary-bg-color: #222;
|
||||
--popup-text-color: #{$oc-gray-4};
|
||||
--popup-text-inverted-color: #2c2c2c;
|
||||
--dialog-border: #{$oc-gray-9};
|
||||
--select-highlight-color: #{$oc-blue-4};
|
||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import "open-color/open-color.scss";
|
||||
|
||||
// keep up to date with is-mobile.tsx
|
||||
// Keep up to date with is-mobile.tsx
|
||||
$is-mobile-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)";
|
||||
|
||||
:export {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import { ExcalidrawGenericElement, NonDeleted } from "../element/types";
|
||||
import { newElement } from "../element";
|
||||
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
|
||||
import { randomId } from "../random";
|
||||
|
||||
const loadImage = async (url: string): Promise<HTMLImageElement> => {
|
||||
const image = new Image();
|
||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
image.onload = () => resolve(image);
|
||||
image.onerror = (err) =>
|
||||
reject(
|
||||
new Error(
|
||||
`Failed to load image: ${err ? err.toString : "unknown error"}`,
|
||||
),
|
||||
);
|
||||
image.onabort = () =>
|
||||
reject(new Error(`Failed to load image: image load aborted`));
|
||||
image.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
const commonProps = {
|
||||
fillStyle: "solid",
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
opacity: 100,
|
||||
roughness: 1,
|
||||
strokeColor: "transparent",
|
||||
strokeSharpness: "sharp",
|
||||
strokeStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
verticalAlign: "middle",
|
||||
} as const;
|
||||
|
||||
export const pixelateImage = async (
|
||||
blob: Blob,
|
||||
cellSize: number,
|
||||
suggestedMaxShapeCount: number,
|
||||
x: number,
|
||||
y: number,
|
||||
) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
try {
|
||||
const image = await loadImage(url);
|
||||
|
||||
// initialize canvas for pixelation
|
||||
const { width, height } = image;
|
||||
let canvasWidth = Math.floor(width / cellSize);
|
||||
let canvasHeight = Math.floor(height / cellSize);
|
||||
const shapeCount = canvasHeight * canvasWidth;
|
||||
if (shapeCount > suggestedMaxShapeCount) {
|
||||
canvasWidth = Math.floor(
|
||||
canvasWidth * (suggestedMaxShapeCount / shapeCount),
|
||||
);
|
||||
canvasHeight = Math.floor(
|
||||
canvasHeight * (suggestedMaxShapeCount / shapeCount),
|
||||
);
|
||||
}
|
||||
const xOffset = x - (canvasWidth * cellSize) / 2;
|
||||
const yOffset = y - (canvasHeight * cellSize) / 2;
|
||||
|
||||
const canvas =
|
||||
"OffscreenCanvas" in window
|
||||
? new OffscreenCanvas(canvasWidth, canvasHeight)
|
||||
: document.createElement("canvas");
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
|
||||
// Draw image on canvas
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.drawImage(image, 0, 0, width, height, 0, 0, canvasWidth, canvasHeight);
|
||||
const imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
|
||||
const buffer = imageData.data;
|
||||
|
||||
const groupId = randomId();
|
||||
const shapes: NonDeleted<ExcalidrawGenericElement>[] = [];
|
||||
|
||||
for (let row = 0; row < canvasHeight; row++) {
|
||||
for (let col = 0; col < canvasWidth; col++) {
|
||||
const offset = row * canvasWidth * 4 + col * 4;
|
||||
const r = buffer[offset];
|
||||
const g = buffer[offset + 1];
|
||||
const b = buffer[offset + 2];
|
||||
const alpha = buffer[offset + 3];
|
||||
if (alpha) {
|
||||
const color = `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
const rectangle = newElement({
|
||||
backgroundColor: color,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
x: xOffset + col * cellSize,
|
||||
y: yOffset + row * cellSize,
|
||||
width: cellSize,
|
||||
height: cellSize,
|
||||
});
|
||||
shapes.push(rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shapes;
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
@@ -7,8 +7,7 @@ export const showSelectedShapeActions = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
) =>
|
||||
Boolean(
|
||||
!appState.viewModeEnabled &&
|
||||
(appState.editingElement ||
|
||||
getSelectedElements(elements, appState).length ||
|
||||
appState.elementType !== "selection"),
|
||||
appState.editingElement ||
|
||||
getSelectedElements(elements, appState).length ||
|
||||
appState.elementType !== "selection",
|
||||
);
|
||||
|
||||
@@ -89,6 +89,9 @@ export const textWysiwyg = ({
|
||||
editable.dataset.type = "wysiwyg";
|
||||
// prevent line wrapping on Safari
|
||||
editable.wrap = "off";
|
||||
editable.className = `excalidraw ${
|
||||
appState.appearance === "dark" ? "Appearance_dark" : ""
|
||||
}`;
|
||||
|
||||
Object.assign(editable.style, {
|
||||
position: "fixed",
|
||||
@@ -104,8 +107,6 @@ export const textWysiwyg = ({
|
||||
overflow: "hidden",
|
||||
// prevent line wrapping (`whitespace: nowrap` doesn't work on FF)
|
||||
whiteSpace: "pre",
|
||||
// must be specified because in dark mode canvas creates a stacking context
|
||||
zIndex: "var(--zIndex-wysiwyg)",
|
||||
});
|
||||
|
||||
updateWysiwygStyle();
|
||||
@@ -159,7 +160,7 @@ export const textWysiwyg = ({
|
||||
|
||||
unbindUpdate();
|
||||
|
||||
editable.remove();
|
||||
document.body.removeChild(editable);
|
||||
};
|
||||
|
||||
const rebindBlur = () => {
|
||||
@@ -205,9 +206,7 @@ export const textWysiwyg = ({
|
||||
passive: false,
|
||||
capture: true,
|
||||
});
|
||||
document
|
||||
.querySelector(".excalidraw-textEditorContainer")!
|
||||
.appendChild(editable);
|
||||
document.body.appendChild(editable);
|
||||
editable.focus();
|
||||
editable.select();
|
||||
};
|
||||
|
||||
@@ -19,16 +19,12 @@ import {
|
||||
} from "../app_constants";
|
||||
import {
|
||||
decryptAESGEM,
|
||||
generateCollaborationLinkData,
|
||||
getCollaborationLink,
|
||||
generateCollaborationLink,
|
||||
getCollaborationLinkData,
|
||||
SocketUpdateDataSource,
|
||||
SOCKET_SERVER,
|
||||
} from "../data";
|
||||
import {
|
||||
isSavedToFirebase,
|
||||
loadFromFirebase,
|
||||
saveToFirebase,
|
||||
} from "../data/firebase";
|
||||
import { isSavedToFirebase, saveToFirebase } from "../data/firebase";
|
||||
import {
|
||||
importUsernameFromLocalStorage,
|
||||
saveUsernameToLocalStorage,
|
||||
@@ -37,25 +33,20 @@ import {
|
||||
import Portal from "./Portal";
|
||||
import RoomDialog from "./RoomDialog";
|
||||
import { createInverseContext } from "../../createInverseContext";
|
||||
import { t } from "../../i18n";
|
||||
import { UserIdleState } from "./types";
|
||||
import { IDLE_THRESHOLD, ACTIVE_THRESHOLD } from "../../constants";
|
||||
|
||||
interface CollabState {
|
||||
isCollaborating: boolean;
|
||||
modalIsShown: boolean;
|
||||
errorMessage: string;
|
||||
username: string;
|
||||
userState: UserIdleState;
|
||||
activeRoomLink: string;
|
||||
}
|
||||
|
||||
type CollabInstance = InstanceType<typeof CollabWrapper>;
|
||||
|
||||
export interface CollabAPI {
|
||||
/** function so that we can access the latest value from stale callbacks */
|
||||
isCollaborating: () => boolean;
|
||||
isCollaborating: CollabState["isCollaborating"];
|
||||
username: CollabState["username"];
|
||||
userState: CollabState["userState"];
|
||||
onPointerUpdate: CollabInstance["onPointerUpdate"];
|
||||
initializeSocketClient: CollabInstance["initializeSocketClient"];
|
||||
onCollabButtonClick: CollabInstance["onCollabButtonClick"];
|
||||
@@ -81,10 +72,6 @@ export { CollabContext, CollabContextConsumer };
|
||||
class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
portal: Portal;
|
||||
excalidrawAPI: Props["excalidrawAPI"];
|
||||
isCollaborating: boolean = false;
|
||||
activeIntervalId: number | null;
|
||||
idleTimeoutId: number | null;
|
||||
|
||||
private socketInitializationTimer?: NodeJS.Timeout;
|
||||
private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
||||
private collaborators = new Map<string, Collaborator>();
|
||||
@@ -92,16 +79,14 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isCollaborating: false,
|
||||
modalIsShown: false,
|
||||
errorMessage: "",
|
||||
username: importUsernameFromLocalStorage() || "",
|
||||
userState: UserIdleState.ACTIVE,
|
||||
activeRoomLink: "",
|
||||
};
|
||||
this.portal = new Portal(this);
|
||||
this.excalidrawAPI = props.excalidrawAPI;
|
||||
this.activeIntervalId = null;
|
||||
this.idleTimeoutId = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -125,32 +110,18 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload);
|
||||
window.removeEventListener(EVENT.UNLOAD, this.onUnload);
|
||||
window.removeEventListener(EVENT.POINTER_MOVE, this.onPointerMove);
|
||||
window.removeEventListener(
|
||||
EVENT.VISIBILITY_CHANGE,
|
||||
this.onVisibilityChange,
|
||||
);
|
||||
if (this.activeIntervalId) {
|
||||
window.clearInterval(this.activeIntervalId);
|
||||
this.activeIntervalId = null;
|
||||
}
|
||||
if (this.idleTimeoutId) {
|
||||
window.clearTimeout(this.idleTimeoutId);
|
||||
this.idleTimeoutId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private onUnload = () => {
|
||||
this.destroySocketClient({ isUnload: true });
|
||||
this.destroySocketClient();
|
||||
};
|
||||
|
||||
private beforeUnload = withBatchedUpdates((event: BeforeUnloadEvent) => {
|
||||
const syncableElements = getSyncableElements(
|
||||
this.getSceneElementsIncludingDeleted(),
|
||||
);
|
||||
|
||||
if (
|
||||
this.isCollaborating &&
|
||||
this.state.isCollaborating &&
|
||||
!isSavedToFirebase(this.portal, syncableElements)
|
||||
) {
|
||||
// this won't run in time if user decides to leave the site, but
|
||||
@@ -162,7 +133,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
event.returnValue = "";
|
||||
}
|
||||
|
||||
if (this.isCollaborating || this.portal.roomId) {
|
||||
if (this.state.isCollaborating || this.portal.roomId) {
|
||||
try {
|
||||
localStorage?.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
||||
@@ -188,188 +159,143 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
};
|
||||
|
||||
openPortal = async () => {
|
||||
return this.initializeSocketClient(null);
|
||||
window.history.pushState({}, APP_NAME, await generateCollaborationLink());
|
||||
const elements = this.excalidrawAPI.getSceneElements();
|
||||
// remove deleted elements from elements array & history to ensure we don't
|
||||
// expose potentially sensitive user data in case user manually deletes
|
||||
// existing elements (or clears scene), which would otherwise be persisted
|
||||
// to database even if deleted before creating the room.
|
||||
this.excalidrawAPI.history.clear();
|
||||
this.excalidrawAPI.updateScene({
|
||||
elements,
|
||||
commitToHistory: true,
|
||||
});
|
||||
return this.initializeSocketClient();
|
||||
};
|
||||
|
||||
closePortal = () => {
|
||||
this.saveCollabRoomToFirebase();
|
||||
if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
|
||||
window.history.pushState({}, APP_NAME, window.location.origin);
|
||||
this.destroySocketClient();
|
||||
}
|
||||
window.history.pushState({}, APP_NAME, window.location.origin);
|
||||
this.destroySocketClient();
|
||||
};
|
||||
|
||||
private destroySocketClient = (opts?: { isUnload: boolean }) => {
|
||||
if (!opts?.isUnload) {
|
||||
this.collaborators = new Map();
|
||||
this.excalidrawAPI.updateScene({
|
||||
collaborators: this.collaborators,
|
||||
});
|
||||
this.setState({
|
||||
activeRoomLink: "",
|
||||
});
|
||||
this.isCollaborating = false;
|
||||
}
|
||||
private destroySocketClient = () => {
|
||||
this.collaborators = new Map();
|
||||
this.excalidrawAPI.updateScene({
|
||||
collaborators: this.collaborators,
|
||||
});
|
||||
this.setState({
|
||||
isCollaborating: false,
|
||||
activeRoomLink: "",
|
||||
});
|
||||
this.portal.close();
|
||||
};
|
||||
|
||||
private initializeSocketClient = async (
|
||||
existingRoomLinkData: null | { roomId: string; roomKey: string },
|
||||
): Promise<ImportedDataState | null> => {
|
||||
private initializeSocketClient = async (): Promise<ImportedDataState | null> => {
|
||||
if (this.portal.socket) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let roomId;
|
||||
let roomKey;
|
||||
|
||||
if (existingRoomLinkData) {
|
||||
({ roomId, roomKey } = existingRoomLinkData);
|
||||
} else {
|
||||
({ roomId, roomKey } = await generateCollaborationLinkData());
|
||||
window.history.pushState(
|
||||
{},
|
||||
APP_NAME,
|
||||
getCollaborationLink({ roomId, roomKey }),
|
||||
);
|
||||
}
|
||||
|
||||
const scenePromise = resolvablePromise<ImportedDataState | null>();
|
||||
|
||||
this.isCollaborating = true;
|
||||
const roomMatch = getCollaborationLinkData(window.location.href);
|
||||
|
||||
const { default: socketIOClient }: any = await import(
|
||||
/* webpackChunkName: "socketIoClient" */ "socket.io-client"
|
||||
);
|
||||
if (roomMatch) {
|
||||
const roomId = roomMatch[1];
|
||||
const roomKey = roomMatch[2];
|
||||
|
||||
this.portal.open(socketIOClient(SOCKET_SERVER), roomId, roomKey);
|
||||
// fallback in case you're not alone in the room but still don't receive
|
||||
// initial SCENE_UPDATE message
|
||||
this.socketInitializationTimer = setTimeout(() => {
|
||||
this.initializeSocket();
|
||||
scenePromise.resolve(null);
|
||||
}, INITIAL_SCENE_UPDATE_TIMEOUT);
|
||||
|
||||
if (existingRoomLinkData) {
|
||||
this.excalidrawAPI.resetScene();
|
||||
const { default: socketIOClient }: any = await import(
|
||||
/* webpackChunkName: "socketIoClient" */ "socket.io-client"
|
||||
);
|
||||
|
||||
try {
|
||||
const elements = await loadFromFirebase(
|
||||
roomId,
|
||||
roomKey,
|
||||
this.portal.socket,
|
||||
);
|
||||
if (elements) {
|
||||
scenePromise.resolve({
|
||||
elements,
|
||||
});
|
||||
this.portal.open(socketIOClient(SOCKET_SERVER), roomId, roomKey);
|
||||
|
||||
// All socket listeners are moving to Portal
|
||||
this.portal.socket!.on(
|
||||
"client-broadcast",
|
||||
async (encryptedData: ArrayBuffer, iv: Uint8Array) => {
|
||||
if (!this.portal.roomKey) {
|
||||
return;
|
||||
}
|
||||
const decryptedData = await decryptAESGEM(
|
||||
encryptedData,
|
||||
this.portal.roomKey,
|
||||
iv,
|
||||
);
|
||||
|
||||
switch (decryptedData.type) {
|
||||
case "INVALID_RESPONSE":
|
||||
return;
|
||||
case SCENE.INIT: {
|
||||
if (!this.portal.socketInitialized) {
|
||||
const remoteElements = decryptedData.payload.elements;
|
||||
const reconciledElements = this.reconcileElements(
|
||||
remoteElements,
|
||||
);
|
||||
this.handleRemoteSceneUpdate(reconciledElements, {
|
||||
init: true,
|
||||
});
|
||||
this.initializeSocket();
|
||||
scenePromise.resolve({ elements: reconciledElements });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SCENE.UPDATE:
|
||||
this.handleRemoteSceneUpdate(
|
||||
this.reconcileElements(decryptedData.payload.elements),
|
||||
);
|
||||
break;
|
||||
case "MOUSE_LOCATION": {
|
||||
const {
|
||||
pointer,
|
||||
button,
|
||||
username,
|
||||
selectedElementIds,
|
||||
} = decryptedData.payload;
|
||||
const socketId: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["socketId"] =
|
||||
decryptedData.payload.socketId ||
|
||||
// @ts-ignore legacy, see #2094 (#2097)
|
||||
decryptedData.payload.socketID;
|
||||
|
||||
const collaborators = new Map(this.collaborators);
|
||||
const user = collaborators.get(socketId) || {}!;
|
||||
user.pointer = pointer;
|
||||
user.button = button;
|
||||
user.selectedElementIds = selectedElementIds;
|
||||
user.username = username;
|
||||
collaborators.set(socketId, user);
|
||||
this.excalidrawAPI.updateScene({
|
||||
collaborators,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
this.portal.socket!.on("first-in-room", () => {
|
||||
if (this.portal.socket) {
|
||||
this.portal.socket.off("first-in-room");
|
||||
}
|
||||
} catch (error) {
|
||||
// log the error and move on. other peers will sync us the scene.
|
||||
console.error(error);
|
||||
}
|
||||
} else {
|
||||
const elements = this.excalidrawAPI.getSceneElements();
|
||||
// remove deleted elements from elements array & history to ensure we don't
|
||||
// expose potentially sensitive user data in case user manually deletes
|
||||
// existing elements (or clears scene), which would otherwise be persisted
|
||||
// to database even if deleted before creating the room.
|
||||
this.excalidrawAPI.history.clear();
|
||||
this.excalidrawAPI.updateScene({
|
||||
elements,
|
||||
commitToHistory: true,
|
||||
this.initializeSocket();
|
||||
scenePromise.resolve(null);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isCollaborating: true,
|
||||
activeRoomLink: window.location.href,
|
||||
});
|
||||
|
||||
return scenePromise;
|
||||
}
|
||||
|
||||
// fallback in case you're not alone in the room but still don't receive
|
||||
// initial SCENE_UPDATE message
|
||||
this.socketInitializationTimer = setTimeout(() => {
|
||||
this.initializeSocket();
|
||||
scenePromise.resolve(null);
|
||||
}, INITIAL_SCENE_UPDATE_TIMEOUT);
|
||||
|
||||
// All socket listeners are moving to Portal
|
||||
this.portal.socket!.on(
|
||||
"client-broadcast",
|
||||
async (encryptedData: ArrayBuffer, iv: Uint8Array) => {
|
||||
if (!this.portal.roomKey) {
|
||||
return;
|
||||
}
|
||||
const decryptedData = await decryptAESGEM(
|
||||
encryptedData,
|
||||
this.portal.roomKey,
|
||||
iv,
|
||||
);
|
||||
|
||||
switch (decryptedData.type) {
|
||||
case "INVALID_RESPONSE":
|
||||
return;
|
||||
case SCENE.INIT: {
|
||||
if (!this.portal.socketInitialized) {
|
||||
this.initializeSocket();
|
||||
const remoteElements = decryptedData.payload.elements;
|
||||
const reconciledElements = this.reconcileElements(remoteElements);
|
||||
this.handleRemoteSceneUpdate(reconciledElements, {
|
||||
init: true,
|
||||
});
|
||||
// noop if already resolved via init from firebase
|
||||
scenePromise.resolve({ elements: reconciledElements });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SCENE.UPDATE:
|
||||
this.handleRemoteSceneUpdate(
|
||||
this.reconcileElements(decryptedData.payload.elements),
|
||||
);
|
||||
break;
|
||||
case "MOUSE_LOCATION": {
|
||||
const {
|
||||
pointer,
|
||||
button,
|
||||
username,
|
||||
selectedElementIds,
|
||||
} = decryptedData.payload;
|
||||
const socketId: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["socketId"] =
|
||||
decryptedData.payload.socketId ||
|
||||
// @ts-ignore legacy, see #2094 (#2097)
|
||||
decryptedData.payload.socketID;
|
||||
|
||||
const collaborators = new Map(this.collaborators);
|
||||
const user = collaborators.get(socketId) || {}!;
|
||||
user.pointer = pointer;
|
||||
user.button = button;
|
||||
user.selectedElementIds = selectedElementIds;
|
||||
user.username = username;
|
||||
collaborators.set(socketId, user);
|
||||
this.excalidrawAPI.updateScene({
|
||||
collaborators,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "IDLE_STATUS": {
|
||||
const { userState, socketId, username } = decryptedData.payload;
|
||||
const collaborators = new Map(this.collaborators);
|
||||
const user = collaborators.get(socketId) || {}!;
|
||||
user.userState = userState;
|
||||
user.username = username;
|
||||
this.excalidrawAPI.updateScene({
|
||||
collaborators,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.portal.socket!.on("first-in-room", () => {
|
||||
if (this.portal.socket) {
|
||||
this.portal.socket.off("first-in-room");
|
||||
}
|
||||
this.initializeSocket();
|
||||
scenePromise.resolve(null);
|
||||
});
|
||||
|
||||
this.initializeIdleDetector();
|
||||
|
||||
this.setState({
|
||||
activeRoomLink: window.location.href,
|
||||
});
|
||||
|
||||
return scenePromise;
|
||||
return null;
|
||||
};
|
||||
|
||||
private initializeSocket = () => {
|
||||
@@ -433,7 +359,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
// Avoid broadcasting to the rest of the collaborators the scene
|
||||
// we just received!
|
||||
// Note: this needs to be set before updating the scene as it
|
||||
// synchronously calls render.
|
||||
// syncronously calls render.
|
||||
this.setLastBroadcastedOrReceivedSceneVersion(getSceneVersion(newElements));
|
||||
|
||||
return newElements as ReconciledElements;
|
||||
@@ -462,58 +388,6 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
this.excalidrawAPI.history.clear();
|
||||
};
|
||||
|
||||
private onPointerMove = () => {
|
||||
if (this.idleTimeoutId) {
|
||||
window.clearTimeout(this.idleTimeoutId);
|
||||
this.idleTimeoutId = null;
|
||||
}
|
||||
this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
|
||||
if (!this.activeIntervalId) {
|
||||
this.activeIntervalId = window.setInterval(
|
||||
this.reportActive,
|
||||
ACTIVE_THRESHOLD,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private onVisibilityChange = () => {
|
||||
if (document.hidden) {
|
||||
if (this.idleTimeoutId) {
|
||||
window.clearTimeout(this.idleTimeoutId);
|
||||
this.idleTimeoutId = null;
|
||||
}
|
||||
if (this.activeIntervalId) {
|
||||
window.clearInterval(this.activeIntervalId);
|
||||
this.activeIntervalId = null;
|
||||
}
|
||||
this.onIdleStateChange(UserIdleState.AWAY);
|
||||
} else {
|
||||
this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
|
||||
this.activeIntervalId = window.setInterval(
|
||||
this.reportActive,
|
||||
ACTIVE_THRESHOLD,
|
||||
);
|
||||
this.onIdleStateChange(UserIdleState.ACTIVE);
|
||||
}
|
||||
};
|
||||
|
||||
private reportIdle = () => {
|
||||
this.onIdleStateChange(UserIdleState.IDLE);
|
||||
if (this.activeIntervalId) {
|
||||
window.clearInterval(this.activeIntervalId);
|
||||
this.activeIntervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
private reportActive = () => {
|
||||
this.onIdleStateChange(UserIdleState.ACTIVE);
|
||||
};
|
||||
|
||||
private initializeIdleDetector = () => {
|
||||
document.addEventListener(EVENT.POINTER_MOVE, this.onPointerMove);
|
||||
document.addEventListener(EVENT.VISIBILITY_CHANGE, this.onVisibilityChange);
|
||||
};
|
||||
|
||||
setCollaborators(sockets: string[]) {
|
||||
this.setState((state) => {
|
||||
const collaborators: InstanceType<
|
||||
@@ -553,11 +427,6 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
this.portal.broadcastMouseLocation(payload);
|
||||
};
|
||||
|
||||
onIdleStateChange = (userState: UserIdleState) => {
|
||||
this.setState({ userState });
|
||||
this.portal.broadcastIdleChange(userState);
|
||||
};
|
||||
|
||||
broadcastElements = (elements: readonly ExcalidrawElement[]) => {
|
||||
if (
|
||||
getSceneVersion(elements) >
|
||||
@@ -611,11 +480,9 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||
|
||||
/** Getter of context value. Returned object is stable. */
|
||||
getContextValue = (): CollabAPI => {
|
||||
if (!this.contextValue) {
|
||||
this.contextValue = {} as CollabAPI;
|
||||
}
|
||||
this.contextValue = this.contextValue || ({} as CollabAPI);
|
||||
|
||||
this.contextValue.isCollaborating = () => this.isCollaborating;
|
||||
this.contextValue.isCollaborating = this.state.isCollaborating;
|
||||
this.contextValue.username = this.state.username;
|
||||
this.contextValue.onPointerUpdate = this.onPointerUpdate;
|
||||
this.contextValue.initializeSocketClient = this.initializeSocketClient;
|
||||
|
||||
@@ -9,7 +9,6 @@ import CollabWrapper from "./CollabWrapper";
|
||||
import { getSyncableElements } from "../../packages/excalidraw/index";
|
||||
import { ExcalidrawElement } from "../../element/types";
|
||||
import { BROADCAST, SCENE } from "../app_constants";
|
||||
import { UserIdleState } from "./types";
|
||||
|
||||
class Portal {
|
||||
collab: CollabWrapper;
|
||||
@@ -123,7 +122,7 @@ class Portal {
|
||||
data as SocketUpdateData,
|
||||
);
|
||||
|
||||
if (syncAll && this.collab.isCollaborating) {
|
||||
if (syncAll && this.collab.state.isCollaborating) {
|
||||
await Promise.all([
|
||||
broadcastPromise,
|
||||
this.collab.saveCollabRoomToFirebase(syncableElements),
|
||||
@@ -133,23 +132,6 @@ class Portal {
|
||||
}
|
||||
};
|
||||
|
||||
broadcastIdleChange = (userState: UserIdleState) => {
|
||||
if (this.socket?.id) {
|
||||
const data: SocketUpdateDataSource["IDLE_STATUS"] = {
|
||||
type: "IDLE_STATUS",
|
||||
payload: {
|
||||
socketId: this.socket.id,
|
||||
userState,
|
||||
username: this.collab.state.username,
|
||||
},
|
||||
};
|
||||
return this._broadcastSocketData(
|
||||
data as SocketUpdateData,
|
||||
true, // volatile
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
broadcastMouseLocation = (payload: {
|
||||
pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
||||
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
|
||||
.RoomDialog-username {
|
||||
background-color: var(--input-background-color);
|
||||
background-color: var(--input-bg-color);
|
||||
border-color: var(--input-border-color);
|
||||
appearance: none;
|
||||
min-width: 0;
|
||||
@@ -53,7 +53,7 @@
|
||||
}
|
||||
|
||||
.Modal .RoomDialog-stopSession {
|
||||
background-color: var(--button-destructive-background-color);
|
||||
background-color: var(--button-destructive-bg-color);
|
||||
|
||||
.ToolIcon__label,
|
||||
.ToolIcon__icon svg {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum UserIdleState {
|
||||
ACTIVE = "active",
|
||||
AWAY = "away",
|
||||
IDLE = "idle",
|
||||
}
|
||||
@@ -148,7 +148,6 @@ export const saveToFirebase = async (
|
||||
export const loadFromFirebase = async (
|
||||
roomId: string,
|
||||
roomKey: string,
|
||||
socket: SocketIOClient.Socket | null,
|
||||
): Promise<readonly ExcalidrawElement[] | null> => {
|
||||
const firebase = await getFirebase();
|
||||
const db = firebase.firestore();
|
||||
@@ -161,12 +160,5 @@ export const loadFromFirebase = async (
|
||||
const storedScene = doc.data() as FirebaseStoredScene;
|
||||
const ciphertext = storedScene.ciphertext.toUint8Array();
|
||||
const iv = storedScene.iv.toUint8Array();
|
||||
|
||||
const elements = await decryptElements(roomKey, iv, ciphertext);
|
||||
|
||||
if (socket) {
|
||||
firebaseSceneVersionCache.set(socket, getSceneVersion(elements));
|
||||
}
|
||||
|
||||
return restoreElements(elements);
|
||||
return restoreElements(await decryptElements(roomKey, iv, ciphertext));
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ImportedDataState } from "../../data/types";
|
||||
import { ExcalidrawElement } from "../../element/types";
|
||||
import { t } from "../../i18n";
|
||||
import { AppState } from "../../types";
|
||||
import { UserIdleState } from "../collab/types";
|
||||
|
||||
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
|
||||
|
||||
@@ -60,14 +59,6 @@ export type SocketUpdateDataSource = {
|
||||
username: string;
|
||||
};
|
||||
};
|
||||
IDLE_STATUS: {
|
||||
type: "IDLE_STATUS";
|
||||
payload: {
|
||||
socketId: string;
|
||||
userState: UserIdleState;
|
||||
username: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type SocketUpdateDataIncoming =
|
||||
@@ -134,27 +125,17 @@ export const decryptAESGEM = async (
|
||||
};
|
||||
|
||||
export const getCollaborationLinkData = (link: string) => {
|
||||
const hash = new URL(link).hash;
|
||||
const match = hash.match(/^#room=([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+)$/);
|
||||
return match ? { roomId: match[1], roomKey: match[2] } : null;
|
||||
};
|
||||
|
||||
export const generateCollaborationLinkData = async () => {
|
||||
const roomId = await generateRandomID();
|
||||
const roomKey = await generateEncryptionKey();
|
||||
|
||||
if (!roomKey) {
|
||||
throw new Error("Couldn't generate room key");
|
||||
if (link.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return { roomId, roomKey };
|
||||
const hash = new URL(link).hash;
|
||||
return hash.match(/^#room=([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+)$/);
|
||||
};
|
||||
|
||||
export const getCollaborationLink = (data: {
|
||||
roomId: string;
|
||||
roomKey: string;
|
||||
}) => {
|
||||
return `${window.location.origin}${window.location.pathname}#room=${data.roomId},${data.roomKey}`;
|
||||
export const generateCollaborationLink = async () => {
|
||||
const id = await generateRandomID();
|
||||
const key = await generateEncryptionKey();
|
||||
return `${window.location.origin}${window.location.pathname}#room=${id},${key}`;
|
||||
};
|
||||
|
||||
export const getImportedKey = (key: string, usage: KeyUsage) =>
|
||||
|
||||
+100
-24
@@ -39,9 +39,11 @@ import CollabWrapper, {
|
||||
} from "./collab/CollabWrapper";
|
||||
import { LanguageList } from "./components/LanguageList";
|
||||
import { exportToBackend, getCollaborationLinkData, loadScene } from "./data";
|
||||
import { loadFromFirebase } from "./data/firebase";
|
||||
import {
|
||||
importFromLocalStorage,
|
||||
saveToLocalStorage,
|
||||
STORAGE_KEYS,
|
||||
} from "./data/localStorage";
|
||||
|
||||
const languageDetector = new LanguageDetector();
|
||||
@@ -64,9 +66,50 @@ const onBlur = () => {
|
||||
saveDebounced.flush();
|
||||
};
|
||||
|
||||
const shouldForceLoadScene = (
|
||||
scene: ResolutionType<typeof loadScene>,
|
||||
): boolean => {
|
||||
if (!scene.elements.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const roomMatch = getCollaborationLinkData(window.location.href);
|
||||
|
||||
if (!roomMatch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const roomId = roomMatch[1];
|
||||
|
||||
let collabForceLoadFlag;
|
||||
try {
|
||||
collabForceLoadFlag = localStorage?.getItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
||||
);
|
||||
} catch {}
|
||||
|
||||
if (collabForceLoadFlag) {
|
||||
try {
|
||||
const {
|
||||
room: previousRoom,
|
||||
timestamp,
|
||||
}: { room: string; timestamp: number } = JSON.parse(collabForceLoadFlag);
|
||||
// if loading same room as the one previously unloaded within 15sec
|
||||
// force reload without prompting
|
||||
if (previousRoom === roomId && Date.now() - timestamp < 15000) {
|
||||
return true;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
type Scene = ImportedDataState & { commitToHistory: boolean };
|
||||
|
||||
const initializeScene = async (opts: {
|
||||
collabAPI: CollabAPI;
|
||||
}): Promise<ImportedDataState | null> => {
|
||||
resetScene: ExcalidrawImperativeAPI["resetScene"];
|
||||
initializeSocketClient: CollabAPI["initializeSocketClient"];
|
||||
}): Promise<Scene | null> => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const id = searchParams.get("id");
|
||||
const jsonMatch = window.location.hash.match(
|
||||
@@ -77,17 +120,20 @@ const initializeScene = async (opts: {
|
||||
|
||||
let scene = await loadScene(null, null, initialData);
|
||||
|
||||
let roomLinkData = getCollaborationLinkData(window.location.href);
|
||||
const isExternalScene = !!(id || jsonMatch || roomLinkData);
|
||||
let isCollabScene = !!getCollaborationLinkData(window.location.href);
|
||||
const isExternalScene = !!(id || jsonMatch || isCollabScene);
|
||||
if (isExternalScene) {
|
||||
if (roomLinkData || window.confirm(t("alerts.loadSceneOverridePrompt"))) {
|
||||
if (
|
||||
shouldForceLoadScene(scene) ||
|
||||
window.confirm(t("alerts.loadSceneOverridePrompt"))
|
||||
) {
|
||||
// Backwards compatibility with legacy url format
|
||||
if (id) {
|
||||
scene = await loadScene(id, null, initialData);
|
||||
} else if (jsonMatch) {
|
||||
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
|
||||
}
|
||||
if (!roomLinkData) {
|
||||
if (!isCollabScene) {
|
||||
window.history.replaceState({}, APP_NAME, window.location.origin);
|
||||
}
|
||||
} else {
|
||||
@@ -104,19 +150,45 @@ const initializeScene = async (opts: {
|
||||
});
|
||||
}
|
||||
|
||||
roomLinkData = null;
|
||||
isCollabScene = false;
|
||||
window.history.replaceState({}, APP_NAME, window.location.origin);
|
||||
}
|
||||
}
|
||||
if (roomLinkData) {
|
||||
return opts.collabAPI.initializeSocketClient(roomLinkData);
|
||||
if (isCollabScene) {
|
||||
// when joining a room we don't want user's local scene data to be merged
|
||||
// into the remote scene
|
||||
opts.resetScene();
|
||||
const scenePromise = opts.initializeSocketClient();
|
||||
|
||||
try {
|
||||
const [, roomId, roomKey] = getCollaborationLinkData(
|
||||
window.location.href,
|
||||
)!;
|
||||
const elements = await loadFromFirebase(roomId, roomKey);
|
||||
if (elements) {
|
||||
return {
|
||||
elements,
|
||||
commitToHistory: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...(await scenePromise),
|
||||
commitToHistory: true,
|
||||
};
|
||||
} catch (error) {
|
||||
// log the error and move on. other peers will sync us the scene.
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (scene) {
|
||||
return scene;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function ExcalidrawWrapper() {
|
||||
const ExcalidrawWrapper = () => {
|
||||
// dimensions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -170,16 +242,24 @@ function ExcalidrawWrapper() {
|
||||
return;
|
||||
}
|
||||
|
||||
initializeScene({ collabAPI }).then((scene) => {
|
||||
initializeScene({
|
||||
resetScene: excalidrawAPI.resetScene,
|
||||
initializeSocketClient: collabAPI.initializeSocketClient,
|
||||
}).then((scene) => {
|
||||
initialStatePromiseRef.current.promise.resolve(scene);
|
||||
});
|
||||
|
||||
const onHashChange = (_: HashChangeEvent) => {
|
||||
initializeScene({ collabAPI }).then((scene) => {
|
||||
if (scene) {
|
||||
excalidrawAPI.updateScene(scene);
|
||||
}
|
||||
});
|
||||
if (window.location.hash.length > 1) {
|
||||
initializeScene({
|
||||
resetScene: excalidrawAPI.resetScene,
|
||||
initializeSocketClient: collabAPI.initializeSocketClient,
|
||||
}).then((scene) => {
|
||||
if (scene) {
|
||||
excalidrawAPI.updateScene(scene);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const titleTimeout = setTimeout(
|
||||
@@ -205,13 +285,9 @@ function ExcalidrawWrapper() {
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
if (collabAPI?.isCollaborating()) {
|
||||
saveDebounced(elements, appState);
|
||||
if (collabAPI?.isCollaborating) {
|
||||
collabAPI.broadcastElements(elements);
|
||||
} else {
|
||||
// collab scenes are persisted to the server, so we don't have to persist
|
||||
// them locally, which has the added benefit of not overwriting whatever
|
||||
// the user was working on before joining
|
||||
saveDebounced(elements, appState);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -276,7 +352,7 @@ function ExcalidrawWrapper() {
|
||||
initialData={initialStatePromiseRef.current.promise}
|
||||
user={{ name: collabAPI?.username }}
|
||||
onCollabButtonClick={collabAPI?.onCollabButtonClick}
|
||||
isCollaborating={collabAPI?.isCollaborating()}
|
||||
isCollaborating={collabAPI?.isCollaborating}
|
||||
onPointerUpdate={collabAPI?.onPointerUpdate}
|
||||
onExportToBackend={onExportToBackend}
|
||||
renderFooter={renderFooter}
|
||||
@@ -291,7 +367,7 @@ function ExcalidrawWrapper() {
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default function ExcalidrawApp() {
|
||||
return (
|
||||
|
||||
@@ -27,7 +27,6 @@ const allLanguages: Language[] = [
|
||||
{ code: "id-ID", label: "Bahasa Indonesia" },
|
||||
{ code: "it-IT", label: "Italiano" },
|
||||
{ code: "ja-JP", label: "日本語" },
|
||||
{ code: "kab-KAB", label: "Taqbaylit" },
|
||||
{ code: "ko-KR", label: "한국어" },
|
||||
{ code: "my-MM", label: "Burmese" },
|
||||
{ code: "nb-NO", label: "Norsk bokmål" },
|
||||
|
||||
@@ -21,7 +21,6 @@ export const CODES = {
|
||||
V: "KeyV",
|
||||
X: "KeyX",
|
||||
Z: "KeyZ",
|
||||
R: "KeyR",
|
||||
} as const;
|
||||
|
||||
export const KEYS = {
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "وضع الشبكة",
|
||||
"addToLibrary": "أضف إلى المكتبة",
|
||||
"removeFromLibrary": "حذف من المكتبة",
|
||||
"libraryLoadingMessage": "جارٍ تحميل المكتبة…",
|
||||
"libraryLoadingMessage": "جارٍ تحميل المكتبة...",
|
||||
"libraries": "تصفح المكتبات",
|
||||
"loadingScene": "جاري تحميل المشهد…",
|
||||
"loadingScene": "جاري تحميل المشهد...",
|
||||
"align": "محاذاة",
|
||||
"alignTop": "محاذاة إلى اﻷعلى",
|
||||
"alignBottom": "محاذاة إلى اﻷسفل",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "توسيط عمودي",
|
||||
"centerHorizontally": "توسيط أفقي",
|
||||
"distributeHorizontally": "التوزيع الأفقي",
|
||||
"distributeVertically": "التوزيع عمودياً",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "التوزيع عمودياً"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "إعادة تعيين اللوحة",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "تعذر فك تشفير البيانات.",
|
||||
"uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",
|
||||
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
|
||||
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
|
||||
"imageDoesNotContainScene": "استيراد الصور غير مدعوم في الوقت الراهن.\n\nهل تريد استيراد مشهد؟ لا يبدو أن هذه الصورة تحتوي على أي بيانات مشهد. هل قمت بسماح هذا أثناء التصدير؟",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "التخزين",
|
||||
"title": "إحصائيات للمهووسين",
|
||||
"total": "المجموع",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "العرض"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Решетъчен режим",
|
||||
"addToLibrary": "Добавяне към библиотеката",
|
||||
"removeFromLibrary": "Премахване от библиотеката",
|
||||
"libraryLoadingMessage": "Зареждане на библиотеката…",
|
||||
"libraryLoadingMessage": "Зареждане на библиотеката...",
|
||||
"libraries": "Разглеждане на библиотеките",
|
||||
"loadingScene": "Зареждане на сцена…",
|
||||
"loadingScene": "Зареждане на сцена...",
|
||||
"align": "Подравняване",
|
||||
"alignTop": "Подравняване отгоре",
|
||||
"alignBottom": "Подравняване отдолу",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Центрирай вертикално",
|
||||
"centerHorizontally": "Центрирай хоризонтално",
|
||||
"distributeHorizontally": "Разпредели хоризонтално",
|
||||
"distributeVertically": "Разпредели вертикално",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Разпредели вертикално"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Нулиране на платно",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Данните не можаха да се дешифрират.",
|
||||
"uploadedSecurly": "Качването е защитено с криптиране от край до край, което означава, че сървърът Excalidraw и трети страни не могат да четат съдържанието.",
|
||||
"loadSceneOverridePrompt": "Зареждането на външна рисунка ще презапише настоящото ви съдържание. Желаете ли да продължите?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Възникна грешка при зареждането на външна библиотека.",
|
||||
"confirmAddLibrary": "Ще се добавят {{numShapes}} фигура(и) във вашата библиотека. Сигурни ли сте?",
|
||||
"imageDoesNotContainScene": "Импортирането на картинки не се поддържва в момента.\n\nИскате да импортнете сцена? Тази картинка не съдържа данни от сцена. Разрешили ли сте последното при експортирането?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Съхранение на данни",
|
||||
"title": "Статистика за хакери",
|
||||
"total": "Общо",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Широчина"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Mode quadrícula",
|
||||
"addToLibrary": "Afegir a la biblioteca",
|
||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||
"libraryLoadingMessage": "Carregant la biblioteca…",
|
||||
"libraryLoadingMessage": "Carregant la biblioteca...",
|
||||
"libraries": "Explorar biblioteques",
|
||||
"loadingScene": "Carregant escena…",
|
||||
"loadingScene": "Carregant escena...",
|
||||
"align": "Alinear",
|
||||
"alignTop": "Alinear a dalt",
|
||||
"alignBottom": "Alinear a baix",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centrar verticalment",
|
||||
"centerHorizontally": "Centrar horitzontalment",
|
||||
"distributeHorizontally": "Distribuir horitzontalment",
|
||||
"distributeVertically": "Distribuir verticalment",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Distribuir verticalment"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Netejar el llenç",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "No s'ha pogut desencriptar.",
|
||||
"uploadedSecurly": "La càrrega s'ha assegurat amb xifratge punta a punta, cosa que significa que el servidor Excalidraw i tercers no poden llegir el contingut.",
|
||||
"loadSceneOverridePrompt": "Si carregas aquest dibuix extern, substituirá el que tens. Vols continuar?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "S'ha produït un error en carregar la biblioteca de tercers.",
|
||||
"confirmAddLibrary": "Això afegirà {{numShapes}} forma(es) a la vostra biblioteca. Estàs segur?",
|
||||
"imageDoesNotContainScene": "En aquest moment no s’admet la importació d’imatges.\n\nVolies importar una escena? Sembla que aquesta imatge no conté cap dada d’escena. Ho has activat durant l'exportació?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Emmagatzematge",
|
||||
"title": "Estadístiques per nerds",
|
||||
"total": "Total",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Amplada"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Rastermodus",
|
||||
"addToLibrary": "Zur Bibliothek hinzufügen",
|
||||
"removeFromLibrary": "Aus Bibliothek entfernen",
|
||||
"libraryLoadingMessage": "Lade Bibliothek…",
|
||||
"libraryLoadingMessage": "Lade Bibliothek...",
|
||||
"libraries": "Bibliotheken durchsuchen",
|
||||
"loadingScene": "Lade Zeichnung…",
|
||||
"loadingScene": "Lade Zeichnung...",
|
||||
"align": "Ausrichten",
|
||||
"alignTop": "Obere Kanten",
|
||||
"alignBottom": "Untere Kanten",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Vertikal zentrieren",
|
||||
"centerHorizontally": "Horizontal zentrieren",
|
||||
"distributeHorizontally": "Horizontal verteilen",
|
||||
"distributeVertically": "Vertikal verteilen",
|
||||
"viewMode": "Ansichtsmodus"
|
||||
"distributeVertically": "Vertikal verteilen"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Zeichenfläche löschen & Hintergrundfarbe zurücksetzen",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Daten konnten nicht entschlüsselt werden.",
|
||||
"uploadedSecurly": "Der Upload wurde mit Ende-zu-Ende-Verschlüsselung gespeichert. Weder Excalidraw noch Dritte können den Inhalt einsehen.",
|
||||
"loadSceneOverridePrompt": "Das Laden der externen Zeichnung ersetzt den vorhandenen Inhalt. Möchtest Du fortfahren?",
|
||||
"collabStopOverridePrompt": "Das Stoppen der Sitzung wird Deine vorherige, lokal gespeicherte Zeichnung überschreiben. Bist Du sicher?\n\n(Wenn Du Deine lokale Zeichnung behalten möchtest, schließe einfach stattdessen den Browser-Tab.)",
|
||||
"errorLoadingLibrary": "Beim Laden der Drittanbieter-Bibliothek ist ein Fehler aufgetreten.",
|
||||
"confirmAddLibrary": "Dieses fügt {{numShapes}} Form(en) zu deiner Bibliothek hinzu. Bist du sicher?",
|
||||
"imageDoesNotContainScene": "Das Importieren von Bildern wird derzeit nicht unterstützt.\n\nMöchtest du eine Szene importieren? Dieses Bild scheint keine Zeichnungsdaten zu enthalten. Hast du dies beim Exportieren aktiviert?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Speicher",
|
||||
"title": "Statistiken für Nerds",
|
||||
"total": "Gesamt",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Breite"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Formatierung kopiert.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "In die Zwischenablage als PNG kopiert."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Εμφάνιση σε πλέγμα",
|
||||
"addToLibrary": "Προσθήκη στη βιβλιοθήκη",
|
||||
"removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη",
|
||||
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης…",
|
||||
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...",
|
||||
"libraries": "Άλλες βιβλιοθήκες",
|
||||
"loadingScene": "Φόρτωση σκηνής…",
|
||||
"loadingScene": "Φόρτωση σκηνής...",
|
||||
"align": "Στοίχιση",
|
||||
"alignTop": "Στοίχιση πάνω",
|
||||
"alignBottom": "Στοίχιση κάτω",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Κέντρο κάθετα",
|
||||
"centerHorizontally": "Κέντρο οριζόντια",
|
||||
"distributeHorizontally": "Οριζόντια κατανομή",
|
||||
"distributeVertically": "Κατακόρυφη κατανομή",
|
||||
"viewMode": "Λειτουργία προβολής"
|
||||
"distributeVertically": "Κατακόρυφη κατανομή"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Επαναφορά του καμβά",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Δεν ήταν δυνατή η αποκρυπτογράφηση δεδομένων.",
|
||||
"uploadedSecurly": "Η μεταφόρτωση έχει εξασφαλιστεί με κρυπτογράφηση από άκρο σε άκρο, πράγμα που σημαίνει ότι ο διακομιστής Excalidraw και τρίτα μέρη δεν μπορούν να διαβάσουν το περιεχόμενο.",
|
||||
"loadSceneOverridePrompt": "Η φόρτωση εξωτερικού σχεδίου θα αντικαταστήσει το υπάρχον περιεχόμενο. Επιθυμείτε να συνεχίσετε;",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Υπήρξε ένα σφάλμα κατά τη φόρτωση της βιβλιοθήκης τρίτου μέρους.",
|
||||
"confirmAddLibrary": "Αυτό θα προσθέσει {{numShapes}} σχήμα(τα) στη βιβιλιοθήκη σας. Είστε σίγουροι;",
|
||||
"imageDoesNotContainScene": "Η εισαγωγή εικόνων δεν υποστηρίζεται αυτή τη στιγμή.\n\nΜήπως θέλετε να εισαγάγετε μια σκηνή; Αυτή η εικόνα δεν φαίνεται να περιέχει δεδομένα σκηνής. Έχετε ενεργοποιήσει αυτό κατά την εξαγωγή;",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Χώρος",
|
||||
"title": "Στατιστικά για σπασίκλες",
|
||||
"total": "Σύνολο ",
|
||||
"version": "Έκδοση",
|
||||
"versionCopy": "Κάνε κλικ για αντιγραφή",
|
||||
"versionNotAvailable": "Έκδοση μη διαθέσιμη",
|
||||
"width": "Πλάτος"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Αντιγράφηκαν στυλ.",
|
||||
"copyToClipboard": "Αντιγράφηκε στο πρόχειρο.",
|
||||
"copyToClipboardAsPng": "Αντιγράφτηκε στο πρόχειρο ως PNG."
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Grid mode",
|
||||
"addToLibrary": "Add to library",
|
||||
"removeFromLibrary": "Remove from library",
|
||||
"libraryLoadingMessage": "Loading library…",
|
||||
"libraryLoadingMessage": "Loading library...",
|
||||
"libraries": "Browse libraries",
|
||||
"loadingScene": "Loading scene…",
|
||||
"loadingScene": "Loading scene...",
|
||||
"align": "Align",
|
||||
"alignTop": "Align top",
|
||||
"alignBottom": "Align bottom",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Center vertically",
|
||||
"centerHorizontally": "Center horizontally",
|
||||
"distributeHorizontally": "Distribute horizontally",
|
||||
"distributeVertically": "Distribute vertically",
|
||||
"viewMode": "View mode"
|
||||
"distributeVertically": "Distribute vertically"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Reset the canvas",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Couldn't decrypt data.",
|
||||
"uploadedSecurly": "The upload has been secured with end-to-end encryption, which means that Excalidraw server and third parties can't read the content.",
|
||||
"loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?",
|
||||
"collabStopOverridePrompt": "Stopping the session will overwrite your previous, locally stored drawing. Are you sure?\n\n(If you want to keep your local drawing, simply close the browser tab instead.)",
|
||||
"errorLoadingLibrary": "There was an error loading the third party library.",
|
||||
"confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
|
||||
"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?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Storage",
|
||||
"title": "Stats for nerds",
|
||||
"total": "Total",
|
||||
"version": "Version",
|
||||
"versionCopy": "Click to copy",
|
||||
"versionNotAvailable": "Version not available",
|
||||
"width": "Width"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Copied styles.",
|
||||
"copyToClipboard": "Copied to clipboard.",
|
||||
"copyToClipboardAsPng": "Copied to clipboard as PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Modo cuadrícula",
|
||||
"addToLibrary": "Añadir a la biblioteca",
|
||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||
"libraryLoadingMessage": "Cargando biblioteca…",
|
||||
"libraryLoadingMessage": "Cargando biblioteca...",
|
||||
"libraries": "Explorar bibliotecas",
|
||||
"loadingScene": "Cargando escena…",
|
||||
"loadingScene": "Cargando escena...",
|
||||
"align": "Alinear",
|
||||
"alignTop": "Alineación superior",
|
||||
"alignBottom": "Alineación inferior",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centrar verticalmente",
|
||||
"centerHorizontally": "Centrar horizontalmente",
|
||||
"distributeHorizontally": "Distribuir horizontalmente",
|
||||
"distributeVertically": "Distribuir verticalmente",
|
||||
"viewMode": "Modo presentación"
|
||||
"distributeVertically": "Distribuir verticalmente"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "No se pudieron descifrar los datos.",
|
||||
"uploadedSecurly": "La carga ha sido asegurada con cifrado de principio a fin, lo que significa que el servidor de Excalidraw y terceros no pueden leer el contenido.",
|
||||
"loadSceneOverridePrompt": "Si carga este dibujo externo, reemplazará el que tiene. ¿Desea continuar?",
|
||||
"collabStopOverridePrompt": "Detener la sesión sobrescribirá su dibujo anterior almacenado en local. ¿Estás seguro?\n\n(Si quieres mantener tu dibujo en local, simplemente cierre la pestaña del navegador en su lugar.)",
|
||||
"errorLoadingLibrary": "Se ha producido un error al cargar la biblioteca de terceros.",
|
||||
"confirmAddLibrary": "Esto añadirá {{numShapes}} forma(s) a tu biblioteca. ¿Estás seguro?",
|
||||
"imageDoesNotContainScene": "La importación de imágenes no está homologada en este momento.\n\n¿Deseas importar una escena? Esta imagen no parece contener ningún dato de escena. ¿Lo has activado durante la exportación?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Almacenamiento",
|
||||
"title": "Estadísticas para nerds",
|
||||
"total": "Total",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Ancho"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Estilos copiados.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Copiado al portapapeles como PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "حالت شبکه ای",
|
||||
"addToLibrary": "افزودن به کتابخانه",
|
||||
"removeFromLibrary": "حذف از کتابخانه",
|
||||
"libraryLoadingMessage": "بارگذاری کتابخانه…",
|
||||
"libraryLoadingMessage": "بارگذاری کتابخانه...",
|
||||
"libraries": "مرور کردن کتابخانه ها",
|
||||
"loadingScene": "باگذاری صحنه…",
|
||||
"loadingScene": "باگذاری صحنه...",
|
||||
"align": "تراز",
|
||||
"alignTop": "تراز به بالا",
|
||||
"alignBottom": "تراز به پایین",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "وسط قرار دادن به صورت عمودی",
|
||||
"centerHorizontally": "وسط قرار دادن به صورت افقی",
|
||||
"distributeHorizontally": "توزیع کردن به صورت افقی",
|
||||
"distributeVertically": "توزیع کردن به صورت عمودی",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "توزیع کردن به صورت عمودی"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "پاکسازی بوم نقاشی",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "رمزگشایی داده ها امکان پذیر نیست.",
|
||||
"uploadedSecurly": "آپلود با رمزگذاری دو طرفه انجام میشود، به این معنی که سرور Excalidraw و اشخاص ثالث نمی توانند مطالب شما را بخوانند.",
|
||||
"loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.",
|
||||
"confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟",
|
||||
"imageDoesNotContainScene": "وارد کردن تصویر در این لحظه امکان پذیر نمی باشد.\nآیا مایل به وارد کردن یک صحنه هستید؟ این تصویر به نظر می رسد که فاقد هرگونه اطلاعاتی مربوط به صحنه باشد. آیا این گزینه را در زمان وارد کردن تصویر فعال کرده اید؟",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "حافظه",
|
||||
"title": "آمار برای نردها",
|
||||
"total": "مجموع",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "عرض"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "کپی سبک.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "کپی در حافطه موقت به صورت PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Ruudukkotila",
|
||||
"addToLibrary": "Lisää kirjastoon",
|
||||
"removeFromLibrary": "Poista kirjastosta",
|
||||
"libraryLoadingMessage": "Ladataan kirjastoa…",
|
||||
"libraryLoadingMessage": "Ladataan kirjastoa...",
|
||||
"libraries": "Selaa kirjastoja",
|
||||
"loadingScene": "Ladataan työtä…",
|
||||
"loadingScene": "Ladataan työtä...",
|
||||
"align": "Tasaa",
|
||||
"alignTop": "Tasaa ylös",
|
||||
"alignBottom": "Tasaa alas",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Keskitä pystysuunnassa",
|
||||
"centerHorizontally": "Keskitä vaakasuunnassa",
|
||||
"distributeHorizontally": "Jaa vaakasuunnassa",
|
||||
"distributeVertically": "Jaa pystysuunnassa",
|
||||
"viewMode": "Katselutila"
|
||||
"distributeVertically": "Jaa pystysuunnassa"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Tyhjennä piirtoalue",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Salauksen purkaminen epäonnistui.",
|
||||
"uploadedSecurly": "Lähetys on turvattu päästä päähän salauksella. Excalidrawin palvelin ja kolmannet osapuolet eivät voi lukea sisältöä.",
|
||||
"loadSceneOverridePrompt": "Ulkopuolisen piirroksen lataaminen korvaa nykyisen sisältösi. Haluatko jatkaa?",
|
||||
"collabStopOverridePrompt": "Istunnon lopettaminen korvaa aiemman, paikallisesti tallennetun piirustuksen. Oletko varma?\n\n(Jos haluat pitää paikallisen piirustuksen, sulje selaimen välilehti sen sijaan.)",
|
||||
"errorLoadingLibrary": "Kolmannen osapuolen kirjastoa ladattaessa tapahtui virhe.",
|
||||
"confirmAddLibrary": "Tämä lisää {{numShapes}} muotoa kirjastoosi. Oletko varma?",
|
||||
"imageDoesNotContainScene": "Kuvien lisääminen ei ole tällä hetkellä mahdollista.\n\nHaluatko tuoda piirroksen? Tämä kuva ei näytä sisältävän tarvittavia tietoja. Oletko ottanut piirrostietojen tallennuksen käyttöön viennin aikana?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Tallennustila",
|
||||
"title": "Nörttien tilastot",
|
||||
"total": "Yhteensä",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Leveys"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Tyylit kopioitu.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Kopioitu leikepöydälle PNG-tiedostona."
|
||||
}
|
||||
}
|
||||
|
||||
+4
-10
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Mode grille",
|
||||
"addToLibrary": "Ajouter à la bibliothèque",
|
||||
"removeFromLibrary": "Supprimer de la bibliothèque",
|
||||
"libraryLoadingMessage": "Chargement de la bibliothèque…",
|
||||
"libraryLoadingMessage": "Chargement de la bibliothèque...",
|
||||
"libraries": "Parcourir les bibliothèques",
|
||||
"loadingScene": "Chargement de la scène…",
|
||||
"loadingScene": "Chargement de la scène...",
|
||||
"align": "Aligner",
|
||||
"alignTop": "Aligner en haut",
|
||||
"alignBottom": "Aligner en bas",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centrer verticalement",
|
||||
"centerHorizontally": "Centrer horizontalement",
|
||||
"distributeHorizontally": "Distribuer horizontalement",
|
||||
"distributeVertically": "Distribuer verticalement",
|
||||
"viewMode": "Mode présentation"
|
||||
"distributeVertically": "Distribuer verticalement"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Réinitialiser le canevas",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Les données n'ont pas pu être déchiffrées.",
|
||||
"uploadedSecurly": "Le téléchargement a été sécurisé avec un chiffrement de bout en bout, ce qui signifie que ni Excalidraw ni personne d'autre ne peut en lire le contenu.",
|
||||
"loadSceneOverridePrompt": "Le chargement d'un dessin externe remplacera votre contenu actuel. Souhaitez-vous continuer ?",
|
||||
"collabStopOverridePrompt": "Arrêter la session écrasera votre précédent dessin stocké localement. Êtes-vous sûr·e ?\n\n(Si vous voulez garder votre dessin local, fermez simplement l'onglet du navigateur à la place.)",
|
||||
"errorLoadingLibrary": "Une erreur s'est produite lors du chargement de la bibliothèque tierce.",
|
||||
"confirmAddLibrary": "Cela va ajouter {{numShapes}} forme(s) à votre bibliothèque. Êtes-vous sûr·e ?",
|
||||
"imageDoesNotContainScene": "L'importation d'images n'est pas prise en charge pour le moment.\n\nVouliez-vous importer une scène ? Cette image ne semble pas contenir de données de scène. Avez-vous activé cette option lors de l'exportation ?",
|
||||
@@ -231,18 +229,14 @@
|
||||
"elements": "Éléments",
|
||||
"height": "Hauteur",
|
||||
"scene": "Scène",
|
||||
"selected": "Sélection",
|
||||
"selected": "Sélectionné",
|
||||
"storage": "Stockage",
|
||||
"title": "Stats pour les nerds",
|
||||
"total": "Total",
|
||||
"version": "Version",
|
||||
"versionCopy": "Cliquer pour copier",
|
||||
"versionNotAvailable": "Version non disponible",
|
||||
"width": "Largeur"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Styles copiés.",
|
||||
"copyToClipboard": "Copié vers le presse-papiers.",
|
||||
"copyToClipboardAsPng": "Copié vers le presse-papier en PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "מצב רשת",
|
||||
"addToLibrary": "הוסף לספריה",
|
||||
"removeFromLibrary": "הסר מספריה",
|
||||
"libraryLoadingMessage": "טוען ספריה…",
|
||||
"libraryLoadingMessage": "טוען ספריה...",
|
||||
"libraries": "דפדף בספריות",
|
||||
"loadingScene": "טוען תצוגה…",
|
||||
"loadingScene": "טוען תצוגה...",
|
||||
"align": "יישר",
|
||||
"alignTop": "יישר למעלה",
|
||||
"alignBottom": "יישר למטה",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "מרכז אנכית",
|
||||
"centerHorizontally": "מרכז אופקית",
|
||||
"distributeHorizontally": "חלוקה אופקית",
|
||||
"distributeVertically": "חלוקה אנכית",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "חלוקה אנכית"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "אפס את הלוח",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "לא ניתן לפענח מידע.",
|
||||
"uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.",
|
||||
"loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "קרתה שגיאה בטעינת הספריה החיצונית.",
|
||||
"confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?",
|
||||
"imageDoesNotContainScene": "אין תמיכה בייבוא תמונות כעת.\n\nהאם אתה רוצה לייבא תצוגה? התמונה הזאת אינה מכילה מידע על תצוגה. האם הפעלת את האפשרות הזאת בזמן הוצאת המידע?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "אחסון",
|
||||
"title": "סטטיסטיקות לחנונים",
|
||||
"total": "סה״כ",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "רוחב"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
+21
-27
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "लंबवत केन्द्रित",
|
||||
"centerHorizontally": "क्षैतिज केन्द्रित",
|
||||
"distributeHorizontally": "क्षैतिज रूप से वितरित करें",
|
||||
"distributeVertically": "खड़ी रूप से वितरित करें",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "खड़ी रूप से वितरित करें"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "कैनवास रीसेट करें",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "डेटा को डिक्रिप्ट नहीं किया जा सका।",
|
||||
"uploadedSecurly": "अपलोड को एंड-टू-एंड एन्क्रिप्शन के साथ सुरक्षित किया गया है, जिसका मतलब है कि एक्सक्लूसिव सर्वर और थर्ड पार्टी कंटेंट नहीं पढ़ सकते हैं।",
|
||||
"loadSceneOverridePrompt": "लोड हो रहा है बाहरी ड्राइंग आपके मौजूदा सामग्री को बदल देगा। क्या आप जारी रखना चाहते हैं?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "लाइब्रेरी लोड करने में त्रुटि",
|
||||
"confirmAddLibrary": "लाइब्रेरी जोड़ें पुष्टि करें आकार संख्या",
|
||||
"imageDoesNotContainScene": "दृश्य में छवि नहीं है",
|
||||
@@ -202,25 +200,25 @@
|
||||
"title": "गलती"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "हमारा ब्लॉग पढे",
|
||||
"click": "क्लिक करें",
|
||||
"curvedArrow": "वक्र तीर",
|
||||
"curvedLine": "वक्र रेखा",
|
||||
"blog": "",
|
||||
"click": "",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"drag": "खींचें",
|
||||
"editor": "संपादक",
|
||||
"github": "मुद्दा मिला? प्रस्तुत करें",
|
||||
"howto": "हमारे गाइड का पालन करें",
|
||||
"or": "या",
|
||||
"preventBinding": "तीर बंधन रोकें",
|
||||
"shapes": "आकृतियाँ",
|
||||
"shortcuts": "कीबोर्ड के शॉर्टकट्स",
|
||||
"textFinish": "संपादन समाप्त करें (पाठ)",
|
||||
"textNewLine": "नई पंक्ति जोड़ें (पाठ)",
|
||||
"title": "मदद",
|
||||
"view": "दृश्य",
|
||||
"zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें",
|
||||
"zoomToSelection": "चयन तक ज़ूम करे"
|
||||
"drag": "",
|
||||
"editor": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "",
|
||||
"preventBinding": "",
|
||||
"shapes": "",
|
||||
"shortcuts": "",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"title": "",
|
||||
"view": "",
|
||||
"zoomToFit": "",
|
||||
"zoomToSelection": ""
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "संग्रह",
|
||||
"title": "बेवकूफ के लिए आँकड़े",
|
||||
"total": "कुल",
|
||||
"version": "संस्करण",
|
||||
"versionCopy": "काॅपी करने के लिए क्लिक करें",
|
||||
"versionNotAvailable": "संस्करण उपलब्ध नहीं है",
|
||||
"width": "चौड़ाई"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "काॅपी कीए स्टाइल",
|
||||
"copyToClipboard": "क्लिपबोर्ड में कॉपी कीए",
|
||||
"copyToClipboardAsPng": "क्लिपबोर्ड में PNG के रूप में कॉपी किए"
|
||||
"copyStyles": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Hálómód",
|
||||
"addToLibrary": "Hozzáadás a könyvtárhoz",
|
||||
"removeFromLibrary": "Eltávólítás a könyvtárból",
|
||||
"libraryLoadingMessage": "Könyvtár betöltése…",
|
||||
"libraryLoadingMessage": "Könyvtár betöltése...",
|
||||
"libraries": "Könyvtárak böngészése",
|
||||
"loadingScene": "Jelenet betöltése…",
|
||||
"loadingScene": "Jelenet betöltése...",
|
||||
"align": "Igazítás",
|
||||
"alignTop": "Felülre igazítás",
|
||||
"alignBottom": "Alulra igazítás",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Függőlegesen középre igazított",
|
||||
"centerHorizontally": "Vízszintesen középre igazított",
|
||||
"distributeHorizontally": "Vízszintes elosztás",
|
||||
"distributeVertically": "Függőleges elosztás",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Függőleges elosztás"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Vászon törlése",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Nem sikerült visszafejteni a titkosított adatot.",
|
||||
"uploadedSecurly": "A feltöltést végpontok közötti titkosítással biztosítottuk, ami azt jelenti, hogy egy harmadik fél nem tudja megnézni a tartalmát, beleértve az Excalidraw szervereit is.",
|
||||
"loadSceneOverridePrompt": "A betöltött külső rajz felül fogja írnia meglévőt. Szeretnéd folytatni?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Hibába ütközött a harmarmadik féltől származó könyvtár betöltése.",
|
||||
"confirmAddLibrary": "Ez a művelet {{numShapes}} formát fog hozzáadni a könyvtáradhoz. Biztos vagy benne?",
|
||||
"imageDoesNotContainScene": "Képek importálása egyelőre nem támogatott.\n\nEgy jelenetet szeretnél betölteni? Úgy tűnik ez a kép fájl nem tartalmazza a szükséges adatokat. Exportáláskor ezt egy külön opcióval lehet beállítani.",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Tárhely",
|
||||
"title": "Statisztikák",
|
||||
"total": "Összesen",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Szélesség"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Mode grid",
|
||||
"addToLibrary": "Tambahkan ke pustaka",
|
||||
"removeFromLibrary": "Hapus dari pustaka",
|
||||
"libraryLoadingMessage": "Memuat pustaka…",
|
||||
"libraryLoadingMessage": "Memuat pustaka...",
|
||||
"libraries": "Telusur pustaka",
|
||||
"loadingScene": "Memuat pemandangan…",
|
||||
"loadingScene": "Memuat pemandangan...",
|
||||
"align": "Perataan",
|
||||
"alignTop": "Rata atas",
|
||||
"alignBottom": "Rata bawah",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Pusatkan secara vertikal",
|
||||
"centerHorizontally": "Pusatkan secara horizontal",
|
||||
"distributeHorizontally": "Distribusikan horizontal",
|
||||
"distributeVertically": "Distribusikan vertikal",
|
||||
"viewMode": "Mode tampilan"
|
||||
"distributeVertically": "Distribusikan vertikal"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Setel Ulang Kanvas",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Tidak dapat mengdekripsi data.",
|
||||
"uploadedSecurly": "Pengunggahan ini telah diamankan menggunakan enkripsi end-to-end, artinya server Excalidraw dan pihak ketiga tidak data membaca nya",
|
||||
"loadSceneOverridePrompt": "Memuat gambar external akan mengganti konten Anda yang ada. Apakah Anda ingin melanjutkan?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Terdapat kesalahan dalam memuat pustaka pihak ketiga.",
|
||||
"confirmAddLibrary": "Ini akan menambahkan {{numShapes}} bentuk ke pustaka Anda. Anda yakin?",
|
||||
"imageDoesNotContainScene": "Mengimpor gambar tidak didukung saat ini.\n\nApakah Anda ingin impor pemandangan? Gambar ini tidak berisi data pemandangan. Sudah ka Anda aktifkan ini ketika ekspor?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Penyimpanan",
|
||||
"title": "Statistik untuk nerd",
|
||||
"total": "Total",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Lebar"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Gaya tersalin.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Tersalin ke clipboard sebagai PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Modalità griglia",
|
||||
"addToLibrary": "Aggiungi alla libreria",
|
||||
"removeFromLibrary": "Rimuovi dalla libreria",
|
||||
"libraryLoadingMessage": "Caricamento libreria…",
|
||||
"libraryLoadingMessage": "Caricamento della biblioteca...",
|
||||
"libraries": "Sfoglia librerie",
|
||||
"loadingScene": "Caricamento della scena…",
|
||||
"loadingScene": "Caricamento della scena...",
|
||||
"align": "Allinea",
|
||||
"alignTop": "Allinea in alto",
|
||||
"alignBottom": "Allinea in basso",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centra Verticalmente",
|
||||
"centerHorizontally": "Centra orizzontalmente",
|
||||
"distributeHorizontally": "Distribuisci orizzontalmente",
|
||||
"distributeVertically": "Distribuisci verticalmente",
|
||||
"viewMode": "Modalità visualizzazione"
|
||||
"distributeVertically": "Distribuisci verticalmente"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Svuota la tela",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Impossibile decriptare i dati.",
|
||||
"uploadedSecurly": "L'upload è stato protetto con la crittografia end-to-end, il che significa che il server Excalidraw e terze parti non possono leggere il contenuto.",
|
||||
"loadSceneOverridePrompt": "Se carichi questo disegno esterno, sostituirà quello che hai. Vuoi continuare?",
|
||||
"collabStopOverridePrompt": "Interrompere la sessione sovrascriverà il precedente disegno memorizzato localmente. Sei sicuro?\n\n(Se vuoi mantenere il tuo disegno locale, chiudi semplicemente la scheda del browser.)",
|
||||
"errorLoadingLibrary": "Si è verificato un errore nel caricamento della libreria di terze parti.",
|
||||
"confirmAddLibrary": "Questo aggiungerà {{numShapes}} forma(e) alla tua libreria. Sei sicuro?",
|
||||
"imageDoesNotContainScene": "L'importazione di immagini al momento non è supportata.\n\nVuoi importare una scena? Questa immagine non sembra contenere alcun dato di scena. Hai abilitato questa opzione durante l'esportazione?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Memoria",
|
||||
"title": "Statistiche per nerd",
|
||||
"total": "Totale",
|
||||
"version": "Versione",
|
||||
"versionCopy": "Clicca per copiare",
|
||||
"versionNotAvailable": "Versione non disponibile",
|
||||
"width": "Larghezza"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Stili copiati.",
|
||||
"copyToClipboard": "Copiato negli appunti.",
|
||||
"copyToClipboardAsPng": "Copiato negli appunti come PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "",
|
||||
"addToLibrary": "ライブラリに追加",
|
||||
"removeFromLibrary": "ライブラリから削除",
|
||||
"libraryLoadingMessage": "ライブラリを読み込み中…",
|
||||
"libraryLoadingMessage": "ライブラリを読み込み中...",
|
||||
"libraries": "",
|
||||
"loadingScene": "シーンを読み込み中…",
|
||||
"loadingScene": "シーンを読み込み中...",
|
||||
"align": "整列",
|
||||
"alignTop": "上揃え",
|
||||
"alignBottom": "下揃え",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "縦方向に中央揃え",
|
||||
"centerHorizontally": "横方向に中央揃え",
|
||||
"distributeHorizontally": "",
|
||||
"distributeVertically": "",
|
||||
"viewMode": ""
|
||||
"distributeVertically": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "キャンバスのリセット",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "データを復号できませんでした。",
|
||||
"uploadedSecurly": "データのアップロードはエンドツーエンド暗号化によって保護されています。Excalidrawサーバーと第三者はデータの内容を見ることができません。",
|
||||
"loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "サードパーティライブラリの読み込み中にエラーが発生しました。",
|
||||
"confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?",
|
||||
"imageDoesNotContainScene": "",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "",
|
||||
"title": "",
|
||||
"total": "合計",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "幅"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Senṭeḍ",
|
||||
"pasteCharts": "Senṭeḍ udlifen",
|
||||
"selectAll": "Fren akk",
|
||||
"multiSelect": "Rnu aferdis ɣer tefrayt",
|
||||
"moveCanvas": "Smutti taɣzut n usuneɣ",
|
||||
"cut": "Gzem",
|
||||
"copy": "Nɣel",
|
||||
"copyAsPng": "Nɣel ɣer tecfawit am PNG",
|
||||
"copyAsSvg": "Nɣel ɣer tecfawit am SVG",
|
||||
"bringForward": "Awi ɣer sdat",
|
||||
"sendToBack": "Awi s agilal",
|
||||
"bringToFront": "Err ɣer deffir",
|
||||
"sendBackward": "Awi ɣer deffir",
|
||||
"delete": "Kkes",
|
||||
"copyStyles": "Nɣel iɣunab",
|
||||
"pasteStyles": "Senṭeḍ iɣunab",
|
||||
"stroke": "Azizdew",
|
||||
"background": "Agilal",
|
||||
"fill": "Taččart",
|
||||
"strokeWidth": "Tehri n yizirig",
|
||||
"strokeStyle": "Aɣanib n tizirig",
|
||||
"strokeStyle_solid": "Aččuran",
|
||||
"strokeStyle_dashed": "S tjerriḍin",
|
||||
"strokeStyle_dotted": "S tenqiḍin",
|
||||
"sloppiness": "",
|
||||
"opacity": "Tiḍullest",
|
||||
"textAlign": "Areyyec n uḍris",
|
||||
"edges": "Leryuf",
|
||||
"sharp": "Yemsed",
|
||||
"round": "Imdewer",
|
||||
"arrowheads": "Ixfawen n tenccabt",
|
||||
"arrowhead_none": "Ulac",
|
||||
"arrowhead_arrow": "Taneccabt",
|
||||
"arrowhead_bar": "Afeggag",
|
||||
"arrowhead_dot": "Tanqiḍt",
|
||||
"fontSize": "Tiddi n tsefsit",
|
||||
"fontFamily": "Tawacult n tsefsiyin",
|
||||
"onlySelected": "Tafrayt kan",
|
||||
"withBackground": "S ugilal",
|
||||
"exportEmbedScene": "Seddu asayes deg ufaylu yettwasifḍen",
|
||||
"exportEmbedScene_details": "Asayes ad yettwasekles deg ufaylu n usifeḍ PNG/SVG akken akken ad yili wamek ara d-yettwarr seg-s usayes. Ayagi ad isimɣur tiddi n ufaylu n usifeḍ.",
|
||||
"addWatermark": "Seddu \"Yettwaxdem s Excalidraw\"",
|
||||
"handDrawn": "Asuneɣ s ufus",
|
||||
"normal": "Amagnu",
|
||||
"code": "Tangalt",
|
||||
"small": "Meẓẓi",
|
||||
"medium": "Alemmas",
|
||||
"large": "Ameqran",
|
||||
"veryLarge": "Meqqer aṭas",
|
||||
"solid": "Aččuran",
|
||||
"hachure": "Azerreg",
|
||||
"crossHatch": "Azerreg anmidag",
|
||||
"thin": "Arqaq",
|
||||
"bold": "Azuran",
|
||||
"left": "Azelmaḍ",
|
||||
"center": "Talemmast",
|
||||
"right": "Ayfus",
|
||||
"extraBold": "Azuran aṭas",
|
||||
"architect": "Amasdag",
|
||||
"artist": "Anaẓur",
|
||||
"cartoonist": "",
|
||||
"fileTitle": "Azwel n ufaylu",
|
||||
"colorPicker": "Amafran n yini",
|
||||
"canvasBackground": "Agilal n teɣzut n usuneɣ",
|
||||
"drawingCanvas": "Taɣzut n usuneɣ",
|
||||
"layers": "Tissiyin",
|
||||
"actions": "Tigawin",
|
||||
"language": "Tutlayt",
|
||||
"createRoom": "Bḍu tiɣimit n umɛawen s srid",
|
||||
"duplicateSelection": "Sisleg",
|
||||
"untitled": "War azwel",
|
||||
"name": "Isem",
|
||||
"yourName": "Isem-ik (im)",
|
||||
"madeWithExcalidraw": "Yettwaxdem s Excalidraw",
|
||||
"group": "Segrew tafrayt",
|
||||
"ungroup": "Kkess asegrew i tefrayt",
|
||||
"collaborators": "Imɛiwnen",
|
||||
"gridMode": "Askar n uferrug",
|
||||
"addToLibrary": "Rnu ɣer temkarḍit",
|
||||
"removeFromLibrary": "Kkes si temkarḍit",
|
||||
"libraryLoadingMessage": "Asali n temkarḍit…",
|
||||
"libraries": "Snirem timkarḍiyin",
|
||||
"loadingScene": "Asali n usayes…",
|
||||
"align": "Reyyec",
|
||||
"alignTop": "Areyyec uksawen",
|
||||
"alignBottom": "Areyyec ukessar",
|
||||
"alignLeft": "Reyyec s azelmaḍ",
|
||||
"alignRight": "Areyyec s ayfus",
|
||||
"centerVertically": "Di tlemmast s ibeddi",
|
||||
"centerHorizontally": "Di tlemmast s uglawi",
|
||||
"distributeHorizontally": "Freq s uglawi",
|
||||
"distributeVertically": "Freq s yibeddi",
|
||||
"viewMode": "Askar n tmuɣli"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Ales awennez n teɣzut n usuneɣ",
|
||||
"export": "Sifeḍ",
|
||||
"exportToPng": "Sifeḍ ɣer PNG",
|
||||
"exportToSvg": "Sifeḍ ɣer SVG",
|
||||
"copyToClipboard": "Nɣel ɣer tecfawit",
|
||||
"copyPngToClipboard": "Nɣel PNG ɣer tecfawit",
|
||||
"scale": "Taskala",
|
||||
"save": "Sekles",
|
||||
"saveAs": "Sekles am",
|
||||
"load": "Sali-d",
|
||||
"getShareableLink": "Awi-d aseɣwen n beṭṭu",
|
||||
"close": "Mdel",
|
||||
"selectLanguage": "Fren tutlayt",
|
||||
"scrollBackToContent": "Uɣal s agbur",
|
||||
"zoomIn": "Simɣur",
|
||||
"zoomOut": "Simẓi",
|
||||
"resetZoom": "Ales awennez n usemɣer",
|
||||
"menu": "Umuɣ",
|
||||
"done": "Ifukk",
|
||||
"edit": "Ẓreg",
|
||||
"undo": "Sefsex",
|
||||
"redo": "Err-d",
|
||||
"roomDialog": "Bdu amɛawen s srid",
|
||||
"createNewRoom": "Snulfu-d taxxamt tamaynutt",
|
||||
"fullScreen": "Agdil aččuran",
|
||||
"darkMode": "Askar imsulles",
|
||||
"lightMode": "Askar afaw",
|
||||
"zenMode": "Askar Zen",
|
||||
"exitZenMode": "Ffeɣ seg uskar Zen"
|
||||
},
|
||||
"alerts": {
|
||||
"clearReset": "Ayagi ad isfeḍ akk taɣzut n usuneɣ. Tetḥeqqeḍ?",
|
||||
"couldNotCreateShareableLink": "D awezɣi asnulfu n useɣwen n beṭṭu.",
|
||||
"couldNotCreateShareableLinkTooBig": "D awezɣi asnulfu n useɣwen n beṭṭu. Asayes ɣezzif aṭas",
|
||||
"couldNotLoadInvalidFile": "D awezɣi asali n ufaylu armeɣtu",
|
||||
"importBackendFailed": "",
|
||||
"cannotExportEmptyCanvas": "D awezɣi asifeḍ n teɣzut n usuneɣ tilemt.",
|
||||
"couldNotCopyToClipboard": "D awezɣi anɣal ɣer tecfawit. Eɛreḍ ad tesqedceḍ iminig Chrome.",
|
||||
"decryptFailed": "D awezɣi tukksa n uwgelhen i yisefka.",
|
||||
"uploadedSecurly": "Asili yettwasɣelles s uwgelhen ixef s ixef, ayagi yebɣa ad d-yini belli aqeddac n Excalidraw akked medden ur zmiren ara ad ɣren agbur.",
|
||||
"loadSceneOverridePrompt": "Asali n wunuɣ uffiɣ ad isemselsi agbur-inek (m) yellan. Tebɣiḍ ad tkemmeleḍ?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Teḍra-d tuccḍa deg usali n temkarḍit n wis kraḍ.",
|
||||
"confirmAddLibrary": "Ayagi adirnu talɣa (win) {{numShapes}} ɣer temkarḍit-inek (m). Tetḥeqqeḍ?",
|
||||
"imageDoesNotContainScene": "Taktert n tugniwin ur tettwadhel ara akka tura.\nTebɣiḍ ad tketreḍ asayes? Tugna-agi tettban-d ur tegbir ara isefka n usnas. Tesremdeḍ ayagi deg usifeḍ?",
|
||||
"cannotRestoreFromImage": "Asayes ulamek ara d-yettwarr seg ufaylu-agi n tugna"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Tafrayt",
|
||||
"draw": "Unuɣ ilelli",
|
||||
"rectangle": "Asrem",
|
||||
"diamond": "Ameɣṛun",
|
||||
"ellipse": "Taglayt",
|
||||
"arrow": "Taneccabt",
|
||||
"line": "Izirig",
|
||||
"text": "Aḍris",
|
||||
"library": "Tamkarḍit",
|
||||
"lock": "Eǧǧ afecku n tefrayt yermed mbaɛd asuneɣ"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Tigawin n teɣzut n usuneɣ",
|
||||
"selectedShapeActions": "Tigawin n talɣa yettwafernen",
|
||||
"shapes": "Talɣiwin"
|
||||
},
|
||||
"hints": {
|
||||
"linearElement": "Ssit akken ad tebduḍ aṭas n tenqiḍin, zuɣer i yiwen n yizirig",
|
||||
"freeDraw": "Ssit yerna zuɣer, serreḥ ticki tfukeḍ",
|
||||
"text": "Tixidest: tzemreḍ daɣen ad ternuḍ aḍris s usiti snat n tikkal anida tebɣiḍ s ufecku n tefrayt",
|
||||
"linearElementMulti": "Ssit ɣef tenqiḍt taneggarut neɣ ssed taqeffalt Escape neɣ taqeffalt Kcem akken ad tfakkeḍ",
|
||||
"lockAngle": "Tzemreḍ ad tḥettmeḍ tiɣmert s tuṭṭfa n tqeffalt SHIFT",
|
||||
"resize": "Tzemreḍ ad tḥettemeḍ assaɣ s tuṭṭfa n tqeffalt SHIFT mi ara tettbeddileḍ tiddi,\nma teṭṭfeḍ ALT abeddel n tiddi ad yili si tlemmast",
|
||||
"rotate": "Tzemreḍ ad tḥettemeḍ tiɣemmar s tuṭṭfa n SHIFT di tuzzya",
|
||||
"lineEditor_info": "Ssit snat n tikkal neɣ ssed taqeffalt Kcem akken ad tẓergeḍ tinqiḍin",
|
||||
"lineEditor_pointSelected": "Ssed taqeffalt kkes akken ad tekkseḍ tanqiḍt, CtrlOrCmd+D akken ad tsiselgeḍ, neɣ zuɣer akken ad tesmuttiḍ",
|
||||
"lineEditor_nothingSelected": "Fren tanqiḍt ara tesmuttiḍ neɣ ara tekkseḍ, neɣ ṭṭef taqeffalt Alt akken ad ternuḍ tinqiḍin timaynutin"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Ulamek abeqqeḍ n teskant",
|
||||
"canvasTooBig": "Taɣzut n usuneɣ tezmer ad tili temeqqer aṭas.",
|
||||
"canvasTooBigTip": "Tixidest: eɛreḍ ad tesqerbeḍ ciṭ iferdisen yembaɛaden."
|
||||
},
|
||||
"errorSplash": {
|
||||
"headingMain_pre": "Teḍra-d tuccḍa. Eɛreḍ ",
|
||||
"headingMain_button": "asali n usebter tikkelt-nniḍen.",
|
||||
"clearCanvasMessage": "Ma yella tulsa n usali ur tefri ara ugur, eɛreḍ ",
|
||||
"clearCanvasMessage_button": "asfaḍ n teɣzut n usuneɣ.",
|
||||
"clearCanvasCaveat": " Ayagi ad d-iglu s usṛuḥu n umahil ",
|
||||
"trackedToSentry_pre": "Tuccḍa akked umesmagi ",
|
||||
"trackedToSentry_post": " tettwasekles deg unagraw-nneɣ.",
|
||||
"openIssueMessage_pre": "",
|
||||
"openIssueMessage_button": "afecku n weḍfar n yibugen.",
|
||||
"openIssueMessage_post": " Ma ulac uɣilif seddu talɣut ukessar-agi s wenɣal akked usenṭeḍ di GitHub issue.",
|
||||
"sceneContent": "Agbur n usayes:"
|
||||
},
|
||||
"roomDialog": {
|
||||
"desc_intro": "Tzemreḍ ad d-teɛerḍeḍ medden ɣer usayes-inek (m) amiran akken ad ttekkin yid-k.",
|
||||
"desc_privacy": "Ur tqelliq ara, tiɣimit tsseqdac awgelhen ixef s ixef, dɣa ayen ara tsunɣeḍ ad iqqim d amaẓlay. Ula d aqeddac-nneɣ ur yezmir ara ad iwali acu txeddemeḍ.",
|
||||
"button_startSession": "Bdu tiɣimit",
|
||||
"button_stopSession": "Ḥbes tiɣimit",
|
||||
"desc_inProgressIntro": "Tiɣimit n umɛawen s srid tetteddu akka tura.",
|
||||
"desc_shareLink": "Bḍu aseɣwen-agi akked medden ukud tebɣiḍ ad temɛawaneḍ:",
|
||||
"desc_exitSession": "Aḥbas n tɣimit ad k (m) yesenser si texxamt, maca ad tizmireḍ ad tkemmeleḍ amahil s usayes, s wudem adigan. Ẓer belli ayagi ur yettḥaz ara imdanen-nniḍen, yerna ad izmiren ad kemmelen ad mɛawanen di tsuffeɣt-nnsen."
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Tuccḍa"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Ɣeṛ ablug-nneɣ",
|
||||
"click": "ssit",
|
||||
"curvedArrow": "Taneccabt izelgen",
|
||||
"curvedLine": "Izirig izelgen",
|
||||
"documentation": "Tasemlit",
|
||||
"drag": "zuɣer",
|
||||
"editor": "Amaẓrag",
|
||||
"github": "Tufiḍ-d ugur? Azen-aɣ-d",
|
||||
"howto": "Ḍfer imniren-nneɣ",
|
||||
"or": "neɣ",
|
||||
"preventBinding": "",
|
||||
"shapes": "Talɣiwin",
|
||||
"shortcuts": "Inegzumen n unasiw",
|
||||
"textFinish": "Fak asiẓreg (aḍris)",
|
||||
"textNewLine": "Rnu ajerriḍ amaynut (aḍris)",
|
||||
"title": "Tallelt",
|
||||
"view": "Tamuɣli",
|
||||
"zoomToFit": "Simɣur akken ad twliḍ akk iferdisen",
|
||||
"zoomToSelection": "Simɣur ɣer tefrayt"
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "Unuɣen-inek (m) ttuwgelhnen seg yixef s ixef dɣa iqeddacen n Excalidraw werǧin ad ten-walin. "
|
||||
},
|
||||
"stats": {
|
||||
"angle": "Tiɣmeṛt",
|
||||
"element": "Aferdis",
|
||||
"elements": "Iferdisen",
|
||||
"height": "Tattayt",
|
||||
"scene": "Asayes",
|
||||
"selected": "Yettwafren",
|
||||
"storage": "Aḥraz",
|
||||
"title": "",
|
||||
"total": "Aɣrud",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Tehri"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Iɣunab yettwaneɣlen.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Yettwanɣel ɣer tecfawit am PNG."
|
||||
}
|
||||
}
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "격자 방식",
|
||||
"addToLibrary": "라이브러리에 추가",
|
||||
"removeFromLibrary": "라이브러리에서 제거",
|
||||
"libraryLoadingMessage": "라이브러리 불러오는 중…",
|
||||
"libraryLoadingMessage": "라이브러리 불러오는 중...",
|
||||
"libraries": "라이브러리 찾기",
|
||||
"loadingScene": "화면 불러오는 중…",
|
||||
"loadingScene": "화면 불러오는 중...",
|
||||
"align": "정렬",
|
||||
"alignTop": "상단 정렬",
|
||||
"alignBottom": "하단 정렬",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "수직으로 중앙 정렬",
|
||||
"centerHorizontally": "수평으로 중앙 정렬",
|
||||
"distributeHorizontally": "수평으로 분배",
|
||||
"distributeVertically": "수직으로 분배",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "수직으로 분배"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "캔버스 초기화",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "데이터를 복호화하지 못했습니다.",
|
||||
"uploadedSecurly": "업로드는 종단 간 암호화로 보호되므로 Excalidraw 서버 및 타사가 콘텐츠를 읽을 수 없습니다.",
|
||||
"loadSceneOverridePrompt": "외부 파일을 불러 오면 기존 콘텐츠가 대체됩니다. 계속 진행할까요?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "외부 라이브러리를 불러오는 중에 문제가 발생했습니다.",
|
||||
"confirmAddLibrary": "{{numShapes}}개의 모양이 라이브러리에 추가됩니다. 계속하시겠어요?",
|
||||
"imageDoesNotContainScene": "이미지에서 불러오기는 현재 지원되지 않습니다.\n\n화면을 불러오려고 하셨나요? 이미지에 화면 정보가 없는 것 같습니다. 내보낼 때 화면을 포함했나요?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "저장공간",
|
||||
"title": "덕후들을 위한 통계",
|
||||
"total": "합계",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "너비"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "",
|
||||
"addToLibrary": "မှတ်တမ်းတင်",
|
||||
"removeFromLibrary": "မှတ်တမ်းမှထုတ်",
|
||||
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်…",
|
||||
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...",
|
||||
"libraries": "စာကြည့်တိုက်တွင်ရှာဖွေပါ",
|
||||
"loadingScene": "မြင်ကွင်းဖော်နေသည်…",
|
||||
"loadingScene": "မြင်ကွင်းဖော်နေသည်...",
|
||||
"align": "ချိန်ညှိ",
|
||||
"alignTop": "ထိပ်ညှိ",
|
||||
"alignBottom": "အခြေညှိ",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "ဒေါင်လိုက်အလယ်ညှိ",
|
||||
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
|
||||
"distributeHorizontally": "အလျားလိုက်",
|
||||
"distributeVertically": "ထောင်လိုက်",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "ထောင်လိုက်"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "ကားချပ်ရှင်းလင်း",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "အချက်အလက်ဖော်ယူ၍မရပါ။",
|
||||
"uploadedSecurly": "တင်သွင်းအချက်အလက်များအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်စနစ်အသုံးပြု၍လုံခြုံစွာထိန်းသိမ်းထားပါသဖြင့် Excalidraw ဆာဗာနှင့်ဆက်စပ်အဖွဲ့အစည်းများပင်လျှင်မဖတ်ရှုနိုင်ပါ။",
|
||||
"loadSceneOverridePrompt": "လက်ရှိရေးဆွဲထားသမျှအား ပြင်ပမှတင်သွင်းသောပုံနှင့်အစားထိုးပါမည်။ ဆက်လက်ဆောင်ရွက်လိုပါသလား။",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "ပြင်ပမှမှတ်တမ်းအားတင်သွင်းရာတွင်အမှားအယွင်းရှိနေသည်။",
|
||||
"confirmAddLibrary": "{{numShapes}} ခုသောပုံသဏ္ဌာန်အားမှတ်တမ်းတင်ပါမည်။ အတည်ပြုပါ။",
|
||||
"imageDoesNotContainScene": "",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "သိုလှောင်ခန်း",
|
||||
"title": "အက္ခရာများအတွက်အချက်အလက်များ",
|
||||
"total": "စုစုပေါင်း",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "အကျယ်"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Rutevisning",
|
||||
"addToLibrary": "Legg til i 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",
|
||||
"alignTop": "Juster øverst",
|
||||
"alignBottom": "Juster nederst",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Midtstill vertikalt",
|
||||
"centerHorizontally": "Midtstill horisontalt",
|
||||
"distributeHorizontally": "Distribuer horisontalt",
|
||||
"distributeVertically": "Distribuer vertikalt",
|
||||
"viewMode": "Visningsmodus"
|
||||
"distributeVertically": "Distribuer vertikalt"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Tøm lerretet og tilbakestill bakgrunnsfargen",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Kunne ikke dekryptere data.",
|
||||
"uploadedSecurly": "Opplastingen er kryptert og kan ikke leses av Excalidraw-serveren eller tredjeparter.",
|
||||
"loadSceneOverridePrompt": "Å laste inn ekstern tegning vil erstatte det eksisterende innholdet. Ønsker du å fortsette?",
|
||||
"collabStopOverridePrompt": "Hvis du slutter økten, overskrives din forrige, lokalt lagrede tegning. Er du sikker?\n\n(Hvis du ønsker å beholde din lokale tegning, bare lukk nettleserfanen i stedet.)",
|
||||
"errorLoadingLibrary": "Det oppstod en feil under lasting av tredjepartsbiblioteket.",
|
||||
"confirmAddLibrary": "Dette vil legge til {{numShapes}} figur(er) i biblioteket ditt. Er du sikker?",
|
||||
"imageDoesNotContainScene": "Importering av bilder støttes ikke for øyeblikket.\n\nVil du importere en scene? Dette bildet ser ikke ut til å inneholde noen scene-data. Har du aktivert dette under eksporten?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Lagring",
|
||||
"title": "Statistikk for nerder",
|
||||
"total": "Totalt",
|
||||
"version": "Versjon",
|
||||
"versionCopy": "Klikk for å kopiere",
|
||||
"versionNotAvailable": "Versjon ikke tilgjengelig",
|
||||
"width": "Bredde"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Kopierte stiler.",
|
||||
"copyToClipboard": "Kopiert til utklippstavlen.",
|
||||
"copyToClipboardAsPng": "Kopiert til utklippstavlen som PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Rasterweergave",
|
||||
"addToLibrary": "Voeg toe aan bibliotheek",
|
||||
"removeFromLibrary": "Verwijder uit bibliotheek",
|
||||
"libraryLoadingMessage": "Bibliotheek laden…",
|
||||
"libraryLoadingMessage": "Bibliotheek laden...",
|
||||
"libraries": "Blader door bibliotheken",
|
||||
"loadingScene": "Scène laden…",
|
||||
"loadingScene": "Scène laden...",
|
||||
"align": "Uitlijnen",
|
||||
"alignTop": "Boven uitlijnen",
|
||||
"alignBottom": "Onder uitlijnen",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Verticaal Centreren",
|
||||
"centerHorizontally": "Horizontaal Centreren",
|
||||
"distributeHorizontally": "Horizontaal verspreiden",
|
||||
"distributeVertically": "Verticaal distribueren",
|
||||
"viewMode": "Weergavemodus"
|
||||
"distributeVertically": "Verticaal distribueren"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Canvas opnieuw instellen",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Kan gegevens niet decoderen.",
|
||||
"uploadedSecurly": "De upload is beveiligd met end-to-end encryptie, wat betekent dat de Excalidraw server en derden de inhoud niet kunnen lezen.",
|
||||
"loadSceneOverridePrompt": "Het laden van externe tekening zal uw bestaande inhoud vervangen. Wil je doorgaan?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Bij het laden van de externe bibliotheek is een fout opgetreden.",
|
||||
"confirmAddLibrary": "Hiermee worden {{numShapes}} vorm(n) aan uw bibliotheek toegevoegd. Ben je het zeker?",
|
||||
"imageDoesNotContainScene": "Afbeeldingen importeren wordt op dit moment niet ondersteund.\n\nWil je een scène importeren? Deze afbeelding lijkt geen scène gegevens te bevatten. Heb je dit geactiveerd tijdens het exporteren?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Opslag",
|
||||
"title": "Statistieken voor nerds",
|
||||
"total": "Totaal",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Breedte"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Stijlen gekopieerd.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Gekopieerd naar klembord als PNG."
|
||||
}
|
||||
}
|
||||
|
||||
+9
-15
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Rutevisning",
|
||||
"addToLibrary": "Legg til i bibliotek",
|
||||
"removeFromLibrary": "Fjern frå bibliotek",
|
||||
"libraryLoadingMessage": "Laster bibliotek…",
|
||||
"libraryLoadingMessage": "Laster bibliotek...",
|
||||
"libraries": "Blad gjennom bibliotek",
|
||||
"loadingScene": "Laster scene…",
|
||||
"loadingScene": "Laster scene...",
|
||||
"align": "Juster",
|
||||
"alignTop": "Juster til topp",
|
||||
"alignBottom": "Juster til botn",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Midtstill vertikalt",
|
||||
"centerHorizontally": "Midtstill horisontalt",
|
||||
"distributeHorizontally": "Sprei horisontalt",
|
||||
"distributeVertically": "Sprei vertikalt",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Sprei vertikalt"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Tilbakestill lerretet",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Kunne ikkje dekryptere data.",
|
||||
"uploadedSecurly": "Opplastinga er kryptert og er ikkje mogleg å lese av Excalidraw-serveren eller tredjepartar.",
|
||||
"loadSceneOverridePrompt": "Innlasting av ekstern teikning erstattar ditt eksisterande innhald. Ynskjer du å fortsette?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Det oppstod ein feil under lastinga av tredjepartsbibliotek.",
|
||||
"confirmAddLibrary": "Dette vil legge til {{numShapes}} form(er) i biblioteket ditt. Er du sikker?",
|
||||
"imageDoesNotContainScene": "Importering av bilder støttes ikkje for p. t.\n\nVil du importere ein scene? Dette bildet ser ikkje ut til å inneholde noen scene-data. Har du aktivert dette under eksporten?",
|
||||
@@ -203,22 +201,22 @@
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "",
|
||||
"click": "klikk",
|
||||
"click": "",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"drag": "",
|
||||
"editor": "Redigering",
|
||||
"editor": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "eller",
|
||||
"or": "",
|
||||
"preventBinding": "",
|
||||
"shapes": "Formar",
|
||||
"shapes": "",
|
||||
"shortcuts": "",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"title": "Hjelp",
|
||||
"view": "Vising",
|
||||
"title": "",
|
||||
"view": "",
|
||||
"zoomToFit": "",
|
||||
"zoomToSelection": ""
|
||||
},
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Lagring",
|
||||
"title": "Statistikk for nerdar",
|
||||
"total": "Totalt",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Breidde"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "ਜਾਲੀਦਾਰ ਮੋਡ",
|
||||
"addToLibrary": "ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਜੋੜੋ",
|
||||
"removeFromLibrary": "ਲਾਇਬ੍ਰੇਰੀ 'ਚੋਂ ਹਟਾਓ",
|
||||
"libraryLoadingMessage": "ਲਾਇਬ੍ਰੇਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…",
|
||||
"libraryLoadingMessage": "ਲਾਇਬ੍ਰੇਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...",
|
||||
"libraries": "ਲਾਇਬ੍ਰੇਰੀਆਂ ਬਰਾਉਜ਼ ਕਰੋ",
|
||||
"loadingScene": "ਦ੍ਰਿਸ਼ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…",
|
||||
"loadingScene": "ਦ੍ਰਿਸ਼ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...",
|
||||
"align": "ਇਕਸਾਰ",
|
||||
"alignTop": "ਉੱਪਰ ਇਕਸਾਰ ਕਰੋ",
|
||||
"alignBottom": "ਹੇਠਾਂ ਇਕਸਾਰ ਕਰੋ",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "ਲੇਟਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
||||
"centerHorizontally": "ਖੜ੍ਹਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
||||
"distributeHorizontally": "ਖੜ੍ਹਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
|
||||
"distributeVertically": "ਲੇਟਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
|
||||
"viewMode": "ਦੇਖੋ ਮੋਡ"
|
||||
"distributeVertically": "ਲੇਟਵੇਂ ਇਕਸਾਰ ਵੰਡੋ"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "ਕੈਨਵਸ ਰੀਸੈੱਟ ਕਰੋ",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "ਡਾਟਾ ਡੀਕਰਿਪਟ ਨਹੀਂ ਕਰ ਸਕੇ।",
|
||||
"uploadedSecurly": "ਅੱਪਲੋਡ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕਰਿਪਸ਼ਨ ਨਾਲ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਹੋਈ ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਇਹ ਹੈ ਕਿ Excalidraw ਸਰਵਰ ਅਤੇ ਤੀਜੀ ਧਿਰ ਦੇ ਬੰਦੇ ਸਮੱਗਰੀ ਨੂੰ ਪੜ੍ਹ ਨਹੀਂ ਸਕਦੇ।",
|
||||
"loadSceneOverridePrompt": "ਬਾਹਰੀ ਡਰਾਇੰਗ ਨੂੰ ਲੋਡ ਕਰਨਾ ਤੁਹਾਡੀ ਮੌਜੂਦਾ ਸਮੱਗਰੀ ਦੀ ਥਾਂ ਲੈ ਲਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
||||
"collabStopOverridePrompt": "ਇਜਲਾਸ ਨੂੰ ਰੋਕਣਾ ਪਿਛਲੀ ਲੋਕਲ ਸਾਂਭੀ ਡਰਾਇੰਗ ਦੀ ਥਾਂ ਲੈ ਲਵੇਗਾ। ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?\n\n(ਜੇ ਤੁਸੀਂ ਆਪਣੀ ਲੋਕਲ ਡਰਾਇੰਗ ਨੂੰ ਬਰਕਰਾਰ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਇਹ ਕਰਨ ਦੀ ਬਜਾਏ ਬੱਸ ਆਪਣਾ ਟੈਬ ਬੰਦ ਕਰ ਦਿਉ।)",
|
||||
"errorLoadingLibrary": "ਤੀਜੀ ਧਿਰ ਦੀ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਲੋਡ ਕਰਨ ਵਿੱਚ ਗਲਤੀ ਹੋਈ ਸੀ।",
|
||||
"confirmAddLibrary": "ਇਹ ਤੁਹਾਡੀ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ {{numShapes}} ਆਕ੍ਰਿਤੀ(ਆਂ) ਨੂੰ ਜੋੜ ਦੇਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
||||
"imageDoesNotContainScene": "ਫਿਲਹਾਲ ਤਸਵੀਰਾਂ ਨੂੰ ਆਯਾਤ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ।\n\nਕੀ ਤੁਸੀਂ ਦ੍ਰਿਸ਼ ਨੂੰ ਆਯਾਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਸੀ? ਇਸ ਤਸਵੀਰ ਵਿੱਚ ਦ੍ਰਿਸ਼ ਦਾ ਕੋਈ ਵੀ ਡਾਟਾ ਨਜ਼ਰ ਨਹੀਂ ਆ ਰਿਹਾ। ਕੀ ਨਿਰਯਾਤ ਦੌਰਾਨ ਤੁਸੀਂ ਇਹ ਸਮਰੱਥ ਕੀਤਾ ਸੀ?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "ਸਟੋਰੇਜ",
|
||||
"title": "ਪੜਾਕੂਆਂ ਲਈ ਅੰਕੜੇ",
|
||||
"total": "ਕੁੱਲ",
|
||||
"version": "ਸੰਸਕਰਨ",
|
||||
"versionCopy": "ਕਾਪੀ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ",
|
||||
"versionNotAvailable": "ਸੰਸਕਰਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ",
|
||||
"width": "ਚੌੜਾਈ"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "ਕਾਪੀ ਕੀਤੇ ਸਟਾਇਲ।",
|
||||
"copyToClipboard": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ ਕਾਪੀ ਕੀਤਾ।",
|
||||
"copyToClipboardAsPng": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ PNG ਵਜੋਂ ਕਾਪੀ ਕੀਤਾ।"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
{
|
||||
"ar-SA": 87,
|
||||
"bg-BG": 91,
|
||||
"ca-ES": 87,
|
||||
"de-DE": 98,
|
||||
"el-GR": 99,
|
||||
"ar-SA": 90,
|
||||
"bg-BG": 94,
|
||||
"ca-ES": 90,
|
||||
"de-DE": 100,
|
||||
"el-GR": 100,
|
||||
"en": 100,
|
||||
"es-ES": 98,
|
||||
"fa-IR": 95,
|
||||
"fi-FI": 98,
|
||||
"es-ES": 100,
|
||||
"fa-IR": 98,
|
||||
"fi-FI": 100,
|
||||
"fr-FR": 100,
|
||||
"he-IL": 87,
|
||||
"hi-IN": 98,
|
||||
"hu-HU": 87,
|
||||
"id-ID": 97,
|
||||
"he-IL": 90,
|
||||
"hi-IN": 90,
|
||||
"hu-HU": 90,
|
||||
"id-ID": 100,
|
||||
"it-IT": 100,
|
||||
"ja-JP": 79,
|
||||
"kab-KAB": 94,
|
||||
"ko-KR": 87,
|
||||
"my-MM": 81,
|
||||
"ja-JP": 81,
|
||||
"ko-KR": 90,
|
||||
"my-MM": 83,
|
||||
"nb-NO": 100,
|
||||
"nl-NL": 97,
|
||||
"nl-NL": 100,
|
||||
"nn-NO": 90,
|
||||
"pa-IN": 100,
|
||||
"pl-PL": 88,
|
||||
"pl-PL": 90,
|
||||
"pt-BR": 100,
|
||||
"pt-PT": 97,
|
||||
"pt-PT": 100,
|
||||
"ro-RO": 100,
|
||||
"ru-RU": 97,
|
||||
"ru-RU": 100,
|
||||
"sk-SK": 100,
|
||||
"sv-SE": 98,
|
||||
"tr-TR": 87,
|
||||
"uk-UA": 97,
|
||||
"zh-CN": 98,
|
||||
"sv-SE": 100,
|
||||
"tr-TR": 90,
|
||||
"uk-UA": 100,
|
||||
"zh-CN": 100,
|
||||
"zh-TW": 100
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Tryb siatki",
|
||||
"addToLibrary": "Dodaj do biblioteki",
|
||||
"removeFromLibrary": "Usuń z biblioteki",
|
||||
"libraryLoadingMessage": "Ładowanie biblioteki…",
|
||||
"libraryLoadingMessage": "Wczytywanie biblioteki...",
|
||||
"libraries": "Przeglądaj biblioteki",
|
||||
"loadingScene": "Ładowanie sceny…",
|
||||
"loadingScene": "Ładowanie sceny...",
|
||||
"align": "Wyrównaj",
|
||||
"alignTop": "Wyrównaj do góry",
|
||||
"alignBottom": "Wyrównaj do dołu",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Wyśrodkuj w pionie",
|
||||
"centerHorizontally": "Wyśrodkuj w poziomie",
|
||||
"distributeHorizontally": "Rozłóż poziomo",
|
||||
"distributeVertically": "Rozłóż pionowo",
|
||||
"viewMode": "Tryb widoku"
|
||||
"distributeVertically": "Rozłóż pionowo"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Wyczyść dokument i zresetuj kolor dokumentu",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Nie udało się odszyfrować danych.",
|
||||
"uploadedSecurly": "By zapewnić Ci prywatność, udostępnianie projektu jest zabezpieczone szyfrowaniem end-to-end, co oznacza, że poza tobą i osobą z którą podzielisz się linkiem, nikt nie ma dostępu do tego co udostępniasz.",
|
||||
"loadSceneOverridePrompt": "Wczytanie zewnętrznego rysunku zastąpi istniejącą zawartość. Czy chcesz kontynuować?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Wystąpił błąd podczas ładowania zewnętrznej biblioteki.",
|
||||
"confirmAddLibrary": "To doda {{numShapes}} kształtów do twojej biblioteki. Jesteś pewien?",
|
||||
"imageDoesNotContainScene": "Importowanie zdjęć nie jest obecnie obsługiwane.\n\nCzy chciałeś zaimportować scenę? Ten obraz nie zawiera żadnych danych sceny. Czy włączyłeś to podczas eksportowania?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Pamięć",
|
||||
"title": "Statystyki dla nerdów",
|
||||
"total": "Łącznie",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Szerokość"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Modo grade",
|
||||
"addToLibrary": "Adicionar à biblioteca",
|
||||
"removeFromLibrary": "Remover da biblioteca",
|
||||
"libraryLoadingMessage": "Carregando biblioteca…",
|
||||
"libraryLoadingMessage": "Carregando biblioteca...",
|
||||
"libraries": "Procurar bibliotecas",
|
||||
"loadingScene": "Carregando cena…",
|
||||
"loadingScene": "Carregando cena...",
|
||||
"align": "Alinhamento",
|
||||
"alignTop": "Alinhar ao topo",
|
||||
"alignBottom": "Alinhar embaixo",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centralizar verticalmente",
|
||||
"centerHorizontally": "Centralizar horizontalmente",
|
||||
"distributeHorizontally": "Distribuir horizontalmente",
|
||||
"distributeVertically": "Distribuir verticalmente",
|
||||
"viewMode": "Modo de visualização"
|
||||
"distributeVertically": "Distribuir verticalmente"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Não foi possível descriptografar os dados.",
|
||||
"uploadedSecurly": "O upload foi protegido com criptografia de ponta a ponta, o que significa que o servidor do Excalidraw e terceiros não podem ler o conteúdo.",
|
||||
"loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?",
|
||||
"collabStopOverridePrompt": "Ao interromper a sessão, você substituirá seu desenho anterior, armazenado localmente. Você tem certeza?\n\n(Se você deseja manter seu desenho local, simplesmente feche a aba do navegador.)",
|
||||
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
|
||||
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
|
||||
"imageDoesNotContainScene": "A importação de imagens não é suportada no momento.\n\nVocê deseja importar uma cena? Esta imagem parece não conter dados de cena. Você ativou isto durante a exportação?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Armazenamento",
|
||||
"title": "Estatísticas para nerds",
|
||||
"total": "Total",
|
||||
"version": "Versão",
|
||||
"versionCopy": "Clique para copiar",
|
||||
"versionNotAvailable": "Versão não disponível",
|
||||
"width": "Largura"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Estilos copiados.",
|
||||
"copyToClipboard": "Copiado para área de transferência.",
|
||||
"copyToClipboardAsPng": "Copiado para a área de transferência como PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Modo grade",
|
||||
"addToLibrary": "Adicionar à biblioteca",
|
||||
"removeFromLibrary": "Remover da biblioteca",
|
||||
"libraryLoadingMessage": "Carregando biblioteca…",
|
||||
"libraryLoadingMessage": "Carregando biblioteca...",
|
||||
"libraries": "Procurar bibliotecas",
|
||||
"loadingScene": "Carregando cena…",
|
||||
"loadingScene": "Carregando cena...",
|
||||
"align": "Alinhamento",
|
||||
"alignTop": "Alinhar ao topo",
|
||||
"alignBottom": "Alinhar ao fundo",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centralizar verticalmente",
|
||||
"centerHorizontally": "Centralizar horizontalmente",
|
||||
"distributeHorizontally": "Distribuir horizontalmente",
|
||||
"distributeVertically": "Distribuir verticalmente",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Distribuir verticalmente"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Não foi possível descriptografar os dados.",
|
||||
"uploadedSecurly": "O upload foi protegido com criptografia de ponta a ponta, o que significa que o servidor do Excalidraw e terceiros não podem ler o conteúdo.",
|
||||
"loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
|
||||
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
|
||||
"imageDoesNotContainScene": "A importação de imagens não é suportada no momento.\n\nVocê deseja importar uma cena? Esta imagem parece não conter dados de cena. Você ativou isto durante a exportação?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Armazenamento",
|
||||
"title": "Estatísticas para nerds",
|
||||
"total": "Total",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Largura"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Estilos copiados.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Copiado para o clipboard como PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Mod grilă",
|
||||
"addToLibrary": "Adăugare la 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",
|
||||
"alignTop": "Aliniere sus",
|
||||
"alignBottom": "Aliniere jos",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centrare verticală",
|
||||
"centerHorizontally": "Centrare orizontală",
|
||||
"distributeHorizontally": "Distribuie orizontal",
|
||||
"distributeVertically": "Distribuie vertical",
|
||||
"viewMode": "Mod de vizualizare"
|
||||
"distributeVertically": "Distribuie vertical"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Resetare pânză",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Datele nu au putut fi decriptate.",
|
||||
"uploadedSecurly": "Încărcarea a fost securizată prin criptare integrală, însemnând că serverul Excalidraw și terții nu pot citi conținutul.",
|
||||
"loadSceneOverridePrompt": "Încărcarea desenului extern va înlocui conținutul existent. Dorești să continui?",
|
||||
"collabStopOverridePrompt": "Oprirea sesiunii va suprascrie desenul anterior stocat local. Confirmi alegerea?\n\n(Dacă vrei să păstrezi desenul local, pur și simplu închide fila navigatorului în schimb.)",
|
||||
"errorLoadingLibrary": "A apărut o eroare la încărcarea bibliotecii terțe.",
|
||||
"confirmAddLibrary": "Această acțiune va adăuga {{numShapes}} formă(e) la biblioteca ta. Confirmi?",
|
||||
"imageDoesNotContainScene": "Importarea imaginilor nu este acceptată în acest moment.\n\nVoiai să imporți o scenă? Această imagine nu pare să conțină date de scenă. Ai activat această opțiune pe durata exportării?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Stocare",
|
||||
"title": "Statistici pentru pasionați",
|
||||
"total": "Total",
|
||||
"version": "Versiune",
|
||||
"versionCopy": "Clic pentru copiere",
|
||||
"versionNotAvailable": "Versiune indisponibilă",
|
||||
"width": "Lățime"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Stiluri copiate.",
|
||||
"copyToClipboard": "Copiat în memoria temporară.",
|
||||
"copyToClipboardAsPng": "Copiat în memoria temporară ca PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Сетка",
|
||||
"addToLibrary": "Добавить в библиотеку",
|
||||
"removeFromLibrary": "Удалить из библиотеки",
|
||||
"libraryLoadingMessage": "Загрузка библиотеки…",
|
||||
"libraryLoadingMessage": "Загрузка библиотеки...",
|
||||
"libraries": "Просмотреть библиотеки",
|
||||
"loadingScene": "Загрузка сцены…",
|
||||
"loadingScene": "Загрузка сцены...",
|
||||
"align": "Выровнять",
|
||||
"alignTop": "Выровнять по верхнему краю",
|
||||
"alignBottom": "Выровнять по нижнему краю",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Центрировать по вертикали",
|
||||
"centerHorizontally": "Центрировать по горизонтали",
|
||||
"distributeHorizontally": "Распределить по горизонтали",
|
||||
"distributeVertically": "Распределить по вертикали",
|
||||
"viewMode": "Вид"
|
||||
"distributeVertically": "Распределить по вертикали"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Очистить холст и сбросить цвет фона",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Не удалось расшифровать данные.",
|
||||
"uploadedSecurly": "Загружаемые данные защищена сквозным шифрованием, что означает, что сервер Excalidraw и третьи стороны не могут прочитать содержимое.",
|
||||
"loadSceneOverridePrompt": "Загрузка рисунка приведёт к замене имеющегося содержимого. Вы хотите продолжить?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Произошла ошибка при загрузке сторонней библиотеки.",
|
||||
"confirmAddLibrary": "Будет добавлено {{numShapes}} фигур в вашу библиотеку. Продолжить?",
|
||||
"imageDoesNotContainScene": "Импорт изображений не поддерживается в данный момент.\n\nХотите импортировать сцену? Данное изображение не содержит данных о сцене. Было ли включено это во время экспорта?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Хранилище",
|
||||
"title": "Статистика для ботаников",
|
||||
"total": "Всего",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Ширина"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Скопированы стили.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Скопировано в буфер обмена в формате PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Režim mriežky",
|
||||
"addToLibrary": "Pridať do 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",
|
||||
"alignTop": "Zarovnať nahor",
|
||||
"alignBottom": "Zarovnať nadol",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Zarovnať zvislo na stred",
|
||||
"centerHorizontally": "Zarovnať vodorovne na stred",
|
||||
"distributeHorizontally": "Rozmiestniť vodorovne",
|
||||
"distributeVertically": "Rozmiestniť zvisle",
|
||||
"viewMode": "Režim zobrazenia"
|
||||
"distributeVertically": "Rozmiestniť zvisle"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Obnoviť plátno",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Nepodarilo sa rozšifrovať údaje.",
|
||||
"uploadedSecurly": "Nahratie je zabezpečené end-to-end šifrovaním, takže Excalidraw server a tretie strany nedokážu prečítať jeho obsah.",
|
||||
"loadSceneOverridePrompt": "Nahratie externej kresby nahradí existujúci obsah. Prajete si pokračovať?",
|
||||
"collabStopOverridePrompt": "Ukončenie schôdze nahradí vašu predchádzajúcu lokálne uloženú scénu. Ste si istý?\n\n(Ak si chcete ponechať lokálnu scénu, jednoducho iba zavrite kartu prehliadača.)",
|
||||
"errorLoadingLibrary": "Nepodarilo sa načítať externú knižnicu.",
|
||||
"confirmAddLibrary": "Týmto sa pridá {{numShapes}} tvar(ov) do vašej knižnice. Ste si istí?",
|
||||
"imageDoesNotContainScene": "Importovanie obrázku v tomto momente nie je možné.\n\nChceli ste importovať scénu? Tento obrázok neobsahuje žiadne údaje scény. Povolili ste túto možnosť počas exportovania?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Úložisko",
|
||||
"title": "Štatistiky",
|
||||
"total": "Celkom",
|
||||
"version": "Verzia",
|
||||
"versionCopy": "Kliknutím skopírujete",
|
||||
"versionNotAvailable": "Verzia nie je k dispozícii",
|
||||
"width": "Šírka"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Štýly skopírované.",
|
||||
"copyToClipboard": "Skopírované do schránky.",
|
||||
"copyToClipboardAsPng": "Skopírované do schránky ako PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Rutnätsläge",
|
||||
"addToLibrary": "Lägg till i biblioteket",
|
||||
"removeFromLibrary": "Ta bort från bibliotek",
|
||||
"libraryLoadingMessage": "Laddar bibliotek…",
|
||||
"libraryLoadingMessage": "Laddar bibliotek...",
|
||||
"libraries": "Bläddra i bibliotek",
|
||||
"loadingScene": "Laddar skiss…",
|
||||
"loadingScene": "Laddar scen...",
|
||||
"align": "Justera",
|
||||
"alignTop": "Justera överkant",
|
||||
"alignBottom": "Justera underkant",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Centrera vertikalt",
|
||||
"centerHorizontally": "Centrera horisontellt",
|
||||
"distributeHorizontally": "Fördela horisontellt",
|
||||
"distributeVertically": "Fördela vertikalt",
|
||||
"viewMode": "Visningsläge"
|
||||
"distributeVertically": "Fördela vertikalt"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Återställ canvasen",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Kunde inte avkryptera data.",
|
||||
"uploadedSecurly": "Uppladdning har säkrats med kryptering från ände till ände. vilket innebär att Excalidraw server och tredje part inte kan läsa innehållet.",
|
||||
"loadSceneOverridePrompt": "Laddning av extern skiss kommer att ersätta ditt befintliga innehåll. Vill du fortsätta?",
|
||||
"collabStopOverridePrompt": "Att stoppa sessionen kommer att skriva över din föregående, lokalt lagrade skiss. Är du säker?\n\n(Om du vill behålla din lokala skiss, stäng bara webbläsarfliken istället.)",
|
||||
"errorLoadingLibrary": "Fel vid inläsning av tredjeparts bibliotek.",
|
||||
"confirmAddLibrary": "Detta kommer att lägga till {{numShapes}} form(er) till ditt bibliotek. Är du säker?",
|
||||
"imageDoesNotContainScene": "Importering av bilder stöds inte just nu.\n\nVill du importera en skiss? Den här bilden verkar inte innehålla någon skissdata. Har du aktiverat detta under export?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Lagring",
|
||||
"title": "Statistik för nördar",
|
||||
"total": "Totalt",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Bredd"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Kopierade stilar.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Kopierat till urklipp som PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Izgara modu",
|
||||
"addToLibrary": "Kütüphaneye ekle",
|
||||
"removeFromLibrary": "Kütüphaneden kaldır",
|
||||
"libraryLoadingMessage": "Kütüphane yükleniyor…",
|
||||
"libraryLoadingMessage": "Kütüphane yükleniyor...",
|
||||
"libraries": "Kütüphanelere gözat",
|
||||
"loadingScene": "Çalışma alanı yükleniyor…",
|
||||
"loadingScene": "Çalışma alanı yükleniyor...",
|
||||
"align": "Hizala",
|
||||
"alignTop": "Yukarı hizala",
|
||||
"alignBottom": "Aşağı hizala",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Dikeyde ortala",
|
||||
"centerHorizontally": "Yatayda ortala",
|
||||
"distributeHorizontally": "Yatay dağıt",
|
||||
"distributeVertically": "Dikey dağıt",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Dikey dağıt"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Tuvali sıfırla",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Şifrelenmiş veri çözümlenemedi.",
|
||||
"uploadedSecurly": "Yükleme uçtan uca şifreleme ile korunmaktadır. Excalidraw sunucusu ve üçüncül şahıslar içeriği okuyamayacaktır.",
|
||||
"loadSceneOverridePrompt": "Harici çizimler yüklemek mevcut olan içeriği değiştirecektir. Devam etmek istiyor musunuz?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Üçüncü taraf kitaplığı yüklerken bir hata oluştu.",
|
||||
"confirmAddLibrary": "Bu, kitaplığınıza {{numShapes}} tane şekil ekleyecek. Emin misiniz?",
|
||||
"imageDoesNotContainScene": "Resim ekleme şuan için desteklenmiyor.\nBir sahneyi içeri aktarmak mı istediniz? Bu dosya herhangi bir sahne içeriyor gibi durmuyor. Çıktı alırken sahneyi dahil ettiniz mi?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Depolama",
|
||||
"title": "İnekler için istatistikler",
|
||||
"total": "Toplam",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Genişlik"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "Режим сітки",
|
||||
"addToLibrary": "Додати до бібліотеки",
|
||||
"removeFromLibrary": "Видалити з бібліотеки",
|
||||
"libraryLoadingMessage": "Завантажити бібліотеку…",
|
||||
"libraryLoadingMessage": "Завантажити бібліотеку...",
|
||||
"libraries": "Огляд бібліотек",
|
||||
"loadingScene": "Завантаження сцени…",
|
||||
"loadingScene": "Завантаження сцени...",
|
||||
"align": "Вирівнювання",
|
||||
"alignTop": "Вирівняти по верхньому краю",
|
||||
"alignBottom": "Вирівняти по нижньому краю",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "Центрувати по вертикалі",
|
||||
"centerHorizontally": "Центрувати по горизонталі",
|
||||
"distributeHorizontally": "Розподілити по горизонталі",
|
||||
"distributeVertically": "Розподілити вертикально",
|
||||
"viewMode": ""
|
||||
"distributeVertically": "Розподілити вертикально"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Очистити полотно",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "Не вдалося розшифрувати дані.",
|
||||
"uploadedSecurly": "Це завантаження було захищене наскрізним шифруванням, а це означає що сервер Excalidraw та інші не зможуть прочитати вміст.",
|
||||
"loadSceneOverridePrompt": "Завантаження зовнішнього креслення замінить ваш наявний контент. Продовжити?",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "Помилка при завантаженні сторонньої бібліотеки.",
|
||||
"confirmAddLibrary": "Це призведе до додавання {{numShapes}} фігур до вашої бібліотеки. Ви впевнені?",
|
||||
"imageDoesNotContainScene": "Імпортування зображень на даний момент не підтримується.\n\nЧи хочете ви імпортувати сцену? Це зображення не містить ніяких даних сцен. Ви увімкнули це під час експорту?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "Сховище",
|
||||
"title": "Статистика",
|
||||
"total": "Всього",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Ширина"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Скопійовані стилі.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Скопійовано в буфер обміну як PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"gridMode": "网格模式",
|
||||
"addToLibrary": "添加到库中",
|
||||
"removeFromLibrary": "从库中移除",
|
||||
"libraryLoadingMessage": "正在加载库…",
|
||||
"libraryLoadingMessage": "正在加载库...",
|
||||
"libraries": "浏览库",
|
||||
"loadingScene": "正在加载绘图…",
|
||||
"loadingScene": "正在加载绘图...",
|
||||
"align": "对齐",
|
||||
"alignTop": "顶部对齐",
|
||||
"alignBottom": "底端对齐",
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "垂直居中",
|
||||
"centerHorizontally": "水平居中",
|
||||
"distributeHorizontally": "水平等距分布",
|
||||
"distributeVertically": "垂直等距分布",
|
||||
"viewMode": "查看模式"
|
||||
"distributeVertically": "垂直等距分布"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "重置画布",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "无法解密数据。",
|
||||
"uploadedSecurly": "上传已被端到端加密保护,这意味着 Excalidraw 的服务器和第三方都无法读取内容。",
|
||||
"loadSceneOverridePrompt": "加载外部绘图将取代您现有的内容。您想要继续吗?",
|
||||
"collabStopOverridePrompt": "停止会话将覆盖您先前本地存储的绘图。 您确定吗?\n\n(如果您想保持本地绘图,只需关闭浏览器选项卡。)",
|
||||
"errorLoadingLibrary": "加载第三方库时出错。",
|
||||
"confirmAddLibrary": "这将添加 {{numShapes}} 个形状到您的库。您确定吗?",
|
||||
"imageDoesNotContainScene": "当前不支持导入图片。\n\n您想要导入画布数据吗?此图像似乎不包含任何画布数据。您是否在导出过程中启用了嵌入画布的选项?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "存储",
|
||||
"title": "详细统计信息",
|
||||
"total": "总计",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "宽度"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "复制样式",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "复制为 PNG 到剪贴板"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,7 @@
|
||||
"centerVertically": "垂直置中",
|
||||
"centerHorizontally": "水平置中",
|
||||
"distributeHorizontally": "水平分布",
|
||||
"distributeVertically": "垂直分布",
|
||||
"viewMode": "檢視模式"
|
||||
"distributeVertically": "垂直分布"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "重置 canvas",
|
||||
@@ -136,7 +135,6 @@
|
||||
"decryptFailed": "無法解密資料。",
|
||||
"uploadedSecurly": "上傳已通過 end-to-end 加密,Excalidraw 伺服器和第三方無法皆讀取其內容。",
|
||||
"loadSceneOverridePrompt": "讀取外部圖樣將取代目前的內容。是否要繼續?",
|
||||
"collabStopOverridePrompt": "停止連線將覆蓋您先前於本機儲存的繪圖進度,是否確認?\n\n(如要保留原有的本機繪圖進度,直接關閉瀏覽器分頁即可。)",
|
||||
"errorLoadingLibrary": "載入第三方套件時出現錯誤。",
|
||||
"confirmAddLibrary": "這將會將 {{numShapes}} 個圖形加入你的資料庫,你確定嗎?",
|
||||
"imageDoesNotContainScene": "目前尚不支援載入圖片。\n您是否要載入場景?此圖片中並無任何場景資料,輸出時是否有選擇包含?",
|
||||
@@ -235,14 +233,10 @@
|
||||
"storage": "儲存",
|
||||
"title": "詳細統計",
|
||||
"total": "合計",
|
||||
"version": "版本",
|
||||
"versionCopy": "點擊複製",
|
||||
"versionNotAvailable": "無法取得版本",
|
||||
"width": "寬度"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "已複製樣式",
|
||||
"copyToClipboard": "複製至剪貼簿。",
|
||||
"copyToClipboardAsPng": "已複製 PNG 至剪貼簿"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,17 @@ Please add the latest change on the top under the correct section.
|
||||
|
||||
## Excalidraw API
|
||||
|
||||
### Features
|
||||
|
||||
- Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host.
|
||||
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
||||
|
||||
## Excalidraw Library
|
||||
|
||||
### Features
|
||||
|
||||
- Add view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840).
|
||||
- Remove `copy`, `cut`, and `paste` actions from contextmenu [#2872](https://github.com/excalidraw/excalidraw/pull/2872)
|
||||
- Support `Ctrl-Y` shortcut to redo on Windows [#2831](https://github.com/excalidraw/excalidraw/pull/2831).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix UI pointer-events not disabled when dragging on canvas [#2856](https://github.com/excalidraw/excalidraw/pull/2856).
|
||||
- Fix remote pointers not accounting for offset [#2855](https://github.com/excalidraw/excalidraw/pull/2855).
|
||||
|
||||
## 0.2.1
|
||||
|
||||
@@ -138,7 +138,6 @@ export default function App() {
|
||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
||||
| [`langCode`](#langCode) | string | `en` | Language code string |
|
||||
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
||||
| [`viewModeEnabled`](#viewModeEnabled) | boolean | false | This implies if the app is in view mode. |
|
||||
|
||||
### `Extra API's`
|
||||
|
||||
@@ -331,7 +330,3 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
|
||||
#### `renderFooter`
|
||||
|
||||
A function that renders (returns JSX) custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
|
||||
|
||||
#### `viewModeEnabled`
|
||||
|
||||
This prop indicates if the app is in `view mode`. When this prop is used, the `view mode` will not show up in context menu is so it is fully controlled by host. Also the value of this prop if passed will be used over the value of `intialData.appState.viewModeEnabled`
|
||||
|
||||
@@ -26,7 +26,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
||||
onExportToBackend,
|
||||
renderFooter,
|
||||
langCode = defaultLang.code,
|
||||
viewModeEnabled,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -65,7 +64,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
||||
onExportToBackend={onExportToBackend}
|
||||
renderFooter={renderFooter}
|
||||
langCode={langCode}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
/>
|
||||
</IsMobileProvider>
|
||||
</InitializeApp>
|
||||
@@ -83,6 +81,7 @@ const areEqual = (
|
||||
|
||||
const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[];
|
||||
const nextKeys = Object.keys(nextProps) as (keyof typeof next)[];
|
||||
|
||||
return (
|
||||
prevUser?.name === nextUser?.name &&
|
||||
prevKeys.length === nextKeys.length &&
|
||||
@@ -90,10 +89,6 @@ const areEqual = (
|
||||
);
|
||||
};
|
||||
|
||||
Excalidraw.defaultProps = {
|
||||
lanCode: defaultLang.code,
|
||||
};
|
||||
|
||||
const forwardedRefComp = forwardRef<
|
||||
ExcalidrawAPIRefValue,
|
||||
PublicExcalidrawProps
|
||||
|
||||
@@ -36,7 +36,7 @@ export const exportToCanvas = ({
|
||||
canvas.width = ret.width;
|
||||
canvas.height = ret.height;
|
||||
|
||||
return { canvas, scale: ret.scale };
|
||||
return canvas;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -349,12 +349,12 @@ const generateElementShape = (
|
||||
if (element.type === "arrow") {
|
||||
const { startArrowhead = null, endArrowhead = "arrow" } = element;
|
||||
|
||||
function getArrowheadShapes(
|
||||
const getArrowheadShapes = (
|
||||
element: ExcalidrawLinearElement,
|
||||
shape: Drawable[],
|
||||
position: "start" | "end",
|
||||
arrowhead: Arrowhead,
|
||||
) {
|
||||
) => {
|
||||
const arrowheadPoints = getArrowheadPoints(
|
||||
element,
|
||||
shape,
|
||||
@@ -392,7 +392,7 @@ const generateElementShape = (
|
||||
generator.line(x3, y3, x2, y2, options),
|
||||
generator.line(x4, y4, x2, y2, options),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
if (startArrowhead !== null) {
|
||||
const shapes = getArrowheadShapes(
|
||||
|
||||
+11
-23
@@ -48,7 +48,6 @@ import {
|
||||
TransformHandleType,
|
||||
} from "../element/transformHandles";
|
||||
import { viewportCoordsToSceneCoords } from "../utils";
|
||||
import { UserIdleState } from "../excalidraw-app/collab/types";
|
||||
|
||||
const strokeRectWithRotation = (
|
||||
context: CanvasRenderingContext2D,
|
||||
@@ -374,14 +373,12 @@ export const renderScene = (
|
||||
sceneState.zoom,
|
||||
"mouse", // when we render we don't know which pointer type so use mouse
|
||||
);
|
||||
if (!appState.viewModeEnabled) {
|
||||
renderTransformHandles(
|
||||
context,
|
||||
sceneState,
|
||||
transformHandles,
|
||||
locallySelectedElements[0].angle,
|
||||
);
|
||||
}
|
||||
renderTransformHandles(
|
||||
context,
|
||||
sceneState,
|
||||
transformHandles,
|
||||
locallySelectedElements[0].angle,
|
||||
);
|
||||
} else if (locallySelectedElements.length > 1 && !appState.isRotating) {
|
||||
const dashedLinePadding = 4 / sceneState.zoom.value;
|
||||
context.fillStyle = oc.white;
|
||||
@@ -446,9 +443,7 @@ export const renderScene = (
|
||||
const globalAlpha = context.globalAlpha;
|
||||
context.strokeStyle = stroke;
|
||||
context.fillStyle = background;
|
||||
|
||||
const userState = sceneState.remotePointerUserStates[clientId];
|
||||
if (isOutOfBounds || userState === UserIdleState.AWAY) {
|
||||
if (isOutOfBounds) {
|
||||
context.globalAlpha = 0.2;
|
||||
}
|
||||
|
||||
@@ -481,25 +476,19 @@ export const renderScene = (
|
||||
context.stroke();
|
||||
|
||||
const username = sceneState.remotePointerUsernames[clientId];
|
||||
const usernameAndIdleState = `${username ? `${username} ` : ""}${
|
||||
userState === UserIdleState.AWAY
|
||||
? "⚫️"
|
||||
: userState === UserIdleState.IDLE
|
||||
? "💤"
|
||||
: "🟢"
|
||||
}`;
|
||||
|
||||
if (!isOutOfBounds && usernameAndIdleState) {
|
||||
if (!isOutOfBounds && username) {
|
||||
const offsetX = x + width;
|
||||
const offsetY = y + height;
|
||||
const paddingHorizontal = 4;
|
||||
const paddingVertical = 4;
|
||||
const measure = context.measureText(usernameAndIdleState);
|
||||
const measure = context.measureText(username);
|
||||
const measureHeight =
|
||||
measure.actualBoundingBoxDescent + measure.actualBoundingBoxAscent;
|
||||
|
||||
// Border
|
||||
context.fillStyle = stroke;
|
||||
context.globalAlpha = globalAlpha;
|
||||
context.fillRect(
|
||||
offsetX - 1,
|
||||
offsetY - 1,
|
||||
@@ -515,9 +504,8 @@ export const renderScene = (
|
||||
measureHeight + 2 * paddingVertical,
|
||||
);
|
||||
context.fillStyle = oc.white;
|
||||
|
||||
context.fillText(
|
||||
usernameAndIdleState,
|
||||
username,
|
||||
offsetX + paddingHorizontal,
|
||||
offsetY + paddingVertical + measure.actualBoundingBoxAscent,
|
||||
);
|
||||
|
||||
+7
-11
@@ -29,14 +29,14 @@ export const exportToCanvas = (
|
||||
viewBackgroundColor: string;
|
||||
shouldAddWatermark: boolean;
|
||||
},
|
||||
createCanvas: (
|
||||
width: number,
|
||||
height: number,
|
||||
) => { canvas: HTMLCanvasElement; scale: number } = (width, height) => {
|
||||
createCanvas: (width: number, height: number) => HTMLCanvasElement = (
|
||||
width,
|
||||
height,
|
||||
) => {
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
tempCanvas.width = width * scale;
|
||||
tempCanvas.height = height * scale;
|
||||
return { canvas: tempCanvas, scale };
|
||||
return tempCanvas;
|
||||
},
|
||||
) => {
|
||||
const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
|
||||
@@ -47,16 +47,13 @@ export const exportToCanvas = (
|
||||
shouldAddWatermark,
|
||||
);
|
||||
|
||||
const { canvas: tempCanvas, scale: newScale = scale } = createCanvas(
|
||||
width,
|
||||
height,
|
||||
);
|
||||
const tempCanvas = createCanvas(width, height);
|
||||
|
||||
renderScene(
|
||||
sceneElements,
|
||||
appState,
|
||||
null,
|
||||
newScale,
|
||||
scale,
|
||||
rough.canvas(tempCanvas),
|
||||
tempCanvas,
|
||||
{
|
||||
@@ -68,7 +65,6 @@ export const exportToCanvas = (
|
||||
remoteSelectedElementIds: {},
|
||||
shouldCacheIgnoreZoom: false,
|
||||
remotePointerUsernames: {},
|
||||
remotePointerUserStates: {},
|
||||
},
|
||||
{
|
||||
renderScrollbars: false,
|
||||
|
||||
@@ -12,7 +12,6 @@ export type SceneState = {
|
||||
remotePointerButton?: { [id: string]: string | undefined };
|
||||
remoteSelectedElementIds: { [elementId: string]: string[] };
|
||||
remotePointerUsernames: { [id: string]: string };
|
||||
remotePointerUserStates: { [id: string]: string };
|
||||
};
|
||||
|
||||
export type SceneScroll = {
|
||||
|
||||
@@ -76,7 +76,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -543,7 +542,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -992,7 +990,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -1769,7 +1766,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -1977,7 +1973,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -2429,7 +2424,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -2678,7 +2672,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -2844,7 +2837,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -3317,7 +3309,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -3627,7 +3618,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -3832,7 +3822,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -4073,7 +4062,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -4325,7 +4313,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -4727,7 +4714,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -4999,7 +4985,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -5325,7 +5310,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -5510,7 +5494,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -5673,7 +5656,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -6132,7 +6114,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -6442,7 +6423,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -8480,7 +8460,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -8842,7 +8821,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -9094,7 +9072,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -9347,7 +9324,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -9656,7 +9632,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -9819,7 +9794,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -9982,7 +9956,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -10145,7 +10118,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -10338,7 +10310,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -10531,7 +10502,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -10724,7 +10694,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -10917,7 +10886,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -11080,7 +11048,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -11243,7 +11210,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -11436,7 +11402,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -11599,7 +11564,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -11803,7 +11767,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -12511,7 +12474,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -12759,7 +12721,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -12858,7 +12819,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -12959,7 +12919,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -13122,7 +13081,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -13429,7 +13387,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -13736,7 +13693,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": "Copied styles.",
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -13897,7 +13853,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -14094,7 +14049,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -14348,7 +14302,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -14665,7 +14618,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": "Copied styles.",
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -15503,7 +15455,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -15810,7 +15761,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -16121,7 +16071,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -16498,7 +16447,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -16670,7 +16618,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -16984,7 +16931,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -17225,7 +17171,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -17481,7 +17426,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -17797,7 +17741,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -17898,7 +17841,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -18072,7 +18014,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -18879,7 +18820,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -18982,7 +18922,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -19759,7 +19698,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -20161,7 +20099,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -20409,7 +20346,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -20510,7 +20446,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -21005,7 +20940,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
@@ -21104,7 +21038,6 @@ Object {
|
||||
"suggestedBindings": Array [],
|
||||
"toastMessage": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"width": 1024,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": Object {
|
||||
|
||||
@@ -623,7 +623,6 @@ describe("regression tests", () => {
|
||||
"selectAll",
|
||||
"gridMode",
|
||||
"zenMode",
|
||||
"viewMode",
|
||||
"stats",
|
||||
];
|
||||
|
||||
|
||||
@@ -122,12 +122,12 @@ describe("resize rectangle ellipses and diamond elements", () => {
|
||||
);
|
||||
});
|
||||
|
||||
function resize(
|
||||
const resize = (
|
||||
element: ExcalidrawElement,
|
||||
handleDir: TransformHandleDirection,
|
||||
mouseMove: [number, number],
|
||||
keyboardModifiers: KeyboardModifiers = {},
|
||||
) {
|
||||
) => {
|
||||
mouse.select(element);
|
||||
const handle = getTransformHandles(element, h.state.zoom, "mouse")[
|
||||
handleDir
|
||||
@@ -140,4 +140,4 @@ function resize(
|
||||
mouse.move(mouseMove[0], mouseMove[1]);
|
||||
mouse.up();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ import { ExcalidrawImperativeAPI } from "./components/App";
|
||||
import type { ResolvablePromise } from "./utils";
|
||||
import { Spreadsheet } from "./charts";
|
||||
import { Language } from "./i18n";
|
||||
import { UserIdleState } from "./excalidraw-app/collab/types";
|
||||
|
||||
export type Point = Readonly<RoughPoint>;
|
||||
|
||||
@@ -32,7 +31,6 @@ export type Collaborator = {
|
||||
button?: "up" | "down";
|
||||
selectedElementIds?: AppState["selectedElementIds"];
|
||||
username?: string | null;
|
||||
userState?: UserIdleState;
|
||||
};
|
||||
|
||||
export type AppState = {
|
||||
@@ -87,7 +85,6 @@ export type AppState = {
|
||||
zenModeEnabled: boolean;
|
||||
appearance: "light" | "dark";
|
||||
gridSize: number | null;
|
||||
viewModeEnabled: boolean;
|
||||
|
||||
/** top-most selected groups (i.e. does not include nested groups) */
|
||||
selectedGroupIds: { [groupId: string]: boolean };
|
||||
@@ -184,7 +181,6 @@ export interface ExcalidrawProps {
|
||||
) => void;
|
||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
||||
langCode?: Language["code"];
|
||||
viewModeEnabled?: boolean;
|
||||
}
|
||||
|
||||
export type SceneData = {
|
||||
|
||||
Reference in New Issue
Block a user