{"version":3,"sources":["../index.ts"],"sourcesContent":["import cp from 'node:child_process';\nimport { detectTerminal } from 'detect-terminal';\n\nconst escapeQuotedPath = (input: string): string => {\n  return input.replace(/(?<!\\\\(?<!\\\\))\"/g, '\\\\$&');\n};\n\nconst isCanceled = (v: string): boolean => /cancell?ed/i.test(v);\n\ninterface OpenDialogOptions {\n  filters?: string[];\n  limit?: number;\n  terminal?: string;\n}\n\n/**\n * Builds the AppleScript 'choose file' command.\n * - allowMultiple: when limit is greater than 1, this will be true.\n * - filters: file type filters (e.g. {\"public.jpeg\", \"public.png\"}).\n */\n\nconst buildCommand = (\n  allowMultiple: boolean,\n  filters: string[]\n): string => {\n  const base = 'choose file';\n  const withPrompt = ' with prompt \"Select files to open\"';\n  const ofType = filters.length > 0\n    ? ` of type {${filters.map(f => `\"${f}\"`).join(', ')}}`\n    : '';\n  const multi = allowMultiple ? ' with multiple selections allowed' : '';\n  return base + withPrompt + ofType + multi;\n};\n\n/**\n * Opens a Finder dialog to select files.\n * @param initialDirectory - Directory dialog opens to.\n * @param options - Dialog config options.\n */\n\nexport const openFinderDialog = async (\n  initialDirectory: string = process.cwd(),\n  options: OpenDialogOptions = {}\n): Promise<{ files: string[]; canceled: boolean }> => {\n  return new Promise((resolve, reject) => {\n    const { filters = [], terminal = detectTerminal(), limit = 100 } = options;\n\n    const maxFiles = Math.max(1, limit);\n    const command = buildCommand(limit > 1, filters);\n    const escapedPath = escapeQuotedPath(initialDirectory);\n    const pathSeparator = '<__PATH_SEPARATOR__>';\n\n    const appleScript = `\n      set defaultPath to POSIX file \"${escapedPath}\"\n      set limit to ${maxFiles}\n\n      try\n        -- Open the file dialog within SystemUIServer to bring it to focus\n        tell application \"SystemUIServer\"\n          activate\n          delay 0.2 -- Allow SystemUIServer to activate\n          set file_list to ${command} default location defaultPath\n          if limit = 1 then\n            set file_list to {file_list}\n          end if\n        end tell\n\n        -- Apply the maximum-files rule\n        if limit > 0 and (count of file_list) > limit then\n          set file_list to items 1 thru limit of file_list\n        end if\n\n        -- Process the selected files\n        set posixPaths to {}\n\n        repeat with aFile in file_list\n          set end of posixPaths to POSIX path of aFile & \"${pathSeparator}\"\n        end repeat\n\n        -- Return focus to ${terminal}\n        tell application \"${terminal}\"\n          activate\n        end tell\n\n        set text item delimiters to \"\"\n        set posixPathsString to posixPaths as string\n\n        return posixPathsString\n      on error\n        -- Return focus to terminal app even if the dialog is canceled\n        tell application \"${terminal}\"\n          activate\n        end tell\n        return \"CANCELLED\"\n      end try\n    `;\n\n    const child = cp.spawn('osascript', ['-']);\n\n    let stdoutData = '';\n    let stderrData = '';\n\n    child.stdout.on('data', data => {\n      stdoutData += data.toString();\n    });\n\n    child.stderr.on('data', data => {\n      stderrData += data.toString();\n    });\n\n    child.on('close', code => {\n      if (code !== 0 && stderrData) {\n        reject(new Error(stderrData.trim()));\n        return;\n      }\n\n      const output = stdoutData.trim();\n\n      if (isCanceled(output)) {\n        resolve({ files: [], canceled: true });\n        return;\n      }\n\n      const files = output\n        .split(pathSeparator)\n        .map(s => s.trim())\n        .filter(s => s !== '');\n\n      resolve({ files, canceled: false });\n    });\n\n    // Write the AppleScript code to stdin\n    child.stdin.write(appleScript);\n    child.stdin.end();\n  });\n};\n\nexport default openFinderDialog;\n"],"mappings":";;;;AAAA,OAAO,QAAQ;AACf,SAAS,sBAAsB;AAE/B,IAAM,mBAAmB,wBAAC,UAA0B;AAClD,SAAO,MAAM,QAAQ,oBAAoB,MAAM;AACjD,GAFyB;AAIzB,IAAM,aAAa,wBAAC,MAAuB,cAAc,KAAK,CAAC,GAA5C;AAcnB,IAAM,eAAe,wBACnB,eACA,YACW;AACX,QAAM,OAAO;AACb,QAAM,aAAa;AACnB,QAAM,SAAS,QAAQ,SAAS,IAC5B,aAAa,QAAQ,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,MAClD;AACJ,QAAM,QAAQ,gBAAgB,sCAAsC;AACpE,SAAO,OAAO,aAAa,SAAS;AACtC,GAXqB;AAmBd,IAAM,mBAAmB,8BAC9B,mBAA2B,QAAQ,IAAI,GACvC,UAA6B,CAAC,MACsB;AACpD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,EAAE,UAAU,CAAC,GAAG,WAAW,eAAe,GAAG,QAAQ,IAAI,IAAI;AAEnE,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK;AAClC,UAAM,UAAU,aAAa,QAAQ,GAAG,OAAO;AAC/C,UAAM,cAAc,iBAAiB,gBAAgB;AACrD,UAAM,gBAAgB;AAEtB,UAAM,cAAc;AAAA,uCACe,WAAW;AAAA,qBAC7B,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DAewB,aAAa;AAAA;AAAA;AAAA,6BAG5C,QAAQ;AAAA,4BACT,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAUR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAOhC,UAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC;AAEzC,QAAI,aAAa;AACjB,QAAI,aAAa;AAEjB,UAAM,OAAO,GAAG,QAAQ,UAAQ;AAC9B,oBAAc,KAAK,SAAS;AAAA,IAC9B,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,UAAQ;AAC9B,oBAAc,KAAK,SAAS;AAAA,IAC9B,CAAC;AAED,UAAM,GAAG,SAAS,UAAQ;AACxB,UAAI,SAAS,KAAK,YAAY;AAC5B,eAAO,IAAI,MAAM,WAAW,KAAK,CAAC,CAAC;AACnC;AAAA,MACF;AAEA,YAAM,SAAS,WAAW,KAAK;AAE/B,UAAI,WAAW,MAAM,GAAG;AACtB,gBAAQ,EAAE,OAAO,CAAC,GAAG,UAAU,KAAK,CAAC;AACrC;AAAA,MACF;AAEA,YAAM,QAAQ,OACX,MAAM,aAAa,EACnB,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAK,MAAM,EAAE;AAEvB,cAAQ,EAAE,OAAO,UAAU,MAAM,CAAC;AAAA,IACpC,CAAC;AAGD,UAAM,MAAM,MAAM,WAAW;AAC7B,UAAM,MAAM,IAAI;AAAA,EAClB,CAAC;AACH,GA/FgC;AAiGhC,IAAO,6BAAQ;","names":[]}