Files
OSIT-AE-App-Native-Electron/dist/main/file_handlers.js
Scott Idem 002c27e73c fix(file_handlers): use keystroke Cmd+Return to start pptx slideshow
Replace unreliable AppleScript PowerPoint API (run slide show of settings)
with System Events keystroke approach, matching proven behavior from the
old MasterKey app. Opens the file, waits 3s for load, then sends Cmd+Return
to start the slideshow from slide 1.
2026-04-20 17:01:04 -04:00

220 lines
10 KiB
JavaScript

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerFileHandlers = registerFileHandlers;
const electron_1 = require("electron");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const crypto = __importStar(require("crypto"));
const child_process_1 = require("child_process");
const axios_1 = __importDefault(require("axios"));
const file_utils_1 = require("./file_utils");
let endpoints_in_progress = [];
function registerFileHandlers() {
// Flexible organization: [root]/[prefix_len-char-prefix]/[hash].file
function get_organized_hashed_path(root, hash, prefix_len = 2) {
const expanded_root = (0, file_utils_1.expandPath)(root);
const prefix = hash.substring(0, Math.max(1, Math.min(prefix_len, 8)));
const dir = path.join(expanded_root, prefix);
if (!fs.existsSync(dir))
fs.mkdirSync(dir, { recursive: true });
return path.join(dir, `${hash}.file`);
}
electron_1.ipcMain.handle('native:check-cache', async (event, { cache_root, hash, hash_prefix_length = 2, verify_hash = false }) => {
const full_path = get_organized_hashed_path(cache_root, hash, hash_prefix_length);
if (!fs.existsSync(full_path))
return false;
if (verify_hash) {
try {
const file_buffer = fs.readFileSync(full_path);
const actual_hash = crypto.createHash('sha256').update(file_buffer).digest('hex');
return actual_hash === hash;
}
catch (e) {
return false;
}
}
return true;
});
electron_1.ipcMain.handle('native:download-to-cache', async (event, { url, cache_root, hash, api_key, account_id, hash_prefix_length = 2 }) => {
const full_path = get_organized_hashed_path(cache_root, hash, hash_prefix_length);
const tmp_path = `${full_path}.tmp`;
if (endpoints_in_progress.includes(url))
return { success: true, status: 'in_progress' };
// 1. If final file exists, skip
if (fs.existsSync(full_path))
return { success: true, path: full_path, status: 'exists' };
// 2. Handle stale .tmp files (Legacy "Trust No One" pattern)
if (fs.existsSync(tmp_path)) {
const stats = fs.statSync(tmp_path);
const age_ms = Date.now() - stats.mtimeMs;
// If the tmp file is older than 5 minutes, assume previous download crashed and delete it
if (age_ms > 5 * 60 * 1000) {
console.log(`Native: Deleting stale temp file (${Math.round(age_ms / 1000)}s old)`);
fs.unlinkSync(tmp_path);
}
else {
return { success: true, status: 'in_progress', detail: 'fresh_tmp_exists' };
}
}
console.log(`Native: Hardened Download -> ${full_path}`);
try {
endpoints_in_progress.push(url);
const response = await (0, axios_1.default)({
method: 'get', url, responseType: 'stream',
headers: {
'x-aether-api-key': api_key,
'x-account-id': account_id || '',
'x-no-account-id': 'Nothing to See Here'
}
});
const writer = fs.createWriteStream(tmp_path);
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', () => resolve());
writer.on('error', reject);
});
// 3. Verify Integrity before renaming (The "Trust No One" Check)
const file_buffer = fs.readFileSync(tmp_path);
const actual_hash = crypto.createHash('sha256').update(file_buffer).digest('hex');
if (actual_hash !== hash) {
console.error(`Native: Hash Mismatch! Expected ${hash}, got ${actual_hash}`);
fs.unlinkSync(tmp_path);
return { success: false, error: 'Integrity check failed: Hash mismatch' };
}
fs.renameSync(tmp_path, full_path);
console.log(`Native: Cache Integrity Verified. File moved to final destination.`);
return { success: true, path: full_path };
}
catch (error) {
if (fs.existsSync(tmp_path))
fs.unlinkSync(tmp_path);
return { success: false, error: error.message };
}
finally {
endpoints_in_progress = endpoints_in_progress.filter(e => e !== url);
}
});
electron_1.ipcMain.handle('native:launch-from-cache', async (event, { cache_root, hash, temp_root, filename, hash_prefix_length = 2 }) => {
try {
const source = get_organized_hashed_path(cache_root, hash, hash_prefix_length);
const expanded_temp = (0, file_utils_1.expandPath)(temp_root);
const target = path.join(expanded_temp, filename);
console.log(`Native: Launching from Cache -> ${filename}`);
if (!fs.existsSync(expanded_temp))
fs.mkdirSync(expanded_temp, { recursive: true });
// 1. Copy the file to temp folder with original name
fs.copyFileSync(source, target);
// 2. Determine file type
const ext = path.extname(filename).toLowerCase().replace('.', '');
const is_pres = ['pptx', 'ppt', 'key', 'pdf', 'odp'].includes(ext);
// 3. Optimized Launch (LibreOffice / AppleScript)
if (is_pres) {
if (os.platform() === 'linux') {
console.log(`Native: Launching LibreOffice (--impress) for ${target}`);
return new Promise((resolve) => {
(0, child_process_1.exec)(`libreoffice --impress "${target}"`, (err) => {
if (err)
resolve({ success: false, error: err.message });
else
resolve({ success: true });
});
});
}
if (os.platform() === 'darwin') {
let script = '';
if (ext === 'key') {
script = `
tell application "Keynote"
activate
open (POSIX file "${target}")
delay 1
start (front document)
end tell
`;
}
else if (ext === 'pptx' || ext === 'ppt') {
script = `
tell application "Microsoft PowerPoint"
activate
open (POSIX file "${target}")
delay 3
end tell
tell application "System Events"
keystroke return using command down
end tell
`;
}
if (script) {
console.log(`Native: Launching ${ext} via AppleScript for ${target}`);
// Write to a temp .scpt file instead of passing via -e flag.
// The -e approach breaks on multi-line scripts and paths with spaces or quotes.
const tmp_script_path = path.join(os.tmpdir(), `ae_launch_${Date.now()}.scpt`);
return new Promise((resolve) => {
try {
fs.writeFileSync(tmp_script_path, script.trim());
}
catch (e) {
resolve({ success: false, error: `Failed to write AppleScript temp file: ${e.message}` });
return;
}
(0, child_process_1.exec)(`osascript "${tmp_script_path}"`, (err) => {
try {
fs.unlinkSync(tmp_script_path);
}
catch { }
if (err)
resolve({ success: false, error: err.message });
else
resolve({ success: true });
});
});
}
}
}
// 4. Default Fallback
await electron_1.shell.openPath(target);
return { success: true };
}
catch (error) {
return { success: false, error: error.message };
}
});
}
//# sourceMappingURL=file_handlers.js.map