Last round of prettier: npx prettier --write src/

This commit is contained in:
Scott Idem
2026-03-24 13:27:40 -04:00
parent 23d25bf65a
commit a8f3c29b9f
146 changed files with 13201 additions and 9277 deletions

View File

@@ -9,14 +9,14 @@ The native implementation lives in a separate repo:
## Technical Manual
For detailed architecture, lifecycle, and IPC specifications, see:
[PROJECT__AE_Events_Launcher_Native_integration.md](../../../documentation/PROJECT__AE_Events_Launcher_Native_integration.md)
[PROJECT\_\_AE_Events_Launcher_Native_integration.md](../../../documentation/PROJECT__AE_Events_Launcher_Native_integration.md)
## File Manifest
| File | Role | Description |
| :--- | :--- | :--- |
| `electron_relay.ts` | **Active — Messenger** | The TypeScript API used by Svelte components. Standardizes all IPC calls to `snake_case`. This is the only file in this directory that should be imported. |
| `electron_native.js` | **DEPRECATED — Do not import** | Legacy V2/V4 reference file. The active native logic lives in `aether_app_native_electron/`. Kept for historical reference only. |
| File | Role | Description |
| :------------------- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `electron_relay.ts` | **Active — Messenger** | The TypeScript API used by Svelte components. Standardizes all IPC calls to `snake_case`. This is the only file in this directory that should be imported. |
| `electron_native.js` | **DEPRECATED — Do not import** | Legacy V2/V4 reference file. The active native logic lives in `aether_app_native_electron/`. Kept for historical reference only. |
## Usage Example

View File

