UNPKG

open-linux-file-dialog

Version:

Open a file dialog window programmatically to allow the user to select one or more files. Only works on Linux. No dependencies. Supports zenity (GNOME), kdialog (KDE), yad (Yet Another Dialog), qarma (Qt-based), matedialog (MATE), rofi (window switcher wi

1 lines 16.9 kB
{"version":3,"sources":["../index.ts"],"sourcesContent":["import cp from 'node:child_process';\nimport path from 'node:path';\nimport util from 'node:util';\n\nconst execAsync = util.promisify(cp.exec);\n\ninterface FileDialogOptions {\n multiple?: boolean;\n title?: string;\n fileTypes?: string[]; // e.g., ['*.txt', '*.md']\n}\n\ninterface DialogTool {\n name: string;\n check: () => Promise<boolean>;\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => string[];\n parseOutput?: (output: string) => string[];\n}\n\nclass FileDialogError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FileDialogError';\n }\n}\n\nconst escapeShellArg = (arg: string): string => {\n // Escape single quotes and wrap in single quotes\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n};\n\nconst checkCommand = async (command: string): Promise<boolean> => {\n try {\n const { stdout } = await execAsync(`command -v ${command}`);\n return Boolean(stdout.trim());\n } catch {\n return false;\n }\n};\n\nconst zenityTool: DialogTool = {\n name: 'zenity',\n check: () => checkCommand('zenity'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const args = ['zenity', '--file-selection'];\n\n if (options.multiple) {\n args.push('--multiple', '--separator=\\\\n');\n }\n\n if (options.title) {\n args.push('--title', escapeShellArg(options.title));\n }\n\n if (options.fileTypes && options.fileTypes.length > 0) {\n for (const type of options.fileTypes) {\n args.push('--file-filter', escapeShellArg(type));\n }\n }\n\n args.push('--filename', escapeShellArg(`${initialDirectory}${path.sep}`));\n return args;\n }\n};\n\nconst kdialogTool: DialogTool = {\n name: 'kdialog',\n check: () => checkCommand('kdialog'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const args = ['kdialog'];\n\n if (options.multiple) {\n args.push('--getopenfilenames');\n } else {\n args.push('--getopenfilename');\n }\n\n args.push(escapeShellArg(initialDirectory));\n\n if (options.fileTypes && options.fileTypes.length > 0) {\n args.push(escapeShellArg(options.fileTypes.join(' ')));\n }\n\n if (options.title) {\n args.push('--title', escapeShellArg(options.title));\n }\n\n return args;\n }\n};\n\nconst yad: DialogTool = {\n name: 'yad',\n check: () => checkCommand('yad'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const args = ['yad', '--file-selection'];\n\n if (options.multiple) {\n args.push('--multiple', '--separator=\\\\n');\n }\n\n if (options.title) {\n args.push('--title', escapeShellArg(options.title));\n }\n\n if (options.fileTypes && options.fileTypes.length > 0) {\n for (const type of options.fileTypes) {\n args.push('--file-filter', escapeShellArg(type));\n }\n }\n\n args.push('--filename', escapeShellArg(`${initialDirectory}${path.sep}`));\n return args;\n }\n};\n\nconst qarma: DialogTool = {\n name: 'qarma',\n check: () => checkCommand('qarma'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const args = ['qarma', '--file-selection'];\n\n if (options.multiple) {\n args.push('--multiple', '--separator=\\\\n');\n }\n\n if (options.title) {\n args.push('--title', escapeShellArg(options.title));\n }\n\n if (options.fileTypes && options.fileTypes.length > 0) {\n for (const type of options.fileTypes) {\n args.push('--file-filter', escapeShellArg(type));\n }\n }\n\n args.push('--filename', escapeShellArg(`${initialDirectory}${path.sep}`));\n return args;\n }\n};\n\nconst matedialog: DialogTool = {\n name: 'matedialog',\n check: () => checkCommand('matedialog'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const args = ['matedialog', '--file-selection'];\n\n if (options.multiple) {\n args.push('--multiple', '--separator=\\\\n');\n }\n\n if (options.title) {\n args.push('--title', escapeShellArg(options.title));\n }\n\n args.push('--filename', escapeShellArg(`${initialDirectory}${path.sep}`));\n return args;\n }\n};\n\nconst osascript: DialogTool = {\n name: 'osascript',\n check: async () => {\n // Only available on macOS\n try {\n const { stdout } = await execAsync('uname -s');\n return stdout.trim() === 'Darwin' && await checkCommand('osascript');\n } catch {\n return false;\n }\n },\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n let script = 'tell application \"System Events\" to activate\\n';\n\n if (options.multiple) {\n script += `set theFiles to choose file with prompt \"${options.title || 'Select files'}\" `;\n script += `default location POSIX file \"${initialDirectory}\" `;\n script += 'with multiple selections allowed\\n';\n script += 'set output to \"\"\\n';\n script += 'repeat with aFile in theFiles\\n';\n script += 'set output to output & POSIX path of aFile & \"\\n\"\\n';\n script += 'end repeat\\n';\n script += 'return output';\n } else {\n script += `set theFile to choose file with prompt \"${options.title || 'Select a file'}\" `;\n script += `default location POSIX file \"${initialDirectory}\"\\n`;\n script += 'return POSIX path of theFile';\n }\n\n return ['osascript', '-e', escapeShellArg(script)];\n },\n parseOutput: (output: string) => {\n // osascript adds trailing newline, remove it\n return output.trim().split('\\n').filter(Boolean);\n }\n};\n\nconst rofi: DialogTool = {\n name: 'rofi',\n check: () => checkCommand('rofi'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n // Rofi with file browser mode\n const args = [\n 'bash',\n '-c',\n escapeShellArg(\n `cd ${escapeShellArg(initialDirectory)} && find . -type f | rofi -dmenu -p \"${options.title || 'Select file'}\" -multi-select`\n )\n ];\n\n return args;\n },\n parseOutput: (output: string) => {\n // Convert relative paths to absolute\n return output\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map(p => {\n if (p.startsWith('./')) {\n return path.resolve(p);\n }\n return p;\n });\n }\n};\n\nconst dmenu: DialogTool = {\n name: 'dmenu',\n check: () => checkCommand('dmenu'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n // dmenu with file listing\n const args = [\n 'bash',\n '-c',\n escapeShellArg(\n `cd ${escapeShellArg(initialDirectory)} && find . -type f | dmenu -p \"${options.title || 'Select file'}\"`\n )\n ];\n\n return args;\n },\n parseOutput: (output: string) => {\n // Convert relative paths to absolute and handle single selection\n const selected = output.trim();\n if (!selected) return [];\n\n if (selected.startsWith('./')) {\n return [path.resolve(selected)];\n }\n return [selected];\n }\n};\n\n// Terminal-based fallback using fzf\nconst fzf: DialogTool = {\n name: 'fzf',\n check: () => checkCommand('fzf'),\n buildCommand: (initialDirectory: string, options: FileDialogOptions) => {\n const fzfOpts = ['--prompt', `\"${options.title || 'Select file'}> \"`];\n\n if (options.multiple) {\n fzfOpts.push('-m'); // multi-select mode\n }\n\n const args = [\n 'bash',\n '-c',\n escapeShellArg(`cd ${escapeShellArg(initialDirectory)} && find . -type f | fzf ${fzfOpts.join(' ')}`)\n ];\n\n return args;\n },\n parseOutput: (output: string) => {\n // Convert relative paths to absolute\n return output\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map(p => {\n if (p.startsWith('./')) {\n return path.resolve(p);\n }\n return p;\n });\n }\n};\n\n// Order matters - more feature-rich tools first\nconst tools: DialogTool[] = [zenityTool, kdialogTool, yad, qarma, matedialog, osascript, rofi, fzf, dmenu];\n\nexport const openLinuxFileDialog = async (\n initialDirectory: string = process.cwd(),\n options: FileDialogOptions = {}\n): Promise<string[]> => {\n if (typeof initialDirectory !== 'string') {\n throw new FileDialogError('Filepath must be a string');\n }\n\n const normalizedPath = path.resolve(initialDirectory);\n let availableTool: DialogTool | null = null;\n\n for (const tool of tools) {\n if (await tool.check()) {\n availableTool = tool;\n break;\n }\n }\n\n if (!availableTool) {\n throw new FileDialogError(\n 'No suitable file dialog tool found. Please install one of: zenity, kdialog, yad, qarma, matedialog, rofi, fzf, or dmenu.'\n );\n }\n\n const commandArgs = availableTool.buildCommand(normalizedPath, options);\n const command = commandArgs.join(' ');\n\n try {\n const { stdout, stderr } = await execAsync(command, {\n shell: '/bin/bash',\n maxBuffer: 10 * 1024 * 1024 // 10MB buffer for large file selections\n });\n\n if (stderr && !stderr.includes('Gtk-WARNING')) {\n console.warn(`File dialog warning: ${stderr}`);\n }\n\n const output = stdout.trim();\n if (!output) {\n // User cancelled the dialog\n return [];\n }\n\n // Parse the output using custom parser if available\n let filePaths: string[];\n if (availableTool.parseOutput) {\n filePaths = availableTool.parseOutput(output);\n } else {\n filePaths = output.split('\\n').filter(Boolean);\n }\n\n // Validate that all paths are absolute\n const validPaths = filePaths.filter(p => path.isAbsolute(p));\n if (validPaths.length !== filePaths.length) {\n console.warn('Some file paths were not absolute and were filtered out');\n }\n\n return validPaths;\n } catch (error) {\n // Check if user cancelled (exit code 1)\n if (error instanceof Error && 'code' in error && error.code === 1) {\n return [];\n }\n\n throw new FileDialogError(\n `Failed to open file dialog with ${availableTool.name}: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n};\n\nexport const getAvailableDialogTools = async (): Promise<string[]> => {\n const available: string[] = [];\n\n for (const tool of tools) {\n if (await tool.check()) {\n available.push(tool.name);\n }\n }\n\n return available;\n};\n\nexport default openLinuxFileDialog;\n"],"mappings":"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","names":[]}