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.
220 lines
10 KiB
JavaScript
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
|