@@ -1,5 +1,5 @@
// [DEPRECATED] 2026-02-10: This file is a legacy reference from Aether V2/V4.
// Do NOT import this into the SvelteKit frontend.
// [DEPRECATED] 2026-02-10: This file is a legacy reference from Aether V2/V4.
// Do NOT import this into the SvelteKit frontend.
// The active native logic resides in the 'aether_app_native_electron' repository.
// @ts-nocheck
@@ -51,7 +51,8 @@ let default_osit_sync_app_root_path = path.join(
let default_osit_sync_native_app_relative_path =
'OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/osit_aether.current.app'; // Mainly for macOS laptops
let default_native_app_relative_path = 'OSIT/native_app/osit_aether.app'; // Mainly for macOS laptops
let default_current_native_app_relative_path = 'OSIT/native_app/osit_aether.current.app'; // Mainly for macOS laptops
let default_current_native_app_relative_path =
'OSIT/native_app/osit_aether.current.app'; // Mainly for macOS laptops
let test_native_app_relative_path = 'OSIT/native_app/osit_aether.remote.app'; // Mainly for macOS laptops
// exports.check_for_native_app_update = function () {
@@ -115,7 +116,10 @@ exports.load_init_config = function () {
config_directory = path.join(home_directory, 'OSIT/native_app');
console.log('macOS config directory: ' + config_directory);
let electron_app_path = path.join(home_directory, default_native_app_relative_path);
let electron_app_path = path.join(
home_directory,
default_native_app_relative_path
);
let test_stats = fs.statSync(electron_app_path);
console.log(test_stats);
} else if (os.platform == 'linux') {
@@ -140,8 +144,13 @@ exports.load_init_config = function () {
// Attempt to open the config file. The preferred location is based on the OS's config directory.
if (fs.existsSync(config_path)) {
console.log(`Config file (ae_native_app_config.json) found under ${config_directory}`);
} else if (!fs.existsSync(config_path) && fs.existsSync(default_config_path)) {
console.log(
`Config file (ae_native_app_config.json) found under ${config_directory}`
);
} else if (
!fs.existsSync(config_path) &&
fs.existsSync(default_config_path)
) {
fs.copyFileSync(default_config_path, config_path);
console.log('Default config file copied: ' + config_directory);
@@ -163,11 +172,16 @@ exports.load_init_config = function () {
fs.copyFileSync(found_config_path, config_path);
console.log(`Found config file copied: ${config_directory}`);
} else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.current.json'))) {
} else if (
fs.existsSync(path.join(cwd, 'ae_native_app_config.current.json'))
) {
console.log(
`Config file (config.json) not found under ${config_directory} or CWD. Using default config in CWD. ${cwd}`
);
default_config_path = path.join(cwd, 'ae_native_app_config.current.json');
default_config_path = path.join(
cwd,
'ae_native_app_config.current.json'
);
fs.copyFileSync(default_config_path, config_path);
console.log(`Default config file copied: ${config_directory}`);
@@ -186,31 +200,53 @@ exports.load_init_config = function () {
config.home_directory = home_directory; // From the OS platform
config.tmp_directory = tmp_directory; // From the OS platform
config.app_root_path = config.app_root_path.replace('[home]', home_directory);
config.app_root_path = config.app_root_path.replace(
'[home]',
home_directory
);
config.app_root_path = config.app_root_path.replace('[tmp]', tmp_directory);
console.log(`App Root Path: ${config.app_root_path}`);
config.local_file_cache_path = config.local_file_cache_path.replace('[home]', home_directory);
config.local_file_cache_path = config.local_file_cache_path.replace('[tmp]', tmp_directory);
config.local_file_cache_path = config.local_file_cache_path.replace(
'[home]',
home_directory
);
config.local_file_cache_path = config.local_file_cache_path.replace(
'[tmp]',
tmp_directory
);
console.log(`Local File Cache Path: ${config.local_file_cache_path}`);
if (fs.existsSync(config.local_file_cache_path)) {
} else {
fs.mkdirSync(config.local_file_cache_path);
console.log(`Host file cache directory created: ${config.local_file_cache_path}`);
console.log(
`Host file cache directory created: ${config.local_file_cache_path}`
);
}
config.host_file_temp_path = config.host_file_temp_path.replace('[home]', home_directory);
config.host_file_temp_path = config.host_file_temp_path.replace('[tmp]', tmp_directory);
config.host_file_temp_path = config.host_file_temp_path.replace(
'[home]',
home_directory
);
config.host_file_temp_path = config.host_file_temp_path.replace(
'[tmp]',
tmp_directory
);
console.log(`Host file temp path: ${config.host_file_temp_path}`);
if (fs.existsSync(config.host_file_temp_path)) {
} else {
fs.mkdirSync(config.host_file_temp_path);
console.log(`Host file temp directory created: ${config.host_file_temp_path}`);
console.log(
`Host file temp directory created: ${config.host_file_temp_path}`
);
}
// NOTE: This is not ideal...
if (!config.account_id) {
if (config.event_id == 'pjrcghqwert' || config.event_id == 'nmBfuGFeR0k') {
if (
config.event_id == 'pjrcghqwert' ||
config.event_id == 'nmBfuGFeR0k'
) {
config.account_id = '_XY7DXtc9MY';
} else if (config.event_id == 'r8c-rr-I4-52') {
// CMSC
@@ -235,11 +271,13 @@ exports.load_init_config = function () {
}
}
let import_config_to_ipc_result = ipcRenderer.invoke('import_config', config).then((result) => {
console.log('IPC import config finished');
// console.log(result);
return result; // Result should be "true"
});
let import_config_to_ipc_result = ipcRenderer
.invoke('import_config', config)
.then((result) => {
console.log('IPC import config finished');
// console.log(result);
return result; // Result should be "true"
});
//console.log(config);
return config;
@@ -263,37 +301,52 @@ exports.load_full_config = function (init_config) {
new_config.event_location_id = cfg.event_location_id;
new_config.event_session_id = cfg.event_session_id;
new_config.check_event_device_loop_period = cfg.check_event_device_loop_period;
new_config.check_event_location_loop_period = cfg.check_event_location_loop_period;
new_config.check_event_device_loop_period =
cfg.check_event_device_loop_period;
new_config.check_event_location_loop_period =
cfg.check_event_location_loop_period;
new_config.check_event_loop_period = cfg.check_event_loop_period;
new_config.check_event_session_loop_period = cfg.check_event_session_loop_period;
new_config.check_event_session_loop_period =
cfg.check_event_session_loop_period;
new_config.record_audio = cfg.record_audio;
new_config.record_video = cfg.record_video;
new_config.recording_path = cfg.recording_path;
new_config.recording_path = new_config.recording_path.replace('[home]', home_directory);
new_config.recording_path = new_config.recording_path.replace('[tmp]', home_directory);
new_config.recording_path = new_config.recording_path.replace(
'[home]',
home_directory
);
new_config.recording_path = new_config.recording_path.replace(
'[tmp]',
home_directory
);
new_config.home_directory = home_directory; // From the OS platform
new_config.tmp_directory = tmp_directory; // From the OS platform
new_config.app_root_path = new_config.app_root_path.replace('[home]', home_directory);
new_config.app_root_path = new_config.app_root_path.replace('[tmp]', tmp_directory);
console.log(`App Root Path: ${new_config.app_root_path}`);
new_config.local_file_cache_path = new_config.local_file_cache_path.replace(
new_config.app_root_path = new_config.app_root_path.replace(
'[home]',
home_directory
);
new_config.local_file_cache_path = new_config.local_file_cache_path.replace(
new_config.app_root_path = new_config.app_root_path.replace(
'[tmp]',
tmp_directory
);
console.log(`Local File Cache Path: ${new_config.local_file_cache_path}`);
console.log(`App Root Path: ${new_config.app_root_path}`);
new_config.local_file_cache_path =
new_config.local_file_cache_path.replace('[home]', home_directory);
new_config.local_file_cache_path =
new_config.local_file_cache_path.replace('[tmp]', tmp_directory);
console.log(
`Local File Cache Path: ${new_config.local_file_cache_path}`
);
if (fs.existsSync(new_config.local_file_cache_path)) {
} else {
fs.mkdirSync(new_config.local_file_cache_path);
console.log(`Host file cache directory created: ${new_config.local_file_cache_path}`);
console.log(
`Host file cache directory created: ${new_config.local_file_cache_path}`
);
}
new_config.host_file_temp_path = new_config.host_file_temp_path.replace(
@@ -308,7 +361,9 @@ exports.load_full_config = function (init_config) {
if (fs.existsSync(new_config.host_file_temp_path)) {
} else {
fs.mkdirSync(new_config.host_file_temp_path);
console.log(`Host file temp directory created: ${new_config.host_file_temp_path}`);
console.log(
`Host file temp directory created: ${new_config.host_file_temp_path}`
);
}
let import_config_to_ipc_result = ipcRenderer
@@ -374,9 +429,13 @@ async function get_url_cfg(cfg) {
let return_data = response.data['data'];
if (Array.isArray(return_data)) {
console.log(`Data result is an array/list. Array length: ${return_data.length}`);
console.log(
`Data result is an array/list. Array length: ${return_data.length}`
);
} else {
console.log(`Data result is a dictionary/object, not an array/list.`);
console.log(
`Data result is a dictionary/object, not an array/list.`
);
}
return return_data;
})
@@ -423,7 +482,10 @@ exports.check_local_file = async function ({ local_file_path, filename }) {
// Used by Svelte Event Launcher
// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11
// Updated 2022-05-06
exports.check_hash_file_cache = async function ({ local_file_cache_path, hash }) {
exports.check_hash_file_cache = async function ({
local_file_cache_path,
hash
}) {
// console.log('*** Electron framework export: check_hash_file_cache() ***');
// console.log('Check local hash file cache');
console.log(
@@ -436,7 +498,9 @@ exports.check_hash_file_cache = async function ({ local_file_cache_path, hash })
let subdirectory_path = path.join(local_file_cache_path, subdirectory);
if (fs.existsSync(subdirectory_path)) {
} else {
console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`);
console.log(
`Hashed file subdirectory not found in cache: ${subdirectory_path}`
);
return false;
}
@@ -461,12 +525,16 @@ exports.check_hash_file_cache_v2 = async function ({
verify_hash = false
}) {
console.log('*** Aether App Native export: check_hash_file_cache_v2() ***');
console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}`);
console.log(
`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}`
);
if (fs.existsSync(local_file_cache_path)) {
} else {
// This should not happen. The directory needs to be created.
console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`);
console.log(
`Cache directory for hashed files was not found: ${local_file_cache_path}`
);
return false;
}
@@ -478,7 +546,9 @@ exports.check_hash_file_cache_v2 = async function ({
if (fs.existsSync(subdirectory_path)) {
} else {
// This should not happen. The subdirectory needs to be created.
console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`);
console.log(
`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`
);
return false;
}
@@ -589,7 +659,9 @@ exports.download_hash_file_to_cache_v2 = async function ({
verify_hash = true,
overwrite_existing = false
}) {
console.log('*** Aether App Native export: download_hash_file_to_cache_v2() ***');
console.log(
'*** Aether App Native export: download_hash_file_to_cache_v2() ***'
);
console.log(
`Base URL: ${api_base_url}; Base URL Backup: ${api_base_url_backup}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`
);
@@ -597,7 +669,9 @@ exports.download_hash_file_to_cache_v2 = async function ({
if (fs.existsSync(local_file_cache_path)) {
} else {
// This should not happen. The directory needs to be created.
console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`);
console.log(
`Cache directory for hashed files was not found: ${local_file_cache_path}`
);
return false;
}
@@ -668,7 +742,9 @@ exports.download_hash_file_to_cache_v2 = async function ({
let current_datetime = new Date();
let offset_minutes = 3; // In minutes. 5 minutes of no changes to the file content seems reasonable? Trying with 3 minutes 2022-10-12
let offset_datetime = new Date(current_datetime.getTime() - offset_minutes * 60000);
let offset_datetime = new Date(
current_datetime.getTime() - offset_minutes * 60000
);
// console.log(`Times: ${current_datetime} ${offset_datetime} | File ${stats.mtime}`);
if (stats.mtime < offset_datetime) {
@@ -696,10 +772,15 @@ exports.download_hash_file_to_cache_v2 = async function ({
)
.then(async (result) => {
if (result) {
console.log(`IPC API download file process finished successfully: ${hash}`);
console.log(
`IPC API download file process finished successfully: ${hash}`
);
return true;
} else if (result == null) {
console.log(`IPC API download result (file not found?):`, result);
console.log(
`IPC API download result (file not found?):`,
result
);
return null;
} else if (result === 'in_progress') {
console.log(`IPC API primary download IN PROGRESS: ${hash}`);
@@ -744,7 +825,9 @@ exports.download_hash_file_to_cache_v2 = async function ({
console.log(
`IPC API primary download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`
);
console.log(`Trying IPC API backup download from: ${api_base_url_backup}`);
console.log(
`Trying IPC API backup download from: ${api_base_url_backup}`
);
let download_backup_file_result = await ipcRenderer
.invoke(
'download_file',
@@ -832,12 +915,20 @@ exports.open_hash_file_to_temp = async function ({
let subdirectory_path = path.join(local_file_cache_path, subdirectory);
if (fs.existsSync(subdirectory_path)) {
} else {
console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`);
console.log(
`Hashed file subdirectory not found in cache: ${subdirectory_path}`
);
return false;
}
let open_hash_file_to_temp_result = await ipcRenderer
.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename)
.invoke(
'open_hash_file_to_temp',
subdirectory_path,
hash,
host_file_temp_path,
filename
)
.then((result) => {
console.log('IPC open hash file to temp finished');
console.log(result);
@@ -868,7 +959,9 @@ exports.open_hash_file_to_temp_v2 = async function ({
filename,
verify_hash = true
}) {
console.log('*** Aether App Native export: open_hash_file_to_temp_v2() ***');
console.log(
'*** Aether App Native export: open_hash_file_to_temp_v2() ***'
);
console.log(
`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Local File Temp Path: ${host_file_temp_path}; Filename: ${filename}`
);
@@ -878,7 +971,9 @@ exports.open_hash_file_to_temp_v2 = async function ({
if (fs.existsSync(local_file_cache_path)) {
} else {
// This should not happen. The directory needs to be created.
console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`);
console.log(
`Cache directory for hashed files was not found: ${local_file_cache_path}`
);
return false;
}
@@ -890,7 +985,9 @@ exports.open_hash_file_to_temp_v2 = async function ({
if (fs.existsSync(subdirectory_path)) {
} else {
// This should not happen. The subdirectory needs to be created.
console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`);
console.log(
`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`
);
return false;
}
@@ -1066,7 +1163,12 @@ exports.open_local_file = async function ({ local_file_path, filename }) {
// Check local file cache and download from server if needed. Must use IPC to Main to download file. Set a Promise to wait for download_file_reply.
// Updated 2022-03-09
async function check_file_cache({ api_base_url, local_file_cache_path, event_file_id, hash }) {
async function check_file_cache({
api_base_url,
local_file_cache_path,
event_file_id,
hash
}) {
console.log('*** Electron framework: check_file_cache() ***');
// console.log('Check local file cache and download from server if needed.');
console.log(
@@ -1083,9 +1185,17 @@ async function check_file_cache({ api_base_url, local_file_cache_path, event_fil
console.log('Hashed file cache already exists: ' + save_path);
return true;
} else {
console.log('Hashed file not found in local cache. Downloading file: ' + save_path);
console.log(
'Hashed file not found in local cache. Downloading file: ' +
save_path
);
let endpoint = `/event/file/${event_file_id}/download`;
let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread.
let result = await ipcRenderer.send(
'download_file',
api_base_url,
endpoint,
save_path
); // Must download file using main node.js thread.
console.log(result);
return new Promise((resolve, reject) => {
@@ -1122,7 +1232,12 @@ async function check_file_cache({ api_base_url, local_file_cache_path, event_fil
// IPC to Main: Open local file cache if available. Copy to temp directory with given filename first.
// Updated 2022-03-09
async function open_local_file({ local_file_cache_path, hash, host_file_temp_path, filename }) {
async function open_local_file({
local_file_cache_path,
hash,
host_file_temp_path,
filename
}) {
console.log('*** Electron framework: open_local_file() ***');
// console.log('Open local file cache if available. Copy to temp directory with given filename first.');
console.log(
@@ -1153,7 +1268,9 @@ exports.check_file_cache_and_open_local_file = async function ({
host_file_temp_path,
filename
}) {
console.log('*** Electron framework: check_file_cache_and_open_local_file() ***');
console.log(
'*** Electron framework: check_file_cache_and_open_local_file() ***'
);
console.log(
'Checking the local file cache against the remote server and then opening the local file.'
);
@@ -1349,7 +1466,9 @@ exports.run_cmd = async function ({
// Resolve placeholders in the command string
let cleaned_cmd = cmd;
if (cmd && typeof cmd === 'string') {
cleaned_cmd = cmd.replace(/\[home\]/g, home_directory).replace(/\[tmp\]/g, tmp_directory);
cleaned_cmd = cmd
.replace(/\[home\]/g, home_directory)
.replace(/\[tmp\]/g, tmp_directory);
}
console.log(`Command String: ${cleaned_cmd}`);
@@ -1402,13 +1521,19 @@ exports.run_cmd = async function ({
// Run raw command sync
// Updated 2026-01-26
exports.run_cmd_sync = function ({ cmd = null, return_stdout = null, return_stdin = null }) {
exports.run_cmd_sync = function ({
cmd = null,
return_stdout = null,
return_stdin = null
}) {
console.log('*** Electron framework export: run_cmd_sync() ***');
// Resolve placeholders in the command string
let cleaned_cmd = cmd;
if (cmd && typeof cmd === 'string') {
cleaned_cmd = cmd.replace(/\[home\]/g, home_directory).replace(/\[tmp\]/g, tmp_directory);
cleaned_cmd = cmd
.replace(/\[home\]/g, home_directory)
.replace(/\[tmp\]/g, tmp_directory);
}
console.log(`Command String: ${cleaned_cmd}`);
@@ -1451,7 +1576,7 @@ exports.get_device_info = async function () {
data['release'] = os.release();
data['uptime'] = os.uptime();
data['version'] = os.version();
// Add directory info for placeholder resolution in UI
data['home_directory'] = home_directory;
data['tmp_directory'] = tmp_directory;
@@ -1465,19 +1590,23 @@ exports.get_device_info = async function () {
* Moves a file from the hashed cache to the operational temp directory
* and triggers the system launcher.
*/
exports.launch_from_cache = async function ({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length = 2
exports.launch_from_cache = async function ({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length = 2
}) {
console.log('*** Aether App Native export: launch_from_cache() ***');
// 1. Resolve Path Placeholders (using global regex)
const clean_cache_root = cache_root.replace(/\[home\]/g, home_directory).replace(/\[tmp\]/g, tmp_directory);
const clean_temp_root = temp_root.replace(/\[home\]/g, home_directory).replace(/\[tmp\]/g, tmp_directory);
const clean_cache_root = cache_root
.replace(/\[home\]/g, home_directory)
.replace(/\[tmp\]/g, tmp_directory);
const clean_temp_root = temp_root
.replace(/\[home\]/g, home_directory)
.replace(/\[tmp\]/g, tmp_directory);
const hash_filename = `${hash}.file`;
const prefix = hash.substring(0, hash_prefix_length);
const source_path = path.join(clean_cache_root, prefix, hash_filename);
@@ -1506,15 +1635,14 @@ exports.launch_from_cache = async function ({
const is_pres = ['pptx', 'ppt', 'key', 'pdf', 'odp'].includes(ext);
if (is_pres) {
return await exports.launch_presentation({
path: dest_path,
app: ext === 'key' ? 'keynote' : 'default'
return await exports.launch_presentation({
path: dest_path,
app: ext === 'key' ? 'keynote' : 'default'
});
}
// 6. Default Fallback
return await ipcRenderer.invoke('open_local_file', '', dest_path);
} catch (err) {
console.error('Launch Error:', err);
return { success: false, error: err.message };
@@ -1523,23 +1651,25 @@ exports.launch_from_cache = async function ({
/**
* Specialized Presentation Launcher (Phase 5)
* Handles platform-specific application selection (LibreOffice on Linux,
* Handles platform-specific application selection (LibreOffice on Linux,
* PowerPoint/Keynote on macOS).
* Updated 2026-01-26
*/
exports.launch_presentation = async function ({
path: raw_path,
exports.launch_presentation = async function ({
path: raw_path,
app = 'default',
os_platform = 'auto'
}) {
console.log('*** Aether App Native export: launch_presentation() ***');
// Resolve placeholders if they exist in the incoming path (using global regex)
let cleaned_path = raw_path
.replace(/\[home\]/g, home_directory)
.replace(/\[tmp\]/g, tmp_directory);
console.log(`Raw Path: ${raw_path}; Cleaned Path: ${cleaned_path}; App: ${app}; OS: ${os_platform}`);
console.log(
`Raw Path: ${raw_path}; Cleaned Path: ${cleaned_path}; App: ${app}; OS: ${os_platform}`
);
// 1. Detect OS
let platform = os_platform;
@@ -1549,9 +1679,11 @@ exports.launch_presentation = async function ({
// 2. Handle Linux (LibreOffice Testing)
if (platform === 'linux') {
console.log(`Native: Launching LibreOffice on Linux for path: ${cleaned_path}`);
console.log(
`Native: Launching LibreOffice on Linux for path: ${cleaned_path}`
);
const cmd = `libreoffice --impress "${cleaned_path}"`;
return new Promise((resolve) => {
child_process.exec(cmd, (err, stdout, stderr) => {
if (err) {
@@ -1570,7 +1702,7 @@ exports.launch_presentation = async function ({
const script = `tell application "Keynote" to open POSIX file "${cleaned_path}"`;
return exports.run_osascript({ cmd: script });
}
// Default to shell open
return ipcRenderer.invoke('open_local_file', '', cleaned_path);
}
@@ -1622,7 +1754,9 @@ exports.check_and_get_updated_native_app_config = function ({
file_path = 'device_configs/ae_native_app_config.default.json',
overwrite = false
}) {
console.log('*** Aether App Native export: check_and_get_updated_native_app_config() ***');
console.log(
'*** Aether App Native export: check_and_get_updated_native_app_config() ***'
);
if (os.platform == 'darwin') {
let default_osit_sync_app_config_path = path.join(
@@ -1630,17 +1764,33 @@ exports.check_and_get_updated_native_app_config = function ({
file_path
);
let default_app_config_path = path.join(app_root_path, 'ae_native_app_config.default.json');
console.log('macOS app root and config directory: ' + default_app_config_path);
let default_app_config_path = path.join(
app_root_path,
'ae_native_app_config.default.json'
);
console.log(
'macOS app root and config directory: ' + default_app_config_path
);
fs.copyFileSync(default_osit_sync_app_config_path, default_app_config_path);
fs.copyFileSync(
default_osit_sync_app_config_path,
default_app_config_path
);
if (overwrite) {
console.log('Overwriting the current active config file...');
let active_app_config_path = path.join(app_root_path, 'ae_native_app_config.json');
console.log('macOS app root and config directory: ' + active_app_config_path);
let active_app_config_path = path.join(
app_root_path,
'ae_native_app_config.json'
);
console.log(
'macOS app root and config directory: ' + active_app_config_path
);
fs.copyFileSync(default_osit_sync_app_config_path, active_app_config_path);
fs.copyFileSync(
default_osit_sync_app_config_path,
active_app_config_path
);
}
} else if (os.platform == 'linux') {
app_root_path = path.join(home_directory, '.config/OSIT');

View File

@@ -7,51 +7,107 @@ import { ae_loc, ae_api, ae_sess, slct } from '$lib/stores/ae_stores';
* Standardizes all IPC calls to snake_case.
*/
const native = (typeof window !== 'undefined') ? (window as any).aetherNative : null;
const native =
typeof window !== 'undefined' ? (window as any).aetherNative : null;
export const is_native = !!native;
// 1. Core Config
export async function get_device_config() {
if (!native) return null;
return await native.get_device_config();
if (!native) return null;
return await native.get_device_config();
}
export async function get_device_info() {
if (!native) return null;
return await native.get_device_info();
if (!native) return null;
return await native.get_device_info();
}
// 2. File & Cache Management
export async function check_hash_file_cache({ cache_root, hash, hash_prefix_length = 2, verify_hash = false }: any) {
if (!native) return false;
return await native.check_cache({ cache_root, hash, hash_prefix_length, verify_hash });
export async function check_hash_file_cache({
cache_root,
hash,
hash_prefix_length = 2,
verify_hash = false
}: any) {
if (!native) return false;
return await native.check_cache({
cache_root,
hash,
hash_prefix_length,
verify_hash
});
}
export async function download_to_cache({ url, cache_root, hash, api_key, account_id, hash_prefix_length = 2 }: any) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.download_to_cache({ url, cache_root, hash, api_key, account_id, hash_prefix_length });
export async function download_to_cache({
url,
cache_root,
hash,
api_key,
account_id,
hash_prefix_length = 2
}: any) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.download_to_cache({
url,
cache_root,
hash,
api_key,
account_id,
hash_prefix_length
});
}
export async function launch_from_cache({ cache_root, hash, temp_root, filename, hash_prefix_length = 2 }: any) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.launch_from_cache({ cache_root, hash, temp_root, filename, hash_prefix_length });
export async function launch_from_cache({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length = 2
}: any) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.launch_from_cache({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length
});
}
// 3. OS Shell Commands (Phase 3)
export async function open_folder(path: string) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.open_folder(path);
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.open_folder(path);
}
export async function run_cmd({ cmd, timeout = 30000, return_stdout = true }: { cmd: string, timeout?: number, return_stdout?: boolean }) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.run_cmd({ cmd, timeout, return_stdout });
export async function run_cmd({
cmd,
timeout = 30000,
return_stdout = true
}: {
cmd: string;
timeout?: number;
return_stdout?: boolean;
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.run_cmd({ cmd, timeout, return_stdout });
}
export async function run_cmd_sync({ cmd, return_stdout = true }: { cmd: string, return_stdout?: boolean }) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.run_cmd_sync({ cmd, return_stdout });
export async function run_cmd_sync({
cmd,
return_stdout = true
}: {
cmd: string;
return_stdout?: boolean;
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.run_cmd_sync({ cmd, return_stdout });
}
/**
@@ -60,30 +116,44 @@ export async function run_cmd_sync({ cmd, return_stdout = true }: { cmd: string,
* Called at launcher startup to prevent cache directory bloat from interrupted downloads.
* Default: 1440 minutes = 24 hours.
*/
export async function cleanup_tmp_files({ cache_root, max_age_minutes = 1440 }: { cache_root: string, max_age_minutes?: number }) {
if (!native) return { success: false, error: 'Native bridge not available' };
export async function cleanup_tmp_files({
cache_root,
max_age_minutes = 1440
}: {
cache_root: string;
max_age_minutes?: number;
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
const cmd = `find "${cache_root}" -name "*.tmp" -mmin +${max_age_minutes} -type f -delete`;
return await native.run_cmd({ cmd, timeout: 30000, return_stdout: false });
}
export async function run_osascript(script: string) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.run_osascript(script);
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.run_osascript(script);
}
export async function kill_processes({ process_name_li = [] }: { process_name_li: string[] }) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.kill_processes({ process_name_li });
export async function kill_processes({
process_name_li = []
}: {
process_name_li: string[];
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.kill_processes({ process_name_li });
}
export async function open_local_file_v2(path: string) {
if (!native) return { success: false, error: 'Native bridge not available' };
return await native.open_local_file_v2(path);
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.open_local_file_v2(path);
}
/**
* Specialized Presentation Launcher (Phase 5)
* Handles platform-specific application selection (LibreOffice on Linux,
* Handles platform-specific application selection (LibreOffice on Linux,
* PowerPoint/Keynote on macOS).
*/
export async function launch_presentation({
@@ -92,36 +162,49 @@ export async function launch_presentation({
os = 'auto',
log_lvl = 0
}: {
path: string,
app?: 'default' | 'powerpoint' | 'keynote' | 'libreoffice',
os?: 'auto' | 'linux' | 'darwin' | 'win32',
log_lvl?: number
path: string;
app?: 'default' | 'powerpoint' | 'keynote' | 'libreoffice';
os?: 'auto' | 'linux' | 'darwin' | 'win32';
log_lvl?: number;
}) {
if (!native) return { success: false, error: 'Native bridge not available' };
if (!native)
return { success: false, error: 'Native bridge not available' };
// 1. Detect OS if set to auto
let platform = os;
if (platform === 'auto') {
const info = await get_device_info();
platform = info?.platform || 'linux';
platform = info?.platform || 'linux';
}
// 2. Prefer the Native Bridge implementation (Atomic Copy-and-Launch)
// This delegates to the hardened logic in electron_native.js
if (native.launch_presentation) {
if (log_lvl) console.log('Relay: Using native.launch_presentation');
return await native.launch_presentation({ path, app, os_platform: platform });
return await native.launch_presentation({
path,
app,
os_platform: platform
});
}
// 3. Relay-side Fallback (Mock/Legacy)
// Manually resolve placeholders using all available context
const info = await get_device_info();
const loc = get(ae_loc);
// Attempt to find home/tmp from bridge info OR local hydrated store
const home = info?.home_directory || loc.home_directory || loc.native_device?.home_directory || '';
const tmp = info?.tmp_directory || loc.tmp_directory || loc.native_device?.tmp_directory || '';
const home =
info?.home_directory ||
loc.home_directory ||
loc.native_device?.home_directory ||
'';
const tmp =
info?.tmp_directory ||
loc.tmp_directory ||
loc.native_device?.tmp_directory ||
'';
if (log_lvl) console.log('Relay Debug:', { home, tmp, raw_path: path });
// CRITICAL: Resolve all instances of placeholders using global regex
@@ -129,22 +212,33 @@ export async function launch_presentation({
if (home) cleaned_path = cleaned_path.replace(/\[home\]/g, home);
if (tmp) cleaned_path = cleaned_path.replace(/\[tmp\]/g, tmp);
if (log_lvl) console.log(`Relay Fallback: Resolving ${path} -> ${cleaned_path}`);
if (log_lvl)
console.log(`Relay Fallback: Resolving ${path} -> ${cleaned_path}`);
// If path still contains [home] or [tmp], it means we failed to resolve it.
if (cleaned_path.includes('[home]') || cleaned_path.includes('[tmp]')) {
console.error('Relay Error: Could not resolve path placeholders. Home or Tmp directory unknown.', { home, tmp });
console.error(
'Relay Error: Could not resolve path placeholders. Home or Tmp directory unknown.',
{ home, tmp }
);
return { success: false, error: 'Could not resolve path placeholders' };
}
if (platform === 'linux') {
if (log_lvl) console.log(`Relay: Launching LibreOffice on Linux for path: ${cleaned_path}`);
return await run_cmd({ cmd: `libreoffice --impress "${cleaned_path}"` });
if (log_lvl)
console.log(
`Relay: Launching LibreOffice on Linux for path: ${cleaned_path}`
);
return await run_cmd({
cmd: `libreoffice --impress "${cleaned_path}"`
});
}
if (platform === 'darwin') {
if (app === 'keynote') {
return await run_osascript(`tell application "Keynote" to open POSIX file "${cleaned_path}"`);
return await run_osascript(
`tell application "Keynote" to open POSIX file "${cleaned_path}"`
);
}
return await open_local_file_v2(cleaned_path);
}
@@ -160,11 +254,12 @@ export async function control_presentation({
app,
action
}: {
app: 'powerpoint' | 'keynote',
action: 'next' | 'prev' | 'start' | 'stop'
app: 'powerpoint' | 'keynote';
action: 'next' | 'prev' | 'start' | 'stop';
}) {
if (!native) return { success: false, error: 'Native bridge not available' };
if (!native)
return { success: false, error: 'Native bridge not available' };
// Check if the native bridge has the direct implementation
if (native.control_presentation) {
return await native.control_presentation({ app, action });
@@ -174,17 +269,37 @@ export async function control_presentation({
let script = '';
if (app === 'powerpoint') {
switch (action) {
case 'next': script = 'tell application "Microsoft PowerPoint" to next slide of slide show view of active presentation'; break;
case 'prev': script = 'tell application "Microsoft PowerPoint" to previous slide of slide show view of active presentation'; break;
case 'start': script = 'tell application "Microsoft PowerPoint" to run slide show of active presentation'; break;
case 'stop': script = 'tell application "Microsoft PowerPoint" to stop slide show of active presentation'; break;
case 'next':
script =
'tell application "Microsoft PowerPoint" to next slide of slide show view of active presentation';
break;
case 'prev':
script =
'tell application "Microsoft PowerPoint" to previous slide of slide show view of active presentation';
break;
case 'start':
script =
'tell application "Microsoft PowerPoint" to run slide show of active presentation';
break;
case 'stop':
script =
'tell application "Microsoft PowerPoint" to stop slide show of active presentation';
break;
}
} else if (app === 'keynote') {
switch (action) {
case 'next': script = 'tell application "Keynote" to show next'; break;
case 'prev': script = 'tell application "Keynote" to show previous'; break;
case 'start': script = 'tell application "Keynote" to start (front document)'; break;
case 'stop': script = 'tell application "Keynote" to stop'; break;
case 'next':
script = 'tell application "Keynote" to show next';
break;
case 'prev':
script = 'tell application "Keynote" to show previous';
break;
case 'start':
script = 'tell application "Keynote" to start (front document)';
break;
case 'stop':
script = 'tell application "Keynote" to stop';
break;
}
}
@@ -192,43 +307,106 @@ export async function control_presentation({
return await run_osascript(script);
}
return { success: false, error: `Unsupported app or action: ${app}/${action}` };
return {
success: false,
error: `Unsupported app or action: ${app}/${action}`
};
}
// 4. System Management (Phase 5+)
export async function set_wallpaper({ path }: { path: string }) {
if (!native || !native.set_wallpaper) return { success: false, error: 'Native handler set_wallpaper not available' };
if (!native || !native.set_wallpaper)
return {
success: false,
error: 'Native handler set_wallpaper not available'
};
return await native.set_wallpaper({ path });
}
export async function update_app(args: { source: 'url' | 'file', url?: string, path?: string }) {
if (!native || !native.update_app) return { success: false, error: 'Native handler update_app not available' };
export async function update_app(args: {
source: 'url' | 'file';
url?: string;
path?: string;
}) {
if (!native || !native.update_app)
return {
success: false,
error: 'Native handler update_app not available'
};
return await native.update_app(args);
}
export async function window_control({ action, value }: { action: string, value?: any }) {
if (!native || !native.window_control) return { success: false, error: 'Native handler window_control not available' };
export async function window_control({
action,
value
}: {
action: string;
value?: any;
}) {
if (!native || !native.window_control)
return {
success: false,
error: 'Native handler window_control not available'
};
return await native.window_control({ action, value });
}
export async function manage_recording({ action, options }: { action: 'start' | 'stop' | 'status', options?: any }) {
if (!native || !native.manage_recording) return { success: false, error: 'Native handler manage_recording not available' };
export async function manage_recording({
action,
options
}: {
action: 'start' | 'stop' | 'status';
options?: any;
}) {
if (!native || !native.manage_recording)
return {
success: false,
error: 'Native handler manage_recording not available'
};
return await native.manage_recording({ action, options });
}
export async function set_display_layout({ mode, configStr }: { mode: 'mirror' | 'extend', configStr?: string }) {
if (!native || !native.set_display_layout) return { success: false, error: 'Native handler set_display_layout not available' };
export async function set_display_layout({
mode,
configStr
}: {
mode: 'mirror' | 'extend';
configStr?: string;
}) {
if (!native || !native.set_display_layout)
return {
success: false,
error: 'Native handler set_display_layout not available'
};
return await native.set_display_layout({ mode, configStr });
}
export async function power_control({ action }: { action: 'shutdown' | 'reboot' | 'sleep' }) {
if (!native || !native.power_control) return { success: false, error: 'Native handler power_control not available' };
export async function power_control({
action
}: {
action: 'shutdown' | 'reboot' | 'sleep';
}) {
if (!native || !native.power_control)
return {
success: false,
error: 'Native handler power_control not available'
};
return await native.power_control({ action });
}
export async function open_external({ url, app }: { url: string, app?: 'chrome' | 'firefox' | 'default' }) {
if (!native || !native.open_external) return { success: false, error: 'Native handler open_external not available' };
export async function open_external({
url,
app
}: {
url: string;
app?: 'chrome' | 'firefox' | 'default';
}) {
if (!native || !native.open_external)
return {
success: false,
error: 'Native handler open_external not available'
};
return await native.open_external({ url, app });
}