This commit is contained in:
2026-09-12 13:59:12 +08:00
commit 941ce4baab
71 changed files with 13037 additions and 0 deletions

87
preload.js Normal file
View File

@@ -0,0 +1,87 @@
/**
* preload.js — contextBridge 暴露给渲染进程的安全 IPC API
*/
'use strict';
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
// 数据查询(列表不含图片数据,图片走 getImage 按需拉)。
// limit 留空 → 主进程用配置里的 displayLimit默认 300改设置后下次拉就生效。
list: (query, limit) => ipcRenderer.invoke('clips:list', { query: query || '', limit: limit || 0 }),
getImage: (id) => ipcRenderer.invoke('clips:get-image', { id }),
count: () => ipcRenderer.invoke('clips:count'),
// 操作
movetop: (id) => ipcRenderer.invoke('clips:movetop', { id }),
remove: (id) => ipcRenderer.invoke('clips:delete', { id }),
hide: () => ipcRenderer.invoke('window:hide'),
openSettings: () => ipcRenderer.invoke('settings:open'),
// 窗口置顶
toggleAlwaysOnTop: (on) => ipcRenderer.invoke('window:toggle-always-on-top', { on }),
getAlwaysOnTop: () => ipcRenderer.invoke('window:get-always-on-top'),
// 应用图标(标题栏左上角小图标):返回 PNG data URL喂给 <img src>。
// 主进程会缓存 .ico→PNG 的转换结果,每个窗口首次调用一次即可。
getAppIcon: () => ipcRenderer.invoke('app:get-icon'),
// 订阅
subscribe: () => ipcRenderer.invoke('clips:subscribe'),
onAdded: (cb) => {
const handler = (_e, clip) => cb(clip);
ipcRenderer.on('clips:added', handler);
return () => ipcRenderer.removeListener('clips:added', handler);
},
onRefresh: (cb) => {
const handler = () => cb();
ipcRenderer.on('clips:refresh', handler);
return () => ipcRenderer.removeListener('clips:refresh', handler);
},
});
contextBridge.exposeInMainWorld('favoritesApi', {
add: (clipId) => ipcRenderer.invoke('favorites:add', { clipId }),
remove: (favId) => ipcRenderer.invoke('favorites:remove', { favId }),
// 收藏 Tab 的复制写收藏自身存的内容favId 与历史 id 不同库,不能走 movetop
copy: (favId) => ipcRenderer.invoke('favorites:copy', { favId }),
list: (query, limit) => ipcRenderer.invoke('favorites:list', { query: query || '', limit: limit || 0 }),
getImage: (favId) => ipcRenderer.invoke('favorites:get-image', { favId }),
count: () => ipcRenderer.invoke('favorites:count'),
subscribe: () => ipcRenderer.invoke('favorites:subscribe'),
onChanged: (cb) => {
const handler = () => cb();
ipcRenderer.on('favorites:changed', handler);
return () => ipcRenderer.removeListener('favorites:changed', handler);
},
});
contextBridge.exposeInMainWorld('settingsApi', {
get: () => ipcRenderer.invoke('settings:get'),
set: (key, value) => ipcRenderer.invoke('settings:set', { key, value }),
clearHistory: () => ipcRenderer.invoke('settings:clear-history'),
openDir: () => ipcRenderer.invoke('settings:open-dir'),
chooseDir: () => ipcRenderer.invoke('settings:choose-dir'),
// 外链交给系统浏览器关于页。scheme 白名单在主进程卡,渲染进程只管递 URL
openExternal: (url) => ipcRenderer.invoke('settings:open-external', { url }),
// 设置窗口订阅 displayLimit 变更,主进程设置被改后给所有窗口推一条刷新通知
onDisplayLimitChanged: (cb) => {
const handler = (_e, payload) => cb(payload);
ipcRenderer.on('settings:display-limit-changed', handler);
return () => ipcRenderer.removeListener('settings:display-limit-changed', handler);
},
});
contextBridge.exposeInMainWorld('themeApi', {
get: () => ipcRenderer.invoke('theme:get'),
set: (theme) => ipcRenderer.invoke('theme:set', { theme }),
setAccent: (accent) => ipcRenderer.invoke('theme:set-accent', { accent }),
setFollowSystem: (followSystem)=> ipcRenderer.invoke('theme:set-follow-system', { followSystem }),
// 必须先订阅,主进程才会把 theme:system-changed 发过来
subscribe: () => ipcRenderer.invoke('theme:subscribe'),
onSystemChanged: (cb) => {
const handler = (_e, payload) => cb(payload);
ipcRenderer.on('theme:system-changed', handler);
return () => ipcRenderer.removeListener('theme:system-changed', handler);
},
});