@bigbluebutton/tldraw 2.0.0-alpha.31 → 2.0.0-alpha.32

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.
@@ -129,7 +129,9 @@ class ImageShapeUtil extends import_editor.BaseBoxShapeUtil {
129
129
  {
130
130
  className: "tl-image",
131
131
  style: {
132
- backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`
132
+ backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`,
133
+ // Force hardware acceleration to prevent Safari flickering issue
134
+ transform: this.editor.environment.isSafari ? "translateZ(0)" : void 0
133
135
  },
134
136
  draggable: false
135
137
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/image/ImageShapeUtil.tsx"],
4
- "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tTLImageShape,\n\tTLOnDoubleClickHandler,\n\tTLShapePartial,\n\tVec2d,\n\tdeepCopy,\n\timageShapeMigrations,\n\timageShapeProps,\n\ttoDomPrecision,\n\tuseIsCropping,\n\tuseValue,\n} from '@bigbluebutton/editor'\nimport { useEffect, useState } from 'react'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst loadImage = async (url: string): Promise<HTMLImageElement> => {\n\treturn new Promise((resolve, reject) => {\n\t\tconst image = new Image()\n\t\timage.onload = () => resolve(image)\n\t\timage.onerror = () => reject(new Error('Failed to load image'))\n\t\timage.crossOrigin = 'anonymous'\n\t\timage.src = url\n\t})\n}\n\nconst getStateFrame = async (url: string) => {\n\tconst image = await loadImage(url)\n\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = image.width\n\tcanvas.height = image.height\n\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return\n\n\tctx.drawImage(image, 0, 0)\n\treturn canvas.toDataURL()\n}\n\nasync function getDataURIFromURL(url: string): Promise<string> {\n\tconst response = await fetch(url)\n\tconst blob = await response.blob()\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onloadend = () => resolve(reader.result as string)\n\t\treader.onerror = reject\n\t\treader.readAsDataURL(blob)\n\t})\n}\n\n/** @public */\nexport class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {\n\tstatic override type = 'image' as const\n\tstatic override props = imageShapeProps\n\tstatic override migrations = imageShapeMigrations\n\n\toverride isAspectRatioLocked = () => true\n\toverride canCrop = () => true\n\n\toverride getDefaultProps(): TLImageShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tplaying: true,\n\t\t\turl: '',\n\t\t\tcrop: null,\n\t\t}\n\t}\n\n\tcomponent(shape: TLImageShape) {\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tconst prefersReducedMotion = usePrefersReducedMotion()\n\t\tconst [staticFrameSrc, setStaticFrameSrc] = useState('')\n\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (asset?.type === 'bookmark') {\n\t\t\tthrow Error(\"Bookmark assets can't be rendered as images\")\n\t\t}\n\n\t\tconst isSelected = useValue(\n\t\t\t'onlySelectedShape',\n\t\t\t() => shape.id === this.editor.getOnlySelectedShape()?.id,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst showCropPreview =\n\t\t\tisSelected &&\n\t\t\tisCropping &&\n\t\t\tthis.editor.isInAny('select.crop', 'select.cropping', 'select.pointing_crop_handle')\n\n\t\t// We only want to reduce motion for mimeTypes that have motion\n\t\tconst reduceMotion =\n\t\t\tprefersReducedMotion &&\n\t\t\t(asset?.props.mimeType?.includes('video') || asset?.props.mimeType?.includes('gif'))\n\n\t\tuseEffect(() => {\n\t\t\tif (asset?.props.src && 'mimeType' in asset.props && asset?.props.mimeType === 'image/gif') {\n\t\t\t\tlet cancelled = false\n\t\t\t\tconst run = async () => {\n\t\t\t\t\tconst newStaticFrame = await getStateFrame(asset.props.src!)\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tif (newStaticFrame) {\n\t\t\t\t\t\tsetStaticFrameSrc(newStaticFrame)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun()\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcancelled = true\n\t\t\t\t}\n\t\t\t}\n\t\t}, [prefersReducedMotion, asset?.props])\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{asset?.props.src && showCropPreview && (\n\t\t\t\t\t<div style={containerStyle}>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\topacity: 0.1,\n\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<HTMLContainer\n\t\t\t\t\tid={shape.id}\n\t\t\t\t\tstyle={{ overflow: 'hidden', width: shape.props.w, height: shape.props.h }}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"tl-image-container\" style={containerStyle}>\n\t\t\t\t\t\t{asset?.props.src ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{asset?.props.isAnimated && !shape.props.playing && (\n\t\t\t\t\t\t\t<div className=\"tl-image__tg\">GIF</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{'url' in shape.props && shape.props.url && (\n\t\t\t\t\t\t<HyperlinkButton url={shape.props.url} zoomLevel={this.editor.getZoomLevel()} />\n\t\t\t\t\t)}\n\t\t\t\t</HTMLContainer>\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLImageShape) {\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tif (isCropping) {\n\t\t\treturn null\n\t\t}\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLImageShape) {\n\t\tconst g = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null\n\n\t\tlet src = asset?.props.src || ''\n\t\tif (src && src.startsWith('http')) {\n\t\t\t// If it's a remote image, we need to fetch it and convert it to a data URI\n\t\t\tsrc = (await getDataURIFromURL(src)) || ''\n\t\t}\n\n\t\tconst image = document.createElementNS('http://www.w3.org/2000/svg', 'image')\n\t\timage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src)\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst crop = shape.props.crop\n\t\tif (containerStyle.transform && crop) {\n\t\t\tconst { transform, width, height } = containerStyle\n\t\t\tconst croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width\n\t\t\tconst croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height\n\n\t\t\tconst points = [\n\t\t\t\tnew Vec2d(0, 0),\n\t\t\t\tnew Vec2d(croppedWidth, 0),\n\t\t\t\tnew Vec2d(croppedWidth, croppedHeight),\n\t\t\t\tnew Vec2d(0, croppedHeight),\n\t\t\t]\n\n\t\t\tconst polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\n\t\t\tpolygon.setAttribute('points', points.map((p) => `${p.x},${p.y}`).join(' '))\n\n\t\t\tconst clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')\n\t\t\tclipPath.setAttribute('id', 'cropClipPath')\n\t\t\tclipPath.appendChild(polygon)\n\n\t\t\tconst defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\n\t\t\tdefs.appendChild(clipPath)\n\t\t\tg.appendChild(defs)\n\n\t\t\tconst innerElement = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\t\tinnerElement.setAttribute('clip-path', 'url(#cropClipPath)')\n\t\t\timage.setAttribute('width', width.toString())\n\t\t\timage.setAttribute('height', height.toString())\n\t\t\timage.style.transform = transform\n\t\t\tinnerElement.appendChild(image)\n\t\t\tg.appendChild(innerElement)\n\t\t} else {\n\t\t\timage.setAttribute('width', shape.props.w.toString())\n\t\t\timage.setAttribute('height', shape.props.h.toString())\n\t\t\tg.appendChild(image)\n\t\t}\n\n\t\treturn g\n\t}\n\n\toverride onDoubleClick = (shape: TLImageShape) => {\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (!asset) return\n\n\t\tconst canPlay =\n\t\t\tasset.props.src && 'mimeType' in asset.props && asset.props.mimeType === 'image/gif'\n\n\t\tif (!canPlay) return\n\n\t\tthis.editor.updateShapes([\n\t\t\t{\n\t\t\t\ttype: 'image',\n\t\t\t\tid: shape.id,\n\t\t\t\tprops: {\n\t\t\t\t\tplaying: !shape.props.playing,\n\t\t\t\t},\n\t\t\t},\n\t\t])\n\t}\n\n\toverride onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape> = (shape) => {\n\t\tconst props = shape.props\n\t\tif (!props) return\n\n\t\tif (this.editor.getCroppingShapeId() !== shape.id) {\n\t\t\treturn\n\t\t}\n\n\t\tconst crop = deepCopy(props.crop) || {\n\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t}\n\n\t\t// The true asset dimensions\n\t\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\t\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\t\tconst pointDelta = new Vec2d(crop.topLeft.x * w, crop.topLeft.y * h).rot(shape.rotation)\n\n\t\tconst partial: TLShapePartial<TLImageShape> = {\n\t\t\tid: shape.id,\n\t\t\ttype: shape.type,\n\t\t\tx: shape.x - pointDelta.x,\n\t\t\ty: shape.y - pointDelta.y,\n\t\t\tprops: {\n\t\t\t\tcrop: {\n\t\t\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t\t\t},\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t},\n\t\t}\n\n\t\tthis.editor.updateShapes([partial])\n\t}\n}\n\n/**\n * When an image is cropped we need to translate the image to show the portion withing the cropped\n * area. We do this by translating the image by the negative of the top left corner of the crop\n * area.\n *\n * @param shape - Shape The image shape for which to get the container style\n * @returns - Styles to apply to the image container\n */\nfunction getContainerStyle(shape: TLImageShape) {\n\tconst crop = shape.props.crop\n\tconst topLeft = crop?.topLeft\n\tif (!topLeft) {\n\t\treturn {\n\t\t\twidth: shape.props.w,\n\t\t\theight: shape.props.h,\n\t\t}\n\t}\n\n\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\tconst offsetX = -topLeft.x * w\n\tconst offsetY = -topLeft.y * h\n\treturn {\n\t\ttransform: `translate(${offsetX}px, ${offsetY}px)`,\n\t\twidth: w,\n\t\theight: h,\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tTLImageShape,\n\tTLOnDoubleClickHandler,\n\tTLShapePartial,\n\tVec2d,\n\tdeepCopy,\n\timageShapeMigrations,\n\timageShapeProps,\n\ttoDomPrecision,\n\tuseIsCropping,\n\tuseValue,\n} from '@bigbluebutton/editor'\nimport { useEffect, useState } from 'react'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst loadImage = async (url: string): Promise<HTMLImageElement> => {\n\treturn new Promise((resolve, reject) => {\n\t\tconst image = new Image()\n\t\timage.onload = () => resolve(image)\n\t\timage.onerror = () => reject(new Error('Failed to load image'))\n\t\timage.crossOrigin = 'anonymous'\n\t\timage.src = url\n\t})\n}\n\nconst getStateFrame = async (url: string) => {\n\tconst image = await loadImage(url)\n\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = image.width\n\tcanvas.height = image.height\n\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return\n\n\tctx.drawImage(image, 0, 0)\n\treturn canvas.toDataURL()\n}\n\nasync function getDataURIFromURL(url: string): Promise<string> {\n\tconst response = await fetch(url)\n\tconst blob = await response.blob()\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onloadend = () => resolve(reader.result as string)\n\t\treader.onerror = reject\n\t\treader.readAsDataURL(blob)\n\t})\n}\n\n/** @public */\nexport class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {\n\tstatic override type = 'image' as const\n\tstatic override props = imageShapeProps\n\tstatic override migrations = imageShapeMigrations\n\n\toverride isAspectRatioLocked = () => true\n\toverride canCrop = () => true\n\n\toverride getDefaultProps(): TLImageShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tplaying: true,\n\t\t\turl: '',\n\t\t\tcrop: null,\n\t\t}\n\t}\n\n\tcomponent(shape: TLImageShape) {\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tconst prefersReducedMotion = usePrefersReducedMotion()\n\t\tconst [staticFrameSrc, setStaticFrameSrc] = useState('')\n\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (asset?.type === 'bookmark') {\n\t\t\tthrow Error(\"Bookmark assets can't be rendered as images\")\n\t\t}\n\n\t\tconst isSelected = useValue(\n\t\t\t'onlySelectedShape',\n\t\t\t() => shape.id === this.editor.getOnlySelectedShape()?.id,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst showCropPreview =\n\t\t\tisSelected &&\n\t\t\tisCropping &&\n\t\t\tthis.editor.isInAny('select.crop', 'select.cropping', 'select.pointing_crop_handle')\n\n\t\t// We only want to reduce motion for mimeTypes that have motion\n\t\tconst reduceMotion =\n\t\t\tprefersReducedMotion &&\n\t\t\t(asset?.props.mimeType?.includes('video') || asset?.props.mimeType?.includes('gif'))\n\n\t\tuseEffect(() => {\n\t\t\tif (asset?.props.src && 'mimeType' in asset.props && asset?.props.mimeType === 'image/gif') {\n\t\t\t\tlet cancelled = false\n\t\t\t\tconst run = async () => {\n\t\t\t\t\tconst newStaticFrame = await getStateFrame(asset.props.src!)\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tif (newStaticFrame) {\n\t\t\t\t\t\tsetStaticFrameSrc(newStaticFrame)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun()\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcancelled = true\n\t\t\t\t}\n\t\t\t}\n\t\t}, [prefersReducedMotion, asset?.props])\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{asset?.props.src && showCropPreview && (\n\t\t\t\t\t<div style={containerStyle}>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\topacity: 0.1,\n\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<HTMLContainer\n\t\t\t\t\tid={shape.id}\n\t\t\t\t\tstyle={{ overflow: 'hidden', width: shape.props.w, height: shape.props.h }}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"tl-image-container\" style={containerStyle}>\n\t\t\t\t\t\t{asset?.props.src ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t\t\t// Force hardware acceleration to prevent Safari flickering issue\n\t\t\t\t\t\t\t\t\ttransform: this.editor.environment.isSafari ? 'translateZ(0)' : undefined,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{asset?.props.isAnimated && !shape.props.playing && (\n\t\t\t\t\t\t\t<div className=\"tl-image__tg\">GIF</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{'url' in shape.props && shape.props.url && (\n\t\t\t\t\t\t<HyperlinkButton url={shape.props.url} zoomLevel={this.editor.getZoomLevel()} />\n\t\t\t\t\t)}\n\t\t\t\t</HTMLContainer>\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLImageShape) {\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tif (isCropping) {\n\t\t\treturn null\n\t\t}\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLImageShape) {\n\t\tconst g = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null\n\n\t\tlet src = asset?.props.src || ''\n\t\tif (src && src.startsWith('http')) {\n\t\t\t// If it's a remote image, we need to fetch it and convert it to a data URI\n\t\t\tsrc = (await getDataURIFromURL(src)) || ''\n\t\t}\n\n\t\tconst image = document.createElementNS('http://www.w3.org/2000/svg', 'image')\n\t\timage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src)\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst crop = shape.props.crop\n\t\tif (containerStyle.transform && crop) {\n\t\t\tconst { transform, width, height } = containerStyle\n\t\t\tconst croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width\n\t\t\tconst croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height\n\n\t\t\tconst points = [\n\t\t\t\tnew Vec2d(0, 0),\n\t\t\t\tnew Vec2d(croppedWidth, 0),\n\t\t\t\tnew Vec2d(croppedWidth, croppedHeight),\n\t\t\t\tnew Vec2d(0, croppedHeight),\n\t\t\t]\n\n\t\t\tconst polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\n\t\t\tpolygon.setAttribute('points', points.map((p) => `${p.x},${p.y}`).join(' '))\n\n\t\t\tconst clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')\n\t\t\tclipPath.setAttribute('id', 'cropClipPath')\n\t\t\tclipPath.appendChild(polygon)\n\n\t\t\tconst defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\n\t\t\tdefs.appendChild(clipPath)\n\t\t\tg.appendChild(defs)\n\n\t\t\tconst innerElement = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\t\tinnerElement.setAttribute('clip-path', 'url(#cropClipPath)')\n\t\t\timage.setAttribute('width', width.toString())\n\t\t\timage.setAttribute('height', height.toString())\n\t\t\timage.style.transform = transform\n\t\t\tinnerElement.appendChild(image)\n\t\t\tg.appendChild(innerElement)\n\t\t} else {\n\t\t\timage.setAttribute('width', shape.props.w.toString())\n\t\t\timage.setAttribute('height', shape.props.h.toString())\n\t\t\tg.appendChild(image)\n\t\t}\n\n\t\treturn g\n\t}\n\n\toverride onDoubleClick = (shape: TLImageShape) => {\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (!asset) return\n\n\t\tconst canPlay =\n\t\t\tasset.props.src && 'mimeType' in asset.props && asset.props.mimeType === 'image/gif'\n\n\t\tif (!canPlay) return\n\n\t\tthis.editor.updateShapes([\n\t\t\t{\n\t\t\t\ttype: 'image',\n\t\t\t\tid: shape.id,\n\t\t\t\tprops: {\n\t\t\t\t\tplaying: !shape.props.playing,\n\t\t\t\t},\n\t\t\t},\n\t\t])\n\t}\n\n\toverride onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape> = (shape) => {\n\t\tconst props = shape.props\n\t\tif (!props) return\n\n\t\tif (this.editor.getCroppingShapeId() !== shape.id) {\n\t\t\treturn\n\t\t}\n\n\t\tconst crop = deepCopy(props.crop) || {\n\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t}\n\n\t\t// The true asset dimensions\n\t\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\t\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\t\tconst pointDelta = new Vec2d(crop.topLeft.x * w, crop.topLeft.y * h).rot(shape.rotation)\n\n\t\tconst partial: TLShapePartial<TLImageShape> = {\n\t\t\tid: shape.id,\n\t\t\ttype: shape.type,\n\t\t\tx: shape.x - pointDelta.x,\n\t\t\ty: shape.y - pointDelta.y,\n\t\t\tprops: {\n\t\t\t\tcrop: {\n\t\t\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t\t\t},\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t},\n\t\t}\n\n\t\tthis.editor.updateShapes([partial])\n\t}\n}\n\n/**\n * When an image is cropped we need to translate the image to show the portion withing the cropped\n * area. We do this by translating the image by the negative of the top left corner of the crop\n * area.\n *\n * @param shape - Shape The image shape for which to get the container style\n * @returns - Styles to apply to the image container\n */\nfunction getContainerStyle(shape: TLImageShape) {\n\tconst crop = shape.props.crop\n\tconst topLeft = crop?.topLeft\n\tif (!topLeft) {\n\t\treturn {\n\t\t\twidth: shape.props.w,\n\t\t\theight: shape.props.h,\n\t\t}\n\t}\n\n\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\tconst offsetX = -topLeft.x * w\n\tconst offsetY = -topLeft.y * h\n\treturn {\n\t\ttransform: `translate(${offsetX}px, ${offsetY}px)`,\n\t\twidth: w,\n\t\theight: h,\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyHG;AAxHH,oBAaO;AACP,mBAAoC;AACpC,6BAAgC;AAChC,qCAAwC;AAExC,MAAM,YAAY,OAAO,QAA2C;AACnE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,UAAM,QAAQ,IAAI,MAAM;AACxB,UAAM,SAAS,MAAM,QAAQ,KAAK;AAClC,UAAM,UAAU,MAAM,OAAO,IAAI,MAAM,sBAAsB,CAAC;AAC9D,UAAM,cAAc;AACpB,UAAM,MAAM;AAAA,EACb,CAAC;AACF;AAEA,MAAM,gBAAgB,OAAO,QAAgB;AAC5C,QAAM,QAAQ,MAAM,UAAU,GAAG;AAEjC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ,MAAM;AACrB,SAAO,SAAS,MAAM;AAEtB,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,CAAC;AAAK;AAEV,MAAI,UAAU,OAAO,GAAG,CAAC;AACzB,SAAO,OAAO,UAAU;AACzB;AAEA,eAAe,kBAAkB,KAA8B;AAC9D,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,YAAY,MAAM,QAAQ,OAAO,MAAgB;AACxD,WAAO,UAAU;AACjB,WAAO,cAAc,IAAI;AAAA,EAC1B,CAAC;AACF;AAGO,MAAM,uBAAuB,+BAA+B;AAAA,EAClE,OAAgB,OAAO;AAAA,EACvB,OAAgB,QAAQ;AAAA,EACxB,OAAgB,aAAa;AAAA,EAEpB,sBAAsB,MAAM;AAAA,EAC5B,UAAU,MAAM;AAAA,EAEhB,kBAAyC;AACjD,WAAO;AAAA,MACN,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,IACP;AAAA,EACD;AAAA,EAEA,UAAU,OAAqB;AAC9B,UAAM,iBAAiB,kBAAkB,KAAK;AAC9C,UAAM,iBAAa,6BAAc,MAAM,EAAE;AACzC,UAAM,2BAAuB,wDAAwB;AACrD,UAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAS,EAAE;AAEvD,UAAM,QAAQ,MAAM,MAAM,UAAU,KAAK,OAAO,SAAS,MAAM,MAAM,OAAO,IAAI;AAEhF,QAAI,OAAO,SAAS,YAAY;AAC/B,YAAM,MAAM,6CAA6C;AAAA,IAC1D;AAEA,UAAM,iBAAa;AAAA,MAClB;AAAA,MACA,MAAM,MAAM,OAAO,KAAK,OAAO,qBAAqB,GAAG;AAAA,MACvD,CAAC,KAAK,MAAM;AAAA,IACb;AAEA,UAAM,kBACL,cACA,cACA,KAAK,OAAO,QAAQ,eAAe,mBAAmB,6BAA6B;AAGpF,UAAM,eACL,yBACC,OAAO,MAAM,UAAU,SAAS,OAAO,KAAK,OAAO,MAAM,UAAU,SAAS,KAAK;AAEnF,gCAAU,MAAM;AACf,UAAI,OAAO,MAAM,OAAO,cAAc,MAAM,SAAS,OAAO,MAAM,aAAa,aAAa;AAC3F,YAAI,YAAY;AAChB,cAAM,MAAM,YAAY;AACvB,gBAAM,iBAAiB,MAAM,cAAc,MAAM,MAAM,GAAI;AAC3D,cAAI;AAAW;AACf,cAAI,gBAAgB;AACnB,8BAAkB,cAAc;AAAA,UACjC;AAAA,QACD;AACA,YAAI;AAEJ,eAAO,MAAM;AACZ,sBAAY;AAAA,QACb;AAAA,MACD;AAAA,IACD,GAAG,CAAC,sBAAsB,OAAO,KAAK,CAAC;AAEvC,WACC,4EACE;AAAA,aAAO,MAAM,OAAO,mBACpB,4CAAC,SAAI,OAAO,gBACX;AAAA,QAAC;AAAA;AAAA,UACA,WAAU;AAAA,UACV,OAAO;AAAA,YACN,SAAS;AAAA,YACT,iBAAiB,OAChB,CAAC,MAAM,MAAM,WAAW,eAAe,iBAAiB,MAAM,MAAM,GACrE;AAAA,UACD;AAAA,UACA,WAAW;AAAA;AAAA,MACZ,GACD;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,IAAI,MAAM;AAAA,UACV,OAAO,EAAE,UAAU,UAAU,OAAO,MAAM,MAAM,GAAG,QAAQ,MAAM,MAAM,EAAE;AAAA,UAEzE;AAAA,yDAAC,SAAI,WAAU,sBAAqB,OAAO,gBACzC;AAAA,qBAAO,MAAM,MACb;AAAA,gBAAC;AAAA;AAAA,kBACA,WAAU;AAAA,kBACV,OAAO;AAAA,oBACN,iBAAiB,OAChB,CAAC,MAAM,MAAM,WAAW,eAAe,iBAAiB,MAAM,MAAM,GACrE;AAAA;AAAA,oBAEA,WAAW,KAAK,OAAO,YAAY,WAAW,kBAAkB;AAAA,kBACjE;AAAA,kBACA,WAAW;AAAA;AAAA,cACZ,IACG;AAAA,cACH,OAAO,MAAM,cAAc,CAAC,MAAM,MAAM,WACxC,4CAAC,SAAI,WAAU,gBAAe,iBAAG;AAAA,eAEnC;AAAA,YACC,SAAS,MAAM,SAAS,MAAM,MAAM,OACpC,4CAAC,0CAAgB,KAAK,MAAM,MAAM,KAAK,WAAW,KAAK,OAAO,aAAa,GAAG;AAAA;AAAA;AAAA,MAEhF;AAAA,OACD;AAAA,EAEF;AAAA,EAEA,UAAU,OAAqB;AAC9B,UAAM,iBAAa,6BAAc,MAAM,EAAE;AACzC,QAAI,YAAY;AACf,aAAO;AAAA,IACR;AACA,WAAO,4CAAC,UAAK,WAAO,8BAAe,MAAM,MAAM,CAAC,GAAG,YAAQ,8BAAe,MAAM,MAAM,CAAC,GAAG;AAAA,EAC3F;AAAA,EAEA,MAAe,MAAM,OAAqB;AACzC,UAAM,IAAI,SAAS,gBAAgB,8BAA8B,GAAG;AACpE,UAAM,QAAQ,MAAM,MAAM,UAAU,KAAK,OAAO,SAAS,MAAM,MAAM,OAAO,IAAI;AAEhF,QAAI,MAAM,OAAO,MAAM,OAAO;AAC9B,QAAI,OAAO,IAAI,WAAW,MAAM,GAAG;AAElC,YAAO,MAAM,kBAAkB,GAAG,KAAM;AAAA,IACzC;AAEA,UAAM,QAAQ,SAAS,gBAAgB,8BAA8B,OAAO;AAC5E,UAAM,eAAe,gCAAgC,QAAQ,GAAG;AAChE,UAAM,iBAAiB,kBAAkB,KAAK;AAC9C,UAAM,OAAO,MAAM,MAAM;AACzB,QAAI,eAAe,aAAa,MAAM;AACrC,YAAM,EAAE,WAAW,OAAO,OAAO,IAAI;AACrC,YAAM,gBAAgB,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAK;AAC7D,YAAM,iBAAiB,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAK;AAE9D,YAAM,SAAS;AAAA,QACd,IAAI,oBAAM,GAAG,CAAC;AAAA,QACd,IAAI,oBAAM,cAAc,CAAC;AAAA,QACzB,IAAI,oBAAM,cAAc,aAAa;AAAA,QACrC,IAAI,oBAAM,GAAG,aAAa;AAAA,MAC3B;AAEA,YAAM,UAAU,SAAS,gBAAgB,8BAA8B,SAAS;AAChF,cAAQ,aAAa,UAAU,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AAE3E,YAAM,WAAW,SAAS,gBAAgB,8BAA8B,UAAU;AAClF,eAAS,aAAa,MAAM,cAAc;AAC1C,eAAS,YAAY,OAAO;AAE5B,YAAM,OAAO,SAAS,gBAAgB,8BAA8B,MAAM;AAC1E,WAAK,YAAY,QAAQ;AACzB,QAAE,YAAY,IAAI;AAElB,YAAM,eAAe,SAAS,gBAAgB,8BAA8B,GAAG;AAC/E,mBAAa,aAAa,aAAa,oBAAoB;AAC3D,YAAM,aAAa,SAAS,MAAM,SAAS,CAAC;AAC5C,YAAM,aAAa,UAAU,OAAO,SAAS,CAAC;AAC9C,YAAM,MAAM,YAAY;AACxB,mBAAa,YAAY,KAAK;AAC9B,QAAE,YAAY,YAAY;AAAA,IAC3B,OAAO;AACN,YAAM,aAAa,SAAS,MAAM,MAAM,EAAE,SAAS,CAAC;AACpD,YAAM,aAAa,UAAU,MAAM,MAAM,EAAE,SAAS,CAAC;AACrD,QAAE,YAAY,KAAK;AAAA,IACpB;AAEA,WAAO;AAAA,EACR;AAAA,EAES,gBAAgB,CAAC,UAAwB;AACjD,UAAM,QAAQ,MAAM,MAAM,UAAU,KAAK,OAAO,SAAS,MAAM,MAAM,OAAO,IAAI;AAEhF,QAAI,CAAC;AAAO;AAEZ,UAAM,UACL,MAAM,MAAM,OAAO,cAAc,MAAM,SAAS,MAAM,MAAM,aAAa;AAE1E,QAAI,CAAC;AAAS;AAEd,SAAK,OAAO,aAAa;AAAA,MACxB;AAAA,QACC,MAAM;AAAA,QACN,IAAI,MAAM;AAAA,QACV,OAAO;AAAA,UACN,SAAS,CAAC,MAAM,MAAM;AAAA,QACvB;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAES,oBAA0D,CAAC,UAAU;AAC7E,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC;AAAO;AAEZ,QAAI,KAAK,OAAO,mBAAmB,MAAM,MAAM,IAAI;AAClD;AAAA,IACD;AAEA,UAAM,WAAO,wBAAS,MAAM,IAAI,KAAK;AAAA,MACpC,SAAS,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,MACtB,aAAa,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,IAC3B;AAGA,UAAM,IAAK,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAM,MAAM,MAAM;AACpE,UAAM,IAAK,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAM,MAAM,MAAM;AAEpE,UAAM,aAAa,IAAI,oBAAM,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,EAAE,IAAI,MAAM,QAAQ;AAEvF,UAAM,UAAwC;AAAA,MAC7C,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,MACZ,GAAG,MAAM,IAAI,WAAW;AAAA,MACxB,GAAG,MAAM,IAAI,WAAW;AAAA,MACxB,OAAO;AAAA,QACN,MAAM;AAAA,UACL,SAAS,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,UACtB,aAAa,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,SAAK,OAAO,aAAa,CAAC,OAAO,CAAC;AAAA,EACnC;AACD;AAUA,SAAS,kBAAkB,OAAqB;AAC/C,QAAM,OAAO,MAAM,MAAM;AACzB,QAAM,UAAU,MAAM;AACtB,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,MACN,OAAO,MAAM,MAAM;AAAA,MACnB,QAAQ,MAAM,MAAM;AAAA,IACrB;AAAA,EACD;AAEA,QAAM,IAAK,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAM,MAAM,MAAM;AACpE,QAAM,IAAK,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAM,MAAM,MAAM;AAEpE,QAAM,UAAU,CAAC,QAAQ,IAAI;AAC7B,QAAM,UAAU,CAAC,QAAQ,IAAI;AAC7B,SAAO;AAAA,IACN,WAAW,aAAa,OAAO,OAAO,OAAO;AAAA,IAC7C,OAAO;AAAA,IACP,QAAQ;AAAA,EACT;AACD;",
6
6
  "names": []
7
7
  }
@@ -116,7 +116,9 @@ class ImageShapeUtil extends BaseBoxShapeUtil {
116
116
  {
117
117
  className: "tl-image",
118
118
  style: {
119
- backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`
119
+ backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`,
120
+ // Force hardware acceleration to prevent Safari flickering issue
121
+ transform: this.editor.environment.isSafari ? "translateZ(0)" : void 0
120
122
  },
121
123
  draggable: false
122
124
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/image/ImageShapeUtil.tsx"],
4
- "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tTLImageShape,\n\tTLOnDoubleClickHandler,\n\tTLShapePartial,\n\tVec2d,\n\tdeepCopy,\n\timageShapeMigrations,\n\timageShapeProps,\n\ttoDomPrecision,\n\tuseIsCropping,\n\tuseValue,\n} from '@bigbluebutton/editor'\nimport { useEffect, useState } from 'react'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst loadImage = async (url: string): Promise<HTMLImageElement> => {\n\treturn new Promise((resolve, reject) => {\n\t\tconst image = new Image()\n\t\timage.onload = () => resolve(image)\n\t\timage.onerror = () => reject(new Error('Failed to load image'))\n\t\timage.crossOrigin = 'anonymous'\n\t\timage.src = url\n\t})\n}\n\nconst getStateFrame = async (url: string) => {\n\tconst image = await loadImage(url)\n\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = image.width\n\tcanvas.height = image.height\n\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return\n\n\tctx.drawImage(image, 0, 0)\n\treturn canvas.toDataURL()\n}\n\nasync function getDataURIFromURL(url: string): Promise<string> {\n\tconst response = await fetch(url)\n\tconst blob = await response.blob()\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onloadend = () => resolve(reader.result as string)\n\t\treader.onerror = reject\n\t\treader.readAsDataURL(blob)\n\t})\n}\n\n/** @public */\nexport class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {\n\tstatic override type = 'image' as const\n\tstatic override props = imageShapeProps\n\tstatic override migrations = imageShapeMigrations\n\n\toverride isAspectRatioLocked = () => true\n\toverride canCrop = () => true\n\n\toverride getDefaultProps(): TLImageShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tplaying: true,\n\t\t\turl: '',\n\t\t\tcrop: null,\n\t\t}\n\t}\n\n\tcomponent(shape: TLImageShape) {\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tconst prefersReducedMotion = usePrefersReducedMotion()\n\t\tconst [staticFrameSrc, setStaticFrameSrc] = useState('')\n\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (asset?.type === 'bookmark') {\n\t\t\tthrow Error(\"Bookmark assets can't be rendered as images\")\n\t\t}\n\n\t\tconst isSelected = useValue(\n\t\t\t'onlySelectedShape',\n\t\t\t() => shape.id === this.editor.getOnlySelectedShape()?.id,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst showCropPreview =\n\t\t\tisSelected &&\n\t\t\tisCropping &&\n\t\t\tthis.editor.isInAny('select.crop', 'select.cropping', 'select.pointing_crop_handle')\n\n\t\t// We only want to reduce motion for mimeTypes that have motion\n\t\tconst reduceMotion =\n\t\t\tprefersReducedMotion &&\n\t\t\t(asset?.props.mimeType?.includes('video') || asset?.props.mimeType?.includes('gif'))\n\n\t\tuseEffect(() => {\n\t\t\tif (asset?.props.src && 'mimeType' in asset.props && asset?.props.mimeType === 'image/gif') {\n\t\t\t\tlet cancelled = false\n\t\t\t\tconst run = async () => {\n\t\t\t\t\tconst newStaticFrame = await getStateFrame(asset.props.src!)\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tif (newStaticFrame) {\n\t\t\t\t\t\tsetStaticFrameSrc(newStaticFrame)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun()\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcancelled = true\n\t\t\t\t}\n\t\t\t}\n\t\t}, [prefersReducedMotion, asset?.props])\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{asset?.props.src && showCropPreview && (\n\t\t\t\t\t<div style={containerStyle}>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\topacity: 0.1,\n\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<HTMLContainer\n\t\t\t\t\tid={shape.id}\n\t\t\t\t\tstyle={{ overflow: 'hidden', width: shape.props.w, height: shape.props.h }}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"tl-image-container\" style={containerStyle}>\n\t\t\t\t\t\t{asset?.props.src ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{asset?.props.isAnimated && !shape.props.playing && (\n\t\t\t\t\t\t\t<div className=\"tl-image__tg\">GIF</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{'url' in shape.props && shape.props.url && (\n\t\t\t\t\t\t<HyperlinkButton url={shape.props.url} zoomLevel={this.editor.getZoomLevel()} />\n\t\t\t\t\t)}\n\t\t\t\t</HTMLContainer>\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLImageShape) {\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tif (isCropping) {\n\t\t\treturn null\n\t\t}\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLImageShape) {\n\t\tconst g = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null\n\n\t\tlet src = asset?.props.src || ''\n\t\tif (src && src.startsWith('http')) {\n\t\t\t// If it's a remote image, we need to fetch it and convert it to a data URI\n\t\t\tsrc = (await getDataURIFromURL(src)) || ''\n\t\t}\n\n\t\tconst image = document.createElementNS('http://www.w3.org/2000/svg', 'image')\n\t\timage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src)\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst crop = shape.props.crop\n\t\tif (containerStyle.transform && crop) {\n\t\t\tconst { transform, width, height } = containerStyle\n\t\t\tconst croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width\n\t\t\tconst croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height\n\n\t\t\tconst points = [\n\t\t\t\tnew Vec2d(0, 0),\n\t\t\t\tnew Vec2d(croppedWidth, 0),\n\t\t\t\tnew Vec2d(croppedWidth, croppedHeight),\n\t\t\t\tnew Vec2d(0, croppedHeight),\n\t\t\t]\n\n\t\t\tconst polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\n\t\t\tpolygon.setAttribute('points', points.map((p) => `${p.x},${p.y}`).join(' '))\n\n\t\t\tconst clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')\n\t\t\tclipPath.setAttribute('id', 'cropClipPath')\n\t\t\tclipPath.appendChild(polygon)\n\n\t\t\tconst defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\n\t\t\tdefs.appendChild(clipPath)\n\t\t\tg.appendChild(defs)\n\n\t\t\tconst innerElement = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\t\tinnerElement.setAttribute('clip-path', 'url(#cropClipPath)')\n\t\t\timage.setAttribute('width', width.toString())\n\t\t\timage.setAttribute('height', height.toString())\n\t\t\timage.style.transform = transform\n\t\t\tinnerElement.appendChild(image)\n\t\t\tg.appendChild(innerElement)\n\t\t} else {\n\t\t\timage.setAttribute('width', shape.props.w.toString())\n\t\t\timage.setAttribute('height', shape.props.h.toString())\n\t\t\tg.appendChild(image)\n\t\t}\n\n\t\treturn g\n\t}\n\n\toverride onDoubleClick = (shape: TLImageShape) => {\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (!asset) return\n\n\t\tconst canPlay =\n\t\t\tasset.props.src && 'mimeType' in asset.props && asset.props.mimeType === 'image/gif'\n\n\t\tif (!canPlay) return\n\n\t\tthis.editor.updateShapes([\n\t\t\t{\n\t\t\t\ttype: 'image',\n\t\t\t\tid: shape.id,\n\t\t\t\tprops: {\n\t\t\t\t\tplaying: !shape.props.playing,\n\t\t\t\t},\n\t\t\t},\n\t\t])\n\t}\n\n\toverride onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape> = (shape) => {\n\t\tconst props = shape.props\n\t\tif (!props) return\n\n\t\tif (this.editor.getCroppingShapeId() !== shape.id) {\n\t\t\treturn\n\t\t}\n\n\t\tconst crop = deepCopy(props.crop) || {\n\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t}\n\n\t\t// The true asset dimensions\n\t\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\t\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\t\tconst pointDelta = new Vec2d(crop.topLeft.x * w, crop.topLeft.y * h).rot(shape.rotation)\n\n\t\tconst partial: TLShapePartial<TLImageShape> = {\n\t\t\tid: shape.id,\n\t\t\ttype: shape.type,\n\t\t\tx: shape.x - pointDelta.x,\n\t\t\ty: shape.y - pointDelta.y,\n\t\t\tprops: {\n\t\t\t\tcrop: {\n\t\t\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t\t\t},\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t},\n\t\t}\n\n\t\tthis.editor.updateShapes([partial])\n\t}\n}\n\n/**\n * When an image is cropped we need to translate the image to show the portion withing the cropped\n * area. We do this by translating the image by the negative of the top left corner of the crop\n * area.\n *\n * @param shape - Shape The image shape for which to get the container style\n * @returns - Styles to apply to the image container\n */\nfunction getContainerStyle(shape: TLImageShape) {\n\tconst crop = shape.props.crop\n\tconst topLeft = crop?.topLeft\n\tif (!topLeft) {\n\t\treturn {\n\t\t\twidth: shape.props.w,\n\t\t\theight: shape.props.h,\n\t\t}\n\t}\n\n\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\tconst offsetX = -topLeft.x * w\n\tconst offsetY = -topLeft.y * h\n\treturn {\n\t\ttransform: `translate(${offsetX}px, ${offsetY}px)`,\n\t\twidth: w,\n\t\theight: h,\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tTLImageShape,\n\tTLOnDoubleClickHandler,\n\tTLShapePartial,\n\tVec2d,\n\tdeepCopy,\n\timageShapeMigrations,\n\timageShapeProps,\n\ttoDomPrecision,\n\tuseIsCropping,\n\tuseValue,\n} from '@bigbluebutton/editor'\nimport { useEffect, useState } from 'react'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst loadImage = async (url: string): Promise<HTMLImageElement> => {\n\treturn new Promise((resolve, reject) => {\n\t\tconst image = new Image()\n\t\timage.onload = () => resolve(image)\n\t\timage.onerror = () => reject(new Error('Failed to load image'))\n\t\timage.crossOrigin = 'anonymous'\n\t\timage.src = url\n\t})\n}\n\nconst getStateFrame = async (url: string) => {\n\tconst image = await loadImage(url)\n\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = image.width\n\tcanvas.height = image.height\n\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return\n\n\tctx.drawImage(image, 0, 0)\n\treturn canvas.toDataURL()\n}\n\nasync function getDataURIFromURL(url: string): Promise<string> {\n\tconst response = await fetch(url)\n\tconst blob = await response.blob()\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onloadend = () => resolve(reader.result as string)\n\t\treader.onerror = reject\n\t\treader.readAsDataURL(blob)\n\t})\n}\n\n/** @public */\nexport class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {\n\tstatic override type = 'image' as const\n\tstatic override props = imageShapeProps\n\tstatic override migrations = imageShapeMigrations\n\n\toverride isAspectRatioLocked = () => true\n\toverride canCrop = () => true\n\n\toverride getDefaultProps(): TLImageShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tplaying: true,\n\t\t\turl: '',\n\t\t\tcrop: null,\n\t\t}\n\t}\n\n\tcomponent(shape: TLImageShape) {\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tconst prefersReducedMotion = usePrefersReducedMotion()\n\t\tconst [staticFrameSrc, setStaticFrameSrc] = useState('')\n\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (asset?.type === 'bookmark') {\n\t\t\tthrow Error(\"Bookmark assets can't be rendered as images\")\n\t\t}\n\n\t\tconst isSelected = useValue(\n\t\t\t'onlySelectedShape',\n\t\t\t() => shape.id === this.editor.getOnlySelectedShape()?.id,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst showCropPreview =\n\t\t\tisSelected &&\n\t\t\tisCropping &&\n\t\t\tthis.editor.isInAny('select.crop', 'select.cropping', 'select.pointing_crop_handle')\n\n\t\t// We only want to reduce motion for mimeTypes that have motion\n\t\tconst reduceMotion =\n\t\t\tprefersReducedMotion &&\n\t\t\t(asset?.props.mimeType?.includes('video') || asset?.props.mimeType?.includes('gif'))\n\n\t\tuseEffect(() => {\n\t\t\tif (asset?.props.src && 'mimeType' in asset.props && asset?.props.mimeType === 'image/gif') {\n\t\t\t\tlet cancelled = false\n\t\t\t\tconst run = async () => {\n\t\t\t\t\tconst newStaticFrame = await getStateFrame(asset.props.src!)\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tif (newStaticFrame) {\n\t\t\t\t\t\tsetStaticFrameSrc(newStaticFrame)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun()\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcancelled = true\n\t\t\t\t}\n\t\t\t}\n\t\t}, [prefersReducedMotion, asset?.props])\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{asset?.props.src && showCropPreview && (\n\t\t\t\t\t<div style={containerStyle}>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\topacity: 0.1,\n\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<HTMLContainer\n\t\t\t\t\tid={shape.id}\n\t\t\t\t\tstyle={{ overflow: 'hidden', width: shape.props.w, height: shape.props.h }}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"tl-image-container\" style={containerStyle}>\n\t\t\t\t\t\t{asset?.props.src ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"tl-image\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackgroundImage: `url(${\n\t\t\t\t\t\t\t\t\t\t!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src\n\t\t\t\t\t\t\t\t\t})`,\n\t\t\t\t\t\t\t\t\t// Force hardware acceleration to prevent Safari flickering issue\n\t\t\t\t\t\t\t\t\ttransform: this.editor.environment.isSafari ? 'translateZ(0)' : undefined,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{asset?.props.isAnimated && !shape.props.playing && (\n\t\t\t\t\t\t\t<div className=\"tl-image__tg\">GIF</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{'url' in shape.props && shape.props.url && (\n\t\t\t\t\t\t<HyperlinkButton url={shape.props.url} zoomLevel={this.editor.getZoomLevel()} />\n\t\t\t\t\t)}\n\t\t\t\t</HTMLContainer>\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLImageShape) {\n\t\tconst isCropping = useIsCropping(shape.id)\n\t\tif (isCropping) {\n\t\t\treturn null\n\t\t}\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLImageShape) {\n\t\tconst g = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null\n\n\t\tlet src = asset?.props.src || ''\n\t\tif (src && src.startsWith('http')) {\n\t\t\t// If it's a remote image, we need to fetch it and convert it to a data URI\n\t\t\tsrc = (await getDataURIFromURL(src)) || ''\n\t\t}\n\n\t\tconst image = document.createElementNS('http://www.w3.org/2000/svg', 'image')\n\t\timage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src)\n\t\tconst containerStyle = getContainerStyle(shape)\n\t\tconst crop = shape.props.crop\n\t\tif (containerStyle.transform && crop) {\n\t\t\tconst { transform, width, height } = containerStyle\n\t\t\tconst croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width\n\t\t\tconst croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height\n\n\t\t\tconst points = [\n\t\t\t\tnew Vec2d(0, 0),\n\t\t\t\tnew Vec2d(croppedWidth, 0),\n\t\t\t\tnew Vec2d(croppedWidth, croppedHeight),\n\t\t\t\tnew Vec2d(0, croppedHeight),\n\t\t\t]\n\n\t\t\tconst polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\n\t\t\tpolygon.setAttribute('points', points.map((p) => `${p.x},${p.y}`).join(' '))\n\n\t\t\tconst clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')\n\t\t\tclipPath.setAttribute('id', 'cropClipPath')\n\t\t\tclipPath.appendChild(polygon)\n\n\t\t\tconst defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\n\t\t\tdefs.appendChild(clipPath)\n\t\t\tg.appendChild(defs)\n\n\t\t\tconst innerElement = document.createElementNS('http://www.w3.org/2000/svg', 'g')\n\t\t\tinnerElement.setAttribute('clip-path', 'url(#cropClipPath)')\n\t\t\timage.setAttribute('width', width.toString())\n\t\t\timage.setAttribute('height', height.toString())\n\t\t\timage.style.transform = transform\n\t\t\tinnerElement.appendChild(image)\n\t\t\tg.appendChild(innerElement)\n\t\t} else {\n\t\t\timage.setAttribute('width', shape.props.w.toString())\n\t\t\timage.setAttribute('height', shape.props.h.toString())\n\t\t\tg.appendChild(image)\n\t\t}\n\n\t\treturn g\n\t}\n\n\toverride onDoubleClick = (shape: TLImageShape) => {\n\t\tconst asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : undefined\n\n\t\tif (!asset) return\n\n\t\tconst canPlay =\n\t\t\tasset.props.src && 'mimeType' in asset.props && asset.props.mimeType === 'image/gif'\n\n\t\tif (!canPlay) return\n\n\t\tthis.editor.updateShapes([\n\t\t\t{\n\t\t\t\ttype: 'image',\n\t\t\t\tid: shape.id,\n\t\t\t\tprops: {\n\t\t\t\t\tplaying: !shape.props.playing,\n\t\t\t\t},\n\t\t\t},\n\t\t])\n\t}\n\n\toverride onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape> = (shape) => {\n\t\tconst props = shape.props\n\t\tif (!props) return\n\n\t\tif (this.editor.getCroppingShapeId() !== shape.id) {\n\t\t\treturn\n\t\t}\n\n\t\tconst crop = deepCopy(props.crop) || {\n\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t}\n\n\t\t// The true asset dimensions\n\t\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\t\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\t\tconst pointDelta = new Vec2d(crop.topLeft.x * w, crop.topLeft.y * h).rot(shape.rotation)\n\n\t\tconst partial: TLShapePartial<TLImageShape> = {\n\t\t\tid: shape.id,\n\t\t\ttype: shape.type,\n\t\t\tx: shape.x - pointDelta.x,\n\t\t\ty: shape.y - pointDelta.y,\n\t\t\tprops: {\n\t\t\t\tcrop: {\n\t\t\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t\t\t},\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t},\n\t\t}\n\n\t\tthis.editor.updateShapes([partial])\n\t}\n}\n\n/**\n * When an image is cropped we need to translate the image to show the portion withing the cropped\n * area. We do this by translating the image by the negative of the top left corner of the crop\n * area.\n *\n * @param shape - Shape The image shape for which to get the container style\n * @returns - Styles to apply to the image container\n */\nfunction getContainerStyle(shape: TLImageShape) {\n\tconst crop = shape.props.crop\n\tconst topLeft = crop?.topLeft\n\tif (!topLeft) {\n\t\treturn {\n\t\t\twidth: shape.props.w,\n\t\t\theight: shape.props.h,\n\t\t}\n\t}\n\n\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w\n\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h\n\n\tconst offsetX = -topLeft.x * w\n\tconst offsetY = -topLeft.y * h\n\treturn {\n\t\ttransform: `translate(${offsetX}px, ${offsetY}px)`,\n\t\twidth: w,\n\t\theight: h,\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bigbluebutton/tldraw",
3
3
  "description": "BigBlueButton's fork of tldraw 2.0-alpha.19 - A tiny little drawing editor.",
4
- "version": "2.0.0-alpha.31",
4
+ "version": "2.0.0-alpha.32",
5
5
  "packageManager": "yarn@3.5.0",
6
6
  "author": {
7
7
  "name": "tldraw GB Ltd.",
@@ -35,7 +35,7 @@
35
35
  "src"
36
36
  ],
37
37
  "dependencies": {
38
- "@bigbluebutton/editor": "2.0.0-alpha.31",
38
+ "@bigbluebutton/editor": "2.0.0-alpha.32",
39
39
  "@radix-ui/react-alert-dialog": "^1.0.0",
40
40
  "@radix-ui/react-context-menu": "^2.1.5",
41
41
  "@radix-ui/react-dialog": "^1.0.5",
@@ -146,6 +146,8 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
146
146
  backgroundImage: `url(${
147
147
  !shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src
148
148
  })`,
149
+ // Force hardware acceleration to prevent Safari flickering issue
150
+ transform: this.editor.environment.isSafari ? 'translateZ(0)' : undefined,
149
151
  }}
150
152
  draggable={false}
151
153
  />