@myoc/element 0.19.532 → 0.19.533

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 (44) hide show
  1. package/dist/dev/index.js +783 -16
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +15 -15
  4. package/dist/types/common/src/constants.d.ts +1 -0
  5. package/dist/types/element/src/gif.d.ts +29 -0
  6. package/dist/types/element/src/image.d.ts +6 -1
  7. package/dist/types/element/src/index.d.ts +2 -0
  8. package/dist/types/element/src/newElement.d.ts +1 -0
  9. package/dist/types/element/src/renderGif.d.ts +5 -0
  10. package/dist/types/element/src/types.d.ts +12 -0
  11. package/dist/types/excalidraw/actions/actionBoundText.d.ts +2 -2
  12. package/dist/types/excalidraw/actions/actionCanvas.d.ts +6 -6
  13. package/dist/types/excalidraw/actions/actionClipboard.d.ts +2 -2
  14. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +1 -1
  15. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
  16. package/dist/types/excalidraw/actions/actionDeselect.d.ts +1 -1
  17. package/dist/types/excalidraw/actions/actionElementLock.d.ts +2 -2
  18. package/dist/types/excalidraw/actions/actionExport.d.ts +2 -2
  19. package/dist/types/excalidraw/actions/actionFrame.d.ts +4 -3
  20. package/dist/types/excalidraw/actions/actionGroup.d.ts +2 -2
  21. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +2 -1
  22. package/dist/types/excalidraw/actions/actionLink.d.ts +1 -1
  23. package/dist/types/excalidraw/actions/actionProperties.d.ts +2 -2
  24. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +1 -1
  25. package/dist/types/excalidraw/actions/actionStyles.d.ts +1 -1
  26. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +1 -1
  27. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
  28. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +1 -1
  29. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
  30. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +1 -1
  31. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +1 -1
  32. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
  33. package/dist/types/excalidraw/components/App.d.ts +8 -1
  34. package/dist/types/excalidraw/components/App.gif.d.ts +18 -0
  35. package/dist/types/excalidraw/components/GifPlaybackControls.d.ts +10 -0
  36. package/dist/types/excalidraw/components/icons.d.ts +4 -0
  37. package/dist/types/excalidraw/data/blob.d.ts +2 -2
  38. package/dist/types/excalidraw/data/json.d.ts +1 -1
  39. package/dist/types/excalidraw/data/restoreGif.d.ts +4 -0
  40. package/dist/types/excalidraw/scene/Renderer.d.ts +1 -0
  41. package/dist/types/excalidraw/scene/types.d.ts +2 -1
  42. package/dist/types/excalidraw/types.d.ts +12 -2
  43. package/dist/types/excalidraw/viewport.d.ts +1 -1
  44. package/package.json +5 -4
