Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b20fe944e7 | |||
| 1156ef6b96 | |||
| dc25fe06d0 | |||
| 1e17c1967b | |||
| 23a8891e0e | |||
| 6c81a32d62 | |||
| f0f5430313 | |||
| e18e945cd3 | |||
| bd0c6e63ff | |||
| dbae33e4f8 | |||
| 0f4a053759 | |||
| 1837147c55 | |||
| 15f698dc21 | |||
| ce507b0a0b | |||
| 02598c6163 | |||
| 3e2890bd21 | |||
| 210649f383 | |||
| f8087e01c8 | |||
| e2522645f7 | |||
| d46a9166be | |||
| 675da16ca4 | |||
| 2b1b62d8f2 | |||
| 627c56ef1c |
@@ -1,6 +1,10 @@
|
|||||||
name: Cancel previous runs
|
name: Cancel previous runs
|
||||||
|
|
||||||
on: push
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cancel:
|
cancel:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
|
|
||||||
on: push
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: Tests
|
name: Tests
|
||||||
|
|
||||||
on: push
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
|
|||||||
Generated
+55
-39
@@ -2663,70 +2663,86 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/browser": {
|
"@sentry/browser": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.0.3.tgz",
|
||||||
"integrity": "sha512-iP8Bqxj4Ye8CXA4ja77buPZfXsKiZYUgHFzBQxVMihTHA8ZZLgBMPLQI6uFfHuJJW+1/yLzOf8BhvF2zknAebg==",
|
"integrity": "sha512-Ukxh83Twql4UmUgds9wPWllE62NG71cYvm5AM6daTojvM8wFR2jh7G6GiA0WYfgMb2fw6SlbevB2xb6RDG5DzQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/core": "6.0.1",
|
"@sentry/core": "6.0.3",
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"@sentry/utils": "6.0.1",
|
"@sentry/utils": "6.0.3",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/core": {
|
"@sentry/core": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.0.3.tgz",
|
||||||
"integrity": "sha512-EoxgodyClasI8PA4GyU8Cp88W3R5ebpiLsE7fCcBcOU0DOBRkO8GAZ5IzfCDtYDJ50c9npivum5Oyj2wf8CXYw==",
|
"integrity": "sha512-UykB/4/98y2DkNvwTiL2ofFPuK3KDHc7rIRNsdj6dg6D+Cf7FRexgmWUUkZrpC/y+QBj0TPqkcFDcZAuQDa3Ag==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "6.0.1",
|
"@sentry/hub": "6.0.3",
|
||||||
"@sentry/minimal": "6.0.1",
|
"@sentry/minimal": "6.0.3",
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"@sentry/utils": "6.0.1",
|
"@sentry/utils": "6.0.3",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/hub": {
|
"@sentry/hub": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.0.3.tgz",
|
||||||
"integrity": "sha512-pGckNdhKcr7qYVXgSgA/QVGArATcmQu54YFAR5xTnkWVHpAwNmh0fc4CJCc4JBwS/LXSU1Y0nYiLQduVfnv8Cg==",
|
"integrity": "sha512-BfV32tE09rjTWM9W0kk8gzxUC2k1h57Z5dNWJ35na79+LguNNtCcI6fHlFQ3PkJca6ITYof9FI8iQHUfsHFZnw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"@sentry/utils": "6.0.1",
|
"@sentry/utils": "6.0.3",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/integrations": {
|
"@sentry/integrations": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.0.3.tgz",
|
||||||
"integrity": "sha512-5HGwKW0otSVXSLAJ9ezqlux4AYdeX6ElzQgpm6roWEBXEWf/5OyD0n+M3+yHq4NdQXk2kkfL/0DCyNdy8zZX2Q==",
|
"integrity": "sha512-SE/rQ+ttfoC6FlHDibB4e9lV95j78YkjQ6PvYNUe+zGkGIretCJREqgaS+W3qTNYvOdbUViuiiqtdfyvW9nM2g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"@sentry/utils": "6.0.1",
|
"@sentry/utils": "6.0.3",
|
||||||
"localforage": "1.8.1",
|
"localforage": "^1.8.1",
|
||||||
"tslib": "^1.9.3"
|
"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": {
|
"@sentry/minimal": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.0.3.tgz",
|
||||||
"integrity": "sha512-TQ/M5A+OsxtQJ8dzHwrclxKXpJNdQeM1PUoYhff4BvsOXJScvZb7+Yn0OUEQXEc9pSMNt62tnQy4ct80iAMTHw==",
|
"integrity": "sha512-YsW+nw0SMyyb7UQdjZeKlZjxbGsJFpXNLh9iIp6fHKnoLTTv17YPm2ej9sOikDsQuVotaPg/xn/Qt5wySGHIxw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "6.0.1",
|
"@sentry/hub": "6.0.3",
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/types": {
|
"@sentry/types": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.0.3.tgz",
|
||||||
"integrity": "sha512-cEoe19vtam75Tf6eWmaobfbeV8XwBdr5FJoSVTomzcSsEiP2FHGOEhlE7kVBigzeH5Lri0aibiW6BDi1hIqHdg=="
|
"integrity": "sha512-266aBQbk9AGedhG2dzXshWbn23LYLElXqlI74DLku48UrU2v7TGKdyik/8/nfOfquCoRSp0GFGYHbItwU124XQ=="
|
||||||
},
|
},
|
||||||
"@sentry/utils": {
|
"@sentry/utils": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.0.3.tgz",
|
||||||
"integrity": "sha512-bjGuBYnG6fulZ8mLhPGBxttNu96DCN6d7Glw2sfLf4aurn1kjJ/58hP2c8dH0OqWO5e+rGYTsZ5Dr5kqVKNGTg==",
|
"integrity": "sha512-lvuBFvZHYs1zYwI8dkC8Z8ryb0aYnwPFUl1rbZiMwJpYI2Dgl1jpqqZWv9luux2rSRYOMid74uGedV708rvEgA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "6.0.1",
|
"@sentry/types": "6.0.3",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -14246,9 +14262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"localforage": {
|
"localforage": {
|
||||||
"version": "1.8.1",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz",
|
||||||
"integrity": "sha512-azSSJJfc7h4bVpi0PGi+SmLQKJl2/8NErI+LhJsrORNikMZnhaQ7rv9fHj+ofwgSHrKRlsDCL/639a6nECIKuQ==",
|
"integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lie": "3.1.1"
|
"lie": "3.1.1"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/browser": "6.0.1",
|
"@sentry/browser": "6.0.3",
|
||||||
"@sentry/integrations": "6.0.1",
|
"@sentry/integrations": "6.0.3",
|
||||||
"@testing-library/jest-dom": "5.11.9",
|
"@testing-library/jest-dom": "5.11.9",
|
||||||
"@testing-library/react": "11.2.3",
|
"@testing-library/react": "11.2.3",
|
||||||
"@types/jest": "26.0.20",
|
"@types/jest": "26.0.20",
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
|
|
||||||
<!-- Excalidraw version -->
|
<!-- Excalidraw version -->
|
||||||
<meta name="version" content="{version}" />
|
<meta name="version" content="{version}" />
|
||||||
|
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="FG_Virgil.woff2"
|
href="FG_Virgil.woff2"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const crowdinMap = {
|
|||||||
"id-ID": "en-id",
|
"id-ID": "en-id",
|
||||||
"it-IT": "en-it",
|
"it-IT": "en-it",
|
||||||
"ja-JP": "en-ja",
|
"ja-JP": "en-ja",
|
||||||
|
"kab-KAB": "en-kab",
|
||||||
"ko-KR": "en-ko",
|
"ko-KR": "en-ko",
|
||||||
"my-MM": "en-my",
|
"my-MM": "en-my",
|
||||||
"nb-NO": "en-nb",
|
"nb-NO": "en-nb",
|
||||||
@@ -40,7 +41,7 @@ const crowdinMap = {
|
|||||||
const flags = {
|
const flags = {
|
||||||
"ar-SA": "🇸🇦",
|
"ar-SA": "🇸🇦",
|
||||||
"bg-BG": "🇧🇬",
|
"bg-BG": "🇧🇬",
|
||||||
"ca-ES": "🇪🇸",
|
"ca-ES": "🏳",
|
||||||
"de-DE": "🇩🇪",
|
"de-DE": "🇩🇪",
|
||||||
"el-GR": "🇬🇷",
|
"el-GR": "🇬🇷",
|
||||||
"es-ES": "🇪🇸",
|
"es-ES": "🇪🇸",
|
||||||
@@ -53,6 +54,7 @@ const flags = {
|
|||||||
"id-ID": "🇮🇩",
|
"id-ID": "🇮🇩",
|
||||||
"it-IT": "🇮🇹",
|
"it-IT": "🇮🇹",
|
||||||
"ja-JP": "🇯🇵",
|
"ja-JP": "🇯🇵",
|
||||||
|
"kab-KAB": "🏳",
|
||||||
"ko-KR": "🇰🇷",
|
"ko-KR": "🇰🇷",
|
||||||
"my-MM": "🇲🇲",
|
"my-MM": "🇲🇲",
|
||||||
"nb-NO": "🇳🇴",
|
"nb-NO": "🇳🇴",
|
||||||
@@ -88,6 +90,7 @@ const languages = {
|
|||||||
"id-ID": "Bahasa Indonesia",
|
"id-ID": "Bahasa Indonesia",
|
||||||
"it-IT": "Italiano",
|
"it-IT": "Italiano",
|
||||||
"ja-JP": "日本語",
|
"ja-JP": "日本語",
|
||||||
|
"kab-KAB": "Taqbaylit",
|
||||||
"ko-KR": "한국어",
|
"ko-KR": "한국어",
|
||||||
"my-MM": "Burmese",
|
"my-MM": "Burmese",
|
||||||
"nb-NO": "Norsk bokmål",
|
"nb-NO": "Norsk bokmål",
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { CODES, KEYS } from "../keys";
|
|||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
import { GRID_SIZE } from "../constants";
|
import { GRID_SIZE } from "../constants";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
|
import { trackEvent } from "../analytics";
|
||||||
|
|
||||||
export const actionToggleGridMode = register({
|
export const actionToggleGridMode = register({
|
||||||
name: "gridMode",
|
name: "gridMode",
|
||||||
perform(elements, appState) {
|
perform(elements, appState) {
|
||||||
|
trackEvent("view", "mode", "grid");
|
||||||
return {
|
return {
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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,9 +1,12 @@
|
|||||||
import { CODES, KEYS } from "../keys";
|
import { CODES, KEYS } from "../keys";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
|
import { trackEvent } from "../analytics";
|
||||||
|
|
||||||
export const actionToggleZenMode = register({
|
export const actionToggleZenMode = register({
|
||||||
name: "zenMode",
|
name: "zenMode",
|
||||||
perform(elements, appState) {
|
perform(elements, appState) {
|
||||||
|
trackEvent("view", "mode", "zen");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
ActionResult,
|
ActionResult,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { AppState } from "../types";
|
import { AppState, ExcalidrawProps } from "../types";
|
||||||
|
|
||||||
// This is the <App> component, but for now we don't care about anything but its
|
// This is the <App> component, but for now we don't care about anything but its
|
||||||
// `canvas` state.
|
// `canvas` state.
|
||||||
type App = { canvas: HTMLCanvasElement | null };
|
type App = { canvas: HTMLCanvasElement | null; props: ExcalidrawProps };
|
||||||
|
|
||||||
export class ActionManager implements ActionsManagerInterface {
|
export class ActionManager implements ActionsManagerInterface {
|
||||||
actions = {} as ActionsManagerInterface["actions"];
|
actions = {} as ActionsManagerInterface["actions"];
|
||||||
@@ -66,6 +66,12 @@ export class ActionManager implements ActionsManagerInterface {
|
|||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const { viewModeEnabled } = this.getAppState();
|
||||||
|
if (viewModeEnabled) {
|
||||||
|
if (data[0].name !== "viewMode") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.updater(
|
this.updater(
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export type ShortcutName =
|
|||||||
| "gridMode"
|
| "gridMode"
|
||||||
| "zenMode"
|
| "zenMode"
|
||||||
| "stats"
|
| "stats"
|
||||||
| "addToLibrary";
|
| "addToLibrary"
|
||||||
|
| "viewMode";
|
||||||
|
|
||||||
const shortcutMap: Record<ShortcutName, string[]> = {
|
const shortcutMap: Record<ShortcutName, string[]> = {
|
||||||
cut: [getShortcutKey("CtrlOrCmd+X")],
|
cut: [getShortcutKey("CtrlOrCmd+X")],
|
||||||
@@ -56,6 +57,7 @@ const shortcutMap: Record<ShortcutName, string[]> = {
|
|||||||
zenMode: [getShortcutKey("Alt+Z")],
|
zenMode: [getShortcutKey("Alt+Z")],
|
||||||
stats: [],
|
stats: [],
|
||||||
addToLibrary: [],
|
addToLibrary: [],
|
||||||
|
viewMode: [getShortcutKey("Alt+R")],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getShortcutFromShortcutName = (name: ShortcutName) => {
|
export const getShortcutFromShortcutName = (name: ShortcutName) => {
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ export type ActionName =
|
|||||||
| "alignVerticallyCentered"
|
| "alignVerticallyCentered"
|
||||||
| "alignHorizontallyCentered"
|
| "alignHorizontallyCentered"
|
||||||
| "distributeHorizontally"
|
| "distributeHorizontally"
|
||||||
| "distributeVertically";
|
| "distributeVertically"
|
||||||
|
| "viewMode";
|
||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
name: ActionName;
|
name: ActionName;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export const getDefaultAppState = (): Omit<
|
|||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
zenModeEnabled: false,
|
zenModeEnabled: false,
|
||||||
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
||||||
|
viewModeEnabled: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,6 +152,7 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
width: { browser: false, export: false },
|
width: { browser: false, export: false },
|
||||||
zenModeEnabled: { browser: true, export: false },
|
zenModeEnabled: { browser: true, export: false },
|
||||||
zoom: { browser: true, export: false },
|
zoom: { browser: true, export: false },
|
||||||
|
viewModeEnabled: { browser: false, export: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
||||||
|
|||||||
+209
-62
@@ -2,6 +2,8 @@ import { Point, simplify } from "points-on-curve";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "roughjs/bin/rough";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
import "../actions";
|
import "../actions";
|
||||||
import {
|
import {
|
||||||
actionAddToLibrary,
|
actionAddToLibrary,
|
||||||
@@ -175,10 +177,11 @@ import {
|
|||||||
withBatchedUpdates,
|
withBatchedUpdates,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { isMobile } from "../is-mobile";
|
import { isMobile } from "../is-mobile";
|
||||||
import ContextMenu from "./ContextMenu";
|
import ContextMenu, { ContextMenuOption } from "./ContextMenu";
|
||||||
import LayerUI from "./LayerUI";
|
import LayerUI from "./LayerUI";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { Toast } from "./Toast";
|
import { Toast } from "./Toast";
|
||||||
|
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||||
|
|
||||||
const { history } = createHistory();
|
const { history } = createHistory();
|
||||||
|
|
||||||
@@ -295,6 +298,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetLeft,
|
offsetLeft,
|
||||||
offsetTop,
|
offsetTop,
|
||||||
excalidrawRef,
|
excalidrawRef,
|
||||||
|
viewModeEnabled = false,
|
||||||
} = props;
|
} = props;
|
||||||
this.state = {
|
this.state = {
|
||||||
...defaultAppState,
|
...defaultAppState,
|
||||||
@@ -302,6 +306,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
||||||
|
viewModeEnabled,
|
||||||
};
|
};
|
||||||
if (excalidrawRef) {
|
if (excalidrawRef) {
|
||||||
const readyPromise =
|
const readyPromise =
|
||||||
@@ -342,6 +347,62 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.actionManager.registerAction(createRedoAction(history));
|
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() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
@@ -349,20 +410,19 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
height: canvasDOMHeight,
|
height: canvasDOMHeight,
|
||||||
offsetTop,
|
offsetTop,
|
||||||
offsetLeft,
|
offsetLeft,
|
||||||
|
viewModeEnabled,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
|
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_X = canvasDOMWidth / 2;
|
||||||
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
|
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="excalidraw"
|
className={clsx("excalidraw", {
|
||||||
|
"excalidraw--view-mode": viewModeEnabled,
|
||||||
|
})}
|
||||||
ref={this.excalidrawContainerRef}
|
ref={this.excalidrawContainerRef}
|
||||||
style={{
|
style={{
|
||||||
width: canvasDOMWidth,
|
width: canvasDOMWidth,
|
||||||
@@ -392,10 +452,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
isCollaborating={this.props.isCollaborating || false}
|
isCollaborating={this.props.isCollaborating || false}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderCustomFooter={renderFooter}
|
renderCustomFooter={renderFooter}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
|
<div className="excalidraw-textEditorContainer" />
|
||||||
{this.state.showStats && (
|
{this.state.showStats && (
|
||||||
<Stats
|
<Stats
|
||||||
appState={this.state}
|
appState={this.state}
|
||||||
|
setAppState={this.setAppState}
|
||||||
elements={this.scene.getElements()}
|
elements={this.scene.getElements()}
|
||||||
onClose={this.toggleStats}
|
onClose={this.toggleStats}
|
||||||
/>
|
/>
|
||||||
@@ -406,28 +469,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
clearToast={this.clearToast}
|
clearToast={this.clearToast}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<main>
|
<main>{this.renderCanvas()}</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -467,6 +509,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (actionResult.commitToHistory) {
|
if (actionResult.commitToHistory) {
|
||||||
history.resumeRecording();
|
history.resumeRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||||
|
|
||||||
|
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||||
|
viewModeEnabled = this.props.viewModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
...actionResult.appState,
|
...actionResult.appState,
|
||||||
@@ -476,6 +525,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
height: state.height,
|
height: state.height,
|
||||||
offsetTop: state.offsetTop,
|
offsetTop: state.offsetTop,
|
||||||
offsetLeft: state.offsetLeft,
|
offsetLeft: state.offsetLeft,
|
||||||
|
viewModeEnabled,
|
||||||
}),
|
}),
|
||||||
() => {
|
() => {
|
||||||
if (actionResult.syncHistory) {
|
if (actionResult.syncHistory) {
|
||||||
@@ -658,7 +708,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scene.addCallback(this.onSceneUpdated);
|
this.scene.addCallback(this.onSceneUpdated);
|
||||||
|
|
||||||
this.addEventListeners();
|
this.addEventListeners();
|
||||||
|
|
||||||
// optim to avoid extra render on init
|
// optim to avoid extra render on init
|
||||||
@@ -725,25 +774,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private addEventListeners() {
|
private addEventListeners() {
|
||||||
|
this.removeEventListeners();
|
||||||
document.addEventListener(EVENT.COPY, this.onCopy);
|
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.KEYDOWN, this.onKeyDown, false);
|
||||||
document.addEventListener(EVENT.KEYUP, this.onKeyUp, { passive: true });
|
document.addEventListener(EVENT.KEYUP, this.onKeyUp, { passive: true });
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
EVENT.MOUSE_MOVE,
|
EVENT.MOUSE_MOVE,
|
||||||
this.updateCurrentCursorPosition,
|
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
|
// rerender text elements on font load to fix #637 && #1553
|
||||||
document.fonts?.addEventListener?.("loadingdone", this.onFontLoaded);
|
document.fonts?.addEventListener?.("loadingdone", this.onFontLoaded);
|
||||||
|
|
||||||
// Safari-only desktop pinch zoom
|
// Safari-only desktop pinch zoom
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
EVENT.GESTURE_START,
|
EVENT.GESTURE_START,
|
||||||
@@ -760,6 +800,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.onGestureEnd as any,
|
this.onGestureEnd as any,
|
||||||
false,
|
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) {
|
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
||||||
@@ -782,6 +834,17 @@ 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
|
document
|
||||||
.querySelector(".excalidraw")
|
.querySelector(".excalidraw")
|
||||||
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
||||||
@@ -821,6 +884,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
const pointerViewportCoords: SceneState["remotePointerViewportCoords"] = {};
|
const pointerViewportCoords: SceneState["remotePointerViewportCoords"] = {};
|
||||||
const remoteSelectedElementIds: SceneState["remoteSelectedElementIds"] = {};
|
const remoteSelectedElementIds: SceneState["remoteSelectedElementIds"] = {};
|
||||||
const pointerUsernames: { [id: string]: string } = {};
|
const pointerUsernames: { [id: string]: string } = {};
|
||||||
|
const pointerUserStates: { [id: string]: string } = {};
|
||||||
this.state.collaborators.forEach((user, socketId) => {
|
this.state.collaborators.forEach((user, socketId) => {
|
||||||
if (user.selectedElementIds) {
|
if (user.selectedElementIds) {
|
||||||
for (const id of Object.keys(user.selectedElementIds)) {
|
for (const id of Object.keys(user.selectedElementIds)) {
|
||||||
@@ -836,6 +900,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (user.username) {
|
if (user.username) {
|
||||||
pointerUsernames[socketId] = user.username;
|
pointerUsernames[socketId] = user.username;
|
||||||
}
|
}
|
||||||
|
if (user.userState) {
|
||||||
|
pointerUserStates[socketId] = user.userState;
|
||||||
|
}
|
||||||
pointerViewportCoords[socketId] = sceneCoordsToViewportCoords(
|
pointerViewportCoords[socketId] = sceneCoordsToViewportCoords(
|
||||||
{
|
{
|
||||||
sceneX: user.pointer.x,
|
sceneX: user.pointer.x,
|
||||||
@@ -870,6 +937,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
remotePointerButton: cursorButton,
|
remotePointerButton: cursorButton,
|
||||||
remoteSelectedElementIds,
|
remoteSelectedElementIds,
|
||||||
remotePointerUsernames: pointerUsernames,
|
remotePointerUsernames: pointerUsernames,
|
||||||
|
remotePointerUserStates: pointerUserStates,
|
||||||
shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom,
|
shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1134,10 +1202,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.actionManager.executeAction(actionToggleZenMode);
|
this.actionManager.executeAction(actionToggleZenMode);
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleGridMode = () => {
|
|
||||||
this.actionManager.executeAction(actionToggleGridMode);
|
|
||||||
};
|
|
||||||
|
|
||||||
toggleStats = () => {
|
toggleStats = () => {
|
||||||
if (!this.state.showStats) {
|
if (!this.state.showStats) {
|
||||||
trackEvent("dialog", "stats");
|
trackEvent("dialog", "stats");
|
||||||
@@ -1232,14 +1296,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event[KEYS.CTRL_OR_CMD]) {
|
|
||||||
this.setState({ isBindingEnabled: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.actionManager.handleKeyDown(event)) {
|
if (this.actionManager.handleKeyDown(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.viewModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event[KEYS.CTRL_OR_CMD]) {
|
||||||
|
this.setState({ isBindingEnabled: false });
|
||||||
|
}
|
||||||
|
|
||||||
if (event.code === CODES.NINE) {
|
if (event.code === CODES.NINE) {
|
||||||
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
||||||
}
|
}
|
||||||
@@ -1762,10 +1830,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPointerOverScrollBars = isOverScrollBars(
|
const isPointerOverScrollBars = isOverScrollBars(
|
||||||
currentScrollBars,
|
currentScrollBars,
|
||||||
event.clientX,
|
event.clientX - this.state.offsetLeft,
|
||||||
event.clientY,
|
event.clientY - this.state.offsetTop,
|
||||||
);
|
);
|
||||||
const isOverScrollBar = isPointerOverScrollBars.isOverEither;
|
const isOverScrollBar = isPointerOverScrollBars.isOverEither;
|
||||||
if (!this.state.draggingElement && !this.state.multiElement) {
|
if (!this.state.draggingElement && !this.state.multiElement) {
|
||||||
@@ -2046,14 +2115,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
lastPointerUp = onPointerUp;
|
lastPointerUp = onPointerUp;
|
||||||
|
|
||||||
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
if (!this.state.viewModeEnabled) {
|
||||||
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
||||||
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
||||||
window.addEventListener(EVENT.KEYUP, onKeyUp);
|
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
||||||
pointerDownState.eventListeners.onMove = onPointerMove;
|
window.addEventListener(EVENT.KEYUP, onKeyUp);
|
||||||
pointerDownState.eventListeners.onUp = onPointerUp;
|
pointerDownState.eventListeners.onMove = onPointerMove;
|
||||||
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
pointerDownState.eventListeners.onUp = onPointerUp;
|
||||||
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
||||||
|
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
|
private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
|
||||||
@@ -2103,7 +2174,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
!(
|
!(
|
||||||
gesture.pointers.size === 0 &&
|
gesture.pointers.size === 0 &&
|
||||||
(event.button === POINTER_BUTTON.WHEEL ||
|
(event.button === POINTER_BUTTON.WHEEL ||
|
||||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace))
|
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
|
||||||
|
this.state.viewModeEnabled)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2218,8 +2290,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
),
|
),
|
||||||
scrollbars: isOverScrollBars(
|
scrollbars: isOverScrollBars(
|
||||||
currentScrollBars,
|
currentScrollBars,
|
||||||
event.clientX,
|
event.clientX - this.state.offsetLeft,
|
||||||
event.clientY,
|
event.clientY - this.state.offsetTop,
|
||||||
),
|
),
|
||||||
// we need to duplicate because we'll be updating this state
|
// we need to duplicate because we'll be updating this state
|
||||||
lastCoords: { ...origin },
|
lastCoords: { ...origin },
|
||||||
@@ -3381,11 +3453,40 @@ 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 (
|
private handleCanvasOnDrop = async (
|
||||||
event: React.DragEvent<HTMLCanvasElement>,
|
event: React.DragEvent<HTMLCanvasElement>,
|
||||||
) => {
|
) => {
|
||||||
|
let imageFile: File | null = null;
|
||||||
try {
|
try {
|
||||||
const file = event.dataTransfer.files[0];
|
const file = event.dataTransfer.files[0];
|
||||||
|
if (file?.type.indexOf("image/") === 0) {
|
||||||
|
imageFile = file;
|
||||||
|
}
|
||||||
if (file?.type === "image/png" || file?.type === "image/svg+xml") {
|
if (file?.type === "image/png" || file?.type === "image/svg+xml") {
|
||||||
const { elements, appState } = await loadFromBlob(file, this.state);
|
const { elements, appState } = await loadFromBlob(file, this.state);
|
||||||
this.syncActionResult({
|
this.syncActionResult({
|
||||||
@@ -3397,8 +3498,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
commitToHistory: true,
|
commitToHistory: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} else if (imageFile) {
|
||||||
|
return await this.handleCanvasImageDrop(event, imageFile);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (imageFile) {
|
||||||
|
return await this.handleCanvasImageDrop(event, imageFile);
|
||||||
|
}
|
||||||
return this.setState({
|
return this.setState({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: error.message,
|
errorMessage: error.message,
|
||||||
@@ -3590,7 +3696,36 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
const elements = this.scene.getElements();
|
const elements = this.scene.getElements();
|
||||||
const element = this.getElementAtPosition(x, y);
|
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) {
|
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({
|
ContextMenu.push({
|
||||||
options: [
|
options: [
|
||||||
_isMobile &&
|
_isMobile &&
|
||||||
@@ -3618,6 +3753,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
separator,
|
separator,
|
||||||
actionToggleGridMode,
|
actionToggleGridMode,
|
||||||
actionToggleZenMode,
|
actionToggleZenMode,
|
||||||
|
typeof this.props.viewModeEnabled === "undefined" &&
|
||||||
|
actionToggleViewMode,
|
||||||
actionToggleStats,
|
actionToggleStats,
|
||||||
],
|
],
|
||||||
top: clientY,
|
top: clientY,
|
||||||
@@ -3632,6 +3769,17 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.setState({ selectedElementIds: { [element.id]: true } });
|
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({
|
ContextMenu.push({
|
||||||
options: [
|
options: [
|
||||||
_isMobile && actionCut,
|
_isMobile && actionCut,
|
||||||
@@ -3648,8 +3796,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
contextItemLabel: "labels.paste",
|
contextItemLabel: "labels.paste",
|
||||||
},
|
},
|
||||||
_isMobile && separator,
|
_isMobile && separator,
|
||||||
probablySupportsClipboardBlob && actionCopyAsPng,
|
...options,
|
||||||
probablySupportsClipboardWriteText && actionCopyAsSvg,
|
|
||||||
separator,
|
separator,
|
||||||
actionCopyStyles,
|
actionCopyStyles,
|
||||||
actionPasteStyles,
|
actionPasteStyles,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Action } from "../actions/types";
|
|||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
|
|
||||||
type ContextMenuOption = "separator" | Action;
|
export type ContextMenuOption = "separator" | Action;
|
||||||
|
|
||||||
type ContextMenuProps = {
|
type ContextMenuProps = {
|
||||||
options: ContextMenuOption[];
|
options: ContextMenuOption[];
|
||||||
|
|||||||
@@ -227,6 +227,10 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
|||||||
label={t("labels.gridMode")}
|
label={t("labels.gridMode")}
|
||||||
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
||||||
/>
|
/>
|
||||||
|
<Shortcut
|
||||||
|
label={t("labels.viewMode")}
|
||||||
|
shortcuts={[getShortcutKey("Alt+R")]}
|
||||||
|
/>
|
||||||
</ShortcutIsland>
|
</ShortcutIsland>
|
||||||
</Column>
|
</Column>
|
||||||
<Column>
|
<Column>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.layer-ui__wrapper {
|
.layer-ui__wrapper {
|
||||||
|
z-index: var(--zIndex-layerUI);
|
||||||
|
|
||||||
.encrypted-icon {
|
.encrypted-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-inline-start: 15px;
|
margin-inline-start: 15px;
|
||||||
|
|||||||
+83
-44
@@ -11,7 +11,7 @@ import { CLASSES } from "../constants";
|
|||||||
import { exportCanvas } from "../data";
|
import { exportCanvas } from "../data";
|
||||||
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
|
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
|
||||||
import { Library } from "../data/library";
|
import { Library } from "../data/library";
|
||||||
import { showSelectedShapeActions } from "../element";
|
import { isTextElement, showSelectedShapeActions } from "../element";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { Language, t } from "../i18n";
|
import { Language, t } from "../i18n";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
@@ -61,6 +61,7 @@ interface LayerUIProps {
|
|||||||
canvas: HTMLCanvasElement | null,
|
canvas: HTMLCanvasElement | null,
|
||||||
) => void;
|
) => void;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useOnClickOutside = (
|
const useOnClickOutside = (
|
||||||
@@ -299,6 +300,7 @@ const LayerUI = ({
|
|||||||
isCollaborating,
|
isCollaborating,
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
|
viewModeEnabled,
|
||||||
}: LayerUIProps) => {
|
}: LayerUIProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
@@ -358,6 +360,28 @@ 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 = () => (
|
const renderCanvasActions = () => (
|
||||||
<Section
|
<Section
|
||||||
heading="canvasActions"
|
heading="canvasActions"
|
||||||
@@ -448,38 +472,42 @@ const LayerUI = ({
|
|||||||
gap={4}
|
gap={4}
|
||||||
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
||||||
>
|
>
|
||||||
{renderCanvasActions()}
|
{viewModeEnabled
|
||||||
|
? renderViewModeCanvasActions()
|
||||||
|
: renderCanvasActions()}
|
||||||
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
<Section heading="shapes">
|
{!viewModeEnabled && (
|
||||||
{(heading) => (
|
<Section heading="shapes">
|
||||||
<Stack.Col gap={4} align="start">
|
{(heading) => (
|
||||||
<Stack.Row gap={1}>
|
<Stack.Col gap={4} align="start">
|
||||||
<Island
|
<Stack.Row gap={1}>
|
||||||
padding={1}
|
<Island
|
||||||
className={clsx({ "zen-mode": zenModeEnabled })}
|
padding={1}
|
||||||
>
|
className={clsx({ "zen-mode": zenModeEnabled })}
|
||||||
<HintViewer appState={appState} elements={elements} />
|
>
|
||||||
{heading}
|
<HintViewer appState={appState} elements={elements} />
|
||||||
<Stack.Row gap={1}>
|
{heading}
|
||||||
<ShapesSwitcher
|
<Stack.Row gap={1}>
|
||||||
elementType={appState.elementType}
|
<ShapesSwitcher
|
||||||
setAppState={setAppState}
|
elementType={appState.elementType}
|
||||||
isLibraryOpen={appState.isLibraryOpen}
|
setAppState={setAppState}
|
||||||
/>
|
isLibraryOpen={appState.isLibraryOpen}
|
||||||
</Stack.Row>
|
/>
|
||||||
</Island>
|
</Stack.Row>
|
||||||
<LockIcon
|
</Island>
|
||||||
zenModeEnabled={zenModeEnabled}
|
<LockIcon
|
||||||
checked={appState.elementLocked}
|
zenModeEnabled={zenModeEnabled}
|
||||||
onChange={onLockToggle}
|
checked={appState.elementLocked}
|
||||||
title={t("toolBar.lock")}
|
onChange={onLockToggle}
|
||||||
/>
|
title={t("toolBar.lock")}
|
||||||
</Stack.Row>
|
/>
|
||||||
{libraryMenu}
|
</Stack.Row>
|
||||||
</Stack.Col>
|
{libraryMenu}
|
||||||
)}
|
</Stack.Col>
|
||||||
</Section>
|
)}
|
||||||
|
</Section>
|
||||||
|
)}
|
||||||
<UserList
|
<UserList
|
||||||
className={clsx("zen-mode-transition", {
|
className={clsx("zen-mode-transition", {
|
||||||
"transition-right": zenModeEnabled,
|
"transition-right": zenModeEnabled,
|
||||||
@@ -524,6 +552,20 @@ 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 = () => (
|
const renderFooter = () => (
|
||||||
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
||||||
<div
|
<div
|
||||||
@@ -599,25 +641,22 @@ const LayerUI = ({
|
|||||||
canvas={canvas}
|
canvas={canvas}
|
||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
renderCustomFooter={renderCustomFooter}
|
renderCustomFooter={renderCustomFooter}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="layer-ui__wrapper">
|
<div
|
||||||
|
className={clsx("layer-ui__wrapper", {
|
||||||
|
"disable-pointerEvents":
|
||||||
|
appState.draggingElement ||
|
||||||
|
appState.resizingElement ||
|
||||||
|
(appState.editingElement && !isTextElement(appState.editingElement)),
|
||||||
|
})}
|
||||||
|
>
|
||||||
{dialogs}
|
{dialogs}
|
||||||
{renderFixedSideContainer()}
|
{renderFixedSideContainer()}
|
||||||
{renderBottomAppMenu()}
|
{renderBottomAppMenu()}
|
||||||
{
|
{renderGitHubCorner()}
|
||||||
<aside
|
|
||||||
className={clsx(
|
|
||||||
"layer-ui__wrapper__github-corner zen-mode-transition",
|
|
||||||
{
|
|
||||||
"transition-right": zenModeEnabled,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<GitHubCorner appearance={appState.appearance} />
|
|
||||||
</aside>
|
|
||||||
}
|
|
||||||
{renderFooter()}
|
{renderFooter()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+160
-116
@@ -29,6 +29,7 @@ type MobileMenuProps = {
|
|||||||
canvas: HTMLCanvasElement | null;
|
canvas: HTMLCanvasElement | null;
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MobileMenu = ({
|
export const MobileMenu = ({
|
||||||
@@ -43,121 +44,164 @@ export const MobileMenu = ({
|
|||||||
canvas,
|
canvas,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
}: MobileMenuProps) => (
|
viewModeEnabled,
|
||||||
<>
|
}: MobileMenuProps) => {
|
||||||
<FixedSideContainer side="top">
|
const renderToolbar = () => {
|
||||||
<Section heading="shapes">
|
return (
|
||||||
{(heading) => (
|
<FixedSideContainer side="top" className="App-top-bar">
|
||||||
<Stack.Col gap={4} align="center">
|
<Section heading="shapes">
|
||||||
<Stack.Row gap={1}>
|
{(heading) => (
|
||||||
<Island padding={1}>
|
<Stack.Col gap={4} align="center">
|
||||||
{heading}
|
<Stack.Row gap={1}>
|
||||||
<Stack.Row gap={1}>
|
<Island padding={1}>
|
||||||
<ShapesSwitcher
|
{heading}
|
||||||
elementType={appState.elementType}
|
<Stack.Row gap={1}>
|
||||||
setAppState={setAppState}
|
<ShapesSwitcher
|
||||||
isLibraryOpen={appState.isLibraryOpen}
|
elementType={appState.elementType}
|
||||||
/>
|
setAppState={setAppState}
|
||||||
</Stack.Row>
|
isLibraryOpen={appState.isLibraryOpen}
|
||||||
</Island>
|
/>
|
||||||
<LockIcon
|
</Stack.Row>
|
||||||
checked={appState.elementLocked}
|
</Island>
|
||||||
onChange={onLockToggle}
|
<LockIcon
|
||||||
title={t("toolBar.lock")}
|
checked={appState.elementLocked}
|
||||||
/>
|
onChange={onLockToggle}
|
||||||
</Stack.Row>
|
title={t("toolBar.lock")}
|
||||||
{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)}
|
</Stack.Row>
|
||||||
<fieldset>
|
{libraryMenu}
|
||||||
<legend>{t("labels.collaborators")}</legend>
|
</Stack.Col>
|
||||||
<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",
|
|
||||||
)}
|
|
||||||
{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>
|
</Section>
|
||||||
</Island>
|
<HintViewer appState={appState} elements={elements} />
|
||||||
</div>
|
</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}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
)}
|
||||||
|
</footer>
|
||||||
|
</Island>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { copyTextToSystemClipboard } from "../clipboard";
|
||||||
|
import { DEFAULT_VERSION } from "../constants";
|
||||||
import { getCommonBounds } from "../element/bounds";
|
import { getCommonBounds } from "../element/bounds";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import {
|
import {
|
||||||
@@ -9,7 +11,7 @@ import { t } from "../i18n";
|
|||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
import { getTargetElements } from "../scene";
|
import { getTargetElements } from "../scene";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
import { debounce, nFormatter } from "../utils";
|
import { debounce, getVersion, nFormatter } from "../utils";
|
||||||
import { close } from "./icons";
|
import { close } from "./icons";
|
||||||
import { Island } from "./Island";
|
import { Island } from "./Island";
|
||||||
import "./Stats.scss";
|
import "./Stats.scss";
|
||||||
@@ -25,6 +27,7 @@ const getStorageSizes = debounce((cb: (sizes: StorageSizes) => void) => {
|
|||||||
|
|
||||||
export const Stats = (props: {
|
export const Stats = (props: {
|
||||||
appState: AppState;
|
appState: AppState;
|
||||||
|
setAppState: React.Component<any, AppState>["setState"];
|
||||||
elements: readonly NonDeletedExcalidrawElement[];
|
elements: readonly NonDeletedExcalidrawElement[];
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -50,6 +53,17 @@ export const Stats = (props: {
|
|||||||
return null;
|
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 (
|
return (
|
||||||
<div className="Stats">
|
<div className="Stats">
|
||||||
<Island padding={2}>
|
<Island padding={2}>
|
||||||
@@ -156,6 +170,28 @@ export const Stats = (props: {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Island>
|
</Island>
|
||||||
|
|||||||
@@ -48,15 +48,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the following 3 rules ensure that the tooltip doesn't show (nor affect
|
.Tooltip:hover .Tooltip__label {
|
||||||
// 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;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export enum EVENT {
|
|||||||
TOUCH_START = "touchstart",
|
TOUCH_START = "touchstart",
|
||||||
TOUCH_END = "touchend",
|
TOUCH_END = "touchend",
|
||||||
HASHCHANGE = "hashchange",
|
HASHCHANGE = "hashchange",
|
||||||
|
VISIBILITY_CHANGE = "visibilitychange",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ENV = {
|
export const ENV = {
|
||||||
@@ -93,3 +94,8 @@ export const TOAST_TIMEOUT = 5000;
|
|||||||
export const VERSION_TIMEOUT = 30000;
|
export const VERSION_TIMEOUT = 30000;
|
||||||
|
|
||||||
export const ZOOM_STEP = 0.1;
|
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;
|
||||||
|
|||||||
+23
-2
@@ -1,6 +1,12 @@
|
|||||||
@import "./variables.module";
|
@import "./variables.module";
|
||||||
@import "./theme";
|
@import "./theme";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--zIndex-canvas: 1;
|
||||||
|
--zIndex-wysiwyg: 2;
|
||||||
|
--zIndex-layerUI: 3;
|
||||||
|
}
|
||||||
|
|
||||||
.excalidraw {
|
.excalidraw {
|
||||||
color: var(--text-color-primary);
|
color: var(--text-color-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -30,6 +36,8 @@
|
|||||||
image-rendering: pixelated; // chromium
|
image-rendering: pixelated; // chromium
|
||||||
// NOTE: must be declared *after* the above
|
// NOTE: must be declared *after* the above
|
||||||
image-rendering: -moz-crisp-edges; // FF
|
image-rendering: -moz-crisp-edges; // FF
|
||||||
|
|
||||||
|
z-index: var(--zIndex-canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.Appearance_dark {
|
&.Appearance_dark {
|
||||||
@@ -216,6 +224,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.App-top-bar {
|
||||||
|
z-index: var(--zIndex-layerUI);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.App-bottom-bar {
|
.App-bottom-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -282,7 +296,7 @@
|
|||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-menu_top > * {
|
.layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top > * {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +337,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-menu_bottom > * {
|
.layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_bottom > * {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +506,13 @@
|
|||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.excalidraw--view-mode {
|
||||||
|
.App-menu {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.App-bottom-bar,
|
.App-bottom-bar,
|
||||||
.FixedSideContainer,
|
.FixedSideContainer,
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
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,7 +7,8 @@ export const showSelectedShapeActions = (
|
|||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
) =>
|
) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
appState.editingElement ||
|
!appState.viewModeEnabled &&
|
||||||
getSelectedElements(elements, appState).length ||
|
(appState.editingElement ||
|
||||||
appState.elementType !== "selection",
|
getSelectedElements(elements, appState).length ||
|
||||||
|
appState.elementType !== "selection"),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -89,9 +89,6 @@ export const textWysiwyg = ({
|
|||||||
editable.dataset.type = "wysiwyg";
|
editable.dataset.type = "wysiwyg";
|
||||||
// prevent line wrapping on Safari
|
// prevent line wrapping on Safari
|
||||||
editable.wrap = "off";
|
editable.wrap = "off";
|
||||||
editable.className = `excalidraw ${
|
|
||||||
appState.appearance === "dark" ? "Appearance_dark" : ""
|
|
||||||
}`;
|
|
||||||
|
|
||||||
Object.assign(editable.style, {
|
Object.assign(editable.style, {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
@@ -107,6 +104,8 @@ export const textWysiwyg = ({
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
// prevent line wrapping (`whitespace: nowrap` doesn't work on FF)
|
// prevent line wrapping (`whitespace: nowrap` doesn't work on FF)
|
||||||
whiteSpace: "pre",
|
whiteSpace: "pre",
|
||||||
|
// must be specified because in dark mode canvas creates a stacking context
|
||||||
|
zIndex: "var(--zIndex-wysiwyg)",
|
||||||
});
|
});
|
||||||
|
|
||||||
updateWysiwygStyle();
|
updateWysiwygStyle();
|
||||||
@@ -160,7 +159,7 @@ export const textWysiwyg = ({
|
|||||||
|
|
||||||
unbindUpdate();
|
unbindUpdate();
|
||||||
|
|
||||||
document.body.removeChild(editable);
|
editable.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
const rebindBlur = () => {
|
const rebindBlur = () => {
|
||||||
@@ -206,7 +205,9 @@ export const textWysiwyg = ({
|
|||||||
passive: false,
|
passive: false,
|
||||||
capture: true,
|
capture: true,
|
||||||
});
|
});
|
||||||
document.body.appendChild(editable);
|
document
|
||||||
|
.querySelector(".excalidraw-textEditorContainer")!
|
||||||
|
.appendChild(editable);
|
||||||
editable.focus();
|
editable.focus();
|
||||||
editable.select();
|
editable.select();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ import {
|
|||||||
} from "../app_constants";
|
} from "../app_constants";
|
||||||
import {
|
import {
|
||||||
decryptAESGEM,
|
decryptAESGEM,
|
||||||
generateCollaborationLink,
|
generateCollaborationLinkData,
|
||||||
getCollaborationLinkData,
|
getCollaborationLink,
|
||||||
SocketUpdateDataSource,
|
SocketUpdateDataSource,
|
||||||
SOCKET_SERVER,
|
SOCKET_SERVER,
|
||||||
} from "../data";
|
} from "../data";
|
||||||
import { isSavedToFirebase, saveToFirebase } from "../data/firebase";
|
import {
|
||||||
|
isSavedToFirebase,
|
||||||
|
loadFromFirebase,
|
||||||
|
saveToFirebase,
|
||||||
|
} from "../data/firebase";
|
||||||
import {
|
import {
|
||||||
importUsernameFromLocalStorage,
|
importUsernameFromLocalStorage,
|
||||||
saveUsernameToLocalStorage,
|
saveUsernameToLocalStorage,
|
||||||
@@ -33,20 +37,25 @@ import {
|
|||||||
import Portal from "./Portal";
|
import Portal from "./Portal";
|
||||||
import RoomDialog from "./RoomDialog";
|
import RoomDialog from "./RoomDialog";
|
||||||
import { createInverseContext } from "../../createInverseContext";
|
import { createInverseContext } from "../../createInverseContext";
|
||||||
|
import { t } from "../../i18n";
|
||||||
|
import { UserIdleState } from "./types";
|
||||||
|
import { IDLE_THRESHOLD, ACTIVE_THRESHOLD } from "../../constants";
|
||||||
|
|
||||||
interface CollabState {
|
interface CollabState {
|
||||||
isCollaborating: boolean;
|
|
||||||
modalIsShown: boolean;
|
modalIsShown: boolean;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
userState: UserIdleState;
|
||||||
activeRoomLink: string;
|
activeRoomLink: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CollabInstance = InstanceType<typeof CollabWrapper>;
|
type CollabInstance = InstanceType<typeof CollabWrapper>;
|
||||||
|
|
||||||
export interface CollabAPI {
|
export interface CollabAPI {
|
||||||
isCollaborating: CollabState["isCollaborating"];
|
/** function so that we can access the latest value from stale callbacks */
|
||||||
|
isCollaborating: () => boolean;
|
||||||
username: CollabState["username"];
|
username: CollabState["username"];
|
||||||
|
userState: CollabState["userState"];
|
||||||
onPointerUpdate: CollabInstance["onPointerUpdate"];
|
onPointerUpdate: CollabInstance["onPointerUpdate"];
|
||||||
initializeSocketClient: CollabInstance["initializeSocketClient"];
|
initializeSocketClient: CollabInstance["initializeSocketClient"];
|
||||||
onCollabButtonClick: CollabInstance["onCollabButtonClick"];
|
onCollabButtonClick: CollabInstance["onCollabButtonClick"];
|
||||||
@@ -72,6 +81,10 @@ export { CollabContext, CollabContextConsumer };
|
|||||||
class CollabWrapper extends PureComponent<Props, CollabState> {
|
class CollabWrapper extends PureComponent<Props, CollabState> {
|
||||||
portal: Portal;
|
portal: Portal;
|
||||||
excalidrawAPI: Props["excalidrawAPI"];
|
excalidrawAPI: Props["excalidrawAPI"];
|
||||||
|
isCollaborating: boolean = false;
|
||||||
|
activeIntervalId: number | null;
|
||||||
|
idleTimeoutId: number | null;
|
||||||
|
|
||||||
private socketInitializationTimer?: NodeJS.Timeout;
|
private socketInitializationTimer?: NodeJS.Timeout;
|
||||||
private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
||||||
private collaborators = new Map<string, Collaborator>();
|
private collaborators = new Map<string, Collaborator>();
|
||||||
@@ -79,14 +92,16 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isCollaborating: false,
|
|
||||||
modalIsShown: false,
|
modalIsShown: false,
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
username: importUsernameFromLocalStorage() || "",
|
username: importUsernameFromLocalStorage() || "",
|
||||||
|
userState: UserIdleState.ACTIVE,
|
||||||
activeRoomLink: "",
|
activeRoomLink: "",
|
||||||
};
|
};
|
||||||
this.portal = new Portal(this);
|
this.portal = new Portal(this);
|
||||||
this.excalidrawAPI = props.excalidrawAPI;
|
this.excalidrawAPI = props.excalidrawAPI;
|
||||||
|
this.activeIntervalId = null;
|
||||||
|
this.idleTimeoutId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -110,18 +125,32 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload);
|
window.removeEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload);
|
||||||
window.removeEventListener(EVENT.UNLOAD, this.onUnload);
|
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 = () => {
|
private onUnload = () => {
|
||||||
this.destroySocketClient();
|
this.destroySocketClient({ isUnload: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
private beforeUnload = withBatchedUpdates((event: BeforeUnloadEvent) => {
|
private beforeUnload = withBatchedUpdates((event: BeforeUnloadEvent) => {
|
||||||
const syncableElements = getSyncableElements(
|
const syncableElements = getSyncableElements(
|
||||||
this.getSceneElementsIncludingDeleted(),
|
this.getSceneElementsIncludingDeleted(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.state.isCollaborating &&
|
this.isCollaborating &&
|
||||||
!isSavedToFirebase(this.portal, syncableElements)
|
!isSavedToFirebase(this.portal, syncableElements)
|
||||||
) {
|
) {
|
||||||
// this won't run in time if user decides to leave the site, but
|
// this won't run in time if user decides to leave the site, but
|
||||||
@@ -133,7 +162,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
event.returnValue = "";
|
event.returnValue = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.isCollaborating || this.portal.roomId) {
|
if (this.isCollaborating || this.portal.roomId) {
|
||||||
try {
|
try {
|
||||||
localStorage?.setItem(
|
localStorage?.setItem(
|
||||||
STORAGE_KEYS.LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
STORAGE_KEYS.LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
||||||
@@ -159,143 +188,188 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
openPortal = async () => {
|
openPortal = async () => {
|
||||||
window.history.pushState({}, APP_NAME, await generateCollaborationLink());
|
return this.initializeSocketClient(null);
|
||||||
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 = () => {
|
closePortal = () => {
|
||||||
this.saveCollabRoomToFirebase();
|
this.saveCollabRoomToFirebase();
|
||||||
window.history.pushState({}, APP_NAME, window.location.origin);
|
if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
|
||||||
this.destroySocketClient();
|
window.history.pushState({}, APP_NAME, window.location.origin);
|
||||||
|
this.destroySocketClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private destroySocketClient = () => {
|
private destroySocketClient = (opts?: { isUnload: boolean }) => {
|
||||||
this.collaborators = new Map();
|
if (!opts?.isUnload) {
|
||||||
this.excalidrawAPI.updateScene({
|
this.collaborators = new Map();
|
||||||
collaborators: this.collaborators,
|
this.excalidrawAPI.updateScene({
|
||||||
});
|
collaborators: this.collaborators,
|
||||||
this.setState({
|
});
|
||||||
isCollaborating: false,
|
this.setState({
|
||||||
activeRoomLink: "",
|
activeRoomLink: "",
|
||||||
});
|
});
|
||||||
|
this.isCollaborating = false;
|
||||||
|
}
|
||||||
this.portal.close();
|
this.portal.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
private initializeSocketClient = async (): Promise<ImportedDataState | null> => {
|
private initializeSocketClient = async (
|
||||||
|
existingRoomLinkData: null | { roomId: string; roomKey: string },
|
||||||
|
): Promise<ImportedDataState | null> => {
|
||||||
if (this.portal.socket) {
|
if (this.portal.socket) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scenePromise = resolvablePromise<ImportedDataState | null>();
|
let roomId;
|
||||||
|
let roomKey;
|
||||||
|
|
||||||
const roomMatch = getCollaborationLinkData(window.location.href);
|
if (existingRoomLinkData) {
|
||||||
|
({ roomId, roomKey } = existingRoomLinkData);
|
||||||
if (roomMatch) {
|
} else {
|
||||||
const roomId = roomMatch[1];
|
({ roomId, roomKey } = await generateCollaborationLinkData());
|
||||||
const roomKey = roomMatch[2];
|
window.history.pushState(
|
||||||
|
{},
|
||||||
// fallback in case you're not alone in the room but still don't receive
|
APP_NAME,
|
||||||
// initial SCENE_UPDATE message
|
getCollaborationLink({ roomId, roomKey }),
|
||||||
this.socketInitializationTimer = setTimeout(() => {
|
|
||||||
this.initializeSocket();
|
|
||||||
scenePromise.resolve(null);
|
|
||||||
}, INITIAL_SCENE_UPDATE_TIMEOUT);
|
|
||||||
|
|
||||||
const { default: socketIOClient }: any = await import(
|
|
||||||
/* webpackChunkName: "socketIoClient" */ "socket.io-client"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
this.initializeSocket();
|
|
||||||
scenePromise.resolve(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
isCollaborating: true,
|
|
||||||
activeRoomLink: window.location.href,
|
|
||||||
});
|
|
||||||
|
|
||||||
return scenePromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
const scenePromise = resolvablePromise<ImportedDataState | null>();
|
||||||
|
|
||||||
|
this.isCollaborating = true;
|
||||||
|
|
||||||
|
const { default: socketIOClient }: any = await import(
|
||||||
|
/* webpackChunkName: "socketIoClient" */ "socket.io-client"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.portal.open(socketIOClient(SOCKET_SERVER), roomId, roomKey);
|
||||||
|
|
||||||
|
if (existingRoomLinkData) {
|
||||||
|
this.excalidrawAPI.resetScene();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const elements = await loadFromFirebase(
|
||||||
|
roomId,
|
||||||
|
roomKey,
|
||||||
|
this.portal.socket,
|
||||||
|
);
|
||||||
|
if (elements) {
|
||||||
|
scenePromise.resolve({
|
||||||
|
elements,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
private initializeSocket = () => {
|
private initializeSocket = () => {
|
||||||
@@ -359,7 +433,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
// Avoid broadcasting to the rest of the collaborators the scene
|
// Avoid broadcasting to the rest of the collaborators the scene
|
||||||
// we just received!
|
// we just received!
|
||||||
// Note: this needs to be set before updating the scene as it
|
// Note: this needs to be set before updating the scene as it
|
||||||
// syncronously calls render.
|
// synchronously calls render.
|
||||||
this.setLastBroadcastedOrReceivedSceneVersion(getSceneVersion(newElements));
|
this.setLastBroadcastedOrReceivedSceneVersion(getSceneVersion(newElements));
|
||||||
|
|
||||||
return newElements as ReconciledElements;
|
return newElements as ReconciledElements;
|
||||||
@@ -388,6 +462,58 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
this.excalidrawAPI.history.clear();
|
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[]) {
|
setCollaborators(sockets: string[]) {
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
const collaborators: InstanceType<
|
const collaborators: InstanceType<
|
||||||
@@ -427,6 +553,11 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
this.portal.broadcastMouseLocation(payload);
|
this.portal.broadcastMouseLocation(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onIdleStateChange = (userState: UserIdleState) => {
|
||||||
|
this.setState({ userState });
|
||||||
|
this.portal.broadcastIdleChange(userState);
|
||||||
|
};
|
||||||
|
|
||||||
broadcastElements = (elements: readonly ExcalidrawElement[]) => {
|
broadcastElements = (elements: readonly ExcalidrawElement[]) => {
|
||||||
if (
|
if (
|
||||||
getSceneVersion(elements) >
|
getSceneVersion(elements) >
|
||||||
@@ -480,9 +611,11 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
|
|
||||||
/** Getter of context value. Returned object is stable. */
|
/** Getter of context value. Returned object is stable. */
|
||||||
getContextValue = (): CollabAPI => {
|
getContextValue = (): CollabAPI => {
|
||||||
this.contextValue = this.contextValue || ({} as CollabAPI);
|
if (!this.contextValue) {
|
||||||
|
this.contextValue = {} as CollabAPI;
|
||||||
|
}
|
||||||
|
|
||||||
this.contextValue.isCollaborating = this.state.isCollaborating;
|
this.contextValue.isCollaborating = () => this.isCollaborating;
|
||||||
this.contextValue.username = this.state.username;
|
this.contextValue.username = this.state.username;
|
||||||
this.contextValue.onPointerUpdate = this.onPointerUpdate;
|
this.contextValue.onPointerUpdate = this.onPointerUpdate;
|
||||||
this.contextValue.initializeSocketClient = this.initializeSocketClient;
|
this.contextValue.initializeSocketClient = this.initializeSocketClient;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import CollabWrapper from "./CollabWrapper";
|
|||||||
import { getSyncableElements } from "../../packages/excalidraw/index";
|
import { getSyncableElements } from "../../packages/excalidraw/index";
|
||||||
import { ExcalidrawElement } from "../../element/types";
|
import { ExcalidrawElement } from "../../element/types";
|
||||||
import { BROADCAST, SCENE } from "../app_constants";
|
import { BROADCAST, SCENE } from "../app_constants";
|
||||||
|
import { UserIdleState } from "./types";
|
||||||
|
|
||||||
class Portal {
|
class Portal {
|
||||||
collab: CollabWrapper;
|
collab: CollabWrapper;
|
||||||
@@ -122,7 +123,7 @@ class Portal {
|
|||||||
data as SocketUpdateData,
|
data as SocketUpdateData,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (syncAll && this.collab.state.isCollaborating) {
|
if (syncAll && this.collab.isCollaborating) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
broadcastPromise,
|
broadcastPromise,
|
||||||
this.collab.saveCollabRoomToFirebase(syncableElements),
|
this.collab.saveCollabRoomToFirebase(syncableElements),
|
||||||
@@ -132,6 +133,23 @@ 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: {
|
broadcastMouseLocation = (payload: {
|
||||||
pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
||||||
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum UserIdleState {
|
||||||
|
ACTIVE = "active",
|
||||||
|
AWAY = "away",
|
||||||
|
IDLE = "idle",
|
||||||
|
}
|
||||||
@@ -148,6 +148,7 @@ export const saveToFirebase = async (
|
|||||||
export const loadFromFirebase = async (
|
export const loadFromFirebase = async (
|
||||||
roomId: string,
|
roomId: string,
|
||||||
roomKey: string,
|
roomKey: string,
|
||||||
|
socket: SocketIOClient.Socket | null,
|
||||||
): Promise<readonly ExcalidrawElement[] | null> => {
|
): Promise<readonly ExcalidrawElement[] | null> => {
|
||||||
const firebase = await getFirebase();
|
const firebase = await getFirebase();
|
||||||
const db = firebase.firestore();
|
const db = firebase.firestore();
|
||||||
@@ -160,5 +161,12 @@ export const loadFromFirebase = async (
|
|||||||
const storedScene = doc.data() as FirebaseStoredScene;
|
const storedScene = doc.data() as FirebaseStoredScene;
|
||||||
const ciphertext = storedScene.ciphertext.toUint8Array();
|
const ciphertext = storedScene.ciphertext.toUint8Array();
|
||||||
const iv = storedScene.iv.toUint8Array();
|
const iv = storedScene.iv.toUint8Array();
|
||||||
return restoreElements(await decryptElements(roomKey, iv, ciphertext));
|
|
||||||
|
const elements = await decryptElements(roomKey, iv, ciphertext);
|
||||||
|
|
||||||
|
if (socket) {
|
||||||
|
firebaseSceneVersionCache.set(socket, getSceneVersion(elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
return restoreElements(elements);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ImportedDataState } from "../../data/types";
|
|||||||
import { ExcalidrawElement } from "../../element/types";
|
import { ExcalidrawElement } from "../../element/types";
|
||||||
import { t } from "../../i18n";
|
import { t } from "../../i18n";
|
||||||
import { AppState } from "../../types";
|
import { AppState } from "../../types";
|
||||||
|
import { UserIdleState } from "../collab/types";
|
||||||
|
|
||||||
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
|
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
|
||||||
|
|
||||||
@@ -59,6 +60,14 @@ export type SocketUpdateDataSource = {
|
|||||||
username: string;
|
username: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
IDLE_STATUS: {
|
||||||
|
type: "IDLE_STATUS";
|
||||||
|
payload: {
|
||||||
|
socketId: string;
|
||||||
|
userState: UserIdleState;
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SocketUpdateDataIncoming =
|
export type SocketUpdateDataIncoming =
|
||||||
@@ -125,17 +134,27 @@ export const decryptAESGEM = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getCollaborationLinkData = (link: string) => {
|
export const getCollaborationLinkData = (link: string) => {
|
||||||
if (link.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const hash = new URL(link).hash;
|
const hash = new URL(link).hash;
|
||||||
return hash.match(/^#room=([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+)$/);
|
const match = hash.match(/^#room=([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+)$/);
|
||||||
|
return match ? { roomId: match[1], roomKey: match[2] } : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateCollaborationLink = async () => {
|
export const generateCollaborationLinkData = async () => {
|
||||||
const id = await generateRandomID();
|
const roomId = await generateRandomID();
|
||||||
const key = await generateEncryptionKey();
|
const roomKey = await generateEncryptionKey();
|
||||||
return `${window.location.origin}${window.location.pathname}#room=${id},${key}`;
|
|
||||||
|
if (!roomKey) {
|
||||||
|
throw new Error("Couldn't generate room key");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { roomId, roomKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCollaborationLink = (data: {
|
||||||
|
roomId: string;
|
||||||
|
roomKey: string;
|
||||||
|
}) => {
|
||||||
|
return `${window.location.origin}${window.location.pathname}#room=${data.roomId},${data.roomKey}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getImportedKey = (key: string, usage: KeyUsage) =>
|
export const getImportedKey = (key: string, usage: KeyUsage) =>
|
||||||
|
|||||||
@@ -39,11 +39,9 @@ import CollabWrapper, {
|
|||||||
} from "./collab/CollabWrapper";
|
} from "./collab/CollabWrapper";
|
||||||
import { LanguageList } from "./components/LanguageList";
|
import { LanguageList } from "./components/LanguageList";
|
||||||
import { exportToBackend, getCollaborationLinkData, loadScene } from "./data";
|
import { exportToBackend, getCollaborationLinkData, loadScene } from "./data";
|
||||||
import { loadFromFirebase } from "./data/firebase";
|
|
||||||
import {
|
import {
|
||||||
importFromLocalStorage,
|
importFromLocalStorage,
|
||||||
saveToLocalStorage,
|
saveToLocalStorage,
|
||||||
STORAGE_KEYS,
|
|
||||||
} from "./data/localStorage";
|
} from "./data/localStorage";
|
||||||
|
|
||||||
const languageDetector = new LanguageDetector();
|
const languageDetector = new LanguageDetector();
|
||||||
@@ -66,50 +64,9 @@ const onBlur = () => {
|
|||||||
saveDebounced.flush();
|
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: {
|
const initializeScene = async (opts: {
|
||||||
resetScene: ExcalidrawImperativeAPI["resetScene"];
|
collabAPI: CollabAPI;
|
||||||
initializeSocketClient: CollabAPI["initializeSocketClient"];
|
}): Promise<ImportedDataState | null> => {
|
||||||
}): Promise<Scene | null> => {
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const id = searchParams.get("id");
|
const id = searchParams.get("id");
|
||||||
const jsonMatch = window.location.hash.match(
|
const jsonMatch = window.location.hash.match(
|
||||||
@@ -120,20 +77,17 @@ const initializeScene = async (opts: {
|
|||||||
|
|
||||||
let scene = await loadScene(null, null, initialData);
|
let scene = await loadScene(null, null, initialData);
|
||||||
|
|
||||||
let isCollabScene = !!getCollaborationLinkData(window.location.href);
|
let roomLinkData = getCollaborationLinkData(window.location.href);
|
||||||
const isExternalScene = !!(id || jsonMatch || isCollabScene);
|
const isExternalScene = !!(id || jsonMatch || roomLinkData);
|
||||||
if (isExternalScene) {
|
if (isExternalScene) {
|
||||||
if (
|
if (roomLinkData || window.confirm(t("alerts.loadSceneOverridePrompt"))) {
|
||||||
shouldForceLoadScene(scene) ||
|
|
||||||
window.confirm(t("alerts.loadSceneOverridePrompt"))
|
|
||||||
) {
|
|
||||||
// Backwards compatibility with legacy url format
|
// Backwards compatibility with legacy url format
|
||||||
if (id) {
|
if (id) {
|
||||||
scene = await loadScene(id, null, initialData);
|
scene = await loadScene(id, null, initialData);
|
||||||
} else if (jsonMatch) {
|
} else if (jsonMatch) {
|
||||||
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
|
scene = await loadScene(jsonMatch[1], jsonMatch[2], initialData);
|
||||||
}
|
}
|
||||||
if (!isCollabScene) {
|
if (!roomLinkData) {
|
||||||
window.history.replaceState({}, APP_NAME, window.location.origin);
|
window.history.replaceState({}, APP_NAME, window.location.origin);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -150,38 +104,12 @@ const initializeScene = async (opts: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollabScene = false;
|
roomLinkData = null;
|
||||||
window.history.replaceState({}, APP_NAME, window.location.origin);
|
window.history.replaceState({}, APP_NAME, window.location.origin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isCollabScene) {
|
if (roomLinkData) {
|
||||||
// when joining a room we don't want user's local scene data to be merged
|
return opts.collabAPI.initializeSocketClient(roomLinkData);
|
||||||
// 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) {
|
} else if (scene) {
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
@@ -242,24 +170,16 @@ function ExcalidrawWrapper() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeScene({
|
initializeScene({ collabAPI }).then((scene) => {
|
||||||
resetScene: excalidrawAPI.resetScene,
|
|
||||||
initializeSocketClient: collabAPI.initializeSocketClient,
|
|
||||||
}).then((scene) => {
|
|
||||||
initialStatePromiseRef.current.promise.resolve(scene);
|
initialStatePromiseRef.current.promise.resolve(scene);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onHashChange = (_: HashChangeEvent) => {
|
const onHashChange = (_: HashChangeEvent) => {
|
||||||
if (window.location.hash.length > 1) {
|
initializeScene({ collabAPI }).then((scene) => {
|
||||||
initializeScene({
|
if (scene) {
|
||||||
resetScene: excalidrawAPI.resetScene,
|
excalidrawAPI.updateScene(scene);
|
||||||
initializeSocketClient: collabAPI.initializeSocketClient,
|
}
|
||||||
}).then((scene) => {
|
});
|
||||||
if (scene) {
|
|
||||||
excalidrawAPI.updateScene(scene);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const titleTimeout = setTimeout(
|
const titleTimeout = setTimeout(
|
||||||
@@ -285,9 +205,13 @@ function ExcalidrawWrapper() {
|
|||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
) => {
|
) => {
|
||||||
saveDebounced(elements, appState);
|
if (collabAPI?.isCollaborating()) {
|
||||||
if (collabAPI?.isCollaborating) {
|
|
||||||
collabAPI.broadcastElements(elements);
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -352,7 +276,7 @@ function ExcalidrawWrapper() {
|
|||||||
initialData={initialStatePromiseRef.current.promise}
|
initialData={initialStatePromiseRef.current.promise}
|
||||||
user={{ name: collabAPI?.username }}
|
user={{ name: collabAPI?.username }}
|
||||||
onCollabButtonClick={collabAPI?.onCollabButtonClick}
|
onCollabButtonClick={collabAPI?.onCollabButtonClick}
|
||||||
isCollaborating={collabAPI?.isCollaborating}
|
isCollaborating={collabAPI?.isCollaborating()}
|
||||||
onPointerUpdate={collabAPI?.onPointerUpdate}
|
onPointerUpdate={collabAPI?.onPointerUpdate}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderFooter={renderFooter}
|
renderFooter={renderFooter}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const allLanguages: Language[] = [
|
|||||||
{ code: "id-ID", label: "Bahasa Indonesia" },
|
{ code: "id-ID", label: "Bahasa Indonesia" },
|
||||||
{ code: "it-IT", label: "Italiano" },
|
{ code: "it-IT", label: "Italiano" },
|
||||||
{ code: "ja-JP", label: "日本語" },
|
{ code: "ja-JP", label: "日本語" },
|
||||||
|
{ code: "kab-KAB", label: "Taqbaylit" },
|
||||||
{ code: "ko-KR", label: "한국어" },
|
{ code: "ko-KR", label: "한국어" },
|
||||||
{ code: "my-MM", label: "Burmese" },
|
{ code: "my-MM", label: "Burmese" },
|
||||||
{ code: "nb-NO", label: "Norsk bokmål" },
|
{ code: "nb-NO", label: "Norsk bokmål" },
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const CODES = {
|
|||||||
V: "KeyV",
|
V: "KeyV",
|
||||||
X: "KeyX",
|
X: "KeyX",
|
||||||
Z: "KeyZ",
|
Z: "KeyZ",
|
||||||
|
R: "KeyR",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const KEYS = {
|
export const KEYS = {
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "وضع الشبكة",
|
"gridMode": "وضع الشبكة",
|
||||||
"addToLibrary": "أضف إلى المكتبة",
|
"addToLibrary": "أضف إلى المكتبة",
|
||||||
"removeFromLibrary": "حذف من المكتبة",
|
"removeFromLibrary": "حذف من المكتبة",
|
||||||
"libraryLoadingMessage": "جارٍ تحميل المكتبة...",
|
"libraryLoadingMessage": "جارٍ تحميل المكتبة…",
|
||||||
"libraries": "تصفح المكتبات",
|
"libraries": "تصفح المكتبات",
|
||||||
"loadingScene": "جاري تحميل المشهد...",
|
"loadingScene": "جاري تحميل المشهد…",
|
||||||
"align": "محاذاة",
|
"align": "محاذاة",
|
||||||
"alignTop": "محاذاة إلى اﻷعلى",
|
"alignTop": "محاذاة إلى اﻷعلى",
|
||||||
"alignBottom": "محاذاة إلى اﻷسفل",
|
"alignBottom": "محاذاة إلى اﻷسفل",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "توسيط عمودي",
|
"centerVertically": "توسيط عمودي",
|
||||||
"centerHorizontally": "توسيط أفقي",
|
"centerHorizontally": "توسيط أفقي",
|
||||||
"distributeHorizontally": "التوزيع الأفقي",
|
"distributeHorizontally": "التوزيع الأفقي",
|
||||||
"distributeVertically": "التوزيع عمودياً"
|
"distributeVertically": "التوزيع عمودياً",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "إعادة تعيين اللوحة",
|
"clearReset": "إعادة تعيين اللوحة",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "تعذر فك تشفير البيانات.",
|
"decryptFailed": "تعذر فك تشفير البيانات.",
|
||||||
"uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",
|
"uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",
|
||||||
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
|
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
|
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
|
||||||
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
|
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
|
||||||
"imageDoesNotContainScene": "استيراد الصور غير مدعوم في الوقت الراهن.\n\nهل تريد استيراد مشهد؟ لا يبدو أن هذه الصورة تحتوي على أي بيانات مشهد. هل قمت بسماح هذا أثناء التصدير؟",
|
"imageDoesNotContainScene": "استيراد الصور غير مدعوم في الوقت الراهن.\n\nهل تريد استيراد مشهد؟ لا يبدو أن هذه الصورة تحتوي على أي بيانات مشهد. هل قمت بسماح هذا أثناء التصدير؟",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "التخزين",
|
"storage": "التخزين",
|
||||||
"title": "إحصائيات للمهووسين",
|
"title": "إحصائيات للمهووسين",
|
||||||
"total": "المجموع",
|
"total": "المجموع",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "العرض"
|
"width": "العرض"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Решетъчен режим",
|
"gridMode": "Решетъчен режим",
|
||||||
"addToLibrary": "Добавяне към библиотеката",
|
"addToLibrary": "Добавяне към библиотеката",
|
||||||
"removeFromLibrary": "Премахване от библиотеката",
|
"removeFromLibrary": "Премахване от библиотеката",
|
||||||
"libraryLoadingMessage": "Зареждане на библиотеката...",
|
"libraryLoadingMessage": "Зареждане на библиотеката…",
|
||||||
"libraries": "Разглеждане на библиотеките",
|
"libraries": "Разглеждане на библиотеките",
|
||||||
"loadingScene": "Зареждане на сцена...",
|
"loadingScene": "Зареждане на сцена…",
|
||||||
"align": "Подравняване",
|
"align": "Подравняване",
|
||||||
"alignTop": "Подравняване отгоре",
|
"alignTop": "Подравняване отгоре",
|
||||||
"alignBottom": "Подравняване отдолу",
|
"alignBottom": "Подравняване отдолу",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Центрирай вертикално",
|
"centerVertically": "Центрирай вертикално",
|
||||||
"centerHorizontally": "Центрирай хоризонтално",
|
"centerHorizontally": "Центрирай хоризонтално",
|
||||||
"distributeHorizontally": "Разпредели хоризонтално",
|
"distributeHorizontally": "Разпредели хоризонтално",
|
||||||
"distributeVertically": "Разпредели вертикално"
|
"distributeVertically": "Разпредели вертикално",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Нулиране на платно",
|
"clearReset": "Нулиране на платно",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Данните не можаха да се дешифрират.",
|
"decryptFailed": "Данните не можаха да се дешифрират.",
|
||||||
"uploadedSecurly": "Качването е защитено с криптиране от край до край, което означава, че сървърът Excalidraw и трети страни не могат да четат съдържанието.",
|
"uploadedSecurly": "Качването е защитено с криптиране от край до край, което означава, че сървърът Excalidraw и трети страни не могат да четат съдържанието.",
|
||||||
"loadSceneOverridePrompt": "Зареждането на външна рисунка ще презапише настоящото ви съдържание. Желаете ли да продължите?",
|
"loadSceneOverridePrompt": "Зареждането на външна рисунка ще презапише настоящото ви съдържание. Желаете ли да продължите?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Възникна грешка при зареждането на външна библиотека.",
|
"errorLoadingLibrary": "Възникна грешка при зареждането на външна библиотека.",
|
||||||
"confirmAddLibrary": "Ще се добавят {{numShapes}} фигура(и) във вашата библиотека. Сигурни ли сте?",
|
"confirmAddLibrary": "Ще се добавят {{numShapes}} фигура(и) във вашата библиотека. Сигурни ли сте?",
|
||||||
"imageDoesNotContainScene": "Импортирането на картинки не се поддържва в момента.\n\nИскате да импортнете сцена? Тази картинка не съдържа данни от сцена. Разрешили ли сте последното при експортирането?",
|
"imageDoesNotContainScene": "Импортирането на картинки не се поддържва в момента.\n\nИскате да импортнете сцена? Тази картинка не съдържа данни от сцена. Разрешили ли сте последното при експортирането?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Съхранение на данни",
|
"storage": "Съхранение на данни",
|
||||||
"title": "Статистика за хакери",
|
"title": "Статистика за хакери",
|
||||||
"total": "Общо",
|
"total": "Общо",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Широчина"
|
"width": "Широчина"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Mode quadrícula",
|
"gridMode": "Mode quadrícula",
|
||||||
"addToLibrary": "Afegir a la biblioteca",
|
"addToLibrary": "Afegir a la biblioteca",
|
||||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||||
"libraryLoadingMessage": "Carregant la biblioteca...",
|
"libraryLoadingMessage": "Carregant la biblioteca…",
|
||||||
"libraries": "Explorar biblioteques",
|
"libraries": "Explorar biblioteques",
|
||||||
"loadingScene": "Carregant escena...",
|
"loadingScene": "Carregant escena…",
|
||||||
"align": "Alinear",
|
"align": "Alinear",
|
||||||
"alignTop": "Alinear a dalt",
|
"alignTop": "Alinear a dalt",
|
||||||
"alignBottom": "Alinear a baix",
|
"alignBottom": "Alinear a baix",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centrar verticalment",
|
"centerVertically": "Centrar verticalment",
|
||||||
"centerHorizontally": "Centrar horitzontalment",
|
"centerHorizontally": "Centrar horitzontalment",
|
||||||
"distributeHorizontally": "Distribuir horitzontalment",
|
"distributeHorizontally": "Distribuir horitzontalment",
|
||||||
"distributeVertically": "Distribuir verticalment"
|
"distributeVertically": "Distribuir verticalment",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Netejar el llenç",
|
"clearReset": "Netejar el llenç",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "No s'ha pogut desencriptar.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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ó?",
|
"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ó?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Emmagatzematge",
|
"storage": "Emmagatzematge",
|
||||||
"title": "Estadístiques per nerds",
|
"title": "Estadístiques per nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Amplada"
|
"width": "Amplada"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Rastermodus",
|
"gridMode": "Rastermodus",
|
||||||
"addToLibrary": "Zur Bibliothek hinzufügen",
|
"addToLibrary": "Zur Bibliothek hinzufügen",
|
||||||
"removeFromLibrary": "Aus Bibliothek entfernen",
|
"removeFromLibrary": "Aus Bibliothek entfernen",
|
||||||
"libraryLoadingMessage": "Lade Bibliothek...",
|
"libraryLoadingMessage": "Lade Bibliothek…",
|
||||||
"libraries": "Bibliotheken durchsuchen",
|
"libraries": "Bibliotheken durchsuchen",
|
||||||
"loadingScene": "Lade Zeichnung...",
|
"loadingScene": "Lade Zeichnung…",
|
||||||
"align": "Ausrichten",
|
"align": "Ausrichten",
|
||||||
"alignTop": "Obere Kanten",
|
"alignTop": "Obere Kanten",
|
||||||
"alignBottom": "Untere Kanten",
|
"alignBottom": "Untere Kanten",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Vertikal zentrieren",
|
"centerVertically": "Vertikal zentrieren",
|
||||||
"centerHorizontally": "Horizontal zentrieren",
|
"centerHorizontally": "Horizontal zentrieren",
|
||||||
"distributeHorizontally": "Horizontal verteilen",
|
"distributeHorizontally": "Horizontal verteilen",
|
||||||
"distributeVertically": "Vertikal verteilen"
|
"distributeVertically": "Vertikal verteilen",
|
||||||
|
"viewMode": "Ansichtsmodus"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Zeichenfläche löschen & Hintergrundfarbe zurücksetzen",
|
"clearReset": "Zeichenfläche löschen & Hintergrundfarbe zurücksetzen",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Daten konnten nicht entschlüsselt werden.",
|
"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.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Beim Laden der Drittanbieter-Bibliothek ist ein Fehler aufgetreten.",
|
||||||
"confirmAddLibrary": "Dieses fügt {{numShapes}} Form(en) zu deiner Bibliothek hinzu. Bist du sicher?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Speicher",
|
"storage": "Speicher",
|
||||||
"title": "Statistiken für Nerds",
|
"title": "Statistiken für Nerds",
|
||||||
"total": "Gesamt",
|
"total": "Gesamt",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Breite"
|
"width": "Breite"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Formatierung kopiert.",
|
"copyStyles": "Formatierung kopiert.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "In die Zwischenablage als PNG kopiert."
|
"copyToClipboardAsPng": "In die Zwischenablage als PNG kopiert."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Εμφάνιση σε πλέγμα",
|
"gridMode": "Εμφάνιση σε πλέγμα",
|
||||||
"addToLibrary": "Προσθήκη στη βιβλιοθήκη",
|
"addToLibrary": "Προσθήκη στη βιβλιοθήκη",
|
||||||
"removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη",
|
"removeFromLibrary": "Αφαίρεση από τη βιβλιοθήκη",
|
||||||
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης...",
|
"libraryLoadingMessage": "Φόρτωση βιβλιοθήκης…",
|
||||||
"libraries": "Άλλες βιβλιοθήκες",
|
"libraries": "Άλλες βιβλιοθήκες",
|
||||||
"loadingScene": "Φόρτωση σκηνής...",
|
"loadingScene": "Φόρτωση σκηνής…",
|
||||||
"align": "Στοίχιση",
|
"align": "Στοίχιση",
|
||||||
"alignTop": "Στοίχιση πάνω",
|
"alignTop": "Στοίχιση πάνω",
|
||||||
"alignBottom": "Στοίχιση κάτω",
|
"alignBottom": "Στοίχιση κάτω",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Κέντρο κάθετα",
|
"centerVertically": "Κέντρο κάθετα",
|
||||||
"centerHorizontally": "Κέντρο οριζόντια",
|
"centerHorizontally": "Κέντρο οριζόντια",
|
||||||
"distributeHorizontally": "Οριζόντια κατανομή",
|
"distributeHorizontally": "Οριζόντια κατανομή",
|
||||||
"distributeVertically": "Κατακόρυφη κατανομή"
|
"distributeVertically": "Κατακόρυφη κατανομή",
|
||||||
|
"viewMode": "Λειτουργία προβολής"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Επαναφορά του καμβά",
|
"clearReset": "Επαναφορά του καμβά",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Δεν ήταν δυνατή η αποκρυπτογράφηση δεδομένων.",
|
"decryptFailed": "Δεν ήταν δυνατή η αποκρυπτογράφηση δεδομένων.",
|
||||||
"uploadedSecurly": "Η μεταφόρτωση έχει εξασφαλιστεί με κρυπτογράφηση από άκρο σε άκρο, πράγμα που σημαίνει ότι ο διακομιστής Excalidraw και τρίτα μέρη δεν μπορούν να διαβάσουν το περιεχόμενο.",
|
"uploadedSecurly": "Η μεταφόρτωση έχει εξασφαλιστεί με κρυπτογράφηση από άκρο σε άκρο, πράγμα που σημαίνει ότι ο διακομιστής Excalidraw και τρίτα μέρη δεν μπορούν να διαβάσουν το περιεχόμενο.",
|
||||||
"loadSceneOverridePrompt": "Η φόρτωση εξωτερικού σχεδίου θα αντικαταστήσει το υπάρχον περιεχόμενο. Επιθυμείτε να συνεχίσετε;",
|
"loadSceneOverridePrompt": "Η φόρτωση εξωτερικού σχεδίου θα αντικαταστήσει το υπάρχον περιεχόμενο. Επιθυμείτε να συνεχίσετε;",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Υπήρξε ένα σφάλμα κατά τη φόρτωση της βιβλιοθήκης τρίτου μέρους.",
|
"errorLoadingLibrary": "Υπήρξε ένα σφάλμα κατά τη φόρτωση της βιβλιοθήκης τρίτου μέρους.",
|
||||||
"confirmAddLibrary": "Αυτό θα προσθέσει {{numShapes}} σχήμα(τα) στη βιβιλιοθήκη σας. Είστε σίγουροι;",
|
"confirmAddLibrary": "Αυτό θα προσθέσει {{numShapes}} σχήμα(τα) στη βιβιλιοθήκη σας. Είστε σίγουροι;",
|
||||||
"imageDoesNotContainScene": "Η εισαγωγή εικόνων δεν υποστηρίζεται αυτή τη στιγμή.\n\nΜήπως θέλετε να εισαγάγετε μια σκηνή; Αυτή η εικόνα δεν φαίνεται να περιέχει δεδομένα σκηνής. Έχετε ενεργοποιήσει αυτό κατά την εξαγωγή;",
|
"imageDoesNotContainScene": "Η εισαγωγή εικόνων δεν υποστηρίζεται αυτή τη στιγμή.\n\nΜήπως θέλετε να εισαγάγετε μια σκηνή; Αυτή η εικόνα δεν φαίνεται να περιέχει δεδομένα σκηνής. Έχετε ενεργοποιήσει αυτό κατά την εξαγωγή;",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Χώρος",
|
"storage": "Χώρος",
|
||||||
"title": "Στατιστικά για σπασίκλες",
|
"title": "Στατιστικά για σπασίκλες",
|
||||||
"total": "Σύνολο ",
|
"total": "Σύνολο ",
|
||||||
|
"version": "Έκδοση",
|
||||||
|
"versionCopy": "Κάνε κλικ για αντιγραφή",
|
||||||
|
"versionNotAvailable": "Έκδοση μη διαθέσιμη",
|
||||||
"width": "Πλάτος"
|
"width": "Πλάτος"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Αντιγράφηκαν στυλ.",
|
"copyStyles": "Αντιγράφηκαν στυλ.",
|
||||||
|
"copyToClipboard": "Αντιγράφηκε στο πρόχειρο.",
|
||||||
"copyToClipboardAsPng": "Αντιγράφτηκε στο πρόχειρο ως PNG."
|
"copyToClipboardAsPng": "Αντιγράφτηκε στο πρόχειρο ως PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Grid mode",
|
"gridMode": "Grid mode",
|
||||||
"addToLibrary": "Add to library",
|
"addToLibrary": "Add to library",
|
||||||
"removeFromLibrary": "Remove from library",
|
"removeFromLibrary": "Remove from library",
|
||||||
"libraryLoadingMessage": "Loading library...",
|
"libraryLoadingMessage": "Loading library…",
|
||||||
"libraries": "Browse libraries",
|
"libraries": "Browse libraries",
|
||||||
"loadingScene": "Loading scene...",
|
"loadingScene": "Loading scene…",
|
||||||
"align": "Align",
|
"align": "Align",
|
||||||
"alignTop": "Align top",
|
"alignTop": "Align top",
|
||||||
"alignBottom": "Align bottom",
|
"alignBottom": "Align bottom",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Center vertically",
|
"centerVertically": "Center vertically",
|
||||||
"centerHorizontally": "Center horizontally",
|
"centerHorizontally": "Center horizontally",
|
||||||
"distributeHorizontally": "Distribute horizontally",
|
"distributeHorizontally": "Distribute horizontally",
|
||||||
"distributeVertically": "Distribute vertically"
|
"distributeVertically": "Distribute vertically",
|
||||||
|
"viewMode": "View mode"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Reset the canvas",
|
"clearReset": "Reset the canvas",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Couldn't decrypt data.",
|
"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.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "There was an error loading the third party library.",
|
||||||
"confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
|
"confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
|
||||||
"imageDoesNotContainScene": "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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Storage",
|
"storage": "Storage",
|
||||||
"title": "Stats for nerds",
|
"title": "Stats for nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "Version",
|
||||||
|
"versionCopy": "Click to copy",
|
||||||
|
"versionNotAvailable": "Version not available",
|
||||||
"width": "Width"
|
"width": "Width"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Copied styles.",
|
"copyStyles": "Copied styles.",
|
||||||
|
"copyToClipboard": "Copied to clipboard.",
|
||||||
"copyToClipboardAsPng": "Copied to clipboard as PNG."
|
"copyToClipboardAsPng": "Copied to clipboard as PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Modo cuadrícula",
|
"gridMode": "Modo cuadrícula",
|
||||||
"addToLibrary": "Añadir a la biblioteca",
|
"addToLibrary": "Añadir a la biblioteca",
|
||||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||||
"libraryLoadingMessage": "Cargando biblioteca...",
|
"libraryLoadingMessage": "Cargando biblioteca…",
|
||||||
"libraries": "Explorar bibliotecas",
|
"libraries": "Explorar bibliotecas",
|
||||||
"loadingScene": "Cargando escena...",
|
"loadingScene": "Cargando escena…",
|
||||||
"align": "Alinear",
|
"align": "Alinear",
|
||||||
"alignTop": "Alineación superior",
|
"alignTop": "Alineación superior",
|
||||||
"alignBottom": "Alineación inferior",
|
"alignBottom": "Alineación inferior",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centrar verticalmente",
|
"centerVertically": "Centrar verticalmente",
|
||||||
"centerHorizontally": "Centrar horizontalmente",
|
"centerHorizontally": "Centrar horizontalmente",
|
||||||
"distributeHorizontally": "Distribuir horizontalmente",
|
"distributeHorizontally": "Distribuir horizontalmente",
|
||||||
"distributeVertically": "Distribuir verticalmente"
|
"distributeVertically": "Distribuir verticalmente",
|
||||||
|
"viewMode": "Modo presentación"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
|
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "No se pudieron descifrar los datos.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Almacenamiento",
|
"storage": "Almacenamiento",
|
||||||
"title": "Estadísticas para nerds",
|
"title": "Estadísticas para nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Ancho"
|
"width": "Ancho"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Estilos copiados.",
|
"copyStyles": "Estilos copiados.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Copiado al portapapeles como PNG."
|
"copyToClipboardAsPng": "Copiado al portapapeles como PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "حالت شبکه ای",
|
"gridMode": "حالت شبکه ای",
|
||||||
"addToLibrary": "افزودن به کتابخانه",
|
"addToLibrary": "افزودن به کتابخانه",
|
||||||
"removeFromLibrary": "حذف از کتابخانه",
|
"removeFromLibrary": "حذف از کتابخانه",
|
||||||
"libraryLoadingMessage": "بارگذاری کتابخانه...",
|
"libraryLoadingMessage": "بارگذاری کتابخانه…",
|
||||||
"libraries": "مرور کردن کتابخانه ها",
|
"libraries": "مرور کردن کتابخانه ها",
|
||||||
"loadingScene": "باگذاری صحنه...",
|
"loadingScene": "باگذاری صحنه…",
|
||||||
"align": "تراز",
|
"align": "تراز",
|
||||||
"alignTop": "تراز به بالا",
|
"alignTop": "تراز به بالا",
|
||||||
"alignBottom": "تراز به پایین",
|
"alignBottom": "تراز به پایین",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "وسط قرار دادن به صورت عمودی",
|
"centerVertically": "وسط قرار دادن به صورت عمودی",
|
||||||
"centerHorizontally": "وسط قرار دادن به صورت افقی",
|
"centerHorizontally": "وسط قرار دادن به صورت افقی",
|
||||||
"distributeHorizontally": "توزیع کردن به صورت افقی",
|
"distributeHorizontally": "توزیع کردن به صورت افقی",
|
||||||
"distributeVertically": "توزیع کردن به صورت عمودی"
|
"distributeVertically": "توزیع کردن به صورت عمودی",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "پاکسازی بوم نقاشی",
|
"clearReset": "پاکسازی بوم نقاشی",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "رمزگشایی داده ها امکان پذیر نیست.",
|
"decryptFailed": "رمزگشایی داده ها امکان پذیر نیست.",
|
||||||
"uploadedSecurly": "آپلود با رمزگذاری دو طرفه انجام میشود، به این معنی که سرور Excalidraw و اشخاص ثالث نمی توانند مطالب شما را بخوانند.",
|
"uploadedSecurly": "آپلود با رمزگذاری دو طرفه انجام میشود، به این معنی که سرور Excalidraw و اشخاص ثالث نمی توانند مطالب شما را بخوانند.",
|
||||||
"loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟",
|
"loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.",
|
"errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.",
|
||||||
"confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟",
|
"confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟",
|
||||||
"imageDoesNotContainScene": "وارد کردن تصویر در این لحظه امکان پذیر نمی باشد.\nآیا مایل به وارد کردن یک صحنه هستید؟ این تصویر به نظر می رسد که فاقد هرگونه اطلاعاتی مربوط به صحنه باشد. آیا این گزینه را در زمان وارد کردن تصویر فعال کرده اید؟",
|
"imageDoesNotContainScene": "وارد کردن تصویر در این لحظه امکان پذیر نمی باشد.\nآیا مایل به وارد کردن یک صحنه هستید؟ این تصویر به نظر می رسد که فاقد هرگونه اطلاعاتی مربوط به صحنه باشد. آیا این گزینه را در زمان وارد کردن تصویر فعال کرده اید؟",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "حافظه",
|
"storage": "حافظه",
|
||||||
"title": "آمار برای نردها",
|
"title": "آمار برای نردها",
|
||||||
"total": "مجموع",
|
"total": "مجموع",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "عرض"
|
"width": "عرض"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "کپی سبک.",
|
"copyStyles": "کپی سبک.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "کپی در حافطه موقت به صورت PNG."
|
"copyToClipboardAsPng": "کپی در حافطه موقت به صورت PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Ruudukkotila",
|
"gridMode": "Ruudukkotila",
|
||||||
"addToLibrary": "Lisää kirjastoon",
|
"addToLibrary": "Lisää kirjastoon",
|
||||||
"removeFromLibrary": "Poista kirjastosta",
|
"removeFromLibrary": "Poista kirjastosta",
|
||||||
"libraryLoadingMessage": "Ladataan kirjastoa...",
|
"libraryLoadingMessage": "Ladataan kirjastoa…",
|
||||||
"libraries": "Selaa kirjastoja",
|
"libraries": "Selaa kirjastoja",
|
||||||
"loadingScene": "Ladataan työtä...",
|
"loadingScene": "Ladataan työtä…",
|
||||||
"align": "Tasaa",
|
"align": "Tasaa",
|
||||||
"alignTop": "Tasaa ylös",
|
"alignTop": "Tasaa ylös",
|
||||||
"alignBottom": "Tasaa alas",
|
"alignBottom": "Tasaa alas",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Keskitä pystysuunnassa",
|
"centerVertically": "Keskitä pystysuunnassa",
|
||||||
"centerHorizontally": "Keskitä vaakasuunnassa",
|
"centerHorizontally": "Keskitä vaakasuunnassa",
|
||||||
"distributeHorizontally": "Jaa vaakasuunnassa",
|
"distributeHorizontally": "Jaa vaakasuunnassa",
|
||||||
"distributeVertically": "Jaa pystysuunnassa"
|
"distributeVertically": "Jaa pystysuunnassa",
|
||||||
|
"viewMode": "Katselutila"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Tyhjennä piirtoalue",
|
"clearReset": "Tyhjennä piirtoalue",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Salauksen purkaminen epäonnistui.",
|
"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öä.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Kolmannen osapuolen kirjastoa ladattaessa tapahtui virhe.",
|
||||||
"confirmAddLibrary": "Tämä lisää {{numShapes}} muotoa kirjastoosi. Oletko varma?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Tallennustila",
|
"storage": "Tallennustila",
|
||||||
"title": "Nörttien tilastot",
|
"title": "Nörttien tilastot",
|
||||||
"total": "Yhteensä",
|
"total": "Yhteensä",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Leveys"
|
"width": "Leveys"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Tyylit kopioitu.",
|
"copyStyles": "Tyylit kopioitu.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Kopioitu leikepöydälle PNG-tiedostona."
|
"copyToClipboardAsPng": "Kopioitu leikepöydälle PNG-tiedostona."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Mode grille",
|
"gridMode": "Mode grille",
|
||||||
"addToLibrary": "Ajouter à la bibliothèque",
|
"addToLibrary": "Ajouter à la bibliothèque",
|
||||||
"removeFromLibrary": "Supprimer de la bibliothèque",
|
"removeFromLibrary": "Supprimer de la bibliothèque",
|
||||||
"libraryLoadingMessage": "Chargement de la bibliothèque...",
|
"libraryLoadingMessage": "Chargement de la bibliothèque…",
|
||||||
"libraries": "Parcourir les bibliothèques",
|
"libraries": "Parcourir les bibliothèques",
|
||||||
"loadingScene": "Chargement de la scène...",
|
"loadingScene": "Chargement de la scène…",
|
||||||
"align": "Aligner",
|
"align": "Aligner",
|
||||||
"alignTop": "Aligner en haut",
|
"alignTop": "Aligner en haut",
|
||||||
"alignBottom": "Aligner en bas",
|
"alignBottom": "Aligner en bas",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centrer verticalement",
|
"centerVertically": "Centrer verticalement",
|
||||||
"centerHorizontally": "Centrer horizontalement",
|
"centerHorizontally": "Centrer horizontalement",
|
||||||
"distributeHorizontally": "Distribuer horizontalement",
|
"distributeHorizontally": "Distribuer horizontalement",
|
||||||
"distributeVertically": "Distribuer verticalement"
|
"distributeVertically": "Distribuer verticalement",
|
||||||
|
"viewMode": "Mode présentation"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Réinitialiser le canevas",
|
"clearReset": "Réinitialiser le canevas",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Les données n'ont pas pu être déchiffrées.",
|
"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.",
|
"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 ?",
|
"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.",
|
"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 ?",
|
"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 ?",
|
"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 ?",
|
||||||
@@ -229,14 +231,18 @@
|
|||||||
"elements": "Éléments",
|
"elements": "Éléments",
|
||||||
"height": "Hauteur",
|
"height": "Hauteur",
|
||||||
"scene": "Scène",
|
"scene": "Scène",
|
||||||
"selected": "Sélectionné",
|
"selected": "Sélection",
|
||||||
"storage": "Stockage",
|
"storage": "Stockage",
|
||||||
"title": "Stats pour les nerds",
|
"title": "Stats pour les nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "Version",
|
||||||
|
"versionCopy": "Cliquer pour copier",
|
||||||
|
"versionNotAvailable": "Version non disponible",
|
||||||
"width": "Largeur"
|
"width": "Largeur"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Styles copiés.",
|
"copyStyles": "Styles copiés.",
|
||||||
|
"copyToClipboard": "Copié vers le presse-papiers.",
|
||||||
"copyToClipboardAsPng": "Copié vers le presse-papier en PNG."
|
"copyToClipboardAsPng": "Copié vers le presse-papier en PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "מצב רשת",
|
"gridMode": "מצב רשת",
|
||||||
"addToLibrary": "הוסף לספריה",
|
"addToLibrary": "הוסף לספריה",
|
||||||
"removeFromLibrary": "הסר מספריה",
|
"removeFromLibrary": "הסר מספריה",
|
||||||
"libraryLoadingMessage": "טוען ספריה...",
|
"libraryLoadingMessage": "טוען ספריה…",
|
||||||
"libraries": "דפדף בספריות",
|
"libraries": "דפדף בספריות",
|
||||||
"loadingScene": "טוען תצוגה...",
|
"loadingScene": "טוען תצוגה…",
|
||||||
"align": "יישר",
|
"align": "יישר",
|
||||||
"alignTop": "יישר למעלה",
|
"alignTop": "יישר למעלה",
|
||||||
"alignBottom": "יישר למטה",
|
"alignBottom": "יישר למטה",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "מרכז אנכית",
|
"centerVertically": "מרכז אנכית",
|
||||||
"centerHorizontally": "מרכז אופקית",
|
"centerHorizontally": "מרכז אופקית",
|
||||||
"distributeHorizontally": "חלוקה אופקית",
|
"distributeHorizontally": "חלוקה אופקית",
|
||||||
"distributeVertically": "חלוקה אנכית"
|
"distributeVertically": "חלוקה אנכית",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "אפס את הלוח",
|
"clearReset": "אפס את הלוח",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "לא ניתן לפענח מידע.",
|
"decryptFailed": "לא ניתן לפענח מידע.",
|
||||||
"uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.",
|
"uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.",
|
||||||
"loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?",
|
"loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "קרתה שגיאה בטעינת הספריה החיצונית.",
|
"errorLoadingLibrary": "קרתה שגיאה בטעינת הספריה החיצונית.",
|
||||||
"confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?",
|
"confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?",
|
||||||
"imageDoesNotContainScene": "אין תמיכה בייבוא תמונות כעת.\n\nהאם אתה רוצה לייבא תצוגה? התמונה הזאת אינה מכילה מידע על תצוגה. האם הפעלת את האפשרות הזאת בזמן הוצאת המידע?",
|
"imageDoesNotContainScene": "אין תמיכה בייבוא תמונות כעת.\n\nהאם אתה רוצה לייבא תצוגה? התמונה הזאת אינה מכילה מידע על תצוגה. האם הפעלת את האפשרות הזאת בזמן הוצאת המידע?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "אחסון",
|
"storage": "אחסון",
|
||||||
"title": "סטטיסטיקות לחנונים",
|
"title": "סטטיסטיקות לחנונים",
|
||||||
"total": "סה״כ",
|
"total": "סה״כ",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "רוחב"
|
"width": "רוחב"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-21
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "लंबवत केन्द्रित",
|
"centerVertically": "लंबवत केन्द्रित",
|
||||||
"centerHorizontally": "क्षैतिज केन्द्रित",
|
"centerHorizontally": "क्षैतिज केन्द्रित",
|
||||||
"distributeHorizontally": "क्षैतिज रूप से वितरित करें",
|
"distributeHorizontally": "क्षैतिज रूप से वितरित करें",
|
||||||
"distributeVertically": "खड़ी रूप से वितरित करें"
|
"distributeVertically": "खड़ी रूप से वितरित करें",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "कैनवास रीसेट करें",
|
"clearReset": "कैनवास रीसेट करें",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "डेटा को डिक्रिप्ट नहीं किया जा सका।",
|
"decryptFailed": "डेटा को डिक्रिप्ट नहीं किया जा सका।",
|
||||||
"uploadedSecurly": "अपलोड को एंड-टू-एंड एन्क्रिप्शन के साथ सुरक्षित किया गया है, जिसका मतलब है कि एक्सक्लूसिव सर्वर और थर्ड पार्टी कंटेंट नहीं पढ़ सकते हैं।",
|
"uploadedSecurly": "अपलोड को एंड-टू-एंड एन्क्रिप्शन के साथ सुरक्षित किया गया है, जिसका मतलब है कि एक्सक्लूसिव सर्वर और थर्ड पार्टी कंटेंट नहीं पढ़ सकते हैं।",
|
||||||
"loadSceneOverridePrompt": "लोड हो रहा है बाहरी ड्राइंग आपके मौजूदा सामग्री को बदल देगा। क्या आप जारी रखना चाहते हैं?",
|
"loadSceneOverridePrompt": "लोड हो रहा है बाहरी ड्राइंग आपके मौजूदा सामग्री को बदल देगा। क्या आप जारी रखना चाहते हैं?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "लाइब्रेरी लोड करने में त्रुटि",
|
"errorLoadingLibrary": "लाइब्रेरी लोड करने में त्रुटि",
|
||||||
"confirmAddLibrary": "लाइब्रेरी जोड़ें पुष्टि करें आकार संख्या",
|
"confirmAddLibrary": "लाइब्रेरी जोड़ें पुष्टि करें आकार संख्या",
|
||||||
"imageDoesNotContainScene": "दृश्य में छवि नहीं है",
|
"imageDoesNotContainScene": "दृश्य में छवि नहीं है",
|
||||||
@@ -200,25 +202,25 @@
|
|||||||
"title": "गलती"
|
"title": "गलती"
|
||||||
},
|
},
|
||||||
"helpDialog": {
|
"helpDialog": {
|
||||||
"blog": "",
|
"blog": "हमारा ब्लॉग पढे",
|
||||||
"click": "",
|
"click": "क्लिक करें",
|
||||||
"curvedArrow": "",
|
"curvedArrow": "वक्र तीर",
|
||||||
"curvedLine": "",
|
"curvedLine": "वक्र रेखा",
|
||||||
"documentation": "",
|
"documentation": "",
|
||||||
"drag": "",
|
"drag": "खींचें",
|
||||||
"editor": "",
|
"editor": "संपादक",
|
||||||
"github": "",
|
"github": "मुद्दा मिला? प्रस्तुत करें",
|
||||||
"howto": "",
|
"howto": "हमारे गाइड का पालन करें",
|
||||||
"or": "",
|
"or": "या",
|
||||||
"preventBinding": "",
|
"preventBinding": "तीर बंधन रोकें",
|
||||||
"shapes": "",
|
"shapes": "आकृतियाँ",
|
||||||
"shortcuts": "",
|
"shortcuts": "कीबोर्ड के शॉर्टकट्स",
|
||||||
"textFinish": "",
|
"textFinish": "संपादन समाप्त करें (पाठ)",
|
||||||
"textNewLine": "",
|
"textNewLine": "नई पंक्ति जोड़ें (पाठ)",
|
||||||
"title": "",
|
"title": "मदद",
|
||||||
"view": "",
|
"view": "दृश्य",
|
||||||
"zoomToFit": "",
|
"zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें",
|
||||||
"zoomToSelection": ""
|
"zoomToSelection": "चयन तक ज़ूम करे"
|
||||||
},
|
},
|
||||||
"encrypted": {
|
"encrypted": {
|
||||||
"tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
|
"tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "संग्रह",
|
"storage": "संग्रह",
|
||||||
"title": "बेवकूफ के लिए आँकड़े",
|
"title": "बेवकूफ के लिए आँकड़े",
|
||||||
"total": "कुल",
|
"total": "कुल",
|
||||||
|
"version": "संस्करण",
|
||||||
|
"versionCopy": "काॅपी करने के लिए क्लिक करें",
|
||||||
|
"versionNotAvailable": "संस्करण उपलब्ध नहीं है",
|
||||||
"width": "चौड़ाई"
|
"width": "चौड़ाई"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "काॅपी कीए स्टाइल",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboard": "क्लिपबोर्ड में कॉपी कीए",
|
||||||
|
"copyToClipboardAsPng": "क्लिपबोर्ड में PNG के रूप में कॉपी किए"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Hálómód",
|
"gridMode": "Hálómód",
|
||||||
"addToLibrary": "Hozzáadás a könyvtárhoz",
|
"addToLibrary": "Hozzáadás a könyvtárhoz",
|
||||||
"removeFromLibrary": "Eltávólítás a könyvtárból",
|
"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",
|
"libraries": "Könyvtárak böngészése",
|
||||||
"loadingScene": "Jelenet betöltése...",
|
"loadingScene": "Jelenet betöltése…",
|
||||||
"align": "Igazítás",
|
"align": "Igazítás",
|
||||||
"alignTop": "Felülre igazítás",
|
"alignTop": "Felülre igazítás",
|
||||||
"alignBottom": "Alulra igazítás",
|
"alignBottom": "Alulra igazítás",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Függőlegesen középre igazított",
|
"centerVertically": "Függőlegesen középre igazított",
|
||||||
"centerHorizontally": "Vízszintesen középre igazított",
|
"centerHorizontally": "Vízszintesen középre igazított",
|
||||||
"distributeHorizontally": "Vízszintes elosztás",
|
"distributeHorizontally": "Vízszintes elosztás",
|
||||||
"distributeVertically": "Függőleges elosztás"
|
"distributeVertically": "Függőleges elosztás",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Vászon törlése",
|
"clearReset": "Vászon törlése",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Nem sikerült visszafejteni a titkosított adatot.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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.",
|
"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.",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Tárhely",
|
"storage": "Tárhely",
|
||||||
"title": "Statisztikák",
|
"title": "Statisztikák",
|
||||||
"total": "Összesen",
|
"total": "Összesen",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Szélesség"
|
"width": "Szélesség"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Mode grid",
|
"gridMode": "Mode grid",
|
||||||
"addToLibrary": "Tambahkan ke pustaka",
|
"addToLibrary": "Tambahkan ke pustaka",
|
||||||
"removeFromLibrary": "Hapus dari pustaka",
|
"removeFromLibrary": "Hapus dari pustaka",
|
||||||
"libraryLoadingMessage": "Memuat pustaka...",
|
"libraryLoadingMessage": "Memuat pustaka…",
|
||||||
"libraries": "Telusur pustaka",
|
"libraries": "Telusur pustaka",
|
||||||
"loadingScene": "Memuat pemandangan...",
|
"loadingScene": "Memuat pemandangan…",
|
||||||
"align": "Perataan",
|
"align": "Perataan",
|
||||||
"alignTop": "Rata atas",
|
"alignTop": "Rata atas",
|
||||||
"alignBottom": "Rata bawah",
|
"alignBottom": "Rata bawah",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Pusatkan secara vertikal",
|
"centerVertically": "Pusatkan secara vertikal",
|
||||||
"centerHorizontally": "Pusatkan secara horizontal",
|
"centerHorizontally": "Pusatkan secara horizontal",
|
||||||
"distributeHorizontally": "Distribusikan horizontal",
|
"distributeHorizontally": "Distribusikan horizontal",
|
||||||
"distributeVertically": "Distribusikan vertikal"
|
"distributeVertically": "Distribusikan vertikal",
|
||||||
|
"viewMode": "Mode tampilan"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Setel Ulang Kanvas",
|
"clearReset": "Setel Ulang Kanvas",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Tidak dapat mengdekripsi data.",
|
"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",
|
"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?",
|
"loadSceneOverridePrompt": "Memuat gambar external akan mengganti konten Anda yang ada. Apakah Anda ingin melanjutkan?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Terdapat kesalahan dalam memuat pustaka pihak ketiga.",
|
"errorLoadingLibrary": "Terdapat kesalahan dalam memuat pustaka pihak ketiga.",
|
||||||
"confirmAddLibrary": "Ini akan menambahkan {{numShapes}} bentuk ke pustaka Anda. Anda yakin?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Penyimpanan",
|
"storage": "Penyimpanan",
|
||||||
"title": "Statistik untuk nerd",
|
"title": "Statistik untuk nerd",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Lebar"
|
"width": "Lebar"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Gaya tersalin.",
|
"copyStyles": "Gaya tersalin.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Tersalin ke clipboard sebagai PNG."
|
"copyToClipboardAsPng": "Tersalin ke clipboard sebagai PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Modalità griglia",
|
"gridMode": "Modalità griglia",
|
||||||
"addToLibrary": "Aggiungi alla libreria",
|
"addToLibrary": "Aggiungi alla libreria",
|
||||||
"removeFromLibrary": "Rimuovi dalla libreria",
|
"removeFromLibrary": "Rimuovi dalla libreria",
|
||||||
"libraryLoadingMessage": "Caricamento della biblioteca...",
|
"libraryLoadingMessage": "Caricamento libreria…",
|
||||||
"libraries": "Sfoglia librerie",
|
"libraries": "Sfoglia librerie",
|
||||||
"loadingScene": "Caricamento della scena...",
|
"loadingScene": "Caricamento della scena…",
|
||||||
"align": "Allinea",
|
"align": "Allinea",
|
||||||
"alignTop": "Allinea in alto",
|
"alignTop": "Allinea in alto",
|
||||||
"alignBottom": "Allinea in basso",
|
"alignBottom": "Allinea in basso",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centra Verticalmente",
|
"centerVertically": "Centra Verticalmente",
|
||||||
"centerHorizontally": "Centra orizzontalmente",
|
"centerHorizontally": "Centra orizzontalmente",
|
||||||
"distributeHorizontally": "Distribuisci orizzontalmente",
|
"distributeHorizontally": "Distribuisci orizzontalmente",
|
||||||
"distributeVertically": "Distribuisci verticalmente"
|
"distributeVertically": "Distribuisci verticalmente",
|
||||||
|
"viewMode": "Modalità visualizzazione"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Svuota la tela",
|
"clearReset": "Svuota la tela",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Impossibile decriptare i dati.",
|
"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.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Si è verificato un errore nel caricamento della libreria di terze parti.",
|
||||||
"confirmAddLibrary": "Questo aggiungerà {{numShapes}} forma(e) alla tua libreria. Sei sicuro?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Memoria",
|
"storage": "Memoria",
|
||||||
"title": "Statistiche per nerd",
|
"title": "Statistiche per nerd",
|
||||||
"total": "Totale",
|
"total": "Totale",
|
||||||
|
"version": "Versione",
|
||||||
|
"versionCopy": "Clicca per copiare",
|
||||||
|
"versionNotAvailable": "Versione non disponibile",
|
||||||
"width": "Larghezza"
|
"width": "Larghezza"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Stili copiati.",
|
"copyStyles": "Stili copiati.",
|
||||||
|
"copyToClipboard": "Copiato negli appunti.",
|
||||||
"copyToClipboardAsPng": "Copiato negli appunti come PNG."
|
"copyToClipboardAsPng": "Copiato negli appunti come PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "",
|
"gridMode": "",
|
||||||
"addToLibrary": "ライブラリに追加",
|
"addToLibrary": "ライブラリに追加",
|
||||||
"removeFromLibrary": "ライブラリから削除",
|
"removeFromLibrary": "ライブラリから削除",
|
||||||
"libraryLoadingMessage": "ライブラリを読み込み中...",
|
"libraryLoadingMessage": "ライブラリを読み込み中…",
|
||||||
"libraries": "",
|
"libraries": "",
|
||||||
"loadingScene": "シーンを読み込み中...",
|
"loadingScene": "シーンを読み込み中…",
|
||||||
"align": "整列",
|
"align": "整列",
|
||||||
"alignTop": "上揃え",
|
"alignTop": "上揃え",
|
||||||
"alignBottom": "下揃え",
|
"alignBottom": "下揃え",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "縦方向に中央揃え",
|
"centerVertically": "縦方向に中央揃え",
|
||||||
"centerHorizontally": "横方向に中央揃え",
|
"centerHorizontally": "横方向に中央揃え",
|
||||||
"distributeHorizontally": "",
|
"distributeHorizontally": "",
|
||||||
"distributeVertically": ""
|
"distributeVertically": "",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "キャンバスのリセット",
|
"clearReset": "キャンバスのリセット",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "データを復号できませんでした。",
|
"decryptFailed": "データを復号できませんでした。",
|
||||||
"uploadedSecurly": "データのアップロードはエンドツーエンド暗号化によって保護されています。Excalidrawサーバーと第三者はデータの内容を見ることができません。",
|
"uploadedSecurly": "データのアップロードはエンドツーエンド暗号化によって保護されています。Excalidrawサーバーと第三者はデータの内容を見ることができません。",
|
||||||
"loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?",
|
"loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "サードパーティライブラリの読み込み中にエラーが発生しました。",
|
"errorLoadingLibrary": "サードパーティライブラリの読み込み中にエラーが発生しました。",
|
||||||
"confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?",
|
"confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?",
|
||||||
"imageDoesNotContainScene": "",
|
"imageDoesNotContainScene": "",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "",
|
"storage": "",
|
||||||
"title": "",
|
"title": "",
|
||||||
"total": "合計",
|
"total": "合計",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "幅"
|
"width": "幅"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
{
|
||||||
|
"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": "격자 방식",
|
"gridMode": "격자 방식",
|
||||||
"addToLibrary": "라이브러리에 추가",
|
"addToLibrary": "라이브러리에 추가",
|
||||||
"removeFromLibrary": "라이브러리에서 제거",
|
"removeFromLibrary": "라이브러리에서 제거",
|
||||||
"libraryLoadingMessage": "라이브러리 불러오는 중...",
|
"libraryLoadingMessage": "라이브러리 불러오는 중…",
|
||||||
"libraries": "라이브러리 찾기",
|
"libraries": "라이브러리 찾기",
|
||||||
"loadingScene": "화면 불러오는 중...",
|
"loadingScene": "화면 불러오는 중…",
|
||||||
"align": "정렬",
|
"align": "정렬",
|
||||||
"alignTop": "상단 정렬",
|
"alignTop": "상단 정렬",
|
||||||
"alignBottom": "하단 정렬",
|
"alignBottom": "하단 정렬",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "수직으로 중앙 정렬",
|
"centerVertically": "수직으로 중앙 정렬",
|
||||||
"centerHorizontally": "수평으로 중앙 정렬",
|
"centerHorizontally": "수평으로 중앙 정렬",
|
||||||
"distributeHorizontally": "수평으로 분배",
|
"distributeHorizontally": "수평으로 분배",
|
||||||
"distributeVertically": "수직으로 분배"
|
"distributeVertically": "수직으로 분배",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "캔버스 초기화",
|
"clearReset": "캔버스 초기화",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "데이터를 복호화하지 못했습니다.",
|
"decryptFailed": "데이터를 복호화하지 못했습니다.",
|
||||||
"uploadedSecurly": "업로드는 종단 간 암호화로 보호되므로 Excalidraw 서버 및 타사가 콘텐츠를 읽을 수 없습니다.",
|
"uploadedSecurly": "업로드는 종단 간 암호화로 보호되므로 Excalidraw 서버 및 타사가 콘텐츠를 읽을 수 없습니다.",
|
||||||
"loadSceneOverridePrompt": "외부 파일을 불러 오면 기존 콘텐츠가 대체됩니다. 계속 진행할까요?",
|
"loadSceneOverridePrompt": "외부 파일을 불러 오면 기존 콘텐츠가 대체됩니다. 계속 진행할까요?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "외부 라이브러리를 불러오는 중에 문제가 발생했습니다.",
|
"errorLoadingLibrary": "외부 라이브러리를 불러오는 중에 문제가 발생했습니다.",
|
||||||
"confirmAddLibrary": "{{numShapes}}개의 모양이 라이브러리에 추가됩니다. 계속하시겠어요?",
|
"confirmAddLibrary": "{{numShapes}}개의 모양이 라이브러리에 추가됩니다. 계속하시겠어요?",
|
||||||
"imageDoesNotContainScene": "이미지에서 불러오기는 현재 지원되지 않습니다.\n\n화면을 불러오려고 하셨나요? 이미지에 화면 정보가 없는 것 같습니다. 내보낼 때 화면을 포함했나요?",
|
"imageDoesNotContainScene": "이미지에서 불러오기는 현재 지원되지 않습니다.\n\n화면을 불러오려고 하셨나요? 이미지에 화면 정보가 없는 것 같습니다. 내보낼 때 화면을 포함했나요?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "저장공간",
|
"storage": "저장공간",
|
||||||
"title": "덕후들을 위한 통계",
|
"title": "덕후들을 위한 통계",
|
||||||
"total": "합계",
|
"total": "합계",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "너비"
|
"width": "너비"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "",
|
"gridMode": "",
|
||||||
"addToLibrary": "မှတ်တမ်းတင်",
|
"addToLibrary": "မှတ်တမ်းတင်",
|
||||||
"removeFromLibrary": "မှတ်တမ်းမှထုတ်",
|
"removeFromLibrary": "မှတ်တမ်းမှထုတ်",
|
||||||
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်...",
|
"libraryLoadingMessage": "မှတ်တမ်းအား တင်သွင်းနေသည်…",
|
||||||
"libraries": "စာကြည့်တိုက်တွင်ရှာဖွေပါ",
|
"libraries": "စာကြည့်တိုက်တွင်ရှာဖွေပါ",
|
||||||
"loadingScene": "မြင်ကွင်းဖော်နေသည်...",
|
"loadingScene": "မြင်ကွင်းဖော်နေသည်…",
|
||||||
"align": "ချိန်ညှိ",
|
"align": "ချိန်ညှိ",
|
||||||
"alignTop": "ထိပ်ညှိ",
|
"alignTop": "ထိပ်ညှိ",
|
||||||
"alignBottom": "အခြေညှိ",
|
"alignBottom": "အခြေညှိ",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "ဒေါင်လိုက်အလယ်ညှိ",
|
"centerVertically": "ဒေါင်လိုက်အလယ်ညှိ",
|
||||||
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
|
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
|
||||||
"distributeHorizontally": "အလျားလိုက်",
|
"distributeHorizontally": "အလျားလိုက်",
|
||||||
"distributeVertically": "ထောင်လိုက်"
|
"distributeVertically": "ထောင်လိုက်",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "ကားချပ်ရှင်းလင်း",
|
"clearReset": "ကားချပ်ရှင်းလင်း",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "အချက်အလက်ဖော်ယူ၍မရပါ။",
|
"decryptFailed": "အချက်အလက်ဖော်ယူ၍မရပါ။",
|
||||||
"uploadedSecurly": "တင်သွင်းအချက်အလက်များအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်စနစ်အသုံးပြု၍လုံခြုံစွာထိန်းသိမ်းထားပါသဖြင့် Excalidraw ဆာဗာနှင့်ဆက်စပ်အဖွဲ့အစည်းများပင်လျှင်မဖတ်ရှုနိုင်ပါ။",
|
"uploadedSecurly": "တင်သွင်းအချက်အလက်များအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်စနစ်အသုံးပြု၍လုံခြုံစွာထိန်းသိမ်းထားပါသဖြင့် Excalidraw ဆာဗာနှင့်ဆက်စပ်အဖွဲ့အစည်းများပင်လျှင်မဖတ်ရှုနိုင်ပါ။",
|
||||||
"loadSceneOverridePrompt": "လက်ရှိရေးဆွဲထားသမျှအား ပြင်ပမှတင်သွင်းသောပုံနှင့်အစားထိုးပါမည်။ ဆက်လက်ဆောင်ရွက်လိုပါသလား။",
|
"loadSceneOverridePrompt": "လက်ရှိရေးဆွဲထားသမျှအား ပြင်ပမှတင်သွင်းသောပုံနှင့်အစားထိုးပါမည်။ ဆက်လက်ဆောင်ရွက်လိုပါသလား။",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "ပြင်ပမှမှတ်တမ်းအားတင်သွင်းရာတွင်အမှားအယွင်းရှိနေသည်။",
|
"errorLoadingLibrary": "ပြင်ပမှမှတ်တမ်းအားတင်သွင်းရာတွင်အမှားအယွင်းရှိနေသည်။",
|
||||||
"confirmAddLibrary": "{{numShapes}} ခုသောပုံသဏ္ဌာန်အားမှတ်တမ်းတင်ပါမည်။ အတည်ပြုပါ။",
|
"confirmAddLibrary": "{{numShapes}} ခုသောပုံသဏ္ဌာန်အားမှတ်တမ်းတင်ပါမည်။ အတည်ပြုပါ။",
|
||||||
"imageDoesNotContainScene": "",
|
"imageDoesNotContainScene": "",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "သိုလှောင်ခန်း",
|
"storage": "သိုလှောင်ခန်း",
|
||||||
"title": "အက္ခရာများအတွက်အချက်အလက်များ",
|
"title": "အက္ခရာများအတွက်အချက်အလက်များ",
|
||||||
"total": "စုစုပေါင်း",
|
"total": "စုစုပေါင်း",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "အကျယ်"
|
"width": "အကျယ်"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Rutevisning",
|
"gridMode": "Rutevisning",
|
||||||
"addToLibrary": "Legg til i bibliotek",
|
"addToLibrary": "Legg til i bibliotek",
|
||||||
"removeFromLibrary": "Fjern fra bibliotek",
|
"removeFromLibrary": "Fjern fra bibliotek",
|
||||||
"libraryLoadingMessage": "Laster bibliotek...",
|
"libraryLoadingMessage": "Laster bibliotek…",
|
||||||
"libraries": "Bla gjennom biblioteker",
|
"libraries": "Bla gjennom biblioteker",
|
||||||
"loadingScene": "Laster inn scene...",
|
"loadingScene": "Laster inn scene…",
|
||||||
"align": "Juster",
|
"align": "Juster",
|
||||||
"alignTop": "Juster øverst",
|
"alignTop": "Juster øverst",
|
||||||
"alignBottom": "Juster nederst",
|
"alignBottom": "Juster nederst",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Midtstill vertikalt",
|
"centerVertically": "Midtstill vertikalt",
|
||||||
"centerHorizontally": "Midtstill horisontalt",
|
"centerHorizontally": "Midtstill horisontalt",
|
||||||
"distributeHorizontally": "Distribuer horisontalt",
|
"distributeHorizontally": "Distribuer horisontalt",
|
||||||
"distributeVertically": "Distribuer vertikalt"
|
"distributeVertically": "Distribuer vertikalt",
|
||||||
|
"viewMode": "Visningsmodus"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Tøm lerretet og tilbakestill bakgrunnsfargen",
|
"clearReset": "Tøm lerretet og tilbakestill bakgrunnsfargen",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Kunne ikke dekryptere data.",
|
"decryptFailed": "Kunne ikke dekryptere data.",
|
||||||
"uploadedSecurly": "Opplastingen er kryptert og kan ikke leses av Excalidraw-serveren eller tredjeparter.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Det oppstod en feil under lasting av tredjepartsbiblioteket.",
|
||||||
"confirmAddLibrary": "Dette vil legge til {{numShapes}} figur(er) i biblioteket ditt. Er du sikker?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Lagring",
|
"storage": "Lagring",
|
||||||
"title": "Statistikk for nerder",
|
"title": "Statistikk for nerder",
|
||||||
"total": "Totalt",
|
"total": "Totalt",
|
||||||
|
"version": "Versjon",
|
||||||
|
"versionCopy": "Klikk for å kopiere",
|
||||||
|
"versionNotAvailable": "Versjon ikke tilgjengelig",
|
||||||
"width": "Bredde"
|
"width": "Bredde"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Kopierte stiler.",
|
"copyStyles": "Kopierte stiler.",
|
||||||
|
"copyToClipboard": "Kopiert til utklippstavlen.",
|
||||||
"copyToClipboardAsPng": "Kopiert til utklippstavlen som PNG."
|
"copyToClipboardAsPng": "Kopiert til utklippstavlen som PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Rasterweergave",
|
"gridMode": "Rasterweergave",
|
||||||
"addToLibrary": "Voeg toe aan bibliotheek",
|
"addToLibrary": "Voeg toe aan bibliotheek",
|
||||||
"removeFromLibrary": "Verwijder uit bibliotheek",
|
"removeFromLibrary": "Verwijder uit bibliotheek",
|
||||||
"libraryLoadingMessage": "Bibliotheek laden...",
|
"libraryLoadingMessage": "Bibliotheek laden…",
|
||||||
"libraries": "Blader door bibliotheken",
|
"libraries": "Blader door bibliotheken",
|
||||||
"loadingScene": "Scène laden...",
|
"loadingScene": "Scène laden…",
|
||||||
"align": "Uitlijnen",
|
"align": "Uitlijnen",
|
||||||
"alignTop": "Boven uitlijnen",
|
"alignTop": "Boven uitlijnen",
|
||||||
"alignBottom": "Onder uitlijnen",
|
"alignBottom": "Onder uitlijnen",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Verticaal Centreren",
|
"centerVertically": "Verticaal Centreren",
|
||||||
"centerHorizontally": "Horizontaal Centreren",
|
"centerHorizontally": "Horizontaal Centreren",
|
||||||
"distributeHorizontally": "Horizontaal verspreiden",
|
"distributeHorizontally": "Horizontaal verspreiden",
|
||||||
"distributeVertically": "Verticaal distribueren"
|
"distributeVertically": "Verticaal distribueren",
|
||||||
|
"viewMode": "Weergavemodus"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Canvas opnieuw instellen",
|
"clearReset": "Canvas opnieuw instellen",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Kan gegevens niet decoderen.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Opslag",
|
"storage": "Opslag",
|
||||||
"title": "Statistieken voor nerds",
|
"title": "Statistieken voor nerds",
|
||||||
"total": "Totaal",
|
"total": "Totaal",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Breedte"
|
"width": "Breedte"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Stijlen gekopieerd.",
|
"copyStyles": "Stijlen gekopieerd.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Gekopieerd naar klembord als PNG."
|
"copyToClipboardAsPng": "Gekopieerd naar klembord als PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-9
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Rutevisning",
|
"gridMode": "Rutevisning",
|
||||||
"addToLibrary": "Legg til i bibliotek",
|
"addToLibrary": "Legg til i bibliotek",
|
||||||
"removeFromLibrary": "Fjern frå bibliotek",
|
"removeFromLibrary": "Fjern frå bibliotek",
|
||||||
"libraryLoadingMessage": "Laster bibliotek...",
|
"libraryLoadingMessage": "Laster bibliotek…",
|
||||||
"libraries": "Blad gjennom bibliotek",
|
"libraries": "Blad gjennom bibliotek",
|
||||||
"loadingScene": "Laster scene...",
|
"loadingScene": "Laster scene…",
|
||||||
"align": "Juster",
|
"align": "Juster",
|
||||||
"alignTop": "Juster til topp",
|
"alignTop": "Juster til topp",
|
||||||
"alignBottom": "Juster til botn",
|
"alignBottom": "Juster til botn",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Midtstill vertikalt",
|
"centerVertically": "Midtstill vertikalt",
|
||||||
"centerHorizontally": "Midtstill horisontalt",
|
"centerHorizontally": "Midtstill horisontalt",
|
||||||
"distributeHorizontally": "Sprei horisontalt",
|
"distributeHorizontally": "Sprei horisontalt",
|
||||||
"distributeVertically": "Sprei vertikalt"
|
"distributeVertically": "Sprei vertikalt",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Tilbakestill lerretet",
|
"clearReset": "Tilbakestill lerretet",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Kunne ikkje dekryptere data.",
|
"decryptFailed": "Kunne ikkje dekryptere data.",
|
||||||
"uploadedSecurly": "Opplastinga er kryptert og er ikkje mogleg å lese av Excalidraw-serveren eller tredjepartar.",
|
"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?",
|
"loadSceneOverridePrompt": "Innlasting av ekstern teikning erstattar ditt eksisterande innhald. Ynskjer du å fortsette?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Det oppstod ein feil under lastinga av tredjepartsbibliotek.",
|
"errorLoadingLibrary": "Det oppstod ein feil under lastinga av tredjepartsbibliotek.",
|
||||||
"confirmAddLibrary": "Dette vil legge til {{numShapes}} form(er) i biblioteket ditt. Er du sikker?",
|
"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?",
|
"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?",
|
||||||
@@ -201,22 +203,22 @@
|
|||||||
},
|
},
|
||||||
"helpDialog": {
|
"helpDialog": {
|
||||||
"blog": "",
|
"blog": "",
|
||||||
"click": "",
|
"click": "klikk",
|
||||||
"curvedArrow": "",
|
"curvedArrow": "",
|
||||||
"curvedLine": "",
|
"curvedLine": "",
|
||||||
"documentation": "",
|
"documentation": "",
|
||||||
"drag": "",
|
"drag": "",
|
||||||
"editor": "",
|
"editor": "Redigering",
|
||||||
"github": "",
|
"github": "",
|
||||||
"howto": "",
|
"howto": "",
|
||||||
"or": "",
|
"or": "eller",
|
||||||
"preventBinding": "",
|
"preventBinding": "",
|
||||||
"shapes": "",
|
"shapes": "Formar",
|
||||||
"shortcuts": "",
|
"shortcuts": "",
|
||||||
"textFinish": "",
|
"textFinish": "",
|
||||||
"textNewLine": "",
|
"textNewLine": "",
|
||||||
"title": "",
|
"title": "Hjelp",
|
||||||
"view": "",
|
"view": "Vising",
|
||||||
"zoomToFit": "",
|
"zoomToFit": "",
|
||||||
"zoomToSelection": ""
|
"zoomToSelection": ""
|
||||||
},
|
},
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Lagring",
|
"storage": "Lagring",
|
||||||
"title": "Statistikk for nerdar",
|
"title": "Statistikk for nerdar",
|
||||||
"total": "Totalt",
|
"total": "Totalt",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Breidde"
|
"width": "Breidde"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "ਜਾਲੀਦਾਰ ਮੋਡ",
|
"gridMode": "ਜਾਲੀਦਾਰ ਮੋਡ",
|
||||||
"addToLibrary": "ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਜੋੜੋ",
|
"addToLibrary": "ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਜੋੜੋ",
|
||||||
"removeFromLibrary": "ਲਾਇਬ੍ਰੇਰੀ 'ਚੋਂ ਹਟਾਓ",
|
"removeFromLibrary": "ਲਾਇਬ੍ਰੇਰੀ 'ਚੋਂ ਹਟਾਓ",
|
||||||
"libraryLoadingMessage": "ਲਾਇਬ੍ਰੇਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...",
|
"libraryLoadingMessage": "ਲਾਇਬ੍ਰੇਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…",
|
||||||
"libraries": "ਲਾਇਬ੍ਰੇਰੀਆਂ ਬਰਾਉਜ਼ ਕਰੋ",
|
"libraries": "ਲਾਇਬ੍ਰੇਰੀਆਂ ਬਰਾਉਜ਼ ਕਰੋ",
|
||||||
"loadingScene": "ਦ੍ਰਿਸ਼ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...",
|
"loadingScene": "ਦ੍ਰਿਸ਼ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…",
|
||||||
"align": "ਇਕਸਾਰ",
|
"align": "ਇਕਸਾਰ",
|
||||||
"alignTop": "ਉੱਪਰ ਇਕਸਾਰ ਕਰੋ",
|
"alignTop": "ਉੱਪਰ ਇਕਸਾਰ ਕਰੋ",
|
||||||
"alignBottom": "ਹੇਠਾਂ ਇਕਸਾਰ ਕਰੋ",
|
"alignBottom": "ਹੇਠਾਂ ਇਕਸਾਰ ਕਰੋ",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "ਲੇਟਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
"centerVertically": "ਲੇਟਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
||||||
"centerHorizontally": "ਖੜ੍ਹਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
"centerHorizontally": "ਖੜ੍ਹਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
|
||||||
"distributeHorizontally": "ਖੜ੍ਹਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
|
"distributeHorizontally": "ਖੜ੍ਹਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
|
||||||
"distributeVertically": "ਲੇਟਵੇਂ ਇਕਸਾਰ ਵੰਡੋ"
|
"distributeVertically": "ਲੇਟਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
|
||||||
|
"viewMode": "ਦੇਖੋ ਮੋਡ"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "ਕੈਨਵਸ ਰੀਸੈੱਟ ਕਰੋ",
|
"clearReset": "ਕੈਨਵਸ ਰੀਸੈੱਟ ਕਰੋ",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "ਡਾਟਾ ਡੀਕਰਿਪਟ ਨਹੀਂ ਕਰ ਸਕੇ।",
|
"decryptFailed": "ਡਾਟਾ ਡੀਕਰਿਪਟ ਨਹੀਂ ਕਰ ਸਕੇ।",
|
||||||
"uploadedSecurly": "ਅੱਪਲੋਡ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕਰਿਪਸ਼ਨ ਨਾਲ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਹੋਈ ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਇਹ ਹੈ ਕਿ Excalidraw ਸਰਵਰ ਅਤੇ ਤੀਜੀ ਧਿਰ ਦੇ ਬੰਦੇ ਸਮੱਗਰੀ ਨੂੰ ਪੜ੍ਹ ਨਹੀਂ ਸਕਦੇ।",
|
"uploadedSecurly": "ਅੱਪਲੋਡ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕਰਿਪਸ਼ਨ ਨਾਲ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਹੋਈ ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਇਹ ਹੈ ਕਿ Excalidraw ਸਰਵਰ ਅਤੇ ਤੀਜੀ ਧਿਰ ਦੇ ਬੰਦੇ ਸਮੱਗਰੀ ਨੂੰ ਪੜ੍ਹ ਨਹੀਂ ਸਕਦੇ।",
|
||||||
"loadSceneOverridePrompt": "ਬਾਹਰੀ ਡਰਾਇੰਗ ਨੂੰ ਲੋਡ ਕਰਨਾ ਤੁਹਾਡੀ ਮੌਜੂਦਾ ਸਮੱਗਰੀ ਦੀ ਥਾਂ ਲੈ ਲਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
"loadSceneOverridePrompt": "ਬਾਹਰੀ ਡਰਾਇੰਗ ਨੂੰ ਲੋਡ ਕਰਨਾ ਤੁਹਾਡੀ ਮੌਜੂਦਾ ਸਮੱਗਰੀ ਦੀ ਥਾਂ ਲੈ ਲਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
||||||
|
"collabStopOverridePrompt": "ਇਜਲਾਸ ਨੂੰ ਰੋਕਣਾ ਪਿਛਲੀ ਲੋਕਲ ਸਾਂਭੀ ਡਰਾਇੰਗ ਦੀ ਥਾਂ ਲੈ ਲਵੇਗਾ। ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?\n\n(ਜੇ ਤੁਸੀਂ ਆਪਣੀ ਲੋਕਲ ਡਰਾਇੰਗ ਨੂੰ ਬਰਕਰਾਰ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਇਹ ਕਰਨ ਦੀ ਬਜਾਏ ਬੱਸ ਆਪਣਾ ਟੈਬ ਬੰਦ ਕਰ ਦਿਉ।)",
|
||||||
"errorLoadingLibrary": "ਤੀਜੀ ਧਿਰ ਦੀ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਲੋਡ ਕਰਨ ਵਿੱਚ ਗਲਤੀ ਹੋਈ ਸੀ।",
|
"errorLoadingLibrary": "ਤੀਜੀ ਧਿਰ ਦੀ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਲੋਡ ਕਰਨ ਵਿੱਚ ਗਲਤੀ ਹੋਈ ਸੀ।",
|
||||||
"confirmAddLibrary": "ਇਹ ਤੁਹਾਡੀ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ {{numShapes}} ਆਕ੍ਰਿਤੀ(ਆਂ) ਨੂੰ ਜੋੜ ਦੇਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
"confirmAddLibrary": "ਇਹ ਤੁਹਾਡੀ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ {{numShapes}} ਆਕ੍ਰਿਤੀ(ਆਂ) ਨੂੰ ਜੋੜ ਦੇਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇੰਝ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
||||||
"imageDoesNotContainScene": "ਫਿਲਹਾਲ ਤਸਵੀਰਾਂ ਨੂੰ ਆਯਾਤ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ।\n\nਕੀ ਤੁਸੀਂ ਦ੍ਰਿਸ਼ ਨੂੰ ਆਯਾਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਸੀ? ਇਸ ਤਸਵੀਰ ਵਿੱਚ ਦ੍ਰਿਸ਼ ਦਾ ਕੋਈ ਵੀ ਡਾਟਾ ਨਜ਼ਰ ਨਹੀਂ ਆ ਰਿਹਾ। ਕੀ ਨਿਰਯਾਤ ਦੌਰਾਨ ਤੁਸੀਂ ਇਹ ਸਮਰੱਥ ਕੀਤਾ ਸੀ?",
|
"imageDoesNotContainScene": "ਫਿਲਹਾਲ ਤਸਵੀਰਾਂ ਨੂੰ ਆਯਾਤ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ।\n\nਕੀ ਤੁਸੀਂ ਦ੍ਰਿਸ਼ ਨੂੰ ਆਯਾਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਸੀ? ਇਸ ਤਸਵੀਰ ਵਿੱਚ ਦ੍ਰਿਸ਼ ਦਾ ਕੋਈ ਵੀ ਡਾਟਾ ਨਜ਼ਰ ਨਹੀਂ ਆ ਰਿਹਾ। ਕੀ ਨਿਰਯਾਤ ਦੌਰਾਨ ਤੁਸੀਂ ਇਹ ਸਮਰੱਥ ਕੀਤਾ ਸੀ?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "ਸਟੋਰੇਜ",
|
"storage": "ਸਟੋਰੇਜ",
|
||||||
"title": "ਪੜਾਕੂਆਂ ਲਈ ਅੰਕੜੇ",
|
"title": "ਪੜਾਕੂਆਂ ਲਈ ਅੰਕੜੇ",
|
||||||
"total": "ਕੁੱਲ",
|
"total": "ਕੁੱਲ",
|
||||||
|
"version": "ਸੰਸਕਰਨ",
|
||||||
|
"versionCopy": "ਕਾਪੀ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ",
|
||||||
|
"versionNotAvailable": "ਸੰਸਕਰਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ",
|
||||||
"width": "ਚੌੜਾਈ"
|
"width": "ਚੌੜਾਈ"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "ਕਾਪੀ ਕੀਤੇ ਸਟਾਇਲ।",
|
"copyStyles": "ਕਾਪੀ ਕੀਤੇ ਸਟਾਇਲ।",
|
||||||
|
"copyToClipboard": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ ਕਾਪੀ ਕੀਤਾ।",
|
||||||
"copyToClipboardAsPng": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ PNG ਵਜੋਂ ਕਾਪੀ ਕੀਤਾ।"
|
"copyToClipboardAsPng": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ PNG ਵਜੋਂ ਕਾਪੀ ਕੀਤਾ।"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,36 @@
|
|||||||
{
|
{
|
||||||
"ar-SA": 90,
|
"ar-SA": 87,
|
||||||
"bg-BG": 94,
|
"bg-BG": 91,
|
||||||
"ca-ES": 90,
|
"ca-ES": 87,
|
||||||
"de-DE": 100,
|
"de-DE": 98,
|
||||||
"el-GR": 100,
|
"el-GR": 99,
|
||||||
"en": 100,
|
"en": 100,
|
||||||
"es-ES": 100,
|
"es-ES": 98,
|
||||||
"fa-IR": 98,
|
"fa-IR": 95,
|
||||||
"fi-FI": 100,
|
"fi-FI": 98,
|
||||||
"fr-FR": 100,
|
"fr-FR": 100,
|
||||||
"he-IL": 90,
|
"he-IL": 87,
|
||||||
"hi-IN": 90,
|
"hi-IN": 98,
|
||||||
"hu-HU": 90,
|
"hu-HU": 87,
|
||||||
"id-ID": 100,
|
"id-ID": 97,
|
||||||
"it-IT": 100,
|
"it-IT": 100,
|
||||||
"ja-JP": 81,
|
"ja-JP": 79,
|
||||||
"ko-KR": 90,
|
"kab-KAB": 94,
|
||||||
"my-MM": 83,
|
"ko-KR": 87,
|
||||||
|
"my-MM": 81,
|
||||||
"nb-NO": 100,
|
"nb-NO": 100,
|
||||||
"nl-NL": 100,
|
"nl-NL": 97,
|
||||||
"nn-NO": 90,
|
"nn-NO": 90,
|
||||||
"pa-IN": 100,
|
"pa-IN": 100,
|
||||||
"pl-PL": 90,
|
"pl-PL": 88,
|
||||||
"pt-BR": 100,
|
"pt-BR": 100,
|
||||||
"pt-PT": 100,
|
"pt-PT": 97,
|
||||||
"ro-RO": 100,
|
"ro-RO": 100,
|
||||||
"ru-RU": 100,
|
"ru-RU": 97,
|
||||||
"sk-SK": 100,
|
"sk-SK": 100,
|
||||||
"sv-SE": 100,
|
"sv-SE": 98,
|
||||||
"tr-TR": 90,
|
"tr-TR": 87,
|
||||||
"uk-UA": 100,
|
"uk-UA": 97,
|
||||||
"zh-CN": 100,
|
"zh-CN": 98,
|
||||||
"zh-TW": 100
|
"zh-TW": 100
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Tryb siatki",
|
"gridMode": "Tryb siatki",
|
||||||
"addToLibrary": "Dodaj do biblioteki",
|
"addToLibrary": "Dodaj do biblioteki",
|
||||||
"removeFromLibrary": "Usuń z biblioteki",
|
"removeFromLibrary": "Usuń z biblioteki",
|
||||||
"libraryLoadingMessage": "Wczytywanie biblioteki...",
|
"libraryLoadingMessage": "Ładowanie biblioteki…",
|
||||||
"libraries": "Przeglądaj biblioteki",
|
"libraries": "Przeglądaj biblioteki",
|
||||||
"loadingScene": "Ładowanie sceny...",
|
"loadingScene": "Ładowanie sceny…",
|
||||||
"align": "Wyrównaj",
|
"align": "Wyrównaj",
|
||||||
"alignTop": "Wyrównaj do góry",
|
"alignTop": "Wyrównaj do góry",
|
||||||
"alignBottom": "Wyrównaj do dołu",
|
"alignBottom": "Wyrównaj do dołu",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Wyśrodkuj w pionie",
|
"centerVertically": "Wyśrodkuj w pionie",
|
||||||
"centerHorizontally": "Wyśrodkuj w poziomie",
|
"centerHorizontally": "Wyśrodkuj w poziomie",
|
||||||
"distributeHorizontally": "Rozłóż poziomo",
|
"distributeHorizontally": "Rozłóż poziomo",
|
||||||
"distributeVertically": "Rozłóż pionowo"
|
"distributeVertically": "Rozłóż pionowo",
|
||||||
|
"viewMode": "Tryb widoku"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Wyczyść dokument i zresetuj kolor dokumentu",
|
"clearReset": "Wyczyść dokument i zresetuj kolor dokumentu",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Nie udało się odszyfrować danych.",
|
"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.",
|
"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ć?",
|
"loadSceneOverridePrompt": "Wczytanie zewnętrznego rysunku zastąpi istniejącą zawartość. Czy chcesz kontynuować?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Wystąpił błąd podczas ładowania zewnętrznej biblioteki.",
|
"errorLoadingLibrary": "Wystąpił błąd podczas ładowania zewnętrznej biblioteki.",
|
||||||
"confirmAddLibrary": "To doda {{numShapes}} kształtów do twojej biblioteki. Jesteś pewien?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Pamięć",
|
"storage": "Pamięć",
|
||||||
"title": "Statystyki dla nerdów",
|
"title": "Statystyki dla nerdów",
|
||||||
"total": "Łącznie",
|
"total": "Łącznie",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Szerokość"
|
"width": "Szerokość"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Modo grade",
|
"gridMode": "Modo grade",
|
||||||
"addToLibrary": "Adicionar à biblioteca",
|
"addToLibrary": "Adicionar à biblioteca",
|
||||||
"removeFromLibrary": "Remover da biblioteca",
|
"removeFromLibrary": "Remover da biblioteca",
|
||||||
"libraryLoadingMessage": "Carregando biblioteca...",
|
"libraryLoadingMessage": "Carregando biblioteca…",
|
||||||
"libraries": "Procurar bibliotecas",
|
"libraries": "Procurar bibliotecas",
|
||||||
"loadingScene": "Carregando cena...",
|
"loadingScene": "Carregando cena…",
|
||||||
"align": "Alinhamento",
|
"align": "Alinhamento",
|
||||||
"alignTop": "Alinhar ao topo",
|
"alignTop": "Alinhar ao topo",
|
||||||
"alignBottom": "Alinhar embaixo",
|
"alignBottom": "Alinhar embaixo",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centralizar verticalmente",
|
"centerVertically": "Centralizar verticalmente",
|
||||||
"centerHorizontally": "Centralizar horizontalmente",
|
"centerHorizontally": "Centralizar horizontalmente",
|
||||||
"distributeHorizontally": "Distribuir horizontalmente",
|
"distributeHorizontally": "Distribuir horizontalmente",
|
||||||
"distributeVertically": "Distribuir verticalmente"
|
"distributeVertically": "Distribuir verticalmente",
|
||||||
|
"viewMode": "Modo de visualização"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Não foi possível descriptografar os dados.",
|
"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.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
|
||||||
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Armazenamento",
|
"storage": "Armazenamento",
|
||||||
"title": "Estatísticas para nerds",
|
"title": "Estatísticas para nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "Versão",
|
||||||
|
"versionCopy": "Clique para copiar",
|
||||||
|
"versionNotAvailable": "Versão não disponível",
|
||||||
"width": "Largura"
|
"width": "Largura"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Estilos copiados.",
|
"copyStyles": "Estilos copiados.",
|
||||||
|
"copyToClipboard": "Copiado para área de transferência.",
|
||||||
"copyToClipboardAsPng": "Copiado para a área de transferência como PNG."
|
"copyToClipboardAsPng": "Copiado para a área de transferência como PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Modo grade",
|
"gridMode": "Modo grade",
|
||||||
"addToLibrary": "Adicionar à biblioteca",
|
"addToLibrary": "Adicionar à biblioteca",
|
||||||
"removeFromLibrary": "Remover da biblioteca",
|
"removeFromLibrary": "Remover da biblioteca",
|
||||||
"libraryLoadingMessage": "Carregando biblioteca...",
|
"libraryLoadingMessage": "Carregando biblioteca…",
|
||||||
"libraries": "Procurar bibliotecas",
|
"libraries": "Procurar bibliotecas",
|
||||||
"loadingScene": "Carregando cena...",
|
"loadingScene": "Carregando cena…",
|
||||||
"align": "Alinhamento",
|
"align": "Alinhamento",
|
||||||
"alignTop": "Alinhar ao topo",
|
"alignTop": "Alinhar ao topo",
|
||||||
"alignBottom": "Alinhar ao fundo",
|
"alignBottom": "Alinhar ao fundo",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centralizar verticalmente",
|
"centerVertically": "Centralizar verticalmente",
|
||||||
"centerHorizontally": "Centralizar horizontalmente",
|
"centerHorizontally": "Centralizar horizontalmente",
|
||||||
"distributeHorizontally": "Distribuir horizontalmente",
|
"distributeHorizontally": "Distribuir horizontalmente",
|
||||||
"distributeVertically": "Distribuir verticalmente"
|
"distributeVertically": "Distribuir verticalmente",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
"clearReset": "Limpar o canvas e redefinir a cor de fundo",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Não foi possível descriptografar os dados.",
|
"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.",
|
"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?",
|
"loadSceneOverridePrompt": "Carregar um desenho externo substituirá o seu conteúdo existente. Deseja continuar?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
|
"errorLoadingLibrary": "Houve um erro ao carregar a biblioteca de terceiros.",
|
||||||
"confirmAddLibrary": "Isso adicionará {{numShapes}} forma(s) à sua biblioteca. Tem certeza?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Armazenamento",
|
"storage": "Armazenamento",
|
||||||
"title": "Estatísticas para nerds",
|
"title": "Estatísticas para nerds",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Largura"
|
"width": "Largura"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Estilos copiados.",
|
"copyStyles": "Estilos copiados.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Copiado para o clipboard como PNG."
|
"copyToClipboardAsPng": "Copiado para o clipboard como PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Mod grilă",
|
"gridMode": "Mod grilă",
|
||||||
"addToLibrary": "Adăugare la bibliotecă",
|
"addToLibrary": "Adăugare la bibliotecă",
|
||||||
"removeFromLibrary": "Eliminare din bibliotecă",
|
"removeFromLibrary": "Eliminare din bibliotecă",
|
||||||
"libraryLoadingMessage": "Se încarcă biblioteca...",
|
"libraryLoadingMessage": "Se încarcă biblioteca…",
|
||||||
"libraries": "Răsfoiește bibliotecile",
|
"libraries": "Răsfoiește bibliotecile",
|
||||||
"loadingScene": "Se încarcă scena...",
|
"loadingScene": "Se încarcă scena…",
|
||||||
"align": "Aliniere",
|
"align": "Aliniere",
|
||||||
"alignTop": "Aliniere sus",
|
"alignTop": "Aliniere sus",
|
||||||
"alignBottom": "Aliniere jos",
|
"alignBottom": "Aliniere jos",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centrare verticală",
|
"centerVertically": "Centrare verticală",
|
||||||
"centerHorizontally": "Centrare orizontală",
|
"centerHorizontally": "Centrare orizontală",
|
||||||
"distributeHorizontally": "Distribuie orizontal",
|
"distributeHorizontally": "Distribuie orizontal",
|
||||||
"distributeVertically": "Distribuie vertical"
|
"distributeVertically": "Distribuie vertical",
|
||||||
|
"viewMode": "Mod de vizualizare"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Resetare pânză",
|
"clearReset": "Resetare pânză",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Datele nu au putut fi decriptate.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Stocare",
|
"storage": "Stocare",
|
||||||
"title": "Statistici pentru pasionați",
|
"title": "Statistici pentru pasionați",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"version": "Versiune",
|
||||||
|
"versionCopy": "Clic pentru copiere",
|
||||||
|
"versionNotAvailable": "Versiune indisponibilă",
|
||||||
"width": "Lățime"
|
"width": "Lățime"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Stiluri copiate.",
|
"copyStyles": "Stiluri copiate.",
|
||||||
|
"copyToClipboard": "Copiat în memoria temporară.",
|
||||||
"copyToClipboardAsPng": "Copiat în memoria temporară ca PNG."
|
"copyToClipboardAsPng": "Copiat în memoria temporară ca PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Сетка",
|
"gridMode": "Сетка",
|
||||||
"addToLibrary": "Добавить в библиотеку",
|
"addToLibrary": "Добавить в библиотеку",
|
||||||
"removeFromLibrary": "Удалить из библиотеки",
|
"removeFromLibrary": "Удалить из библиотеки",
|
||||||
"libraryLoadingMessage": "Загрузка библиотеки...",
|
"libraryLoadingMessage": "Загрузка библиотеки…",
|
||||||
"libraries": "Просмотреть библиотеки",
|
"libraries": "Просмотреть библиотеки",
|
||||||
"loadingScene": "Загрузка сцены...",
|
"loadingScene": "Загрузка сцены…",
|
||||||
"align": "Выровнять",
|
"align": "Выровнять",
|
||||||
"alignTop": "Выровнять по верхнему краю",
|
"alignTop": "Выровнять по верхнему краю",
|
||||||
"alignBottom": "Выровнять по нижнему краю",
|
"alignBottom": "Выровнять по нижнему краю",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Центрировать по вертикали",
|
"centerVertically": "Центрировать по вертикали",
|
||||||
"centerHorizontally": "Центрировать по горизонтали",
|
"centerHorizontally": "Центрировать по горизонтали",
|
||||||
"distributeHorizontally": "Распределить по горизонтали",
|
"distributeHorizontally": "Распределить по горизонтали",
|
||||||
"distributeVertically": "Распределить по вертикали"
|
"distributeVertically": "Распределить по вертикали",
|
||||||
|
"viewMode": "Вид"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Очистить холст и сбросить цвет фона",
|
"clearReset": "Очистить холст и сбросить цвет фона",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Не удалось расшифровать данные.",
|
"decryptFailed": "Не удалось расшифровать данные.",
|
||||||
"uploadedSecurly": "Загружаемые данные защищена сквозным шифрованием, что означает, что сервер Excalidraw и третьи стороны не могут прочитать содержимое.",
|
"uploadedSecurly": "Загружаемые данные защищена сквозным шифрованием, что означает, что сервер Excalidraw и третьи стороны не могут прочитать содержимое.",
|
||||||
"loadSceneOverridePrompt": "Загрузка рисунка приведёт к замене имеющегося содержимого. Вы хотите продолжить?",
|
"loadSceneOverridePrompt": "Загрузка рисунка приведёт к замене имеющегося содержимого. Вы хотите продолжить?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Произошла ошибка при загрузке сторонней библиотеки.",
|
"errorLoadingLibrary": "Произошла ошибка при загрузке сторонней библиотеки.",
|
||||||
"confirmAddLibrary": "Будет добавлено {{numShapes}} фигур в вашу библиотеку. Продолжить?",
|
"confirmAddLibrary": "Будет добавлено {{numShapes}} фигур в вашу библиотеку. Продолжить?",
|
||||||
"imageDoesNotContainScene": "Импорт изображений не поддерживается в данный момент.\n\nХотите импортировать сцену? Данное изображение не содержит данных о сцене. Было ли включено это во время экспорта?",
|
"imageDoesNotContainScene": "Импорт изображений не поддерживается в данный момент.\n\nХотите импортировать сцену? Данное изображение не содержит данных о сцене. Было ли включено это во время экспорта?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Хранилище",
|
"storage": "Хранилище",
|
||||||
"title": "Статистика для ботаников",
|
"title": "Статистика для ботаников",
|
||||||
"total": "Всего",
|
"total": "Всего",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Ширина"
|
"width": "Ширина"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Скопированы стили.",
|
"copyStyles": "Скопированы стили.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Скопировано в буфер обмена в формате PNG."
|
"copyToClipboardAsPng": "Скопировано в буфер обмена в формате PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Režim mriežky",
|
"gridMode": "Režim mriežky",
|
||||||
"addToLibrary": "Pridať do knižnice",
|
"addToLibrary": "Pridať do knižnice",
|
||||||
"removeFromLibrary": "Odstrániť z knižnice",
|
"removeFromLibrary": "Odstrániť z knižnice",
|
||||||
"libraryLoadingMessage": "Načítavanie knižnice...",
|
"libraryLoadingMessage": "Načítavanie knižnice…",
|
||||||
"libraries": "Prehliadať knižnice",
|
"libraries": "Prehliadať knižnice",
|
||||||
"loadingScene": "Načítavanie scény...",
|
"loadingScene": "Načítavanie scény…",
|
||||||
"align": "Zarovnanie",
|
"align": "Zarovnanie",
|
||||||
"alignTop": "Zarovnať nahor",
|
"alignTop": "Zarovnať nahor",
|
||||||
"alignBottom": "Zarovnať nadol",
|
"alignBottom": "Zarovnať nadol",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Zarovnať zvislo na stred",
|
"centerVertically": "Zarovnať zvislo na stred",
|
||||||
"centerHorizontally": "Zarovnať vodorovne na stred",
|
"centerHorizontally": "Zarovnať vodorovne na stred",
|
||||||
"distributeHorizontally": "Rozmiestniť vodorovne",
|
"distributeHorizontally": "Rozmiestniť vodorovne",
|
||||||
"distributeVertically": "Rozmiestniť zvisle"
|
"distributeVertically": "Rozmiestniť zvisle",
|
||||||
|
"viewMode": "Režim zobrazenia"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Obnoviť plátno",
|
"clearReset": "Obnoviť plátno",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Nepodarilo sa rozšifrovať údaje.",
|
"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.",
|
"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ť?",
|
"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.",
|
"errorLoadingLibrary": "Nepodarilo sa načítať externú knižnicu.",
|
||||||
"confirmAddLibrary": "Týmto sa pridá {{numShapes}} tvar(ov) do vašej knižnice. Ste si istí?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Úložisko",
|
"storage": "Úložisko",
|
||||||
"title": "Štatistiky",
|
"title": "Štatistiky",
|
||||||
"total": "Celkom",
|
"total": "Celkom",
|
||||||
|
"version": "Verzia",
|
||||||
|
"versionCopy": "Kliknutím skopírujete",
|
||||||
|
"versionNotAvailable": "Verzia nie je k dispozícii",
|
||||||
"width": "Šírka"
|
"width": "Šírka"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Štýly skopírované.",
|
"copyStyles": "Štýly skopírované.",
|
||||||
|
"copyToClipboard": "Skopírované do schránky.",
|
||||||
"copyToClipboardAsPng": "Skopírované do schránky ako PNG."
|
"copyToClipboardAsPng": "Skopírované do schránky ako PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Rutnätsläge",
|
"gridMode": "Rutnätsläge",
|
||||||
"addToLibrary": "Lägg till i biblioteket",
|
"addToLibrary": "Lägg till i biblioteket",
|
||||||
"removeFromLibrary": "Ta bort från bibliotek",
|
"removeFromLibrary": "Ta bort från bibliotek",
|
||||||
"libraryLoadingMessage": "Laddar bibliotek...",
|
"libraryLoadingMessage": "Laddar bibliotek…",
|
||||||
"libraries": "Bläddra i bibliotek",
|
"libraries": "Bläddra i bibliotek",
|
||||||
"loadingScene": "Laddar scen...",
|
"loadingScene": "Laddar skiss…",
|
||||||
"align": "Justera",
|
"align": "Justera",
|
||||||
"alignTop": "Justera överkant",
|
"alignTop": "Justera överkant",
|
||||||
"alignBottom": "Justera underkant",
|
"alignBottom": "Justera underkant",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Centrera vertikalt",
|
"centerVertically": "Centrera vertikalt",
|
||||||
"centerHorizontally": "Centrera horisontellt",
|
"centerHorizontally": "Centrera horisontellt",
|
||||||
"distributeHorizontally": "Fördela horisontellt",
|
"distributeHorizontally": "Fördela horisontellt",
|
||||||
"distributeVertically": "Fördela vertikalt"
|
"distributeVertically": "Fördela vertikalt",
|
||||||
|
"viewMode": "Visningsläge"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Återställ canvasen",
|
"clearReset": "Återställ canvasen",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Kunde inte avkryptera data.",
|
"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.",
|
"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?",
|
"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.",
|
"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?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Lagring",
|
"storage": "Lagring",
|
||||||
"title": "Statistik för nördar",
|
"title": "Statistik för nördar",
|
||||||
"total": "Totalt",
|
"total": "Totalt",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Bredd"
|
"width": "Bredd"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Kopierade stilar.",
|
"copyStyles": "Kopierade stilar.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Kopierat till urklipp som PNG."
|
"copyToClipboardAsPng": "Kopierat till urklipp som PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Izgara modu",
|
"gridMode": "Izgara modu",
|
||||||
"addToLibrary": "Kütüphaneye ekle",
|
"addToLibrary": "Kütüphaneye ekle",
|
||||||
"removeFromLibrary": "Kütüphaneden kaldır",
|
"removeFromLibrary": "Kütüphaneden kaldır",
|
||||||
"libraryLoadingMessage": "Kütüphane yükleniyor...",
|
"libraryLoadingMessage": "Kütüphane yükleniyor…",
|
||||||
"libraries": "Kütüphanelere gözat",
|
"libraries": "Kütüphanelere gözat",
|
||||||
"loadingScene": "Çalışma alanı yükleniyor...",
|
"loadingScene": "Çalışma alanı yükleniyor…",
|
||||||
"align": "Hizala",
|
"align": "Hizala",
|
||||||
"alignTop": "Yukarı hizala",
|
"alignTop": "Yukarı hizala",
|
||||||
"alignBottom": "Aşağı hizala",
|
"alignBottom": "Aşağı hizala",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Dikeyde ortala",
|
"centerVertically": "Dikeyde ortala",
|
||||||
"centerHorizontally": "Yatayda ortala",
|
"centerHorizontally": "Yatayda ortala",
|
||||||
"distributeHorizontally": "Yatay dağıt",
|
"distributeHorizontally": "Yatay dağıt",
|
||||||
"distributeVertically": "Dikey dağıt"
|
"distributeVertically": "Dikey dağıt",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Tuvali sıfırla",
|
"clearReset": "Tuvali sıfırla",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Şifrelenmiş veri çözümlenemedi.",
|
"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.",
|
"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?",
|
"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.",
|
"errorLoadingLibrary": "Üçüncü taraf kitaplığı yüklerken bir hata oluştu.",
|
||||||
"confirmAddLibrary": "Bu, kitaplığınıza {{numShapes}} tane şekil ekleyecek. Emin misiniz?",
|
"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?",
|
"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?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Depolama",
|
"storage": "Depolama",
|
||||||
"title": "İnekler için istatistikler",
|
"title": "İnekler için istatistikler",
|
||||||
"total": "Toplam",
|
"total": "Toplam",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Genişlik"
|
"width": "Genişlik"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "",
|
"copyStyles": "",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": ""
|
"copyToClipboardAsPng": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "Режим сітки",
|
"gridMode": "Режим сітки",
|
||||||
"addToLibrary": "Додати до бібліотеки",
|
"addToLibrary": "Додати до бібліотеки",
|
||||||
"removeFromLibrary": "Видалити з бібліотеки",
|
"removeFromLibrary": "Видалити з бібліотеки",
|
||||||
"libraryLoadingMessage": "Завантажити бібліотеку...",
|
"libraryLoadingMessage": "Завантажити бібліотеку…",
|
||||||
"libraries": "Огляд бібліотек",
|
"libraries": "Огляд бібліотек",
|
||||||
"loadingScene": "Завантаження сцени...",
|
"loadingScene": "Завантаження сцени…",
|
||||||
"align": "Вирівнювання",
|
"align": "Вирівнювання",
|
||||||
"alignTop": "Вирівняти по верхньому краю",
|
"alignTop": "Вирівняти по верхньому краю",
|
||||||
"alignBottom": "Вирівняти по нижньому краю",
|
"alignBottom": "Вирівняти по нижньому краю",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Центрувати по вертикалі",
|
"centerVertically": "Центрувати по вертикалі",
|
||||||
"centerHorizontally": "Центрувати по горизонталі",
|
"centerHorizontally": "Центрувати по горизонталі",
|
||||||
"distributeHorizontally": "Розподілити по горизонталі",
|
"distributeHorizontally": "Розподілити по горизонталі",
|
||||||
"distributeVertically": "Розподілити вертикально"
|
"distributeVertically": "Розподілити вертикально",
|
||||||
|
"viewMode": ""
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Очистити полотно",
|
"clearReset": "Очистити полотно",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "Не вдалося розшифрувати дані.",
|
"decryptFailed": "Не вдалося розшифрувати дані.",
|
||||||
"uploadedSecurly": "Це завантаження було захищене наскрізним шифруванням, а це означає що сервер Excalidraw та інші не зможуть прочитати вміст.",
|
"uploadedSecurly": "Це завантаження було захищене наскрізним шифруванням, а це означає що сервер Excalidraw та інші не зможуть прочитати вміст.",
|
||||||
"loadSceneOverridePrompt": "Завантаження зовнішнього креслення замінить ваш наявний контент. Продовжити?",
|
"loadSceneOverridePrompt": "Завантаження зовнішнього креслення замінить ваш наявний контент. Продовжити?",
|
||||||
|
"collabStopOverridePrompt": "",
|
||||||
"errorLoadingLibrary": "Помилка при завантаженні сторонньої бібліотеки.",
|
"errorLoadingLibrary": "Помилка при завантаженні сторонньої бібліотеки.",
|
||||||
"confirmAddLibrary": "Це призведе до додавання {{numShapes}} фігур до вашої бібліотеки. Ви впевнені?",
|
"confirmAddLibrary": "Це призведе до додавання {{numShapes}} фігур до вашої бібліотеки. Ви впевнені?",
|
||||||
"imageDoesNotContainScene": "Імпортування зображень на даний момент не підтримується.\n\nЧи хочете ви імпортувати сцену? Це зображення не містить ніяких даних сцен. Ви увімкнули це під час експорту?",
|
"imageDoesNotContainScene": "Імпортування зображень на даний момент не підтримується.\n\nЧи хочете ви імпортувати сцену? Це зображення не містить ніяких даних сцен. Ви увімкнули це під час експорту?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "Сховище",
|
"storage": "Сховище",
|
||||||
"title": "Статистика",
|
"title": "Статистика",
|
||||||
"total": "Всього",
|
"total": "Всього",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "Ширина"
|
"width": "Ширина"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "Скопійовані стилі.",
|
"copyStyles": "Скопійовані стилі.",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "Скопійовано в буфер обміну як PNG."
|
"copyToClipboardAsPng": "Скопійовано в буфер обміну як PNG."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"gridMode": "网格模式",
|
"gridMode": "网格模式",
|
||||||
"addToLibrary": "添加到库中",
|
"addToLibrary": "添加到库中",
|
||||||
"removeFromLibrary": "从库中移除",
|
"removeFromLibrary": "从库中移除",
|
||||||
"libraryLoadingMessage": "正在加载库...",
|
"libraryLoadingMessage": "正在加载库…",
|
||||||
"libraries": "浏览库",
|
"libraries": "浏览库",
|
||||||
"loadingScene": "正在加载绘图...",
|
"loadingScene": "正在加载绘图…",
|
||||||
"align": "对齐",
|
"align": "对齐",
|
||||||
"alignTop": "顶部对齐",
|
"alignTop": "顶部对齐",
|
||||||
"alignBottom": "底端对齐",
|
"alignBottom": "底端对齐",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "垂直居中",
|
"centerVertically": "垂直居中",
|
||||||
"centerHorizontally": "水平居中",
|
"centerHorizontally": "水平居中",
|
||||||
"distributeHorizontally": "水平等距分布",
|
"distributeHorizontally": "水平等距分布",
|
||||||
"distributeVertically": "垂直等距分布"
|
"distributeVertically": "垂直等距分布",
|
||||||
|
"viewMode": "查看模式"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "重置画布",
|
"clearReset": "重置画布",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "无法解密数据。",
|
"decryptFailed": "无法解密数据。",
|
||||||
"uploadedSecurly": "上传已被端到端加密保护,这意味着 Excalidraw 的服务器和第三方都无法读取内容。",
|
"uploadedSecurly": "上传已被端到端加密保护,这意味着 Excalidraw 的服务器和第三方都无法读取内容。",
|
||||||
"loadSceneOverridePrompt": "加载外部绘图将取代您现有的内容。您想要继续吗?",
|
"loadSceneOverridePrompt": "加载外部绘图将取代您现有的内容。您想要继续吗?",
|
||||||
|
"collabStopOverridePrompt": "停止会话将覆盖您先前本地存储的绘图。 您确定吗?\n\n(如果您想保持本地绘图,只需关闭浏览器选项卡。)",
|
||||||
"errorLoadingLibrary": "加载第三方库时出错。",
|
"errorLoadingLibrary": "加载第三方库时出错。",
|
||||||
"confirmAddLibrary": "这将添加 {{numShapes}} 个形状到您的库。您确定吗?",
|
"confirmAddLibrary": "这将添加 {{numShapes}} 个形状到您的库。您确定吗?",
|
||||||
"imageDoesNotContainScene": "当前不支持导入图片。\n\n您想要导入画布数据吗?此图像似乎不包含任何画布数据。您是否在导出过程中启用了嵌入画布的选项?",
|
"imageDoesNotContainScene": "当前不支持导入图片。\n\n您想要导入画布数据吗?此图像似乎不包含任何画布数据。您是否在导出过程中启用了嵌入画布的选项?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "存储",
|
"storage": "存储",
|
||||||
"title": "详细统计信息",
|
"title": "详细统计信息",
|
||||||
"total": "总计",
|
"total": "总计",
|
||||||
|
"version": "",
|
||||||
|
"versionCopy": "",
|
||||||
|
"versionNotAvailable": "",
|
||||||
"width": "宽度"
|
"width": "宽度"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "复制样式",
|
"copyStyles": "复制样式",
|
||||||
|
"copyToClipboard": "",
|
||||||
"copyToClipboardAsPng": "复制为 PNG 到剪贴板"
|
"copyToClipboardAsPng": "复制为 PNG 到剪贴板"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "垂直置中",
|
"centerVertically": "垂直置中",
|
||||||
"centerHorizontally": "水平置中",
|
"centerHorizontally": "水平置中",
|
||||||
"distributeHorizontally": "水平分布",
|
"distributeHorizontally": "水平分布",
|
||||||
"distributeVertically": "垂直分布"
|
"distributeVertically": "垂直分布",
|
||||||
|
"viewMode": "檢視模式"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "重置 canvas",
|
"clearReset": "重置 canvas",
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
"decryptFailed": "無法解密資料。",
|
"decryptFailed": "無法解密資料。",
|
||||||
"uploadedSecurly": "上傳已通過 end-to-end 加密,Excalidraw 伺服器和第三方無法皆讀取其內容。",
|
"uploadedSecurly": "上傳已通過 end-to-end 加密,Excalidraw 伺服器和第三方無法皆讀取其內容。",
|
||||||
"loadSceneOverridePrompt": "讀取外部圖樣將取代目前的內容。是否要繼續?",
|
"loadSceneOverridePrompt": "讀取外部圖樣將取代目前的內容。是否要繼續?",
|
||||||
|
"collabStopOverridePrompt": "停止連線將覆蓋您先前於本機儲存的繪圖進度,是否確認?\n\n(如要保留原有的本機繪圖進度,直接關閉瀏覽器分頁即可。)",
|
||||||
"errorLoadingLibrary": "載入第三方套件時出現錯誤。",
|
"errorLoadingLibrary": "載入第三方套件時出現錯誤。",
|
||||||
"confirmAddLibrary": "這將會將 {{numShapes}} 個圖形加入你的資料庫,你確定嗎?",
|
"confirmAddLibrary": "這將會將 {{numShapes}} 個圖形加入你的資料庫,你確定嗎?",
|
||||||
"imageDoesNotContainScene": "目前尚不支援載入圖片。\n您是否要載入場景?此圖片中並無任何場景資料,輸出時是否有選擇包含?",
|
"imageDoesNotContainScene": "目前尚不支援載入圖片。\n您是否要載入場景?此圖片中並無任何場景資料,輸出時是否有選擇包含?",
|
||||||
@@ -233,10 +235,14 @@
|
|||||||
"storage": "儲存",
|
"storage": "儲存",
|
||||||
"title": "詳細統計",
|
"title": "詳細統計",
|
||||||
"total": "合計",
|
"total": "合計",
|
||||||
|
"version": "版本",
|
||||||
|
"versionCopy": "點擊複製",
|
||||||
|
"versionNotAvailable": "無法取得版本",
|
||||||
"width": "寬度"
|
"width": "寬度"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"copyStyles": "已複製樣式",
|
"copyStyles": "已複製樣式",
|
||||||
|
"copyToClipboard": "複製至剪貼簿。",
|
||||||
"copyToClipboardAsPng": "已複製 PNG 至剪貼簿"
|
"copyToClipboardAsPng": "已複製 PNG 至剪貼簿"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,22 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
## Excalidraw API
|
## 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).
|
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
||||||
|
|
||||||
## Excalidraw Library
|
## Excalidraw Library
|
||||||
|
|
||||||
### Features
|
### 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)
|
- 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).
|
- Support `Ctrl-Y` shortcut to redo on Windows [#2831](https://github.com/excalidraw/excalidraw/pull/2831).
|
||||||
|
|
||||||
### Fixes
|
### 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).
|
- Fix remote pointers not accounting for offset [#2855](https://github.com/excalidraw/excalidraw/pull/2855).
|
||||||
|
|
||||||
## 0.2.1
|
## 0.2.1
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export default function App() {
|
|||||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
||||||
| [`langCode`](#langCode) | string | `en` | Language code string |
|
| [`langCode`](#langCode) | string | `en` | Language code string |
|
||||||
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
| [`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`
|
### `Extra API's`
|
||||||
|
|
||||||
@@ -330,3 +331,7 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
|
|||||||
#### `renderFooter`
|
#### `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).
|
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,6 +26,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderFooter,
|
renderFooter,
|
||||||
langCode = defaultLang.code,
|
langCode = defaultLang.code,
|
||||||
|
viewModeEnabled,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -64,6 +65,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderFooter={renderFooter}
|
renderFooter={renderFooter}
|
||||||
langCode={langCode}
|
langCode={langCode}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
</IsMobileProvider>
|
</IsMobileProvider>
|
||||||
</InitializeApp>
|
</InitializeApp>
|
||||||
@@ -81,7 +83,6 @@ const areEqual = (
|
|||||||
|
|
||||||
const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[];
|
const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[];
|
||||||
const nextKeys = Object.keys(nextProps) as (keyof typeof next)[];
|
const nextKeys = Object.keys(nextProps) as (keyof typeof next)[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
prevUser?.name === nextUser?.name &&
|
prevUser?.name === nextUser?.name &&
|
||||||
prevKeys.length === nextKeys.length &&
|
prevKeys.length === nextKeys.length &&
|
||||||
@@ -89,6 +90,10 @@ const areEqual = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Excalidraw.defaultProps = {
|
||||||
|
lanCode: defaultLang.code,
|
||||||
|
};
|
||||||
|
|
||||||
const forwardedRefComp = forwardRef<
|
const forwardedRefComp = forwardRef<
|
||||||
ExcalidrawAPIRefValue,
|
ExcalidrawAPIRefValue,
|
||||||
PublicExcalidrawProps
|
PublicExcalidrawProps
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const exportToCanvas = ({
|
|||||||
canvas.width = ret.width;
|
canvas.width = ret.width;
|
||||||
canvas.height = ret.height;
|
canvas.height = ret.height;
|
||||||
|
|
||||||
return canvas;
|
return { canvas, scale: ret.scale };
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+23
-11
@@ -48,6 +48,7 @@ import {
|
|||||||
TransformHandleType,
|
TransformHandleType,
|
||||||
} from "../element/transformHandles";
|
} from "../element/transformHandles";
|
||||||
import { viewportCoordsToSceneCoords } from "../utils";
|
import { viewportCoordsToSceneCoords } from "../utils";
|
||||||
|
import { UserIdleState } from "../excalidraw-app/collab/types";
|
||||||
|
|
||||||
const strokeRectWithRotation = (
|
const strokeRectWithRotation = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
@@ -373,12 +374,14 @@ export const renderScene = (
|
|||||||
sceneState.zoom,
|
sceneState.zoom,
|
||||||
"mouse", // when we render we don't know which pointer type so use mouse
|
"mouse", // when we render we don't know which pointer type so use mouse
|
||||||
);
|
);
|
||||||
renderTransformHandles(
|
if (!appState.viewModeEnabled) {
|
||||||
context,
|
renderTransformHandles(
|
||||||
sceneState,
|
context,
|
||||||
transformHandles,
|
sceneState,
|
||||||
locallySelectedElements[0].angle,
|
transformHandles,
|
||||||
);
|
locallySelectedElements[0].angle,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (locallySelectedElements.length > 1 && !appState.isRotating) {
|
} else if (locallySelectedElements.length > 1 && !appState.isRotating) {
|
||||||
const dashedLinePadding = 4 / sceneState.zoom.value;
|
const dashedLinePadding = 4 / sceneState.zoom.value;
|
||||||
context.fillStyle = oc.white;
|
context.fillStyle = oc.white;
|
||||||
@@ -443,7 +446,9 @@ export const renderScene = (
|
|||||||
const globalAlpha = context.globalAlpha;
|
const globalAlpha = context.globalAlpha;
|
||||||
context.strokeStyle = stroke;
|
context.strokeStyle = stroke;
|
||||||
context.fillStyle = background;
|
context.fillStyle = background;
|
||||||
if (isOutOfBounds) {
|
|
||||||
|
const userState = sceneState.remotePointerUserStates[clientId];
|
||||||
|
if (isOutOfBounds || userState === UserIdleState.AWAY) {
|
||||||
context.globalAlpha = 0.2;
|
context.globalAlpha = 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,19 +481,25 @@ export const renderScene = (
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
const username = sceneState.remotePointerUsernames[clientId];
|
const username = sceneState.remotePointerUsernames[clientId];
|
||||||
|
const usernameAndIdleState = `${username ? `${username} ` : ""}${
|
||||||
|
userState === UserIdleState.AWAY
|
||||||
|
? "⚫️"
|
||||||
|
: userState === UserIdleState.IDLE
|
||||||
|
? "💤"
|
||||||
|
: "🟢"
|
||||||
|
}`;
|
||||||
|
|
||||||
if (!isOutOfBounds && username) {
|
if (!isOutOfBounds && usernameAndIdleState) {
|
||||||
const offsetX = x + width;
|
const offsetX = x + width;
|
||||||
const offsetY = y + height;
|
const offsetY = y + height;
|
||||||
const paddingHorizontal = 4;
|
const paddingHorizontal = 4;
|
||||||
const paddingVertical = 4;
|
const paddingVertical = 4;
|
||||||
const measure = context.measureText(username);
|
const measure = context.measureText(usernameAndIdleState);
|
||||||
const measureHeight =
|
const measureHeight =
|
||||||
measure.actualBoundingBoxDescent + measure.actualBoundingBoxAscent;
|
measure.actualBoundingBoxDescent + measure.actualBoundingBoxAscent;
|
||||||
|
|
||||||
// Border
|
// Border
|
||||||
context.fillStyle = stroke;
|
context.fillStyle = stroke;
|
||||||
context.globalAlpha = globalAlpha;
|
|
||||||
context.fillRect(
|
context.fillRect(
|
||||||
offsetX - 1,
|
offsetX - 1,
|
||||||
offsetY - 1,
|
offsetY - 1,
|
||||||
@@ -504,8 +515,9 @@ export const renderScene = (
|
|||||||
measureHeight + 2 * paddingVertical,
|
measureHeight + 2 * paddingVertical,
|
||||||
);
|
);
|
||||||
context.fillStyle = oc.white;
|
context.fillStyle = oc.white;
|
||||||
|
|
||||||
context.fillText(
|
context.fillText(
|
||||||
username,
|
usernameAndIdleState,
|
||||||
offsetX + paddingHorizontal,
|
offsetX + paddingHorizontal,
|
||||||
offsetY + paddingVertical + measure.actualBoundingBoxAscent,
|
offsetY + paddingVertical + measure.actualBoundingBoxAscent,
|
||||||
);
|
);
|
||||||
|
|||||||
+11
-7
@@ -29,14 +29,14 @@ export const exportToCanvas = (
|
|||||||
viewBackgroundColor: string;
|
viewBackgroundColor: string;
|
||||||
shouldAddWatermark: boolean;
|
shouldAddWatermark: boolean;
|
||||||
},
|
},
|
||||||
createCanvas: (width: number, height: number) => HTMLCanvasElement = (
|
createCanvas: (
|
||||||
width,
|
width: number,
|
||||||
height,
|
height: number,
|
||||||
) => {
|
) => { canvas: HTMLCanvasElement; scale: number } = (width, height) => {
|
||||||
const tempCanvas = document.createElement("canvas");
|
const tempCanvas = document.createElement("canvas");
|
||||||
tempCanvas.width = width * scale;
|
tempCanvas.width = width * scale;
|
||||||
tempCanvas.height = height * scale;
|
tempCanvas.height = height * scale;
|
||||||
return tempCanvas;
|
return { canvas: tempCanvas, scale };
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
|
const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
|
||||||
@@ -47,13 +47,16 @@ export const exportToCanvas = (
|
|||||||
shouldAddWatermark,
|
shouldAddWatermark,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tempCanvas = createCanvas(width, height);
|
const { canvas: tempCanvas, scale: newScale = scale } = createCanvas(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
);
|
||||||
|
|
||||||
renderScene(
|
renderScene(
|
||||||
sceneElements,
|
sceneElements,
|
||||||
appState,
|
appState,
|
||||||
null,
|
null,
|
||||||
scale,
|
newScale,
|
||||||
rough.canvas(tempCanvas),
|
rough.canvas(tempCanvas),
|
||||||
tempCanvas,
|
tempCanvas,
|
||||||
{
|
{
|
||||||
@@ -65,6 +68,7 @@ export const exportToCanvas = (
|
|||||||
remoteSelectedElementIds: {},
|
remoteSelectedElementIds: {},
|
||||||
shouldCacheIgnoreZoom: false,
|
shouldCacheIgnoreZoom: false,
|
||||||
remotePointerUsernames: {},
|
remotePointerUsernames: {},
|
||||||
|
remotePointerUserStates: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
renderScrollbars: false,
|
renderScrollbars: false,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export type SceneState = {
|
|||||||
remotePointerButton?: { [id: string]: string | undefined };
|
remotePointerButton?: { [id: string]: string | undefined };
|
||||||
remoteSelectedElementIds: { [elementId: string]: string[] };
|
remoteSelectedElementIds: { [elementId: string]: string[] };
|
||||||
remotePointerUsernames: { [id: string]: string };
|
remotePointerUsernames: { [id: string]: string };
|
||||||
|
remotePointerUserStates: { [id: string]: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SceneScroll = {
|
export type SceneScroll = {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -542,6 +543,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -990,6 +992,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -1766,6 +1769,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -1973,6 +1977,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -2424,6 +2429,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -2672,6 +2678,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -2837,6 +2844,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -3309,6 +3317,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -3618,6 +3627,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -3822,6 +3832,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -4062,6 +4073,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -4313,6 +4325,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -4714,6 +4727,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -4985,6 +4999,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -5310,6 +5325,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -5494,6 +5510,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -5656,6 +5673,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -6114,6 +6132,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -6423,6 +6442,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -8460,6 +8480,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -8821,6 +8842,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -9072,6 +9094,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -9324,6 +9347,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -9632,6 +9656,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -9794,6 +9819,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -9956,6 +9982,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -10118,6 +10145,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -10310,6 +10338,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -10502,6 +10531,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -10694,6 +10724,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -10886,6 +10917,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -11048,6 +11080,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -11210,6 +11243,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -11402,6 +11436,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -11564,6 +11599,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -11767,6 +11803,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -12474,6 +12511,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -12721,6 +12759,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -12819,6 +12858,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -12919,6 +12959,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -13081,6 +13122,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -13387,6 +13429,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -13693,6 +13736,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": "Copied styles.",
|
"toastMessage": "Copied styles.",
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -13853,6 +13897,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -14049,6 +14094,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -14302,6 +14348,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -14618,6 +14665,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": "Copied styles.",
|
"toastMessage": "Copied styles.",
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -15455,6 +15503,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -15761,6 +15810,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -16071,6 +16121,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -16447,6 +16498,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -16618,6 +16670,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -16931,6 +16984,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -17171,6 +17225,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -17426,6 +17481,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -17741,6 +17797,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -17841,6 +17898,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -18014,6 +18072,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -18820,6 +18879,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -18922,6 +18982,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -19698,6 +19759,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -20099,6 +20161,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -20346,6 +20409,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -20446,6 +20510,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -20940,6 +21005,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@@ -21038,6 +21104,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
|
|||||||
@@ -623,6 +623,7 @@ describe("regression tests", () => {
|
|||||||
"selectAll",
|
"selectAll",
|
||||||
"gridMode",
|
"gridMode",
|
||||||
"zenMode",
|
"zenMode",
|
||||||
|
"viewMode",
|
||||||
"stats",
|
"stats",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { ExcalidrawImperativeAPI } from "./components/App";
|
|||||||
import type { ResolvablePromise } from "./utils";
|
import type { ResolvablePromise } from "./utils";
|
||||||
import { Spreadsheet } from "./charts";
|
import { Spreadsheet } from "./charts";
|
||||||
import { Language } from "./i18n";
|
import { Language } from "./i18n";
|
||||||
|
import { UserIdleState } from "./excalidraw-app/collab/types";
|
||||||
|
|
||||||
export type Point = Readonly<RoughPoint>;
|
export type Point = Readonly<RoughPoint>;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ export type Collaborator = {
|
|||||||
button?: "up" | "down";
|
button?: "up" | "down";
|
||||||
selectedElementIds?: AppState["selectedElementIds"];
|
selectedElementIds?: AppState["selectedElementIds"];
|
||||||
username?: string | null;
|
username?: string | null;
|
||||||
|
userState?: UserIdleState;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppState = {
|
export type AppState = {
|
||||||
@@ -85,6 +87,7 @@ export type AppState = {
|
|||||||
zenModeEnabled: boolean;
|
zenModeEnabled: boolean;
|
||||||
appearance: "light" | "dark";
|
appearance: "light" | "dark";
|
||||||
gridSize: number | null;
|
gridSize: number | null;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
|
|
||||||
/** top-most selected groups (i.e. does not include nested groups) */
|
/** top-most selected groups (i.e. does not include nested groups) */
|
||||||
selectedGroupIds: { [groupId: string]: boolean };
|
selectedGroupIds: { [groupId: string]: boolean };
|
||||||
@@ -181,6 +184,7 @@ export interface ExcalidrawProps {
|
|||||||
) => void;
|
) => void;
|
||||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
renderFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
langCode?: Language["code"];
|
langCode?: Language["code"];
|
||||||
|
viewModeEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SceneData = {
|
export type SceneData = {
|
||||||
|
|||||||
@@ -21,12 +21,5 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"redirects": [
|
|
||||||
{
|
|
||||||
"source": "/([^.]+)",
|
|
||||||
"destination": "/",
|
|
||||||
"statusCode": 301
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user