/ src / main / aid / commands.ts
commands.ts
  1  import { Application } from './types'
  2  import { app, BrowserWindow, screen } from 'electron'
  3  import { createWindow, ensureOnCurrentScreen, releaseFocus, loadWindowUrl } from './index'
  4  //import MacosAutomator from '../../automations/macos';
  5  //import WindowsAutomator from '../../automations/windows';
  6  //import Computer from '../../automations/computer_nut';
  7  import { wait, anyDict } from './utils'
  8  import Constants from '../utils/Constants'
  9  
 10  export let commandPicker: BrowserWindow = null
 11  
 12  const width = 300
 13  const height = 320
 14  
 15  const POPUP_NAME = 'popup'
 16  const POPUP_HASH = `/${POPUP_NAME}`
 17  
 18  let commanderStartTime: number | undefined
 19  let sourceApp: Application | undefined
 20  let cursorAtOpen: { x: number; y: number } | undefined
 21  
 22  export const prepareCommandPicker = (queryParams?: anyDict): void => {
 23    const macOS = process.platform === 'darwin'
 24  
 25    // open a new one
 26    commandPicker = createWindow({
 27      hash: POPUP_HASH,
 28      title: `${Constants.APP_NAME} - ${POPUP_NAME}`,
 29      x: 0,
 30      y: 0,
 31      width: width,
 32      height: height,
 33      frame: false,
 34      skipTaskbar: true,
 35      alwaysOnTop: true,
 36      transparent: macOS,
 37      resizable: process.env.DEBUG ? true : false,
 38      hiddenInMissionControl: true,
 39      queryParams: queryParams,
 40      keepHidden: true,
 41      hasShadow: macOS
 42    })
 43  
 44    // focus tricks
 45    commandPicker.on('show', () => {
 46      // macos can use app.focus which is more elegant
 47      // windows will use activateCommandPicker
 48  
 49      if (macOS) {
 50        app.focus({ steal: true })
 51      }
 52  
 53      // focus
 54      commandPicker.moveTop()
 55      commandPicker.focusOnWebView()
 56  
 57      // try to activate (make foremost)
 58      activateCommandPicker()
 59  
 60      // log
 61      if (commanderStartTime) {
 62        console.log(`Command picker total time: ${Date.now() - commanderStartTime}ms`)
 63      }
 64    })
 65  
 66    // prevent close with keyboard shortcut
 67    commandPicker.on('close', (event) => {
 68      closeCommandPicker(sourceApp)
 69      event.preventDefault()
 70    })
 71  }
 72  
 73  export const openCommandPicker = (params: anyDict): void => {
 74    // save
 75    sourceApp = params.sourceApp
 76    commanderStartTime = params.startTime
 77  
 78    // if we don't have a window, create one
 79    if (!commandPicker || commandPicker.isDestroyed()) {
 80      prepareCommandPicker(params)
 81    } else {
 82      loadWindowUrl(commandPicker, { queryParams: params, hash: POPUP_HASH })
 83      // commandPicker.webContents.send('show', params)
 84    }
 85  
 86    // check prompt is on the right screen
 87    ensureOnCurrentScreen(commandPicker)
 88  
 89    // and at right location
 90    cursorAtOpen = screen.getCursorScreenPoint()
 91  
 92    const screenBounds = screen.getDisplayNearestPoint(cursorAtOpen).bounds
 93    const adjustedX = Math.max(0, Math.min(cursorAtOpen.x - width / 2, screenBounds.width - width))
 94    const adjustedY = Math.max(
 95      0,
 96      Math.min(cursorAtOpen.y - (params.sourceApp ? 64 : 24), screenBounds.height - height)
 97    )
 98  
 99    commandPicker.setBounds({
100      x: adjustedX,
101      y: adjustedY,
102      width: width,
103      height: height
104    })
105  
106    // done
107    commandPicker.show()
108  }
109  
110  export const closeCommandPicker = async (sourceApp?: Application): Promise<void> => {
111    // check
112    if (commandPicker === null || commandPicker.isDestroyed()) {
113      return
114    }
115  
116    try {
117      // remove blur handler
118      //console.log('Removing blur handler from command picker');
119      commandPicker.removeAllListeners('blur')
120      commandPicker.setOpacity(0)
121  
122      // now release focus
123      await releaseFocus({ sourceApp })
124  
125      // now hide
126      commandPicker.hide()
127      commandPicker.setOpacity(1)
128    } catch (error) {
129      console.error('Error while hiding command picker', error)
130      commandPicker = null
131    }
132  }
133  
134  const activateCommandPicker = async () => {
135    const isThere = () =>
136      commandPicker &&
137      !commandPicker.isDestroyed() &&
138      commandPicker.isVisible() &&
139      commandPicker.getOpacity() > 0
140  
141    // wait for command picker to be visible
142    const start = Date.now()
143    const totalWait = 1000
144    while (!isThere() && Date.now() - start < totalWait) {
145      await wait(50)
146    }
147  
148    if (!isThere()) {
149      console.log('Command picker is not visible after 1 second, not activating')
150      return
151    }
152  
153    commandPicker.removeAllListeners('blur')
154  
155    if (isThere()) {
156      //console.log('Adding blur handler to command picker');
157      commandPicker.removeAllListeners('blur')
158      commandPicker.on('blur', () => {
159        closeCommandPicker(sourceApp)
160      })
161    }
162  }