refactor(launcher): canonicalize default profiles

This commit is contained in:
Scott Idem
2026-05-13 12:15:13 -04:00
parent c79ae92be0
commit 1374f0728e
5 changed files with 210 additions and 286 deletions

View File

@@ -8,7 +8,7 @@
* 1. event_file.cfg_json.display_override — per-file, display_mode only
* 2. event_device.data_json.launch_profiles[ext] — full profile, per device (API)
* 3. $events_loc.launcher.launch_profiles[ext] — local persistent override
* 4. DEFAULT_LAUNCH_PROFILES[ext] — these built-ins
* 4. DEFAULT_LAUNCH_PROFILES[ext] — extension aliases to canonical built-ins
* 5. DEFAULT_LAUNCH_PROFILES['default'] — catch-all
*
* Keys are lowercase file extensions without the dot: "pptx", "key", "pdf", etc.
@@ -18,9 +18,12 @@
* - Plain string → run as AppleScript via run_osascript() (macOS only)
* - "shell:..." prefix → run as shell command via run_cmd()
*
* Reserved for future use (not yet read anywhere):
* Reserved for future use:
* - speed_factor: number — delay multiplier for slower machines (1.0 = normal)
* - url: string — for URL-type presentations (e.g. Google Slides)
*
* Special pseudo-extension:
* - url — web-based presentations. Handled by the launcher URL branch rather
* than a cache-to-temp open flow.
*/
export interface LaunchProfile {
@@ -46,18 +49,29 @@ export interface LaunchProfile {
// url?: string;
}
export const DEFAULT_LAUNCH_PROFILES: Record<string, LaunchProfile> = {
// -------------------------------------------------------------------------
// macOS presentation formats
// -------------------------------------------------------------------------
pptx: {
app: 'Microsoft PowerPoint',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft PowerPoint" "{{path}}"',
function make_vlc_mirror_profile(): LaunchProfile {
return {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Microsoft PowerPoint"
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
};
}
const POWERPOINT_MAC_EXTEND_PROFILE: LaunchProfile = {
app: 'Microsoft PowerPoint',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft PowerPoint" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Microsoft PowerPoint"
activate
end tell
tell application "System Events"
@@ -65,40 +79,25 @@ tell application "System Events"
keystroke return using command down
end tell
end tell`
},
};
ppt: {
app: 'Microsoft PowerPoint',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft PowerPoint" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Microsoft PowerPoint"
activate
end tell
tell application "System Events"
tell process "Microsoft PowerPoint"
keystroke return using command down
end tell
end tell`
},
key: {
app: 'Keynote',
display_mode: 'extend',
open_cmd: 'open -a "Keynote" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Keynote"
const KEYNOTE_MAC_EXTEND_PROFILE: LaunchProfile = {
app: 'Keynote',
display_mode: 'extend',
open_cmd: 'open -a "Keynote" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Keynote"
activate
start (front document)
end tell`
},
};
odp: {
app: 'LibreOffice',
display_mode: 'extend',
open_cmd: 'open -a "LibreOffice" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "LibreOffice"
const LIBREOFFICE_MAC_EXTEND_PROFILE: LaunchProfile = {
app: 'LibreOffice',
display_mode: 'extend',
open_cmd: 'open -a "LibreOffice" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "LibreOffice"
activate
end tell
tell application "System Events"
@@ -106,14 +105,14 @@ tell application "System Events"
key code 96
end tell
end tell`
},
};
pdf: {
app: 'Adobe Acrobat Reader DC',
display_mode: 'mirror',
open_cmd: 'open -a "Adobe Acrobat Reader DC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Adobe Acrobat Reader DC"
const ACROBAT_MAC_MIRROR_PROFILE: LaunchProfile = {
app: 'Adobe Acrobat Reader DC',
display_mode: 'mirror',
open_cmd: 'open -a "Adobe Acrobat Reader DC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "Adobe Acrobat Reader DC"
activate
end tell
tell application "System Events"
@@ -121,198 +120,29 @@ tell application "System Events"
keystroke "l" using command down
end tell
end tell`
},
};
// -------------------------------------------------------------------------
// Media (VLC) — mirror display
// -------------------------------------------------------------------------
const VLC_MIRROR_PROFILE: LaunchProfile = make_vlc_mirror_profile();
mp4: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
mkv: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
mov: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
mpeg: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
avi: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
flv: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
ogg: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
ogv: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
mp3: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
wmv: {
app: 'VLC',
display_mode: 'mirror',
open_cmd: 'open -a "VLC" "{{path}}"',
post_delay_ms: 2000,
post_script: `tell application "VLC"
activate
end tell
tell application "System Events"
tell process "VLC"
keystroke "f" using command down
end tell
end tell`
},
// -------------------------------------------------------------------------
// Windows / Parallels variants — longer post_delay_ms (Parallels needs more time)
// -------------------------------------------------------------------------
pptxwin: {
app: 'Microsoft Office PowerPoint (Windows)',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft Office PowerPoint" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "Microsoft Office PowerPoint"
const POWERPOINT_WIN_EXTEND_PROFILE: LaunchProfile = {
app: 'Microsoft Office PowerPoint (Windows)',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft Office PowerPoint" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "Microsoft Office PowerPoint"
activate
end tell
tell application "System Events"
key code 96
end tell`
},
};
pptwin: {
app: 'Microsoft Office PowerPoint (Windows)',
display_mode: 'extend',
open_cmd: 'open -a "Microsoft Office PowerPoint" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "Microsoft Office PowerPoint"
activate
end tell
tell application "System Events"
key code 96
end tell`
},
odpwin: {
app: 'LibreOffice (Windows)',
display_mode: 'extend',
open_cmd: 'open -a "LibreOffice" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "LibreOffice"
const LIBREOFFICE_WIN_EXTEND_PROFILE: LaunchProfile = {
app: 'LibreOffice (Windows)',
display_mode: 'extend',
open_cmd: 'open -a "LibreOffice" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "LibreOffice"
activate
end tell
tell application "System Events"
@@ -320,46 +150,110 @@ tell application "System Events"
key code 96
end tell
end tell`
},
};
pdfwin: {
app: 'Acrobat Reader (Windows)',
display_mode: 'mirror',
open_cmd: 'open -a "Acrobat Reader Windows" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "Acrobat Reader Windows"
const ACROBAT_WIN_MIRROR_PROFILE: LaunchProfile = {
app: 'Acrobat Reader (Windows)',
display_mode: 'mirror',
open_cmd: 'open -a "Acrobat Reader Windows" "{{path}}"',
post_delay_ms: 3000,
post_script: `tell application "Acrobat Reader Windows"
activate
end tell
tell application "System Events"
key code 108 using control down
end tell`
},
// -------------------------------------------------------------------------
// Catch-all — OS default handler, no display change
// Works on macOS (open) and Linux (xdg-open via open_local_file_v2)
// -------------------------------------------------------------------------
default: {
app: 'OS Default',
display_mode: 'none'
// No open_cmd — execution falls through to open_local_file_v2(path)
// No post_script
},
// -------------------------------------------------------------------------
// URL-type files: event_file.filename IS the URL (https://...)
// Opened via native.open_external({ url, app: 'chrome' }) — no local file involved.
// display_mode 'extend' is the default for URL presentations (e.g. Google Slides).
// -------------------------------------------------------------------------
url: {
app: 'Chrome',
display_mode: 'mirror'
// No open_cmd or post_script — URL branch in handle_open_file() handles this
}
};
const URL_WEB_PROFILE: LaunchProfile = {
app: 'Chrome',
display_mode: 'extend',
// No open_cmd or post_script — URL branch in handle_open_file() handles this
};
const DEFAULT_OS_PROFILE: LaunchProfile = {
app: 'OS Default',
display_mode: 'none',
// No open_cmd — execution falls through to open_local_file_v2(path)
// No post_script
};
type DefaultLaunchProfileDefinition = {
name: string;
aliases: string[];
profile: LaunchProfile;
};
export const DEFAULT_LAUNCH_PROFILE_DEFS: DefaultLaunchProfileDefinition[] = [
{
name: 'powerpoint_mac_extend',
aliases: ['pptx', 'ppt'],
profile: POWERPOINT_MAC_EXTEND_PROFILE
},
{
name: 'keynote_mac_extend',
aliases: ['key'],
profile: KEYNOTE_MAC_EXTEND_PROFILE
},
{
name: 'libreoffice_mac_extend',
aliases: ['odp'],
profile: LIBREOFFICE_MAC_EXTEND_PROFILE
},
{
name: 'acrobat_mac_mirror',
aliases: ['pdf'],
profile: ACROBAT_MAC_MIRROR_PROFILE
},
{
name: 'vlc_mirror',
aliases: ['mp4', 'mkv', 'mov', 'mpeg', 'avi', 'flv', 'ogg', 'ogv', 'mp3', 'm4v', 'm4a', 'webm', 'wmv', 'wav', 'aac', 'flac'],
profile: VLC_MIRROR_PROFILE
},
{
name: 'powerpoint_win_extend',
aliases: ['pptxwin', 'pptwin'],
profile: POWERPOINT_WIN_EXTEND_PROFILE
},
{
name: 'libreoffice_win_extend',
aliases: ['odpwin'],
profile: LIBREOFFICE_WIN_EXTEND_PROFILE
},
{
name: 'acrobat_win_mirror',
aliases: ['pdfwin'],
profile: ACROBAT_WIN_MIRROR_PROFILE
},
{
name: 'url_web',
aliases: ['url'],
profile: URL_WEB_PROFILE
},
{
name: 'os_default',
aliases: ['default'],
profile: DEFAULT_OS_PROFILE
}
];
export const DEFAULT_LAUNCH_PROFILE_LIBRARY: Record<string, LaunchProfile> = Object.fromEntries(
DEFAULT_LAUNCH_PROFILE_DEFS.map(({ name, profile }) => [name, profile])
);
export const DEFAULT_LAUNCH_PROFILE_ALIASES: Record<string, string> = Object.fromEntries(
DEFAULT_LAUNCH_PROFILE_DEFS.flatMap(({ name, aliases }) =>
aliases.map((alias) => [alias, name])
)
);
export const DEFAULT_LAUNCH_PROFILES: Record<string, LaunchProfile> = Object.fromEntries(
DEFAULT_LAUNCH_PROFILE_DEFS.flatMap(({ name, aliases, profile }) => [
[name, { ...profile }],
...aliases.map((alias) => [alias, { ...profile }])
])
);
/**
* Returns a shallow copy of the built-in profile for the given extension,
* with a display_override applied if provided.
@@ -373,15 +267,16 @@ export function resolve_launch_profile(
local_profiles?: Record<string, LaunchProfile> | null
): LaunchProfile {
const ext = (extension || '').toLowerCase().replace(/^\./, '');
const default_profile_name = DEFAULT_LAUNCH_PROFILE_ALIASES[ext] ?? ext;
// Priority: device config → local config → built-ins → default
// Priority: device config → local config → canonical built-ins → default
const source =
device_profiles?.[ext] ??
device_profiles?.['default'] ??
local_profiles?.[ext] ??
local_profiles?.['default'] ??
DEFAULT_LAUNCH_PROFILES[ext] ??
DEFAULT_LAUNCH_PROFILES['default'];
DEFAULT_LAUNCH_PROFILES[default_profile_name] ??
DEFAULT_LAUNCH_PROFILE_LIBRARY.os_default;
const profile = { ...source };

View File

@@ -65,7 +65,7 @@ export async function launch_from_cache({
temp_root,
filename,
hash_prefix_length = 2,
script_template = null
native_template = null
}: {
cache_root: string;
hash: string;
@@ -73,17 +73,17 @@ export async function launch_from_cache({
filename: string;
hash_prefix_length?: number;
/**
* Optional data-driven launch script. If provided, Electron runs this instead of
* its hardcoded extension-based logic — no app rebuild needed for script changes.
* Resolved native launch template. If provided, Electron executes this string
* after the file is copied to temp.
*
* Two formats:
* - AppleScript: multi-line string with {{path}} placeholder (macOS only)
* - Shell command: prefix with "shell:" → e.g. "shell:open \"{{path}}\""
*
* Configure via event_device.data_json.launch_profiles or $events_loc.launcher.launch_profiles.
* If null, Electron falls through to its built-in hardcoded defaults.
* If null, Electron should treat that as a missing profile error.
*/
script_template?: string | null;
native_template?: string | null;
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
@@ -93,7 +93,7 @@ export async function launch_from_cache({
temp_root,
filename,
hash_prefix_length,
script_template
native_template
});
}