mce 0.21.0 → 0.22.0

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.
package/dist/index.css CHANGED
@@ -1686,6 +1686,15 @@
1686
1686
  .m-workflow__menu-item:hover {
1687
1687
  background: #f3f4f6;
1688
1688
  }
1689
+ .m-workflow__menu-item-main {
1690
+ display: flex;
1691
+ align-items: center;
1692
+ gap: 8px;
1693
+ }
1694
+ .m-workflow__menu-item-main .m-icon {
1695
+ font-size: 18px;
1696
+ color: #6b7280;
1697
+ }
1689
1698
  .m-workflow__menu-kbd {
1690
1699
  font-size: 12px;
1691
1700
  color: #9ca3af;
package/dist/index.js CHANGED
@@ -6057,6 +6057,9 @@ var en_default = {
6057
6057
  "collapse": "Collapse",
6058
6058
  "statusbar": "Statusbar",
6059
6059
  "creator": "Create node",
6060
+ "workflow:text": "Text",
6061
+ "workflow:image": "Image",
6062
+ "workflow:video": "Video",
6060
6063
  "memoryManager": "Manage memory",
6061
6064
  "toolbelt": "Toolbelt",
6062
6065
  "msaa": "MSAA",
@@ -6189,6 +6192,9 @@ var zh_Hans_default = {
6189
6192
  "stepForward": "前进一帧",
6190
6193
  "collapse": "折叠",
6191
6194
  "creator": "创建节点",
6195
+ "workflow:text": "文字生成",
6196
+ "workflow:image": "图片生成",
6197
+ "workflow:video": "视频生成",
6192
6198
  "memoryManager": "管理内存",
6193
6199
  "toolbelt": "工具腰带",
6194
6200
  "msaa": "抗锯齿",
@@ -8886,6 +8892,8 @@ function useNode(nodeRef, editor = useEditor()) {
8886
8892
  const node = nodeRef.value;
8887
8893
  let value = node.name;
8888
8894
  if (!value || value[0] === "@") {
8895
+ const inEditor = node.meta?.inEditorIs;
8896
+ if (typeof inEditor === "string" && inEditor.startsWith("Workflow")) return t(`workflow:${inEditor.slice(8).toLowerCase()}`);
8889
8897
  if (inEditorIs(node, "Frame")) return t("frame");
8890
8898
  else if (node.children.filter(isElement).length) value = t("group");
8891
8899
  else if (node instanceof Lottie2D) value = t("lottie");
@@ -9960,7 +9968,7 @@ var _hoisted_6$5 = {
9960
9968
  key: 2,
9961
9969
  class: "m-list-item__kbd"
9962
9970
  };
9963
- var _hoisted_7$4 = {
9971
+ var _hoisted_7$5 = {
9964
9972
  key: 3,
9965
9973
  class: "m-list-item__append"
9966
9974
  };
@@ -10079,7 +10087,7 @@ var Menu_default = /* @__PURE__ */ defineComponent({
10079
10087
  _ctx.$slots.prepend ? (openBlock(), createElementBlock("div", _hoisted_4$8, [renderSlot(_ctx.$slots, "prepend", { item })])) : createCommentVNode("", true),
10080
10088
  createElementVNode("div", _hoisted_5$6, [renderSlot(_ctx.$slots, "title", { item }, () => [createTextVNode(toDisplayString(item.key), 1)])]),
10081
10089
  _ctx.$slots.kbd ? (openBlock(), createElementBlock("div", _hoisted_6$5, [renderSlot(_ctx.$slots, "kbd", { item })])) : createCommentVNode("", true),
10082
- item.children?.length || _ctx.$slots.append ? (openBlock(), createElementBlock("div", _hoisted_7$4, [renderSlot(_ctx.$slots, "append", { item }), item.children?.length ? (openBlock(), createBlock(unref(Icon_default), {
10090
+ item.children?.length || _ctx.$slots.append ? (openBlock(), createElementBlock("div", _hoisted_7$5, [renderSlot(_ctx.$slots, "append", { item }), item.children?.length ? (openBlock(), createBlock(unref(Icon_default), {
10083
10091
  key: 0,
10084
10092
  icon: "$arrowRight"
10085
10093
  })) : createCommentVNode("", true)])) : createCommentVNode("", true)
@@ -11651,8 +11659,8 @@ var _hoisted_5$5 = [
11651
11659
  "ry"
11652
11660
  ];
11653
11661
  var _hoisted_6$4 = ["transform"];
11654
- var _hoisted_7$3 = { "pointer-events": "all" };
11655
- var _hoisted_8$3 = [
11662
+ var _hoisted_7$4 = { "pointer-events": "all" };
11663
+ var _hoisted_8$4 = [
11656
11664
  "x",
11657
11665
  "y",
11658
11666
  "width",
@@ -12297,7 +12305,7 @@ var Transform_default = /* @__PURE__ */ defineComponent({
12297
12305
  transform: `matrix(1, 0, 0, 1, -32, ${model.value.height}) rotate(270 16 16)`,
12298
12306
  class: "m-transform__rotator"
12299
12307
  }, [..._cache[0] || (_cache[0] = [createElementVNode("path", { d: "M22.4789 9.45728L25.9935 12.9942L22.4789 16.5283V14.1032C18.126 14.1502 14.6071 17.6737 14.5675 22.0283H17.05L13.513 25.543L9.97889 22.0283H12.5674C12.6071 16.5691 17.0214 12.1503 22.4789 12.1031L22.4789 9.45728Z" }, null, -1)])], 8, _hoisted_6$4)) : createCommentVNode("", true)]),
12300
- createElementVNode("g", _hoisted_7$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
12308
+ createElementVNode("g", _hoisted_7$4, [(openBlock(true), createElementBlock(Fragment, null, renderList(computedHandles.value, (handle, index) => {
12301
12309
  return openBlock(), createElementBlock("rect", {
12302
12310
  key: index,
12303
12311
  ref_for: true,
@@ -12311,7 +12319,7 @@ var Transform_default = /* @__PURE__ */ defineComponent({
12311
12319
  class: "m-transform__handle-rect",
12312
12320
  cursor: transforming.value ? "auto" : getCursor(handle.type),
12313
12321
  onPointerdown: (event) => onPointerDown(event, index)
12314
- }, null, 40, _hoisted_8$3);
12322
+ }, null, 40, _hoisted_8$4);
12315
12323
  }), 128))]),
12316
12324
  createElementVNode("g", _hoisted_9$3, [renderSlot(_ctx.$slots, "svg", { box: model.value })])
12317
12325
  ], 4)),
@@ -15840,8 +15848,8 @@ var _hoisted_3$7 = { class: "m-statusbar__item" };
15840
15848
  var _hoisted_4$4 = { class: "m-statusbar__kbd" };
15841
15849
  var _hoisted_5$3 = { class: "m-statusbar__kbd" };
15842
15850
  var _hoisted_6$3 = { class: "m-statusbar__item" };
15843
- var _hoisted_7$2 = { class: "m-statusbar__kbd" };
15844
- var _hoisted_8$2 = { class: "m-statusbar__item" };
15851
+ var _hoisted_7$3 = { class: "m-statusbar__kbd" };
15852
+ var _hoisted_8$3 = { class: "m-statusbar__item" };
15845
15853
  var _hoisted_9$2 = { class: "m-statusbar__item" };
15846
15854
  var _hoisted_10$1 = { class: "m-statusbar__kbd" };
15847
15855
  var _hoisted_11$1 = { class: "m-statusbar__item" };
@@ -15866,9 +15874,9 @@ var Statusbar_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
15866
15874
  return openBlock(), createElementBlock("div", _hoisted_1$12, [createElementVNode("div", _hoisted_2$7, [unref(state) === "typing" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
15867
15875
  createElementVNode("div", _hoisted_3$7, [createElementVNode("span", _hoisted_4$4, toDisplayString(unref(getKbd)("Command")), 1), createElementVNode("span", _hoisted_5$3, toDisplayString(unref(getKbd)("Enter")), 1)]),
15868
15876
  _cache[1] || (_cache[1] = createElementVNode("span", null, "/", -1)),
15869
- createElementVNode("div", _hoisted_6$3, [createElementVNode("span", _hoisted_7$2, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("commitChanges")), 1)])
15877
+ createElementVNode("div", _hoisted_6$3, [createElementVNode("span", _hoisted_7$3, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("commitChanges")), 1)])
15870
15878
  ], 64)) : unref(state) === "transforming" || unref(state) === "moving" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
15871
- createElementVNode("div", _hoisted_8$2, [createVNode(unref(Icon_default), { icon: "$mouseRightClick" })]),
15879
+ createElementVNode("div", _hoisted_8$3, [createVNode(unref(Icon_default), { icon: "$mouseRightClick" })]),
15872
15880
  _cache[3] || (_cache[3] = createElementVNode("span", null, "\xA0/\xA0", -1)),
15873
15881
  createElementVNode("div", _hoisted_9$2, [createElementVNode("span", _hoisted_10$1, toDisplayString(unref(getKbd)("Escape")), 1), createElementVNode("span", null, toDisplayString(unref(t)("cancel")), 1)]),
15874
15882
  unref(state) === "moving" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[2] || (_cache[2] = createElementVNode("div", { class: "m-statusbar__divider" }, null, -1)), createElementVNode("div", _hoisted_11$1, [createElementVNode("span", _hoisted_12$1, toDisplayString(unref(getKbd)("Shift")), 1), createElementVNode("span", null, toDisplayString(unref(t)("constrainMovement")), 1)])], 64)) : createCommentVNode("", true)
@@ -16582,13 +16590,13 @@ var _hoisted_3$6 = ["onPointerdown"];
16582
16590
  var _hoisted_4$3 = ["onPointerdown"];
16583
16591
  var _hoisted_5$2 = ["onPointerdown"];
16584
16592
  var _hoisted_6$2 = ["onPointerdown"];
16585
- var _hoisted_7$1 = [
16593
+ var _hoisted_7$2 = [
16586
16594
  "onPointerdown",
16587
16595
  "onPointerenter",
16588
16596
  "onDblclick",
16589
16597
  "onContextmenu"
16590
16598
  ];
16591
- var _hoisted_8$1 = ["disabled"];
16599
+ var _hoisted_8$2 = ["disabled"];
16592
16600
  var _hoisted_9$1 = ["disabled"];
16593
16601
  var COL_H = 22;
16594
16602
  var ROW_W = 28;
@@ -17268,7 +17276,7 @@ var TableEditor_default = /* @__PURE__ */ defineComponent({
17268
17276
  onPointerenter: ($event) => onCellPointerenter(cell),
17269
17277
  onDblclick: ($event) => onCellDblclick($event, cell),
17270
17278
  onContextmenu: ($event) => onCellContextmenu($event, cell)
17271
- }, null, 44, _hoisted_7$1);
17279
+ }, null, 44, _hoisted_7$2);
17272
17280
  }), 128)),
17273
17281
  createElementVNode("div", {
17274
17282
  class: normalizeClass(["m-table-editor__selection", { "m-table-editor__selection--range": !isSingleSelection.value }]),
@@ -17323,7 +17331,7 @@ var TableEditor_default = /* @__PURE__ */ defineComponent({
17323
17331
  class: "m-table-editor__menu-item",
17324
17332
  disabled: isSingleSelection.value,
17325
17333
  onClick: _cache[9] || (_cache[9] = ($event) => doMerge())
17326
- }, toDisplayString(unref(t)("table:mergeCells")), 9, _hoisted_8$1),
17334
+ }, toDisplayString(unref(t)("table:mergeCells")), 9, _hoisted_8$2),
17327
17335
  createElementVNode("button", {
17328
17336
  class: "m-table-editor__menu-item",
17329
17337
  disabled: !selectedAnchorMerged.value,
@@ -17743,8 +17751,8 @@ var _hoisted_3$3 = { class: "m-timeline__time--muted" };
17743
17751
  var _hoisted_4$1 = { class: "m-timeline__controls" };
17744
17752
  var _hoisted_5$1 = ["title"];
17745
17753
  var _hoisted_6$1 = ["title"];
17746
- var _hoisted_7 = ["title"];
17747
- var _hoisted_8 = ["title"];
17754
+ var _hoisted_7$1 = ["title"];
17755
+ var _hoisted_8$1 = ["title"];
17748
17756
  var _hoisted_9 = ["title"];
17749
17757
  var _hoisted_10 = ["title"];
17750
17758
  var _hoisted_11 = { class: "m-timeline__main" };
@@ -17864,13 +17872,13 @@ var Timeline_default = /* @__PURE__ */ defineComponent({
17864
17872
  type: "button",
17865
17873
  title: unref(paused) ? unref(t)("play") : unref(t)("pause"),
17866
17874
  onClick: _cache[2] || (_cache[2] = ($event) => unref(exec)("togglePlay"))
17867
- }, [createVNode(unref(Icon_default), { icon: unref(paused) ? "$play" : "$pause" }, null, 8, ["icon"])], 8, _hoisted_7),
17875
+ }, [createVNode(unref(Icon_default), { icon: unref(paused) ? "$play" : "$pause" }, null, 8, ["icon"])], 8, _hoisted_7$1),
17868
17876
  createElementVNode("button", {
17869
17877
  class: "m-timeline__btn",
17870
17878
  type: "button",
17871
17879
  title: unref(t)("stepForward"),
17872
17880
  onClick: _cache[3] || (_cache[3] = ($event) => unref(exec)("stepForward"))
17873
- }, [createVNode(unref(Icon_default), { icon: "$stepForward" })], 8, _hoisted_8),
17881
+ }, [createVNode(unref(Icon_default), { icon: "$stepForward" })], 8, _hoisted_8$1),
17874
17882
  createElementVNode("button", {
17875
17883
  class: "m-timeline__btn",
17876
17884
  type: "button",
@@ -19227,29 +19235,31 @@ var _hoisted_2$1 = {
19227
19235
  };
19228
19236
  var _hoisted_3$1 = ["d"];
19229
19237
  var _hoisted_4 = ["onPointerdown"];
19230
- var _hoisted_5 = ["onClick"];
19231
- var _hoisted_6 = { class: "m-workflow__menu-kbd" };
19238
+ var _hoisted_5 = { class: "m-workflow__menu-title" };
19239
+ var _hoisted_6 = ["onClick"];
19240
+ var _hoisted_7 = { class: "m-workflow__menu-item-main" };
19241
+ var _hoisted_8 = { class: "m-workflow__menu-kbd" };
19232
19242
  var PORT_GAP = 16;
19233
19243
  //#endregion
19234
19244
  //#region src/components/Workflow.vue
19235
19245
  var Workflow_default = /* @__PURE__ */ defineComponent({
19236
19246
  __name: "Workflow",
19237
19247
  setup(__props) {
19238
- const { mode, elementSelection, getAabb, camera, drawboardAabb, root, isElement, exec } = useEditor();
19248
+ const { mode, elementSelection, getAabb, camera, drawboardAabb, root, isElement, exec, t } = useEditor();
19239
19249
  const NODE_TYPES = [
19240
19250
  {
19241
19251
  type: "text",
19242
- label: "文字生成",
19252
+ icon: "$text",
19243
19253
  kbd: "⇧T"
19244
19254
  },
19245
19255
  {
19246
19256
  type: "image",
19247
- label: "图片生成",
19257
+ icon: "$image",
19248
19258
  kbd: "⇧I"
19249
19259
  },
19250
19260
  {
19251
19261
  type: "video",
19252
- label: "视频生成",
19262
+ icon: "$video",
19253
19263
  kbd: "⇧V"
19254
19264
  }
19255
19265
  ];
@@ -19394,13 +19404,13 @@ var Workflow_default = /* @__PURE__ */ defineComponent({
19394
19404
  left: `${menu.value.x}px`,
19395
19405
  top: `${menu.value.y}px`
19396
19406
  })
19397
- }, [_cache[0] || (_cache[0] = createElementVNode("div", { class: "m-workflow__menu-title" }, " 新增节点 ", -1)), (openBlock(), createElementBlock(Fragment, null, renderList(NODE_TYPES, (n) => {
19407
+ }, [createElementVNode("div", _hoisted_5, toDisplayString(unref(t)("creator")), 1), (openBlock(), createElementBlock(Fragment, null, renderList(NODE_TYPES, (n) => {
19398
19408
  return createElementVNode("button", {
19399
19409
  key: n.type,
19400
19410
  type: "button",
19401
19411
  class: "m-workflow__menu-item",
19402
19412
  onClick: ($event) => chooseNodeType(n.type)
19403
- }, [createElementVNode("span", null, toDisplayString(n.label), 1), createElementVNode("span", _hoisted_6, toDisplayString(n.kbd), 1)], 8, _hoisted_5);
19413
+ }, [createElementVNode("span", _hoisted_7, [createVNode(unref(Icon_default), { icon: n.icon }, null, 8, ["icon"]), createTextVNode(" " + toDisplayString(unref(t)(`workflow:${n.type}`)), 1)]), createElementVNode("span", _hoisted_8, toDisplayString(n.kbd), 1)], 8, _hoisted_6);
19404
19414
  }), 64))], 4)], 64)) : createCommentVNode("", true)
19405
19415
  ])) : createCommentVNode("", true);
