@excalidraw/excalidraw 0.17.1-7500-ac247a0 → 0.17.1-a38e82f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js +20279 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js.map +7 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-2W5GQUR4.js → chunk-IM4WTX2M.js} +12 -6
  5. package/dist/browser/dev/excalidraw-assets-dev/chunk-IM4WTX2M.js.map +7 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js → en-IOBA4CS2.js} +4 -2
  7. package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css +6 -0
  8. package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js → image-VKDAL6BQ.js} +2 -4
  10. package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js +161 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js.map +7 -0
  12. package/dist/browser/dev/index.css +189 -129
  13. package/dist/browser/dev/index.css.map +3 -3
  14. package/dist/browser/dev/index.js +34708 -37
  15. package/dist/browser/dev/index.js.map +4 -4
  16. package/dist/browser/prod/excalidraw-assets/chunk-LIG3S5TN.js +11 -0
  17. package/dist/browser/prod/excalidraw-assets/chunk-N2C5DK3B.js +55 -0
  18. package/dist/browser/prod/excalidraw-assets/en-WFZVQ7I6.js +1 -0
  19. package/dist/browser/prod/excalidraw-assets/image-4AT7LYMR.js +1 -0
  20. package/dist/browser/prod/excalidraw-assets/image-X66R2EM5.css +1 -0
  21. package/dist/browser/prod/excalidraw-assets/roundRect-2ACQK4DA.js +1 -0
  22. package/dist/browser/prod/index.css +1 -1
  23. package/dist/browser/prod/index.js +203 -1
  24. package/dist/{prod/en-RLIAOBCI.json → dev/en-TDNWCAOT.json} +9 -5
  25. package/dist/dev/index.css +189 -129
  26. package/dist/dev/index.css.map +3 -3
  27. package/dist/dev/index.js +39078 -40080
  28. package/dist/dev/index.js.map +4 -4
  29. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +15 -15
  30. package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
  31. package/dist/excalidraw/actions/actionAlign.js +2 -1
  32. package/dist/excalidraw/actions/actionBoundText.d.ts +10 -10
  33. package/dist/excalidraw/actions/actionBoundText.js +8 -8
  34. package/dist/excalidraw/actions/actionCanvas.d.ts +58 -58
  35. package/dist/excalidraw/actions/actionClipboard.d.ts +34 -34
  36. package/dist/excalidraw/actions/actionClipboard.js +9 -2
  37. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +15 -15
  38. package/dist/excalidraw/actions/actionDeleteSelected.js +3 -2
  39. package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
  40. package/dist/excalidraw/actions/actionDistribute.js +1 -1
  41. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
  42. package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -3
  43. package/dist/excalidraw/actions/actionElementLock.d.ts +10 -10
  44. package/dist/excalidraw/actions/actionExport.d.ts +43 -43
  45. package/dist/excalidraw/actions/actionExport.js +4 -4
  46. package/dist/excalidraw/actions/actionFinalize.d.ts +9 -9
  47. package/dist/excalidraw/actions/actionFinalize.js +7 -6
  48. package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
  49. package/dist/excalidraw/actions/actionFlip.js +11 -11
  50. package/dist/excalidraw/actions/actionFrame.d.ts +16 -16
  51. package/dist/excalidraw/actions/actionFrame.js +1 -1
  52. package/dist/excalidraw/actions/actionGroup.d.ts +10 -10
  53. package/dist/excalidraw/actions/actionGroup.js +3 -2
  54. package/dist/excalidraw/actions/actionLinearEditor.d.ts +5 -5
  55. package/dist/excalidraw/actions/actionLinearEditor.js +1 -1
  56. package/dist/excalidraw/{element/Hyperlink.d.ts → actions/actionLink.d.ts} +29 -51
  57. package/dist/excalidraw/actions/actionLink.js +40 -0
  58. package/dist/excalidraw/actions/actionMenu.d.ts +13 -13
  59. package/dist/excalidraw/actions/actionNavigate.d.ts +10 -10
  60. package/dist/excalidraw/actions/actionNavigate.js +1 -1
  61. package/dist/excalidraw/actions/actionProperties.d.ts +77 -77
  62. package/dist/excalidraw/actions/actionProperties.js +32 -27
  63. package/dist/excalidraw/actions/actionSelectAll.d.ts +5 -5
  64. package/dist/excalidraw/actions/actionSelectAll.js +1 -1
  65. package/dist/excalidraw/actions/actionStyles.d.ts +7 -7
  66. package/dist/excalidraw/actions/actionStyles.js +4 -4
  67. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +5 -5
  68. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +5 -5
  69. package/dist/excalidraw/actions/actionToggleStats.d.ts +5 -5
  70. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +5 -5
  71. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +5 -5
  72. package/dist/excalidraw/actions/index.d.ts +1 -1
  73. package/dist/excalidraw/actions/index.js +1 -1
  74. package/dist/excalidraw/actions/manager.js +2 -1
  75. package/dist/excalidraw/align.d.ts +2 -2
  76. package/dist/excalidraw/align.js +2 -2
  77. package/dist/excalidraw/animated-trail.d.ts +33 -0
  78. package/dist/excalidraw/animated-trail.js +96 -0
  79. package/dist/excalidraw/animation-frame-handler.d.ts +16 -0
  80. package/dist/excalidraw/animation-frame-handler.js +55 -0
  81. package/dist/excalidraw/appState.d.ts +1 -1
  82. package/dist/excalidraw/appState.js +1 -3
  83. package/dist/excalidraw/clipboard.js +5 -5
  84. package/dist/excalidraw/components/Actions.d.ts +3 -3
  85. package/dist/excalidraw/components/Actions.js +18 -7
  86. package/dist/excalidraw/components/App.d.ts +23 -16
  87. package/dist/excalidraw/components/App.js +387 -272
  88. package/dist/excalidraw/components/Button.d.ts +1 -1
  89. package/dist/excalidraw/components/FilledButton.d.ts +2 -2
  90. package/dist/excalidraw/components/FilledButton.js +27 -3
  91. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  92. package/dist/excalidraw/components/ImageExportDialog.d.ts +2 -1
  93. package/dist/excalidraw/components/ImageExportDialog.js +17 -13
  94. package/dist/excalidraw/components/JSONExportDialog.js +1 -1
  95. package/dist/excalidraw/components/{LaserTool/LaserPointerButton.d.ts → LaserPointerButton.d.ts} +1 -1
  96. package/dist/excalidraw/components/{LaserTool/LaserPointerButton.js → LaserPointerButton.js} +2 -2
  97. package/dist/excalidraw/components/LayerUI.js +3 -3
  98. package/dist/excalidraw/components/MobileMenu.js +1 -1
  99. package/dist/excalidraw/components/ProjectName.d.ts +0 -1
  100. package/dist/excalidraw/components/ProjectName.js +1 -1
  101. package/dist/excalidraw/components/PublishLibrary.js +1 -1
  102. package/dist/excalidraw/components/SVGLayer.d.ts +8 -0
  103. package/dist/excalidraw/components/SVGLayer.js +20 -0
  104. package/dist/excalidraw/components/ShareableLinkDialog.js +10 -10
  105. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
  106. package/dist/excalidraw/components/Stack.d.ts +2 -2
  107. package/dist/excalidraw/components/TTDDialog/common.js +10 -1
  108. package/dist/excalidraw/components/TextField.d.ts +5 -2
  109. package/dist/excalidraw/components/TextField.js +6 -3
  110. package/dist/excalidraw/components/Toast.d.ts +3 -2
  111. package/dist/excalidraw/components/Toast.js +2 -2
  112. package/dist/excalidraw/components/ToolButton.js +2 -1
  113. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +2 -2
  114. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +6 -5
  115. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +4 -3
  116. package/dist/excalidraw/components/canvases/StaticCanvas.js +7 -5
  117. package/dist/excalidraw/components/dropdownMenu/DropdownMenuContent.js +22 -2
  118. package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +19 -0
  119. package/dist/excalidraw/{element → components/hyperlink}/Hyperlink.js +40 -115
  120. package/dist/excalidraw/components/hyperlink/helpers.d.ts +7 -0
  121. package/dist/excalidraw/components/hyperlink/helpers.js +49 -0
  122. package/dist/excalidraw/components/icons.d.ts +2 -1
  123. package/dist/excalidraw/components/icons.js +2 -1
  124. package/dist/excalidraw/components/live-collaboration/LiveCollaborationTrigger.js +3 -2
  125. package/dist/excalidraw/components/main-menu/DefaultItems.js +5 -2
  126. package/dist/excalidraw/constants.d.ts +6 -0
  127. package/dist/excalidraw/constants.js +6 -0
  128. package/dist/excalidraw/data/blob.js +13 -14
  129. package/dist/excalidraw/data/filesystem.d.ts +1 -1
  130. package/dist/excalidraw/data/index.d.ts +2 -1
  131. package/dist/excalidraw/data/index.js +20 -16
  132. package/dist/excalidraw/data/json.d.ts +1 -1
  133. package/dist/excalidraw/data/json.js +5 -3
  134. package/dist/excalidraw/data/resave.d.ts +1 -1
  135. package/dist/excalidraw/data/resave.js +2 -2
  136. package/dist/excalidraw/data/restore.js +8 -13
  137. package/dist/excalidraw/data/transform.js +13 -9
  138. package/dist/excalidraw/distribute.d.ts +2 -2
  139. package/dist/excalidraw/distribute.js +2 -2
  140. package/dist/excalidraw/element/ElementCanvasButtons.d.ts +3 -2
  141. package/dist/excalidraw/element/ElementCanvasButtons.js +4 -4
  142. package/dist/excalidraw/element/binding.d.ts +9 -9
  143. package/dist/excalidraw/element/binding.js +61 -59
  144. package/dist/excalidraw/element/bounds.d.ts +5 -5
  145. package/dist/excalidraw/element/bounds.js +29 -32
  146. package/dist/excalidraw/element/collision.d.ts +11 -11
  147. package/dist/excalidraw/element/collision.js +49 -46
  148. package/dist/excalidraw/element/containerCache.d.ts +11 -0
  149. package/dist/excalidraw/element/containerCache.js +14 -0
  150. package/dist/excalidraw/element/dragElements.js +10 -19
  151. package/dist/excalidraw/element/embeddable.d.ts +12 -13
  152. package/dist/excalidraw/element/embeddable.js +17 -27
  153. package/dist/excalidraw/element/image.js +1 -2
  154. package/dist/excalidraw/element/index.d.ts +0 -1
  155. package/dist/excalidraw/element/index.js +0 -1
  156. package/dist/excalidraw/element/linearElementEditor.d.ts +36 -36
  157. package/dist/excalidraw/element/linearElementEditor.js +79 -80
  158. package/dist/excalidraw/element/newElement.d.ts +4 -6
  159. package/dist/excalidraw/element/newElement.js +11 -16
  160. package/dist/excalidraw/element/resizeElements.d.ts +6 -6
  161. package/dist/excalidraw/element/resizeElements.js +40 -46
  162. package/dist/excalidraw/element/resizeTest.d.ts +3 -3
  163. package/dist/excalidraw/element/resizeTest.js +4 -4
  164. package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
  165. package/dist/excalidraw/element/sizeHelpers.js +2 -2
  166. package/dist/excalidraw/element/textElement.d.ts +18 -20
  167. package/dist/excalidraw/element/textElement.js +80 -111
  168. package/dist/excalidraw/element/textWysiwyg.d.ts +1 -6
  169. package/dist/excalidraw/element/textWysiwyg.js +15 -37
  170. package/dist/excalidraw/element/transformHandles.d.ts +4 -4
  171. package/dist/excalidraw/element/transformHandles.js +6 -6
  172. package/dist/excalidraw/element/typeChecks.js +4 -1
  173. package/dist/excalidraw/element/types.d.ts +24 -11
  174. package/dist/excalidraw/frame.d.ts +26 -20
  175. package/dist/excalidraw/frame.js +157 -84
  176. package/dist/excalidraw/groups.d.ts +3 -3
  177. package/dist/excalidraw/groups.js +11 -3
  178. package/dist/excalidraw/history.d.ts +1 -1
  179. package/dist/excalidraw/hooks/useLibraryItemSvg.js +1 -1
  180. package/dist/excalidraw/index.d.ts +8 -9
  181. package/dist/excalidraw/index.js +15 -11
  182. package/dist/excalidraw/laser-trails.d.ts +19 -0
  183. package/dist/excalidraw/laser-trails.js +95 -0
  184. package/dist/excalidraw/locales/en.json +9 -5
  185. package/dist/excalidraw/reactUtils.d.ts +14 -0
  186. package/dist/excalidraw/reactUtils.js +45 -0
  187. package/dist/excalidraw/renderer/helpers.d.ts +13 -0
  188. package/dist/excalidraw/renderer/helpers.js +39 -0
  189. package/dist/excalidraw/renderer/interactiveScene.d.ts +20 -0
  190. package/dist/excalidraw/renderer/{renderScene.js → interactiveScene.js} +199 -474
  191. package/dist/excalidraw/renderer/renderElement.d.ts +6 -6
  192. package/dist/excalidraw/renderer/renderElement.js +54 -366
  193. package/dist/excalidraw/renderer/staticScene.d.ts +11 -0
  194. package/dist/excalidraw/renderer/staticScene.js +205 -0
  195. package/dist/excalidraw/renderer/staticSvgScene.d.ts +5 -0
  196. package/dist/excalidraw/renderer/staticSvgScene.js +385 -0
  197. package/dist/excalidraw/scene/Fonts.js +2 -1
  198. package/dist/excalidraw/scene/Renderer.d.ts +1 -1
  199. package/dist/excalidraw/scene/Renderer.js +32 -20
  200. package/dist/excalidraw/scene/Scene.d.ts +10 -9
  201. package/dist/excalidraw/scene/Scene.js +45 -21
  202. package/dist/excalidraw/scene/Shape.d.ts +3 -1
  203. package/dist/excalidraw/scene/Shape.js +7 -5
  204. package/dist/excalidraw/scene/ShapeCache.d.ts +2 -1
  205. package/dist/excalidraw/scene/ShapeCache.js +1 -0
  206. package/dist/excalidraw/scene/comparisons.js +2 -1
  207. package/dist/excalidraw/scene/export.d.ts +3 -0
  208. package/dist/excalidraw/scene/export.js +20 -40
  209. package/dist/excalidraw/scene/index.d.ts +0 -1
  210. package/dist/excalidraw/scene/index.js +0 -1
  211. package/dist/excalidraw/scene/scrollbars.d.ts +1 -1
  212. package/dist/excalidraw/scene/scrollbars.js +1 -1
  213. package/dist/excalidraw/scene/selection.d.ts +5 -5
  214. package/dist/excalidraw/scene/selection.js +16 -14
  215. package/dist/excalidraw/scene/types.d.ts +11 -5
  216. package/dist/excalidraw/snapping.d.ts +7 -7
  217. package/dist/excalidraw/snapping.js +21 -20
  218. package/dist/excalidraw/types.d.ts +10 -11
  219. package/dist/excalidraw/utility-types.d.ts +5 -0
  220. package/dist/excalidraw/utils.d.ts +18 -15
  221. package/dist/excalidraw/utils.js +37 -45
  222. package/dist/{dev/en-RLIAOBCI.json → prod/en-TDNWCAOT.json} +9 -5
  223. package/dist/prod/index.css +1 -1
  224. package/dist/prod/index.js +42 -42
  225. package/dist/utils/bbox.d.ts +2 -2
  226. package/dist/utils/export.d.ts +3 -3
  227. package/dist/utils/export.js +3 -13
  228. package/dist/utils/index.d.ts +2 -2
  229. package/dist/utils/index.js +2 -2
  230. package/dist/utils/withinBounds.d.ts +1 -1
  231. package/dist/utils/withinBounds.js +5 -2
  232. package/package.json +4 -4
  233. package/dist/browser/dev/excalidraw-assets-dev/chunk-2W5GQUR4.js.map +0 -7
  234. package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js +0 -53497
  235. package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js.map +0 -7
  236. package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css +0 -5797
  237. package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css.map +0 -7
  238. package/dist/browser/prod/excalidraw-assets/chunk-4YN2HN3S.js +0 -257
  239. package/dist/browser/prod/excalidraw-assets/chunk-OWLL6VOG.js +0 -11
  240. package/dist/browser/prod/excalidraw-assets/en-ERQOR3OC.js +0 -1
  241. package/dist/browser/prod/excalidraw-assets/image-LTLHTTSE.js +0 -1
  242. package/dist/browser/prod/excalidraw-assets/image-QBL334OA.css +0 -1
  243. package/dist/excalidraw/components/LaserTool/LaserPathManager.d.ts +0 -28
  244. package/dist/excalidraw/components/LaserTool/LaserPathManager.js +0 -225
  245. package/dist/excalidraw/components/LaserTool/LaserTool.d.ts +0 -8
  246. package/dist/excalidraw/components/LaserTool/LaserTool.js +0 -15
  247. package/dist/excalidraw/renderer/renderScene.d.ts +0 -25
  248. package/dist/excalidraw/vite.config.d.mts +0 -2
  249. package/dist/excalidraw/vite.config.mjs +0 -13
  250. /package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js.map → en-IOBA4CS2.js.map} +0 -0
  251. /package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js.map → image-VKDAL6BQ.js.map} +0 -0
