wip working preferences, wip passing image on hidden widget with serialize
This commit is contained in:
+8
-5
@@ -43,6 +43,7 @@ class Compositor4:
|
||||
"hidden": {
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
"node_id": "UNIQUE_ID",
|
||||
"test":"STRING"
|
||||
},
|
||||
}
|
||||
|
||||
@@ -56,7 +57,8 @@ class Compositor4:
|
||||
node_id = kwargs.pop('node_id', None)
|
||||
|
||||
|
||||
imageName = kwargs.get('imageName', "new.png")
|
||||
imageName = kwargs.get('test', "new.png")
|
||||
#compositionName = kwargs.get('test', "new.png")
|
||||
|
||||
config = kwargs.get('config', "default")
|
||||
padding = config["padding"]
|
||||
@@ -92,15 +94,16 @@ class Compositor4:
|
||||
}
|
||||
|
||||
# break and send a message to the gui as if it was "executed" below
|
||||
detail = {"output": ui, "node": node_id}
|
||||
PromptServer.instance.send_sync("compositor_init", detail)
|
||||
# detail = {"output": ui, "node": node_id}
|
||||
# PromptServer.instance.send_sync("compositor_init", detail)
|
||||
|
||||
imageExists = folder_paths.exists_annotated_filepath(imageName)
|
||||
# block when config changed
|
||||
if imageName == "new.png" or not imageExists or configChanged:
|
||||
#if imageName == "new.png" or not imageExists or configChanged:
|
||||
if not imageExists or configChanged:
|
||||
return {
|
||||
"ui": ui,
|
||||
"result": (ExecutionBlocker(None), ExecutionBlocker(None))
|
||||
"result": (ExecutionBlocker(None), ExecutionBlocker(None))
|
||||
}
|
||||
|
||||
image_path = folder_paths.get_annotated_filepath(imageName)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { Toolbar, ICON_URLS, BUTTON_INDICES, fetchSVGIcon } from './toolbar4.js';
|
||||
class Compositor4 {
|
||||
|
||||
@@ -292,6 +293,7 @@ class Compositor4 {
|
||||
this.fabricCanvas.renderAll();
|
||||
}
|
||||
|
||||
/** downloads the iamge */
|
||||
downloadFile(content, filename) {
|
||||
let link = document.createElement("a");
|
||||
|
||||
@@ -317,6 +319,7 @@ class Compositor4 {
|
||||
}
|
||||
}
|
||||
|
||||
/** saves the image as base64*/
|
||||
exportAsBase64() {
|
||||
return this.fabricCanvas.toDataURL({
|
||||
format: "png",
|
||||
@@ -371,6 +374,41 @@ class Compositor4 {
|
||||
// Redraw the new images on the canvas
|
||||
this.setupImages();
|
||||
}
|
||||
|
||||
dataURLToBlob(dataURL){
|
||||
const parts = dataURL.split(',');
|
||||
const mime = parts[0].match(/:(.*?);/)[1];
|
||||
const binary = atob(parts[1]);
|
||||
const array = [];
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
array.push(binary.charCodeAt(i));
|
||||
}
|
||||
return new Blob([new Uint8Array(array)], {type: mime});
|
||||
}
|
||||
|
||||
// take #compositor3.js as example and make a version of uploadImage that takes the ouptupt of exportAsBase64and uploads it to the /upload/image endpoint, make the method async, keep the naming convention and the logic to hanle the subfolder
|
||||
async uploadImage(dataURL){
|
||||
const b64 = this.dataURLToBlob(dataURL);
|
||||
const UPLOAD_ENDPOINT = "/upload/image";
|
||||
|
||||
const name = `${+new Date()}.png`;
|
||||
const file = new File([b64], name);
|
||||
const body = new FormData();
|
||||
|
||||
body.append("image", file);
|
||||
body.append("subfolder", "compositor");
|
||||
body.append("type", "temp");
|
||||
|
||||
const result = await api.fetchApi(UPLOAD_ENDPOINT, {
|
||||
method: "POST",
|
||||
body,
|
||||
})
|
||||
|
||||
|
||||
return await result.json()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Compositor4;
|
||||
+48
-34
@@ -1,6 +1,8 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
import Compositor4 from "./compositor4.js";
|
||||
import { fabric } from "./fabric.js";
|
||||
import {PREFERENCES, addCompositorPreferences, getPreferences} from "./compositor4preferences.js";
|
||||
import Init from "./init.js";
|
||||
|
||||
const COMPOSITOR = Symbol();
|
||||
@@ -21,23 +23,26 @@ function getCompositorWidget(node, widgetName) {
|
||||
app.registerExtension({
|
||||
name: "Comfy.Compositor4",
|
||||
|
||||
// async getCustomWidgets(app) {
|
||||
// return {
|
||||
// async COMPOSITOR(node, inputName, inputData, app) {
|
||||
|
||||
// const container = document.createElement("div");
|
||||
// await this.initEditor(container);
|
||||
// async getCustomWidgets(app) {
|
||||
// return {
|
||||
// async COMPOSITOR(node, inputName, inputData, app) {
|
||||
|
||||
// /**
|
||||
// * NOTE: hideOnZoom:false FIXES not being able to take screenshot and disappearing on zoom out
|
||||
// * but creates some inconsistencies as lines get too small to be rendered properly
|
||||
// */
|
||||
// return {widget: node.addDOMWidget(inputName, "COMPOSITOR4", container, {hideOnZoom: false})};
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// const container = document.createElement("div");
|
||||
// await this.initEditor(container);
|
||||
|
||||
// /**
|
||||
// * NOTE: hideOnZoom:false FIXES not being able to take screenshot and disappearing on zoom out
|
||||
// * but creates some inconsistencies as lines get too small to be rendered properly
|
||||
// */
|
||||
// return {widget: node.addDOMWidget(inputName, "COMPOSITOR4", container, {hideOnZoom: false})};
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
|
||||
async setup(app) {
|
||||
|
||||
addCompositorPreferences(PREFERENCES);
|
||||
|
||||
function executingMessageHandler(event) {}
|
||||
|
||||
function executedMessageHandler(event, a, b) {}
|
||||
@@ -82,31 +87,40 @@ app.registerExtension({
|
||||
el.style.backgroundColor = "transparent";
|
||||
el.width = 800;
|
||||
el.height = 600;
|
||||
el.id="compositor4container";
|
||||
|
||||
|
||||
|
||||
|
||||
el.id = "compositor4container";
|
||||
el.value = "foobar.jpg";
|
||||
|
||||
const imageNameWidget = getCompositorWidget(node, "imageName");
|
||||
|
||||
|
||||
node.editorWidget = node.addDOMWidget("test", "test", el, {
|
||||
serialize: false,
|
||||
serialize: true,
|
||||
hideOnZoom: false,
|
||||
resizable: false,
|
||||
|
||||
getValue() {
|
||||
console.log("getValue", el.value);
|
||||
return el.value;
|
||||
},
|
||||
setValue(v2) {
|
||||
// imageNameWidget.value = v2;
|
||||
console.log("setValue", v2);
|
||||
el.value = v2;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// const originalSerializeValue = node.editorWidget.serializeValue;
|
||||
// node.editorWidget.serializeValue = (e) => {
|
||||
// console.log("serializeValue",e);
|
||||
// //return el.value;
|
||||
// };
|
||||
|
||||
const init = new Init();
|
||||
const compositor = await init.initEditor(el);
|
||||
console.log("compositor", compositor);
|
||||
node.setSize(compositor.getWidgetDimensions());
|
||||
const preferences = getPreferences()
|
||||
const editor = await init.initEditor(el, preferences);
|
||||
console.log("editor", editor);
|
||||
node.setSize(editor.getWidgetDimensions());
|
||||
|
||||
node.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
const PREFERENCES = {
|
||||
composition: {
|
||||
fill: "#444444", // Dark gray color for the rectangle
|
||||
stroke: "green", // Green border
|
||||
strokeWidth: 1,
|
||||
},
|
||||
toolbar: {
|
||||
fill: "#666666",
|
||||
height: 40,
|
||||
position: { left: 0, top: 0 },
|
||||
buttonSpacing: 4,
|
||||
iconSize: 40, // Set the icon size to 30
|
||||
},
|
||||
fabricCanvas: {
|
||||
backgroundColor: "#555555", // Darker gray color for the canvas background
|
||||
},
|
||||
equalizeHeight: {
|
||||
enabled: true,
|
||||
},
|
||||
snapToGrid: {
|
||||
gridSize: 20,
|
||||
enabled: false,
|
||||
isGridVisible: false,
|
||||
},
|
||||
ignoreTransparentPixels: {
|
||||
enabled: false,
|
||||
},
|
||||
erosColor: "magenta", // Add eros color preference
|
||||
activeBorderColor: "magenta", // Border color when button is active
|
||||
inactiveBorderColor: "black", // Border color when button is inactive
|
||||
toggledBorderColor: "orange", // Border color when button is toggled
|
||||
standalone: false, // whether we are testing this in index.html or in the comfy editor
|
||||
};
|
||||
|
||||
function addCompositorPreferences(preferences) {
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Composition.FILL",
|
||||
name: "Composition Fill",
|
||||
tooltip: "Fill color for the composition rectangle",
|
||||
type: "text",
|
||||
defaultValue: preferences.composition.fill,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Composition.STROKE",
|
||||
name: "Composition Stroke",
|
||||
tooltip: "Stroke color for the composition rectangle",
|
||||
type: "text",
|
||||
defaultValue: preferences.composition.stroke,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Composition.STROKE_WIDTH",
|
||||
name: "Composition Stroke Width",
|
||||
tooltip: "Stroke width for the composition rectangle",
|
||||
type: "number",
|
||||
defaultValue: preferences.composition.strokeWidth,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Toolbar.FILL",
|
||||
name: "Toolbar Fill",
|
||||
tooltip: "Fill color for the toolbar",
|
||||
type: "text",
|
||||
defaultValue: preferences.toolbar.fill,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Toolbar.HEIGHT",
|
||||
name: "Toolbar Height",
|
||||
tooltip: "Height of the toolbar",
|
||||
type: "number",
|
||||
defaultValue: preferences.toolbar.height,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Toolbar.POSITION",
|
||||
name: "Toolbar Position",
|
||||
tooltip: "Position of the toolbar",
|
||||
type: "object",
|
||||
defaultValue: preferences.toolbar.position,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Toolbar.BUTTON_SPACING",
|
||||
name: "Toolbar Button Spacing",
|
||||
tooltip: "Spacing between toolbar buttons",
|
||||
type: "number",
|
||||
defaultValue: preferences.toolbar.buttonSpacing,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Toolbar.ICON_SIZE",
|
||||
name: "Toolbar Icon Size",
|
||||
tooltip: "Size of the toolbar icons",
|
||||
type: "number",
|
||||
defaultValue: preferences.toolbar.iconSize,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.FabricCanvas.BACKGROUND_COLOR",
|
||||
name: "Fabric Canvas Background Color",
|
||||
tooltip: "Background color for the fabric canvas",
|
||||
type: "text",
|
||||
defaultValue: preferences.fabricCanvas.backgroundColor,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.EqualizeHeight.ENABLED",
|
||||
name: "Equalize Height Enabled",
|
||||
tooltip: "Enable or disable equalize height",
|
||||
type: "boolean",
|
||||
defaultValue: preferences.equalizeHeight.enabled,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.SnapToGrid.GRID_SIZE",
|
||||
name: "Snap to Grid Size",
|
||||
tooltip: "Grid size for snap to grid",
|
||||
type: "number",
|
||||
defaultValue: preferences.snapToGrid.gridSize,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.SnapToGrid.ENABLED",
|
||||
name: "Snap to Grid Enabled",
|
||||
tooltip: "Enable or disable snap to grid",
|
||||
type: "boolean",
|
||||
defaultValue: preferences.snapToGrid.enabled,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.SnapToGrid.IS_GRID_VISIBLE",
|
||||
name: "Snap to Grid Visibility",
|
||||
tooltip: "Visibility of the snap to grid",
|
||||
type: "boolean",
|
||||
defaultValue: preferences.snapToGrid.isGridVisible,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.IgnoreTransparentPixels.ENABLED",
|
||||
name: "Ignore Transparent Pixels Enabled",
|
||||
tooltip: "Enable or disable ignoring transparent pixels",
|
||||
type: "boolean",
|
||||
defaultValue: preferences.ignoreTransparentPixels.enabled,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.ErosColor",
|
||||
name: "Eros Color",
|
||||
tooltip: "Color for eros",
|
||||
type: "text",
|
||||
defaultValue: preferences.erosColor,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.ActiveBorderColor",
|
||||
name: "Active Border Color",
|
||||
tooltip: "Border color when button is active",
|
||||
type: "text",
|
||||
defaultValue: preferences.activeBorderColor,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.InactiveBorderColor",
|
||||
name: "Inactive Border Color",
|
||||
tooltip: "Border color when button is inactive",
|
||||
type: "text",
|
||||
defaultValue: preferences.inactiveBorderColor,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.ToggledBorderColor",
|
||||
name: "Toggled Border Color",
|
||||
tooltip: "Border color when button is toggled",
|
||||
type: "text",
|
||||
defaultValue: preferences.toggledBorderColor,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "Compositor4.Standalone",
|
||||
name: "Standalone Mode",
|
||||
tooltip: "Whether we are testing this in index.html or in the comfy editor",
|
||||
type: "boolean",
|
||||
defaultValue: preferences.standalone,
|
||||
onChange: (newVal, oldVal) => {
|
||||
// Handle change
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getPreferences() {
|
||||
return {
|
||||
composition: {
|
||||
fill: app.ui.settings.getSettingValue("Compositor4.Composition.FILL", "#444444"),
|
||||
stroke: app.ui.settings.getSettingValue("Compositor4.Composition.STROKE", "green"),
|
||||
strokeWidth: app.ui.settings.getSettingValue("Compositor4.Composition.STROKE_WIDTH", 1),
|
||||
},
|
||||
toolbar: {
|
||||
fill: app.ui.settings.getSettingValue("Compositor4.Toolbar.FILL", "#666666"),
|
||||
height: app.ui.settings.getSettingValue("Compositor4.Toolbar.HEIGHT", 40),
|
||||
position: app.ui.settings.getSettingValue("Compositor4.Toolbar.POSITION", { left: 0, top: 0 }),
|
||||
buttonSpacing: app.ui.settings.getSettingValue("Compositor4.Toolbar.BUTTON_SPACING", 4),
|
||||
iconSize: app.ui.settings.getSettingValue("Compositor4.Toolbar.ICON_SIZE", 40),
|
||||
},
|
||||
fabricCanvas: {
|
||||
backgroundColor: app.ui.settings.getSettingValue("Compositor4.FabricCanvas.BACKGROUND_COLOR", "#555555"),
|
||||
},
|
||||
equalizeHeight: {
|
||||
enabled: app.ui.settings.getSettingValue("Compositor4.EqualizeHeight.ENABLED", true),
|
||||
},
|
||||
snapToGrid: {
|
||||
gridSize: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.GRID_SIZE", 20),
|
||||
enabled: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.ENABLED", false),
|
||||
isGridVisible: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.IS_GRID_VISIBLE", false),
|
||||
},
|
||||
ignoreTransparentPixels: {
|
||||
enabled: app.ui.settings.getSettingValue("Compositor4.IgnoreTransparentPixels.ENABLED", false),
|
||||
},
|
||||
erosColor: app.ui.settings.getSettingValue("Compositor4.ErosColor", "magenta"),
|
||||
activeBorderColor: app.ui.settings.getSettingValue("Compositor4.ActiveBorderColor", "magenta"),
|
||||
inactiveBorderColor: app.ui.settings.getSettingValue("Compositor4.InactiveBorderColor", "black"),
|
||||
toggledBorderColor: app.ui.settings.getSettingValue("Compositor4.ToggledBorderColor", "orange"),
|
||||
standalone: app.ui.settings.getSettingValue("Compositor4.Standalone", false),
|
||||
};
|
||||
}
|
||||
|
||||
// Call the function to add settings
|
||||
export { addCompositorPreferences, PREFERENCES, getPreferences};
|
||||
@@ -45,6 +45,7 @@
|
||||
activeBorderColor: "magenta", // Border color when button is active
|
||||
inactiveBorderColor: "black", // Border color when button is inactive
|
||||
toggledBorderColor: "orange", // Border color when button is toggled
|
||||
standalone:true,
|
||||
};
|
||||
|
||||
const config = JSON.stringify({
|
||||
|
||||
+3
-36
@@ -1,45 +1,12 @@
|
||||
import Compositor4 from "./compositor4.js";
|
||||
import { fabric } from "./fabric.js";
|
||||
|
||||
class Init {
|
||||
async initEditor(container) {
|
||||
async initEditor(container, preferences) {
|
||||
// const query = 'cats'; // Change this to your desired search query
|
||||
const query = "matisse"; // Change this to your desired search query
|
||||
try {
|
||||
const images = await this.fetchArtInstituteImages(query, 6);
|
||||
|
||||
const PREFERENCES = {
|
||||
composition: {
|
||||
fill: "#444444", // Dark gray color for the rectangle
|
||||
stroke: "green", // Green border
|
||||
strokeWidth: 1,
|
||||
},
|
||||
toolbar: {
|
||||
fill: "#666666",
|
||||
height: 40,
|
||||
position: { left: 0, top: 0 },
|
||||
buttonSpacing: 4,
|
||||
iconSize: 40, // Set the icon size to 30
|
||||
},
|
||||
fabricCanvas: {
|
||||
backgroundColor: "#555555", // Darker gray color for the canvas background
|
||||
},
|
||||
equalizeHeight: {
|
||||
enabled: true,
|
||||
},
|
||||
snapToGrid: {
|
||||
gridSize: 20,
|
||||
enabled: false,
|
||||
isGridVisible: false,
|
||||
},
|
||||
ignoreTransparentPixels: {
|
||||
enabled: false,
|
||||
},
|
||||
erosColor: "magenta", // Add eros color preference
|
||||
activeBorderColor: "magenta", // Border color when button is active
|
||||
inactiveBorderColor: "black", // Border color when button is inactive
|
||||
toggledBorderColor: "orange", // Border color when button is toggled
|
||||
};
|
||||
|
||||
const config = JSON.stringify({
|
||||
width: 800,
|
||||
height: 600,
|
||||
@@ -52,7 +19,7 @@ class Init {
|
||||
});
|
||||
// const container = document.createElement("div");
|
||||
// container.id = "compositor4";
|
||||
const compositor = new Compositor4(container, config, PREFERENCES);
|
||||
const compositor = new Compositor4(container, config, preferences);
|
||||
// trigger the executed event
|
||||
const event = new CustomEvent("executed", { detail: config });
|
||||
document.dispatchEvent(event);
|
||||
|
||||
+14
-2
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
export const ICON_URLS = {
|
||||
loadPreset: "https://icons.getbootstrap.com/assets/icons/file-earmark-arrow-up.svg",
|
||||
savePreset: "https://icons.getbootstrap.com/assets/icons/file-earmark-arrow-down.svg",
|
||||
@@ -203,10 +205,20 @@ export class Toolbar {
|
||||
}
|
||||
|
||||
async addExportButton() {
|
||||
const onClick = () => {
|
||||
const onClick = async() => {
|
||||
console.log("Exporting image...");
|
||||
const base64Image = this.compositor.exportAsBase64();
|
||||
this.compositor.downloadFile(base64Image, `${this.compositor.seed}.png`);
|
||||
//
|
||||
if(this.preferences.standalone){
|
||||
this.compositor.downloadFile(base64Image, `${this.compositor.seed}.png`);
|
||||
|
||||
}else{
|
||||
const result = await this.compositor.uploadImage(base64Image);
|
||||
//
|
||||
this.compositor.containerEl.value = `compositor/${result.name} [temp]`;
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
await this.addToolbarButton(ICON_URLS.export, onClick, BUTTON_INDICES.indexOf("export"));
|
||||
|
||||
Reference in New Issue
Block a user