diff --git a/spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts b/spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts
index 0ffb3e643..2596dc444 100644
--- a/spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts
+++ b/spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts
@@ -27,6 +27,187 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
+// Common theme type
+interface ModalTheme {
+ overlayBg: string;
+ captionBg: string;
+ captionText: string;
+ contentBg: string;
+ contentText: string;
+ buttonBg: string;
+ buttonText: string;
+ buttonBorder: string;
+ buttonHoverBg: string;
+ closeColor: string;
+ itemHoverBg: string;
+ itemSelectedBg: string;
+}
+
+// Helper: Create theme
+function getTheme (darkMode: boolean): ModalTheme {
+ return darkMode ? {
+ overlayBg: 'rgba(0, 0, 0, 0.5)',
+ captionBg: 'rgb(71, 71, 71)', // gray9
+ captionText: 'rgb(214, 214, 214)', // gray27
+ contentBg: 'rgb(87, 87, 87)', // gray11
+ contentText: 'rgb(214, 214, 214)', // gray27
+ buttonBg: 'rgb(71, 71, 71)', // gray9
+ buttonText: 'rgb(214, 214, 214)', // gray27
+ buttonBorder: 'rgb(56, 56, 56)', // gray7
+ buttonHoverBg: 'rgb(79, 79, 79)', // gray10
+ closeColor: 'rgb(168, 168, 168)', // gray21
+ itemHoverBg: 'rgb(79, 79, 79)', // gray10
+ itemSelectedBg: 'rgb(56, 56, 56)', // gray7
+ } : {
+ overlayBg: 'rgba(0, 0, 0, 0.3)',
+ captionBg: 'rgb(247, 247, 247)', // gray31
+ captionText: 'rgb(94, 94, 94)', // gray12
+ contentBg: 'rgb(232, 232, 232)', // gray29
+ contentText: 'rgb(94, 94, 94)', // gray12
+ buttonBg: 'rgb(222, 222, 222)', // gray28
+ buttonText: 'rgb(94, 94, 94)', // gray12
+ buttonBorder: 'rgb(199, 199, 199)', // gray25
+ buttonHoverBg: 'rgb(214, 214, 214)', // gray27
+ closeColor: 'rgb(94, 94, 94)', // gray12
+ itemHoverBg: 'rgb(214, 214, 214)', // gray27
+ itemSelectedBg: 'rgb(199, 199, 199)', // gray25
+ };
+}
+
+// Helper: Create overlay
+function createOverlay (theme: ModalTheme): HTMLDivElement {
+ const overlay = document.createElement('div');
+ overlay.style.cssText = `
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: ${theme.overlayBg};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 999999;
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
+ font-size: 14px;
+ `;
+ return overlay;
+}
+
+// Helper: Create dialog
+function createDialog (theme: ModalTheme, minWidth: number): HTMLDivElement {
+ const dialog = document.createElement('div');
+ dialog.style.cssText = `
+ background: ${theme.contentBg};
+ border-radius: 6px;
+ min-width: ${minWidth}px;
+ max-width: 550px;
+ display: flex;
+ flex-direction: column;
+ filter: drop-shadow(0 4px 5px rgba(10,10,10,0.35)) drop-shadow(0 2px 1px rgba(10,10,10,0.5));
+ `;
+ return dialog;
+}
+
+// Helper: Create caption with close button
+function createCaption (title: string, theme: ModalTheme, onClose: () => void): HTMLDivElement {
+ const caption = document.createElement('div');
+ caption.style.cssText = `
+ background: ${theme.captionBg};
+ color: ${theme.captionText};
+ padding: 6px 10px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ user-select: none;
+ border-radius: 6px 6px 0 0;
+ `;
+
+ const titleSpan = document.createElement('span');
+ titleSpan.textContent = title;
+
+ const closeBtn = document.createElement('button');
+ closeBtn.innerHTML = ``;
+ closeBtn.style.cssText = `
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 2px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 3px;
+ opacity: 0.7;
+ `;
+ closeBtn.onmouseover = () => { closeBtn.style.opacity = '1'; };
+ closeBtn.onmouseout = () => { closeBtn.style.opacity = '0.7'; };
+ closeBtn.addEventListener('click', onClose);
+
+ caption.appendChild(titleSpan);
+ caption.appendChild(closeBtn);
+
+ return caption;
+}
+
+// Helper: Create footer
+function createFooter (): HTMLDivElement {
+ const footer = document.createElement('div');
+ footer.style.cssText = `
+ padding: 8px 14px 12px;
+ display: flex;
+ justify-content: flex-end;
+ gap: 6px;
+ `;
+ return footer;
+}
+
+// Helper: Create button
+function createButton (text: string, theme: ModalTheme, onClick: () => void): HTMLButtonElement {
+ const btn = document.createElement('button');
+ btn.textContent = text;
+ btn.style.cssText = `
+ padding: 4px 14px;
+ border: 1px solid ${theme.buttonBorder};
+ border-radius: 3px;
+ background: ${theme.buttonBg};
+ color: ${theme.buttonText};
+ font-size: 14px;
+ font-family: inherit;
+ cursor: pointer;
+ `;
+ btn.onmouseover = () => { btn.style.background = theme.buttonHoverBg; };
+ btn.onmouseout = () => { btn.style.background = theme.buttonBg; };
+ btn.addEventListener('click', onClick);
+ return btn;
+}
+
+// Helper: Setup modal event handlers
+function setupModalHandlers (overlay: HTMLDivElement, onCancel: () => void, extraKeyHandler?: (e: KeyboardEvent) => boolean) {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') {
+ onCancel();
+ return;
+ }
+ if (extraKeyHandler?.(e)) {
+ return;
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyDown);
+
+ overlay.addEventListener('click', (e) => {
+ if (e.target === overlay) {
+ onCancel();
+ }
+ });
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ overlay.remove();
+ };
+}
+
+// Original interfaces
interface ModalButton {
text: string;
color?: string;
@@ -46,138 +227,35 @@ export function showModal (options: ModalOptions): Promise
return new Promise((resolve) => {
const { title, text, buttons, darkMode = false } = options;
- const theme = darkMode ? {
- overlayBg: 'rgba(0, 0, 0, 0.5)',
- captionBg: 'rgb(71, 71, 71)', // gray9
- captionText: 'rgb(214, 214, 214)', // gray27
- contentBg: 'rgb(87, 87, 87)', // gray11
- contentText: 'rgb(214, 214, 214)', // gray27
- buttonBg: 'rgb(71, 71, 71)', // gray9
- buttonText: 'rgb(214, 214, 214)', // gray27
- buttonBorder: 'rgb(56, 56, 56)', // gray7
- buttonHoverBg: 'rgb(79, 79, 79)', // gray10
- closeColor: 'rgb(168, 168, 168)', // gray21
- } : {
- overlayBg: 'rgba(0, 0, 0, 0.3)',
- captionBg: 'rgb(247, 247, 247)', // gray31
- captionText: 'rgb(94, 94, 94)', // gray12
- contentBg: 'rgb(232, 232, 232)', // gray29
- contentText: 'rgb(94, 94, 94)', // gray12
- buttonBg: 'rgb(222, 222, 222)', // gray28
- buttonText: 'rgb(94, 94, 94)', // gray12
- buttonBorder: 'rgb(199, 199, 199)', // gray25
- buttonHoverBg: 'rgb(214, 214, 214)', // gray27
- closeColor: 'rgb(94, 94, 94)', // gray12
- };
+ const theme = getTheme(darkMode);
+ const overlay = createOverlay(theme);
+ const dialog = createDialog(theme, 200);
- const overlay = document.createElement('div');
- overlay.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: ${theme.overlayBg};
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 999999;
- font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
- font-size: 14px;
- `;
+ const cleanup = setupModalHandlers(overlay, () => {
+ cleanup();
+ resolve(undefined);
+ });
- const dialog = document.createElement('div');
- dialog.style.cssText = `
- background: ${theme.contentBg};
- border-radius: 6px;
- min-width: 200px;
- max-width: 550px;
- display: flex;
- flex-direction: column;
- filter: drop-shadow(0 4px 5px rgba(10,10,10,0.35)) drop-shadow(0 2px 1px rgba(10,10,10,0.5));
- `;
-
- const caption = document.createElement('div');
- caption.style.cssText = `
- background: ${theme.captionBg};
- color: ${theme.captionText};
- padding: 6px 10px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- user-select: none;
- border-radius: 6px 6px 0 0;
- `;
-
- const titleSpan = document.createElement('span');
- titleSpan.textContent = title;
-
- const closeBtn = document.createElement('button');
- closeBtn.innerHTML = ``;
- closeBtn.style.cssText = `
- background: transparent;
- border: none;
- cursor: pointer;
- padding: 2px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 3px;
- opacity: 0.7;
- `;
- closeBtn.onmouseover = () => { closeBtn.style.opacity = '1'; };
- closeBtn.onmouseout = () => { closeBtn.style.opacity = '0.7'; };
-
- caption.appendChild(titleSpan);
- caption.appendChild(closeBtn);
+ const caption = createCaption(title, theme, () => {
+ cleanup();
+ resolve(undefined);
+ });
const contents = document.createElement('div');
contents.style.cssText = `
padding: 12px 14px;
color: ${theme.contentText};
line-height: 1.4;
- `;
+ `;
contents.textContent = text;
- const footer = document.createElement('div');
- footer.style.cssText = `
- padding: 8px 14px 12px;
- display: flex;
- justify-content: flex-end;
- gap: 6px;
- `;
-
- const cleanup = () => {
- document.removeEventListener('keydown', handleKeyDown);
- overlay.remove();
- };
-
- closeBtn.addEventListener('click', () => {
- cleanup();
- resolve(undefined);
- });
+ const footer = createFooter();
buttons.forEach((buttonConfig, index) => {
- const btn = document.createElement('button');
- btn.textContent = buttonConfig.text;
- btn.style.cssText = `
- padding: 4px 14px;
- border: 1px solid ${theme.buttonBorder};
- border-radius: 3px;
- background: ${theme.buttonBg};
- color: ${theme.buttonText};
- font-size: 14px;
- font-family: inherit;
- cursor: pointer;
- `;
- btn.onmouseover = () => { btn.style.background = theme.buttonHoverBg; };
- btn.onmouseout = () => { btn.style.background = theme.buttonBg; };
-
- btn.addEventListener('click', () => {
+ const btn = createButton(buttonConfig.text, theme, () => {
cleanup();
resolve(buttonConfig.value);
});
-
footer.appendChild(btn);
if (index === buttons.length - 1) {
@@ -185,21 +263,6 @@ export function showModal (options: ModalOptions): Promise
}
});
- overlay.addEventListener('click', (e) => {
- if (e.target === overlay) {
- cleanup();
- resolve(undefined);
- }
- });
-
- const handleKeyDown = (e: KeyboardEvent) => {
- if (e.key === 'Escape') {
- cleanup();
- resolve(undefined);
- }
- };
- document.addEventListener('keydown', handleKeyDown);
-
dialog.appendChild(caption);
dialog.appendChild(contents);
dialog.appendChild(footer);
@@ -208,3 +271,296 @@ export function showModal (options: ModalOptions): Promise
});
}
+interface ListSelectionOptions {
+ darkMode: boolean;
+ title: string;
+ items: string[];
+ maxWidth?: number;
+ maxHeight?: number;
+}
+
+export function showListSelectionModal (options: ListSelectionOptions): Promise {
+ return new Promise((resolve) => {
+ const { title, items, darkMode = false, maxHeight = 400 } = options;
+
+ const theme = getTheme(darkMode);
+ const overlay = createOverlay(theme);
+ const dialog = createDialog(theme, 300);
+
+ let selectedItem: string | undefined;
+
+ const cleanup = setupModalHandlers(overlay, () => {
+ cleanup();
+ resolve(undefined);
+ }, (e) => {
+ if (e.key === 'Enter' && selectedItem) {
+ cleanup();
+ resolve(selectedItem);
+ return true;
+ }
+ return false;
+ });
+
+ const caption = createCaption(title, theme, () => {
+ cleanup();
+ resolve(undefined);
+ });
+
+ const contents = document.createElement('div');
+ contents.style.cssText = `
+ padding: 12px 0;
+ color: ${theme.contentText};
+ max-height: ${maxHeight}px;
+ overflow-y: auto;
+ `;
+
+ items.forEach((item) => {
+ const itemDiv = document.createElement('div');
+ itemDiv.textContent = item;
+ itemDiv.style.cssText = `
+ padding: 8px 14px;
+ cursor: pointer;
+ user-select: none;
+ `;
+
+ itemDiv.addEventListener('click', () => {
+ // Deselect all items
+ contents.querySelectorAll('div').forEach(div => {
+ div.style.background = 'transparent';
+ });
+ // Select this item
+ itemDiv.style.background = theme.itemSelectedBg;
+ selectedItem = item;
+ });
+
+ itemDiv.addEventListener('mouseover', () => {
+ if (itemDiv.style.background !== theme.itemSelectedBg) {
+ itemDiv.style.background = theme.itemHoverBg;
+ }
+ });
+
+ itemDiv.addEventListener('mouseout', () => {
+ if (itemDiv.style.background !== theme.itemSelectedBg) {
+ itemDiv.style.background = 'transparent';
+ }
+ });
+
+ contents.appendChild(itemDiv);
+ });
+
+ const footer = createFooter();
+
+ const cancelBtn = createButton('Cancel', theme, () => {
+ cleanup();
+ resolve(undefined);
+ });
+
+ const okBtn = createButton('OK', theme, () => {
+ cleanup();
+ resolve(selectedItem);
+ });
+
+ footer.appendChild(cancelBtn);
+ footer.appendChild(okBtn);
+
+ dialog.appendChild(caption);
+ dialog.appendChild(contents);
+ dialog.appendChild(footer);
+ overlay.appendChild(dialog);
+ document.body.appendChild(overlay);
+
+ setTimeout(() => okBtn.focus(), 0);
+ });
+}
+
+interface AlertOptions {
+ darkMode: boolean;
+ title: string;
+ message: string;
+}
+
+export function showAlertModal (options: AlertOptions): Promise {
+ return new Promise((resolve) => {
+ const { title, message, darkMode = false } = options;
+
+ const theme = getTheme(darkMode);
+ const overlay = createOverlay(theme);
+ const dialog = createDialog(theme, 300);
+
+ const cleanup = setupModalHandlers(overlay, () => {
+ cleanup();
+ resolve();
+ }, (e) => {
+ if (e.key === 'Enter') {
+ cleanup();
+ resolve();
+ return true;
+ }
+ return false;
+ });
+
+ const caption = createCaption(title, theme, () => {
+ cleanup();
+ resolve();
+ });
+
+ const contents = document.createElement('div');
+ contents.style.cssText = `
+ padding: 12px 14px;
+ color: ${theme.contentText};
+ line-height: 1.4;
+ `;
+ contents.textContent = message;
+
+ const footer = createFooter();
+
+ const okBtn = createButton('OK', theme, () => {
+ cleanup();
+ resolve();
+ });
+
+ footer.appendChild(okBtn);
+
+ dialog.appendChild(caption);
+ dialog.appendChild(contents);
+ dialog.appendChild(footer);
+ overlay.appendChild(dialog);
+ document.body.appendChild(overlay);
+
+ setTimeout(() => okBtn.focus(), 0);
+ });
+}
+
+interface MultiListSelectionOptions {
+ darkMode: boolean;
+ title: string;
+ items: string[];
+ selectedItems?: string[];
+ maxWidth?: number;
+ maxHeight?: number;
+}
+
+export function showMultiListSelectionModal (options: MultiListSelectionOptions): Promise {
+ return new Promise((resolve) => {
+ const { title, items, selectedItems = [], darkMode = false, maxHeight = 400 } = options;
+
+ const theme = getTheme(darkMode);
+ const overlay = createOverlay(theme);
+ const dialog = createDialog(theme, 300);
+
+ const selectedSet = new Set(selectedItems);
+
+ const cleanup = setupModalHandlers(overlay, () => {
+ cleanup();
+ resolve(undefined);
+ }, (e) => {
+ if (e.key === 'Enter') {
+ cleanup();
+ resolve(Array.from(selectedSet));
+ return true;
+ }
+ return false;
+ });
+
+ const caption = createCaption(title, theme, () => {
+ cleanup();
+ resolve(undefined);
+ });
+
+ const contents = document.createElement('div');
+ contents.style.cssText = `
+ padding: 12px 0;
+ color: ${theme.contentText};
+ max-height: ${maxHeight}px;
+ overflow-y: auto;
+ `;
+
+ items.forEach((item) => {
+ const itemDiv = document.createElement('div');
+ itemDiv.style.cssText = `
+ padding: 8px 14px;
+ cursor: pointer;
+ user-select: none;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ `;
+
+ // Create checkbox
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.checked = selectedSet.has(item);
+ checkbox.style.cssText = `
+ cursor: pointer;
+ `;
+
+ const label = document.createElement('span');
+ label.textContent = item;
+ label.style.cssText = `
+ flex: 1;
+ cursor: pointer;
+ `;
+
+ itemDiv.appendChild(checkbox);
+ itemDiv.appendChild(label);
+
+ const toggleSelection = () => {
+ if (selectedSet.has(item)) {
+ selectedSet.delete(item);
+ checkbox.checked = false;
+ } else {
+ selectedSet.add(item);
+ checkbox.checked = true;
+ }
+ };
+
+ itemDiv.addEventListener('click', (e) => {
+ // Don't toggle if clicking directly on checkbox (it handles itself)
+ if (e.target !== checkbox) {
+ toggleSelection();
+ }
+ });
+
+ checkbox.addEventListener('change', () => {
+ if (checkbox.checked) {
+ selectedSet.add(item);
+ } else {
+ selectedSet.delete(item);
+ }
+ });
+
+ itemDiv.addEventListener('mouseover', () => {
+ itemDiv.style.background = theme.itemHoverBg;
+ });
+
+ itemDiv.addEventListener('mouseout', () => {
+ itemDiv.style.background = 'transparent';
+ });
+
+ contents.appendChild(itemDiv);
+ });
+
+ const footer = createFooter();
+
+ const cancelBtn = createButton('Cancel', theme, () => {
+ cleanup();
+ resolve(undefined);
+ });
+
+ const okBtn = createButton('OK', theme, () => {
+ cleanup();
+ resolve(Array.from(selectedSet));
+ });
+
+ footer.appendChild(cancelBtn);
+ footer.appendChild(okBtn);
+
+ dialog.appendChild(caption);
+ dialog.appendChild(contents);
+ dialog.appendChild(footer);
+ overlay.appendChild(dialog);
+ document.body.appendChild(overlay);
+
+ setTimeout(() => okBtn.focus(), 0);
+ });
+}
diff --git a/spine-ts/spine-construct3/src/instance.ts b/spine-ts/spine-construct3/src/instance.ts
index 192f46868..465c52238 100644
--- a/spine-ts/spine-construct3/src/instance.ts
+++ b/spine-ts/spine-construct3/src/instance.ts
@@ -579,7 +579,66 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
}
public async selectAnimation () {
- console.log('[Spine] Select animation dialog called');
+ if (!this.skeleton) {
+ await spine.showAlertModal({
+ darkMode: false,
+ title: 'Error',
+ message: 'Skeleton not loaded. Please ensure atlas and skeleton files are set.',
+ });
+ return;
+ }
+
+ const animations = this.skeleton.data.animations.map(anim => anim.name);
+ if (animations.length === 0) {
+ await spine.showAlertModal({
+ darkMode: false,
+ title: 'No Animations',
+ message: 'No animations found in the skeleton.',
+ });
+ return;
+ }
+
+ const selectedAnimation = await spine.showListSelectionModal({
+ darkMode: false,
+ title: 'Select Animation',
+ items: animations,
+ });
+
+ if (selectedAnimation) {
+ this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_ANIMATION, selectedAnimation);
+ }
+ }
+
+ public async selectSkin () {
+ if (!this.skeleton) {
+ await spine.showAlertModal({
+ darkMode: false,
+ title: 'Error',
+ message: 'Skeleton not loaded. Please ensure atlas and skeleton files are set.',
+ });
+ return;
+ }
+
+ const skins = this.skeleton.data.skins.map(skin => skin.name).filter(s => s !== "default");
+ if (skins.length === 0) {
+ await spine.showAlertModal({
+ darkMode: false,
+ title: 'No Skins',
+ message: 'No skins found in the skeleton.',
+ });
+ return;
+ }
+
+ const selectedSkins = await spine.showMultiListSelectionModal({
+ darkMode: false,
+ title: 'Select Skins',
+ items: skins,
+ selectedItems: this.skins,
+ });
+
+ if (selectedSkins !== undefined) {
+ this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKIN, selectedSkins.join(","));
+ }
}
private lang (stringKey: string, interpolate: (string | number)[] = []): string {
diff --git a/spine-ts/spine-construct3/src/lang/en-US.json b/spine-ts/spine-construct3/src/lang/en-US.json
index f0ec763aa..b2a6437a3 100644
--- a/spine-ts/spine-construct3/src/lang/en-US.json
+++ b/spine-ts/spine-construct3/src/lang/en-US.json
@@ -22,6 +22,11 @@
"name": "Loader scale",
"desc": "Loader scale"
},
+ "select-skin": {
+ "name": "Select skin",
+ "desc": "Open a dialog to select skins from the skeleton",
+ "link-text": "Select"
+ },
"select-animation": {
"name": "Select animation",
"desc": "Open a dialog to select an animation from the skeleton",
diff --git a/spine-ts/spine-construct3/src/lang/zh-CN.json b/spine-ts/spine-construct3/src/lang/zh-CN.json
index 61db94f3c..e4b86dbb5 100644
--- a/spine-ts/spine-construct3/src/lang/zh-CN.json
+++ b/spine-ts/spine-construct3/src/lang/zh-CN.json
@@ -22,6 +22,11 @@
"name": "加载比例",
"desc": "加载比例"
},
+ "select-skin": {
+ "name": "选择皮肤",
+ "desc": "打开对话框从骨架中选择皮肤",
+ "link-text": "选择"
+ },
"select-animation": {
"name": "选择动画",
"desc": "打开对话框从骨架中选择动画",
diff --git a/spine-ts/spine-construct3/src/plugin.ts b/spine-ts/spine-construct3/src/plugin.ts
index 0397e0d29..de3ac30fc 100644
--- a/spine-ts/spine-construct3/src/plugin.ts
+++ b/spine-ts/spine-construct3/src/plugin.ts
@@ -78,6 +78,13 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
new SDK.PluginProperty("projectfile", SpineC3Plugin.PROP_ATLAS, { initialValue: "", filter: ".atlas" }),
new SDK.PluginProperty("projectfile", SpineC3Plugin.PROP_SKELETON, { initialValue: "", filter: ".json,.skel" }),
new SDK.PluginProperty("float", SpineC3Plugin.PROP_LOADER_SCALE, 1),
+ new SDK.PluginProperty("link", "select-skin", {
+ linkCallback: async (instance) => {
+ const sdkInst = instance as SDKEditorInstanceClass;
+ await sdkInst.selectSkin();
+ },
+ callbackType: "for-each-instance"
+ }),
new SDK.PluginProperty("text", SpineC3Plugin.PROP_SKIN, ""),
new SDK.PluginProperty("link", "select-animation", {
linkCallback: async (instance) => {