Visitar URL original
feat: add liquid glass and translation refocus · PDFMathTranslate/PDFMathReader@d68b761 · GitHub
Skip to content

Commit d68b761

Browse files
committed
feat: add liquid glass and translation refocus
Add macOS Liquid Glass settings and controls, progressive title bar blur, glass menus and feedback surfaces. Fix sidebar tab interaction and inline tool icons. Use paragraph or page blur-and-refocus translation transitions while preserving character reveal code. Recover translation jobs and cover queue and animation edge cases.
1 parent e7c855d commit d68b761

41 files changed

Lines changed: 2028 additions & 62 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎bun.lock‎

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎electron/main.mjs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { registerGlassMenuIPC } from './main/services/glass-menu.mjs';
12
import { app, dialog, ipcMain, nativeTheme, safeStorage, systemPreferences } from 'electron';
23
import { randomBytes } from 'node:crypto';
34
import { execFile } from 'node:child_process';
@@ -113,6 +114,7 @@ else {
113114
ipcMain,
114115
operationMetrics: developerOperationMetrics,
115116
});
117+
registerGlassMenuIPC({ handle: handleMeasuredIPC });
116118
let backend;
117119
let window;
118120
let credentials;
@@ -145,6 +147,7 @@ else {
145147
return {
146148
platform: process.platform,
147149
accent: '#' + systemAccent(),
150+
interfaceStyle: state.interfaceStyle || 'default',
148151
appearance: state.appearance || 'system',
149152
accentColor: state.accentColor || 'system',
150153
reduceMotion: !!state.reduceMotion,

‎electron/main/ipc/annotation-ipc.mjs‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { popupGlassMenu } from '../services/glass-menu.mjs';
12
import { createAIDiscussionService } from '../services/ai-discussion.mjs';
23
import { clipboard, dialog, Menu, ShareMenu, shell } from 'electron';
34

@@ -89,10 +90,17 @@ export function registerAnnotationIPC({
8990
...(kind === 'comment' ? [item('修改')] : []),
9091
item('删除'),
9192
];
92-
Menu.buildFromTemplate(template).popup({
93-
window: target,
94-
callback: () => resolve(selected),
95-
});
93+
const callback = () => resolve(selected);
94+
if (
95+
!popupGlassMenu({
96+
target,
97+
template,
98+
Menu,
99+
callback,
100+
enabled: registry.stateFor(target)?.preferences?.interfaceStyle === 'liquid-glass',
101+
})
102+
)
103+
Menu.buildFromTemplate(template).popup({ window: target, callback });
96104
});
97105
});
98106
handle('previewAnnotations:markDeleteHint', async (event) => {

‎electron/main/ipc/preferences-ipc.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const SETTINGS_PREFERENCES = [
4242
'translationServiceHistory',
4343
];
4444
const APPEARANCE_PREFERENCES = [
45+
'interfaceStyle',
4546
'appearance',
4647
'accentColor',
4748
'reduceMotion',

‎electron/main/ipc/recents-ipc.mjs‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { popupGlassMenu } from '../services/glass-menu.mjs';
12
import { app, clipboard, dialog, Menu, shell } from 'electron';
23
import { randomBytes } from 'node:crypto';
34

@@ -197,10 +198,17 @@ export function registerRecentsIPC({
197198
},
198199
];
199200
try {
200-
Menu.buildFromTemplate(template).popup({
201-
window: target,
202-
callback: () => pending.then(() => resolve(selected), reject),
203-
});
201+
const callback = () => pending.then(() => resolve(selected), reject);
202+
if (
203+
!popupGlassMenu({
204+
target,
205+
template,
206+
Menu,
207+
callback,
208+
enabled: preferences?.load?.().interfaceStyle === 'liquid-glass',
209+
})
210+
)
211+
Menu.buildFromTemplate(template).popup({ window: target, callback });
204212
} catch (error) {
205213
reject(error);
206214
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { randomUUID } from 'node:crypto';
2+
3+
const pending = new Map();
4+
export function registerGlassMenuIPC({ handle }) {
5+
handle('glass-menu:choice', (event, value) => {
6+
const entry = pending.get(event.sender.id);
7+
if (!entry || event.senderFrame !== event.sender.mainFrame || value?.id !== entry.id)
8+
return false;
9+
const item = value.item === null ? null : entry.items.get(value.item);
10+
if (value.item !== null && (!item || item.enabled === false || item.type === 'separator'))
11+
return false;
12+
try {
13+
if (item?.role === 'copy') event.sender.copy();
14+
else if (item?.role === 'selectAll') event.sender.selectAll();
15+
else item?.click?.(item, entry.target);
16+
} finally {
17+
entry.finish();
18+
}
19+
return true;
20+
});
21+
}
22+
23+
// Keep commands and file paths in the main process. The renderer receives only
24+
// presentation data and can choose one item from its own outstanding menu.
25+
export function popupGlassMenu({ target, template, enabled, Menu, callback, x, y }) {
26+
if (!enabled || process.platform !== 'darwin') return false;
27+
const wc = target.webContents;
28+
pending.get(wc.id)?.finish();
29+
const nativeItems = Menu.buildFromTemplate(template).items;
30+
const id = randomUUID();
31+
const items = new Map(template.map((item, index) => [String(index), item]));
32+
const finish = () => {
33+
if (pending.get(wc.id)?.id !== id) return;
34+
pending.delete(wc.id);
35+
wc.removeListener('destroyed', finish);
36+
wc.removeListener('did-start-navigation', finish);
37+
if (!wc.isDestroyed()) wc.send('glass-menu:closed', id);
38+
callback?.();
39+
};
40+
pending.set(wc.id, { id, items, target, finish });
41+
wc.once('destroyed', finish);
42+
wc.once('did-start-navigation', finish);
43+
wc.send('glass-menu:open', {
44+
id,
45+
x,
46+
y,
47+
items: template.map((item, index) => ({
48+
id: String(index),
49+
label: nativeItems[index]?.label || item.label || item.role,
50+
type: item.type || 'normal',
51+
enabled: item.enabled !== false,
52+
})),
53+
});
54+
return true;
55+
}

‎electron/main/services/preferences.mjs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const DEFAULT_PREFERENCES = Object.freeze({
4141
emphasizeOrdinals: true,
4242
emphasizeKeyVerbs: true,
4343
emphasizeLogicalConnectives: true,
44+
interfaceStyle: 'default',
4445
appearance: 'system',
4546
accentColor: 'system',
4647
reduceMotion: false,
@@ -250,6 +251,7 @@ const VALIDATORS = {
250251
zoom: isValidZoom,
251252
translationMode: (value) => ['full', 'reading'].includes(value),
252253
interactionMode: (value) => ['reading', 'comparison'].includes(value),
254+
interfaceStyle: (value) => ['default', 'liquid-glass'].includes(value),
253255
appearance: (value) => ['light', 'dark', 'system'].includes(value),
254256
accentColor: isValidAccentColor,
255257
reduceMotion: (value) => typeof value === 'boolean',
@@ -282,6 +284,7 @@ const VALIDATION_MESSAGES = {
282284
direction: 'Invalid layout direction',
283285
columns: 'Invalid layout columns',
284286
engine: 'Invalid translation kernel',
287+
interfaceStyle: 'Invalid interface style',
285288
appearance: 'Invalid appearance',
286289
accentColor: 'Invalid accent color',
287290
reduceMotion: 'Invalid reduce motion preference',

‎electron/main/windows/window-controller.mjs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { popupGlassMenu } from '../services/glass-menu.mjs';
12
import { applicationPath } from '../../../runtime/node/application-paths.mjs';
23
import { app, BrowserWindow, dialog, Menu, shell } from 'electron';
34
import { randomBytes } from 'node:crypto';
@@ -401,6 +402,17 @@ export function createWindowController({
401402
click: () => void shellApi.openExternal(`https://scholar.google.com/scholar?q=${query}`),
402403
},
403404
);
405+
if (
406+
popupGlassMenu({
407+
target,
408+
template,
409+
Menu: MenuApi,
410+
enabled: registry.stateFor(target)?.preferences?.interfaceStyle === 'liquid-glass',
411+
x: params.x,
412+
y: params.y,
413+
})
414+
)
415+
return;
404416
MenuApi.buildFromTemplate(template).popup({ window: target });
405417
});
406418
const session = target.webContents.session;

‎electron/preload.cjs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,20 @@ contextBridge.exposeInMainWorld(
296296
},
297297
}),
298298
);
299+
300+
contextBridge.exposeInMainWorld(
301+
'previewGlassMenu',
302+
Object.freeze({
303+
choose: (value) => ipcRenderer.invoke('glass-menu:choice', value),
304+
onOpen: (callback) => {
305+
const listener = (_event, value) => callback(value);
306+
ipcRenderer.on('glass-menu:open', listener);
307+
return () => ipcRenderer.removeListener('glass-menu:open', listener);
308+
},
309+
onClose: (callback) => {
310+
const listener = (_event, id) => callback(id);
311+
ipcRenderer.on('glass-menu:closed', listener);
312+
return () => ipcRenderer.removeListener('glass-menu:closed', listener);
313+
},
314+
}),
315+
);

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
},
2323
"dependencies": {
2424
"@firecrawl/pdf-inspector": "latest",
25+
"@glass-sdk/liquid-glass": "0.0.1",
2526
"@thednp/dommatrix": "3.1.2",
2627
"express": "^5.1.0",
2728
"onnxruntime-node": "1.22.0",

0 commit comments

Comments
 (0)