const psapi = require('./psapi') const html_manip = require('./utility/html_manip') function finalWidthHeight( selectionWidth, selectionHeight, minWidth, minHeight ) { // const minWidth = 512 // const minHeight = 512 // const selectionWidth = 256 // const selectionHeight = 1000 let finalWidth = 0 let finalHeight = 0 if (selectionWidth <= selectionHeight) { //do operation on the smaller dimension const scaleRatio = selectionWidth / minWidth finalWidth = minWidth finalHeight = selectionHeight / scaleRatio } else { const scaleRatio = selectionHeight / minHeight finalHeight = minHeight finalWidth = selectionWidth / scaleRatio } return [finalWidth, finalHeight] } async function selectionToFinalWidthHeight( selectionInfo, minWidth = 512, minHeight = 512 ) { // const { getSelectionInfoExe } = require('./psapi') try { // const selectionInfo = await psapi.getSelectionInfoExe() const [finalWidth, finalHeight] = finalWidthHeight( selectionInfo.width, selectionInfo.height, minWidth, minHeight ) return [ parseInt(finalWidth), parseInt(finalHeight), selectionInfo.width, selectionInfo.height, ] } catch (e) { console.warn('you need a rectangular selection', e) } } async function selectBoundingBox() { let l = await app.activeDocument.activeLayers[0] let bounds = await l.boundsNoEffects let selectionInfo = convertSelectionObjectToSelectionInfo(bounds) await psapi.reSelectMarqueeExe(selectionInfo) return selectionInfo } function convertSelectionObjectToSelectionInfo(selection_obj) { let selection_info = { left: selection_obj._left, right: selection_obj._right, bottom: selection_obj._bottom, top: selection_obj._top, height: selection_obj._bottom - selection_obj._top, width: selection_obj._right - selection_obj._left, } return selection_info } const SelectionInfoDesc = () => ({ _obj: 'get', _target: [ { _property: 'selection', }, { _ref: 'document', _id: app.activeDocument._id, }, ], _options: { dialogOptions: 'dontDisplay', }, }) async function createChannelIfNotExists(channelName) { // const photoshop = require('photoshop') // const app = photoshop.app // const batchPlay = photoshop.action.batchPlay // // Check if the channel exists // let channelExists = false // for (const channel of app.activeDocument.channels) { // if (channel.name === channelName) { // channelExists = true // break // } // } // // Create the channel if it doesn't exist // if (!channelExists) { // await batchPlay( // [ // { // _obj: 'make', // _target: [ // { // _ref: 'channel', // }, // ], // using: { // _obj: 'channel', // name: channelName, // }, // }, // ], // {} // ) // } if (!app.activeDocument.channels.getByName(channelName)) { } } const deleteChannel = (channel_name = 'mask') => app.activeDocument.channels.getByName(channel_name) ? [ { _obj: 'delete', _target: { _ref: 'channel', _name: channel_name }, options: { failOnMissingProperty: false, failOnMissingElement: false, }, }, ] : [] const makeMaskChannel = (channel_name = 'mask') => ({ // _obj: 'set', // _target: { _ref: 'channel', _property: 'selection' }, // to: { _ref: 'channel', _name: channel_name }, _obj: 'duplicate', _target: [ { _ref: 'channel', _property: 'selection', }, ], name: channel_name, _isCommand: true, options: { failOnMissingProperty: false, failOnMissingElement: false }, }) async function makeMaskChannelExe(channel_name = 'mask') { await executeAsModal(async () => { // const channel = app.activeDocument.channels.getByName(channel_name) // channel?.remove() const result = await batchPlay( [...deleteChannel(channel_name), makeMaskChannel(channel_name)], // [ // { // _obj: 'duplicate', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // name: channel_name, // _isCommand: true, // }, // { // _obj: 'make', // new: { _class: 'channel' }, // at: { _ref: 'channel', _enum: 'channel', _value: 'mask' }, // using: { // _enum: 'userMaskEnabled', // _value: 'revealSelection', // }, // }, // ], { synchronousExecution: true, modalBehavior: 'execute', } ) console.log('result: ', result) }) } async function multiGetExe() { desc = { _obj: 'multiGet', _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }, extendedReference: [['layerID', 'itemIndex', 'count']], options: { failOnMissingProperty: false, failOnMissingElement: false }, } try { const result = await batchPlay([desc], { modalBehavior: 'execute', synchronousExecution: true, }) console.log('multiGetExe result: ', result) // await executeAsModal(async () => {}) } catch (e) { console.warn(e) } } async function applyMaskChannelExe(channel_name = 'mask') { await executeAsModal(async () => { const result = await batchPlay( [ // SelectionInfoDesc(), // makeMaskChannel(), { _obj: 'set', _target: { _ref: 'channel', _property: 'selection' }, to: { _ref: 'channel', _name: channel_name }, }, { _obj: 'make', new: { _class: 'channel' }, at: { _ref: 'channel', _enum: 'channel', _value: 'mask' }, using: { _enum: 'userMaskEnabled', _value: 'revealSelection', _name: channel_name, }, }, ], { synchronousExecution: true, modalBehavior: 'execute', } ) console.log('result: ', result) }) } async function selectionToChannel(channel_name) { const channelToSelection = { _obj: 'set', _target: { _ref: 'channel', _property: 'selection' }, to: { _ref: 'channel', _name: channel_name }, } let result try { await executeAsModal(async () => { result = await batchPlay( [ ...deleteChannel(channel_name), makeMaskChannel(channel_name), channelToSelection, ], { modalBehavior: 'execute', synchronousExecution: true } ) }) } catch (e) { console.warn(e) } return result } async function createLayerFromMaskChannel(channel_name = 'mask') { await executeAsModal(async () => { const result = await batchPlay( [ // SelectionInfoDesc(), // makeMaskChannel(), // { // _obj: 'set', // _target: { _ref: 'channel', _property: 'selection' }, // to: { _ref: 'channel', _name: channel_name }, // }, // { // _obj: 'make', // new: { _class: 'channel' }, // at: { _ref: 'channel', _enum: 'channel', _value: 'mask' }, // using: { // _enum: 'userMaskEnabled', // _value: 'revealSelection', // _name: channel_name, // }, // }, { _obj: 'set', _target: { _ref: 'layer' }, to: { _ref: 'channel', _name: channel_name }, }, ], { synchronousExecution: true, modalBehavior: 'execute', } ) console.log('result: ', result) }) } async function channelToSelectionExe(channel_name = 'mask') { const channelToSelection = { _obj: 'set', _target: { _ref: 'channel', _property: 'selection' }, to: { _ref: 'channel', _name: channel_name }, } try { await executeAsModal(async () => { const result = await batchPlay([channelToSelection], { modalBehavior: 'execute', synchronousExecution: true, }) }) } catch (e) { console.warn(e) } } function keepRatio(selectionInfo, offset) { // Calculate the current width and height let width = selectionInfo.right - selectionInfo.left let height = selectionInfo.bottom - selectionInfo.top // Calculate the new coordinates with the offset selectionInfo.right += offset selectionInfo.left -= offset selectionInfo.bottom += offset selectionInfo.top -= offset // Update width and height selectionInfo.width = width + 2 * offset selectionInfo.height = height + 2 * offset return selectionInfo } function makeSquare(selectionInfo, offset) { // Calculate the current width and height let width = selectionInfo.right - selectionInfo.left let height = selectionInfo.bottom - selectionInfo.top // Determine the maximum dimension let maxDim = Math.max(width, height) // Calculate the difference for width and height let diffWidth = maxDim - width let diffHeight = maxDim - height // Add half the difference to 'right' and 'bottom', subtract the rest from 'left' and 'top' selectionInfo.right += Math.floor(diffWidth / 2) + offset selectionInfo.left -= diffWidth - Math.floor(diffWidth / 2) + offset selectionInfo.bottom += Math.floor(diffHeight / 2) + offset selectionInfo.top -= diffHeight - Math.floor(diffHeight / 2) + offset // Update width and height selectionInfo.width = maxDim + 2 * offset selectionInfo.height = maxDim + 2 * offset return selectionInfo } async function inpaintLassoInitImageAndMask( channel_name = 'mask', offset = 0, make_square = true ) { const selectionInfo = await psapi.getSelectionInfoExe() //convert the selection box into square box so that you have best output results const newSelection = make_square ? makeSquare(selectionInfo, offset) : keepRatio(selectionInfo, offset) //correct width and height sliders, since this is lasso mode. await calcWidthHeightFromSelection(newSelection) async function getImageFromCanvas() { const width = html_manip.getWidth() const height = html_manip.getHeight() const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New( width, height, newSelection, true ) return base64 } const channelToSelection = { _obj: 'set', _target: { _ref: 'channel', _property: 'selection' }, to: { _ref: 'channel', _name: channel_name }, } // await executeAsModal(async () => { // const result = await batchPlay( // [ // ...deleteChannel(channel_name), // makeMaskChannel(channel_name), // channelToSelection, // ], // { modalBehavior: 'execute', synchronousExecution: true } // ) // const selection_info = await psapi.getSelectionInfoExe() // }) await selectionToChannel(channel_name) //lasso selection to channel called 'mask' const init_base64 = await getImageFromCanvas() html_manip.setInitImageSrc(general.base64ToBase64Url(init_base64)) let mask_base64 await executeAsModal(async () => { const result = await batchPlay([channelToSelection], { modalBehavior: 'execute', synchronousExecution: true, }) // const selection_info = await psapi.getSelectionInfoExe() mask_base64 = await fillSelectionWhiteOutsideBlack(newSelection) }) //save laso selection to channel //get laso selection //make rect selection //base64 from selection //and display it in init image html element //get laso selection: //make mask layer //get rectangular selection //get base64 from selection //display it in mask image html element // let jimp_mask = await Jimp.read(Buffer.from(mask_base64, 'base64')) // html_manip.setInitImageMaskSrc( // await jimp_mask.getBase64Async(Jimp.MIME_PNG) // ) html_manip.setInitImageMaskSrc(general.base64ToBase64Url(mask_base64)) return [init_base64, mask_base64] // //return // //init image //mask } async function fillSelectionWhiteOutsideBlack(selectionInfo) { // Create a new layer const layer_name = 'mask' const getSelectionDesc = () => ({ _obj: 'get', _target: [ { _property: 'selection', }, { _ref: 'document', _id: app.activeDocument._id, }, ], _options: { dialogOptions: 'dontDisplay', }, }) const invertSelection = () => ({ _obj: 'inverse', _isCommand: true, }) await psapi.unselectActiveLayers() const mask_layer = await layer_util.createNewLayerExe('mask') await moveToTopLayerStackExe() await batchPlay( [ getSelectionDesc(), { _obj: 'fill', using: { _enum: 'fillContents', _value: 'white', }, opacity: { _unit: 'percentUnit', _value: 100, }, mode: { _enum: 'blendMode', _value: 'normal', }, }, // { // _obj: 'select', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // }, invertSelection(), { _obj: 'fill', using: { _enum: 'fillContents', _value: 'black', }, opacity: { _unit: 'percentUnit', _value: 100, }, mode: { _enum: 'blendMode', _value: 'normal', }, }, // { // _obj: 'invert', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // }, //make new layer // { // _obj: 'make', // _target: [ // { // _ref: 'layer', // }, // ], // using: { // _obj: 'layer', // name: 'Fill Layer', // }, // }, { _obj: 'set', _target: [ { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }, ], to: { _obj: 'layer', name: layer_name }, _options: { dialogOptions: 'dontDisplay' }, _isCommand: true, }, // getSelectionDesc(), //undo the first inversion of the selection invertSelection(), ], { modalBehavior: 'execute' } ) await psapi.reSelectMarqueeExe(selectionInfo) const width = html_manip.getWidth() const height = html_manip.getHeight() //convert the selection area on the canvas to base64 image const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New( width, height, selectionInfo, true, layer_name + '.png' ) //import the base64 image into the canvas as a new layer // await io.IO.base64ToLayer( // base64, // 'base64_to_layer_temp.png', // rect_selection_info.left, // rect_selection_info.top, // rect_selection_info.width, // rect_selection_info.height // ) await psapi.cleanLayers([mask_layer]) return base64 } async function inpaintLasoInitImage() { // Create a new layer const layer_name = 'inpaint_laso_init_image' const getSelectionDesc = () => ({ _obj: 'get', _target: [ { _property: 'selection', }, { _ref: 'document', _id: app.activeDocument._id, }, ], _options: { dialogOptions: 'dontDisplay', }, }) const invertSelection = () => ({ _obj: 'inverse', _isCommand: true, }) await batchPlay( [ getSelectionDesc(), { _obj: 'fill', using: { _enum: 'fillContents', _value: 'white', }, opacity: { _unit: 'percentUnit', _value: 100, }, mode: { _enum: 'blendMode', _value: 'normal', }, }, // { // _obj: 'select', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // }, invertSelection(), { _obj: 'fill', using: { _enum: 'fillContents', _value: 'black', }, opacity: { _unit: 'percentUnit', _value: 100, }, mode: { _enum: 'blendMode', _value: 'normal', }, }, // { // _obj: 'invert', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // }, //make new layer // { // _obj: 'make', // _target: [ // { // _ref: 'layer', // }, // ], // using: { // _obj: 'layer', // name: 'Fill Layer', // }, // }, { _obj: 'set', _target: [ { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }, ], to: { _obj: 'layer', name: layer_name }, _options: { dialogOptions: 'dontDisplay' }, _isCommand: true, }, // getSelectionDesc(), //undo the first inversion of the selection invertSelection(), ], { modalBehavior: 'execute' } ) //get the rectangular bounding box selection const rect_selection_info = await psapi.getSelectionInfoExe() await psapi.reSelectMarqueeExe(rect_selection_info) const width = html_manip.getWidth() const height = html_manip.getHeight() //convert the selection area on the canvas to base64 image const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New( width, height, rect_selection_info, true, layer_name + '.png' ) //import the base64 image into the canvas as a new layer await io.IO.base64ToLayer( base64, 'base64_to_layer_temp.png', rect_selection_info.left, rect_selection_info.top, rect_selection_info.width, rect_selection_info.height ) // Fill the current selection with white // await batchPlay( // [ // { // _obj: 'fill', // using: { // _enum: 'fillContents', // _value: 'white', // }, // opacity: { // _unit: 'percentUnit', // _value: 100, // }, // mode: { // _enum: 'blendMode', // _value: 'normal', // }, // }, // ], // { modalBehavior: 'execute' } // ) // // Invert the selection // await batchPlay( // [ // { // _obj: 'invert', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // }, // ], // { modalBehavior: 'execute' } // ) // // Fill the inverted selection with black // await batchPlay( // [ // { // _obj: 'fill', // using: { // _enum: 'fillContents', // _value: 'black', // }, // opacity: { // _unit: 'percentUnit', // _value: 100, // }, // mode: { // _enum: 'blendMode', // _value: 'normal', // }, // }, // ], // { modalBehavior: 'execute' } // ) } class Selection { static async getSelectionInfoExe() { //return a selectionInfo object or undefined try { const selection = await executeAsModal(async () => { const result = await batchPlay([SelectionInfoDesc()], { synchronousExecution: true, modalBehavior: 'execute', }) return result[0]?.selection }) if (this.isSelectionValid(selection)) { let selection_info = { left: selection.left._value, right: selection.right._value, bottom: selection.bottom._value, top: selection.top._value, height: selection.bottom._value - selection.top._value, width: selection.right._value - selection.left._value, } // console.dir({selection_info}) return selection_info } } catch (e) { console.warn('selection info error', e) } } static isSelectionValid(selection) { console.warn( 'isSelectionValid is deprecated use selection.isSelectionValid instead' ) if ( selection && // check if the selection is defined selection.hasOwnProperty('left') && selection.hasOwnProperty('right') && selection.hasOwnProperty('top') && selection.hasOwnProperty('bottom') ) { return true } return false } static reselectArea(selection_info) {} static isSameSelection(selection_info_1, selection_info_2) {} static async getImageToSelectionDifference() { // const selectionInfo = await psapi.getSelectionInfoExe() // const width = html_manip.getWidth() // const height = html_manip.getHeight() const selectionInfo = session_store.data.current_selection_info const width = sd_tab_store.data.width const height = sd_tab_store.data.height let ratio = (width * height) / (selectionInfo.width * selectionInfo.height) return ratio } static {} } async function moveToTopLayerStackExe() { const moveToTop = { _obj: 'move', _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }, to: { _ref: 'layer', _enum: 'ordinal', _value: 'front' }, // _options: { dialogOptions: 'dontDisplay' }, options: { failOnMissingProperty: false, failOnMissingElement: false }, } try { await executeAsModal(async () => { const layer = app.activeDocument.activeLayers[0] while (layer.parent) { console.log(layer.parent) const result = await batchPlay([moveToTop], { modalBehavior: 'execute', synchronousExecution: true, }) } }) } catch (e) { console.warn(e) } } async function colorRange() { // const select_current_layer_cmd = { // _obj: 'set', // _target: [ // { // _ref: 'channel', // _property: 'selection', // }, // ], // to: { // _ref: 'channel', // _enum: 'channel', // _value: 'transparencyEnum', // }, // _isCommand: true, // } const cmd = { _obj: 'colorRange', fuzziness: 0, minimum: { _obj: 'labColor', luminance: 100, a: 0, b: 0, }, maximum: { _obj: 'labColor', luminance: 100, a: 0, b: 0, }, colorModel: 0, _isCommand: true, } const result = await batchPlay([cmd], { modalBehavior: 'execute', synchronousExecution: true, }) return result } async function colorRangeExe() { let result try { await executeAsModal( async () => { result = await colorRange() }, { commandName: 'Convert Black and White Layer to mask selection' } ) } catch (e) { console.warn(e) } return result } async function base64ToLassoSelection(base64, selection_info) { //place the mask on the canvas const mask_layer = await io.IO.base64ToLayer( base64, 'monochrome_mask.png', selection_info.left, selection_info.top, selection_info.width, selection_info.height ) //reselect the selection area await psapi.reSelectMarqueeExe(selection_info) //reselect the layer await psapi.selectLayersExe([mask_layer]) await executeAsModal(async () => { await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible //select the white pixels await colorRange() }) // await colorRangeExe() //delete the mask layer. we only needed to select the white pixels await executeAsModal(async () => { await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible }) await layer_util.deleteLayers([mask_layer]) } async function base64ToChannel(base64, selection_info, channel_name) { try { await executeAsModal(async (context) => { const history_id = await context.hostControl.suspendHistory({ documentID: app.activeDocument.id, //TODO: change this to the session document id name: 'Mask Image', }) const layer = app.activeDocument.activeLayers[0] if (!layer_util.Layer.doesLayerExist(layer)) { return null } await psapi.unSelectMarqueeExe() await psapi.unselectActiveLayersExe() await base64ToLassoSelection(base64, selection_info) const expand_cmd = { _obj: 'expand', by: { _unit: 'pixelsUnit', _value: 10, }, selectionModifyEffectAtCanvasBounds: true, _isCommand: true, } const channelToSelection = { _obj: 'set', _target: { _ref: 'channel', _property: 'selection' }, to: { _ref: 'channel', _name: channel_name }, } await executeAsModal(async () => { const result = await batchPlay( [ expand_cmd, ...deleteChannel(channel_name), makeMaskChannel(channel_name), channelToSelection, ], { modalBehavior: 'execute', synchronousExecution: true } ) }) await psapi.selectLayersExe([layer]) await applyMaskChannelExe(channel_name) // context = stored_context await context.hostControl.resumeHistory(history_id) }) } catch (e) { console.warn(e) } } async function black_white_layer_to_mask(mask_id, target_layer_id, mask_name) { let result let psAction = require('photoshop').action let command = [ // ...deleteChannel(mask_name), // Select layer “Layer 33” { _obj: 'select', _target: [{ _id: mask_id, _ref: 'layer' }], // layerID: [3862], makeVisible: false, }, // Set Selection { _obj: 'set', _target: [{ _property: 'selection', _ref: 'channel' }], to: { _enum: 'channel', _ref: 'channel', _value: 'transparencyEnum', }, }, // Color Range { _obj: 'colorRange', colorModel: 0, fuzziness: 0, maximum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 }, minimum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 }, }, // Duplicate Selection, make sure you delete the any mask that share the same name { _obj: 'duplicate', _target: [{ _property: 'selection', _ref: 'channel' }], name: mask_name, }, // Select channel “Alpha 1” { _obj: 'select', _target: [{ _name: mask_name, _ref: 'channel' }] }, // Set Selection { _obj: 'set', _target: [{ _property: 'selection', _ref: 'channel' }], to: { _enum: 'ordinal', _ref: 'channel', _value: 'targetEnum' }, }, // Select layer “Layer 43” { _obj: 'select', _target: [{ _id: target_layer_id, _ref: 'layer' }], // layerID: [3879], makeVisible: false, }, // Make { _obj: 'make', at: { _enum: 'channel', _ref: 'channel', _value: 'mask' }, new: { _class: 'channel' }, using: { _enum: 'userMaskEnabled', _value: 'revealSelection' }, }, ] result = await psAction.batchPlay(command, {}) } async function black_white_layer_to_mask_multi_batchplay( mask_id, target_layer_id, mask_name, expand_by = 10 ) { let result let psAction = require('photoshop').action const timer = (ms) => new Promise((res) => setTimeout(res, ms)) let command1 = [ { _obj: 'set', _target: [{ _property: 'selection', _ref: 'channel' }], to: { _enum: 'ordinal', _value: 'none' }, }, // ...deleteChannel(mask_name), // Select layer “Layer 33” { _obj: 'select', _target: [{ _id: mask_id, _ref: 'layer' }], // layerID: [3862], makeVisible: false, }, // Set Selection { _obj: 'set', _target: [{ _property: 'selection', _ref: 'channel' }], to: { _enum: 'channel', _ref: 'channel', _value: 'transparencyEnum', }, }, ] let command2 = [ // Color Range { _obj: 'colorRange', colorModel: 0, fuzziness: 0, maximum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 }, minimum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 }, }, { _obj: 'expand', by: { _unit: 'pixelsUnit', _value: expand_by, }, selectionModifyEffectAtCanvasBounds: true, _isCommand: true, }, ] let command3 = [ // Duplicate Selection, make sure you delete the any mask that share the same name { _obj: 'duplicate', _target: [{ _property: 'selection', _ref: 'channel' }], name: mask_name, }, // Select channel “Alpha 1” { _obj: 'select', _target: [{ _name: mask_name, _ref: 'channel' }], }, // Set Selection { _obj: 'set', _target: [{ _property: 'selection', _ref: 'channel' }], to: { _enum: 'ordinal', _ref: 'channel', _value: 'targetEnum' }, }, // Select layer “Layer 43” { _obj: 'select', _target: [{ _id: target_layer_id, _ref: 'layer' }], // layerID: [3879], makeVisible: false, }, // Make { _obj: 'make', at: { _enum: 'channel', _ref: 'channel', _value: 'mask' }, new: { _class: 'channel' }, using: { _enum: 'userMaskEnabled', _value: 'revealSelection' }, }, ] await timer(g_timer_value) // result = await psAction.batchPlay(command, {}) await executeAsModal(async () => { result = await psAction.batchPlay(command1, {}) }) await timer(g_timer_value) await executeAsModal(async () => { await layer_util.toggleActiveLayer() // toggle active layer will be visible, note: doesn't solve the issue in outpaint mode result = await psAction.batchPlay(command2, {}) }) await timer(g_timer_value) await executeAsModal(async () => { await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible, note: doesn't solve the issue in outpaint mode result = await psAction.batchPlay(command3, {}) }) } module.exports = { finalWidthHeight, selectionToFinalWidthHeight, selectBoundingBox, convertSelectionObjectToSelectionInfo, Selection, makeMaskChannel, makeMaskChannelExe, fillSelectionWhiteOutsideBlack, inpaintLasoInitImage, applyMaskChannelExe, createLayerFromMaskChannel, multiGetExe, inpaintLassoInitImageAndMask, channelToSelectionExe, moveToTopLayerStackExe, colorRangeExe, base64ToLassoSelection, base64ToChannel, deleteChannel, selectionToChannel, black_white_layer_to_mask, black_white_layer_to_mask_multi_batchplay, }