mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 11:28:30 +08:00
Rework wasm initialization and reduce global state (#379)
* wasm: do the async initialization only once This allows the rest of the app to access wasm synchronously. This allows removing of a global. * provide the wasm via vue provide/inject. There's still code directly accessing the wasm. That will be changed later. * MenuBarInput: use injected wasm instead of the global instance * Let the App handle event listeners * move stateful modules into state/ * state/fullscreen: create per instance * App: load the initial document list on mount. This got lost a few commits ago. Now it's back. * state/dialog: create per instance * util/input: remove dependency on global dialog instance * state/documents: create per instance * reponse-handler: move into EditorWasm * comingSoon: move into dialog * wasm: allow instantiating multiple editors * input handlers: do not look at canvases outside the mounted App * input: listen on the container instead of the window when possible * - removed proxy from wasm-loader - integrated with js-dispatcher - state functions to classes - integrated some upstream changes * fix errors caused by merge * Getting closer: - added global state to track all instances - fix fullscreen close trigger - wasm-loader is statefull - panic across instanes * - fix outline while using editor - removed circular import rule - added editorInstance to js message constructor * - changed input handler to a class - still need a better way of handeling it in App.vue * - fixed single instance of inputManager to weakmap * - fix no-explicit-any in a few places - removed global state from input.ts * simplified two long lines * removed global state * removed $data from App * add mut self to functions in api.rs * Update Workspace.vue remove outdated import * fixed missing import * Changes throughout code review; note this causes some bugs to be fixed in a later commit * PR review round 1 * - fix coming soon bugs - changed folder structure * moved declaration to .d.ts * - changed from classes to functions - moved decs back to app.vue * removed need to export js function to rust * changed folder structure * fixed indentation breaking multiline strings * Fix eslint rule to whitelist @/../ * Simplify strip-indents implementation * replace type assertions with better annotations or proper runtime checks * Small tweaks and code rearranging improvements after second code review pass * maybe fix mouse events * Add back preventDefault for mouse scroll * code review round 2 * Comment improvements * -removed runtime checks - fixed layers not showing * - extened proxy to cover classes - stopped multiple panics from logging - Stop wasm-bindgen from mut ref counting our struct * cleaned up messageConstructors exports * Fix input and fullscreen regressions Co-authored-by: Max Fisher <maxmfishernj@gmail.com> Co-authored-by: mfish33 <32677537+mfish33@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Max Fisher
mfish33
Keavon Chambers
parent
3c1ed1a235
commit
0aefa1bb07
@@ -18,7 +18,7 @@
|
||||
|
||||
<Separator :type="SeparatorType.Unrelated" />
|
||||
|
||||
<OptionalInput v-model:checked="gridEnabled" @update:checked="comingSoon(318)" :icon="'Grid'" title="Grid" />
|
||||
<OptionalInput v-model:checked="gridEnabled" @update:checked="dialog.comingSoon(318)" :icon="'Grid'" title="Grid" />
|
||||
<PopoverButton>
|
||||
<h3>Grid</h3>
|
||||
<p>The contents of this popover menu are coming soon</p>
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<Separator :type="SeparatorType.Unrelated" />
|
||||
|
||||
<OptionalInput v-model:checked="overlaysEnabled" @update:checked="comingSoon(99)" :icon="'Overlays'" title="Overlays" />
|
||||
<OptionalInput v-model:checked="overlaysEnabled" @update:checked="dialog.comingSoon(99)" :icon="'Overlays'" title="Overlays" />
|
||||
<PopoverButton>
|
||||
<h3>Overlays</h3>
|
||||
<p>The contents of this popover menu are coming soon</p>
|
||||
@@ -70,31 +70,31 @@
|
||||
<LayoutCol :class="'shelf'">
|
||||
<div class="tools scrollable-y">
|
||||
<ShelfItemInput icon="LayoutSelectTool" title="Select Tool (V)" :active="activeTool === 'Select'" :action="() => selectTool('Select')" />
|
||||
<ShelfItemInput icon="LayoutCropTool" title="Crop Tool" :active="activeTool === 'Crop'" :action="() => comingSoon(289) && selectTool('Crop')" />
|
||||
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => comingSoon(155) && selectTool('Navigate')" />
|
||||
<ShelfItemInput icon="LayoutCropTool" title="Crop Tool" :active="activeTool === 'Crop'" :action="() => dialog.comingSoon(289) && selectTool('Crop')" />
|
||||
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => dialog.comingSoon(155) && selectTool('Navigate')" />
|
||||
<ShelfItemInput icon="LayoutEyedropperTool" title="Eyedropper Tool (I)" :active="activeTool === 'Eyedropper'" :action="() => selectTool('Eyedropper')" />
|
||||
|
||||
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
|
||||
|
||||
<ShelfItemInput icon="ParametricTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => comingSoon(153) && selectTool('Text')" />
|
||||
<ShelfItemInput icon="ParametricTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => dialog.comingSoon(153) && selectTool('Text')" />
|
||||
<ShelfItemInput icon="ParametricFillTool" title="Fill Tool (F)" :active="activeTool === 'Fill'" :action="() => selectTool('Fill')" />
|
||||
<ShelfItemInput icon="ParametricGradientTool" title="Gradient Tool (H)" :active="activeTool === 'Gradient'" :action="() => comingSoon() && selectTool('Gradient')" />
|
||||
<ShelfItemInput icon="ParametricGradientTool" title="Gradient Tool (H)" :active="activeTool === 'Gradient'" :action="() => dialog.comingSoon() && selectTool('Gradient')" />
|
||||
|
||||
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
|
||||
|
||||
<ShelfItemInput icon="RasterBrushTool" title="Brush Tool (B)" :active="activeTool === 'Brush'" :action="() => comingSoon() && selectTool('Brush')" />
|
||||
<ShelfItemInput icon="RasterHealTool" title="Heal Tool (J)" :active="activeTool === 'Heal'" :action="() => comingSoon() && selectTool('Heal')" />
|
||||
<ShelfItemInput icon="RasterCloneTool" title="Clone Tool (C)" :active="activeTool === 'Clone'" :action="() => comingSoon() && selectTool('Clone')" />
|
||||
<ShelfItemInput icon="RasterPatchTool" title="Patch Tool" :active="activeTool === 'Patch'" :action="() => comingSoon() && selectTool('Patch')" />
|
||||
<ShelfItemInput icon="RasterBlurSharpenTool" title="Detail Tool (D)" :active="activeTool === 'BlurSharpen'" :action="() => comingSoon() && selectTool('BlurSharpen')" />
|
||||
<ShelfItemInput icon="RasterRelightTool" title="Relight Tool (O)" :active="activeTool === 'Relight'" :action="() => comingSoon() && selectTool('Relight')" />
|
||||
<ShelfItemInput icon="RasterBrushTool" title="Brush Tool (B)" :active="activeTool === 'Brush'" :action="() => dialog.comingSoon() && selectTool('Brush')" />
|
||||
<ShelfItemInput icon="RasterHealTool" title="Heal Tool (J)" :active="activeTool === 'Heal'" :action="() => dialog.comingSoon() && selectTool('Heal')" />
|
||||
<ShelfItemInput icon="RasterCloneTool" title="Clone Tool (C)" :active="activeTool === 'Clone'" :action="() => dialog.comingSoon() && selectTool('Clone')" />
|
||||
<ShelfItemInput icon="RasterPatchTool" title="Patch Tool" :active="activeTool === 'Patch'" :action="() => dialog.comingSoon() && selectTool('Patch')" />
|
||||
<ShelfItemInput icon="RasterBlurSharpenTool" title="Detail Tool (D)" :active="activeTool === 'BlurSharpen'" :action="() => dialog.comingSoon() && selectTool('BlurSharpen')" />
|
||||
<ShelfItemInput icon="RasterRelightTool" title="Relight Tool (O)" :active="activeTool === 'Relight'" :action="() => dialog.comingSoon() && selectTool('Relight')" />
|
||||
|
||||
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
|
||||
|
||||
<ShelfItemInput icon="VectorPathTool" title="Path Tool (A)" :active="activeTool === 'Path'" :action="() => selectTool('Path')" />
|
||||
<ShelfItemInput icon="VectorPenTool" title="Pen Tool (P)" :active="activeTool === 'Pen'" :action="() => selectTool('Pen')" />
|
||||
<ShelfItemInput icon="VectorFreehandTool" title="Freehand Tool (N)" :active="activeTool === 'Freehand'" :action="() => comingSoon() && selectTool('Freehand')" />
|
||||
<ShelfItemInput icon="VectorSplineTool" title="Spline Tool" :active="activeTool === 'Spline'" :action="() => comingSoon() && selectTool('Spline')" />
|
||||
<ShelfItemInput icon="VectorFreehandTool" title="Freehand Tool (N)" :active="activeTool === 'Freehand'" :action="() => dialog.comingSoon() && selectTool('Freehand')" />
|
||||
<ShelfItemInput icon="VectorSplineTool" title="Spline Tool" :active="activeTool === 'Spline'" :action="() => dialog.comingSoon() && selectTool('Spline')" />
|
||||
<ShelfItemInput icon="VectorLineTool" title="Line Tool (L)" :active="activeTool === 'Line'" :action="() => selectTool('Line')" />
|
||||
<ShelfItemInput icon="VectorRectangleTool" title="Rectangle Tool (M)" :active="activeTool === 'Rectangle'" :action="() => selectTool('Rectangle')" />
|
||||
<ShelfItemInput icon="VectorEllipseTool" title="Ellipse Tool (E)" :active="activeTool === 'Ellipse'" :action="() => selectTool('Ellipse')" />
|
||||
@@ -238,11 +238,8 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { subscribeJsMessage } from "@/utilities/js-message-dispatcher";
|
||||
import { UpdateCanvas, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/utilities/js-messages";
|
||||
import { UpdateCanvas, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/dispatcher/js-messages";
|
||||
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
|
||||
import { comingSoon } from "@/utilities/errors";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
@@ -261,27 +258,13 @@ import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue";
|
||||
import ToolOptions from "@/components/widgets/options/ToolOptions.vue";
|
||||
import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
const documentModeEntries: SectionsOfMenuListEntries = [
|
||||
[
|
||||
{ label: "Design Mode", icon: "ViewportDesignMode" },
|
||||
{ label: "Select Mode", icon: "ViewportSelectMode", action: () => comingSoon(330) },
|
||||
{ label: "Guide Mode", icon: "ViewportGuideMode", action: () => comingSoon(331) },
|
||||
],
|
||||
];
|
||||
const viewModeEntries: RadioEntries = [
|
||||
{ value: "normal", icon: "ViewModeNormal", tooltip: "View Mode: Normal" },
|
||||
{ value: "outline", icon: "ViewModeOutline", tooltip: "View Mode: Outline", action: () => comingSoon(319) },
|
||||
{ value: "pixels", icon: "ViewModePixels", tooltip: "View Mode: Pixels", action: () => comingSoon(320) },
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["editor", "dialog"],
|
||||
methods: {
|
||||
async setSnap(newStatus: boolean) {
|
||||
(await wasm).set_snapping(newStatus);
|
||||
setSnap(newStatus: boolean) {
|
||||
this.editor.instance.set_snapping(newStatus);
|
||||
},
|
||||
async viewportResize() {
|
||||
viewportResize() {
|
||||
const canvas = this.$refs.canvas as HTMLElement;
|
||||
// Get the width and height rounded up to the nearest even number because resizing is centered and dividing an odd number by 2 for centering causes antialiasing
|
||||
let width = Math.ceil(parseFloat(getComputedStyle(canvas).width));
|
||||
@@ -292,73 +275,73 @@ export default defineComponent({
|
||||
this.canvasSvgWidth = `${width}px`;
|
||||
this.canvasSvgHeight = `${height}px`;
|
||||
},
|
||||
async setCanvasZoom(newZoom: number) {
|
||||
(await wasm).set_canvas_zoom(newZoom / 100);
|
||||
setCanvasZoom(newZoom: number) {
|
||||
this.editor.instance.set_canvas_zoom(newZoom / 100);
|
||||
},
|
||||
async increaseCanvasZoom() {
|
||||
(await wasm).increase_canvas_zoom();
|
||||
increaseCanvasZoom() {
|
||||
this.editor.instance.increase_canvas_zoom();
|
||||
},
|
||||
async decreaseCanvasZoom() {
|
||||
(await wasm).decrease_canvas_zoom();
|
||||
decreaseCanvasZoom() {
|
||||
this.editor.instance.decrease_canvas_zoom();
|
||||
},
|
||||
async setRotation(newRotation: number) {
|
||||
(await wasm).set_rotation(newRotation * (Math.PI / 180));
|
||||
setRotation(newRotation: number) {
|
||||
this.editor.instance.set_rotation(newRotation * (Math.PI / 180));
|
||||
},
|
||||
async translateCanvasX(newValue: number) {
|
||||
translateCanvasX(newValue: number) {
|
||||
const delta = newValue - this.scrollbarPos.x;
|
||||
this.scrollbarPos.x = newValue;
|
||||
(await wasm).translate_canvas(-delta * this.scrollbarMultiplier.x, 0);
|
||||
this.editor.instance.translate_canvas(-delta * this.scrollbarMultiplier.x, 0);
|
||||
},
|
||||
async translateCanvasY(newValue: number) {
|
||||
translateCanvasY(newValue: number) {
|
||||
const delta = newValue - this.scrollbarPos.y;
|
||||
this.scrollbarPos.y = newValue;
|
||||
(await wasm).translate_canvas(0, -delta * this.scrollbarMultiplier.y);
|
||||
this.editor.instance.translate_canvas(0, -delta * this.scrollbarMultiplier.y);
|
||||
},
|
||||
async pageX(delta: number) {
|
||||
pageX(delta: number) {
|
||||
const move = delta < 0 ? 1 : -1;
|
||||
(await wasm).translate_canvas_by_fraction(move, 0);
|
||||
this.editor.instance.translate_canvas_by_fraction(move, 0);
|
||||
},
|
||||
async pageY(delta: number) {
|
||||
pageY(delta: number) {
|
||||
const move = delta < 0 ? 1 : -1;
|
||||
(await wasm).translate_canvas_by_fraction(0, move);
|
||||
this.editor.instance.translate_canvas_by_fraction(0, move);
|
||||
},
|
||||
async selectTool(toolName: string) {
|
||||
(await wasm).select_tool(toolName);
|
||||
selectTool(toolName: string) {
|
||||
this.editor.instance.select_tool(toolName);
|
||||
},
|
||||
async swapWorkingColors() {
|
||||
(await wasm).swap_colors();
|
||||
swapWorkingColors() {
|
||||
this.editor.instance.swap_colors();
|
||||
},
|
||||
async resetWorkingColors() {
|
||||
(await wasm).reset_colors();
|
||||
resetWorkingColors() {
|
||||
this.editor.instance.reset_colors();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
subscribeJsMessage(UpdateCanvas, (updateCanvas) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(UpdateCanvas, (updateCanvas) => {
|
||||
this.viewportSvg = updateCanvas.document;
|
||||
});
|
||||
|
||||
subscribeJsMessage(UpdateScrollbars, (updateScrollbars) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(UpdateScrollbars, (updateScrollbars) => {
|
||||
this.scrollbarPos = updateScrollbars.position;
|
||||
this.scrollbarSize = updateScrollbars.size;
|
||||
this.scrollbarMultiplier = updateScrollbars.multiplier;
|
||||
});
|
||||
|
||||
subscribeJsMessage(UpdateRulers, (updateRulers) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(UpdateRulers, (updateRulers) => {
|
||||
this.rulerOrigin = updateRulers.origin;
|
||||
this.rulerSpacing = updateRulers.spacing;
|
||||
this.rulerInterval = updateRulers.interval;
|
||||
});
|
||||
|
||||
subscribeJsMessage(SetActiveTool, (setActiveTool) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(SetActiveTool, (setActiveTool) => {
|
||||
this.activeTool = setActiveTool.tool_name;
|
||||
this.activeToolOptions = setActiveTool.tool_options;
|
||||
});
|
||||
|
||||
subscribeJsMessage(SetCanvasZoom, (setCanvasZoom) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(SetCanvasZoom, (setCanvasZoom) => {
|
||||
this.documentZoom = setCanvasZoom.new_zoom * 100;
|
||||
});
|
||||
|
||||
subscribeJsMessage(SetCanvasRotation, (setCanvasRotation) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(SetCanvasRotation, (setCanvasRotation) => {
|
||||
const newRotation = setCanvasRotation.new_radians * (180 / Math.PI);
|
||||
this.documentRotation = (360 + (newRotation % 360)) % 360;
|
||||
});
|
||||
@@ -367,6 +350,19 @@ export default defineComponent({
|
||||
window.addEventListener("DOMContentLoaded", this.viewportResize);
|
||||
},
|
||||
data() {
|
||||
const documentModeEntries: SectionsOfMenuListEntries = [
|
||||
[
|
||||
{ label: "Design Mode", icon: "ViewportDesignMode" },
|
||||
{ label: "Select Mode", icon: "ViewportSelectMode", action: () => this.dialog.comingSoon(330) },
|
||||
{ label: "Guide Mode", icon: "ViewportGuideMode", action: () => this.dialog.comingSoon(331) },
|
||||
],
|
||||
];
|
||||
const viewModeEntries: RadioEntries = [
|
||||
{ value: "normal", icon: "ViewModeNormal", tooltip: "View Mode: Normal" },
|
||||
{ value: "outline", icon: "ViewModeOutline", tooltip: "View Mode: Outline", action: () => this.dialog.comingSoon(319) },
|
||||
{ value: "pixels", icon: "ViewModePixels", tooltip: "View Mode: Pixels", action: () => this.dialog.comingSoon(320) },
|
||||
];
|
||||
|
||||
return {
|
||||
viewportSvg: "",
|
||||
canvasSvgWidth: "100%",
|
||||
@@ -395,7 +391,6 @@ export default defineComponent({
|
||||
ScrollbarDirection,
|
||||
RulerDirection,
|
||||
SeparatorType,
|
||||
comingSoon,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
||||
@@ -197,9 +197,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { subscribeJsMessage } from "@/utilities/js-message-dispatcher";
|
||||
import { BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry, LayerTypeOptions } from "@/utilities/js-messages";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import { BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry, LayerTypeOptions } from "@/dispatcher/js-messages";
|
||||
import { SeparatorType } from "@/components/widgets/widgets";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
@@ -213,8 +211,6 @@ import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
|
||||
import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
const blendModeEntries: SectionsOfMenuListEntries<BlendMode> = [
|
||||
[{ label: "Normal", value: "Normal" }],
|
||||
[
|
||||
@@ -255,6 +251,7 @@ const blendModeEntries: SectionsOfMenuListEntries<BlendMode> = [
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["editor"],
|
||||
data() {
|
||||
return {
|
||||
blendModeEntries,
|
||||
@@ -278,19 +275,19 @@ export default defineComponent({
|
||||
return `${(layer.path.length - 1) * 16}px`;
|
||||
},
|
||||
async toggleLayerVisibility(path: BigUint64Array) {
|
||||
(await wasm).toggle_layer_visibility(path);
|
||||
this.editor.instance.toggle_layer_visibility(path);
|
||||
},
|
||||
async handleNodeConnectorClick(path: BigUint64Array) {
|
||||
(await wasm).toggle_layer_expansion(path);
|
||||
this.editor.instance.toggle_layer_expansion(path);
|
||||
},
|
||||
async setLayerBlendMode() {
|
||||
const blendMode = this.blendModeEntries.flat()[this.blendModeSelectedIndex].value;
|
||||
if (blendMode) {
|
||||
(await wasm).set_blend_mode_for_selected_layers(blendMode);
|
||||
this.editor.instance.set_blend_mode_for_selected_layers(blendMode);
|
||||
}
|
||||
},
|
||||
async setLayerOpacity() {
|
||||
(await wasm).set_opacity_for_selected_layers(this.opacity);
|
||||
this.editor.instance.set_opacity_for_selected_layers(this.opacity);
|
||||
},
|
||||
async handleControlClick(clickedLayer: LayerPanelEntry) {
|
||||
const index = this.layers.indexOf(clickedLayer);
|
||||
@@ -330,7 +327,7 @@ export default defineComponent({
|
||||
this.selectionRangeStartLayer = undefined;
|
||||
this.selectionRangeEndLayer = undefined;
|
||||
|
||||
(await wasm).deselect_all_layers();
|
||||
this.editor.instance.deselect_all_layers();
|
||||
},
|
||||
async fillSelectionRange(start: LayerPanelEntry, end: LayerPanelEntry, selected = true) {
|
||||
const startIndex = this.layers.findIndex((layer) => layer.path.join() === start.path.join());
|
||||
@@ -363,7 +360,7 @@ export default defineComponent({
|
||||
}
|
||||
i += 1;
|
||||
});
|
||||
(await wasm).select_layers(output);
|
||||
this.editor.instance.select_layers(output);
|
||||
},
|
||||
setBlendModeForSelectedLayers() {
|
||||
const selected = this.layers.filter((layer) => layer.layer_data.selected);
|
||||
@@ -407,7 +404,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
subscribeJsMessage(DisplayFolderTreeStructure, (displayFolderTreeStructure) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(DisplayFolderTreeStructure, (displayFolderTreeStructure) => {
|
||||
const path = [] as bigint[];
|
||||
this.layers = [] as LayerPanelEntry[];
|
||||
function recurse(folder: DisplayFolderTreeStructure, layers: LayerPanelEntry[], cache: Map<string, LayerPanelEntry>) {
|
||||
@@ -423,7 +420,7 @@ export default defineComponent({
|
||||
recurse(displayFolderTreeStructure, this.layers, this.layerCache);
|
||||
});
|
||||
|
||||
subscribeJsMessage(UpdateLayer, (updateLayer) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(UpdateLayer, (updateLayer) => {
|
||||
const targetPath = updateLayer.data.path;
|
||||
const targetLayer = updateLayer.data;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user