mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-08-15 14:29:46 +02:00
Compare commits
1 Commits
v0.10.1
...
cycle_sele
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0e3a5b2042 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,11 +5,9 @@
|
||||
.env.test.local
|
||||
.envrc
|
||||
.eslintcache
|
||||
.history
|
||||
.idea
|
||||
.vercel
|
||||
.vscode
|
||||
.yarn
|
||||
*.log
|
||||
*.tgz
|
||||
build
|
||||
|
@@ -10,7 +10,7 @@ ARG NODE_ENV=production
|
||||
COPY . .
|
||||
RUN yarn build:app:docker
|
||||
|
||||
FROM nginx:1.21-alpine
|
||||
FROM nginx:1.17-alpine
|
||||
|
||||
COPY --from=build /opt/node_app/build /usr/share/nginx/html
|
||||
|
||||
|
@@ -70,8 +70,6 @@ The first set of digits is the room. This is visible from the server that’s go
|
||||
|
||||
The second set of digits is the encryption key. The Excalidraw server doesn’t know about it. This is what all the participants use to encrypt/decrypt the messages.
|
||||
|
||||
> Note: Please ensure that the encryption key is 22 characters long.
|
||||
|
||||
## Shape libraries
|
||||
|
||||
Find a growing list of libraries containing assets for your drawings at [libraries.excalidraw.com](https://libraries.excalidraw.com).
|
||||
@@ -95,7 +93,7 @@ These instructions will get you a copy of the project up and running on your loc
|
||||
#### Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org/en/)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install) (v1 or v2.4.2+)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
#### Clone the repo
|
||||
|
@@ -2,8 +2,5 @@
|
||||
"firestore": {
|
||||
"rules": "firestore.rules",
|
||||
"indexes": "firestore.indexes.json"
|
||||
},
|
||||
"storage": {
|
||||
"rules": "storage.rules"
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
rules_version = '2';
|
||||
service firebase.storage {
|
||||
match /b/{bucket}/o {
|
||||
match /{migrations} {
|
||||
match /{scenes}/{scene} {
|
||||
allow get, write: if true;
|
||||
// redundant, but let's be explicit'
|
||||
allow list: if false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,16 +19,15 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@dwelle/browser-fs-access": "0.21.1",
|
||||
"@sentry/browser": "6.2.5",
|
||||
"@sentry/integrations": "6.2.5",
|
||||
"@testing-library/jest-dom": "5.11.10",
|
||||
"@testing-library/react": "11.2.6",
|
||||
"@tldraw/vec": "0.0.106",
|
||||
"@types/jest": "26.0.22",
|
||||
"@types/react": "17.0.3",
|
||||
"@types/react-dom": "17.0.3",
|
||||
"@types/socket.io-client": "1.4.36",
|
||||
"browser-fs-access": "0.16.4",
|
||||
"clsx": "1.1.1",
|
||||
"firebase": "8.3.3",
|
||||
"i18next-browser-languagedetector": "6.1.0",
|
||||
@@ -36,7 +35,7 @@
|
||||
"nanoid": "3.1.22",
|
||||
"open-color": "1.8.0",
|
||||
"pako": "1.0.11",
|
||||
"perfect-freehand": "1.0.15",
|
||||
"perfect-freehand": "0.4.7",
|
||||
"png-chunk-text": "1.0.0",
|
||||
"png-chunks-encode": "1.0.0",
|
||||
"png-chunks-extract": "1.0.0",
|
||||
@@ -77,7 +76,7 @@
|
||||
},
|
||||
"jest": {
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!(roughjs|points-on-curve|path-data-parser|points-on-path|@dwelle/browser-fs-access)/)"
|
||||
"node_modules/(?!(roughjs|points-on-curve|path-data-parser|points-on-path|browser-fs-access)/)"
|
||||
],
|
||||
"resetMocks": false
|
||||
},
|
||||
|
Binary file not shown.
@@ -13,18 +13,6 @@
|
||||
|
||||
<meta name="theme-color" content="#000" />
|
||||
|
||||
<!-- Declarative Link Capturing (https://web.dev/declarative-link-capturing/) -->
|
||||
<meta
|
||||
http-equiv="origin-trial"
|
||||
content="Ak3VyzTheARtX2CnxBZ3ZKxImB0mNyvDakmMxeAChgxrWFMZ3IGN64VP+uj36VxM5OegsbLmrP258b1xvqp7+Q8AAABbeyJvcmlnaW4iOiJodHRwczovL2V4Y2FsaWRyYXcuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJBcHBMaW5rQ2FwdHVyaW5nIiwiZXhwaXJ5IjoxNjM0MDgzMTk5fQ=="
|
||||
/>
|
||||
|
||||
<!-- File Handling (https://web.dev/file-handling/) -->
|
||||
<meta
|
||||
http-equiv="origin-trial"
|
||||
content="AkMQsAnFmKfRfPKQHNCv2WmZREqgwkqhyt2M7aOwQiCStB+hPYnGnv+mNbkPDAsGXrwsj/waFi76wPzTDUaEeQ0AAABUeyJvcmlnaW4iOiJodHRwczovL2V4Y2FsaWRyYXcuY29tOjQ0MyIsImZlYXR1cmUiOiJGaWxlSGFuZGxpbmciLCJleHBpcnkiOjE2MzQwODMxOTl9"
|
||||
/>
|
||||
|
||||
<!-- General tags -->
|
||||
<meta
|
||||
name="description"
|
||||
|
@@ -26,7 +26,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"capture_links": "new-client",
|
||||
"capture_links": "new_client",
|
||||
"share_target": {
|
||||
"action": "/web-share-target",
|
||||
"method": "POST",
|
||||
|
@@ -38,8 +38,7 @@ const crowdinMap = {
|
||||
"zh-CN": "en-zhcn",
|
||||
"zh-TW": "en-zhtw",
|
||||
"lv-LV": "en-lv",
|
||||
"cs-CZ": "en-cs",
|
||||
"kk-KZ": "en-kk",
|
||||
"cs-CZ": "cs-cz",
|
||||
};
|
||||
|
||||
const flags = {
|
||||
@@ -79,7 +78,6 @@ const flags = {
|
||||
"zh-TW": "🇹🇼",
|
||||
"lv-LV": "🇱🇻",
|
||||
"cs-CZ": "🇨🇿",
|
||||
"kk-KZ": "🇰🇿",
|
||||
};
|
||||
|
||||
const languages = {
|
||||
@@ -119,7 +117,6 @@ const languages = {
|
||||
"zh-TW": "繁體中文",
|
||||
"lv-LV": "Latviešu",
|
||||
"cs-CZ": "Česky",
|
||||
"kk-KZ": "Қазақ тілі",
|
||||
};
|
||||
|
||||
const percentages = fs.readFileSync(
|
||||
|
@@ -1,39 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
const updateReadme = require("./updateReadme");
|
||||
const updateChangelog = require("./updateChangelog");
|
||||
|
||||
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
|
||||
const excalidrawPackage = `${excalidrawDir}/package.json`;
|
||||
|
||||
const updatePackageVersion = (nextVersion) => {
|
||||
const pkg = require(excalidrawPackage);
|
||||
pkg.version = nextVersion;
|
||||
const content = `${JSON.stringify(pkg, null, 2)}\n`;
|
||||
fs.writeFileSync(excalidrawPackage, content, "utf-8");
|
||||
};
|
||||
|
||||
const release = async (nextVersion) => {
|
||||
try {
|
||||
updateReadme();
|
||||
await updateChangelog(nextVersion);
|
||||
updatePackageVersion(nextVersion);
|
||||
await exec(`git add -u`);
|
||||
await exec(
|
||||
`git commit -m "docs: release @excalidraw/excalidraw@${nextVersion} 🎉"`,
|
||||
);
|
||||
/* eslint-disable no-console */
|
||||
console.log("Done!");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const nextVersion = process.argv.slice(2)[0];
|
||||
if (!nextVersion) {
|
||||
console.error("Pass the next version to release!");
|
||||
process.exit(1);
|
||||
}
|
||||
release(nextVersion);
|
@@ -1,97 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
|
||||
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
|
||||
const excalidrawPackage = `${excalidrawDir}/package.json`;
|
||||
const pkg = require(excalidrawPackage);
|
||||
const lastVersion = pkg.version;
|
||||
const existingChangeLog = fs.readFileSync(
|
||||
`${excalidrawDir}/CHANGELOG.md`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const supportedTypes = ["feat", "fix", "style", "refactor", "perf", "build"];
|
||||
const headerForType = {
|
||||
feat: "Features",
|
||||
fix: "Fixes",
|
||||
style: "Styles",
|
||||
refactor: " Refactor",
|
||||
perf: "Performance",
|
||||
build: "Build",
|
||||
};
|
||||
|
||||
const getCommitHashForLastVersion = async () => {
|
||||
try {
|
||||
const commitMessage = `"release @excalidraw/excalidraw@${lastVersion}"`;
|
||||
const { stdout } = await exec(
|
||||
`git log --format=format:"%H" --grep=${commitMessage}`,
|
||||
);
|
||||
return stdout;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const getLibraryCommitsSinceLastRelease = async () => {
|
||||
const commitHash = await getCommitHashForLastVersion();
|
||||
const { stdout } = await exec(
|
||||
`git log --pretty=format:%s ${commitHash}...master`,
|
||||
);
|
||||
const commitsSinceLastRelease = stdout.split("\n");
|
||||
const commitList = {};
|
||||
supportedTypes.forEach((type) => {
|
||||
commitList[type] = [];
|
||||
});
|
||||
|
||||
commitsSinceLastRelease.forEach((commit) => {
|
||||
const indexOfColon = commit.indexOf(":");
|
||||
const type = commit.slice(0, indexOfColon);
|
||||
if (!supportedTypes.includes(type)) {
|
||||
return;
|
||||
}
|
||||
const messageWithoutType = commit.slice(indexOfColon + 1).trim();
|
||||
const messageWithCapitalizeFirst =
|
||||
messageWithoutType.charAt(0).toUpperCase() + messageWithoutType.slice(1);
|
||||
const prNumber = commit.match(/\(#([0-9]*)\)/)[1];
|
||||
|
||||
// return if the changelog already contains the pr number which would happen for package updates
|
||||
if (existingChangeLog.includes(prNumber)) {
|
||||
return;
|
||||
}
|
||||
const prMarkdown = `[#${prNumber}](https://github.com/excalidraw/excalidraw/pull/${prNumber})`;
|
||||
const messageWithPRLink = messageWithCapitalizeFirst.replace(
|
||||
/\(#[0-9]*\)/,
|
||||
prMarkdown,
|
||||
);
|
||||
commitList[type].push(messageWithPRLink);
|
||||
});
|
||||
return commitList;
|
||||
};
|
||||
|
||||
const updateChangelog = async (nextVersion) => {
|
||||
const commitList = await getLibraryCommitsSinceLastRelease();
|
||||
let changelogForLibrary =
|
||||
"## Excalidraw Library\n\n**_This section lists the updates made to the excalidraw library and will not affect the integration._**\n\n";
|
||||
supportedTypes.forEach((type) => {
|
||||
if (commitList[type].length) {
|
||||
changelogForLibrary += `### ${headerForType[type]}\n\n`;
|
||||
const commits = commitList[type];
|
||||
commits.forEach((commit) => {
|
||||
changelogForLibrary += `- ${commit}\n\n`;
|
||||
});
|
||||
}
|
||||
});
|
||||
changelogForLibrary += "---\n";
|
||||
const lastVersionIndex = existingChangeLog.indexOf(`## ${lastVersion}`);
|
||||
let updatedContent =
|
||||
existingChangeLog.slice(0, lastVersionIndex) +
|
||||
changelogForLibrary +
|
||||
existingChangeLog.slice(lastVersionIndex);
|
||||
const currentDate = new Date().toISOString().slice(0, 10);
|
||||
const newVersion = `## ${nextVersion} (${currentDate})`;
|
||||
updatedContent = updatedContent.replace(`## Unreleased`, newVersion);
|
||||
fs.writeFileSync(`${excalidrawDir}/CHANGELOG.md`, updatedContent, "utf8");
|
||||
};
|
||||
|
||||
module.exports = updateChangelog;
|
@@ -1,27 +0,0 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const updateReadme = () => {
|
||||
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
|
||||
let data = fs.readFileSync(`${excalidrawDir}/README_NEXT.md`, "utf8");
|
||||
|
||||
// remove note for unstable release
|
||||
data = data.replace(
|
||||
/<!-- unstable-readme-start-->[\s\S]*?<!-- unstable-readme-end-->/,
|
||||
"",
|
||||
);
|
||||
|
||||
// replace "excalidraw-next" with "excalidraw"
|
||||
data = data.replace(/excalidraw-next/g, "excalidraw");
|
||||
data = data.trim();
|
||||
|
||||
const demoIndex = data.indexOf("### Demo");
|
||||
const excalidrawNextNote =
|
||||
"#### Note\n\n**If you don't want to wait for the next stable release and try out the unreleased changes you can use [@excalidraw/excalidraw-next](https://www.npmjs.com/package/@excalidraw/excalidraw-next).**\n\n";
|
||||
// Add excalidraw next note to try out for unreleased changes
|
||||
data = data.slice(0, demoIndex) + excalidrawNextNote + data.slice(demoIndex);
|
||||
|
||||
// update readme
|
||||
fs.writeFileSync(`${excalidrawDir}/README.md`, data, "utf8");
|
||||
};
|
||||
|
||||
module.exports = updateReadme;
|
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
import { ColorPicker } from "../components/ColorPicker";
|
||||
import { trash, zoomIn, zoomOut } from "../components/icons";
|
||||
import { resetZoom, trash, zoomIn, zoomOut } from "../components/icons";
|
||||
import { ToolButton } from "../components/ToolButton";
|
||||
import { DarkModeToggle } from "../components/DarkModeToggle";
|
||||
import { ZOOM_STEP } from "../constants";
|
||||
@@ -17,7 +17,6 @@ import { getNewZoom } from "../scene/zoom";
|
||||
import { AppState, NormalizedZoomValue } from "../types";
|
||||
import { getShortcutKey } from "../utils";
|
||||
import { register } from "./register";
|
||||
import { Tooltip } from "../components/Tooltip";
|
||||
|
||||
export const actionChangeViewBackgroundColor = register({
|
||||
name: "changeViewBackgroundColor",
|
||||
@@ -35,9 +34,9 @@ export const actionChangeViewBackgroundColor = register({
|
||||
type="canvasBackground"
|
||||
color={appState.viewBackgroundColor}
|
||||
onChange={(color) => updateData({ viewBackgroundColor: color })}
|
||||
isActive={appState.openPopup === "canvasColorPicker"}
|
||||
isActive={appState.openMenu === "canvasColorPicker"}
|
||||
setActive={(active) =>
|
||||
updateData({ openPopup: active ? "canvasColorPicker" : null })
|
||||
updateData({ openMenu: active ? "canvasColorPicker" : null })
|
||||
}
|
||||
data-testid="canvas-background-picker"
|
||||
/>
|
||||
@@ -109,7 +108,6 @@ export const actionZoomIn = register({
|
||||
onClick={() => {
|
||||
updateData(null);
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
),
|
||||
keyTest: (event) =>
|
||||
@@ -144,7 +142,6 @@ export const actionZoomOut = register({
|
||||
onClick={() => {
|
||||
updateData(null);
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
),
|
||||
keyTest: (event) =>
|
||||
@@ -171,21 +168,16 @@ export const actionResetZoom = register({
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ updateData, appState }) => (
|
||||
<Tooltip label={t("buttons.resetZoom")}>
|
||||
<ToolButton
|
||||
type="button"
|
||||
className="reset-zoom-button"
|
||||
title={t("buttons.resetZoom")}
|
||||
aria-label={t("buttons.resetZoom")}
|
||||
onClick={() => {
|
||||
updateData(null);
|
||||
}}
|
||||
size="small"
|
||||
>
|
||||
{(appState.zoom.value * 100).toFixed(0)}%
|
||||
</ToolButton>
|
||||
</Tooltip>
|
||||
PanelComponent: ({ updateData }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={resetZoom}
|
||||
title={t("buttons.resetZoom")}
|
||||
aria-label={t("buttons.resetZoom")}
|
||||
onClick={() => {
|
||||
updateData(null);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
keyTest: (event) =>
|
||||
(event.code === CODES.ZERO || event.code === CODES.NUM_ZERO) &&
|
||||
|
@@ -1,25 +1,18 @@
|
||||
import React from "react";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { load, questionCircle, saveAs } from "../components/icons";
|
||||
import { load, questionCircle, save, saveAs } from "../components/icons";
|
||||
import { ProjectName } from "../components/ProjectName";
|
||||
import { ToolButton } from "../components/ToolButton";
|
||||
import "../components/ToolIcon.scss";
|
||||
import { Tooltip } from "../components/Tooltip";
|
||||
import { DarkModeToggle, Appearence } from "../components/DarkModeToggle";
|
||||
import { loadFromJSON, saveAsJSON } from "../data";
|
||||
import { resaveAsImageWithScene } from "../data/resave";
|
||||
import { t } from "../i18n";
|
||||
import { useIsMobile } from "../components/App";
|
||||
import { KEYS } from "../keys";
|
||||
import { register } from "./register";
|
||||
import { supported as fsSupported } from "browser-fs-access";
|
||||
import { CheckboxItem } from "../components/CheckboxItem";
|
||||
import { getExportSize } from "../scene/export";
|
||||
import { DEFAULT_EXPORT_PADDING, EXPORT_SCALES } from "../constants";
|
||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||
import { getNonDeletedElements } from "../element";
|
||||
import { ActiveFile } from "../components/ActiveFile";
|
||||
import { isImageFileHandle } from "../data/blob";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
|
||||
export const actionChangeProjectName = register({
|
||||
name: "changeProjectName",
|
||||
@@ -39,54 +32,6 @@ export const actionChangeProjectName = register({
|
||||
),
|
||||
});
|
||||
|
||||
export const actionChangeExportScale = register({
|
||||
name: "changeExportScale",
|
||||
perform: (_elements, appState, value) => {
|
||||
return {
|
||||
appState: { ...appState, exportScale: value },
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements: allElements, appState, updateData }) => {
|
||||
const elements = getNonDeletedElements(allElements);
|
||||
const exportSelected = isSomeElementSelected(elements, appState);
|
||||
const exportedElements = exportSelected
|
||||
? getSelectedElements(elements, appState)
|
||||
: elements;
|
||||
|
||||
return (
|
||||
<>
|
||||
{EXPORT_SCALES.map((s) => {
|
||||
const [width, height] = getExportSize(
|
||||
exportedElements,
|
||||
DEFAULT_EXPORT_PADDING,
|
||||
s,
|
||||
);
|
||||
|
||||
const scaleButtonTitle = `${t(
|
||||
"buttons.scale",
|
||||
)} ${s}x (${width}x${height})`;
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
key={s}
|
||||
size="small"
|
||||
type="radio"
|
||||
icon={`${s}x`}
|
||||
name="export-canvas-scale"
|
||||
title={scaleButtonTitle}
|
||||
aria-label={scaleButtonTitle}
|
||||
id="export-canvas-scale"
|
||||
checked={s === appState.exportScale}
|
||||
onChange={() => updateData(s)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const actionChangeExportBackground = register({
|
||||
name: "changeExportBackground",
|
||||
perform: (_elements, appState, value) => {
|
||||
@@ -120,7 +65,7 @@ export const actionChangeExportEmbedScene = register({
|
||||
>
|
||||
{t("labels.exportEmbedScene")}
|
||||
<Tooltip label={t("labels.exportEmbedScene_details")} long={true}>
|
||||
<div className="excalidraw-tooltip-icon">{questionCircle}</div>
|
||||
<div className="Tooltip-icon">{questionCircle}</div>
|
||||
</Tooltip>
|
||||
</CheckboxItem>
|
||||
),
|
||||
@@ -130,19 +75,15 @@ export const actionSaveToActiveFile = register({
|
||||
name: "saveToActiveFile",
|
||||
perform: async (elements, appState, value) => {
|
||||
const fileHandleExists = !!appState.fileHandle;
|
||||
|
||||
try {
|
||||
const { fileHandle } = isImageFileHandle(appState.fileHandle)
|
||||
? await resaveAsImageWithScene(elements, appState)
|
||||
: await saveAsJSON(elements, appState);
|
||||
|
||||
const { fileHandle } = await saveAsJSON(elements, appState);
|
||||
return {
|
||||
commitToHistory: false,
|
||||
appState: {
|
||||
...appState,
|
||||
fileHandle,
|
||||
toastMessage: fileHandleExists
|
||||
? fileHandle?.name
|
||||
? fileHandle.name
|
||||
? t("toast.fileSavedToFilename").replace(
|
||||
"{filename}",
|
||||
`"${fileHandle.name}"`,
|
||||
@@ -160,10 +101,14 @@ export const actionSaveToActiveFile = register({
|
||||
},
|
||||
keyTest: (event) =>
|
||||
event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
|
||||
PanelComponent: ({ updateData, appState }) => (
|
||||
<ActiveFile
|
||||
onSave={() => updateData(null)}
|
||||
fileName={appState.fileHandle?.name}
|
||||
PanelComponent: ({ updateData }) => (
|
||||
<ToolButton
|
||||
type="icon"
|
||||
icon={save}
|
||||
title={t("buttons.save")}
|
||||
aria-label={t("buttons.save")}
|
||||
onClick={() => updateData(null)}
|
||||
data-testid="save-button"
|
||||
/>
|
||||
),
|
||||
});
|
||||
@@ -193,7 +138,7 @@ export const actionSaveFileToDisk = register({
|
||||
title={t("buttons.saveAs")}
|
||||
aria-label={t("buttons.saveAs")}
|
||||
showAriaLabel={useIsMobile()}
|
||||
hidden={!nativeFileSystemSupported}
|
||||
hidden={!fsSupported}
|
||||
onClick={() => updateData(null)}
|
||||
data-testid="save-as-button"
|
||||
/>
|
||||
@@ -207,7 +152,7 @@ export const actionLoadScene = register({
|
||||
const {
|
||||
elements: loadedElements,
|
||||
appState: loadedAppState,
|
||||
} = await loadFromJSON(appState, elements);
|
||||
} = await loadFromJSON(appState);
|
||||
return {
|
||||
elements: loadedElements,
|
||||
appState: loadedAppState,
|
||||
|
@@ -69,13 +69,12 @@ export const createUndoAction: ActionCreator = (history) => ({
|
||||
event[KEYS.CTRL_OR_CMD] &&
|
||||
event.key.toLowerCase() === KEYS.Z &&
|
||||
!event.shiftKey,
|
||||
PanelComponent: ({ updateData, data }) => (
|
||||
PanelComponent: ({ updateData }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={undo}
|
||||
aria-label={t("buttons.undo")}
|
||||
onClick={updateData}
|
||||
size={data?.size || "medium"}
|
||||
/>
|
||||
),
|
||||
commitToHistory: () => false,
|
||||
@@ -90,13 +89,12 @@ export const createRedoAction: ActionCreator = (history) => ({
|
||||
event.shiftKey &&
|
||||
event.key.toLowerCase() === KEYS.Z) ||
|
||||
(isWindows && event.ctrlKey && !event.shiftKey && event.key === KEYS.Y),
|
||||
PanelComponent: ({ updateData, data }) => (
|
||||
PanelComponent: ({ updateData }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={redo}
|
||||
aria-label={t("buttons.redo")}
|
||||
onClick={updateData}
|
||||
size={data?.size || "medium"}
|
||||
/>
|
||||
),
|
||||
commitToHistory: () => false,
|
||||
|
@@ -30,8 +30,8 @@ export const actionGoToCollaborator = register({
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ appState, updateData, data }) => {
|
||||
const clientId: string | undefined = data?.id;
|
||||
PanelComponent: ({ appState, updateData, id }) => {
|
||||
const clientId = id;
|
||||
if (!clientId) {
|
||||
return null;
|
||||
}
|
||||
|
@@ -13,13 +13,6 @@ import {
|
||||
FillCrossHatchIcon,
|
||||
FillHachureIcon,
|
||||
FillSolidIcon,
|
||||
FontFamilyCodeIcon,
|
||||
FontFamilyHandDrawnIcon,
|
||||
FontFamilyNormalIcon,
|
||||
FontSizeExtraLargeIcon,
|
||||
FontSizeLargeIcon,
|
||||
FontSizeMediumIcon,
|
||||
FontSizeSmallIcon,
|
||||
SloppinessArchitectIcon,
|
||||
SloppinessArtistIcon,
|
||||
SloppinessCartoonistIcon,
|
||||
@@ -27,15 +20,18 @@ import {
|
||||
StrokeStyleDottedIcon,
|
||||
StrokeStyleSolidIcon,
|
||||
StrokeWidthIcon,
|
||||
TextAlignCenterIcon,
|
||||
FontSizeSmallIcon,
|
||||
FontSizeMediumIcon,
|
||||
FontSizeLargeIcon,
|
||||
FontSizeExtraLargeIcon,
|
||||
FontFamilyHandDrawnIcon,
|
||||
FontFamilyNormalIcon,
|
||||
FontFamilyCodeIcon,
|
||||
TextAlignLeftIcon,
|
||||
TextAlignCenterIcon,
|
||||
TextAlignRightIcon,
|
||||
} from "../components/icons";
|
||||
import {
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_FONT_SIZE,
|
||||
FONT_FAMILY,
|
||||
} from "../constants";
|
||||
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
|
||||
import {
|
||||
getNonDeletedElements,
|
||||
isTextElement,
|
||||
@@ -48,7 +44,7 @@ import {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawLinearElement,
|
||||
ExcalidrawTextElement,
|
||||
FontFamilyValues,
|
||||
FontFamily,
|
||||
TextAlign,
|
||||
} from "../element/types";
|
||||
import { getLanguage, t } from "../i18n";
|
||||
@@ -130,9 +126,9 @@ export const actionChangeStrokeColor = register({
|
||||
appState.currentItemStrokeColor,
|
||||
)}
|
||||
onChange={(color) => updateData({ currentItemStrokeColor: color })}
|
||||
isActive={appState.openPopup === "strokeColorPicker"}
|
||||
isActive={appState.openMenu === "strokeColorPicker"}
|
||||
setActive={(active) =>
|
||||
updateData({ openPopup: active ? "strokeColorPicker" : null })
|
||||
updateData({ openMenu: active ? "strokeColorPicker" : null })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
@@ -170,9 +166,9 @@ export const actionChangeBackgroundColor = register({
|
||||
appState.currentItemBackgroundColor,
|
||||
)}
|
||||
onChange={(color) => updateData({ currentItemBackgroundColor: color })}
|
||||
isActive={appState.openPopup === "backgroundColorPicker"}
|
||||
isActive={appState.openMenu === "backgroundColorPicker"}
|
||||
setActive={(active) =>
|
||||
updateData({ openPopup: active ? "backgroundColorPicker" : null })
|
||||
updateData({ openMenu: active ? "backgroundColorPicker" : null })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
@@ -503,23 +499,19 @@ export const actionChangeFontFamily = register({
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData }) => {
|
||||
const options: {
|
||||
value: FontFamilyValues;
|
||||
text: string;
|
||||
icon: JSX.Element;
|
||||
}[] = [
|
||||
const options: { value: FontFamily; text: string; icon: JSX.Element }[] = [
|
||||
{
|
||||
value: FONT_FAMILY.Virgil,
|
||||
value: 1,
|
||||
text: t("labels.handDrawn"),
|
||||
icon: <FontFamilyHandDrawnIcon theme={appState.theme} />,
|
||||
},
|
||||
{
|
||||
value: FONT_FAMILY.Helvetica,
|
||||
value: 2,
|
||||
text: t("labels.normal"),
|
||||
icon: <FontFamilyNormalIcon theme={appState.theme} />,
|
||||
},
|
||||
{
|
||||
value: FONT_FAMILY.Cascadia,
|
||||
value: 3,
|
||||
text: t("labels.code"),
|
||||
icon: <FontFamilyCodeIcon theme={appState.theme} />,
|
||||
},
|
||||
@@ -528,7 +520,7 @@ export const actionChangeFontFamily = register({
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>{t("labels.fontFamily")}</legend>
|
||||
<ButtonIconSelect<FontFamilyValues | false>
|
||||
<ButtonIconSelect<FontFamily | false>
|
||||
group="font-family"
|
||||
options={options}
|
||||
value={getFormValue(
|
||||
|
@@ -10,6 +10,7 @@ export const actionToggleViewMode = register({
|
||||
appState: {
|
||||
...appState,
|
||||
viewModeEnabled: !this.checked!(appState),
|
||||
selectedElementIds: {},
|
||||
},
|
||||
commitToHistory: false,
|
||||
};
|
||||
|
@@ -5,7 +5,6 @@ import {
|
||||
UpdaterFn,
|
||||
ActionName,
|
||||
ActionResult,
|
||||
PanelComponentProps,
|
||||
} from "./types";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppProps, AppState } from "../types";
|
||||
@@ -108,10 +107,11 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data additional data sent to the PanelComponent
|
||||
*/
|
||||
renderAction = (name: ActionName, data?: PanelComponentProps["data"]) => {
|
||||
// Id is an attribute that we can use to pass in data like keys.
|
||||
// This is needed for dynamically generated action components
|
||||
// like the user list. We can use this key to extract more
|
||||
// data from app state. This is an alternative to generic prop hell!
|
||||
renderAction = (name: ActionName, id?: string) => {
|
||||
const canvasActions = this.app.props.UIOptions.canvasActions;
|
||||
|
||||
if (
|
||||
@@ -139,8 +139,8 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
elements={this.getElementsIncludingDeleted()}
|
||||
appState={this.getAppState()}
|
||||
updateData={updateData}
|
||||
id={id}
|
||||
appProps={this.app.props}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ import React from "react";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState, ExcalidrawProps } from "../types";
|
||||
import Library from "../data/library";
|
||||
import { ToolButtonSize } from "../components/ToolButton";
|
||||
|
||||
/** if false, the action should be prevented */
|
||||
export type ActionResult =
|
||||
@@ -67,7 +66,6 @@ export type ActionName =
|
||||
| "changeProjectName"
|
||||
| "changeExportBackground"
|
||||
| "changeExportEmbedScene"
|
||||
| "changeExportScale"
|
||||
| "saveToActiveFile"
|
||||
| "saveFileToDisk"
|
||||
| "loadScene"
|
||||
@@ -103,17 +101,15 @@ export type ActionName =
|
||||
| "exportWithDarkMode"
|
||||
| "toggleTheme";
|
||||
|
||||
export type PanelComponentProps = {
|
||||
elements: readonly ExcalidrawElement[];
|
||||
appState: AppState;
|
||||
updateData: (formData?: any) => void;
|
||||
appProps: ExcalidrawProps;
|
||||
data?: Partial<{ id: string; size: ToolButtonSize }>;
|
||||
};
|
||||
|
||||
export interface Action {
|
||||
name: ActionName;
|
||||
PanelComponent?: React.FC<PanelComponentProps>;
|
||||
PanelComponent?: React.FC<{
|
||||
elements: readonly ExcalidrawElement[];
|
||||
appState: AppState;
|
||||
updateData: (formData?: any) => void;
|
||||
appProps: ExcalidrawProps;
|
||||
id?: string;
|
||||
}>;
|
||||
perform: ActionFn;
|
||||
keyPriority?: number;
|
||||
keyTest?: (
|
||||
|
@@ -3,16 +3,11 @@ import {
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_TEXT_ALIGN,
|
||||
EXPORT_SCALES,
|
||||
} from "./constants";
|
||||
import { t } from "./i18n";
|
||||
import { AppState, NormalizedZoomValue } from "./types";
|
||||
import { getDateTime } from "./utils";
|
||||
|
||||
const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
|
||||
? devicePixelRatio
|
||||
: 1;
|
||||
|
||||
export const getDefaultAppState = (): Omit<
|
||||
AppState,
|
||||
"offsetTop" | "offsetLeft" | "width" | "height"
|
||||
@@ -44,7 +39,6 @@ export const getDefaultAppState = (): Omit<
|
||||
elementType: "selection",
|
||||
errorMessage: null,
|
||||
exportBackground: true,
|
||||
exportScale: defaultExportScale,
|
||||
exportEmbedScene: false,
|
||||
exportWithDarkMode: false,
|
||||
fileHandle: null,
|
||||
@@ -58,7 +52,6 @@ export const getDefaultAppState = (): Omit<
|
||||
multiElement: null,
|
||||
name: `${t("labels.untitled")}-${getDateTime()}`,
|
||||
openMenu: null,
|
||||
openPopup: null,
|
||||
pasteDialog: { shown: false, data: null },
|
||||
previousSelectedElementIds: {},
|
||||
resizingElement: null,
|
||||
@@ -123,7 +116,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
errorMessage: { browser: false, export: false },
|
||||
exportBackground: { browser: true, export: false },
|
||||
exportEmbedScene: { browser: true, export: false },
|
||||
exportScale: { browser: true, export: false },
|
||||
exportWithDarkMode: { browser: true, export: false },
|
||||
fileHandle: { browser: false, export: false },
|
||||
gridSize: { browser: true, export: true },
|
||||
@@ -139,7 +131,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
offsetLeft: { browser: false, export: false },
|
||||
offsetTop: { browser: false, export: false },
|
||||
openMenu: { browser: true, export: false },
|
||||
openPopup: { browser: false, export: false },
|
||||
pasteDialog: { browser: false, export: false },
|
||||
previousSelectedElementIds: { browser: true, export: false },
|
||||
resizingElement: { browser: false, export: false },
|
||||
|
@@ -151,14 +151,23 @@ export const SelectedShapeActions = ({
|
||||
);
|
||||
};
|
||||
|
||||
const LIBRARY_ICON = (
|
||||
// fa-th-large
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path d="M296 32h192c13.255 0 24 10.745 24 24v160c0 13.255-10.745 24-24 24H296c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24zm-80 0H24C10.745 32 0 42.745 0 56v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24zM0 296v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm296 184h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H296c-13.255 0-24 10.745-24 24v160c0 13.255 10.745 24 24 24z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ShapesSwitcher = ({
|
||||
canvas,
|
||||
elementType,
|
||||
setAppState,
|
||||
isLibraryOpen,
|
||||
}: {
|
||||
canvas: HTMLCanvasElement | null;
|
||||
elementType: ExcalidrawElement["type"];
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
isLibraryOpen: boolean;
|
||||
}) => (
|
||||
<>
|
||||
{SHAPES.map(({ value, icon, key }, index) => {
|
||||
@@ -192,6 +201,19 @@ export const ShapesSwitcher = ({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ToolButton
|
||||
className="Shape ToolIcon_type_button__library"
|
||||
type="button"
|
||||
icon={LIBRARY_ICON}
|
||||
name="editor-library"
|
||||
keyBindingLabel="9"
|
||||
aria-keyshortcuts="9"
|
||||
title={`${capitalizeString(t("toolBar.library"))} — 9`}
|
||||
aria-label={capitalizeString(t("toolBar.library"))}
|
||||
onClick={() => {
|
||||
setAppState({ isLibraryOpen: !isLibraryOpen });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -204,9 +226,12 @@ export const ZoomActions = ({
|
||||
}) => (
|
||||
<Stack.Col gap={1}>
|
||||
<Stack.Row gap={1} align="center">
|
||||
{renderAction("zoomOut")}
|
||||
{renderAction("zoomIn")}
|
||||
{renderAction("zoomOut")}
|
||||
{renderAction("resetZoom")}
|
||||
<div style={{ marginInlineStart: 4 }}>
|
||||
{(zoom.value * 100).toFixed(0)}%
|
||||
</div>
|
||||
</Stack.Row>
|
||||
</Stack.Col>
|
||||
);
|
||||
|
@@ -1,21 +0,0 @@
|
||||
.excalidraw {
|
||||
.ActiveFile {
|
||||
.ActiveFile__fileName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 9.3em;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1.15em;
|
||||
margin-inline-end: 0.3em;
|
||||
transform: scaleY(0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
import React from "react";
|
||||
import Stack from "../components/Stack";
|
||||
import { ToolButton } from "../components/ToolButton";
|
||||
import { save, file } from "../components/icons";
|
||||
import { t } from "../i18n";
|
||||
|
||||
import "./ActiveFile.scss";
|
||||
|
||||
type ActiveFileProps = {
|
||||
fileName?: string;
|
||||
onSave: () => void;
|
||||
};
|
||||
|
||||
export const ActiveFile = ({ fileName, onSave }: ActiveFileProps) => (
|
||||
<Stack.Row className="ActiveFile" gap={1} align="center">
|
||||
<span className="ActiveFile__fileName">
|
||||
{file}
|
||||
<span>{fileName}</span>
|
||||
</span>
|
||||
<ToolButton
|
||||
type="icon"
|
||||
icon={save}
|
||||
title={t("buttons.save")}
|
||||
aria-label={t("buttons.save")}
|
||||
onClick={onSave}
|
||||
data-testid="save-button"
|
||||
/>
|
||||
</Stack.Row>
|
||||
);
|
@@ -2,6 +2,7 @@ import React, { useContext } from "react";
|
||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
import rough from "roughjs/bin/rough";
|
||||
import clsx from "clsx";
|
||||
import { supported as fsSupported } from "browser-fs-access";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import {
|
||||
@@ -110,6 +111,7 @@ import {
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import { mutateElement } from "../element/mutateElement";
|
||||
import { deepCopyElement, newFreeDrawElement } from "../element/newElement";
|
||||
import { MaybeTransformHandleType } from "../element/transformHandles";
|
||||
import {
|
||||
isBindingElement,
|
||||
isBindingElementType,
|
||||
@@ -155,7 +157,6 @@ import {
|
||||
getElementsWithinSelection,
|
||||
getNormalizedZoom,
|
||||
getSelectedElements,
|
||||
hasBackground,
|
||||
isOverScrollBars,
|
||||
isSomeElementSelected,
|
||||
} from "../scene";
|
||||
@@ -166,11 +167,9 @@ import { findShapeByKey } from "../shapes";
|
||||
import {
|
||||
AppProps,
|
||||
AppState,
|
||||
ExcalidrawImperativeAPI,
|
||||
Gesture,
|
||||
GestureEvent,
|
||||
LibraryItems,
|
||||
PointerDownState,
|
||||
SceneData,
|
||||
} from "../types";
|
||||
import {
|
||||
@@ -181,6 +180,7 @@ import {
|
||||
isToolIcon,
|
||||
isWritableElement,
|
||||
resetCursor,
|
||||
ResolvablePromise,
|
||||
resolvablePromise,
|
||||
sceneCoordsToViewportCoords,
|
||||
setCursor,
|
||||
@@ -194,14 +194,12 @@ import LayerUI from "./LayerUI";
|
||||
import { Stats } from "./Stats";
|
||||
import { Toast } from "./Toast";
|
||||
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
|
||||
const IsMobileContext = React.createContext(false);
|
||||
export const useIsMobile = () => useContext(IsMobileContext);
|
||||
const ExcalidrawContainerContext = React.createContext<{
|
||||
container: HTMLDivElement | null;
|
||||
id: string | null;
|
||||
}>({ container: null, id: null });
|
||||
const ExcalidrawContainerContext = React.createContext<HTMLDivElement | null>(
|
||||
null,
|
||||
);
|
||||
export const useExcalidrawContainer = () =>
|
||||
useContext(ExcalidrawContainerContext);
|
||||
|
||||
@@ -224,6 +222,83 @@ const gesture: Gesture = {
|
||||
initialScale: null,
|
||||
};
|
||||
|
||||
export type PointerDownState = Readonly<{
|
||||
// The first position at which pointerDown happened
|
||||
origin: Readonly<{ x: number; y: number }>;
|
||||
// Same as "origin" but snapped to the grid, if grid is on
|
||||
originInGrid: Readonly<{ x: number; y: number }>;
|
||||
// Scrollbar checks
|
||||
scrollbars: ReturnType<typeof isOverScrollBars>;
|
||||
// The previous pointer position
|
||||
lastCoords: { x: number; y: number };
|
||||
// map of original elements data
|
||||
originalElements: Map<string, NonDeleted<ExcalidrawElement>>;
|
||||
resize: {
|
||||
// Handle when resizing, might change during the pointer interaction
|
||||
handleType: MaybeTransformHandleType;
|
||||
// This is determined on the initial pointer down event
|
||||
isResizing: boolean;
|
||||
// This is determined on the initial pointer down event
|
||||
offset: { x: number; y: number };
|
||||
// This is determined on the initial pointer down event
|
||||
arrowDirection: "origin" | "end";
|
||||
// This is a center point of selected elements determined on the initial pointer down event (for rotation only)
|
||||
center: { x: number; y: number };
|
||||
};
|
||||
hit: {
|
||||
// The element the pointer is "hitting", is determined on the initial
|
||||
// pointer down event
|
||||
element: NonDeleted<ExcalidrawElement> | null;
|
||||
// The elements the pointer is "hitting", is determined on the initial
|
||||
// pointer down event
|
||||
allHitElements: NonDeleted<ExcalidrawElement>[];
|
||||
// This is determined on the initial pointer down event
|
||||
wasAddedToSelection: boolean;
|
||||
// Whether selected element(s) were duplicated, might change during the
|
||||
// pointer interaction
|
||||
hasBeenDuplicated: boolean;
|
||||
hasHitCommonBoundingBoxOfSelectedElements: boolean;
|
||||
};
|
||||
withCmdOrCtrl: boolean;
|
||||
drag: {
|
||||
// Might change during the pointer interation
|
||||
hasOccurred: boolean;
|
||||
// Might change during the pointer interation
|
||||
offset: { x: number; y: number } | null;
|
||||
};
|
||||
// We need to have these in the state so that we can unsubscribe them
|
||||
eventListeners: {
|
||||
// It's defined on the initial pointer down event
|
||||
onMove: null | ((event: PointerEvent) => void);
|
||||
// It's defined on the initial pointer down event
|
||||
onUp: null | ((event: PointerEvent) => void);
|
||||
// It's defined on the initial pointer down event
|
||||
onKeyDown: null | ((event: KeyboardEvent) => void);
|
||||
// It's defined on the initial pointer down event
|
||||
onKeyUp: null | ((event: KeyboardEvent) => void);
|
||||
};
|
||||
}>;
|
||||
|
||||
export type ExcalidrawImperativeAPI = {
|
||||
updateScene: InstanceType<typeof App>["updateScene"];
|
||||
resetScene: InstanceType<typeof App>["resetScene"];
|
||||
getSceneElementsIncludingDeleted: InstanceType<
|
||||
typeof App
|
||||
>["getSceneElementsIncludingDeleted"];
|
||||
history: {
|
||||
clear: InstanceType<typeof App>["resetHistory"];
|
||||
};
|
||||
scrollToContent: InstanceType<typeof App>["scrollToContent"];
|
||||
getSceneElements: InstanceType<typeof App>["getSceneElements"];
|
||||
getAppState: () => InstanceType<typeof App>["state"];
|
||||
refresh: InstanceType<typeof App>["refresh"];
|
||||
importLibrary: InstanceType<typeof App>["importLibraryFromUrl"];
|
||||
setToastMessage: InstanceType<typeof App>["setToastMessage"];
|
||||
readyPromise: ResolvablePromise<ExcalidrawImperativeAPI>;
|
||||
ready: true;
|
||||
id: string;
|
||||
};
|
||||
|
||||
class App extends React.Component<AppProps, AppState> {
|
||||
canvas: HTMLCanvasElement | null = null;
|
||||
rc: RoughCanvas | null = null;
|
||||
@@ -246,10 +321,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
public libraryItemsFromStorage: LibraryItems | undefined;
|
||||
private id: string;
|
||||
private history: History;
|
||||
private excalidrawContainerValue: {
|
||||
container: HTMLDivElement | null;
|
||||
id: string;
|
||||
};
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
@@ -306,12 +377,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
readyPromise.resolve(api);
|
||||
}
|
||||
|
||||
this.excalidrawContainerValue = {
|
||||
container: this.excalidrawContainerRef.current,
|
||||
id: this.id,
|
||||
};
|
||||
|
||||
this.scene = new Scene();
|
||||
this.library = new Library(this);
|
||||
this.history = new History();
|
||||
@@ -339,11 +404,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
if (viewModeEnabled) {
|
||||
return (
|
||||
<canvas
|
||||
className="excalidraw__canvas"
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
cursor: CURSOR_TYPE.GRAB,
|
||||
cursor: "grabbing",
|
||||
}}
|
||||
width={canvasWidth}
|
||||
height={canvasHeight}
|
||||
@@ -361,7 +426,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
return (
|
||||
<canvas
|
||||
className="excalidraw__canvas"
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasDOMWidth,
|
||||
height: canvasDOMHeight,
|
||||
@@ -406,7 +471,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
>
|
||||
<ExcalidrawContainerContext.Provider
|
||||
value={this.excalidrawContainerValue}
|
||||
value={this.excalidrawContainerRef.current}
|
||||
>
|
||||
<IsMobileContext.Provider value={this.isMobile}>
|
||||
<LayerUI
|
||||
@@ -469,9 +534,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
public focusContainer = () => {
|
||||
if (this.props.autoFocus) {
|
||||
this.excalidrawContainerRef.current?.focus();
|
||||
}
|
||||
this.excalidrawContainerRef.current?.focus();
|
||||
};
|
||||
|
||||
public getSceneElementsIncludingDeleted = () => {
|
||||
@@ -655,11 +718,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
const fileHandle = launchParams.files[0];
|
||||
const blob: Blob = await fileHandle.getFile();
|
||||
blob.handle = fileHandle;
|
||||
loadFromBlob(
|
||||
blob,
|
||||
this.state,
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
)
|
||||
loadFromBlob(blob, this.state)
|
||||
.then(({ elements, appState }) =>
|
||||
this.syncActionResult({
|
||||
elements,
|
||||
@@ -686,7 +745,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
if (initialData?.libraryItems) {
|
||||
this.libraryItemsFromStorage = initialData.libraryItems;
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
initialData = {
|
||||
appState: {
|
||||
@@ -697,7 +756,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
};
|
||||
}
|
||||
|
||||
const scene = restore(initialData, null, null);
|
||||
const scene = restore(initialData, null);
|
||||
scene.appState = {
|
||||
...scene.appState,
|
||||
isLoading: false,
|
||||
@@ -741,8 +800,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
};
|
||||
|
||||
public async componentDidMount() {
|
||||
this.excalidrawContainerValue.container = this.excalidrawContainerRef.current;
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV === ENV.TEST ||
|
||||
process.env.NODE_ENV === ENV.DEVELOPMENT
|
||||
@@ -831,7 +888,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
|
||||
private removeEventListeners() {
|
||||
document.removeEventListener(EVENT.POINTER_UP, this.removePointer);
|
||||
document.removeEventListener(EVENT.COPY, this.onCopy);
|
||||
document.removeEventListener(EVENT.PASTE, this.pasteFromClipboard);
|
||||
document.removeEventListener(EVENT.CUT, this.onCut);
|
||||
@@ -873,7 +929,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private addEventListeners() {
|
||||
this.removeEventListeners();
|
||||
document.addEventListener(EVENT.POINTER_UP, this.removePointer); // #3553
|
||||
document.addEventListener(EVENT.COPY, this.onCopy);
|
||||
if (this.props.handleKeyboardGlobally) {
|
||||
document.addEventListener(EVENT.KEYDOWN, this.onKeyDown, false);
|
||||
@@ -929,12 +984,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
if (prevProps.viewModeEnabled !== this.props.viewModeEnabled) {
|
||||
this.setState({ viewModeEnabled: !!this.props.viewModeEnabled });
|
||||
this.setState(
|
||||
{ viewModeEnabled: !!this.props.viewModeEnabled },
|
||||
this.addEventListeners,
|
||||
);
|
||||
}
|
||||
|
||||
if (prevState.viewModeEnabled !== this.state.viewModeEnabled) {
|
||||
this.addEventListeners();
|
||||
this.deselectElements();
|
||||
}
|
||||
|
||||
if (prevProps.zenModeEnabled !== this.props.zenModeEnabled) {
|
||||
@@ -1193,12 +1250,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
const data = await parseClipboard(event);
|
||||
if (this.props.onPaste) {
|
||||
try {
|
||||
if ((await this.props.onPaste(data, event)) === false) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (await this.props.onPaste(data, event)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data.errorMessage) {
|
||||
@@ -1212,7 +1265,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
} else if (data.elements) {
|
||||
this.addElementsFromPasteOrLibrary({
|
||||
elements: data.elements,
|
||||
elements: restoreElements(data.elements),
|
||||
position: "cursor",
|
||||
});
|
||||
} else if (data.text) {
|
||||
@@ -1227,7 +1280,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
elements: readonly ExcalidrawElement[];
|
||||
position: { clientX: number; clientY: number } | "cursor" | "center";
|
||||
}) => {
|
||||
const elements = restoreElements(opts.elements, null);
|
||||
const elements = restoreElements(opts.elements);
|
||||
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
|
||||
|
||||
const elementsCenterX = distance(minX, maxX) / 2;
|
||||
@@ -1293,7 +1346,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.scene.getElements(),
|
||||
),
|
||||
);
|
||||
this.selectShapeTool("selection");
|
||||
};
|
||||
|
||||
private addTextFromPaste(text: any) {
|
||||
@@ -1334,7 +1386,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.setState(obj);
|
||||
};
|
||||
|
||||
removePointer = (event: React.PointerEvent<HTMLElement> | PointerEvent) => {
|
||||
removePointer = (event: React.PointerEvent<HTMLElement>) => {
|
||||
// remove touch handler for context menu on touch devices
|
||||
if (event.pointerType === "touch" && touchTimeout) {
|
||||
clearTimeout(touchTimeout);
|
||||
@@ -1400,7 +1452,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
await webShareTargetCache.delete("shared-file");
|
||||
window.history.replaceState(null, APP_NAME, window.location.pathname);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
this.setState({ errorMessage: error.message });
|
||||
}
|
||||
};
|
||||
@@ -1597,22 +1649,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.scene.getElements(),
|
||||
this.state,
|
||||
);
|
||||
if (
|
||||
this.state.elementType === "selection" &&
|
||||
!selectedElements.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event.key === KEYS.G &&
|
||||
(hasBackground(this.state.elementType) ||
|
||||
selectedElements.some((element) => hasBackground(element.type)))
|
||||
) {
|
||||
this.setState({ openPopup: "backgroundColorPicker" });
|
||||
}
|
||||
if (event.key === KEYS.S) {
|
||||
this.setState({ openPopup: "strokeColorPicker" });
|
||||
if (selectedElements.length) {
|
||||
if (event.key === KEYS.G) {
|
||||
this.setState({ openMenu: "backgroundColorPicker" });
|
||||
}
|
||||
if (event.key === KEYS.S) {
|
||||
this.setState({ openMenu: "strokeColorPicker" });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1620,9 +1663,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private onKeyUp = withBatchedUpdates((event: KeyboardEvent) => {
|
||||
if (event.key === KEYS.SPACE) {
|
||||
if (this.state.viewModeEnabled) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.GRAB);
|
||||
} else if (this.state.elementType === "selection") {
|
||||
if (this.state.elementType === "selection") {
|
||||
resetCursor(this.canvas);
|
||||
} else {
|
||||
setCursorForShape(this.canvas, this.state.elementType);
|
||||
@@ -1770,8 +1811,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
[element.id]: true,
|
||||
},
|
||||
}));
|
||||
}
|
||||
if (isDeleted) {
|
||||
} else {
|
||||
fixBindingsAfterDeletion(this.scene.getElements(), [element]);
|
||||
}
|
||||
if (!isDeleted || isExistingElement) {
|
||||
@@ -1792,19 +1832,15 @@ class App extends React.Component<AppProps, AppState> {
|
||||
excalidrawContainer: this.excalidrawContainerRef.current,
|
||||
});
|
||||
// deselect all other elements when inserting text
|
||||
this.deselectElements();
|
||||
|
||||
// do an initial update to re-initialize element position since we were
|
||||
// modifying element's x/y for sake of editor (case: syncing to remote)
|
||||
updateElement(element.text);
|
||||
}
|
||||
|
||||
private deselectElements() {
|
||||
this.setState({
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
editingGroupId: null,
|
||||
});
|
||||
|
||||
// do an initial update to re-initialize element position since we were
|
||||
// modifying element's x/y for sake of editor (case: syncing to remote)
|
||||
updateElement(element.text);
|
||||
}
|
||||
|
||||
private getTextElementAtPosition(
|
||||
@@ -1826,6 +1862,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
/** if true, returns the first selected element (with highest z-index)
|
||||
of all hit elements */
|
||||
preferSelected?: boolean;
|
||||
cycleElementsUnderCursor?: boolean;
|
||||
},
|
||||
): NonDeleted<ExcalidrawElement> | null {
|
||||
const allHitElements = this.getElementsAtPosition(x, y);
|
||||
@@ -1836,6 +1873,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return allHitElements[index];
|
||||
}
|
||||
}
|
||||
} else if (opts?.cycleElementsUnderCursor) {
|
||||
const selectedIdx = allHitElements.findIndex(
|
||||
(element) => this.state.selectedElementIds[element.id],
|
||||
);
|
||||
return selectedIdx > 0
|
||||
? allHitElements[selectedIdx - 1]
|
||||
: allHitElements[allHitElements.length - 1];
|
||||
}
|
||||
const elementWithHighestZIndex =
|
||||
allHitElements[allHitElements.length - 1];
|
||||
@@ -2247,8 +2291,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.canvas,
|
||||
isTextElement(hitElement) ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR,
|
||||
);
|
||||
} else if (this.state.viewModeEnabled) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.GRAB);
|
||||
} else if (isOverScrollBar) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.AUTO);
|
||||
} else if (
|
||||
@@ -2274,6 +2316,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private handleCanvasPointerDown = (
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
event.persist();
|
||||
|
||||
// remove any active selection when we start to interact with canvas
|
||||
// (mainly, we care about removing selection outside the component which
|
||||
// would prevent our copy handling otherwise)
|
||||
@@ -2486,11 +2530,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
lastPointerUp = null;
|
||||
isPanning = false;
|
||||
if (!isHoldingSpace) {
|
||||
if (this.state.viewModeEnabled) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.GRAB);
|
||||
} else {
|
||||
setCursorForShape(this.canvas, this.state.elementType);
|
||||
}
|
||||
setCursorForShape(this.canvas, this.state.elementType);
|
||||
}
|
||||
this.setState({
|
||||
cursorButton: "up",
|
||||
@@ -2714,10 +2754,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
// hitElement may already be set above, so check first
|
||||
pointerDownState.hit.element =
|
||||
pointerDownState.hit.element ??
|
||||
(!event[KEYS.CTRL_OR_CMD] ? pointerDownState.hit.element : null) ??
|
||||
this.getElementAtPosition(
|
||||
pointerDownState.origin.x,
|
||||
pointerDownState.origin.y,
|
||||
{
|
||||
cycleElementsUnderCursor: event[KEYS.CTRL_OR_CMD],
|
||||
},
|
||||
);
|
||||
|
||||
// For overlapped elements one position may hit
|
||||
@@ -3494,7 +3537,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
mutateElement(draggingElement, {
|
||||
points: [...points, [dx, dy]],
|
||||
pressures,
|
||||
lastCommittedPoint: [dx, dy],
|
||||
});
|
||||
|
||||
this.actionManager.executeAction(actionFinalize);
|
||||
@@ -3549,7 +3591,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
elementType: "selection",
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[draggingElement.id]: true,
|
||||
[this.state.draggingElement!.id]: true,
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
@@ -3557,7 +3599,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
draggingElement: null,
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[draggingElement.id]: true,
|
||||
[this.state.draggingElement!.id]: true,
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -3834,22 +3876,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
try {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (file?.type === "image/png" || file?.type === "image/svg+xml") {
|
||||
if (nativeFileSystemSupported) {
|
||||
try {
|
||||
// This will only work as of Chrome 86,
|
||||
// but can be safely ignored on older releases.
|
||||
const item = event.dataTransfer.items[0];
|
||||
(file as any).handle = await (item as any).getAsFileSystemHandle();
|
||||
} catch (error: any) {
|
||||
console.warn(error.name, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
const { elements, appState } = await loadFromBlob(
|
||||
file,
|
||||
this.state,
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
);
|
||||
const { elements, appState } = await loadFromBlob(file, this.state);
|
||||
this.syncActionResult({
|
||||
elements,
|
||||
appState: {
|
||||
@@ -3860,7 +3887,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
return this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: error.message,
|
||||
@@ -3894,13 +3921,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
// default: assume an Excalidraw file regardless of extension/MimeType
|
||||
} else {
|
||||
this.setState({ isLoading: true });
|
||||
if (nativeFileSystemSupported) {
|
||||
if (fsSupported) {
|
||||
try {
|
||||
// This will only work as of Chrome 86,
|
||||
// but can be safely ignored on older releases.
|
||||
const item = event.dataTransfer.items[0];
|
||||
(file as any).handle = await (item as any).getAsFileSystemHandle();
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.warn(error.name, error.message);
|
||||
}
|
||||
}
|
||||
@@ -3909,7 +3936,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
};
|
||||
|
||||
loadFileToCanvas = (file: Blob) => {
|
||||
loadFromBlob(file, this.state, this.scene.getElementsIncludingDeleted())
|
||||
loadFromBlob(file, this.state)
|
||||
.then(({ elements, appState }) =>
|
||||
this.syncActionResult({
|
||||
elements,
|
||||
@@ -4093,112 +4120,116 @@ class App extends React.Component<AppProps, AppState> {
|
||||
actionToggleStats,
|
||||
];
|
||||
|
||||
ContextMenu.push({
|
||||
options: viewModeOptions,
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
ContextMenu.push({
|
||||
options: viewModeOptions,
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
} else {
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
this.isMobile &&
|
||||
navigator.clipboard && {
|
||||
name: "paste",
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
return {
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.paste",
|
||||
},
|
||||
this.isMobile && navigator.clipboard && separator,
|
||||
probablySupportsClipboardBlob &&
|
||||
elements.length > 0 &&
|
||||
actionCopyAsPng,
|
||||
probablySupportsClipboardWriteText &&
|
||||
elements.length > 0 &&
|
||||
actionCopyAsSvg,
|
||||
((probablySupportsClipboardBlob && elements.length > 0) ||
|
||||
(probablySupportsClipboardWriteText && elements.length > 0)) &&
|
||||
separator,
|
||||
actionSelectAll,
|
||||
separator,
|
||||
typeof this.props.gridModeEnabled === "undefined" &&
|
||||
actionToggleGridMode,
|
||||
typeof this.props.zenModeEnabled === "undefined" &&
|
||||
actionToggleZenMode,
|
||||
typeof this.props.viewModeEnabled === "undefined" &&
|
||||
actionToggleViewMode,
|
||||
actionToggleStats,
|
||||
],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (type === "element") {
|
||||
if (this.state.viewModeEnabled) {
|
||||
ContextMenu.push({
|
||||
options: [navigator.clipboard && actionCopy, ...options],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
} else {
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
this.isMobile && actionCut,
|
||||
this.isMobile && navigator.clipboard && actionCopy,
|
||||
this.isMobile &&
|
||||
navigator.clipboard && {
|
||||
name: "paste",
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
return {
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.paste",
|
||||
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
this.isMobile &&
|
||||
navigator.clipboard && {
|
||||
name: "paste",
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
return {
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
this.isMobile && separator,
|
||||
...options,
|
||||
contextItemLabel: "labels.paste",
|
||||
},
|
||||
this.isMobile && navigator.clipboard && separator,
|
||||
probablySupportsClipboardBlob &&
|
||||
elements.length > 0 &&
|
||||
actionCopyAsPng,
|
||||
probablySupportsClipboardWriteText &&
|
||||
elements.length > 0 &&
|
||||
actionCopyAsSvg,
|
||||
((probablySupportsClipboardBlob && elements.length > 0) ||
|
||||
(probablySupportsClipboardWriteText && elements.length > 0)) &&
|
||||
separator,
|
||||
actionCopyStyles,
|
||||
actionPasteStyles,
|
||||
separator,
|
||||
maybeGroupAction && actionGroup,
|
||||
maybeUngroupAction && actionUngroup,
|
||||
(maybeGroupAction || maybeUngroupAction) && separator,
|
||||
actionAddToLibrary,
|
||||
separator,
|
||||
actionSendBackward,
|
||||
actionBringForward,
|
||||
actionSendToBack,
|
||||
actionBringToFront,
|
||||
separator,
|
||||
maybeFlipHorizontal && actionFlipHorizontal,
|
||||
maybeFlipVertical && actionFlipVertical,
|
||||
(maybeFlipHorizontal || maybeFlipVertical) && separator,
|
||||
actionDuplicateSelection,
|
||||
actionDeleteSelected,
|
||||
],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
}
|
||||
actionSelectAll,
|
||||
separator,
|
||||
typeof this.props.gridModeEnabled === "undefined" &&
|
||||
actionToggleGridMode,
|
||||
typeof this.props.zenModeEnabled === "undefined" &&
|
||||
actionToggleZenMode,
|
||||
typeof this.props.viewModeEnabled === "undefined" &&
|
||||
actionToggleViewMode,
|
||||
actionToggleStats,
|
||||
],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
ContextMenu.push({
|
||||
options: [navigator.clipboard && actionCopy, ...options],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ContextMenu.push({
|
||||
options: [
|
||||
this.isMobile && actionCut,
|
||||
this.isMobile && navigator.clipboard && actionCopy,
|
||||
this.isMobile &&
|
||||
navigator.clipboard && {
|
||||
name: "paste",
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
return {
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.paste",
|
||||
},
|
||||
this.isMobile && separator,
|
||||
...options,
|
||||
separator,
|
||||
actionCopyStyles,
|
||||
actionPasteStyles,
|
||||
separator,
|
||||
maybeGroupAction && actionGroup,
|
||||
maybeUngroupAction && actionUngroup,
|
||||
(maybeGroupAction || maybeUngroupAction) && separator,
|
||||
actionAddToLibrary,
|
||||
separator,
|
||||
actionSendBackward,
|
||||
actionBringForward,
|
||||
actionSendToBack,
|
||||
actionBringToFront,
|
||||
separator,
|
||||
maybeFlipHorizontal && actionFlipHorizontal,
|
||||
maybeFlipVertical && actionFlipVertical,
|
||||
(maybeFlipHorizontal || maybeFlipVertical) && separator,
|
||||
actionDuplicateSelection,
|
||||
actionDeleteSelected,
|
||||
],
|
||||
top,
|
||||
left,
|
||||
actionManager: this.actionManager,
|
||||
appState: this.state,
|
||||
container: this.excalidrawContainerRef.current!,
|
||||
});
|
||||
};
|
||||
|
||||
private handleWheel = withBatchedUpdates((event: WheelEvent) => {
|
||||
|
@@ -16,5 +16,10 @@ export const BackgroundPickerAndDarkModeToggle = ({
|
||||
<div style={{ display: "flex" }}>
|
||||
{actionManager.renderAction("changeViewBackgroundColor")}
|
||||
{showThemeBtn && actionManager.renderAction("toggleTheme")}
|
||||
{appState.fileHandle && (
|
||||
<div style={{ marginInlineStart: "0.25rem" }}>
|
||||
{actionManager.renderAction("saveToActiveFile")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@@ -81,7 +81,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.excalidraw-tooltip-icon {
|
||||
.Tooltip-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import { checkIcon } from "./icons";
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { Popover } from "./Popover";
|
||||
import { isTransparent } from "../utils";
|
||||
|
||||
import "./ColorPicker.scss";
|
||||
import { isArrowKey, KEYS } from "../keys";
|
||||
@@ -15,7 +14,7 @@ const isValidColor = (color: string) => {
|
||||
};
|
||||
|
||||
const getColor = (color: string): string | null => {
|
||||
if (isTransparent(color)) {
|
||||
if (color === "transparent") {
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -138,41 +137,36 @@ const Picker = ({
|
||||
}}
|
||||
tabIndex={0}
|
||||
>
|
||||
{colors.map((_color, i) => {
|
||||
const _colorWithoutHash = _color.replace("#", "");
|
||||
return (
|
||||
<button
|
||||
className="color-picker-swatch"
|
||||
onClick={(event) => {
|
||||
(event.currentTarget as HTMLButtonElement).focus();
|
||||
onChange(_color);
|
||||
}}
|
||||
title={`${t(`colors.${_colorWithoutHash}`)}${
|
||||
!isTransparent(_color) ? ` (${_color})` : ""
|
||||
} — ${keyBindings[i].toUpperCase()}`}
|
||||
aria-label={t(`colors.${_colorWithoutHash}`)}
|
||||
aria-keyshortcuts={keyBindings[i]}
|
||||
style={{ color: _color }}
|
||||
key={_color}
|
||||
ref={(el) => {
|
||||
if (el && i === 0) {
|
||||
firstItem.current = el;
|
||||
}
|
||||
if (el && _color === color) {
|
||||
activeItem.current = el;
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
onChange(_color);
|
||||
}}
|
||||
>
|
||||
{isTransparent(_color) ? (
|
||||
<div className="color-picker-transparent"></div>
|
||||
) : undefined}
|
||||
<span className="color-picker-keybinding">{keyBindings[i]}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{colors.map((_color, i) => (
|
||||
<button
|
||||
className="color-picker-swatch"
|
||||
onClick={(event) => {
|
||||
(event.currentTarget as HTMLButtonElement).focus();
|
||||
onChange(_color);
|
||||
}}
|
||||
title={`${_color} — ${keyBindings[i].toUpperCase()}`}
|
||||
aria-label={_color}
|
||||
aria-keyshortcuts={keyBindings[i]}
|
||||
style={{ color: _color }}
|
||||
key={_color}
|
||||
ref={(el) => {
|
||||
if (el && i === 0) {
|
||||
firstItem.current = el;
|
||||
}
|
||||
if (el && _color === color) {
|
||||
activeItem.current = el;
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
onChange(_color);
|
||||
}}
|
||||
>
|
||||
{_color === "transparent" ? (
|
||||
<div className="color-picker-transparent"></div>
|
||||
) : undefined}
|
||||
<span className="color-picker-keybinding">{keyBindings[i]}</span>
|
||||
</button>
|
||||
))}
|
||||
{showInput && (
|
||||
<ColorInput
|
||||
color={color}
|
||||
|
@@ -2,7 +2,7 @@ import clsx from "clsx";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useCallbackRefState } from "../hooks/useCallbackRefState";
|
||||
import { t } from "../i18n";
|
||||
import { useExcalidrawContainer, useIsMobile } from "../components/App";
|
||||
import { useIsMobile } from "../components/App";
|
||||
import { KEYS } from "../keys";
|
||||
import "./Dialog.scss";
|
||||
import { back, close } from "./icons";
|
||||
@@ -21,7 +21,6 @@ export const Dialog = (props: {
|
||||
}) => {
|
||||
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
|
||||
const [lastActiveElement] = useState(document.activeElement);
|
||||
const { id } = useExcalidrawContainer();
|
||||
|
||||
useEffect(() => {
|
||||
if (!islandNode) {
|
||||
@@ -83,7 +82,7 @@ export const Dialog = (props: {
|
||||
theme={props.theme}
|
||||
>
|
||||
<Island ref={setIslandNode}>
|
||||
<h2 id={`${id}-dialog-title`} className="Dialog__title">
|
||||
<h2 id="dialog-title" className="Dialog__title">
|
||||
<span className="Dialog__titleContent">{props.title}</span>
|
||||
<button
|
||||
className="Modal__close"
|
||||
|
@@ -12,7 +12,7 @@ export const ErrorDialog = ({
|
||||
onClose?: () => void;
|
||||
}) => {
|
||||
const [modalIsShown, setModalIsShown] = useState(!!message);
|
||||
const { container: excalidrawContainer } = useExcalidrawContainer();
|
||||
const excalidrawContainer = useExcalidrawContainer();
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
setModalIsShown(false);
|
||||
|
@@ -97,8 +97,7 @@
|
||||
|
||||
border-radius: 1rem;
|
||||
background-color: var(--button-color);
|
||||
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.28),
|
||||
0 6px 10px 0 rgba(0, 0, 0, 0.14);
|
||||
box-shadow: 0 3px 5px -1px rgb(0 0 0 / 28%), 0 6px 10px 0 rgb(0 0 0 / 14%);
|
||||
|
||||
font-family: Cascadia;
|
||||
font-size: 1.8em;
|
||||
|
@@ -157,13 +157,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
shortcuts={["Shift+P", "7"]}
|
||||
/>
|
||||
<Shortcut label={t("toolBar.text")} shortcuts={["T", "8"]} />
|
||||
<Shortcut
|
||||
label={t("helpDialog.editSelectedShape")}
|
||||
shortcuts={[
|
||||
getShortcutKey("Enter"),
|
||||
t("helpDialog.doubleClick"),
|
||||
]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("helpDialog.textNewLine")}
|
||||
shortcuts={[
|
||||
|
@@ -5,7 +5,7 @@ import { getSelectedElements } from "../scene";
|
||||
|
||||
import "./HintViewer.scss";
|
||||
import { AppState } from "../types";
|
||||
import { isLinearElement, isTextElement } from "../element/typeChecks";
|
||||
import { isLinearElement } from "../element/typeChecks";
|
||||
import { getShortcutKey } from "../utils";
|
||||
|
||||
interface Hint {
|
||||
@@ -57,14 +57,6 @@ const getHints = ({ appState, elements }: Hint) => {
|
||||
return t("hints.lineEditor_info");
|
||||
}
|
||||
|
||||
if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
|
||||
return t("hints.text_selected");
|
||||
}
|
||||
|
||||
if (appState.editingElement && isTextElement(appState.editingElement)) {
|
||||
return t("hints.text_editing");
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@@ -8,17 +8,20 @@ import { CanvasError } from "../errors";
|
||||
import { t } from "../i18n";
|
||||
import { useIsMobile } from "./App";
|
||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||
import { exportToCanvas } from "../scene/export";
|
||||
import { exportToCanvas, getExportSize } from "../scene/export";
|
||||
import { AppState } from "../types";
|
||||
import { Dialog } from "./Dialog";
|
||||
import { clipboard, exportImage } from "./icons";
|
||||
import Stack from "./Stack";
|
||||
import { ToolButton } from "./ToolButton";
|
||||
|
||||
import "./ExportDialog.scss";
|
||||
import { supported as fsSupported } from "browser-fs-access";
|
||||
import OpenColor from "open-color";
|
||||
import { CheckboxItem } from "./CheckboxItem";
|
||||
import { DEFAULT_EXPORT_PADDING } from "../constants";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
|
||||
const scales = [1, 2, 3];
|
||||
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
|
||||
|
||||
const supportsContextFilters =
|
||||
"filter" in document.createElement("canvas").getContext("2d")!;
|
||||
@@ -79,7 +82,7 @@ const ExportButton: React.FC<{
|
||||
const ImageExportModal = ({
|
||||
elements,
|
||||
appState,
|
||||
exportPadding = DEFAULT_EXPORT_PADDING,
|
||||
exportPadding = 10,
|
||||
actionManager,
|
||||
onExportToPng,
|
||||
onExportToSvg,
|
||||
@@ -95,6 +98,7 @@ const ImageExportModal = ({
|
||||
onCloseRequest: () => void;
|
||||
}) => {
|
||||
const someElementIsSelected = isSomeElementSelected(elements, appState);
|
||||
const [scale, setScale] = useState(defaultScale);
|
||||
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
|
||||
const previewRef = useRef<HTMLDivElement>(null);
|
||||
const { exportBackground, viewBackgroundColor } = appState;
|
||||
@@ -117,6 +121,7 @@ const ImageExportModal = ({
|
||||
exportBackground,
|
||||
viewBackgroundColor,
|
||||
exportPadding,
|
||||
scale,
|
||||
});
|
||||
|
||||
// if converting to blob fails, there's some problem that will
|
||||
@@ -139,6 +144,7 @@ const ImageExportModal = ({
|
||||
exportBackground,
|
||||
exportPadding,
|
||||
viewBackgroundColor,
|
||||
scale,
|
||||
]);
|
||||
|
||||
return (
|
||||
@@ -169,8 +175,33 @@ const ImageExportModal = ({
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", marginTop: ".6em" }}>
|
||||
<Stack.Row gap={2}>
|
||||
{actionManager.renderAction("changeExportScale")}
|
||||
<Stack.Row gap={2} justifyContent={"center"}>
|
||||
{scales.map((_scale) => {
|
||||
const [width, height] = getExportSize(
|
||||
exportedElements,
|
||||
exportPadding,
|
||||
_scale,
|
||||
);
|
||||
|
||||
const scaleButtonTitle = `${t(
|
||||
"buttons.scale",
|
||||
)} ${_scale}x (${width}x${height})`;
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
key={_scale}
|
||||
size="s"
|
||||
type="radio"
|
||||
icon={`${_scale}x`}
|
||||
name="export-canvas-scale"
|
||||
title={scaleButtonTitle}
|
||||
aria-label={scaleButtonTitle}
|
||||
id="export-canvas-scale"
|
||||
checked={_scale === scale}
|
||||
onChange={() => setScale(_scale)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack.Row>
|
||||
<p style={{ marginLeft: "1em", userSelect: "none" }}>Scale</p>
|
||||
</div>
|
||||
@@ -182,15 +213,14 @@ const ImageExportModal = ({
|
||||
margin: ".6em 0",
|
||||
}}
|
||||
>
|
||||
{!nativeFileSystemSupported &&
|
||||
actionManager.renderAction("changeProjectName")}
|
||||
{!fsSupported && actionManager.renderAction("changeProjectName")}
|
||||
</div>
|
||||
<Stack.Row gap={2} justifyContent="center" style={{ margin: "2em 0" }}>
|
||||
<ExportButton
|
||||
color="indigo"
|
||||
title={t("buttons.exportToPng")}
|
||||
aria-label={t("buttons.exportToPng")}
|
||||
onClick={() => onExportToPng(exportedElements)}
|
||||
onClick={() => onExportToPng(exportedElements, scale)}
|
||||
>
|
||||
PNG
|
||||
</ExportButton>
|
||||
@@ -198,14 +228,14 @@ const ImageExportModal = ({
|
||||
color="red"
|
||||
title={t("buttons.exportToSvg")}
|
||||
aria-label={t("buttons.exportToSvg")}
|
||||
onClick={() => onExportToSvg(exportedElements)}
|
||||
onClick={() => onExportToSvg(exportedElements, scale)}
|
||||
>
|
||||
SVG
|
||||
</ExportButton>
|
||||
{probablySupportsClipboardBlob && (
|
||||
<ExportButton
|
||||
title={t("buttons.copyPngToClipboard")}
|
||||
onClick={() => onExportToClipboard(exportedElements)}
|
||||
onClick={() => onExportToClipboard(exportedElements, scale)}
|
||||
color="gray"
|
||||
shade={7}
|
||||
>
|
||||
@@ -220,7 +250,7 @@ const ImageExportModal = ({
|
||||
export const ImageExportDialog = ({
|
||||
elements,
|
||||
appState,
|
||||
exportPadding = DEFAULT_EXPORT_PADDING,
|
||||
exportPadding = 10,
|
||||
actionManager,
|
||||
onExportToPng,
|
||||
onExportToSvg,
|
||||
|
@@ -11,7 +11,7 @@ import { actionSaveFileToDisk } from "../actions/actionExport";
|
||||
import { Card } from "./Card";
|
||||
|
||||
import "./ExportDialog.scss";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import { supported as fsSupported } from "browser-fs-access";
|
||||
|
||||
export type ExportCB = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
@@ -42,8 +42,7 @@ const JSONExportModal = ({
|
||||
<h2>{t("exportDialog.disk_title")}</h2>
|
||||
<div className="Card-details">
|
||||
{t("exportDialog.disk_details")}
|
||||
{!nativeFileSystemSupported &&
|
||||
actionManager.renderAction("changeProjectName")}
|
||||
{!fsSupported && actionManager.renderAction("changeProjectName")}
|
||||
</div>
|
||||
<ToolButton
|
||||
className="Card-button"
|
||||
|
@@ -73,10 +73,10 @@
|
||||
}
|
||||
|
||||
:root[dir="ltr"] &.layer-ui__wrapper__footer-left--transition-left {
|
||||
transform: translate(-76px, 0);
|
||||
transform: translate(-92px, 0);
|
||||
}
|
||||
:root[dir="rtl"] &.layer-ui__wrapper__footer-left--transition-left {
|
||||
transform: translate(76px, 0);
|
||||
transform: translate(92px, 0);
|
||||
}
|
||||
|
||||
&.layer-ui__wrapper__footer-left--transition-bottom {
|
||||
@@ -116,19 +116,8 @@
|
||||
}
|
||||
}
|
||||
.layer-ui__wrapper__footer-left,
|
||||
.layer-ui__wrapper__footer-right,
|
||||
.disable-zen-mode--visible {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.layer-ui__wrapper__footer-left {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.layer-ui__wrapper__footer-right {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-inline-end: 1em;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ import { Island } from "./Island";
|
||||
import "./LayerUI.scss";
|
||||
import { LibraryUnit } from "./LibraryUnit";
|
||||
import { LoadingMessage } from "./LoadingMessage";
|
||||
import { LockButton } from "./LockButton";
|
||||
import { LockIcon } from "./LockIcon";
|
||||
import { MobileMenu } from "./MobileMenu";
|
||||
import { PasteChartDialog } from "./PasteChartDialog";
|
||||
import { Section } from "./Section";
|
||||
@@ -47,8 +47,6 @@ import { Tooltip } from "./Tooltip";
|
||||
import { UserList } from "./UserList";
|
||||
import Library from "../data/library";
|
||||
import { JSONExportDialog } from "./JSONExportDialog";
|
||||
import { LibraryButton } from "./LibraryButton";
|
||||
import { isImageFileHandle } from "../data/blob";
|
||||
|
||||
interface LayerUIProps {
|
||||
actionManager: ActionManager;
|
||||
@@ -109,7 +107,6 @@ const LibraryMenuItems = ({
|
||||
onAddToLibrary,
|
||||
onInsertShape,
|
||||
pendingElements,
|
||||
theme,
|
||||
setAppState,
|
||||
setLibraryItems,
|
||||
libraryReturnUrl,
|
||||
@@ -122,7 +119,6 @@ const LibraryMenuItems = ({
|
||||
onRemoveFromLibrary: (index: number) => void;
|
||||
onInsertShape: (elements: LibraryItem) => void;
|
||||
onAddToLibrary: (elements: LibraryItem) => void;
|
||||
theme: AppState["theme"];
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
setLibraryItems: (library: LibraryItems) => void;
|
||||
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
||||
@@ -196,7 +192,7 @@ const LibraryMenuItems = ({
|
||||
<a
|
||||
href={`https://libraries.excalidraw.com?target=${
|
||||
window.name || "_blank"
|
||||
}&referrer=${referrer}&useHash=true&token=${id}&theme=${theme}`}
|
||||
}&referrer=${referrer}&useHash=true&token=${id}`}
|
||||
target="_excalidraw_libraries"
|
||||
>
|
||||
{t("labels.libraries")}
|
||||
@@ -250,7 +246,6 @@ const LibraryMenu = ({
|
||||
onInsertShape,
|
||||
pendingElements,
|
||||
onAddToLibrary,
|
||||
theme,
|
||||
setAppState,
|
||||
libraryReturnUrl,
|
||||
focusContainer,
|
||||
@@ -261,7 +256,6 @@ const LibraryMenu = ({
|
||||
onClickOutside: (event: MouseEvent) => void;
|
||||
onInsertShape: (elements: LibraryItem) => void;
|
||||
onAddToLibrary: () => void;
|
||||
theme: AppState["theme"];
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
||||
focusContainer: () => void;
|
||||
@@ -351,7 +345,6 @@ const LibraryMenu = ({
|
||||
libraryReturnUrl={libraryReturnUrl}
|
||||
focusContainer={focusContainer}
|
||||
library={library}
|
||||
theme={theme}
|
||||
id={id}
|
||||
/>
|
||||
)}
|
||||
@@ -407,25 +400,19 @@ const LayerUI = ({
|
||||
|
||||
const createExporter = (type: ExportType): ExportCB => async (
|
||||
exportedElements,
|
||||
scale,
|
||||
) => {
|
||||
const fileHandle = await exportCanvas(type, exportedElements, appState, {
|
||||
await exportCanvas(type, exportedElements, appState, {
|
||||
exportBackground: appState.exportBackground,
|
||||
name: appState.name,
|
||||
viewBackgroundColor: appState.viewBackgroundColor,
|
||||
scale,
|
||||
})
|
||||
.catch(muteFSAbortError)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setAppState({ errorMessage: error.message });
|
||||
});
|
||||
|
||||
if (
|
||||
appState.exportEmbedScene &&
|
||||
fileHandle &&
|
||||
isImageFileHandle(fileHandle)
|
||||
) {
|
||||
setAppState({ fileHandle });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -497,9 +484,6 @@ const LayerUI = ({
|
||||
setAppState={setAppState}
|
||||
showThemeBtn={showThemeBtn}
|
||||
/>
|
||||
{appState.fileHandle && (
|
||||
<>{actionManager.renderAction("saveToActiveFile")}</>
|
||||
)}
|
||||
</Stack.Col>
|
||||
</Island>
|
||||
</Section>
|
||||
@@ -518,8 +502,7 @@ const LayerUI = ({
|
||||
style={{
|
||||
// we want to make sure this doesn't overflow so substracting 200
|
||||
// which is approximately height of zoom footer and top left menu items with some buffer
|
||||
// if active file name is displayed, subtracting 248 to account for its height
|
||||
maxHeight: `${appState.height - (appState.fileHandle ? 248 : 200)}px`,
|
||||
maxHeight: `${appState.height - 200}px`,
|
||||
}}
|
||||
>
|
||||
<SelectedShapeActions
|
||||
@@ -556,7 +539,6 @@ const LayerUI = ({
|
||||
libraryReturnUrl={libraryReturnUrl}
|
||||
focusContainer={focusContainer}
|
||||
library={library}
|
||||
theme={appState.theme}
|
||||
id={id}
|
||||
/>
|
||||
) : null;
|
||||
@@ -584,12 +566,6 @@ const LayerUI = ({
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="start">
|
||||
<Stack.Row gap={1}>
|
||||
<LockButton
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
<Island
|
||||
padding={1}
|
||||
className={clsx({ "zen-mode": zenModeEnabled })}
|
||||
@@ -601,12 +577,15 @@ const LayerUI = ({
|
||||
canvas={canvas}
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LibraryButton
|
||||
appState={appState}
|
||||
setAppState={setAppState}
|
||||
<LockIcon
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
@@ -632,9 +611,7 @@ const LayerUI = ({
|
||||
label={client.username || "Unknown user"}
|
||||
key={clientId}
|
||||
>
|
||||
{actionManager.renderAction("goToCollaborator", {
|
||||
id: clientId,
|
||||
})}
|
||||
{actionManager.renderAction("goToCollaborator", clientId)}
|
||||
</Tooltip>
|
||||
))}
|
||||
</UserList>
|
||||
@@ -667,16 +644,6 @@ const LayerUI = ({
|
||||
zoom={appState.zoom}
|
||||
/>
|
||||
</Island>
|
||||
{!viewModeEnabled && (
|
||||
<div
|
||||
className={clsx("undo-redo-buttons zen-mode-transition", {
|
||||
"layer-ui__wrapper__footer-left--transition-bottom": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
{actionManager.renderAction("undo", { size: "small" })}
|
||||
{actionManager.renderAction("redo", { size: "small" })}
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
</Stack.Col>
|
||||
</div>
|
||||
|
@@ -1,46 +0,0 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import { t } from "../i18n";
|
||||
import { AppState } from "../types";
|
||||
import { capitalizeString } from "../utils";
|
||||
|
||||
const LIBRARY_ICON = (
|
||||
<svg viewBox="0 0 576 512">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M542.22 32.05c-54.8 3.11-163.72 14.43-230.96 55.59-4.64 2.84-7.27 7.89-7.27 13.17v363.87c0 11.55 12.63 18.85 23.28 13.49 69.18-34.82 169.23-44.32 218.7-46.92 16.89-.89 30.02-14.43 30.02-30.66V62.75c.01-17.71-15.35-31.74-33.77-30.7zM264.73 87.64C197.5 46.48 88.58 35.17 33.78 32.05 15.36 31.01 0 45.04 0 62.75V400.6c0 16.24 13.13 29.78 30.02 30.66 49.49 2.6 149.59 12.11 218.77 46.95 10.62 5.35 23.21-1.94 23.21-13.46V100.63c0-5.29-2.62-10.14-7.27-12.99z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const LibraryButton: React.FC<{
|
||||
appState: AppState;
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
}> = ({ appState, setAppState }) => {
|
||||
return (
|
||||
<label
|
||||
className={clsx(
|
||||
"ToolIcon ToolIcon_type_floating ToolIcon__library zen-mode-visibility",
|
||||
`ToolIcon_size_medium`,
|
||||
{
|
||||
"zen-mode-visibility--hidden": appState.zenModeEnabled,
|
||||
},
|
||||
)}
|
||||
title={`${capitalizeString(t("toolBar.library"))} — 9`}
|
||||
style={{ marginInlineStart: "var(--space-factor)" }}
|
||||
>
|
||||
<input
|
||||
className="ToolIcon_type_checkbox"
|
||||
type="checkbox"
|
||||
name="editor-library"
|
||||
onChange={(event) => {
|
||||
setAppState({ isLibraryOpen: event.target.checked });
|
||||
}}
|
||||
checked={appState.isLibraryOpen}
|
||||
aria-label={capitalizeString(t("toolBar.library"))}
|
||||
aria-keyshortcuts="9"
|
||||
/>
|
||||
<div className="ToolIcon__icon">{LIBRARY_ICON}</div>
|
||||
</label>
|
||||
);
|
||||
};
|
@@ -36,27 +36,21 @@ export const LibraryUnit = ({
|
||||
if (!elementsToRender) {
|
||||
return;
|
||||
}
|
||||
let svg: SVGSVGElement;
|
||||
const svg = exportToSvg(elementsToRender, {
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: oc.white,
|
||||
});
|
||||
for (const child of ref.current!.children) {
|
||||
if (child.tagName !== "svg") {
|
||||
continue;
|
||||
}
|
||||
ref.current!.removeChild(child);
|
||||
}
|
||||
ref.current!.appendChild(svg);
|
||||
|
||||
const current = ref.current!;
|
||||
|
||||
(async () => {
|
||||
svg = await exportToSvg(elementsToRender, {
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: oc.white,
|
||||
});
|
||||
for (const child of ref.current!.children) {
|
||||
if (child.tagName !== "svg") {
|
||||
continue;
|
||||
}
|
||||
current!.removeChild(child);
|
||||
}
|
||||
current!.appendChild(svg);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
if (svg) {
|
||||
current.removeChild(svg);
|
||||
}
|
||||
current.removeChild(svg);
|
||||
};
|
||||
}, [elements, pendingElements]);
|
||||
|
||||
|
@@ -2,17 +2,20 @@ import "./ToolIcon.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import { ToolButtonSize } from "./ToolButton";
|
||||
|
||||
type LockIconSize = "s" | "m";
|
||||
|
||||
type LockIconProps = {
|
||||
title?: string;
|
||||
name?: string;
|
||||
id?: string;
|
||||
checked: boolean;
|
||||
onChange?(): void;
|
||||
size?: LockIconSize;
|
||||
zenModeEnabled?: boolean;
|
||||
};
|
||||
|
||||
const DEFAULT_SIZE: ToolButtonSize = "medium";
|
||||
const DEFAULT_SIZE: LockIconSize = "m";
|
||||
|
||||
const ICONS = {
|
||||
CHECKED: (
|
||||
@@ -38,12 +41,12 @@ const ICONS = {
|
||||
),
|
||||
};
|
||||
|
||||
export const LockButton = (props: LockIconProps) => {
|
||||
export const LockIcon = (props: LockIconProps) => {
|
||||
return (
|
||||
<label
|
||||
className={clsx(
|
||||
"ToolIcon ToolIcon__lock ToolIcon_type_floating zen-mode-visibility",
|
||||
`ToolIcon_size_${DEFAULT_SIZE}`,
|
||||
`ToolIcon_size_${props.size || DEFAULT_SIZE}`,
|
||||
{
|
||||
"zen-mode-visibility--hidden": props.zenModeEnabled,
|
||||
},
|
||||
@@ -54,6 +57,7 @@ export const LockButton = (props: LockIconProps) => {
|
||||
className="ToolIcon_type_checkbox"
|
||||
type="checkbox"
|
||||
name={props.name}
|
||||
id={props.id}
|
||||
onChange={props.onChange}
|
||||
checked={props.checked}
|
||||
aria-label={props.title}
|
@@ -13,10 +13,9 @@ import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
|
||||
import { Section } from "./Section";
|
||||
import CollabButton from "./CollabButton";
|
||||
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
|
||||
import { LockButton } from "./LockButton";
|
||||
import { LockIcon } from "./LockIcon";
|
||||
import { UserList } from "./UserList";
|
||||
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
||||
import { LibraryButton } from "./LibraryButton";
|
||||
|
||||
type MobileMenuProps = {
|
||||
appState: AppState;
|
||||
@@ -65,15 +64,15 @@ export const MobileMenu = ({
|
||||
canvas={canvas}
|
||||
elementType={appState.elementType}
|
||||
setAppState={setAppState}
|
||||
isLibraryOpen={appState.isLibraryOpen}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
<LockButton
|
||||
<LockIcon
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
<LibraryButton appState={appState} setAppState={setAppState} />
|
||||
</Stack.Row>
|
||||
{libraryMenu}
|
||||
</Stack.Col>
|
||||
@@ -168,9 +167,10 @@ export const MobileMenu = ({
|
||||
)
|
||||
.map(([clientId, client]) => (
|
||||
<React.Fragment key={clientId}>
|
||||
{actionManager.renderAction("goToCollaborator", {
|
||||
id: clientId,
|
||||
})}
|
||||
{actionManager.renderAction(
|
||||
"goToCollaborator",
|
||||
clientId,
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</UserList>
|
||||
|
@@ -58,7 +58,7 @@ const useBodyRoot = (theme: AppState["theme"]) => {
|
||||
const isMobileRef = useRef(isMobile);
|
||||
isMobileRef.current = isMobile;
|
||||
|
||||
const { container: excalidrawContainer } = useExcalidrawContainer();
|
||||
const excalidrawContainer = useExcalidrawContainer();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (div) {
|
||||
|
@@ -34,21 +34,19 @@ const ChartPreviewBtn = (props: {
|
||||
0,
|
||||
);
|
||||
setChartElements(elements);
|
||||
let svg: SVGSVGElement;
|
||||
|
||||
const svg = exportToSvg(elements, {
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: oc.white,
|
||||
});
|
||||
|
||||
const previewNode = previewRef.current!;
|
||||
|
||||
(async () => {
|
||||
svg = await exportToSvg(elements, {
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: oc.white,
|
||||
});
|
||||
previewNode.appendChild(svg);
|
||||
|
||||
previewNode.appendChild(svg);
|
||||
|
||||
if (props.selected) {
|
||||
(previewNode.parentNode as HTMLDivElement).focus();
|
||||
}
|
||||
})();
|
||||
if (props.selected) {
|
||||
(previewNode.parentNode as HTMLDivElement).focus();
|
||||
}
|
||||
|
||||
return () => {
|
||||
previewNode.removeChild(svg);
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import "./TextInput.scss";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { Component } from "react";
|
||||
import { focusNearestParent } from "../utils";
|
||||
|
||||
import "./ProjectName.scss";
|
||||
import { useExcalidrawContainer } from "./App";
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
@@ -13,19 +12,22 @@ type Props = {
|
||||
isNameEditable: boolean;
|
||||
};
|
||||
|
||||
export const ProjectName = (props: Props) => {
|
||||
const { id } = useExcalidrawContainer();
|
||||
const [fileName, setFileName] = useState<string>(props.value);
|
||||
|
||||
const handleBlur = (event: any) => {
|
||||
type State = {
|
||||
fileName: string;
|
||||
};
|
||||
export class ProjectName extends Component<Props, State> {
|
||||
state = {
|
||||
fileName: this.props.value,
|
||||
};
|
||||
private handleBlur = (event: any) => {
|
||||
focusNearestParent(event.target);
|
||||
const value = event.target.value;
|
||||
if (value !== props.value) {
|
||||
props.onChange(value);
|
||||
if (value !== this.props.value) {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
private handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
if (event.nativeEvent.isComposing || event.keyCode === 229) {
|
||||
@@ -35,25 +37,29 @@ export const ProjectName = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ProjectName">
|
||||
<label className="ProjectName-label" htmlFor="filename">
|
||||
{`${props.label}${props.isNameEditable ? "" : ":"}`}
|
||||
</label>
|
||||
{props.isNameEditable ? (
|
||||
<input
|
||||
className="TextInput"
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
id={`${id}-filename`}
|
||||
value={fileName}
|
||||
onChange={(event) => setFileName(event.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<span className="TextInput TextInput--readonly" id={`${id}-filename`}>
|
||||
{props.value}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
public render() {
|
||||
return (
|
||||
<div className="ProjectName">
|
||||
<label className="ProjectName-label" htmlFor="filename">
|
||||
{`${this.props.label}${this.props.isNameEditable ? "" : ":"}`}
|
||||
</label>
|
||||
{this.props.isNameEditable ? (
|
||||
<input
|
||||
className="TextInput"
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
id="filename"
|
||||
value={this.state.fileName}
|
||||
onChange={(event) =>
|
||||
this.setState({ fileName: event.target.value })
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span className="TextInput TextInput--readonly" id="filename">
|
||||
{this.props.value}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { t } from "../i18n";
|
||||
import { useExcalidrawContainer } from "./App";
|
||||
|
||||
interface SectionProps extends React.HTMLProps<HTMLElement> {
|
||||
heading: string;
|
||||
@@ -8,14 +7,13 @@ interface SectionProps extends React.HTMLProps<HTMLElement> {
|
||||
}
|
||||
|
||||
export const Section = ({ heading, children, ...props }: SectionProps) => {
|
||||
const { id } = useExcalidrawContainer();
|
||||
const header = (
|
||||
<h2 className="visually-hidden" id={`${id}-${heading}-title`}>
|
||||
<h2 className="visually-hidden" id={`${heading}-title`}>
|
||||
{t(`headings.${heading}`)}
|
||||
</h2>
|
||||
);
|
||||
return (
|
||||
<section {...props} aria-labelledby={`${id}-${heading}-title`}>
|
||||
<section {...props} aria-labelledby={`${heading}-title`}>
|
||||
{typeof children === "function" ? (
|
||||
children(header)
|
||||
) : (
|
||||
|
@@ -2,9 +2,8 @@ import "./ToolIcon.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import { useExcalidrawContainer } from "./App";
|
||||
|
||||
export type ToolButtonSize = "small" | "medium";
|
||||
type ToolIconSize = "s" | "m";
|
||||
|
||||
type ToolButtonBaseProps = {
|
||||
icon?: React.ReactNode;
|
||||
@@ -15,7 +14,7 @@ type ToolButtonBaseProps = {
|
||||
title?: string;
|
||||
name?: string;
|
||||
id?: string;
|
||||
size?: ToolButtonSize;
|
||||
size?: ToolIconSize;
|
||||
keyBindingLabel?: string;
|
||||
showAriaLabel?: boolean;
|
||||
hidden?: boolean;
|
||||
@@ -41,11 +40,12 @@ type ToolButtonProps =
|
||||
onChange?(): void;
|
||||
});
|
||||
|
||||
const DEFAULT_SIZE: ToolIconSize = "m";
|
||||
|
||||
export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
const { id: excalId } = useExcalidrawContainer();
|
||||
const innerRef = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => innerRef.current);
|
||||
const sizeCn = `ToolIcon_size_${props.size}`;
|
||||
const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`;
|
||||
|
||||
if (props.type === "button" || props.type === "icon") {
|
||||
return (
|
||||
@@ -98,7 +98,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
aria-label={props["aria-label"]}
|
||||
aria-keyshortcuts={props["aria-keyshortcuts"]}
|
||||
data-testid={props["data-testid"]}
|
||||
id={`${excalId}-${props.id}`}
|
||||
id={props.id}
|
||||
onChange={props.onChange}
|
||||
checked={props.checked}
|
||||
ref={innerRef}
|
||||
@@ -116,5 +116,4 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
ToolButton.defaultProps = {
|
||||
visible: true,
|
||||
className: "",
|
||||
size: "medium",
|
||||
};
|
||||
|
@@ -8,18 +8,10 @@
|
||||
position: relative;
|
||||
font-family: Cascadia;
|
||||
cursor: pointer;
|
||||
background-color: var(--button-gray-1);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
border-radius: var(--space-factor);
|
||||
user-select: none;
|
||||
|
||||
background-color: var(--button-gray-1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-gray-2);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--button-gray-3);
|
||||
}
|
||||
}
|
||||
|
||||
.ToolIcon--plain {
|
||||
@@ -60,9 +52,9 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ToolIcon_size_small .ToolIcon__icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
.ToolIcon_size_s .ToolIcon__icon {
|
||||
width: 1.4rem;
|
||||
height: 1.4rem;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@@ -74,6 +66,14 @@
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-gray-1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--button-gray-2);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px var(--focus-highlight-color);
|
||||
}
|
||||
@@ -86,14 +86,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-gray-2);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--button-gray-3);
|
||||
}
|
||||
|
||||
&--show {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -111,9 +103,6 @@
|
||||
|
||||
&:not(.ToolIcon_toggle_opaque):checked + .ToolIcon__icon {
|
||||
background-color: var(--button-gray-2);
|
||||
&:active {
|
||||
background-color: var(--button-gray-3);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus + .ToolIcon__icon {
|
||||
@@ -141,21 +130,12 @@
|
||||
}
|
||||
|
||||
.ToolIcon__icon {
|
||||
background-color: var(--button-gray-1);
|
||||
&:hover {
|
||||
background-color: var(--button-gray-2);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--button-gray-3);
|
||||
}
|
||||
|
||||
width: 2rem;
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.ToolIcon.ToolIcon__lock {
|
||||
margin-inline-end: var(--space-factor);
|
||||
&.ToolIcon_type_floating {
|
||||
margin-left: 0.1rem;
|
||||
}
|
||||
@@ -186,9 +166,10 @@
|
||||
// move the lock button out of the way on small viewports
|
||||
// it begins to collide with the GitHub icon before we switch to mobile mode
|
||||
@media (max-width: 760px) {
|
||||
.ToolIcon.ToolIcon_type_floating {
|
||||
.ToolIcon.ToolIcon__lock {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: -8px;
|
||||
|
||||
margin-left: 0;
|
||||
@@ -213,14 +194,6 @@
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
.ToolIcon.ToolIcon__library {
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.ToolIcon.ToolIcon__lock {
|
||||
margin-inline-end: 0;
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.unlocked-icon {
|
||||
|
@@ -1,6 +1,4 @@
|
||||
@import "../css/variables.module";
|
||||
|
||||
// container in body where the actual tooltip is appended to
|
||||
.excalidraw-tooltip {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
@@ -26,20 +24,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
// wraps the element we want to apply the tooltip to
|
||||
.excalidraw-tooltip-wrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.excalidraw {
|
||||
.Tooltip-icon {
|
||||
width: 0.9em;
|
||||
height: 0.9em;
|
||||
margin-left: 5px;
|
||||
margin-top: 1px;
|
||||
display: flex;
|
||||
|
||||
.excalidraw-tooltip-icon {
|
||||
width: 0.9em;
|
||||
height: 0.9em;
|
||||
margin-left: 5px;
|
||||
margin-top: 1px;
|
||||
display: flex;
|
||||
|
||||
@include isMobile {
|
||||
display: none;
|
||||
@include isMobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -74,7 +74,6 @@ export const Tooltip = ({ children, label, long = false }: TooltipProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="excalidraw-tooltip-wrapper"
|
||||
onPointerEnter={(event) =>
|
||||
updateTooltip(
|
||||
event.currentTarget as HTMLDivElement,
|
||||
|
@@ -24,10 +24,7 @@ type Opts = {
|
||||
mirror?: true;
|
||||
} & React.SVGProps<SVGSVGElement>;
|
||||
|
||||
export const createIcon = (
|
||||
d: string | React.ReactNode,
|
||||
opts: number | Opts = 512,
|
||||
) => {
|
||||
const createIcon = (d: string | React.ReactNode, opts: number | Opts = 512) => {
|
||||
const { width = 512, height = width, mirror, style } =
|
||||
typeof opts === "number" ? ({ width: opts } as Opts) : opts;
|
||||
return (
|
||||
@@ -477,11 +474,6 @@ export const shield = createIcon(
|
||||
{ width: 24 },
|
||||
);
|
||||
|
||||
export const file = createIcon(
|
||||
"M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zM332.1 128H256V51.9l76.1 76.1zM48 464V48h160v104c0 13.3 10.7 24 24 24h104v288H48zm32-48h224V288l-23.5-23.5c-4.7-4.7-12.3-4.7-17 0L176 352l-39.5-39.5c-4.7-4.7-12.3-4.7-17 0L80 352v64zm48-240c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48z",
|
||||
{ width: 384, height: 512 },
|
||||
);
|
||||
|
||||
export const GroupIcon = React.memo(({ theme }: { theme: "light" | "dark" }) =>
|
||||
createIcon(
|
||||
<>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { FontFamily } from "./element/types";
|
||||
import cssVariables from "./css/variables.module.scss";
|
||||
import { AppProps } from "./types";
|
||||
import { FontFamilyValues } from "./element/types";
|
||||
|
||||
export const APP_NAME = "Excalidraw";
|
||||
|
||||
@@ -14,7 +14,6 @@ export const CURSOR_TYPE = {
|
||||
TEXT: "text",
|
||||
CROSSHAIR: "crosshair",
|
||||
GRABBING: "grabbing",
|
||||
GRAB: "grab",
|
||||
POINTER: "pointer",
|
||||
MOVE: "move",
|
||||
AUTO: "",
|
||||
@@ -35,7 +34,6 @@ export enum EVENT {
|
||||
MOUSE_MOVE = "mousemove",
|
||||
RESIZE = "resize",
|
||||
UNLOAD = "unload",
|
||||
FOCUS = "focus",
|
||||
BLUR = "blur",
|
||||
DRAG_OVER = "dragover",
|
||||
DROP = "drop",
|
||||
@@ -65,15 +63,15 @@ export const CLASSES = {
|
||||
|
||||
// 1-based in case we ever do `if(element.fontFamily)`
|
||||
export const FONT_FAMILY = {
|
||||
Virgil: 1,
|
||||
Helvetica: 2,
|
||||
Cascadia: 3,
|
||||
};
|
||||
1: "Virgil",
|
||||
2: "Helvetica",
|
||||
3: "Cascadia",
|
||||
} as const;
|
||||
|
||||
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
|
||||
|
||||
export const DEFAULT_FONT_SIZE = 20;
|
||||
export const DEFAULT_FONT_FAMILY: FontFamilyValues = FONT_FAMILY.Virgil;
|
||||
export const DEFAULT_FONT_FAMILY: FontFamily = 1;
|
||||
export const DEFAULT_TEXT_ALIGN = "left";
|
||||
export const DEFAULT_VERTICAL_ALIGN = "top";
|
||||
export const DEFAULT_VERSION = "{version}";
|
||||
@@ -85,7 +83,7 @@ export const GRID_SIZE = 20; // TODO make it configurable?
|
||||
export const MIME_TYPES = {
|
||||
excalidraw: "application/vnd.excalidraw+json",
|
||||
excalidrawlib: "application/vnd.excalidrawlib+json",
|
||||
} as const;
|
||||
};
|
||||
|
||||
export const EXPORT_DATA_TYPES = {
|
||||
excalidraw: "excalidraw",
|
||||
@@ -146,6 +144,3 @@ export const MQ_MAX_WIDTH_LANDSCAPE = 1000;
|
||||
export const MQ_MAX_HEIGHT_LANDSCAPE = 500;
|
||||
|
||||
export const MAX_DECIMALS_FOR_SVG_EXPORT = 2;
|
||||
|
||||
export const EXPORT_SCALES = [1, 2, 3];
|
||||
export const DEFAULT_EXPORT_PADDING = 10; // px
|
||||
|
@@ -51,12 +51,11 @@
|
||||
image-rendering: -moz-crisp-edges; // FF
|
||||
|
||||
z-index: var(--zIndex-canvas);
|
||||
|
||||
// Remove the main canvas from document flow to avoid resizeObserver
|
||||
// feedback loop (see https://github.com/excalidraw/excalidraw/pull/3379)
|
||||
}
|
||||
|
||||
&__canvas {
|
||||
#canvas {
|
||||
// Remove the main canvas from document flow to avoid resizeObserver
|
||||
// feedback loop (see https://github.com/excalidraw/excalidraw/pull/3379)
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@@ -414,6 +413,22 @@
|
||||
&:active {
|
||||
background-color: var(--button-gray-2);
|
||||
}
|
||||
|
||||
&.dropdown-select--floating {
|
||||
margin: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-select__language.dropdown-select--floating {
|
||||
bottom: 10px;
|
||||
|
||||
:root[dir="ltr"] & {
|
||||
right: 44px;
|
||||
}
|
||||
|
||||
:root[dir="rtl"] & {
|
||||
left: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.zIndexButton {
|
||||
@@ -440,38 +455,26 @@
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
fill: $oc-gray-6;
|
||||
width: 1.5rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 5px;
|
||||
background: none;
|
||||
color: var(--icon-fill-color);
|
||||
|
||||
svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.reset-zoom-button {
|
||||
padding: 0.2em;
|
||||
background: transparent;
|
||||
color: var(--text-primary-color);
|
||||
font-family: var(--ui-font);
|
||||
}
|
||||
:root[dir="ltr"] & {
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
.undo-redo-buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 0.4em;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-inline-start: 0.6em;
|
||||
:root[dir="rtl"] & {
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@include isMobile {
|
||||
|
@@ -1,12 +1,10 @@
|
||||
import { cleanAppStateForExport } from "../appState";
|
||||
import { EXPORT_DATA_TYPES } from "../constants";
|
||||
import { clearElementsForExport } from "../element";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { CanvasError } from "../errors";
|
||||
import { t } from "../i18n";
|
||||
import { calculateScrollCenter } from "../scene";
|
||||
import { AppState } from "../types";
|
||||
import { FileSystemHandle } from "./filesystem";
|
||||
import { isValidExcalidrawData } from "./json";
|
||||
import { restore } from "./restore";
|
||||
import { ImportedLibraryData } from "./types";
|
||||
@@ -81,30 +79,10 @@ export const getMimeType = (blob: Blob | string): string => {
|
||||
return "";
|
||||
};
|
||||
|
||||
export const getFileHandleType = (handle: FileSystemHandle | null) => {
|
||||
if (!handle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return handle.name.match(/\.(json|excalidraw|png|svg)$/)?.[1] || null;
|
||||
};
|
||||
|
||||
export const isImageFileHandleType = (
|
||||
type: string | null,
|
||||
): type is "png" | "svg" => {
|
||||
return type === "png" || type === "svg";
|
||||
};
|
||||
|
||||
export const isImageFileHandle = (handle: FileSystemHandle | null) => {
|
||||
const type = getFileHandleType(handle);
|
||||
return type === "png" || type === "svg";
|
||||
};
|
||||
|
||||
export const loadFromBlob = async (
|
||||
blob: Blob,
|
||||
/** @see restore.localAppState */
|
||||
localAppState: AppState | null,
|
||||
localElements: readonly ExcalidrawElement[] | null,
|
||||
) => {
|
||||
const contents = await parseFileContents(blob);
|
||||
try {
|
||||
@@ -117,7 +95,7 @@ export const loadFromBlob = async (
|
||||
elements: clearElementsForExport(data.elements || []),
|
||||
appState: {
|
||||
theme: localAppState?.theme,
|
||||
fileHandle: blob.handle || null,
|
||||
fileHandle: (!blob.type.startsWith("image/") && blob.handle) || null,
|
||||
...cleanAppStateForExport(data.appState || {}),
|
||||
...(localAppState
|
||||
? calculateScrollCenter(data.elements || [], localAppState, null)
|
||||
@@ -125,7 +103,6 @@ export const loadFromBlob = async (
|
||||
},
|
||||
},
|
||||
localAppState,
|
||||
localElements,
|
||||
);
|
||||
|
||||
return result;
|
||||
|
@@ -1,122 +0,0 @@
|
||||
import {
|
||||
FileWithHandle,
|
||||
fileOpen as _fileOpen,
|
||||
fileSave as _fileSave,
|
||||
FileSystemHandle,
|
||||
supported as nativeFileSystemSupported,
|
||||
} from "@dwelle/browser-fs-access";
|
||||
import { EVENT, MIME_TYPES } from "../constants";
|
||||
import { AbortError } from "../errors";
|
||||
import { debounce } from "../utils";
|
||||
|
||||
type FILE_EXTENSION =
|
||||
| "jpg"
|
||||
| "png"
|
||||
| "svg"
|
||||
| "json"
|
||||
| "excalidraw"
|
||||
| "excalidrawlib";
|
||||
|
||||
const FILE_TYPE_TO_MIME_TYPE: Record<FILE_EXTENSION, string> = {
|
||||
jpg: "image/jpeg",
|
||||
png: "image/png",
|
||||
svg: "image/svg+xml",
|
||||
json: "application/json",
|
||||
excalidraw: MIME_TYPES.excalidraw,
|
||||
excalidrawlib: MIME_TYPES.excalidrawlib,
|
||||
};
|
||||
|
||||
const INPUT_CHANGE_INTERVAL_MS = 500;
|
||||
|
||||
export const fileOpen = <M extends boolean | undefined = false>(opts: {
|
||||
extensions?: FILE_EXTENSION[];
|
||||
description?: string;
|
||||
multiple?: M;
|
||||
}): Promise<
|
||||
M extends false | undefined ? FileWithHandle : FileWithHandle[]
|
||||
> => {
|
||||
// an unsafe TS hack, alas not much we can do AFAIK
|
||||
type RetType = M extends false | undefined
|
||||
? FileWithHandle
|
||||
: FileWithHandle[];
|
||||
|
||||
const mimeTypes = opts.extensions?.reduce((mimeTypes, type) => {
|
||||
mimeTypes.push(FILE_TYPE_TO_MIME_TYPE[type]);
|
||||
|
||||
return mimeTypes;
|
||||
}, [] as string[]);
|
||||
|
||||
const extensions = opts.extensions?.reduce((acc, ext) => {
|
||||
if (ext === "jpg") {
|
||||
return acc.concat(".jpg", ".jpeg");
|
||||
}
|
||||
return acc.concat(`.${ext}`);
|
||||
}, [] as string[]);
|
||||
|
||||
return _fileOpen({
|
||||
description: opts.description,
|
||||
extensions,
|
||||
mimeTypes,
|
||||
multiple: opts.multiple ?? false,
|
||||
legacySetup: (resolve, reject, input) => {
|
||||
const scheduleRejection = debounce(reject, INPUT_CHANGE_INTERVAL_MS);
|
||||
const focusHandler = () => {
|
||||
checkForFile();
|
||||
document.addEventListener(EVENT.KEYUP, scheduleRejection);
|
||||
document.addEventListener(EVENT.POINTER_UP, scheduleRejection);
|
||||
scheduleRejection();
|
||||
};
|
||||
const checkForFile = () => {
|
||||
// this hack might not work when expecting multiple files
|
||||
if (input.files?.length) {
|
||||
const ret = opts.multiple ? [...input.files] : input.files[0];
|
||||
resolve(ret as RetType);
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(() => {
|
||||
window.addEventListener(EVENT.FOCUS, focusHandler);
|
||||
});
|
||||
const interval = window.setInterval(() => {
|
||||
checkForFile();
|
||||
}, INPUT_CHANGE_INTERVAL_MS);
|
||||
return (rejectPromise) => {
|
||||
clearInterval(interval);
|
||||
scheduleRejection.cancel();
|
||||
window.removeEventListener(EVENT.FOCUS, focusHandler);
|
||||
document.removeEventListener(EVENT.KEYUP, scheduleRejection);
|
||||
document.removeEventListener(EVENT.POINTER_UP, scheduleRejection);
|
||||
if (rejectPromise) {
|
||||
// so that something is shown in console if we need to debug this
|
||||
console.warn("Opening the file was canceled (legacy-fs).");
|
||||
rejectPromise(new AbortError());
|
||||
}
|
||||
};
|
||||
},
|
||||
}) as Promise<RetType>;
|
||||
};
|
||||
|
||||
export const fileSave = (
|
||||
blob: Blob,
|
||||
opts: {
|
||||
/** supply without the extension */
|
||||
name: string;
|
||||
/** file extension */
|
||||
extension: FILE_EXTENSION;
|
||||
description?: string;
|
||||
/** existing FileSystemHandle */
|
||||
fileHandle?: FileSystemHandle | null;
|
||||
},
|
||||
) => {
|
||||
return _fileSave(
|
||||
blob,
|
||||
{
|
||||
fileName: `${opts.name}.${opts.extension}`,
|
||||
description: opts.description,
|
||||
extensions: [`.${opts.extension}`],
|
||||
},
|
||||
opts.fileHandle,
|
||||
);
|
||||
};
|
||||
|
||||
export type { FileSystemHandle };
|
||||
export { nativeFileSystemSupported };
|
@@ -1,15 +1,14 @@
|
||||
import { fileSave } from "browser-fs-access";
|
||||
import {
|
||||
copyBlobToClipboardAsPng,
|
||||
copyTextToSystemClipboard,
|
||||
} from "../clipboard";
|
||||
import { DEFAULT_EXPORT_PADDING } from "../constants";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { exportToCanvas, exportToSvg } from "../scene/export";
|
||||
import { ExportType } from "../scene/types";
|
||||
import { AppState } from "../types";
|
||||
import { canvasToBlob } from "./blob";
|
||||
import { fileSave, FileSystemHandle } from "./filesystem";
|
||||
import { serializeAsJSON } from "./json";
|
||||
|
||||
export { loadFromBlob } from "./blob";
|
||||
@@ -21,41 +20,45 @@ export const exportCanvas = async (
|
||||
appState: AppState,
|
||||
{
|
||||
exportBackground,
|
||||
exportPadding = DEFAULT_EXPORT_PADDING,
|
||||
exportPadding = 10,
|
||||
viewBackgroundColor,
|
||||
name,
|
||||
fileHandle = null,
|
||||
scale = 1,
|
||||
}: {
|
||||
exportBackground: boolean;
|
||||
exportPadding?: number;
|
||||
viewBackgroundColor: string;
|
||||
name: string;
|
||||
fileHandle?: FileSystemHandle | null;
|
||||
scale?: number;
|
||||
},
|
||||
) => {
|
||||
if (elements.length === 0) {
|
||||
throw new Error(t("alerts.cannotExportEmptyCanvas"));
|
||||
}
|
||||
if (type === "svg" || type === "clipboard-svg") {
|
||||
const tempSvg = await exportToSvg(elements, {
|
||||
const tempSvg = exportToSvg(elements, {
|
||||
exportBackground,
|
||||
exportWithDarkMode: appState.exportWithDarkMode,
|
||||
viewBackgroundColor,
|
||||
exportPadding,
|
||||
exportScale: appState.exportScale,
|
||||
exportEmbedScene: appState.exportEmbedScene && type === "svg",
|
||||
scale,
|
||||
metadata:
|
||||
appState.exportEmbedScene && type === "svg"
|
||||
? await (
|
||||
await import(/* webpackChunkName: "image" */ "./image")
|
||||
).encodeSvgMetadata({
|
||||
text: serializeAsJSON(elements, appState),
|
||||
})
|
||||
: undefined,
|
||||
});
|
||||
if (type === "svg") {
|
||||
return await fileSave(
|
||||
new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }),
|
||||
{
|
||||
name,
|
||||
extension: "svg",
|
||||
fileHandle,
|
||||
},
|
||||
);
|
||||
await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
|
||||
fileName: `${name}.svg`,
|
||||
extensions: [".svg"],
|
||||
});
|
||||
return;
|
||||
} else if (type === "clipboard-svg") {
|
||||
await copyTextToSystemClipboard(tempSvg.outerHTML);
|
||||
copyTextToSystemClipboard(tempSvg.outerHTML);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +67,7 @@ export const exportCanvas = async (
|
||||
exportBackground,
|
||||
viewBackgroundColor,
|
||||
exportPadding,
|
||||
scale,
|
||||
});
|
||||
tempCanvas.style.display = "none";
|
||||
document.body.appendChild(tempCanvas);
|
||||
@@ -71,6 +75,7 @@ export const exportCanvas = async (
|
||||
tempCanvas.remove();
|
||||
|
||||
if (type === "png") {
|
||||
const fileName = `${name}.png`;
|
||||
if (appState.exportEmbedScene) {
|
||||
blob = await (
|
||||
await import(/* webpackChunkName: "image" */ "./image")
|
||||
@@ -80,10 +85,9 @@ export const exportCanvas = async (
|
||||
});
|
||||
}
|
||||
|
||||
return await fileSave(blob, {
|
||||
name,
|
||||
extension: "png",
|
||||
fileHandle,
|
||||
await fileSave(blob, {
|
||||
fileName,
|
||||
extensions: [".png"],
|
||||
});
|
||||
} else if (type === "clipboard") {
|
||||
try {
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { fileOpen, fileSave } from "./filesystem";
|
||||
import { fileOpen, fileSave } from "browser-fs-access";
|
||||
import { cleanAppStateForExport } from "../appState";
|
||||
import { EXPORT_DATA_TYPES, EXPORT_SOURCE, MIME_TYPES } from "../constants";
|
||||
import { clearElementsForExport } from "../element";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
import { isImageFileHandle, loadFromBlob } from "./blob";
|
||||
import { loadFromBlob } from "./blob";
|
||||
|
||||
import {
|
||||
ExportedDataState,
|
||||
@@ -15,7 +15,7 @@ import Library from "./library";
|
||||
|
||||
export const serializeAsJSON = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: Partial<AppState>,
|
||||
appState: AppState,
|
||||
): string => {
|
||||
const data: ExportedDataState = {
|
||||
type: EXPORT_DATA_TYPES.excalidraw,
|
||||
@@ -37,21 +37,19 @@ export const saveAsJSON = async (
|
||||
type: MIME_TYPES.excalidraw,
|
||||
});
|
||||
|
||||
const fileHandle = await fileSave(blob, {
|
||||
name: appState.name,
|
||||
extension: "excalidraw",
|
||||
description: "Excalidraw file",
|
||||
fileHandle: isImageFileHandle(appState.fileHandle)
|
||||
? null
|
||||
: appState.fileHandle,
|
||||
});
|
||||
const fileHandle = await fileSave(
|
||||
blob,
|
||||
{
|
||||
fileName: `${appState.name}.excalidraw`,
|
||||
description: "Excalidraw file",
|
||||
extensions: [".excalidraw"],
|
||||
},
|
||||
appState.fileHandle,
|
||||
);
|
||||
return { fileHandle };
|
||||
};
|
||||
|
||||
export const loadFromJSON = async (
|
||||
localAppState: AppState,
|
||||
localElements: readonly ExcalidrawElement[] | null,
|
||||
) => {
|
||||
export const loadFromJSON = async (localAppState: AppState) => {
|
||||
const blob = await fileOpen({
|
||||
description: "Excalidraw files",
|
||||
// ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442
|
||||
@@ -66,7 +64,7 @@ export const loadFromJSON = async (
|
||||
],
|
||||
*/
|
||||
});
|
||||
return loadFromBlob(blob, localAppState, localElements);
|
||||
return loadFromBlob(blob, localAppState);
|
||||
};
|
||||
|
||||
export const isValidExcalidrawData = (data?: {
|
||||
@@ -100,16 +98,15 @@ export const saveLibraryAsJSON = async (library: Library) => {
|
||||
library: libraryItems,
|
||||
};
|
||||
const serialized = JSON.stringify(data, null, 2);
|
||||
await fileSave(
|
||||
new Blob([serialized], {
|
||||
type: MIME_TYPES.excalidrawlib,
|
||||
}),
|
||||
{
|
||||
name: "library",
|
||||
extension: "excalidrawlib",
|
||||
description: "Excalidraw library file",
|
||||
},
|
||||
);
|
||||
const fileName = "library.excalidrawlib";
|
||||
const blob = new Blob([serialized], {
|
||||
type: MIME_TYPES.excalidrawlib,
|
||||
});
|
||||
await fileSave(blob, {
|
||||
fileName,
|
||||
description: "Excalidraw library file",
|
||||
extensions: [".excalidrawlib"],
|
||||
});
|
||||
};
|
||||
|
||||
export const importLibraryFromJSON = async (library: Library) => {
|
||||
|
@@ -2,7 +2,7 @@ import { loadLibraryFromBlob } from "./blob";
|
||||
import { LibraryItems, LibraryItem } from "../types";
|
||||
import { restoreElements } from "./restore";
|
||||
import { getNonDeletedElements } from "../element";
|
||||
import type App from "../components/App";
|
||||
import App from "../components/App";
|
||||
|
||||
class Library {
|
||||
private libraryCache: LibraryItems | null = null;
|
||||
@@ -18,7 +18,7 @@ class Library {
|
||||
};
|
||||
|
||||
restoreLibraryItem = (libraryItem: LibraryItem): LibraryItem | null => {
|
||||
const elements = getNonDeletedElements(restoreElements(libraryItem, null));
|
||||
const elements = getNonDeletedElements(restoreElements(libraryItem));
|
||||
return elements.length ? elements : null;
|
||||
};
|
||||
|
||||
|
@@ -1,38 +0,0 @@
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
import { exportCanvas } from ".";
|
||||
import { getNonDeletedElements } from "../element";
|
||||
import { getFileHandleType, isImageFileHandleType } from "./blob";
|
||||
|
||||
export const resaveAsImageWithScene = async (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
const { exportBackground, viewBackgroundColor, name, fileHandle } = appState;
|
||||
|
||||
const fileHandleType = getFileHandleType(fileHandle);
|
||||
|
||||
if (!fileHandle || !isImageFileHandleType(fileHandleType)) {
|
||||
throw new Error(
|
||||
"fileHandle should exist and should be of type svg or png when resaving",
|
||||
);
|
||||
}
|
||||
appState = {
|
||||
...appState,
|
||||
exportEmbedScene: true,
|
||||
};
|
||||
|
||||
await exportCanvas(
|
||||
fileHandleType,
|
||||
getNonDeletedElements(elements),
|
||||
appState,
|
||||
{
|
||||
exportBackground,
|
||||
viewBackgroundColor,
|
||||
name,
|
||||
fileHandle,
|
||||
},
|
||||
);
|
||||
|
||||
return { fileHandle };
|
||||
};
|
@@ -1,26 +1,21 @@
|
||||
import {
|
||||
ExcalidrawElement,
|
||||
FontFamily,
|
||||
ExcalidrawSelectionElement,
|
||||
FontFamilyValues,
|
||||
} from "../element/types";
|
||||
import { AppState, NormalizedZoomValue } from "../types";
|
||||
import { ImportedDataState } from "./types";
|
||||
import {
|
||||
getElementMap,
|
||||
getNormalizedDimensions,
|
||||
isInvisiblySmallElement,
|
||||
} from "../element";
|
||||
import { isInvisiblySmallElement, getNormalizedDimensions } from "../element";
|
||||
import { isLinearElementType } from "../element/typeChecks";
|
||||
import { randomId } from "../random";
|
||||
import {
|
||||
FONT_FAMILY,
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_TEXT_ALIGN,
|
||||
DEFAULT_VERTICAL_ALIGN,
|
||||
FONT_FAMILY,
|
||||
} from "../constants";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import { bumpVersion } from "../element/mutateElement";
|
||||
|
||||
type RestoredAppState = Omit<
|
||||
AppState,
|
||||
@@ -46,11 +41,11 @@ export type RestoredDataState = {
|
||||
appState: RestoredAppState;
|
||||
};
|
||||
|
||||
const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
|
||||
if (Object.keys(FONT_FAMILY).includes(fontFamilyName)) {
|
||||
return FONT_FAMILY[
|
||||
fontFamilyName as keyof typeof FONT_FAMILY
|
||||
] as FontFamilyValues;
|
||||
const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
|
||||
for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) {
|
||||
if (fontFamilyString.includes(fontFamilyName)) {
|
||||
return parseInt(id) as FontFamily;
|
||||
}
|
||||
}
|
||||
return DEFAULT_FONT_FAMILY;
|
||||
};
|
||||
@@ -186,20 +181,13 @@ const restoreElement = (
|
||||
|
||||
export const restoreElements = (
|
||||
elements: ImportedDataState["elements"],
|
||||
/** NOTE doesn't serve for reconciliation */
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
): ExcalidrawElement[] => {
|
||||
const localElementsMap = localElements ? getElementMap(localElements) : null;
|
||||
return (elements || []).reduce((elements, element) => {
|
||||
// filtering out selection, which is legacy, no longer kept in elements,
|
||||
// and causing issues if retained
|
||||
if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
|
||||
let migratedElement: ExcalidrawElement = restoreElement(element);
|
||||
const migratedElement = restoreElement(element);
|
||||
if (migratedElement) {
|
||||
const localElement = localElementsMap?.[element.id];
|
||||
if (localElement && localElement.version > migratedElement.version) {
|
||||
migratedElement = bumpVersion(migratedElement, localElement.version);
|
||||
}
|
||||
elements.push(migratedElement);
|
||||
}
|
||||
}
|
||||
@@ -209,25 +197,25 @@ export const restoreElements = (
|
||||
|
||||
export const restoreAppState = (
|
||||
appState: ImportedDataState["appState"],
|
||||
localAppState: Partial<AppState> | null | undefined,
|
||||
localAppState: Partial<AppState> | null,
|
||||
): RestoredAppState => {
|
||||
appState = appState || {};
|
||||
|
||||
const defaultAppState = getDefaultAppState();
|
||||
const nextAppState = {} as typeof defaultAppState;
|
||||
|
||||
for (const [key, defaultValue] of Object.entries(defaultAppState) as [
|
||||
for (const [key, val] of Object.entries(defaultAppState) as [
|
||||
keyof typeof defaultAppState,
|
||||
any,
|
||||
][]) {
|
||||
const suppliedValue = appState[key];
|
||||
const restoredValue = appState[key];
|
||||
const localValue = localAppState ? localAppState[key] : undefined;
|
||||
(nextAppState as any)[key] =
|
||||
suppliedValue !== undefined
|
||||
? suppliedValue
|
||||
restoredValue !== undefined
|
||||
? restoredValue
|
||||
: localValue !== undefined
|
||||
? localValue
|
||||
: defaultValue;
|
||||
: val;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -255,10 +243,9 @@ export const restore = (
|
||||
* Supply `null` if you can't get access to it.
|
||||
*/
|
||||
localAppState: Partial<AppState> | null | undefined,
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
): RestoredDataState => {
|
||||
return {
|
||||
elements: restoreElements(data?.elements, localElements),
|
||||
elements: restoreElements(data?.elements),
|
||||
appState: restoreAppState(data?.appState, localAppState || null),
|
||||
};
|
||||
};
|
||||
|
@@ -328,15 +328,15 @@ const hitTestFreeDrawElement = (
|
||||
let P: readonly [number, number];
|
||||
|
||||
// For freedraw dots
|
||||
if (
|
||||
distance2d(A[0], A[1], x, y) < threshold ||
|
||||
distance2d(B[0], B[1], x, y) < threshold
|
||||
) {
|
||||
return true;
|
||||
if (element.points.length === 2) {
|
||||
return (
|
||||
distance2d(A[0], A[1], x, y) < threshold ||
|
||||
distance2d(B[0], B[1], x, y) < threshold
|
||||
);
|
||||
}
|
||||
|
||||
// For freedraw lines
|
||||
for (let i = 0; i < element.points.length; i++) {
|
||||
for (let i = 1; i < element.points.length - 1; i++) {
|
||||
const delta = [B[0] - A[0], B[1] - A[1]];
|
||||
const length = Math.hypot(delta[1], delta[0]);
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import { mutateElement } from "./mutateElement";
|
||||
import { getPerfectElementSize } from "./sizeHelpers";
|
||||
import Scene from "../scene/Scene";
|
||||
import { NonDeletedExcalidrawElement } from "./types";
|
||||
import { PointerDownState } from "../types";
|
||||
import { PointerDownState } from "../components/App";
|
||||
|
||||
export const dragSelectedElements = (
|
||||
pointerDownState: PointerDownState,
|
||||
|
@@ -120,11 +120,8 @@ export const newElementWith = <TElement extends ExcalidrawElement>(
|
||||
*
|
||||
* NOTE: does not trigger re-render.
|
||||
*/
|
||||
export const bumpVersion = (
|
||||
element: Mutable<ExcalidrawElement>,
|
||||
version?: ExcalidrawElement["version"],
|
||||
) => {
|
||||
element.version = (version ?? element.version) + 1;
|
||||
export const bumpVersion = (element: Mutable<ExcalidrawElement>) => {
|
||||
element.version = element.version + 1;
|
||||
element.versionNonce = randomInteger();
|
||||
return element;
|
||||
};
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { duplicateElement } from "./newElement";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
import { API } from "../tests/helpers/api";
|
||||
import { FONT_FAMILY } from "../constants";
|
||||
|
||||
const isPrimitive = (val: any) => {
|
||||
const type = typeof val;
|
||||
@@ -80,7 +79,7 @@ it("clones text element", () => {
|
||||
opacity: 100,
|
||||
text: "hello",
|
||||
fontSize: 20,
|
||||
fontFamily: FONT_FAMILY.Virgil,
|
||||
fontFamily: 1,
|
||||
textAlign: "left",
|
||||
verticalAlign: "top",
|
||||
});
|
||||
|
@@ -5,11 +5,11 @@ import {
|
||||
ExcalidrawGenericElement,
|
||||
NonDeleted,
|
||||
TextAlign,
|
||||
FontFamily,
|
||||
GroupId,
|
||||
VerticalAlign,
|
||||
Arrowhead,
|
||||
ExcalidrawFreeDrawElement,
|
||||
FontFamilyValues,
|
||||
} from "../element/types";
|
||||
import { measureText, getFontString } from "../utils";
|
||||
import { randomInteger, randomId } from "../random";
|
||||
@@ -109,7 +109,7 @@ export const newTextElement = (
|
||||
opts: {
|
||||
text: string;
|
||||
fontSize: number;
|
||||
fontFamily: FontFamilyValues;
|
||||
fontFamily: FontFamily;
|
||||
textAlign: TextAlign;
|
||||
verticalAlign: VerticalAlign;
|
||||
} & ElementConstructorOpts,
|
||||
|
@@ -32,7 +32,8 @@ import {
|
||||
MaybeTransformHandleType,
|
||||
TransformHandleDirection,
|
||||
} from "./transformHandles";
|
||||
import { Point, PointerDownState } from "../types";
|
||||
import { PointerDownState } from "../components/App";
|
||||
import { Point } from "../types";
|
||||
|
||||
export const normalizeAngle = (angle: number): number => {
|
||||
if (angle >= 2 * Math.PI) {
|
||||
|
@@ -3,8 +3,7 @@ import { FONT_FAMILY } from "../constants";
|
||||
|
||||
export type ChartType = "bar" | "line";
|
||||
export type FillStyle = "hachure" | "cross-hatch" | "solid";
|
||||
export type FontFamilyKeys = keyof typeof FONT_FAMILY;
|
||||
export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys];
|
||||
export type FontFamily = keyof typeof FONT_FAMILY;
|
||||
export type FontString = string & { _brand: "fontString" };
|
||||
export type GroupId = string;
|
||||
export type PointerType = "mouse" | "pen" | "touch";
|
||||
@@ -92,7 +91,7 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase &
|
||||
Readonly<{
|
||||
type: "text";
|
||||
fontSize: number;
|
||||
fontFamily: FontFamilyValues;
|
||||
fontFamily: FontFamily;
|
||||
text: string;
|
||||
baseline: number;
|
||||
textAlign: TextAlign;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
type CANVAS_ERROR_NAMES = "CANVAS_ERROR" | "CANVAS_POSSIBLY_TOO_BIG";
|
||||
|
||||
export class CanvasError extends Error {
|
||||
constructor(
|
||||
message: string = "Couldn't export canvas.",
|
||||
@@ -10,9 +9,3 @@ export class CanvasError extends Error {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
export class AbortError extends DOMException {
|
||||
constructor(message: string = "Request Aborted") {
|
||||
super(message, "AbortError");
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import throttle from "lodash.throttle";
|
||||
import React, { PureComponent } from "react";
|
||||
import { ExcalidrawImperativeAPI } from "../../types";
|
||||
import { ExcalidrawImperativeAPI } from "../../components/App";
|
||||
import { ErrorDialog } from "../../components/ErrorDialog";
|
||||
import { APP_NAME, ENV, EVENT } from "../../constants";
|
||||
import { ImportedDataState } from "../../data/types";
|
||||
|
@@ -1,92 +0,0 @@
|
||||
import React from "react";
|
||||
import { Card } from "../../components/Card";
|
||||
import { ToolButton } from "../../components/ToolButton";
|
||||
import { serializeAsJSON } from "../../data/json";
|
||||
import { getImportedKey, createIV, generateEncryptionKey } from "../data";
|
||||
import { loadFirebaseStorage } from "../data/firebase";
|
||||
import { NonDeletedExcalidrawElement } from "../../element/types";
|
||||
import { AppState } from "../../types";
|
||||
import { nanoid } from "nanoid";
|
||||
import { t } from "../../i18n";
|
||||
import { excalidrawPlusIcon } from "./icons";
|
||||
|
||||
const encryptData = async (
|
||||
key: string,
|
||||
json: string,
|
||||
): Promise<{ blob: Blob; iv: Uint8Array }> => {
|
||||
const importedKey = await getImportedKey(key, "encrypt");
|
||||
const iv = createIV();
|
||||
const encoded = new TextEncoder().encode(json);
|
||||
const ciphertext = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv,
|
||||
},
|
||||
importedKey,
|
||||
encoded,
|
||||
);
|
||||
|
||||
return { blob: new Blob([new Uint8Array(ciphertext)]), iv };
|
||||
};
|
||||
|
||||
const exportToExcalidrawPlus = async (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
const firebase = await loadFirebaseStorage();
|
||||
|
||||
const id = `${nanoid(12)}`;
|
||||
|
||||
const key = (await generateEncryptionKey())!;
|
||||
const encryptedData = await encryptData(
|
||||
key,
|
||||
serializeAsJSON(elements, appState),
|
||||
);
|
||||
|
||||
const blob = new Blob([encryptedData.iv, encryptedData.blob], {
|
||||
type: "application/octet-stream",
|
||||
});
|
||||
|
||||
await firebase
|
||||
.storage()
|
||||
.ref(`/migrations/scenes/${id}`)
|
||||
.put(blob, {
|
||||
customMetadata: {
|
||||
data: JSON.stringify({ version: 1, name: appState.name }),
|
||||
created: Date.now().toString(),
|
||||
},
|
||||
});
|
||||
|
||||
window.open(`https://plus.excalidraw.com/import?excalidraw=${id},${key}`);
|
||||
};
|
||||
|
||||
export const ExportToExcalidrawPlus: React.FC<{
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
appState: AppState;
|
||||
onError: (error: Error) => void;
|
||||
}> = ({ elements, appState, onError }) => {
|
||||
return (
|
||||
<Card color="indigo">
|
||||
<div className="Card-icon">{excalidrawPlusIcon}</div>
|
||||
<h2>Excalidraw+</h2>
|
||||
<div className="Card-details">
|
||||
{t("exportDialog.excalidrawplus_description")}
|
||||
</div>
|
||||
<ToolButton
|
||||
className="Card-button"
|
||||
type="button"
|
||||
title={t("exportDialog.excalidrawplus_button")}
|
||||
aria-label={t("exportDialog.excalidrawplus_button")}
|
||||
showAriaLabel={true}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await exportToExcalidrawPlus(elements, appState);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
onError(new Error(t("exportDialog.excalidrawplus_exportError")));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
@@ -1,18 +1,23 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import * as i18n from "../../i18n";
|
||||
|
||||
export const LanguageList = ({
|
||||
onChange,
|
||||
languages = i18n.languages,
|
||||
currentLangCode = i18n.getLanguage().code,
|
||||
floating,
|
||||
}: {
|
||||
languages?: { code: string; label: string }[];
|
||||
onChange: (langCode: i18n.Language["code"]) => void;
|
||||
currentLangCode?: i18n.Language["code"];
|
||||
floating?: boolean;
|
||||
}) => (
|
||||
<React.Fragment>
|
||||
<select
|
||||
className="dropdown-select dropdown-select__language"
|
||||
className={clsx("dropdown-select dropdown-select__language", {
|
||||
"dropdown-select--floating": floating,
|
||||
})}
|
||||
onChange={({ target }) => onChange(target.value)}
|
||||
value={currentLangCode}
|
||||
aria-label={i18n.t("buttons.selectLanguage")}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,19 +5,15 @@ import { getSceneVersion } from "../../element";
|
||||
import Portal from "../collab/Portal";
|
||||
import { restoreElements } from "../../data/restore";
|
||||
|
||||
// private
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
let firebasePromise: Promise<
|
||||
typeof import("firebase/app").default
|
||||
> | null = null;
|
||||
let firestorePromise: Promise<any> | null = null;
|
||||
let firebseStoragePromise: Promise<any> | null = null;
|
||||
|
||||
const _loadFirebase = async () => {
|
||||
const loadFirebase = async () => {
|
||||
const firebase = (
|
||||
await import(/* webpackChunkName: "firebase" */ "firebase/app")
|
||||
).default;
|
||||
await import(/* webpackChunkName: "firestore" */ "firebase/firestore");
|
||||
|
||||
const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
@@ -25,37 +21,13 @@ const _loadFirebase = async () => {
|
||||
return firebase;
|
||||
};
|
||||
|
||||
const _getFirebase = async (): Promise<
|
||||
const getFirebase = async (): Promise<
|
||||
typeof import("firebase/app").default
|
||||
> => {
|
||||
if (!firebasePromise) {
|
||||
firebasePromise = _loadFirebase();
|
||||
firebasePromise = loadFirebase();
|
||||
}
|
||||
return firebasePromise;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const loadFirestore = async () => {
|
||||
const firebase = await _getFirebase();
|
||||
if (!firestorePromise) {
|
||||
firestorePromise = import(
|
||||
/* webpackChunkName: "firestore" */ "firebase/firestore"
|
||||
);
|
||||
await firestorePromise;
|
||||
}
|
||||
return firebase;
|
||||
};
|
||||
|
||||
export const loadFirebaseStorage = async () => {
|
||||
const firebase = await _getFirebase();
|
||||
if (!firebseStoragePromise) {
|
||||
firebseStoragePromise = import(
|
||||
/* webpackChunkName: "storage" */ "firebase/storage"
|
||||
);
|
||||
await firebseStoragePromise;
|
||||
}
|
||||
return firebase;
|
||||
return await firebasePromise!;
|
||||
};
|
||||
|
||||
interface FirebaseStoredScene {
|
||||
@@ -136,7 +108,7 @@ export const saveToFirebase = async (
|
||||
return true;
|
||||
}
|
||||
|
||||
const firebase = await loadFirestore();
|
||||
const firebase = await getFirebase();
|
||||
const sceneVersion = getSceneVersion(elements);
|
||||
const { ciphertext, iv } = await encryptElements(roomKey, elements);
|
||||
|
||||
@@ -178,7 +150,7 @@ export const loadFromFirebase = async (
|
||||
roomKey: string,
|
||||
socket: SocketIOClient.Socket | null,
|
||||
): Promise<readonly ExcalidrawElement[] | null> => {
|
||||
const firebase = await loadFirestore();
|
||||
const firebase = await getFirebase();
|
||||
const db = firebase.firestore();
|
||||
|
||||
const docRef = db.collection("scenes").doc(roomId);
|
||||
@@ -196,5 +168,5 @@ export const loadFromFirebase = async (
|
||||
firebaseSceneVersionCache.set(socket, getSceneVersion(elements));
|
||||
}
|
||||
|
||||
return restoreElements(elements, null);
|
||||
return restoreElements(elements);
|
||||
};
|
||||
|
@@ -17,7 +17,7 @@ const generateRandomID = async () => {
|
||||
return Array.from(arr, byteToHex).join("");
|
||||
};
|
||||
|
||||
export const generateEncryptionKey = async () => {
|
||||
const generateEncryptionKey = async () => {
|
||||
const key = await window.crypto.subtle.generateKey(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
@@ -137,10 +137,6 @@ export const decryptAESGEM = async (
|
||||
export const getCollaborationLinkData = (link: string) => {
|
||||
const hash = new URL(link).hash;
|
||||
const match = hash.match(/^#room=([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+)$/);
|
||||
if (match && match[2].length !== 22) {
|
||||
window.alert(t("alerts.invalidEncryptionKey"));
|
||||
return null;
|
||||
}
|
||||
return match ? { roomId: match[1], roomKey: match[2] } : null;
|
||||
};
|
||||
|
||||
@@ -180,7 +176,7 @@ export const getImportedKey = (key: string, usage: KeyUsage) =>
|
||||
[usage],
|
||||
);
|
||||
|
||||
export const decryptImported = async (
|
||||
const decryptImported = async (
|
||||
iv: ArrayBuffer,
|
||||
encrypted: ArrayBuffer,
|
||||
privateKey: string,
|
||||
@@ -261,10 +257,9 @@ export const loadScene = async (
|
||||
data = restore(
|
||||
await importFromBackend(id, privateKey),
|
||||
localDataState?.appState,
|
||||
localDataState?.elements,
|
||||
);
|
||||
} else {
|
||||
data = restore(localDataState || null, null, null);
|
||||
data = restore(localDataState || null, null);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@@ -2,18 +2,12 @@
|
||||
.layer-ui__wrapper__footer-center {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.encrypted-icon {
|
||||
border-radius: var(--space-factor);
|
||||
color: var(--icon-green-fill-color);
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: 0.6em;
|
||||
margin-top: 13px;
|
||||
|
||||
svg {
|
||||
width: 1.2rem;
|
||||
|
@@ -8,6 +8,7 @@ import React, {
|
||||
} from "react";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
import { ExcalidrawImperativeAPI } from "../components/App";
|
||||
import { ErrorDialog } from "../components/ErrorDialog";
|
||||
import { TopErrorBoundary } from "../components/TopErrorBoundary";
|
||||
import {
|
||||
@@ -30,7 +31,7 @@ import Excalidraw, {
|
||||
defaultLang,
|
||||
languages,
|
||||
} from "../packages/excalidraw/index";
|
||||
import { AppState, LibraryItems, ExcalidrawImperativeAPI } from "../types";
|
||||
import { AppState, LibraryItems } from "../types";
|
||||
import {
|
||||
debounce,
|
||||
getVersion,
|
||||
@@ -55,7 +56,6 @@ import { Tooltip } from "../components/Tooltip";
|
||||
import { shield } from "../components/icons";
|
||||
|
||||
import "./index.scss";
|
||||
import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
|
||||
|
||||
const languageDetector = new LanguageDetector();
|
||||
languageDetector.init({
|
||||
@@ -141,7 +141,7 @@ const initializeScene = async (opts: {
|
||||
const url = externalUrlMatch[1];
|
||||
try {
|
||||
const request = await fetch(window.decodeURIComponent(url));
|
||||
const data = await loadFromBlob(await request.blob(), null, null);
|
||||
const data = await loadFromBlob(await request.blob(), null);
|
||||
if (
|
||||
!scene.elements.length ||
|
||||
window.confirm(t("alerts.loadSceneOverridePrompt"))
|
||||
@@ -348,8 +348,11 @@ const ExcalidrawWrapper = () => {
|
||||
|
||||
const renderLanguageList = () => (
|
||||
<LanguageList
|
||||
onChange={(langCode) => setLangCode(langCode)}
|
||||
onChange={(langCode) => {
|
||||
setLangCode(langCode);
|
||||
}}
|
||||
languages={languages}
|
||||
floating={!isMobile}
|
||||
currentLangCode={langCode}
|
||||
/>
|
||||
);
|
||||
@@ -425,21 +428,6 @@ const ExcalidrawWrapper = () => {
|
||||
canvasActions: {
|
||||
export: {
|
||||
onExportToBackend,
|
||||
renderCustomUI: (elements, appState) => {
|
||||
return (
|
||||
<ExportToExcalidrawPlus
|
||||
elements={elements}
|
||||
appState={appState}
|
||||
onError={(error) => {
|
||||
excalidrawAPI?.updateScene({
|
||||
appState: {
|
||||
errorMessage: error.message,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -450,7 +438,6 @@ const ExcalidrawWrapper = () => {
|
||||
detectScroll={false}
|
||||
handleKeyboardGlobally={true}
|
||||
onLibraryChange={onLibraryChange}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{excalidrawAPI && <CollabWrapper excalidrawAPI={excalidrawAPI} />}
|
||||
{errorMessage && (
|
||||
|
@@ -49,7 +49,6 @@ const allLanguages: Language[] = [
|
||||
{ code: "zh-TW", label: "繁體中文" },
|
||||
{ code: "lv-LV", label: "Latviešu" },
|
||||
{ code: "cs-CZ", label: "Česky" },
|
||||
{ code: "kk-KZ", label: "Қазақ тілі" },
|
||||
].concat([defaultLang]);
|
||||
|
||||
export const languages: Language[] = allLanguages
|
||||
|
@@ -69,6 +69,7 @@ const canvas = exportToCanvas(
|
||||
{
|
||||
exportBackground: true,
|
||||
viewBackgroundColor: "#ffffff",
|
||||
scale: 1,
|
||||
},
|
||||
createCanvas,
|
||||
);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
"pasteCharts": "لصق الرسوم البيانية",
|
||||
"selectAll": "تحديد الكل",
|
||||
"multiSelect": "إضافة عنصر للتحديد",
|
||||
"moveCanvas": "نقل لوح الرسم",
|
||||
"moveCanvas": "نقل لوح رسم",
|
||||
"cut": "قص",
|
||||
"copy": "نسخ",
|
||||
"copyAsPng": "نسخ إلى الحافظة بصيغة PNG",
|
||||
@@ -14,18 +14,18 @@
|
||||
"bringToFront": "أحضر للأمام",
|
||||
"sendBackward": "أرسل للخلف",
|
||||
"delete": "حذف",
|
||||
"copyStyles": "نسخ الأنماط",
|
||||
"pasteStyles": "لصق الأنماط",
|
||||
"stroke": "الخط",
|
||||
"copyStyles": "نسخ النمط",
|
||||
"pasteStyles": "لصق النمط",
|
||||
"stroke": "الحدود",
|
||||
"background": "الخلفية",
|
||||
"fill": "التعبئة",
|
||||
"strokeWidth": "سُمك الخط",
|
||||
"strokeShape": "شكل الخط",
|
||||
"strokeShape_gel": "قلم جل",
|
||||
"strokeShape_fountain": "قلم رش",
|
||||
"strokeShape_brush": "فرشاه",
|
||||
"strokeStyle": "نمط الخط",
|
||||
"strokeStyle_solid": "كامل",
|
||||
"strokeWidth": "حجم الحدود",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "نمط الحدود",
|
||||
"strokeStyle_solid": "صلبة",
|
||||
"strokeStyle_dashed": "متقطع",
|
||||
"strokeStyle_dotted": "منقط",
|
||||
"sloppiness": "الإمالة",
|
||||
@@ -43,12 +43,12 @@
|
||||
"fontFamily": "نوع الخط",
|
||||
"onlySelected": "المحدد فقط",
|
||||
"withBackground": "الخلفية",
|
||||
"exportEmbedScene": "تضمين المشهد",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "سيتم حفظ بيانات المشهد في ملف PNG/SVG المصدّر بحيث يمكن استعادة المشهد منه.\nسيزيد حجم الملف المصدر.",
|
||||
"addWatermark": "إضافة \"مصنوعة بواسطة Excalidraw\"",
|
||||
"handDrawn": "رسم باليد",
|
||||
"normal": "عادي",
|
||||
"code": "رمز",
|
||||
"code": "الرمز",
|
||||
"small": "صغير",
|
||||
"medium": "متوسط",
|
||||
"large": "كبير",
|
||||
@@ -66,7 +66,7 @@
|
||||
"artist": "رسام",
|
||||
"cartoonist": "كرتوني",
|
||||
"fileTitle": "إسم الملف",
|
||||
"colorPicker": "منتقي اللون",
|
||||
"colorPicker": "اختيار الألوان",
|
||||
"canvasBackground": "خلفية اللوحة",
|
||||
"drawingCanvas": "لوحة الرسم",
|
||||
"layers": "الطبقات",
|
||||
@@ -99,10 +99,8 @@
|
||||
"flipHorizontal": "قلب عامودي",
|
||||
"flipVertical": "قلب أفقي",
|
||||
"viewMode": "نمط العرض",
|
||||
"toggleExportColorScheme": "تبديل نظام ألوان الصادرات",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "مشاركة",
|
||||
"showStroke": "إظهار منتقي لون الخط",
|
||||
"showBackground": "إظهار منتقي لون الخلفية",
|
||||
"toggleTheme": "غير النمط"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -151,14 +149,13 @@
|
||||
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
|
||||
"collabStopOverridePrompt": "إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟\n\n(إذا كنت ترغب في الاحتفاظ برسمك المخزن داخليا، ببساطة أغلق علامة تبويب المتصفح بدلاً من ذلك.)",
|
||||
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
|
||||
"errorAddingToLibrary": "تعذر إضافة العنصر للمكتبة",
|
||||
"errorRemovingFromLibrary": "تعذر إزالة العنصر من المكتبة",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
|
||||
"imageDoesNotContainScene": "استيراد الصور غير مدعوم في الوقت الراهن.\n\nهل تريد استيراد مشهد؟ لا يبدو أن هذه الصورة تحتوي على أي بيانات مشهد. هل قمت بسماح هذا أثناء التصدير؟",
|
||||
"cannotRestoreFromImage": "تعذر استعادة المشهد من ملف الصورة",
|
||||
"invalidSceneUrl": "تعذر استيراد المشهد من عنوان URL المتوفر. إما أنها مشوهة، أو لا تحتوي على بيانات Excalidraw JSON صالحة.",
|
||||
"resetLibrary": "هذا سوف يمسح مكتبتك. هل أنت متأكد؟",
|
||||
"invalidEncryptionKey": "مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل."
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "تحديد",
|
||||
@@ -167,7 +164,7 @@
|
||||
"ellipse": "دائرة",
|
||||
"arrow": "سهم",
|
||||
"line": "خط",
|
||||
"freedraw": "رسم",
|
||||
"freedraw": "",
|
||||
"text": "نص",
|
||||
"library": "مكتبة",
|
||||
"lock": "الحفاظ على أداة التحديد نشطة بعد الرسم"
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "انقر لبدء نقاط متعددة، اسحب لخط واحد",
|
||||
"freeDraw": "انقر واسحب، افرج عند الانتهاء",
|
||||
"text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار",
|
||||
"text_selected": "انقر نقراً مزدوجاً أو اضغط ادخال لتعديل النص",
|
||||
"text_editing": "اضغط على Esc أو (Ctrl أو Cmd) + Enter لإنهاء التعديل",
|
||||
"linearElementMulti": "انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء",
|
||||
"lockAngle": "يمكنك تقييد الزاوية بالضغط على SHIFT",
|
||||
"resize": "يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،\nاضغط على ALT لتغيير الحجم من المركز",
|
||||
@@ -217,21 +212,18 @@
|
||||
"desc_inProgressIntro": "تجري الآن المشاركة الحية.",
|
||||
"desc_shareLink": "شارك هذا الرابط مع أي شخص تريده أن يشاركك الجلسة:",
|
||||
"desc_exitSession": "إيقاف الجلسة سيؤدي إلى قطع الاتصال الخاص بك من الغرفة، ولكن ستتمكن من مواصلة العمل مع المشهد، محليا. لاحظ أن هذا لن يؤثر على الأشخاص الآخرين، و سيظلون قادرين على التعاون في إصدارهم.",
|
||||
"shareTitle": "الانضمام إلى جلسة تعاون حية على Excalidraw"
|
||||
"shareTitle": ""
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "خطأ"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "حفظ الملف للجهاز",
|
||||
"disk_details": "تصدير بيانات المشهد إلى ملف يمكنك الاستيراد منه لاحقاً.",
|
||||
"disk_details": "",
|
||||
"disk_button": "إحفظ لملف",
|
||||
"link_title": "رابط قابل للمشاركة",
|
||||
"link_details": "صدر الملف للمشاهدة فقط.",
|
||||
"link_button": "التصدير كرابط",
|
||||
"excalidrawplus_description": "حفظ المشهد إلى مساحة العمل +Excalidraw الخاصة بك.",
|
||||
"excalidrawplus_button": "تصدير",
|
||||
"excalidrawplus_exportError": "تعذر التصدير إلى +Excalidraw في الوقت الحالي..."
|
||||
"link_button": "التصدير كرابط"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "اقرأ مدونتنا",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "سهم مائل",
|
||||
"curvedLine": "خط مائل",
|
||||
"documentation": "دليل الاستخدام",
|
||||
"doubleClick": "انقر مرتين",
|
||||
"drag": "اسحب",
|
||||
"editor": "المحرر",
|
||||
"editSelectedShape": "تعديل الشكل المحدد (النص/السهم/الخط)",
|
||||
"github": "عثرت على مشكلة؟ إرسال",
|
||||
"howto": "اتبع التعليمات",
|
||||
"or": "أو",
|
||||
"preventBinding": "منع ارتبط السهم",
|
||||
"shapes": "أشكال",
|
||||
"shortcuts": "اختصارات لوحة المفاتيح",
|
||||
"textFinish": "إنهاء التعديل (محرر النص)",
|
||||
"textNewLine": "أضف سطر جديد (محرر نص)",
|
||||
"textFinish": "الانتهاء من التحرير (نص)",
|
||||
"textNewLine": "اضف سطر جديد (نص)",
|
||||
"title": "المساعدة",
|
||||
"view": "عرض",
|
||||
"zoomToFit": "تكبير للملائمة",
|
||||
@@ -276,59 +266,12 @@
|
||||
"width": "العرض"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "نسخت الانماط.",
|
||||
"copyStyles": "نسخ النمط.",
|
||||
"copyToClipboard": "نسخ إلى الحافظة.",
|
||||
"copyToClipboardAsPng": "تم نسخ {{exportSelection}} إلى الحافظة بصيغة PNG\n({{exportColorScheme}})",
|
||||
"copyToClipboardAsPng": "تم نسخ {{exportSelection}} إلى الحافظة بصيغةPNG\n({{exportColorScheme}})",
|
||||
"fileSaved": "تم حفظ الملف.",
|
||||
"fileSavedToFilename": "حفظ باسم {filename}",
|
||||
"canvas": "لوحة الرسم",
|
||||
"selection": "العنصر المحدد"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "أبيض",
|
||||
"f8f9fa": "رمادي 0",
|
||||
"f1f3f5": "رمادي 1",
|
||||
"fff5f5": "أحمر 0",
|
||||
"fff0f6": "وردي 0",
|
||||
"f8f0fc": "عنبي 0",
|
||||
"f3f0ff": "بنفسجي 0",
|
||||
"edf2ff": "نيلي 0",
|
||||
"e7f5ff": "أزرق 0",
|
||||
"e3fafc": "سماوي 0",
|
||||
"e6fcf5": "تركواز 0",
|
||||
"ebfbee": "أخضر 0",
|
||||
"f4fce3": "ليموني 0",
|
||||
"fff9db": "أصفر 0",
|
||||
"fff4e6": "برتقالي 0",
|
||||
"transparent": "شفاف",
|
||||
"ced4da": "رمادي 4",
|
||||
"868e96": "رمادي 6",
|
||||
"fa5252": "أحمر 6",
|
||||
"e64980": "وردي 6",
|
||||
"be4bdb": "عنبي 6",
|
||||
"7950f2": "بنفسجي 6",
|
||||
"4c6ef5": "نيلي 6",
|
||||
"228be6": "أزرق 6",
|
||||
"15aabf": "سماوي 6",
|
||||
"12b886": "تركواز 6",
|
||||
"40c057": "أخضر 6",
|
||||
"82c91e": "ليموني 6",
|
||||
"fab005": "أصفر 6",
|
||||
"fd7e14": "برتقالي 6",
|
||||
"000000": "أسود",
|
||||
"343a40": "رمادي 8",
|
||||
"495057": "رمادي 7",
|
||||
"c92a2a": "أحمر 9",
|
||||
"a61e4d": "وردي 9",
|
||||
"862e9c": "عنبي 9",
|
||||
"5f3dc4": "بنفسجي 9",
|
||||
"364fc7": "نيلي 9",
|
||||
"1864ab": "أزرق 9",
|
||||
"0b7285": "سماوي 9",
|
||||
"087f5b": "تركواز 9",
|
||||
"2b8a3e": "أخضر 9",
|
||||
"5c940d": "ليموني 9",
|
||||
"e67700": "أصفر 9",
|
||||
"d9480f": "برتقالي 9"
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Изглед",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "Импортирането на картинки не се поддържва в момента.\n\nИскате да импортнете сцена? Тази картинка не съдържа данни от сцена. Разрешили ли сте последното при експортирането?",
|
||||
"cannotRestoreFromImage": "Не може да бъде възстановена сцена от този файл",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Селекция",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Кликнете, за да стартирате няколко точки, плъзнете за една линия",
|
||||
"freeDraw": "Натиснете и влачете, пуснете като сте готови",
|
||||
"text": "Подсказка: Можете също да добавите текст като натиснете някъде два път с инструмента за селекция",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "Кликнете върху последната точка или натиснете Escape или Enter, за да завършите",
|
||||
"lockAngle": "Можете да ограничите ъгъла, като задържите SHIFT",
|
||||
"resize": "Може да ограничите при преоразмеряване като задържите SHIFT,\nзадръжте ALT за преоразмерите през центъра",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Прочетете нашия блог",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Извита стрелка",
|
||||
"curvedLine": "Извита линия",
|
||||
"documentation": "Документация",
|
||||
"doubleClick": "",
|
||||
"drag": "плъзнете",
|
||||
"editor": "Редактор",
|
||||
"editSelectedShape": "",
|
||||
"github": "Намерихте проблем? Изпратете",
|
||||
"howto": "Следвайте нашите ръководства",
|
||||
"or": "или",
|
||||
"preventBinding": "Спри прилепяне на стрелките",
|
||||
"shapes": "Фигури",
|
||||
"shortcuts": "Клавиши за бърз достъп",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"textFinish": "Завършете редактирането (текст)",
|
||||
"textNewLine": "Добавяне на нов ред (текст)",
|
||||
"title": "Помощ",
|
||||
"view": "Преглед",
|
||||
"zoomToFit": "Приближи докато се виждат всички елементи",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Enganxa",
|
||||
"pasteCharts": "Enganxa els diagrames",
|
||||
"selectAll": "Selecciona-ho tot",
|
||||
"multiSelect": "Afegeix un element a la selecció",
|
||||
"moveCanvas": "Mou el llenç",
|
||||
"cut": "Retalla",
|
||||
"copy": "Copia",
|
||||
"copyAsPng": "Copia al porta-retalls com a PNG",
|
||||
"copyAsSvg": "Copia al porta-retalls com a SVG",
|
||||
"bringForward": "Porta endavant",
|
||||
"sendToBack": "Envia enrere",
|
||||
"bringToFront": "Porta al davant",
|
||||
"sendBackward": "Envia al fons",
|
||||
"delete": "Elimina",
|
||||
"copyStyles": "Copia els estils",
|
||||
"pasteStyles": "Enganxa els estils",
|
||||
"paste": "Enganxar",
|
||||
"pasteCharts": "Enganxar diagrames",
|
||||
"selectAll": "Seleccionar tot",
|
||||
"multiSelect": "Afegir element a la selecció",
|
||||
"moveCanvas": "Moure el llenç",
|
||||
"cut": "Tallar",
|
||||
"copy": "Copiar",
|
||||
"copyAsPng": "Copiar al porta-retalls com a PNG",
|
||||
"copyAsSvg": "Copiar al porta-retalls com a SVG",
|
||||
"bringForward": "Portar endavant",
|
||||
"sendToBack": "Enviar endarrere",
|
||||
"bringToFront": "Portar al capdavant",
|
||||
"sendBackward": "Enviar al fons",
|
||||
"delete": "Eliminar",
|
||||
"copyStyles": "Copiar estils",
|
||||
"pasteStyles": "Enganxar estils",
|
||||
"stroke": "Color del traç",
|
||||
"background": "Color del fons",
|
||||
"fill": "Estil del fons",
|
||||
"strokeWidth": "Amplada del traç",
|
||||
"strokeShape": "Estil del traç",
|
||||
"strokeShape_gel": "Bolígraf de gel",
|
||||
"strokeShape_fountain": "Bolígraf de font",
|
||||
"strokeShape_brush": "Bolígraf de raspall",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "Estil del traç",
|
||||
"strokeStyle_solid": "Sòlid",
|
||||
"strokeStyle_dashed": "Guions",
|
||||
@@ -42,10 +42,10 @@
|
||||
"fontSize": "Mida de lletra",
|
||||
"fontFamily": "Tipus de lletra",
|
||||
"onlySelected": "Només seleccionats",
|
||||
"withBackground": "Fons",
|
||||
"exportEmbedScene": "Insereix l'escena",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "Les dades de l’escena es desaran al fitxer PNG/SVG de manera que es pugui restaurar l’escena.\nAugmentarà la mida del fitxer exportat.",
|
||||
"addWatermark": "Afegeix-hi «Fet amb Excalidraw»",
|
||||
"addWatermark": "Afegir \"Fet amb Excalidraw\"",
|
||||
"handDrawn": "Dibuixat a mà",
|
||||
"normal": "Normal",
|
||||
"code": "Codi",
|
||||
@@ -73,75 +73,73 @@
|
||||
"actions": "Accions",
|
||||
"language": "Llengua",
|
||||
"liveCollaboration": "Col·laboració en directe",
|
||||
"duplicateSelection": "Duplica",
|
||||
"duplicateSelection": "Duplicar",
|
||||
"untitled": "Sense títol",
|
||||
"name": "Nom",
|
||||
"yourName": "El vostre nom",
|
||||
"yourName": "El teu nom",
|
||||
"madeWithExcalidraw": "Fet amb Excalidraw",
|
||||
"group": "Agrupa la selecció",
|
||||
"ungroup": "Desagrupa la selecció",
|
||||
"group": "Agrupar la selecció",
|
||||
"ungroup": "Desagrupar la selecció",
|
||||
"collaborators": "Col·laboradors",
|
||||
"showGrid": "Mostra la graella",
|
||||
"addToLibrary": "Afegir a la biblioteca",
|
||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||
"libraryLoadingMessage": "S'està carregant la biblioteca…",
|
||||
"libraries": "Explora les biblioteques",
|
||||
"loadingScene": "S'està carregant l'escena…",
|
||||
"align": "Alinea",
|
||||
"alignTop": "Alinea a la part superior",
|
||||
"alignBottom": "Alinea a la part inferior",
|
||||
"alignLeft": "Alinea a l’esquerra",
|
||||
"alignRight": "Alinea a la dreta",
|
||||
"centerVertically": "Centra verticalment",
|
||||
"centerHorizontally": "Centra horitzontalment",
|
||||
"distributeHorizontally": "Distribueix horitzontalment",
|
||||
"distributeVertically": "Distribueix verticalment",
|
||||
"libraryLoadingMessage": "Carregant la biblioteca…",
|
||||
"libraries": "Explorar biblioteques",
|
||||
"loadingScene": "Carregant escena…",
|
||||
"align": "Alinear",
|
||||
"alignTop": "Alinear a dalt",
|
||||
"alignBottom": "Alinear a baix",
|
||||
"alignLeft": "Alinear a l’esquerra",
|
||||
"alignRight": "Alinear a la dreta",
|
||||
"centerVertically": "Centrar verticalment",
|
||||
"centerHorizontally": "Centrar horitzontalment",
|
||||
"distributeHorizontally": "Distribuir horitzontalment",
|
||||
"distributeVertically": "Distribuir verticalment",
|
||||
"flipHorizontal": "Capgira horitzontalment",
|
||||
"flipVertical": "Capgira verticalment",
|
||||
"viewMode": "Mode de visualització",
|
||||
"toggleExportColorScheme": "Canvia l'esquema de colors de l'exportació",
|
||||
"share": "Comparteix",
|
||||
"showStroke": "Mostra el selector de color del traç",
|
||||
"showBackground": "Mostra el selector de color de fons",
|
||||
"toggleTheme": "Activa o desactiva el tema"
|
||||
"share": "Compartir",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Neteja el llenç",
|
||||
"exportJSON": "Exporta a un fitxer",
|
||||
"exportImage": "Desa com a imatge",
|
||||
"export": "Exporta",
|
||||
"exportToPng": "Exporta a PNG",
|
||||
"exportToSvg": "Exporta a SNG",
|
||||
"copyToClipboard": "Copia al porta-retalls",
|
||||
"copyPngToClipboard": "Copia el PNG al porta-retalls",
|
||||
"clearReset": "Netejar el llenç",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "Exportar",
|
||||
"exportToPng": "Exportar a PNG",
|
||||
"exportToSvg": "Exportar a SNG",
|
||||
"copyToClipboard": "Copiar al porta-retalls",
|
||||
"copyPngToClipboard": "Copiar PNG al porta-retalls",
|
||||
"scale": "Escala",
|
||||
"save": "Desa al fitxer actual",
|
||||
"saveAs": "Anomena i desa",
|
||||
"load": "Carrega",
|
||||
"getShareableLink": "Obté l'enllaç per a compartir",
|
||||
"close": "Tanca",
|
||||
"selectLanguage": "Trieu la llengua",
|
||||
"scrollBackToContent": "Torna al contingut",
|
||||
"zoomIn": "Apropa't",
|
||||
"zoomOut": "Allunya't",
|
||||
"resetZoom": "Restableix el zoom",
|
||||
"save": "",
|
||||
"saveAs": "Desar com",
|
||||
"load": "Carregar",
|
||||
"getShareableLink": "Obtenir enllaç per compartir",
|
||||
"close": "Tancar",
|
||||
"selectLanguage": "Triar idioma",
|
||||
"scrollBackToContent": "Tornar al contingut",
|
||||
"zoomIn": "Ampliar",
|
||||
"zoomOut": "Reduir",
|
||||
"resetZoom": "Restablir zoom",
|
||||
"menu": "Menú",
|
||||
"done": "Fet",
|
||||
"edit": "Edita",
|
||||
"undo": "Desfés",
|
||||
"redo": "Refés",
|
||||
"resetLibrary": "Restableix la biblioteca",
|
||||
"createNewRoom": "Crea una sala nova",
|
||||
"edit": "Editar",
|
||||
"undo": "Desfer",
|
||||
"redo": "Refer",
|
||||
"resetLibrary": "Restablir biblioteca",
|
||||
"createNewRoom": "Crear sala nova",
|
||||
"fullScreen": "Pantalla completa",
|
||||
"darkMode": "Mode fosc",
|
||||
"lightMode": "Mode clar",
|
||||
"zenMode": "Mode zen",
|
||||
"exitZenMode": "Surt de mode zen"
|
||||
"zenMode": "Mode Zen",
|
||||
"exitZenMode": "Sortir de modo zen"
|
||||
},
|
||||
"alerts": {
|
||||
"clearReset": "S'esborrarà tot el llenç. N'esteu segur?",
|
||||
"couldNotCreateShareableLink": "No s'ha pogut crear un enllaç per a compartir.",
|
||||
"couldNotCreateShareableLinkTooBig": "No s’ha pogut crear un enllaç per a compartir: l’escena és massa gran",
|
||||
"clearReset": "Tot el llenç s'esborrarà. Estàs segur?",
|
||||
"couldNotCreateShareableLink": "No s'ha pogut crear un enllaç per compartir.",
|
||||
"couldNotCreateShareableLinkTooBig": "No s’ha pogut crear un enllaç per compartir: l’escena és massa gran",
|
||||
"couldNotLoadInvalidFile": "No s'ha pogut carregar un fitxer no vàlid",
|
||||
"importBackendFailed": "Importació fallida.",
|
||||
"cannotExportEmptyCanvas": "No es pot exportar un llenç buit.",
|
||||
@@ -151,14 +149,13 @@
|
||||
"loadSceneOverridePrompt": "Si carregas aquest dibuix extern, substituirá el que tens. Vols continuar?",
|
||||
"collabStopOverridePrompt": "Aturar la sessió provocarà la sobreescriptura del dibuix previ, que hi ha desat en l'emmagatzematge local. N'esteu segur?\n\n(Si voleu conservar el dibuix local, tanqueu la pentanya del navegador en comptes d'aturar la sessió).",
|
||||
"errorLoadingLibrary": "S'ha produït un error en carregar la biblioteca de tercers.",
|
||||
"errorAddingToLibrary": "No s'ha pogut afegir l'element a la biblioteca",
|
||||
"errorRemovingFromLibrary": "No s'ha pogut eliminar l'element de la biblioteca",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"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ó?",
|
||||
"cannotRestoreFromImage": "L’escena no s’ha pogut restaurar des d’aquest fitxer d’imatge",
|
||||
"invalidSceneUrl": "No s'ha pogut importar l'escena des de l'adreça URL proporcionada. Està malformada o no conté dades Excalidraw JSON vàlides.",
|
||||
"resetLibrary": "Això buidarà la biblioteca. N'esteu segur?",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": "Tot el llenç s'esborrarà. Estàs segur?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selecció",
|
||||
@@ -178,60 +175,55 @@
|
||||
"shapes": "Formes"
|
||||
},
|
||||
"hints": {
|
||||
"linearElement": "Feu clic per a dibuixar múltiples punts; arrossegueu per a una sola línia",
|
||||
"freeDraw": "Feu clic i arrossegueu, deixeu anar per a finalitzar",
|
||||
"text": "Consell: també podeu afegir text fent doble clic en qualsevol lloc amb l'eina de selecció",
|
||||
"text_selected": "Feu doble clic o premeu Retorn per a editar el text",
|
||||
"text_editing": "Premeu Escapada o Ctrl+Retorn (o Ordre+Retorn) per a finalitzar l'edició",
|
||||
"linearElementMulti": "Feu clic a l'ultim punt, o pitgeu Esc o Retorn per a finalitzar",
|
||||
"linearElement": "Fer clic per dibuixar múltiples punts; arrossegar per una sola línea",
|
||||
"freeDraw": "Fer clic i arrosegar, deixar anar al punt final",
|
||||
"text": "Consell: també pots afegir text fent doble clic a qualsevol lloc amb l'eina de selecció",
|
||||
"linearElementMulti": "Fer clic a l'ultim punt, o polsar Escape o Enter per acabar",
|
||||
"lockAngle": "Per restringir els angles, mantenir premut el majúscul (SHIFT)",
|
||||
"resize": "Per restringir les proporcions mentres es canvia la mida, mantenir premut el majúscul (SHIFT); per canviar la mida des del centre, mantenir premut ALT",
|
||||
"rotate": "Per restringir els angles mentre gira, mantenir premut el majúscul (SHIFT)",
|
||||
"lineEditor_info": "Fes doble clic o premi Enter per editar punts",
|
||||
"lineEditor_pointSelected": "Premeu Suprimir per a eliminar el punt, CtrlOrCmd+D per a duplicar-lo, o arrossegueu-lo per a moure'l",
|
||||
"lineEditor_pointSelected": "Premi Suprimir per eliminar el punt, CtrlOrCmd+D per duplicar-lo, o arrosega'l per moure'l",
|
||||
"lineEditor_nothingSelected": "Selecciona un punt per moure o eliminar, o manté premut Alt i fes clic per afegir punts nous"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "No es pot mostrar la previsualització",
|
||||
"cannotShowPreview": "No es pot mostrar la vista prèvia",
|
||||
"canvasTooBig": "Pot ser que el llenç sigui massa gran.",
|
||||
"canvasTooBigTip": "Consell: proveu d’acostar una mica els elements més allunyats."
|
||||
"canvasTooBigTip": "Consell: prova d’acostar una mica els elements més allunyats."
|
||||
},
|
||||
"errorSplash": {
|
||||
"headingMain_pre": "S'ha produït un error. Proveu ",
|
||||
"headingMain_pre": "S'ha produït un error. Intentar ",
|
||||
"headingMain_button": "recarregar la pàgina.",
|
||||
"clearCanvasMessage": "Si la recàrrega no funciona, proveu ",
|
||||
"clearCanvasMessage": "Si la recarrega no funciona, intentar ",
|
||||
"clearCanvasMessage_button": "esborrar el llenç.",
|
||||
"clearCanvasCaveat": " Això resultarà en la pèrdua de feina ",
|
||||
"clearCanvasCaveat": " Això resultarà en pèrdua de feina ",
|
||||
"trackedToSentry_pre": "L'error amb l'identificador ",
|
||||
"trackedToSentry_post": " s'ha rastrejat en el nostre sistema.",
|
||||
"openIssueMessage_pre": "Anàvem amb molta cura de no incloure la informació de la vostra escena en l'error. Si l'escena no és privada, podeu fer-ne el seguiment al nostre ",
|
||||
"openIssueMessage_pre": "Estàvem molt amb compte de no incloure la teva informació de l'escena en l'error. Si la teva escena no és privada, pots fer el seguiment al nostre ",
|
||||
"openIssueMessage_button": "rastrejador d'errors.",
|
||||
"openIssueMessage_post": " Incloeu la informació a continuació copiant i enganxant a GitHub Issues.",
|
||||
"openIssueMessage_post": " Si us plau incloure la informació a continuació copiant i enganxant a GitHub Issues.",
|
||||
"sceneContent": "Contingut de l'escena:"
|
||||
},
|
||||
"roomDialog": {
|
||||
"desc_intro": "Podeu convidar persones a la vostra escena actual a col·laborar amb vós.",
|
||||
"desc_privacy": "No us preocupeu, la sessió utilitza el xifratge de punta a punta, de manera que qualsevol cosa que dibuixeu romandrà privada. Ni tan sols el nostre servidor podrà veure què feu.",
|
||||
"button_startSession": "Inicia la sessió",
|
||||
"button_stopSession": "Atura la sessió",
|
||||
"desc_intro": "Pots convidar persones a la teva escena actual a col·laborar amb tu.",
|
||||
"desc_privacy": "No et preocupis, la sessió utilitza el xifratge de punta a punta, de manera que qualsevol cosa que dibuixis quedarà privada. Ni tan sols el nostre servidor podrà veure el que fas.",
|
||||
"button_startSession": "Iniciar sessió",
|
||||
"button_stopSession": "Aturar sessió",
|
||||
"desc_inProgressIntro": "La sessió de col·laboració en directe està en marxa.",
|
||||
"desc_shareLink": "Comparteix aquest enllaç amb qualsevol persona amb qui vulgueu col·laborar:",
|
||||
"desc_exitSession": "Si atureu la sessió, us desconectareu de la sala, però podreu continuar treballant amb el dibuix localment. Tingues en compte que això no afectarà a altres persones, i encara podran col·laborar en la seva versió.",
|
||||
"desc_shareLink": "Comparteix aquest enllaç amb qualsevol persona amb qui vulguis col·laborar:",
|
||||
"desc_exitSession": "Si aturas la sessió, et desconectarás de la sala, però podrás continuar treballant amb el dibuix localment. Tingues en compte que això no afectarà a altres persones, i encara podran col·laborar en la seva versió.",
|
||||
"shareTitle": "Uniu-vos a una sessió de col·laboració en directe a Excalidraw"
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Error"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Desa al disc",
|
||||
"disk_details": "Exporta les dades de l'escena a un fitxer que després podreu importar.",
|
||||
"disk_button": "Desa en un fitxer",
|
||||
"link_title": "Enllaç per a compartir",
|
||||
"link_details": "Exporta com a un enllaç de només lectura.",
|
||||
"link_button": "Exporta a un enllaç",
|
||||
"excalidrawplus_description": "Desa l'escena en el vostre espai de treball Excalidraw+.",
|
||||
"excalidrawplus_button": "Exporta",
|
||||
"excalidrawplus_exportError": "No és possible exportar a Excalidraw+ ara mateix..."
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Llegiu el nostre blog",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Fletxa corba",
|
||||
"curvedLine": "Línia corba",
|
||||
"documentation": "Documentació",
|
||||
"doubleClick": "doble clic",
|
||||
"drag": "arrossega",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Edita la forma seleccionada (text, fletxa o línia)",
|
||||
"github": "Hi heu trobat un problema? Informeu-ne",
|
||||
"howto": "Seguiu les nostres guies",
|
||||
"or": "o",
|
||||
"preventBinding": "Prevenir vinculació de la fletxa",
|
||||
"shapes": "Formes",
|
||||
"shortcuts": "Dreceres de teclat",
|
||||
"textFinish": "Finalitza l'edició (editor de text)",
|
||||
"textNewLine": "Afegeix una línia nova (editor de text)",
|
||||
"textFinish": "Acaba d'editar (text)",
|
||||
"textNewLine": "Afegeix línea nova (text)",
|
||||
"title": "Ajuda",
|
||||
"view": "Visualització",
|
||||
"zoomToFit": "Zoom per veure tots els elements",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "S'ha desat a {filename}",
|
||||
"canvas": "el llenç",
|
||||
"selection": "la selecció"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Náhled",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "Sdílet",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": "Přepnout tmavý řežim"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "",
|
||||
"cannotRestoreFromImage": "",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Výběr",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "",
|
||||
"freeDraw": "",
|
||||
"text": "",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "",
|
||||
"lockAngle": "",
|
||||
"resize": "",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "",
|
||||
@@ -239,10 +231,8 @@
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"doubleClick": "",
|
||||
"drag": "tažení",
|
||||
"editor": "",
|
||||
"editSelectedShape": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "nebo",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "plátno",
|
||||
"selection": "výběr"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -1,334 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Indsæt",
|
||||
"pasteCharts": "",
|
||||
"selectAll": "Marker alle",
|
||||
"multiSelect": "",
|
||||
"moveCanvas": "",
|
||||
"cut": "",
|
||||
"copy": "Kopier",
|
||||
"copyAsPng": "Kopier til klippebord som PNG",
|
||||
"copyAsSvg": "Kopier til klippebord som SVG",
|
||||
"bringForward": "",
|
||||
"sendToBack": "",
|
||||
"bringToFront": "",
|
||||
"sendBackward": "",
|
||||
"delete": "Fjern",
|
||||
"copyStyles": "",
|
||||
"pasteStyles": "",
|
||||
"stroke": "Linje",
|
||||
"background": "Baggrund",
|
||||
"fill": "",
|
||||
"strokeWidth": "Linjebredde",
|
||||
"strokeShape": "Linjeform",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "",
|
||||
"strokeStyle_solid": "",
|
||||
"strokeStyle_dashed": "",
|
||||
"strokeStyle_dotted": "",
|
||||
"sloppiness": "",
|
||||
"opacity": "",
|
||||
"textAlign": "",
|
||||
"edges": "",
|
||||
"sharp": "",
|
||||
"round": "",
|
||||
"arrowheads": "",
|
||||
"arrowhead_none": "",
|
||||
"arrowhead_arrow": "Pil",
|
||||
"arrowhead_bar": "",
|
||||
"arrowhead_dot": "",
|
||||
"fontSize": "",
|
||||
"fontFamily": "",
|
||||
"onlySelected": "",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "",
|
||||
"addWatermark": "",
|
||||
"handDrawn": "",
|
||||
"normal": "",
|
||||
"code": "",
|
||||
"small": "",
|
||||
"medium": "",
|
||||
"large": "",
|
||||
"veryLarge": "",
|
||||
"solid": "",
|
||||
"hachure": "",
|
||||
"crossHatch": "",
|
||||
"thin": "",
|
||||
"bold": "Fed",
|
||||
"left": "Venstre",
|
||||
"center": "Centrere",
|
||||
"right": "Højre",
|
||||
"extraBold": "Extra fed",
|
||||
"architect": "",
|
||||
"artist": "",
|
||||
"cartoonist": "",
|
||||
"fileTitle": "Filnavn",
|
||||
"colorPicker": "Farvevælger",
|
||||
"canvasBackground": "",
|
||||
"drawingCanvas": "",
|
||||
"layers": "",
|
||||
"actions": "",
|
||||
"language": "Sprog",
|
||||
"liveCollaboration": "Direkte samarbejde",
|
||||
"duplicateSelection": "",
|
||||
"untitled": "",
|
||||
"name": "",
|
||||
"yourName": "Dit navn",
|
||||
"madeWithExcalidraw": "Fremstillet med Excalidraw",
|
||||
"group": "",
|
||||
"ungroup": "",
|
||||
"collaborators": "",
|
||||
"showGrid": "",
|
||||
"addToLibrary": "",
|
||||
"removeFromLibrary": "",
|
||||
"libraryLoadingMessage": "",
|
||||
"libraries": "",
|
||||
"loadingScene": "",
|
||||
"align": "",
|
||||
"alignTop": "",
|
||||
"alignBottom": "",
|
||||
"alignLeft": "",
|
||||
"alignRight": "",
|
||||
"centerVertically": "",
|
||||
"centerHorizontally": "",
|
||||
"distributeHorizontally": "",
|
||||
"distributeVertically": "",
|
||||
"flipHorizontal": "",
|
||||
"flipVertical": "",
|
||||
"viewMode": "",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "Del",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "",
|
||||
"exportToPng": "",
|
||||
"exportToSvg": "",
|
||||
"copyToClipboard": "Kopier til klippebord",
|
||||
"copyPngToClipboard": "Kopier PNG til klippebord",
|
||||
"scale": "",
|
||||
"save": "",
|
||||
"saveAs": "",
|
||||
"load": "",
|
||||
"getShareableLink": "",
|
||||
"close": "",
|
||||
"selectLanguage": "Vælg sprog",
|
||||
"scrollBackToContent": "Scroll tilbage til indhold",
|
||||
"zoomIn": "Zoom ind",
|
||||
"zoomOut": "Zoom ud",
|
||||
"resetZoom": "Nulstil zoom",
|
||||
"menu": "Menu",
|
||||
"done": "Færdig",
|
||||
"edit": "Rediger",
|
||||
"undo": "Fortryd",
|
||||
"redo": "Gendan",
|
||||
"resetLibrary": "",
|
||||
"createNewRoom": "Opret nyt rum",
|
||||
"fullScreen": "Fuld skærm",
|
||||
"darkMode": "Mørk tilstand",
|
||||
"lightMode": "Lys baggrund",
|
||||
"zenMode": "",
|
||||
"exitZenMode": ""
|
||||
},
|
||||
"alerts": {
|
||||
"clearReset": "",
|
||||
"couldNotCreateShareableLink": "",
|
||||
"couldNotCreateShareableLinkTooBig": "",
|
||||
"couldNotLoadInvalidFile": "",
|
||||
"importBackendFailed": "",
|
||||
"cannotExportEmptyCanvas": "",
|
||||
"couldNotCopyToClipboard": "Kunne ikke kopiere til klippebord. Prøv at bruge Chrome browser.",
|
||||
"decryptFailed": "",
|
||||
"uploadedSecurly": "",
|
||||
"loadSceneOverridePrompt": "",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"confirmAddLibrary": "",
|
||||
"imageDoesNotContainScene": "",
|
||||
"cannotRestoreFromImage": "",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "",
|
||||
"rectangle": "",
|
||||
"diamond": "",
|
||||
"ellipse": "",
|
||||
"arrow": "",
|
||||
"line": "",
|
||||
"freedraw": "",
|
||||
"text": "",
|
||||
"library": "",
|
||||
"lock": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "",
|
||||
"selectedShapeActions": "",
|
||||
"shapes": ""
|
||||
},
|
||||
"hints": {
|
||||
"linearElement": "",
|
||||
"freeDraw": "Klik og træk, slip når du er færdig",
|
||||
"text": "",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "",
|
||||
"lockAngle": "",
|
||||
"resize": "",
|
||||
"rotate": "",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
"canvasTooBig": "",
|
||||
"canvasTooBigTip": ""
|
||||
},
|
||||
"errorSplash": {
|
||||
"headingMain_pre": "",
|
||||
"headingMain_button": "",
|
||||
"clearCanvasMessage": "",
|
||||
"clearCanvasMessage_button": "",
|
||||
"clearCanvasCaveat": "",
|
||||
"trackedToSentry_pre": "",
|
||||
"trackedToSentry_post": "",
|
||||
"openIssueMessage_pre": "",
|
||||
"openIssueMessage_button": "",
|
||||
"openIssueMessage_post": " Kopiere og indsæt venligst oplysningerne nedenfor i et GitHub problem.",
|
||||
"sceneContent": "Scene indhold:"
|
||||
},
|
||||
"roomDialog": {
|
||||
"desc_intro": "Du kan invitere folk til din nuværende scene, så de kan samarbejde med dig.",
|
||||
"desc_privacy": "Bare rolig, sessionen bruger end-to-end kryptering, så uanset hvad du tegner vil det forblive privat. Ikke engang vores server vil kunne se, hvad du kommer op med.",
|
||||
"button_startSession": "Start session",
|
||||
"button_stopSession": "Stop session",
|
||||
"desc_inProgressIntro": "Live-samarbejde session er nu begyndt.",
|
||||
"desc_shareLink": "Del dette link med enhver, du ønsker at samarbejde med:",
|
||||
"desc_exitSession": "",
|
||||
"shareTitle": ""
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Fejl"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Gem til disk",
|
||||
"disk_details": "",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Læs vores blog",
|
||||
"click": "",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"doubleClick": "",
|
||||
"drag": "",
|
||||
"editor": "",
|
||||
"editSelectedShape": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "",
|
||||
"preventBinding": "",
|
||||
"shapes": "",
|
||||
"shortcuts": "",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"title": "",
|
||||
"view": "",
|
||||
"zoomToFit": "",
|
||||
"zoomToSelection": ""
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "",
|
||||
"link": ""
|
||||
},
|
||||
"stats": {
|
||||
"angle": "",
|
||||
"element": "",
|
||||
"elements": "",
|
||||
"height": "",
|
||||
"scene": "",
|
||||
"selected": "",
|
||||
"storage": "",
|
||||
"title": "Statistik for nørder",
|
||||
"total": "",
|
||||
"version": "",
|
||||
"versionCopy": "Klik for at kopiere",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Bredde"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Kopieret stilarter.",
|
||||
"copyToClipboard": "Kopieret til klippebord.",
|
||||
"copyToClipboardAsPng": "Kopieret {{exportSelection}} til klippebord som PNG\n({{exportColorScheme}})",
|
||||
"fileSaved": "Fil gemt.",
|
||||
"fileSavedToFilename": "Gemt som {filename}",
|
||||
"canvas": "canvas",
|
||||
"selection": "markering"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Ansichtsmodus",
|
||||
"toggleExportColorScheme": "Exportfarbschema umschalten",
|
||||
"share": "Teilen",
|
||||
"showStroke": "Auswahl für Strichfarbe anzeigen",
|
||||
"showBackground": "Hintergrundfarbe auswählen",
|
||||
"toggleTheme": "Design umschalten"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"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?",
|
||||
"cannotRestoreFromImage": "Die Zeichnung konnte aus dieser Bilddatei nicht wiederhergestellt werden",
|
||||
"invalidSceneUrl": "Die Szene konnte nicht von der angegebenen URL importiert werden. Sie ist entweder fehlerhaft oder enthält keine gültigen Excalidraw JSON-Daten.",
|
||||
"resetLibrary": "Dieses löscht deine Bibliothek. Bist du sicher?",
|
||||
"invalidEncryptionKey": "Verschlüsselungsschlüssel muss 22 Zeichen lang sein. Die Live-Zusammenarbeit ist deaktiviert."
|
||||
"resetLibrary": "Dieses löscht deine Bibliothek. Bist du sicher?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Auswahl",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Klicken für Linie mit mehreren Punkten, Ziehen für einzelne Linie",
|
||||
"freeDraw": "Klicke und ziehe. Lass los, wenn du fertig bist",
|
||||
"text": "Tipp: Du kannst auch Text hinzufügen, indem du mit dem Auswahlwerkzeug auf eine beliebige Stelle doppelklickst",
|
||||
"text_selected": "Doppelklicken oder Eingabetaste drücken, um Text zu bearbeiten",
|
||||
"text_editing": "Drücke Escape oder Strg/Cmd+Eingabetaste, um die Bearbeitung abzuschließen",
|
||||
"linearElementMulti": "Zum Beenden auf den letzten Punkt klicken oder Escape oder Eingabe drücken",
|
||||
"lockAngle": "Du kannst Winkel einschränken, indem du SHIFT gedrückt hältst",
|
||||
"resize": "Du kannst die Proportionen einschränken, indem du SHIFT während der Größenänderung gedrückt hältst. Halte ALT gedrückt, um die Größe vom Zentrum aus zu ändern",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "In Datei speichern",
|
||||
"link_title": "Teilbarer Link",
|
||||
"link_details": "Als schreibgeschützten Link exportieren.",
|
||||
"link_button": "Als Link exportieren",
|
||||
"excalidrawplus_description": "Speichere die Szene in deinem Excalidraw+ Arbeitsbereich.",
|
||||
"excalidrawplus_button": "Exportieren",
|
||||
"excalidrawplus_exportError": "Konnte nicht nach Excalidraw+ exportieren..."
|
||||
"link_button": "Als Link exportieren"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Lies unseren Blog",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Gebogener Pfeil",
|
||||
"curvedLine": "Gebogene Linie",
|
||||
"documentation": "Dokumentation",
|
||||
"doubleClick": "doppelklicken",
|
||||
"drag": "ziehen",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Ausgewählte Form bearbeiten (Text/Pfeil/Linie)",
|
||||
"github": "Ein Problem gefunden? Informiere uns",
|
||||
"howto": "Folge unseren Anleitungen",
|
||||
"or": "oder",
|
||||
"preventBinding": "Pfeil-Bindung verhindern",
|
||||
"shapes": "Formen",
|
||||
"shortcuts": "Tastaturkürzel",
|
||||
"textFinish": "Bearbeitung beenden (Texteditor)",
|
||||
"textNewLine": "Neue Zeile hinzufügen (Texteditor)",
|
||||
"textFinish": "Bearbeiten beenden (Text)",
|
||||
"textNewLine": "Neue Zeile hinzufügen (Text)",
|
||||
"title": "Hilfe",
|
||||
"view": "Ansicht",
|
||||
"zoomToFit": "Zoomen um alle Elemente einzupassen",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Als {filename} gespeichert",
|
||||
"canvas": "Zeichenfläche",
|
||||
"selection": "Auswahl"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Weiß",
|
||||
"f8f9fa": "Grau 0",
|
||||
"f1f3f5": "Grau 1",
|
||||
"fff5f5": "Rot 0",
|
||||
"fff0f6": "Pink 0",
|
||||
"f8f0fc": "Traube 0",
|
||||
"f3f0ff": "Violett 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Blau 0",
|
||||
"e3fafc": "Cyan 0",
|
||||
"e6fcf5": "Teal 0",
|
||||
"ebfbee": "Grün 0",
|
||||
"f4fce3": "Limette 0",
|
||||
"fff9db": "Gelb 0",
|
||||
"fff4e6": "Orange 0",
|
||||
"transparent": "Transparent",
|
||||
"ced4da": "Grau 4",
|
||||
"868e96": "Grau 6",
|
||||
"fa5252": "Rot 6",
|
||||
"e64980": "Pink 6",
|
||||
"be4bdb": "Traube 6",
|
||||
"7950f2": "Violett 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Blau 6",
|
||||
"15aabf": "Cyan 6",
|
||||
"12b886": "Teal 6",
|
||||
"40c057": "Grün 6",
|
||||
"82c91e": "Limette 6",
|
||||
"fab005": "Gelb 6",
|
||||
"fd7e14": "Orange 6",
|
||||
"000000": "Schwarz",
|
||||
"343a40": "Grau 8",
|
||||
"495057": "Grau 7",
|
||||
"c92a2a": "Rot 9",
|
||||
"a61e4d": "Pink 9",
|
||||
"862e9c": "Traube 9",
|
||||
"5f3dc4": "Violett 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Blau 9",
|
||||
"0b7285": "Cyan 9",
|
||||
"087f5b": "Teal 9",
|
||||
"2b8a3e": "Grün 9",
|
||||
"5c940d": "Limette 9",
|
||||
"e67700": "Gelb 9",
|
||||
"d9480f": "Orange 9"
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Λειτουργία προβολής",
|
||||
"toggleExportColorScheme": "Εναλλαγή εξαγωγής θέματος χρωμάτων",
|
||||
"share": "Κοινοποίηση",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "Η εισαγωγή εικόνων δεν υποστηρίζεται αυτή τη στιγμή.\n\nΜήπως θέλετε να εισαγάγετε μια σκηνή; Αυτή η εικόνα δεν φαίνεται να περιέχει δεδομένα σκηνής. Έχετε ενεργοποιήσει αυτό κατά την εξαγωγή;",
|
||||
"cannotRestoreFromImage": "Η σκηνή δεν ήταν δυνατό να αποκατασταθεί από αυτό το αρχείο εικόνας",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "Αυτό θα καθαρίσει τη βιβλιοθήκη σας. Είστε σίγουροι;",
|
||||
"invalidEncryptionKey": "Το κλειδί κρυπτογράφησης πρέπει να είναι 22 χαρακτήρες. Η ζωντανή συνεργασία είναι απενεργοποιημένη."
|
||||
"resetLibrary": "Αυτό θα καθαρίσει τη βιβλιοθήκη σας. Είστε σίγουροι;"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Επιλογή",
|
||||
@@ -167,7 +164,7 @@
|
||||
"ellipse": "Έλλειψη",
|
||||
"arrow": "Βέλος",
|
||||
"line": "Γραμμή",
|
||||
"freedraw": "Σχεδίαση",
|
||||
"freedraw": "",
|
||||
"text": "Κείμενο",
|
||||
"library": "Βιβλιοθήκη",
|
||||
"lock": "Κράτησε επιλεγμένο το εργαλείο μετά το σχέδιο"
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Κάνε κλικ για να ξεκινήσεις πολλαπλά σημεία, σύρε για μια γραμμή",
|
||||
"freeDraw": "Κάντε κλικ και σύρτε, απελευθερώσατε όταν έχετε τελειώσει",
|
||||
"text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών",
|
||||
"text_selected": "Κάντε διπλό κλικ ή πατήστε ENTER για να επεξεργαστείτε το κείμενο",
|
||||
"text_editing": "Πατήστε Escape ή CtrlOrCmd+ENTER για να ολοκληρώσετε την επεξεργασία",
|
||||
"linearElementMulti": "Κάνε κλικ στο τελευταίο σημείο ή πάτησε Escape ή Enter για να τελειώσεις",
|
||||
"lockAngle": "Μπορείτε να περιορίσετε τη γωνία κρατώντας πατημένο το SHIFT",
|
||||
"resize": "Μπορείς να περιορίσεις τις αναλογίες κρατώντας το SHIFT ενώ αλλάζεις μέγεθος,\nκράτησε πατημένο το ALT για αλλαγή μεγέθους από το κέντρο",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Διαβάστε το Blog μας",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Κυρτό βέλος",
|
||||
"curvedLine": "Κυρτή γραμμή",
|
||||
"documentation": "Εγχειρίδιο",
|
||||
"doubleClick": "",
|
||||
"drag": "σύρε",
|
||||
"editor": "Επεξεργαστής",
|
||||
"editSelectedShape": "",
|
||||
"github": "Βρήκατε πρόβλημα; Υποβάλετε το",
|
||||
"howto": "Ακολουθήστε τους οδηγούς μας",
|
||||
"or": "ή",
|
||||
"preventBinding": "Αποτροπή δέσμευσης βέλων",
|
||||
"shapes": "Σχήματα",
|
||||
"shortcuts": "Συντομεύσεις πληκτρολογίου",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"textFinish": "Ολοκλήρωση επεξεργασίας (κείμενο)",
|
||||
"textNewLine": "Προσθήκη νέας γραμμής (κείμενο)",
|
||||
"title": "Βοήθεια",
|
||||
"view": "Προβολή",
|
||||
"zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Αποθηκεύτηκε στο {filename}",
|
||||
"canvas": "καμβάς",
|
||||
"selection": "επιλογή"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -157,8 +157,7 @@
|
||||
"imageDoesNotContainScene": "Importing images isn't supported at the moment.\n\nDid you want to import a scene? This image does not seem to contain any scene data. Have you enabled this during export?",
|
||||
"cannotRestoreFromImage": "Scene couldn't be restored from this image file",
|
||||
"invalidSceneUrl": "Couldn't import scene from the supplied URL. It's either malformed, or doesn't contain valid Excalidraw JSON data.",
|
||||
"resetLibrary": "This will clear your library. Are you sure?",
|
||||
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled."
|
||||
"resetLibrary": "This will clear your library. Are you sure?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selection",
|
||||
@@ -181,8 +180,6 @@
|
||||
"linearElement": "Click to start multiple points, drag for single line",
|
||||
"freeDraw": "Click and drag, release when you're finished",
|
||||
"text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
|
||||
"text_selected": "Double-click or press ENTER to edit text",
|
||||
"text_editing": "Press Escape or CtrlOrCmd+ENTER to finish editing",
|
||||
"linearElementMulti": "Click on last point or press Escape or Enter to finish",
|
||||
"lockAngle": "You can constrain angle by holding SHIFT",
|
||||
"resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
|
||||
@@ -228,10 +225,7 @@
|
||||
"disk_button": "Save to file",
|
||||
"link_title": "Shareable link",
|
||||
"link_details": "Export as a read-only link.",
|
||||
"link_button": "Export to Link",
|
||||
"excalidrawplus_description": "Save the scene to your Excalidraw+ workspace.",
|
||||
"excalidrawplus_button": "Export",
|
||||
"excalidrawplus_exportError": "Couldn't export to Excalidraw+ at this moment..."
|
||||
"link_button": "Export to Link"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Read our blog",
|
||||
@@ -239,18 +233,16 @@
|
||||
"curvedArrow": "Curved arrow",
|
||||
"curvedLine": "Curved line",
|
||||
"documentation": "Documentation",
|
||||
"doubleClick": "double-click",
|
||||
"drag": "drag",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Edit selected shape (text/arrow/line)",
|
||||
"github": "Found an issue? Submit",
|
||||
"howto": "Follow our guides",
|
||||
"or": "or",
|
||||
"preventBinding": "Prevent arrow binding",
|
||||
"shapes": "Shapes",
|
||||
"shortcuts": "Keyboard shortcuts",
|
||||
"textFinish": "Finish editing (text editor)",
|
||||
"textNewLine": "Add new line (text editor)",
|
||||
"textFinish": "Finish editing (text)",
|
||||
"textNewLine": "Add new line (text)",
|
||||
"title": "Help",
|
||||
"view": "View",
|
||||
"zoomToFit": "Zoom to fit all elements",
|
||||
@@ -283,52 +275,5 @@
|
||||
"fileSavedToFilename": "Saved to {filename}",
|
||||
"canvas": "canvas",
|
||||
"selection": "selection"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "White",
|
||||
"f8f9fa": "Gray 0",
|
||||
"f1f3f5": "Gray 1",
|
||||
"fff5f5": "Red 0",
|
||||
"fff0f6": "Pink 0",
|
||||
"f8f0fc": "Grape 0",
|
||||
"f3f0ff": "Violet 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Blue 0",
|
||||
"e3fafc": "Cyan 0",
|
||||
"e6fcf5": "Teal 0",
|
||||
"ebfbee": "Green 0",
|
||||
"f4fce3": "Lime 0",
|
||||
"fff9db": "Yellow 0",
|
||||
"fff4e6": "Orange 0",
|
||||
"transparent": "Transparent",
|
||||
"ced4da": "Gray 4",
|
||||
"868e96": "Gray 6",
|
||||
"fa5252": "Red 6",
|
||||
"e64980": "Pink 6",
|
||||
"be4bdb": "Grape 6",
|
||||
"7950f2": "Violet 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Blue 6",
|
||||
"15aabf": "Cyan 6",
|
||||
"12b886": "Teal 6",
|
||||
"40c057": "Green 6",
|
||||
"82c91e": "Lime 6",
|
||||
"fab005": "Yellow 6",
|
||||
"fd7e14": "Orange 6",
|
||||
"000000": "Black",
|
||||
"343a40": "Gray 8",
|
||||
"495057": "Gray 7",
|
||||
"c92a2a": "Red 9",
|
||||
"a61e4d": "Pink 9",
|
||||
"862e9c": "Grape 9",
|
||||
"5f3dc4": "Violet 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Blue 9",
|
||||
"0b7285": "Cyan 9",
|
||||
"087f5b": "Teal 9",
|
||||
"2b8a3e": "Green 9",
|
||||
"5c940d": "Lime 9",
|
||||
"e67700": "Yellow 9",
|
||||
"d9480f": "Orange 9"
|
||||
}
|
||||
}
|
||||
|
@@ -20,10 +20,10 @@
|
||||
"background": "Fondo",
|
||||
"fill": "Rellenar",
|
||||
"strokeWidth": "Grosor del trazo",
|
||||
"strokeShape": "Estilo del trazo",
|
||||
"strokeShape_gel": "Bolígrafo de gel",
|
||||
"strokeShape_fountain": "Pluma estilográfica",
|
||||
"strokeShape_brush": "Rotulador",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "Estilo del trazo",
|
||||
"strokeStyle_solid": "Sólido",
|
||||
"strokeStyle_dashed": "Discontinua",
|
||||
@@ -42,8 +42,8 @@
|
||||
"fontSize": "Tamaño de la fuente",
|
||||
"fontFamily": "Tipo de fuente",
|
||||
"onlySelected": "Sólo seleccionados",
|
||||
"withBackground": "Fondo",
|
||||
"exportEmbedScene": "Embeber escena",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "Los datos de escena se guardarán en el archivo PNG/SVG exportado, así la escena puede ser restaurada de la misma.\nEsto aumentará el tamaño del archivo exportado.",
|
||||
"addWatermark": "Agregar \"Hecho con Excalidraw\"",
|
||||
"handDrawn": "Dibujado a mano",
|
||||
@@ -101,21 +101,19 @@
|
||||
"viewMode": "Modo presentación",
|
||||
"toggleExportColorScheme": "Cambiar el esquema de colores de exportación",
|
||||
"share": "Compartir",
|
||||
"showStroke": "Mostrar el selector de color del trazo",
|
||||
"showBackground": "Mostrar el selector de color de fondo",
|
||||
"toggleTheme": "Alternar tema"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
|
||||
"exportJSON": "Exportar como archivo",
|
||||
"exportImage": "Guardar como imagen",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "Exportar",
|
||||
"exportToPng": "Exportar a PNG",
|
||||
"exportToSvg": "Exportar a SVG",
|
||||
"copyToClipboard": "Copiar al portapapeles",
|
||||
"copyPngToClipboard": "Copiar PNG al portapapeles",
|
||||
"scale": "Escalar",
|
||||
"save": "Guardal al archivo actual",
|
||||
"save": "",
|
||||
"saveAs": "Guardar como",
|
||||
"load": "Cargar",
|
||||
"getShareableLink": "Obtener enlace para compartir",
|
||||
@@ -124,7 +122,7 @@
|
||||
"scrollBackToContent": "Volver al contenido",
|
||||
"zoomIn": "Acercarse",
|
||||
"zoomOut": "Alejarse",
|
||||
"resetZoom": "Restablecer zoom",
|
||||
"resetZoom": "Restablecer acercamiento",
|
||||
"menu": "Menú",
|
||||
"done": "Hecho",
|
||||
"edit": "Editar",
|
||||
@@ -157,8 +155,7 @@
|
||||
"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?",
|
||||
"cannotRestoreFromImage": "No se pudo restaurar la escena desde este archivo de imagen",
|
||||
"invalidSceneUrl": "No se ha podido importar la escena desde la URL proporcionada. Está mal formada, o no contiene datos de Excalidraw JSON válidos.",
|
||||
"resetLibrary": "Esto eliminará tu librería. ¿Estás seguro?",
|
||||
"invalidEncryptionKey": "La clave de cifrado debe tener 22 caracteres. La colaboración en vivo está deshabilitada."
|
||||
"resetLibrary": "Esto eliminará tu librería. ¿Estás seguro?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selección",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Haz clic para dibujar múltiples puntos, arrastrar para solo una línea",
|
||||
"freeDraw": "Haz clic y arrastra, suelta al terminar",
|
||||
"text": "Consejo: también puedes añadir texto haciendo doble clic en cualquier lugar con la herramienta de selección",
|
||||
"text_selected": "Doble clic o pulse ENTER para editar el texto",
|
||||
"text_editing": "Pulse Escape o CtrlOrCmd+ENTER para terminar de editar",
|
||||
"linearElementMulti": "Haz clic en el último punto o presiona Escape o Enter para finalizar",
|
||||
"lockAngle": "Puedes restringir el ángulo manteniendo presionado el botón SHIFT",
|
||||
"resize": "Para mantener las proporciones mantén SHIFT presionado mientras modificas el tamaño, \nmantén presionado ALT para modificar el tamaño desde el centro",
|
||||
@@ -223,15 +218,12 @@
|
||||
"title": "Error"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Guardar en el disco",
|
||||
"disk_details": "Exportar los datos de la escena a un archivo desde el cual se puede importar más tarde.",
|
||||
"disk_button": "Guardar en el archivo",
|
||||
"link_title": "Enlace para compartir",
|
||||
"link_details": "Exportar como enlace de sólo lectura.",
|
||||
"link_button": "Exportar al link",
|
||||
"excalidrawplus_description": "Guarda la escena en tu espacio de trabajo de Excalidraw+.",
|
||||
"excalidrawplus_button": "Exportar",
|
||||
"excalidrawplus_exportError": "No se pudo exportar a Excalidraw+ en este momento..."
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Lee nuestro blog",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Flecha curvada",
|
||||
"curvedLine": "Línea curva",
|
||||
"documentation": "Documentación",
|
||||
"doubleClick": "doble clic",
|
||||
"drag": "arrastrar",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Editar la forma seleccionada (texto/flecha/línea)",
|
||||
"github": "¿Has encontrado un problema? Envíalo",
|
||||
"howto": "Siga nuestras guías",
|
||||
"or": "o",
|
||||
"preventBinding": "Evitar yuxtaposición de flechas",
|
||||
"shapes": "Formas",
|
||||
"shortcuts": "Atajos del teclado",
|
||||
"textFinish": "Finalizar edición (editor de texto)",
|
||||
"textNewLine": "Añadir nueva linea (editor de texto)",
|
||||
"textFinish": "Finalizar edición (texto)",
|
||||
"textNewLine": "Añadir nueva línea (texto)",
|
||||
"title": "Ayuda",
|
||||
"view": "Vista",
|
||||
"zoomToFit": "Ajustar la vista para mostrar todos los elementos",
|
||||
@@ -258,7 +248,7 @@
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán.",
|
||||
"link": "Entrada en el blog sobre cifrado de extremo a extremo"
|
||||
"link": ""
|
||||
},
|
||||
"stats": {
|
||||
"angle": "Ángulo",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Guardado en {filename}",
|
||||
"canvas": "lienzo",
|
||||
"selection": "selección"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Blanco",
|
||||
"f8f9fa": "Gris 0",
|
||||
"f1f3f5": "Gris 1",
|
||||
"fff5f5": "Rojo 0",
|
||||
"fff0f6": "Rosa 0",
|
||||
"f8f0fc": "Uva 0",
|
||||
"f3f0ff": "Violeta 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Azul 0",
|
||||
"e3fafc": "Cian 0",
|
||||
"e6fcf5": "Turquesa 0",
|
||||
"ebfbee": "Verde 0",
|
||||
"f4fce3": "Lima 0",
|
||||
"fff9db": "Amarillo 0",
|
||||
"fff4e6": "Naranja 0",
|
||||
"transparent": "Transparente",
|
||||
"ced4da": "Gris 4",
|
||||
"868e96": "Gris 6",
|
||||
"fa5252": "Rojo 6",
|
||||
"e64980": "Rosa 6",
|
||||
"be4bdb": "Uva 6",
|
||||
"7950f2": "Violeta 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Azul 6",
|
||||
"15aabf": "Cian 6",
|
||||
"12b886": "Turquesa 6",
|
||||
"40c057": "Verde 6",
|
||||
"82c91e": "Lima 6",
|
||||
"fab005": "Amarillo 6",
|
||||
"fd7e14": "Naranja 6",
|
||||
"000000": "Negro",
|
||||
"343a40": "Gris 8",
|
||||
"495057": "Gris 7",
|
||||
"c92a2a": "Rojo 9",
|
||||
"a61e4d": "Rosa 9",
|
||||
"862e9c": "Uva 9",
|
||||
"5f3dc4": "Violeta 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Azul 9",
|
||||
"0b7285": "Cian 9",
|
||||
"087f5b": "Turquesa 9",
|
||||
"2b8a3e": "Verde 9",
|
||||
"5c940d": "Lima 9",
|
||||
"e67700": "Amarillo 9",
|
||||
"d9480f": "Naranja 9"
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "جای گذاری",
|
||||
"pasteCharts": "قراردادن نمودارها",
|
||||
"pasteCharts": "قراردادن نمودار",
|
||||
"selectAll": "انتخاب همه",
|
||||
"multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید.",
|
||||
"moveCanvas": "جابجایی بوم",
|
||||
"cut": "بریدن",
|
||||
"moveCanvas": "بوم را حرکت بدهید",
|
||||
"cut": "جابجایی",
|
||||
"copy": "کپی",
|
||||
"copyAsPng": "کپی در حافطه موقت به صورت PNG",
|
||||
"copyAsSvg": "کپی در حافطه موقت به صورت SVG",
|
||||
@@ -16,33 +16,33 @@
|
||||
"delete": "حذف",
|
||||
"copyStyles": "کپی سبک",
|
||||
"pasteStyles": "جای گذاری سبک",
|
||||
"stroke": "حاشیه",
|
||||
"stroke": "خط",
|
||||
"background": "پس زمینه",
|
||||
"fill": "رنگ آمیزی",
|
||||
"strokeWidth": "ضخامت حاشیه",
|
||||
"strokeShape": "حاشیه شکل",
|
||||
"strokeWidth": "ضخامت خط",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "استایل حاشیه",
|
||||
"strokeStyle": "استایل خط",
|
||||
"strokeStyle_solid": "یکدست",
|
||||
"strokeStyle_dashed": "خط چین",
|
||||
"strokeStyle_dotted": "نقطه چین",
|
||||
"sloppiness": "دقت",
|
||||
"opacity": "شفافیت",
|
||||
"opacity": "تاری",
|
||||
"textAlign": "چیدمان متن",
|
||||
"edges": "لبه ها",
|
||||
"sharp": "تیز",
|
||||
"round": "دور",
|
||||
"arrowheads": "سر پیکان",
|
||||
"arrowhead_none": "هیچ کدام",
|
||||
"arrowhead_arrow": "پیکان",
|
||||
"arrowhead_arrow": "فلش",
|
||||
"arrowhead_bar": "میله ای",
|
||||
"arrowhead_dot": "نقطه",
|
||||
"fontSize": "اندازه قلم",
|
||||
"fontFamily": "نوع قلم",
|
||||
"onlySelected": "فقط انتخاب شده ها",
|
||||
"withBackground": "پس زمینه",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "متحوای صحنه به فایل خروجی SVG/PNG اضافه خواهد شد برای بازیابی صحنه به آن اضافه خواهد شد.\nباعث افزایش حجم فایل خروجی میشود.",
|
||||
"addWatermark": "\"ساخته شده با Excalidraw\" را اضافه کن",
|
||||
@@ -65,7 +65,7 @@
|
||||
"architect": "معمار",
|
||||
"artist": "هنرمند",
|
||||
"cartoonist": "کارتونیست",
|
||||
"fileTitle": "نام فایل",
|
||||
"fileTitle": "",
|
||||
"colorPicker": "انتخابگر رنگ",
|
||||
"canvasBackground": "بوم",
|
||||
"drawingCanvas": "بوم نقاشی",
|
||||
@@ -81,7 +81,7 @@
|
||||
"group": "گروهبندی انتخابها",
|
||||
"ungroup": "حذف گروهبندی انتخابها",
|
||||
"collaborators": "همکاران",
|
||||
"showGrid": "نمایش گرید",
|
||||
"showGrid": "",
|
||||
"addToLibrary": "افزودن به کتابخانه",
|
||||
"removeFromLibrary": "حذف از کتابخانه",
|
||||
"libraryLoadingMessage": "بارگذاری کتابخانه…",
|
||||
@@ -96,26 +96,24 @@
|
||||
"centerHorizontally": "وسط قرار دادن به صورت افقی",
|
||||
"distributeHorizontally": "توزیع کردن به صورت افقی",
|
||||
"distributeVertically": "توزیع کردن به صورت عمودی",
|
||||
"flipHorizontal": "چرخش افقی",
|
||||
"flipVertical": "چرخش عمودی",
|
||||
"viewMode": "حالت نمایش",
|
||||
"flipHorizontal": "",
|
||||
"flipVertical": "",
|
||||
"viewMode": "",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "اشتراکگذاری",
|
||||
"showStroke": "نمایش انتخاب کننده رنگ حاشیه",
|
||||
"showBackground": "نمایش انتخاب کننده رنگ پس زمینه",
|
||||
"toggleTheme": "تغییر تم"
|
||||
"share": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "پاکسازی بوم نقاشی",
|
||||
"exportJSON": "خروجی در فایل",
|
||||
"exportImage": "ذخیره به عنوان عکس",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "تبدیل",
|
||||
"exportToPng": "تبدیل به PNG",
|
||||
"exportToSvg": "تبدیل به SVG",
|
||||
"copyToClipboard": "کپی در حافظه موقت",
|
||||
"copyPngToClipboard": "کپی PNG در حافظه موقت",
|
||||
"scale": "مقیاس",
|
||||
"save": "ذخیره در همین فایل",
|
||||
"save": "",
|
||||
"saveAs": "ذخیره با نام",
|
||||
"load": "بارگذاری",
|
||||
"getShareableLink": "دریافت لینک قابل اشتراک",
|
||||
@@ -151,14 +149,13 @@
|
||||
"loadSceneOverridePrompt": "بارگزاری یک طرح خارجی محتوای فعلی رو از بین میبرد. آیا میخواهید ادامه دهید؟",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "خطایی در بارگذاری کتابخانه ثالث وجود داشت.",
|
||||
"errorAddingToLibrary": "مورد به کتابخانه اضافه نشد",
|
||||
"errorRemovingFromLibrary": "مورد از کتابخانه حذف نشد",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"confirmAddLibrary": "{{numShapes}} از اشکال به کتابخانه شما اضافه خواهد شد. مطمئن هستید؟",
|
||||
"imageDoesNotContainScene": "وارد کردن تصویر در این لحظه امکان پذیر نمی باشد.\nآیا مایل به وارد کردن یک صحنه هستید؟ این تصویر به نظر می رسد که فاقد هرگونه اطلاعاتی مربوط به صحنه باشد. آیا این گزینه را در زمان وارد کردن تصویر فعال کرده اید؟",
|
||||
"cannotRestoreFromImage": "صحنه را نمی توان از این فایل تصویری بازیابی کرد",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "گزینش",
|
||||
@@ -167,7 +164,7 @@
|
||||
"ellipse": "بیضی",
|
||||
"arrow": "پیکان",
|
||||
"line": "خط",
|
||||
"freedraw": "کشیدن",
|
||||
"freedraw": "",
|
||||
"text": "متن",
|
||||
"library": "کتابخانه",
|
||||
"lock": "ابزار انتخاب شده را بعد از کشیدن نگه دار"
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "برای چند نقطه کلیک و برای یک خط بکشید",
|
||||
"freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید",
|
||||
"text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "روی آخرین نقطه کلیک کنید یا کلید ESC را بزنید یا کلید Enter را بزنید برای اتمام کار",
|
||||
"lockAngle": "با نگه داشتن SHIFT هنگام چرخش می توانید زاویه ها را محدود کنید",
|
||||
"resize": "می توانید با نگه داشتن SHIFT در هنگام تغییر اندازه، نسبت ها را محدود کنید،ALT را برای تغییر اندازه از مرکز نگه دارید",
|
||||
@@ -223,34 +218,29 @@
|
||||
"title": "خطا"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "ذخیره در دیسک",
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
"disk_button": "ذخیره در فایل",
|
||||
"link_title": "لینک قابل اشتراکگذاری",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "خروجی گرفتن",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "بلاگ ما را بخوانید",
|
||||
"click": "کلیک",
|
||||
"click": "",
|
||||
"curvedArrow": "فلش خمیده",
|
||||
"curvedLine": "منحنی",
|
||||
"documentation": "مستندات",
|
||||
"doubleClick": "دابل کلیک",
|
||||
"drag": "کشیدن",
|
||||
"drag": "",
|
||||
"editor": "ویرایشگر",
|
||||
"editSelectedShape": "ویرایش شکل انتخاب شده (متن/فلش/خط)",
|
||||
"github": "اشکالی می بینید؟ گزارش دهید",
|
||||
"howto": "راهنمای ما را دنبال کنید",
|
||||
"or": "یا",
|
||||
"preventBinding": "مانع شدن از چسبیدن فلش ها",
|
||||
"shapes": "شکلها",
|
||||
"shortcuts": "میانبرهای صفحه کلید",
|
||||
"textFinish": "پایان ویرایش (ویرایشگر متن)",
|
||||
"textNewLine": "افزودن خط جدید (ویرایشگر متن)",
|
||||
"textFinish": "",
|
||||
"textNewLine": "یک خط جدید اضافه کنید (متن)",
|
||||
"title": "راهنما",
|
||||
"view": "مشاهده",
|
||||
"zoomToFit": "بزرگنمایی برای دیدن تمام آیتم ها",
|
||||
@@ -270,65 +260,18 @@
|
||||
"storage": "حافظه",
|
||||
"title": "آمار برای نردها",
|
||||
"total": "مجموع",
|
||||
"version": "نسخه",
|
||||
"versionCopy": "برای کپی کردن کلیک کنید",
|
||||
"versionNotAvailable": "نسخه غیرقابل دسترس",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "عرض"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "کپی سبک.",
|
||||
"copyToClipboard": "در کلیپبورد کپی شد.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "",
|
||||
"fileSaved": "فایل ذخیره شد.",
|
||||
"fileSavedToFilename": "ذخیره در {filename}",
|
||||
"canvas": "بوم",
|
||||
"selection": "انتخاب"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
"fileSaved": "",
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
}
|
||||
}
|
||||
|
@@ -42,8 +42,8 @@
|
||||
"fontSize": "Kirjasinkoko",
|
||||
"fontFamily": "Kirjasintyyppi",
|
||||
"onlySelected": "Vain valitut",
|
||||
"withBackground": "Taustalla",
|
||||
"exportEmbedScene": "Upota työ",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "Teoksen tiedot tallennetaan PNG/SVG-tiedostoon, jolloin teoksen voi palauttaa siitä. Kasvattaa tallennetun tiedoston kokoa.",
|
||||
"addWatermark": "Lisää \"Tehty Excalidrawilla\"",
|
||||
"handDrawn": "Käsinkirjoitettu",
|
||||
@@ -101,21 +101,19 @@
|
||||
"viewMode": "Katselutila",
|
||||
"toggleExportColorScheme": "Vaihda viennin väriteema",
|
||||
"share": "Jaa",
|
||||
"showStroke": "Näytä viivan värin valitsin",
|
||||
"showBackground": "Näytä taustavärin valitsin",
|
||||
"toggleTheme": "Vaihda teema"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Tyhjennä piirtoalue",
|
||||
"exportJSON": "Vie tiedostoon",
|
||||
"exportImage": "Tallenna kuvana",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "Vie",
|
||||
"exportToPng": "Vie PNG-tiedostona",
|
||||
"exportToSvg": "Vie SVG-tiedostona",
|
||||
"copyToClipboard": "Kopioi leikepöydälle",
|
||||
"copyPngToClipboard": "Kopioi PNG-tiedosto leikepöydälle",
|
||||
"scale": "Koko",
|
||||
"save": "Tallenna nykyiseen tiedostoon",
|
||||
"save": "",
|
||||
"saveAs": "Tallenna nimellä",
|
||||
"load": "Avaa",
|
||||
"getShareableLink": "Hae jaettava linkki",
|
||||
@@ -157,8 +155,7 @@
|
||||
"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?",
|
||||
"cannotRestoreFromImage": "Teosta ei voitu palauttaa tästä kuvatiedostosta",
|
||||
"invalidSceneUrl": "Teosta ei voitu tuoda annetusta URL-osoitteesta. Tallenne on vioittunut, tai osoitteessa ei ole Excalidraw JSON-dataa.",
|
||||
"resetLibrary": "Tämä tyhjentää kirjastosi. Jatketaanko?",
|
||||
"invalidEncryptionKey": "Salausavaimen on oltava 22 merkkiä pitkä. Live-yhteistyö ei ole käytössä."
|
||||
"resetLibrary": "Tämä tyhjentää kirjastosi. Jatketaanko?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Valinta",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Klikkaa piirtääksesi useampi piste, raahaa piirtääksesi yksittäinen viiva",
|
||||
"freeDraw": "Paina ja raahaa, päästä irti kun olet valmis",
|
||||
"text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla",
|
||||
"text_selected": "Kaksoisnapsauta tai paina ENTER muokataksesi tekstiä",
|
||||
"text_editing": "Paina Escape tai CtrlOrCmd+ENTER lopettaaksesi muokkaamisen",
|
||||
"linearElementMulti": "Lopeta klikkaamalla viimeistä pistettä, painamalla Escape- tai Enter-näppäintä",
|
||||
"lockAngle": "Voit rajoittaa kulmaa pitämällä SHIFT-näppäintä alaspainettuna",
|
||||
"resize": "Voit rajoittaa mittasuhteet pitämällä SHIFT-näppäintä alaspainettuna kun muutat kokoa, pidä ALT-näppäintä alaspainettuna muuttaaksesi kokoa keskipisteen suhteen",
|
||||
@@ -223,15 +218,12 @@
|
||||
"title": "Virhe"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Tallenna levylle",
|
||||
"disk_details": "Vie työn tiedot tiedostoon, josta sen voi tuoda myöhemmin.",
|
||||
"disk_button": "Tallenna tiedostoon",
|
||||
"link_title": "Jaettava linkki",
|
||||
"link_details": "Vie vain luku -linkkinä.",
|
||||
"link_button": "Vie linkkinä",
|
||||
"excalidrawplus_description": "Tallenna teos Excalidraw+ tilaan.",
|
||||
"excalidrawplus_button": "Vie",
|
||||
"excalidrawplus_exportError": "Ei voitu viedä Excalidraw+-palveluun tällä hetkellä..."
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Lue blogiamme",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Kaareva nuoli",
|
||||
"curvedLine": "Kaareva viiva",
|
||||
"documentation": "Käyttöohjeet",
|
||||
"doubleClick": "kaksoisnapsautus",
|
||||
"drag": "vedä",
|
||||
"editor": "Muokkausohjelma",
|
||||
"editSelectedShape": "Muokkaa valittua muotoa (teksti/nuoli/viiva)",
|
||||
"github": "Löysitkö ongelman? Kerro meille",
|
||||
"howto": "Seuraa oppaitamme",
|
||||
"or": "tai",
|
||||
"preventBinding": "Estä nuolten kiinnitys",
|
||||
"shapes": "Muodot",
|
||||
"shortcuts": "Pikanäppäimet",
|
||||
"textFinish": "Lopeta muokkaus (tekstieditori)",
|
||||
"textNewLine": "Lisää uusi rivi (tekstieditori)",
|
||||
"textFinish": "Lopeta muokkaus (teksti)",
|
||||
"textNewLine": "Lisää uusi rivi (teksti)",
|
||||
"title": "Ohjeet",
|
||||
"view": "Näkymä",
|
||||
"zoomToFit": "Näytä kaikki elementit",
|
||||
@@ -258,7 +248,7 @@
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "Piirroksesi ovat päästä-päähän-salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä.",
|
||||
"link": "Blogiartikkeli päästä päähän -salauksesta Excalidraw:ssa"
|
||||
"link": ""
|
||||
},
|
||||
"stats": {
|
||||
"angle": "Kulma",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Tallennettiin kohteeseen {filename}",
|
||||
"canvas": "piirtoalue",
|
||||
"selection": "valinta"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Valkoinen",
|
||||
"f8f9fa": "Harmaa 0",
|
||||
"f1f3f5": "Harmaa 1",
|
||||
"fff5f5": "Punainen 0",
|
||||
"fff0f6": "Pinkki 0",
|
||||
"f8f0fc": "Rypäle 0",
|
||||
"f3f0ff": "Violetti 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Sininen 0",
|
||||
"e3fafc": "Syaani 0",
|
||||
"e6fcf5": "Sinivihreä 0",
|
||||
"ebfbee": "Vihreä 0",
|
||||
"f4fce3": "Limenvihreä 0",
|
||||
"fff9db": "Keltainen 0",
|
||||
"fff4e6": "Oranssi 0",
|
||||
"transparent": "Läpinäkyvä",
|
||||
"ced4da": "Harmaa 4",
|
||||
"868e96": "Harmaa 6",
|
||||
"fa5252": "Punainen 6",
|
||||
"e64980": "Pinkki 6",
|
||||
"be4bdb": "Rypäle 6",
|
||||
"7950f2": "Violetti 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Sininen 6",
|
||||
"15aabf": "Syaani 6",
|
||||
"12b886": "Sinivihreä 6",
|
||||
"40c057": "Vihreä 6",
|
||||
"82c91e": "Limenvihreä 6",
|
||||
"fab005": "Keltainen 6",
|
||||
"fd7e14": "Oranssi 6",
|
||||
"000000": "Musta",
|
||||
"343a40": "Harmaa 8",
|
||||
"495057": "Harmaa 7",
|
||||
"c92a2a": "Punainen 9",
|
||||
"a61e4d": "Pinkki 9",
|
||||
"862e9c": "Rypäle 9",
|
||||
"5f3dc4": "Violetti 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Sininen 9",
|
||||
"0b7285": "Syaani 9",
|
||||
"087f5b": "Sinivihreä 9",
|
||||
"2b8a3e": "Vihreä 9",
|
||||
"5c940d": "Limenvihreä 9",
|
||||
"e67700": "Keltainen 9",
|
||||
"d9480f": "Oranssi 9"
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
"delete": "Supprimer",
|
||||
"copyStyles": "Copier les styles",
|
||||
"pasteStyles": "Coller les styles",
|
||||
"stroke": "Trait",
|
||||
"stroke": "Contour",
|
||||
"background": "Arrière-plan",
|
||||
"fill": "Remplissage",
|
||||
"strokeWidth": "Largeur du trait",
|
||||
@@ -43,10 +43,10 @@
|
||||
"fontFamily": "Police",
|
||||
"onlySelected": "Uniquement la sélection",
|
||||
"withBackground": "Arrière-plan",
|
||||
"exportEmbedScene": "Intégrer la scène",
|
||||
"exportEmbedScene": "Scène intégrée",
|
||||
"exportEmbedScene_details": "Les données de scène seront enregistrées dans le fichier PNG/SVG exporté, afin que la scène puisse être restaurée à partir de celui-ci.\nCela augmentera la taille du fichier exporté.",
|
||||
"addWatermark": "Ajouter \"Fait avec Excalidraw\"",
|
||||
"handDrawn": "Manuscrit",
|
||||
"handDrawn": "À main levée",
|
||||
"normal": "Normale",
|
||||
"code": "Code",
|
||||
"small": "Petit",
|
||||
@@ -101,21 +101,19 @@
|
||||
"viewMode": "Mode présentation",
|
||||
"toggleExportColorScheme": "Activer/Désactiver l'export du thème de couleur",
|
||||
"share": "Partager",
|
||||
"showStroke": "Afficher le sélecteur de couleur de trait",
|
||||
"showBackground": "Afficher le sélecteur de couleur d'arrière-plan",
|
||||
"toggleTheme": "Changer le thème"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Réinitialiser le canevas",
|
||||
"exportJSON": "Exporter comme fichier",
|
||||
"exportJSON": "Exporter dans un fichier",
|
||||
"exportImage": "Enregistrer comme image",
|
||||
"export": "Exporter",
|
||||
"exportToPng": "Enregistrer en PNG",
|
||||
"exportToSvg": "Enregistrer en SVG",
|
||||
"exportToPng": "Exporter en PNG",
|
||||
"exportToSvg": "Exporter en SVG",
|
||||
"copyToClipboard": "Copier dans le presse-papier",
|
||||
"copyPngToClipboard": "Copier le PNG dans le presse-papier",
|
||||
"copyPngToClipboard": "Copier le PNG vers le presse-papier",
|
||||
"scale": "Échelle",
|
||||
"save": "Enregistrer dans le fichier actuel",
|
||||
"save": "Sauvegarder dans le fichier actuel",
|
||||
"saveAs": "Enregistrer sous",
|
||||
"load": "Ouvrir",
|
||||
"getShareableLink": "Obtenir un lien de partage",
|
||||
@@ -157,8 +155,7 @@
|
||||
"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 ?",
|
||||
"cannotRestoreFromImage": "Impossible de restaurer la scène depuis ce fichier image",
|
||||
"invalidSceneUrl": "Impossible d'importer la scène depuis l'URL fournie. Elle est soit incorrecte, soit ne contient pas de données JSON Excalidraw valides.",
|
||||
"resetLibrary": "Cela va effacer votre bibliothèque. Êtes-vous sûr·e ?",
|
||||
"invalidEncryptionKey": "La clé de chiffrement doit comporter 22 caractères. La collaboration en direct est désactivée."
|
||||
"resetLibrary": "Cela va effacer votre bibliothèque. Êtes-vous sûr·e ?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Sélection",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Cliquez pour démarrer plusieurs points, faites glisser pour une seule ligne",
|
||||
"freeDraw": "Cliquez et faites glissez, relâchez quand vous avez terminé",
|
||||
"text": "Astuce : vous pouvez aussi ajouter du texte en double-cliquant n'importe où avec l'outil de sélection",
|
||||
"text_selected": "Double-cliquez ou appuyez sur ENTRÉE pour modifier le texte",
|
||||
"text_editing": "Appuyez sur ÉCHAP ou Ctrl/Cmd+ENTRÉE pour terminer l'édition",
|
||||
"linearElementMulti": "Cliquez sur le dernier point ou appuyez sur Échap ou Entrée pour terminer",
|
||||
"lockAngle": "Vous pouvez restreindre l'angle en maintenant MAJ",
|
||||
"resize": "Vous pouvez conserver les proportions en maintenant la touche MAJ pendant le redimensionnement,\nmaintenez la touche ALT pour redimensionner par rapport au centre",
|
||||
@@ -223,15 +218,12 @@
|
||||
"title": "Erreur"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Enregistrer sur le disque",
|
||||
"disk_details": "Exporter les données de la scène comme un fichier que vous pourrez importer ultérieurement.",
|
||||
"disk_button": "Enregistrer comme fichier",
|
||||
"link_title": "Lien partageable",
|
||||
"disk_title": "Sauvegarder sur le disque",
|
||||
"disk_details": "Exportez les données de la scène dans un fichier que vous pourrez importer ultérieurement.",
|
||||
"disk_button": "Sauvegarder dans un fichier",
|
||||
"link_title": "Lien à partager",
|
||||
"link_details": "Exporter comme un lien en lecture seule.",
|
||||
"link_button": "Exporter comme lien",
|
||||
"excalidrawplus_description": "Enregistrer la scène dans votre espace de travail Excalidraw+.",
|
||||
"excalidrawplus_button": "Exporter",
|
||||
"excalidrawplus_exportError": "Impossible d'exporter vers Excalidraw+ pour le moment..."
|
||||
"link_button": "Exporter vers un lien"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Lire notre blog",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Flèche courbée",
|
||||
"curvedLine": "Ligne courbée",
|
||||
"documentation": "Documentation",
|
||||
"doubleClick": "double-clic",
|
||||
"drag": "glisser",
|
||||
"editor": "Éditeur",
|
||||
"editSelectedShape": "Modifier la forme sélectionnée (texte/flèche/ligne)",
|
||||
"github": "Problème trouvé ? Soumettre",
|
||||
"howto": "Suivez nos guides",
|
||||
"or": "ou",
|
||||
"preventBinding": "Empêcher la liaison de flèche",
|
||||
"shapes": "Formes",
|
||||
"shortcuts": "Raccourcis clavier",
|
||||
"textFinish": "Terminer l'édition (éditeur de texte)",
|
||||
"textNewLine": "Ajouter une nouvelle ligne (éditeur de texte)",
|
||||
"textFinish": "Terminer l'édition (texte)",
|
||||
"textNewLine": "Ajouter une nouvelle ligne (texte)",
|
||||
"title": "Aide",
|
||||
"view": "Affichage",
|
||||
"zoomToFit": "Zoomer pour voir tous les éléments",
|
||||
@@ -277,58 +267,11 @@
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Styles copiés.",
|
||||
"copyToClipboard": "Copié dans le presse-papier.",
|
||||
"copyToClipboardAsPng": "{{exportSelection}} copié dans le presse-papier en PNG\n({{exportColorScheme}})",
|
||||
"copyToClipboard": "Copié vers le presse-papiers.",
|
||||
"copyToClipboardAsPng": "{{exportSelection}} copié dans le presse-papiers en PNG\n({{exportColorScheme}})",
|
||||
"fileSaved": "Fichier enregistré.",
|
||||
"fileSavedToFilename": "Enregistré sous {filename}",
|
||||
"canvas": "canevas",
|
||||
"selection": "sélection"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Blanc",
|
||||
"f8f9fa": "Gris 0",
|
||||
"f1f3f5": "Gris 1",
|
||||
"fff5f5": "Rouge 0",
|
||||
"fff0f6": "Rose 0",
|
||||
"f8f0fc": "Mauve 0",
|
||||
"f3f0ff": "Violet 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Bleu 0",
|
||||
"e3fafc": "Cyan 0",
|
||||
"e6fcf5": "Turquoise 0",
|
||||
"ebfbee": "Vert 0",
|
||||
"f4fce3": "Citron vert 0",
|
||||
"fff9db": "Jaune 0",
|
||||
"fff4e6": "Orange 0",
|
||||
"transparent": "Transparent",
|
||||
"ced4da": "Gris 4",
|
||||
"868e96": "Gris 6",
|
||||
"fa5252": "Rouge 6",
|
||||
"e64980": "Rose 6",
|
||||
"be4bdb": "Mauve 6",
|
||||
"7950f2": "Violet 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Bleu 6",
|
||||
"15aabf": "Cyan 6",
|
||||
"12b886": "Turquoise 6",
|
||||
"40c057": "Vert 6",
|
||||
"82c91e": "Citron vert 6",
|
||||
"fab005": "Jaune 6",
|
||||
"fd7e14": "Orange 6",
|
||||
"000000": "Noir",
|
||||
"343a40": "Gris 8",
|
||||
"495057": "Gris 7",
|
||||
"c92a2a": "Rouge 9",
|
||||
"a61e4d": "Rose 9",
|
||||
"862e9c": "Mauve 9",
|
||||
"5f3dc4": "Violet 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Bleu 9",
|
||||
"0b7285": "Cyan 9",
|
||||
"087f5b": "Turquoise 9",
|
||||
"2b8a3e": "Vert 9",
|
||||
"5c940d": "Citron vert 9",
|
||||
"e67700": "Jaune 9",
|
||||
"d9480f": "Orange 9"
|
||||
}
|
||||
}
|
||||
|
@@ -20,10 +20,10 @@
|
||||
"background": "רקע",
|
||||
"fill": "מילוי",
|
||||
"strokeWidth": "עובי קו מתאר",
|
||||
"strokeShape": "סגנון קו המתאר",
|
||||
"strokeShape_gel": "עט נובע",
|
||||
"strokeShape_fountain": "עט נובע",
|
||||
"strokeShape_brush": "מברשת",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "סגנון קו המתאר",
|
||||
"strokeStyle_solid": "מלא",
|
||||
"strokeStyle_dashed": "מקווקו",
|
||||
@@ -42,8 +42,8 @@
|
||||
"fontSize": "גודל גופן",
|
||||
"fontFamily": "סוג הגופן",
|
||||
"onlySelected": "רק מה שנבחר",
|
||||
"withBackground": "רקע",
|
||||
"exportEmbedScene": "הטמעה של מידע הסצנה",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "מידע התצוגה יישמר לקובץ המיוצא מסוג PNG/SVG כך שיהיה ניתן לשחזרה ממנו.\nהפעולה תגדיל את גודל הקובץ המיוצא.",
|
||||
"addWatermark": "הוסף \"נוצר באמצעות Excalidraw\"",
|
||||
"handDrawn": "כתב יד",
|
||||
@@ -65,14 +65,14 @@
|
||||
"architect": "ארכיטקט",
|
||||
"artist": "אמן",
|
||||
"cartoonist": "קריקטוריסט",
|
||||
"fileTitle": "שם קובץ",
|
||||
"fileTitle": "",
|
||||
"colorPicker": "בחירת צבע",
|
||||
"canvasBackground": "רקע הלוח",
|
||||
"drawingCanvas": "לוח ציור",
|
||||
"layers": "שכבות",
|
||||
"actions": "פעולות",
|
||||
"language": "שפה",
|
||||
"liveCollaboration": "התחל שיתוף חי",
|
||||
"liveCollaboration": "",
|
||||
"duplicateSelection": "שכפל",
|
||||
"untitled": "ללא כותרת",
|
||||
"name": "שם",
|
||||
@@ -81,7 +81,7 @@
|
||||
"group": "אחד לקבוצה",
|
||||
"ungroup": "פרק קבוצה",
|
||||
"collaborators": "שותפים",
|
||||
"showGrid": "הצג רשת",
|
||||
"showGrid": "",
|
||||
"addToLibrary": "הוסף לספריה",
|
||||
"removeFromLibrary": "הסר מספריה",
|
||||
"libraryLoadingMessage": "טוען ספריה…",
|
||||
@@ -96,26 +96,24 @@
|
||||
"centerHorizontally": "מרכז אופקית",
|
||||
"distributeHorizontally": "חלוקה אופקית",
|
||||
"distributeVertically": "חלוקה אנכית",
|
||||
"flipHorizontal": "סובב אופקית",
|
||||
"flipVertical": "סובב אנכית",
|
||||
"flipHorizontal": "",
|
||||
"flipVertical": "",
|
||||
"viewMode": "מצב תצוגה",
|
||||
"toggleExportColorScheme": "שנה את ערכת צבעי הייצוא",
|
||||
"share": "שתף",
|
||||
"showStroke": "הצג צבעי קו מתאר",
|
||||
"showBackground": "הצג צבעי רקע",
|
||||
"toggleTheme": "שינוי ערכת העיצוב"
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "אפס את הלוח",
|
||||
"exportJSON": "ייצא לקובץ",
|
||||
"exportImage": "שמירה כתמונה",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "ייצא",
|
||||
"exportToPng": "יצא ל PNG",
|
||||
"exportToSvg": "יצא ל SVG",
|
||||
"copyToClipboard": "העתק ללוח",
|
||||
"copyPngToClipboard": "העתק PNG ללוח",
|
||||
"scale": "קנה מידה",
|
||||
"save": "שמירת קובץ נוכחי",
|
||||
"save": "",
|
||||
"saveAs": "שמירה בשם",
|
||||
"load": "טען",
|
||||
"getShareableLink": "קבל קישור לשיתוף",
|
||||
@@ -149,16 +147,15 @@
|
||||
"decryptFailed": "לא ניתן לפענח מידע.",
|
||||
"uploadedSecurly": "ההעלאה הוצפנה מקצה לקצה, ולכן שרת Excalidraw וצד שלישי לא יכולים לקרוא את התוכן.",
|
||||
"loadSceneOverridePrompt": "טעינה של ציור חיצוני תחליף את התוכן הקיים שלך. האם תרצה להמשיך?",
|
||||
"collabStopOverridePrompt": "עצירת השיתוף תוביל למחיקת התרשימים השמורים בדפדפן. האם את/ה בטוח/ה?\n(אם תרצה לשמור את התרשימים הקיימים, תוכל לסגור את הדפדפן מבלי לסיים את השיתוף.)",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "קרתה שגיאה בטעינת הספריה החיצונית.",
|
||||
"errorAddingToLibrary": "לא ניתן להוסיף פריט לספרייה",
|
||||
"errorRemovingFromLibrary": "לא ניתן למחוק פריט מהספריה",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"confirmAddLibrary": "הפעולה תוסיף {{numShapes}} צורה(ות) לספריה שלך. האם אתה בטוח?",
|
||||
"imageDoesNotContainScene": "אין תמיכה בייבוא תמונות כעת.\n\nהאם אתה רוצה לייבא תצוגה? התמונה הזאת אינה מכילה מידע על תצוגה. האם הפעלת את האפשרות הזאת בזמן הוצאת המידע?",
|
||||
"cannotRestoreFromImage": "לא הצלחנו לשחזר את התצוגה מקובץ התמונה",
|
||||
"invalidSceneUrl": "ייבוא המידע מן סצינה מכתובת האינטרנט נכשלה. המידע בנוי באופן משובש או שהוא אינו קובץ JSON תקין של Excalidraw.",
|
||||
"resetLibrary": "פעולה זו תנקה את כל הלוח. אתה בטוח?",
|
||||
"invalidEncryptionKey": ""
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "בחירה",
|
||||
@@ -167,7 +164,7 @@
|
||||
"ellipse": "אליפסה",
|
||||
"arrow": "חץ",
|
||||
"line": "קו",
|
||||
"freedraw": "צייר",
|
||||
"freedraw": "",
|
||||
"text": "טקסט",
|
||||
"library": "ספריה",
|
||||
"lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור"
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד",
|
||||
"freeDraw": "לחץ וגרור, שחרר כשסיימת",
|
||||
"text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה",
|
||||
"text_selected": "לחץ לחיצה כפולה או אנטר לעריכת הנקודות",
|
||||
"text_editing": "כדי לסיים את העריכה לחצו על מקש Escape או על Ctrl ומקש Enter (Cmd במחשבי אפל)",
|
||||
"linearElementMulti": "הקלק על הנקודה האחרונה או הקש Escape או Enter לסיום",
|
||||
"lockAngle": "אתה יכול להגביל זווית ע״י לחיצה על SHIFT",
|
||||
"resize": "ניתן להגביל פרופורציות על ידי לחיצה על SHIFT תוך כדי שינוי גודל,\nהחזק ALT בשביל לשנות גודל ביחס למרכז",
|
||||
@@ -217,48 +212,43 @@
|
||||
"desc_inProgressIntro": "שיתוף חי כרגע בפעולה.",
|
||||
"desc_shareLink": "שתף את הקישור עם כל מי שאתה מעוניין לעבוד אתו:",
|
||||
"desc_exitSession": "עצירת השיתוף תנתק אותך מהחדר, אבל עדיין תוכל להמשיך לעבוד על הלוח, מקומית. שים לב שזה לא ישפיע על אנשים אחרים, והם עדיין יוכלו לשתף פעולה עם הגירסה שלהם.",
|
||||
"shareTitle": "הצטרף לסשן שיתוף בזמן אמת של Excalidraw"
|
||||
"shareTitle": ""
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "שגיאה"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "שמור לכונן",
|
||||
"disk_details": "ייצוא מידע הסצינה לקובץ אותו ניתן יהיה לייבא בהמשך.",
|
||||
"disk_button": "שמירה לקובץ",
|
||||
"link_title": "העתקת קישור לשיתוף",
|
||||
"link_details": "ייצוא כקישור לקריאה בלבד.",
|
||||
"link_button": "ייצוא כקישור",
|
||||
"excalidrawplus_description": "שמור את המפה לסביבת העבודה שלך ב-Excalidraw+.",
|
||||
"excalidrawplus_button": "ייצוא",
|
||||
"excalidrawplus_exportError": "הייצוא ל-Excalidraw+ לא הצליח לעת עתה..."
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "קרא את הבלוג שלנו",
|
||||
"click": "קליק",
|
||||
"curvedArrow": "חץ מעוגל",
|
||||
"curvedLine": "קו מעוגל",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "תיעוד",
|
||||
"doubleClick": "לחיצה כפולה",
|
||||
"drag": "לגרור",
|
||||
"editor": "עורך",
|
||||
"editSelectedShape": "ערוך את הצורה הנבחרת (טקסט/חץ/קו)",
|
||||
"github": "מצאת בעיה? דווח",
|
||||
"howto": "עקוב אחר המדריכים שלנו",
|
||||
"or": "או",
|
||||
"preventBinding": "למנוע נעיצת חיצים",
|
||||
"preventBinding": "",
|
||||
"shapes": "צורות",
|
||||
"shortcuts": "קיצורי מקלדת",
|
||||
"textFinish": "סיים עריכה (טקסט)",
|
||||
"textNewLine": "הוסף שורה חדשה (טקסט)",
|
||||
"title": "עזרה",
|
||||
"view": "תצוגה",
|
||||
"zoomToFit": "גלילה להצגת כל האלמנטים במסך",
|
||||
"zoomToFit": "",
|
||||
"zoomToSelection": "התמקד בבחירה"
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם.",
|
||||
"link": "פוסט בבלוג על הצפנה מקצה לקצב ב-Excalidraw"
|
||||
"link": ""
|
||||
},
|
||||
"stats": {
|
||||
"angle": "זווית",
|
||||
@@ -270,65 +260,18 @@
|
||||
"storage": "אחסון",
|
||||
"title": "סטטיסטיקות לחנונים",
|
||||
"total": "סה״כ",
|
||||
"version": "גרסה",
|
||||
"version": "",
|
||||
"versionCopy": "לחץ להעתקה",
|
||||
"versionNotAvailable": "הגרסה אינה זמינה",
|
||||
"versionNotAvailable": "",
|
||||
"width": "רוחב"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "העתק סגנונות.",
|
||||
"copyToClipboard": "הועתק אל הלוח.",
|
||||
"copyToClipboardAsPng": "{{exportSelection}} הועתקה ללוח כ-PNG\n({{exportColorScheme}})",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "",
|
||||
"fileSaved": "קובץ נשמר.",
|
||||
"fileSavedToFilename": "נשמר לקובץ {filename}",
|
||||
"canvas": "משטח ציור",
|
||||
"selection": "בחירה"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "לבן",
|
||||
"f8f9fa": "אפור 0",
|
||||
"f1f3f5": "אפור 1",
|
||||
"fff5f5": "אדום 0",
|
||||
"fff0f6": "ורוד 0",
|
||||
"f8f0fc": "ענבים 0",
|
||||
"f3f0ff": "סגול 0",
|
||||
"edf2ff": "כחול כהה 0",
|
||||
"e7f5ff": "כחול 0",
|
||||
"e3fafc": "טורקיז 0",
|
||||
"e6fcf5": "ירקרק 0",
|
||||
"ebfbee": "ירוק 0",
|
||||
"f4fce3": "ליים 0",
|
||||
"fff9db": "צהוב",
|
||||
"fff4e6": "כתום 0",
|
||||
"transparent": "שקוף",
|
||||
"ced4da": "אפור 4",
|
||||
"868e96": "אפור 6",
|
||||
"fa5252": "אדום 6",
|
||||
"e64980": "ורוד 6",
|
||||
"be4bdb": "ענבים 6",
|
||||
"7950f2": "סגול 6",
|
||||
"4c6ef5": "כחול כהה 6",
|
||||
"228be6": "כחול 6",
|
||||
"15aabf": "טורקיז 6",
|
||||
"12b886": "ירקרק 6",
|
||||
"40c057": "ירוק 6",
|
||||
"82c91e": "ליים 6",
|
||||
"fab005": "צהוב 6",
|
||||
"fd7e14": "כתום 6",
|
||||
"000000": "שחור",
|
||||
"343a40": "אפור 8",
|
||||
"495057": "אפור 7",
|
||||
"c92a2a": "אדום 9",
|
||||
"a61e4d": "ורוד 9",
|
||||
"862e9c": "ענבים 9",
|
||||
"5f3dc4": "סגול 9",
|
||||
"364fc7": "כחול כהה 9",
|
||||
"1864ab": "כחול 9",
|
||||
"0b7285": "טורקיז 9",
|
||||
"087f5b": "ירקרק 9",
|
||||
"2b8a3e": "ירוק 9",
|
||||
"5c940d": "ליים 9",
|
||||
"e67700": "ירוק 9",
|
||||
"d9480f": "כתום 9"
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "दृश्य में छवि नहीं है",
|
||||
"cannotRestoreFromImage": "छवि फ़ाइल बहाल दृश्य नहीं है",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "चयन",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "कई बिंदुओं को शुरू करने के लिए क्लिक करें, सिंगल लाइन के लिए खींचें",
|
||||
"freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो",
|
||||
"text": "आप चयन टूल से कहीं भी डबल-क्लिक करके टेक्स्ट जोड़ सकते हैं",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "अंतिम बिंदु पर क्लिक करें या समाप्त होने के लिए एस्केप या एंटर दबाएं",
|
||||
"lockAngle": "आप घूर्णन करते समय SHIFT पकड़कर कोणों को मोड़ सकते हैं",
|
||||
"resize": "आकार बदलते समय आप SHIFT को पकड़ कर अनुपात में कमी कर सकते हैं,\nकेंद्र से आकार बदलने के लिए ALT दबाए रखें",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "हमारा ब्लॉग पढे",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "वक्र तीर",
|
||||
"curvedLine": "वक्र रेखा",
|
||||
"documentation": "",
|
||||
"doubleClick": "",
|
||||
"drag": "खींचें",
|
||||
"editor": "संपादक",
|
||||
"editSelectedShape": "",
|
||||
"github": "मुद्दा मिला? प्रस्तुत करें",
|
||||
"howto": "हमारे गाइड का पालन करें",
|
||||
"or": "या",
|
||||
"preventBinding": "तीर बंधन रोकें",
|
||||
"shapes": "आकृतियाँ",
|
||||
"shortcuts": "कीबोर्ड के शॉर्टकट्स",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"textFinish": "संपादन समाप्त करें (पाठ)",
|
||||
"textNewLine": "नई पंक्ति जोड़ें (पाठ)",
|
||||
"title": "मदद",
|
||||
"view": "दृश्य",
|
||||
"zoomToFit": "सभी तत्वों को फिट करने के लिए ज़ूम करें",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"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.",
|
||||
"cannotRestoreFromImage": "A jelenet visszaállítása nem sikerült ebből a kép fájlból",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Kijelölés",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Kattintással görbe, az eger húzásával pedig egyenes nyilat rajzolhatsz",
|
||||
"freeDraw": "Kattints és húzd, majd engedd el, amikor végeztél",
|
||||
"text": "Tipp: A kijelölés eszközzel a dupla kattintás új szöveget hoz létre",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "Kattints a következő ív pozíciójára, vagy fejezd be a nyilat az Escape vagy Enter megnyomásával",
|
||||
"lockAngle": "A SHIFT billentyű lenyomva tartásával korlátozhatja forgatás szögét",
|
||||
"resize": "A SHIFT billentyű lenyomva tartásával az átméretezés megtartja az arányokat,\naz ALT lenyomva tartásával pedig a középpont egy helyben marad",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "",
|
||||
@@ -239,10 +231,8 @@
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"doubleClick": "",
|
||||
"drag": "",
|
||||
"editor": "",
|
||||
"editSelectedShape": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Mode tampilan",
|
||||
"toggleExportColorScheme": "Ubah skema warna ekspor",
|
||||
"share": "Bagikan",
|
||||
"showStroke": "Tampilkan garis pengambil warna",
|
||||
"showBackground": "Tampilkan latar pengambil warna",
|
||||
"toggleTheme": "Ubah tema"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"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?",
|
||||
"cannotRestoreFromImage": "Pemandangan tidak dapat dipulihkan dari file gambar ini",
|
||||
"invalidSceneUrl": "Tidak dapat impor pemandangan dari URL. Kemungkinan URL itu rusak atau tidak berisi data JSON Excalidraw yang valid.",
|
||||
"resetLibrary": "Ini akan menghapus pustaka Anda. Anda yakin?",
|
||||
"invalidEncryptionKey": "Sandi enkripsi harus 22 karakter. Kolaborasi langsung dinonaktifkan."
|
||||
"resetLibrary": "Ini akan menghapus pustaka Anda. Anda yakin?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Pilihan",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Klik untuk memulai banyak poin, seret untuk satu baris",
|
||||
"freeDraw": "Klik dan seret, lepaskan jika Anda selesai",
|
||||
"text": "Tip: Anda juga dapat menambahkan teks dengan klik ganda di mana saja dengan alat pemilihan",
|
||||
"text_selected": "Klik ganda atau tekan ENTER untuk edit teks",
|
||||
"text_editing": "Tekan Escape atau CtrlAtauCmd+ENTER untuk selesai mengedit",
|
||||
"linearElementMulti": "Klik pada titik akhir atau tekan Escape atau Enter untuk menyelesaikan",
|
||||
"lockAngle": "Anda dapat menjaga sudut dengan menahan SHIFT",
|
||||
"resize": "Anda dapat menjaga proposi dengan menekan SHIFT sambil mengubah ukuran,\ntekan AlT untuk mengubah ukuran dari tengah",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "Simpan ke file",
|
||||
"link_title": "Tautan",
|
||||
"link_details": "Ekspor sebagai tautan yang hanya dibaca.",
|
||||
"link_button": "Ekspor ke tautan",
|
||||
"excalidrawplus_description": "Simpan pemandangan ke ruang kerja Excalidraw+ Anda.",
|
||||
"excalidrawplus_button": "Ekspor",
|
||||
"excalidrawplus_exportError": "Tidak dapat ekspor ke Excalidraw+ saat ini..."
|
||||
"link_button": "Ekspor ke tautan"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Baca blog kami",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Panah lengkung",
|
||||
"curvedLine": "Garis lengkung",
|
||||
"documentation": "Dokumentasi",
|
||||
"doubleClick": "klik-ganda",
|
||||
"drag": "seret",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Edit bentuk yang dipilih (teks/panah/garis)",
|
||||
"github": "Menemukan masalah? Kirimkan",
|
||||
"howto": "Ikuti panduan kami",
|
||||
"or": "atau",
|
||||
"preventBinding": "Cegah pengikatan panah",
|
||||
"shapes": "Bentuk",
|
||||
"shortcuts": "Pintasan keyboard",
|
||||
"textFinish": "Selesai mengedit (editor teks)",
|
||||
"textNewLine": "Tambahkan garis baru (editor teks)",
|
||||
"textFinish": "Selesai mengedit (teks)",
|
||||
"textNewLine": "Tambahkan baris baru (teks)",
|
||||
"title": "Bantuan",
|
||||
"view": "Tampilan",
|
||||
"zoomToFit": "Perbesar agar sesuai dengan semua elemen",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Disimpan ke {filename}",
|
||||
"canvas": "kanvas",
|
||||
"selection": "pilihan"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Putih",
|
||||
"f8f9fa": "Abu-abu 0",
|
||||
"f1f3f5": "Abu-abu 1",
|
||||
"fff5f5": "Merah 0",
|
||||
"fff0f6": "Merah muda 0",
|
||||
"f8f0fc": "Ungu 0",
|
||||
"f3f0ff": "Violet 0",
|
||||
"edf2ff": "Indigo 0",
|
||||
"e7f5ff": "Biru 0",
|
||||
"e3fafc": "Cyan 0",
|
||||
"e6fcf5": "Teal 0",
|
||||
"ebfbee": "Hijau 0",
|
||||
"f4fce3": "Lime 0",
|
||||
"fff9db": "Kuning 0",
|
||||
"fff4e6": "Jingga 0",
|
||||
"transparent": "Transparan",
|
||||
"ced4da": "Abu-abu 4",
|
||||
"868e96": "Abu-abu 6",
|
||||
"fa5252": "Merah 6",
|
||||
"e64980": "Merah muda 6",
|
||||
"be4bdb": "Ungu 6",
|
||||
"7950f2": "Violet 6",
|
||||
"4c6ef5": "Indigo 6",
|
||||
"228be6": "Biru 6",
|
||||
"15aabf": "Cyan 6",
|
||||
"12b886": "Teal 6",
|
||||
"40c057": "Hijau 6",
|
||||
"82c91e": "Lime 6",
|
||||
"fab005": "Kuning 6",
|
||||
"fd7e14": "Jingga 6",
|
||||
"000000": "Hitam",
|
||||
"343a40": "Abu-abu 8",
|
||||
"495057": "Abu-abu 7",
|
||||
"c92a2a": "Merah 9",
|
||||
"a61e4d": "Merah muda 9",
|
||||
"862e9c": "Ungu 9",
|
||||
"5f3dc4": "Violet 9",
|
||||
"364fc7": "Indigo 9",
|
||||
"1864ab": "Biru 9",
|
||||
"0b7285": "Cyan 9",
|
||||
"087f5b": "Teal 9",
|
||||
"2b8a3e": "Hijau 9",
|
||||
"5c940d": "Lime 9",
|
||||
"e67700": "Kuning 9",
|
||||
"d9480f": "Jingga 9"
|
||||
}
|
||||
}
|
||||
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Modalità visualizzazione",
|
||||
"toggleExportColorScheme": "Cambia lo schema di colori in esportazione",
|
||||
"share": "Condividi",
|
||||
"showStroke": "Mostra selettore colore del tratto",
|
||||
"showBackground": "Mostra selettore colore di sfondo",
|
||||
"toggleTheme": "Cambia tema"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"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?",
|
||||
"cannotRestoreFromImage": "Impossibile ripristinare la scena da questo file immagine",
|
||||
"invalidSceneUrl": "Impossibile importare la scena dall'URL fornito. Potrebbe essere malformato o non contenere dati JSON Excalidraw validi.",
|
||||
"resetLibrary": "Questa azione cancellerà l'intera libreria. Sei sicuro?",
|
||||
"invalidEncryptionKey": "La chiave di cifratura deve essere composta da 22 caratteri. La collaborazione live è disabilitata."
|
||||
"resetLibrary": "Questa azione cancellerà l'intera libreria. Sei sicuro?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selezione",
|
||||
@@ -167,7 +164,7 @@
|
||||
"ellipse": "Ellisse",
|
||||
"arrow": "Freccia",
|
||||
"line": "Linea",
|
||||
"freedraw": "Disegno",
|
||||
"freedraw": "",
|
||||
"text": "Testo",
|
||||
"library": "Libreria",
|
||||
"lock": "Mantieni lo strumento selezionato attivo dopo aver disegnato"
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "Clicca per iniziare una linea in più punti, trascina per singola linea",
|
||||
"freeDraw": "Clicca e trascina, rilascia quando avrai finito",
|
||||
"text": "Suggerimento: puoi anche aggiungere del testo facendo doppio clic ovunque con lo strumento di selezione",
|
||||
"text_selected": "Fai doppio click o premi INVIO per modificare il testo",
|
||||
"text_editing": "Premi ESC o CtrlOCmd+INVIO per completare le modifiche",
|
||||
"linearElementMulti": "Clicca sull'ultimo punto o premi Esc o Invio per finire",
|
||||
"lockAngle": "Puoi limitare l'angolo tenendo premuto SHIFT",
|
||||
"resize": "Per vincolare le proporzioni, tieni premuto MAIUSC durante il ridimensionamento;\nper ridimensionare dal centro, tieni premuto ALT",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "Salva su file",
|
||||
"link_title": "Link condivisibile",
|
||||
"link_details": "Esporta come link di sola lettura.",
|
||||
"link_button": "Esporta come Link",
|
||||
"excalidrawplus_description": "Salva la scena nel tuo spazio di lavoro Excalidraw+.",
|
||||
"excalidrawplus_button": "Esporta",
|
||||
"excalidrawplus_exportError": "Non è stato possibile esportare su Excalidraw+ al questo momento..."
|
||||
"link_button": "Esporta come Link"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Leggi il nostro blog",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Freccia curva",
|
||||
"curvedLine": "Linea curva",
|
||||
"documentation": "Documentazione",
|
||||
"doubleClick": "doppio-click",
|
||||
"drag": "trascina",
|
||||
"editor": "Editor",
|
||||
"editSelectedShape": "Modifica la forma selezionata (testo/freccia/linea)",
|
||||
"github": "Trovato un problema? Segnalalo",
|
||||
"howto": "Segui le nostre guide",
|
||||
"or": "oppure",
|
||||
"preventBinding": "Impedisci legame della freccia",
|
||||
"shapes": "Forme",
|
||||
"shortcuts": "Scorciatoie da tastiera",
|
||||
"textFinish": "Completa la modifica (editor di testo)",
|
||||
"textNewLine": "Aggiungi nuova riga (editor di testo)",
|
||||
"textFinish": "Termina la modifica (testo)",
|
||||
"textNewLine": "Aggiungi nuova riga (testo)",
|
||||
"title": "Guida",
|
||||
"view": "Vista",
|
||||
"zoomToFit": "Adatta zoom per mostrare tutti gli elementi",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Salvato in {filename}",
|
||||
"canvas": "tela",
|
||||
"selection": "selezione"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Bianco",
|
||||
"f8f9fa": "Grigio 0",
|
||||
"f1f3f5": "Grigio 1",
|
||||
"fff5f5": "Rosso 0",
|
||||
"fff0f6": "Rosa 0",
|
||||
"f8f0fc": "Uva 0",
|
||||
"f3f0ff": "Viola 0",
|
||||
"edf2ff": "Indaco 0",
|
||||
"e7f5ff": "Blu 0",
|
||||
"e3fafc": "Ciano 0",
|
||||
"e6fcf5": "Verde acqua 0",
|
||||
"ebfbee": "Verde 0",
|
||||
"f4fce3": "Lime 0",
|
||||
"fff9db": "Giallo 0",
|
||||
"fff4e6": "Arancio 0",
|
||||
"transparent": "Trasparente",
|
||||
"ced4da": "Grigio 4",
|
||||
"868e96": "Grigio 6",
|
||||
"fa5252": "Rosso 6",
|
||||
"e64980": "Rosa 6",
|
||||
"be4bdb": "Uva 6",
|
||||
"7950f2": "Viola 6",
|
||||
"4c6ef5": "Indaco 6",
|
||||
"228be6": "Blu 6",
|
||||
"15aabf": "Ciano 6",
|
||||
"12b886": "Verde acqua 6",
|
||||
"40c057": "Verde 6",
|
||||
"82c91e": "Lime 6",
|
||||
"fab005": "Giallo 6",
|
||||
"fd7e14": "Arancio 6",
|
||||
"000000": "Nero",
|
||||
"343a40": "Grigio 8",
|
||||
"495057": "Grigio 7",
|
||||
"c92a2a": "Rosso 9",
|
||||
"a61e4d": "Rosa 9",
|
||||
"862e9c": "Uva 9",
|
||||
"5f3dc4": "Viola 9",
|
||||
"364fc7": "Indaco 9",
|
||||
"1864ab": "Blu 9",
|
||||
"0b7285": "Ciano 9",
|
||||
"087f5b": "Verde acqua 9",
|
||||
"2b8a3e": "Verde 9",
|
||||
"5c940d": "Lime 9",
|
||||
"e67700": "Giallo 9",
|
||||
"d9480f": "Arancio 9"
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@
|
||||
"strokeWidth": "線の幅",
|
||||
"strokeShape": "ストロークの形状",
|
||||
"strokeShape_gel": "ジェルペン",
|
||||
"strokeShape_fountain": "万年筆",
|
||||
"strokeShape_fountain": "噴水ペン",
|
||||
"strokeShape_brush": "ブラシペン",
|
||||
"strokeStyle": "線の種類",
|
||||
"strokeStyle_solid": "実線",
|
||||
@@ -43,7 +43,7 @@
|
||||
"fontFamily": "フォントの種類",
|
||||
"onlySelected": "選択中のみ",
|
||||
"withBackground": "背景",
|
||||
"exportEmbedScene": "埋め込みシーン",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "シーンデータはエクスポートされたPNG/SVGファイルに保存され、シーンを復元することができます。\nエクスポートされたファイルのサイズは増加します。",
|
||||
"addWatermark": "\"Made with Excalidraw\"と表示",
|
||||
"handDrawn": "手描き風",
|
||||
@@ -99,10 +99,8 @@
|
||||
"flipHorizontal": "水平方向に反転",
|
||||
"flipVertical": "垂直方向に反転",
|
||||
"viewMode": "閲覧モード",
|
||||
"toggleExportColorScheme": "エクスポートカラースキームの切り替え",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "共有",
|
||||
"showStroke": "ストロークカラーピッカーを表示",
|
||||
"showBackground": "背景色ピッカーを表示",
|
||||
"toggleTheme": "テーマの切り替え"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "現在、画像のインポートはサポートされていません。\n\nシーンをインポートしようとしましたか?この画像にはシーンデータが含まれていないようです。エクスポート中に有効にしていましたか?",
|
||||
"cannotRestoreFromImage": "このイメージファイルからシーンを復元できませんでした",
|
||||
"invalidSceneUrl": "指定された URL からシーンをインポートできませんでした。不正な形式であるか、有効な Excalidraw JSON データが含まれていません。",
|
||||
"resetLibrary": "ライブラリを消去します。本当によろしいですか?",
|
||||
"invalidEncryptionKey": "暗号化キーは22文字でなければなりません。ライブコラボレーションは無効化されています。"
|
||||
"resetLibrary": "ライブラリを消去します。本当によろしいですか?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "選択",
|
||||
@@ -178,11 +175,9 @@
|
||||
"shapes": "図形"
|
||||
},
|
||||
"hints": {
|
||||
"linearElement": "クリックして複数の点を開始し、ドラッグで直線を引きます。",
|
||||
"linearElement": "クリックして複数の点を開始し、1行にドラッグします。",
|
||||
"freeDraw": "クリックしてドラッグします。離すと終了します",
|
||||
"text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます",
|
||||
"text_selected": "テキストを編集するには、ダブルクリックまたはEnterキーを押します",
|
||||
"text_editing": "Esc キーまたは CtrlOrCmd+ENTER キーを押して編集を終了します",
|
||||
"linearElementMulti": "最後のポイントをクリックするか、エスケープまたはEnterを押して終了します",
|
||||
"lockAngle": "SHIFTを押したままにすると、角度を制限することができます",
|
||||
"resize": "サイズを変更中にSHIFTを押しすと比率を制御できます。Altを押すと中央からサイズを変更できます。",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "ファイルへ保存",
|
||||
"link_title": "共有可能なリンク",
|
||||
"link_details": "読み取り専用リンクとしてエクスポート",
|
||||
"link_button": "リンクとしてエクスポート",
|
||||
"excalidrawplus_description": "Excalidraw+ ワークスペースにシーンを保存します。",
|
||||
"excalidrawplus_button": "エクスポート",
|
||||
"excalidrawplus_exportError": "Excalidraw+ にエクスポートできませんでした..."
|
||||
"link_button": "リンクとしてエクスポート"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "公式ブログを読む",
|
||||
@@ -239,17 +231,15 @@
|
||||
"curvedArrow": "カーブした矢印",
|
||||
"curvedLine": "曲線",
|
||||
"documentation": "ドキュメント",
|
||||
"doubleClick": "ダブルクリック",
|
||||
"drag": "ドラッグ",
|
||||
"editor": "エディタ",
|
||||
"editSelectedShape": "選択した図形の編集 (テキスト/矢印/線)",
|
||||
"github": "不具合報告はこちら",
|
||||
"howto": "ヘルプ・マニュアル",
|
||||
"or": "または",
|
||||
"preventBinding": "矢印を結合しない",
|
||||
"shapes": "図形",
|
||||
"shortcuts": "キーボードショートカット",
|
||||
"textFinish": "編集を終了 (テキストエディタ)",
|
||||
"textFinish": "編集を終了する (テキスト)",
|
||||
"textNewLine": "新しい行を追加 (テキスト)",
|
||||
"title": "ヘルプ",
|
||||
"view": "表示",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "{filename} に保存しました",
|
||||
"canvas": "キャンバス",
|
||||
"selection": "選択"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "ホワイト",
|
||||
"f8f9fa": "グレー 0",
|
||||
"f1f3f5": "グレー 1",
|
||||
"fff5f5": "レッド 0",
|
||||
"fff0f6": "ピンク 0",
|
||||
"f8f0fc": "グレープ 0",
|
||||
"f3f0ff": "バイオレット 0",
|
||||
"edf2ff": "インディゴ 0",
|
||||
"e7f5ff": "ブルー 0",
|
||||
"e3fafc": "シアン 0",
|
||||
"e6fcf5": "ティール 0",
|
||||
"ebfbee": "グリーン 0",
|
||||
"f4fce3": "ライム 0",
|
||||
"fff9db": "イエロー 0",
|
||||
"fff4e6": "オレンジ 0",
|
||||
"transparent": "透明",
|
||||
"ced4da": "グレー 4",
|
||||
"868e96": "グレー 6",
|
||||
"fa5252": "レッド 6",
|
||||
"e64980": "ピンク 6",
|
||||
"be4bdb": "グレープ 6",
|
||||
"7950f2": "バイオレット 6",
|
||||
"4c6ef5": "インディゴ 6",
|
||||
"228be6": "ブルー 6",
|
||||
"15aabf": "シアン 6",
|
||||
"12b886": "ティール 6",
|
||||
"40c057": "グリーン 6",
|
||||
"82c91e": "ライム 6",
|
||||
"fab005": "イエロー 6",
|
||||
"fd7e14": "オレンジ 6",
|
||||
"000000": "ブラック",
|
||||
"343a40": "グレー 8",
|
||||
"495057": "グレー 7",
|
||||
"c92a2a": "レッド 9",
|
||||
"a61e4d": "ピンク 9",
|
||||
"862e9c": "グレープ 9",
|
||||
"5f3dc4": "バイオレット 9",
|
||||
"364fc7": "インディゴ 9",
|
||||
"1864ab": "ブルー 9",
|
||||
"0b7285": "シアン 9",
|
||||
"087f5b": "ティール 9",
|
||||
"2b8a3e": "グリーン 9",
|
||||
"5c940d": "ライム 9",
|
||||
"e67700": "イエロー 9",
|
||||
"d9480f": "オレンジ 9"
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
"background": "Agilal",
|
||||
"fill": "Taččart",
|
||||
"strokeWidth": "Tehri n yizirig",
|
||||
"strokeShape": "Talɣa n yizirig",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "Amfezzu",
|
||||
@@ -101,8 +101,6 @@
|
||||
"viewMode": "Askar n tmuɣli",
|
||||
"toggleExportColorScheme": "Sermed/sens asifeḍ usentel n yini",
|
||||
"share": "Bḍu",
|
||||
"showStroke": "Beqqeḍ amelqaḍ n yini n yizirig",
|
||||
"showBackground": "Beqqeḍ amelqaḍ n yini n ugilal",
|
||||
"toggleTheme": "Snifel asentel"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"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",
|
||||
"invalidSceneUrl": "Ulamek taktert n usayes seg URL i d-ittunefken. Ahat mačči d tameɣtut neɣ ur tegbir ara isefka JSON n Excalidraw.",
|
||||
"resetLibrary": "Ayagi ad isfeḍ tamkarḍit-inek•m. Tetḥeqqeḍ?",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": "Ayagi ad isfeḍ tamkarḍit-inek•m. Tetḥeqqeḍ?"
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Tafrayt",
|
||||
@@ -181,8 +178,6 @@
|
||||
"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",
|
||||
"text_selected": "Ssit snat n tikkal neɣ ssed taqeffalt Kcem akken ad tẓergeḍ aḍris",
|
||||
"text_editing": "Ssit Escape neɣ CtrlOrCmd+ENTER akken ad tfakkeḍ asiẓreg",
|
||||
"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",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "Sekles deg ufaylu",
|
||||
"link_title": "Aseɣwen n beṭṭu",
|
||||
"link_details": "Sifeḍ am useɣwen n tɣuri kan.",
|
||||
"link_button": "Sifeḍ deg useɣwen",
|
||||
"excalidrawplus_description": "Sekles asayes-inek•inem di tallunt n umahil Excalidraw+.",
|
||||
"excalidrawplus_button": "Sifeḍ",
|
||||
"excalidrawplus_exportError": "Ulamek asifeḍ ɣer Excalidraw+ akka tura..."
|
||||
"link_button": "Sifeḍ deg useɣwen"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Ɣeṛ ablug-nneɣ",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "Taneccabt izelgen",
|
||||
"curvedLine": "Izirig izelgen",
|
||||
"documentation": "Tasemlit",
|
||||
"doubleClick": "ssit snat n tikkal",
|
||||
"drag": "zuɣer",
|
||||
"editor": "Amaẓrag",
|
||||
"editSelectedShape": "Ẓreg talɣa yettwafernen (aḍris/taneccabt/izirig)",
|
||||
"github": "Tufiḍ-d ugur? Azen-aɣ-d",
|
||||
"howto": "Ḍfer imniren-nneɣ",
|
||||
"or": "neɣ",
|
||||
"preventBinding": "Seḥbes tuqqna n tneccabin",
|
||||
"shapes": "Talɣiwin",
|
||||
"shortcuts": "Inegzumen n unasiw",
|
||||
"textFinish": "Fak asiẓreg (amaẓrag n uḍris)",
|
||||
"textNewLine": "Rnu ajerriḍ amaynut (amaẓrag n uḍris)",
|
||||
"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",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "Yettwasekles di {filename}",
|
||||
"canvas": "taɣzut n usuneɣ",
|
||||
"selection": "tafrayt"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Amellal",
|
||||
"f8f9fa": "Aɣiɣdi 0",
|
||||
"f1f3f5": "Aɣiɣdi 1",
|
||||
"fff5f5": "Azeggaɣ",
|
||||
"fff0f6": "Axuxi 0",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "Amidadi 0",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "Anili 0",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "Azegzaw 0",
|
||||
"f4fce3": "",
|
||||
"fff9db": "Awraɣ 0",
|
||||
"fff4e6": "Aččinawi 0",
|
||||
"transparent": "Afrawan",
|
||||
"ced4da": "Aɣiɣdi 4",
|
||||
"868e96": "Aɣiɣdi 6",
|
||||
"fa5252": "Azeggaɣ 6",
|
||||
"e64980": "Axuxi 6",
|
||||
"be4bdb": "",
|
||||
"7950f2": "Amidadi 6",
|
||||
"4c6ef5": "",
|
||||
"228be6": "Anili 6",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "Azegzaw 0",
|
||||
"82c91e": "",
|
||||
"fab005": "Awraɣ 6",
|
||||
"fd7e14": "Aččinawi 6",
|
||||
"000000": "Aberkan",
|
||||
"343a40": "Aɣiɣdi 8",
|
||||
"495057": "Aɣiɣdi 7",
|
||||
"c92a2a": "Azeggaɣ 9",
|
||||
"a61e4d": "Axuxi 9",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "Amidadi 9",
|
||||
"364fc7": "",
|
||||
"1864ab": "Anili 9",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "Azegzaw 9",
|
||||
"5c940d": "",
|
||||
"e67700": "Awraɣ 9",
|
||||
"d9480f": "Aččinawi 9"
|
||||
}
|
||||
}
|
||||
|
@@ -1,334 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Қою",
|
||||
"pasteCharts": "Диаграммаларды қою",
|
||||
"selectAll": "Бәрін таңдау",
|
||||
"multiSelect": "",
|
||||
"moveCanvas": "",
|
||||
"cut": "Қию",
|
||||
"copy": "Көшіру",
|
||||
"copyAsPng": "",
|
||||
"copyAsSvg": "",
|
||||
"bringForward": "",
|
||||
"sendToBack": "",
|
||||
"bringToFront": "",
|
||||
"sendBackward": "",
|
||||
"delete": "Жою",
|
||||
"copyStyles": "Стильдерді көшіру",
|
||||
"pasteStyles": "Стильдерді қою",
|
||||
"stroke": "",
|
||||
"background": "",
|
||||
"fill": "",
|
||||
"strokeWidth": "",
|
||||
"strokeShape": "",
|
||||
"strokeShape_gel": "",
|
||||
"strokeShape_fountain": "",
|
||||
"strokeShape_brush": "",
|
||||
"strokeStyle": "",
|
||||
"strokeStyle_solid": "",
|
||||
"strokeStyle_dashed": "",
|
||||
"strokeStyle_dotted": "",
|
||||
"sloppiness": "",
|
||||
"opacity": "",
|
||||
"textAlign": "",
|
||||
"edges": "",
|
||||
"sharp": "",
|
||||
"round": "",
|
||||
"arrowheads": "Нұсқар ұштары",
|
||||
"arrowhead_none": "Жоқ",
|
||||
"arrowhead_arrow": "Нұсқар",
|
||||
"arrowhead_bar": "Тосқауыл",
|
||||
"arrowhead_dot": "Нүкте",
|
||||
"fontSize": "Қаріп өлшемі",
|
||||
"fontFamily": "Қаріп тобы",
|
||||
"onlySelected": "",
|
||||
"withBackground": "",
|
||||
"exportEmbedScene": "",
|
||||
"exportEmbedScene_details": "",
|
||||
"addWatermark": "",
|
||||
"handDrawn": "",
|
||||
"normal": "Қалыпты",
|
||||
"code": "",
|
||||
"small": "Кіші",
|
||||
"medium": "Орта",
|
||||
"large": "Үлкен",
|
||||
"veryLarge": "Өте үлкен",
|
||||
"solid": "",
|
||||
"hachure": "",
|
||||
"crossHatch": "",
|
||||
"thin": "",
|
||||
"bold": "",
|
||||
"left": "Солға",
|
||||
"center": "Ортаға",
|
||||
"right": "Оңға",
|
||||
"extraBold": "",
|
||||
"architect": "",
|
||||
"artist": "",
|
||||
"cartoonist": "",
|
||||
"fileTitle": "Файл атауы",
|
||||
"colorPicker": "",
|
||||
"canvasBackground": "",
|
||||
"drawingCanvas": "",
|
||||
"layers": "",
|
||||
"actions": "",
|
||||
"language": "Тіл",
|
||||
"liveCollaboration": "",
|
||||
"duplicateSelection": "Көшірме",
|
||||
"untitled": "Атауысыз",
|
||||
"name": "",
|
||||
"yourName": "",
|
||||
"madeWithExcalidraw": "",
|
||||
"group": "",
|
||||
"ungroup": "",
|
||||
"collaborators": "",
|
||||
"showGrid": "",
|
||||
"addToLibrary": "",
|
||||
"removeFromLibrary": "",
|
||||
"libraryLoadingMessage": "",
|
||||
"libraries": "",
|
||||
"loadingScene": "",
|
||||
"align": "",
|
||||
"alignTop": "",
|
||||
"alignBottom": "",
|
||||
"alignLeft": "",
|
||||
"alignRight": "",
|
||||
"centerVertically": "",
|
||||
"centerHorizontally": "",
|
||||
"distributeHorizontally": "",
|
||||
"distributeVertically": "",
|
||||
"flipHorizontal": "",
|
||||
"flipVertical": "",
|
||||
"viewMode": "",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "",
|
||||
"exportJSON": "",
|
||||
"exportImage": "",
|
||||
"export": "Экспорт",
|
||||
"exportToPng": "",
|
||||
"exportToSvg": "",
|
||||
"copyToClipboard": "",
|
||||
"copyPngToClipboard": "",
|
||||
"scale": "",
|
||||
"save": "",
|
||||
"saveAs": "",
|
||||
"load": "",
|
||||
"getShareableLink": "",
|
||||
"close": "Жабу",
|
||||
"selectLanguage": "Тілді таңдау",
|
||||
"scrollBackToContent": "",
|
||||
"zoomIn": "",
|
||||
"zoomOut": "",
|
||||
"resetZoom": "",
|
||||
"menu": "Mәзір",
|
||||
"done": "Дайын",
|
||||
"edit": "",
|
||||
"undo": "",
|
||||
"redo": "",
|
||||
"resetLibrary": "",
|
||||
"createNewRoom": "",
|
||||
"fullScreen": "",
|
||||
"darkMode": "",
|
||||
"lightMode": "",
|
||||
"zenMode": "",
|
||||
"exitZenMode": ""
|
||||
},
|
||||
"alerts": {
|
||||
"clearReset": "",
|
||||
"couldNotCreateShareableLink": "",
|
||||
"couldNotCreateShareableLinkTooBig": "",
|
||||
"couldNotLoadInvalidFile": "",
|
||||
"importBackendFailed": "",
|
||||
"cannotExportEmptyCanvas": "",
|
||||
"couldNotCopyToClipboard": "",
|
||||
"decryptFailed": "",
|
||||
"uploadedSecurly": "",
|
||||
"loadSceneOverridePrompt": "",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "",
|
||||
"errorAddingToLibrary": "",
|
||||
"errorRemovingFromLibrary": "",
|
||||
"confirmAddLibrary": "",
|
||||
"imageDoesNotContainScene": "",
|
||||
"cannotRestoreFromImage": "",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "",
|
||||
"rectangle": "",
|
||||
"diamond": "",
|
||||
"ellipse": "",
|
||||
"arrow": "Нұсқар",
|
||||
"line": "",
|
||||
"freedraw": "",
|
||||
"text": "Мәтін",
|
||||
"library": "",
|
||||
"lock": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "",
|
||||
"selectedShapeActions": "",
|
||||
"shapes": ""
|
||||
},
|
||||
"hints": {
|
||||
"linearElement": "",
|
||||
"freeDraw": "",
|
||||
"text": "",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "",
|
||||
"lockAngle": "",
|
||||
"resize": "",
|
||||
"rotate": "",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
"canvasTooBig": "",
|
||||
"canvasTooBigTip": ""
|
||||
},
|
||||
"errorSplash": {
|
||||
"headingMain_pre": "",
|
||||
"headingMain_button": "",
|
||||
"clearCanvasMessage": "",
|
||||
"clearCanvasMessage_button": "",
|
||||
"clearCanvasCaveat": "",
|
||||
"trackedToSentry_pre": "",
|
||||
"trackedToSentry_post": "",
|
||||
"openIssueMessage_pre": "",
|
||||
"openIssueMessage_button": "",
|
||||
"openIssueMessage_post": "",
|
||||
"sceneContent": ""
|
||||
},
|
||||
"roomDialog": {
|
||||
"desc_intro": "",
|
||||
"desc_privacy": "",
|
||||
"button_startSession": "",
|
||||
"button_stopSession": "",
|
||||
"desc_inProgressIntro": "",
|
||||
"desc_shareLink": "",
|
||||
"desc_exitSession": "",
|
||||
"shareTitle": ""
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Қате"
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "Сахна деректерін кейін қайта импорттауға болатын файлға экспорттаңыз.",
|
||||
"disk_button": "Файлға сақтау",
|
||||
"link_title": "Ортақ сілтеме",
|
||||
"link_details": "Тек оқуға арналған сілтеме ретінде экспорттау.",
|
||||
"link_button": "Сілтемеге экспорттау",
|
||||
"excalidrawplus_description": "Сахнаны өзіңіздің Excalidraw+ жұмыс кеңістігінде сақтаңыз.",
|
||||
"excalidrawplus_button": "Экспорт",
|
||||
"excalidrawplus_exportError": "Қазіргі уақытта Excalidraw+ үшін экспорттау мүмкін емес..."
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "Біздің блогты оқу",
|
||||
"click": "шерту",
|
||||
"curvedArrow": "Майысқан нұсқар",
|
||||
"curvedLine": "Майысқан сызық",
|
||||
"documentation": "Құжаттама",
|
||||
"doubleClick": "қос шерту",
|
||||
"drag": "апару",
|
||||
"editor": "Өңдеу",
|
||||
"editSelectedShape": "Таңдалған пішінді өңдеу (мәтін/нұсқар/сызық)",
|
||||
"github": "Қате таптыңыз ба? Жолдаңыз",
|
||||
"howto": "Біздің нұсқаулықтарды орындаңыз",
|
||||
"or": "немесе",
|
||||
"preventBinding": "Нұсқарды байланыстыруға жол бермеу",
|
||||
"shapes": "Пішіндер",
|
||||
"shortcuts": "Пернетақта пәрмендері",
|
||||
"textFinish": "Өңдеуді аяқтау (мәтіндік редактор)",
|
||||
"textNewLine": "Жаңа жолға көшу (мәтіндік редактор)",
|
||||
"title": "Көмек",
|
||||
"view": "Көру",
|
||||
"zoomToFit": "Барлық элементтердің көлеміне сәйкес үлкейту",
|
||||
"zoomToSelection": "Таңдалғанды үлкейту"
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "Сіздің сызбаларыңыз өтпелі шифрлеу арқылы шифрланған, сондықтан Excalidraw серверлері оларды ешқашан көрмейді.",
|
||||
"link": "Excalidraw қолданатын өтпелі шифрлеу туралы блог жазбасы"
|
||||
},
|
||||
"stats": {
|
||||
"angle": "Бұрыш",
|
||||
"element": "Элемент",
|
||||
"elements": "Элементтер",
|
||||
"height": "Биіктігі",
|
||||
"scene": "Сахна",
|
||||
"selected": "Таңдалды",
|
||||
"storage": "Сақтау көлемі",
|
||||
"title": "",
|
||||
"total": "Барлығы",
|
||||
"version": "Нұсқа",
|
||||
"versionCopy": "Көшіру үшін басыңыз",
|
||||
"versionNotAvailable": "Бұл нұсқа қолжетімсіз",
|
||||
"width": "Ені"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Стильдер көшірілді.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "",
|
||||
"fileSaved": "Файл сақталды.",
|
||||
"fileSavedToFilename": "{filename} сақталды",
|
||||
"canvas": "",
|
||||
"selection": "таңдау"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
@@ -101,8 +101,6 @@
|
||||
"viewMode": "보기 모드",
|
||||
"toggleExportColorScheme": "",
|
||||
"share": "",
|
||||
"showStroke": "",
|
||||
"showBackground": "",
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"buttons": {
|
||||
@@ -157,8 +155,7 @@
|
||||
"imageDoesNotContainScene": "이미지에서 불러오기는 현재 지원되지 않습니다.\n\n화면을 불러오려고 하셨나요? 이미지에 화면 정보가 없는 것 같습니다. 내보낼 때 화면을 포함했나요?",
|
||||
"cannotRestoreFromImage": "이미지 파일에서 화면을 복구할 수 없었습니다",
|
||||
"invalidSceneUrl": "",
|
||||
"resetLibrary": "",
|
||||
"invalidEncryptionKey": ""
|
||||
"resetLibrary": ""
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "선택",
|
||||
@@ -181,8 +178,6 @@
|
||||
"linearElement": "여러 점을 연결하려면 클릭하고, 직선을 그리려면 바로 드래그하세요.",
|
||||
"freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.",
|
||||
"text": "팁: 선택 툴로 아무 곳이나 더블 클릭해 텍스트를 추가할 수도 있습니다.",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "마지막 지점을 클릭하거나 Esc 또는 Enter 키를 눌러 완료하세요.",
|
||||
"lockAngle": "SHIFT 키를 누르면서 회전하면 각도를 제한할 수 있습니다.",
|
||||
"resize": "SHIFT 키를 누르면서 조정하면 크기의 비율이 제한됩니다.\nALT를 누르면서 조정하면 중앙을 기준으로 크기를 조정합니다.",
|
||||
@@ -228,10 +223,7 @@
|
||||
"disk_button": "",
|
||||
"link_title": "",
|
||||
"link_details": "",
|
||||
"link_button": "",
|
||||
"excalidrawplus_description": "",
|
||||
"excalidrawplus_button": "",
|
||||
"excalidrawplus_exportError": ""
|
||||
"link_button": ""
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "블로그 읽어보기",
|
||||
@@ -239,18 +231,16 @@
|
||||
"curvedArrow": "곡선 화살표",
|
||||
"curvedLine": "곡선",
|
||||
"documentation": "설명서",
|
||||
"doubleClick": "",
|
||||
"drag": "드래그",
|
||||
"editor": "에디터",
|
||||
"editSelectedShape": "",
|
||||
"github": "문제 제보하기",
|
||||
"howto": "가이드 참고하기",
|
||||
"or": "또는",
|
||||
"preventBinding": "화살표가 붙지 않게 하기",
|
||||
"shapes": "도형",
|
||||
"shortcuts": "키보드 단축키",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"textFinish": "편집 완료 (텍스트)",
|
||||
"textNewLine": "줄바꿈 (텍스트)",
|
||||
"title": "도움말",
|
||||
"view": "보기",
|
||||
"zoomToFit": "모든 요소가 보이도록 확대/축소",
|
||||
@@ -283,52 +273,5 @@
|
||||
"fileSavedToFilename": "",
|
||||
"canvas": "",
|
||||
"selection": ""
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "",
|
||||
"f8f9fa": "",
|
||||
"f1f3f5": "",
|
||||
"fff5f5": "",
|
||||
"fff0f6": "",
|
||||
"f8f0fc": "",
|
||||
"f3f0ff": "",
|
||||
"edf2ff": "",
|
||||
"e7f5ff": "",
|
||||
"e3fafc": "",
|
||||
"e6fcf5": "",
|
||||
"ebfbee": "",
|
||||
"f4fce3": "",
|
||||
"fff9db": "",
|
||||
"fff4e6": "",
|
||||
"transparent": "",
|
||||
"ced4da": "",
|
||||
"868e96": "",
|
||||
"fa5252": "",
|
||||
"e64980": "",
|
||||
"be4bdb": "",
|
||||
"7950f2": "",
|
||||
"4c6ef5": "",
|
||||
"228be6": "",
|
||||
"15aabf": "",
|
||||
"12b886": "",
|
||||
"40c057": "",
|
||||
"82c91e": "",
|
||||
"fab005": "",
|
||||
"fd7e14": "",
|
||||
"000000": "",
|
||||
"343a40": "",
|
||||
"495057": "",
|
||||
"c92a2a": "",
|
||||
"a61e4d": "",
|
||||
"862e9c": "",
|
||||
"5f3dc4": "",
|
||||
"364fc7": "",
|
||||
"1864ab": "",
|
||||
"0b7285": "",
|
||||
"087f5b": "",
|
||||
"2b8a3e": "",
|
||||
"5c940d": "",
|
||||
"e67700": "",
|
||||
"d9480f": ""
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user