minor refactor, cleanup

This commit is contained in:
Nystik
2026-03-11 22:08:30 +01:00
parent 2b9ebf1fbd
commit 9789be6d70
38 changed files with 259 additions and 379 deletions

View File

@@ -1,6 +1,3 @@
// Electron module shim
// Returned when Obsidian calls: window.require('electron')
import { ipcRenderer } from "./ipc-renderer.js";
import { webFrame } from "./web-frame.js";
import { remoteShim } from "./remote/index.js";
@@ -10,14 +7,12 @@ export const electronShim = {
webFrame,
remote: remoteShim,
// electron.webUtils - used for drag/drop file path extraction (desktop only)
webUtils: {
getPathForFile(file) {
return "";
},
},
// electron.deprecate - used by Obsidian to mark deprecated APIs
deprecate: {
function(fn, name) {
return fn;

View File

@@ -1,28 +1,7 @@
// Shim for electron.ipcRenderer
// Obsidian uses: .send(), .sendSync(), .on(), .once()
//
// sendSync channels discovered in app.js:
// vault → {id, path} - critical for startup
// version → string - app version
// is-dev → boolean - dev mode flag
// file-url → string - base URL prefix for vault assets
// disable-update → boolean - whether updates are disabled
// update → string - update status
// disable-gpu → boolean - GPU acceleration toggle
// frame → void - window frame style
// set-icon → void - custom vault icon
// get-icon → null|object - get custom vault icon
// relaunch → void - restart app
// starter → void - open vault chooser
// help → void - open help
// sandbox → void - open sandbox vault
// copy-asar → boolean - install update
import { showVaultManager } from "../ui/vault-manager.js";
const listeners = new Map();
// Sync channel handlers - must return values synchronously
const syncHandlers = {
vault: () => window.__vaultConfig || { id: "default-vault", path: "/" },
version: () => "1.8.9",
@@ -51,7 +30,6 @@ const syncHandlers = {
"copy-asar": () => false,
"check-update": () => null,
"vault-list": () => {
// Starter expects an object keyed by ID: {id: {path, ts, name}}
const result = {};
for (const v of window.__vaultList || []) {
result[v.id] = {
@@ -66,14 +44,12 @@ const syncHandlers = {
const id = (vaultPath || "").replace(/^\/+/, "");
const vault = (window.__vaultList || []).find((v) => v.id === id);
if (!vault && id) {
// New vault created by starter - create it on the server
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/vault/create", false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name: id }));
if (xhr.status >= 400) return "Failed to create vault";
}
// Navigate - use parent if in iframe, otherwise current window
const target = window.parent !== window ? window.parent : window;
target.location.href = "/?vault=" + encodeURIComponent(id);
return true;
@@ -90,7 +66,6 @@ const syncHandlers = {
return xhr.status < 400;
},
"vault-move": (oldPath, newPath) => {
// Not supported in web context
return "Moving vaults is not supported in the web version";
},
"vault-message": () => null,
@@ -105,10 +80,6 @@ export const ipcRenderer = {
send(channel, ...args) {
console.log("[shim:ipcRenderer] send:", channel, args);
// context-menu: Obsidian sends this and waits (up to 1s) for a response.
// In Electron, the main process returns spell-check info + edit flags.
// We reply immediately with a response object so Obsidian proceeds to
// build and show its HTML context menu without delay.
if (channel === "context-menu") {
queueMicrotask(() =>
ipcRenderer._emit("context-menu", {
@@ -163,7 +134,6 @@ export const ipcRenderer = {
return ipcRenderer;
},
// Internal: emit an event to registered listeners (used by ws bridge)
_emit(channel, ...args) {
const arr = listeners.get(channel);
if (arr) {

View File

@@ -1,36 +1,32 @@
// Shim for remote.app
// Obsidian uses: getPath, getVersion, getName, quit, isPackaged, getLocale
export const appShim = {
getPath(name) {
// Return web-friendly paths; config lives server-side in the vault's .obsidian/ dir
const paths = {
userData: '/.obsidian',
home: '/',
documents: '/documents',
desktop: '/desktop',
temp: '/tmp',
appData: '/.obsidian',
userData: "/.obsidian",
home: "/",
documents: "/documents",
desktop: "/desktop",
temp: "/tmp",
appData: "/.obsidian",
};
return paths[name] || '/';
return paths[name] || "/";
},
getVersion() {
return '1.8.9';
return "1.8.9";
},
getName() {
return 'Obsidian';
return "Obsidian";
},
getLocale() {
return navigator.language || 'en-US';
return navigator.language || "en-US";
},
isPackaged: true,
quit() {
console.log('[shim:app] quit (stub)');
console.log("[shim:app] quit (stub)");
},
relaunch() {

View File

@@ -1,35 +1,29 @@
// Shim for remote.clipboard
// Obsidian uses: readText, writeText, readImage, writeImage, readHTML, writeHTML
export const clipboardShim = {
readText() {
// navigator.clipboard.readText() is async; return empty for sync calls
// TODO: maintain a local mirror updated via async reads
return '';
return "";
},
writeText(text) {
navigator.clipboard.writeText(text).catch((e) => {
console.warn('[shim:clipboard] writeText failed:', e);
console.warn("[shim:clipboard] writeText failed:", e);
});
},
readHTML() {
return '';
return "";
},
writeHTML(html) {
// TODO: use clipboard API with text/html mime type
console.log('[shim:clipboard] writeHTML (stub)');
console.log("[shim:clipboard] writeHTML (stub)");
},
readImage() {
// TODO: implement if needed
return { isEmpty: () => true, toPNG: () => new Uint8Array(0) };
},
writeImage(image) {
console.log('[shim:clipboard] writeImage (stub)');
console.log("[shim:clipboard] writeImage (stub)");
},
has(format) {
@@ -37,10 +31,10 @@ export const clipboardShim = {
},
read(format) {
return '';
return "";
},
clear() {
navigator.clipboard.writeText('').catch(() => {});
navigator.clipboard.writeText("").catch(() => {});
},
};

View File

@@ -1,38 +1,40 @@
// Shim for remote.dialog
// Obsidian uses: showOpenDialog, showSaveDialog, showMessageBox, showErrorBox
export const dialogShim = {
async showOpenDialog(browserWindow, options) {
// TODO: implement custom modal UI with server-side file listing
console.log('[shim:dialog] showOpenDialog (stub):', options);
// TODO: implement custom modal with server-side file listing
console.log("[shim:dialog] showOpenDialog (stub):", options);
return { canceled: true, filePaths: [] };
},
async showSaveDialog(browserWindow, options) {
// TODO: implement custom modal UI
console.log('[shim:dialog] showSaveDialog (stub):', options);
// TODO: implement custom modal
console.log("[shim:dialog] showSaveDialog (stub):", options);
return { canceled: true, filePath: undefined };
},
async showMessageBox(browserWindow, options) {
// TODO: implement custom modal matching Electron's return format
// For now, use browser confirm/alert as rough approximation
if (typeof browserWindow === 'object' && !options) {
if (typeof browserWindow === "object" && !options) {
options = browserWindow;
}
console.log('[shim:dialog] showMessageBox:', options);
console.log("[shim:dialog] showMessageBox:", options);
const message = options.message || '';
const detail = options.detail || '';
const buttons = options.buttons || ['OK'];
const message = options.message || "";
const detail = options.detail || "";
const buttons = options.buttons || ["OK"];
// Simple fallback: use confirm for 2-button, alert for 1-button
if (buttons.length <= 1) {
alert(message + (detail ? '\n\n' + detail : ''));
alert(message + (detail ? "\n\n" + detail : ""));
return { response: 0, checkboxChecked: false };
}
const result = confirm(message + (detail ? '\n\n' + detail : '') + '\n\n[OK] = "' + buttons[0] + '", [Cancel] = "' + buttons[1] + '"');
const result = confirm(
message +
(detail ? "\n\n" + detail : "") +
'\n\n[OK] = "' +
buttons[0] +
'", [Cancel] = "' +
buttons[1] +
'"',
);
return {
response: result ? 0 : 1,
checkboxChecked: false,
@@ -40,7 +42,7 @@ export const dialogShim = {
},
showErrorBox(title, content) {
console.error('[shim:dialog] Error:', title, content);
alert(title + '\n\n' + content);
console.error("[shim:dialog] Error:", title, content);
alert(title + "\n\n" + content);
},
};

View File

@@ -1,6 +1,3 @@
// @electron/remote shim
// Returned when Obsidian calls: window.require('@electron/remote')
import { clipboardShim } from "./clipboard.js";
import { shellShim } from "./shell.js";
import { dialogShim } from "./dialog.js";

View File

@@ -1,6 +1,3 @@
// Shim for remote.Menu and remote.MenuItem
// Obsidian uses: Menu.buildFromTemplate, Menu.popup, Menu.setApplicationMenu
export class menuShim {
constructor() {
this.items = [];
@@ -8,13 +5,12 @@ export class menuShim {
static buildFromTemplate(template) {
const menu = new menuShim();
menu.items = (template || []).map(item => new menuItemShim(item));
menu.items = (template || []).map((item) => new menuItemShim(item));
return menu;
}
static setApplicationMenu(menu) {
// No native menu bar in browser - no-op
console.log('[shim:Menu] setApplicationMenu (stub)');
console.log("[shim:Menu] setApplicationMenu (stub)");
}
static getApplicationMenu() {
@@ -22,8 +18,8 @@ export class menuShim {
}
popup(options) {
// TODO: implement custom HTML context menu rendered at mouse position
console.log('[shim:Menu] popup (stub)', options);
// TODO: render custom HTML context menu at mouse position
console.log("[shim:Menu] popup (stub)", options);
}
append(menuItem) {
@@ -41,19 +37,19 @@ export class menuShim {
export class menuItemShim {
constructor(options = {}) {
this.label = options.label || '';
this.type = options.type || 'normal';
this.label = options.label || "";
this.type = options.type || "normal";
this.click = options.click || null;
this.role = options.role || null;
this.accelerator = options.accelerator || '';
this.accelerator = options.accelerator || "";
this.enabled = options.enabled !== false;
this.visible = options.visible !== false;
this.checked = !!options.checked;
this.submenu = options.submenu
? menuShim.buildFromTemplate(
Array.isArray(options.submenu) ? options.submenu : []
Array.isArray(options.submenu) ? options.submenu : [],
)
: null;
this.id = options.id || '';
this.id = options.id || "";
}
}

View File

@@ -1,6 +1,3 @@
// Shim for remote.nativeImage
// Minimal stub - Obsidian's renderer-side usage is limited
export const nativeImageShim = {
createFromBuffer(buffer) {
return {
@@ -8,7 +5,7 @@ export const nativeImageShim = {
getSize: () => ({ width: 0, height: 0 }),
toPNG: () => buffer || new Uint8Array(0),
toJPEG: (quality) => buffer || new Uint8Array(0),
toDataURL: () => '',
toDataURL: () => "",
};
},

View File

@@ -1,21 +1,24 @@
// Shim for remote.Notification
// Maps to browser Notification API
export class notificationShim {
constructor(options = {}) {
this.title = options.title || '';
this.body = options.body || '';
this.title = options.title || "";
this.body = options.body || "";
this.silent = options.silent || false;
this._handlers = {};
}
show() {
if ('Notification' in window && Notification.permission === 'granted') {
if ("Notification" in window && Notification.permission === "granted") {
new Notification(this.title, { body: this.body, silent: this.silent });
} else if ('Notification' in window && Notification.permission !== 'denied') {
} else if (
"Notification" in window &&
Notification.permission !== "denied"
) {
Notification.requestPermission().then((perm) => {
if (perm === 'granted') {
new Notification(this.title, { body: this.body, silent: this.silent });
if (perm === "granted") {
new Notification(this.title, {
body: this.body,
silent: this.silent,
});
}
});
}
@@ -29,6 +32,6 @@ export class notificationShim {
}
static isSupported() {
return 'Notification' in window;
return "Notification" in window;
}
}

View File

@@ -1,14 +1,24 @@
// Shim for remote.screen
// Obsidian uses screen for display/monitor info
export const screenShim = {
getPrimaryDisplay() {
return {
workAreaSize: { width: window.screen.availWidth, height: window.screen.availHeight },
workAreaSize: {
width: window.screen.availWidth,
height: window.screen.availHeight,
},
size: { width: window.screen.width, height: window.screen.height },
scaleFactor: window.devicePixelRatio || 1,
bounds: { x: 0, y: 0, width: window.screen.width, height: window.screen.height },
workArea: { x: 0, y: 0, width: window.screen.availWidth, height: window.screen.availHeight },
bounds: {
x: 0,
y: 0,
width: window.screen.width,
height: window.screen.height,
},
workArea: {
x: 0,
y: 0,
width: window.screen.availWidth,
height: window.screen.availHeight,
},
};
},

View File

@@ -1,6 +1,3 @@
// Shim for remote.session
// Mostly no-op; Obsidian's use is minimal
export const sessionShim = {
defaultSession: {
clearCache() {
@@ -12,7 +9,9 @@ export const sessionShim = {
},
setSpellCheckerLanguages(langs) {},
getSpellCheckerLanguages() { return []; },
getSpellCheckerLanguages() {
return [];
},
on() {},
once() {},

View File

@@ -1,20 +1,15 @@
// Shim for remote.shell
// Obsidian uses: openExternal, openPath, showItemInFolder
export const shellShim = {
openExternal(url) {
window.open(url, '_blank');
window.open(url, "_blank");
return Promise.resolve();
},
openPath(filePath) {
// TODO: could trigger a server-side download or preview
console.log('[shim:shell] openPath (stub):', filePath);
return Promise.resolve('');
console.log("[shim:shell] openPath (stub):", filePath);
return Promise.resolve("");
},
showItemInFolder(filePath) {
// No OS file manager in browser context
console.log('[shim:shell] showItemInFolder (stub):', filePath);
console.log("[shim:shell] showItemInFolder (stub):", filePath);
},
};

View File

@@ -1,9 +1,6 @@
// Shim for remote.systemPreferences
// No-op with safe defaults
export const systemPreferencesShim = {
getAccentColor() {
return '0078d4'; // Default Windows accent blue
return "0078d4"; // Default Windows accent blue
},
isAeroGlassEnabled() {
@@ -11,7 +8,7 @@ export const systemPreferencesShim = {
},
getMediaAccessStatus(mediaType) {
return 'granted';
return "granted";
},
askForMediaAccess(mediaType) {

View File

@@ -1,14 +1,12 @@
// Shim for remote.nativeTheme
// Obsidian uses: shouldUseDarkColors, on('updated', cb)
const listeners = [];
const darkQuery = typeof window !== 'undefined'
? window.matchMedia('(prefers-color-scheme: dark)')
: null;
const darkQuery =
typeof window !== "undefined"
? window.matchMedia("(prefers-color-scheme: dark)")
: null;
if (darkQuery?.addEventListener) {
darkQuery.addEventListener('change', () => {
darkQuery.addEventListener("change", () => {
for (const fn of listeners) {
fn();
}
@@ -21,7 +19,7 @@ export const themeShim = {
},
get themeSource() {
return 'system';
return "system";
},
set themeSource(val) {
@@ -29,14 +27,14 @@ export const themeShim = {
},
on(event, callback) {
if (event === 'updated') {
if (event === "updated") {
listeners.push(callback);
}
return themeShim;
},
once(event, callback) {
if (event === 'updated') {
if (event === "updated") {
const wrapped = () => {
const idx = listeners.indexOf(wrapped);
if (idx >= 0) listeners.splice(idx, 1);

View File

@@ -1,8 +1,3 @@
// Shim for remote.getCurrentWindow() / remote.BrowserWindow
// Obsidian uses: isMaximized, isMinimized, isFullScreen, minimize, maximize,
// unmaximize, close, setTitle, setAlwaysOnTop, isAlwaysOnTop,
// getBounds, setBounds, show, focus, setFullScreen, etc.
const currentWindowState = {
title: "Obsidian",
isMaximized: false,
@@ -80,7 +75,6 @@ const currentWindow = {
},
setBounds(bounds) {
// Cannot resize browser window from JS
console.log("[shim:window] setBounds (stub):", bounds);
},
@@ -113,7 +107,6 @@ const currentWindow = {
},
on(event, handler) {
// Map some Electron window events to browser equivalents
if (event === "focus") window.addEventListener("focus", handler);
else if (event === "blur") window.addEventListener("blur", handler);
else if (event === "resize") window.addEventListener("resize", handler);

View File

@@ -1,6 +1,3 @@
// Shim for electron.webFrame
// Obsidian uses: getZoomLevel(), setZoomLevel()
let currentZoom = 0;
export const webFrame = {