19406
19416
  };
@@ -19408,22 +19418,18 @@ var Workflow_default = /* @__PURE__ */ defineComponent({
19408
19418
  });
19409
19419
  //#endregion
19410
19420
  //#region src/plugins/workflow.ts
19421
+ function placeholderImage(svg) {
19422
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
19423
+ }
19424
+ var IMAGE_PLACEHOLDER = placeholderImage("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"380\" height=\"280\" viewBox=\"0 0 380 280\" fill=\"none\"><rect width=\"380\" height=\"280\" rx=\"20\" fill=\"#f3f4f6\"/><rect x=\"124\" y=\"92\" width=\"132\" height=\"96\" rx=\"12\" stroke=\"#c8ccd4\" stroke-width=\"6\" stroke-linejoin=\"round\"/><circle cx=\"156\" cy=\"124\" r=\"12\" fill=\"#c8ccd4\"/><path d=\"M132 184l40-40 26 26 22-22 36 36\" stroke=\"#c8ccd4\" stroke-width=\"6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>");
19425
+ var VIDEO_PLACEHOLDER = placeholderImage("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"380\" height=\"280\" viewBox=\"0 0 380 280\" fill=\"none\"><rect width=\"380\" height=\"280\" rx=\"20\" fill=\"#f3f4f6\"/><circle cx=\"190\" cy=\"140\" r=\"48\" stroke=\"#c8ccd4\" stroke-width=\"6\"/><path d=\"M178 116l40 24-40 24z\" fill=\"#c8ccd4\"/></svg>");
19411
19426
  var DEFAULT_NODES = {
19412
19427
  text: {
19413
- label: "文字生成",
19414
- title: "✍️ 双击此处输入文字..",
19415
- body: ["可以在此输入提示词,比如画面描述、产品卖点、活动主题、场景描述、品牌 slogan 等内容。若暂时没有明确内容,也可先输入关键词或草稿,后续随时修改优化哦!", "也可以在底部输入框输入需求,让 AI 自动完成文字生成。"]
19416
- },
19417
- image: {
19418
- label: "图片生成",
19419
- title: "🖼️ 双击此处描述画面..",
19420
- body: ["输入画面描述、风格、主体等内容,让 AI 自动完成图片生成。"]
19428
+ title: "✍️ Double-click to add text",
19429
+ body: ["Or describe it below and let AI write for you."]
19421
19430
  },
19422
- video: {
19423
- label: "视频生成",
19424
- title: "🎬 双击此处描述视频..",
19425
- body: ["输入分镜、动作、风格等内容,让 AI 自动完成视频生成。"]
19426
- }
19431
+ image: { image: IMAGE_PLACEHOLDER },
19432
+ video: { image: VIDEO_PLACEHOLDER }
19427
19433
  };
