Mini Kabibi Habibi

Current Path : C:/Program Files/Adobe/Adobe Photoshop 2025/Required/UXP/
Upload File :
Current File : C:/Program Files/Adobe/Adobe Photoshop 2025/Required/UXP/hostActions.js

/*************************************************************************
* ADOBE CONFIDENTIAL
* ___________________
*
*  Copyright 2024 Adobe
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Inc. (“Adobe”) and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe and its
* suppliers and are protected by all applicable intellectual property
* laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe.
**************************************************************************/

const psAction = window.require('photoshop').action;
const psCore = window.require('photoshop').core;

const hostActions = {};

async function showCoachMarkPs(command){
    try {
        await psAction.batchPlay([command], {}, function() { });

    } catch (error) {
        console.error('Error showing coachmark:', error);
    }
}

hostActions.showCoachMark = async function(cmd) {
    const target = {
        _ref: 'application',
        _enum: 'ordinal',
        _value: 'targetEnum'
    };

    const command = {
        _obj : 'showCoachmark',
        _target : target,
        type : 'text',
        position : {
            arrow : cmd.arrow ? cmd.arrow : 'top-right',
            arrowOrientation : cmd.arrowOrientation,
            type : 'absolute',
            x : cmd.position.x,
            y : cmd.position.y
        },
        description : cmd.message
    };
    // console.log('What Plugins Panel passes to batchplay:');
    // console.log('arrowOrientation : ' + cmd.arrowOrientation);
    // console.log('x: ' + cmd.position.x);
    // console.log('y: ' + cmd.position.y);

    await showCoachMarkPs(command);
}

async function isDocumentView() {
    const target = { _ref: [{ _property: 'visible' }, { _ref: 'document', _enum: 'ordinal', _value: 'targetEnum' }] };
    const result = await psAction.batchPlay([{ _obj: 'get', _target: target }], {});
    return result && result.length >= 1 && result[0]['visible'];
}

async function isHomescreenVisible() {
    const target = { _ref: [{ _property: 'homeScreenVisibility' }, { _ref: 'application' }] };
    const visibility =  await psAction.batchPlay([{ _obj: 'get', _target: target }], {});
    return visibility && visibility.length >= 1 && visibility[0].homeScreenVisibility;
}

function listenToHomeScreenVisibility(homeScreenVisibilityChangedCallback) {
    psAction.addNotificationListener([{event: 'homeScreenVisibilityChanged'}], homeScreenVisibilityChangedCallback);
}

function listenToAppVisibility(appVisibilityChangedCallback) {
    psCore.addNotificationListener('OS', ['activationChanged'], appVisibilityChangedCallback);
}

hostActions.addUINotificationListener = function(events, handler) {
    psCore.addNotificationListener('UI', events, handler);
}

async function isAppInForeground() {
    const command = [
        {
            _obj: 'multiGet',
            _target: {
                _ref: 'application',
                _enum: 'ordinal',
                _value: 'targetEnum'
            },
            extendedReference: [['active']]
        }
    ];
    const appVisibility =  await psAction.batchPlay(command, {});
    return appVisibility && appVisibility.length >= 1 && appVisibility[0].active;
}

async function getApplicationFrameInfo() {
    const command = {
        _obj: 'owlAction',
        _target: {
            _ref: 'application',
            _enum: 'ordinal',
            _value: 'targetEnum'
        },
        owlCommand: 'getApplicationFrameInfo'
    };
    try {
        const applicationFrameInfo =  await psAction.batchPlay([command], {}, function() {});
        return applicationFrameInfo[0].globalBounds;
    } catch (error) {
        console.error('Error getting application frame info:', error);
    }
}

function getDisplayConfiguration() {
    return psCore.getDisplayConfiguration({});
}

hostActions.hideCoachMark = async function() {
    // [TODO] send unique Id as well
    const command = {
        _obj: 'hideCoachmark',
        _target: {
            _ref: 'application',
            _enum: 'ordinal',
            _value: 'targetEnum'
        }
    };
    try {
        await psAction.batchPlay([command], {}, function() { });
    } catch(error) {
        console.error('Error hiding coachmark:', error);
    }
}

async function getIconBounds(command) {
    try {
        const globalIconBounds = await psAction.batchPlay([command], {modalBehavior: 'execute'});
        const iconBounds = {
            left : globalIconBounds[0].panelInfo[0].globalIconicBounds.left,
            right : globalIconBounds[0].panelInfo[0].globalIconicBounds.right,
            top : globalIconBounds[0].panelInfo[0].globalIconicBounds.top,
            bottom : globalIconBounds[0].panelInfo[0].globalIconicBounds.bottom
        };
        return iconBounds;
    } catch (error) {
        console.error('Error getting the icon bounds:', error);
    }
}

async function getPanelBounds(command) {
    try {
        const globalPanelBounds = await psAction.batchPlay([command], {modalBehavior: 'execute'});
        const panelBounds = {
            left : globalPanelBounds[0].panelInfo[0].globalBounds.left,
            right : globalPanelBounds[0].panelInfo[0].globalBounds.right,
            top : globalPanelBounds[0].panelInfo[0].globalBounds.top,
            bottom : globalPanelBounds[0].panelInfo[0].globalBounds.bottom
        };
        return panelBounds;
    } catch (error) {
        console.error('Error getting the panel bounds:', error);
    }
}