@@ -1,8 +1,9 @@
1
- import { ExcalidrawElement, ExcalidrawTextElement, NonDeletedExcalidrawElement, ExcalidrawFreeDrawElement } from "../element/types";
1
+ import { ExcalidrawElement, ExcalidrawTextElement, NonDeletedExcalidrawElement, ExcalidrawFreeDrawElement, ExcalidrawFrameLikeElement, NonDeletedSceneElementsMap } from "../element/types";
2
2
  import type { RoughCanvas } from "roughjs/bin/canvas";
3
- import type { RoughSVG } from "roughjs/bin/svg";
4
- import { SVGRenderConfig, StaticCanvasRenderConfig } from "../scene/types";
5
- import { AppState, StaticCanvasAppState, BinaryFiles, InteractiveCanvasAppState } from "../types";
3
+ import { StaticCanvasRenderConfig, RenderableElementsMap } from "../scene/types";
4
+ import { AppState, StaticCanvasAppState, InteractiveCanvasAppState, ElementsPendingErasure } from "../types";
5
+ export declare const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
6
+ export declare const getRenderOpacity: (element: ExcalidrawElement, containingFrame: ExcalidrawFrameLikeElement | null, elementsPendingErasure: ElementsPendingErasure) => number;
6
7
  export interface ExcalidrawElementWithCanvas {
7
8
  element: ExcalidrawElement | ExcalidrawTextElement;
8
9
  canvas: HTMLCanvasElement;
@@ -17,8 +18,7 @@ export interface ExcalidrawElementWithCanvas {
17
18
  export declare const DEFAULT_LINK_SIZE = 14;
18
19
  export declare const elementWithCanvasCache: WeakMap<ExcalidrawElement, ExcalidrawElementWithCanvas>;
19
20
  export declare const renderSelectionElement: (element: NonDeletedExcalidrawElement, context: CanvasRenderingContext2D, appState: InteractiveCanvasAppState) => void;
20
- export declare const renderElement: (element: NonDeletedExcalidrawElement, rc: RoughCanvas, context: CanvasRenderingContext2D, renderConfig: StaticCanvasRenderConfig, appState: StaticCanvasAppState) => void;
21
- export declare const renderElementToSvg: (element: NonDeletedExcalidrawElement, rsvg: RoughSVG, svgRoot: SVGElement, files: BinaryFiles, offsetX: number, offsetY: number, renderConfig: SVGRenderConfig) => void;
21
+ export declare const renderElement: (element: NonDeletedExcalidrawElement, elementsMap: RenderableElementsMap, allElementsMap: NonDeletedSceneElementsMap, rc: RoughCanvas, context: CanvasRenderingContext2D, renderConfig: StaticCanvasRenderConfig, appState: StaticCanvasAppState) => void;
22
22
  export declare const pathsCache: WeakMap<ExcalidrawFreeDrawElement, Path2D>;
23
23
  export declare function generateFreeDrawShape(element: ExcalidrawFreeDrawElement): Path2D;
24
24
  export declare function getFreeDrawPath2D(element: ExcalidrawFreeDrawElement): Path2D | undefined;
@@ -1,22 +1,20 @@
1
1
  import { isTextElement, isLinearElement, isFreeDrawElement, isInitializedImageElement, isArrowElement, hasBoundTextElement, isMagicFrameElement, } from "../element/typeChecks";
2
2
  import { getElementAbsoluteCoords } from "../element/bounds";
3
- import { distance, getFontString, getFontFamilyString, isRTL, isTestEnv, } from "../utils";
4
- import { getCornerRadius, isPathALoop, isRightAngle } from "../math";
3
+ import { distance, getFontString, isRTL } from "../utils";
4
+ import { getCornerRadius, isRightAngle } from "../math";
5
5
  import rough from "roughjs/bin/rough";
6
6
  import { getDefaultAppState } from "../appState";
7
- import { BOUND_TEXT_PADDING, FRAME_STYLE, MAX_DECIMALS_FOR_SVG_EXPORT, MIME_TYPES, SVG_NS, } from "../constants";
7
+ import { BOUND_TEXT_PADDING, ELEMENT_READY_TO_ERASE_OPACITY, FRAME_STYLE, MIME_TYPES, } from "../constants";
8
8
  import { getStroke } from "perfect-freehand";
9
- import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth, } from "../element/textElement";
9
+ import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth, getVerticalOffset, } from "../element/textElement";
10
10
  import { LinearElementEditor } from "../element/linearElementEditor";
