From 21726e22cce04aa2d3bdac7f270e2748156ca40e Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 18:42:48 +0530 Subject: [PATCH 01/29] fix: mark more props as optional for element (#6448) * fix: mark more props as optional for element * fix --- src/appState.ts | 17 +++++----- src/charts.ts | 18 +---------- src/components/App.tsx | 2 -- src/constants.ts | 23 ++++++++++++- src/element/newElement.ts | 68 +++++++++++++++++++++++++++------------ 5 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/appState.ts b/src/appState.ts index f02d5943c7..6f4db75572 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -1,5 +1,6 @@ import oc from "open-color"; import { + DEFAULT_ELEMENT_PROPS, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_TEXT_ALIGN, @@ -23,18 +24,18 @@ export const getDefaultAppState = (): Omit< theme: THEME.LIGHT, collaborators: new Map(), currentChartType: "bar", - currentItemBackgroundColor: "transparent", + currentItemBackgroundColor: DEFAULT_ELEMENT_PROPS.backgroundColor, currentItemEndArrowhead: "arrow", - currentItemFillStyle: "hachure", + currentItemFillStyle: DEFAULT_ELEMENT_PROPS.fillStyle, currentItemFontFamily: DEFAULT_FONT_FAMILY, currentItemFontSize: DEFAULT_FONT_SIZE, - currentItemOpacity: 100, - currentItemRoughness: 1, + currentItemOpacity: DEFAULT_ELEMENT_PROPS.opacity, + currentItemRoughness: DEFAULT_ELEMENT_PROPS.roughness, currentItemStartArrowhead: null, - currentItemStrokeColor: oc.black, + currentItemStrokeColor: DEFAULT_ELEMENT_PROPS.strokeColor, currentItemRoundness: "round", - currentItemStrokeStyle: "solid", - currentItemStrokeWidth: 1, + currentItemStrokeStyle: DEFAULT_ELEMENT_PROPS.strokeStyle, + currentItemStrokeWidth: DEFAULT_ELEMENT_PROPS.strokeWidth, currentItemTextAlign: DEFAULT_TEXT_ALIGN, cursorButton: "up", draggingElement: null, @@ -44,7 +45,7 @@ export const getDefaultAppState = (): Omit< activeTool: { type: "selection", customType: null, - locked: false, + locked: DEFAULT_ELEMENT_PROPS.locked, lastActiveTool: null, }, penMode: false, diff --git a/src/charts.ts b/src/charts.ts index e8980db6c8..c3b0950d1f 100644 --- a/src/charts.ts +++ b/src/charts.ts @@ -1,10 +1,5 @@ import colors from "./colors"; -import { - DEFAULT_FONT_FAMILY, - DEFAULT_FONT_SIZE, - ENV, - VERTICAL_ALIGN, -} from "./constants"; +import { DEFAULT_FONT_SIZE, ENV } from "./constants"; import { newElement, newLinearElement, newTextElement } from "./element"; import { NonDeletedExcalidrawElement } from "./element/types"; import { randomId } from "./random"; @@ -166,17 +161,7 @@ const bgColors = colors.elementBackground.slice( // Put all the common properties here so when the whole chart is selected // the properties dialog shows the correct selected values const commonProps = { - fillStyle: "hachure", - fontFamily: DEFAULT_FONT_FAMILY, - fontSize: DEFAULT_FONT_SIZE, - opacity: 100, - roughness: 1, strokeColor: colors.elementStroke[0], - roundness: null, - strokeStyle: "solid", - strokeWidth: 1, - verticalAlign: VERTICAL_ALIGN.MIDDLE, - locked: false, } as const; const getChartDimentions = (spreadsheet: Spreadsheet) => { @@ -323,7 +308,6 @@ const chartBaseElements = ( x: x + chartWidth / 2, y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE, roundness: null, - strokeStyle: "solid", textAlign: "center", }) : null; diff --git a/src/components/App.tsx b/src/components/App.tsx index be5561995a..fcbcca873f 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2732,7 +2732,6 @@ class App extends React.Component { strokeStyle: this.state.currentItemStrokeStyle, roughness: this.state.currentItemRoughness, opacity: this.state.currentItemOpacity, - roundness: null, text: "", fontSize, fontFamily, @@ -2744,7 +2743,6 @@ class App extends React.Component { : DEFAULT_VERTICAL_ALIGN, containerId: shouldBindToContainer ? container?.id : undefined, groupIds: container?.groupIds ?? [], - locked: false, lineHeight, }); diff --git a/src/constants.ts b/src/constants.ts index ef563e4a41..23fefa6e54 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,7 @@ import cssVariables from "./css/variables.module.scss"; import { AppProps } from "./types"; -import { FontFamilyValues } from "./element/types"; +import { ExcalidrawElement, FontFamilyValues } from "./element/types"; +import oc from "open-color"; export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform); export const isWindows = /^Win/.test(navigator.platform); @@ -254,3 +255,23 @@ export const ROUNDNESS = { /** key containt id of precedeing elemnt id we use in reconciliation during * collaboration */ export const PRECEDING_ELEMENT_KEY = "__precedingElement__"; + +export const DEFAULT_ELEMENT_PROPS: { + strokeColor: ExcalidrawElement["strokeColor"]; + backgroundColor: ExcalidrawElement["backgroundColor"]; + fillStyle: ExcalidrawElement["fillStyle"]; + strokeWidth: ExcalidrawElement["strokeWidth"]; + strokeStyle: ExcalidrawElement["strokeStyle"]; + roughness: ExcalidrawElement["roughness"]; + opacity: ExcalidrawElement["opacity"]; + locked: ExcalidrawElement["locked"]; +} = { + strokeColor: oc.black, + backgroundColor: "transparent", + fillStyle: "hachure", + strokeWidth: 1, + strokeStyle: "solid", + roughness: 1, + opacity: 100, + locked: false, +}; diff --git a/src/element/newElement.ts b/src/element/newElement.ts index e3b25e848d..36c8cc0e02 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -36,7 +36,14 @@ import { getMaxContainerWidth, getDefaultLineHeight, } from "./textElement"; -import { VERTICAL_ALIGN } from "../constants"; +import { + DEFAULT_ELEMENT_PROPS, + DEFAULT_FONT_FAMILY, + DEFAULT_FONT_SIZE, + DEFAULT_TEXT_ALIGN, + DEFAULT_VERTICAL_ALIGN, + VERTICAL_ALIGN, +} from "../constants"; import { isArrowElement } from "./typeChecks"; import { MarkOptional, Merge, Mutable } from "../utility-types"; @@ -51,6 +58,15 @@ type ElementConstructorOpts = MarkOptional< | "version" | "versionNonce" | "link" + | "strokeStyle" + | "fillStyle" + | "strokeColor" + | "backgroundColor" + | "roughness" + | "strokeWidth" + | "roundness" + | "locked" + | "opacity" >; const _newElementBase = ( @@ -58,13 +74,13 @@ const _newElementBase = ( { x, y, - strokeColor, - backgroundColor, - fillStyle, - strokeWidth, - strokeStyle, - roughness, - opacity, + strokeColor = DEFAULT_ELEMENT_PROPS.strokeColor, + backgroundColor = DEFAULT_ELEMENT_PROPS.backgroundColor, + fillStyle = DEFAULT_ELEMENT_PROPS.fillStyle, + strokeWidth = DEFAULT_ELEMENT_PROPS.strokeWidth, + strokeStyle = DEFAULT_ELEMENT_PROPS.strokeStyle, + roughness = DEFAULT_ELEMENT_PROPS.roughness, + opacity = DEFAULT_ELEMENT_PROPS.opacity, width = 0, height = 0, angle = 0, @@ -72,7 +88,7 @@ const _newElementBase = ( roundness = null, boundElements = null, link = null, - locked, + locked = DEFAULT_ELEMENT_PROPS.locked, ...rest }: ElementConstructorOpts & Omit, "type">, ) => { @@ -138,27 +154,39 @@ const getTextElementPositionOffsets = ( export const newTextElement = ( opts: { text: string; - fontSize: number; - fontFamily: FontFamilyValues; - textAlign: TextAlign; - verticalAlign: VerticalAlign; + fontSize?: number; + fontFamily?: FontFamilyValues; + textAlign?: TextAlign; + verticalAlign?: VerticalAlign; containerId?: ExcalidrawTextContainer["id"]; lineHeight?: ExcalidrawTextElement["lineHeight"]; + strokeWidth?: ExcalidrawTextElement["strokeWidth"]; } & ElementConstructorOpts, ): NonDeleted => { - const lineHeight = opts.lineHeight || getDefaultLineHeight(opts.fontFamily); + const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY; + const fontSize = opts.fontSize || DEFAULT_FONT_SIZE; + const lineHeight = opts.lineHeight || getDefaultLineHeight(fontFamily); const text = normalizeText(opts.text); - const metrics = measureText(text, getFontString(opts), lineHeight); - const offsets = getTextElementPositionOffsets(opts, metrics); + const metrics = measureText( + text, + getFontString({ fontFamily, fontSize }), + lineHeight, + ); + const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN; + const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN; + const offsets = getTextElementPositionOffsets( + { textAlign, verticalAlign }, + metrics, + ); const textElement = newElementWith( { ..._newElementBase("text", opts), text, - fontSize: opts.fontSize, - fontFamily: opts.fontFamily, - textAlign: opts.textAlign, - verticalAlign: opts.verticalAlign, + fontSize, + fontFamily, + textAlign, + verticalAlign, x: opts.x - offsets.x, y: opts.y - offsets.y, width: metrics.width, From 801412bf6bd15b81403ec976a2089f2e303fff45 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 18:50:25 +0530 Subject: [PATCH 02/29] fix: restore original container height when unbinding text which was binded via context menu (#6444) * fix: restore original container height when unbinding text which was binded via context menu * remove flag * comment --- src/actions/actionBoundText.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 3f240b5d82..830af37333 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -16,6 +16,7 @@ import { import { getOriginalContainerHeightFromCache, resetOriginalContainerCache, + updateOriginalContainerCache, } from "../element/textWysiwyg"; import { hasBoundTextElement, @@ -145,7 +146,11 @@ export const actionBindText = register({ id: textElement.id, }), }); + const originalContainerHeight = container.height; redrawTextBoundingBox(textElement, container); + // overwritting the cache with original container height so + // it can be restored when unbind + updateOriginalContainerCache(container.id, originalContainerHeight); return { elements: pushTextAboveContainer(elements, container, textElement), From 4d0d844e390e8b41d059f1fe646783c0465d1a41 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 18 Apr 2023 15:27:51 +0200 Subject: [PATCH 03/29] feat: constrain export dialog preview size (#6475) --- src/components/ImageExportDialog.tsx | 24 +++++++++++------------- src/packages/utils.ts | 6 +++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/ImageExportDialog.tsx b/src/components/ImageExportDialog.tsx index fb2c1ec810..0e4eff3657 100644 --- a/src/components/ImageExportDialog.tsx +++ b/src/components/ImageExportDialog.tsx @@ -4,7 +4,6 @@ import { canvasToBlob } from "../data/blob"; import { NonDeletedExcalidrawElement } from "../element/types"; import { t } from "../i18n"; import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { exportToCanvas } from "../scene/export"; import { AppState, BinaryFiles } from "../types"; import { Dialog } from "./Dialog"; import { clipboard } from "./icons"; @@ -15,6 +14,7 @@ import { CheckboxItem } from "./CheckboxItem"; import { DEFAULT_EXPORT_PADDING, isFirefox } from "../constants"; import { nativeFileSystemSupported } from "../data/filesystem"; import { ActionManager } from "../actions/manager"; +import { exportToCanvas } from "../packages/utils"; const supportsContextFilters = "filter" in document.createElement("canvas").getContext("2d")!; @@ -83,7 +83,6 @@ const ImageExportModal = ({ const someElementIsSelected = isSomeElementSelected(elements, appState); const [exportSelected, setExportSelected] = useState(someElementIsSelected); const previewRef = useRef(null); - const { exportBackground, viewBackgroundColor } = appState; const [renderError, setRenderError] = useState(null); const exportedElements = exportSelected @@ -99,10 +98,16 @@ const ImageExportModal = ({ if (!previewNode) { return; } - exportToCanvas(exportedElements, appState, files, { - exportBackground, - viewBackgroundColor, + const maxWidth = previewNode.offsetWidth; + if (!maxWidth) { + return; + } + exportToCanvas({ + elements: exportedElements, + appState, + files, exportPadding, + maxWidthOrHeight: maxWidth, }) .then((canvas) => { setRenderError(null); @@ -116,14 +121,7 @@ const ImageExportModal = ({ console.error(error); setRenderError(error); }); - }, [ - appState, - files, - exportedElements, - exportBackground, - exportPadding, - viewBackgroundColor, - ]); + }, [appState, files, exportedElements, exportPadding]); return (
diff --git a/src/packages/utils.ts b/src/packages/utils.ts index 1fb6cd3d95..560fa13ca4 100644 --- a/src/packages/utils.ts +++ b/src/packages/utils.ts @@ -79,7 +79,11 @@ export const exportToCanvas = ({ const max = Math.max(width, height); - const scale = maxWidthOrHeight / max; + // if content is less then maxWidthOrHeight, fallback on supplied scale + const scale = + maxWidthOrHeight < max + ? maxWidthOrHeight / max + : appState?.exportScale ?? 1; canvas.width = width * scale; canvas.height = height * scale; From 979312f779b675b9e6ad917ae0b04512fcfbd783 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 19:44:14 +0530 Subject: [PATCH 04/29] fix: center align text when wrapped in container via context menu (#6480) * rename action to wrapTextInContainer * fix: center align text when wrapped in container via context menu * revert translation key * fix tests --- src/actions/actionBoundText.tsx | 5 +++-- src/actions/types.ts | 2 +- src/components/App.tsx | 4 ++-- src/element/textWysiwyg.test.tsx | 2 +- src/tests/__snapshots__/contextmenu.test.tsx.snap | 10 +++++----- src/tests/binding.test.tsx | 4 ++-- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx index 830af37333..658bdf8ce7 100644 --- a/src/actions/actionBoundText.tsx +++ b/src/actions/actionBoundText.tsx @@ -196,8 +196,8 @@ const pushContainerBelowText = ( return updatedElements; }; -export const actionCreateContainerFromText = register({ - name: "createContainerFromText", +export const actionWrapTextInContainer = register({ + name: "wrapTextInContainer", contextItemLabel: "labels.createContainerFromText", trackEvent: { category: "element" }, predicate: (elements, appState) => { @@ -286,6 +286,7 @@ export const actionCreateContainerFromText = register({ containerId: container.id, verticalAlign: VERTICAL_ALIGN.MIDDLE, boundElements: null, + textAlign: TEXT_ALIGN.CENTER, }, false, ); diff --git a/src/actions/types.ts b/src/actions/types.ts index e46cd2ab80..b03e1053b2 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -115,7 +115,7 @@ export type ActionName = | "toggleLinearEditor" | "toggleEraserTool" | "toggleHandTool" - | "createContainerFromText"; + | "wrapTextInContainer"; export type PanelComponentProps = { elements: readonly ExcalidrawElement[]; diff --git a/src/components/App.tsx b/src/components/App.tsx index fcbcca873f..20def468aa 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -295,7 +295,7 @@ import { } from "../actions/actionCanvas"; import { jotaiStore } from "../jotai"; import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; -import { actionCreateContainerFromText } from "../actions/actionBoundText"; +import { actionWrapTextInContainer } from "../actions/actionBoundText"; import BraveMeasureTextError from "./BraveMeasureTextError"; const deviceContextInitialValue = { @@ -6364,7 +6364,7 @@ class App extends React.Component { actionGroup, actionUnbindText, actionBindText, - actionCreateContainerFromText, + actionWrapTextInContainer, actionUngroup, CONTEXT_MENU_SEPARATOR, actionAddToLibrary, diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 4ae3f26f97..71c75c5a06 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -1506,7 +1506,7 @@ describe("textWysiwyg", () => { expect.objectContaining({ text: "Excalidraw is an opensource virtual collaborative whiteboard", verticalAlign: VERTICAL_ALIGN.MIDDLE, - textAlign: TEXT_ALIGN.LEFT, + textAlign: TEXT_ALIGN.CENTER, boundElements: null, }), ); diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 18656edd19..c5b61e42fb 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -121,7 +121,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -4518,7 +4518,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -5068,7 +5068,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -5917,7 +5917,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { @@ -6263,7 +6263,7 @@ Object { }, Object { "contextItemLabel": "labels.createContainerFromText", - "name": "createContainerFromText", + "name": "wrapTextInContainer", "perform": [Function], "predicate": [Function], "trackEvent": Object { diff --git a/src/tests/binding.test.tsx b/src/tests/binding.test.tsx index c615eb9259..07af36569d 100644 --- a/src/tests/binding.test.tsx +++ b/src/tests/binding.test.tsx @@ -4,7 +4,7 @@ import { UI, Pointer, Keyboard } from "./helpers/ui"; import { getTransformHandles } from "../element/transformHandles"; import { API } from "./helpers/api"; import { KEYS } from "../keys"; -import { actionCreateContainerFromText } from "../actions/actionBoundText"; +import { actionWrapTextInContainer } from "../actions/actionBoundText"; const { h } = window; @@ -277,7 +277,7 @@ describe("element binding", () => { expect(h.state.selectedElementIds[text1.id]).toBe(true); - h.app.actionManager.executeAction(actionCreateContainerFromText); + h.app.actionManager.executeAction(actionWrapTextInContainer); // new text container will be placed before the text element const container = h.elements.at(-2)!; From c9c79646c535dbca2342c5f3de26c24c6aadce19 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 20:48:30 +0530 Subject: [PATCH 05/29] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@?= =?UTF-8?q?0.15.0=20=20=F0=9F=8E=89=20(#6481)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 150 ++++++++++++++++++++++++++- src/packages/excalidraw/package.json | 2 +- 2 files changed, 150 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index c36883b8e8..5143bda315 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,7 +11,7 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased +## 0.15.0 (2023-04-18) ### Features @@ -37,6 +37,154 @@ For more details refer to the [docs](https://docs.excalidraw.com) - Exporting labelled arrows via export utils [#6443](https://github.com/excalidraw/excalidraw/pull/6443) +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +### Features + +- Constrain export dialog preview size [#6475](https://github.com/excalidraw/excalidraw/pull/6475) + +- Zigzag fill easter egg [#6439](https://github.com/excalidraw/excalidraw/pull/6439) + +- Add container to multiple text elements [#6428](https://github.com/excalidraw/excalidraw/pull/6428) + +- Starting migration from GA to Matomo for better privacy [#6398](https://github.com/excalidraw/excalidraw/pull/6398) + +- Add line height attribute to text element [#6360](https://github.com/excalidraw/excalidraw/pull/6360) + +- Add thai lang support [#6314](https://github.com/excalidraw/excalidraw/pull/6314) + +- Create bound container from text [#6301](https://github.com/excalidraw/excalidraw/pull/6301) + +- Improve text measurements in bound containers [#6187](https://github.com/excalidraw/excalidraw/pull/6187) + +- Bind text to container if double clicked on filled shape or stroke [#6250](https://github.com/excalidraw/excalidraw/pull/6250) + +- Make repair and refreshDimensions configurable in restoreElements [#6238](https://github.com/excalidraw/excalidraw/pull/6238) + +- Show error message when not connected to internet while collabo… [#6165](https://github.com/excalidraw/excalidraw/pull/6165) + +- Shortcut for clearCanvas confirmDialog [#6114](https://github.com/excalidraw/excalidraw/pull/6114) + +- Disable canvas smoothing (antialiasing) for right-angled elements [#6186](https://github.com/excalidraw/excalidraw/pull/6186)Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> + +### Fixes + +- Center align text when wrapped in container via context menu [#6480](https://github.com/excalidraw/excalidraw/pull/6480) + +- Restore original container height when unbinding text which was binded via context menu [#6444](https://github.com/excalidraw/excalidraw/pull/6444) + +- Mark more props as optional for element [#6448](https://github.com/excalidraw/excalidraw/pull/6448) + +- Improperly cache-busting on canvas scale instead of zoom [#6473](https://github.com/excalidraw/excalidraw/pull/6473) + +- Incorrectly duplicating items on paste/library insert [#6467](https://github.com/excalidraw/excalidraw/pull/6467) + +- Library ids cross-contamination on multiple insert [#6466](https://github.com/excalidraw/excalidraw/pull/6466) + +- Color picker keyboard handling not working [#6464](https://github.com/excalidraw/excalidraw/pull/6464) + +- Abort freedraw line if second touch is detected [#6440](https://github.com/excalidraw/excalidraw/pull/6440) + +- Utils leaking Scene state [#6461](https://github.com/excalidraw/excalidraw/pull/6461) + +- Split "Edit selected shape" shortcut [#6457](https://github.com/excalidraw/excalidraw/pull/6457) + +- Center align text when bind to container via context menu [#6451](https://github.com/excalidraw/excalidraw/pull/6451) + +- Update coords when text unbinded from its container [#6445](https://github.com/excalidraw/excalidraw/pull/6445) + +- Autoredirect to plus in prod only [#6446](https://github.com/excalidraw/excalidraw/pull/6446) + +- Fixing popover overflow on small screen [#6433](https://github.com/excalidraw/excalidraw/pull/6433) + +- Introduce baseline to fix the layout shift when switching to text editor [#6397](https://github.com/excalidraw/excalidraw/pull/6397) + +- Don't refresh dimensions for deleted text elements [#6438](https://github.com/excalidraw/excalidraw/pull/6438) + +- Element vanishes when zoomed in [#6417](https://github.com/excalidraw/excalidraw/pull/6417) + +- Don't jump text to end when out of viewport in safari [#6416](https://github.com/excalidraw/excalidraw/pull/6416) + +- GetDefaultLineHeight should return default font family line height for unknown font [#6399](https://github.com/excalidraw/excalidraw/pull/6399) + +- Revert use `ideographic` textBaseline to improve layout shift when editing text" [#6400](https://github.com/excalidraw/excalidraw/pull/6400) + +- Call stack size exceeded when paste large text [#6373](https://github.com/excalidraw/excalidraw/pull/6373) (#6396) + +- Use `ideographic` textBaseline to improve layout shift when editing text [#6384](https://github.com/excalidraw/excalidraw/pull/6384) + +- Chrome crashing when embedding scene on chrome arm [#6383](https://github.com/excalidraw/excalidraw/pull/6383) + +- Division by zero in findFocusPointForEllipse leads to infinite loop in wrapText freezing Excalidraw [#6377](https://github.com/excalidraw/excalidraw/pull/6377) + +- Containerizing text incorrectly updates arrow bindings [#6369](https://github.com/excalidraw/excalidraw/pull/6369) + +- Ensure export preview is centered [#6337](https://github.com/excalidraw/excalidraw/pull/6337) + +- Hide text align for labelled arrows [#6339](https://github.com/excalidraw/excalidraw/pull/6339) + +- Refresh dimensions when elements loaded from shareable link and blob [#6333](https://github.com/excalidraw/excalidraw/pull/6333) + +- Show error message when measureText API breaks in brave [#6336](https://github.com/excalidraw/excalidraw/pull/6336) + +- Add an offset of 0.5px for text editor in containers [#6328](https://github.com/excalidraw/excalidraw/pull/6328) + +- Move utility types out of `.d.ts` file to fix exported declaration files [#6315](https://github.com/excalidraw/excalidraw/pull/6315) + +- More jotai scopes missing [#6313](https://github.com/excalidraw/excalidraw/pull/6313) + +- Provide HelpButton title prop [#6209](https://github.com/excalidraw/excalidraw/pull/6209) + +- Respect text align when wrapping in a container [#6310](https://github.com/excalidraw/excalidraw/pull/6310) + +- Compute bounding box correctly for text element when multiple element resizing [#6307](https://github.com/excalidraw/excalidraw/pull/6307) + +- Use jotai scope for editor-specific atoms [#6308](https://github.com/excalidraw/excalidraw/pull/6308) + +- Consider arrow for bound text element [#6297](https://github.com/excalidraw/excalidraw/pull/6297) + +- Text never goes beyond max width for unbound text elements [#6288](https://github.com/excalidraw/excalidraw/pull/6288) + +- Svg text baseline [#6285](https://github.com/excalidraw/excalidraw/pull/6273) + +- Compute container height from bound text correctly [#6273](https://github.com/excalidraw/excalidraw/pull/6273) + +- Fit mobile toolbar and make scrollable [#6270](https://github.com/excalidraw/excalidraw/pull/6270) + +- Indenting via `tab` clashing with IME compositor [#6258](https://github.com/excalidraw/excalidraw/pull/6258) + +- Improve text wrapping inside rhombus and more fixes [#6265](https://github.com/excalidraw/excalidraw/pull/6265) + +- Improve text wrapping in ellipse and alignment [#6172](https://github.com/excalidraw/excalidraw/pull/6172) + +- Don't allow blank space in collab name [#6211](https://github.com/excalidraw/excalidraw/pull/6211) + +- Docker build architecture:linux/amd64 error occur on linux/arm64 instance [#6197](https://github.com/excalidraw/excalidraw/pull/6197) + +- Sort bound text elements to fix text duplication z-index error [#5130](https://github.com/excalidraw/excalidraw/pull/5130) + +- Hide welcome screen on mobile once user interacts [#6185](https://github.com/excalidraw/excalidraw/pull/6185) + +- Edit link in docs [#6182](https://github.com/excalidraw/excalidraw/pull/6182) + +### Refactor + +- Inline `SingleLibraryItem` into `PublishLibrary` [#6462](https://github.com/excalidraw/excalidraw/pull/6462) + +- Make the example React app reusable without duplication [#6188](https://github.com/excalidraw/excalidraw/pull/6188) + +### Performance + +- Break early if the line width <= max width of the container [#6347](https://github.com/excalidraw/excalidraw/pull/6347) + +### Build + +- Move TS and types to devDependencies [#6346](https://github.com/excalidraw/excalidraw/pull/6346) + +--- + ## 0.14.2 (2023-02-01) ### Features diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index be4e61d27c..2351ee6a5b 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.14.2", + "version": "0.15.0", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From 1d0653ce50cfc56b92235bf793cfa638f39144c4 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:03:17 +0530 Subject: [PATCH 06/29] docs: update docs for next version (#6251) * update docs for opts param inr estore utils * docs: add docs for `useI18n` hook (#6269) Co-authored-by: Aakansha Doshi * upgrade excal --------- Co-authored-by: David Luzar --- .../excalidraw/api/utils/restore.mdx | 28 +++++++++--- .../excalidraw/api/utils/utils-intro.md | 44 +++++++++++++++++++ dev-docs/package.json | 2 +- dev-docs/src/theme/ReactLiveScope/index.js | 1 + dev-docs/yarn.lock | 8 ++-- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx index 198626eeca..665a1ef9f2 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx @@ -31,10 +31,29 @@ You can pass `null` / `undefined` if not applicable. restoreElements( elements: ImportedDataState["elements"],
  localElements: ExcalidrawElement[] | null | undefined): ExcalidrawElement[],
  - refreshDimensions?: boolean
+ opts: { refreshDimensions?: boolean, repairBindings?: boolean }
) +| Prop | Type | Description | +| ---- | ---- | ---- | +| `elements` | ImportedDataState["elements"] | The `elements` to be restored | +| [`localElements`](#localelements) | ExcalidrawElement[] | null | undefined | When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. | +| [`opts`](#opts) | `Object` | The extra optional parameter to configure restored elements + +#### localElements + +When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. +Use this when you `import` elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the update + +#### opts +The extra optional parameter to configure restored elements. It has the following attributes + +| Prop | Type | Description| +| --- | --- | ------| +| `refreshDimensions` | `boolean` | Indicates whether we should also `recalculate` text element dimensions. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. | +| `repairBindings` |`boolean` | Indicates whether the `bindings` for the elements should be repaired. This is to make sure there are no containers with non existent bound text element id and no bound text elements with non existent container id. | + **_How to use_** ```js @@ -43,9 +62,6 @@ import { restoreElements } from "@excalidraw/excalidraw"; This function will make sure all properties of element is correctly set and if any attribute is missing, it will be set to its default value. -When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. -Use this when you import elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the updates. - Parameter `refreshDimensions` indicates whether we should also `recalculate` text element dimensions. Defaults to `false`. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. ### restore @@ -56,7 +72,9 @@ Parameter `refreshDimensions` indicates whether we should also `recalculate` tex restore( data: ImportedDataState,
  localAppState: Partial<AppState> | null | undefined,
  - localElements: ExcalidrawElement[] | null | undefined
): DataState + localElements: ExcalidrawElement[] | null | undefined
): DataState
+ opts: { refreshDimensions?: boolean, repairBindings?: boolean }
+ ) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md b/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md index c215923821..4d2745c090 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md +++ b/dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md @@ -339,3 +339,47 @@ The `device` has the following `attributes` | `isMobile` | `boolean` | Set to `true` when the device is `mobile` | | `isTouchScreen` | `boolean` | Set to `true` for `touch` devices | | `canDeviceFitSidebar` | `boolean` | Implies whether there is enough space to fit the `sidebar` | + +### i18n + +To help with localization, we export the following. + +| name | type | +| --- | --- | +| `defaultLang` | `string` | +| `languages` | [`Language[]`](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L15) | +| `useI18n` | [`() => { langCode, t }`](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L15) | + +```js +import { defaultLang, languages, useI18n } from "@excalidraw/excalidraw"; +``` + +#### defaultLang + +Default language code, `en`. + +#### languages + +List of supported language codes. You can pass any of these to `Excalidraw`'s [`langCode` prop](/docs/@excalidraw/excalidraw/api/props/#langcode). + +#### useI18n + +A hook that returns the current language code and translation helper function. You can use this to translate strings in the components you render as children of ``. + +```jsx live +function App() { + const { t } = useI18n(); + return ( +
+ + + +
+ ); +} +``` diff --git a/dev-docs/package.json b/dev-docs/package.json index dd3c45872e..1e87459104 100644 --- a/dev-docs/package.json +++ b/dev-docs/package.json @@ -18,7 +18,7 @@ "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", "@docusaurus/theme-live-codeblock": "2.2.0", - "@excalidraw/excalidraw": "0.14.2", + "@excalidraw/excalidraw": "0.15.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "docusaurus-plugin-sass": "0.2.3", diff --git a/dev-docs/src/theme/ReactLiveScope/index.js b/dev-docs/src/theme/ReactLiveScope/index.js index a282ad6f0b..e5263e1dbc 100644 --- a/dev-docs/src/theme/ReactLiveScope/index.js +++ b/dev-docs/src/theme/ReactLiveScope/index.js @@ -24,6 +24,7 @@ const ExcalidrawScope = { Sidebar: ExcalidrawComp.Sidebar, exportToCanvas: ExcalidrawComp.exportToCanvas, initialData, + useI18n: ExcalidrawComp.useI18n, }; export default ExcalidrawScope; diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index 041f39b559..ee3d50cbf8 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -1631,10 +1631,10 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@excalidraw/excalidraw@0.14.2": - version "0.14.2" - resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.14.2.tgz#150cb4b7a1bf0d11cd64295936c930e7e0db8375" - integrity sha512-8LdjpTBWEK5waDWB7Bt/G9YBI4j0OxkstUhvaDGz7dwQGfzF6FW5CXBoYHNEoX0qmb+Fg/NPOlZ7FrKsrSVCqg== +"@excalidraw/excalidraw@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.0.tgz#47170de8d3ff006e9d09dfede2815682b0d4485b" + integrity sha512-PJmh1VcuRHG4l+Zgt9qhezxrJ16tYCZFZ8if5IEfmTL9A/7c5mXxY/qrPTqiGlVC7jYs+ciePXQ0YUDzfOfbzw== "@hapi/hoek@^9.0.0": version "9.3.0" From 89304c9f66ac9949edb47d254ae3d99602b2dff4 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:23:47 +0530 Subject: [PATCH 07/29] fix: add readme back to npm package which was mistakenly removed (#6484) * fix: remove update readme script from release * update docs * remove * fix --- scripts/release.js | 20 -------------------- src/packages/excalidraw/CHANGELOG.md | 6 ++++++ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/scripts/release.js b/scripts/release.js index 986eadc2a3..24ac89c6be 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -1,22 +1,9 @@ -const fs = require("fs"); const { execSync } = require("child_process"); const excalidrawDir = `${__dirname}/../src/packages/excalidraw`; const excalidrawPackage = `${excalidrawDir}/package.json`; const pkg = require(excalidrawPackage); -const originalReadMe = fs.readFileSync(`${excalidrawDir}/README.md`, "utf8"); - -const updateReadme = () => { - const excalidrawIndex = originalReadMe.indexOf("### Excalidraw"); - - // remove note for stable readme - const data = originalReadMe.slice(excalidrawIndex); - - // update readme - fs.writeFileSync(`${excalidrawDir}/README.md`, data, "utf8"); -}; - const publish = () => { try { execSync(`yarn --frozen-lockfile`); @@ -30,15 +17,8 @@ const publish = () => { }; const release = () => { - updateReadme(); - console.info("Note for stable readme removed"); - publish(); console.info(`Published ${pkg.version}!`); - - // revert readme after release - fs.writeFileSync(`${excalidrawDir}/README.md`, originalReadMe, "utf8"); - console.info("Readme reverted"); }; release(); diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 5143bda315..b2ac3a682c 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## Unreleased + +### Docs + +- Add the readme back to the package which was mistakenly removed [#6484](https://github.com/excalidraw/excalidraw/pull/6484) + ## 0.15.0 (2023-04-18) ### Features From b64beaf5ba42ab55302fb9f5b752b06b28c0c213 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:32:33 +0530 Subject: [PATCH 08/29] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@?= =?UTF-8?q?0.15.1=20=20=F0=9F=8E=89=20(#6485)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 8 +++++++- src/packages/excalidraw/package.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index b2ac3a682c..f285349f07 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,12 +11,18 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased +## 0.15.1 (2023-04-18) ### Docs - Add the readme back to the package which was mistakenly removed [#6484](https://github.com/excalidraw/excalidraw/pull/6484) +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +--- + ## 0.15.0 (2023-04-18) ### Features diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index 2351ee6a5b..a598374548 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.15.0", + "version": "0.15.1", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From ff3c2e5a160d2982d48657091f1fbcaacf82a6e2 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 18 Apr 2023 21:52:08 +0530 Subject: [PATCH 09/29] docs: fix docs link in readme (#6486) * docs: fix docs link in readme * update changelog --- src/packages/excalidraw/CHANGELOG.md | 6 ++++++ src/packages/excalidraw/README.md | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index f285349f07..e69f05d0a3 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> +## Unreleased + +### Docs + +- Fix docs link in readme [#6486](https://github.com/excalidraw/excalidraw/pull/6486) + ## 0.15.1 (2023-04-18) ### Docs diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md index eaeef4b0c6..d650885df0 100644 --- a/src/packages/excalidraw/README.md +++ b/src/packages/excalidraw/README.md @@ -38,8 +38,8 @@ Excalidraw takes _100%_ of `width` and `height` of the containing block so make ## Integration -Head over to the [docs](https://docs.excalidraw.com/docs/package/integration) +Head over to the [docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/integration) ## API -Head over to the [docs](https://docs.excalidraw.com/docs/package/api) +Head over to the [docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api) From 98a77d7426ea3de9c26418f35ef9c6389f3e1d25 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Wed, 19 Apr 2023 17:02:20 +0530 Subject: [PATCH 10/29] chore: show bounding box only when flag is true (#6490) --- src/renderer/renderElement.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 21de70cb91..f85c83a6b7 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -864,7 +864,8 @@ const drawElementFromCanvas = ( ); if ( - process.env.REACT_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX && + process.env.REACT_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX === + "true" && hasBoundTextElement(element) ) { const coords = getContainerCoords(element); From eea30da05a5847e28df7f56051575c5daf7ae49e Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 19 Apr 2023 16:23:24 +0200 Subject: [PATCH 11/29] fix: incorrect background fill button active state (#6491) --- src/actions/actionProperties.tsx | 12 +++++++----- src/locales/en.json | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/actions/actionProperties.tsx b/src/actions/actionProperties.tsx index ed714816b6..382e964b9c 100644 --- a/src/actions/actionProperties.tsx +++ b/src/actions/actionProperties.tsx @@ -84,7 +84,7 @@ import { isSomeElementSelected, } from "../scene"; import { hasStrokeColor } from "../scene/comparisons"; -import { arrayToMap } from "../utils"; +import { arrayToMap, getShortcutKey } from "../utils"; import { register } from "./register"; const FONT_SIZE_RELATIVE_INCREASE_STEP = 0.1; @@ -314,9 +314,9 @@ export const actionChangeFillStyle = register({ }, PanelComponent: ({ elements, appState, updateData }) => { const selectedElements = getSelectedElements(elements, appState); - const allElementsZigZag = selectedElements.every( - (el) => el.fillStyle === "zigzag", - ); + const allElementsZigZag = + selectedElements.length > 0 && + selectedElements.every((el) => el.fillStyle === "zigzag"); return (
@@ -326,7 +326,9 @@ export const actionChangeFillStyle = register({ options={[ { value: "hachure", - text: t("labels.hachure"), + text: `${ + allElementsZigZag ? t("labels.zigzag") : t("labels.hachure") + } (${getShortcutKey("Alt-Click")})`, icon: allElementsZigZag ? FillZigZagIcon : FillHachureIcon, active: allElementsZigZag ? true : undefined, }, diff --git a/src/locales/en.json b/src/locales/en.json index 8752d415aa..7e250a800e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -54,6 +54,7 @@ "veryLarge": "Very large", "solid": "Solid", "hachure": "Hachure", + "zigzag": "Zigzag", "crossHatch": "Cross-hatch", "thin": "Thin", "bold": "Bold", From 404a79e241e4aa2eda287513e9cef497e40da832 Mon Sep 17 00:00:00 2001 From: Max Kovalenko Date: Wed, 19 Apr 2023 13:18:03 -0400 Subject: [PATCH 12/29] chore: typo (collab) - reconciliation.ts (#6447) --- src/excalidraw-app/collab/reconciliation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/excalidraw-app/collab/reconciliation.ts b/src/excalidraw-app/collab/reconciliation.ts index 76b6f052a3..3f50bc3587 100644 --- a/src/excalidraw-app/collab/reconciliation.ts +++ b/src/excalidraw-app/collab/reconciliation.ts @@ -65,7 +65,7 @@ export const reconcileElements = ( // Mark duplicate for removal as it'll be replaced with the remote element if (local) { - // Unless the ramote and local elements are the same element in which case + // Unless the remote and local elements are the same element in which case // we need to keep it as we'd otherwise discard it from the resulting // array. if (local[0] === remoteElement) { From 5ddb28d37830174798ad3a4e7b1ce319ea8b1cfb Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 20 Apr 2023 11:10:46 +0530 Subject: [PATCH 13/29] fix: support breaking words containing hyphen - (#6014) * fix: support breaking words containing hyphen - * fix * add spec * fix * fix * fix * fix and add spec * improve code and add more tests --- src/element/textElement.test.ts | 51 +++++++++++++++++++++++++++++++++ src/element/textElement.ts | 47 ++++++++++++++++++++++++------ 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/element/textElement.test.ts b/src/element/textElement.test.ts index 106ed7beab..f83eafd1b4 100644 --- a/src/element/textElement.test.ts +++ b/src/element/textElement.test.ts @@ -9,6 +9,7 @@ import { detectLineHeight, getLineHeightInPx, getDefaultLineHeight, + parseTokens, } from "./textElement"; import { FontString } from "./types"; @@ -183,6 +184,56 @@ now`, expect(wrapText(text, font, -1)).toEqual(text); expect(wrapText(text, font, Infinity)).toEqual(text); }); + + it("should wrap the text correctly when text contains hyphen", () => { + let text = + "Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects"; + const res = wrapText(text, font, 110); + expect(res).toBe( + `Wikipedia \nis hosted \nby \nWikimedia-\nFoundation,\na non-\nprofit \norganizati\non that \nalso hosts\na range-of\nother \nprojects`, + ); + + text = "Hello thereusing-now"; + expect(wrapText(text, font, 100)).toEqual("Hello \nthereusin\ng-now"); + }); +}); + +describe("Test parseTokens", () => { + it("should split into tokens correctly", () => { + let text = "Excalidraw is a virtual collaborative whiteboard"; + expect(parseTokens(text)).toEqual([ + "Excalidraw", + "is", + "a", + "virtual", + "collaborative", + "whiteboard", + ]); + + text = + "Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects"; + expect(parseTokens(text)).toEqual([ + "Wikipedia", + "is", + "hosted", + "by", + "Wikimedia-", + "", + "Foundation,", + "a", + "non-", + "profit", + "organization", + "that", + "also", + "hosts", + "a", + "range-", + "of", + "other", + "projects", + ]); + }); }); describe("Test measureText", () => { diff --git a/src/element/textElement.ts b/src/element/textElement.ts index 38da5df5a4..f01ba3e1b9 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -419,6 +419,24 @@ export const getTextHeight = ( return getLineHeightInPx(fontSize, lineHeight) * lineCount; }; +export const parseTokens = (text: string) => { + // Splitting words containing "-" as those are treated as separate words + // by css wrapping algorithm eg non-profit => non-, profit + const words = text.split("-"); + if (words.length > 1) { + // non-proft org => ['non-', 'profit org'] + words.forEach((word, index) => { + if (index !== words.length - 1) { + words[index] = word += "-"; + } + }); + } + // Joining the words with space and splitting them again with space to get the + // final list of tokens + // ['non-', 'profit org'] =>,'non- proft org' => ['non-','profit','org'] + return words.join(" ").split(" "); +}; + export const wrapText = (text: string, font: FontString, maxWidth: number) => { // if maxWidth is not finite or NaN which can happen in case of bugs in // computation, we need to make sure we don't continue as we'll end up @@ -444,17 +462,16 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { currentLine = ""; currentLineWidthTillNow = 0; }; - originalLines.forEach((originalLine) => { const currentLineWidth = getTextWidth(originalLine, font); - //Push the line if its <= maxWidth + // Push the line if its <= maxWidth if (currentLineWidth <= maxWidth) { lines.push(originalLine); return; // continue } - const words = originalLine.split(" "); + const words = parseTokens(originalLine); resetParams(); let index = 0; @@ -472,6 +489,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { else if (currentWordWidth > maxWidth) { // push current line since the current word exceeds the max width // so will be appended in next line + push(currentLine); resetParams(); @@ -492,15 +510,15 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { currentLine += currentChar; } } - // push current line if appending space exceeds max width if (currentLineWidthTillNow + spaceWidth >= maxWidth) { push(currentLine); resetParams(); - } else { // space needs to be appended before next word // as currentLine contains chars which couldn't be appended - // to previous line + // to previous line unless the line ends with hyphen to sync + // with css word-wrap + } else if (!currentLine.endsWith("-")) { currentLine += " "; currentLineWidthTillNow += spaceWidth; } @@ -518,12 +536,23 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { break; } index++; - currentLine += `${word} `; + + // if word ends with "-" then we don't need to add space + // to sync with css word-wrap + const shouldAppendSpace = !word.endsWith("-"); + currentLine += word; + + if (shouldAppendSpace) { + currentLine += " "; + } // Push the word if appending space exceeds max width if (currentLineWidthTillNow + spaceWidth >= maxWidth) { - const word = currentLine.slice(0, -1); - push(word); + if (shouldAppendSpace) { + lines.push(currentLine.slice(0, -1)); + } else { + lines.push(currentLine); + } resetParams(); break; } From 851b9b7aecb97bee6a55da669f4a431a3fca733c Mon Sep 17 00:00:00 2001 From: siddhant <30566406+siddhant1@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:34:39 +0530 Subject: [PATCH 14/29] fix: rotate the text element when binding to a rotated container (#6477) * Updated logic to update the bound child angle from the parent * update angle when generating text element * add test * remove * fix --------- Co-authored-by: Aakansha Doshi --- src/components/App.tsx | 1 + src/element/textWysiwyg.test.tsx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/components/App.tsx b/src/components/App.tsx index 20def468aa..413a130d86 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2744,6 +2744,7 @@ class App extends React.Component { containerId: shouldBindToContainer ? container?.id : undefined, groupIds: container?.groupIds ?? [], lineHeight, + angle: container?.angle ?? 0, }); if (!existingTextElement && shouldBindToContainer && container) { diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 71c75c5a06..f3c75db8bb 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -526,6 +526,36 @@ describe("textWysiwyg", () => { ]); }); + it("should set the text element angle to same as container angle when binding to rotated container", async () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 90, + height: 75, + angle: 45, + }); + h.elements = [rectangle]; + mouse.doubleClickAt(rectangle.x + 10, rectangle.y + 10); + const text = h.elements[1] as ExcalidrawTextElementWithContainer; + expect(text.type).toBe("text"); + expect(text.containerId).toBe(rectangle.id); + expect(rectangle.boundElements).toStrictEqual([ + { id: text.id, type: "text" }, + ]); + expect(text.angle).toBe(rectangle.angle); + mouse.down(); + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + + fireEvent.change(editor, { target: { value: "Hello World!" } }); + + await new Promise((r) => setTimeout(r, 0)); + editor.blur(); + expect(rectangle.boundElements).toStrictEqual([ + { id: text.id, type: "text" }, + ]); + }); + it("should compute the container height correctly and not throw error when height is updated while editing the text", async () => { const diamond = API.createElement({ type: "diamond", From 9368a9ce3ec3a49d92b0d8cf2a6910d94d7b6191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Apr 2023 17:05:03 +0530 Subject: [PATCH 15/29] build(deps): bump webpack from 5.75.0 to 5.76.1 (#6357) Bumps [webpack](https://github.com/webpack/webpack) from 5.75.0 to 5.76.1. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.75.0...v5.76.1) --- updated-dependencies: - dependency-name: webpack dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 31624f92bc..89d1539098 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10601,9 +10601,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.64.4: - version "5.75.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" - integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + version "5.76.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.1.tgz#7773de017e988bccb0f13c7d75ec245f377d295c" + integrity sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" From d12a9fdd40a10394a2f78f8cef825633ad0f0b87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Apr 2023 17:07:56 +0530 Subject: [PATCH 16/29] build(deps): bump ua-parser-js from 0.7.31 to 0.7.33 in /dev-docs (#6164) Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33. - [Release notes](https://github.com/faisalman/ua-parser-js/releases) - [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md) - [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.31...0.7.33) --- updated-dependencies: - dependency-name: ua-parser-js dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dev-docs/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index ee3d50cbf8..1b547bbbc5 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -7159,9 +7159,9 @@ typescript@^4.7.4: integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== ua-parser-js@^0.7.30: - version "0.7.31" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== unescape@^1.0.1: version "1.0.1" From c4445c181b9f1e87b346a38a6d3e39ed1e1e67b1 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 20 Apr 2023 17:34:06 +0530 Subject: [PATCH 17/29] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@?= =?UTF-8?q?0.15.2=20=20=F0=9F=8E=89=20(#6495)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 16 +++++++++++++++- src/packages/excalidraw/package.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index e69f05d0a3..a2f7466b8d 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,12 +11,26 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased +## 0.15.2 (2023-04-20) ### Docs - Fix docs link in readme [#6486](https://github.com/excalidraw/excalidraw/pull/6486) +## Excalidraw Library + +**_This section lists the updates made to the excalidraw library and will not affect the integration._** + +### Fixes + +- Rotate the text element when binding to a rotated container [#6477](https://github.com/excalidraw/excalidraw/pull/6477) + +- Support breaking words containing hyphen - [#6014](https://github.com/excalidraw/excalidraw/pull/6014) + +- Incorrect background fill button active state [#6491](https://github.com/excalidraw/excalidraw/pull/6491) + +--- + ## 0.15.1 (2023-04-18) ### Docs diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index a598374548..57f6ce3951 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.15.1", + "version": "0.15.2", "main": "main.js", "types": "types/packages/excalidraw/index.d.ts", "files": [ From 2a4799d8c86964a3e3fd6f08defc3452c033d8c9 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 20 Apr 2023 17:40:08 +0530 Subject: [PATCH 18/29] chore: upgrade excalidraw version to 0.15.2 (#6496) chore: updragde excalidraw version to 0.15.2 --- dev-docs/package.json | 2 +- dev-docs/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev-docs/package.json b/dev-docs/package.json index 1e87459104..0aee8e01f4 100644 --- a/dev-docs/package.json +++ b/dev-docs/package.json @@ -18,7 +18,7 @@ "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", "@docusaurus/theme-live-codeblock": "2.2.0", - "@excalidraw/excalidraw": "0.15.0", + "@excalidraw/excalidraw": "0.15.2", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "docusaurus-plugin-sass": "0.2.3", diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index 1b547bbbc5..6206a60e9b 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -1631,10 +1631,10 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@excalidraw/excalidraw@0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.0.tgz#47170de8d3ff006e9d09dfede2815682b0d4485b" - integrity sha512-PJmh1VcuRHG4l+Zgt9qhezxrJ16tYCZFZ8if5IEfmTL9A/7c5mXxY/qrPTqiGlVC7jYs+ciePXQ0YUDzfOfbzw== +"@excalidraw/excalidraw@0.15.2": + version "0.15.2" + resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.2.tgz#7dba4f6e10c52015a007efb75a9fc1afe598574c" + integrity sha512-rTI02kgWSTXiUdIkBxt9u/581F3eXcqQgJdIxmz54TFtG3ughoxO5fr4t7Fr2LZIturBPqfocQHGKZ0t2KLKgw== "@hapi/hoek@^9.0.0": version "9.3.0" From fee760d38c18c4d90c053b98d7cfa355707fc427 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Fri, 21 Apr 2023 22:53:49 +0200 Subject: [PATCH 19/29] feat: allow `avif`, `jfif`, `webp`, `bmp`, `ico` image types (#6500 * feat: allow `avif`, `jfif`, `webp`, `bmp`, `ico` image types * dedupe for SSOT * more SSOT --- src/components/App.tsx | 5 ++++- src/constants.ts | 32 ++++++++++++++++---------------- src/data/blob.ts | 8 +++----- src/data/filesystem.ts | 11 +---------- src/types.ts | 8 ++++---- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 413a130d86..8000ea560d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -60,6 +60,7 @@ import { ENV, EVENT, GRID_SIZE, + IMAGE_MIME_TYPES, IMAGE_RENDER_TIMEOUT, isAndroid, isBrave, @@ -5743,7 +5744,9 @@ class App extends React.Component { const imageFile = await fileOpen({ description: "Image", - extensions: ["jpg", "png", "svg", "gif"], + extensions: Object.keys( + IMAGE_MIME_TYPES, + ) as (keyof typeof IMAGE_MIME_TYPES)[], }); const imageElement = this.createImageElement({ diff --git a/src/constants.ts b/src/constants.ts index 23fefa6e54..19b41b6880 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -105,20 +105,30 @@ export const CANVAS_ONLY_ACTIONS = ["selectAll"]; export const GRID_SIZE = 20; // TODO make it configurable? -export const MIME_TYPES = { - excalidraw: "application/vnd.excalidraw+json", - excalidrawlib: "application/vnd.excalidrawlib+json", - json: "application/json", +export const IMAGE_MIME_TYPES = { svg: "image/svg+xml", - "excalidraw.svg": "image/svg+xml", png: "image/png", - "excalidraw.png": "image/png", jpg: "image/jpeg", gif: "image/gif", webp: "image/webp", bmp: "image/bmp", ico: "image/x-icon", + avif: "image/avif", + jfif: "image/jfif", +} as const; + +export const MIME_TYPES = { + json: "application/json", + // excalidraw data + excalidraw: "application/vnd.excalidraw+json", + excalidrawlib: "application/vnd.excalidrawlib+json", + // image-encoded excalidraw data + "excalidraw.svg": "image/svg+xml", + "excalidraw.png": "image/png", + // binary binary: "application/octet-stream", + // image + ...IMAGE_MIME_TYPES, } as const; export const EXPORT_DATA_TYPES = { @@ -189,16 +199,6 @@ export const DEFAULT_EXPORT_PADDING = 10; // px export const DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT = 1440; -export const ALLOWED_IMAGE_MIME_TYPES = [ - MIME_TYPES.png, - MIME_TYPES.jpg, - MIME_TYPES.svg, - MIME_TYPES.gif, - MIME_TYPES.webp, - MIME_TYPES.bmp, - MIME_TYPES.ico, -] as const; - export const MAX_ALLOWED_FILE_BYTES = 2 * 1024 * 1024; export const SVG_NS = "http://www.w3.org/2000/svg"; diff --git a/src/data/blob.ts b/src/data/blob.ts index 47cff293fa..4565b5cb58 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -1,6 +1,6 @@ import { nanoid } from "nanoid"; import { cleanAppStateForExport } from "../appState"; -import { ALLOWED_IMAGE_MIME_TYPES, MIME_TYPES } from "../constants"; +import { IMAGE_MIME_TYPES, MIME_TYPES } from "../constants"; import { clearElementsForExport } from "../element"; import { ExcalidrawElement, FileId } from "../element/types"; import { CanvasError } from "../errors"; @@ -117,11 +117,9 @@ export const isImageFileHandle = (handle: FileSystemHandle | null) => { export const isSupportedImageFile = ( blob: Blob | null | undefined, -): blob is Blob & { type: typeof ALLOWED_IMAGE_MIME_TYPES[number] } => { +): blob is Blob & { type: ValueOf } => { const { type } = blob || {}; - return ( - !!type && (ALLOWED_IMAGE_MIME_TYPES as readonly string[]).includes(type) - ); + return !!type && (Object.values(IMAGE_MIME_TYPES) as string[]).includes(type); }; export const loadSceneOrLibraryFromBlob = async ( diff --git a/src/data/filesystem.ts b/src/data/filesystem.ts index ffe088fafc..fa29604f45 100644 --- a/src/data/filesystem.ts +++ b/src/data/filesystem.ts @@ -8,16 +8,7 @@ import { EVENT, MIME_TYPES } from "../constants"; import { AbortError } from "../errors"; import { debounce } from "../utils"; -type FILE_EXTENSION = - | "gif" - | "jpg" - | "png" - | "excalidraw.png" - | "svg" - | "excalidraw.svg" - | "json" - | "excalidraw" - | "excalidrawlib"; +type FILE_EXTENSION = Exclude; const INPUT_CHANGE_INTERVAL_MS = 500; diff --git a/src/types.ts b/src/types.ts index 09848df1dc..e5ad01b595 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,9 +29,9 @@ import { isOverScrollBars } from "./scene"; import { MaybeTransformHandleType } from "./element/transformHandles"; import Library from "./data/library"; import type { FileSystemHandle } from "./data/filesystem"; -import type { ALLOWED_IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; +import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; import { ContextMenuItems } from "./components/ContextMenu"; -import { Merge, ForwardRef } from "./utility-types"; +import { Merge, ForwardRef, ValueOf } from "./utility-types"; import React from "react"; export type Point = Readonly; @@ -60,7 +60,7 @@ export type DataURL = string & { _brand: "DataURL" }; export type BinaryFileData = { mimeType: - | typeof ALLOWED_IMAGE_MIME_TYPES[number] + | ValueOf // future user or unknown file type | typeof MIME_TYPES.binary; id: FileId; @@ -419,7 +419,7 @@ export type AppClassProperties = { FileId, { image: HTMLImageElement | Promise; - mimeType: typeof ALLOWED_IMAGE_MIME_TYPES[number]; + mimeType: ValueOf; } >; files: BinaryFiles; From 9d5cfbbfb73c9abcf9ae534eb61548a9aa6fe225 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Sat, 22 Apr 2023 14:17:13 +0200 Subject: [PATCH 20/29] fix: text jumps when editing on Android Chrome (#6503) * debug logging * debug * debugging * Update textWysiwyg.tsx * Update textWysiwyg.tsx * extended debug information * debug * debug * trace * further debug * don't drag while editing * removing all console.logs * revert all changes to textWysiwyt.tsx * updated comment --- src/components/App.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 8000ea560d..546261436b 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -4721,7 +4721,12 @@ class App extends React.Component { pointerDownState.drag.hasOccurred = true; // prevent dragging even if we're no longer holding cmd/ctrl otherwise // it would have weird results (stuff jumping all over the screen) - if (selectedElements.length > 0 && !pointerDownState.withCmdOrCtrl) { + // Checking for editingElement to avoid jump while editing on mobile #6503 + if ( + selectedElements.length > 0 && + !pointerDownState.withCmdOrCtrl && + !this.state.editingElement + ) { const [dragX, dragY] = getGridPoint( pointerCoords.x - pointerDownState.drag.offset.x, pointerCoords.y - pointerDownState.drag.offset.y, From d35386755f951ae7612c2c793ad11d79503e3482 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Mon, 24 Apr 2023 10:26:21 +0200 Subject: [PATCH 21/29] feat: retain `seed` on shift-paste (#6509) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thanks for the review 👍 --- src/actions/actionClipboard.tsx | 2 +- src/clipboard.ts | 38 ++++++++++++------ src/components/App.tsx | 5 +++ src/components/LibraryMenuItems.tsx | 2 +- src/element/newElement.ts | 17 +++++++- src/packages/utils.ts | 10 +---- src/tests/clipboard.test.tsx | 61 ++++++++++++++++++++--------- src/tests/flip.test.tsx | 21 ++++------ src/tests/test-utils.ts | 21 ++++++++++ 9 files changed, 120 insertions(+), 57 deletions(-) diff --git a/src/actions/actionClipboard.tsx b/src/actions/actionClipboard.tsx index 661f65f38f..18fefafd22 100644 --- a/src/actions/actionClipboard.tsx +++ b/src/actions/actionClipboard.tsx @@ -18,7 +18,7 @@ export const actionCopy = register({ perform: (elements, appState, _, app) => { const selectedElements = getSelectedElements(elements, appState, true); - copyToClipboard(selectedElements, appState, app.files); + copyToClipboard(selectedElements, app.files); return { commitToHistory: false, diff --git a/src/clipboard.ts b/src/clipboard.ts index 5f7950c53e..c0f5844dd2 100644 --- a/src/clipboard.ts +++ b/src/clipboard.ts @@ -2,12 +2,12 @@ import { ExcalidrawElement, NonDeletedExcalidrawElement, } from "./element/types"; -import { AppState, BinaryFiles } from "./types"; +import { BinaryFiles } from "./types"; import { SVG_EXPORT_TAG } from "./scene/export"; import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "./constants"; import { isInitializedImageElement } from "./element/typeChecks"; -import { isPromiseLike } from "./utils"; +import { isPromiseLike, isTestEnv } from "./utils"; type ElementsClipboard = { type: typeof EXPORT_DATA_TYPES.excalidrawClipboard; @@ -55,24 +55,40 @@ const clipboardContainsElements = ( export const copyToClipboard = async ( elements: readonly NonDeletedExcalidrawElement[], - appState: AppState, files: BinaryFiles | null, ) => { + let foundFile = false; + + const _files = elements.reduce((acc, element) => { + if (isInitializedImageElement(element)) { + foundFile = true; + if (files && files[element.fileId]) { + acc[element.fileId] = files[element.fileId]; + } + } + return acc; + }, {} as BinaryFiles); + + if (foundFile && !files) { + console.warn( + "copyToClipboard: attempting to file element(s) without providing associated `files` object.", + ); + } + // select binded text elements when copying const contents: ElementsClipboard = { type: EXPORT_DATA_TYPES.excalidrawClipboard, elements, - files: files - ? elements.reduce((acc, element) => { - if (isInitializedImageElement(element) && files[element.fileId]) { - acc[element.fileId] = files[element.fileId]; - } - return acc; - }, {} as BinaryFiles) - : undefined, + files: files ? _files : undefined, }; const json = JSON.stringify(contents); + + if (isTestEnv()) { + return json; + } + CLIPBOARD = json; + try { PREFER_APP_CLIPBOARD = false; await copyTextToSystemClipboard(json); diff --git a/src/components/App.tsx b/src/components/App.tsx index 546261436b..d22a0507cb 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1590,6 +1590,7 @@ class App extends React.Component { elements: data.elements, files: data.files || null, position: "cursor", + retainSeed: isPlainPaste, }); } else if (data.text) { this.addTextFromPaste(data.text, isPlainPaste); @@ -1603,6 +1604,7 @@ class App extends React.Component { elements: readonly ExcalidrawElement[]; files: BinaryFiles | null; position: { clientX: number; clientY: number } | "cursor" | "center"; + retainSeed?: boolean; }) => { const elements = restoreElements(opts.elements, null); const [minX, minY, maxX, maxY] = getCommonBounds(elements); @@ -1640,6 +1642,9 @@ class App extends React.Component { y: element.y + gridY - minY, }); }), + { + randomizeSeed: !opts.retainSeed, + }, ); const nextElements = [ diff --git a/src/components/LibraryMenuItems.tsx b/src/components/LibraryMenuItems.tsx index 7ae6517a8f..19bb33308e 100644 --- a/src/components/LibraryMenuItems.tsx +++ b/src/components/LibraryMenuItems.tsx @@ -102,7 +102,7 @@ const LibraryMenuItems = ({ ...item, // duplicate each library item before inserting on canvas to confine // ids and bindings to each library item. See #6465 - elements: duplicateElements(item.elements), + elements: duplicateElements(item.elements, { randomizeSeed: true }), }; }); }; diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 36c8cc0e02..c1b0f17bfa 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -20,7 +20,7 @@ import { isTestEnv, } from "../utils"; import { randomInteger, randomId } from "../random"; -import { mutateElement, newElementWith } from "./mutateElement"; +import { bumpVersion, mutateElement, newElementWith } from "./mutateElement"; import { getNewGroupIdsForDuplication } from "../groups"; import { AppState } from "../types"; import { getElementAbsoluteCoords } from "."; @@ -539,8 +539,16 @@ export const duplicateElement = ( * it's advised to supply the whole elements array, or sets of elements that * are encapsulated (such as library items), if the purpose is to retain * bindings to the cloned elements intact. + * + * NOTE by default does not randomize or regenerate anything except the id. */ -export const duplicateElements = (elements: readonly ExcalidrawElement[]) => { +export const duplicateElements = ( + elements: readonly ExcalidrawElement[], + opts?: { + /** NOTE also updates version flags and `updated` */ + randomizeSeed: boolean; + }, +) => { const clonedElements: ExcalidrawElement[] = []; const origElementsMap = arrayToMap(elements); @@ -574,6 +582,11 @@ export const duplicateElements = (elements: readonly ExcalidrawElement[]) => { clonedElement.id = maybeGetNewId(element.id)!; + if (opts?.randomizeSeed) { + clonedElement.seed = randomInteger(); + bumpVersion(clonedElement); + } + if (clonedElement.groupIds) { clonedElement.groupIds = clonedElement.groupIds.map((groupId) => { if (!groupNewIdsMap.has(groupId)) { diff --git a/src/packages/utils.ts b/src/packages/utils.ts index 560fa13ca4..d9365895e4 100644 --- a/src/packages/utils.ts +++ b/src/packages/utils.ts @@ -220,15 +220,7 @@ export const exportToClipboard = async ( } else if (opts.type === "png") { await copyBlobToClipboardAsPng(exportToBlob(opts)); } else if (opts.type === "json") { - const appState = { - offsetTop: 0, - offsetLeft: 0, - width: 0, - height: 0, - ...getDefaultAppState(), - ...opts.appState, - }; - await copyToClipboard(opts.elements, appState, opts.files); + await copyToClipboard(opts.elements, opts.files); } else { throw new Error("Invalid export type"); } diff --git a/src/tests/clipboard.test.tsx b/src/tests/clipboard.test.tsx index 1fdc0f4521..bbaa4d1799 100644 --- a/src/tests/clipboard.test.tsx +++ b/src/tests/clipboard.test.tsx @@ -1,5 +1,10 @@ import ReactDOM from "react-dom"; -import { render, waitFor, GlobalTestState } from "./test-utils"; +import { + render, + waitFor, + GlobalTestState, + createPasteEvent, +} from "./test-utils"; import { Pointer, Keyboard } from "./helpers/ui"; import ExcalidrawApp from "../excalidraw-app"; import { KEYS } from "../keys"; @@ -9,6 +14,8 @@ import { } from "../element/textElement"; import { getElementBounds } from "../element"; import { NormalizedZoomValue } from "../types"; +import { API } from "./helpers/api"; +import { copyToClipboard } from "../clipboard"; const { h } = window; @@ -35,38 +42,28 @@ const setClipboardText = (text: string) => { }); }; -const sendPasteEvent = () => { - const clipboardEvent = new Event("paste", { - bubbles: true, - cancelable: true, - composed: true, - }); - - // set `clipboardData` properties. - // @ts-ignore - clipboardEvent.clipboardData = { - getData: () => window.navigator.clipboard.readText(), - files: [], - }; - +const sendPasteEvent = (text?: string) => { + const clipboardEvent = createPasteEvent( + text || (() => window.navigator.clipboard.readText()), + ); document.dispatchEvent(clipboardEvent); }; -const pasteWithCtrlCmdShiftV = () => { +const pasteWithCtrlCmdShiftV = (text?: string) => { Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { //triggering keydown with an empty clipboard Keyboard.keyPress(KEYS.V); //triggering paste event with faked clipboard - sendPasteEvent(); + sendPasteEvent(text); }); }; -const pasteWithCtrlCmdV = () => { +const pasteWithCtrlCmdV = (text?: string) => { Keyboard.withModifierKeys({ ctrl: true }, () => { //triggering keydown with an empty clipboard Keyboard.keyPress(KEYS.V); //triggering paste event with faked clipboard - sendPasteEvent(); + sendPasteEvent(text); }); }; @@ -89,6 +86,32 @@ beforeEach(async () => { }); }); +describe("general paste behavior", () => { + it("should randomize seed on paste", async () => { + const rectangle = API.createElement({ type: "rectangle" }); + const clipboardJSON = (await copyToClipboard([rectangle], null))!; + + pasteWithCtrlCmdV(clipboardJSON); + + await waitFor(() => { + expect(h.elements.length).toBe(1); + expect(h.elements[0].seed).not.toBe(rectangle.seed); + }); + }); + + it("should retain seed on shift-paste", async () => { + const rectangle = API.createElement({ type: "rectangle" }); + const clipboardJSON = (await copyToClipboard([rectangle], null))!; + + // assert we don't randomize seed on shift-paste + pasteWithCtrlCmdShiftV(clipboardJSON); + await waitFor(() => { + expect(h.elements.length).toBe(1); + expect(h.elements[0].seed).toBe(rectangle.seed); + }); + }); +}); + describe("paste text as single lines", () => { it("should create an element for each line when copying with Ctrl/Cmd+V", async () => { const text = "sajgfakfn\naaksfnknas\nakefnkasf"; diff --git a/src/tests/flip.test.tsx b/src/tests/flip.test.tsx index 45a5e14776..c1469bc831 100644 --- a/src/tests/flip.test.tsx +++ b/src/tests/flip.test.tsx @@ -1,5 +1,10 @@ import ReactDOM from "react-dom"; -import { GlobalTestState, render, waitFor } from "./test-utils"; +import { + createPasteEvent, + GlobalTestState, + render, + waitFor, +} from "./test-utils"; import { UI, Pointer } from "./helpers/ui"; import { API } from "./helpers/api"; import { actionFlipHorizontal, actionFlipVertical } from "../actions"; @@ -680,19 +685,7 @@ describe("freedraw", () => { describe("image", () => { const createImage = async () => { const sendPasteEvent = (file?: File) => { - const clipboardEvent = new Event("paste", { - bubbles: true, - cancelable: true, - composed: true, - }); - - // set `clipboardData` properties. - // @ts-ignore - clipboardEvent.clipboardData = { - getData: () => window.navigator.clipboard.readText(), - files: [file], - }; - + const clipboardEvent = createPasteEvent("", file ? [file] : []); document.dispatchEvent(clipboardEvent); }; diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts index c33e80c7d8..9560f681fd 100644 --- a/src/tests/test-utils.ts +++ b/src/tests/test-utils.ts @@ -190,3 +190,24 @@ export const toggleMenu = (container: HTMLElement) => { // open menu fireEvent.click(container.querySelector(".dropdown-menu-button")!); }; + +export const createPasteEvent = ( + text: + | string + | /* getData function */ ((type: string) => string | Promise), + files?: File[], +) => { + return Object.assign( + new Event("paste", { + bubbles: true, + cancelable: true, + composed: true, + }), + { + clipboardData: { + getData: typeof text === "string" ? () => text : text, + files: files || [], + }, + }, + ); +}; From 1815cf3213b1d19cb6aa1a54dabd969196a65a28 Mon Sep 17 00:00:00 2001 From: Nainterceptor Date: Tue, 25 Apr 2023 13:21:25 +0200 Subject: [PATCH 22/29] build: Add version tags to Docker build (#6508) ci: Add version tags --- .github/workflows/publish-docker.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index a4a8a4c5ff..3602bb6606 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -12,14 +12,24 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + excalidraw/excalidraw + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} - name: Login to DockerHub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . push: true - tags: excalidraw/excalidraw:latest + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} From 1e9943323ad5f34bb4efc48c5b5a9b01387dbc36 Mon Sep 17 00:00:00 2001 From: suwalkanishka Date: Tue, 25 Apr 2023 17:35:19 +0545 Subject: [PATCH 23/29] style: fix font family inconsistencies (#6501) style: font fix for four components The browser default font was showing up in various locations. Fixed them to show the desired ui font. --- src/components/ColorPicker.scss | 1 + src/components/ContextMenu.scss | 1 + src/components/Tooltip.scss | 3 +++ src/css/styles.scss | 2 ++ 4 files changed, 7 insertions(+) diff --git a/src/components/ColorPicker.scss b/src/components/ColorPicker.scss index 52ea20a195..b816b25536 100644 --- a/src/components/ColorPicker.scss +++ b/src/components/ColorPicker.scss @@ -183,6 +183,7 @@ width: 100%; margin: 0; font-size: 0.875rem; + font-family: inherit; background-color: transparent; color: var(--text-primary-color); border: 0; diff --git a/src/components/ContextMenu.scss b/src/components/ContextMenu.scss index 5797631196..81ced38807 100644 --- a/src/components/ContextMenu.scss +++ b/src/components/ContextMenu.scss @@ -30,6 +30,7 @@ background-color: transparent; border: none; white-space: nowrap; + font-family: inherit; display: grid; grid-template-columns: 1fr 0.2fr; diff --git a/src/components/Tooltip.scss b/src/components/Tooltip.scss index bb2b2f72e6..490e255780 100644 --- a/src/components/Tooltip.scss +++ b/src/components/Tooltip.scss @@ -2,6 +2,9 @@ // container in body where the actual tooltip is appended to .excalidraw-tooltip { + --ui-font: Assistant, system-ui, BlinkMacSystemFont, -apple-system, Segoe UI, + Roboto, Helvetica, Arial, sans-serif; + font-family: var(--ui-font); position: fixed; z-index: 1000; diff --git a/src/css/styles.scss b/src/css/styles.scss index 8dafbfbdfa..29e52011e7 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -354,6 +354,7 @@ border-radius: var(--space-factor); border: 1px solid var(--button-gray-2); font-size: 0.8rem; + font-family: inherit; outline: none; appearance: none; background-image: var(--dropdown-icon); @@ -413,6 +414,7 @@ bottom: 30px; transform: translateX(-50%); pointer-events: all; + font-family: inherit; &:hover { background-color: var(--button-hover-bg); From dae81c0a2cb8c21381830635d5bb992eb30b6f36 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 25 Apr 2023 17:57:53 +0530 Subject: [PATCH 24/29] fix: cleanup redrawTextBoundingBox (#6518) * chore: cleanup redrawTextBoundingBox * fix --- src/element/textElement.ts | 44 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/element/textElement.ts b/src/element/textElement.ts index f01ba3e1b9..339f68b967 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -83,35 +83,25 @@ export const redrawTextBoundingBox = ( boundTextUpdates.baseline = metrics.baseline; if (container) { - if (isArrowElement(container)) { - const centerX = textElement.x + textElement.width / 2; - const centerY = textElement.y + textElement.height / 2; - const diffWidth = metrics.width - textElement.width; - const diffHeight = metrics.height - textElement.height; - boundTextUpdates.x = centerY - (textElement.height + diffHeight) / 2; - boundTextUpdates.y = centerX - (textElement.width + diffWidth) / 2; - } else { - const containerDims = getContainerDims(container); - let maxContainerHeight = getMaxContainerHeight(container); + const containerDims = getContainerDims(container); + const maxContainerHeight = getMaxContainerHeight(container); - let nextHeight = containerDims.height; - if (metrics.height > maxContainerHeight) { - nextHeight = computeContainerDimensionForBoundText( - metrics.height, - container.type, - ); - mutateElement(container, { height: nextHeight }); - maxContainerHeight = getMaxContainerHeight(container); - updateOriginalContainerCache(container.id, nextHeight); - } - const updatedTextElement = { - ...textElement, - ...boundTextUpdates, - } as ExcalidrawTextElementWithContainer; - const { x, y } = computeBoundTextPosition(container, updatedTextElement); - boundTextUpdates.x = x; - boundTextUpdates.y = y; + let nextHeight = containerDims.height; + if (metrics.height > maxContainerHeight) { + nextHeight = computeContainerDimensionForBoundText( + metrics.height, + container.type, + ); + mutateElement(container, { height: nextHeight }); + updateOriginalContainerCache(container.id, nextHeight); } + const updatedTextElement = { + ...textElement, + ...boundTextUpdates, + } as ExcalidrawTextElementWithContainer; + const { x, y } = computeBoundTextPosition(container, updatedTextElement); + boundTextUpdates.x = x; + boundTextUpdates.y = y; } mutateElement(textElement, boundTextUpdates); From da8dd389a9dd7e6528fbd6bf85b12e8ec052f325 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 25 Apr 2023 18:06:23 +0530 Subject: [PATCH 25/29] fix: cleanup getMaxContainerHeight and getMaxContainerWidth (#6519) * fix: cleanup getMaxContainerHeight and getMaxContainerWidth * rename getMaxContainerWidth -> getBoundTextMaxMaxWidth and getMaxContainerHeight -> getBoundTextMaxHeight * add specs --- src/element/newElement.ts | 4 +- src/element/resizeElements.ts | 14 +++--- src/element/textElement.test.ts | 59 +++++++++++++++++++++----- src/element/textElement.ts | 41 +++++++++--------- src/element/textWysiwyg.tsx | 15 ++++--- src/renderer/renderElement.ts | 11 +++-- src/tests/linearElementEditor.test.tsx | 8 ++-- 7 files changed, 96 insertions(+), 56 deletions(-) diff --git a/src/element/newElement.ts b/src/element/newElement.ts index c1b0f17bfa..4922a5b4ee 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -33,7 +33,7 @@ import { measureText, normalizeText, wrapText, - getMaxContainerWidth, + getBoundTextMaxWidth, getDefaultLineHeight, } from "./textElement"; import { @@ -310,7 +310,7 @@ export const refreshTextDimensions = ( text = wrapText( text, getFontString(textElement), - getMaxContainerWidth(container), + getBoundTextMaxWidth(container), ); } const dimensions = getAdjustedDimensions(textElement, text); diff --git a/src/element/resizeElements.ts b/src/element/resizeElements.ts index 69b8afae74..67a6346be4 100644 --- a/src/element/resizeElements.ts +++ b/src/element/resizeElements.ts @@ -44,10 +44,10 @@ import { getBoundTextElementId, getContainerElement, handleBindTextResize, - getMaxContainerWidth, + getBoundTextMaxWidth, getApproxMinLineHeight, measureText, - getMaxContainerHeight, + getBoundTextMaxHeight, } from "./textElement"; export const normalizeAngle = (angle: number): number => { @@ -204,7 +204,7 @@ const measureFontSizeFromWidth = ( if (hasContainer) { const container = getContainerElement(element); if (container) { - width = getMaxContainerWidth(container); + width = getBoundTextMaxWidth(container); } } const nextFontSize = element.fontSize * (nextWidth / width); @@ -435,8 +435,8 @@ export const resizeSingleElement = ( const nextFont = measureFontSizeFromWidth( boundTextElement, - getMaxContainerWidth(updatedElement), - getMaxContainerHeight(updatedElement), + getBoundTextMaxWidth(updatedElement), + getBoundTextMaxHeight(updatedElement, boundTextElement), ); if (nextFont === null) { return; @@ -718,10 +718,10 @@ const resizeMultipleElements = ( const metrics = measureFontSizeFromWidth( boundTextElement ?? (element.orig as ExcalidrawTextElement), boundTextElement - ? getMaxContainerWidth(updatedElement) + ? getBoundTextMaxWidth(updatedElement) : updatedElement.width, boundTextElement - ? getMaxContainerHeight(updatedElement) + ? getBoundTextMaxHeight(updatedElement, boundTextElement) : updatedElement.height, ); diff --git a/src/element/textElement.test.ts b/src/element/textElement.test.ts index f83eafd1b4..b6221336d1 100644 --- a/src/element/textElement.test.ts +++ b/src/element/textElement.test.ts @@ -3,15 +3,15 @@ import { API } from "../tests/helpers/api"; import { computeContainerDimensionForBoundText, getContainerCoords, - getMaxContainerWidth, - getMaxContainerHeight, + getBoundTextMaxWidth, + getBoundTextMaxHeight, wrapText, detectLineHeight, getLineHeightInPx, getDefaultLineHeight, parseTokens, } from "./textElement"; -import { FontString } from "./types"; +import { ExcalidrawTextElementWithContainer, FontString } from "./types"; describe("Test wrapText", () => { const font = "20px Cascadia, width: Segoe UI Emoji" as FontString; @@ -311,7 +311,7 @@ describe("Test measureText", () => { }); }); - describe("Test getMaxContainerWidth", () => { + describe("Test getBoundTextMaxWidth", () => { const params = { width: 178, height: 194, @@ -319,39 +319,76 @@ describe("Test measureText", () => { it("should return max width when container is rectangle", () => { const container = API.createElement({ type: "rectangle", ...params }); - expect(getMaxContainerWidth(container)).toBe(168); + expect(getBoundTextMaxWidth(container)).toBe(168); }); it("should return max width when container is ellipse", () => { const container = API.createElement({ type: "ellipse", ...params }); - expect(getMaxContainerWidth(container)).toBe(116); + expect(getBoundTextMaxWidth(container)).toBe(116); }); it("should return max width when container is diamond", () => { const container = API.createElement({ type: "diamond", ...params }); - expect(getMaxContainerWidth(container)).toBe(79); + expect(getBoundTextMaxWidth(container)).toBe(79); }); }); - describe("Test getMaxContainerHeight", () => { + describe("Test getBoundTextMaxHeight", () => { const params = { width: 178, height: 194, + id: '"container-id', }; + const boundTextElement = API.createElement({ + type: "text", + id: "text-id", + x: 560.51171875, + y: 202.033203125, + width: 154, + height: 175, + fontSize: 20, + fontFamily: 1, + text: "Excalidraw is a\nvirtual \nopensource \nwhiteboard for \nsketching \nhand-drawn like\ndiagrams", + textAlign: "center", + verticalAlign: "middle", + containerId: params.id, + }) as ExcalidrawTextElementWithContainer; + it("should return max height when container is rectangle", () => { const container = API.createElement({ type: "rectangle", ...params }); - expect(getMaxContainerHeight(container)).toBe(184); + expect(getBoundTextMaxHeight(container, boundTextElement)).toBe(184); }); it("should return max height when container is ellipse", () => { const container = API.createElement({ type: "ellipse", ...params }); - expect(getMaxContainerHeight(container)).toBe(127); + expect(getBoundTextMaxHeight(container, boundTextElement)).toBe(127); }); it("should return max height when container is diamond", () => { const container = API.createElement({ type: "diamond", ...params }); - expect(getMaxContainerHeight(container)).toBe(87); + expect(getBoundTextMaxHeight(container, boundTextElement)).toBe(87); + }); + + it("should return max height when container is arrow", () => { + const container = API.createElement({ + type: "arrow", + ...params, + }); + expect(getBoundTextMaxHeight(container, boundTextElement)).toBe(194); + }); + + it("should return max height when container is arrow and height is less than threshold", () => { + const container = API.createElement({ + type: "arrow", + ...params, + height: 70, + boundElements: [{ type: "text", id: "text-id" }], + }); + + expect(getBoundTextMaxHeight(container, boundTextElement)).toBe( + boundTextElement.height, + ); }); }); }); diff --git a/src/element/textElement.ts b/src/element/textElement.ts index 339f68b967..a6d0c3271c 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -65,7 +65,7 @@ export const redrawTextBoundingBox = ( boundTextUpdates.text = textElement.text; if (container) { - maxWidth = getMaxContainerWidth(container); + maxWidth = getBoundTextMaxWidth(container); boundTextUpdates.text = wrapText( textElement.originalText, getFontString(textElement), @@ -84,7 +84,10 @@ export const redrawTextBoundingBox = ( if (container) { const containerDims = getContainerDims(container); - const maxContainerHeight = getMaxContainerHeight(container); + const maxContainerHeight = getBoundTextMaxHeight( + container, + textElement as ExcalidrawTextElementWithContainer, + ); let nextHeight = containerDims.height; if (metrics.height > maxContainerHeight) { @@ -173,8 +176,11 @@ export const handleBindTextResize = ( let nextHeight = textElement.height; let nextWidth = textElement.width; const containerDims = getContainerDims(container); - const maxWidth = getMaxContainerWidth(container); - const maxHeight = getMaxContainerHeight(container); + const maxWidth = getBoundTextMaxWidth(container); + const maxHeight = getBoundTextMaxHeight( + container, + textElement as ExcalidrawTextElementWithContainer, + ); let containerHeight = containerDims.height; let nextBaseLine = textElement.baseline; if (transformHandleType !== "n" && transformHandleType !== "s") { @@ -246,8 +252,8 @@ export const computeBoundTextPosition = ( ); } const containerCoords = getContainerCoords(container); - const maxContainerHeight = getMaxContainerHeight(container); - const maxContainerWidth = getMaxContainerWidth(container); + const maxContainerHeight = getBoundTextMaxHeight(container, boundTextElement); + const maxContainerWidth = getBoundTextMaxWidth(container); let x; let y; @@ -880,18 +886,10 @@ export const computeContainerDimensionForBoundText = ( return dimension + padding; }; -export const getMaxContainerWidth = (container: ExcalidrawElement) => { +export const getBoundTextMaxWidth = (container: ExcalidrawElement) => { const width = getContainerDims(container).width; if (isArrowElement(container)) { - const containerWidth = width - BOUND_TEXT_PADDING * 8 * 2; - if (containerWidth <= 0) { - const boundText = getBoundTextElement(container); - if (boundText) { - return boundText.width; - } - return BOUND_TEXT_PADDING * 8 * 2; - } - return containerWidth; + return width - BOUND_TEXT_PADDING * 8 * 2; } if (container.type === "ellipse") { @@ -908,16 +906,15 @@ export const getMaxContainerWidth = (container: ExcalidrawElement) => { return width - BOUND_TEXT_PADDING * 2; }; -export const getMaxContainerHeight = (container: ExcalidrawElement) => { +export const getBoundTextMaxHeight = ( + container: ExcalidrawElement, + boundTextElement: ExcalidrawTextElementWithContainer, +) => { const height = getContainerDims(container).height; if (isArrowElement(container)) { const containerHeight = height - BOUND_TEXT_PADDING * 8 * 2; if (containerHeight <= 0) { - const boundText = getBoundTextElement(container); - if (boundText) { - return boundText.height; - } - return BOUND_TEXT_PADDING * 8 * 2; + return boundTextElement.height; } return height; } diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index ef4f7c926e..63bc9e4a44 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -32,8 +32,8 @@ import { normalizeText, redrawTextBoundingBox, wrapText, - getMaxContainerHeight, - getMaxContainerWidth, + getBoundTextMaxHeight, + getBoundTextMaxWidth, computeContainerDimensionForBoundText, detectLineHeight, } from "./textElement"; @@ -205,8 +205,11 @@ export const textWysiwyg = ({ } } - maxWidth = getMaxContainerWidth(container); - maxHeight = getMaxContainerHeight(container); + maxWidth = getBoundTextMaxWidth(container); + maxHeight = getBoundTextMaxHeight( + container, + updatedTextElement as ExcalidrawTextElementWithContainer, + ); // autogrow container height if text exceeds if (!isArrowElement(container) && textElementHeight > maxHeight) { @@ -377,7 +380,7 @@ export const textWysiwyg = ({ const wrappedText = wrapText( `${editable.value}${data}`, font, - getMaxContainerWidth(container), + getBoundTextMaxWidth(container), ); const width = getTextWidth(wrappedText, font); editable.style.width = `${width}px`; @@ -394,7 +397,7 @@ export const textWysiwyg = ({ const wrappedText = wrapText( normalizeText(editable.value), font, - getMaxContainerWidth(container!), + getBoundTextMaxWidth(container!), ); const { width, height } = measureText( wrappedText, diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index f85c83a6b7..77ea14587d 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -44,8 +44,8 @@ import { getContainerCoords, getContainerElement, getLineHeightInPx, - getMaxContainerHeight, - getMaxContainerWidth, + getBoundTextMaxHeight, + getBoundTextMaxWidth, } from "../element/textElement"; import { LinearElementEditor } from "../element/linearElementEditor"; @@ -868,14 +868,17 @@ const drawElementFromCanvas = ( "true" && hasBoundTextElement(element) ) { + const textElement = getBoundTextElement( + element, + ) as ExcalidrawTextElementWithContainer; const coords = getContainerCoords(element); context.strokeStyle = "#c92a2a"; context.lineWidth = 3; context.strokeRect( (coords.x + renderConfig.scrollX) * window.devicePixelRatio, (coords.y + renderConfig.scrollY) * window.devicePixelRatio, - getMaxContainerWidth(element) * window.devicePixelRatio, - getMaxContainerHeight(element) * window.devicePixelRatio, + getBoundTextMaxWidth(element) * window.devicePixelRatio, + getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio, ); } } diff --git a/src/tests/linearElementEditor.test.tsx b/src/tests/linearElementEditor.test.tsx index 15fd105ec4..c71283a4c0 100644 --- a/src/tests/linearElementEditor.test.tsx +++ b/src/tests/linearElementEditor.test.tsx @@ -20,7 +20,7 @@ import { resize, rotate } from "./utils"; import { getBoundTextElementPosition, wrapText, - getMaxContainerWidth, + getBoundTextMaxWidth, } from "../element/textElement"; import * as textElementUtils from "../element/textElement"; import { ROUNDNESS, VERTICAL_ALIGN } from "../constants"; @@ -729,7 +729,7 @@ describe("Test Linear Elements", () => { type: "text", x: 0, y: 0, - text: wrapText(text, font, getMaxContainerWidth(container)), + text: wrapText(text, font, getBoundTextMaxWidth(container)), containerId: container.id, width: 30, height: 20, @@ -1149,7 +1149,7 @@ describe("Test Linear Elements", () => { expect(rect.x).toBe(400); expect(rect.y).toBe(0); expect( - wrapText(textElement.originalText, font, getMaxContainerWidth(arrow)), + wrapText(textElement.originalText, font, getBoundTextMaxWidth(arrow)), ).toMatchInlineSnapshot(` "Online whiteboard collaboration made easy" @@ -1172,7 +1172,7 @@ describe("Test Linear Elements", () => { false, ); expect( - wrapText(textElement.originalText, font, getMaxContainerWidth(arrow)), + wrapText(textElement.originalText, font, getBoundTextMaxWidth(arrow)), ).toMatchInlineSnapshot(` "Online whiteboard collaboration made From 45a57d70de75fbb47f7c3c45cae675341c9bcab5 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Wed, 26 Apr 2023 21:35:06 +0530 Subject: [PATCH 26/29] fix: don't refresh dimensions for text containers on font load (#6523) --- src/data/blob.ts | 2 +- src/excalidraw-app/data/index.ts | 2 +- src/scene/Fonts.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/blob.ts b/src/data/blob.ts index 4565b5cb58..c0aa66ee73 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -155,7 +155,7 @@ export const loadSceneOrLibraryFromBlob = async ( }, localAppState, localElements, - { repairBindings: true, refreshDimensions: true }, + { repairBindings: true, refreshDimensions: false }, ), }; } else if (isValidLibrary(data)) { diff --git a/src/excalidraw-app/data/index.ts b/src/excalidraw-app/data/index.ts index 7f13bc6153..2e50abf1f9 100644 --- a/src/excalidraw-app/data/index.ts +++ b/src/excalidraw-app/data/index.ts @@ -263,7 +263,7 @@ export const loadScene = async ( await importFromBackend(id, privateKey), localDataState?.appState, localDataState?.elements, - { repairBindings: true, refreshDimensions: true }, + { repairBindings: true, refreshDimensions: false }, ); } else { data = restore(localDataState || null, null, null, { diff --git a/src/scene/Fonts.ts b/src/scene/Fonts.ts index cc206c776b..e245eb16e2 100644 --- a/src/scene/Fonts.ts +++ b/src/scene/Fonts.ts @@ -1,5 +1,6 @@ import { isTextElement, refreshTextDimensions } from "../element"; import { newElementWith } from "../element/mutateElement"; +import { isBoundToContainer } from "../element/typeChecks"; import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types"; import { invalidateShapeForElement } from "../renderer/renderElement"; import { getFontString } from "../utils"; @@ -52,7 +53,7 @@ export class Fonts { let didUpdate = false; this.scene.mapElements((element) => { - if (isTextElement(element)) { + if (isTextElement(element) && !isBoundToContainer(element)) { invalidateShapeForElement(element); didUpdate = true; return newElementWith(element, { From 6b0218b01251dfd2143cd974279b70e10a2180a6 Mon Sep 17 00:00:00 2001 From: Milos Vetesnik Date: Thu, 27 Apr 2023 19:11:42 +0200 Subject: [PATCH 27/29] feat: testing simple analytics and fathom analytics for better privacy of the users (#6529) Co-authored-by: dwelle --- public/index.html | 45 ++++++++++++++++++++------------------------- src/analytics.ts | 17 ++++++++++++++--- src/global.d.ts | 4 ++-- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/public/index.html b/public/index.html index a8633fc4df..f65e481f33 100644 --- a/public/index.html +++ b/public/index.html @@ -150,6 +150,14 @@ <% if (process.env.REACT_APP_DISABLE_TRACKING !== 'true') { %> + + + + <% if (process.env.REACT_APP_GOOGLE_ANALYTICS_ID) { %> - <% } %> - - <% } %> @@ -244,5 +227,17 @@

Excalidraw

+ + + diff --git a/src/analytics.ts b/src/analytics.ts index 1e9a429b62..e952bc6807 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -20,9 +20,20 @@ export const trackEvent = ( }); } - // MATOMO event tracking _paq must be same as the one in index.html - if (window._paq) { - window._paq.push(["trackEvent", category, action, label, value]); + if (window.sa_event) { + window.sa_event(action, { + category, + label, + value, + }); + } + + if (window.fathom) { + window.fathom.trackEvent(action, { + category, + label, + value, + }); } } catch (error) { console.error("error during analytics", error); diff --git a/src/global.d.ts b/src/global.d.ts index 73c8fc8139..3a666e11ae 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -18,8 +18,8 @@ interface Window { EXCALIDRAW_EXPORT_SOURCE: string; EXCALIDRAW_THROTTLE_RENDER: boolean | undefined; gtag: Function; - _paq: any[]; - _mtm: any[]; + sa_event: Function; + fathom: { trackEvent: Function }; } interface CanvasRenderingContext2D { From 2a39d0b9a72b65679d213683cf4786d847a29dc6 Mon Sep 17 00:00:00 2001 From: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:27:36 +0200 Subject: [PATCH 28/29] chore: Update translations from Crowdin (#6471) --- src/locales/ar-SA.json | 1 + src/locales/bg-BG.json | 1 + src/locales/bn-BD.json | 1 + src/locales/ca-ES.json | 1 + src/locales/cs-CZ.json | 1 + src/locales/da-DK.json | 1 + src/locales/de-DE.json | 1 + src/locales/el-GR.json | 1 + src/locales/es-ES.json | 9 ++--- src/locales/eu-ES.json | 1 + src/locales/fa-IR.json | 1 + src/locales/fi-FI.json | 1 + src/locales/fr-FR.json | 5 +-- src/locales/gl-ES.json | 1 + src/locales/he-IL.json | 1 + src/locales/hi-IN.json | 1 + src/locales/hu-HU.json | 1 + src/locales/id-ID.json | 1 + src/locales/it-IT.json | 3 +- src/locales/ja-JP.json | 1 + src/locales/kab-KAB.json | 1 + src/locales/kk-KZ.json | 1 + src/locales/ko-KR.json | 11 +++--- src/locales/ku-TR.json | 69 ++++++++++++++++++------------------ src/locales/lt-LT.json | 1 + src/locales/lv-LV.json | 55 ++++++++++++++-------------- src/locales/mr-IN.json | 1 + src/locales/my-MM.json | 1 + src/locales/nb-NO.json | 1 + src/locales/nl-NL.json | 1 + src/locales/nn-NO.json | 1 + src/locales/oc-FR.json | 1 + src/locales/pa-IN.json | 1 + src/locales/percentages.json | 36 +++++++++---------- src/locales/pl-PL.json | 1 + src/locales/pt-BR.json | 1 + src/locales/pt-PT.json | 5 +-- src/locales/ro-RO.json | 1 + src/locales/ru-RU.json | 33 ++++++++--------- src/locales/si-LK.json | 1 + src/locales/sk-SK.json | 5 +-- src/locales/sl-SI.json | 1 + src/locales/sv-SE.json | 5 +-- src/locales/ta-IN.json | 1 + src/locales/th-TH.json | 1 + src/locales/tr-TR.json | 1 + src/locales/uk-UA.json | 1 + src/locales/vi-VN.json | 1 + src/locales/zh-CN.json | 5 +-- src/locales/zh-HK.json | 1 + src/locales/zh-TW.json | 1 + 51 files changed, 165 insertions(+), 115 deletions(-) diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json index 25a32f2228..77387b11d6 100644 --- a/src/locales/ar-SA.json +++ b/src/locales/ar-SA.json @@ -54,6 +54,7 @@ "veryLarge": "كبير جدا", "solid": "كامل", "hachure": "خطوط", + "zigzag": "", "crossHatch": "خطوط متقطعة", "thin": "نحيف", "bold": "داكن", diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json index 501ce7399d..d5421ccef9 100644 --- a/src/locales/bg-BG.json +++ b/src/locales/bg-BG.json @@ -54,6 +54,7 @@ "veryLarge": "Много голям", "solid": "Солиден", "hachure": "Хералдика", + "zigzag": "", "crossHatch": "Двойно-пресечено", "thin": "Тънък", "bold": "Ясно очертан", diff --git a/src/locales/bn-BD.json b/src/locales/bn-BD.json index a5d9dec0f9..ce17d6670f 100644 --- a/src/locales/bn-BD.json +++ b/src/locales/bn-BD.json @@ -54,6 +54,7 @@ "veryLarge": "অনেক বড়", "solid": "দৃঢ়", "hachure": "ভ্রুলেখা", + "zigzag": "", "crossHatch": "ক্রস হ্যাচ", "thin": "পাতলা", "bold": "পুরু", diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json index ae45e764de..425070e49e 100644 --- a/src/locales/ca-ES.json +++ b/src/locales/ca-ES.json @@ -54,6 +54,7 @@ "veryLarge": "Molt gran", "solid": "Sòlid", "hachure": "Ratlletes", + "zigzag": "", "crossHatch": "Ratlletes creuades", "thin": "Fi", "bold": "Negreta", diff --git a/src/locales/cs-CZ.json b/src/locales/cs-CZ.json index d57a8837d7..d039a78a23 100644 --- a/src/locales/cs-CZ.json +++ b/src/locales/cs-CZ.json @@ -54,6 +54,7 @@ "veryLarge": "Velmi velké", "solid": "Plný", "hachure": "", + "zigzag": "", "crossHatch": "", "thin": "Tenký", "bold": "Tlustý", diff --git a/src/locales/da-DK.json b/src/locales/da-DK.json index c8b5ad6e32..4d74ab80f7 100644 --- a/src/locales/da-DK.json +++ b/src/locales/da-DK.json @@ -54,6 +54,7 @@ "veryLarge": "Meget stor", "solid": "Solid", "hachure": "Skravering", + "zigzag": "", "crossHatch": "Krydsskravering", "thin": "Tynd", "bold": "Fed", diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index bdf30a371d..86b168ae51 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -54,6 +54,7 @@ "veryLarge": "Sehr groß", "solid": "Deckend", "hachure": "Schraffiert", + "zigzag": "Zickzack", "crossHatch": "Kreuzschraffiert", "thin": "Dünn", "bold": "Fett", diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json index 888c395683..f4e0cfcaa2 100644 --- a/src/locales/el-GR.json +++ b/src/locales/el-GR.json @@ -54,6 +54,7 @@ "veryLarge": "Πολύ μεγάλο", "solid": "Συμπαγής", "hachure": "Εκκόλαψη", + "zigzag": "", "crossHatch": "Διασταυρούμενη εκκόλαψη", "thin": "Λεπτή", "bold": "Έντονη", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index 67a1102935..c345c5f831 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -54,6 +54,7 @@ "veryLarge": "Muy grande", "solid": "Sólido", "hachure": "Folleto", + "zigzag": "Zigzag", "crossHatch": "Rayado transversal", "thin": "Fino", "bold": "Grueso", @@ -207,8 +208,8 @@ "collabSaveFailed": "No se pudo guardar en la base de datos del backend. Si los problemas persisten, debería guardar su archivo localmente para asegurarse de que no pierde su trabajo.", "collabSaveFailed_sizeExceeded": "No se pudo guardar en la base de datos del backend, el lienzo parece ser demasiado grande. Debería guardar el archivo localmente para asegurarse de que no pierde su trabajo.", "brave_measure_text_error": { - "start": "", - "aggressive_block_fingerprint": "", + "start": "Parece que estás usando el navegador Brave", + "aggressive_block_fingerprint": "Bloquear huellas dactilares agresivamente", "setting_enabled": "ajuste activado", "break": "Esto podría resultar en romper los", "text_elements": "Elementos de texto", @@ -319,8 +320,8 @@ "doubleClick": "doble clic", "drag": "arrastrar", "editor": "Editor", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Editar puntos de línea/flecha", + "editText": "Editar texto / añadir etiqueta", "github": "¿Ha encontrado un problema? Envíelo", "howto": "Siga nuestras guías", "or": "o", diff --git a/src/locales/eu-ES.json b/src/locales/eu-ES.json index 1aec330cb3..9c5f14facf 100644 --- a/src/locales/eu-ES.json +++ b/src/locales/eu-ES.json @@ -54,6 +54,7 @@ "veryLarge": "Oso handia", "solid": "Solidoa", "hachure": "Itzalduna", + "zigzag": "", "crossHatch": "Marraduna", "thin": "Mehea", "bold": "Lodia", diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json index 44cf7ae00a..a22ad86a95 100644 --- a/src/locales/fa-IR.json +++ b/src/locales/fa-IR.json @@ -54,6 +54,7 @@ "veryLarge": "بسیار بزرگ", "solid": "توپر", "hachure": "هاشور", + "zigzag": "", "crossHatch": "هاشور متقاطع", "thin": "نازک", "bold": "ضخیم", diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json index e0701f2d23..403bf00733 100644 --- a/src/locales/fi-FI.json +++ b/src/locales/fi-FI.json @@ -54,6 +54,7 @@ "veryLarge": "Erittäin suuri", "solid": "Yhtenäinen", "hachure": "Vinoviivoitus", + "zigzag": "", "crossHatch": "Ristiviivoitus", "thin": "Ohut", "bold": "Lihavoitu", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 49135c3b62..406a11a166 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -54,6 +54,7 @@ "veryLarge": "Très grande", "solid": "Solide", "hachure": "Hachures", + "zigzag": "", "crossHatch": "Hachures croisées", "thin": "Fine", "bold": "Épaisse", @@ -319,8 +320,8 @@ "doubleClick": "double-clic", "drag": "glisser", "editor": "Éditeur", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Modifier les points de ligne/flèche", + "editText": "Modifier le texte / ajouter un libellé", "github": "Problème trouvé ? Soumettre", "howto": "Suivez nos guides", "or": "ou", diff --git a/src/locales/gl-ES.json b/src/locales/gl-ES.json index 5571f3f15a..53ae05d6b7 100644 --- a/src/locales/gl-ES.json +++ b/src/locales/gl-ES.json @@ -54,6 +54,7 @@ "veryLarge": "Moi grande", "solid": "Sólido", "hachure": "Folleto", + "zigzag": "", "crossHatch": "Raiado transversal", "thin": "Estreito", "bold": "Groso", diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json index 810fc1776c..4cd8c11404 100644 --- a/src/locales/he-IL.json +++ b/src/locales/he-IL.json @@ -54,6 +54,7 @@ "veryLarge": "גדול מאוד", "solid": "מוצק", "hachure": "קווים מקבילים קצרים להצגת כיוון וחדות שיפוע במפה", + "zigzag": "", "crossHatch": "קווים מוצלבים שתי וערב", "thin": "דק", "bold": "מודגש", diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json index 77d6dae2dc..d9462e78b4 100644 --- a/src/locales/hi-IN.json +++ b/src/locales/hi-IN.json @@ -54,6 +54,7 @@ "veryLarge": "बहुत बड़ा", "solid": "दृढ़", "hachure": "हैशूर", + "zigzag": "तेढ़ी मेढ़ी", "crossHatch": "क्रॉस हैच", "thin": "पतला", "bold": "मोटा", diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json index d514520ed1..5dc19945b4 100644 --- a/src/locales/hu-HU.json +++ b/src/locales/hu-HU.json @@ -54,6 +54,7 @@ "veryLarge": "Nagyon nagy", "solid": "Kitöltött", "hachure": "Vonalkázott", + "zigzag": "", "crossHatch": "Keresztcsíkozott", "thin": "Vékony", "bold": "Félkövér", diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json index 01b510fcd6..eb5d8df71e 100644 --- a/src/locales/id-ID.json +++ b/src/locales/id-ID.json @@ -54,6 +54,7 @@ "veryLarge": "Sangat besar", "solid": "Padat", "hachure": "Garis-garis", + "zigzag": "", "crossHatch": "Asiran silang", "thin": "Lembut", "bold": "Tebal", diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json index 8380fd8e5d..c31462ce2a 100644 --- a/src/locales/it-IT.json +++ b/src/locales/it-IT.json @@ -54,6 +54,7 @@ "veryLarge": "Molto grande", "solid": "Pieno", "hachure": "Tratteggio obliquo", + "zigzag": "Zig zag", "crossHatch": "Tratteggio incrociato", "thin": "Sottile", "bold": "Grassetto", @@ -319,7 +320,7 @@ "doubleClick": "doppio-click", "drag": "trascina", "editor": "Editor", - "editLineArrowPoints": "", + "editLineArrowPoints": "Modifica punti linea/freccia", "editText": "Modifica testo / aggiungi etichetta", "github": "Trovato un problema? Segnalalo", "howto": "Segui le nostre guide", diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json index 53333aea30..a457b1dfec 100644 --- a/src/locales/ja-JP.json +++ b/src/locales/ja-JP.json @@ -54,6 +54,7 @@ "veryLarge": "特大", "solid": "ベタ塗り", "hachure": "斜線", + "zigzag": "", "crossHatch": "網掛け", "thin": "細", "bold": "太字", diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json index ba6a3de7e9..62c6071c45 100644 --- a/src/locales/kab-KAB.json +++ b/src/locales/kab-KAB.json @@ -54,6 +54,7 @@ "veryLarge": "Meqqer aṭas", "solid": "Aččuran", "hachure": "Azerreg", + "zigzag": "", "crossHatch": "Azerreg anmidag", "thin": "Arqaq", "bold": "Azuran", diff --git a/src/locales/kk-KZ.json b/src/locales/kk-KZ.json index 97a9063fa4..38acace0e2 100644 --- a/src/locales/kk-KZ.json +++ b/src/locales/kk-KZ.json @@ -54,6 +54,7 @@ "veryLarge": "Өте үлкен", "solid": "", "hachure": "", + "zigzag": "", "crossHatch": "", "thin": "", "bold": "", diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json index f170e4cbf8..aa4647f287 100644 --- a/src/locales/ko-KR.json +++ b/src/locales/ko-KR.json @@ -54,6 +54,7 @@ "veryLarge": "매우 크게", "solid": "단색", "hachure": "평행선", + "zigzag": "지그재그", "crossHatch": "교차선", "thin": "얇게", "bold": "굵게", @@ -256,7 +257,7 @@ "resize": "SHIFT 키를 누르면서 조정하면 크기의 비율이 제한됩니다.\nALT를 누르면서 조정하면 중앙을 기준으로 크기를 조정합니다.", "resizeImage": "SHIFT를 눌러서 자유롭게 크기를 변경하거나,\nALT를 눌러서 중앙을 고정하고 크기를 변경하기", "rotate": "SHIFT 키를 누르면서 회전하면 각도를 제한할 수 있습니다.", - "lineEditor_info": "포인트를 편집하려면 Ctrl/Cmd을 누르고 더블 클릭을 하거나 Ctrl/Cmd + Enter를 누르세요", + "lineEditor_info": "꼭짓점을 수정하려면 CtrlOrCmd 키를 누르고 더블 클릭을 하거나 CtrlOrCmd + Enter를 누르세요.", "lineEditor_pointSelected": "Delete 키로 꼭짓점을 제거하거나,\nCtrlOrCmd+D 로 복제하거나, 드래그 해서 이동시키기", "lineEditor_nothingSelected": "꼭짓점을 선택해서 수정하거나 (SHIFT를 눌러서 여러개 선택),\nAlt를 누르고 클릭해서 새로운 꼭짓점 추가하기", "placeImage": "클릭해서 이미지를 배치하거나, 클릭하고 드래그해서 사이즈를 조정하기", @@ -319,8 +320,8 @@ "doubleClick": "더블 클릭", "drag": "드래그", "editor": "에디터", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "직선 / 화살표 꼭짓점 수정", + "editText": "텍스트 수정 / 라벨 추가", "github": "문제 제보하기", "howto": "가이드 참고하기", "or": "또는", @@ -382,8 +383,8 @@ }, "publishSuccessDialog": { "title": "라이브러리 제출됨", - "content": "{{authorName}}님 감사합니다. 당신의 라이브러리가 심사를 위해 제출되었습니다. 진행 상황을 다음의 링크에서 확인할 수 있습니다.", - "link": "여기" + "content": "{{authorName}}님 감사합니다. 당신의 라이브러리가 심사를 위해 제출되었습니다. 진행 상황을", + "link": "여기에서 확인하실 수 있습니다." }, "confirmDialog": { "resetLibrary": "라이브러리 리셋", diff --git a/src/locales/ku-TR.json b/src/locales/ku-TR.json index 76b5086e8f..4fbf60492f 100644 --- a/src/locales/ku-TR.json +++ b/src/locales/ku-TR.json @@ -1,7 +1,7 @@ { "labels": { "paste": "دانانەوە", - "pasteAsPlaintext": "", + "pasteAsPlaintext": "دایبنێ وەک دەقی سادە", "pasteCharts": "دانانەوەی خشتەکان", "selectAll": "دیاریکردنی هەموو", "multiSelect": "زیادکردنی بۆ دیاریکراوەکان", @@ -54,6 +54,7 @@ "veryLarge": "زۆر گه‌وره‌", "solid": "سادە", "hachure": "هاچور", + "zigzag": "زیگزاگ", "crossHatch": "کرۆس هاتچ", "thin": "تەنک", "bold": "تۆخ", @@ -110,7 +111,7 @@ "increaseFontSize": "زایدکردنی قەبارەی فۆنت", "unbindText": "دەقەکە جیابکەرەوە", "bindText": "دەقەکە ببەستەوە بە کۆنتەینەرەکەوە", - "createContainerFromText": "", + "createContainerFromText": "دەق لە چوارچێوەیەکدا بپێچە", "link": { "edit": "دەستکاریکردنی بەستەر", "create": "دروستکردنی بەستەر", @@ -194,7 +195,7 @@ "resetLibrary": "ئەمە کتێبخانەکەت خاوێن دەکاتەوە. ئایا دڵنیایت?", "removeItemsFromsLibrary": "سڕینەوەی {{count}} ئایتم(ەکان) لە کتێبخانە؟", "invalidEncryptionKey": "کلیلی رەمزاندن دەبێت لە 22 پیت بێت. هاوکاری ڕاستەوخۆ لە کارخراوە.", - "collabOfflineWarning": "" + "collabOfflineWarning": "هێڵی ئینتەرنێت بەردەست نییە.\n گۆڕانکارییەکانت سەیڤ ناکرێن!" }, "errors": { "unsupportedFileType": "جۆری فایلی پشتگیری نەکراو.", @@ -204,22 +205,22 @@ "invalidSVGString": "ئێس ڤی جی نادروستە.", "cannotResolveCollabServer": "ناتوانێت پەیوەندی بکات بە سێرڤەری کۆلاب. تکایە لاپەڕەکە دووبارە باربکەوە و دووبارە هەوڵ بدەوە.", "importLibraryError": "نەیتوانی کتێبخانە بار بکات", - "collabSaveFailed": "", - "collabSaveFailed_sizeExceeded": "", + "collabSaveFailed": "نەتوانرا لە بنکەدراوەی ڕاژەدا پاشەکەوت بکرێت. ئەگەر کێشەکان بەردەوام بوون، پێویستە فایلەکەت لە ناوخۆدا هەڵبگریت بۆ ئەوەی دڵنیا بیت کە کارەکانت لەدەست نادەیت.", + "collabSaveFailed_sizeExceeded": "نەتوانرا لە بنکەدراوەی ڕاژەدا پاشەکەوت بکرێت، پێدەچێت تابلۆکە زۆر گەورە بێت. پێویستە فایلەکە لە ناوخۆدا هەڵبگریت بۆ ئەوەی دڵنیا بیت کە کارەکانت لەدەست نادەیت.", "brave_measure_text_error": { - "start": "", - "aggressive_block_fingerprint": "", - "setting_enabled": "", - "break": "", - "text_elements": "", - "in_your_drawings": "", - "strongly_recommend": "", - "steps": "", - "how": "", - "disable_setting": "", - "issue": "", - "write": "", - "discord": "" + "start": "پێدەچێت وێبگەڕی Brave بەکاربهێنیت لەگەڵ", + "aggressive_block_fingerprint": "بلۆککردنی Fingerprinting بەشێوەیەکی توندوتیژانە", + "setting_enabled": "ڕێکخستن چالاک کراوە", + "break": "ئەمە دەکرێت ببێتە هۆی تێکدانی", + "text_elements": "دانە دەقییەکان", + "in_your_drawings": "لە وێنەکێشانەکانتدا", + "strongly_recommend": "بە توندی پێشنیار دەکەین ئەم ڕێکخستنە لەکاربخەیت. دەتوانیت بڕۆیت بە دوای", + "steps": "ئەم هەنگاوانەدا", + "how": "بۆ ئەوەی ئەنجامی بدەیت", + "disable_setting": " ئەگەر لەکارخستنی ئەم ڕێکخستنە پیشاندانی توخمەکانی دەق چاک نەکاتەوە، تکایە هەڵبستە بە کردنەوەی", + "issue": "کێشەیەک", + "write": "لەسەر گیتهەبەکەمان، یان بۆمان بنوسە لە", + "discord": "دیسکۆرد" } }, "toolBar": { @@ -237,7 +238,7 @@ "penMode": "شێوازی قەڵەم - دەست لێدان ڕابگرە", "link": "زیادکردن/ نوێکردنەوەی لینک بۆ شێوەی دیاریکراو", "eraser": "سڕەر", - "hand": "" + "hand": "دەست (ئامرازی پانکردن)" }, "headings": { "canvasActions": "کردارەکانی تابلۆ", @@ -245,7 +246,7 @@ "shapes": "شێوەکان" }, "hints": { - "canvasPanning": "", + "canvasPanning": "بۆ جوڵاندنی تابلۆ، ویلی ماوسەکەت یان دوگمەی سپەیس بگرە لەکاتی ڕاکێشاندە، یانیش ئامرازی دەستەکە بەکاربهێنە", "linearElement": "کرتە بکە بۆ دەستپێکردنی چەند خاڵێک، ڕایبکێشە بۆ یەک هێڵ", "freeDraw": "کرتە بکە و ڕایبکێشە، کاتێک تەواو بوویت دەست هەڵگرە", "text": "زانیاری: هەروەها دەتوانیت دەق زیادبکەیت بە دوو کرتەکردن لە هەر شوێنێک لەگەڵ ئامڕازی دەستنیشانکردن", @@ -256,7 +257,7 @@ "resize": "دەتوانیت ڕێژەکان سنووردار بکەیت بە ڕاگرتنی SHIFT لەکاتی گۆڕینی قەبارەدا،\nALT ڕابگرە بۆ گۆڕینی قەبارە لە ناوەندەوە", "resizeImage": "دەتوانیت بە ئازادی قەبارە بگۆڕیت بە ڕاگرتنی SHIFT،\nALT ڕابگرە بۆ گۆڕینی قەبارە لە ناوەندەوە", "rotate": "دەتوانیت گۆشەکان سنووردار بکەیت بە ڕاگرتنی SHIFT لەکاتی سوڕانەوەدا", - "lineEditor_info": "", + "lineEditor_info": "یان Ctrl یان Cmd بگرە و دوانە کلیک بکە یانیش پەنجە بنێ بە Ctrl یان Cmd + ئینتەر بۆ دەستکاریکردنی خاڵەکان", "lineEditor_pointSelected": "بۆ لابردنی خاڵەکان Delete دابگرە، Ctrl Cmd+D بکە بۆ لەبەرگرتنەوە، یان بۆ جووڵە ڕاکێشان بکە", "lineEditor_nothingSelected": "خاڵێک هەڵبژێرە بۆ دەستکاریکردن (SHIFT ڕابگرە بۆ هەڵبژاردنی چەندین)،\nیان Alt ڕابگرە و کلیک بکە بۆ زیادکردنی خاڵە نوێیەکان", "placeImage": "کلیک بکە بۆ دانانی وێنەکە، یان کلیک بکە و ڕایبکێشە بۆ ئەوەی قەبارەکەی بە دەستی دابنێیت", @@ -264,7 +265,7 @@ "bindTextToElement": "بۆ زیادکردنی دەق enter بکە", "deepBoxSelect": "CtrlOrCmd ڕابگرە بۆ هەڵبژاردنی قووڵ، و بۆ ڕێگریکردن لە ڕاکێشان", "eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "ئەم تایبەتمەندییە بە ئەگەرێکی زۆرەوە دەتوانرێت چالاک بکرێت بە ڕێکخستنی ئاڵای \"dom.events.asyncClipboard.clipboardItem\" بۆ \"true\". بۆ گۆڕینی ئاڵاکانی وێبگەڕ لە فایەرفۆکسدا، سەردانی لاپەڕەی \"about:config\" بکە." }, "canvasError": { "cannotShowPreview": "ناتوانرێ پێشبینین پیشان بدرێت", @@ -319,8 +320,8 @@ "doubleClick": "دوو گرتە", "drag": "راکێشان", "editor": "دەستکاریکەر", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "دەستکاری خاڵەکانی هێڵ/تیر بکە", + "editText": "دەستکاری دەق بکە / لەیبڵێک زیاد بکە", "github": "کێشەیەکت دۆزیەوە؟ پێشکەشکردن", "howto": "شوێن ڕینماییەکانمان بکەوە", "or": "یان", @@ -334,8 +335,8 @@ "zoomToFit": "زووم بکە بۆ ئەوەی لەگەڵ هەموو توخمەکاندا بگونجێت", "zoomToSelection": "زووم بکە بۆ دەستنیشانکراوەکان", "toggleElementLock": "قفڵ/کردنەوەی دەستنیشانکراوەکان", - "movePageUpDown": "", - "movePageLeftRight": "" + "movePageUpDown": "لاپەڕەکە بجوڵێنە بۆ سەرەوە/خوارەوە", + "movePageLeftRight": "لاپەڕەکە بجوڵێنە بۆ چەپ/ڕاست" }, "clearCanvasDialog": { "title": "تابلۆکە خاوێن بکەرەوە" @@ -417,7 +418,7 @@ "fileSavedToFilename": "هەڵگیراوە بۆ {filename}", "canvas": "تابلۆ", "selection": "دەستنیشانکراوەکان", - "pasteAsSingleElement": "" + "pasteAsSingleElement": "بۆ دانانەوە وەکو یەک توخم یان دانانەوە بۆ نێو دەسکاریکەرێکی دەق کە بوونی هەیە {{shortcut}} بەکاربهێنە" }, "colors": { "ffffff": "سپی", @@ -468,15 +469,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/lt-LT.json b/src/locales/lt-LT.json index d80739f093..1c18c2729e 100644 --- a/src/locales/lt-LT.json +++ b/src/locales/lt-LT.json @@ -54,6 +54,7 @@ "veryLarge": "Labai didelis", "solid": "", "hachure": "", + "zigzag": "", "crossHatch": "", "thin": "Plonas", "bold": "Pastorintas", diff --git a/src/locales/lv-LV.json b/src/locales/lv-LV.json index 4a311f7cd5..bc8a3a6781 100644 --- a/src/locales/lv-LV.json +++ b/src/locales/lv-LV.json @@ -54,6 +54,7 @@ "veryLarge": "Ļoti liels", "solid": "Pilns", "hachure": "Svītrots", + "zigzag": "Zigzaglīnija", "crossHatch": "Šķērssvītrots", "thin": "Šaurs", "bold": "Trekns", @@ -110,7 +111,7 @@ "increaseFontSize": "Palielināt fonta izmēru", "unbindText": "Atdalīt tekstu", "bindText": "Piesaistīt tekstu figūrai", - "createContainerFromText": "", + "createContainerFromText": "Ietilpināt tekstu figurā", "link": { "edit": "Rediģēt saiti", "create": "Izveidot saiti", @@ -194,7 +195,7 @@ "resetLibrary": "Šī funkcija iztukšos bibliotēku. Vai turpināt?", "removeItemsFromsLibrary": "Vai izņemt {{count}} vienumu(s) no bibliotēkas?", "invalidEncryptionKey": "Šifrēšanas atslēgai jābūt 22 simbolus garai. Tiešsaistes sadarbība ir izslēgta.", - "collabOfflineWarning": "" + "collabOfflineWarning": "Nav pieejams interneta pieslēgums.\nJūsu izmaiņas netiks saglabātas!" }, "errors": { "unsupportedFileType": "Neatbalstīts datnes veids.", @@ -207,19 +208,19 @@ "collabSaveFailed": "Darbs nav saglabāts datubāzē. Ja problēma turpinās, saglabājiet datni lokālajā krātuvē, lai nodrošinātos pret darba pazaudēšanu.", "collabSaveFailed_sizeExceeded": "Darbs nav saglabāts datubāzē, šķiet, ka tāfele ir pārāk liela. Saglabājiet datni lokālajā krātuvē, lai nodrošinātos pret darba pazaudēšanu.", "brave_measure_text_error": { - "start": "", - "aggressive_block_fingerprint": "", - "setting_enabled": "", - "break": "", - "text_elements": "", - "in_your_drawings": "", - "strongly_recommend": "", - "steps": "", - "how": "", - "disable_setting": "", - "issue": "", - "write": "", - "discord": "" + "start": "Izskatās, ka izmanto Brave interneta plārlūku ar ieslēgtu", + "aggressive_block_fingerprint": "Aggressively Block Fingerprinting", + "setting_enabled": "ieslēgtu iestatījumu", + "break": "Tas var salauzt", + "text_elements": "Teksta elementus", + "in_your_drawings": "tavos zīmējumos", + "strongly_recommend": "Mēs iesakām izslēgt šo iestatījumu. Tu vari sekot", + "steps": "šiem soļiem", + "how": "kā to izdarīt", + "disable_setting": " Ja šī iestatījuma izslēgšana neatrisina teksta elementu attēlošanu, tad, lūdzu, atver", + "issue": "problēmu", + "write": "mūsu GitHub vai raksti mums", + "discord": "Discord" } }, "toolBar": { @@ -237,7 +238,7 @@ "penMode": "Pildspalvas režīms – novērst pieskaršanos", "link": "Pievienot/rediģēt atlasītās figūras saiti", "eraser": "Dzēšgumija", - "hand": "" + "hand": "Roka (panoramēšanas rīks)" }, "headings": { "canvasActions": "Tāfeles darbības", @@ -245,7 +246,7 @@ "shapes": "Formas" }, "hints": { - "canvasPanning": "", + "canvasPanning": "Lai bīdītu tāfeli, turiet nospiestu ritināšanas vai atstarpes taustiņu, vai izmanto rokas rīku", "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", @@ -264,7 +265,7 @@ "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", - "firefox_clipboard_write": "" + "firefox_clipboard_write": "Šis iestatījums var tikt ieslēgts ar \"dom.events.asyncClipboard.clipboardItem\" marķieri pārslēgtu uz \"true\". Lai mainītu pārlūka marķierus Firefox, apmeklē \"about:config\" lapu." }, "canvasError": { "cannotShowPreview": "Nevar rādīt priekšskatījumu", @@ -319,8 +320,8 @@ "doubleClick": "dubultklikšķis", "drag": "vilkt", "editor": "Redaktors", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Rediģēt līniju/bultu punktus", + "editText": "Rediģēt tekstu/pievienot birku", "github": "Sastapāt kļūdu? Ziņot", "howto": "Sekojiet mūsu instrukcijām", "or": "vai", @@ -468,15 +469,15 @@ }, "welcomeScreen": { "app": { - "center_heading": "", - "center_heading_plus": "", - "menuHint": "" + "center_heading": "Visi jūsu dati tiek glabāti uz vietas jūsu pārlūkā.", + "center_heading_plus": "Vai tā vietā vēlies doties uz Excalidraw+?", + "menuHint": "Eksportēšana, iestatījumi, valodas..." }, "defaults": { - "menuHint": "", - "center_heading": "", - "toolbarHint": "", - "helpHint": "" + "menuHint": "Eksportēšana, iestatījumi un vēl...", + "center_heading": "Diagrammas. Izveidotas. Vienkārši.", + "toolbarHint": "Izvēlies rīku un sāc zīmēt!", + "helpHint": "Īsceļi un palīdzība" } } } diff --git a/src/locales/mr-IN.json b/src/locales/mr-IN.json index 4f58009c95..346ae5603f 100644 --- a/src/locales/mr-IN.json +++ b/src/locales/mr-IN.json @@ -54,6 +54,7 @@ "veryLarge": "फार मोठं", "solid": "भरीव", "hachure": "हैशूर रेखांकन", + "zigzag": "वाकडी तिकड़ी", "crossHatch": "आडव्या रेघा", "thin": "पातळ", "bold": "जाड", diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json index efc874218c..437e9d13b0 100644 --- a/src/locales/my-MM.json +++ b/src/locales/my-MM.json @@ -54,6 +54,7 @@ "veryLarge": "ပိုကြီး", "solid": "အပြည့်", "hachure": "မျဉ်းစောင်း", + "zigzag": "", "crossHatch": "ဇကာကွက်", "thin": "ပါး", "bold": "ထူ", diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json index 27e717d2af..7a2e72c55d 100644 --- a/src/locales/nb-NO.json +++ b/src/locales/nb-NO.json @@ -54,6 +54,7 @@ "veryLarge": "Svært stor", "solid": "Helfarge", "hachure": "Skravert", + "zigzag": "Sikk-sakk", "crossHatch": "Krysskravert", "thin": "Tynn", "bold": "Tykk", diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json index 7c2bb105b1..62baedf839 100644 --- a/src/locales/nl-NL.json +++ b/src/locales/nl-NL.json @@ -54,6 +54,7 @@ "veryLarge": "Zeer groot", "solid": "Ingekleurd", "hachure": "Arcering", + "zigzag": "", "crossHatch": "Tweemaal gearceerd", "thin": "Dun", "bold": "Vet", diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json index 1733b84c48..49333b910e 100644 --- a/src/locales/nn-NO.json +++ b/src/locales/nn-NO.json @@ -54,6 +54,7 @@ "veryLarge": "Svært stor", "solid": "Solid", "hachure": "Skravert", + "zigzag": "", "crossHatch": "Krysskravert", "thin": "Tynn", "bold": "Tjukk", diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json index a9ca70a845..cae6682dff 100644 --- a/src/locales/oc-FR.json +++ b/src/locales/oc-FR.json @@ -54,6 +54,7 @@ "veryLarge": "Gradassa", "solid": "Solide", "hachure": "Raia", + "zigzag": "", "crossHatch": "Raia crosada", "thin": "Fin", "bold": "Espés", diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json index 31c11ceb80..2b7c66a33e 100644 --- a/src/locales/pa-IN.json +++ b/src/locales/pa-IN.json @@ -54,6 +54,7 @@ "veryLarge": "ਬਹੁਤ ਵੱਡਾ", "solid": "ਠੋਸ", "hachure": "ਤਿਰਛੀਆਂ ਗਰਿੱਲਾਂ", + "zigzag": "", "crossHatch": "ਜਾਲੀ", "thin": "ਪਤਲੀ", "bold": "ਮੋਟੀ", diff --git a/src/locales/percentages.json b/src/locales/percentages.json index b28b6c259d..b129286980 100644 --- a/src/locales/percentages.json +++ b/src/locales/percentages.json @@ -1,17 +1,17 @@ { - "ar-SA": 89, + "ar-SA": 88, "bg-BG": 52, "bn-BD": 57, - "ca-ES": 96, - "cs-CZ": 72, + "ca-ES": 95, + "cs-CZ": 71, "da-DK": 31, "de-DE": 100, "el-GR": 98, "en": 100, - "es-ES": 99, + "es-ES": 100, "eu-ES": 99, "fa-IR": 91, - "fi-FI": 96, + "fi-FI": 95, "fr-FR": 99, "gl-ES": 99, "he-IL": 99, @@ -19,35 +19,35 @@ "hu-HU": 85, "id-ID": 98, "it-IT": 99, - "ja-JP": 97, + "ja-JP": 96, "kab-KAB": 93, "kk-KZ": 19, - "ko-KR": 99, - "ku-TR": 91, + "ko-KR": 100, + "ku-TR": 100, "lt-LT": 61, - "lv-LV": 93, + "lv-LV": 100, "mr-IN": 100, - "my-MM": 40, + "my-MM": 39, "nb-NO": 100, "nl-NL": 92, - "nn-NO": 86, + "nn-NO": 85, "oc-FR": 94, "pa-IN": 79, "pl-PL": 87, - "pt-BR": 96, - "pt-PT": 99, + "pt-BR": 95, + "pt-PT": 100, "ro-RO": 100, - "ru-RU": 96, + "ru-RU": 100, "si-LK": 8, "sk-SK": 99, "sl-SI": 100, - "sv-SE": 99, + "sv-SE": 100, "ta-IN": 90, "th-TH": 39, - "tr-TR": 98, - "uk-UA": 93, + "tr-TR": 97, + "uk-UA": 92, "vi-VN": 52, "zh-CN": 99, - "zh-HK": 25, + "zh-HK": 24, "zh-TW": 100 } diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json index 1abaa038f0..797d88fa15 100644 --- a/src/locales/pl-PL.json +++ b/src/locales/pl-PL.json @@ -54,6 +54,7 @@ "veryLarge": "Bardzo duży", "solid": "Pełne", "hachure": "Linie", + "zigzag": "", "crossHatch": "Zakreślone", "thin": "Cienkie", "bold": "Pogrubione", diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json index d790c5db53..12a1a5d46b 100644 --- a/src/locales/pt-BR.json +++ b/src/locales/pt-BR.json @@ -54,6 +54,7 @@ "veryLarge": "Muito grande", "solid": "Sólido", "hachure": "Hachura", + "zigzag": "", "crossHatch": "Hachura cruzada", "thin": "Fino", "bold": "Espesso", diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json index 7ce28f00f0..947682311a 100644 --- a/src/locales/pt-PT.json +++ b/src/locales/pt-PT.json @@ -54,6 +54,7 @@ "veryLarge": "Muito grande", "solid": "Sólido", "hachure": "Eclosão", + "zigzag": "ziguezague", "crossHatch": "Sombreado", "thin": "Fino", "bold": "Espesso", @@ -319,8 +320,8 @@ "doubleClick": "clique duplo", "drag": "arrastar", "editor": "Editor", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Editar pontos de linha/seta", + "editText": "Editar texto / adicionar etiqueta", "github": "Encontrou algum problema? Informe-nos", "howto": "Siga os nossos guias", "or": "ou", diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json index a0e3b5883a..611ed56769 100644 --- a/src/locales/ro-RO.json +++ b/src/locales/ro-RO.json @@ -54,6 +54,7 @@ "veryLarge": "Foarte mare", "solid": "Plină", "hachure": "Hașură", + "zigzag": "Zigzag", "crossHatch": "Hașură transversală", "thin": "Subțire", "bold": "Îngroșată", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index d4030897ce..cda20347a9 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -54,6 +54,7 @@ "veryLarge": "Очень большой", "solid": "Однотонная", "hachure": "Штрихованная", + "zigzag": "Зигзаг", "crossHatch": "Перекрестная", "thin": "Тонкая", "bold": "Жирная", @@ -110,7 +111,7 @@ "increaseFontSize": "Увеличить шрифт", "unbindText": "Отвязать текст", "bindText": "Привязать текст к контейнеру", - "createContainerFromText": "", + "createContainerFromText": "Поместить текст в контейнер", "link": { "edit": "Редактировать ссылку", "create": "Создать ссылку", @@ -207,19 +208,19 @@ "collabSaveFailed": "Не удалось сохранить в базу данных. Если проблема повторится, нужно будет сохранить файл локально, чтобы быть уверенным, что вы не потеряете вашу работу.", "collabSaveFailed_sizeExceeded": "Не удалось сохранить в базу данных. Похоже, что холст слишком большой. Нужно сохранить файл локально, чтобы быть уверенным, что вы не потеряете вашу работу.", "brave_measure_text_error": { - "start": "", - "aggressive_block_fingerprint": "", - "setting_enabled": "", - "break": "", - "text_elements": "", - "in_your_drawings": "", - "strongly_recommend": "", - "steps": "", - "how": "", - "disable_setting": "", - "issue": "", - "write": "", - "discord": "" + "start": "Похоже, вы используете браузер Brave с", + "aggressive_block_fingerprint": "Агрессивно блокировать фингерпринтинг", + "setting_enabled": "параметр включен", + "break": "Это может привести к поломке", + "text_elements": "Текстовых элементов", + "in_your_drawings": "в ваших рисунках", + "strongly_recommend": "Мы настоятельно рекомендуем отключить эту настройку. Вы можете выполнить", + "steps": "эти действия", + "how": "для отключения", + "disable_setting": " Если отключение этого параметра не исправит отображение текстовых элементов, пожалуйста, откройте", + "issue": "issue", + "write": "на нашем GitHub, или напишите нам в", + "discord": "Discord" } }, "toolBar": { @@ -319,8 +320,8 @@ "doubleClick": "двойной клик", "drag": "перетащить", "editor": "Редактор", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Редактировать концы линий/стрелок", + "editText": "Редактировать текст / добавить метку", "github": "Нашли проблему? Отправьте", "howto": "Следуйте нашим инструкциям", "or": "или", diff --git a/src/locales/si-LK.json b/src/locales/si-LK.json index b84ad567fe..01f5bcac20 100644 --- a/src/locales/si-LK.json +++ b/src/locales/si-LK.json @@ -54,6 +54,7 @@ "veryLarge": "ඉතා විශාල", "solid": "විශාල", "hachure": "මධ්‍යම", + "zigzag": "", "crossHatch": "", "thin": "කෙට්ටු", "bold": "තද", diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json index d6322ac1c8..4bb6f0e76c 100644 --- a/src/locales/sk-SK.json +++ b/src/locales/sk-SK.json @@ -54,6 +54,7 @@ "veryLarge": "Veľmi veľké", "solid": "Plná", "hachure": "Šrafovaná", + "zigzag": "", "crossHatch": "Mriežkovaná", "thin": "Tenká", "bold": "Hrubá", @@ -319,8 +320,8 @@ "doubleClick": "dvojklik", "drag": "potiahnutie", "editor": "Editovanie", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Editácia bodov čiary/šípky", + "editText": "Editácia textu / pridanie štítku", "github": "Objavili ste problém? Nahláste ho", "howto": "Postupujte podľa naších návodov", "or": "alebo", diff --git a/src/locales/sl-SI.json b/src/locales/sl-SI.json index 57199c8138..dc073c0cb0 100644 --- a/src/locales/sl-SI.json +++ b/src/locales/sl-SI.json @@ -54,6 +54,7 @@ "veryLarge": "Zelo velika", "solid": "Polno", "hachure": "Šrafura", + "zigzag": "Cikcak", "crossHatch": "Križno", "thin": "Tanko", "bold": "Krepko", diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json index d0392b119e..502c1bb1ec 100644 --- a/src/locales/sv-SE.json +++ b/src/locales/sv-SE.json @@ -54,6 +54,7 @@ "veryLarge": "Mycket stor", "solid": "Solid", "hachure": "Skraffering", + "zigzag": "Sicksack", "crossHatch": "Skraffera med kors", "thin": "Tunn", "bold": "Fet", @@ -319,8 +320,8 @@ "doubleClick": "dubbelklicka", "drag": "dra", "editor": "Redigerare", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "Redigera linje-/pilpunkter", + "editText": "Redigera text / lägg till etikett", "github": "Hittat ett problem? Rapportera", "howto": "Följ våra guider", "or": "eller", diff --git a/src/locales/ta-IN.json b/src/locales/ta-IN.json index 6dc865766f..47c6e644b2 100644 --- a/src/locales/ta-IN.json +++ b/src/locales/ta-IN.json @@ -54,6 +54,7 @@ "veryLarge": "மிகப் பெரிய", "solid": "திடமான", "hachure": "மலைக்குறிக்கோடு", + "zigzag": "", "crossHatch": "குறுக்குகதவு", "thin": "மெல்லிய", "bold": "பட்டை", diff --git a/src/locales/th-TH.json b/src/locales/th-TH.json index 2dcb9cb433..021c0ce977 100644 --- a/src/locales/th-TH.json +++ b/src/locales/th-TH.json @@ -54,6 +54,7 @@ "veryLarge": "ใหญ่มาก", "solid": "", "hachure": "", + "zigzag": "", "crossHatch": "", "thin": "บาง", "bold": "หนา", diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json index 78dd9329a7..8e1f2b4a07 100644 --- a/src/locales/tr-TR.json +++ b/src/locales/tr-TR.json @@ -54,6 +54,7 @@ "veryLarge": "Çok geniş", "solid": "Dolu", "hachure": "Taralı", + "zigzag": "", "crossHatch": "Çapraz-taralı", "thin": "İnce", "bold": "Kalın", diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json index ed1a01e684..417e0a16ab 100644 --- a/src/locales/uk-UA.json +++ b/src/locales/uk-UA.json @@ -54,6 +54,7 @@ "veryLarge": "Дуже великий", "solid": "Суцільна", "hachure": "Штриховка", + "zigzag": "", "crossHatch": "Перехресна штриховка", "thin": "Тонкий", "bold": "Жирний", diff --git a/src/locales/vi-VN.json b/src/locales/vi-VN.json index 887c32e3e7..27fccfb531 100644 --- a/src/locales/vi-VN.json +++ b/src/locales/vi-VN.json @@ -54,6 +54,7 @@ "veryLarge": "Rất lớn", "solid": "Đặc", "hachure": "Nét gạch gạch", + "zigzag": "", "crossHatch": "Nét gạch chéo", "thin": "Mỏng", "bold": "In đậm", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 5863f5ceb1..18584cda76 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -54,6 +54,7 @@ "veryLarge": "加大", "solid": "实心", "hachure": "线条", + "zigzag": "", "crossHatch": "交叉线条", "thin": "细", "bold": "粗", @@ -319,8 +320,8 @@ "doubleClick": "双击", "drag": "拖动", "editor": "编辑器", - "editLineArrowPoints": "", - "editText": "", + "editLineArrowPoints": "编辑线条或箭头的点", + "editText": "添加或编辑文本", "github": "提交问题", "howto": "帮助文档", "or": "或", diff --git a/src/locales/zh-HK.json b/src/locales/zh-HK.json index bbf23f7f74..5cff35a5c6 100644 --- a/src/locales/zh-HK.json +++ b/src/locales/zh-HK.json @@ -54,6 +54,7 @@ "veryLarge": "勁大", "solid": "實心", "hachure": "斜線", + "zigzag": "", "crossHatch": "交叉格仔", "thin": "幼", "bold": "粗", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index f4462842cd..25c9b0f57c 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -54,6 +54,7 @@ "veryLarge": "特大", "solid": "實心", "hachure": "斜線筆觸", + "zigzag": "Z字形", "crossHatch": "交叉筆觸", "thin": "細", "bold": "粗", From b1311a407a636c87ee0ca326fd20599d0ce4ba9b Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 2 May 2023 12:49:11 +0530 Subject: [PATCH 29/29] fix: Revert add version tags to Docker build (#6540) Revert "build: Add version tags to Docker build (#6508)" This reverts commit 1815cf3213b1d19cb6aa1a54dabd969196a65a28. --- .github/workflows/publish-docker.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 3602bb6606..a4a8a4c5ff 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -12,24 +12,14 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - excalidraw/excalidraw - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - name: Login to DockerHub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v3 with: context: . push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + tags: excalidraw/excalidraw:latest