codes
// 获取当前窗口或请求关联的服务实例。
const service = () => {
// Keep the synchronization engine out of the startup path. Reading the
// lightweight timer setting below does not initialize a repository client.
servicePromise ||= Promise.resolve().then(() => {
const { SyncManager } = require('./sync/manager') as typeof import('./sync/manager');
return new SyncManager({ userDataPath, safeStorage, fetchImpl });
});
return servicePromise;
};
// 笔记库全局状态:保存当前修改,供后续读取与恢复。
async save() {
// Multiple windows share this store. Serialize snapshots so an older write
// cannot finish after a newer mutation and roll recent-workspace metadata
// back on disk.
this.savePromise = this.savePromise
.catch(() => {
})
.then(async () => {
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
await atomicWriteJson(this.filePath, this.state);
});
return this.savePromise;
}
// 保存仅本地使用的配置;整个 localSettings 对象均不进入云同步快照。
async updateLocalSettings(input: Partial<LocalSettings>) {
return this.queueSettingsMutation(async () => {
const state = this.load();
if ('hotkeysEnabled' in input && typeof input.hotkeysEnabled !== 'boolean') {
throw new Error('启用热键必须是布尔值');
}
const transaction = this.prepareLocalSettings?.(input);
const previous = state.localSettings;
state.localSettings = { ...previous, ...input };
try {
await this.save();
transaction?.commit();
} catch (error) {
state.localSettings = previous;
transaction?.rollback();
throw error;
}
for (const listener of this.localSettingsListeners) {
try {
listener({ ...state.localSettings });
} catch {
// A closing renderer must not turn a committed save into a failure.
}
}
return state.localSettings;
});
}
// 串行合并设置修改,避免异步保存覆盖较新的设置。
queueSettingsMutation<T>(mutate: () => Promise<T>): Promise<T> {
const task = this.settingsPromise.catch(() => {
}).then(mutate);
this.settingsPromise = task;
return task;
}
document.documentElement.style.setProperty('--editor-font-size', `${fontSize}px`);
let hotkeysEnabled = !(await stateStore.publicState()).localSettings.hotkeysEnabled;
await stateStore.updateLocalSettings({ hotkeysEnabled });
// 返回已完成初始化的同步状态。
get state() {
return this.store.state;
}
// 异步版本
async getState() {
return this.ready;
}
const BrowserWindow = {
// 将测试发送对象映射到对应窗口,供权限与作用范围验证。
fromWebContents() {
return parentWindow;
}
};
registerIpcHandlers<BrowserWindow>({
// 在目标锁保护下建立新状态,完整成功后才释放旧仓库。
private async openLocked(rootPath: string) {
// 同步完成校验、非阻塞加锁与进程内登记,使两个并发打开请求不能穿过登记间隙。
export function acquireWorkspaceLock(rootPath: string, owner: object | null = null) {
// 串行化打开和释放,失败不阻断后续生命周期请求。
private queueLifecycle<T>(operation: () => Promise<T>): Promise<T> {
import type {
BrowserWindow as ElectronWindow,
// 清理失效仓库时保留锁直到索引关闭。
private async forgetLocked(rootPath: string) {
const windowWorkspaces = new Map<BrowserWindow, WorkspaceManager>();
interface WindowSessionOptions {
createWindow: (options: Pick<Parameters<typeof createWindow<BrowserWindow>>[0],
interface WindowSessionOptions {
createWindow?: (options: Pick<Parameters<typeof createWindow<BrowserWindow>>[0],
'restoreWorkspace' | 'startupActivation' | 'onCreated'>) => BrowserWindow;
clone.addEventListener('click', async () => {
clone.disabled = true;
clone.textContent = '正在克隆…';
runtimeState.syncError.hidden = true;
try {
await persistSession();
const result = await window.markdownAPI.cloneSyncLibrary(library.id);
if (!result) {
return;
}
if ('workspaceOccupied' in result) {
handleOccupiedWorkspace(result);
return;
}
let workspace = await manager.open(rootPath);
if ('workspaceOccupied' in workspace) {
return workspace;
}
keybindings: {
snapshot: () => object;
apply: (key: string, value: unknown) => unknown;
};
// Write keybindings.
writeKeybindings: (text, expected) => keybindingRequest('keybindings:write', text, expected),
// 快捷键文件存储:将本次内容写入目标资源。
const write = (event: KeybindingEvent, text: unknown, expected: unknown) => {
// Compare the actual file to the last read snapshot to protect external edits.
if (this.read().text !== expected) {
throw new Error('快捷键文件已变化,请重新打开设置后重试');
}
// 合并默认和用户规则,应用移除项与平台覆盖,并按需保留禁用项。
bindings(platform = this.platform, includeDisabled = false) {
// Disk config loads before plugins. Retain unresolved selectors so a
// later plugin registration can honor its default removals immediately.
return [
{
removeRule: {
...entry,
command: entry.command,
123