11
- import { createPlaceholderEmbeddableLabel, getEmbedLink, } from "../element/embeddable";
12
11
  import { getContainingFrame } from "../frame";
13
- import { normalizeLink, toValidURL } from "../data/url";
14
12
  import { ShapeCache } from "../scene/ShapeCache";
15
13
  // using a stronger invert (100% vs our regular 93%) and saturate
16
14
  // as a temp hack to make images in dark theme look closer to original
17
15
  // color scheme (it's still not quite there and the colors look slightly
18
16
  // desatured, alas...)
19
- const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
17
+ export const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
20
18
  const defaultAppState = getDefaultAppState();
21
19
  const isPendingImageElement = (element, renderConfig) => isInitializedImageElement(element) &&
22
20
  !renderConfig.imageCache.has(element.fileId);
@@ -27,7 +25,19 @@ const shouldResetImageFilter = (element, renderConfig, appState) => {
27
25
  renderConfig.imageCache.get(element.fileId)?.mimeType !== MIME_TYPES.svg);
28
26
  };
29
27
  const getCanvasPadding = (element) => element.type === "freedraw" ? element.strokeWidth * 12 : 20;
30
- const cappedElementCanvasSize = (element, zoom) => {
28
+ export const getRenderOpacity = (element, containingFrame, elementsPendingErasure) => {
29
+ // multiplying frame opacity with element opacity to combine them
30
+ // (e.g. frame 50% and element 50% opacity should result in 25% opacity)
31
+ let opacity = ((containingFrame?.opacity ?? 100) * element.opacity) / 10000;
32
+ // if pending erasure, multiply again to combine further
33
+ // (so that erasing always results in lower opacity than original)
34
+ if (elementsPendingErasure.has(element.id) ||
35
+ (containingFrame && elementsPendingErasure.has(containingFrame.id))) {
36
+ opacity *= ELEMENT_READY_TO_ERASE_OPACITY / 100;
37
+ }
38
+ return opacity;
39
+ };
40
+ const cappedElementCanvasSize = (element, elementsMap, zoom) => {
31
41
  // these limits are ballpark, they depend on specific browsers and device.
32
42
  // We've chosen lower limits to be safe. We might want to change these limits
33
43
  // based on browser/device type, if we get reports of low quality rendering
@@ -38,7 +48,7 @@ const cappedElementCanvasSize = (element, zoom) => {
38
48
  // ~ safari width/height limit based on developer.mozilla.org.
39
49
  const WIDTH_HEIGHT_LIMIT = 32767;
40
50
  const padding = getCanvasPadding(element);
41
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
51
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
42
52
  const elementWidth = isLinearElement(element) || isFreeDrawElement(element)
43
53
  ? distance(x1, x2)
44
54
  : element.width;
@@ -61,17 +71,17 @@ const cappedElementCanvasSize = (element, zoom) => {
61
71
  height = Math.floor(height * scale);
62
72
  return { width, height, scale };
63
73
  };
64
- const generateElementCanvas = (element, zoom, renderConfig, appState) => {
74
+ const generateElementCanvas = (element, elementsMap, zoom, renderConfig, appState) => {
65
75
  const canvas = document.createElement("canvas");
66
76
  const context = canvas.getContext("2d");
67
77
  const padding = getCanvasPadding(element);
68
- const { width, height, scale } = cappedElementCanvasSize(element, zoom);
78
+ const { width, height, scale } = cappedElementCanvasSize(element, elementsMap, zoom);
69
79
  canvas.width = width;
70
80
  canvas.height = height;
71
81
  let canvasOffsetX = 0;
72
82
  let canvasOffsetY = 0;
73
83
  if (isLinearElement(element) || isFreeDrawElement(element)) {
74
- const [x1, y1] = getElementAbsoluteCoords(element);
84
+ const [x1, y1] = getElementAbsoluteCoords(element, elementsMap);
75
85
  canvasOffsetX =
76
86
  element.x > x1
77
87
  ? distance(element.x, x1) * window.devicePixelRatio * scale
@@ -100,8 +110,8 @@ const generateElementCanvas = (element, zoom, renderConfig, appState) => {
100
110
  zoomValue: zoom.value,
101
111
  canvasOffsetX,
102
112
  canvasOffsetY,
103
- boundTextElementVersion: getBoundTextElement(element)?.version || null,
104
- containingFrameOpacity: getContainingFrame(element)?.opacity || 100,
113
+ boundTextElementVersion: getBoundTextElement(element, elementsMap)?.version || null,
114
+ containingFrameOpacity: getContainingFrame(element, elementsMap)?.opacity || 100,
105
115
  };
106
116
  };
107
117
  export const DEFAULT_LINK_SIZE = 14;
@@ -119,8 +129,6 @@ const drawImagePlaceholder = (element, context, zoomValue) => {
119
129
  : IMAGE_PLACEHOLDER_IMG, element.width / 2 - size / 2, element.height / 2 - size / 2, size, size);
120
130
  };
121
131
  const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
122
- context.globalAlpha =
123
- ((getContainingFrame(element)?.opacity ?? 100) * element.opacity) / 10000;
124
132
  switch (element.type) {
125
133
  case "rectangle":
126
134
  case "iframe":
@@ -160,6 +168,11 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
160
168
  ? renderConfig.imageCache.get(element.fileId)?.image
161
169
  : undefined;
162
170
  if (img != null && !(img instanceof Promise)) {
171
+ if (element.roundness && context.roundRect) {
172
+ context.beginPath();
173
+ context.roundRect(0, 0, element.width, element.height, getCornerRadius(Math.min(element.width, element.height), element));
174
+ context.clip();
175
+ }
163
176
  context.drawImage(img, 0 /* hardcoded for the selection box*/, 0, element.width, element.height);
164
177
  }
165
178
  else {
@@ -189,9 +202,9 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
189
202
  ? element.width
190
203
  : 0;
191
204
  const lineHeightPx = getLineHeightInPx(element.fontSize, element.lineHeight);
192
- const verticalOffset = element.height - element.baseline;
205
+ const verticalOffset = getVerticalOffset(element.fontFamily, element.fontSize, lineHeightPx);
193
206
  for (let index = 0; index < lines.length; index++) {
194
- context.fillText(lines[index], horizontalOffset, (index + 1) * lineHeightPx - verticalOffset);
207
+ context.fillText(lines[index], horizontalOffset, index * lineHeightPx + verticalOffset);
195
208
  }
196
209
  context.restore();
197
210
  if (shouldTemporarilyAttach) {
@@ -203,33 +216,32 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
203
216
  }
204
217
  }
205
218
  }
206
- context.globalAlpha = 1;
207
219
  };
208
220
  export const elementWithCanvasCache = new WeakMap();
209
- const generateElementWithCanvas = (element, renderConfig, appState) => {
221
+ const generateElementWithCanvas = (element, elementsMap, renderConfig, appState) => {
210
222
  const zoom = renderConfig ? appState.zoom : defaultAppState.zoom;
211
223
  const prevElementWithCanvas = elementWithCanvasCache.get(element);
212
224
  const shouldRegenerateBecauseZoom = prevElementWithCanvas &&
213
225
  prevElementWithCanvas.zoomValue !== zoom.value &&
214
226
  !appState?.shouldCacheIgnoreZoom;
215
- const boundTextElementVersion = getBoundTextElement(element)?.version || null;
216
- const containingFrameOpacity = getContainingFrame(element)?.opacity || 100;
227
+ const boundTextElementVersion = getBoundTextElement(element, elementsMap)?.version || null;
228
+ const containingFrameOpacity = getContainingFrame(element, elementsMap)?.opacity || 100;
217
229
  if (!prevElementWithCanvas ||
218
230
  shouldRegenerateBecauseZoom ||
219
231
  prevElementWithCanvas.theme !== appState.theme ||
220
232
  prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion ||
221
233
  prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity) {
222
- const elementWithCanvas = generateElementCanvas(element, zoom, renderConfig, appState);
234
+ const elementWithCanvas = generateElementCanvas(element, elementsMap, zoom, renderConfig, appState);
223
235
  elementWithCanvasCache.set(element, elementWithCanvas);
224
236
  return elementWithCanvas;
225
237
  }
226
238
  return prevElementWithCanvas;
227
239
  };
228
- const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState) => {
240
+ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState, allElementsMap) => {
229
241
  const element = elementWithCanvas.element;
230
242
  const padding = getCanvasPadding(element);
231
243
  const zoom = elementWithCanvas.scale;
232
- let [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
244
+ let [x1, y1, x2, y2] = getElementAbsoluteCoords(element, allElementsMap);
233
245
  // Free draw elements will otherwise "shuffle" as the min x and y change
234
246
  if (isFreeDrawElement(element)) {
235
247
  x1 = Math.floor(x1);
@@ -241,7 +253,7 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
241
253
  const cy = ((y1 + y2) / 2 + appState.scrollY) * window.devicePixelRatio;
242
254
  context.save();
243
255
  context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
244
- const boundTextElement = getBoundTextElement(element);
256
+ const boundTextElement = getBoundTextElement(element, allElementsMap);
245
257
  if (isArrowElement(element) && boundTextElement) {
246
258
  const tempCanvas = document.createElement("canvas");
247
259
  const tempCanvasContext = tempCanvas.getContext("2d");
@@ -259,7 +271,7 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
259
271
  tempCanvasContext.translate(tempCanvas.width / 2, tempCanvas.height / 2);
260
272
  tempCanvasContext.rotate(element.angle);
261
273
  tempCanvasContext.drawImage(elementWithCanvas.canvas, -elementWithCanvas.canvas.width / 2, -elementWithCanvas.canvas.height / 2, elementWithCanvas.canvas.width, elementWithCanvas.canvas.height);
262
- const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement);
274
+ const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, allElementsMap);
263
275
  tempCanvasContext.rotate(-element.angle);
264
276
  // Shift the canvas to the center of the bound text element
265
277
  const shiftX = tempCanvas.width / 2 -
@@ -301,11 +313,11 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
301
313
  if (import.meta.env.VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX ===
302
314
  "true" &&
303
315
  hasBoundTextElement(element)) {
304
- const textElement = getBoundTextElement(element);
316
+ const textElement = getBoundTextElement(element, allElementsMap);
305
317
  const coords = getContainerCoords(element);
306
318
  context.strokeStyle = "#c92a2a";
307
319
  context.lineWidth = 3;
308
- context.strokeRect((coords.x + appState.scrollX) * window.devicePixelRatio, (coords.y + appState.scrollY) * window.devicePixelRatio, getBoundTextMaxWidth(element) * window.devicePixelRatio, getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio);
320
+ context.strokeRect((coords.x + appState.scrollX) * window.devicePixelRatio, (coords.y + appState.scrollY) * window.devicePixelRatio, getBoundTextMaxWidth(element, textElement) * window.devicePixelRatio, getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio);
309
321
  }
310
322
  }
311
323
  context.restore();
@@ -326,7 +338,8 @@ export const renderSelectionElement = (element, context, appState) => {
326
338
  context.strokeRect(offset, offset, element.width, element.height);
327
339
  context.restore();
328
340
  };
329
- export const renderElement = (element, rc, context, renderConfig, appState) => {
341
+ export const renderElement = (element, elementsMap, allElementsMap, rc, context, renderConfig, appState) => {
342
+ context.globalAlpha = getRenderOpacity(element, getContainingFrame(element, elementsMap), renderConfig.elementsPendingErasure);
330
343
  switch (element.type) {
331
344
  case "magicframe":
332
345
  case "frame": {
@@ -360,7 +373,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
360
373
  // rely on existing shapes
361
374
  ShapeCache.generateElementShape(element, null);
362
375
  if (renderConfig.isExporting) {
363
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
376
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
364
377
  const cx = (x1 + x2) / 2 + appState.scrollX;
365
378
  const cy = (y1 + y2) / 2 + appState.scrollY;
366
379
  const shiftX = (x2 - x1) / 2 - (element.x - x1);
@@ -373,8 +386,8 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
373
386
  context.restore();
374
387
  }
375
388
  else {
376
- const elementWithCanvas = generateElementWithCanvas(element, renderConfig, appState);
377
- drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState);
389
+ const elementWithCanvas = generateElementWithCanvas(element, elementsMap, renderConfig, appState);
390
+ drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState, allElementsMap);
378
391
  }
379
392
  break;
380
393
  }
@@ -392,15 +405,15 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
392
405
  // rely on existing shapes
393
406
  ShapeCache.generateElementShape(element, renderConfig);
394
407
  if (renderConfig.isExporting) {
395
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
408
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
396
409
  const cx = (x1 + x2) / 2 + appState.scrollX;
397
410
  const cy = (y1 + y2) / 2 + appState.scrollY;
398
411
  let shiftX = (x2 - x1) / 2 - (element.x - x1);
399
412
  let shiftY = (y2 - y1) / 2 - (element.y - y1);
400
413
  if (isTextElement(element)) {
401
- const container = getContainerElement(element);
414
+ const container = getContainerElement(element, elementsMap);
402
415
  if (isArrowElement(container)) {
403
- const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element);
416
+ const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element, elementsMap);
404
417
  shiftX = (x2 - x1) / 2 - (boundTextCoords.x - x1);
405
418
  shiftY = (y2 - y1) / 2 - (boundTextCoords.y - y1);
406
419
  }
@@ -410,7 +423,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
410
423
  if (shouldResetImageFilter(element, renderConfig, appState)) {
411
424
  context.filter = "none";
412
425
  }
413
- const boundTextElement = getBoundTextElement(element);
426
+ const boundTextElement = getBoundTextElement(element, elementsMap);
414
427
  if (isArrowElement(element) && boundTextElement) {
415
428
  const tempCanvas = document.createElement("canvas");
416
429
  const tempCanvasContext = tempCanvas.getContext("2d");
@@ -434,7 +447,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
434
447
  tempCanvasContext.translate(shiftX, shiftY);
435
448
  tempCanvasContext.rotate(-element.angle);
436
449
  // Shift the canvas to center of bound text
437
- const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement);
450
+ const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, elementsMap);
438
451
  const boundTextShiftX = (x1 + x2) / 2 - boundTextCx;
439
452
  const boundTextShiftY = (y1 + y2) / 2 - boundTextCy;
440
453
  tempCanvasContext.translate(-boundTextShiftX, -boundTextShiftY);
@@ -457,7 +470,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
457
470
  // canvases)
458
471
  }
459
472
  else {
460
- const elementWithCanvas = generateElementWithCanvas(element, renderConfig, appState);
473
+ const elementWithCanvas = generateElementWithCanvas(element, elementsMap, renderConfig, appState);
461
474
  const currentImageSmoothingStatus = context.imageSmoothingEnabled;
462
475
  if (
463
476
  // do not disable smoothing during zoom as blurry shapes look better
@@ -477,7 +490,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
477
490
  //
478
491
  context.imageSmoothingEnabled = false;
479
492
  }
480
- drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState);
493
+ drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState, allElementsMap);
481
494
  // reset
482
495
  context.imageSmoothingEnabled = currentImageSmoothingStatus;
483
496
  }
@@ -488,332 +501,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
488
501
  throw new Error(`Unimplemented type ${element.type}`);
489
502
  }