async function getElementBounds(element, id) {
    const command = {
        _obj: 'owlAction',
        _target: { _ref: 'application' },
        owlCommand: 'getPanelInfo',
        owlPanelID: id
    };
    if (element == 'panel') {
        return await getPanelBounds(command);
    } else if (element == 'icon') {
        return await getIconBounds(command);
    } else {
        console.error(element + ' is not a valid input');
    }

}
async function getPluginsMenuBarBounds() {
    try {
        const mainMenuItems = await require('photoshop').core.getMainMenuInfo(1);
        const bounds = mainMenuItems.children.find((child) => child['aria-label'] == 'Plugins').globalBounds;
        return bounds;
    } catch (error) {
        console.error('Error obtaining main menu info:', error);
    }
}

hostActions.getGlobalBounds = async function(type, id) {
    let bounds;
    if (type == 'pluginIcon') {
        bounds = await getElementBounds('icon', id);
    } else if (type == 'hostApp') {
        bounds = await getApplicationFrameInfo();
    } else if (type == 'appmenu') {
        bounds = await getPluginsMenuBarBounds();
    } else if (type == 'pluginsPanel') {
        const isMac = require('os').platform() === 'darwin';
        if (isMac) {
            // [TODO] : Currently, the Browse Plugins coachmark can appear in the wrong
            // location on windows only. Temporarily disabling until we get a fix. If
            // win, this will return undefined, which plugins panel will return early.
            bounds = await getElementBounds('panel', id);
        }
    } else {
        console.error(type + ' is not a correct global bounds type');
    }
    // console.log('What we pass to Plugins Panel');
    // console.log('bounds: {left: ' + bounds.left + ', right: ' + bounds.right + ', top: ' + bounds.top + ', bottom: ' + bounds.bottom + '}');
    return bounds;
}

hostActions.isVisible = function(viewType) {
    switch (viewType) {
        case 'document': {
            return isDocumentView();
        }
        case 'homeScreen': {
            return isHomescreenVisible();
        }
        case 'appInForeground': {
            return isAppInForeground();
        }
        default:
            return new Promise(resolve => {resolve (false);});
    }
}

hostActions.addListenerToHostEvents = function(eventStr, callback) {
    switch (eventStr) {
        case 'homeScreenVisibility': {
            try {
                listenToHomeScreenVisibility((event, description) => {callback(description.visible);});
            } catch (error) {
                console.error('Error registering for home screen visibility', error);
            }
            break;
        }
        case 'appVisibility': {
            try {
                listenToAppVisibility(async (event, description)=> {callback(description.active);});
            } catch (error) {
                console.error('Error registering for app visibility', error);
            }
            break;
        }
        default:
            console.error('Error: registering for unknown event');
            break;
    }
}

// [TODO] : check if HostApps return scaler as 1 for Mac.
// If not, then this API will be required. Also put this in platform specific file.
function getScaleFactorFromScreen(screen /*IDisplayConfig*/) {
    const isMac = require('os').platform() === 'darwin';
    if (isMac || !screen) {
      // Scale factor on MAC is always 1.
      return 1;
    }
    return screen.scaleFactor;
}

async function getScreens() /*: Promise<IDisplayConfig[]> */ {
    return await getDisplayConfiguration();
}

function scaleWithScaleFactor(position, factor) {
    const result = {};
    for (const key in position) {
      result[key] = Math.floor(position[key] * factor);
    }
    return result;
  }

/**
* Given available screens, determine which screen the specified point (targetX, targetY)
*/
function getOwnerScreen(screens /*IDisplayConfig[]*/, targetX /*number*/, targetY /*number*/) /*: false | IDisplayConfig*/ {
    const screen = screens.find(screen => {
    const scaleFactor = getScaleFactorFromScreen(screen);
    const globalBounds = scaleWithScaleFactor(screen.globalBounds, 1/scaleFactor) /*as IBounds*/;
    return  targetX >= globalBounds.left &&
            targetX <= globalBounds.right &&
            targetY >= globalBounds.top &&
            targetY <= globalBounds.bottom;
    });
    if (!screen) {
        return false;
    }
    return screen;
}

async function getScalerFactorPs() {
    const appFrameBounds = await hostActions.getGlobalBounds('hostApp', '');
    let top = 0;
    let left = 0;

    const screens = await getScreens();
    let ownerScreen;

    // Try the bottom mid position of the app frame
    top = Math.floor(appFrameBounds.bottom);
    left = Math.floor((appFrameBounds.right + appFrameBounds.left)/2);
    ownerScreen = getOwnerScreen(screens, left, top);
    // Try the left corner of the app frame
    if (ownerScreen == false) {
        // Try the left corner of the app frame
        ownerScreen = getOwnerScreen(screens, appFrameBounds.left, appFrameBounds.top);
        if (ownerScreen == false) {
            // Try the right corner of the app frame
            ownerScreen = getOwnerScreen(screens, appFrameBounds.right, appFrameBounds.top);
            if (ownerScreen == false) {
                return;
            }
        }
    }

    let scalerFactor = getScaleFactorFromScreen(ownerScreen);
    return new Promise(resolve => {resolve (scalerFactor);});
}

hostActions.getScalerFactor = async function() {
    return await getScalerFactorPs();
}

hostActions;