package/dist/dev/index.js CHANGED
@@ -14535,7 +14535,11 @@ var determineCustomLinearAngle = (pivotPoint, draggedPoint) => Math.atan2(dragge
14535
14535
 
14536
14536
  // src/image.ts
14537
14537
  init_define_import_meta_env();
14538
- import { MIME_TYPES, SVG_NS } from "@excalidraw/common";
14538
+ import {
14539
+ DEFAULT_GIF_AUTO_DECODE_MAX_FILE_SIZE_BYTES,
14540
+ MIME_TYPES,
14541
+ SVG_NS
14542
+ } from "@excalidraw/common";
14539
14543
 
14540
14544
  // ../../node_modules/thumbhash/thumbhash.js
14541
14545
  init_define_import_meta_env();
@@ -14689,6 +14693,614 @@ function thumbHashToApproximateAspectRatio(hash) {
14689
14693
  return lx / ly;
14690
14694
  }
14691
14695
 
14696
+ // src/gif.ts
14697
+ init_define_import_meta_env();
14698
+
14699
+ // ../../node_modules/modern-gif/dist/index.mjs
14700
+ init_define_import_meta_env();
14701
+ var SIGNATURE = "GIF";
14702
+ var VERSIONS = ["87a", "89a"];
14703
+ var IMAGE_DESCRIPTOR = 44;
14704
+ var EXTENSION = 33;
14705
+ var EXTENSION_APPLICATION = 255;
14706
+ var EXTENSION_APPLICATION_BLOCK_SIZE = 11;
14707
+ var EXTENSION_COMMENT = 254;
14708
+ var EXTENSION_GRAPHIC_CONTROL = 249;
14709
+ var EXTENSION_GRAPHIC_CONTROL_BLOCK_SIZE = 4;
14710
+ var EXTENSION_PLAIN_TEXT = 1;
14711
+ var EXTENSION_PLAIN_TEXT_BLOCK_SIZE = 1;
14712
+ var TRAILER = 59;
14713
+ function mergeBuffers(buffers) {
14714
+ const container = new Uint8Array(
14715
+ buffers.reduce((total, array) => total + array.byteLength, 0)
14716
+ );
14717
+ buffers.reduce((offset, array) => {
14718
+ container.set(array, offset);
14719
+ return offset + array.byteLength;
14720
+ }, 0);
14721
+ return container;
14722
+ }
14723
+ function resovleSource(source, output, options) {
14724
+ let buffer;
14725
+ if (ArrayBuffer.isView(source)) {
14726
+ buffer = source.buffer;
14727
+ } else if (source instanceof ArrayBuffer) {
14728
+ buffer = source;
14729
+ } else {
14730
+ const canvas = document.createElement("canvas");
14731
+ const { width, height } = options || {};
14732
+ const context2d = canvas.getContext("2d");
14733
+ if (!context2d) {
14734
+ throw new Error("Failed to create canvas context2d");
14735
+ }
14736
+ canvas.width = width ?? ("width" in source ? typeof source.width === "number" ? source.width : source.width.baseVal.value : 0);
14737
+ canvas.height = height ?? ("height" in source ? typeof source.height === "number" ? source.height : source.height.baseVal.value : 0);
14738
+ context2d.drawImage(source, 0, 0, canvas.width, canvas.height);
14739
+ buffer = context2d.getImageData(0, 0, canvas.width, canvas.height).data.buffer;
14740
+ }
14741
+ switch (output) {
14742
+ case "uint8Array":
14743
+ return new Uint8Array(buffer);
14744
+ case "uint8ClampedArray":
14745
+ return new Uint8ClampedArray(buffer);
14746
+ case "dataView":
14747
+ return new DataView(buffer);
14748
+ default:
14749
+ throw new Error("Unsupported output format");
14750
+ }
14751
+ }
14752
+ var Reader = class {
14753
+ _view;
14754
+ offset = 0;
14755
+ constructor(source) {
14756
+ this._view = resovleSource(source, "dataView");
14757
+ }
14758
+ readByte() {
14759
+ return this._view.getUint8(this.offset++);
14760
+ }
14761
+ readBytes(length) {
14762
+ return Array.from({ length }).map(() => this.readByte());
14763
+ }
14764
+ readString(length) {
14765
+ return String.fromCharCode(...this.readBytes(length));
14766
+ }
14767
+ readUnsigned() {
14768
+ return [this._view.getUint16(this.offset, true), this.offset += 2][0];
14769
+ }
14770
+ readBits() {
14771
+ return this._view.getUint8(this.offset++).toString(2).padStart(8, "0").split("").map(Number);
14772
+ }
14773
+ readColorTable(length) {
14774
+ return Array.from({ length }, () => Array.from(this.readBytes(3)));
14775
+ }
14776
+ readSubBlock() {
14777
+ const block = [];
14778
+ while (true) {
14779
+ const val = this.readByte();
14780
+ if (val === 0 && this._view.getUint8(this.offset) !== 0)
14781
+ break;
14782
+ block.push(val);
14783
+ }
14784
+ return block;
14785
+ }
14786
+ };
14787
+ function decode(source) {
14788
+ const gif = {};
14789
+ const reader = new Reader(source);
14790
+ const createFrame = () => ({ index: 0, delay: 100, disposal: 0 });
14791
+ const signature = reader.readString(3);
14792
+ const version = reader.readString(3);
14793
+ if (signature !== SIGNATURE || !VERSIONS.includes(version)) {
14794
+ throw new Error("This is not a 87a/89a GIF data.");
14795
+ }
14796
+ gif.version = version;
14797
+ gif.width = reader.readUnsigned();
14798
+ gif.height = reader.readUnsigned();
14799
+ const bits = reader.readBits();
14800
+ gif.globalColorTable = Boolean(bits[0]);
14801
+ gif.colorResoluTion = Number.parseInt(bits.slice(1, 4).join(""), 2) + 1;
14802
+ gif.colorTableSorted = Boolean(bits[4]);
14803
+ const colorTableSize = Number.parseInt(bits.slice(5, 8).join(""), 2);
14804
+ gif.colorTableSize = 2 ** (colorTableSize + 1);
14805
+ gif.backgroundColorIndex = reader.readByte();
14806
+ gif.pixelAspectRatio = reader.readByte();
14807
+ if (gif.globalColorTable) {
14808
+ if (gif.colorTableSize) {
14809
+ gif.colorTable = reader.readColorTable(gif.colorTableSize);
14810
+ } else {
14811
+ reader.readSubBlock();
14812
+ }
14813
+ }
14814
+ gif.frames = [];
14815
+ let frame = createFrame();
14816
+ const flags = [];
14817
+ const extensionFlags = [];
14818
+ while (true) {
14819
+ const flag = reader.readByte();
14820
+ flags.push(flag);
14821
+ if (flag === IMAGE_DESCRIPTOR) {
14822
+ frame.left = reader.readUnsigned();
14823
+ frame.top = reader.readUnsigned();
14824
+ frame.width = reader.readUnsigned();
14825
+ frame.height = reader.readUnsigned();
14826
+ const bits2 = reader.readBits();
14827
+ frame.localColorTable = Boolean(bits2[0]);
14828
+ frame.interlaced = Boolean(bits2[1]);
14829
+ frame.colorTableSorted = Boolean(bits2[2]);
14830
+ frame.reserved = Number.parseInt(bits2.slice(3, 5).join(""), 2);
14831
+ const colorTableSize2 = Number.parseInt(bits2.slice(5, 8).join(""), 2);
14832
+ frame.colorTableSize = 2 ** (colorTableSize2 + 1);
14833
+ if (frame.localColorTable) {
14834
+ frame.colorTable = reader.readColorTable(frame.colorTableSize);
14835
+ }
14836
+ frame.lzwMinCodeSize = reader.readByte();
14837
+ frame.dataPositions = [];
14838
+ while (true) {
14839
+ const length = reader.readByte();
14840
+ if (length === 0)
14841
+ break;
14842
+ const offset = reader.offset;
14843
+ frame.dataPositions.push([offset, length]);
14844
+ reader.offset = offset + length;
14845
+ }
14846
+ gif.frames.push(frame);
14847
+ frame = createFrame();
14848
+ frame.index = gif.frames.length;
14849
+ continue;
14850
+ }
14851
+ if (flag === EXTENSION) {
14852
+ const extensionFlag = reader.readByte();
14853
+ extensionFlags.push(extensionFlag);
14854
+ if (extensionFlag === EXTENSION_APPLICATION) {
14855
+ if (reader.readByte() !== EXTENSION_APPLICATION_BLOCK_SIZE)
14856
+ continue;
14857
+ const application = {
14858
+ identifier: reader.readString(8),
14859
+ code: reader.readString(3),
14860
+ data: []
14861
+ };
14862
+ if (`${application.identifier}${application.code}` === "NETSCAPE2.0") {
14863
+ if (reader.readByte() === 3) {
14864
+ gif.looped = Boolean(reader.readByte());
14865
+ gif.loopCount = reader.readUnsigned();
14866
+ }
14867
+ }
14868
+ application.data = reader.readSubBlock();
14869
+ frame.application = application;
14870
+ continue;
14871
+ }
14872
+ if (extensionFlag === EXTENSION_COMMENT) {
14873
+ frame.comment = reader.readSubBlock().map((val) => String.fromCharCode(val)).join("");
14874
+ continue;
14875
+ }
14876
+ if (extensionFlag === EXTENSION_GRAPHIC_CONTROL) {
14877
+ if (reader.readByte() !== EXTENSION_GRAPHIC_CONTROL_BLOCK_SIZE)
14878
+ continue;
14879
+ const bits2 = reader.readBits();
14880
+ const graphicControl = {
14881
+ // ↓ <Packed Fields>
14882
+ reserved: Number.parseInt(bits2.slice(0, 3).join(""), 2),
14883
+ disposal: Number.parseInt(bits2.slice(3, 6).join(""), 2),
14884
+ userInput: Boolean(bits2[6]),
14885
+ transparent: Boolean(bits2[7]),
14886
+ // ↑ <Packed Fields>
14887
+ delayTime: reader.readUnsigned(),
14888
+ transparentIndex: reader.readByte()
14889
+ };
14890
+ reader.readSubBlock();
14891
+ frame.graphicControl = graphicControl;
14892
+ frame.disposal = graphicControl.disposal;
14893
+ frame.delay = (graphicControl.delayTime || 10) * 10;
14894
+ continue;
14895
+ }
14896
+ if (extensionFlag === EXTENSION_PLAIN_TEXT) {
14897
+ if (reader.readByte() !== EXTENSION_PLAIN_TEXT_BLOCK_SIZE)
14898
+ continue;
14899
+ frame.plainText = {
14900
+ left: reader.readUnsigned(),
14901
+ top: reader.readUnsigned(),
14902
+ width: reader.readUnsigned(),
14903
+ height: reader.readUnsigned(),
14904
+ cellWidth: reader.readByte(),
14905
+ cellHeight: reader.readByte(),
14906
+ colorIndex: reader.readByte(),
14907
+ backgroundColorIndex: reader.readByte(),
14908
+ data: reader.readSubBlock()
14909
+ };
14910
+ continue;
14911
+ }
14912
+ console.warn(
14913
+ `Unknown extension block: 0x${extensionFlag.toString(16)}`,
14914
+ flags.slice(0, flags.length - 1).map((val) => `0x${val.toString(16)}`),
14915
+ extensionFlags.slice(0, extensionFlags.length - 1).map((val) => `0x${val.toString(16)}`)
14916
+ );
14917
+ continue;
14918
+ }
14919
+ if (flag === TRAILER)
14920
+ break;
14921
+ console.warn(
14922
+ `Unknown block: 0x${flag.toString(16)}`,
14923
+ flags.slice(0, flags.length - 1).map((val) => `0x${val.toString(16)}`),
14924
+ extensionFlags.slice(0, extensionFlags.length - 1).map((val) => `0x${val.toString(16)}`)
14925
+ );
14926
+ }
14927
+ return gif;
14928
+ }
14929
+ function createWorker(options) {
14930
+ const callbacks = /* @__PURE__ */ new Map();
14931
+ const { workerUrl } = options;
14932
+ let { workerNumber = 1 } = options;
14933
+ const workers = [...Array.from({ length: workerUrl ? workerNumber : 0 })].map(() => {
14934
+ try {
14935
+ const worker = new Worker(workerUrl);
14936
+ worker.onmessage = onMessage;
14937
+ return worker;
14938
+ } catch (err) {
14939
+ console.warn(err);
14940
+ return null;
14941
+ }
14942
+ }).filter(Boolean);
14943
+ workerNumber = workers.length;
14944
+ function onMessage(event) {
14945
+ const { id, data } = event.data;
14946
+ callbacks.get(id)?.(data);
14947
+ callbacks.delete(id);
14948
+ }
14949
+ const getWorker = /* @__PURE__ */ function() {
14950
+ let id = 0;
14951
+ return (index) => workers[(index ?? id++) % workerNumber];
14952
+ }();
14953
+ const call = /* @__PURE__ */ function() {
14954
+ let id = 0;
14955
+ return (type, data, transfer, index) => {
14956
+ return new Promise((resolve) => {
14957
+ const worker = getWorker(index);
14958
+ if (!worker)
14959
+ return resolve(void 0);
14960
+ callbacks.set(id, resolve);
14961
+ worker.postMessage({ id: id++, type, data }, { transfer });
14962
+ });
14963
+ };
14964
+ }();
14965
+ return {
14966
+ call
14967
+ };
14968
+ }
14969
+ function deinterlace(pixels, width) {
14970
+ const newPixels = Array.from({ length: pixels.length });
14971
+ const rows = pixels.length / width;
14972
+ const offsets = [0, 4, 2, 1];
14973
+ const steps = [8, 8, 4, 2];
14974
+ let fromRow = 0;
14975
+ for (let pass = 0; pass < 4; pass++) {
14976
+ for (let toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
14977
+ newPixels.splice.apply(
14978
+ newPixels,
14979
+ [toRow * width, width].concat(
14980
+ pixels.slice(fromRow * width, (fromRow + 1) * width)
14981
+ )
14982
+ );
14983
+ fromRow++;
14984
+ }
14985
+ }
14986
+ return newPixels;
14987
+ }
14988
+ function lzwDecode(minCodeSize, data, pixelCount) {
14989
+ const MAX_STACK_SIZE = 4096;
14990
+ const nullCode = -1;
14991
+ const npix = pixelCount;
14992
+ let available, codeMask, codeSize, inCode, oldCode, code, i;
14993
+ const dstPixels = Array.from({ length: pixelCount });
14994
+ const prefix = Array.from({ length: MAX_STACK_SIZE });
14995
+ const suffix = Array.from({ length: MAX_STACK_SIZE });
14996
+ const pixelStack = Array.from({ length: MAX_STACK_SIZE + 1 });
14997
+ const dataSize = minCodeSize;
14998
+ const clear = 1 << dataSize;
14999
+ const endOfInformation = clear + 1;
15000
+ available = clear + 2;
15001
+ oldCode = nullCode;
15002
+ codeSize = dataSize + 1;
15003
+ codeMask = (1 << codeSize) - 1;
15004
+ for (code = 0; code < clear; code++) {
15005
+ prefix[code] = 0;
15006
+ suffix[code] = code;
15007
+ }
15008
+ let datum, bits, first, top, pi, bi;
15009
+ datum = bits = first = top = pi = bi = 0;
15010
+ for (i = 0; i < npix; ) {
15011
+ if (top === 0) {
15012
+ if (bits < codeSize) {
15013
+ datum += data[bi] << bits;
15014
+ bits += 8;
15015
+ bi++;
15016
+ continue;
15017
+ }
15018
+ code = datum & codeMask;
15019
+ datum >>= codeSize;
15020
+ bits -= codeSize;
15021
+ if (code > available || code === endOfInformation) {
15022
+ break;
15023
+ }
15024
+ if (code === clear) {
15025
+ codeSize = dataSize + 1;
15026
+ codeMask = (1 << codeSize) - 1;
15027
+ available = clear + 2;
15028
+ oldCode = nullCode;
15029
+ continue;
15030
+ }
15031
+ if (oldCode === nullCode) {
15032
+ pixelStack[top++] = suffix[code];
15033
+ oldCode = code;
15034
+ first = code;
15035
+ continue;
15036
+ }
15037
+ inCode = code;
15038
+ if (code === available) {
15039
+ pixelStack[top++] = first;
15040
+ code = oldCode;
15041
+ }
15042
+ while (code > clear) {
15043
+ pixelStack[top++] = suffix[code];
15044
+ code = prefix[code];
15045
+ }
15046
+ first = suffix[code] & 255;
15047
+ pixelStack[top++] = first;
15048
+ if (available < MAX_STACK_SIZE) {
15049
+ prefix[available] = oldCode;
15050
+ suffix[available] = first;
15051
+ available++;
15052
+ if ((available & codeMask) === 0 && available < MAX_STACK_SIZE) {
15053
+ codeSize++;
15054
+ codeMask += available;
15055
+ }
15056
+ }
15057
+ oldCode = inCode;
15058
+ }
15059
+ top--;
15060
+ dstPixels[pi++] = pixelStack[top];
15061
+ i++;
15062
+ }
15063
+ for (i = pi; i < npix; i++) {
15064
+ dstPixels[i] = 0;
15065
+ }
15066
+ return dstPixels;
15067
+ }
15068
+ function decodeFrames(source, options) {
15069
+ const array = resovleSource(source, "uint8Array");
15070
+ if (options?.workerUrl) {
15071
+ return createWorker({ workerUrl: options.workerUrl }).call(
15072
+ "frames:decode",
15073
+ array,
15074
+ [array.buffer]
15075
+ );
15076
+ }
15077
+ const {
15078
+ gif = decode(source),
15079
+ range
15080
+ } = options ?? {};
15081
+ const {
15082
+ width: globalWidth,
15083
+ height: globalHeight,
15084
+ colorTable: globalColorTable,
15085
+ frames: globalFrames
15086
+ } = gif;
15087
+ const frames2 = range ? globalFrames.slice(range[0], range[1] + 1) : globalFrames;
15088
+ const hasDisposal3 = frames2.some((frame) => frame.disposal === 3);
15089
+ let pixels = new Uint8ClampedArray(globalWidth * globalHeight * 4);
15090
+ let previousFrame;
15091
+ let previousPixels = pixels.slice();
15092
+ return frames2.map((frame) => {
15093
+ const {
15094
+ left,
15095
+ top,
15096
+ width,
15097
+ height,
15098
+ interlaced,
15099
+ localColorTable,
15100
+ colorTable,
15101
+ lzwMinCodeSize,
15102
+ dataPositions,
15103
+ graphicControl,
15104
+ delay,
15105
+ disposal
15106
+ } = frame;
15107
+ const previousDisposal = previousFrame?.disposal;
15108
+ const bottom = top + height;
15109
+ const {
15110
+ transparent,
15111
+ transparentIndex: localTransparentIndex
15112
+ } = graphicControl ?? {};
15113
+ const palette2 = localColorTable ? colorTable : globalColorTable;
15114
+ const transparentIndex = transparent ? localTransparentIndex : -1;
15115
+ const compressedData = mergeBuffers(
15116
+ dataPositions.map(
15117
+ ([begin, length]) => array.subarray(begin, begin + length)
15118
+ )
15119
+ );
15120
+ let colorIndexes = lzwDecode(lzwMinCodeSize, compressedData, width * height);
15121
+ if (interlaced) {
15122
+ colorIndexes = deinterlace(colorIndexes, width);
15123
+ }
15124
+ if (previousDisposal === 3) {
15125
+ pixels = previousPixels.slice();
15126
+ } else if (previousDisposal === 2) {
15127
+ const { left: left2, top: top2, width: width2, height: height2 } = previousFrame;
15128
+ const bottom2 = top2 + height2;
15129
+ for (let y = top2; y < bottom2; y++) {
15130
+ const globalOffset = y * globalWidth + left2;
15131
+ for (let x = 0; x < width2; x++) {
15132
+ const index = (globalOffset + x) * 4;
15133
+ pixels[index] = pixels[index + 1] = pixels[index + 2] = pixels[index + 3] = 0;
15134
+ }
15135
+ }
15136
+ }
15137
+ for (let y = top; y < bottom; y++) {
15138
+ const globalOffset = y * globalWidth + left;
15139
+ const localOffset = (y - top) * width;
15140
+ for (let x = 0; x < width; x++) {
15141
+ const colorIndex = colorIndexes[localOffset + x];
15142
+ const index = (globalOffset + x) * 4;
15143
+ if (colorIndex !== transparentIndex) {
15144
+ const [r, g, b2] = palette2?.[colorIndex] ?? [0, 0, 0];
15145
+ pixels[index] = r;
15146
+ pixels[index + 1] = g;
15147
+ pixels[index + 2] = b2;
15148
+ pixels[index + 3] = 255;
15149
+ }
15150
+ }
15151
+ }
15152
+ if (hasDisposal3 && disposal !== 1 && disposal !== 3) {
15153
+ previousPixels = pixels.slice();
15154
+ }
15155
+ previousFrame = frame;
15156
+ return {
15157
+ width: globalWidth,
15158
+ height: globalHeight,
15159
+ delay,
15160
+ data: pixels.slice()
15161
+ };
15162
+ });
15163
+ }
15164
+ var ditherKernels = {
15165
+ "floyd-steinberg": [
15166
+ [7 / 16, 1, 0],
15167
+ [3 / 16, -1, 1],
15168
+ [5 / 16, 0, 1],
15169
+ [1 / 16, 1, 1]
15170
+ ],
15171
+ "atkinson": [
15172
+ [1 / 8, 1, 0],
15173
+ [1 / 8, 2, 0],
15174
+ [1 / 8, -1, 1],
15175
+ [1 / 8, 0, 1],
15176
+ [1 / 8, 1, 1],
15177
+ [1 / 8, 0, 2]
15178
+ ],
15179
+ "stucki": [
15180
+ [8 / 42, 1, 0],
15181
+ [4 / 42, 2, 0],
15182
+ [2 / 42, -2, 1],
15183
+ [4 / 42, -1, 1],
15184
+ [8 / 42, 0, 1],
15185
+ [4 / 42, 1, 1],
15186
+ [2 / 42, 2, 1],
15187
+ [1 / 42, -2, 2],
15188
+ [2 / 42, -1, 2],
15189
+ [4 / 42, 0, 2],
15190
+ [2 / 42, 1, 2],
15191
+ [1 / 42, 2, 2]
15192
+ ]
15193
+ };
15194
+
15195
+ // src/gif.ts
15196
+ var MAX_CONCURRENT_GIF_DECODES = 5;
15197
+ var gifWorkerUrl;
15198
+ var configureGifWorkerUrl = (workerUrl) => {
15199
+ gifWorkerUrl = workerUrl;
15200
+ };
15201
+ var dataURLToArrayBuffer = (dataURL) => {
15202
+ const dataIndexStart = dataURL.indexOf(",");
15203
+ const byteString = atob(dataURL.slice(dataIndexStart + 1));
15204
+ const buffer = new ArrayBuffer(byteString.length);
15205
+ const bytes = new Uint8Array(buffer);
15206
+ for (let index = 0; index < byteString.length; index++) {
15207
+ bytes[index] = byteString.charCodeAt(index);
15208
+ }
15209
+ return buffer;
15210
+ };
15211
+ var decodeGifFrames = async (dataURL) => {
15212
+ const buffer = dataURLToArrayBuffer(dataURL);
15213
+ const gif = decode(buffer);
15214
+ const decodedFrames = gifWorkerUrl ? await decodeFrames(buffer, { gif, workerUrl: gifWorkerUrl }) : await decodeFrames(buffer, { gif });
15215
+ const frames2 = decodedFrames.map((frame) => {
15216
+ const canvas = document.createElement("canvas");
15217
+ canvas.width = frame.width;
15218
+ canvas.height = frame.height;
15219
+ const context = canvas.getContext("2d");
15220
+ if (context) {
15221
+ const imageData = context.createImageData(frame.width, frame.height);
15222
+ imageData.data.set(frame.data);
15223
+ context.putImageData(imageData, 0, 0);
15224
+ }
15225
+ return canvas;
15226
+ });
15227
+ return {
15228
+ frames: frames2,
15229
+ delays: decodedFrames.map((frame) => Math.max(frame.delay, 16)),
15230
+ width: gif.width,
15231
+ height: gif.height
15232
+ };
15233
+ };
15234
+ var activeGifDecodeCount = 0;
15235
+ var gifDecodeSequence = 0;
15236
+ var gifDecodeSchedulePending = false;
15237
+ var gifDecodeQueue = [];
15238
+ var getDataURLByteLength = (dataURL) => {
15239
+ const dataIndexStart = dataURL.indexOf(",");
15240
+ const encoded = dataURL.slice(dataIndexStart + 1);
15241
+ const padding = encoded.endsWith("==") && 2 || encoded.endsWith("=") && 1 || 0;
15242
+ return Math.floor(encoded.length * 3 / 4) - padding;
15243
+ };
15244
+ var runGifDecodeTask = (task) => {
15245
+ activeGifDecodeCount++;
15246
+ decodeGifFrames(task.dataURL).then(task.resolve, task.reject).finally(() => {
15247
+ activeGifDecodeCount--;
15248
+ queueGifDecodeScheduler();
15249
+ });
15250
+ };
15251
+ var scheduleNextGifDecode = () => {
15252
+ gifDecodeSchedulePending = false;
15253
+ while (activeGifDecodeCount < MAX_CONCURRENT_GIF_DECODES && gifDecodeQueue.length) {
15254
+ runGifDecodeTask(gifDecodeQueue.shift());
15255
+ }
15256
+ };
15257
+ var queueGifDecodeScheduler = () => {
15258
+ if (gifDecodeSchedulePending) {
15259
+ return;
15260
+ }
15261
+ gifDecodeSchedulePending = true;
15262
+ queueMicrotask(scheduleNextGifDecode);
15263
+ };
15264
+ var decodeGifFramesQueued = (dataURL) => {
15265
+ return new Promise((resolve, reject) => {
15266
+ const task = {
15267
+ dataURL,
15268
+ priority: getDataURLByteLength(dataURL),
15269
+ sequence: gifDecodeSequence++,
15270
+ resolve,
15271
+ reject
15272
+ };
15273
+ const insertionIndex = gifDecodeQueue.findIndex(
15274
+ (queuedTask) => queuedTask.priority > task.priority || queuedTask.priority === task.priority && queuedTask.sequence > task.sequence
15275
+ );
15276
+ if (insertionIndex === -1) {
15277
+ gifDecodeQueue.push(task);
15278
+ } else {
15279
+ gifDecodeQueue.splice(insertionIndex, 0, task);
15280
+ }
15281
+ queueGifDecodeScheduler();
15282
+ });
15283
+ };
15284
+ var createGifPlaybackMetadata = (opts = {}) => ({
15285
+ frameIndex: 0,
15286
+ speed: 1,
15287
+ playing: opts.playing ?? false
15288
+ });
15289
+ var getGifRenderFrame = (element, cacheEntry, runtimeFrameIndex) => {
15290
+ const gifFrameIndex = element.gifPlayback?.playing ? runtimeFrameIndex ?? element.gifPlayback.frameIndex : element.gifPlayback?.frameIndex ?? 0;
15291
+ return cacheEntry?.gif?.frames[gifFrameIndex];
15292
+ };
15293
+ var hasActiveGifDecode = (visibleElements, imageCache) => visibleElements.some((element) => {
15294
+ if (!isInitializedImageElement(element)) {
15295
+ return false;
15296
+ }
15297
+ const cacheEntry = imageCache.get(element.fileId);
15298
+ return cacheEntry?.gifDecodeStatus === "pending" && !cacheEntry.gif?.frames.length;
15299
+ });
15300
+ var hasPlayableGif = (visibleElements, imageCache) => visibleElements.some(
15301
+ (element) => isInitializedImageElement(element) && element.gifPlayback?.playing && imageCache.get(element.fileId)?.gif?.frames.length
15302
+ );
15303
+
14692
15304
  // src/image.ts
14693
15305
  var THUMB_HASH_MAX_DIMENSION = 100;
14694
15306
  var thumbHashCanvasCache = /* @__PURE__ */ new Map();
@@ -14774,7 +15386,10 @@ var loadHTMLImageElement = (dataURL) => {
14774
15386
  var updateImageCache = async ({
14775
15387
  fileIds,
14776
15388
  files,
14777
- imageCache
15389
+ imageCache,
15390
+ onImageCacheUpdate,
15391
+ gifAutoDecodeMaxFileSizeBytes = DEFAULT_GIF_AUTO_DECODE_MAX_FILE_SIZE_BYTES,
15392
+ forceGifDecode = false
14778
15393
  }) => {
14779
15394
  const updatedFiles = /* @__PURE__ */ new Map();
14780
15395
  const erroredFiles = /* @__PURE__ */ new Map();
@@ -14793,20 +15408,72 @@ var updateImageCache = async ({
14793
15408
  const imagePromise = loadHTMLImageElement(fileData.dataURL);
14794
15409
  const data = {
14795
15410
  image: imagePromise,
14796
- mimeType: fileData.mimeType
15411
+ mimeType: fileData.mimeType,
15412
+ gifDecodeStatus: fileData.mimeType === MIME_TYPES.gif ? "pending" : void 0
14797
15413
  };
14798
15414
  imageCache.set(fileId, data);
14799
- const image = await imagePromise;
15415
+ if (data.gifDecodeStatus === "pending") {
15416
+ onImageCacheUpdate?.(fileId);
15417
+ }
15418
+ let image = imagePromise;
15419
+ let gif;
15420
+ let gifDecodeStatus;
15421
+ try {
15422
+ image = await imagePromise;
15423
+ if (fileData.mimeType === MIME_TYPES.gif) {
15424
+ const decodingData = {
15425
+ ...data,
15426
+ image,
15427
+ gifDecodeStatus: "pending"
15428
+ };
15429
+ imageCache.set(fileId, decodingData);
15430
+ onImageCacheUpdate?.(fileId);
15431
+ }
15432
+ if (fileData.mimeType === MIME_TYPES.gif && !forceGifDecode && getDataURLByteLength(fileData.dataURL) > gifAutoDecodeMaxFileSizeBytes) {
15433
+ gifDecodeStatus = "deferred";
15434
+ } else {
15435
+ gif = fileData.mimeType === MIME_TYPES.gif ? await decodeGifFramesQueued(fileData.dataURL) : void 0;
15436
+ if (fileData.mimeType === MIME_TYPES.gif) {
15437
+ gifDecodeStatus = "success";
15438
+ }
15439
+ }
15440
+ } catch (error) {
15441
+ erroredFiles.set(fileId, true);
15442
+ if (fileData.mimeType === MIME_TYPES.gif) {
15443
+ gifDecodeStatus = "error";
15444
+ }
15445
+ } finally {
15446
+ if (fileData.mimeType === MIME_TYPES.gif) {
15447
+ imageCache.set(fileId, {
15448
+ ...data,
15449
+ image,
15450
+ gif,
15451
+ gifDecodeStatus: gifDecodeStatus ?? "error"
15452
+ });
15453
+ onImageCacheUpdate?.(fileId);
15454
+ }
15455
+ }
15456
+ if (erroredFiles.has(fileId)) {
15457
+ return;
15458
+ }
14800
15459
  if (previousCacheEntry?.isPlaceholder) {
14801
15460
  imageCache.set(fileId, {
14802
15461
  ...data,
14803
15462
  image,
15463
+ gif,
15464
+ gifDecodeStatus: fileData.mimeType === MIME_TYPES.gif ? gifDecodeStatus : void 0,
14804
15465
  placeholderImage: await previousCacheEntry.image,
14805
15466
  transitionStart: performance.now()
14806
15467
  });
14807
15468
  } else {
14808
- imageCache.set(fileId, { ...data, image });
15469
+ imageCache.set(fileId, {
15470
+ ...data,
15471
+ image,
15472
+ gif,
15473
+ gifDecodeStatus: fileData.mimeType === MIME_TYPES.gif ? gifDecodeStatus : void 0
15474
+ });
14809
15475
  }
15476
+ onImageCacheUpdate?.(fileId);
14810
15477
  } catch (error) {
14811
15478
  erroredFiles.set(fileId, true);
14812
15479
  }
@@ -14870,6 +15537,52 @@ var normalizeSVG = (SVGString) => {
14870
15537
  }
14871
15538
  };
14872
15539
 
15540
+ // src/renderGif.ts
15541
+ init_define_import_meta_env();
15542
+ var drawGifDecodePlaceholder = (element, context, theme, drawImagePlaceholder2) => {
15543
+ drawImagePlaceholder2(element, context, theme);
15544
+ const pulse = 0.55 + Math.sin(performance.now() / 350) * 0.25;
15545
+ const text = "GIF Decoding";
15546
+ const fontFamily = "Assistant, system-ui, BlinkMacSystemFont, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif";
15547
+ const textX = element.width / 2;
15548
+ const textY = element.height / 2;
15549
+ context.save();
15550
+ context.globalAlpha *= pulse;
15551
+ context.textAlign = "center";
15552
+ context.textBaseline = "middle";
15553
+ context.font = `600 100px ${fontFamily}`;
15554
+ const targetTextWidth = element.width * 0.6;
15555
+ const measuredWidth = context.measureText(text).width;
15556
+ const widthFitFontSize = measuredWidth ? 100 * targetTextWidth / measuredWidth : 24;
15557
+ const fontSize = Math.max(
15558
+ 12,
15559
+ Math.min(widthFitFontSize, element.height * 0.35)
15560
+ );
15561
+ context.font = `600 ${fontSize}px ${fontFamily}`;
15562
+ const finalFontSize = Number.parseFloat(
15563
+ context.font.match(/\d+(\.\d+)?px/)?.[0] ?? `${fontSize}`
15564
+ );
15565
+ const finalMeasuredWidth = context.measureText(text).width;
15566
+ const paddingX = Math.max(12, finalFontSize * 0.55);
15567
+ const paddingY = Math.max(8, finalFontSize * 0.35);
15568
+ const pillWidth = finalMeasuredWidth + paddingX * 2;
15569
+ const pillHeight = finalFontSize + paddingY * 2;
15570
+ const pillX = textX - pillWidth / 2;
15571
+ const pillY = textY - pillHeight / 2;
15572
+ const radius = Math.min(pillHeight / 2, finalFontSize * 0.25);
15573
+ context.fillStyle = "rgba(0, 0, 0, 0.62)";
15574
+ context.beginPath();
15575
+ if (context.roundRect) {
15576
+ context.roundRect(pillX, pillY, pillWidth, pillHeight, radius);
15577
+ } else {
15578
+ context.rect(pillX, pillY, pillWidth, pillHeight);
15579
+ }
15580
+ context.fill();
15581
+ context.fillStyle = "#fff";
15582
+ context.fillText(text, textX, textY);
15583
+ context.restore();
15584
+ };
15585
+
14873
15586
  // src/renderElement.ts
14874
15587
  var isPendingImageElement = (element, renderConfig) => isInitializedImageElement(element) && !renderConfig.imageCache.has(element.fileId);
14875
15588
  var getCanvasPadding = (element) => {
@@ -15039,7 +15752,14 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
15039
15752
  context.save();
15040
15753
  const cacheEntry = element.fileId !== null ? renderConfig.imageCache.get(element.fileId) : null;
15041
15754
  const img = isInitializedImageElement(element) ? cacheEntry?.image : void 0;
15042
- if (img != null && !(img instanceof Promise)) {
15755
+ if (cacheEntry?.gifDecodeStatus === "pending" && !cacheEntry.gif?.frames.length) {
15756
+ drawGifDecodePlaceholder(
15757
+ element,
15758
+ context,
15759
+ renderConfig.theme,
15760
+ drawImagePlaceholder
15761
+ );
15762
+ } else if (img != null && !(img instanceof Promise)) {
15043
15763
  if (element.roundness && context.roundRect) {
15044
15764
  context.beginPath();
15045
15765
  context.roundRect(
@@ -15080,11 +15800,17 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
15080
15800
  delete cacheEntry.transitionStart;
15081
15801
  }
15082
15802
  }
15803
+ const gifFrame = getGifRenderFrame(
15804
+ element,
15805
+ cacheEntry,
15806
+ renderConfig.getGifFrameIndex?.(element)
15807
+ );
15808
+ const imageForRender = gifFrame ?? img;
15083
15809
  const { x, y, width, height } = element.crop ? element.crop : {
15084
15810
  x: 0,
15085
15811
  y: 0,
15086
- width: img.naturalWidth,
15087
- height: img.naturalHeight
15812
+ width: imageForRender instanceof HTMLCanvasElement ? imageForRender.width : imageForRender.naturalWidth,
15813
+ height: imageForRender instanceof HTMLCanvasElement ? imageForRender.height : imageForRender.naturalHeight
15088
15814
  };
15089
15815
  const shouldInvertImage = renderConfig.theme === THEME.DARK && cacheEntry?.mimeType === MIME_TYPES2.svg;
15090
15816
  if (shouldInvertImage && isSafari) {
@@ -15096,7 +15822,7 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
15096
15822
  if (tempContext) {
15097
15823
  tempContext.scale(devicePixelRatio, devicePixelRatio);
15098
15824
  tempContext.drawImage(
15099
- img,
15825
+ imageForRender,
15100
15826
  x,
15101
15827
  y,
15102
15828
  width,
@@ -15136,7 +15862,7 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
15136
15862
  context.filter = DARK_THEME_FILTER;
15137
15863
  }
15138
15864
  context.drawImage(
15139
- img,
15865
+ imageForRender,
15140
15866
  x,
15141
15867
  y,
15142
15868
  width,
@@ -18496,7 +19222,8 @@ var newImageElement = (opts) => {
18496
19222
  fileName: opts.fileName ?? null,
18497
19223
  thumbHash: opts.thumbHash ?? null,
18498
19224
  scale: opts.scale ?? [1, 1],
18499
- crop: opts.crop ?? null
19225
+ crop: opts.crop ?? null,
19226
+ gifPlayback: opts.gifPlayback ?? null
18500
19227
  };
18501
19228
  };
18502
19229
 
@@ -22336,10 +23063,10 @@ var dragSelectedElements = (pointerDownState, _selectedElements, offset, scene,
22336
23063
  const elementsToUpdate = new Set(
22337
23064
  selectedElements
22338
23065
  );
22339
- const frames = selectedElements.filter((e) => isFrameLikeElement(e)).map((f) => f.id);
22340
- if (frames.length > 0) {
23066
+ const frames2 = selectedElements.filter((e) => isFrameLikeElement(e)).map((f) => f.id);
23067
+ if (frames2.length > 0) {
22341
23068
  for (const element of scene.getNonDeletedElements()) {
22342
- if (element.frameId !== null && frames.includes(element.frameId)) {
23069
+ if (element.frameId !== null && frames2.includes(element.frameId)) {
22343
23070
  elementsToUpdate.add(element);
22344
23071
  }
22345
23072
  }
@@ -25521,6 +26248,10 @@ var PlusIcon = createIcon(
25521
26248
  ] }),
25522
26249
  tablerIconProps
25523
26250
  );
26251
+ var CirclePauseIcon = createIcon(
26252
+ "M320 112C434.9 112 528 205.1 528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112zM320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM288 248C288 234.7 277.3 224 264 224C250.7 224 240 234.7 240 248L240 392C240 405.3 250.7 416 264 416C277.3 416 288 405.3 288 392L288 248zM400 248C400 234.7 389.3 224 376 224C362.7 224 352 234.7 352 248L352 392C352 405.3 362.7 416 376 416C389.3 416 400 405.3 400 392L400 248z",
26253
+ 640
26254
+ );
25524
26255
  var DotsIcon = createIcon(
25525
26256
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { strokeWidth: "1.5", children: [
25526
26257
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
@@ -27364,6 +28095,33 @@ var playerStopFilledIcon = createIcon(
27364
28095
  ] }),
27365
28096
  tablerIconProps
27366
28097
  );
28098
+ var prevFrame = createIcon(
28099
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
28100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 5v14" }),
28101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 6l-9 6l9 6z" })
28102
+ ] }),
28103
+ tablerIconProps
28104
+ );
28105
+ var nextFrame = createIcon(
28106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
28107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 5v14" }),
28108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 6l9 6l-9 6z" })
28109
+ ] }),
28110
+ tablerIconProps
28111
+ );
28112
+ var frames = createIcon(
28113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { strokeWidth: 1.25, children: [
28114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
28115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6h16a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1z" }),
28116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 6v12" }),
28117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 6v12" }),
28118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 10h4" }),
28119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 14h4" }),
28120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 10h4" }),
28121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 14h4" })
28122
+ ] }),
28123
+ tablerIconProps
28124
+ );
27367
28125
  var tablerCheckIcon = createIcon(
27368
28126
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
27369
28127
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
@@ -28947,7 +29705,7 @@ var recognizeShape = (points, previousElement, zoom = 1) => {
28947
29705
  const type = previousElement?.type === "arrow" && recognized !== "arrow" ? "freedraw" : recognized;
28948
29706
  return { type, points, boundingBox };
28949
29707
  };
28950
- var convertToShape = (points, appState, elementsMap, previousElement, frames) => {
29708
+ var convertToShape = (points, appState, elementsMap, previousElement, frames2) => {
28951
29709
  const recognizedShape = recognizeShape(
28952
29710
  points,
28953
29711
  previousElement,
@@ -28955,7 +29713,7 @@ var convertToShape = (points, appState, elementsMap, previousElement, frames) =>
28955
29713
  );
28956
29714
  const boundingBox = recognizedShape.boundingBox;
28957
29715
  const [minX, minY, maxX, maxY] = boundingBox;
28958
- const frameLikeElements = frames ?? getFrameLikeElements([...elementsMap.values()]);
29716
+ const frameLikeElements = frames2 ?? getFrameLikeElements([...elementsMap.values()]);
28959
29717
  const frameId = frameLikeElements.find((frame) => {
28960
29718
  const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords3(frame, elementsMap);
28961
29719
  return fx1 <= minX && fy1 <= minY && fx2 >= maxX && fy2 >= maxY;
@@ -29252,13 +30010,17 @@ export {
29252
30010
  computeBoundTextPosition,
29253
30011
  computeBucketFillPolygon,
29254
30012
  computeContainerDimensionForBoundText,
30013
+ configureGifWorkerUrl,
29255
30014
  containsCJK,
29256
30015
  convertToExcalidrawElements,
29257
30016
  convertToShape,
29258
30017
  convertToShapeHandlePointerMoveFromPointerDown,
30018
+ createGifPlaybackMetadata,
29259
30019
  createPlaceholderEmbeddableLabel,
29260
30020
  createSrcDoc,
29261
30021
  cropElement,
30022
+ decodeGifFrames,
30023
+ decodeGifFramesQueued,
29262
30024
  deconstructDiamondElement,
29263
30025
  deconstructLinearOrFreeDrawElement,
29264
30026
  deconstructRectanguloidElement,
@@ -29271,6 +30033,7 @@ export {
29271
30033
  dragNewElement,
29272
30034
  dragNewTextElement,
29273
30035
  dragSelectedElements,
30036
+ drawGifDecodePlaceholder,
29274
30037
  duplicateElement,
29275
30038
  duplicateElements,
29276
30039
  editGroupForSelectedElement,
@@ -29321,6 +30084,7 @@ export {
29321
30084
  getCornerRadius,
29322
30085
  getCubicBezierCurveBound,
29323
30086
  getCursorForResizingElement,
30087
+ getDataURLByteLength,
29324
30088
  getDefaultFrameName,
29325
30089
  getDefaultRoundnessTypeForElement,
29326
30090
  getDiamondBaseCorners,
@@ -29350,6 +30114,7 @@ export {
29350
30114
  getFreedrawOutlineAsSegments,
29351
30115
  getFreedrawOutlinePoints,
29352
30116
  getFreedrawStrokeCenterPoints,
30117
+ getGifRenderFrame,
29353
30118
  getGlobalFixedPointForBindableElement,
29354
30119
  getGlobalFixedPoints,
29355
30120
  getHeadingForElbowArrowSnap,
@@ -29416,10 +30181,12 @@ export {
29416
30181
  handleFocusPointHover,
29417
30182
  handleFocusPointPointerDown,
29418
30183
  handleFocusPointPointerUp,
30184
+ hasActiveGifDecode,
29419
30185
  hasBackground,
29420
30186
  hasBoundTextElement,
29421
30187
  hasBoundingBox,
29422
30188
  hasFreedrawMode,
30189
+ hasPlayableGif,
29423
30190
  hasStrokeColor,
29424
30191
  hasStrokeStyle,
29425
30192
  hasStrokeWidth,