@noxfly/noxus 3.0.0-dev.4 → 3.0.0-dev.5
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/dist/child.js.map +1 -0
- package/dist/child.mjs.map +1 -0
- package/dist/main.js.map +1 -0
- package/dist/main.mjs.map +1 -0
- package/dist/preload.js.map +1 -0
- package/dist/preload.mjs.map +1 -0
- package/dist/renderer.js.map +1 -0
- package/dist/renderer.mjs.map +1 -0
- package/package.json +10 -9
- package/.editorconfig +0 -16
- package/.github/copilot-instructions.md +0 -128
- package/.vscode/settings.json +0 -3
- package/AGENTS.md +0 -5
- package/eslint.config.js +0 -109
- package/scripts/postbuild.js +0 -31
- package/src/DI/app-injector.ts +0 -173
- package/src/DI/injector-explorer.ts +0 -201
- package/src/DI/token.ts +0 -53
- package/src/decorators/controller.decorator.ts +0 -58
- package/src/decorators/guards.decorator.ts +0 -15
- package/src/decorators/injectable.decorator.ts +0 -81
- package/src/decorators/method.decorator.ts +0 -66
- package/src/decorators/middleware.decorator.ts +0 -15
- package/src/index.ts +0 -10
- package/src/internal/app.ts +0 -219
- package/src/internal/bootstrap.ts +0 -141
- package/src/internal/exceptions.ts +0 -57
- package/src/internal/preload-bridge.ts +0 -75
- package/src/internal/renderer-client.ts +0 -374
- package/src/internal/renderer-events.ts +0 -110
- package/src/internal/request.ts +0 -102
- package/src/internal/router.ts +0 -365
- package/src/internal/routes.ts +0 -142
- package/src/internal/socket.ts +0 -75
- package/src/main.ts +0 -26
- package/src/non-electron-process.ts +0 -22
- package/src/preload.ts +0 -10
- package/src/renderer.ts +0 -13
- package/src/utils/forward-ref.ts +0 -31
- package/src/utils/logger.ts +0 -430
- package/src/utils/radix-tree.ts +0 -243
- package/src/utils/types.ts +0 -21
- package/src/window/window-manager.ts +0 -302
- package/tsconfig.json +0 -29
- package/tsup.config.ts +0 -50
package/src/utils/radix-tree.ts
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
type Params = Record<string, string>;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Represents a search result in the Radix Tree.
|
|
14
|
-
*/
|
|
15
|
-
interface ISearchResult<T> {
|
|
16
|
-
node: RadixNode<T>;
|
|
17
|
-
params: Params;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Represents a node in the Radix Tree.
|
|
22
|
-
* The represents a path segment
|
|
23
|
-
*/
|
|
24
|
-
class RadixNode<T> {
|
|
25
|
-
public segment: string;
|
|
26
|
-
public children: RadixNode<T>[] = [];
|
|
27
|
-
public value?: T;
|
|
28
|
-
public isParam: boolean;
|
|
29
|
-
public paramName?: string;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Creates a new RadixNode.
|
|
33
|
-
* @param segment - The segment of the path this node represents.
|
|
34
|
-
*/
|
|
35
|
-
constructor(segment: string) {
|
|
36
|
-
this.segment = segment;
|
|
37
|
-
this.isParam = segment.startsWith(":");
|
|
38
|
-
|
|
39
|
-
if(this.isParam) {
|
|
40
|
-
this.paramName = segment.slice(1);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Matches a child node against a given segment.
|
|
46
|
-
* This method checks if the segment matches any of the children nodes.
|
|
47
|
-
* @param segment - The segment to match against the children of this node.
|
|
48
|
-
* @returns A child node that matches the segment, or undefined if no match is found.
|
|
49
|
-
*/
|
|
50
|
-
public matchChild(segment: string): RadixNode<T> | undefined {
|
|
51
|
-
for(const child of this.children) {
|
|
52
|
-
if(child.isParam || segment.startsWith(child.segment))
|
|
53
|
-
return child; // param match
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Finds a child node that matches the segment exactly.
|
|
61
|
-
* This method checks if there is a child node that matches the segment exactly.
|
|
62
|
-
* @param segment - The segment to find an exact match for among the children of this node.
|
|
63
|
-
* @returns A child node that matches the segment exactly, or undefined if no match is found.
|
|
64
|
-
*/
|
|
65
|
-
public findExactChild(segment: string): RadixNode<T> | undefined {
|
|
66
|
-
return this.children.find(c => c.segment === segment);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Adds a child node to this node's children.
|
|
71
|
-
* This method adds a new child node to the list of children for this node.
|
|
72
|
-
* @param node - The child node to add to this node's children.
|
|
73
|
-
*/
|
|
74
|
-
public addChild(node: RadixNode<T>): void {
|
|
75
|
-
this.children.push(node);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
81
|
-
*/
|
|
82
|
-
export class RadixTree<T> {
|
|
83
|
-
private readonly root = new RadixNode<T>("");
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Inserts a path and its associated value into the Radix Tree.
|
|
87
|
-
* This method normalizes the path and inserts it into the tree, associating it with
|
|
88
|
-
* @param path - The path to insert into the tree.
|
|
89
|
-
* @param value - The value to associate with the path.
|
|
90
|
-
*/
|
|
91
|
-
public insert(path: string, value: T): void {
|
|
92
|
-
const segments = this.normalize(path);
|
|
93
|
-
this.insertRecursive(this.root, segments, value);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Recursively inserts a path into the Radix Tree.
|
|
98
|
-
* This method traverses the tree and inserts the segments of the path, creating new nodes
|
|
99
|
-
* @param node - The node to start inserting from.
|
|
100
|
-
* @param segments - The segments of the path to insert.
|
|
101
|
-
* @param value - The value to associate with the path.
|
|
102
|
-
*/
|
|
103
|
-
private insertRecursive(node: RadixNode<T>, segments: string[], value: T): void {
|
|
104
|
-
if(segments.length === 0) {
|
|
105
|
-
node.value = value;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const segment = segments[0] ?? "";
|
|
110
|
-
|
|
111
|
-
let child = node.children.find(c =>
|
|
112
|
-
c.isParam === segment.startsWith(":") &&
|
|
113
|
-
(c.isParam || c.segment === segment)
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
if(!child) {
|
|
117
|
-
child = new RadixNode<T>(segment);
|
|
118
|
-
node.addChild(child);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.insertRecursive(child, segments.slice(1), value);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Searches for a path in the Radix Tree.
|
|
126
|
-
* This method normalizes the path and searches for it in the tree, returning the node
|
|
127
|
-
* @param path - The path to search for in the Radix Tree.
|
|
128
|
-
* @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
|
|
129
|
-
*/
|
|
130
|
-
public search(path: string): ISearchResult<T> | undefined {
|
|
131
|
-
const segments = this.normalize(path);
|
|
132
|
-
return this.searchRecursive(this.root, segments, {});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Collects all values in the subtree rooted at the given node.
|
|
137
|
-
* This method traverses the subtree starting from the given node and collects all values
|
|
138
|
-
* @param node - The node to start collecting values from.
|
|
139
|
-
* @param values - An array to store the collected values. This parameter is optional and can be used for recursive calls.
|
|
140
|
-
* @returns An array of all values found in the subtree rooted at the given node.
|
|
141
|
-
*/
|
|
142
|
-
public collectValues(): T[];
|
|
143
|
-
public collectValues(node: RadixNode<T>, values: T[]): T[];
|
|
144
|
-
public collectValues(node?: RadixNode<T>, values: T[] = []): T[] {
|
|
145
|
-
if(!node) {
|
|
146
|
-
node = this.root;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if(node.value !== undefined) {
|
|
150
|
-
values.push(node.value);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
for(const child of node.children) {
|
|
154
|
-
this.collectValues(child, values);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return values;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Recursively searches for a path in the Radix Tree.
|
|
162
|
-
* This method traverses the tree and searches for the segments of the path, collecting parameters
|
|
163
|
-
* @param node - The node to start searching from.
|
|
164
|
-
* @param segments - The segments of the path to search for.
|
|
165
|
-
* @param params - The parameters collected during the search.
|
|
166
|
-
* @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
|
|
167
|
-
*/
|
|
168
|
-
private searchRecursive(node: RadixNode<T>, segments: string[], params: Params): ISearchResult<T> | undefined {
|
|
169
|
-
if(segments.length === 0) {
|
|
170
|
-
if(node.value !== undefined) {
|
|
171
|
-
return {
|
|
172
|
-
node: node,
|
|
173
|
-
params
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const [segment, ...rest] = segments;
|
|
181
|
-
|
|
182
|
-
// Try static (exact) matches first, then param matches.
|
|
183
|
-
// This ensures e.g. 'addNote' is preferred over ':id'.
|
|
184
|
-
const staticChildren: RadixNode<T>[] = [];
|
|
185
|
-
const paramChildren: RadixNode<T>[] = [];
|
|
186
|
-
|
|
187
|
-
for(const child of node.children) {
|
|
188
|
-
if(child.isParam) {
|
|
189
|
-
paramChildren.push(child);
|
|
190
|
-
}
|
|
191
|
-
else if(segment === child.segment) {
|
|
192
|
-
staticChildren.push(child);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
for(const child of staticChildren) {
|
|
197
|
-
if(rest.length === 0) {
|
|
198
|
-
// Only return leaf-level matches (has children for method nodes, or has a value)
|
|
199
|
-
if(child.value !== undefined || child.children.length > 0) {
|
|
200
|
-
return { node: child, params };
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const result = this.searchRecursive(child, rest, params);
|
|
205
|
-
if(result) return result;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
for(const child of paramChildren) {
|
|
209
|
-
const paramName = child.paramName!;
|
|
210
|
-
|
|
211
|
-
const childParams: Params = {
|
|
212
|
-
...params,
|
|
213
|
-
[paramName]: segment ?? "",
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
if(rest.length === 0) {
|
|
217
|
-
if(child.value !== undefined || child.children.length > 0) {
|
|
218
|
-
return { node: child, params: childParams };
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const result = this.searchRecursive(child, rest, childParams);
|
|
223
|
-
if(result) return result;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return undefined;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Normalizes a path into an array of segments.
|
|
231
|
-
* This method removes leading and trailing slashes, splits the path by slashes, and
|
|
232
|
-
* @param path - The path to normalize.
|
|
233
|
-
* @returns An array of normalized path segments.
|
|
234
|
-
*/
|
|
235
|
-
private normalize(path: string): string[] {
|
|
236
|
-
const segments = path
|
|
237
|
-
.replace(/^\/+|\/+$/g, "")
|
|
238
|
-
.split("/")
|
|
239
|
-
.filter(Boolean);
|
|
240
|
-
|
|
241
|
-
return ['', ...segments];
|
|
242
|
-
}
|
|
243
|
-
}
|
package/src/utils/types.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Represents a generic type that can be either a class or a function.
|
|
10
|
-
* This is used to define types that can be instantiated or called.
|
|
11
|
-
*/
|
|
12
|
-
declare const Type: FunctionConstructor;
|
|
13
|
-
export interface Type<T> extends Function {
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
|
15
|
-
new (...args: any[]): T;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Represents a generic type that can be either a value or a promise resolving to that value.
|
|
20
|
-
*/
|
|
21
|
-
export type MaybeAsync<T> = T | Promise<T>;
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { BrowserWindow } from 'electron/main';
|
|
8
|
-
import { Injectable } from '../decorators/injectable.decorator';
|
|
9
|
-
import { Logger } from '../utils/logger';
|
|
10
|
-
|
|
11
|
-
export interface WindowConfig extends Electron.BrowserWindowConstructorOptions {
|
|
12
|
-
/**
|
|
13
|
-
* If true, the window expands to fill the work area after creation
|
|
14
|
-
* using an animated setBounds. The content is loaded only after
|
|
15
|
-
* the animation completes, preventing the viewbox freeze issue.
|
|
16
|
-
* @default false
|
|
17
|
-
*/
|
|
18
|
-
expandToWorkArea?: boolean;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Duration in ms to wait for the setBounds animation to complete
|
|
22
|
-
* before loading content. Only used when expandToWorkArea is true.
|
|
23
|
-
* @default 600
|
|
24
|
-
*/
|
|
25
|
-
expandAnimationDuration?: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface WindowRecord {
|
|
29
|
-
window: BrowserWindow;
|
|
30
|
-
id: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @description
|
|
35
|
-
* The events emitted by WindowManager when windows are created, closed, focused, or blurred.
|
|
36
|
-
*/
|
|
37
|
-
export type WindowEvent = 'created' | 'closed' | 'focused' | 'blurred';
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* WindowManager is a singleton service that centralizes BrowserWindow lifecycle.
|
|
41
|
-
*
|
|
42
|
-
* Features:
|
|
43
|
-
* - Creates and tracks all application windows.
|
|
44
|
-
* - Handles the animated expand-to-work-area pattern correctly,
|
|
45
|
-
* loading content only after the animation ends to avoid the viewbox freeze.
|
|
46
|
-
* - Provides convenience methods to get windows by id, get the main window, etc.
|
|
47
|
-
* - Automatically removes windows from the registry on close.
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* // In your IApp.onReady():
|
|
51
|
-
* const wm = inject(WindowManager);
|
|
52
|
-
*
|
|
53
|
-
* const win = await wm.create({
|
|
54
|
-
* width: 600, height: 600, center: true,
|
|
55
|
-
* expandToWorkArea: true,
|
|
56
|
-
* webPreferences: { preload: path.join(__dirname, 'preload.js') },
|
|
57
|
-
* });
|
|
58
|
-
*
|
|
59
|
-
* win.loadFile('index.html');
|
|
60
|
-
*/
|
|
61
|
-
@Injectable({ lifetime: 'singleton' })
|
|
62
|
-
export class WindowManager {
|
|
63
|
-
private readonly _windows = new Map<number, BrowserWindow>();
|
|
64
|
-
private readonly listeners = new Map<WindowEvent, Set<(win: BrowserWindow) => void>>();
|
|
65
|
-
|
|
66
|
-
private _mainWindowId: number | undefined;
|
|
67
|
-
|
|
68
|
-
// -------------------------------------------------------------------------
|
|
69
|
-
// Creation
|
|
70
|
-
// -------------------------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Creates a BrowserWindow, optionally performs an animated expand to the
|
|
74
|
-
* work area, and registers it in the manager.
|
|
75
|
-
*
|
|
76
|
-
* If expandToWorkArea is true:
|
|
77
|
-
* 1. The window is created at the given initial size (defaults to 600×600, centered).
|
|
78
|
-
* 2. An animated setBounds expands it to the full work area.
|
|
79
|
-
* 3. The returned promise resolves only after the animation, so callers
|
|
80
|
-
* can safely call win.loadFile() without the viewbox freeze.
|
|
81
|
-
*
|
|
82
|
-
* @param config Window configuration.
|
|
83
|
-
* @param isMain Mark this window as the main window (accessible via getMain()).
|
|
84
|
-
*/
|
|
85
|
-
public async create(config: WindowConfig, isMain = false): Promise<BrowserWindow> {
|
|
86
|
-
const {
|
|
87
|
-
expandToWorkArea = false,
|
|
88
|
-
expandAnimationDuration = 600,
|
|
89
|
-
...bwOptions
|
|
90
|
-
} = config;
|
|
91
|
-
|
|
92
|
-
// show: false by default during creation — we control visibility
|
|
93
|
-
const win = new BrowserWindow({ show: false, ...bwOptions });
|
|
94
|
-
|
|
95
|
-
this._register(win, isMain);
|
|
96
|
-
|
|
97
|
-
if (expandToWorkArea) {
|
|
98
|
-
await this._expandToWorkArea(win, expandAnimationDuration);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
win.once('ready-to-show', () => win.show());
|
|
102
|
-
|
|
103
|
-
Logger.log(`[WindowManager] Created window #${win.id}${isMain ? ' (main)' : ''}`);
|
|
104
|
-
|
|
105
|
-
return win;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Creates the initial "splash" window that is shown immediately after
|
|
110
|
-
* app.whenReady(). It is displayed instantly (show: true, no preload
|
|
111
|
-
* loading) and then expanded to the work area with animation.
|
|
112
|
-
*
|
|
113
|
-
* After the animation completes you can call win.loadFile() without
|
|
114
|
-
* experiencing the viewbox freeze.
|
|
115
|
-
*
|
|
116
|
-
* This is the recommended way to get pixels on screen as fast as possible.
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* const win = await wm.createSplash({
|
|
120
|
-
* webPreferences: { preload: path.join(__dirname, 'preload.js') }
|
|
121
|
-
* });
|
|
122
|
-
* win.loadFile('index.html');
|
|
123
|
-
*/
|
|
124
|
-
public async createSplash(
|
|
125
|
-
options: Electron.BrowserWindowConstructorOptions & {
|
|
126
|
-
animationDuration?: number;
|
|
127
|
-
expandToWorkArea?: boolean;
|
|
128
|
-
} = {},
|
|
129
|
-
): Promise<BrowserWindow> {
|
|
130
|
-
const {
|
|
131
|
-
animationDuration = 10,
|
|
132
|
-
expandToWorkArea = true,
|
|
133
|
-
...bwOptions
|
|
134
|
-
} = options;
|
|
135
|
-
|
|
136
|
-
const win = new BrowserWindow({
|
|
137
|
-
width: 600,
|
|
138
|
-
height: 600,
|
|
139
|
-
center: true,
|
|
140
|
-
show: true,
|
|
141
|
-
...bwOptions,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
this._register(win, true);
|
|
145
|
-
|
|
146
|
-
Logger.log(`[WindowManager] Splash window #${win.id} created`);
|
|
147
|
-
|
|
148
|
-
if(expandToWorkArea) {
|
|
149
|
-
await (() => new Promise((r) => setTimeout(r, 500)))();
|
|
150
|
-
await this._expandToWorkArea(win, animationDuration);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return win;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// -------------------------------------------------------------------------
|
|
157
|
-
// Accessors
|
|
158
|
-
// -------------------------------------------------------------------------
|
|
159
|
-
|
|
160
|
-
/** Returns all currently open windows. */
|
|
161
|
-
public getAll(): BrowserWindow[] {
|
|
162
|
-
return [...this._windows.values()];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Returns the window designated as main, or undefined. */
|
|
166
|
-
public getMain(): BrowserWindow | undefined {
|
|
167
|
-
return this._mainWindowId !== undefined
|
|
168
|
-
? this._windows.get(this._mainWindowId)
|
|
169
|
-
: undefined;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** Returns a window by its Electron id, or undefined. */
|
|
173
|
-
public getById(id: number): BrowserWindow | undefined {
|
|
174
|
-
return this._windows.get(id);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** Returns the number of open windows. */
|
|
178
|
-
public get count(): number {
|
|
179
|
-
return this._windows.size;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// -------------------------------------------------------------------------
|
|
183
|
-
// Actions
|
|
184
|
-
// -------------------------------------------------------------------------
|
|
185
|
-
|
|
186
|
-
/** Closes and destroys a window by id. */
|
|
187
|
-
public close(id: number): void {
|
|
188
|
-
const win = this._windows.get(id);
|
|
189
|
-
|
|
190
|
-
if (!win) {
|
|
191
|
-
Logger.warn(`[WindowManager] Window #${id} not found`);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
win.destroy();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Closes all windows. */
|
|
199
|
-
public closeAll(): void {
|
|
200
|
-
for (const win of this._windows.values()) {
|
|
201
|
-
win.destroy();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Sends a message to a specific window via webContents.send.
|
|
207
|
-
* @param id Target window id.
|
|
208
|
-
* @param channel IPC channel name.
|
|
209
|
-
* @param args Payload.
|
|
210
|
-
*/
|
|
211
|
-
public send(id: number, channel: string, ...args: unknown[]): void {
|
|
212
|
-
const win = this._windows.get(id);
|
|
213
|
-
if (!win || win.isDestroyed()) {
|
|
214
|
-
Logger.warn(`[WindowManager] Cannot send to window #${id}: not found or destroyed`);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
win.webContents.send(channel, ...args);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Broadcasts a message to all open windows.
|
|
222
|
-
*/
|
|
223
|
-
public broadcast(channel: string, ...args: unknown[]): void {
|
|
224
|
-
for (const win of this._windows.values()) {
|
|
225
|
-
if (!win.isDestroyed()) {
|
|
226
|
-
win.webContents.send(channel, ...args);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// -------------------------------------------------------------------------
|
|
232
|
-
// Events
|
|
233
|
-
// -------------------------------------------------------------------------
|
|
234
|
-
|
|
235
|
-
public on(event: WindowEvent, handler: (win: BrowserWindow) => void): () => void {
|
|
236
|
-
const set = this.listeners.get(event) ?? new Set();
|
|
237
|
-
set.add(handler);
|
|
238
|
-
this.listeners.set(event, set);
|
|
239
|
-
return () => set.delete(handler); // retourne unsubscribe
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
private _emit(event: WindowEvent, win: BrowserWindow): void {
|
|
243
|
-
this.listeners.get(event)?.forEach(h => h(win));
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// -------------------------------------------------------------------------
|
|
247
|
-
// Private
|
|
248
|
-
// -------------------------------------------------------------------------
|
|
249
|
-
|
|
250
|
-
private _register(win: BrowserWindow, isMain: boolean): void {
|
|
251
|
-
this._windows.set(win.id, win);
|
|
252
|
-
|
|
253
|
-
if (isMain && this._mainWindowId === undefined) {
|
|
254
|
-
this._mainWindowId = win.id;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
this._emit('created', win);
|
|
258
|
-
|
|
259
|
-
win.on('focus', () => this._emit('focused', win));
|
|
260
|
-
win.on('blur', () => this._emit('blurred', win));
|
|
261
|
-
|
|
262
|
-
win.once('closed', () => {
|
|
263
|
-
this._windows.delete(win.id);
|
|
264
|
-
|
|
265
|
-
if (this._mainWindowId === win.id) {
|
|
266
|
-
this._mainWindowId = undefined;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
Logger.log(`[WindowManager] Window #${win.id} closed`);
|
|
270
|
-
|
|
271
|
-
this._emit('closed', win);
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Animates the window to the full work area of the primary display.
|
|
277
|
-
* Resolves only after the animation is complete, so that content loaded
|
|
278
|
-
* afterward gets the correct surface size (no viewbox freeze).
|
|
279
|
-
*/
|
|
280
|
-
private _expandToWorkArea(win: BrowserWindow, animationDuration: number): Promise<void> {
|
|
281
|
-
return new Promise((resolve) => {
|
|
282
|
-
win.maximize();
|
|
283
|
-
|
|
284
|
-
// Wait for the animation to finish before resolving.
|
|
285
|
-
// We listen to the 'resize' event which fires once the OS
|
|
286
|
-
// animation completes, with a safety timeout as fallback.
|
|
287
|
-
let resolved = false;
|
|
288
|
-
|
|
289
|
-
const done = (): void => {
|
|
290
|
-
if (resolved) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
resolved = true;
|
|
294
|
-
win.removeListener('resize', done);
|
|
295
|
-
resolve();
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
win.once('resize', done);
|
|
299
|
-
setTimeout(done, animationDuration);
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compileOnSave": false,
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": "./src",
|
|
5
|
-
"baseUrl": "./",
|
|
6
|
-
"moduleResolution": "node",
|
|
7
|
-
"target": "ES2020",
|
|
8
|
-
"module": "CommonJS",
|
|
9
|
-
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"noImplicitOverride": true,
|
|
12
|
-
"noImplicitReturns": true,
|
|
13
|
-
"noFallthroughCasesInSwitch": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"experimentalDecorators": true,
|
|
16
|
-
"emitDecoratorMetadata": false,
|
|
17
|
-
"importHelpers": false,
|
|
18
|
-
"esModuleInterop": true,
|
|
19
|
-
"useDefineForClassFields": false,
|
|
20
|
-
"noPropertyAccessFromIndexSignature": false,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"skipLibCheck": true
|
|
23
|
-
},
|
|
24
|
-
"include": [
|
|
25
|
-
"src/**/*.ts",
|
|
26
|
-
"src/**/*.d.ts",
|
|
27
|
-
"node_modules/electron/electron.d.ts"
|
|
28
|
-
]
|
|
29
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { defineConfig, Options } from "tsup";
|
|
2
|
-
|
|
3
|
-
const copyrights = `
|
|
4
|
-
/**
|
|
5
|
-
* @copyright 2025 NoxFly
|
|
6
|
-
* @license MIT
|
|
7
|
-
* @author NoxFly
|
|
8
|
-
*/
|
|
9
|
-
`.trim();
|
|
10
|
-
|
|
11
|
-
const options: Options = {
|
|
12
|
-
keepNames: true,
|
|
13
|
-
minifyIdentifiers: false,
|
|
14
|
-
name: "noxus",
|
|
15
|
-
format: ["cjs", "esm"],
|
|
16
|
-
dts: true,
|
|
17
|
-
sourcemap: true,
|
|
18
|
-
clean: false, // ← false dans le base config
|
|
19
|
-
outDir: "dist",
|
|
20
|
-
external: ["electron"],
|
|
21
|
-
target: "es2020",
|
|
22
|
-
minify: false,
|
|
23
|
-
splitting: false,
|
|
24
|
-
shims: false,
|
|
25
|
-
treeshake: false,
|
|
26
|
-
banner: { js: copyrights },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export default defineConfig([
|
|
30
|
-
{
|
|
31
|
-
entry: { main: 'src/main.ts' },
|
|
32
|
-
external: ['electron', 'electron/main'],
|
|
33
|
-
clean: true, // ← true uniquement ici
|
|
34
|
-
...options,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
entry: { renderer: 'src/renderer.ts' },
|
|
38
|
-
external: ['electron', 'electron/renderer'],
|
|
39
|
-
...options,
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
entry: { preload: 'src/preload.ts' },
|
|
43
|
-
external: ['electron', 'electron/renderer'],
|
|
44
|
-
...options,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
entry: { child: 'src/non-electron-process.ts' },
|
|
48
|
-
...options,
|
|
49
|
-
},
|
|
50
|
-
]);
|