490
503
  }
491
- };
492
- const roughSVGDrawWithPrecision = (rsvg, drawable, precision) => {
493
- if (typeof precision === "undefined") {
494
- return rsvg.draw(drawable);
495
- }
496
- const pshape = {
497
- sets: drawable.sets,
498
- shape: drawable.shape,
499
- options: { ...drawable.options, fixedDecimalPlaceDigits: precision },
500
- };
501
- return rsvg.draw(pshape);
502
- };
503
- const maybeWrapNodesInFrameClipPath = (element, root, nodes, frameRendering) => {
504
- if (!frameRendering.enabled || !frameRendering.clip) {
505
- return null;
506
- }
507
- const frame = getContainingFrame(element);
508
- if (frame) {
509
- const g = root.ownerDocument.createElementNS(SVG_NS, "g");
510
- g.setAttributeNS(SVG_NS, "clip-path", `url(#${frame.id})`);
511
- nodes.forEach((node) => g.appendChild(node));
512
- return g;
513
- }
514
- return null;
515
- };
516
- export const renderElementToSvg = (element, rsvg, svgRoot, files, offsetX, offsetY, renderConfig) => {
517
- const offset = { x: offsetX, y: offsetY };
518
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
519
- let cx = (x2 - x1) / 2 - (element.x - x1);
520
- let cy = (y2 - y1) / 2 - (element.y - y1);
521
- if (isTextElement(element)) {
522
- const container = getContainerElement(element);
523
- if (isArrowElement(container)) {
524
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(container);
525
- const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element);
526
- cx = (x2 - x1) / 2 - (boundTextCoords.x - x1);
527
- cy = (y2 - y1) / 2 - (boundTextCoords.y - y1);
528
- offsetX = offsetX + boundTextCoords.x - element.x;
529
- offsetY = offsetY + boundTextCoords.y - element.y;
530
- }
531
- }
532
- const degree = (180 * element.angle) / Math.PI;
533
- // element to append node to, most of the time svgRoot
534
- let root = svgRoot;
535
- // if the element has a link, create an anchor tag and make that the new root
536
- if (element.link) {
537
- const anchorTag = svgRoot.ownerDocument.createElementNS(SVG_NS, "a");
538
- anchorTag.setAttribute("href", normalizeLink(element.link));
539
- root.appendChild(anchorTag);
540
- root = anchorTag;
541
- }
542
- const addToRoot = (node, element) => {
543
- if (isTestEnv()) {
544
- node.setAttribute("data-id", element.id);
545
- }
546
- root.appendChild(node);
547
- };
548
- const opacity = ((getContainingFrame(element)?.opacity ?? 100) * element.opacity) / 10000;
549
- switch (element.type) {
550
- case "selection": {
551
- // Since this is used only during editing experience, which is canvas based,
552
- // this should not happen
553
- throw new Error("Selection rendering is not supported for SVG");
554
- }
555
- case "rectangle":
556
- case "diamond":
557
- case "ellipse": {
558
- const shape = ShapeCache.generateElementShape(element, null);
559
- const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
560
- if (opacity !== 1) {
561
- node.setAttribute("stroke-opacity", `${opacity}`);
562
- node.setAttribute("fill-opacity", `${opacity}`);
563
- }
564
- node.setAttribute("stroke-linecap", "round");
565
- node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
566
- const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
567
- addToRoot(g || node, element);
568
- break;
569
- }
570
- case "iframe":
571
- case "embeddable": {
572
- // render placeholder rectangle
573
- const shape = ShapeCache.generateElementShape(element, renderConfig);
574
- const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
575
- const opacity = element.opacity / 100;
576
- if (opacity !== 1) {
577
- node.setAttribute("stroke-opacity", `${opacity}`);
578
- node.setAttribute("fill-opacity", `${opacity}`);
579
- }
580
- node.setAttribute("stroke-linecap", "round");
581
- node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
582
- addToRoot(node, element);
583
- const label = createPlaceholderEmbeddableLabel(element);
584
- renderElementToSvg(label, rsvg, root, files, label.x + offset.x - element.x, label.y + offset.y - element.y, renderConfig);
585
- // render embeddable element + iframe
586
- const embeddableNode = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
587
- embeddableNode.setAttribute("stroke-linecap", "round");
588
- embeddableNode.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
589
- while (embeddableNode.firstChild) {
590
- embeddableNode.removeChild(embeddableNode.firstChild);
591
- }
592
- const radius = getCornerRadius(Math.min(element.width, element.height), element);
593
- const embedLink = getEmbedLink(toValidURL(element.link || ""));
594
- // if rendering embeddables explicitly disabled or
595
- // embedding documents via srcdoc (which doesn't seem to work for SVGs)
596
- // replace with a link instead
597
- if (renderConfig.renderEmbeddables === false ||
598
- embedLink?.type === "document") {
599
- const anchorTag = svgRoot.ownerDocument.createElementNS(SVG_NS, "a");
600
- anchorTag.setAttribute("href", normalizeLink(element.link || ""));
601
- anchorTag.setAttribute("target", "_blank");
602
- anchorTag.setAttribute("rel", "noopener noreferrer");
603
- anchorTag.style.borderRadius = `${radius}px`;
604
- embeddableNode.appendChild(anchorTag);
605
- }
606
- else {
607
- const foreignObject = svgRoot.ownerDocument.createElementNS(SVG_NS, "foreignObject");
608
- foreignObject.style.width = `${element.width}px`;
609
- foreignObject.style.height = `${element.height}px`;
610
- foreignObject.style.border = "none";
611
- const div = foreignObject.ownerDocument.createElementNS(SVG_NS, "div");
612
- div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
613
- div.style.width = "100%";
614
- div.style.height = "100%";
615
- const iframe = div.ownerDocument.createElement("iframe");
616
- iframe.src = embedLink?.link ?? "";
617
- iframe.style.width = "100%";
618
- iframe.style.height = "100%";
619
- iframe.style.border = "none";
620
- iframe.style.borderRadius = `${radius}px`;
621
- iframe.style.top = "0";
622
- iframe.style.left = "0";
623
- iframe.allowFullscreen = true;
624
- div.appendChild(iframe);
625
- foreignObject.appendChild(div);
626
- embeddableNode.appendChild(foreignObject);
627
- }
628
- addToRoot(embeddableNode, element);
629
- break;
630
- }
631
- case "line":
632
- case "arrow": {
633
- const boundText = getBoundTextElement(element);
634
- const maskPath = svgRoot.ownerDocument.createElementNS(SVG_NS, "mask");
635
- if (boundText) {
636
- maskPath.setAttribute("id", `mask-${element.id}`);
637
- const maskRectVisible = svgRoot.ownerDocument.createElementNS(SVG_NS, "rect");
638
- offsetX = offsetX || 0;
639
- offsetY = offsetY || 0;
640
- maskRectVisible.setAttribute("x", "0");
641
- maskRectVisible.setAttribute("y", "0");
642
- maskRectVisible.setAttribute("fill", "#fff");
643
- maskRectVisible.setAttribute("width", `${element.width + 100 + offsetX}`);
644
- maskRectVisible.setAttribute("height", `${element.height + 100 + offsetY}`);
645
- maskPath.appendChild(maskRectVisible);
646
- const maskRectInvisible = svgRoot.ownerDocument.createElementNS(SVG_NS, "rect");
647
- const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(element, boundText);
648
- const maskX = offsetX + boundTextCoords.x - element.x;
649
- const maskY = offsetY + boundTextCoords.y - element.y;
650
- maskRectInvisible.setAttribute("x", maskX.toString());
651
- maskRectInvisible.setAttribute("y", maskY.toString());
652
- maskRectInvisible.setAttribute("fill", "#000");
653
- maskRectInvisible.setAttribute("width", `${boundText.width}`);
654
- maskRectInvisible.setAttribute("height", `${boundText.height}`);
655
- maskRectInvisible.setAttribute("opacity", "1");
656
- maskPath.appendChild(maskRectInvisible);
657
- }
658
- const group = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
659
- if (boundText) {
660
- group.setAttribute("mask", `url(#mask-${element.id})`);
661
- }
662
- group.setAttribute("stroke-linecap", "round");
663
- const shapes = ShapeCache.generateElementShape(element, renderConfig);
664
- shapes.forEach((shape) => {
665
- const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
666
- if (opacity !== 1) {
667
- node.setAttribute("stroke-opacity", `${opacity}`);
668
- node.setAttribute("fill-opacity", `${opacity}`);
669
- }
670
- node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
671
- if (element.type === "line" &&
672
- isPathALoop(element.points) &&
673
- element.backgroundColor !== "transparent") {
674
- node.setAttribute("fill-rule", "evenodd");
675
- }
676
- group.appendChild(node);
677
- });
678
- const g = maybeWrapNodesInFrameClipPath(element, root, [group, maskPath], renderConfig.frameRendering);
679
- if (g) {
680
- addToRoot(g, element);
681
- root.appendChild(g);
682
- }
683
- else {
684
- addToRoot(group, element);
685
- root.append(maskPath);
686
- }
687
- break;
688
- }
689
- case "freedraw": {
690
- const backgroundFillShape = ShapeCache.generateElementShape(element, renderConfig);
691
- const node = backgroundFillShape
692
- ? roughSVGDrawWithPrecision(rsvg, backgroundFillShape, MAX_DECIMALS_FOR_SVG_EXPORT)
693
- : svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
694
- if (opacity !== 1) {
695
- node.setAttribute("stroke-opacity", `${opacity}`);
696
- node.setAttribute("fill-opacity", `${opacity}`);
697
- }
698
- node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
699
- node.setAttribute("stroke", "none");
700
- const path = svgRoot.ownerDocument.createElementNS(SVG_NS, "path");
701
- path.setAttribute("fill", element.strokeColor);
702
- path.setAttribute("d", getFreeDrawSvgPath(element));
703
- node.appendChild(path);
704
- const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
705
- addToRoot(g || node, element);
706
- break;
707
- }
708
- case "image": {
709
- const width = Math.round(element.width);
710
- const height = Math.round(element.height);
711
- const fileData = isInitializedImageElement(element) && files[element.fileId];
712
- if (fileData) {
713
- const symbolId = `image-${fileData.id}`;
714
- let symbol = svgRoot.querySelector(`#${symbolId}`);
715
- if (!symbol) {
716
- symbol = svgRoot.ownerDocument.createElementNS(SVG_NS, "symbol");
717
- symbol.id = symbolId;
718
- const image = svgRoot.ownerDocument.createElementNS(SVG_NS, "image");
719
- image.setAttribute("width", "100%");
720
- image.setAttribute("height", "100%");
721
- image.setAttribute("href", fileData.dataURL);
722
- symbol.appendChild(image);
723
- root.prepend(symbol);
724
- }
725
- const use = svgRoot.ownerDocument.createElementNS(SVG_NS, "use");
726
- use.setAttribute("href", `#${symbolId}`);
727
- // in dark theme, revert the image color filter
728
- if (renderConfig.exportWithDarkMode &&
729
- fileData.mimeType !== MIME_TYPES.svg) {
730
- use.setAttribute("filter", IMAGE_INVERT_FILTER);
731
- }
732
- use.setAttribute("width", `${width}`);
733
- use.setAttribute("height", `${height}`);
734
- use.setAttribute("opacity", `${opacity}`);
735
- // We first apply `scale` transforms (horizontal/vertical mirroring)
736
- // on the <use> element, then apply translation and rotation
737
- // on the <g> element which wraps the <use>.
738
- // Doing this separately is a quick hack to to work around compositing
739
- // the transformations correctly (the transform-origin was not being
740
- // applied correctly).
741
- if (element.scale[0] !== 1 || element.scale[1] !== 1) {
742
- const translateX = element.scale[0] !== 1 ? -width : 0;
743
- const translateY = element.scale[1] !== 1 ? -height : 0;
744
- use.setAttribute("transform", `scale(${element.scale[0]}, ${element.scale[1]}) translate(${translateX} ${translateY})`);
745
- }
746
- const g = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
747
- g.appendChild(use);
748
- g.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
749
- const clipG = maybeWrapNodesInFrameClipPath(element, root, [g], renderConfig.frameRendering);
750
- addToRoot(clipG || g, element);
751
- }
752
- break;
753
- }
754
- // frames are not rendered and only acts as a container
755
- case "frame":
756
- case "magicframe": {
757
- if (renderConfig.frameRendering.enabled &&
758
- renderConfig.frameRendering.outline) {
759
- const rect = document.createElementNS(SVG_NS, "rect");
760
- rect.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
761
- rect.setAttribute("width", `${element.width}px`);
762
- rect.setAttribute("height", `${element.height}px`);
763
- // Rounded corners
764
- rect.setAttribute("rx", FRAME_STYLE.radius.toString());
765
- rect.setAttribute("ry", FRAME_STYLE.radius.toString());
766
- rect.setAttribute("fill", "none");
767
- rect.setAttribute("stroke", FRAME_STYLE.strokeColor);
768
- rect.setAttribute("stroke-width", FRAME_STYLE.strokeWidth.toString());
769
- addToRoot(rect, element);
770
- }
771
- break;
772
- }
773
- default: {
774
- if (isTextElement(element)) {
775
- const node = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
776
- if (opacity !== 1) {
777
- node.setAttribute("stroke-opacity", `${opacity}`);
778
- node.setAttribute("fill-opacity", `${opacity}`);
779
- }
780
- node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
781
- const lines = element.text.replace(/\r\n?/g, "\n").split("\n");
782
- const lineHeightPx = getLineHeightInPx(element.fontSize, element.lineHeight);
783
- const horizontalOffset = element.textAlign === "center"
784
- ? element.width / 2
785
- : element.textAlign === "right"
786
- ? element.width
787
- : 0;
788
- const direction = isRTL(element.text) ? "rtl" : "ltr";
789
- const textAnchor = element.textAlign === "center"
790
- ? "middle"
791
- : element.textAlign === "right" || direction === "rtl"
792
- ? "end"
793
- : "start";
794
- for (let i = 0; i < lines.length; i++) {
795
- const text = svgRoot.ownerDocument.createElementNS(SVG_NS, "text");
796
- text.textContent = lines[i];
797
- text.setAttribute("x", `${horizontalOffset}`);
798
- text.setAttribute("y", `${i * lineHeightPx}`);
799
- text.setAttribute("font-family", getFontFamilyString(element));
800
- text.setAttribute("font-size", `${element.fontSize}px`);
801
- text.setAttribute("fill", element.strokeColor);
802
- text.setAttribute("text-anchor", textAnchor);
803
- text.setAttribute("style", "white-space: pre;");
804
- text.setAttribute("direction", direction);
805
- text.setAttribute("dominant-baseline", "text-before-edge");
806
- node.appendChild(text);
807
- }
808
- const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
809
- addToRoot(g || node, element);
810
- }
811
- else {
812
- // @ts-ignore
813
- throw new Error(`Unimplemented type ${element.type}`);
814
- }
815
- }
816
- }
504
+ context.globalAlpha = 1;
817
505
  };
818
506
  export const pathsCache = new WeakMap([]);
819
507
  export function generateFreeDrawShape(element) {
@@ -0,0 +1,11 @@
1
+ import { StaticSceneRenderConfig } from "../scene/types";
2
+ /** throttled to animation framerate */
3
+ export declare const renderStaticSceneThrottled: {
4
+ (config: StaticSceneRenderConfig): void;
5
+ flush(): void;
6
+ cancel(): void;
7
+ };
8
+ /**
9
+ * Static scene is the non-ui canvas where we render elements.
10
+ */
11
+ export declare const renderStaticScene: (renderConfig: StaticSceneRenderConfig, throttle?: boolean) => void;