@workbench-kit/electron-shell 0.0.2-prototype.0.2.6 → 0.0.2-prototype.0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/{root-confined-asset-protocol.ts → assets/root-confined-asset-protocol.ts} +162 -148
- package/src/index.ts +49 -49
- package/src/{encrypted-secret-vault.ts → secrets/encrypted-secret-vault.ts} +140 -123
- package/src/{open-allowlisted-external-link.ts → security/open-allowlisted-external-link.ts} +66 -66
- package/src/{require-owned-window-for-sender.ts → security/require-owned-window-for-sender.ts} +31 -31
- package/src/{wallpaper-crop.ts → wallpaper/wallpaper-crop.ts} +83 -83
- package/src/{window-controls.ts → window/window-controls.ts} +150 -150
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
import { requireOwnedWindowForSender } from '
|
|
2
|
-
|
|
3
|
-
export interface WindowControlSurface {
|
|
4
|
-
minimize(): void;
|
|
5
|
-
maximize(): void;
|
|
6
|
-
unmaximize(): void;
|
|
7
|
-
close(): void;
|
|
8
|
-
isMaximized(): boolean;
|
|
9
|
-
onMaximizedChange?(listener: (maximized: boolean) => void): () => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface WindowControlIpcChannels {
|
|
13
|
-
readonly minimize: string;
|
|
14
|
-
readonly toggleMaximized: string;
|
|
15
|
-
readonly close: string;
|
|
16
|
-
readonly isMaximized: string;
|
|
17
|
-
readonly maximizedChanged: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Narrow ipcMain surface — hosts inject Electron `ipcMain` or a fake. */
|
|
21
|
-
export interface WindowControlIpcMain {
|
|
22
|
-
handle(
|
|
23
|
-
channel: string,
|
|
24
|
-
listener: (event: { sender: unknown }, ...args: unknown[]) => unknown,
|
|
25
|
-
): void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Narrow webContents surface for push events. */
|
|
29
|
-
export interface WindowControlWebContents {
|
|
30
|
-
send(channel: string, ...args: unknown[]): void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface RegisterWindowControlIpcOptions {
|
|
34
|
-
readonly ipcMain: WindowControlIpcMain;
|
|
35
|
-
readonly channels: WindowControlIpcChannels;
|
|
36
|
-
readonly resolveWindow: (sender: unknown) => WindowControlSurface | null;
|
|
37
|
-
/**
|
|
38
|
-
* Optional: push maximized-changed to the sender webContents.
|
|
39
|
-
* When omitted, `onMaximizedChange` on the window surface is still subscribed if present,
|
|
40
|
-
* but no IPC push is sent.
|
|
41
|
-
*/
|
|
42
|
-
readonly resolveWebContents?: (sender: unknown) => WindowControlWebContents | null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface WindowControlsBridge {
|
|
46
|
-
minimize(): Promise<void>;
|
|
47
|
-
toggleMaximized(): Promise<void>;
|
|
48
|
-
close(): Promise<void>;
|
|
49
|
-
isMaximized(): Promise<boolean>;
|
|
50
|
-
onMaximizedChanged(listener: (maximized: boolean) => void): () => void;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface CreateWindowControlsBridgeOptions {
|
|
54
|
-
readonly channels: WindowControlIpcChannels;
|
|
55
|
-
readonly invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
|
56
|
-
readonly subscribe: (channel: string, listener: (...args: unknown[]) => void) => () => void;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Pure helper: next maximized state after a toggle. */
|
|
60
|
-
export function nextMaximizedState(isMaximized: boolean): boolean {
|
|
61
|
-
return !isMaximized;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Register frameless window-control IPC handlers on an injected ipcMain.
|
|
66
|
-
* Hosts inject channel names and resolve owned windows (pair with sender gate).
|
|
67
|
-
*/
|
|
68
|
-
export function registerWindowControlIpc(options: RegisterWindowControlIpcOptions): () => void {
|
|
69
|
-
const { ipcMain, channels, resolveWindow, resolveWebContents } = options;
|
|
70
|
-
const unsubscribers = new Map<unknown, () => void>();
|
|
71
|
-
|
|
72
|
-
const resolveOwned = (sender: unknown): WindowControlSurface =>
|
|
73
|
-
requireOwnedWindowForSender(sender, resolveWindow);
|
|
74
|
-
|
|
75
|
-
const ensureMaximizedPush = (sender: unknown, windowSurface: WindowControlSurface): void => {
|
|
76
|
-
if (!windowSurface.onMaximizedChange || unsubscribers.has(sender)) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const unsubscribe = windowSurface.onMaximizedChange((maximized) => {
|
|
80
|
-
const webContents = resolveWebContents?.(sender) ?? null;
|
|
81
|
-
webContents?.send(channels.maximizedChanged, maximized);
|
|
82
|
-
});
|
|
83
|
-
unsubscribers.set(sender, unsubscribe);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
ipcMain.handle(channels.minimize, (event) => {
|
|
87
|
-
const windowSurface = resolveOwned(event.sender);
|
|
88
|
-
ensureMaximizedPush(event.sender, windowSurface);
|
|
89
|
-
windowSurface.minimize();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
ipcMain.handle(channels.toggleMaximized, (event) => {
|
|
93
|
-
const windowSurface = resolveOwned(event.sender);
|
|
94
|
-
ensureMaximizedPush(event.sender, windowSurface);
|
|
95
|
-
if (windowSurface.isMaximized()) {
|
|
96
|
-
windowSurface.unmaximize();
|
|
97
|
-
} else {
|
|
98
|
-
windowSurface.maximize();
|
|
99
|
-
}
|
|
100
|
-
return windowSurface.isMaximized();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
ipcMain.handle(channels.close, (event) => {
|
|
104
|
-
const windowSurface = resolveOwned(event.sender);
|
|
105
|
-
windowSurface.close();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
ipcMain.handle(channels.isMaximized, (event) => {
|
|
109
|
-
const windowSurface = resolveOwned(event.sender);
|
|
110
|
-
ensureMaximizedPush(event.sender, windowSurface);
|
|
111
|
-
return windowSurface.isMaximized();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
return () => {
|
|
115
|
-
for (const unsubscribe of unsubscribers.values()) {
|
|
116
|
-
unsubscribe();
|
|
117
|
-
}
|
|
118
|
-
unsubscribers.clear();
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Preload/renderer bridge factory for window controls.
|
|
124
|
-
* Channel names are injected; kit owns the invoke/subscribe shape.
|
|
125
|
-
*/
|
|
126
|
-
export function createWindowControlsBridge(
|
|
127
|
-
options: CreateWindowControlsBridgeOptions,
|
|
128
|
-
): WindowControlsBridge {
|
|
129
|
-
const { channels, invoke, subscribe } = options;
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
minimize: async () => {
|
|
133
|
-
await invoke(channels.minimize);
|
|
134
|
-
},
|
|
135
|
-
toggleMaximized: async () => {
|
|
136
|
-
await invoke(channels.toggleMaximized);
|
|
137
|
-
},
|
|
138
|
-
close: async () => {
|
|
139
|
-
await invoke(channels.close);
|
|
140
|
-
},
|
|
141
|
-
isMaximized: async () => {
|
|
142
|
-
const value = await invoke(channels.isMaximized);
|
|
143
|
-
return Boolean(value);
|
|
144
|
-
},
|
|
145
|
-
onMaximizedChanged: (listener) =>
|
|
146
|
-
subscribe(channels.maximizedChanged, (maximized) => {
|
|
147
|
-
listener(Boolean(maximized));
|
|
148
|
-
}),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
1
|
+
import { requireOwnedWindowForSender } from '../security/require-owned-window-for-sender.js';
|
|
2
|
+
|
|
3
|
+
export interface WindowControlSurface {
|
|
4
|
+
minimize(): void;
|
|
5
|
+
maximize(): void;
|
|
6
|
+
unmaximize(): void;
|
|
7
|
+
close(): void;
|
|
8
|
+
isMaximized(): boolean;
|
|
9
|
+
onMaximizedChange?(listener: (maximized: boolean) => void): () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface WindowControlIpcChannels {
|
|
13
|
+
readonly minimize: string;
|
|
14
|
+
readonly toggleMaximized: string;
|
|
15
|
+
readonly close: string;
|
|
16
|
+
readonly isMaximized: string;
|
|
17
|
+
readonly maximizedChanged: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Narrow ipcMain surface — hosts inject Electron `ipcMain` or a fake. */
|
|
21
|
+
export interface WindowControlIpcMain {
|
|
22
|
+
handle(
|
|
23
|
+
channel: string,
|
|
24
|
+
listener: (event: { sender: unknown }, ...args: unknown[]) => unknown,
|
|
25
|
+
): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Narrow webContents surface for push events. */
|
|
29
|
+
export interface WindowControlWebContents {
|
|
30
|
+
send(channel: string, ...args: unknown[]): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RegisterWindowControlIpcOptions {
|
|
34
|
+
readonly ipcMain: WindowControlIpcMain;
|
|
35
|
+
readonly channels: WindowControlIpcChannels;
|
|
36
|
+
readonly resolveWindow: (sender: unknown) => WindowControlSurface | null;
|
|
37
|
+
/**
|
|
38
|
+
* Optional: push maximized-changed to the sender webContents.
|
|
39
|
+
* When omitted, `onMaximizedChange` on the window surface is still subscribed if present,
|
|
40
|
+
* but no IPC push is sent.
|
|
41
|
+
*/
|
|
42
|
+
readonly resolveWebContents?: (sender: unknown) => WindowControlWebContents | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WindowControlsBridge {
|
|
46
|
+
minimize(): Promise<void>;
|
|
47
|
+
toggleMaximized(): Promise<void>;
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
isMaximized(): Promise<boolean>;
|
|
50
|
+
onMaximizedChanged(listener: (maximized: boolean) => void): () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CreateWindowControlsBridgeOptions {
|
|
54
|
+
readonly channels: WindowControlIpcChannels;
|
|
55
|
+
readonly invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
|
56
|
+
readonly subscribe: (channel: string, listener: (...args: unknown[]) => void) => () => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Pure helper: next maximized state after a toggle. */
|
|
60
|
+
export function nextMaximizedState(isMaximized: boolean): boolean {
|
|
61
|
+
return !isMaximized;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Register frameless window-control IPC handlers on an injected ipcMain.
|
|
66
|
+
* Hosts inject channel names and resolve owned windows (pair with sender gate).
|
|
67
|
+
*/
|
|
68
|
+
export function registerWindowControlIpc(options: RegisterWindowControlIpcOptions): () => void {
|
|
69
|
+
const { ipcMain, channels, resolveWindow, resolveWebContents } = options;
|
|
70
|
+
const unsubscribers = new Map<unknown, () => void>();
|
|
71
|
+
|
|
72
|
+
const resolveOwned = (sender: unknown): WindowControlSurface =>
|
|
73
|
+
requireOwnedWindowForSender(sender, resolveWindow);
|
|
74
|
+
|
|
75
|
+
const ensureMaximizedPush = (sender: unknown, windowSurface: WindowControlSurface): void => {
|
|
76
|
+
if (!windowSurface.onMaximizedChange || unsubscribers.has(sender)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const unsubscribe = windowSurface.onMaximizedChange((maximized) => {
|
|
80
|
+
const webContents = resolveWebContents?.(sender) ?? null;
|
|
81
|
+
webContents?.send(channels.maximizedChanged, maximized);
|
|
82
|
+
});
|
|
83
|
+
unsubscribers.set(sender, unsubscribe);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
ipcMain.handle(channels.minimize, (event) => {
|
|
87
|
+
const windowSurface = resolveOwned(event.sender);
|
|
88
|
+
ensureMaximizedPush(event.sender, windowSurface);
|
|
89
|
+
windowSurface.minimize();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
ipcMain.handle(channels.toggleMaximized, (event) => {
|
|
93
|
+
const windowSurface = resolveOwned(event.sender);
|
|
94
|
+
ensureMaximizedPush(event.sender, windowSurface);
|
|
95
|
+
if (windowSurface.isMaximized()) {
|
|
96
|
+
windowSurface.unmaximize();
|
|
97
|
+
} else {
|
|
98
|
+
windowSurface.maximize();
|
|
99
|
+
}
|
|
100
|
+
return windowSurface.isMaximized();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
ipcMain.handle(channels.close, (event) => {
|
|
104
|
+
const windowSurface = resolveOwned(event.sender);
|
|
105
|
+
windowSurface.close();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
ipcMain.handle(channels.isMaximized, (event) => {
|
|
109
|
+
const windowSurface = resolveOwned(event.sender);
|
|
110
|
+
ensureMaximizedPush(event.sender, windowSurface);
|
|
111
|
+
return windowSurface.isMaximized();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
for (const unsubscribe of unsubscribers.values()) {
|
|
116
|
+
unsubscribe();
|
|
117
|
+
}
|
|
118
|
+
unsubscribers.clear();
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Preload/renderer bridge factory for window controls.
|
|
124
|
+
* Channel names are injected; kit owns the invoke/subscribe shape.
|
|
125
|
+
*/
|
|
126
|
+
export function createWindowControlsBridge(
|
|
127
|
+
options: CreateWindowControlsBridgeOptions,
|
|
128
|
+
): WindowControlsBridge {
|
|
129
|
+
const { channels, invoke, subscribe } = options;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
minimize: async () => {
|
|
133
|
+
await invoke(channels.minimize);
|
|
134
|
+
},
|
|
135
|
+
toggleMaximized: async () => {
|
|
136
|
+
await invoke(channels.toggleMaximized);
|
|
137
|
+
},
|
|
138
|
+
close: async () => {
|
|
139
|
+
await invoke(channels.close);
|
|
140
|
+
},
|
|
141
|
+
isMaximized: async () => {
|
|
142
|
+
const value = await invoke(channels.isMaximized);
|
|
143
|
+
return Boolean(value);
|
|
144
|
+
},
|
|
145
|
+
onMaximizedChanged: (listener) =>
|
|
146
|
+
subscribe(channels.maximizedChanged, (maximized) => {
|
|
147
|
+
listener(Boolean(maximized));
|
|
148
|
+
}),
|
|
149
|
+
};
|
|
150
|
+
}
|