From a9c5bdb8785be82273761070a270f82cebbe2d44 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Thu, 2 Feb 2023 16:23:39 +0800 Subject: [PATCH 1/7] fix: sort bound text elements to fix text duplication z-index error (#5130) * fix: sort bound text elements to fix text duplication z-index error * improve & sort groups & add tests * fix backtracking and discontiguous groups --------- Co-authored-by: dwelle --- src/actions/actionDuplicateSelection.tsx | 121 +++++- src/element/sortElements.test.ts | 402 ++++++++++++++++++++ src/element/sortElements.ts | 123 ++++++ src/element/textElement.ts | 14 +- src/excalidraw-app/collab/reconciliation.ts | 52 +-- src/tests/zindex.test.tsx | 159 +++++--- src/utils.ts | 8 + 7 files changed, 761 insertions(+), 118 deletions(-) create mode 100644 src/element/sortElements.test.ts create mode 100644 src/element/sortElements.ts diff --git a/src/actions/actionDuplicateSelection.tsx b/src/actions/actionDuplicateSelection.tsx index fcff03f51..e3e5fb85a 100644 --- a/src/actions/actionDuplicateSelection.tsx +++ b/src/actions/actionDuplicateSelection.tsx @@ -16,8 +16,12 @@ import { AppState } from "../types"; import { fixBindingsAfterDuplication } from "../element/binding"; import { ActionResult } from "./types"; import { GRID_SIZE } from "../constants"; -import { bindTextToShapeAfterDuplication } from "../element/textElement"; +import { + bindTextToShapeAfterDuplication, + getBoundTextElement, +} from "../element/textElement"; import { isBoundToContainer } from "../element/typeChecks"; +import { normalizeElementOrder } from "../element/sortElements"; import { DuplicateIcon } from "../components/icons"; export const actionDuplicateSelection = register({ @@ -64,6 +68,11 @@ const duplicateElements = ( elements: readonly ExcalidrawElement[], appState: AppState, ): Partial => { + // --------------------------------------------------------------------------- + + // step (1) + + const sortedElements = normalizeElementOrder(elements); const groupIdMap = new Map(); const newElements: ExcalidrawElement[] = []; const oldElements: ExcalidrawElement[] = []; @@ -85,42 +94,112 @@ const duplicateElements = ( return newElement; }; - const finalElements: ExcalidrawElement[] = []; - - let index = 0; const selectedElementIds = arrayToMap( - getSelectedElements(elements, appState, true), + getSelectedElements(sortedElements, appState, true), ); - while (index < elements.length) { - const element = elements[index]; + + // Ids of elements that have already been processed so we don't push them + // into the array twice if we end up backtracking when retrieving + // discontiguous group of elements (can happen due to a bug, or in edge + // cases such as a group containing deleted elements which were not selected). + // + // This is not enough to prevent duplicates, so we do a second loop afterwards + // to remove them. + // + // For convenience we mark even the newly created ones even though we don't + // loop over them. + const processedIds = new Map(); + + const markAsProcessed = (elements: ExcalidrawElement[]) => { + for (const element of elements) { + processedIds.set(element.id, true); + } + return elements; + }; + + const elementsWithClones: ExcalidrawElement[] = []; + + let index = -1; + + while (++index < sortedElements.length) { + const element = sortedElements[index]; + + if (processedIds.get(element.id)) { + continue; + } + + const boundTextElement = getBoundTextElement(element); if (selectedElementIds.get(element.id)) { - if (element.groupIds.length) { + // if a group or a container/bound-text, duplicate atomically + if (element.groupIds.length || boundTextElement) { const groupId = getSelectedGroupForElement(appState, element); - // if group selected, duplicate it atomically if (groupId) { - const groupElements = getElementsInGroup(elements, groupId); - finalElements.push( - ...groupElements, - ...groupElements.map((element) => - duplicateAndOffsetElement(element), - ), + const groupElements = getElementsInGroup(sortedElements, groupId); + elementsWithClones.push( + ...markAsProcessed([ + ...groupElements, + ...groupElements.map((element) => + duplicateAndOffsetElement(element), + ), + ]), + ); + continue; + } + if (boundTextElement) { + elementsWithClones.push( + ...markAsProcessed([ + element, + boundTextElement, + duplicateAndOffsetElement(element), + duplicateAndOffsetElement(boundTextElement), + ]), ); - index = index + groupElements.length; continue; } } - finalElements.push(element, duplicateAndOffsetElement(element)); + elementsWithClones.push( + ...markAsProcessed([element, duplicateAndOffsetElement(element)]), + ); } else { - finalElements.push(element); + elementsWithClones.push(...markAsProcessed([element])); } - index++; } + + // step (2) + + // second pass to remove duplicates. We loop from the end as it's likelier + // that the last elements are in the correct order (contiguous or otherwise). + // Thus we need to reverse as the last step (3). + + const finalElementsReversed: ExcalidrawElement[] = []; + + const finalElementIds = new Map(); + index = elementsWithClones.length; + + while (--index >= 0) { + const element = elementsWithClones[index]; + if (!finalElementIds.get(element.id)) { + finalElementIds.set(element.id, true); + finalElementsReversed.push(element); + } + } + + // step (3) + + const finalElements = finalElementsReversed.reverse(); + + // --------------------------------------------------------------------------- + bindTextToShapeAfterDuplication( - finalElements, + elementsWithClones, + oldElements, + oldIdToDuplicatedId, + ); + fixBindingsAfterDuplication( + elementsWithClones, oldElements, oldIdToDuplicatedId, ); - fixBindingsAfterDuplication(finalElements, oldElements, oldIdToDuplicatedId); return { elements: finalElements, diff --git a/src/element/sortElements.test.ts b/src/element/sortElements.test.ts new file mode 100644 index 000000000..35cf560ef --- /dev/null +++ b/src/element/sortElements.test.ts @@ -0,0 +1,402 @@ +import { API } from "../tests/helpers/api"; +import { mutateElement } from "./mutateElement"; +import { normalizeElementOrder } from "./sortElements"; +import { ExcalidrawElement } from "./types"; + +const assertOrder = ( + elements: readonly ExcalidrawElement[], + expectedOrder: string[], +) => { + const actualOrder = elements.map((element) => element.id); + expect(actualOrder).toEqual(expectedOrder); +}; + +describe("normalizeElementsOrder", () => { + it("sort bound-text elements", () => { + const container = API.createElement({ + id: "container", + type: "rectangle", + }); + const boundText = API.createElement({ + id: "boundText", + type: "text", + containerId: container.id, + }); + const otherElement = API.createElement({ + id: "otherElement", + type: "rectangle", + boundElements: [], + }); + const otherElement2 = API.createElement({ + id: "otherElement2", + type: "rectangle", + boundElements: [], + }); + + mutateElement(container, { + boundElements: [{ type: "text", id: boundText.id }], + }); + + assertOrder(normalizeElementOrder([container, boundText]), [ + "container", + "boundText", + ]); + assertOrder(normalizeElementOrder([boundText, container]), [ + "container", + "boundText", + ]); + assertOrder( + normalizeElementOrder([ + boundText, + container, + otherElement, + otherElement2, + ]), + ["container", "boundText", "otherElement", "otherElement2"], + ); + assertOrder(normalizeElementOrder([container, otherElement, boundText]), [ + "container", + "boundText", + "otherElement", + ]); + assertOrder( + normalizeElementOrder([ + container, + otherElement, + otherElement2, + boundText, + ]), + ["container", "boundText", "otherElement", "otherElement2"], + ); + + assertOrder( + normalizeElementOrder([ + boundText, + otherElement, + container, + otherElement2, + ]), + ["otherElement", "container", "boundText", "otherElement2"], + ); + + // noop + assertOrder( + normalizeElementOrder([ + otherElement, + container, + boundText, + otherElement2, + ]), + ["otherElement", "container", "boundText", "otherElement2"], + ); + + // text has existing containerId, but container doesn't list is + // as a boundElement + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "boundText", + type: "text", + containerId: "container", + }), + API.createElement({ + id: "container", + type: "rectangle", + }), + ]), + ["boundText", "container"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "boundText", + type: "text", + containerId: "container", + }), + ]), + ["boundText"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "container", + type: "rectangle", + boundElements: [], + }), + ]), + ["container"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "container", + type: "rectangle", + boundElements: [{ id: "x", type: "text" }], + }), + ]), + ["container"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "arrow", + type: "arrow", + }), + API.createElement({ + id: "container", + type: "rectangle", + boundElements: [{ id: "arrow", type: "arrow" }], + }), + ]), + ["arrow", "container"], + ); + }); + + it("normalize group order", () => { + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "A_rect1", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "rect2", + type: "rectangle", + }), + API.createElement({ + id: "rect3", + type: "rectangle", + }), + API.createElement({ + id: "A_rect4", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "A_rect5", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "rect6", + type: "rectangle", + }), + API.createElement({ + id: "A_rect7", + type: "rectangle", + groupIds: ["A"], + }), + ]), + ["A_rect1", "A_rect4", "A_rect5", "A_rect7", "rect2", "rect3", "rect6"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "A_rect1", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "rect2", + type: "rectangle", + }), + API.createElement({ + id: "B_rect3", + type: "rectangle", + groupIds: ["B"], + }), + API.createElement({ + id: "A_rect4", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "B_rect5", + type: "rectangle", + groupIds: ["B"], + }), + API.createElement({ + id: "rect6", + type: "rectangle", + }), + API.createElement({ + id: "A_rect7", + type: "rectangle", + groupIds: ["A"], + }), + ]), + ["A_rect1", "A_rect4", "A_rect7", "rect2", "B_rect3", "B_rect5", "rect6"], + ); + // nested groups + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "A_rect1", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "BA_rect2", + type: "rectangle", + groupIds: ["B", "A"], + }), + ]), + ["A_rect1", "BA_rect2"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "BA_rect1", + type: "rectangle", + groupIds: ["B", "A"], + }), + API.createElement({ + id: "A_rect2", + type: "rectangle", + groupIds: ["A"], + }), + ]), + ["BA_rect1", "A_rect2"], + ); + assertOrder( + normalizeElementOrder([ + API.createElement({ + id: "BA_rect1", + type: "rectangle", + groupIds: ["B", "A"], + }), + API.createElement({ + id: "A_rect2", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "CBA_rect3", + type: "rectangle", + groupIds: ["C", "B", "A"], + }), + API.createElement({ + id: "rect4", + type: "rectangle", + }), + API.createElement({ + id: "A_rect5", + type: "rectangle", + groupIds: ["A"], + }), + API.createElement({ + id: "BA_rect5", + type: "rectangle", + groupIds: ["B", "A"], + }), + API.createElement({ + id: "BA_rect6", + type: "rectangle", + groupIds: ["B", "A"], + }), + API.createElement({ + id: "CBA_rect7", + type: "rectangle", + groupIds: ["C", "B", "A"], + }), + API.createElement({ + id: "X_rect8", + type: "rectangle", + groupIds: ["X"], + }), + API.createElement({ + id: "rect9", + type: "rectangle", + }), + API.createElement({ + id: "YX_rect10", + type: "rectangle", + groupIds: ["Y", "X"], + }), + API.createElement({ + id: "X_rect11", + type: "rectangle", + groupIds: ["X"], + }), + ]), + [ + "BA_rect1", + "BA_rect5", + "BA_rect6", + "A_rect2", + "A_rect5", + "CBA_rect3", + "CBA_rect7", + "rect4", + "X_rect8", + "X_rect11", + "YX_rect10", + "rect9", + ], + ); + }); + + // TODO + it.skip("normalize boundElements array", () => { + const container = API.createElement({ + id: "container", + type: "rectangle", + boundElements: [], + }); + const boundText = API.createElement({ + id: "boundText", + type: "text", + containerId: container.id, + }); + + mutateElement(container, { + boundElements: [ + { type: "text", id: boundText.id }, + { type: "text", id: "xxx" }, + ], + }); + + expect(normalizeElementOrder([container, boundText])).toEqual([ + expect.objectContaining({ + id: container.id, + }), + expect.objectContaining({ id: boundText.id }), + ]); + }); + + // should take around <100ms for 10K iterations (@dwelle's PC 22-05-25) + it.skip("normalizeElementsOrder() perf", () => { + const makeElements = (iterations: number) => { + const elements: ExcalidrawElement[] = []; + while (iterations--) { + const container = API.createElement({ + type: "rectangle", + boundElements: [], + groupIds: ["B", "A"], + }); + const boundText = API.createElement({ + type: "text", + containerId: container.id, + groupIds: ["A"], + }); + const otherElement = API.createElement({ + type: "rectangle", + boundElements: [], + groupIds: ["C", "A"], + }); + mutateElement(container, { + boundElements: [{ type: "text", id: boundText.id }], + }); + + elements.push(boundText, otherElement, container); + } + return elements; + }; + + const elements = makeElements(10000); + const t0 = Date.now(); + normalizeElementOrder(elements); + console.info(`${Date.now() - t0}ms`); + }); +}); diff --git a/src/element/sortElements.ts b/src/element/sortElements.ts new file mode 100644 index 000000000..3c91fc040 --- /dev/null +++ b/src/element/sortElements.ts @@ -0,0 +1,123 @@ +import { arrayToMapWithIndex } from "../utils"; +import { ExcalidrawElement } from "./types"; + +const normalizeGroupElementOrder = (elements: readonly ExcalidrawElement[]) => { + const origElements: ExcalidrawElement[] = elements.slice(); + const sortedElements = new Set(); + + const orderInnerGroups = ( + elements: readonly ExcalidrawElement[], + ): ExcalidrawElement[] => { + const firstGroupSig = elements[0]?.groupIds?.join(""); + const aGroup: ExcalidrawElement[] = [elements[0]]; + const bGroup: ExcalidrawElement[] = []; + for (const element of elements.slice(1)) { + if (element.groupIds?.join("") === firstGroupSig) { + aGroup.push(element); + } else { + bGroup.push(element); + } + } + return bGroup.length ? [...aGroup, ...orderInnerGroups(bGroup)] : aGroup; + }; + + const groupHandledElements = new Map(); + + origElements.forEach((element, idx) => { + if (groupHandledElements.has(element.id)) { + return; + } + if (element.groupIds?.length) { + const topGroup = element.groupIds[element.groupIds.length - 1]; + const groupElements = origElements.slice(idx).filter((element) => { + const ret = element?.groupIds?.some((id) => id === topGroup); + if (ret) { + groupHandledElements.set(element!.id, true); + } + return ret; + }); + + for (const elem of orderInnerGroups(groupElements)) { + sortedElements.add(elem); + } + } else { + sortedElements.add(element); + } + }); + + // if there's a bug which resulted in losing some of the elements, return + // original instead as that's better than losing data + if (sortedElements.size !== elements.length) { + console.error("normalizeGroupElementOrder: lost some elements... bailing!"); + return elements; + } + + return [...sortedElements]; +}; + +/** + * In theory, when we have text elements bound to a container, they + * should be right after the container element in the elements array. + * However, this is not guaranteed due to old and potential future bugs. + * + * This function sorts containers and their bound texts together. It prefers + * original z-index of container (i.e. it moves bound text elements after + * containers). + */ +const normalizeBoundElementsOrder = ( + elements: readonly ExcalidrawElement[], +) => { + const elementsMap = arrayToMapWithIndex(elements); + + const origElements: (ExcalidrawElement | null)[] = elements.slice(); + const sortedElements = new Set(); + + origElements.forEach((element, idx) => { + if (!element) { + return; + } + if (element.boundElements?.length) { + sortedElements.add(element); + origElements[idx] = null; + element.boundElements.forEach((boundElement) => { + const child = elementsMap.get(boundElement.id); + if (child && boundElement.type === "text") { + sortedElements.add(child[0]); + origElements[child[1]] = null; + } + }); + } else if (element.type === "text" && element.containerId) { + const parent = elementsMap.get(element.containerId); + if (!parent?.[0].boundElements?.find((x) => x.id === element.id)) { + sortedElements.add(element); + origElements[idx] = null; + + // if element has a container and container lists it, skip this element + // as it'll be taken care of by the container + } + } else { + sortedElements.add(element); + origElements[idx] = null; + } + }); + + // if there's a bug which resulted in losing some of the elements, return + // original instead as that's better than losing data + if (sortedElements.size !== elements.length) { + console.error( + "normalizeBoundElementsOrder: lost some elements... bailing!", + ); + return elements; + } + + return [...sortedElements]; +}; + +export const normalizeElementOrder = ( + elements: readonly ExcalidrawElement[], +) => { + // console.time(); + const ret = normalizeBoundElementsOrder(normalizeGroupElementOrder(elements)); + // console.timeEnd(); + return ret; +}; diff --git a/src/element/textElement.ts b/src/element/textElement.ts index 033b3db0e..c726c1c3f 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -127,10 +127,16 @@ export const bindTextToShapeAfterDuplication = ( const newContainer = sceneElementMap.get(newElementId); if (newContainer) { mutateElement(newContainer, { - boundElements: (newContainer.boundElements || []).concat({ - type: "text", - id: newTextElementId, - }), + boundElements: (element.boundElements || []) + .filter( + (boundElement) => + boundElement.id !== newTextElementId && + boundElement.id !== boundTextElementId, + ) + .concat({ + type: "text", + id: newTextElementId, + }), }); } const newTextElement = sceneElementMap.get(newTextElementId); diff --git a/src/excalidraw-app/collab/reconciliation.ts b/src/excalidraw-app/collab/reconciliation.ts index dee9f7390..76b6f052a 100644 --- a/src/excalidraw-app/collab/reconciliation.ts +++ b/src/excalidraw-app/collab/reconciliation.ts @@ -1,6 +1,7 @@ import { PRECEDING_ELEMENT_KEY } from "../../constants"; import { ExcalidrawElement } from "../../element/types"; import { AppState } from "../../types"; +import { arrayToMapWithIndex } from "../../utils"; export type ReconciledElements = readonly ExcalidrawElement[] & { _brand: "reconciledElements"; @@ -33,30 +34,13 @@ const shouldDiscardRemoteElement = ( return false; }; -const getElementsMapWithIndex = ( - elements: readonly T[], -) => - elements.reduce( - ( - acc: { - [key: string]: [element: T, index: number] | undefined; - }, - element: T, - idx, - ) => { - acc[element.id] = [element, idx]; - return acc; - }, - {}, - ); - export const reconcileElements = ( localElements: readonly ExcalidrawElement[], remoteElements: readonly BroadcastedExcalidrawElement[], localAppState: AppState, ): ReconciledElements => { const localElementsData = - getElementsMapWithIndex(localElements); + arrayToMapWithIndex(localElements); const reconciledElements: ExcalidrawElement[] = localElements.slice(); @@ -69,7 +53,7 @@ export const reconcileElements = ( for (const remoteElement of remoteElements) { remoteElementIdx++; - const local = localElementsData[remoteElement.id]; + const local = localElementsData.get(remoteElement.id); if (shouldDiscardRemoteElement(localAppState, local?.[0], remoteElement)) { if (remoteElement[PRECEDING_ELEMENT_KEY]) { @@ -105,21 +89,21 @@ export const reconcileElements = ( offset++; if (cursor === 0) { reconciledElements.unshift(remoteElement); - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, cursor - offset, - ]; + ]); } else { reconciledElements.splice(cursor + 1, 0, remoteElement); - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, cursor + 1 - offset, - ]; + ]); cursor++; } } else { - let idx = localElementsData[parent] - ? localElementsData[parent]![1] + let idx = localElementsData.has(parent) + ? localElementsData.get(parent)![1] : null; if (idx != null) { idx += offset; @@ -127,38 +111,38 @@ export const reconcileElements = ( if (idx != null && idx >= cursor) { reconciledElements.splice(idx + 1, 0, remoteElement); offset++; - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, idx + 1 - offset, - ]; + ]); cursor = idx + 1; } else if (idx != null) { reconciledElements.splice(cursor + 1, 0, remoteElement); offset++; - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, cursor + 1 - offset, - ]; + ]); cursor++; } else { reconciledElements.push(remoteElement); - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, reconciledElements.length - 1 - offset, - ]; + ]); } } // no parent z-index information, local element exists → replace in place } else if (local) { reconciledElements[local[1]] = remoteElement; - localElementsData[remoteElement.id] = [remoteElement, local[1]]; + localElementsData.set(remoteElement.id, [remoteElement, local[1]]); // otherwise push to the end } else { reconciledElements.push(remoteElement); - localElementsData[remoteElement.id] = [ + localElementsData.set(remoteElement.id, [ remoteElement, reconciledElements.length - 1 - offset, - ]; + ]); } } diff --git a/src/tests/zindex.test.tsx b/src/tests/zindex.test.tsx index 402e0a2d9..a59af77cf 100644 --- a/src/tests/zindex.test.tsx +++ b/src/tests/zindex.test.tsx @@ -11,6 +11,7 @@ import { } from "../actions"; import { AppState } from "../types"; import { API } from "./helpers/api"; +import { selectGroupsForSelectedElements } from "../groups"; // Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); @@ -34,6 +35,7 @@ const populateElements = ( height?: number; containerId?: string; }[], + appState?: Partial, ) => { const selectedElementIds: any = {}; @@ -84,6 +86,11 @@ const populateElements = ( }); h.setState({ + ...selectGroupsForSelectedElements( + { ...h.state, ...appState, selectedElementIds }, + h.elements, + ), + ...appState, selectedElementIds, }); @@ -111,11 +118,7 @@ const assertZindex = ({ appState?: Partial; operations: [Actions, string[]][]; }) => { - const selectedElementIds = populateElements(elements); - - h.setState({ - editingGroupId: appState?.editingGroupId || null, - }); + const selectedElementIds = populateElements(elements, appState); operations.forEach(([action, expected]) => { h.app.actionManager.executeAction(action); @@ -884,9 +887,6 @@ describe("z-index manipulation", () => { { id: "A", groupIds: ["g1"], isSelected: true }, { id: "B", groupIds: ["g1"], isSelected: true }, ]); - h.setState({ - selectedGroupIds: { g1: true }, - }); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements).toMatchObject([ { id: "A" }, @@ -908,9 +908,6 @@ describe("z-index manipulation", () => { { id: "B", groupIds: ["g1"], isSelected: true }, { id: "C" }, ]); - h.setState({ - selectedGroupIds: { g1: true }, - }); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements).toMatchObject([ { id: "A" }, @@ -933,9 +930,6 @@ describe("z-index manipulation", () => { { id: "B", groupIds: ["g1"], isSelected: true }, { id: "C", isSelected: true }, ]); - h.setState({ - selectedGroupIds: { g1: true }, - }); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -952,9 +946,6 @@ describe("z-index manipulation", () => { { id: "C", groupIds: ["g2"], isSelected: true }, { id: "D", groupIds: ["g2"], isSelected: true }, ]); - h.setState({ - selectedGroupIds: { g1: true, g2: true }, - }); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -967,14 +958,16 @@ describe("z-index manipulation", () => { "D_copy", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"], isSelected: true }, - { id: "B", groupIds: ["g1", "g2"], isSelected: true }, - { id: "C", groupIds: ["g2"], isSelected: true }, - ]); - h.setState({ - selectedGroupIds: { g1: true }, - }); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"], isSelected: true }, + { id: "B", groupIds: ["g1", "g2"], isSelected: true }, + { id: "C", groupIds: ["g2"], isSelected: true }, + ], + { + selectedGroupIds: { g1: true }, + }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -985,14 +978,16 @@ describe("z-index manipulation", () => { "C_copy", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"], isSelected: true }, - { id: "B", groupIds: ["g1", "g2"], isSelected: true }, - { id: "C", groupIds: ["g2"], isSelected: true }, - ]); - h.setState({ - selectedGroupIds: { g2: true }, - }); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"], isSelected: true }, + { id: "B", groupIds: ["g1", "g2"], isSelected: true }, + { id: "C", groupIds: ["g2"], isSelected: true }, + ], + { + selectedGroupIds: { g2: true }, + }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -1003,17 +998,19 @@ describe("z-index manipulation", () => { "C_copy", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"], isSelected: true }, - { id: "B", groupIds: ["g1", "g2"], isSelected: true }, - { id: "C", groupIds: ["g2"], isSelected: true }, - { id: "D", groupIds: ["g3", "g4"], isSelected: true }, - { id: "E", groupIds: ["g3", "g4"], isSelected: true }, - { id: "F", groupIds: ["g4"], isSelected: true }, - ]); - h.setState({ - selectedGroupIds: { g2: true, g4: true }, - }); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"], isSelected: true }, + { id: "B", groupIds: ["g1", "g2"], isSelected: true }, + { id: "C", groupIds: ["g2"], isSelected: true }, + { id: "D", groupIds: ["g3", "g4"], isSelected: true }, + { id: "E", groupIds: ["g3", "g4"], isSelected: true }, + { id: "F", groupIds: ["g4"], isSelected: true }, + ], + { + selectedGroupIds: { g2: true, g4: true }, + }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -1030,11 +1027,14 @@ describe("z-index manipulation", () => { "F_copy", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"], isSelected: true }, - { id: "B", groupIds: ["g1", "g2"] }, - { id: "C", groupIds: ["g2"] }, - ]); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"], isSelected: true }, + { id: "B", groupIds: ["g1", "g2"] }, + { id: "C", groupIds: ["g2"] }, + ], + { editingGroupId: "g1" }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -1043,11 +1043,14 @@ describe("z-index manipulation", () => { "C", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"] }, - { id: "B", groupIds: ["g1", "g2"], isSelected: true }, - { id: "C", groupIds: ["g2"] }, - ]); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"] }, + { id: "B", groupIds: ["g1", "g2"], isSelected: true }, + { id: "C", groupIds: ["g2"] }, + ], + { editingGroupId: "g1" }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -1056,11 +1059,14 @@ describe("z-index manipulation", () => { "C", ]); - populateElements([ - { id: "A", groupIds: ["g1", "g2"], isSelected: true }, - { id: "B", groupIds: ["g1", "g2"], isSelected: true }, - { id: "C", groupIds: ["g2"], isSelected: true }, - ]); + populateElements( + [ + { id: "A", groupIds: ["g1", "g2"], isSelected: true }, + { id: "B", groupIds: ["g1", "g2"], isSelected: true }, + { id: "C", groupIds: ["g2"] }, + ], + { editingGroupId: "g1" }, + ); h.app.actionManager.executeAction(actionDuplicateSelection); expect(h.elements.map((element) => element.id)).toEqual([ "A", @@ -1068,7 +1074,42 @@ describe("z-index manipulation", () => { "B", "B_copy", "C", + ]); + }); + + it("duplicating incorrectly interleaved elements (group elements should be together) should still produce reasonable result", () => { + populateElements([ + { id: "A", groupIds: ["g1"], isSelected: true }, + { id: "B" }, + { id: "C", groupIds: ["g1"], isSelected: true }, + ]); + h.app.actionManager.executeAction(actionDuplicateSelection); + expect(h.elements.map((element) => element.id)).toEqual([ + "A", + "C", + "A_copy", "C_copy", + "B", + ]); + }); + + it("group-selected duplication should includes deleted elements that weren't selected on account of being deleted", () => { + populateElements([ + { id: "A", groupIds: ["g1"], isDeleted: true }, + { id: "B", groupIds: ["g1"], isSelected: true }, + { id: "C", groupIds: ["g1"], isSelected: true }, + { id: "D" }, + ]); + expect(h.state.selectedGroupIds).toEqual({ g1: true }); + h.app.actionManager.executeAction(actionDuplicateSelection); + expect(h.elements.map((element) => element.id)).toEqual([ + "A", + "B", + "C", + "A_copy", + "B_copy", + "C_copy", + "D", ]); }); diff --git a/src/utils.ts b/src/utils.ts index 60ad24f1c..b2d85d4d3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -607,6 +607,14 @@ export const arrayToMap = ( }, new Map()); }; +export const arrayToMapWithIndex = ( + elements: readonly T[], +) => + elements.reduce((acc, element: T, idx) => { + acc.set(element.id, [element, idx]); + return acc; + }, new Map()); + export const isTestEnv = () => typeof process !== "undefined" && process.env?.NODE_ENV === "test"; From 4414069617ae3ebc3fcad7e926a87a9a76e68355 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 3 Feb 2023 17:07:14 +0100 Subject: [PATCH 2/7] feat: disable canvas smoothing (antialiasing) for right-angled elements (#6186)Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * feat: disable canvas smoothing for text and other types * disable smoothing for all right-angled elements * Update src/renderer/renderElement.ts Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * Update src/renderer/renderElement.ts Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * fix lint * always enable smoothing while zooming --------- Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> --- src/math.ts | 12 ++++++++++++ src/renderer/renderElement.ts | 28 +++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/math.ts b/src/math.ts index 2deb221b6..cfa28e230 100644 --- a/src/math.ts +++ b/src/math.ts @@ -459,3 +459,15 @@ export const mapIntervalToBezierT = ( export const arePointsEqual = (p1: Point, p2: Point) => { return p1[0] === p2[0] && p1[1] === p2[1]; }; + +export const isRightAngle = (angle: number) => { + // if our angles were mathematically accurate, we could just check + // + // angle % (Math.PI / 2) === 0 + // + // but since we're in floating point land, we need to round. + // + // Below, after dividing by Math.PI, a multiple of 0.5 indicates a right + // angle, which we can check with modulo after rounding. + return Math.round((angle / Math.PI) * 10000) % 5000 === 0; +}; diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index f77a8c482..39173d7b5 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -27,7 +27,7 @@ import { RoughGenerator } from "roughjs/bin/generator"; import { RenderConfig } from "../scene/types"; import { distance, getFontString, getFontFamilyString, isRTL } from "../utils"; -import { getCornerRadius, isPathALoop } from "../math"; +import { getCornerRadius, isPathALoop, isRightAngle } from "../math"; import rough from "roughjs/bin/rough"; import { AppState, BinaryFiles, Zoom } from "../types"; import { getDefaultAppState } from "../appState"; @@ -989,7 +989,33 @@ export const renderElement = ( element, renderConfig, ); + + const currentImageSmoothingStatus = context.imageSmoothingEnabled; + + if ( + // do not disable smoothing during zoom as blurry shapes look better + // on low resolution (while still zooming in) than sharp ones + !renderConfig?.shouldCacheIgnoreZoom && + // angle is 0 -> always disable smoothing + (!element.angle || + // or check if angle is a right angle in which case we can still + // disable smoothing without adversely affecting the result + isRightAngle(element.angle)) + ) { + // Disabling smoothing makes output much sharper, especially for + // text. Unless for non-right angles, where the aliasing is really + // terrible on Chromium. + // + // Note that `context.imageSmoothingQuality="high"` has almost + // zero effect. + // + context.imageSmoothingEnabled = false; + } + drawElementFromCanvas(elementWithCanvas, rc, context, renderConfig); + + // reset + context.imageSmoothingEnabled = currentImageSmoothingStatus; } break; } From 4db87a0b6a2c58418df657a15050e764da3deeab Mon Sep 17 00:00:00 2001 From: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Date: Sat, 4 Feb 2023 10:04:15 +0100 Subject: [PATCH 3/7] chore: Update translations from Crowdin (#6150) --- src/locales/ar-SA.json | 52 ++++++++++++++++---------------- src/locales/bg-BG.json | 6 ++-- src/locales/bn-BD.json | 8 +++-- src/locales/ca-ES.json | 8 +++-- src/locales/cs-CZ.json | 6 ++-- src/locales/da-DK.json | 6 ++-- src/locales/de-DE.json | 8 +++-- src/locales/el-GR.json | 8 +++-- src/locales/es-ES.json | 8 +++-- src/locales/eu-ES.json | 8 +++-- src/locales/fa-IR.json | 8 +++-- src/locales/fi-FI.json | 8 +++-- src/locales/fr-FR.json | 10 ++++--- src/locales/gl-ES.json | 8 +++-- src/locales/he-IL.json | 8 +++-- src/locales/hi-IN.json | 22 +++++++------- src/locales/hu-HU.json | 8 +++-- src/locales/id-ID.json | 8 +++-- src/locales/it-IT.json | 8 +++-- src/locales/ja-JP.json | 8 +++-- src/locales/kab-KAB.json | 14 +++++---- src/locales/kk-KZ.json | 6 ++-- src/locales/ko-KR.json | 8 +++-- src/locales/ku-TR.json | 8 +++-- src/locales/lt-LT.json | 8 +++-- src/locales/lv-LV.json | 8 +++-- src/locales/mr-IN.json | 22 +++++++------- src/locales/my-MM.json | 6 ++-- src/locales/nb-NO.json | 8 +++-- src/locales/nl-NL.json | 8 +++-- src/locales/nn-NO.json | 8 +++-- src/locales/oc-FR.json | 16 +++++----- src/locales/pa-IN.json | 6 ++-- src/locales/percentages.json | 58 ++++++++++++++++++------------------ src/locales/pl-PL.json | 8 +++-- src/locales/pt-BR.json | 8 +++-- src/locales/pt-PT.json | 8 +++-- src/locales/ro-RO.json | 8 +++-- src/locales/ru-RU.json | 22 +++++++------- src/locales/si-LK.json | 6 ++-- src/locales/sk-SK.json | 8 +++-- src/locales/sl-SI.json | 8 +++-- src/locales/sv-SE.json | 36 +++++++++++----------- src/locales/ta-IN.json | 8 +++-- src/locales/tr-TR.json | 8 +++-- src/locales/uk-UA.json | 8 +++-- src/locales/vi-VN.json | 12 ++++---- src/locales/zh-CN.json | 8 +++-- src/locales/zh-HK.json | 6 ++-- src/locales/zh-TW.json | 8 +++-- 50 files changed, 333 insertions(+), 235 deletions(-) diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json index 8594200c0..2a682ad70 100644 --- a/src/locales/ar-SA.json +++ b/src/locales/ar-SA.json @@ -109,35 +109,35 @@ "decreaseFontSize": "تصغير حجم الخط", "increaseFontSize": "تكبير حجم الخط", "unbindText": "فك ربط النص", - "bindText": "", + "bindText": "ربط النص بالحاوية", "link": { "edit": "تعديل الرابط", "create": "إنشاء رابط", "label": "رابط" }, "lineEditor": { - "edit": "", - "exit": "" + "edit": "تحرير السطر", + "exit": "الخروج من المُحرر" }, "elementLock": { - "lock": "", - "unlock": "", - "lockAll": "", - "unlockAll": "" + "lock": "قفل", + "unlock": "فتح", + "lockAll": "قفل الكل", + "unlockAll": "فتح الكل" }, - "statusPublished": "", - "sidebarLock": "" + "statusPublished": "نُشر", + "sidebarLock": "إبقاء الشريط الجانبي مفتوح" }, "library": { - "noItems": "", - "hint_emptyLibrary": "", - "hint_emptyPrivateLibrary": "" + "noItems": "لا توجد عناصر أضيفت بعد...", + "hint_emptyLibrary": "حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.", + "hint_emptyPrivateLibrary": "حدد عنصر على القماش لإضافته هنا." }, "buttons": { "clearReset": "إعادة تعيين اللوحة", "exportJSON": "صدر الملف", - "exportImage": "", - "export": "", + "exportImage": "تصدير الصورة...", + "export": "حفظ إلى...", "exportToPng": "تصدير بصيغة PNG", "exportToSvg": "تصدير بصيغة SVG", "copyToClipboard": "نسخ إلى الحافظة", @@ -179,7 +179,7 @@ "couldNotLoadInvalidFile": "تعذر التحميل، الملف غير صالح", "importBackendFailed": "فشل الاستيراد من الخادوم.", "cannotExportEmptyCanvas": "لا يمكن تصدير لوحة فارغة.", - "couldNotCopyToClipboard": "", + "couldNotCopyToClipboard": "تعذر النسخ إلى الحافظة.", "decryptFailed": "تعذر فك تشفير البيانات.", "uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.", "loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟", @@ -200,10 +200,10 @@ "fileTooBig": "الملف كبير جداً. الحد الأقصى المسموح به للحجم هو {{maxSize}}.", "svgImageInsertError": "تعذر إدراج صورة SVG. يبدو أن ترميز SVG غير صحيح.", "invalidSVGString": "SVG غير صالح.", - "cannotResolveCollabServer": "", - "importLibraryError": "", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "cannotResolveCollabServer": "تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.", + "importLibraryError": "تعذر تحميل المكتبة", + "collabSaveFailed": "تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.", + "collabSaveFailed_sizeExceeded": "تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك." }, "toolBar": { "selection": "تحديد", @@ -217,9 +217,10 @@ "text": "نص", "library": "مكتبة", "lock": "الحفاظ على أداة التحديد نشطة بعد الرسم", - "penMode": "", - "link": "", - "eraser": "ممحاة" + "penMode": "وضع القلم - امنع اللمس", + "link": "إضافة/تحديث الرابط للشكل المحدد", + "eraser": "ممحاة", + "hand": "" }, "headings": { "canvasActions": "إجراءات اللوحة", @@ -227,7 +228,7 @@ "shapes": "الأشكال" }, "hints": { - "canvasPanning": "لتحريك لوحة الرسم ، استمر في الضغط على عجلة الماوس أو مفتاح المسافة أثناء السحب", + "canvasPanning": "لتحريك القماش، اضغط على عجلة الفأرة أو مفتاح المسافة أثناء السحب، أو استخدم أداة اليد", "linearElement": "انقر لبدء نقاط متعددة، اسحب لخط واحد", "freeDraw": "انقر واسحب، افرج عند الانتهاء", "text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار", @@ -238,14 +239,15 @@ "resize": "يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،\nاضغط على ALT لتغيير الحجم من المركز", "resizeImage": "يمكنك تغيير الحجم بحرية بالضغط بأستمرار على SHIFT،\nاضغط بأستمرار على ALT أيضا لتغيير الحجم من المركز", "rotate": "يمكنك تقييد الزوايا من خلال الضغط على SHIFT أثناء الدوران", - "lineEditor_info": "", + "lineEditor_info": "اضغط على مفتاح (Ctrl أو Cmd) و انقر بشكل مزدوج، أو اضغط على مفتاحي (Ctrl أو Cmd) و (Enter) لتعديل النقاط", "lineEditor_pointSelected": "", "lineEditor_nothingSelected": "", "placeImage": "", "publishLibrary": "نشر مكتبتك", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "تعذر عرض المعاينة", diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json index 06f45a9a7..7f59678c0 100644 --- a/src/locales/bg-BG.json +++ b/src/locales/bg-BG.json @@ -219,7 +219,8 @@ "lock": "Поддържайте избрания инструмент активен след рисуване", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "Действия по платното", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "Натиснете Enter, за да добавите", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Невъзможност за показване на preview", diff --git a/src/locales/bn-BD.json b/src/locales/bn-BD.json index 2bac711c1..29c35eb63 100644 --- a/src/locales/bn-BD.json +++ b/src/locales/bn-BD.json @@ -219,7 +219,8 @@ "lock": "আঁকার পরে নির্বাচিত টুল সক্রিয় রাখুন", "penMode": "", "link": "একটি নির্বাচিত আকৃতির জন্য লিঙ্ক যোগ বা আপডেট করুন", - "eraser": "ঝাড়ন" + "eraser": "ঝাড়ন", + "hand": "" }, "headings": { "canvasActions": "ক্যানভাস কার্যকলাপ", @@ -227,7 +228,7 @@ "shapes": "আকার(গুলি)" }, "hints": { - "canvasPanning": "ক্যানভাস সরানোর জন্য মাউস হুইল বা স্পেসবার ধরে টানুন", + "canvasPanning": "", "linearElement": "একাধিক বিন্দু শুরু করতে ক্লিক করুন, একক লাইনের জন্য টেনে আনুন", "freeDraw": "ক্লিক করুন এবং টেনে আনুন, আপনার কাজ শেষ হলে ছেড়ে দিন", "text": "বিশেষ্য: আপনি নির্বাচন টুলের সাথে যে কোনো জায়গায় ডাবল-ক্লিক করে পাঠ্য যোগ করতে পারেন", @@ -245,7 +246,8 @@ "publishLibrary": "আপনার নিজস্ব সংগ্রহ প্রকাশ করুন", "bindTextToElement": "লেখা যোগ করতে এন্টার টিপুন", "deepBoxSelect": "", - "eraserRevert": "মুছে ফেলার জন্য চিহ্নিত উপাদানগুলিকে ফিরিয়ে আনতে অল্ট ধরে রাখুন" + "eraserRevert": "মুছে ফেলার জন্য চিহ্নিত উপাদানগুলিকে ফিরিয়ে আনতে অল্ট ধরে রাখুন", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "প্রিভিউ দেখাতে অপারগ", diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json index e529383b6..82415ff97 100644 --- a/src/locales/ca-ES.json +++ b/src/locales/ca-ES.json @@ -219,7 +219,8 @@ "lock": "Mantenir activa l'eina seleccionada desprès de dibuixar", "penMode": "", "link": "Afegeix / actualitza l'enllaç per a la forma seleccionada", - "eraser": "Esborrador" + "eraser": "Esborrador", + "hand": "" }, "headings": { "canvasActions": "Accions del llenç", @@ -227,7 +228,7 @@ "shapes": "Formes" }, "hints": { - "canvasPanning": "Per a moure el llenç, mantingueu premuda la roda del ratolí o la tecla espai mentre l'arrossegueu", + "canvasPanning": "", "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ó", @@ -245,7 +246,8 @@ "publishLibrary": "Publiqueu la vostra pròpia llibreria", "bindTextToElement": "Premeu enter per a afegir-hi text", "deepBoxSelect": "Manteniu CtrlOrCmd per a selecció profunda, i per a evitar l'arrossegament", - "eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar" + "eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "No es pot mostrar la previsualització", diff --git a/src/locales/cs-CZ.json b/src/locales/cs-CZ.json index 7dddb1116..fa225d2a1 100644 --- a/src/locales/cs-CZ.json +++ b/src/locales/cs-CZ.json @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "Guma" + "eraser": "Guma", + "hand": "" }, "headings": { "canvasActions": "", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/da-DK.json b/src/locales/da-DK.json index 6f3876dcf..5b41ddb1e 100644 --- a/src/locales/da-DK.json +++ b/src/locales/da-DK.json @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index f5dd739d1..a14f52859 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -219,7 +219,8 @@ "lock": "Ausgewähltes Werkzeug nach Zeichnen aktiv lassen", "penMode": "Stift-Modus - Berührung verhindern", "link": "Link für ausgewählte Form hinzufügen / aktualisieren", - "eraser": "Radierer" + "eraser": "Radierer", + "hand": "Hand (Schwenkwerkzeug)" }, "headings": { "canvasActions": "Aktionen für Zeichenfläche", @@ -227,7 +228,7 @@ "shapes": "Formen" }, "hints": { - "canvasPanning": "Um die Zeichenfläche zu verschieben, halte das Mausrad oder die Leertaste während des Ziehens", + "canvasPanning": "Um die Zeichenfläche zu verschieben, halte das Mausrad oder die Leertaste während des Ziehens, oder verwende das Hand-Werkzeug", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Veröffentliche deine eigene Bibliothek", "bindTextToElement": "Zum Hinzufügen Eingabetaste drücken", "deepBoxSelect": "Halte CtrlOrCmd gedrückt, um innerhalb der Gruppe auszuwählen, und um Ziehen zu vermeiden", - "eraserRevert": "Halte Alt gedrückt, um die zum Löschen markierten Elemente zurückzusetzen" + "eraserRevert": "Halte Alt gedrückt, um die zum Löschen markierten Elemente zurückzusetzen", + "firefox_clipboard_write": "Diese Funktion kann wahrscheinlich aktiviert werden, indem die Einstellung \"dom.events.asyncClipboard.clipboardItem\" auf \"true\" gesetzt wird. Um die Browsereinstellungen in Firefox zu ändern, besuche die Seite \"about:config\"." }, "canvasError": { "cannotShowPreview": "Vorschau kann nicht angezeigt werden", diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json index a603bd18f..02534c326 100644 --- a/src/locales/el-GR.json +++ b/src/locales/el-GR.json @@ -219,7 +219,8 @@ "lock": "Κράτησε επιλεγμένο το εργαλείο μετά το σχέδιο", "penMode": "Λειτουργία μολυβιού - αποτροπή αφής", "link": "Προσθήκη/ Ενημέρωση συνδέσμου για ένα επιλεγμένο σχήμα", - "eraser": "Γόμα" + "eraser": "Γόμα", + "hand": "" }, "headings": { "canvasActions": "Ενέργειες καμβά", @@ -227,7 +228,7 @@ "shapes": "Σχήματα" }, "hints": { - "canvasPanning": "Για να μετακινήσετε καμβά, κρατήστε πατημένο τον τροχό του ποντικιού ή το πλήκτρο διαστήματος ενώ σύρετε", + "canvasPanning": "", "linearElement": "Κάνε κλικ για να ξεκινήσεις πολλαπλά σημεία, σύρε για μια γραμμή", "freeDraw": "Κάντε κλικ και σύρτε, απελευθερώσατε όταν έχετε τελειώσει", "text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών", @@ -245,7 +246,8 @@ "publishLibrary": "Δημοσιεύστε τη δική σας βιβλιοθήκη", "bindTextToElement": "Πατήστε Enter για προσθήκη κειμένου", "deepBoxSelect": "Κρατήστε πατημένο το CtrlOrCmd για να επιλέξετε βαθιά, και να αποτρέψετε τη μεταφορά", - "eraserRevert": "Κρατήστε πατημένο το Alt για να επαναφέρετε τα στοιχεία που σημειώθηκαν για διαγραφή" + "eraserRevert": "Κρατήστε πατημένο το Alt για να επαναφέρετε τα στοιχεία που σημειώθηκαν για διαγραφή", + "firefox_clipboard_write": "Αυτή η επιλογή μπορεί πιθανώς να ενεργοποιηθεί αλλάζοντας την ρύθμιση \"dom.events.asyncClipboard.clipboardItem\" σε \"true\". Για να αλλάξετε τις ρυθμίσεις του προγράμματος περιήγησης στο Firefox, επισκεφθείτε τη σελίδα \"about:config\"." }, "canvasError": { "cannotShowPreview": "Αδυναμία εμφάνισης προεπισκόπησης", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index 81fa8efbb..e70ad64e5 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -219,7 +219,8 @@ "lock": "Mantener la herramienta seleccionada activa después de dibujar", "penMode": "Modo Lápiz - previene toque", "link": "Añadir/Actualizar enlace para una forma seleccionada", - "eraser": "Borrar" + "eraser": "Borrar", + "hand": "Mano (herramienta de panoramización)" }, "headings": { "canvasActions": "Acciones del lienzo", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Para mover el lienzo, mantenga la rueda del ratón o la barra de espacio mientras arrastra", + "canvasPanning": "Para mover el lienzo, mantenga la rueda del ratón o la barra espaciadora mientras arrastra o utilice la herramienta de mano", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Publica tu propia biblioteca", "bindTextToElement": "Presione Entrar para agregar", "deepBoxSelect": "Mantén CtrlOrCmd para seleccionar en profundidad, y para evitar arrastrar", - "eraserRevert": "Mantenga pulsado Alt para revertir los elementos marcados para su eliminación" + "eraserRevert": "Mantenga pulsado Alt para revertir los elementos marcados para su eliminación", + "firefox_clipboard_write": "Esta característica puede ser habilitada estableciendo la bandera \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar las banderas del navegador en Firefox, visite la página \"about:config\"." }, "canvasError": { "cannotShowPreview": "No se puede mostrar la vista previa", diff --git a/src/locales/eu-ES.json b/src/locales/eu-ES.json index c606112cc..e35e8c40c 100644 --- a/src/locales/eu-ES.json +++ b/src/locales/eu-ES.json @@ -219,7 +219,8 @@ "lock": "Mantendu aktibo hautatutako tresna marraztu ondoren", "penMode": "Luma modua - ukipena saihestu", "link": "Gehitu / Eguneratu esteka hautatutako forma baterako", - "eraser": "Borragoma" + "eraser": "Borragoma", + "hand": "" }, "headings": { "canvasActions": "Canvas ekintzak", @@ -227,7 +228,7 @@ "shapes": "Formak" }, "hints": { - "canvasPanning": "Oihala mugitzeko, sakatu saguaren gurpila edo zuriune-barra arrastatzean", + "canvasPanning": "", "linearElement": "Egin klik hainbat puntu hasteko, arrastatu lerro bakarrerako", "freeDraw": "Egin klik eta arrastatu, askatu amaitutakoan", "text": "Aholkua: testua gehitu dezakezu edozein lekutan klik bikoitza eginez hautapen tresnarekin", @@ -245,7 +246,8 @@ "publishLibrary": "Argitaratu zure liburutegia", "bindTextToElement": "Sakatu Sartu testua gehitzeko", "deepBoxSelect": "Eutsi Ctrl edo Cmd sakatuta aukeraketa sakona egiteko eta arrastatzea saihesteko", - "eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko" + "eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Ezin da oihala aurreikusi", diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json index 6d47df998..817ed51dd 100644 --- a/src/locales/fa-IR.json +++ b/src/locales/fa-IR.json @@ -219,7 +219,8 @@ "lock": "ابزار انتخاب شده را بعد از کشیدن نگه دار", "penMode": "حالت قلم - جلوگیری از تماس", "link": "افزودن/به‌روزرسانی پیوند برای شکل انتخابی", - "eraser": "پاک کن" + "eraser": "پاک کن", + "hand": "" }, "headings": { "canvasActions": "عملیات روی بوم", @@ -227,7 +228,7 @@ "shapes": "شکل‌ها" }, "hints": { - "canvasPanning": "برای حرکت دادن بوم، چرخ ماوس یا فاصله را در حین کشیدن نگه دارید", + "canvasPanning": "", "linearElement": "برای چند نقطه کلیک و برای یک خط بکشید", "freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید", "text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید", @@ -245,7 +246,8 @@ "publishLibrary": "کتابخانه خود را منتشر کنید", "bindTextToElement": "برای افزودن اینتر را بزنید", "deepBoxSelect": "CtrlOrCmd را برای انتخاب عمیق و جلوگیری از کشیدن نگه دارید", - "eraserRevert": "Alt را نگه دارید تا عناصر علامت گذاری شده برای حذف برگردند" + "eraserRevert": "Alt را نگه دارید تا عناصر علامت گذاری شده برای حذف برگردند", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "پیش نمایش نشان داده نمی شود", diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json index 1f934d87b..2346a018c 100644 --- a/src/locales/fi-FI.json +++ b/src/locales/fi-FI.json @@ -219,7 +219,8 @@ "lock": "Pidä valittu työkalu aktiivisena piirron jälkeen", "penMode": "", "link": "Lisää/päivitä linkki valitulle muodolle", - "eraser": "Poistotyökalu" + "eraser": "Poistotyökalu", + "hand": "" }, "headings": { "canvasActions": "Piirtoalueen toiminnot", @@ -227,7 +228,7 @@ "shapes": "Muodot" }, "hints": { - "canvasPanning": "Liikuttaaksesi piirtoaluetta, raahaa hiiren vieritysrulla tai välilyöntinäppäin alaspainettuna", + "canvasPanning": "", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Julkaise oma kirjasto", "bindTextToElement": "Lisää tekstiä painamalla enter", "deepBoxSelect": "Käytä syvävalintaa ja estä raahaus painamalla CtrlOrCmd", - "eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen" + "eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Esikatselua ei voitu näyttää", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 2d107b5d3..57afcf9b3 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -45,7 +45,7 @@ "exportEmbedScene": "Intégrer la scène", "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 \"Réalisé avec Excalidraw\"", - "handDrawn": "Manuscrit", + "handDrawn": "À main levée", "normal": "Normale", "code": "Code", "small": "Petite", @@ -219,7 +219,8 @@ "lock": "Garder l'outil sélectionné actif après le dessin", "penMode": "Mode stylo - évite le toucher", "link": "Ajouter/mettre à jour le lien pour une forme sélectionnée", - "eraser": "Gomme" + "eraser": "Gomme", + "hand": "Mains (outil de déplacement de la vue)" }, "headings": { "canvasActions": "Actions du canevas", @@ -227,7 +228,7 @@ "shapes": "Formes" }, "hints": { - "canvasPanning": "Pour déplacer la zone de dessin, maintenez la molette de la souris enfoncée ou la barre d'espace tout en faisant glisser", + "canvasPanning": "Pour déplacer la zone de dessin, maintenez la molette de la souris enfoncée ou la barre d'espace tout en faisant glisser, ou utiliser l'outil main.", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Publier votre propre bibliothèque", "bindTextToElement": "Appuyer sur Entrée pour ajouter du texte", "deepBoxSelect": "Maintenir Ctrl ou Cmd pour sélectionner dans les groupes et empêcher le déplacement", - "eraserRevert": "Maintenez Alt enfoncé pour annuler les éléments marqués pour suppression" + "eraserRevert": "Maintenez Alt enfoncé pour annuler les éléments marqués pour suppression", + "firefox_clipboard_write": "Cette fonctionnalité devrait pouvoir être activée en définissant l'option \"dom.events.asyncClipboard.clipboard.clipboardItem\" à \"true\". Pour modifier les paramètres du navigateur dans Firefox, visitez la page \"about:config\"." }, "canvasError": { "cannotShowPreview": "Impossible d’afficher l’aperçu", diff --git a/src/locales/gl-ES.json b/src/locales/gl-ES.json index 677975018..d7053c961 100644 --- a/src/locales/gl-ES.json +++ b/src/locales/gl-ES.json @@ -219,7 +219,8 @@ "lock": "Manter a ferramenta seleccionada activa despois de debuxar", "penMode": "Modo lapis - evitar o contacto", "link": "Engadir/ Actualizar ligazón para a forma seleccionada", - "eraser": "Goma de borrar" + "eraser": "Goma de borrar", + "hand": "Man (ferramenta de desprazamento)" }, "headings": { "canvasActions": "Accións do lenzo", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Para mover o lenzo, manteña a roda do rato ou a barra de espazo mentres arrastra", + "canvasPanning": "Para mover o lenzo, manteña pulsada a roda do rato ou a barra de espazo mentres arrastra, ou utilice a ferramenta da man", "linearElement": "Faga clic para iniciar varios puntos, arrastre para unha sola liña", "freeDraw": "Fai clic e arrastra, solta cando acabes", "text": "Consello: tamén podes engadir texto facendo dobre-clic en calquera lugar coa ferramenta de selección", @@ -245,7 +246,8 @@ "publishLibrary": "Publica a túa propia biblioteca", "bindTextToElement": "Prema a tecla enter para engadir texto", "deepBoxSelect": "Manteña pulsado CtrlOrCmd para seleccionar en profundidade e evitar o arrastre", - "eraserRevert": "Manteña pulsado Alt para reverter os elementos marcados para a súa eliminación" + "eraserRevert": "Manteña pulsado Alt para reverter os elementos marcados para a súa eliminación", + "firefox_clipboard_write": "Esta función pódese activar establecendo a opción \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar as opcións do navegador en Firefox, visita a páxina \"about:config\"." }, "canvasError": { "cannotShowPreview": "Non se pode mostrar a vista previa", diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json index ba81af3ca..faa0013b6 100644 --- a/src/locales/he-IL.json +++ b/src/locales/he-IL.json @@ -219,7 +219,8 @@ "lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור", "penMode": "", "link": "הוספה/עדכון של קישור עבור הצורה הנבחרת", - "eraser": "מחק" + "eraser": "מחק", + "hand": "" }, "headings": { "canvasActions": "פעולות הלוח", @@ -227,7 +228,7 @@ "shapes": "צורות" }, "hints": { - "canvasPanning": "כדי להזיז את הקנבס לחצו על גלגל העכבר או על מקש הרווח תוך כדי גרירה", + "canvasPanning": "", "linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד", "freeDraw": "לחץ וגרור, שחרר כשסיימת", "text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה", @@ -245,7 +246,8 @@ "publishLibrary": "פירסום ספריה אישית", "bindTextToElement": "יש להקיש Enter כדי להוסיף טקסט", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "לא הצלחנו להציג את התצוגה המקדימה", diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json index b70c0d255..c0aca8982 100644 --- a/src/locales/hi-IN.json +++ b/src/locales/hi-IN.json @@ -219,7 +219,8 @@ "lock": "ड्राइंग के बाद चयनित टूल को सक्रिय रखें", "penMode": "पेन का मोड - स्पर्श टाले", "link": "", - "eraser": "रबड़" + "eraser": "रबड़", + "hand": "हाथ ( खिसकाने का औज़ार)" }, "headings": { "canvasActions": "कैनवास क्रिया", @@ -227,7 +228,7 @@ "shapes": "आकृतियाँ" }, "hints": { - "canvasPanning": "", + "canvasPanning": "कैनवास को सरकाने के लिए, ड्रैग करते समय माउस व्हील को पकड़े रखे या स्पेसबार को दबाए रखे, अथवा हाथ वाले औज़ार का उपयोग करें", "linearElement": "कई बिंदुओं को शुरू करने के लिए क्लिक करें, सिंगल लाइन के लिए खींचें", "freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो", "text": "आप चयन टूल से कहीं भी डबल-क्लिक करके टेक्स्ट जोड़ सकते हैं", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "मिटाने के लिए चुने हुए चीजों को ना चुनने के लिए Alt साथ में दबाए" + "eraserRevert": "मिटाने के लिए चुने हुए चीजों को ना चुनने के लिए Alt साथ में दबाए", + "firefox_clipboard_write": "\"dom.events.asyncClipboard.clipboardItem\" फ़्लैग को \"true\" पर सेट करके इस सुविधा को संभवतः सक्षम किया जा सकता है। Firefox में ब्राउज़र फ़्लैग बदलने के लिए, \"about:config\" पृष्ठ पर जाएँ।" }, "canvasError": { "cannotShowPreview": "पूर्वावलोकन नहीं दिखा सकते हैं", @@ -448,15 +450,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "आपका सर्व डेटा ब्राउज़र के भीतर स्थानिक जगह पे सुरक्षित किया गया.", + "center_heading_plus": "बजाय आपको Excalidraw+ पर जाना है?", + "menuHint": "निर्यात, पसंद, भाषायें, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "निर्यात, पसंद, और भी...", + "center_heading": "चित्रांकन। बनाया गया। सरल।", + "toolbarHint": "एक औजार चुने और चित्रकारी प्रारंभ करे!", + "helpHint": "शॉर्ट्कट और सहाय्य" } } } diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json index 84dc9f61c..faaafed90 100644 --- a/src/locales/hu-HU.json +++ b/src/locales/hu-HU.json @@ -219,7 +219,8 @@ "lock": "Rajzolás után az aktív eszközt tartsa kijelölve", "penMode": "", "link": "Hivatkozás hozzáadása/frissítése a kiválasztott alakzathoz", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "Vászon műveletek", @@ -227,7 +228,7 @@ "shapes": "Alakzatok" }, "hints": { - "canvasPanning": "A vászon mozgatásához tartsd lenyomva az egér görgőjét vagy a szóköz billentyűt húzás közben", + "canvasPanning": "", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Tedd közzé saját könyvtáradat", "bindTextToElement": "Nyomd meg az Entert szöveg hozzáadáshoz", "deepBoxSelect": "Tartsd lenyomva a Ctrl/Cmd billentyűt a mély kijelöléshez és a húzás megakadályozásához", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Előnézet nem jeleníthető meg", diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json index 7804af62e..0632a87eb 100644 --- a/src/locales/id-ID.json +++ b/src/locales/id-ID.json @@ -219,7 +219,8 @@ "lock": "Biarkan alat yang dipilih aktif setelah menggambar", "penMode": "Mode pena - mencegah sentuhan", "link": "Tambah/Perbarui tautan untuk bentuk yang dipilih", - "eraser": "Penghapus" + "eraser": "Penghapus", + "hand": "" }, "headings": { "canvasActions": "Opsi Kanvas", @@ -227,7 +228,7 @@ "shapes": "Bentuk" }, "hints": { - "canvasPanning": "Untuk memindahkan kanvas, tekan roda mouse atau spasi ketika menarik", + "canvasPanning": "", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Terbitkan pustaka Anda", "bindTextToElement": "Tekan enter untuk tambahkan teks", "deepBoxSelect": "Tekan Ctrl atau Cmd untuk memilih yang di dalam, dan mencegah penggeseran", - "eraserRevert": "Tahan Alt untuk mengembalikan elemen yang ditandai untuk dihapus" + "eraserRevert": "Tahan Alt untuk mengembalikan elemen yang ditandai untuk dihapus", + "firefox_clipboard_write": "Fitur ini dapat diaktifkan melalui pengaturan flag \"dom.events.asyncClipboard.clipboardItem\" ke \"true\". Untuk mengganti flag di Firefox, pergi ke laman \"about:config\"." }, "canvasError": { "cannotShowPreview": "Tidak dapat menampilkan pratinjau", diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json index 9ef1fba53..790c63fe6 100644 --- a/src/locales/it-IT.json +++ b/src/locales/it-IT.json @@ -219,7 +219,8 @@ "lock": "Mantieni lo strumento selezionato attivo dopo aver disegnato", "penMode": "Modalità penna - previene il tocco", "link": "Aggiungi/ aggiorna il link per una forma selezionata", - "eraser": "Gomma" + "eraser": "Gomma", + "hand": "Mano (strumento di panoramica)" }, "headings": { "canvasActions": "Azioni sulla Tela", @@ -227,7 +228,7 @@ "shapes": "Forme" }, "hints": { - "canvasPanning": "Per spostare la tela, tieni premuta la rotella del mouse o la barra spaziatrice mentre la trascini", + "canvasPanning": "Per spostare la tela, tieni premuta la rotellina del mouse o la barra spaziatrice mentre trascini oppure usa lo strumento mano", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Pubblica la tua libreria", "bindTextToElement": "Premi invio per aggiungere il testo", "deepBoxSelect": "Tieni premuto CtrlOCmd per selezionare in profondità e per impedire il trascinamento", - "eraserRevert": "Tieni premuto Alt per ripristinare gli elementi contrassegnati per l'eliminazione" + "eraserRevert": "Tieni premuto Alt per ripristinare gli elementi contrassegnati per l'eliminazione", + "firefox_clipboard_write": "Questa funzione può essere abilitata impostando il flag \"dom.events.asyncClipboard.clipboardItem\" su \"true\". Per modificare i flag del browser in Firefox, visitare la pagina \"about:config\"." }, "canvasError": { "cannotShowPreview": "Impossibile visualizzare l'anteprima", diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json index 2ab2cb475..fad9b6f10 100644 --- a/src/locales/ja-JP.json +++ b/src/locales/ja-JP.json @@ -219,7 +219,8 @@ "lock": "描画後も使用中のツールを選択したままにする", "penMode": "ペンモード - タッチ防止", "link": "選択した図形のリンクを追加/更新", - "eraser": "消しゴム" + "eraser": "消しゴム", + "hand": "" }, "headings": { "canvasActions": "キャンバス操作", @@ -227,7 +228,7 @@ "shapes": "図形" }, "hints": { - "canvasPanning": "キャンバスを移動するには、マウスホイールまたはスペースバーを押しながらドラッグします", + "canvasPanning": "", "linearElement": "クリックすると複数の頂点からなる曲線を開始、ドラッグすると直線", "freeDraw": "クリックしてドラッグします。離すと終了します", "text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます", @@ -245,7 +246,8 @@ "publishLibrary": "自分のライブラリを公開", "bindTextToElement": "Enterを押してテキストを追加", "deepBoxSelect": "CtrlOrCmd を押し続けることでドラッグを抑止し、深い選択を行います", - "eraserRevert": "Alt を押し続けることで削除マークされた要素を元に戻す" + "eraserRevert": "Alt を押し続けることで削除マークされた要素を元に戻す", + "firefox_clipboard_write": "この機能は、\"dom.events.asyncClipboard.clipboardItem\" フラグを \"true\" に設定することで有効になる可能性があります。Firefox でブラウザーの設定を変更するには、\"about:config\" ページを参照してください。" }, "canvasError": { "cannotShowPreview": "プレビューを表示できません", diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json index 2818794e6..fdf94fa94 100644 --- a/src/locales/kab-KAB.json +++ b/src/locales/kab-KAB.json @@ -202,8 +202,8 @@ "invalidSVGString": "SVG armeɣtu.", "cannotResolveCollabServer": "Ulamek tuqqna s aqeddac n umyalel. Ma ulac uɣilif ales asali n usebter sakin eɛreḍ tikkelt-nniḍen.", "importLibraryError": "Ur d-ssalay ara tamkarḍit", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed": "Ulamek asekles deg uzadur n yisefka deg ugilal. Ma ikemmel wugur, isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem.", + "collabSaveFailed_sizeExceeded": "Ulamek asekles deg uzadur n yisefka deg ugilal, taɣzut n usuneɣ tettban-d temqer aṭas. Isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem." }, "toolBar": { "selection": "Tafrayt", @@ -217,9 +217,10 @@ "text": "Aḍris", "library": "Tamkarḍit", "lock": "Eǧǧ afecku n tefrayt yermed mbaɛd asuneɣ", - "penMode": "", + "penMode": "Askar n yimru - gdel tanalit", "link": "Rnu/leqqem aseɣwen i talɣa yettwafernen", - "eraser": "Sfeḍ" + "eraser": "Sfeḍ", + "hand": "Afus (afecku n usmutti n tmuɣli)" }, "headings": { "canvasActions": "Tigawin n teɣzut n usuneɣ", @@ -227,7 +228,7 @@ "shapes": "Talɣiwin" }, "hints": { - "canvasPanning": "Akken ad tesmuttiḍ taɣzut n usuneɣ, ṭṭef ṛṛuda n umumed, neɣ afeggag n tallunt mi ara tzuɣreḍ", + "canvasPanning": "", "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", @@ -245,7 +246,8 @@ "publishLibrary": "Siẓreg tamkarḍit-inek•inem", "bindTextToElement": "Ssed ɣef kcem akken ad ternuḍ aḍris", "deepBoxSelect": "Ṭṭef CtrlOrCmd akken ad tferneḍ s telqey, yerna ad trewleḍ i uzuɣer", - "eraserRevert": "Ssed Alt akken ad tsefsxeḍ iferdisen yettwacerḍen i tukksa" + "eraserRevert": "Ssed Alt akken ad tsefsxeḍ iferdisen yettwacerḍen i tukksa", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Ulamek abeqqeḍ n teskant", diff --git a/src/locales/kk-KZ.json b/src/locales/kk-KZ.json index 6eae63dfc..25071aa5e 100644 --- a/src/locales/kk-KZ.json +++ b/src/locales/kk-KZ.json @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json index fc159a2ce..a0368663c 100644 --- a/src/locales/ko-KR.json +++ b/src/locales/ko-KR.json @@ -219,7 +219,8 @@ "lock": "선택된 도구 유지하기", "penMode": "펜 모드 - 터치 방지", "link": "선택한 도형에 대해서 링크를 추가/업데이트", - "eraser": "지우개" + "eraser": "지우개", + "hand": "" }, "headings": { "canvasActions": "캔버스 동작", @@ -227,7 +228,7 @@ "shapes": "모양" }, "hints": { - "canvasPanning": "캔버스를 옮기려면 마우스 휠이나 스페이스바를 누르고 드래그하기", + "canvasPanning": "", "linearElement": "여러 점을 연결하려면 클릭하고, 직선을 그리려면 바로 드래그하세요.", "freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.", "text": "팁: 선택 툴로 아무 곳이나 더블 클릭해 텍스트를 추가할 수도 있습니다.", @@ -245,7 +246,8 @@ "publishLibrary": "당신만의 라이브러리를 게시하기", "bindTextToElement": "Enter 키를 눌러서 텍스트 추가하기", "deepBoxSelect": "CtrlOrCmd 키를 눌러서 깊게 선택하고, 드래그하지 않도록 하기", - "eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기" + "eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "미리보기를 볼 수 없습니다", diff --git a/src/locales/ku-TR.json b/src/locales/ku-TR.json index 738bf5f8e..636a832af 100644 --- a/src/locales/ku-TR.json +++ b/src/locales/ku-TR.json @@ -219,7 +219,8 @@ "lock": "ئامێرە دیاریکراوەکان چالاک بهێڵەوە دوای وێنەکێشان", "penMode": "شێوازی قەڵەم - دەست لێدان ڕابگرە", "link": "زیادکردن/ نوێکردنەوەی لینک بۆ شێوەی دیاریکراو", - "eraser": "سڕەر" + "eraser": "سڕەر", + "hand": "" }, "headings": { "canvasActions": "کردارەکانی تابلۆ", @@ -227,7 +228,7 @@ "shapes": "شێوەکان" }, "hints": { - "canvasPanning": "بۆ جوڵاندنی تابلۆ، لە کاتی ڕاکێشاندا ویلی ماوس یان شریتی بۆشایی دابگرە", + "canvasPanning": "", "linearElement": "کرتە بکە بۆ دەستپێکردنی چەند خاڵێک، ڕایبکێشە بۆ یەک هێڵ", "freeDraw": "کرتە بکە و ڕایبکێشە، کاتێک تەواو بوویت دەست هەڵگرە", "text": "زانیاری: هەروەها دەتوانیت دەق زیادبکەیت بە دوو کرتەکردن لە هەر شوێنێک لەگەڵ ئامڕازی دەستنیشانکردن", @@ -245,7 +246,8 @@ "publishLibrary": "کتێبخانەی تایبەت بە خۆت بڵاوبکەرەوە", "bindTextToElement": "بۆ زیادکردنی دەق enter بکە", "deepBoxSelect": "CtrlOrCmd ڕابگرە بۆ هەڵبژاردنی قووڵ، و بۆ ڕێگریکردن لە ڕاکێشان", - "eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە" + "eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "ناتوانرێ پێشبینین پیشان بدرێت", diff --git a/src/locales/lt-LT.json b/src/locales/lt-LT.json index b45b4fbb2..6eb83b7ac 100644 --- a/src/locales/lt-LT.json +++ b/src/locales/lt-LT.json @@ -219,7 +219,8 @@ "lock": "Baigus piešti, išlaikyti pasirinktą įrankį", "penMode": "Rašyklio režimas - neleisti prisilietimų", "link": "Pridėti / Atnaujinti pasirinktos figūros nuorodą", - "eraser": "Trintukas" + "eraser": "Trintukas", + "hand": "" }, "headings": { "canvasActions": "Veiksmai su drobe", @@ -227,7 +228,7 @@ "shapes": "Figūros" }, "hints": { - "canvasPanning": "Norint judinti drobę, judink pelę kartu įspaudus pelės ratuką arba tarpo klavišą", + "canvasPanning": "", "linearElement": "Paspaudimai sukurs papildomus taškus, nepertraukiamas tempimas sukurs liniją", "freeDraw": "Spausk ir tempk, paleisk kai norėsi pabaigti", "text": "Užuomina: tekstą taip pat galima pridėti bet kur su dvigubu pelės paspaudimu, kol parinkas žymėjimo įrankis", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/lv-LV.json b/src/locales/lv-LV.json index 795330a9a..7341fd3fd 100644 --- a/src/locales/lv-LV.json +++ b/src/locales/lv-LV.json @@ -219,7 +219,8 @@ "lock": "Paturēt izvēlēto rīku pēc darbības", "penMode": "Pildspalvas režīms – novērst pieskaršanos", "link": "Pievienot/rediģēt atlasītās figūras saiti", - "eraser": "Dzēšgumija" + "eraser": "Dzēšgumija", + "hand": "" }, "headings": { "canvasActions": "Tāfeles darbības", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Lai bīdītu tāfeli, turiet nospiestu ritināšanas vai atstarpes taustiņu, velkot ar peli", + "canvasPanning": "", "linearElement": "Klikšķiniet, lai sāktu zīmēt vairākus punktus; velciet, lai zīmētu līniju", "freeDraw": "Spiediet un velciet; atlaidiet, kad pabeidzat", "text": "Ieteikums: lai pievienotu tekstu, varat arī jebkur dubultklikšķināt ar atlases rīku", @@ -245,7 +246,8 @@ "publishLibrary": "Publicēt savu bibliotēku", "bindTextToElement": "Spiediet ievades taustiņu, lai pievienotu tekstu", "deepBoxSelect": "Turient nospiestu Ctrl vai Cmd, lai atlasītu dziļumā un lai nepieļautu objektu pavilkšanu", - "eraserRevert": "Turiet Alt, lai noņemtu elementus no dzēsšanas atlases" + "eraserRevert": "Turiet Alt, lai noņemtu elementus no dzēsšanas atlases", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Nevar rādīt priekšskatījumu", diff --git a/src/locales/mr-IN.json b/src/locales/mr-IN.json index 7ee3d0ced..6e198493d 100644 --- a/src/locales/mr-IN.json +++ b/src/locales/mr-IN.json @@ -219,7 +219,8 @@ "lock": "निवडलेले यंत्र चित्रकरण झाल्या नंतर ही सक्रिय ठेवा", "penMode": "पेन चा मोड - स्पर्श टाळा", "link": "निवडलेल्या आकारासाठी दुवा जोडा/बदल करा", - "eraser": "खोड रबर" + "eraser": "खोड रबर", + "hand": "हात ( सरकवण्या चे उपकरण)" }, "headings": { "canvasActions": "पटल क्रिया", @@ -227,7 +228,7 @@ "shapes": "आकार" }, "hints": { - "canvasPanning": "पटल हलवण्यासाठी, ड्रैग करताना माउस वील ला पकड़ा किव्हा स्पेसबार दाबून ठेवा", + "canvasPanning": "कॅनव्हास सरकवण्या साठी, ड्रॅग करताना माउस व्हील धरा किंवा स्पेसबार दाबून ठेवा अथवा हात वालं उपकरण वापरा", "linearElement": "अनेक बिंदु साठी क्लिक करा, रेघे साठी ड्रैग करा", "freeDraw": "क्लिक आणि ड्रैग करा, झालं तेव्हा सोडा", "text": "टीप: तुम्हीं निवड यंत्रानी कोठेही दुहेरी क्लिक करून टेक्स्ट जोडू शकता", @@ -245,7 +246,8 @@ "publishLibrary": "आपला खाजगी संग्रह प्रकाशित करा", "bindTextToElement": "मजकूर जोडण्यासाठी एंटर की दाबा", "deepBoxSelect": "खोल निवड ह्या साठी कंट्रोल किव्हा कमांड दाबून ठेवा, आणि बाहेर खेचणे वाचवण्या साठी पण", - "eraserRevert": "खोडण्या साठी घेतलेल्या वस्तु ना घेण्या साठी Alt दाबून ठेवावे" + "eraserRevert": "खोडण्या साठी घेतलेल्या वस्तु ना घेण्या साठी Alt दाबून ठेवावे", + "firefox_clipboard_write": "हे वैशिष्ट्य \"dom.events.asyncClipboard.clipboardItem\" फ्लॅग \"सत्य\" वर सेट करून शक्यतो सक्षम केले जाऊ शकते. Firefox मध्ये ब्राउझर फ्लॅग बदलण्यासाठी, \"about:config\" पृष्ठावर जा." }, "canvasError": { "cannotShowPreview": "पूर्वावलोकन दाखवू शकत नाही", @@ -448,15 +450,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "तुमचा सर्व डेटा तुमच्या ब्राउझरमध्ये स्थानिक पातळीवर जतन केला जातो.", + "center_heading_plus": "त्याऐवजी तुम्हाला Excalidraw+ वर जायचे आहे का?", + "menuHint": "निर्यात, आवड़ी-निवडी, भाषा, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "निर्यात, आवड़ी निवडी आणि आणकिही...", + "center_heading": "आकृत्या. काढणे. सोपे.", + "toolbarHint": "एक साधन निवडा आणि चित्रीकरण सुरु करा!", + "helpHint": "शॉर्टकट आणि सहाय्य" } } } diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json index 5f2acd026..8b5e6859f 100644 --- a/src/locales/my-MM.json +++ b/src/locales/my-MM.json @@ -219,7 +219,8 @@ "lock": "ရွေးချယ်ထားသောကိရိယာကိုသာဆက်သုံး", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "ကားချပ်လုပ်ဆောင်ချက်", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "နမူနာမပြသနိုင်ပါ", diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json index 055e33a99..f8ac1e307 100644 --- a/src/locales/nb-NO.json +++ b/src/locales/nb-NO.json @@ -219,7 +219,8 @@ "lock": "Behold merket verktøy som aktivt", "penMode": "Pennemodus - forhindre berøring", "link": "Legg til / oppdater link for en valgt figur", - "eraser": "Viskelær" + "eraser": "Viskelær", + "hand": "Hånd (panoreringsverktøy)" }, "headings": { "canvasActions": "Handlinger: lerret", @@ -227,7 +228,7 @@ "shapes": "Former" }, "hints": { - "canvasPanning": "For å flytte lerretet, hold musehjulet eller mellomromstasten mens du drar", + "canvasPanning": "For å flytte lerretet, hold musehjulet eller mellomromstasten mens du drar, eller bruk hånd-verktøyet", "linearElement": "Klikk for å starte linje med flere punkter, eller dra for en enkel linje", "freeDraw": "Klikk og dra, slipp når du er ferdig", "text": "Tips: du kan også legge til tekst ved å dobbeltklikke hvor som helst med utvalgsverktøyet", @@ -245,7 +246,8 @@ "publishLibrary": "Publiser ditt eget bibliotek", "bindTextToElement": "Trykk Enter for å legge til tekst", "deepBoxSelect": "Hold CTRL/CMD for å markere dypt og forhindre flytting", - "eraserRevert": "Hold Alt for å reversere elementene merket for sletting" + "eraserRevert": "Hold Alt for å reversere elementene merket for sletting", + "firefox_clipboard_write": "Denne funksjonen kan sannsynligvis aktiveres ved å sette \"dom.events.asyncClipboard.clipboardItem\" flagget til \"true\". For å endre nettleserens flagg i Firefox, besøk \"about:config\"-siden." }, "canvasError": { "cannotShowPreview": "Kan ikke vise forhåndsvisning", diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json index 3961f10fe..9152d3bbb 100644 --- a/src/locales/nl-NL.json +++ b/src/locales/nl-NL.json @@ -219,7 +219,8 @@ "lock": "Geselecteerde tool actief houden na tekenen", "penMode": "Pen modus - Blokkeer aanraken", "link": "", - "eraser": "Gum" + "eraser": "Gum", + "hand": "" }, "headings": { "canvasActions": "Canvasacties", @@ -227,7 +228,7 @@ "shapes": "Vormen" }, "hints": { - "canvasPanning": "Om canvas te verplaatsen, houd muiswiel of spatiebalk ingedrukt tijdens slepen", + "canvasPanning": "", "linearElement": "Klik om meerdere punten te starten, sleep voor één lijn", "freeDraw": "Klik en sleep, laat los als je klaar bent", "text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool", @@ -245,7 +246,8 @@ "publishLibrary": "Publiceer je eigen bibliotheek", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Kan voorbeeld niet tonen", diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json index 20fc77651..fa740d258 100644 --- a/src/locales/nn-NO.json +++ b/src/locales/nn-NO.json @@ -219,7 +219,8 @@ "lock": "Hald fram med valt verktøy", "penMode": "", "link": "Legg til/ oppdater lenke til valt figur", - "eraser": "Viskelêr" + "eraser": "Viskelêr", + "hand": "" }, "headings": { "canvasActions": "Handlingar: lerret", @@ -227,7 +228,7 @@ "shapes": "Formar" }, "hints": { - "canvasPanning": "For å flytte lerretet, hald inne musehjulet eller mellomromstasten medan du dreg", + "canvasPanning": "", "linearElement": "Klikk for å starte linje med fleire punkt, eller drag for ei enkel linje", "freeDraw": "Klikk og drag, slepp når du er ferdig", "text": "Tips: du kan òg leggje til tekst ved å dobbeltklikke kor som helst med utvalgsverktyet", @@ -245,7 +246,8 @@ "publishLibrary": "Publiser ditt eige bibliotek", "bindTextToElement": "Trykk på enter for å legge til tekst", "deepBoxSelect": "Hald inne Ctrl / Cmd for å velje djupt, og forhindre flytting", - "eraserRevert": "Hald inne Alt for å reversere markering av element for sletting" + "eraserRevert": "Hald inne Alt for å reversere markering av element for sletting", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Kan ikkje vise førehandsvising", diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json index db03fb22b..c1d3ab341 100644 --- a/src/locales/oc-FR.json +++ b/src/locales/oc-FR.json @@ -38,8 +38,8 @@ "arrowhead_bar": "Barra", "arrowhead_dot": "Ponch", "arrowhead_triangle": "Triangle", - "fontSize": "Talha poliça", - "fontFamily": "Familha de poliça", + "fontSize": "Talha polissa", + "fontFamily": "Familha de polissa", "onlySelected": "Seleccion sonque", "withBackground": "Rèireplan", "exportEmbedScene": "Scèna embarcada", @@ -106,8 +106,8 @@ "toggleTheme": "Alternar tèma", "personalLib": "Bibliotèca personala", "excalidrawLib": "Bibliotèca Excalidraw", - "decreaseFontSize": "Reduire talha poliça", - "increaseFontSize": "Aumentar talha poliça", + "decreaseFontSize": "Reduire talha polissa", + "increaseFontSize": "Aumentar talha polissa", "unbindText": "Dessociar lo tèxte", "bindText": "Ligar lo tèxt al contenidor", "link": { @@ -219,7 +219,8 @@ "lock": "Mantenir activa l’aisina aprèp dessenhar", "penMode": "Mòde estilo - empachar lo contact", "link": "Apondre/Actualizar lo ligam per una fòrma seleccionada", - "eraser": "Goma" + "eraser": "Goma", + "hand": "" }, "headings": { "canvasActions": "Accions del canabàs", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Per desplaçar los canabasses, tenètz la rodeta de la mirga o la barra d’espaci pendent lo desplaçament", + "canvasPanning": "", "linearElement": "Clicatz per començar mantun punt, lisatz per una sola linha", "freeDraw": "Clicatz e lisatz, relargatz un còp acabat", "text": "Astúcia : podètz tanben apondre de tèxt en doble clicant ont que siá amb l’aisina de seleccion", @@ -245,7 +246,8 @@ "publishLibrary": "Publicar vòstra pròpria bibliotèca", "bindTextToElement": "Quichatz Entrada per apondre de tèxte", "deepBoxSelect": "Gardar CtrlOCmd per una seleccion gropada e empachar lo desplaçament", - "eraserRevert": "Tenètz quichat Alt per anullar los elements marcats per supression" + "eraserRevert": "Tenètz quichat Alt per anullar los elements marcats per supression", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Afichatge impossible de l’apercebut", diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json index 9e1bf6436..d5055a917 100644 --- a/src/locales/pa-IN.json +++ b/src/locales/pa-IN.json @@ -219,7 +219,8 @@ "lock": "ਡਰਾਇੰਗ ਤੋਂ ਬਾਅਦ ਵੀ ਚੁਣੇ ਹੋਏ ਸੰਦ ਨੂੰ ਸਰਗਰਮ ਰੱਖੋ ", "penMode": "", "link": "", - "eraser": "ਰਬੜ" + "eraser": "ਰਬੜ", + "hand": "" }, "headings": { "canvasActions": "ਕੈਨਵਸ ਦੀਆਂ ਕਾਰਵਾਈਆਂ", @@ -245,7 +246,8 @@ "publishLibrary": "ਆਪਣੀ ਲਾਇਬ੍ਰੇਰੀ ਪ੍ਰਕਾਸ਼ਿਤ ਕਰੋ", "bindTextToElement": "ਪਾਠ ਜੋੜਨ ਲਈ ਐੰਟਰ ਦਬਾਓ", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "ਝਲਕ ਨਹੀਂ ਦਿਖਾ ਸਕਦੇ", diff --git a/src/locales/percentages.json b/src/locales/percentages.json index 7a3f606ca..997a62077 100644 --- a/src/locales/percentages.json +++ b/src/locales/percentages.json @@ -1,49 +1,49 @@ { - "ar-SA": 87, - "bg-BG": 55, + "ar-SA": 92, + "bg-BG": 54, "bn-BD": 60, - "ca-ES": 94, + "ca-ES": 93, "cs-CZ": 75, "da-DK": 33, "de-DE": 100, - "el-GR": 100, + "el-GR": 99, "en": 100, "es-ES": 100, - "eu-ES": 100, - "fa-IR": 96, - "fi-FI": 93, + "eu-ES": 99, + "fa-IR": 95, + "fi-FI": 92, "fr-FR": 100, "gl-ES": 100, - "he-IL": 90, - "hi-IN": 69, + "he-IL": 89, + "hi-IN": 71, "hu-HU": 89, - "id-ID": 100, + "id-ID": 99, "it-IT": 100, - "ja-JP": 100, - "kab-KAB": 93, - "kk-KZ": 21, - "ko-KR": 99, - "ku-TR": 96, - "lt-LT": 64, - "lv-LV": 98, - "mr-IN": 98, + "ja-JP": 99, + "kab-KAB": 94, + "kk-KZ": 20, + "ko-KR": 98, + "ku-TR": 95, + "lt-LT": 63, + "lv-LV": 97, + "mr-IN": 100, "my-MM": 41, "nb-NO": 100, - "nl-NL": 91, - "nn-NO": 90, - "oc-FR": 98, + "nl-NL": 90, + "nn-NO": 89, + "oc-FR": 97, "pa-IN": 83, - "pl-PL": 85, - "pt-BR": 98, - "pt-PT": 100, - "ro-RO": 100, - "ru-RU": 98, + "pl-PL": 84, + "pt-BR": 97, + "pt-PT": 99, + "ro-RO": 99, + "ru-RU": 100, "si-LK": 8, "sk-SK": 100, "sl-SI": 100, - "sv-SE": 96, - "ta-IN": 93, - "tr-TR": 98, + "sv-SE": 100, + "ta-IN": 92, + "tr-TR": 97, "uk-UA": 96, "vi-VN": 20, "zh-CN": 100, diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json index b02881e35..04cc86898 100644 --- a/src/locales/pl-PL.json +++ b/src/locales/pl-PL.json @@ -219,7 +219,8 @@ "lock": "Zablokuj wybrane narzędzie", "penMode": "", "link": "", - "eraser": "Gumka" + "eraser": "Gumka", + "hand": "" }, "headings": { "canvasActions": "Narzędzia", @@ -227,7 +228,7 @@ "shapes": "Kształty" }, "hints": { - "canvasPanning": "Aby przesunąć płótno, przytrzymaj kółko myszy lub spację podczas przeciągania", + "canvasPanning": "", "linearElement": "Naciśnij, aby zrobić punkt, przeciągnij, aby narysować linię", "freeDraw": "Naciśnij i przeciągnij by rysować, puść kiedy skończysz", "text": "Wskazówka: możesz również dodać tekst klikając dwukrotnie gdziekolwiek za pomocą narzędzia zaznaczania", @@ -245,7 +246,8 @@ "publishLibrary": "Opublikuj własną bibliotekę", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Nie można wyświetlić podglądu", diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json index 24da24ef3..dd3362137 100644 --- a/src/locales/pt-BR.json +++ b/src/locales/pt-BR.json @@ -219,7 +219,8 @@ "lock": "Manter ativa a ferramenta selecionada após desenhar", "penMode": "Modo caneta — impede o toque", "link": "Adicionar/Atualizar link para uma forma selecionada", - "eraser": "Borracha" + "eraser": "Borracha", + "hand": "" }, "headings": { "canvasActions": "Ações da tela", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Para mover a tela, segure a roda do mouse ou a barra de espaço enquanto arrasta", + "canvasPanning": "", "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Toque e arraste, solte quando terminar", "text": "Dica: você também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", @@ -245,7 +246,8 @@ "publishLibrary": "Publicar sua própria biblioteca", "bindTextToElement": "Pressione Enter para adicionar o texto", "deepBoxSelect": "Segure Ctrl/Cmd para seleção profunda e para evitar arrastar", - "eraserRevert": "Segure a tecla Alt para inverter os elementos marcados para exclusão" + "eraserRevert": "Segure a tecla Alt para inverter os elementos marcados para exclusão", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Não é possível mostrar pré-visualização", diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json index 7a9dd400c..d72c7b98f 100644 --- a/src/locales/pt-PT.json +++ b/src/locales/pt-PT.json @@ -219,7 +219,8 @@ "lock": "Manter a ferramenta selecionada ativa após desenhar", "penMode": "Modo caneta - impedir toque", "link": "Acrescentar/ Adicionar ligação para uma forma seleccionada", - "eraser": "Borracha" + "eraser": "Borracha", + "hand": "" }, "headings": { "canvasActions": "Ações da área de desenho", @@ -227,7 +228,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "Para mover a tela, carregue na roda do rato ou na barra de espaço enquanto arrasta", + "canvasPanning": "", "linearElement": "Clique para iniciar vários pontos, arraste para uma única linha", "freeDraw": "Clique e arraste, large quando terminar", "text": "Dica: também pode adicionar texto clicando duas vezes em qualquer lugar com a ferramenta de seleção", @@ -245,7 +246,8 @@ "publishLibrary": "Publique a sua própria biblioteca", "bindTextToElement": "Carregue Enter para acrescentar texto", "deepBoxSelect": "Mantenha a tecla CtrlOrCmd carregada para selecção profunda, impedindo o arrastamento", - "eraserRevert": "Carregue também em Alt para reverter os elementos marcados para serem apagados" + "eraserRevert": "Carregue também em Alt para reverter os elementos marcados para serem apagados", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Não é possível mostrar uma pré-visualização", diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json index 4704426c1..ff76eea60 100644 --- a/src/locales/ro-RO.json +++ b/src/locales/ro-RO.json @@ -219,7 +219,8 @@ "lock": "Menține activ instrumentul selectat după desenare", "penMode": "Mod stilou – împiedică atingerea", "link": "Adăugare/actualizare URL pentru forma selectată", - "eraser": "Radieră" + "eraser": "Radieră", + "hand": "" }, "headings": { "canvasActions": "Acțiuni pentru pânză", @@ -227,7 +228,7 @@ "shapes": "Forme" }, "hints": { - "canvasPanning": "Pentru a muta pânză, ține apăsată rotița mausului sau bara de spațiu în timpul glisării", + "canvasPanning": "", "linearElement": "Dă clic pentru a crea mai multe puncte, glisează pentru a forma o singură linie", "freeDraw": "Dă clic pe pânză și glisează cursorul, apoi eliberează-l când ai terminat", "text": "Sfat: poți adăuga text și dând dublu clic oriunde cu instrumentul de selecție", @@ -245,7 +246,8 @@ "publishLibrary": "Publică propria bibliotecă", "bindTextToElement": "Apasă tasta Enter pentru a adăuga text", "deepBoxSelect": "Ține apăsată tasta Ctrl sau Cmd pentru a efectua selectarea de adâncime și pentru a preveni glisarea", - "eraserRevert": "Ține apăsată tasta Alt pentru a anula elementele marcate pentru ștergere" + "eraserRevert": "Ține apăsată tasta Alt pentru a anula elementele marcate pentru ștergere", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Nu se poate afișa previzualizarea", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index d97c91971..ce785dc83 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -219,7 +219,8 @@ "lock": "Сохранять выбранный инструмент активным после рисования", "penMode": "Режим пера - предотвращение касания", "link": "Добавить/обновить ссылку для выбранной фигуры", - "eraser": "Ластик" + "eraser": "Ластик", + "hand": "Рука (перемещение холста)" }, "headings": { "canvasActions": "Операции холста", @@ -227,7 +228,7 @@ "shapes": "Фигуры" }, "hints": { - "canvasPanning": "Чтобы перемещать холст, удерживайте колесо мыши или пробел во время перетаскивания", + "canvasPanning": "Чтобы двигать холст, удерживайте колесо мыши или пробел во время перетаскивания, или используйте инструмент \"Рука\"", "linearElement": "Нажмите, чтобы начать несколько точек, перетащите для одной линии", "freeDraw": "Нажмите и перетаскивайте, отпустите по завершении", "text": "Совет: при выбранном инструменте выделения дважды щёлкните в любом месте, чтобы добавить текст", @@ -245,7 +246,8 @@ "publishLibrary": "Опубликовать свою собственную библиотеку", "bindTextToElement": "Нажмите Enter для добавления текста", "deepBoxSelect": "Удерживайте Ctrl или Cmd для глубокого выделения, чтобы предотвратить перетаскивание", - "eraserRevert": "Удерживайте Alt, чтобы вернуть элементы, отмеченные для удаления" + "eraserRevert": "Удерживайте Alt, чтобы вернуть элементы, отмеченные для удаления", + "firefox_clipboard_write": "Эта функция может быть включена при изменении значения флага \"dom.events.asyncClipboard.clipboardItem\" на \"true\". Чтобы изменить флаги браузера в Firefox, посетите страницу \"about:config\"." }, "canvasError": { "cannotShowPreview": "Не удается отобразить предпросмотр", @@ -448,15 +450,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "Все ваши данные сохраняются локально в вашем браузере.", + "center_heading_plus": "Хотите перейти на Excalidraw+?", + "menuHint": "Экспорт, настройки, языки, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Экспорт, настройки и другое...", + "center_heading": "Диаграммы. Просто.", + "toolbarHint": "Выберите инструмент и начните рисовать!", + "helpHint": "Сочетания клавиш и помощь" } } } diff --git a/src/locales/si-LK.json b/src/locales/si-LK.json index 1567273da..dd458b31a 100644 --- a/src/locales/si-LK.json +++ b/src/locales/si-LK.json @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json index c3778c3e1..783de30af 100644 --- a/src/locales/sk-SK.json +++ b/src/locales/sk-SK.json @@ -219,7 +219,8 @@ "lock": "Nechať zvolený nástroj aktívny po skončení kreslenia", "penMode": "Režim pera – zabrániť dotyku", "link": "Pridať/ Upraviť odkaz pre vybraný tvar", - "eraser": "Guma" + "eraser": "Guma", + "hand": "Ruka (nástroj pre pohyb plátna)" }, "headings": { "canvasActions": "Akcie plátna", @@ -227,7 +228,7 @@ "shapes": "Tvary" }, "hints": { - "canvasPanning": "Pre pohyb plátna podržte koliesko myši alebo medzerník počas ťahania", + "canvasPanning": "Pre pohyb plátna podržte koliesko myši alebo medzerník počas ťahania, alebo použite nástroj ruka", "linearElement": "Kliknite na vloženie viacerých bodov, potiahnite na vytvorenie jednej priamky", "freeDraw": "Kliknite a ťahajte, pustite na ukončenie", "text": "Tip: text môžete pridať aj dvojklikom kdekoľvek, ak je zvolený nástroj výber", @@ -245,7 +246,8 @@ "publishLibrary": "Uverejniť vašu knižnicu", "bindTextToElement": "Stlačte enter na pridanie textu", "deepBoxSelect": "Podržte CtrlOrCmd na výber v skupine alebo zamedzeniu poťiahnutia", - "eraserRevert": "Podržte Alt pre prehodenie položiek určených na vymazanie" + "eraserRevert": "Podržte Alt pre prehodenie položiek určených na vymazanie", + "firefox_clipboard_write": "Táto sa funkcionalita sa dá zapnúť nastavením \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Pre zmenu nastavení vo Firefox-e otvorte stránku \"about:config\"." }, "canvasError": { "cannotShowPreview": "Nie je možné zobraziť náhľad plátna", diff --git a/src/locales/sl-SI.json b/src/locales/sl-SI.json index 5f2359094..54274932f 100644 --- a/src/locales/sl-SI.json +++ b/src/locales/sl-SI.json @@ -219,7 +219,8 @@ "lock": "Ohrani izbrano orodje aktivno po risanju", "penMode": "Način peresa - prepreči dotik", "link": "Dodaj/posodobi povezavo za izbrano obliko", - "eraser": "Radirka" + "eraser": "Radirka", + "hand": "Roka (orodje za premikanje)" }, "headings": { "canvasActions": "Dejanja za platno", @@ -227,7 +228,7 @@ "shapes": "Oblike" }, "hints": { - "canvasPanning": "Za premik platna med vlečenjem držite kolesce miške ali preslednico", + "canvasPanning": "Za premikanje platna med vlečenjem držite kolesce miške ali preslednico ali uporabite orodje roka", "linearElement": "Kliknite za začetek več točk, povlecite za posamezno črto", "freeDraw": "Kliknite in povlecite, spustite, ko končate", "text": "Namig: besedilo lahko dodate tudi z dvoklikom kjer koli z orodjem za izbiro", @@ -245,7 +246,8 @@ "publishLibrary": "Objavi svojo knjižnico", "bindTextToElement": "Pritisnite tipko Enter za dodajanje besedila", "deepBoxSelect": "Držite tipko CtrlOrCmd za globoko izbiro in preprečitev vlečenja", - "eraserRevert": "Pridržite tipko Alt, da razveljavite elemente, označene za brisanje" + "eraserRevert": "Pridržite tipko Alt, da razveljavite elemente, označene za brisanje", + "firefox_clipboard_write": "To funkcijo lahko verjetno omogočite z nastavitvijo zastavice \"dom.events.asyncClipboard.clipboardItem\" na \"true\". Če želite spremeniti zastavice brskalnika v Firefoxu, obiščite stran \"about:config\"." }, "canvasError": { "cannotShowPreview": "Predogleda ni bilo mogoče prikazati", diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json index 7f0ca8ab3..30638b9e4 100644 --- a/src/locales/sv-SE.json +++ b/src/locales/sv-SE.json @@ -1,7 +1,7 @@ { "labels": { "paste": "Klistra in", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "Klistra som oformaterad text", "pasteCharts": "Klistra in diagram", "selectAll": "Markera alla", "multiSelect": "Lägg till element till markering", @@ -202,8 +202,8 @@ "invalidSVGString": "Ogiltig SVG.", "cannotResolveCollabServer": "Det gick inte att ansluta till samarbets-servern. Ladda om sidan och försök igen.", "importLibraryError": "Kunde inte ladda bibliotek", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "" + "collabSaveFailed": "Det gick inte att spara i backend-databasen. Om problemen kvarstår bör du spara filen lokalt för att se till att du inte förlorar ditt arbete.", + "collabSaveFailed_sizeExceeded": "Det gick inte att spara till backend-databasen, whiteboarden verkar vara för stor. Du bör spara filen lokalt för att du inte ska förlora ditt arbete." }, "toolBar": { "selection": "Markering", @@ -219,7 +219,8 @@ "lock": "Håll valt verktyg aktivt efter ritande", "penMode": "Pennläge - förhindra touch", "link": "Lägg till / Uppdatera länk för en vald form", - "eraser": "Radergummi" + "eraser": "Radergummi", + "hand": "Hand (panoreringsverktyg)" }, "headings": { "canvasActions": "Canvas-åtgärder", @@ -227,7 +228,7 @@ "shapes": "Former" }, "hints": { - "canvasPanning": "För att flytta canvas, håll mushjulet eller mellanslagstangenten medan du drar", + "canvasPanning": "För att flytta whiteboarden, håll mushjulet eller mellanslagstangenten medan du drar eller använd handverktyget", "linearElement": "Klicka för att starta flera punkter, dra för en linje", "freeDraw": "Klicka och dra, släpp när du är klar", "text": "Tips: du kan också lägga till text genom att dubbelklicka var som helst med markeringsverktyget", @@ -238,14 +239,15 @@ "resize": "Du kan behålla proportioner genom att hålla SHIFT medan du ändrar storlek,\nhåller du ALT ändras storlek relativt mitten", "resizeImage": "Du kan ändra storlek fritt genom att hålla SHIFT,\nhåll ALT för att ändra storlek från mitten", "rotate": "Du kan begränsa vinklar genom att hålla SHIFT medan du roterar", - "lineEditor_info": "", + "lineEditor_info": "Håll Ctrl/Cmd och dubbelklicka eller tryck på Ctrl/Cmd + Enter för att redigera punkter", "lineEditor_pointSelected": "Tryck på Ta bort för att ta bort punkt(er), Ctrl + D eller Cmd + D för att duplicera, eller dra för att flytta", "lineEditor_nothingSelected": "Välj en punkt att redigera (håll SHIFT för att välja flera),\neller håll ned Alt och klicka för att lägga till nya punkter", "placeImage": "Klicka för att placera bilden, eller klicka och dra för att ställa in dess storlek manuellt", "publishLibrary": "Publicera ditt eget bibliotek", "bindTextToElement": "Tryck på Enter för att lägga till text", "deepBoxSelect": "Håll Ctrl eller Cmd för att djupvälja, och för att förhindra att dra", - "eraserRevert": "Håll Alt för att återställa de element som är markerade för borttagning" + "eraserRevert": "Håll Alt för att återställa de element som är markerade för borttagning", + "firefox_clipboard_write": "Denna funktion kan sannolikt aktiveras genom att ställa in \"dom.events.asyncClipboard.clipboardItem\" flaggan till \"true\". För att ändra webbläsarens flaggor i Firefox, besök \"about:config\" sidan." }, "canvasError": { "cannotShowPreview": "Kan inte visa förhandsgranskning", @@ -314,8 +316,8 @@ "zoomToFit": "Zooma för att rymma alla element", "zoomToSelection": "Zooma till markering", "toggleElementLock": "Lås/Lås upp valda", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "Flytta sida upp/ner", + "movePageLeftRight": "Flytta sida vänster/höger" }, "clearCanvasDialog": { "title": "Rensa canvas" @@ -397,7 +399,7 @@ "fileSavedToFilename": "Sparad till {filename}", "canvas": "canvas", "selection": "markering", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "Använd {{shortcut}} för att klistra in som ett enda element,\neller klistra in i en befintlig textredigerare" }, "colors": { "ffffff": "Vit", @@ -448,15 +450,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "All data sparas lokalt i din webbläsare.", + "center_heading_plus": "Ville du gå till Excalidraw+ istället?", + "menuHint": "Exportera, inställningar, språk, ..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Exportera, inställningar och mer...", + "center_heading": "Förenklade. Diagram.", + "toolbarHint": "Välj ett verktyg & börja rita!", + "helpHint": "Genvägar & hjälp" } } } diff --git a/src/locales/ta-IN.json b/src/locales/ta-IN.json index d01fabce8..2ff76b058 100644 --- a/src/locales/ta-IN.json +++ b/src/locales/ta-IN.json @@ -219,7 +219,8 @@ "lock": "தேர்ந்த கருவியை வரைந்த பின்பும் வைத்திரு", "penMode": "", "link": "தேர்தெடுத்த வடிவத்திற்குத் தொடுப்பைச் சேர்/ புதுப்பி", - "eraser": "அழிப்பி" + "eraser": "அழிப்பி", + "hand": "" }, "headings": { "canvasActions": "கித்தான் செயல்கள்", @@ -227,7 +228,7 @@ "shapes": "வடிவங்கள்" }, "hints": { - "canvasPanning": "கித்தானை நகர்த்த, பிடித்திழுக்கையில் சுட்டிச்சக்கரத்தை அ இடைவெளிப்பட்டையை அழுத்திப்பிடி", + "canvasPanning": "", "linearElement": "பல புள்ளிகளைத் துவக்க சொடுக்கு, ஒற்றை வரிக்கு பிடித்திழு", "freeDraw": "சொடுக்கி பிடித்திழு, முடித்ததும் விடுவி", "text": "துணுக்குதவி: தெரிவு கருவி கொண்டு எங்காவது இரு-சொடுக்கி உரையைச் சேர்க்கலாம்", @@ -245,7 +246,8 @@ "publishLibrary": "உம் சொந்த நூலகத்தைப் பிரசுரி", "bindTextToElement": "உரையைச் சேர்க்க enterஐ அழுத்து", "deepBoxSelect": "ஆழ்ந்துத் தேரவும் பிடித்திழுத்தலைத் தவிர்க்கவும் CtrlOrCmdஐ அழுத்திப்பிடி", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "முன்னோட்டம் காட்ட இயலவில்லை", diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json index 44a0556e0..00260919c 100644 --- a/src/locales/tr-TR.json +++ b/src/locales/tr-TR.json @@ -219,7 +219,8 @@ "lock": "Seçilen aracı çizimden sonra aktif tut", "penMode": "Kalem modu - dokunmayı engelle", "link": "Seçilen şekil için bağlantı Ekle/Güncelle", - "eraser": "Silgi" + "eraser": "Silgi", + "hand": "" }, "headings": { "canvasActions": "Tuval eylemleri", @@ -227,7 +228,7 @@ "shapes": "Şekiller" }, "hints": { - "canvasPanning": "Tuvali taşımak için, tuvali sürüklerken aynı zamanda fare tekerleğine veya boşluk tuşuna basılı tutun", + "canvasPanning": "", "linearElement": "Birden fazla nokta için tıklayın, tek çizgi için sürükleyin", "freeDraw": "Tıkla ve sürükle, bitirdiğinde serbest bırak", "text": "İpucu: seçme aracıyla herhangi bir yere çift tıklayarak da yazı ekleyebilirsin", @@ -245,7 +246,8 @@ "publishLibrary": "Kendi kitaplığınızı yayınlayın", "bindTextToElement": "Enter tuşuna basarak metin ekleyin", "deepBoxSelect": "Ctrl/Cmd tuşuna basılı tutarak derin seçim yapın ya da sürüklemeyi engelleyin", - "eraserRevert": "Alt tuşuna basılı tutarak silinme için işaretlenmiş ögeleri tersine çevirin" + "eraserRevert": "Alt tuşuna basılı tutarak silinme için işaretlenmiş ögeleri tersine çevirin", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Önizleme gösterilemiyor", diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json index e02e0c07b..73597ce9d 100644 --- a/src/locales/uk-UA.json +++ b/src/locales/uk-UA.json @@ -219,7 +219,8 @@ "lock": "Залишити обраний інструмент після креслення", "penMode": "Режим пера - запобігання дотику", "link": "Додати/Оновити посилання для вибраної форми", - "eraser": "Очищувач" + "eraser": "Очищувач", + "hand": "" }, "headings": { "canvasActions": "Дії з полотном", @@ -227,7 +228,7 @@ "shapes": "Фігури" }, "hints": { - "canvasPanning": "Щоб перемістити полотно, утримуйте коліщатко миші або пробіл під час перетягування", + "canvasPanning": "", "linearElement": "Натисніть щоб додати кілька точок. Перетягніть щоб намалювати одну лінію", "freeDraw": "Натисніть і потягніть, відпустіть коли завершите", "text": "Порада: можна також додати текст, двічі клацнувши по будь-якому місці інструментом вибору", @@ -245,7 +246,8 @@ "publishLibrary": "Опублікувати свою власну бібліотеку", "bindTextToElement": "Натисніть Enter, щоб додати текст", "deepBoxSelect": "Втримуйте Ctrl/Cmd для глибокого виділення та щоб попередити перетягування", - "eraserRevert": "Втримуйте клавішу Alt, щоб повернути елементи позначені для видалення" + "eraserRevert": "Втримуйте клавішу Alt, щоб повернути елементи позначені для видалення", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "Не вдається показати попередній перегляд", diff --git a/src/locales/vi-VN.json b/src/locales/vi-VN.json index d3b1282e0..be5edd61d 100644 --- a/src/locales/vi-VN.json +++ b/src/locales/vi-VN.json @@ -66,9 +66,9 @@ "cartoonist": "Hoạt hình", "fileTitle": "", "colorPicker": "Chọn màu", - "canvasColors": "", - "canvasBackground": "", - "drawingCanvas": "", + "canvasColors": "Đã dùng trên canvas", + "canvasBackground": "Nền canvas", + "drawingCanvas": "Canvas vẽ", "layers": "Lớp", "actions": "Chức năng", "language": "Ngôn ngữ", @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index cd573efd8..6020688a2 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -219,7 +219,8 @@ "lock": "绘制后保持所选的工具栏状态", "penMode": "笔模式 – 避免误触", "link": "为选中的形状添加/更新链接", - "eraser": "橡皮" + "eraser": "橡皮", + "hand": "抓手(平移工具)" }, "headings": { "canvasActions": "画布动作", @@ -227,7 +228,7 @@ "shapes": "形状" }, "hints": { - "canvasPanning": "要移动画布,请按住鼠标滚轮或空格键,再拖拽鼠标", + "canvasPanning": "要移动画布,请按住鼠标滚轮或空格键同时拖拽鼠标,或使用抓手工具。", "linearElement": "点击创建多个点 拖动创建直线", "freeDraw": "点击并拖动,完成时松开", "text": "提示:您也可以使用选择工具双击任意位置来添加文字", @@ -245,7 +246,8 @@ "publishLibrary": "发布您自己的素材库", "bindTextToElement": "按下 Enter 以添加文本", "deepBoxSelect": "按住 CtrlOrCmd 以深度选择,并避免拖拽", - "eraserRevert": "按住 Alt 以反选被标记删除的元素" + "eraserRevert": "按住 Alt 以反选被标记删除的元素", + "firefox_clipboard_write": "将高级配置首选项“dom.events.asyncClipboard.lipboarditem”设置为“true”可以启用此功能。要更改 Firefox 的高级配置首选项,请前往“about:config”页面。" }, "canvasError": { "cannotShowPreview": "无法显示预览", diff --git a/src/locales/zh-HK.json b/src/locales/zh-HK.json index f8968f2b7..4ff1176be 100644 --- a/src/locales/zh-HK.json +++ b/src/locales/zh-HK.json @@ -219,7 +219,8 @@ "lock": "", "penMode": "", "link": "", - "eraser": "" + "eraser": "", + "hand": "" }, "headings": { "canvasActions": "畫布動作", @@ -245,7 +246,8 @@ "publishLibrary": "", "bindTextToElement": "", "deepBoxSelect": "", - "eraserRevert": "" + "eraserRevert": "", + "firefox_clipboard_write": "" }, "canvasError": { "cannotShowPreview": "無法顯示預覽", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 21d7465af..28ac95a26 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -219,7 +219,8 @@ "lock": "可連續使用選取的工具", "penMode": "筆模式 - 避免觸摸", "link": "為所選的形狀增加\b/更新連結", - "eraser": "橡皮擦" + "eraser": "橡皮擦", + "hand": "手形(平移工具)" }, "headings": { "canvasActions": "canvas 動作", @@ -227,7 +228,7 @@ "shapes": "形狀" }, "hints": { - "canvasPanning": "若要移動畫布,請在拖曳時按住滑鼠滾輪或空白鍵", + "canvasPanning": "若要移動畫布,請在拖曳時按住滑鼠滾輪或空白鍵,或使用手形工具", "linearElement": "點擊以繪製多點曲線;或拖曳以繪製直線", "freeDraw": "點擊並拖曳來繪圖,放開即結束", "text": "提示:亦可使用選取工具在任何地方雙擊來加入文字", @@ -245,7 +246,8 @@ "publishLibrary": "發布個人資料庫", "bindTextToElement": "按下 Enter 以加入文字。", "deepBoxSelect": "按住 Ctrl 或 Cmd 以深度選取並避免拖曳", - "eraserRevert": "按住 Alt 以反選取已標記待刪除的元素" + "eraserRevert": "按住 Alt 以反選取已標記待刪除的元素", + "firefox_clipboard_write": "此功能有機會透過將 \"dom.events.asyncClipboard.clipboardItem\" 設定為 \"true\" 來開啟。\n若要變更 Firefox 瀏覽器的此設定值,請至 \"about:config\" 頁面。" }, "canvasError": { "cannotShowPreview": "無法顯示預覽", From 11e2f90ca1d726e609ece695f21a001ccb3b19cb Mon Sep 17 00:00:00 2001 From: Jang Min Date: Sat, 4 Feb 2023 21:33:40 +0900 Subject: [PATCH 4/7] feat: shortcut for clearCanvas confirmDialog (#6114) Co-authored-by: dwelle resolve https://github.com/excalidraw/excalidraw/issues/5818 --- src/actions/actionDeleteSelected.tsx | 4 ++- src/actions/shortcuts.ts | 2 ++ src/components/App.tsx | 10 ++++++- src/components/HelpDialog.tsx | 44 +++++++++++++++------------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/actions/actionDeleteSelected.tsx b/src/actions/actionDeleteSelected.tsx index a1820a330..86b985284 100644 --- a/src/actions/actionDeleteSelected.tsx +++ b/src/actions/actionDeleteSelected.tsx @@ -154,7 +154,9 @@ export const actionDeleteSelected = register({ }; }, contextItemLabel: "labels.delete", - keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE, + keyTest: (event, appState, elements) => + (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) && + !event[KEYS.CTRL_OR_CMD], PanelComponent: ({ elements, appState, updateData }) => ( = { toggleTheme: [getShortcutKey("Shift+Alt+D")], saveScene: [getShortcutKey("CtrlOrCmd+S")], loadScene: [getShortcutKey("CtrlOrCmd+O")], + clearCanvas: [getShortcutKey("CtrlOrCmd+Delete")], imageExport: [getShortcutKey("CtrlOrCmd+Shift+E")], cut: [getShortcutKey("CtrlOrCmd+X")], copy: [getShortcutKey("CtrlOrCmd+C")], diff --git a/src/components/App.tsx b/src/components/App.tsx index 7b2b5e40b..f01faa279 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -279,6 +279,8 @@ import { shouldShowBoundingBox } from "../element/transformHandles"; import { Fonts } from "../scene/Fonts"; import { actionPaste } from "../actions/actionClipboard"; import { actionToggleHandTool } from "../actions/actionCanvas"; +import { jotaiStore } from "../jotai"; +import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; const deviceContextInitialValue = { isSmScreen: false, @@ -1952,7 +1954,6 @@ class App extends React.Component { ); // Input handling - private onKeyDown = withBatchedUpdates( (event: React.KeyboardEvent | KeyboardEvent) => { // normalize `event.key` when CapsLock is pressed #2372 @@ -2194,6 +2195,13 @@ class App extends React.Component { event.stopPropagation(); } } + + if ( + event[KEYS.CTRL_OR_CMD] && + (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) + ) { + jotaiStore.set(activeConfirmDialogAtom, "clearCanvas"); + } }, ); diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 69f3e6a5c..8cb775fc5 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -273,22 +273,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { className="HelpDialog__island--editor" caption={t("helpDialog.editor")} > - - - - void }) => { ]} isOr={true} /> + + void }) => { label={t("labels.pasteAsPlaintext")} shortcuts={[getShortcutKey("CtrlOrCmd+Shift+V")]} /> + + + + {/* firefox supports clipboard API under a flag, so we'll show users what they can do in the error message */} {(probablySupportsClipboardBlob || isFirefox) && ( @@ -329,10 +337,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { label={t("labels.pasteStyles")} shortcuts={[getShortcutKey("CtrlOrCmd+Alt+V")]} /> - Date: Sat, 4 Feb 2023 15:03:39 +0100 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20show=20error=20message=20when=20not?= =?UTF-8?q?=20connected=20to=20internet=20while=20collabo=E2=80=A6=20(#616?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dwelle Resolves https://github.com/excalidraw/excalidraw/issues/5994 --- src/components/LayerUI.tsx | 1 - src/css/theme.scss | 5 +++++ src/excalidraw-app/collab/Collab.tsx | 10 ++++++++++ src/excalidraw-app/index.scss | 17 +++++++++++++++++ src/excalidraw-app/index.tsx | 15 +++++++++++---- src/locales/en.json | 3 ++- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index b7d765a8a..bea074d62 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -124,7 +124,6 @@ const LayerUI = ({ children, }: LayerUIProps) => { const device = useDevice(); - const tunnels = useInitializeTunnels(); const renderJSONExportDialog = () => { diff --git a/src/css/theme.scss b/src/css/theme.scss index 90b1bcdea..fd8067968 100644 --- a/src/css/theme.scss +++ b/src/css/theme.scss @@ -95,6 +95,9 @@ --color-gray-90: #1e1e1e; --color-gray-100: #121212; + --color-warning: #fceeca; + --color-text-warning: var(--text-primary-color); + --color-danger: #db6965; --color-promo: #e70078; @@ -163,6 +166,8 @@ --color-primary-darkest: #beb9ff; --color-primary-light: #4f4d6f; + --color-text-warning: var(--color-gray-80); + --color-danger: #ffa8a5; --color-promo: #d297ff; } diff --git a/src/excalidraw-app/collab/Collab.tsx b/src/excalidraw-app/collab/Collab.tsx index 4f98f58fd..1a996f032 100644 --- a/src/excalidraw-app/collab/Collab.tsx +++ b/src/excalidraw-app/collab/Collab.tsx @@ -75,6 +75,7 @@ import { jotaiStore } from "../../jotai"; export const collabAPIAtom = atom(null); export const collabDialogShownAtom = atom(false); export const isCollaboratingAtom = atom(false); +export const isOfflineAtom = atom(false); interface CollabState { errorMessage: string; @@ -152,6 +153,8 @@ class Collab extends PureComponent { componentDidMount() { window.addEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload); + window.addEventListener("online", this.onOfflineStatusToggle); + window.addEventListener("offline", this.onOfflineStatusToggle); window.addEventListener(EVENT.UNLOAD, this.onUnload); const collabAPI: CollabAPI = { @@ -165,6 +168,7 @@ class Collab extends PureComponent { }; jotaiStore.set(collabAPIAtom, collabAPI); + this.onOfflineStatusToggle(); if ( process.env.NODE_ENV === ENV.TEST || @@ -180,7 +184,13 @@ class Collab extends PureComponent { } } + onOfflineStatusToggle = () => { + jotaiStore.set(isOfflineAtom, !window.navigator.onLine); + }; + componentWillUnmount() { + window.removeEventListener("online", this.onOfflineStatusToggle); + window.removeEventListener("offline", this.onOfflineStatusToggle); window.removeEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload); window.removeEventListener(EVENT.UNLOAD, this.onUnload); window.removeEventListener(EVENT.POINTER_MOVE, this.onPointerMove); diff --git a/src/excalidraw-app/index.scss b/src/excalidraw-app/index.scss index b9e693ec4..38c4c1a38 100644 --- a/src/excalidraw-app/index.scss +++ b/src/excalidraw-app/index.scss @@ -45,6 +45,23 @@ } } } + + .collab-offline-warning { + pointer-events: none; + position: absolute; + top: 6.5rem; + left: 50%; + transform: translateX(-50%); + padding: 0.5rem 1rem; + font-size: 0.875rem; + text-align: center; + line-height: 1.5; + border-radius: var(--border-radius-md); + background-color: var(--color-warning); + color: var(--color-text-warning); + z-index: 6; + white-space: pre; + } } .excalidraw-app.is-collaborating { diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 6d6c9fe7d..fe2ac1a1d 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -52,6 +52,7 @@ import Collab, { collabAPIAtom, collabDialogShownAtom, isCollaboratingAtom, + isOfflineAtom, } from "./collab/Collab"; import { exportToBackend, @@ -66,10 +67,7 @@ import { } from "./data/localStorage"; import CustomStats from "./CustomStats"; import { restore, restoreAppState, RestoredDataState } from "../data/restore"; - -import "./index.scss"; import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus"; - import { updateStaleImageStatuses } from "./data/FileManager"; import { newElementWith } from "../element/mutateElement"; import { isInitializedImageElement } from "../element/typeChecks"; @@ -77,7 +75,7 @@ import { loadFilesFromFirebase } from "./data/firebase"; import { LocalData } from "./data/LocalData"; import { isBrowserStorageStateNewer } from "./data/tabSync"; import clsx from "clsx"; -import { atom, Provider, useAtom } from "jotai"; +import { atom, Provider, useAtom, useAtomValue } from "jotai"; import { jotaiStore, useAtomWithInitialValue } from "../jotai"; import { reconcileElements } from "./collab/reconciliation"; import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library"; @@ -85,6 +83,8 @@ import { AppMainMenu } from "./components/AppMainMenu"; import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; import { AppFooter } from "./components/AppFooter"; +import "./index.scss"; + polyfill(); window.EXCALIDRAW_THROTTLE_RENDER = true; @@ -599,6 +599,8 @@ const ExcalidrawWrapper = () => { localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems); }; + const isOffline = useAtomValue(isOfflineAtom); + return (
{ /> + {isCollaborating && isOffline && ( +
+ {t("alerts.collabOfflineWarning")} +
+ )} {excalidrawAPI && } {errorMessage && ( diff --git a/src/locales/en.json b/src/locales/en.json index 1a46b20e0..31005cb4c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -193,7 +193,8 @@ "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?", "removeItemsFromsLibrary": "Delete {{count}} item(s) from library?", - "invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled." + "invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled.", + "collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!" }, "errors": { "unsupportedFileType": "Unsupported file type.", From c3c45a8c37bac75e9f21780283dcd6a55abc0840 Mon Sep 17 00:00:00 2001 From: Dejavu Moe Date: Tue, 7 Feb 2023 14:14:31 +0800 Subject: [PATCH 6/7] fix: docker build architecture:linux/amd64 error occur on linux/arm64 instance (#6197) fix docker build when in linux/arm64 use docker buildx plugin to build linux/amd64 image, a build error will occur causing the build to break --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f295f7f6a..d1fa424e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM node:14-alpine AS build WORKDIR /opt/node_app COPY package.json yarn.lock ./ -RUN yarn --ignore-optional +RUN yarn --ignore-optional --network-timeout 600000 ARG NODE_ENV=production From 8c1168ef3325dcbd74750076bc2264cbf23b8492 Mon Sep 17 00:00:00 2001 From: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com> Date: Tue, 7 Feb 2023 01:11:20 -0600 Subject: [PATCH 7/7] refactor: Make the example React app reusable without duplication (#6188) --- src/packages/excalidraw/example/App.tsx | 24 ++++++++++++------ src/packages/excalidraw/example/index.tsx | 5 +++- .../example/public/{ => images}/doremon.png | Bin .../example/public/{ => images}/excalibot.png | Bin .../example/public/{ => images}/pika.jpeg | Bin .../example/public/{ => images}/rocket.jpeg | Bin .../example/sidebar/ExampleSidebar.tsx | 4 +-- 7 files changed, 22 insertions(+), 11 deletions(-) rename src/packages/excalidraw/example/public/{ => images}/doremon.png (100%) rename src/packages/excalidraw/example/public/{ => images}/excalibot.png (100%) rename src/packages/excalidraw/example/public/{ => images}/pika.jpeg (100%) rename src/packages/excalidraw/example/public/{ => images}/rocket.jpeg (100%) diff --git a/src/packages/excalidraw/example/App.tsx b/src/packages/excalidraw/example/App.tsx index e394d1a12..1f4a6c7fd 100644 --- a/src/packages/excalidraw/example/App.tsx +++ b/src/packages/excalidraw/example/App.tsx @@ -80,7 +80,13 @@ const COMMENT_ICON_DIMENSION = 32; const COMMENT_INPUT_HEIGHT = 50; const COMMENT_INPUT_WIDTH = 150; -export default function App() { +export interface AppProps { + appTitle: string; + useCustom: (api: ExcalidrawImperativeAPI | null, customArgs?: any[]) => void; + customArgs?: any[]; +} + +export default function App({ appTitle, useCustom, customArgs }: AppProps) { const appRef = useRef(null); const [viewModeEnabled, setViewModeEnabled] = useState(false); const [zenModeEnabled, setZenModeEnabled] = useState(false); @@ -107,6 +113,8 @@ export default function App() { const [excalidrawAPI, setExcalidrawAPI] = useState(null); + useCustom(excalidrawAPI, customArgs); + useHandleLibrary({ excalidrawAPI }); useEffect(() => { @@ -114,7 +122,7 @@ export default function App() { return; } const fetchData = async () => { - const res = await fetch("/rocket.jpeg"); + const res = await fetch("/images/rocket.jpeg"); const imageData = await res.blob(); const reader = new FileReader(); reader.readAsDataURL(imageData); @@ -150,7 +158,7 @@ export default function App() { /> )}
); @@ -525,7 +533,7 @@ export default function App() { }; return (
-

Excalidraw Example

+

{appTitle}

@@ -611,15 +619,15 @@ export default function App() { const collaborators = new Map(); collaborators.set("id1", { username: "Doremon", - avatarUrl: "doremon.png", + avatarUrl: "images/doremon.png", }); collaborators.set("id2", { username: "Excalibot", - avatarUrl: "excalibot.png", + avatarUrl: "images/excalibot.png", }); collaborators.set("id3", { username: "Pika", - avatarUrl: "pika.jpeg", + avatarUrl: "images/pika.jpeg", }); collaborators.set("id4", { username: "fallback", diff --git a/src/packages/excalidraw/example/index.tsx b/src/packages/excalidraw/example/index.tsx index 825a1016e..0f3bad30f 100644 --- a/src/packages/excalidraw/example/index.tsx +++ b/src/packages/excalidraw/example/index.tsx @@ -8,6 +8,9 @@ const root = createRoot(rootElement); root.render( - + {}} + /> , ); diff --git a/src/packages/excalidraw/example/public/doremon.png b/src/packages/excalidraw/example/public/images/doremon.png similarity index 100% rename from src/packages/excalidraw/example/public/doremon.png rename to src/packages/excalidraw/example/public/images/doremon.png diff --git a/src/packages/excalidraw/example/public/excalibot.png b/src/packages/excalidraw/example/public/images/excalibot.png similarity index 100% rename from src/packages/excalidraw/example/public/excalibot.png rename to src/packages/excalidraw/example/public/images/excalibot.png diff --git a/src/packages/excalidraw/example/public/pika.jpeg b/src/packages/excalidraw/example/public/images/pika.jpeg similarity index 100% rename from src/packages/excalidraw/example/public/pika.jpeg rename to src/packages/excalidraw/example/public/images/pika.jpeg diff --git a/src/packages/excalidraw/example/public/rocket.jpeg b/src/packages/excalidraw/example/public/images/rocket.jpeg similarity index 100% rename from src/packages/excalidraw/example/public/rocket.jpeg rename to src/packages/excalidraw/example/public/images/rocket.jpeg diff --git a/src/packages/excalidraw/example/sidebar/ExampleSidebar.tsx b/src/packages/excalidraw/example/sidebar/ExampleSidebar.tsx index 0fa5bf4e0..793d17b05 100644 --- a/src/packages/excalidraw/example/sidebar/ExampleSidebar.tsx +++ b/src/packages/excalidraw/example/sidebar/ExampleSidebar.tsx @@ -10,8 +10,8 @@ export default function Sidebar({ children }: { children: React.ReactNode }) { x
- - {" "} + + {" "}