19428
19434
  //#endregion
19429
19435
  //#region src/plugins/index.ts
@@ -19487,26 +19493,32 @@ var plugins = [
19487
19493
  }
19488
19494
  function createWorkflowNode(type) {
19489
19495
  const t = getTemplate(type);
19490
- return {
19491
- name: t.label ?? type,
19496
+ const node = {
19497
+ name: t.label,
19492
19498
  style: {
19493
19499
  width: 380,
19494
19500
  height: 280,
19495
19501
  borderRadius: 20,
19496
- padding: 28,
19497
- fontSize: 16,
19498
- lineHeight: 1.6,
19499
- backgroundColor: "#ffffff",
19500
19502
  borderColor: "#ececf0",
19501
19503
  borderWidth: 1
19502
19504
  },
19503
- text: { content: buildContent(t) },
19504
19505
  meta: {
19505
19506
  inPptIs: "Shape",
19506
19507
  inCanvasIs: "Element2D",
19507
19508
  inEditorIs: `Workflow${type.charAt(0).toUpperCase()}${type.slice(1)}`
19508
19509
  }
19509
19510
  };
19511
+ if (t.image) node.foreground = { image: t.image };
19512
+ else {
19513
+ Object.assign(node.style, {
19514
+ padding: 28,
19515
+ fontSize: 16,
19516
+ lineHeight: 1.6,
19517
+ backgroundColor: "#ffffff"
19518
+ });
19519
+ node.text = { content: buildContent(t) };
19520
+ }
19521
+ return node;
19510
19522
  }
19511
19523
  function addWorkflowNode(type, position) {
19512
19524
  return addElement(createWorkflowNode(type), {
@@ -91,6 +91,9 @@ declare const _default: {
91
91
  collapse: string;
92
92
  statusbar: string;
93
93
  creator: string;
94
+ 'workflow:text': string;
95
+ 'workflow:image': string;
96
+ 'workflow:video': string;
94
97
  memoryManager: string;
95
98
  toolbelt: string;
96
99
  msaa: string;
@@ -91,6 +91,9 @@ declare const _default: {
91
91
  stepForward: string;
92
92
  collapse: string;
93
93
  creator: string;
94
+ 'workflow:text': string;
95
+ 'workflow:image': string;
96
+ 'workflow:video': string;
94
97
  memoryManager: string;
95
98
  toolbelt: string;
96
99
  msaa: string;
@@ -2,12 +2,20 @@ import type { Element2D } from 'modern-canvas';
2
2
  declare global {
3
3
  namespace Mce {
4
4
  interface WorkflowNodeTemplate {
5
- /** Layer-panel name (and header label). */
5
+ /**
6
+ * 覆盖节点名(写入 node.name)。不设时图层名走 i18n(key:workflow:<type>),
7
+ * 切换语言即时生效;菜单项始终走 i18n。
8
+ */
6
9
  label?: string;
7
10
  /** Bold first line shown inside the node. */
8
11
  title?: string;
9
12
  /** Gray hint paragraphs under the title. */
10
13
  body?: string[];
14
+ /**
15
+ * 默认占位图。设置后节点渲染这张图片而非 title/body 文字
16
+ * (图片/视频生成节点用它呈现一张默认图)。
17
+ */
18
+ image?: string;
11
19
  }
12
20
  interface Options {
13
21
  /** Override/extend the workflow node content templates, keyed by node type. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mce",
3
3
  "type": "module",
4
- "version": "0.21.0",
4
+ "version": "0.22.0",
5
5
  "description": "A headless infinite canvas editor framework built on WebGL rendering, supports exporting to image, video, and PPT. Only the ESM.",
6
6
  "author": "wxm",
7
7
  "license": "MIT",
@@ -69,6 +69,7 @@
69
69
  },
70
70
  "peerDependencies": {
71
71
  "lottie-web": "^5",
72
+ "modern-gif": "^2",
72
73
  "typescript": ">=4.7",
73
74
  "vue": "^3.5.0",
74
75
  "yoga-layout": "^3"
@@ -77,6 +78,9 @@
77
78
  "lottie-web": {
78
79
  "optional": true
79
80
  },
81
+ "modern-gif": {
82
+ "optional": true
83
+ },
80
84
  "typescript": {
81
85
  "optional": true
82
86
  },